From ae78b1626ef682331af055075fbddb672cb19552 Mon Sep 17 00:00:00 2001 From: lianggao Date: Fri, 29 Jul 2016 22:19:36 +0800 Subject: [PATCH 001/125] Add BLE central function, debug interface and refactor peripheral -Add debug interface on Serial1 -Update BLE stack and need update BLE's FW -Reconstruct the BLE peripheral base on V3 -Implement the BLE Central Role base on V3 -Implement some sketches for new BLE library -Add central read/write example -Add set advertising parameter interface -Add API to allow set up advertising after setup -Add interface to set the device name File description Porting from V3 system/libarc32_arduino101/common/atomic.h system/libarc32_arduino101/common/misc/byteorder.h system/libarc32_arduino101/drivers/atomic_native.c system/libarc32_arduino101/drivers/bluetooth/att.h system/libarc32_arduino101/drivers/bluetooth/bluetooth.h system/libarc32_arduino101/drivers/bluetooth/conn.h system/libarc32_arduino101/drivers/bluetooth/conn_internal.h system/libarc32_arduino101/drivers/bluetooth/gatt.h system/libarc32_arduino101/drivers/bluetooth/hci.h system/libarc32_arduino101/drivers/bluetooth/uuid.h system/libarc32_arduino101/drivers/rpc/rpc.h system/libarc32_arduino101/drivers/rpc/rpc_deserialize.c system/libarc32_arduino101/drivers/rpc/rpc_functions_to_ble_core.h system/libarc32_arduino101/drivers/rpc/rpc_functions_to_quark.h system/libarc32_arduino101/drivers/rpc/rpc_serialize.c system/libarc32_arduino101/framework/include/util/misc.h system/libarc32_arduino101/framework/src/os/panic.c system/libarc32_arduino101/framework/src/services/ble/conn.c system/libarc32_arduino101/framework/src/services/ble/conn_internal.h system/libarc32_arduino101/framework/src/services/ble/dtm_tcmd.c system/libarc32_arduino101/framework/src/services/ble/gap.c system/libarc32_arduino101/framework/src/services/ble/gatt.c system/libarc32_arduino101/framework/src/services/ble/hci_core.h system/libarc32_arduino101/framework/src/services/ble/l2cap.c system/libarc32_arduino101/framework/src/services/ble/l2cap_internal.h system/libarc32_arduino101/framework/src/services/ble/smp.h system/libarc32_arduino101/framework/src/services/ble/smp_null.c system/libarc32_arduino101/framework/src/services/ble/uuid.c system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c system/libarc32_arduino101/framework/src/services/ble_service/ble_service_api.c system/libarc32_arduino101/framework/src/services/ble_service/ble_service_int.h system/libarc32_arduino101/framework/src/services/ble_service/ble_service_internal.h system/libarc32_arduino101/framework/src/services/ble_service/ble_service_utils.c system/libarc32_arduino101/framework/src/services/ble_service/gap_internal.h system/libarc32_arduino101/framework/src/services/ble_service/gatt_internal.h system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.c --- README.md | 18 + .../arduino/printk.cpp | 28 +- .../BatteryMonitor/BatteryMonitor.ino | 2 +- .../examples/CallbackLED/CallbackLED.ino | 6 +- .../examples/IMUBleCentral/IMUBleCentral.ino | 143 ++ .../IMUBleNotification/IMUBleNotification.ino | 120 ++ libraries/CurieBLE/examples/LED/LED.ino | 2 +- .../examples/LEDCentral/LEDCentral.ino | 157 ++ .../CurieBLE/examples/MIDIBLE/MIDIBLE.ino | 6 +- .../CurieBLE/examples/Scanning/Scanning.ino | 129 ++ libraries/CurieBLE/src/BLEAttribute.cpp | 59 +- libraries/CurieBLE/src/BLEAttribute.h | 39 +- libraries/CurieBLE/src/BLECentral.cpp | 119 +- libraries/CurieBLE/src/BLECentral.h | 153 +- libraries/CurieBLE/src/BLECentralHelper.cpp | 103 ++ libraries/CurieBLE/src/BLECentralHelper.h | 72 + libraries/CurieBLE/src/BLECentralRole.cpp | 292 +++ libraries/CurieBLE/src/BLECentralRole.h | 276 +++ libraries/CurieBLE/src/BLECharacteristic.cpp | 341 ++-- libraries/CurieBLE/src/BLECharacteristic.h | 164 +- libraries/CurieBLE/src/BLECommon.h | 42 +- libraries/CurieBLE/src/BLEDescriptor.cpp | 20 - libraries/CurieBLE/src/BLEDescriptor.h | 1 - libraries/CurieBLE/src/BLEHelper.cpp | 135 ++ libraries/CurieBLE/src/BLEHelper.h | 79 + libraries/CurieBLE/src/BLEPeripheral.cpp | 479 ++--- libraries/CurieBLE/src/BLEPeripheral.h | 139 +- .../CurieBLE/src/BLEPeripheralHelper.cpp | 117 ++ libraries/CurieBLE/src/BLEPeripheralHelper.h | 84 + libraries/CurieBLE/src/BLEPeripheralRole.cpp | 278 +++ libraries/CurieBLE/src/BLEPeripheralRole.h | 262 +++ libraries/CurieBLE/src/BLEProfile.cpp | 535 ++++++ libraries/CurieBLE/src/BLEProfile.h | 189 ++ libraries/CurieBLE/src/BLERoleBase.cpp | 86 + libraries/CurieBLE/src/BLERoleBase.h | 133 ++ libraries/CurieBLE/src/BLEService.cpp | 30 +- libraries/CurieBLE/src/BLEService.h | 14 +- libraries/CurieBLE/src/BLEUuid.cpp | 54 - libraries/CurieBLE/src/BLEUuid.h | 36 - libraries/CurieBLE/src/CurieBLE.h | 2 + libraries/CurieBLE/src/internal/ble_client.c | 832 +-------- libraries/CurieBLE/src/internal/ble_client.h | 63 +- platform.txt | 4 +- system/libarc32_arduino101/Makefile | 21 +- system/libarc32_arduino101/common/atomic.h | 156 ++ .../common/misc/byteorder.h | 44 + system/libarc32_arduino101/common/misc/util.h | 5 + .../drivers/atomic_native.c | 370 ++++ .../drivers/bluetooth/att.h | 55 + .../drivers/bluetooth/bluetooth.h | 353 ++++ .../drivers/bluetooth/conn.h | 399 +++++ .../drivers/bluetooth/conn_internal.h | 125 ++ .../drivers/bluetooth/gatt.h | 1045 +++++++++++ .../drivers/bluetooth/hci.h | 683 ++++++++ .../drivers/bluetooth/uuid.h | 463 +++++ .../drivers/ipc_uart_ns16550.c | 423 +++-- .../drivers/ipc_uart_ns16550.h | 70 +- system/libarc32_arduino101/drivers/rpc/rpc.h | 152 ++ .../drivers/rpc/rpc_deserialize.c | 457 +++++ .../drivers/rpc/rpc_functions_to_ble_core.h | 106 ++ .../drivers/rpc/rpc_functions_to_quark.h | 124 ++ .../drivers/rpc/rpc_serialize.c | 323 ++++ .../framework/include/cfw/cfw.h | 2 +- .../framework/include/cfw/cfw_client.h | 16 - .../framework/include/cfw_platform.h | 1 - .../framework/include/infra/ipc_uart.h | 19 - .../framework/include/infra/log.h | 10 +- .../framework/include/log_modules | 1 + .../framework/include/panic_api.h | 2 +- .../include/services/ble/ble_service.h | 210 ++- .../services/ble/ble_service_gap_api.h | 1036 ----------- .../include/services/ble/ble_service_gatt.h | 325 ---- .../services/ble/ble_service_gattc_api.h | 372 ---- .../services/ble/ble_service_gatts_api.h | 559 ------ .../framework/include/services/services_ids.h | 1 + .../framework/include/util/misc.h | 110 ++ .../framework/src/cfw/service_api.c | 48 - .../framework/src/cfw_platform.c | 21 +- .../framework/src/infra/log.c | 8 +- .../framework/src/infra/log_impl_printk.c | 5 +- .../framework/src/nordic_interface.c | 192 -- .../libarc32_arduino101/framework/src/os/os.c | 3 + .../framework/src/os/panic.c | 16 + .../framework/src/services/ble/ble_protocol.h | 233 --- .../src/services/ble/ble_service_core_int.h | 207 --- .../src/services/ble/ble_service_gap_api.c | 384 ---- .../src/services/ble/ble_service_gatt_int.h | 102 -- .../src/services/ble/ble_service_gatts_api.c | 287 --- .../framework/src/services/ble/conn.c | 927 ++++++++++ .../src/services/ble/conn_internal.h | 125 ++ .../framework/src/services/ble/dtm_tcmd.c | 12 + .../framework/src/services/ble/gap.c | 912 ++++++++++ .../framework/src/services/ble/gatt.c | 1561 +++++++++++++++++ .../framework/src/services/ble/hci_core.h | 70 + .../framework/src/services/ble/l2cap.c | 62 + .../src/services/ble/l2cap_internal.h | 27 + .../framework/src/services/ble/smp.h | 100 ++ .../framework/src/services/ble/smp_null.c | 118 ++ .../framework/src/services/ble/uuid.c | 135 ++ .../services/ble_service/ble_protocol.h} | 35 +- .../src/services/ble_service/ble_service.c | 297 ++++ .../services/ble_service/ble_service_api.c | 66 + .../services/ble_service/ble_service_int.h | 67 + .../ble_service/ble_service_internal.h | 79 + .../{ble => ble_service}/ble_service_utils.c | 93 +- .../{ble => ble_service}/ble_service_utils.h | 72 +- .../src/services/ble_service/gap_internal.h | 629 +++++++ .../src/services/ble_service/gatt_internal.h | 308 ++++ .../src/services/ble_service/nble_driver.c | 354 ++++ .../src/services/ble_service/nble_driver.h | 55 + variants/arduino_101/variant.cpp | 10 + 111 files changed, 15785 insertions(+), 5716 deletions(-) rename system/libarc32_arduino101/framework/src/nordic_interface.h => cores/arduino/printk.cpp (79%) create mode 100644 libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino create mode 100644 libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino create mode 100644 libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino create mode 100644 libraries/CurieBLE/examples/Scanning/Scanning.ino create mode 100644 libraries/CurieBLE/src/BLECentralHelper.cpp create mode 100644 libraries/CurieBLE/src/BLECentralHelper.h create mode 100644 libraries/CurieBLE/src/BLECentralRole.cpp create mode 100644 libraries/CurieBLE/src/BLECentralRole.h create mode 100644 libraries/CurieBLE/src/BLEHelper.cpp create mode 100644 libraries/CurieBLE/src/BLEHelper.h create mode 100644 libraries/CurieBLE/src/BLEPeripheralHelper.cpp create mode 100644 libraries/CurieBLE/src/BLEPeripheralHelper.h create mode 100644 libraries/CurieBLE/src/BLEPeripheralRole.cpp create mode 100644 libraries/CurieBLE/src/BLEPeripheralRole.h create mode 100644 libraries/CurieBLE/src/BLEProfile.cpp create mode 100644 libraries/CurieBLE/src/BLEProfile.h create mode 100644 libraries/CurieBLE/src/BLERoleBase.cpp create mode 100644 libraries/CurieBLE/src/BLERoleBase.h delete mode 100644 libraries/CurieBLE/src/BLEUuid.cpp delete mode 100644 libraries/CurieBLE/src/BLEUuid.h create mode 100644 system/libarc32_arduino101/common/atomic.h create mode 100644 system/libarc32_arduino101/common/misc/byteorder.h create mode 100644 system/libarc32_arduino101/drivers/atomic_native.c create mode 100644 system/libarc32_arduino101/drivers/bluetooth/att.h create mode 100644 system/libarc32_arduino101/drivers/bluetooth/bluetooth.h create mode 100644 system/libarc32_arduino101/drivers/bluetooth/conn.h create mode 100644 system/libarc32_arduino101/drivers/bluetooth/conn_internal.h create mode 100644 system/libarc32_arduino101/drivers/bluetooth/gatt.h create mode 100644 system/libarc32_arduino101/drivers/bluetooth/hci.h create mode 100644 system/libarc32_arduino101/drivers/bluetooth/uuid.h create mode 100644 system/libarc32_arduino101/drivers/rpc/rpc.h create mode 100644 system/libarc32_arduino101/drivers/rpc/rpc_deserialize.c create mode 100644 system/libarc32_arduino101/drivers/rpc/rpc_functions_to_ble_core.h create mode 100644 system/libarc32_arduino101/drivers/rpc/rpc_functions_to_quark.h create mode 100644 system/libarc32_arduino101/drivers/rpc/rpc_serialize.c delete mode 100644 system/libarc32_arduino101/framework/include/services/ble/ble_service_gap_api.h delete mode 100644 system/libarc32_arduino101/framework/include/services/ble/ble_service_gatt.h delete mode 100644 system/libarc32_arduino101/framework/include/services/ble/ble_service_gattc_api.h delete mode 100644 system/libarc32_arduino101/framework/include/services/ble/ble_service_gatts_api.h create mode 100644 system/libarc32_arduino101/framework/include/util/misc.h delete mode 100644 system/libarc32_arduino101/framework/src/nordic_interface.c create mode 100644 system/libarc32_arduino101/framework/src/os/panic.c delete mode 100644 system/libarc32_arduino101/framework/src/services/ble/ble_protocol.h delete mode 100644 system/libarc32_arduino101/framework/src/services/ble/ble_service_core_int.h delete mode 100644 system/libarc32_arduino101/framework/src/services/ble/ble_service_gap_api.c delete mode 100644 system/libarc32_arduino101/framework/src/services/ble/ble_service_gatt_int.h delete mode 100644 system/libarc32_arduino101/framework/src/services/ble/ble_service_gatts_api.c create mode 100644 system/libarc32_arduino101/framework/src/services/ble/conn.c create mode 100644 system/libarc32_arduino101/framework/src/services/ble/conn_internal.h create mode 100644 system/libarc32_arduino101/framework/src/services/ble/dtm_tcmd.c create mode 100644 system/libarc32_arduino101/framework/src/services/ble/gap.c create mode 100644 system/libarc32_arduino101/framework/src/services/ble/gatt.c create mode 100644 system/libarc32_arduino101/framework/src/services/ble/hci_core.h create mode 100644 system/libarc32_arduino101/framework/src/services/ble/l2cap.c create mode 100644 system/libarc32_arduino101/framework/src/services/ble/l2cap_internal.h create mode 100644 system/libarc32_arduino101/framework/src/services/ble/smp.h create mode 100644 system/libarc32_arduino101/framework/src/services/ble/smp_null.c create mode 100644 system/libarc32_arduino101/framework/src/services/ble/uuid.c rename system/libarc32_arduino101/framework/{include/services/ble/ble_service_msg.h => src/services/ble_service/ble_protocol.h} (70%) create mode 100644 system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c create mode 100644 system/libarc32_arduino101/framework/src/services/ble_service/ble_service_api.c create mode 100644 system/libarc32_arduino101/framework/src/services/ble_service/ble_service_int.h create mode 100644 system/libarc32_arduino101/framework/src/services/ble_service/ble_service_internal.h rename system/libarc32_arduino101/framework/src/services/{ble => ble_service}/ble_service_utils.c (50%) rename system/libarc32_arduino101/framework/src/services/{ble => ble_service}/ble_service_utils.h (66%) create mode 100644 system/libarc32_arduino101/framework/src/services/ble_service/gap_internal.h create mode 100644 system/libarc32_arduino101/framework/src/services/ble_service/gatt_internal.h create mode 100644 system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.c create mode 100644 system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.h diff --git a/README.md b/README.md index a710c0f3..b437314b 100644 --- a/README.md +++ b/README.md @@ -53,3 +53,21 @@ them to the [support forum](https://forum.arduino.cc/index.php?board=103). > "How do I use this library?" > "I can't get this example sketch to work. What am I doing wrong?" + +# Enable debug interface on Serail1 + +* Default disable the debug interface. + +If you want to enable debug trace on Serial1 to debug corelib, follow these instructions. + +1. Shut down the IDE +2. Go to Arduino15 directory + * Windows: `C:\Users\\AppData\Roaming\Arduino15` + * OS X: `~/Library/Arduino15` + * Linux: `~/.arduino15` +3. Modify the platform.txt + * Find `compiler.c.flags` and add `-DCONFIGURE_DEBUG_CORELIB_ENABLED` at the end of this line + * Find `compiler.cpp.flags` and add `-DCONFIGURE_DEBUG_CORELIB_ENABLED` at the end of this line +4. Initial Serial1 in your sketch + * Add `Serial1.begin(115200);` in your `setup()` +5. Adjust the output level at log_init function in log.c diff --git a/system/libarc32_arduino101/framework/src/nordic_interface.h b/cores/arduino/printk.cpp similarity index 79% rename from system/libarc32_arduino101/framework/src/nordic_interface.h rename to cores/arduino/printk.cpp index 1b187b16..babfc0ed 100644 --- a/system/libarc32_arduino101/framework/src/nordic_interface.h +++ b/cores/arduino/printk.cpp @@ -28,13 +28,25 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef NORDIC_INTERFACE_H -#define NORDIC_INTERFACE_H -#include "infra/ipc_uart.h" +#include +#include +#include "UARTClass.h" -void uart_ipc_message_cback(uint8_t cpu_id, int channel, int len, void * p_data); -int send_message_ipc_uart(struct message * message); -void free_message_ipc_uart(struct message * message); -int nordic_interface_init(T_QUEUE queue); +extern "C" void printk(const char *fmt, va_list args); +extern UARTClass Serial1; +#define PRINTK_BUFSIZ 256 + +void printk(const char *fmt, va_list args) +{ +#ifdef CONFIGURE_DEBUG_CORELIB_ENABLED + int len = 0; + + char tmp[PRINTK_BUFSIZ]; + + len = vsnprintf(tmp, PRINTK_BUFSIZ, fmt, args); + + tmp[len] = '\0'; + Serial1.println(tmp); +#endif +} -#endif // NORDIC_INTERFACE_H diff --git a/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino b/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino index 688b147f..7386087f 100644 --- a/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino +++ b/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino @@ -45,7 +45,7 @@ void setup() { void loop() { // listen for BLE peripherals to connect: - BLECentral central = blePeripheral.central(); + BLECentralHelper central = blePeripheral.central(); // if a central is connected to peripheral: if (central) { diff --git a/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino b/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino index f8788731..26946cbf 100644 --- a/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino +++ b/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino @@ -45,19 +45,19 @@ void loop() { blePeripheral.poll(); } -void blePeripheralConnectHandler(BLECentral& central) { +void blePeripheralConnectHandler(BLEHelper& central) { // central connected event handler Serial.print("Connected event, central: "); Serial.println(central.address()); } -void blePeripheralDisconnectHandler(BLECentral& central) { +void blePeripheralDisconnectHandler(BLEHelper& central) { // central disconnected event handler Serial.print("Disconnected event, central: "); Serial.println(central.address()); } -void switchCharacteristicWritten(BLECentral& central, BLECharacteristic& characteristic) { +void switchCharacteristicWritten(BLEHelper& central, BLECharacteristic& characteristic) { // central wrote new value to characteristic, update LED Serial.print("Characteristic event, written: "); diff --git a/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino b/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino new file mode 100644 index 00000000..0523ed55 --- /dev/null +++ b/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino @@ -0,0 +1,143 @@ +/* + 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 + +/* + This sketch example partially implements the standard Bluetooth Low-Energy Battery service. + For more information: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx +*/ + +#define MAX_IMU_RECORD 1 + +struct bt_le_conn_param conn_param = {0x18, 0x28, 0, 400}; +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 + +void ble_connected(BLEHelper &role) +{ + BLEPeripheralHelper&peripheral = *(BLEPeripheralHelper*)(&role); + Serial.println("Connected"); + + // Start discovery the profiles in peripheral device + peripheral.discover(); +} + +void bleImuCharacteristicWritten(BLEHelper& central, BLECharacteristic& characteristic) +{ + // Peripheral wrote new value to characteristic by Notification/Indication + 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 *data, + uint8_t data_len, + void *user_data) +{ + bt_addr_le_t *addr = (bt_addr_le_t *)user_data; + 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 % MAX_UUID_SIZE != 0) + { + Serial.println("AD malformed"); + return true; + } + struct bt_uuid * serviceuuid = bleImuService.uuid(); + for (i = 0; i < data_len; i += MAX_UUID_SIZE) + { + if (memcmp (((struct bt_uuid_128*)serviceuuid)->val, &data[i], MAX_UUID_SIZE) != 0) + { + continue; + } + + // Accept the advertisement + if (!bleCentral.stopScan()) + { + Serial.println("Stop LE scan failed"); + continue; + } + Serial.println("Connecting"); + // Connect to peripheral + bleCentral.connect(addr, &conn_param); + return false; + } + } + } + + return true; +} + +void setup() { + Serial.begin(115200); // initialize serial communication + pinMode(13, OUTPUT); // initialize the LED on pin 13 to indicate when a central is connected + + bleImuChar.setEventHandler(BLEWritten, bleImuCharacteristicWritten); + + /* 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 */ + bleCentral.addAttribute(bleImuService); // Add the BLE IMU service + bleCentral.addAttribute(bleImuChar); // Add the BLE IMU characteristic + + /* Setup callback */ + 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() +{ + delay(2000); +} + diff --git a/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino b/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino new file mode 100644 index 00000000..4a179e2a --- /dev/null +++ b/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino @@ -0,0 +1,120 @@ +/* + 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 + +/* + This sketch example partially implements the standard Bluetooth Low-Energy Battery service. + For more information: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx +*/ + +#define MAX_IMU_RECORD 1 + +typedef struct { + int index; + unsigned int slot[3]; +} imuFrameType; + +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 + // 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 + + + /* 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 BLE Battery service + blePeripheral.addAttribute(bleImuChar); // 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(); + + CurieIMU.begin(); +} + +void recordImuData(int index) { + /* Read IMU data. + */ + int ax, ay, az; + int gx, gy, gz; + + imuBuf[index].index = seqNum++; + CurieIMU.readMotionSensor(ax, ay, az, gx, gy, gz); + + 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)); + +} + + +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()); + + Serial.print("IMU buffer size: "); + Serial.println(sizeof(imuBuf)); + + // turn on the LED to indicate the connection: + digitalWrite(13, HIGH); + + long currentMillis, sentTime; + + // Send IMU data as long as the central is still connected + currentMillis = sentTime = millis(); + while (central.connected()) + { + // Take IMU data every 100 msec + if ((millis() - sentTime) >= 100) + { + recordImuData(0); + sentTime = millis(); + bleImuChar.setValue((unsigned char *)&(imuBuf[0]), sizeof(imuBuf)); + } + } // while + + // when the central disconnects, turn off the LED: + digitalWrite(13, LOW); + Serial.print("Disconnected from central: "); + Serial.println(central.address()); + } +} + diff --git a/libraries/CurieBLE/examples/LED/LED.ino b/libraries/CurieBLE/examples/LED/LED.ino index a55501dc..263c7d5b 100644 --- a/libraries/CurieBLE/examples/LED/LED.ino +++ b/libraries/CurieBLE/examples/LED/LED.ino @@ -38,7 +38,7 @@ void setup() { void loop() { // listen for BLE peripherals to connect: - BLECentral central = blePeripheral.central(); + BLECentralHelper central = blePeripheral.central(); // if a central is connected to peripheral: if (central) { diff --git a/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino b/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino new file mode 100644 index 00000000..04cd0971 --- /dev/null +++ b/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino @@ -0,0 +1,157 @@ +/* + 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 +*/ + +// This example can work with CallbackLED to show the profile read/write operation in central +#include + +struct bt_le_conn_param conn_param = {0x18, 0x28, 0, 400}; + +const int ledPin = 13; // set ledPin to use on-board LED +BLECentral bleCentral; // create central instance +BLEPeripheralHelper *blePeripheral1 = NULL; + +BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // create service +BLECharCharacteristic switchChar("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);// create switch characteristic and allow remote device to read and write + +void bleCentralConnectHandler(BLEHelper& peripheral) +{ + // peripheral connected event handler + blePeripheral1 = (BLEPeripheralHelper *)(&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 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 *data, + uint8_t data_len, + void *user_data) +{ + bt_addr_le_t *addr = (bt_addr_le_t *)user_data; + 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 % MAX_UUID_SIZE != 0) + { + Serial.println("AD malformed"); + return true; + } + struct bt_uuid * serviceuuid = ledService.uuid(); + for (i = 0; i < data_len; i += MAX_UUID_SIZE) + { + if (memcmp (((struct bt_uuid_128*)serviceuuid)->val, &data[i], MAX_UUID_SIZE) != 0) + { + continue; + } + + // Accept the advertisement + if (!bleCentral.stopScan()) + { + Serial.println("Stop LE scan failed"); + continue; + } + Serial.println("Connecting"); + // Connect to peripheral + bleCentral.connect(addr, &conn_param); + return false; + } + } + } + + return true; +} + +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, (unsigned char *)(&ledstate), sizeof (ledstate)); + } + } +} diff --git a/libraries/CurieBLE/examples/MIDIBLE/MIDIBLE.ino b/libraries/CurieBLE/examples/MIDIBLE/MIDIBLE.ino index 2dbe9833..4892f017 100644 --- a/libraries/CurieBLE/examples/MIDIBLE/MIDIBLE.ino +++ b/libraries/CurieBLE/examples/MIDIBLE/MIDIBLE.ino @@ -140,19 +140,19 @@ void loop() { } -void midiDeviceConnectHandler(BLECentral& central) { +void midiDeviceConnectHandler(BLEHelper& central) { // central connected event handler Serial.print("Connected event, central: "); Serial.println(central.address()); } -void midiDeviceDisconnectHandler(BLECentral& central) { +void midiDeviceDisconnectHandler(BLEHelper& central) { // central disconnected event handler Serial.print("Disconnected event, central: "); Serial.println(central.address()); } -void midiCharacteristicWritten(BLECentral& central, BLECharacteristic& characteristic) { +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 new file mode 100644 index 00000000..d2d7d0e2 --- /dev/null +++ b/libraries/CurieBLE/examples/Scanning/Scanning.ino @@ -0,0 +1,129 @@ +/* + 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 + +#define BLE_SCANING_DEVICE_MAX_CNT 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[BLE_SCANING_DEVICE_MAX_CNT]; +uint8_t list_index = 0; + +BLECentral bleCentral; // BLE Central Device (the board you're programming) + +bool adv_list_add(ble_device_info_t &device) +{ + if (list_index >= BLE_SCANING_DEVICE_MAX_CNT) + { + 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 *data, + uint8_t data_len, + void *user_data) +{ + bt_addr_le_t *addr = (bt_addr_le_t *)user_data; + ble_device_info_t device; + bt_addr_le_to_str (addr, device.macaddr, sizeof (device.macaddr)); + memcpy(device.loacalname, " -NA-", sizeof(" -NA-")); + + switch (type) { + case BT_DATA_NAME_SHORTENED: + case BT_DATA_NAME_COMPLETE: + memcpy(device.loacalname, data, data_len); + device.loacalname[data_len] = '\0'; + adv_list_update(device); + break; + } + adv_list_add(device); + return true; +} + +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(); +} + diff --git a/libraries/CurieBLE/src/BLEAttribute.cpp b/libraries/CurieBLE/src/BLEAttribute.cpp index d6f46591..478c2e00 100644 --- a/libraries/CurieBLE/src/BLEAttribute.cpp +++ b/libraries/CurieBLE/src/BLEAttribute.cpp @@ -19,23 +19,60 @@ #include "BLEAttribute.h" -#include "BLEUuid.h" - unsigned char BLEAttribute::_numAttributes = 0; BLEAttribute::BLEAttribute(const char* uuid, enum BLEAttributeType type) : - _uuid(uuid), + _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; + ((struct bt_uuid_16*)(&_uuid.uuid))->val = temp; + } + else + { + _uuid.uuid.type = BT_UUID_TYPE_128; + } } const char* BLEAttribute::uuid() const { - return _uuid; + return _uuid_cstr; +} + +struct bt_uuid *BLEAttribute::uuid(void) +{ + return (struct bt_uuid *)&_uuid; } + enum BLEAttributeType BLEAttribute::type() const { return this->_type; @@ -52,14 +89,14 @@ BLEAttribute::setHandle(uint16_t handle) { } -bt_uuid -BLEAttribute::btUuid() const { - BLEUuid bleUuid = BLEUuid(uuid()); - - return bleUuid.uuid(); -} - unsigned char BLEAttribute::numAttributes() { return _numAttributes; } + +bool BLEAttribute::discovering() +{ + return _discoverying; +} + + diff --git a/libraries/CurieBLE/src/BLEAttribute.h b/libraries/CurieBLE/src/BLEAttribute.h index 8aaca067..71a19fca 100644 --- a/libraries/CurieBLE/src/BLEAttribute.h +++ b/libraries/CurieBLE/src/BLEAttribute.h @@ -28,7 +28,10 @@ enum BLEAttributeType { BLETypeDescriptor = 0x2900 }; +// Class declare +class BLEProfile; class BLEPeripheral; +class BLEPeripheralHelper; class BLEAttribute { public: @@ -39,9 +42,20 @@ class BLEAttribute { * @return const char* string representation of the Attribute */ const char* uuid(void) const; + struct bt_uuid *uuid(void); protected: - friend BLEPeripheral; + //friend BLEPeripheral; + friend BLEProfile; + + friend ssize_t profile_write_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, + uint16_t offset); + friend ssize_t profile_read_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, uint16_t len, + uint16_t offset); BLEAttribute(const char* uuid, enum BLEAttributeType type); @@ -51,13 +65,32 @@ class BLEAttribute { void setHandle(uint16_t handle); static unsigned char numAttributes(void); - + // The below APIs are for central device to discover the + virtual void discover(struct bt_gatt_discover_params *params) = 0; + virtual void discover(const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *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; + const char* _uuid_cstr; + struct bt_uuid_128 _uuid; + enum BLEAttributeType _type; uint16_t _handle; + }; #endif // _BLE_ATTRIBUTE_H_INCLUDED diff --git a/libraries/CurieBLE/src/BLECentral.cpp b/libraries/CurieBLE/src/BLECentral.cpp index 73d0326d..6b4afcf5 100644 --- a/libraries/CurieBLE/src/BLECentral.cpp +++ b/libraries/CurieBLE/src/BLECentral.cpp @@ -17,87 +17,84 @@ * */ -#include "BLECentral.h" - -#include "BLEPeripheral.h" +#include "BLECentralRole.h" +#include "BLECentral.h" -BLECentral::BLECentral(BLEPeripheral* peripheral) : - _peripheral(peripheral) +bool BLECentral::startScan() { - clearAddress(); + return BLECentralRole::instance()->startScan(); } -BLECentral::operator bool() const { - ble_addr_t zero; - - memset(&zero, 0, sizeof(zero)); - - return (memcmp(&_address, &zero, sizeof(_address)) != 0); +bool BLECentral::startScan(const struct bt_le_scan_param &scan_param) +{ + return BLECentralRole::instance()->startScan(scan_param); } -bool -BLECentral::operator==(const BLECentral& rhs) const { - return (memcmp(&_address, &rhs._address, sizeof(_address)) == 0); +bool BLECentral::stopScan() +{ + return BLECentralRole::instance()->stopScan(); } -bool -BLECentral::operator!=(const BLECentral& rhs) const { - return !(*this == rhs); +bool BLECentral::connect(const bt_addr_le_t *addr, const struct bt_le_conn_param *param) +{ + return BLECentralRole::instance()->connect(addr, param); } -bool -BLECentral::connected() { - poll(); - - return (*this && *this == _peripheral->central()); +void BLECentral::discover(BLEPeripheralHelper &peripheral) +{ + peripheral.discover(); } -const char* -BLECentral::address() const { - static char address[18]; - - String addressStr = ""; - - for (int i = 5; i >= 0; i--) { - unsigned char a = _address.addr[i]; - - if (a < 0x10) { - addressStr += "0"; - } - - addressStr += String(a, 16); - - if (i > 0) { - addressStr += ":"; - } - } +void BLECentral::setEventHandler(BLERoleEvent event, BLERoleEventHandler callback) +{ + BLECentralRole::instance()->setEventHandler(event, callback); +} - strcpy(address, addressStr.c_str()); +void BLECentral::setAdvertiseHandler(ble_advertise_handle_cb_t advcb) +{ + BLECentralRole::instance()->setAdvertiseHandler(advcb); +} - return address; +void BLECentral::setScanParam(const struct bt_le_scan_param &scan_param) +{ + BLECentralRole::instance()->setScanParam(scan_param); } -void -BLECentral::poll() { - _peripheral->poll(); +void BLECentral::addAttribute(BLEAttribute& attribute) +{ + BLECentralRole::instance()->addAttribute(attribute); } -bool -BLECentral::disconnect() { - if (connected()) { - return _peripheral->disconnect(); +bool BLECentral::begin(void) +{ + bool retval = BLECentralRole::instance()->begin(); + if (!retval) + { + pr_error(LOG_MODULE_BLE,"%s: Intit failed", __FUNCTION__); + return false; } - - return false; + + // Start scan + const struct bt_le_scan_param *scan_param = BLECentralRole::instance()->getScanParam(); + struct bt_le_scan_param 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; } -void -BLECentral::setAddress(ble_addr_t address) { - _address = address; -} -void -BLECentral::clearAddress() { - memset(&_address, 0x00, sizeof(_address)); -} + diff --git a/libraries/CurieBLE/src/BLECentral.h b/libraries/CurieBLE/src/BLECentral.h index c51250d9..8327f5d6 100644 --- a/libraries/CurieBLE/src/BLECentral.h +++ b/libraries/CurieBLE/src/BLECentral.h @@ -21,50 +21,119 @@ #define _BLE_CENTRAL_H_INCLUDED #include "BLECommon.h" +#include "BLERoleBase.h" -class BLEPeripheral; +class BLEAttribute; -class BLECentral { - friend class BLEPeripheral; - - public: - /** - * Is the Central connected - * - * @return boolean_t true if the central is connected, otherwise false - */ - bool connected(void); - - /** - * Get the address of the Central in string form - * - * @return const char* address of the Central in string form - */ - const char* address(void) const; - - /** - * Disconnect the central if it is connected - * - */ - bool disconnect(void); - - /** - * Poll the central for events - */ - void poll(void); - - operator bool(void) const; - bool operator==(const BLECentral& rhs) const; - bool operator!=(const BLECentral& rhs) const; - - protected: - BLECentral(BLEPeripheral* peripheral); - void setAddress(ble_addr_t address); - void clearAddress(); - - private: - BLEPeripheral* _peripheral; - ble_addr_t _address; +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 none + * + * @return bool Indicate the success or error + * + * @note none + */ + bool startScan(const struct bt_le_scan_param &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 addr The MAC address of peripheral device that want to establish connection + * + * @param param The connetion parameters + * + * @return bool Indicate the success or error + * + * @note none + */ + bool connect(const bt_addr_le_t *addr, const struct bt_le_conn_param *param); + + /** + * @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 Set the scan parameter + * + * @param scan_param The scan parameter want to be set + * + * @return none + * + * @note none + */ + void setScanParam(const struct bt_le_scan_param &scan_param); + + /** + * @brief Add an attribute to the BLE Central Device + * + * @param attribute Attribute to add to Central + * + * @return none + * + * @note The attribute will used for discover the peripheral handler + */ + void addAttribute(BLEAttribute& attribute); + + /** + * Provide a function to be called when events related to this Device are raised + * + * @param event Event type for callback + * @param 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 advcb Pointer to callback function to invoke when advertisement received + * + * @return none + * + * @note none + */ + void setAdvertiseHandler(ble_advertise_handle_cb_t advcb); + + /** + * Setup attributes and start scan + * + * @return bool indicating success or error + */ + bool begin(void); +protected: +private: + }; #endif diff --git a/libraries/CurieBLE/src/BLECentralHelper.cpp b/libraries/CurieBLE/src/BLECentralHelper.cpp new file mode 100644 index 00000000..99e92de9 --- /dev/null +++ b/libraries/CurieBLE/src/BLECentralHelper.cpp @@ -0,0 +1,103 @@ +/* + * 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 "BLECentralHelper.h" + +#include "BLEPeripheralRole.h" + + +BLECentralHelper::BLECentralHelper(BLEPeripheralRole* peripheral) : + _peripheral(peripheral) +{ + clearAddress(); +} + +BLECentralHelper::operator bool() const { + bt_addr_le_t zero; + + memset(&zero, 0, sizeof(zero)); + + return (memcmp(&_address, &zero, sizeof(_address)) != 0); +} + +bool +BLECentralHelper::operator==(const BLECentralHelper& rhs) const { + return (memcmp(&_address, &rhs._address, sizeof(_address)) == 0); +} + +bool +BLECentralHelper::operator!=(const BLECentralHelper& rhs) const { + return !(*this == rhs); +} + +bool +BLECentralHelper::connected() { + poll(); + + return (*this && *this == _peripheral->central()); +} + +const char* +BLECentralHelper::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; +} + +void +BLECentralHelper::poll() { + _peripheral->poll(); +} + +bool +BLECentralHelper::disconnect() { + if (connected()) { + return _peripheral->disconnect(); + } + + return false; +} + +void +BLECentralHelper::setAddress(bt_addr_le_t address) { + _address = address; +} + +void +BLECentralHelper::clearAddress() { + memset(&_address, 0x00, sizeof(_address)); +} diff --git a/libraries/CurieBLE/src/BLECentralHelper.h b/libraries/CurieBLE/src/BLECentralHelper.h new file mode 100644 index 00000000..465d9cab --- /dev/null +++ b/libraries/CurieBLE/src/BLECentralHelper.h @@ -0,0 +1,72 @@ +/* + * 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); + + /** + * Get the address of the Central in string form + * + * @return const char* address of the Central in string form + */ + const char* address(void) const; + + /** + * Disconnect the central if it is connected + * + */ + bool disconnect(void); + + /** + * Poll the central for events + */ + void poll(void); + + operator bool(void) const; + bool operator==(const BLECentralHelper& rhs) const; + bool operator!=(const BLECentralHelper& rhs) const; + + protected: + BLECentralHelper(BLEPeripheralRole* peripheral); + void setAddress(bt_addr_le_t address); + void clearAddress(); + + private: + BLEPeripheralRole* _peripheral; + bt_addr_le_t _address; +}; + +#endif diff --git a/libraries/CurieBLE/src/BLECentralRole.cpp b/libraries/CurieBLE/src/BLECentralRole.cpp new file mode 100644 index 00000000..1595e24d --- /dev/null +++ b/libraries/CurieBLE/src/BLECentralRole.cpp @@ -0,0 +1,292 @@ +/* + * 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 struct bt_le_conn_param *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 + struct bt_conn* 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 struct bt_le_scan_param &scan_param) +{ + setScanParam(scan_param); + return startScan(); +} + +void BLECentralRole::setScanParam(const struct bt_le_scan_param &scan_param) +{ + memcpy(&_scan_param, &scan_param, sizeof (_scan_param)); +} + +const struct bt_le_scan_param* 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(struct bt_conn *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, (void *)addr)) + { + return; + } + + data_len -= len + 1; + data += len + 1; + } + pr_debug(LOG_MODULE_BLE, "%s: done", __FUNCTION__); + } +} + +void BLECentralRole::handleConnectEvent(struct bt_conn *conn, uint8_t err) +{ + if (_event_handlers[BLEConnected]) + { + BLEPeripheralHelper *temp = peripheral(conn); + _event_handlers[BLEConnected](*temp); + } +} + +void BLECentralRole::handleDisconnectEvent(struct bt_conn *conn, uint8_t reason) +{ + if (_event_handlers[BLEDisconnected]) + { + BLEPeripheralHelper *temp = peripheral(conn); + _event_handlers[BLEDisconnected](*temp); + temp->linkLost(); + } +} + +void BLECentralRole::handleParamUpdated(struct bt_conn *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout) +{ + if (_event_handlers[BLEUpdateParam]) + { + // Fix me Add parameter proc + BLEPeripheralHelper *temp = peripheral(conn); + _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; +} + +void BLECentralRole::addAttribute(BLEAttribute& attribute) +{ + for (int i = 0; i < BLE_MAX_CONN_CFG; i++) + { + _peripherial[i]->addAttribute(attribute); + } +} + +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 new file mode 100644 index 00000000..d3757fae --- /dev/null +++ b/libraries/CurieBLE/src/BLECentralRole.h @@ -0,0 +1,276 @@ +/* + * 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 struct bt_le_scan_param &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 addr The MAC address of peripheral device that want to establish connection + * + * @param param The connetion parameters + * + * @return bool Indicate the success or error + * + * @note none + */ + bool connect(const bt_addr_le_t *addr, const struct bt_le_conn_param *param); + + /** + * @brief Set the scan parameter + * + * @param scan_param The scan parameter want to be set + * + * @return none + * + * @note none + */ + void setScanParam(const struct bt_le_scan_param &scan_param); + + /** + * @brief Get the scan parameter + * + * @param none + * + * @return const struct bt_le_scan_param* The scan parameter that current used + * + * @note none + */ + const struct bt_le_scan_param* 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 attribute Attribute to add to Central + * + * @return none + * + * @note The attribute will used for discover the peripheral handler + */ + void addAttribute(BLEAttribute& attribute); + + /** + * Provide a function to be called when events related to this Device are raised + * + * @param event Event type for callback + * @param 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 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 conn The connection object + * + * @return BLEPeripheralHelper* The BLE peripheral helper + * + * @note none + */ + BLEPeripheralHelper* peripheral(struct bt_conn *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 conn The object that established the connection + * + * @param err The code of the process + * + * @return none + * + * @note none + */ + void handleConnectEvent(struct bt_conn *conn, uint8_t err); + + /** + * @brief Handle the disconnected event + * + * @param conn The object that lost the connection + * + * @param reason The link lost reason + * + * @return none + * + * @note none + */ + void handleDisconnectEvent(struct bt_conn *conn, uint8_t reason); + + /** + * @brief Handle the conntion update request + * + * @param conn The connection object that need to process the update request + * + * @param interval The connection interval + * + * @param latency The connection latency + * + * @param timeout The connection timeout + * + * @return none + * + * @note none + */ + void handleParamUpdated(struct bt_conn *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout); + /** + * @brief Handle the advertisement + * + * @param addr The device's MAC address that send out ADV + * + * @param rssi The antenna's RSSI + * + * @param type The advertise type + * + * @param ad The advertisement RAW data + * + * @param 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; + struct bt_le_scan_param _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 a6c46c07..5c810a5f 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.cpp +++ b/libraries/CurieBLE/src/BLECharacteristic.cpp @@ -18,28 +18,75 @@ */ #include "BLECharacteristic.h" +#include "BLEPeripheralHelper.h" #include "internal/ble_client.h" -#define BLE_CCCD_NOTIFY_EN_MASK 0x1 -#define BLE_CCCD_INDICATE_EN_MASK 0x2 +uint8_t profile_notify_process (struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, uint16_t length); +uint8_t profile_read_rsp_process(struct bt_conn *conn, int err, + struct bt_gatt_read_params *params, + const void *data, + uint16_t length); + +unsigned char BLECharacteristic::_numNotifyAttributes = 0; + +struct bt_uuid_16 BLECharacteristic::_gatt_chrc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CHRC_VAL}; +struct bt_uuid_16 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), - _properties(properties), _value_length(0), _written(false), - _cccd_value(0), - _value_handle(0), - _cccd_handle(0), _user_description(NULL), - _presentation_format(NULL) + _presentation_format(NULL), + _attr_chrc_declaration(NULL), + _attr_chrc_value(NULL), + _attr_cccd(NULL) { _value_size = maxLength > BLE_MAX_ATTR_DATA_LEN ? BLE_MAX_ATTR_DATA_LEN : maxLength; _value = (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)); + + _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 = this->uuid(); memset(_event_handlers, 0, sizeof(_event_handlers)); + + _numNotifyAttributes++; + if (properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE)) + { + _numNotifyAttributes++; + } + _sub_params.notify = profile_notify_process; } BLECharacteristic::BLECharacteristic(const char* uuid, @@ -61,44 +108,40 @@ BLECharacteristic::~BLECharacteristic() unsigned char BLECharacteristic::properties() const { - return _properties; + return _gatt_chrc.properties; } bool BLECharacteristic::setValue(const unsigned char value[], uint16_t length) { - BleStatus status; + int status; _setValue(value, length); - if (_value_handle) { - status = ble_client_gatts_set_attribute_value(_value_handle, _value_length, _value, 0); - if (BLE_STATUS_SUCCESS != status) { + if (_attr_chrc_value) + { + // TODO: Notify for peripheral. + // Write request for central. + status = bt_gatt_notify(NULL, _attr_chrc_value, value, length, NULL); + if (0 != status) + { return false; } - - if (subscribed()) { - boolean_t indication = (_cccd_value & BLE_CCCD_INDICATE_EN_MASK); - - status = ble_client_gatts_send_notif_ind(_value_handle, _value_length, _value, 0, indication); - if (BLE_STATUS_SUCCESS != status) { - return false; - } - } } - return true; } void -BLECharacteristic::setValue(BLECentral& central, const unsigned char* value, unsigned short length) +BLECharacteristic::setValue(BLEHelper& blehelper, const unsigned char* value, unsigned short length) { + //BLEHelper *bledevice = ¢ral; _setValue(value, length); _written = true; + _reading = false; if (_event_handlers[BLEWritten]) { - _event_handlers[BLEWritten](central, *this); + _event_handlers[BLEWritten](blehelper, *this); } } @@ -139,7 +182,7 @@ BLECharacteristic::written() bool BLECharacteristic::subscribed() { - return (_cccd_value & (BLE_CCCD_NOTIFY_EN_MASK | BLE_CCCD_INDICATE_EN_MASK)); + return (_gatt_chrc.properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE)); } void @@ -152,114 +195,212 @@ BLECharacteristic::setEventHandler(BLECharacteristicEvent event, BLECharacterist interrupts(); } -bool -BLECharacteristic::add(uint16_t serviceHandle) +uint16_t +BLECharacteristic::valueHandle() { - bt_uuid uuid = btUuid(); + uint16_t handle = 0; + if (NULL != _attr_chrc_value) + { + handle = _attr_chrc_value->handle; + } + + return handle; +} - struct ble_gatts_characteristic char_data; - struct ble_gatts_char_handles handles; - struct ble_gatt_char_user_desc user_desc; - struct ble_gatt_pf_desc pf_desc; +uint16_t +BLECharacteristic::cccdHandle() +{ + uint16_t handle = 0; + if (NULL != _attr_cccd) + { + handle = _attr_cccd->handle; + } + return handle; +} - memset(&char_data, 0, sizeof(char_data)); +void +BLECharacteristic::setUserDescription(BLEDescriptor *descriptor) +{ + _user_description = descriptor; +} - char_data.p_uuid = &uuid; - char_data.props.props = _properties; +void +BLECharacteristic::setPresentationFormat(BLEDescriptor *descriptor) +{ + _presentation_format = descriptor; +} - if (_properties & (BLERead | BLENotify | BLEIndicate)) { - char_data.perms.rd = GAP_SEC_MODE_1 | GAP_SEC_LEVEL_1; - } else { - char_data.perms.rd = GAP_SEC_NO_PERMISSION; +void +BLECharacteristic::_setValue(const uint8_t value[], uint16_t length) +{ + if (length > _value_size) { + length = _value_size; } - if (_properties & (BLEWriteWithoutResponse | BLEWrite)) { - char_data.perms.wr = GAP_SEC_MODE_1 | GAP_SEC_LEVEL_1; - } else { - char_data.perms.wr = GAP_SEC_NO_PERMISSION; - } + memcpy(_value, value, length); + _value_length = length; +} + +unsigned char +BLECharacteristic::numNotifyAttributes(void) { + return _numNotifyAttributes; +} - char_data.init_len = _value_length; - char_data.max_len = _value_size; - char_data.p_value = _value; +struct _bt_gatt_ccc* BLECharacteristic::getCccCfg(void) +{ + return &_ccc_value; +} - if (_user_description) { - user_desc.buffer = (uint8_t*)_user_description->value(); - user_desc.len = _user_description->valueLength(); +struct bt_gatt_chrc* BLECharacteristic::getCharacteristicAttValue(void) +{ + return &_gatt_chrc; +} - char_data.p_user_desc = &user_desc; +uint8_t BLECharacteristic::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; +} - if (_presentation_format) { - const uint8_t* pfValue = _presentation_format->value(); +struct bt_uuid* BLECharacteristic::getCharacteristicAttributeUuid(void) +{ + return (struct bt_uuid*) &_gatt_chrc_uuid; +} +struct bt_uuid* BLECharacteristic::getClientCharacteristicConfigUuid(void) +{ + return (struct bt_uuid*) &_gatt_ccc_uuid; +} - pf_desc.format = pfValue[0]; - pf_desc.exp = pfValue[1]; - pf_desc.unit = (pfValue[3] << 8) | pfValue[2]; - pf_desc.name_spc = pfValue[4]; - pf_desc.descr = (pfValue[6] << 8) | pfValue[5]; - char_data.p_char_pf_desc = &pf_desc; - } - - BleStatus status = ble_client_gatts_add_characteristic(serviceHandle, &char_data, &handles); - if (BLE_STATUS_SUCCESS == status) { - _value_handle = handles.value_handle; - _cccd_handle = handles.cccd_handle; - } +void BLECharacteristic::addCharacteristicDeclaration(struct bt_gatt_attr *gatt_attr) +{ + _attr_chrc_declaration = gatt_attr; +} - return (BLE_STATUS_SUCCESS == status); +void BLECharacteristic::addCharacteristicValue(struct bt_gatt_attr *gatt_attr) +{ + _attr_chrc_value = gatt_attr; } -uint16_t -BLECharacteristic::valueHandle() +void BLECharacteristic::addCharacteristicConfigDescriptor(struct bt_gatt_attr *gatt_attr) { - return _value_handle; + _attr_cccd = gatt_attr; } -uint16_t -BLECharacteristic::cccdHandle() +void BLECharacteristic::discover(struct bt_gatt_discover_params *params) { - return _cccd_handle; + params->type = BT_GATT_DISCOVER_CHARACTERISTIC; + params->uuid = this->uuid(); + // Start discovering + _discoverying = true; + // Re-Init the read/write parameter + _reading = false; } -void -BLECharacteristic::setCccdValue(BLECentral& central, uint16_t value) + +void BLECharacteristic::discover(const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) { - if (_cccd_value != value) { - _cccd_value = value; - - if (subscribed()) { - if (_event_handlers[BLESubscribed]) { - _event_handlers[BLESubscribed](central, *this); - } - } else { - if (_event_handlers[BLEUnsubscribed]) { - _event_handlers[BLEUnsubscribed](central, *this); - } + if (!attr) + { + // Discovery complete + _discoverying = false; + return; + } + + // Chracteristic Char + if (params->uuid == this->uuid()) + { + // Set Discover CCCD parameter + params->start_handle = attr->handle + 2; + if (subscribed()) + { + // Include CCCD + params->type = BT_GATT_DISCOVER_DESCRIPTOR; + params->uuid = this->getClientCharacteristicConfigUuid(); + } + else + { + // Complete the discover + _discoverying = false; } } + else if (params->uuid == this->getClientCharacteristicConfigUuid()) + { + params->start_handle = attr->handle + 1; + _discoverying = false; + } } -void -BLECharacteristic::setUserDescription(BLEDescriptor *descriptor) +struct bt_gatt_subscribe_params *BLECharacteristic::getSubscribeParams() { - _user_description = descriptor; + return &_sub_params; } -void -BLECharacteristic::setPresentationFormat(BLEDescriptor *descriptor) +bool BLECharacteristic::read(BLEPeripheralHelper &peripheral) { - _presentation_format = descriptor; + int retval = 0; + struct bt_conn* conn = NULL; + if (_reading) + { + // Already in reading state + return false; + } + + _read_params.func = profile_read_rsp_process; + _read_params.handle_count = 1; + _read_params.single.handle = peripheral.valueHandle(this); + _read_params.single.offset = 0; + + if (0 == _read_params.single.handle) + { + // Discover not complete + return false; + } + + conn = bt_conn_lookup_addr_le(peripheral.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; } -void -BLECharacteristic::_setValue(const uint8_t value[], uint16_t length) +bool BLECharacteristic::write(BLEPeripheralHelper &peripheral, + const unsigned char value[], + uint16_t length) { - if (length > _value_size) { - length = _value_size; + int retval = 0; + struct bt_conn* conn = NULL; + + conn = bt_conn_lookup_addr_le(peripheral.bt_le_address()); + if (NULL == conn) + { + return false; } - - memcpy(_value, value, length); - _value_length = length; + + // Send read request + retval = bt_gatt_write_without_response(conn, + peripheral.valueHandle(this), + value, length, false); + bt_conn_unref(conn); + return (0 == retval); } + + diff --git a/libraries/CurieBLE/src/BLECharacteristic.h b/libraries/CurieBLE/src/BLECharacteristic.h index a5afaa36..a867e296 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.h +++ b/libraries/CurieBLE/src/BLECharacteristic.h @@ -20,8 +20,10 @@ #ifndef _BLE_CHARACTERISTIC_H_INCLUDED #define _BLE_CHARACTERISTIC_H_INCLUDED +#include "BLECommon.h" + #include "BLEAttribute.h" -#include "BLECentral.h" +#include "BLECentralHelper.h" #include "BLEDescriptor.h" /** @@ -38,9 +40,10 @@ enum BLECharacteristicEvent { /* Forward declaration needed for callback function prototype below */ class BLECharacteristic; class BLEPeripheral; +class BLEHelper; /** Function prototype for BLE Characteristic event callback */ -typedef void (*BLECharacteristicEventHandler)(BLECentral ¢ral, BLECharacteristic &characteristic); +typedef void (*BLECharacteristicEventHandler)(BLEHelper &bleHelper, BLECharacteristic &characteristic); /** * BLE Characteristic Property types @@ -94,6 +97,18 @@ class BLECharacteristic : public BLEAttribute { */ bool setValue(const unsigned char value[], unsigned short length); + /** + * Set the current value of the Characteristic + * + * @param central The central device that update the value. + * @param value New value to set, as a byte array. Data is stored in internal copy. + * @param 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 + */ + void setValue(BLEHelper& blehelper, const uint8_t value[], uint16_t length); + /** * Get the property mask of the Characteristic * @@ -146,38 +161,161 @@ class BLECharacteristic : public BLEAttribute { */ void setEventHandler(BLECharacteristicEvent event, BLECharacteristicEventHandler callback); -protected: - bool add(uint16_t serviceHandle); + /** + * @brief Get Notify Attribute counter that created + * + * @param none + * + * @return unsigned char The totla number of the notify attributes + * + * @note none + */ + static unsigned char numNotifyAttributes(void); + + /** + * @brief Schedule the read request to read the characteristic in peripheral + * + * @param peripheral The peripheral device that want to read. + * + * @return bool Indicate the success or error + * + * @note Only for central device + */ + bool read(BLEPeripheralHelper &peripheral); + + /** + * @brief Schedule the write request to update the characteristic in peripheral + * + * @param peripheral The peripheral device that want to be updated + * @param value New value to set, as a byte array. Data is stored in internal copy. + * @param 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(BLEPeripheralHelper &peripheral, + const unsigned char value[], + uint16_t length); +protected: + friend class BLEProfile; + + void addCharacteristicDeclaration(struct bt_gatt_attr *gatt_attr); + void addCharacteristicValue(struct bt_gatt_attr *gatt_attr); + void addCharacteristicConfigDescriptor(struct bt_gatt_attr *gatt_attr); + + /** + * @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); - void setValue(BLECentral& central, const uint8_t value[], uint16_t length); - void setCccdValue(BLECentral& central, uint16_t value); - + void setUserDescription(BLEDescriptor *descriptor); void setPresentationFormat(BLEDescriptor *descriptor); - - friend class BLEPeripheral; + + struct _bt_gatt_ccc* getCccCfg(void); + struct bt_gatt_chrc* getCharacteristicAttValue(void); + static struct bt_uuid* getCharacteristicAttributeUuid(void); + static struct bt_uuid* 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 attr The discover response + * + * @param params The discover parameter that need to fill + * + * @return none + * + * @note Only for central + */ + void discover(const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params); + + /** + * @brief For central to discover the peripherial profile + * + * @param params The discover parameter that need to fill + * + * @return none + * + * @note Only for central + */ + void discover(struct bt_gatt_discover_params *params); + + /** + * @brief Get the subscribe parameter + * + * @param none + * + * @return struct bt_gatt_subscribe_params * the subscribe parameter + * + * @note Only for central + */ + struct bt_gatt_subscribe_params* getSubscribeParams(); private: void _setValue(const uint8_t value[], uint16_t length); private: - unsigned char _properties; + + static unsigned char _numNotifyAttributes; + static struct bt_uuid_16 _gatt_chrc_uuid; + static struct bt_uuid_16 _gatt_ccc_uuid; + unsigned short _value_size; unsigned short _value_length; unsigned char* _value; bool _written; - uint16_t _cccd_value; uint16_t _value_handle; - uint16_t _cccd_handle; + struct bt_gatt_ccc_cfg _ccc_cfg; + struct _bt_gatt_ccc _ccc_value; + struct bt_gatt_chrc _gatt_chrc; BLEDescriptor* _user_description; BLEDescriptor* _presentation_format; + struct bt_gatt_attr *_attr_chrc_declaration; + struct bt_gatt_attr *_attr_chrc_value; + struct bt_gatt_attr *_attr_cccd; + + // For central device to subscribe the Notification/Indication + struct bt_gatt_subscribe_params _sub_params; + + bool _reading; + struct bt_gatt_read_params _read_params; BLECharacteristicEventHandler _event_handlers[BLECharacteristicEventLast]; }; diff --git a/libraries/CurieBLE/src/BLECommon.h b/libraries/CurieBLE/src/BLECommon.h index 20bf0e23..00c31342 100644 --- a/libraries/CurieBLE/src/BLECommon.h +++ b/libraries/CurieBLE/src/BLECommon.h @@ -22,9 +22,21 @@ #include "Arduino.h" -#include "../src/services/ble/ble_protocol.h" -#include "services/ble/ble_service_gatt.h" -#include "services/ble/ble_service_gatts_api.h" +#include "../src/services/ble_service/ble_protocol.h" + + +#include "infra/log.h" + + +#include +#include +#include +#include + +#define BLE_ADDR_LEN 6 + +#define MAX_UUID_SIZE 16 + /* Theoretically we should be able to support attribute lengths up to 512 bytes * but this involves splitting it across multiple packets. For simplicity, @@ -40,6 +52,30 @@ /* Invalid BLE Address type */ #define BLE_DEVICE_ADDR_INVALID 0xFF +/** BLE response/event status codes. */ +enum BLE_STATUS { + BLE_STATUS_SUCCESS = 0, /**< General BLE Success code */ + 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_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_GAP_BASE = 0x100, /**< GAP specific error base */ + BLE_STATUS_GATT_BASE = 0x200, /**< GATT specific Error base */ +}; + +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 + +typedef bool (*ble_advertise_handle_cb_t)(uint8_t type, const uint8_t *data, + uint8_t data_len, void *user_data); + #endif // _BLE_COMMON_H_INCLUDED diff --git a/libraries/CurieBLE/src/BLEDescriptor.cpp b/libraries/CurieBLE/src/BLEDescriptor.cpp index 8e50120b..d611d471 100644 --- a/libraries/CurieBLE/src/BLEDescriptor.cpp +++ b/libraries/CurieBLE/src/BLEDescriptor.cpp @@ -63,23 +63,3 @@ BLEDescriptor::operator[] (int offset) const return _value[offset]; } -bool -BLEDescriptor::add(uint16_t serviceHandle) -{ - bt_uuid uuid = btUuid(); - struct ble_gatts_descriptor desc; - uint16_t handle = 0; - - memset(&desc, 0, sizeof(desc)); - - desc.p_uuid = &uuid; - - desc.p_value = _value; - desc.length = _value_length; - - // this class only supports read-only descriptors - desc.perms.rd = GAP_SEC_MODE_1 | GAP_SEC_LEVEL_1; - desc.perms.wr = GAP_SEC_NO_PERMISSION; - - return (ble_client_gatts_add_descriptor(serviceHandle, &desc, &handle) == BLE_STATUS_SUCCESS); -} diff --git a/libraries/CurieBLE/src/BLEDescriptor.h b/libraries/CurieBLE/src/BLEDescriptor.h index 08e53f13..498035a0 100644 --- a/libraries/CurieBLE/src/BLEDescriptor.h +++ b/libraries/CurieBLE/src/BLEDescriptor.h @@ -65,7 +65,6 @@ class BLEDescriptor : public BLEAttribute { unsigned char operator[] (int offset) const; protected: - bool add(uint16_t serviceHandle); friend BLEPeripheral; diff --git a/libraries/CurieBLE/src/BLEHelper.cpp b/libraries/CurieBLE/src/BLEHelper.cpp new file mode 100644 index 00000000..b756f5d4 --- /dev/null +++ b/libraries/CurieBLE/src/BLEHelper.cpp @@ -0,0 +1,135 @@ + +#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() +{ + #if 0 + if (NULL != _conn) + { + bt_conn_unref(_conn); + } + #endif +} + +#if 0 +void BLEHelper::setConn(struct bt_conn *conn) +{ + if (conn == _conn) + { + return; + } + + if (NULL != _conn) + { + bt_conn_unref(_conn); + } + _conn = conn; +} +#endif + +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(bt_addr_le_t address) { + _address = address; +} + +void +BLEHelper::clearAddress() { + memset(&_address, 0x00, sizeof(_address)); +} + +const struct bt_le_conn_param *BLEHelper::getConnParams() +{ + return &_conn_params; +} + +void BLEHelper::setConnParames(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; + +} + + diff --git a/libraries/CurieBLE/src/BLEHelper.h b/libraries/CurieBLE/src/BLEHelper.h new file mode 100644 index 00000000..f844eea6 --- /dev/null +++ b/libraries/CurieBLE/src/BLEHelper.h @@ -0,0 +1,79 @@ +/* + * 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 Central in string form + * + * @return const char* address of the Central in string form + */ + const char* address(void) const; + + 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; + void operator=(const BLEHelper& rhs); + void setConn(struct bt_conn *conn); + + const struct bt_le_conn_param *getConnParams(); + void setConnParames(uint16_t intervalmin, + uint16_t intervalmax, + uint16_t latency, + uint16_t timeout); + + protected: + void setAddress(bt_addr_le_t address); + void clearAddress(); + BLEHelper(); + virtual ~BLEHelper(); + + private: + bt_addr_le_t _address; + //struct bt_conn *_conn; + struct bt_le_conn_param _conn_params; +}; + +#endif + + diff --git a/libraries/CurieBLE/src/BLEPeripheral.cpp b/libraries/CurieBLE/src/BLEPeripheral.cpp index f1218bcd..ca40137b 100644 --- a/libraries/CurieBLE/src/BLEPeripheral.cpp +++ b/libraries/CurieBLE/src/BLEPeripheral.cpp @@ -18,112 +18,42 @@ */ #include "BLEPeripheral.h" +#include "BLEPeripheralRole.h" -#include "BLECharacteristic.h" -#include "BLEDescriptor.h" -#include "BLEService.h" -#include "BLEUuid.h" - - -#define BLE_DISCONNECT_REASON_LOCAL_TERMINATION 0x16 - -void -blePeripheralGapEventHandler(ble_client_gap_event_t event, struct ble_gap_event *event_data, void *param) -{ - BLEPeripheral* p = (BLEPeripheral*)param; - - p->handleGapEvent(event, event_data); -} - -void -blePeripheralGattsEventHandler(ble_client_gatts_event_t event, struct ble_gatts_evt_msg *event_data, void *param) -{ - BLEPeripheral* p = (BLEPeripheral*)param; - - p->handleGattsEvent(event, event_data); -} +//#include "BLECharacteristic.h" BLEPeripheral::BLEPeripheral(void) : - _state(BLE_PERIPH_STATE_NOT_READY), - _advertise_service_uuid(NULL), _local_name(NULL), - _service_data_uuid(NULL), - _service_data(NULL), - _service_data_length(0), _appearance(0), - _min_conn_interval(DEFAULT_MIN_CONN_INTERVAL), - _max_conn_interval(DEFAULT_MAX_CONN_INTERVAL), - _central(this), - _attributes(NULL), - _num_attributes(0), - _last_added_characteritic(NULL) + _adv_data_idx(0) { - memset(_event_handlers, 0x00, sizeof(_event_handlers)); - - ble_client_get_factory_config(&_local_bda, _device_name); + memset(_adv_data, 0x00, sizeof(_adv_data)); + + // Default Advertising parameter + setAdvertisingParam(BT_LE_ADV_IND , + 0xA0, + 0xF0); } BLEPeripheral::~BLEPeripheral(void) { - if (this->_attributes) { - free(this->_attributes); - } } bool BLEPeripheral::begin() { - BleStatus status; - - status = _init(); - if (status != BLE_STATUS_SUCCESS) { + bool ret = false; + + pr_info(LOG_MODULE_BLE, "%s: %d", __FUNCTION__, 1); + + ret = BLEPeripheralRole::instance()->begin(); + if (!ret) + { return false; } - - /* Populate advertising data - */ - _advDataInit(); - - status = ble_client_gap_wr_adv_data(_adv_data, _adv_data_len); - if (BLE_STATUS_SUCCESS != status) { - return false; - } - - uint16_t lastServiceHandle = 0; - - for (int i = 0; i < _num_attributes; i++) { - BLEAttribute* attribute = _attributes[i]; - BLEAttributeType type = attribute->type(); - bool addResult = false; - - if (BLETypeService == type) { - BLEService* service = (BLEService*)attribute; - - addResult = service->add(); - - lastServiceHandle = service->handle(); - } else if (BLETypeCharacteristic == type) { - BLECharacteristic* characteristic = (BLECharacteristic*)attribute; - - addResult = characteristic->add(lastServiceHandle); - } else if (BLETypeDescriptor == type) { - BLEDescriptor *descriptor = (BLEDescriptor*)attribute; - - if (strcmp(descriptor->uuid(), "2901") == 0 || - strcmp(descriptor->uuid(), "2902") == 0 || - strcmp(descriptor->uuid(), "2903") == 0 || - strcmp(descriptor->uuid(), "2904") == 0) { - continue; // skip - } - - addResult = descriptor->add(lastServiceHandle); - } - - if (!addResult) { - return false; - } - } - - return (_startAdvertising() == BLE_STATUS_SUCCESS); + + pr_info(LOG_MODULE_BLE, "%s: %d", __FUNCTION__, 2); + + return (startAdvertising() == BLE_STATUS_SUCCESS); } void @@ -136,23 +66,11 @@ BLEPeripheral::poll() void BLEPeripheral::end() { - _stop(); -} - -uint8_t -BLEPeripheral::getAdvertisingLength() -{ - return _adv_data_len; -} - -uint8_t* -BLEPeripheral::getAdvertising() -{ - return _adv_data; + BLEPeripheralRole::instance()->stop(); } void -BLEPeripheral::setAdvertisedServiceUuid(const char* advertisedServiceUuid) +BLEPeripheral::setAdvertisedServiceUuid(const struct bt_uuid* advertisedServiceUuid) { _advertise_service_uuid = advertisedServiceUuid; } @@ -164,23 +82,28 @@ BLEPeripheral::setLocalName(const char* localName) } void -BLEPeripheral::setAdvertisedServiceData(const char* serviceDataUuid, uint8_t* serviceData, uint8_t serviceDataLength) +BLEPeripheral::setAdvertisedServiceData(const struct bt_uuid* serviceDataUuid, + uint8_t* serviceData, + uint8_t serviceDataLength) { _service_data_uuid = serviceDataUuid; _service_data = serviceData; _service_data_length = serviceDataLength; } +void BLEPeripheral::setAdvertisingParam(uint8_t type, + uint16_t interval_min, + uint16_t interval_max) +{ + BLEPeripheralRole::instance()->setAdvertisingParam(type, + interval_min, + interval_max); +} + void BLEPeripheral::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); - } + BLEPeripheralRole::instance()->setDeviceName(deviceName); } void @@ -192,286 +115,160 @@ BLEPeripheral::setAppearance(const uint16_t appearance) void BLEPeripheral::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; - } + BLEPeripheralRole::instance()->setConnectionInterval(minConnInterval, + maxConnInterval); } void -BLEPeripheral::setEventHandler(BLEPeripheralEvent event, BLEPeripheralEventHandler callback) +BLEPeripheral::setEventHandler(BLERoleEvent event, BLERoleEventHandler callback) { - if (event < sizeof(_event_handlers)) { - _event_handlers[event] = callback; - } -} - -void -BLEPeripheral::addAttribute(BLEAttribute& attribute) -{ - if (_attributes == NULL) { - _attributes = (BLEAttribute**)malloc(BLEAttribute::numAttributes() * sizeof(BLEAttribute*)); - } - - _attributes[_num_attributes] = &attribute; - _num_attributes++; - - BLEAttributeType type = attribute.type(); - - if (BLETypeCharacteristic == type) { - _last_added_characteritic = (BLECharacteristic*)&attribute; - } else if (BLETypeDescriptor == type) { - if (_last_added_characteritic) { - BLEDescriptor* descriptor = (BLEDescriptor*)&attribute; - - if (strcmp("2901", descriptor->uuid()) == 0) { - _last_added_characteritic->setUserDescription(descriptor); - } else if (strcmp("2904", descriptor->uuid()) == 0) { - _last_added_characteritic->setPresentationFormat(descriptor); - } - } - } + BLEPeripheralRole::instance()->setEventHandler(event, callback); } bool BLEPeripheral::disconnect() { - BleStatus status; - - if (BLE_PERIPH_STATE_CONNECTED == _state) { - status = ble_client_gap_disconnect(BLE_DISCONNECT_REASON_LOCAL_TERMINATION); - } else { - status = BLE_STATUS_WRONG_STATE; - } - - return (status == BLE_STATUS_SUCCESS); + return BLEPeripheralRole::instance()->disconnect(); } -BLECentral +BLECentralHelper BLEPeripheral::central() { - poll(); - - return _central; + return BLEPeripheralRole::instance()->central(); } bool BLEPeripheral::connected() { - poll(); - - return _central; + return BLEPeripheralRole::instance()->connected(); } -BleStatus -BLEPeripheral::_init() +void BLEPeripheral::addAttribute(BLEAttribute& attribute) { - BleStatus status; - int8_t txPower = 127; - - if (BLE_PERIPH_STATE_NOT_READY != _state) - return BLE_STATUS_WRONG_STATE; - - status = ble_client_init(blePeripheralGapEventHandler, this, - blePeripheralGattsEventHandler, this); - if (BLE_STATUS_SUCCESS != status) { - return status; - } - - status = ble_client_gap_set_enable_config(_device_name, &_local_bda, _appearance, txPower, _min_conn_interval, _max_conn_interval); - if (BLE_STATUS_SUCCESS != status) { - return status; - } - - _state = BLE_PERIPH_STATE_READY; - return BLE_STATUS_SUCCESS; + BLEPeripheralRole::instance()->addAttribute(attribute); } -void + +BleStatus BLEPeripheral::_advDataInit(void) { - uint8_t *adv_tmp = _adv_data; - - memset(_adv_data, 0, sizeof(_adv_data)); - + uint8_t lengthTotal = 2; // Flags data length + _adv_data_idx = 0; + /* Add flags */ - *adv_tmp++ = 2; - *adv_tmp++ = BLE_ADV_TYPE_FLAGS; - *adv_tmp++ = BLE_SVC_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE; - _adv_data_len = 3; - - if (_advertise_service_uuid) { - BLEUuid bleUuid = BLEUuid(_advertise_service_uuid); - struct bt_uuid uuid = bleUuid.uuid(); - - if (BT_UUID16 == uuid.type) { - uint8_t *adv_tmp = &_adv_data[_adv_data_len]; - *adv_tmp++ = (1 + sizeof(uint16_t)); /* Segment data length */ - *adv_tmp++ = BLE_ADV_TYPE_COMP_16_UUID; /* Needed for Eddystone */ - UINT16_TO_LESTREAM(adv_tmp, uuid.uuid16); - _adv_data_len += (2 + sizeof(uint16_t)); - } else if (BT_UUID128 == uuid.type) { - uint8_t *adv_tmp = &_adv_data[_adv_data_len]; - *adv_tmp++ = (1 + MAX_UUID_SIZE); /* Segment data length */ - *adv_tmp++ = BLE_ADV_TYPE_INC_128_UUID; - memcpy(adv_tmp, uuid.uuid128, MAX_UUID_SIZE); - _adv_data_len += (2 + MAX_UUID_SIZE); + _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 *)&(((struct bt_uuid_16 *)_advertise_service_uuid)->val); + length = sizeof(uint16_t); + type = BT_DATA_UUID16_ALL; + } + else if (BT_UUID_TYPE_128 == _advertise_service_uuid->type) + { + data = ((struct bt_uuid_128 *)_advertise_service_uuid)->val; + length = MAX_UUID_SIZE; + 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) { + if (_local_name) + { /* Add device name (truncated if too long) */ - uint8_t calculated_len; - - adv_tmp = &_adv_data[_adv_data_len]; - if (_adv_data_len + strlen(_local_name) + 2 <= BLE_MAX_ADV_SIZE) { - *adv_tmp++ = strlen(_local_name) + 1; - *adv_tmp++ = BLE_ADV_TYPE_COMP_LOCAL_NAME; - calculated_len = strlen(_local_name); - } else { - *adv_tmp++ = BLE_MAX_ADV_SIZE - _adv_data_len - 1; - *adv_tmp++ = BLE_ADV_TYPE_SHORT_LOCAL_NAME; - calculated_len = BLE_MAX_ADV_SIZE - _adv_data_len - 2; - } - - memcpy(adv_tmp, _local_name, calculated_len); - _adv_data_len += calculated_len + 2; + _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) { + if (_service_data) + { /* Add Service Data (if it will fit) */ - BLEUuid bleUuid = BLEUuid(_service_data_uuid); - struct bt_uuid uuid = bleUuid.uuid(); - /* A 128-bit Service Data UUID won't fit in an Advertising packet */ - if (BT_UUID16 != uuid.type) { - return; /* We support service data only for 16-bit service UUID */ + 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 = 1 + sizeof(uint16_t) + _service_data_length; - if (_adv_data_len + 1 + block_len > BLE_MAX_ADV_SIZE) { - return; // Service data block is too large. + 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++; - adv_tmp = &_adv_data[_adv_data_len]; + uint8_t *adv_tmp = _service_data_buf; - *adv_tmp++ = block_len; - _adv_data_len++; - - *adv_tmp++ = BLE_ADV_TYPE_SERVICE_DATA_16_UUID; - UINT16_TO_LESTREAM(adv_tmp, uuid.uuid16); + UINT16_TO_LESTREAM(adv_tmp, (((struct bt_uuid_16 *)_service_data_uuid)->val)); memcpy(adv_tmp, _service_data, _service_data_length); - - _adv_data_len += block_len; + + 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; } -} - -BleStatus -BLEPeripheral::_startAdvertising() -{ - BleStatus status; - - if (_state != BLE_PERIPH_STATE_READY) - return BLE_STATUS_WRONG_STATE; - - status = ble_client_gap_start_advertise(0); // 0 = no timeout - if (BLE_STATUS_SUCCESS != status) - return status; - - _state = BLE_PERIPH_STATE_ADVERTISING; return BLE_STATUS_SUCCESS; } BleStatus -BLEPeripheral::_stop(void) +BLEPeripheral::startAdvertising() { - BleStatus status; - - if (BLE_PERIPH_STATE_ADVERTISING == _state) - status = ble_client_gap_stop_advertise(); - else - status = disconnect(); - + BleStatus status = BLE_STATUS_SUCCESS; + status = _advDataInit(); if (BLE_STATUS_SUCCESS != status) + { return status; - - _state = BLE_PERIPH_STATE_READY; - return BLE_STATUS_SUCCESS; -} - -void -BLEPeripheral::handleGapEvent(ble_client_gap_event_t event, struct ble_gap_event *event_data) -{ - if (BLE_CLIENT_GAP_EVENT_CONNECTED == event) { - _state = BLE_PERIPH_STATE_CONNECTED; - _central.setAddress(event_data->connected.peer_bda); - - if (_event_handlers[BLEConnected]) { - _event_handlers[BLEConnected](_central); - } - } else if (BLE_CLIENT_GAP_EVENT_DISCONNECTED == event) { - - for (int i = 0; i < _num_attributes; i++) { - BLEAttribute* attribute = _attributes[i]; - - if (attribute->type() == BLETypeCharacteristic) { - BLECharacteristic* characteristic = (BLECharacteristic*)attribute; - - characteristic->setCccdValue(_central, 0x0000); // reset CCCD - } - } - - if (_event_handlers[BLEDisconnected]) - _event_handlers[BLEDisconnected](_central); - - _state = BLE_PERIPH_STATE_READY; - _central.clearAddress(); - - _startAdvertising(); - } else if (BLE_CLIENT_GAP_EVENT_CONN_TIMEOUT == event) { - _state = BLE_PERIPH_STATE_READY; - - _startAdvertising(); } + status = BLEPeripheralRole::instance()->startAdvertising(_adv_data, + _adv_data_idx, + NULL, + 0); + return status; } -void -BLEPeripheral::handleGattsEvent(ble_client_gatts_event_t event, struct ble_gatts_evt_msg *event_data) +BleStatus +BLEPeripheral::stopAdvertising() { - if (BLE_CLIENT_GATTS_EVENT_WRITE == event) { - uint16_t handle = event_data->wr.attr_handle; - - for (int i = 0; i < _num_attributes; i++) { - BLEAttribute* attribute = _attributes[i]; - - if (attribute->type() != BLETypeCharacteristic) { - continue; - } - - BLECharacteristic* characteristic = (BLECharacteristic*)attribute; - - if (characteristic->valueHandle() == handle) { - characteristic->setValue(_central, event_data->wr.data, event_data->wr.len); - break; - } else if (characteristic->cccdHandle() == handle) { - uint16_t cccdValue = 0; - - memcpy(&cccdValue, event_data->wr.data, event_data->wr.len); - - characteristic->setCccdValue(_central, cccdValue); - break; - } - } - } + BleStatus status = BLE_STATUS_SUCCESS; + + status = BLEPeripheralRole::instance()->stopAdvertising(); + return status; } + diff --git a/libraries/CurieBLE/src/BLEPeripheral.h b/libraries/CurieBLE/src/BLEPeripheral.h index 054af330..67b6f07b 100644 --- a/libraries/CurieBLE/src/BLEPeripheral.h +++ b/libraries/CurieBLE/src/BLEPeripheral.h @@ -22,28 +22,17 @@ #include "internal/ble_client.h" -#include "BLEAttribute.h" -#include "BLECentral.h" -#include "BLECharacteristic.h" #include "BLECommon.h" - -/** - * BLE Peripheral Events - */ -enum BLEPeripheralEvent { - BLEConnected = 0, - BLEDisconnected = 1, - - BLEPeripheralEventLast = 2 -}; +#include "BLERoleBase.h" +#include "BLEPeripheralHelper.h" /** Function prototype for BLE Peripheral Device event callback */ -typedef void (*BLEPeripheralEventHandler)(BLECentral ¢ral); +typedef void (*BLEPeripheralEventHandler)(BLECentralHelper ¢ral); /** * BLE Peripheral */ -class BLEPeripheral { +class BLEPeripheral{ public: /** * Default Constructor for BLE Peripheral Device @@ -55,23 +44,6 @@ class BLEPeripheral { */ virtual ~BLEPeripheral(void); - /** - * Return the number of bytes in the advertising block. - * Useful for debugging advertising problems. - * - * @note Call only after calling begin(). - */ - uint8_t getAdvertisingLength(); - - /** - * Returns a pointer to the advertising block - * of length getAdvertisingLength(). - * Useful for debugging advertising problems. - * - * @note Call only after calling begin(). - */ - uint8_t* getAdvertising(); - /** * Set the service UUID that the BLE Peripheral Device advertises * @@ -80,7 +52,7 @@ class BLEPeripheral { * * @note This method must be called before the begin method */ - void setAdvertisedServiceUuid(const char* advertisedServiceUuid); + void setAdvertisedServiceUuid(const struct bt_uuid* advertisedServiceUuid); /** * Set the local name that the BLE Peripheral Device advertises @@ -113,8 +85,23 @@ class BLEPeripheral { * the service data will silently not be copied * into the advertising block. */ - void setAdvertisedServiceData(const char* serviceDataUuid, uint8_t* serviceData, uint8_t serviceDataLength); - + void setAdvertisedServiceData(const struct bt_uuid* serviceDataUuid, + uint8_t* serviceData, + uint8_t serviceDataLength); + /** + * Set the ADV parameters about the ADV-Type and interval + * + * @param type Advertising types + * + * @param interval_min Minimum Advertising Interval (N * 0.625) + * + * @param interval_max Maximum Advertising Interval (N * 0.625) + * + * @note none + */ + void setAdvertisingParam(uint8_t type, + uint16_t interval_min, + uint16_t interval_max); /** * Set the device name for the BLE Peripheral Device * @@ -168,7 +155,7 @@ class BLEPeripheral { * @param event Event type for callback * @param callback Pointer to callback function to invoke when an event occurs. */ - void setEventHandler(BLEPeripheralEvent event, BLEPeripheralEventHandler callback); + void setEventHandler(BLERoleEvent event, BLERoleEventHandler callback); /** * Setup attributes and start advertising @@ -199,7 +186,7 @@ class BLEPeripheral { * * @return BleStatus indicating success or error */ - BLECentral central(void); + BLECentralHelper central(void); /** * Is a central connected? @@ -207,52 +194,58 @@ class BLEPeripheral { * @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); + protected: - friend void blePeripheralGapEventHandler(ble_client_gap_event_t event, struct ble_gap_event *event_data, void *param); - friend void blePeripheralGattsEventHandler(ble_client_gatts_event_t event, struct ble_gatts_evt_msg *event_data, void *param); - - void handleGapEvent(ble_client_gap_event_t event, struct ble_gap_event *event_data); - void handleGattsEvent(ble_client_gatts_event_t event, struct ble_gatts_evt_msg *event_data); + void handleConnectEvent(struct bt_conn *conn, uint8_t err); + void handleDisconnectEvent(struct bt_conn *conn, uint8_t reason); + void handleParamUpdated(struct bt_conn *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout); private: - BleStatus _init(void); - BleStatus _startAdvertising(void); + BleStatus _stop(void); - void _advDataInit(void); + BleStatus _advDataInit(void); private: - - enum BLEPeripheralState { - BLE_PERIPH_STATE_NOT_READY = 0, - BLE_PERIPH_STATE_READY, - BLE_PERIPH_STATE_ADVERTISING, - BLE_PERIPH_STATE_CONNECTED, - }; - - BLEPeripheralState _state; - - const char* _advertise_service_uuid; const char* _local_name; - const char* _service_data_uuid; + + const struct bt_uuid* _service_data_uuid; uint8_t* _service_data; - uint8_t _service_data_length; - char _device_name[BLE_MAX_DEVICE_NAME+1]; + uint8_t _service_data_length; + uint8_t _service_data_buf[BLE_MAX_ADV_SIZE]; + uint16_t _appearance; - uint16_t _min_conn_interval; - uint16_t _max_conn_interval; - uint8_t _adv_data[BLE_MAX_ADV_SIZE]; - uint8_t _adv_data_len; - ble_addr_t _local_bda; - BLECentral _central; - - BLEPeripheralEventHandler _event_handlers[BLEPeripheralEventLast]; - - BLEAttribute** _attributes; - uint16_t _num_attributes; - - BLECharacteristic* _last_added_characteritic; + + const struct bt_uuid* _advertise_service_uuid; + + uint8_t _adv_type; + struct bt_data _adv_data[4]; + size_t _adv_data_idx; }; #endif // _BLE_DEVICE_H_INCLUDED diff --git a/libraries/CurieBLE/src/BLEPeripheralHelper.cpp b/libraries/CurieBLE/src/BLEPeripheralHelper.cpp new file mode 100644 index 00000000..65d54c3d --- /dev/null +++ b/libraries/CurieBLE/src/BLEPeripheralHelper.cpp @@ -0,0 +1,117 @@ +/* + * 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(struct bt_gatt_subscribe_params *params) +{ + return _profile.attribute(params); +} + +void BLEPeripheralHelper::discover(const struct bt_gatt_attr *attr) +{ + // Not allow to call the discover + if (NULL == _central) + { + 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; + struct bt_conn* 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) +{ + struct bt_conn* 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(); + } +} + +void BLEPeripheralHelper::addAttribute(BLEAttribute& attribute) +{ + _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 new file mode 100644 index 00000000..2d29a210 --- /dev/null +++ b/libraries/CurieBLE/src/BLEPeripheralHelper.h @@ -0,0 +1,84 @@ +/* + * 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 attribute Attribute to add to Peripheral + * + * @note This method must be called before the begin method + */ + void addAttribute(BLEAttribute& attribute); + + BLEAttribute *attribute(struct bt_gatt_subscribe_params *params); + BLEAttribute *attribute(uint16_t handle); + + /** + * For central to discover the profile + */ + void discover(); + void discover(const struct bt_gatt_attr *attr); + + // For peripheral to register the tree + int registerProfile(); + void linkLost(void); + + // Get value handle. + // 0 is invalid + uint16_t valueHandle(BLEAttribute *attr); + 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 new file mode 100644 index 00000000..dc13b566 --- /dev/null +++ b/libraries/CurieBLE/src/BLEPeripheralRole.cpp @@ -0,0 +1,278 @@ +/* + * 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); +} + +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(); + // Register profile + _peripheral.registerProfile(); + delay(2); // 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; + + if (BLE_PERIPH_STATE_CONNECTED == _state) + { + struct bt_conn *central_conn = bt_conn_lookup_addr_le(_central.bt_le_address()); + if (NULL != central_conn) + { + status = bt_conn_disconnect (central_conn, + BT_HCI_ERR_REMOTE_USER_TERM_CONN); + bt_conn_unref(central_conn); + } + } + return (status == BLE_STATUS_SUCCESS); +} + +BLECentralHelper +BLEPeripheralRole::central() +{ + poll(); + + return _central; +} + +bool +BLEPeripheralRole::connected() +{ + poll(); + + return _central; +} + +void BLEPeripheralRole::addAttribute(BLEAttribute& attribute) +{ + _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 struct bt_data *ad, + size_t ad_len, + const struct bt_data *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::setAdvertisingParam(uint8_t type, + uint16_t interval_min, + uint16_t interval_max) +{ + _adv_param.addr_type = _local_bda.type; + _adv_param.type = type; + _adv_param.interval_min = interval_min; + _adv_param.interval_max = interval_max; +} + +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(struct bt_conn *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); +} + + +void BLEPeripheralRole::handleDisconnectEvent(struct bt_conn *conn, uint8_t reason) +{ + struct bt_conn *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); + } +} + +void BLEPeripheralRole::handleParamUpdated(struct bt_conn *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]) + _event_handlers[BLEUpdateParam](_central); +} + + diff --git a/libraries/CurieBLE/src/BLEPeripheralRole.h b/libraries/CurieBLE/src/BLEPeripheralRole.h new file mode 100644 index 00000000..0fcbd40e --- /dev/null +++ b/libraries/CurieBLE/src/BLEPeripheralRole.h @@ -0,0 +1,262 @@ +/* + * 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 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 minConnInterval Minimum connection interval (1.25 ms units), minimum 0x0006 (7.5ms) + * @param 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 attribute Attribute to add to Peripheral + * + * @return BleStatus indicating success or error + * + * @note This method must be called before the begin method + */ + void addAttribute(BLEAttribute& attribute); + + /** + * Provide a function to be called when events related to this Device are raised + * + * @param event Event type for callback + * @param 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 ad The ADV data array + * + * @param ad_len The ADV data array length + * + * @param sd The Scan response data array + * + * @param sd_len The Scan response data array length + * + * @return BleStatus + * + * @note none + */ + BleStatus startAdvertising(const struct bt_data *ad, + size_t ad_len, + const struct bt_data *sd, + size_t sd_len); + + /** + * @brief Stop send advertisement + * + * @param none + * + * @return none + * + * @note none + */ + BleStatus stopAdvertising(); + + /** + * @brief Set advertising parameter + * + * @param type Advertising type + * + * @param interval_min Minimum Advertising Interval (N * 0.625) + * + * @param interval_max Maximum Advertising Interval (N * 0.625) + * + * @return none + * + * @note none + */ + void setAdvertisingParam(uint8_t type, + uint16_t interval_min, + uint16_t interval_max); + + /** + * @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 conn The object that established the connection + * + * @param err The code of the process + * + * @return none + * + * @note none + */ + void handleConnectEvent(struct bt_conn *conn, uint8_t err); + + /** + * @brief Handle the disconnected event + * + * @param conn The object that lost the connection + * + * @param reason The link lost reason + * + * @return none + * + * @note none + */ + void handleDisconnectEvent(struct bt_conn *conn, uint8_t reason); + + /** + * @brief Handle the conntion update request + * + * @param conn The connection object that need to process the update request + * + * @param interval The connection interval + * + * @param latency The connection latency + * + * @param timeout The connection timeout + * + * @return none + * + * @note none + */ + void handleParamUpdated(struct bt_conn *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 new file mode 100644 index 00000000..186bf4fa --- /dev/null +++ b/libraries/CurieBLE/src/BLEProfile.cpp @@ -0,0 +1,535 @@ +/* + * 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(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, uint16_t len, + uint16_t offset) +{ + const unsigned char *pvalue; + BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; + BLECharacteristic* blecharacteritic; + BLEAttributeType type = bleattr->type(); + if (BLETypeCharacteristic != type) + { + return 0; + } + blecharacteritic = (BLECharacteristic*)bleattr; + pvalue = blecharacteritic->value(); + return bt_gatt_attr_read(conn, attr, buf, len, offset, pvalue, + blecharacteritic->valueLength()); +} + +// Only for peripheral +ssize_t profile_write_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) || 0 != offset) + { + return 0; + } + + blecharacteritic = (BLECharacteristic*)bleattr; + blecharacteritic->setValue(*((BLEHelper *)¢ral), (const uint8_t *) buf, len); + + return len; +} + + +// Only for central +uint8_t profile_notify_process (struct bt_conn *conn, + struct bt_gatt_subscribe_params *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(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) +{ + BLEPeripheralHelper* peripheral = BLECentralRole::instance()->peripheral(conn);// Find peripheral by bt_conn + peripheral->discover(attr); + return BT_GATT_ITER_STOP; +} + +// Only for central +uint8_t profile_read_rsp_process(struct bt_conn *conn, int err, + struct bt_gatt_read_params *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); + } +} + +void BLEProfile::addAttribute (BLEAttribute& attribute) +{ + struct bt_gatt_attr *start; + if (NULL == _attributes) + { + _attributes = (BLEAttribute**)malloc(BLEAttribute::numAttributes() * sizeof(BLEAttribute*)); + memset(_attributes, 0x00, BLEAttribute::numAttributes() * sizeof(BLEAttribute*)); + } + if (NULL == _attr_base) + { + _attr_base = (struct bt_gatt_attr *)malloc((BLEAttribute::numAttributes() + BLECharacteristic::numNotifyAttributes()) * sizeof(struct bt_gatt_attr)); + memset(_attr_base, 0x00, ((BLEAttribute::numAttributes() + BLECharacteristic::numNotifyAttributes()) * sizeof(struct bt_gatt_attr))); + pr_info(LOG_MODULE_BLE, "_attr_base_-%p, size-%d", _attr_base, sizeof(_attr_base)); + } + if (NULL == _sub_param) + { + _sub_param = (struct bt_gatt_subscribe_params *)malloc((BLECharacteristic::numNotifyAttributes()) * sizeof(struct bt_gatt_subscribe_params)); + memset(_sub_param, 0x00, ((BLECharacteristic::numNotifyAttributes()) * sizeof(struct bt_gatt_subscribe_params))); + } + + _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(struct bt_gatt_attr)); + 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(struct bt_gatt_attr)); + start->uuid = characteritic->uuid(); + start->perm = characteritic->getPermission(); + start->read = profile_read_process; + start->write = profile_write_process; + start->user_data = (void*)&attribute; + characteritic->addCharacteristicValue(start); + pr_info(LOG_MODULE_BLE, "desc-%p, uuid: 0x%x", start, ((struct bt_uuid_16*) start->uuid)->val); + + start++; + _attr_index++; + // CCCD + if (characteritic->subscribed()) + { + pr_info(LOG_MODULE_BLE, "cccd-%p", start); + // Descriptor + memset(start, 0, sizeof(struct bt_gatt_attr)); + 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); + + start++; + _attr_index++; + } + } + else if (BLETypeService == type) + { + pr_info(LOG_MODULE_BLE, "service-%p", start); + start->uuid = BLEService::getPrimayUuid(); + start->perm = BT_GATT_PERM_READ; + start->read = bt_gatt_attr_read_service; + start->user_data = attribute.uuid(); + start++; + _attr_index++; + } + +} + +int BLEProfile::registerProfile() +{ + int ret = 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 + + ret = bt_gatt_register(_attr_base, + _attr_index); + pr_info(LOG_MODULE_APP, "%s: ret, %d", __FUNCTION__, ret); + + return ret; +} + +void BLEProfile::discover(const struct bt_gatt_attr *attr) +{ + BLEAttribute* attribute = NULL; + int err; + int i; + bool send_discover = false; + + for (i = 0; i < _num_attributes; i++) + { + attribute = _attributes[i]; + if (attribute->discovering()) + { + if (NULL != attr) + { + // Discover success + switch (_discover_params.type) + { + case BT_GATT_DISCOVER_CHARACTERISTIC: + { + struct bt_gatt_attr *attr_dec = declarationAttr(attribute); + attr_dec++; + attr_dec->handle = attr->handle + 1; + break; + } + case BT_GATT_DISCOVER_DESCRIPTOR: + { + BLECharacteristic *chrc = (BLECharacteristic *)attribute; + struct bt_gatt_attr *attr_dec = declarationAttr(attribute); + struct bt_gatt_attr *attr_chrc = attr_dec + 1; + struct bt_gatt_attr *attr_cccd = attr_dec + 2; + struct bt_gatt_subscribe_params *sub_param_tmp = chrc->getSubscribeParams(); + struct bt_gatt_subscribe_params *sub_param = _sub_param + _sub_param_idx; + struct bt_conn *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(struct bt_gatt_subscribe_params)); + 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); + } + break; + } + case BT_GATT_DISCOVER_PRIMARY: + default: + { + // Do nothing + break; + } + } + } + attribute->discover(attr, &_discover_params); + break; + } + } + + // Send discover + if (attribute->discovering()) + { + send_discover = true; + } + else + { + // Current attribute complete discovery + // Find next attribute to discover + i++; + if (i < _num_attributes) + { + attribute = _attributes[i]; + attribute->discover(&_discover_params); + send_discover = true; + } + } + + if (send_discover) + { + struct bt_conn *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; + } + 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; + } + } +} + + +void BLEProfile::discover() +{ + int err; + BLEService *serviceattr = (BLEService *)_attributes[0]; + struct bt_conn *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(struct bt_gatt_subscribe_params *params) +{ + return attribute(params->value_handle); +} + +BLEAttribute *BLEProfile::attribute(const struct bt_uuid* 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; + struct bt_gatt_attr *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; + struct bt_gatt_attr *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; + } +} + +struct bt_gatt_attr* BLEProfile::declarationAttr(BLEAttribute *attr) +{ + int i; + struct bt_gatt_attr *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; + struct bt_gatt_attr *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; + struct bt_gatt_attr *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 new file mode 100644 index 00000000..33b06c75 --- /dev/null +++ b/libraries/CurieBLE/src/BLEProfile.h @@ -0,0 +1,189 @@ +/* + * 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 attribute Attribute to add to Peripheral + * + * @return BleStatus indicating success or error + * + * @note This method must be called before the begin method + */ + void 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 struct bt_gatt_subscribe_params * Subscribe parameter + * + * @return BLEAttribute * NULL - Not found + * Not NULL - The BLEAttribute object + * + * @note none + */ + BLEAttribute *attribute(struct bt_gatt_subscribe_params *params); + + /** + * @brief Get BLEAttribute by characteristic handle + * + * @param uint16_t 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 const struct bt_gatt_attr * The gatt attribute response + * + * @return none + * + * @note This function only for the central device. + */ + void discover(const struct bt_gatt_attr *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 none + * + * @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 none + * + * @return uint16_t The value handle + * 0 is invalid handle + * + * @note none + */ + uint16_t cccdHandle(BLEAttribute *attr); +protected: + friend ssize_t profile_write_process(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, + uint16_t offset); +private: + /** + * @brief Get BLEAttribute by UUID + * + * @param const struct bt_uuid* 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 struct bt_uuid* uuid); + + /** + * @brief Get bt_gatt_attr by BLEAttribute class + * + * @param BLEAttribute * The BLEAttribute object + * + * @return struct bt_gatt_attr* NULL - Not found + * Not NULL - The bt_gatt_attr in the stack + * + * @note none + */ + struct bt_gatt_attr* declarationAttr(BLEAttribute *attr); + +private: + BLEPeripheralHelper *_peripheral; + struct bt_gatt_attr *_attr_base; + int _attr_index; + + BLEAttribute** _attributes; + uint16_t _num_attributes; + + struct bt_gatt_subscribe_params *_sub_param; + int _sub_param_idx; + + struct bt_gatt_discover_params _discover_params; +}; + +#endif + diff --git a/libraries/CurieBLE/src/BLERoleBase.cpp b/libraries/CurieBLE/src/BLERoleBase.cpp new file mode 100644 index 00000000..6ab9def5 --- /dev/null +++ b/libraries/CurieBLE/src/BLERoleBase.cpp @@ -0,0 +1,86 @@ +/* + * 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(struct bt_conn *conn, + uint8_t err, + void *param) +{ + BLERoleBase* p = (BLERoleBase*)param; + + p->handleConnectEvent(conn, err); +} + + +void bleDisconnectEventHandler(struct bt_conn *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(struct bt_conn *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 new file mode 100644 index 00000000..4db80c48 --- /dev/null +++ b/libraries/CurieBLE/src/BLERoleBase.h @@ -0,0 +1,133 @@ +/* + * 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 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(struct bt_conn *conn, + uint8_t err, + void *param); + friend void bleDisconnectEventHandler(struct bt_conn *conn, + uint8_t reason, + void *param); + friend void bleParamUpdatedEventHandler(struct bt_conn *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout, + void *param); + + /** + * @brief Handle the connected event + * + * @param conn The object that established the connection + * + * @param err The code of the process + * + * @return none + * + * @note virtual function. Just define the interface and the children need to implement + */ + virtual void handleConnectEvent(struct bt_conn *conn, uint8_t err) = 0; + + /** + * @brief Handle the disconnected event + * + * @param conn The object that lost the connection + * + * @param reason The link lost reason + * + * @return none + * + * @note virtual function. Just define the interface and the children need to implement + */ + virtual void handleDisconnectEvent(struct bt_conn *conn, uint8_t reason) = 0; + + /** + * @brief Handle the conntion update request + * + * @param conn The connection object that need to process the update request + * + * @param interval The connection interval + * + * @param latency The connection latency + * + * @param timeout The connection timeout + * + * @return none + * + * @note virtual function. Just define the interface and the children need to implement + */ + virtual void handleParamUpdated (struct bt_conn *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 f7569e3b..2e25f161 100644 --- a/libraries/CurieBLE/src/BLEService.cpp +++ b/libraries/CurieBLE/src/BLEService.cpp @@ -20,21 +20,33 @@ #include "internal/ble_client.h" #include "BLEService.h" +struct bt_uuid_16 BLEService::_gatt_primary_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_PRIMARY_VAL}; +struct bt_uuid *BLEService::getPrimayUuid(void) +{ + return (struct bt_uuid *)&_gatt_primary_uuid; +} BLEService::BLEService(const char* uuid) : BLEAttribute(uuid, BLETypeService) { } -bool -BLEService::add() { - bt_uuid uuid = btUuid(); - uint16_t handle = 0; - BleStatus status = ble_client_gatts_add_service(&uuid, BLE_GATT_SVC_PRIMARY, &handle); - if (BLE_STATUS_SUCCESS == status) { - setHandle(handle); - } +void BLEService::discover(struct bt_gatt_discover_params *params) +{ + params->type = BT_GATT_DISCOVER_PRIMARY; + + params->uuid = this->uuid(); + // Start discovering + _discoverying = true; +} - return (BLE_STATUS_SUCCESS == status); +void BLEService::discover(const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params) +{ + params->start_handle = attr->handle + 1; + + // Complete the discover + _discoverying = false; } + diff --git a/libraries/CurieBLE/src/BLEService.h b/libraries/CurieBLE/src/BLEService.h index 17311f72..2d4c4999 100644 --- a/libraries/CurieBLE/src/BLEService.h +++ b/libraries/CurieBLE/src/BLEService.h @@ -22,6 +22,10 @@ #include "BLEAttribute.h" #include "BLECommon.h" +#include "BLEProfile.h" + +class BLEPeripheral; +class BLEProfile; /** * BLE GATT Service @@ -37,8 +41,14 @@ class BLEService : public BLEAttribute { protected: friend BLEPeripheral; - - bool add(void); + friend BLEProfile; + void discover(const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params); + void discover(struct bt_gatt_discover_params *params); + + static struct bt_uuid *getPrimayUuid(void); +private: + static bt_uuid_16 _gatt_primary_uuid; }; #endif // _BLE_SERVICE_H_INCLUDED diff --git a/libraries/CurieBLE/src/BLEUuid.cpp b/libraries/CurieBLE/src/BLEUuid.cpp deleted file mode 100644 index f0764383..00000000 --- a/libraries/CurieBLE/src/BLEUuid.cpp +++ /dev/null @@ -1,54 +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 "BLEUuid.h" - -BLEUuid::BLEUuid(const char * str) -{ - char temp[] = {0, 0, 0}; - int strLength = strlen(str); - int length = 0; - - memset(&_uuid, 0x00, sizeof(_uuid)); - - for (int i = strLength - 1; i >= 0 && length < MAX_UUID_SIZE; i -= 2) { - if (str[i] == '-') { - i++; - continue; - } - - temp[0] = str[i - 1]; - temp[1] = str[i]; - - _uuid.uuid128[length] = strtoul(temp, NULL, 16); - - length++; - } - - if (length == 2) { - _uuid.type = BT_UUID16; - } else { - _uuid.type = BT_UUID128; - } -} - -bt_uuid BLEUuid::uuid() const -{ - return _uuid; -} diff --git a/libraries/CurieBLE/src/BLEUuid.h b/libraries/CurieBLE/src/BLEUuid.h deleted file mode 100644 index 39b8aff5..00000000 --- a/libraries/CurieBLE/src/BLEUuid.h +++ /dev/null @@ -1,36 +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_UUID_H_INCLUDED -#define _BLE_UUID_H_INCLUDED - -#include "BLECommon.h" - -class BLEUuid -{ -public: - BLEUuid(const char * str); - - bt_uuid uuid(void) const; - -private: - struct bt_uuid _uuid; -}; - -#endif // _BLE_UUID_H_INCLUDED diff --git a/libraries/CurieBLE/src/CurieBLE.h b/libraries/CurieBLE/src/CurieBLE.h index fb51dd58..bc1be251 100644 --- a/libraries/CurieBLE/src/CurieBLE.h +++ b/libraries/CurieBLE/src/CurieBLE.h @@ -23,3 +23,5 @@ #include "BLEService.h" #include "BLEPeripheral.h" #include "BLETypedCharacteristics.h" + +#include "BLECentral.h" diff --git a/libraries/CurieBLE/src/internal/ble_client.c b/libraries/CurieBLE/src/internal/ble_client.c index a4f4cbc6..5731a75f 100644 --- a/libraries/CurieBLE/src/internal/ble_client.c +++ b/libraries/CurieBLE/src/internal/ble_client.c @@ -27,6 +27,8 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ + +#include #include #include "cfw/cfw.h" @@ -48,327 +50,71 @@ #include "ble_client.h" #include "platform.h" -/* Advertising parameters */ -#define BLE_GAP_ADV_TYPE_ADV_IND 0x00 /**< Connectable undirected. */ -#define BLE_GAP_ADV_FP_ANY 0x00 /**< Allow scan requests and connect requests from any device. */ -/** options see \ref BLE_ADV_OPTIONS */ -/* options: BLE_NO_ADV_OPT */ -#define APP_ULTRA_FAST_ADV_INTERVAL 32 -#define APP_ULTRA_FAST_ADV_TIMEOUT_IN_SECONDS 180 -/* options: BLE_SLOW_ADV */ -#define APP_DISC_ADV_INTERVAL 160 -#define APP_DISC_ADV_TIMEOUT_IN_SECONDS 180 -/* options: BLE_NON_DISC_ADV */ -#define APP_NON_DISC_ADV_FAST_INTERVAL 160 -#define APP_NON_DISC_ADV_FAST_TIMEOUT_IN_SECONDS 30 -/* options: BLE_SLOW_ADV | BLE_NON_DISC_ADV */ -#define APP_NON_DISC_ADV_SLOW_INTERVAL 2056 -#define APP_NON_DISC_ADV_SLOW_TIMEOUT_IN_SECONDS 0 +#include "infra/log.h" -struct cfw_msg_rsp_sync { - volatile unsigned response; - volatile ble_status_t status; - void *param; -}; - -#define TIMEOUT_TICKS_1SEC 32768 /* ~1 second in RTC timer ticks */ -#define TIMEOUT_TICKS_1MS 32 /* ~1 millisecond in RTC timer ticks */ -#define wait_for_condition(cond, status) \ -do { \ - unsigned timeout = get_uptime_32k() + TIMEOUT_TICKS_1SEC; \ - status = BLE_STATUS_SUCCESS; \ - while (!(cond)) { \ - if (get_uptime_32k() > timeout) { \ - status = BLE_STATUS_TIMEOUT; \ - break; \ - } \ - } \ -} while(0) - -static cfw_handle_t client_handle; -static svc_client_handle_t *service_handle; -static uint16_t conn_handle; -static bool connected; - -static ble_client_gap_event_cb_t ble_client_gap_event_cb; -static void *ble_client_gap_event_param; - -static ble_client_gatts_event_cb_t ble_client_gatts_event_cb; -static void *ble_client_gatts_event_param; - -volatile struct cfw_msg_rsp_sync sync; - - -static void handle_msg_id_cfw_svc_avail_evt(cfw_svc_available_evt_msg_t *evt, void *param) -{ - if (evt->service_id == BLE_CORE_SERVICE_ID) { - sync.status = BLE_STATUS_SUCCESS; - sync.response = 1; - } -} - -static void handle_msg_id_cfw_open_svc(cfw_open_conn_rsp_msg_t *rsp, void *param) -{ - service_handle = (svc_client_handle_t *)(rsp->client_handle); - - sync.status = BLE_STATUS_SUCCESS; - sync.response = 1; -} - -static void handle_msg_id_ble_gap_wr_conf_rsp(struct ble_rsp *rsp, void *param) -{ - sync.status = rsp->status; - sync.response = 1; -} - -static void handle_msg_id_ble_gap_rd_bda_rsp(ble_bda_rd_rsp_t *rsp, void *param) -{ - ble_addr_t *p_bda = (ble_addr_t *)sync.param; - - if (p_bda && BLE_STATUS_SUCCESS == rsp->status) - memcpy(p_bda, &rsp->bd, sizeof(*p_bda)); - - sync.status = rsp->status; - sync.response = 1; -} - -static void handle_msg_id_ble_gap_sm_config_rsp(struct ble_rsp *rsp, void *param) -{ - sync.status = rsp->status; - sync.response = 1; -} - -static void handle_msg_id_ble_gap_wr_adv_data_rsp(struct ble_rsp *rsp, void *param) -{ - sync.status = rsp->status; - sync.response = 1; -} - -static void handle_msg_id_ble_gap_enable_adv_rsp(struct ble_rsp *rsp, void *param) -{ - /* No waiting for this response, so nothing to do here */ -} - -static void handle_msg_id_ble_gap_disable_adv_rsp(struct ble_rsp *rsp, void *param) -{ - /* No waiting for this response, so nothing to do here */ -} - -static void handle_msg_id_gatts_add_service_rsp(struct ble_gatts_add_svc_rsp *rsp, void *param) -{ - uint16_t *p_svc_handle = (uint16_t *)sync.param; - - if (p_svc_handle && BLE_STATUS_SUCCESS == rsp->status) - *p_svc_handle = rsp->svc_handle; - - sync.status = rsp->status; - sync.response = 1; -} - -static void handle_msg_id_gatts_add_characteristic_rsp(struct ble_gatts_add_char_rsp *rsp, void *param) -{ - struct ble_gatts_char_handles *p_handles = (struct ble_gatts_char_handles *)sync.param; - - if (p_handles && BLE_STATUS_SUCCESS == rsp->status) - memcpy(p_handles, &rsp->char_h, sizeof(*p_handles)); - - sync.status = rsp->status; - sync.response = 1; -} - -static void handle_msg_id_gatts_add_desc_rsp(struct ble_gatts_add_desc_rsp *rsp, void *param) -{ - uint16_t *p_handle = (uint16_t *)sync.param; - - if (p_handle && BLE_STATUS_SUCCESS == rsp->status) - *p_handle = rsp->handle; - - sync.status = rsp->status; - sync.response = 1; -} - -static void handle_msg_id_ble_gatts_set_attribute_value_rsp(struct ble_gatts_set_attr_rsp_msg *rsp, void *param) -{ - sync.status = rsp->status; - sync.response = 1; -} - -static void handle_msg_id_ble_gap_connect_evt_msg(struct ble_gap_event *evt, void *param) -{ - conn_handle = evt->conn_handle; - connected = true; - - if (ble_client_gap_event_cb) - ble_client_gap_event_cb(BLE_CLIENT_GAP_EVENT_CONNECTED, evt, ble_client_gap_event_param); -} - -static void handle_msg_id_ble_gap_disconnect_evt_msg(struct ble_gap_event *evt, void *param) -{ - connected = false; - - if (ble_client_gap_event_cb) - ble_client_gap_event_cb(BLE_CLIENT_GAP_EVENT_DISCONNECTED, evt, ble_client_gap_event_param); -} +// APP callback +static ble_client_connect_event_cb_t ble_client_connect_event_cb = NULL; +static void *ble_client_connect_event_param; -static void handle_msg_id_ble_gap_timeout_evt_msg(struct ble_gap_event *evt, void *param) -{ - connected = false; +static ble_client_disconnect_event_cb_t ble_client_disconnect_event_cb = NULL; +static void *ble_client_disconnect_event_param; - if (!ble_client_gap_event_cb) - return; +static ble_client_update_param_event_cb_t ble_client_update_param_event_cb = NULL; +static void *ble_client_update_param_event_param; - switch (evt->timeout.reason) { - case BLE_SVC_GAP_TO_ADV: - ble_client_gap_event_cb(BLE_CLIENT_GAP_EVENT_ADV_TIMEOUT, evt, ble_client_gap_event_param); - break; - case BLE_SVC_GAP_TO_CONN: - ble_client_gap_event_cb(BLE_CLIENT_GAP_EVENT_CONN_TIMEOUT, evt, ble_client_gap_event_param); - break; - }; -} -static void handle_msg_id_ble_gap_rssi_evt_msg(struct ble_gap_event *evt, void *param) -{ - if (ble_client_gap_event_cb) - ble_client_gap_event_cb(BLE_CLIENT_GAP_EVENT_RSSI, evt, ble_client_gap_event_param); -} +#define NIBBLE_TO_CHAR(n) \ + ((n) >= 0xA ? ('A' + (n) - 0xA) : ('0' + (n))) -static void handle_msg_id_ble_gatts_write_evt_msg(struct ble_gatts_evt_msg *evt, void *param) -{ - if (ble_client_gatts_event_cb) - ble_client_gatts_event_cb(BLE_CLIENT_GATTS_EVENT_WRITE, evt, ble_client_gatts_event_param); -} +#define BYTE_TO_STR(s, byte) \ + do { \ + *s++ = NIBBLE_TO_CHAR(byte >> 4); \ + *s++ = NIBBLE_TO_CHAR(byte & 0xF); \ + }while(0) -static void handle_msg_id_ble_gatts_send_notif_ind_rsp(ble_gatts_rsp_t *rsp, void *param) -{ - sync.status = rsp->status; - sync.response = 1; -} -static void handle_msg_id_ble_gap_disconnect_rsp(struct ble_rsp *rsp, void *param) -{ - sync.status = rsp->status; - sync.response = 1; -} +#ifdef __cplusplus +extern "C" { +#endif -static void handle_msg_id_ble_gap_set_rssi_report_rsp(struct ble_rsp *rsp, void *param) +static void on_connected(struct bt_conn *conn, uint8_t err) { - sync.status = rsp->status; - sync.response = 1; + if (ble_client_connect_event_cb) + { + ble_client_connect_event_cb(conn, err, ble_client_connect_event_param); + } } -static void handle_msg_id_ble_gap_dtm_init_rsp(struct ble_generic_msg *rsp, void *param) +static void on_disconnected(struct bt_conn *conn, uint8_t reason) { - sync.status = rsp->status; - sync.response = 1; + if (ble_client_disconnect_event_cb) + { + ble_client_disconnect_event_cb(conn, reason, ble_client_disconnect_event_param); + } } -static void ble_core_client_handle_message(struct cfw_message *msg, void *param) +static void on_le_param_updated(struct bt_conn *conn, uint16_t interval, + uint16_t latency, uint16_t timeout) { - switch (CFW_MESSAGE_ID(msg)) { - - case MSG_ID_CFW_SVC_AVAIL_EVT: - handle_msg_id_cfw_svc_avail_evt((cfw_svc_available_evt_msg_t *)msg, param); - break; - - case MSG_ID_CFW_OPEN_SERVICE: - handle_msg_id_cfw_open_svc((cfw_open_conn_rsp_msg_t *)msg, param); - break; - - case MSG_ID_BLE_GAP_WR_CONF_RSP: - handle_msg_id_ble_gap_wr_conf_rsp((struct ble_rsp *)msg, param); - break; - - case MSG_ID_BLE_GAP_RD_BDA_RSP: - handle_msg_id_ble_gap_rd_bda_rsp((ble_bda_rd_rsp_t *)msg, param); - break; - - case MSG_ID_BLE_GAP_SM_CONFIG_RSP: - handle_msg_id_ble_gap_sm_config_rsp((struct ble_rsp *)msg, param); - break; - - case MSG_ID_BLE_GAP_WR_ADV_DATA_RSP: - handle_msg_id_ble_gap_wr_adv_data_rsp((struct ble_rsp *)msg, param); - break; - - case MSG_ID_BLE_GAP_ENABLE_ADV_RSP: - handle_msg_id_ble_gap_enable_adv_rsp((struct ble_rsp *)msg, param); - break; - - case MSG_ID_BLE_GAP_DISABLE_ADV_RSP: - handle_msg_id_ble_gap_disable_adv_rsp((struct ble_rsp *)msg, param); - break; - - case MSG_ID_BLE_GATTS_ADD_SERVICE_RSP: - handle_msg_id_gatts_add_service_rsp((struct ble_gatts_add_svc_rsp *)msg, param); - break; - - case MSG_ID_BLE_GATTS_ADD_CHARACTERISTIC_RSP: - handle_msg_id_gatts_add_characteristic_rsp((struct ble_gatts_add_char_rsp *)msg, param); - break; - - case MSG_ID_BLE_GATTS_ADD_DESCRIPTOR_RSP: - handle_msg_id_gatts_add_desc_rsp((struct ble_gatts_add_desc_rsp *)msg, param); - break; - - case MSG_ID_BLE_GATTS_SET_ATTRIBUTE_VALUE_RSP: - handle_msg_id_ble_gatts_set_attribute_value_rsp((struct ble_gatts_set_attr_rsp_msg *)msg, param); - break; - - case MSG_ID_BLE_GATTS_SEND_NOTIF_RSP: - case MSG_ID_BLE_GATTS_SEND_IND_RSP: - handle_msg_id_ble_gatts_send_notif_ind_rsp((ble_gatts_rsp_t *)msg, param); - break; - - case MSG_ID_BLE_GAP_CONNECT_EVT: - handle_msg_id_ble_gap_connect_evt_msg((struct ble_gap_event *)msg, param); - break; - - case MSG_ID_BLE_GAP_DISCONNECT_EVT: - handle_msg_id_ble_gap_disconnect_evt_msg((struct ble_gap_event *)msg, param); - break; - - case MSG_ID_BLE_GAP_TO_EVT: - handle_msg_id_ble_gap_timeout_evt_msg((struct ble_gap_event *)msg, param); - break; - - case MSG_ID_BLE_GAP_RSSI_EVT: - handle_msg_id_ble_gap_rssi_evt_msg((struct ble_gap_event *)msg, param); - break; - - case MSG_ID_BLE_GATTS_WRITE_EVT: - handle_msg_id_ble_gatts_write_evt_msg((struct ble_gatts_evt_msg *)msg, param); - break; - - case MSG_ID_BLE_GAP_DISCONNECT_RSP: - handle_msg_id_ble_gap_disconnect_rsp((struct ble_rsp *)msg, param); - break; - - case MSG_ID_BLE_GAP_SET_RSSI_REPORT_RSP: - handle_msg_id_ble_gap_set_rssi_report_rsp((struct ble_rsp *)msg, param); - break; - - case MSG_ID_BLE_GAP_DTM_INIT_RSP: - handle_msg_id_ble_gap_dtm_init_rsp((struct ble_generic_msg *)msg, param); - break; + if (ble_client_update_param_event_cb) + { + ble_client_update_param_event_cb (conn, + interval, + latency, + timeout, + ble_client_update_param_event_param); } - cfw_msg_free(msg); } -#ifdef __cplusplus -extern "C" { -#endif +static struct bt_conn_cb conn_callbacks = { + .connected = on_connected, + .disconnected = on_disconnected, + .le_param_updated = on_le_param_updated +}; -#define NIBBLE_TO_CHAR(n) \ - ((n) >= 0xA ? ('A' + (n) - 0xA) : ('0' + (n))) -#define BYTE_TO_STR(s, byte) \ - do { \ - *s++ = NIBBLE_TO_CHAR(byte >> 4); \ - *s++ = NIBBLE_TO_CHAR(byte & 0xF); \ - }while(0) -void ble_client_get_factory_config(ble_addr_t *bda, char *name) +void ble_client_get_factory_config(bt_addr_le_t *bda, char *name) { struct curie_oem_data *p_oem = NULL; unsigned i; @@ -382,7 +128,7 @@ void ble_client_get_factory_config(ble_addr_t *bda, char *name) if (p_oem->bt_mac_address_type < 2) { bda->type = p_oem->bt_mac_address_type; for (i = 0; i < BLE_ADDR_LEN; i++) - bda->addr[i] = p_oem->bt_address[BLE_ADDR_LEN - 1 - i]; + bda->val[i] = p_oem->bt_address[BLE_ADDR_LEN - 1 - i]; } } } @@ -419,9 +165,9 @@ void ble_client_get_factory_config(ble_addr_t *bda, char *name) if (bda && bda->type != BLE_DEVICE_ADDR_INVALID) { *suffix++ = '-'; - 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 + 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 automatically move the pointer */ } else @@ -433,453 +179,57 @@ void ble_client_get_factory_config(ble_addr_t *bda, char *name) } } -BleStatus ble_client_init(ble_client_gap_event_cb_t gap_event_cb, void *gap_event_param, - ble_client_gatts_event_cb_t gatts_event_cb, void *gatts_event_param) -{ - BleStatus status; - uint32_t delay_until; - - cfw_platform_nordic_init(); - - client_handle = cfw_init(cfw_get_service_queue(), - ble_core_client_handle_message, - NULL); - - sync.response = 0; - if (cfw_register_svc_available(client_handle, - BLE_CORE_SERVICE_ID, - NULL)) - return BLE_STATUS_ERROR; - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - /* We need to wait for ~1 ms before continuing */ - delay_until = get_uptime_32k() + TIMEOUT_TICKS_1MS; - while (get_uptime_32k() < delay_until); - - sync.response = 0; - cfw_open_service(client_handle, - BLE_CORE_SERVICE_ID, - NULL); - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - ble_client_gap_event_cb = gap_event_cb; - ble_client_gap_event_param = gap_event_param; - - ble_client_gatts_event_cb = gatts_event_cb; - ble_client_gatts_event_param = gatts_event_param; - - return sync.status; -} - -BleStatus ble_client_gap_set_enable_config(const char *name, - const ble_addr_t *bda, - const uint16_t appearance, - const int8_t tx_power, - const uint16_t min_conn_interval, - const uint16_t max_conn_interval) -{ - struct ble_wr_config config; - BleStatus status; - - config.p_bda = (bda && bda->type != BLE_DEVICE_ADDR_INVALID) ? (ble_addr_t *)bda : NULL; - config.p_name = (uint8_t *)name; - config.appearance = appearance; - config.tx_power = tx_power; - config.peripheral_conn_params.interval_min = min_conn_interval; - config.peripheral_conn_params.interval_max = max_conn_interval; - config.peripheral_conn_params.slave_latency = SLAVE_LATENCY; - config.peripheral_conn_params.link_sup_to = CONN_SUP_TIMEOUT; - config.central_conn_params.interval_min = min_conn_interval; - config.central_conn_params.interval_max = max_conn_interval; - config.central_conn_params.slave_latency = SLAVE_LATENCY; - config.central_conn_params.link_sup_to = CONN_SUP_TIMEOUT; - - sync.response = 0; - if (ble_gap_set_enable_config(service_handle, &config, NULL)) - return BLE_STATUS_ERROR; - /* Wait for response message */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - if (sync.status) - return sync.status; - - struct ble_gap_sm_config_params sm_params = { - .options = BLE_GAP_BONDING, - .io_caps = BLE_GAP_IO_NO_INPUT_NO_OUTPUT, - .key_size = 16, - }; - sync.response = 0; - if (ble_gap_sm_config(service_handle, &sm_params, NULL)) - return BLE_STATUS_ERROR; - /* Wait for response message */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_gap_get_bda(ble_addr_t *p_bda) -{ - BleStatus status; - - sync.response = 0; - sync.param = (void *)p_bda; - if (ble_gap_read_bda(service_handle, NULL)) - return BLE_STATUS_ERROR; - /* Wait for response message */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_gap_wr_adv_data(uint8_t *adv_data, const uint8_t adv_data_len) -{ - BleStatus status; - - struct ble_gap_adv_rsp_data adv_rsp_data = { - .p_data = adv_data, - .len = adv_data_len, - }; - - /* write advertisement data */ - sync.response = 0; - if (ble_gap_wr_adv_data(service_handle, &adv_rsp_data, NULL, NULL)) - return BLE_STATUS_ERROR; - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_gap_start_advertise(uint16_t timeout) -{ - /* Hard-coding these advertising parameters for now - * Could be changed to support advanced features such as: - * - slow advertising - * - directed advertising - * - whitelist filtering - * - etc. - */ - ble_gap_adv_param_t adv_params = { - .timeout = timeout, - .interval_min = APP_ULTRA_FAST_ADV_INTERVAL, - .interval_max = APP_ULTRA_FAST_ADV_INTERVAL, - .type = BLE_GAP_ADV_TYPE_ADV_IND, - .filter_policy = BLE_GAP_ADV_FP_ANY, - .p_peer_bda = NULL, - .options = BLE_GAP_OPT_ADV_DEFAULT, - }; - - /* For this message, we don't wait for the response, just fire - * and forget. This allows us to invoke it within the - * disconnect event handler to restart the connection - */ - return ble_gap_start_advertise(service_handle, &adv_params, NULL); -} - -BleStatus ble_client_gap_stop_advertise(void) -{ - /* For this message, we don't wait for the response, just fire - * and forget. - */ - return ble_gap_stop_advertise(service_handle, NULL); -} - -BleStatus ble_client_gatts_add_service(const struct bt_uuid *uuid, - const uint8_t type, - uint16_t *svc_handle) -{ - BleStatus status; - - sync.response = 0; - sync.param = (void *)svc_handle; - if (ble_gatts_add_service(service_handle, uuid, type, NULL, NULL)) - return BLE_STATUS_ERROR; - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_gatts_include_service(const uint16_t primary_svc_handle, - const uint16_t included_svc_handle) -{ - BleStatus status; - - sync.response = 0; - if (ble_gatts_add_included_svc(service_handle, - primary_svc_handle, - included_svc_handle, - NULL)) - return BLE_STATUS_ERROR; - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_gatts_add_characteristic(const uint16_t svc_handle, - struct ble_gatts_characteristic *char_data, - struct ble_gatts_char_handles *handles) -{ - BleStatus status; - - sync.response = 0; - sync.param = (void *)handles; - - if (ble_gatts_add_characteristic(service_handle, svc_handle, char_data, - NULL, NULL)) - return BLE_STATUS_ERROR; - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_gatts_add_descriptor(const uint16_t svc_handle, - struct ble_gatts_descriptor *desc, - uint16_t *handle) -{ - BleStatus status; - - sync.response = 0; - sync.param = (void *)handle; - - if (ble_gatts_add_descriptor(service_handle, desc, NULL, NULL)) - return BLE_STATUS_ERROR; - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_gatts_set_attribute_value(const uint16_t value_handle, - const uint16_t len, const uint8_t * p_value, - const uint16_t offset) -{ - BleStatus status; - - sync.response = 0; - if (ble_gatts_set_attribute_value(service_handle, value_handle, - len, p_value, offset, NULL)) - return BLE_STATUS_ERROR; - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_gatts_send_notif_ind(const uint16_t value_handle, - const uint16_t len, uint8_t * p_value, - const uint16_t offset, - const bool indication) -{ - BleStatus status; - - ble_gatts_ind_params_t ind_params = { - .val_handle = value_handle, - .len = len, - .p_data = p_value, - .offset = offset, - }; - - sync.response = 0; - if (indication) - status = ble_gatts_send_ind(service_handle, conn_handle, &ind_params, NULL, NULL); - else - status = ble_gatts_send_notif(service_handle, conn_handle, &ind_params, NULL, NULL); - - if (status) - return BLE_STATUS_ERROR; - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_gap_disconnect(const uint8_t reason) -{ - BleStatus status; - - if (!connected) - return BLE_STATUS_WRONG_STATE; - - sync.response = 0; - if (ble_gap_disconnect(service_handle, conn_handle, reason, NULL)) - return BLE_STATUS_ERROR; - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_gap_set_rssi_report(boolean_t enable) -{ - BleStatus status; - struct rssi_report_params params; - - if (!connected) - return BLE_STATUS_WRONG_STATE; - - params.conn_hdl = conn_handle; - params.op = enable ? BLE_GAP_RSSI_ENABLE_REPORT : BLE_GAP_RSSI_DISABLE_REPORT; - /* TODO - pick sensible defaults for these and/or allow user to specify */ - params.delta_dBm = 5; - params.min_count = 3; - - sync.response = 0; - if (ble_gap_set_rssi_report(service_handle, ¶ms, NULL)) - return BLE_STATUS_ERROR; - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - return sync.status; -} - -BleStatus ble_client_dtm_init(void) -{ - BleStatus status; - - /* Ensure that the ble_client_init() has been called already */ - if (!service_handle) - return BLE_STATUS_WRONG_STATE; - - /* Instruct the Nordic to enter Direct Test Mode */ - sync.response = 0; - ble_gap_dtm_init_req(service_handle, NULL); - - /* Wait for response messages */ - wait_for_condition(sync.response, status); - if (status != BLE_STATUS_SUCCESS) - return status; - - /* DTM is active. Detach UART IPC driver to allow direct access */ - if (BLE_STATUS_SUCCESS == sync.status) - uart_ipc_disable(IPC_UART); - - return sync.status; -} - -static int uart_raw_ble_core_tx_rx(uint8_t * send_data, uint8_t send_no, - uint8_t * rcv_data, uint8_t rcv_no) -{ - int i; - uint8_t rx_byte; - int res; - /* send command */ - for (i = 0; i < send_no; i++) - uart_poll_out(IPC_UART, send_data[i]); - /* answer */ - i = 0; - do { - res = uart_poll_in(IPC_UART, &rx_byte); - if (res == 0) { - rcv_data[i++] = rx_byte; - } - } while (i < rcv_no); - return i; -} - -BleStatus ble_client_dtm_cmd(const struct ble_test_cmd *test_cmd, - struct ble_dtm_test_result *test_result) -{ - BleStatus status; - - uint8_t send_data[7]; - uint8_t rcv_data[9] = {}; - int send_no; - int rcv_no; - - send_data[0] = DTM_HCI_CMD; - send_data[1] = test_cmd->mode; - send_data[2] = DTM_HCI_OPCODE2; - - switch (test_cmd->mode) { - case BLE_TEST_START_DTM_RX: - send_data[3] = 1; /* length */ - send_data[4] = test_cmd->rx.freq; - send_no = 5; - rcv_no = 7; +void ble_client_init(ble_client_connect_event_cb_t connect_cb, void* connect_param, + ble_client_disconnect_event_cb_t disconnect_cb, void* disconnect_param, + ble_client_update_param_event_cb_t update_param_cb, void* update_param_param) +{ + //uint32_t delay_until; + pr_info(LOG_MODULE_BLE, "%s", __FUNCTION__); + ble_client_connect_event_cb = connect_cb; + ble_client_connect_event_param = connect_param; + + ble_client_disconnect_event_cb = disconnect_cb; + ble_client_disconnect_event_param = disconnect_param; + + ble_client_update_param_event_cb = update_param_cb; + ble_client_update_param_event_param = update_param_param; + + bt_conn_cb_register(&conn_callbacks); + return; +} + +BleStatus errorno_to_ble_status(int err) +{ + BleStatus err_code; + err = 0 - err; + + switch(err) { + case 0: + err_code = BLE_STATUS_SUCCESS; + break; + case EIO: + err_code = BLE_STATUS_WRONG_STATE; break; - case BLE_TEST_START_DTM_TX: - send_data[3] = 3; /* length */ - send_data[4] = test_cmd->tx.freq; - send_data[5] = test_cmd->tx.len; - send_data[6] = test_cmd->tx.pattern; - send_no = 7; - rcv_no = 7; + case EBUSY: + err_code = BLE_STATUS_TIMEOUT; break; - case BLE_TEST_SET_TXPOWER: - send_data[3] = 1; /* length */ - send_data[4] = test_cmd->tx_pwr.dbm; - send_no = 5; - rcv_no = 7; + case EFBIG: + case ENOTSUP: + err_code = BLE_STATUS_NOT_SUPPORTED; break; - case BLE_TEST_END_DTM: - send_data[3] = 0; /* length */ - send_no = 4; - rcv_no = 9; + case EPERM: + case EACCES: + err_code = BLE_STATUS_NOT_ALLOWED; break; + case ENOMEM: // No memeory default: - return BLE_STATUS_NOT_SUPPORTED; - } - - uart_raw_ble_core_tx_rx(send_data, send_no, rcv_data, rcv_no); - - status = rcv_data[DTM_HCI_STATUS_IDX]; - - test_result->mode = test_cmd->mode; - - uint8_t *p; - switch (test_cmd->mode) { - case BLE_TEST_END_DTM: - p = &rcv_data[DTM_HCI_LE_END_IDX]; - LESTREAM_TO_UINT16(p, test_result->nb); + err_code = BLE_STATUS_ERROR; break; } - - return status; + return err_code; } + #ifdef __cplusplus } #endif diff --git a/libraries/CurieBLE/src/internal/ble_client.h b/libraries/CurieBLE/src/internal/ble_client.h index 3fe47bd4..b7495867 100644 --- a/libraries/CurieBLE/src/internal/ble_client.h +++ b/libraries/CurieBLE/src/internal/ble_client.h @@ -87,63 +87,26 @@ enum { uuid.type = BT_UUID128; \ } while(0) -typedef enum { - BLE_CLIENT_GAP_EVENT_CONNECTED = 0, - BLE_CLIENT_GAP_EVENT_DISCONNECTED, - BLE_CLIENT_GAP_EVENT_ADV_TIMEOUT, - BLE_CLIENT_GAP_EVENT_CONN_TIMEOUT, - BLE_CLIENT_GAP_EVENT_RSSI, -} ble_client_gap_event_t; -typedef enum { - BLE_CLIENT_GATTS_EVENT_WRITE = 0, -} ble_client_gatts_event_t; +typedef void (*ble_client_connect_event_cb_t)(struct bt_conn *conn, uint8_t err, void *param); +typedef void (*ble_client_disconnect_event_cb_t)(struct bt_conn *conn, uint8_t reason, void *param); +typedef void (*ble_client_update_param_event_cb_t)(struct bt_conn *conn, + uint16_t interval, + uint16_t latency, + uint16_t timeout, + void *param); -typedef void (*ble_client_gap_event_cb_t)(ble_client_gap_event_t event, struct ble_gap_event *event_data, void *param); -typedef void (*ble_client_gatts_event_cb_t)(ble_client_gatts_event_t event, struct ble_gatts_evt_msg *event_data, void *param); #ifdef __cplusplus extern "C" { #endif -void ble_client_get_factory_config(ble_addr_t *bda, char *name); -BleStatus ble_client_init(ble_client_gap_event_cb_t gap_event_cb, - void *gap_event_param, - ble_client_gatts_event_cb_t gatts_event_cb, - void *gatts_event_param); -BleStatus ble_client_gap_set_enable_config(const char *name, - const ble_addr_t *bda, - const uint16_t appearance, - const int8_t tx_power, - const uint16_t min_conn_interval, - const uint16_t max_conn_interval); -BleStatus ble_client_gap_get_bda(ble_addr_t *p_bda); -BleStatus ble_client_gap_wr_adv_data(uint8_t *adv_data, - const uint8_t adv_data_len); -BleStatus ble_client_gap_start_advertise(uint16_t timeout); -BleStatus ble_client_gap_stop_advertise(void); -BleStatus ble_client_gatts_add_service(const struct bt_uuid *uuid, const uint8_t type, uint16_t *svc_handle); -BleStatus ble_client_gatts_include_service(const uint16_t primary_svc_handle, uint16_t included_svc_handle); -BleStatus ble_client_gatts_add_characteristic(const uint16_t svc_handle, - struct ble_gatts_characteristic *char_data, - struct ble_gatts_char_handles *handles); -BleStatus ble_client_gatts_add_descriptor(const uint16_t svc_handle, - struct ble_gatts_descriptor *desc, - uint16_t *handle); -BleStatus ble_client_gatts_set_attribute_value(const uint16_t value_handle, - const uint16_t len, const uint8_t *value, - const uint16_t offset); -BleStatus ble_client_gatts_send_notif_ind(const uint16_t value_handle, - const uint16_t len, uint8_t * p_value, - const uint16_t offset, - const bool indication); -BleStatus ble_client_gap_disconnect(const uint8_t reason); -BleStatus ble_client_gap_set_rssi_report(boolean_t enable); - -/* Direct Test Mode (DTM) API - for internal use only */ -BleStatus ble_client_dtm_init(void); -BleStatus ble_client_dtm_cmd(const struct ble_test_cmd *test_cmd, - struct ble_dtm_test_result *test_result); +void ble_client_init(ble_client_connect_event_cb_t connect_cb, void* connect_param, + ble_client_disconnect_event_cb_t disconnect_cb, void* disconnect_param, + 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); #ifdef __cplusplus } diff --git a/platform.txt b/platform.txt index e5493cb2..ecac9280 100644 --- a/platform.txt +++ b/platform.txt @@ -14,12 +14,12 @@ compiler.prefix=arc-elf32 compiler.path={runtime.tools.arc-elf32.path}/bin/ compiler.c.cmd=arc-elf32-gcc -compiler.c.flags=-c -std=gnu11 -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 -D__ARDUINO_ARC__ +compiler.c.flags=-c -std=gnu11 -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 -D__ARDUINO_ARC__ -DCONFIG_BLUETOOTH_PERIPHERAL -DCONFIG_BLUETOOTH_CENTRAL -DCONFIG_BLUETOOTH_GATT_CLIENT 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 +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.ar.cmd=arc-elf32-ar compiler.ar.flags=rcs compiler.objcopy.cmd=arc-elf32-objcopy diff --git a/system/libarc32_arduino101/Makefile b/system/libarc32_arduino101/Makefile index 9602e3ad..2e947bc9 100644 --- a/system/libarc32_arduino101/Makefile +++ b/system/libarc32_arduino101/Makefile @@ -3,10 +3,12 @@ ASM_SRC+=$(wildcard $(PWD)/drivers/*.S) ASM_SRC+=$(wildcard $(PWD)/common/*.S) C_SRC+=$(wildcard $(PWD)/bootcode/*.c) C_SRC+=$(wildcard $(PWD)/drivers/*.c) +C_SRC+=$(wildcard $(PWD)/drivers/rpc/*.c) C_SRC+=$(wildcard $(PWD)/common/*.c) C_SRC+=$(wildcard $(PWD)/framework/src/*.c) C_SRC+=$(wildcard $(PWD)/framework/src/services/*.c) C_SRC+=$(wildcard $(PWD)/framework/src/services/ble/*.c) +C_SRC+=$(wildcard $(PWD)/framework/src/services/ble_service/*.c) C_SRC+=$(wildcard $(PWD)/framework/src/cfw/*.c) C_SRC+=$(wildcard $(PWD)/framework/src/infra/*.c) C_SRC+=$(wildcard $(PWD)/framework/src/util/*.c) @@ -25,8 +27,21 @@ HWFLAGS=-mARCv2EM -mav2em -mlittle-endian CFGFLAGS=-DCONFIG_SOC_GPIO_32 -DCONFIG_SOC_GPIO_AON -DINFRA_MULTI_CPU_SUPPORT -DCFW_MULTI_CPU_SUPPORT -DCONFIG_HAS_SHARED_MEM -DCONFIG_INFRA_IS_MASTER OPTFLAGS=-g -Os -Wall -Werror +CFGFLAGS+=-DCONFIG_SOC_QUARK_SE +#CFGFLAGS+=-DTRACK_ALLOCS +#CFGFLAGS+=-DIPC_UART_DBG_RX +#CFGFLAGS+=-DIPC_UART_DBG_TX +CFGFLAGS+=-DBT_GATT_DEBUG +CFGFLAGS+=-DCONFIG_RPC_IN +CFGFLAGS+=-DCONFIG_IPC_UART_NS16550 CFGFLAGS+=-DCONFIG_IPC_UART_BAUDRATE=1000000 -INCLUDES=-I. -Icommon -Idrivers -Ibootcode -Iframework/include -Iframework/src/services/ble +CFGFLAGS+=-DCONFIG_BLUETOOTH_MAX_CONN=2 +CFGFLAGS+=-DCONFIG_BT_GATT_BLE_MAX_SERVICES=10 +CFGFLAGS+=-DCONFIG_BLUETOOTH_GATT_CLIENT +CFGFLAGS+=-DCONFIG_BLUETOOTH_CENTRAL -DCONFIG_BLUETOOTH_PERIPHERAL +INCLUDES=-I. -Icommon -Idrivers -Ibootcode -Iframework/include -Iframework/include/services/ble -Iframework/src/services/ble_service +#-Iframework/src/services/ble -Iframework/include/services/ble +INCLUDES+= -Idrivers/rpc -Iframework/src EXTRA_CFLAGS=-D__CPU_ARC__ -DCLOCK_SPEED=32 -std=c99 -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 CFLAGS=$(HWFLAGS) $(OPTFLAGS) $(EXTRA_CFLAGS) $(CFGFLAGS) $(INCLUDES) @@ -39,7 +54,7 @@ lib: $(TARGET_LIB) $(TARGET_LIB): $(C_OBJ) $(ASM_OBJ) @echo "Link $@" - $(AR) rcs $@ $^ + @$(AR) rcs $@ $^ %.o: %.S @echo "Assembling $<" @@ -47,7 +62,7 @@ $(TARGET_LIB): $(C_OBJ) $(ASM_OBJ) %.o: %.c @echo "Compiling $<" - $(CC) -c $(CFLAGS) $< -o $@ + @$(CC) -c $(CFLAGS) $< -o $@ lib_install: lib @if test "$(LIB_INSTALL_PATH)" = "" ; then \ diff --git a/system/libarc32_arduino101/common/atomic.h b/system/libarc32_arduino101/common/atomic.h new file mode 100644 index 00000000..d30cfbe4 --- /dev/null +++ b/system/libarc32_arduino101/common/atomic.h @@ -0,0 +1,156 @@ +/* atomic operations */ + +/* + * Copyright (c) 1997-2015, Wind River Systems, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ATOMIC_H__ +#define __ATOMIC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int atomic_t; +typedef atomic_t atomic_val_t; + +extern atomic_val_t atomic_add(atomic_t *target, atomic_val_t value); +extern atomic_val_t atomic_and(atomic_t *target, atomic_val_t value); +extern atomic_val_t atomic_dec(atomic_t *target); +extern atomic_val_t atomic_inc(atomic_t *target); +extern atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value); +extern atomic_val_t atomic_or(atomic_t *target, atomic_val_t value); +extern atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value); +extern atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value); +extern atomic_val_t atomic_clear(atomic_t *target); +extern atomic_val_t atomic_get(const atomic_t *target); +extern atomic_val_t atomic_set(atomic_t *target, atomic_val_t value); +extern int atomic_cas(atomic_t *target, + atomic_val_t oldValue, atomic_val_t newValue); + + +#define ATOMIC_INIT(i) {(i)} + +#define ATOMIC_BITS (sizeof(atomic_val_t) * 8) +#define ATOMIC_MASK(bit) (1 << ((bit) & (ATOMIC_BITS - 1))) +#define ATOMIC_ELEM(addr, bit) ((addr) + ((bit) / ATOMIC_BITS)) + +/** @brief Test whether a bit is set + * + * Test whether bit number bit is set or not. + * + * Also works for an array of multiple atomic_t variables, in which + * case the bit number may go beyond the number of bits in a single + * atomic_t variable. + * + * @param addr base address to start counting from + * @param bit bit number counted from the base address + * + * @return 1 if the bit was set, 0 if it wasn't + */ +static inline int atomic_test_bit(const atomic_t *addr, int bit) +{ + atomic_val_t val = atomic_get(ATOMIC_ELEM(addr, bit)); + + return (1 & (val >> (bit & (ATOMIC_BITS - 1)))); +} + +/** @brief Clear a bit and return its old value + * + * Atomically clear a bit and return its old value. + * + * Also works for an array of multiple atomic_t variables, in which + * case the bit number may go beyond the number of bits in a single + * atomic_t variable. + * + * @param addr base address to start counting from + * @param bit bit number counted from the base address + * + * @return 1 if the bit was set, 0 if it wasn't + */ +static inline int atomic_test_and_clear_bit(atomic_t *addr, int bit) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_and(ATOMIC_ELEM(addr, bit), ~mask); + + return (old & mask) != 0; +} + +/** @brief Set a bit and return its old value + * + * Atomically set a bit and return its old value. + * + * Also works for an array of multiple atomic_t variables, in which + * case the bit number may go beyond the number of bits in a single + * atomic_t variable. + * + * @param addr base address to start counting from + * @param bit bit number counted from the base address + * + * @return 1 if the bit was set, 0 if it wasn't + */ +static inline int atomic_test_and_set_bit(atomic_t *addr, int bit) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + atomic_val_t old; + + old = atomic_or(ATOMIC_ELEM(addr, bit), mask); + + return (old & mask) != 0; +} + +/** @brief Clear a bit + * + * Atomically clear a bit. + * + * Also works for an array of multiple atomic_t variables, in which + * case the bit number may go beyond the number of bits in a single + * atomic_t variable. + * + * @param addr base address to start counting from + * @param bit bit number counted from the base address + */ +static inline void atomic_clear_bit(atomic_t *addr, int bit) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_and(ATOMIC_ELEM(addr, bit), ~mask); +} + +/** @brief Set a bit + * + * Atomically set a bit. + * + * Also works for an array of multiple atomic_t variables, in which + * case the bit number may go beyond the number of bits in a single + * atomic_t variable. + * + * @param addr base address to start counting from + * @param bit bit number counted from the base address + */ +static inline void atomic_set_bit(atomic_t *addr, int bit) +{ + atomic_val_t mask = ATOMIC_MASK(bit); + + atomic_or(ATOMIC_ELEM(addr, bit), mask); +} + +#ifdef __cplusplus +} +#endif + +#endif /* __ATOMIC_H__ */ diff --git a/system/libarc32_arduino101/common/misc/byteorder.h b/system/libarc32_arduino101/common/misc/byteorder.h new file mode 100644 index 00000000..67cecbc3 --- /dev/null +++ b/system/libarc32_arduino101/common/misc/byteorder.h @@ -0,0 +1,44 @@ +/* byteorder.h - Byte order helpers */ + +/* + * Copyright (c) 2015, Intel Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define bswap_16(x) ((uint16_t) ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))) + +#define bswap_32(x) ((uint32_t) ((((x) >> 24) & 0xff) | (((x) >> 8) & 0xff00) \ + | (((x) & 0xff00) << 8) | (((x) & 0xff) << 24))) + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define sys_le16_to_cpu(val) (val) +#define sys_cpu_to_le16(val) (val) +#define sys_be16_to_cpu(val) bswap_16(val) +#define sys_cpu_to_be16(val) bswap_16(val) +#define sys_le32_to_cpu(val) (val) +#define sys_cpu_to_le32(val) (val) +#define sys_be32_to_cpu(val) bswap_32(val) +#define sys_cpu_to_be32(val) bswap_32(val) +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define sys_le16_to_cpu(val) bswap_16(val) +#define sys_cpu_to_le16(val) bswap_16(val) +#define sys_be16_to_cpu(val) (val) +#define sys_cpu_to_be16(val) (val) +#define sys_le32_to_cpu(val) bswap_32(val) +#define sys_cpu_to_le32(val) bswap_32(val) +#define sys_be32_to_cpu(val) (val) +#define sys_cpu_to_be32(val) (val) +#else +#error "Unknown byte order" +#endif diff --git a/system/libarc32_arduino101/common/misc/util.h b/system/libarc32_arduino101/common/misc/util.h index 626c2b69..ab45bbb1 100644 --- a/system/libarc32_arduino101/common/misc/util.h +++ b/system/libarc32_arduino101/common/misc/util.h @@ -44,6 +44,11 @@ extern "C" { #ifndef _ASMLANGUAGE +#define ARRAY_SIZE(array) ((unsigned long)(sizeof(array) / sizeof((array)[0]))) +#define CONTAINER_OF(ptr, type, field) \ + ((type *)(((char *)(ptr)) - offsetof(type, field))) + + /* round "x" up/down to next multiple of "align" (which must be a power of 2) */ #define ROUND_UP(x, align) \ (((unsigned long)(x) + ((unsigned long)align - 1)) & \ diff --git a/system/libarc32_arduino101/drivers/atomic_native.c b/system/libarc32_arduino101/drivers/atomic_native.c new file mode 100644 index 00000000..41bed7d4 --- /dev/null +++ b/system/libarc32_arduino101/drivers/atomic_native.c @@ -0,0 +1,370 @@ +/* + * Copyright (c) 2016 Intel Corporation + * Copyright (c) 2011-2014 Wind River Systems, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file Atomic ops for ARC EM + * + * This module provides the atomic operators for ARC EM family processors + * which do not support native atomic operations. + * + * The atomic operations are guaranteed to be atomic with respect + * to interrupt service routines, and to operations performed by peer + * processors. + * + * (originally from x86's atomic.c) + */ + +#include +//#include +//#include +#include +#define irq_lock() interrupt_lock() +#define irq_unlock(key) interrupt_unlock(key) + +#if defined(__clang__) +#define FUNC_NO_FP +#else +#define FUNC_NO_FP __attribute__((optimize("-fomit-frame-pointer"))) +#endif +/** + * + * @brief Atomic compare-and-set primitive + * + * This routine provides the compare-and-set operator. If the original value at + * equals , then is stored at and the + * function returns 1. + * + * If the original value at does not equal , then the store + * is not done and the function returns 0. + * + * The reading of the original value at , the comparison, + * and the write of the new value (if it occurs) all happen atomically with + * respect to both interrupts and accesses of other processors to . + * + * @param target address to be tested + * @param old_value value to compare against + * @param new_value value to compare against + * @return Returns 1 if is written, 0 otherwise. + */ +FUNC_NO_FP int atomic_cas(atomic_t *target, atomic_val_t old_value, + atomic_val_t new_value) +{ + unsigned int key; + int ret = 0; + + key = irq_lock(); + + if (*target == old_value) { + *target = new_value; + ret = 1; + } + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic addition primitive + * + * This routine provides the atomic addition operator. The is + * atomically added to the value at , placing the result at , + * and the old value from is returned. + * + * @param target memory location to add to + * @param value the value to add + * + * @return The previous value from + */ +FUNC_NO_FP atomic_val_t atomic_add(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target += value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic subtraction primitive + * + * This routine provides the atomic subtraction operator. The is + * atomically subtracted from the value at , placing the result at + * , and the old value from is returned. + * + * @param target the memory location to subtract from + * @param value the value to subtract + * + * @return The previous value from + */ +FUNC_NO_FP atomic_val_t atomic_sub(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target -= value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic increment primitive + * + * @param target memory location to increment + * + * This routine provides the atomic increment operator. The value at + * is atomically incremented by 1, and the old value from is returned. + * + * @return The value from before the increment + */ +FUNC_NO_FP atomic_val_t atomic_inc(atomic_t *target) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + (*target)++; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic decrement primitive + * + * @param target memory location to decrement + * + * This routine provides the atomic decrement operator. The value at + * is atomically decremented by 1, and the old value from is returned. + * + * @return The value from prior to the decrement + */ +FUNC_NO_FP atomic_val_t atomic_dec(atomic_t *target) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + (*target)--; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic get primitive + * + * @param target memory location to read from + * + * This routine provides the atomic get primitive to atomically read + * a value from . It simply does an ordinary load. Note that + * is expected to be aligned to a 4-byte boundary. + * + * @return The value read from + */ +FUNC_NO_FP atomic_val_t atomic_get(const atomic_t *target) +{ + return *target; +} + +/** + * + * @brief Atomic get-and-set primitive + * + * This routine provides the atomic set operator. The is atomically + * written at and the previous value at is returned. + * + * @param target the memory location to write to + * @param value the value to write + * + * @return The previous value from + */ +FUNC_NO_FP atomic_val_t atomic_set(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target = value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic clear primitive + * + * This routine provides the atomic clear operator. The value of 0 is atomically + * written at and the previous value at is returned. (Hence, + * atomic_clear(pAtomicVar) is equivalent to atomic_set(pAtomicVar, 0).) + * + * @param target the memory location to write + * + * @return The previous value from + */ +FUNC_NO_FP atomic_val_t atomic_clear(atomic_t *target) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target = 0; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic bitwise inclusive OR primitive + * + * This routine provides the atomic bitwise inclusive OR operator. The + * is atomically bitwise OR'ed with the value at , placing the result + * at , and the previous value at is returned. + * + * @param target the memory location to be modified + * @param value the value to OR + * + * @return The previous value from + */ +FUNC_NO_FP atomic_val_t atomic_or(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target |= value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic bitwise exclusive OR (XOR) primitive + * + * This routine provides the atomic bitwise exclusive OR operator. The + * is atomically bitwise XOR'ed with the value at , placing the result + * at , and the previous value at is returned. + * + * @param target the memory location to be modified + * @param value the value to XOR + * + * @return The previous value from + */ +FUNC_NO_FP atomic_val_t atomic_xor(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target ^= value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic bitwise AND primitive + * + * This routine provides the atomic bitwise AND operator. The is + * atomically bitwise AND'ed with the value at , placing the result + * at , and the previous value at is returned. + * + * @param target the memory location to be modified + * @param value the value to AND + * + * @return The previous value from + */ +FUNC_NO_FP atomic_val_t atomic_and(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target &= value; + + irq_unlock(key); + + return ret; +} + +/** + * + * @brief Atomic bitwise NAND primitive + * + * This routine provides the atomic bitwise NAND operator. The is + * atomically bitwise NAND'ed with the value at , placing the result + * at , and the previous value at is returned. + * + * @param target the memory location to be modified + * @param value the value to NAND + * + * @return The previous value from + */ +FUNC_NO_FP atomic_val_t atomic_nand(atomic_t *target, atomic_val_t value) +{ + unsigned int key; + atomic_val_t ret; + + key = irq_lock(); + + ret = *target; + *target = ~(*target & value); + + irq_unlock(key); + + return ret; +} diff --git a/system/libarc32_arduino101/drivers/bluetooth/att.h b/system/libarc32_arduino101/drivers/bluetooth/att.h new file mode 100644 index 00000000..5752a079 --- /dev/null +++ b/system/libarc32_arduino101/drivers/bluetooth/att.h @@ -0,0 +1,55 @@ +/** @file + * @brief Attribute Protocol handling. + */ + +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __BT_ATT_H +#define __BT_ATT_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Error codes for Error response PDU */ +#define BT_ATT_ERR_INVALID_HANDLE 0x01 +#define BT_ATT_ERR_READ_NOT_PERMITTED 0x02 +#define BT_ATT_ERR_WRITE_NOT_PERMITTED 0x03 +#define BT_ATT_ERR_INVALID_PDU 0x04 +#define BT_ATT_ERR_AUTHENTICATION 0x05 +#define BT_ATT_ERR_NOT_SUPPORTED 0x06 +#define BT_ATT_ERR_INVALID_OFFSET 0x07 +#define BT_ATT_ERR_AUTHORIZATION 0x08 +#define BT_ATT_ERR_PREPARE_QUEUE_FULL 0x09 +#define BT_ATT_ERR_ATTRIBUTE_NOT_FOUND 0x0a +#define BT_ATT_ERR_ATTRIBUTE_NOT_LONG 0x0b +#define BT_ATT_ERR_ENCRYPTION_KEY_SIZE 0x0c +#define BT_ATT_ERR_INVALID_ATTRIBUTE_LEN 0x0d +#define BT_ATT_ERR_UNLIKELY 0x0e +#define BT_ATT_ERR_INSUFFICIENT_ENCRYPTION 0x0f +#define BT_ATT_ERR_UNSUPPORTED_GROUP_TYPE 0x10 +#define BT_ATT_ERR_INSUFFICIENT_RESOURCES 0x11 + +/* Common Profile Error Codes (from CSS) */ +#define BT_ATT_ERR_CCC_IMPROPER_CONF 0xfd +#define BT_ATT_ERR_PROCEDURE_IN_PROGRESS 0xfe +#define BT_ATT_ERR_OUT_OF_RANGE 0xff + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_ATT_H */ diff --git a/system/libarc32_arduino101/drivers/bluetooth/bluetooth.h b/system/libarc32_arduino101/drivers/bluetooth/bluetooth.h new file mode 100644 index 00000000..e87cf2ab --- /dev/null +++ b/system/libarc32_arduino101/drivers/bluetooth/bluetooth.h @@ -0,0 +1,353 @@ +/** @file + * @brief Bluetooth subsystem core APIs. + */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __BT_BLUETOOTH_H +#define __BT_BLUETOOTH_H + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Callback for notifying that Bluetooth has been enabled. + * + * @param err zero on success or (negative) error code otherwise. + */ +typedef void (*bt_ready_cb_t)(int err); + +/** @brief Enable Bluetooth + * + * Enable Bluetooth. Must be the called before any calls that + * require communication with the local Bluetooth hardware. + * + * @param cb Callback to notify completion or NULL to perform the + * enabling synchronously. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_enable(bt_ready_cb_t cb); + +/* Advertising API */ + +/** Description of different data types that can be encoded into + * advertising data. Used to form arrays that are passed to the + * bt_le_adv_start() function. + */ +struct bt_data { + uint8_t type; + uint8_t data_len; + const uint8_t *data; +}; + +/** @brief Helper to declare elements of bt_data arrays + * + * This macro is mainly for creating an array of struct bt_data + * elements which is then passed to bt_le_adv_start(). + * + * @param _type Type of advertising data field + * @param _data Pointer to the data field payload + * @param _data_len Number of bytes behind the _data pointer + */ +#define BT_DATA(_type, _data, _data_len) \ + { \ + .type = (_type), \ + .data_len = (_data_len), \ + .data = (_data), \ + } + +/** @brief Helper to declare elements of bt_data arrays + * + * This macro is mainly for creating an array of struct bt_data + * elements which is then passed to bt_le_adv_start(). + * + * @param _type Type of advertising data field + * @param _bytes Variable number of single-byte parameters + */ +#define BT_DATA_BYTES(_type, _bytes...) \ + BT_DATA(_type, ((uint8_t []) { _bytes }), \ + sizeof((uint8_t []) { _bytes })) + +/** Local advertising address type */ +enum { + /** Use local identity address for advertising. Unless a static + * random address has been configured this will be the public + * address. + */ + BT_LE_ADV_ADDR_IDENTITY, + + /** Use local Non-resolvable Private Address (NRPA) for advertising */ + BT_LE_ADV_ADDR_NRPA, +}; + +/** LE Advertising Parameters. */ +struct bt_le_adv_param { + /** Advertising type */ + uint8_t type; + + /** Which type of own address to use for advertising */ + uint8_t addr_type; + + /** Minimum Advertising Interval (N * 0.625) */ + uint16_t interval_min; + + /** Maximum Advertising Interval (N * 0.625) */ + uint16_t interval_max; +}; + +/** Helper to declare advertising parameters inline + * + * @param _type Advertising Type + * @param _addr_type Local address type to use for advertising + * @param _int_min Minimum advertising interval + * @param _int_max Maximum advertising interval + */ +#define BT_LE_ADV_PARAM(_type, _addr_type, _int_min, _int_max) \ + (&(struct bt_le_adv_param) { \ + .type = (_type), \ + .addr_type = (_addr_type), \ + .interval_min = (_int_min), \ + .interval_max = (_int_max), \ + }) + +#define BT_LE_ADV(t) BT_LE_ADV_PARAM(t, BT_LE_ADV_ADDR_IDENTITY, \ + BT_GAP_ADV_FAST_INT_MIN_2, \ + BT_GAP_ADV_FAST_INT_MAX_2) + +/** @brief Start advertising + * + * Set advertisement data, scan response data, advertisement parameters + * and start advertising. + * + * @param param Advertising parameters. + * @param ad Data to be used in advertisement packets. + * @param ad_len Number of elements in ad + * @param sd Data to be used in scan response packets. + * @param sd_len Number of elements in sd + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_le_adv_start(const struct bt_le_adv_param *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len); + +/** @brief Stop advertising + * + * Stops ongoing advertising. + * + * @return Zero on success or (negative) error code otherwise. + */ +int bt_le_adv_stop(void); + +/** @brief Define a type allowing user to implement a function that can + * be used to get back active LE scan results. + * + * A function of this type will be called back when user application + * triggers active LE scan. The caller will populate all needed + * parameters based on data coming from scan result. + * Such function can be set by user when LE active scan API is used. + * + * @param addr Advertiser LE address and type. + * @param rssi Strength of advertiser signal. + * @param adv_type Type of advertising response from advertiser. + * @param adv_data Address of buffer containig advertiser data. + * @param len Length of advertiser data contained in buffer. + */ +typedef void bt_le_scan_cb_t(const bt_addr_le_t *addr, int8_t rssi, + uint8_t adv_type, const uint8_t *adv_data, + uint8_t len); + +/** LE scan parameters */ +struct bt_le_scan_param { + /** Scan type (BT_HCI_LE_SCAN_ACTIVE or BT_HCI_LE_SCAN_PASSIVE) */ + uint8_t type; + + /** Duplicate filtering (BT_HCI_LE_SCAN_FILTER_DUP_ENABLE or + * BT_HCI_LE_SCAN_FILTER_DUP_DISABLE) + */ + uint8_t filter_dup; + + /** Scan interval (N * 0.625 ms) */ + uint16_t interval; + + /** Scan window (N * 0.625 ms) */ + uint16_t window; +}; + +/** Helper to declare scan parameters inline + * + * @param _type Scan Type (BT_HCI_LE_SCAN_ACTIVE/BT_HCI_LE_SCAN_PASSIVE) + * @param _filter Filter Duplicates + * @param _interval Scan Interval (N * 0.625 ms) + * @param _window Scan Window (N * 0.625 ms) + */ +#define BT_LE_SCAN_PARAM(_type, _filter, _interval, _window) \ + (&(struct bt_le_scan_param) { \ + .type = (_type), \ + .filter_dup = (_filter), \ + .interval = (_interval), \ + .window = (_window), \ + }) + +/** Helper macro to enable active scanning to discover new devices. */ +#define BT_LE_SCAN_ACTIVE BT_LE_SCAN_PARAM(BT_HCI_LE_SCAN_ACTIVE, \ + BT_HCI_LE_SCAN_FILTER_DUP_ENABLE, \ + BT_GAP_SCAN_FAST_INTERVAL, \ + BT_GAP_SCAN_FAST_WINDOW) + +/** Helper macro to enable passive scanning to discover new devices. + * + * This macro should be used if information required for device identification + * (eg UUID) are known to be placed in Advertising Data. + */ +#define BT_LE_SCAN_PASSIVE BT_LE_SCAN_PARAM(BT_HCI_LE_SCAN_PASSIVE, \ + BT_HCI_LE_SCAN_FILTER_DUP_ENABLE, \ + BT_GAP_SCAN_FAST_INTERVAL, \ + BT_GAP_SCAN_FAST_WINDOW) + +/** @brief Start (LE) scanning + * + * Start LE scanning with and provide results through the specified + * callback. + * + * @param param Scan parameters. + * @param cb Callback to notify scan results. + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +int bt_le_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb); + +/** @brief Stop (LE) scanning. + * + * Stops ongoing LE scanning. + * + * @return Zero on success or error code otherwise, positive in case + * of protocol error or negative (POSIX) in case of stack internal error + */ +int bt_le_scan_stop(void); + +/** @def BT_ADDR_STR_LEN + * + * @brief Recommended length of user string buffer for Bluetooth address + * + * @details The recommended length guarantee the output of address + * conversion will not lose valuable information about address being + * processed. + */ +#define BT_ADDR_STR_LEN 18 + +/** @def BT_ADDR_LE_STR_LEN + * + * @brief Recommended length of user string buffer for Bluetooth LE address + * + * @details The recommended length guarantee the output of address + * conversion will not lose valuable information about address being + * processed. + */ +#define BT_ADDR_LE_STR_LEN 27 + +/** @brief Converts binary Bluetooth address to string. + * + * @param addr Address of buffer containing binary Bluetooth address. + * @param str Address of user buffer with enough room to store formatted + * string containing binary address. + * @param len Length of data to be copied to user string buffer. Refer to + * BT_ADDR_STR_LEN about recommended value. + * + * @return Number of successfully formatted bytes from binary address. + */ +static inline int bt_addr_to_str(const bt_addr_t *addr, char *str, size_t len) +{ + return snprintf(str, len, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + addr->val[5], addr->val[4], addr->val[3], + addr->val[2], addr->val[1], addr->val[0]); +} + +/** @brief Converts binary LE Bluetooth address to string. + * + * @param addr Address of buffer containing binary LE Bluetooth address. + * @param str Address of user buffer with enough room to store + * formatted string containing binary LE address. + * @param len Length of data to be copied to user string buffer. Refer to + * BT_ADDR_LE_STR_LEN about recommended value. + * + * @return Number of successfully formatted bytes from binary address. + */ +static inline int bt_addr_le_to_str(const bt_addr_le_t *addr, char *str, + size_t len) +{ + char type[7]; + + switch (addr->type) { + case BT_ADDR_LE_PUBLIC: + strcpy(type, "public"); + break; + case BT_ADDR_LE_RANDOM: + strcpy(type, "random"); + break; + default: + sprintf(type, "0x%02x", addr->type); + break; + } + + return snprintf(str, len, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X (%s)", + addr->val[5], addr->val[4], addr->val[3], + addr->val[2], addr->val[1], addr->val[0], type); +} + +#if defined(CONFIG_BLUETOOTH_BREDR) +/** @brief Enable/disable set controller in discoverable state. + * + * Allows make local controller to listen on INQUIRY SCAN channel and responds + * to devices making general inquiry. To enable this state it's mandatory + * to first be in connectable state. + * + * @param enable Value allowing/disallowing controller to become discoverable. + * + * @return Negative if fail set to requested state or requested state has been + * already set. Zero if done successfully. + */ +int bt_br_set_discoverable(bool enable); + +/** @brief Enable/disable set controller in connectable state. + * + * Allows make local controller to be connectable. It means the controller + * start listen to devices requests on PAGE SCAN channel. If disabled also + * resets discoverability if was set. + * + * @param enable Value allowing/disallowing controller to be connectable. + * + * @return Negative if fail set to requested state or requested state has been + * already set. Zero if done successfully. + */ +int bt_br_set_connectable(bool enable); +#endif + +void bt_le_set_device_name(char *device_name, int len); + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_BLUETOOTH_H */ diff --git a/system/libarc32_arduino101/drivers/bluetooth/conn.h b/system/libarc32_arduino101/drivers/bluetooth/conn.h new file mode 100644 index 00000000..1b710d6f --- /dev/null +++ b/system/libarc32_arduino101/drivers/bluetooth/conn.h @@ -0,0 +1,399 @@ +/** @file + * @brief Bluetooth connection handling + */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __BT_CONN_H +#define __BT_CONN_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(CONFIG_BLUETOOTH_CENTRAL) || defined(CONFIG_BLUETOOTH_PERIPHERAL) +#include + +#include +#include + +/** Opaque type representing a connection to a remote device */ +struct bt_conn; + +/** Connection parameters for LE connections */ +struct bt_le_conn_param { + uint16_t interval_min; + uint16_t interval_max; + uint16_t latency; + uint16_t timeout; +}; + +/** Helper to declare connection parameters inline + * + * @param int_min Minimum Connection Interval (N * 1.25 ms) + * @param int_max Maximum Connection Interval (N * 1.25 ms) + * @param lat Connection Latency + * @param timeout Supervision Timeout (N * 10 ms) + */ +#define BT_LE_CONN_PARAM(int_min, int_max, lat, to) \ + (&(struct bt_le_conn_param) { \ + .interval_min = (int_min), \ + .interval_max = (int_max), \ + .latency = (lat), \ + .timeout = (to), \ + }) + +/** Default LE connection parameters: + * Connection Interval: 30-50 ms + * Latency: 0 + * Timeout: 4 s + */ +#define BT_LE_CONN_PARAM_DEFAULT BT_LE_CONN_PARAM(BT_GAP_INIT_CONN_INT_MIN, \ + BT_GAP_INIT_CONN_INT_MAX, \ + 0, 400) + +/** @brief Increment a connection's reference count. + * + * Increment the reference count of a connection object. + * + * @param conn Connection object. + * + * @return Connection object with incremented reference count. + */ +struct bt_conn *bt_conn_ref(struct bt_conn *conn); + +/** @brief Decrement a connection's reference count. + * + * Decrement the reference count of a connection object. + * + * @param conn Connection object. + */ +void bt_conn_unref(struct bt_conn *conn); + +/** @brief Look up an existing connection by address. + * + * Look up an existing connection based on the remote address. + * + * @param peer Remote address. + * + * @return Connection object or NULL if not found. The caller gets a + * new reference to the connection object which must be released with + * bt_conn_unref() once done using the object. + */ +struct bt_conn *bt_conn_lookup_addr_le(const bt_addr_le_t *peer); + +/** @brief Get destination (peer) address of a connection. + * + * @param conn Connection object. + * + * @return Destination address. + */ +const bt_addr_le_t *bt_conn_get_dst(const struct bt_conn *conn); + +/** Connection Type */ +enum { + BT_CONN_TYPE_LE, /** LE Connection Type */ +#if defined(CONFIG_BLUETOOTH_BREDR) + BT_CONN_TYPE_BR, /** BR/EDR Connection Type */ +#endif +}; + +/** LE Connection Info Structure */ +struct bt_conn_le_info { + const bt_addr_le_t *src; /** Source Address */ + const bt_addr_le_t *dst; /** Destination Address */ + uint16_t interval; /** Connection interval */ + uint16_t latency; /** Connection slave latency */ + uint16_t timeout; /** Connection supervision timeout */ +}; + +#if defined(CONFIG_BLUETOOTH_BREDR) +/** BR/EDR Connection Info Structure */ +struct bt_conn_br_info { + const bt_addr_t *dst; /** Destination BR/EDR address */ +}; +#endif + +/** Connection role (master or slave) */ +enum { + BT_CONN_ROLE_MASTER, + BT_CONN_ROLE_SLAVE, +}; + +/** Connection Info Structure */ +struct bt_conn_info { + /** Connection Type */ + uint8_t type; + + /** Connection Role */ + uint8_t role; + + union { + /** LE Connection specific Info */ + struct bt_conn_le_info le; +#if defined(CONFIG_BLUETOOTH_BREDR) + struct bt_conn_br_info br; +#endif + }; +}; + +/** @brief Get connection info + * + * @param conn Connection object. + * @param info Connection info object. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info); + +/** @brief Update the connection parameters. + * + * @param conn Connection object. + * @param param Updated connection parameters. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_conn_le_param_update(struct bt_conn *conn, + const struct bt_le_conn_param *param); + +/** @brief Disconnect from a remote device or cancel pending connection. + * + * Disconnect an active connection with the specified reason code or cancel + * pending outgoing connection. + * + * @param conn Connection to disconnect. + * @param reason Reason code for the disconnection. + * + * @return Zero on success or (negative) error code on failure. + */ +int bt_conn_disconnect(struct bt_conn *conn, uint8_t reason); + +#if defined(CONFIG_BLUETOOTH_CENTRAL) +/** @brief Initiate an LE connection to a remote device. + * + * Allows initiate new LE link to remote peer using its address. + * Returns a new reference that the the caller is responsible for managing. + * + * @param peer Remote address. + * @param param Initial connection parameters. + * + * @return Valid connection object on success or NULL otherwise. + */ +struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer, + const struct bt_le_conn_param *param); + +/** @brief Automatically connect to remote device if it's in range. + * + * This function enables/disables automatic connection initiation. + * Everytime the device looses the connection with peer, this connection + * will be re-established if connectable advertisement from peer is received. + * + * @param addr Remote Bluetooth address. + * @param param If non-NULL, auto connect is enabled with the given + * parameters. If NULL, auto connect is disabled. + * + * @return Zero on success or error code otherwise. + */ +int bt_le_set_auto_conn(bt_addr_le_t *addr, + const struct bt_le_conn_param *param); +#endif /* CONFIG_BLUETOOTH_CENTRAL */ + +#if defined(CONFIG_BLUETOOTH_PERIPHERAL) +/** @brief Initiate directed advertising to a remote device + * + * Allows initiating a new LE connection to remote peer with the remote + * acting in central role and the local device in peripheral role. + * + * The advertising type must be either BT_LE_ADV_DIRECT_IND or + * BT_LE_ADV_DIRECT_IND_LOW_DUTY. + * + * In case of high duty cycle this will result in a callback with + * connected() with a new connection or with an error. + * + * The advertising may be cancelled with bt_conn_disconnect(). + * + * Returns a new reference that the the caller is responsible for managing. + * + * @param peer Remote address. + * @param param Directed advertising parameters. + * + * @return Valid connection object on success or NULL otherwise. + */ +struct bt_conn *bt_conn_create_slave_le(const bt_addr_le_t *peer, + const struct bt_le_adv_param *param); +#endif /* CONFIG_BLUETOOTH_PERIPHERAL */ + +/** Security level. */ +typedef enum __packed { + BT_SECURITY_LOW, /** No encryption and no authentication. */ + BT_SECURITY_MEDIUM, /** encryption and no authentication (no MITM). */ + BT_SECURITY_HIGH, /** encryption and authentication (MITM). */ + BT_SECURITY_FIPS, /** Authenticated LE Secure Connections and + * encryption. + */ +} bt_security_t; + +#if defined(CONFIG_BLUETOOTH_SMP) +/** @brief Set security level for a connection. + * + * This function enable security (encryption) for a connection. If device is + * already paired with sufficiently strong key encryption will be enabled. If + * link is already encrypted with sufficiently strong key this function does + * nothing. + * + * If device is not paired pairing will be initiated. If device is paired and + * keys are too weak but input output capabilities allow for strong enough keys + * pairing will be initiated. + * + * This function may return error if required level of security is not possible + * to achieve due to local or remote device limitation (eg input output + * capabilities). + * + * @param conn Connection object. + * @param sec Requested security level. + * + * @return 0 on success or negative error + */ +int bt_conn_security(struct bt_conn *conn, bt_security_t sec); + +/** @brief Get encryption key size. + * + * This function gets encryption key size. + * If there is no security (encryption) enabled 0 will be returned. + * + * @param conn Existing connection object. + * + * @return Encryption key size. + */ +uint8_t bt_conn_enc_key_size(struct bt_conn *conn); + +/** @brief Clear device information (bonding, keys). + * + * Clears all a bonding information (keys, etc). A bonded connection is + * disconnected. + * BT_ADDR_LE_ANY removes the of all bonded devices + * + * @param addr identity address of a bonded device + * + * @return 0 in success, error code otherwise + * + */ +int bt_conn_remove_info(const bt_addr_le_t *addr); +#endif /* CONFIG_BLUETOOTH_SMP */ + +/** Connection callback structure */ +struct bt_conn_cb { + void (*connected)(struct bt_conn *conn, uint8_t err); + void (*disconnected)(struct bt_conn *conn, uint8_t reason); + void (*le_param_updated)(struct bt_conn *conn, uint16_t interval, + uint16_t latency, uint16_t timeout); +#if defined(CONFIG_BLUETOOTH_SMP) + void (*identity_resolved)(struct bt_conn *conn, + const bt_addr_le_t *rpa, + const bt_addr_le_t *identity); + void (*security_changed)(struct bt_conn *conn, bt_security_t level); +#endif + struct bt_conn_cb *_next; +}; + +/** @brief Register connection callbacks. + * + * Register callbacks to monitor the state of connections. + * + * @param cb Callback struct. + */ +void bt_conn_cb_register(struct bt_conn_cb *cb); + +#endif /* CONFIG_BLUETOOTH_CENTRAL || CONFIG_BLUETOOTH_PERIPHERAL */ + +#if defined(CONFIG_BLUETOOTH_SMP) || defined(CONFIG_BLUETOOTH_BREDR) +/** Authenticated pairing callback structure */ +struct bt_conn_auth_cb { + void (*passkey_display)(struct bt_conn *conn, unsigned int passkey); + void (*passkey_entry)(struct bt_conn *conn); + void (*passkey_confirm)(struct bt_conn *conn, unsigned int passkey); + void (*cancel)(struct bt_conn *conn); +#if defined(CONFIG_BLUETOOTH_BREDR) + void (*pincode_entry)(struct bt_conn *conn, bool highsec); +#endif +}; + +/** @brief Register authentication callbacks. + * + * Register callbacks to handle authenticated pairing. Passing NULL unregisters + * previous callbacks structure. + * + * @param cb Callback struct. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_cb_register(const struct bt_conn_auth_cb *cb); + +/** @brief Reply with entered passkey. + * + * This function should be called only after passkey_entry callback from + * bt_conn_auth_cb structure was called. + * + * @param conn Connection object. + * @param passkey Entered passkey. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey); + +/** @brief Cancel ongoing authenticated pairing. + * + * This function allows to cancel ongoing authenticated pairing. + * + * @param conn Connection object. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_cancel(struct bt_conn *conn); + +/** @brief Reply if passkey was confirmed by user. + * + * This function should be called only after passkey_confirm callback from + * bt_conn_auth_cb structure was called. If passkey is confirmed to match + * then match should be true. Otherwise match should be false. + * + * @param conn Connection object. + * @param match True if passkey was confirmed to match, false otherwise. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_passkey_confirm(struct bt_conn *conn, bool match); + +#if defined(CONFIG_BLUETOOTH_BREDR) +/** @brief Reply with entered PIN code. + * + * This function should be called only after PIN code callback from + * bt_conn_auth_cb structure was called. It's for legacy 2.0 devices. + * + * @param conn Connection object. + * @param pin Entered PIN code. + * + * @return Zero on success or negative error code otherwise + */ +int bt_conn_auth_pincode_entry(struct bt_conn *conn, const char *pin); +#endif /* CONFIG_BLUETOOTH_BREDR */ +#endif /* CONFIG_BLUETOOTH_SMP || CONFIG_BLUETOOTH_BREDR */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_CONN_H */ diff --git a/system/libarc32_arduino101/drivers/bluetooth/conn_internal.h b/system/libarc32_arduino101/drivers/bluetooth/conn_internal.h new file mode 100644 index 00000000..04c08839 --- /dev/null +++ b/system/libarc32_arduino101/drivers/bluetooth/conn_internal.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +typedef enum { + BT_CONN_DISCONNECTED, + BT_CONN_CONNECT_SCAN, + BT_CONN_CONNECT, + BT_CONN_CONNECTED, + BT_CONN_DISCONNECT, +} bt_conn_state_t; + +/* bt_conn flags: the flags defined here represent connection parameters */ +enum { + BT_CONN_AUTO_CONNECT, +}; + +struct bt_conn_le { + bt_addr_le_t dst; + +#if 0 + bt_addr_le_t init_addr; + bt_addr_le_t resp_addr; +#endif + uint16_t interval; + uint16_t interval_min; + uint16_t interval_max; + + uint16_t latency; + uint16_t timeout; +#if 0 + uint8_t features[8]; +#endif +}; + +#if defined(CONFIG_BLUETOOTH_BREDR) +struct bt_conn_br { + bt_addr_t dst; +}; +#endif + +struct bt_conn { + uint16_t handle; + uint8_t type; + uint8_t role; + +#if defined(CONFIG_BLUETOOTH_SMP) + uint8_t encrypt; + bt_security_t sec_level; + bt_security_t required_sec_level; +#endif /* CONFIG_BLUETOOTH_SMP */ + + atomic_t ref; + + /* Connection error or reason for disconnect */ + uint8_t err; + + bt_conn_state_t state; + union { + struct bt_conn_le le; +#if defined(CONFIG_BLUETOOTH_BREDR) + struct bt_conn_br br; +#endif + }; +}; + +/* Add a new LE connection */ +struct bt_conn *bt_conn_add_le(const bt_addr_le_t *peer); + +#if defined(CONFIG_BLUETOOTH_BREDR) +/* Add a new BR/EDR connection */ +struct bt_conn *bt_conn_add_br(const bt_addr_t *peer); + +/* Look up an existing connection by BT address */ +struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer); +#endif + +/* Look up an existing connection */ +struct bt_conn *bt_conn_lookup_handle(uint16_t handle); + +/* Look up a connection state. For BT_ADDR_LE_ANY, returns the first connection + * with the specific state + */ +struct bt_conn *bt_conn_lookup_state_le(const bt_addr_le_t *peer, + const bt_conn_state_t state); + +/* Set connection object in certain state and perform action related to state */ +void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state); + +void bt_conn_set_param_le(struct bt_conn *conn, + const struct bt_le_conn_param *param); + +int bt_conn_update_param_le(struct bt_conn *conn, + const struct bt_le_conn_param *param); + +int bt_conn_le_conn_update(struct bt_conn *conn, + const struct bt_le_conn_param *param); + +void notify_le_param_updated(struct bt_conn *conn); + +#if defined(CONFIG_BLUETOOTH_SMP) +/* rand and ediv should be in BT order */ +int bt_conn_le_start_encryption(struct bt_conn *conn, uint64_t rand, + uint16_t ediv, const uint8_t *ltk, size_t len); + +/* Notify higher layers that RPA was resolved */ +void bt_conn_identity_resolved(struct bt_conn *conn); + +/* Notify higher layers that connection security changed */ +void bt_conn_security_changed(struct bt_conn *conn); +#endif /* CONFIG_BLUETOOTH_SMP */ +/* Initialize connection management */ +int bt_conn_init(void); diff --git a/system/libarc32_arduino101/drivers/bluetooth/gatt.h b/system/libarc32_arduino101/drivers/bluetooth/gatt.h new file mode 100644 index 00000000..e9eac613 --- /dev/null +++ b/system/libarc32_arduino101/drivers/bluetooth/gatt.h @@ -0,0 +1,1045 @@ +/** @file + * @brief Generic Attribute Profile handling. + */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __BT_GATT_H +#define __BT_GATT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(CONFIG_BLUETOOTH_CENTRAL) || defined(CONFIG_BLUETOOTH_PERIPHERAL) +#include +#include +#include +#include +#include +#include + +/* GATT attribute permission bit field values */ + +/** @def BT_GATT_PERM_READ + * @brief Attribute read permission. + */ +#define BT_GATT_PERM_READ 0x01 +/** @def BT_GATT_PERM_WRITE + * @brief Attribute write permission. + */ +#define BT_GATT_PERM_WRITE 0x02 +/** @def BT_GATT_PERM_READ_ENCRYPT + * @brief Attribute read permission with encryption. + * + * If set, requires encryption for read access. + */ +#define BT_GATT_PERM_READ_ENCRYPT 0x04 +/** @def BT_GATT_PERM_WRITE_ENCRYPT + * @brief Attribute write permission with encryption. + * + * If set, requires encryption for write access. + */ +#define BT_GATT_PERM_WRITE_ENCRYPT 0x08 +/** @def BT_GATT_PERM_READ_AUTHEN + * @brief Attribute read permission with authentication. + * + * If set, requires encryption using authenticated link-key for read access. + */ +#define BT_GATT_PERM_READ_AUTHEN 0x10 +/** @def BT_GATT_PERM_WRITE_AUTHEN + * @brief Attribute write permission with authentication. + * + * If set, requires encryption using authenticated link-key for write access. + */ +#define BT_GATT_PERM_WRITE_AUTHEN 0x20 +/** @def BT_GATT_PERM_READ_AUTHOR + * @brief Attribute read permission with authorization. + * + * If set, requires authorization for read access. + */ +#define BT_GATT_PERM_READ_AUTHOR 0x40 +/** @def BT_GATT_PERM_WRITE_AUTHOR + * @brief Attribute write permission with authorization. + * + * If set, requires authorization for write access. + */ +#define BT_GATT_PERM_WRITE_AUTHOR 0x80 + +/* GATT attribute flush flags */ +/** @def BT_GATT_FLUSH_DISCARD + * @brief Attribute flush discard flag. + */ +#define BT_GATT_FLUSH_DISCARD 0x00 +/** @def BT_GATT_FLUSH_DISCARD + * @brief Attribute flush synchronize flag. + */ +#define BT_GATT_FLUSH_SYNC 0x01 + +/** @def BT_GATT_ERR + * @brief Construct error return value for attribute read, write and + * flush callbacks. + * + * @param _att_err ATT error code + * + * @return Appropriate error code for the attribute callbacks. + * + */ +#define BT_GATT_ERR(_att_err) (-(_att_err)) + +/** @brief GATT Attribute structure. */ +struct bt_gatt_attr { + /** Attribute UUID */ + const struct bt_uuid *uuid; + + /** Attribute read callback + * + * @param conn The connection that is requesting to read + * @param attr The attribute that's being read + * @param buf Buffer to place the read result in + * @param len Length of data to read + * @param offset Offset to start reading from + * + * @return Number fo bytes read, or in case of an error + * BT_GATT_ERR() with a specific ATT error code. + */ + ssize_t (*read)(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, uint16_t len, + uint16_t offset); + + /** Attribute write callback + * + * @param conn The connection that is requesting to write + * @param attr The attribute that's being read + * @param buf Buffer with the data to write + * @param len Number of bytes in the buffer + * @param offset Offset to start writing from + * + * @return Number of bytes written, or in case of an error + * BT_GATT_ERR() with a specific ATT error code. + */ + ssize_t (*write)(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + const void *buf, uint16_t len, + uint16_t offset); + + /** Attribute flush callback + * + * If this callback is provided (non-NULL) every write + * operation will be followed by a call to it. The expectation + * is for the attribute implementation to only commit the write + * result once this is called. + * + * @param conn The connection that is requesting to write + * @param attr The attribute that's being read + * @param flags Flags (BT_GATT_FLUSH_*) + * + * @return Number of bytes flushed, or in case of an error + * BT_GATT_ERR() with a specific ATT error code. + */ + ssize_t (*flush)(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + uint8_t flags); + + /** Attribute user data */ + void *user_data; + /** Attribute handle */ + uint16_t handle; + /** Attribute permissions */ + uint8_t perm; +#if defined(CONFIG_BLUETOOTH_GATT_DYNAMIC_DB) + struct bt_gatt_attr *_next; +#endif /* CONFIG_BLUETOOTH_GATT_DYNAMIC_DB */ +}; + +/** @brief Service Attribute Value. */ +struct bt_gatt_service { + /** Service UUID. */ + const struct bt_uuid *uuid; + /** Service end handle. */ + uint16_t end_handle; +}; + +/** @brief Include Attribute Value. */ +struct bt_gatt_include { + /** Service UUID. */ + const struct bt_uuid *uuid; + /** Service start handle. */ + uint16_t start_handle; + /** Service end handle. */ + uint16_t end_handle; +}; + +/* Characteristic Properties Bit field values */ + +/** @def BT_GATT_CHRC_BROADCAST + * @brief Characteristic broadcast property. + * + * If set, permits broadcasts of the Characteristic Value using Server + * Characteristic Configuration Descriptor. + */ +#define BT_GATT_CHRC_BROADCAST 0x01 +/** @def BT_GATT_CHRC_READ + * @brief Characteristic read property. + * + * If set, permits reads of the Characteristic Value. + */ +#define BT_GATT_CHRC_READ 0x02 +/** @def BT_GATT_CHRC_WRITE_WITHOUT_RESP + * @brief Characteristic write without response property. + * + * If set, permits write of the Characteristic Value without response. + */ +#define BT_GATT_CHRC_WRITE_WITHOUT_RESP 0x04 +/** @def BT_GATT_CHRC_WRITE + * @brief Characteristic write with response property. + * + * If set, permits write of the Characteristic Value with response. + */ +#define BT_GATT_CHRC_WRITE 0x08 +/** @def BT_GATT_CHRC_NOTIFY + * @brief Characteristic notify property. + * + * If set, permits notifications of a Characteristic Value without + * acknowledgment. + */ +#define BT_GATT_CHRC_NOTIFY 0x10 +/** @def BT_GATT_CHRC_INDICATE + * @brief Characteristic indicate property. + * + * If set, permits indications of a Characteristic Value with acknowledgment. + */ +#define BT_GATT_CHRC_INDICATE 0x20 +/** @def BT_GATT_CHRC_AUTH + * @brief Characteristic Authenticated Signed Writes property. + * + * If set, permits signed writes to the Characteristic Value. + */ +#define BT_GATT_CHRC_AUTH 0x40 +/** @def BT_GATT_CHRC_EXT_PROP + * @brief Characteristic Extended Properties property. + * + * If set, additional characteristic properties are defined in the + * Characteristic Extended Properties Descriptor. + */ +#define BT_GATT_CHRC_EXT_PROP 0x80 + +/** @brief Characteristic Attribute Value. */ +struct bt_gatt_chrc { + /** Characteristic UUID. */ + const struct bt_uuid *uuid; + /** Characteristic properties. */ + uint8_t properties; +}; + +/* Characteristic Extended Properties Bit field values */ +#define BT_GATT_CEP_RELIABLE_WRITE 0x0001 +#define BT_GATT_CEP_WRITABLE_AUX 0x0002 + +/** @brief Characteristic Extended Properties Attribute Value. */ +struct bt_gatt_cep { + /** Characteristic Extended properties */ + uint16_t properties; +}; + +/* Client Characteristic Configuration Values */ + +/** @def BT_GATT_CCC_NOTIFY + * @brief Client Characteristic Configuration Notification. + * + * If set, changes to Characteristic Value shall be notified. + */ +#define BT_GATT_CCC_NOTIFY 0x0001 +/** @def BT_GATT_CCC_INDICATE + * @brief Client Characteristic Configuration Indication. + * + * If set, changes to Characteristic Value shall be indicated. + */ +#define BT_GATT_CCC_INDICATE 0x0002 + +/* Client Characteristic Configuration Attribute Value */ +struct bt_gatt_ccc { + /** Client Characteristic Configuration flags */ + uint16_t flags; +}; + +/** @brief GATT Characteristic Presentation Format Attribute Value. */ +struct bt_gatt_cpf { + /** Format of the value of the characteristic */ + uint8_t format; + /** Exponent field to determine how the value of this characteristic is further formatted */ + int8_t exponent; + /** Unit of the characteristic */ + uint16_t unit; + /** Name space of the description */ + uint8_t name_space; + /** Description of the characteristic as defined in a higher layer profile */ + uint16_t description; +} __packed; + +/* Server API */ + +/** @brief Register attribute database. + * + * Register GATT attribute database table. Applications can make use of + * macros such as BT_GATT_PRIMARY_SERVICE, BT_GATT_CHARACTERISTIC, + * BT_GATT_DESCRIPTOR, etc. + * + * @param attrs Database table containing the available attributes. + * @param count Size of the database table. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_register(struct bt_gatt_attr *attrs, size_t count); + +enum { + BT_GATT_ITER_STOP = 0, + BT_GATT_ITER_CONTINUE, +}; + +/** @brief Attribute iterator callback. + * + * @param attr Attribute found. + * @param user_data Data given. + * + * @return BT_GATT_ITER_CONTINUE if should continue to the next attribute + * or BT_GATT_ITER_STOP to stop. + */ +typedef uint8_t (*bt_gatt_attr_func_t)(const struct bt_gatt_attr *attr, + void *user_data); + +/** @brief Attribute iterator. + * + * Iterate attributes in the given range. + * + * @param start_handle Start handle. + * @param end_handle End handle. + * @param func Callback function. + * @param user_data Data to pass to the callback. + */ +void bt_gatt_foreach_attr(uint16_t start_handle, uint16_t end_handle, + bt_gatt_attr_func_t func, void *user_data); + +/** @brief Iterate to the next attribute + * + * Iterate to the next attribute following a given attribute. + * + * @param attr Current Attribute. + * + * @return The next attribute or NULL if it cannot be found. + */ +struct bt_gatt_attr *bt_gatt_attr_next(const struct bt_gatt_attr *attr); + +/** @brief Generic Read Attribute value helper. + * + * Read attribute value storing the result into buffer. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value. + * @param buf_len Buffer length. + * @param offset Start offset. + * @param value Attribute value. + * @param value_len Length of the attribute value. + * + * @return int number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t buf_len, uint16_t offset, + const void *value, uint16_t value_len); + +/** @brief Read Service Attribute helper. + * + * Read service attribute value storing the result into buffer after + * enconding it. + * NOTE: Only use this with attributes which user_data is a bt_uuid. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value read. + * @param len Buffer length. + * @param offset Start offset. + * + * @return int number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_service(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); + +/** @def BT_GATT_SERVICE + * @brief Generic Service Declaration Macro. + * + * Helper macro to declare a service attribute. + * + * @param _uuid Service attribute type. + * @param _data Service attribute value. + */ +#define BT_GATT_SERVICE(_uuid, _service) \ +{ \ + .uuid = _uuid, \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_service, \ + .user_data = _service, \ +} + +/** @def BT_GATT_PRIMARY_SERVICE + * @brief Primary Service Declaration Macro. + * + * Helper macro to declare a primary service attribute. + * + * @param _service Service attribute value. + */ +#define BT_GATT_PRIMARY_SERVICE(_service) \ +{ \ + .uuid = BT_UUID_GATT_PRIMARY, \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_service, \ + .user_data = _service, \ +} + +/** @def BT_GATT_SECONDARY_SERVICE + * @brief Secondary Service Declaration Macro. + * + * Helper macro to declare a secondary service attribute. + * + * @param _service Service attribute value. + */ +#define BT_GATT_SECONDARY_SERVICE(_service) \ +{ \ + .uuid = BT_UUID_GATT_SECONDARY, \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_service, \ + .user_data = _service, \ +} + +/** @brief Read Include Attribute helper. + * + * Read include service attribute value storing the result into buffer after + * enconding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_include. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value read. + * @param len Buffer length. + * @param offset Start offset. + * + * @return int number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_included(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset); + +/** @def BT_GATT_INCLUDE_SERVICE + * @brief Include Service Declaration Macro. + * + * Helper macro to declare a include service attribute. + * + * @param _service Service attribute value. + */ +#define BT_GATT_INCLUDE_SERVICE(_service) \ +{ \ + .uuid = BT_UUID_GATT_INCLUDE, \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_included, \ + .user_data = _service, \ +} + +/** @brief Read Characteristic Attribute helper. + * + * Read characteristic attribute value storing the result into buffer after + * enconding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_chrc. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value read. + * @param len Buffer length. + * @param offset Start offset. + * + * @return number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_chrc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset); + +/** @def BT_GATT_CHARACTERISTIC + * @brief Characteristic Declaration Macro. + * + * Helper macro to declare a characteristic attribute. + * + * @param _uuid Characteristic attribute uuid. + * @param _props Characteristic attribute properties. + */ +#define BT_GATT_CHARACTERISTIC(_uuid, _props) \ +{ \ + .uuid = BT_UUID_GATT_CHRC, \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_chrc, \ + .user_data = (&(struct bt_gatt_chrc) { .uuid = _uuid, \ + .properties = _props, }),\ +} + +/** @brief GATT CCC configuration entry. */ +struct bt_gatt_ccc_cfg { + /** Config peer address. */ + bt_addr_le_t peer; + /** Config peer value. */ + uint16_t value; + /** Config valid flag. */ + uint8_t valid; +}; + +/* Internal representation of CCC value */ +struct _bt_gatt_ccc { + struct bt_gatt_ccc_cfg *cfg; + size_t cfg_len; + uint16_t value; + void (*cfg_changed)(uint16_t value); +}; + +/** @brief Read Client Characteristic Configuration Attribute helper. + * + * Read CCC attribute value storing the result into buffer after + * enconding it. + * NOTE: Only use this with attributes which user_data is a _bt_gatt_ccc. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value read. + * @param len Buffer length. + * @param offset Start offset. + * + * @return number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_ccc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset); + +/** @brief Write Client Characteristic Configuration Attribute helper. + * + * Write value in the buffer into CCC attribute. + * NOTE: Only use this with attributes which user_data is a _bt_gatt_ccc. + * + * @param conn Connection object. + * @param attr Attribute to read. + * @param buf Buffer to store the value read. + * @param len Buffer length. + * @param offset Start offset. + * + * @return number of bytes written in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, const void *buf, + uint16_t len, uint16_t offset); + +/** @def BT_GATT_CCC + * @brief Client Characteristic Configuration Declaration Macro. + * + * Helper macro to declare a CCC attribute. + * + * @param _cfg Initial configuration. + * @param _cfg_changed Configuration changed callback. + */ +#define BT_GATT_CCC(_cfg, _cfg_changed) \ +{ \ + .uuid = BT_UUID_GATT_CCC, \ + .perm = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, \ + .read = bt_gatt_attr_read_ccc, \ + .write = bt_gatt_attr_write_ccc, \ + .user_data = (&(struct _bt_gatt_ccc) { .cfg = _cfg, \ + .cfg_len = ARRAY_SIZE(_cfg), \ + .cfg_changed = _cfg_changed, }),\ +} + +/** @brief Read Characteristic Extended Properties Attribute helper + * + * Read CEP attribute value storing the result into buffer after + * encoding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_cep. + * + * @param conn Connection object + * @param attr Attribute to read + * @param buf Buffer to store the value read + * @param len Buffer length + * @param offset Start offset + * + * @return number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_cep(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset); + +/** @def BT_GATT_CEP + * @brief Characteristic Extended Properties Declaration Macro. + * + * Helper macro to declare a CEP attribute. + * + * @param _value Descriptor attribute value. + */ +#define BT_GATT_CEP(_value) \ +{ \ + .uuid = BT_UUID_GATT_CEP, \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_cep, \ + .user_data = _value, \ +} + +/** @brief Read Characteristic User Description Descriptor Attribute helper + * + * Read CUD attribute value storing the result into buffer after + * encoding it. + * NOTE: Only use this with attributes which user_data is a NULL-terminated C string. + * + * @param conn Connection object + * @param attr Attribute to read + * @param buf Buffer to store the value read + * @param len Buffer length + * @param offset Start offset + * + * @return number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_cud(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset); + +/** @def BT_GATT_CUD + * @brief Characteristic User Format Descriptor Declaration Macro. + * + * Helper macro to declare a CUD attribute. + * + * @param _value User description NULL-terminated C string. + * @param _perm Descriptor attribute access permissions. + */ +#define BT_GATT_CUD(_value, _perm) \ +{ \ + .uuid = BT_UUID_GATT_CUD, \ + .perm = _perm, \ + .read = bt_gatt_attr_read_cud, \ + .user_data = _value, \ +} + +/** @brief Read Characteristic Presentation format Descriptor Attribute helper + * + * Read CPF attribute value storing the result into buffer after + * encoding it. + * NOTE: Only use this with attributes which user_data is a bt_gatt_pf. + * + * @param conn Connection object + * @param attr Attribute to read + * @param buf Buffer to store the value read + * @param len Buffer length + * @param offset Start offset + * + * @return number of bytes read in case of success or negative values in + * case of error. + */ +ssize_t bt_gatt_attr_read_cpf(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset); + +/** @def BT_GATT_CPF + * @brief Characteristic Presentation Format Descriptor Declaration Macro. + * + * Helper macro to declare a CPF attribute. + * + * @param _value Descriptor attribute value. + */ +#define BT_GATT_CPF(_value) \ +{ \ + .uuid = BT_UUID_GATT_CPF, \ + .perm = BT_GATT_PERM_READ, \ + .read = bt_gatt_attr_read_cpf, \ + .user_data = _value, \ +} + +/** @def BT_GATT_DESCRIPTOR + * @brief Descriptor Declaration Macro. + * + * Helper macro to declare a descriptor attribute. + * + * @param _uuid Descriptor attribute uuid. + * @param _perm Descriptor attribute access permissions. + * @param _read Descriptor attribute read callback. + * @param _write Descriptor attribute write callback. + * @param _value Descriptor attribute value. + */ +#define BT_GATT_DESCRIPTOR(_uuid, _perm, _read, _write, _value) \ +{ \ + .uuid = _uuid, \ + .perm = _perm, \ + .read = _read, \ + .write = _write, \ + .user_data = _value, \ +} + +/** @def BT_GATT_LONG_DESCRIPTOR + * @brief Descriptor Declaration Macro. + * + * Helper macro to declare a descriptor attribute. + * + * @param _uuid Descriptor attribute uuid. + * @param _perm Descriptor attribute access permissions. + * @param _read Descriptor attribute read callback. + * @param _write Descriptor attribute write callback. + * @param _flush Descriptor attribute flush callback. + * @param _value Descriptor attribute value. + */ +#define BT_GATT_LONG_DESCRIPTOR(_uuid, _perm, _read, _write, _flush, _value) \ +{ \ + .uuid = _uuid, \ + .perm = _perm, \ + .read = _read, \ + .write = _write, \ + .flush = _flush, \ + .user_data = _value, \ +} + +/** @brief Notify sent callback + * + * This means that the complete attribute has been sent. This does not mean it + * has been received however (use indicate for this). + * This shall be used to flow control the callee to avoid flooding the ble + * controller. + * + * @param conn Connection object. + * @param attr Attribute object. + * @param err 0 if none + */ +typedef void (*bt_gatt_notify_sent_func_t)(struct bt_conn *conn, struct bt_gatt_attr *attr, + uint8_t err); + +/** @brief Notify attribute value change. + * + * Send notification of attribute value change, if connection is NULL notify + * all peer that have notification enabled via CCC otherwise do a direct + * notification only the given connection. + * + * @param conn Connection object. + * @param attr Attribute object. + * @param value Attribute value. + * @param len Attribute value length. + * @param cb callback function called when send is complete (or NULL) + */ +int bt_gatt_notify(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *data, uint16_t len, + bt_gatt_notify_sent_func_t cb); + +/** @brief Indication complete result callback. + * + * @param conn Connection object. + * @param attr Attribute object. + * @param err: 0 success, error in the other case + */ +typedef void (*bt_gatt_indicate_func_t)(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + int err); + +/** @brief GATT Indicate Value parameters */ +struct bt_gatt_indicate_params { + /** Indicate Attribute object*/ + const struct bt_gatt_attr *attr; + /** Indicate Value callback */ + bt_gatt_indicate_func_t func; + /** Indicate Value data*/ + const void *data; + /** Indicate Value length*/ + uint16_t len; +}; + +/** @brief Indicate attribute value change. + * + * Send an indication of attribute value change. + * Note: This function should only be called if CCC is declared with + * BT_GATT_CCC otherwise it cannot find a valid peer configuration. + * + * Note: This procedure is asynchronous therefore the parameters need to + * remains valid while it is active. + * + * @param conn Connection object. + * @param params Indicate parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_indicate(struct bt_conn *conn, + struct bt_gatt_indicate_params *params); + +#if defined(CONFIG_BLUETOOTH_GATT_CLIENT) +/* Client API */ + +/** @brief Response callback function + * + * @param conn Connection object. + * @param err Error code. + */ +typedef void (*bt_gatt_rsp_func_t)(struct bt_conn *conn, uint8_t err); + +/** @brief Exchange MTU + * + * This client procedure can be used to set the MTU to the maximum possible + * size the buffers can hold. + * NOTE: Shall only be used once per connection. + * + * @param conn Connection object. + * @param func Exchange MTU Response callback function. + */ +int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func); + +struct bt_gatt_discover_params; + +/** @brief Discover attribute callback function. + * + * @param conn Connection object. + * @param attr Attribute found. + * @param params Discovery parameters given. + * + * If discovery procedure has completed this callback will be called with + * attr set to NULL. This will not happen if procedure was stopped by returning + * BT_GATT_ITER_STOP. + * + * @return BT_GATT_ITER_CONTINUE if should continue attribute discovery + * or BT_GATT_ITER_STOP to stop discovery procedure. + */ +typedef uint8_t (*bt_gatt_discover_func_t)(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + struct bt_gatt_discover_params *params); + +enum { + BT_GATT_DISCOVER_PRIMARY, + BT_GATT_DISCOVER_SECONDARY, + BT_GATT_DISCOVER_INCLUDE, + BT_GATT_DISCOVER_CHARACTERISTIC, + BT_GATT_DISCOVER_DESCRIPTOR, +}; + +/** @brief GATT Discover Attributes parameters */ +struct bt_gatt_discover_params { + /** Discover UUID type */ + struct bt_uuid *uuid; + /** Discover attribute callback */ + bt_gatt_discover_func_t func; + /** Discover start handle */ + uint16_t start_handle; + /** Discover end handle */ + uint16_t end_handle; + /** Discover type */ + uint8_t type; +}; + +/** @brief GATT Discover function + * + * This procedure is used by a client to discover attributes on a server. + * + * Primary Service Discovery: Procedure allows to discover specific Primary + * Service based on UUID. + * Include Service Discovery: Procedure allows to discover all Include Services + * within specified range. + * Characteristic Discovery: Procedure allows to discover all characteristics + * within specified handle range as well as + * discover characteristics with specified UUID. + * Descriptors Discovery: Procedure allows to discover all characteristic + * descriptors within specified range. + * + * For each attribute found the callback is called which can then decide + * whether to continue discovering or stop. + * + * Note: This procedure is asynchronous therefore the parameters need to + * remains valid while it is active. + * + * @param conn Connection object. + * @param params Discover parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_discover(struct bt_conn *conn, + struct bt_gatt_discover_params *params); + +struct bt_gatt_read_params; + +/** @brief Read callback function + * + * @param conn Connection object. + * @param err Error code. + * @param params Read parameters used. + * @param data Attribute value data. NULL means read has completed. + * @param length Attribute value length. + */ +typedef uint8_t (*bt_gatt_read_func_t)(struct bt_conn *conn, int err, + struct bt_gatt_read_params *params, + const void *data, uint16_t length); + +/** @brief GATT Read parameters */ +struct bt_gatt_read_params { + /** Read attribute callback */ + bt_gatt_read_func_t func; + /** Handles count. + * If equals to 1 single.handle and single.offset are used. + * If >1 Read Multiple Characteristic Values is performed and handles + * are used. + */ + size_t handle_count; + union { + struct { + /** Attribute handle */ + uint16_t handle; + /** Attribute data offset */ + uint16_t offset; + } single; + /** Handles to read in Read Multiple Characteristic Values */ + uint16_t *handles; + }; +}; + +/** @brief Read Attribute Value by handle + * + * This procedure read the attribute value and return it to the callback. + * + * Note: This procedure is asynchronous therefore the parameters need to + * remains valid while it is active. + * + * @param conn Connection object. + * @param params Read parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params); + +/** @brief Write Response callback function + * + * @param conn Connection object. + * @param err Error code. + * @param data Data pointer in the write request. + */ +typedef void (*bt_gatt_write_rsp_func_t)(struct bt_conn *conn, uint8_t err, const void *data); + +/** @brief Write Attribute Value by handle + * + * This procedure write the attribute value and return the result in the + * callback. + * + * @param conn Connection object. + * @param handle Attribute handle. + * @param offset Attribute data offset. + * @param data Data to be written. + * @param length Data length. + * @param func Callback function. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_write(struct bt_conn *conn, uint16_t handle, uint16_t offset, + const void *data, uint16_t length, bt_gatt_write_rsp_func_t func); + +/** @brief Write Attribute Value by handle without response + * + * This procedure write the attribute value without requiring an + * acknowledgement that the write was successfully performed + * + * @param conn Connection object. + * @param handle Attribute handle. + * @param data Data to be written. + * @param length Data length. + * @param sign Whether to sign data + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_write_without_response(struct bt_conn *conn, uint16_t handle, + const void *data, uint16_t length, + bool sign); + +struct bt_gatt_subscribe_params; + +/** @brief Notification callback function + * + * @param conn Connection object. + * @param params Subscription parameters. + * @param data Attribute value data. If NULL then subscription was removed. + * @param length Attribute value length. + */ +typedef uint8_t (*bt_gatt_notify_func_t)(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params, + const void *data, uint16_t length); + +/** @brief GATT Subscribe parameters */ +struct bt_gatt_subscribe_params { + bt_addr_le_t _peer; + /** Notification value callback */ + bt_gatt_notify_func_t notify; + /** Subscribe value handle */ + uint16_t value_handle; + /** Subscribe CCC handle */ + uint16_t ccc_handle; + /** Subscribe value */ + uint16_t value; + struct bt_gatt_subscribe_params *_next; +}; + +/** @brief Subscribe Attribute Value Notification + * + * This procedure subscribe to value notification using the Client + * Characteristic Configuration handle. + * If notification received subscribe value callback is called to return + * notified value. One may then decide whether to unsubscribe directly from + * this callback. Notification callback with NULL data will not be called if + * subscription was removed by this method. + * + * Note: This procedure is asynchronous therefore the parameters need to + * remains valid while it is active. + * + * @param conn Connection object. + * @param params Subscribe parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_subscribe(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params); + +/** @brief Unsubscribe Attribute Value Notification + * + * This procedure unsubscribe to value notification using the Client + * Characteristic Configuration handle. Notification callback with NULL data + * will not be called if subscription was removed by this call. + * + * @param conn Connection object. + * @param params Subscribe parameters. + * + * @return 0 in case of success or negative value in case of error. + */ +int bt_gatt_unsubscribe(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params); + +/** @brief Cancel GATT pending request + * + * @param conn Connection object. + */ +void bt_gatt_cancel(struct bt_conn *conn); + +#endif /* CONFIG_BLUETOOTH_GATT_CLIENT */ +#endif /* CONFIG_BLUETOOTH_CENTRAL || CONFIG_BLUETOOTH_PERIPHERAL */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_GATT_H */ diff --git a/system/libarc32_arduino101/drivers/bluetooth/hci.h b/system/libarc32_arduino101/drivers/bluetooth/hci.h new file mode 100644 index 00000000..f7ff52c1 --- /dev/null +++ b/system/libarc32_arduino101/drivers/bluetooth/hci.h @@ -0,0 +1,683 @@ +/* hci.h - Bluetooth Host Control Interface definitions */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __BT_HCI_H +#define __BT_HCI_H + +//#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BT_ADDR_LE_PUBLIC 0x00 +#define BT_ADDR_LE_RANDOM 0x01 + +typedef struct { + uint8_t val[6]; +} bt_addr_t; + +typedef struct { + uint8_t type; + uint8_t val[6]; +} bt_addr_le_t; + +#define BT_ADDR_ANY (&(bt_addr_t) {{0, 0, 0, 0, 0, 0} }) +#define BT_ADDR_LE_ANY (&(bt_addr_le_t) { 0, {0, 0, 0, 0, 0, 0} }) + +static inline int bt_addr_cmp(const bt_addr_t *a, const bt_addr_t *b) +{ + return memcmp(a, b, sizeof(*a)); +} + +static inline int bt_addr_le_cmp(const bt_addr_le_t *a, const bt_addr_le_t *b) +{ + return memcmp(a, b, sizeof(*a)); +} + +static inline void bt_addr_copy(bt_addr_t *dst, const bt_addr_t *src) +{ + memcpy(dst, src, sizeof(*dst)); +} + +static inline void bt_addr_le_copy(bt_addr_le_t *dst, const bt_addr_le_t *src) +{ + memcpy(dst, src, sizeof(*dst)); +} + +/* HCI Error Codes */ +#define BT_HCI_ERR_UNKNOWN_CONN_ID 0x02 +#define BT_HCI_ERR_AUTHENTICATION_FAIL 0x05 +#define BT_HCI_ERR_INSUFFICIENT_RESOURCES 0x0d +#define BT_HCI_ERR_REMOTE_USER_TERM_CONN 0x13 +#define BT_HCI_ERR_PAIRING_NOT_ALLOWED 0x18 +#define BT_HCI_ERR_UNSUPP_REMOTE_FEATURE 0x1a +#define BT_HCI_ERR_INVALID_LL_PARAMS 0x1e +#define BT_HCI_ERR_UNSPECIFIED 0x1f +#define BT_HCI_ERR_PAIRING_NOT_SUPPORTED 0x29 +#define BT_HCI_ERR_UNACCEPT_CONN_PARAMS 0x3b +#define BT_HCI_ERR_DIRECTED_ADV_TIMEOUT 0x3c + +/* EIR/AD data type definitions */ +#define BT_DATA_FLAGS 0x01 /* AD flags */ +#define BT_DATA_UUID16_SOME 0x02 /* 16-bit UUID, more available */ +#define BT_DATA_UUID16_ALL 0x03 /* 16-bit UUID, all listed */ +#define BT_DATA_UUID32_SOME 0x04 /* 32-bit UUID, more available */ +#define BT_DATA_UUID32_ALL 0x05 /* 32-bit UUID, all listed */ +#define BT_DATA_UUID128_SOME 0x06 /* 128-bit UUID, more available */ +#define BT_DATA_UUID128_ALL 0x07 /* 128-bit UUID, all listed */ +#define BT_DATA_NAME_SHORTENED 0x08 /* Shortened name */ +#define BT_DATA_NAME_COMPLETE 0x09 /* Complete name */ +#define BT_DATA_TX_POWER 0x0a /* Tx Power */ +#define BT_DATA_SOLICIT16 0x14 /* Solicit UUIDs, 16-bit */ +#define BT_DATA_SOLICIT128 0x15 /* Solicit UUIDs, 128-bit */ +#define BT_DATA_SVC_DATA16 0x16 /* Service data, 16-bit UUID */ +#define BT_DATA_GAP_APPEARANCE 0x19 /* GAP appearance */ +#define BT_DATA_SOLICIT32 0x1f /* Solicit UUIDs, 32-bit */ +#define BT_DATA_SVC_DATA32 0x20 /* Service data, 32-bit UUID */ +#define BT_DATA_SVC_DATA128 0x21 /* Service data, 128-bit UUID */ +#define BT_DATA_MANUFACTURER_DATA 0xff /* Manufacturer Specific Data */ + +#define BT_LE_AD_LIMITED 0x01 /* Limited Discoverable */ +#define BT_LE_AD_GENERAL 0x02 /* General Discoverable */ +#define BT_LE_AD_NO_BREDR 0x04 /* BR/EDR not supported */ + +struct bt_hci_evt_hdr { + uint8_t evt; + uint8_t len; +} __packed; + +#define BT_ACL_START_NO_FLUSH 0x00 +#define BT_ACL_CONT 0x01 +#define BT_ACL_START 0x02 + +#define bt_acl_handle(h) ((h) & 0x0fff) +#define bt_acl_flags(h) ((h) >> 12) +#define bt_acl_handle_pack(h, f) ((h) | ((f) << 12)) + +struct bt_hci_acl_hdr { + uint16_t handle; + uint16_t len; +} __packed; + +struct bt_hci_cmd_hdr { + uint16_t opcode; + uint8_t param_len; +} __packed; + +/* LMP features */ +#define BT_LMP_NO_BREDR 0x20 +#define BT_LMP_LE 0x40 + +/* LE features */ +#define BT_HCI_LE_ENCRYPTION 0x01 +#define BT_HCI_LE_CONN_PARAM_REQ_PROC 0x02 +#define BT_HCI_LE_SLAVE_FEATURES 0x08 + +/* Bonding/authentication types */ +#define BT_HCI_NO_BONDING 0x00 +#define BT_HCI_NO_BONDING_MITM 0x01 +#define BT_HCI_DEDICATED_BONDING 0x02 +#define BT_HCI_DEDICATED_BONDING_MITM 0x03 +#define BT_HCI_GENERAL_BONDING 0x04 +#define BT_HCI_GENERAL_BONDING_MITM 0x05 + +/* I/O capabilities */ +#define BT_IO_DISPLAY_ONLY 0x00 +#define BT_IO_DISPLAY_YESNO 0x01 +#define BT_IO_KEYBOARD_ONLY 0x02 +#define BT_IO_NO_INPUT_OUTPUT 0x03 + +/* Defined GAP timers */ +#define BT_GAP_SCAN_FAST_INTERVAL 0x0060 /* 60 ms */ +#define BT_GAP_SCAN_FAST_WINDOW 0x0030 /* 30 ms */ +#define BT_GAP_SCAN_SLOW_INTERVAL_1 0x0800 /* 1.28 s */ +#define BT_GAP_SCAN_SLOW_WINDOW_1 0x0012 /* 11.25 ms */ +#define BT_GAP_SCAN_SLOW_INTERVAL_2 0x1000 /* 2.56 s */ +#define BT_GAP_SCAN_SLOW_WINDOW_2 0x0012 /* 11.25 ms */ +#define BT_GAP_ADV_FAST_INT_MIN_1 0x0030 /* 30 ms */ +#define BT_GAP_ADV_FAST_INT_MAX_1 0x0060 /* 60 ms */ +#define BT_GAP_ADV_FAST_INT_MIN_2 0x00a0 /* 100 ms */ +#define BT_GAP_ADV_FAST_INT_MAX_2 0x00f0 /* 150 ms */ +#define BT_GAP_ADV_SLOW_INT_MIN 0x0640 /* 1 s */ +#define BT_GAP_ADV_SLOW_INT_MAX 0x0780 /* 1.2 s */ +#define BT_GAP_INIT_CONN_INT_MIN 0x0018 /* 30 ms */ +#define BT_GAP_INIT_CONN_INT_MAX 0x0028 /* 50 ms */ + +/* HCI BR/EDR link types */ +#define BT_HCI_SCO 0x00 +#define BT_HCI_ACL 0x01 +#define BT_HCI_ESCO 0x02 + +/* OpCode Group Fields */ +#define BT_OGF_LINK_CTRL 0x01 +#define BT_OGF_BASEBAND 0x03 +#define BT_OGF_INFO 0x04 +#define BT_OGF_LE 0x08 + +/* Construct OpCode from OGF and OCF */ +#define BT_OP(ogf, ocf) ((ocf) | ((ogf) << 10)) + +#define BT_HCI_OP_DISCONNECT BT_OP(BT_OGF_LINK_CTRL, 0x0006) +struct bt_hci_cp_disconnect { + uint16_t handle; + uint8_t reason; +} __packed; + +#define BT_HCI_OP_ACCEPT_CONN_REQ BT_OP(BT_OGF_LINK_CTRL, 0x0009) +struct bt_hci_cp_accept_conn_req { + bt_addr_t bdaddr; + uint8_t role; +} __packed; + +#define BT_HCI_OP_REJECT_CONN_REQ BT_OP(BT_OGF_LINK_CTRL, 0x000a) +struct bt_hci_cp_reject_conn_req { + bt_addr_t bdaddr; + uint8_t reason; +} __packed; + +#define BT_HCI_OP_LINK_KEY_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000b) +struct bt_hci_cp_link_key_reply { + bt_addr_t bdaddr; + uint8_t link_key[16]; +} __packed; + +#define BT_HCI_OP_LINK_KEY_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000c) +struct bt_hci_cp_link_key_neg_reply { + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_PIN_CODE_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000d) +struct bt_hci_cp_pin_code_reply { + bt_addr_t bdaddr; + uint8_t pin_len; + uint8_t pin_code[16]; +} __packed; +struct bt_hci_rp_pin_code_reply { + uint8_t status; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_PIN_CODE_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x000e) +struct bt_hci_cp_pin_code_neg_reply { + bt_addr_t bdaddr; +} __packed; +struct bt_hci_rp_pin_code_neg_reply { + uint8_t status; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_IO_CAPABILITY_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x002b) +struct bt_hci_cp_io_capability_reply { + bt_addr_t bdaddr; + uint8_t capability; + uint8_t oob_data; + uint8_t authentication; +} __packed; + +#define BT_HCI_OP_IO_CAPABILITY_NEG_REPLY BT_OP(BT_OGF_LINK_CTRL, 0x0034) +struct bt_hci_cp_io_capability_neg_reply { + bt_addr_t bdaddr; + uint8_t reason; +} __packed; + +#define BT_HCI_OP_SET_EVENT_MASK BT_OP(BT_OGF_BASEBAND, 0x0001) +struct bt_hci_cp_set_event_mask { + uint8_t events[8]; +} __packed; + +#define BT_HCI_OP_RESET BT_OP(BT_OGF_BASEBAND, 0x0003) + +#define BT_HCI_OP_WRITE_SCAN_ENABLE BT_OP(BT_OGF_BASEBAND, 0x001a) +#define BT_BREDR_SCAN_DISABLED 0x00 +#define BT_BREDR_SCAN_INQUIRY 0x01 +#define BT_BREDR_SCAN_PAGE 0x02 + +#define BT_HCI_CTL_TO_HOST_FLOW_ENABLE 0x01 +#define BT_HCI_OP_SET_CTL_TO_HOST_FLOW BT_OP(BT_OGF_BASEBAND, 0x0031) + +#define BT_HCI_OP_HOST_BUFFER_SIZE BT_OP(BT_OGF_BASEBAND, 0x0033) +struct bt_hci_cp_host_buffer_size { + uint16_t acl_mtu; + uint8_t sco_mtu; + uint16_t acl_pkts; + uint16_t sco_pkts; +} __packed; + +struct bt_hci_handle_count { + uint16_t handle; + uint16_t count; +} __packed; + +#define BT_HCI_OP_HOST_NUM_COMPLETED_PACKETS BT_OP(BT_OGF_BASEBAND, 0x0035) +struct bt_hci_cp_host_num_completed_packets { + uint8_t num_handles; + struct bt_hci_handle_count h[0]; +} __packed; + +#define BT_HCI_OP_WRITE_SSP_MODE BT_OP(BT_OGF_BASEBAND, 0x0056) +struct bt_hci_cp_write_ssp_mode { + uint8_t mode; +} __packed; + +#define BT_HCI_OP_LE_WRITE_LE_HOST_SUPP BT_OP(BT_OGF_BASEBAND, 0x006d) +struct bt_hci_cp_write_le_host_supp { + uint8_t le; + uint8_t simul; +} __packed; + +#define BT_HCI_OP_READ_LOCAL_VERSION_INFO BT_OP(BT_OGF_INFO, 0x0001) +struct bt_hci_rp_read_local_version_info { + uint8_t status; + uint8_t hci_version; + uint16_t hci_revision; + uint8_t lmp_version; + uint16_t manufacturer; + uint16_t lmp_subversion; +} __packed; + +#define BT_HCI_OP_READ_SUPPORTED_COMMANDS BT_OP(BT_OGF_INFO, 0x0002) +struct bt_hci_rp_read_supported_commands { + uint8_t status; + uint8_t commands[36]; +} __packed; + +#define BT_HCI_OP_READ_LOCAL_FEATURES BT_OP(BT_OGF_INFO, 0x0003) +struct bt_hci_rp_read_local_features { + uint8_t status; + uint8_t features[8]; +} __packed; + +#define BT_HCI_OP_READ_BUFFER_SIZE BT_OP(BT_OGF_INFO, 0x0005) +struct bt_hci_rp_read_buffer_size { + uint8_t status; + uint16_t acl_max_len; + uint8_t sco_max_len; + uint16_t acl_max_num; + uint16_t sco_max_num; +} __packed; + +#define BT_HCI_OP_READ_BD_ADDR BT_OP(BT_OGF_INFO, 0x0009) +struct bt_hci_rp_read_bd_addr { + uint8_t status; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_OP_LE_SET_EVENT_MASK BT_OP(BT_OGF_LE, 0x0001) +struct bt_hci_cp_le_set_event_mask { + uint8_t events[8]; +} __packed; +struct bt_hci_rp_le_set_event_mask { + uint8_t status; +} __packed; + +#define BT_HCI_OP_LE_READ_BUFFER_SIZE BT_OP(BT_OGF_LE, 0x0002) +struct bt_hci_rp_le_read_buffer_size { + uint8_t status; + uint16_t le_max_len; + uint8_t le_max_num; +} __packed; + +#define BT_HCI_OP_LE_READ_LOCAL_FEATURES BT_OP(BT_OGF_LE, 0x0003) +struct bt_hci_rp_le_read_local_features { + uint8_t status; + uint8_t features[8]; +} __packed; + +#define BT_HCI_OP_LE_SET_RANDOM_ADDRESS BT_OP(BT_OGF_LE, 0x0005) + +/* Advertising types */ +#define BT_LE_ADV_IND 0x00 +#define BT_LE_ADV_DIRECT_IND 0x01 +#define BT_LE_ADV_SCAN_IND 0x02 +#define BT_LE_ADV_NONCONN_IND 0x03 +#define BT_LE_ADV_DIRECT_IND_LOW_DUTY 0x04 +/* Needed in advertising reports when getting info about */ +#define BT_LE_ADV_SCAN_RSP 0x04 + +#define BT_HCI_OP_LE_SET_ADV_PARAMETERS BT_OP(BT_OGF_LE, 0x0006) +struct bt_hci_cp_le_set_adv_parameters { + uint16_t min_interval; + uint16_t max_interval; + uint8_t type; + uint8_t own_addr_type; + bt_addr_le_t direct_addr; + uint8_t channel_map; + uint8_t filter_policy; +} __packed; + +#define BT_HCI_OP_LE_SET_ADV_DATA BT_OP(BT_OGF_LE, 0x0008) +struct bt_hci_cp_le_set_adv_data { + uint8_t len; + uint8_t data[31]; +} __packed; + +#define BT_HCI_OP_LE_SET_SCAN_RSP_DATA BT_OP(BT_OGF_LE, 0x0009) +struct bt_hci_cp_le_set_scan_rsp_data { + uint8_t len; + uint8_t data[31]; +} __packed; + +#define BT_HCI_LE_ADV_DISABLE 0x00 +#define BT_HCI_LE_ADV_ENABLE 0x01 + +#define BT_HCI_OP_LE_SET_ADV_ENABLE BT_OP(BT_OGF_LE, 0x000a) +struct bt_hci_cp_le_set_adv_enable { + uint8_t enable; +} __packed; + +/* Scan types */ +#define BT_HCI_OP_LE_SET_SCAN_PARAMS BT_OP(BT_OGF_LE, 0x000b) +#define BT_HCI_LE_SCAN_PASSIVE 0x00 +#define BT_HCI_LE_SCAN_ACTIVE 0x01 + +struct bt_hci_cp_le_set_scan_params { + uint8_t scan_type; + uint16_t interval; + uint16_t window; + uint8_t addr_type; + uint8_t filter_policy; +} __packed; + +#define BT_HCI_OP_LE_SET_SCAN_ENABLE BT_OP(BT_OGF_LE, 0x000c) + +#define BT_HCI_LE_SCAN_DISABLE 0x00 +#define BT_HCI_LE_SCAN_ENABLE 0x01 + +#define BT_HCI_LE_SCAN_FILTER_DUP_DISABLE 0x00 +#define BT_HCI_LE_SCAN_FILTER_DUP_ENABLE 0x01 + +struct bt_hci_cp_le_set_scan_enable { + uint8_t enable; + uint8_t filter_dup; +} __packed; + +#define BT_HCI_OP_LE_CREATE_CONN BT_OP(BT_OGF_LE, 0x000d) +struct bt_hci_cp_le_create_conn { + uint16_t scan_interval; + uint16_t scan_window; + uint8_t filter_policy; + bt_addr_le_t peer_addr; + uint8_t own_addr_type; + uint16_t conn_interval_min; + uint16_t conn_interval_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +} __packed; + +#define BT_HCI_OP_LE_CREATE_CONN_CANCEL BT_OP(BT_OGF_LE, 0x000e) + +#define BT_HCI_OP_LE_CONN_UPDATE BT_OP(BT_OGF_LE, 0x0013) +struct hci_cp_le_conn_update { + uint16_t handle; + uint16_t conn_interval_min; + uint16_t conn_interval_max; + uint16_t conn_latency; + uint16_t supervision_timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +} __packed; + +#define BT_HCI_OP_LE_READ_REMOTE_FEATURES BT_OP(BT_OGF_LE, 0x0016) +struct bt_hci_cp_le_read_remote_features { + uint16_t handle; +} __packed; + +#define BT_HCI_OP_LE_ENCRYPT BT_OP(BT_OGF_LE, 0x0017) +struct bt_hci_cp_le_encrypt { + uint8_t key[16]; + uint8_t plaintext[16]; +} __packed; +struct bt_hci_rp_le_encrypt { + uint8_t status; + uint8_t enc_data[16]; +} __packed; + +#define BT_HCI_OP_LE_RAND BT_OP(BT_OGF_LE, 0x0018) +struct bt_hci_rp_le_rand { + uint8_t status; + uint8_t rand[8]; +} __packed; + +#define BT_HCI_OP_LE_START_ENCRYPTION BT_OP(BT_OGF_LE, 0x0019) +struct bt_hci_cp_le_start_encryption { + uint16_t handle; + uint64_t rand; + uint16_t ediv; + uint8_t ltk[16]; +} __packed; + +#define BT_HCI_OP_LE_LTK_REQ_REPLY BT_OP(BT_OGF_LE, 0x001a) +struct bt_hci_cp_le_ltk_req_reply { + uint16_t handle; + uint8_t ltk[16]; +} __packed; + +#define BT_HCI_OP_LE_LTK_REQ_NEG_REPLY BT_OP(BT_OGF_LE, 0x001b) +struct bt_hci_cp_le_ltk_req_neg_reply { + uint16_t handle; +} __packed; + +#define BT_HCI_OP_LE_CONN_PARAM_REQ_REPLY BT_OP(BT_OGF_LE, 0x0020) +struct bt_hci_cp_le_conn_param_req_reply { + uint16_t handle; + uint16_t interval_min; + uint16_t interval_max; + uint16_t latency; + uint16_t timeout; + uint16_t min_ce_len; + uint16_t max_ce_len; +} __packed; + +#define BT_HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY BT_OP(BT_OGF_LE, 0x0021) +struct bt_hci_cp_le_conn_param_req_neg_reply { + uint16_t handle; + uint8_t reason; +} __packed; + +#define BT_HCI_OP_LE_P256_PUBLIC_KEY BT_OP(BT_OGF_LE, 0x0025) + +#define BT_HCI_OP_LE_GENERATE_DHKEY BT_OP(BT_OGF_LE, 0x0026) +struct bt_hci_cp_le_generate_dhkey { + uint8_t key[64]; +} __packed; + +/* Event definitions */ + +#define BT_HCI_EVT_CONN_COMPLETE 0x03 +struct bt_hci_evt_conn_complete { + uint8_t status; + uint16_t handle; + bt_addr_t bdaddr; + uint8_t link_type; + uint8_t encr_enabled; +} __packed; + +#define BT_HCI_EVT_CONN_REQUEST 0x04 +struct bt_hci_evt_conn_request { + bt_addr_t bdaddr; + uint8_t dev_class[3]; + uint8_t link_type; +} __packed; + +#define BT_HCI_EVT_DISCONN_COMPLETE 0x05 +struct bt_hci_evt_disconn_complete { + uint8_t status; + uint16_t handle; + uint8_t reason; +} __packed; + +#define BT_HCI_EVT_ENCRYPT_CHANGE 0x08 +struct bt_hci_evt_encrypt_change { + uint8_t status; + uint16_t handle; + uint8_t encrypt; +} __packed; + +#define BT_HCI_EVT_CMD_COMPLETE 0x0e +struct hci_evt_cmd_complete { + uint8_t ncmd; + uint16_t opcode; +} __packed; + +#define BT_HCI_EVT_CMD_STATUS 0x0f +struct bt_hci_evt_cmd_status { + uint8_t status; + uint8_t ncmd; + uint16_t opcode; +} __packed; + +#define BT_HCI_EVT_NUM_COMPLETED_PACKETS 0x13 +struct bt_hci_evt_num_completed_packets { + uint8_t num_handles; + struct bt_hci_handle_count h[0]; +} __packed; + +#define BT_HCI_EVT_PIN_CODE_REQ 0x16 +struct bt_hci_evt_pin_code_req { + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_EVT_LINK_KEY_REQ 0x17 +struct bt_hci_evt_link_key_req { + bt_addr_t bdaddr; +} __packed; + +/* Link Key types */ +#define BT_LK_COMBINATION 0x00 +#define BT_LK_LOCAL_UNIT 0x01 +#define BT_LK_REMOTE_UNIT 0x02 +#define BT_LK_DEBUG_COMBINATION 0x03 +#define BT_LK_UNAUTH_COMBINATION_P192 0x04 +#define BT_LK_AUTH_COMBINATION_P192 0x05 +#define BT_LK_CHANGED_COMBINATION 0x06 +#define BT_LK_UNAUTH_COMBINATION_P256 0x07 +#define BT_LK_AUTH_COMBINATION_P256 0x08 + +#define BT_HCI_EVT_LINK_KEY_NOTIFY 0x18 +struct bt_hci_ev_link_key_notify { + bt_addr_t bdaddr; + uint8_t link_key[16]; + uint8_t key_type; +} __packed; + +#define BT_HCI_EVT_ENCRYPT_KEY_REFRESH_COMPLETE 0x30 +struct bt_hci_evt_encrypt_key_refresh_complete { + uint8_t status; + uint16_t handle; +} __packed; + +#define BT_HCI_EVT_IO_CAPA_REQ 0x31 +struct bt_hci_evt_io_capa_req { + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_EVT_IO_CAPA_RESP 0x32 +struct bt_hci_evt_io_capa_resp { + bt_addr_t bdaddr; + uint8_t capability; + uint8_t oob_data; + uint8_t authentication; +} __packed; + +#define BT_HCI_EVT_SSP_COMPLETE 0x36 +struct bt_hci_evt_ssp_complete { + uint8_t status; + bt_addr_t bdaddr; +} __packed; + +#define BT_HCI_EVT_LE_META_EVENT 0x3e +struct bt_hci_evt_le_meta_event { + uint8_t subevent; +} __packed; + +#define BT_HCI_ROLE_MASTER 0x00 +#define BT_HCI_ROLE_SLAVE 0x01 + +#define BT_HCI_EVT_LE_CONN_COMPLETE 0x01 +struct bt_hci_evt_le_conn_complete { + uint8_t status; + uint16_t handle; + uint8_t role; + bt_addr_le_t peer_addr; + uint16_t interval; + uint16_t latency; + uint16_t supv_timeout; + uint8_t clock_accuracy; +} __packed; + +#define BT_HCI_EVT_LE_ADVERTISING_REPORT 0x02 +struct bt_hci_ev_le_advertising_info { + uint8_t evt_type; + bt_addr_le_t addr; + uint8_t length; + uint8_t data[0]; +} __packed; + +#define BT_HCI_EVT_LE_CONN_UPDATE_COMPLETE 0x03 +struct bt_hci_evt_le_conn_update_complete { + uint8_t status; + uint16_t handle; + uint16_t interval; + uint16_t latency; + uint16_t supv_timeout; +} __packed; + +#define BT_HCI_EV_LE_REMOTE_FEAT_COMPLETE 0x04 +struct bt_hci_ev_le_remote_feat_complete { + uint8_t status; + uint16_t handle; + uint8_t features[8]; +} __packed; + +#define BT_HCI_EVT_LE_LTK_REQUEST 0x05 +struct bt_hci_evt_le_ltk_request { + uint16_t handle; + uint64_t rand; + uint16_t ediv; +} __packed; + +#define BT_HCI_EVT_LE_CONN_PARAM_REQ 0x06 +struct bt_hci_evt_le_conn_param_req { + uint16_t handle; + uint16_t interval_min; + uint16_t interval_max; + uint16_t latency; + uint16_t timeout; +} __packed; + +#define BT_HCI_EVT_LE_P256_PUBLIC_KEY_COMPLETE 0x08 +struct bt_hci_evt_le_p256_public_key_complete { + uint8_t status; + uint8_t key[64]; +} __packed; + +#define BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE 0x09 +struct bt_hci_evt_le_generate_dhkey_complete { + uint8_t status; + uint8_t dhkey[32]; +} __packed; + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_HCI_H */ diff --git a/system/libarc32_arduino101/drivers/bluetooth/uuid.h b/system/libarc32_arduino101/drivers/bluetooth/uuid.h new file mode 100644 index 00000000..a54108ba --- /dev/null +++ b/system/libarc32_arduino101/drivers/bluetooth/uuid.h @@ -0,0 +1,463 @@ +/** @file + * @brief Bluetooth UUID handling + */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef __BT_UUID_H +#define __BT_UUID_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Bluetooth UUID types */ +enum { + BT_UUID_TYPE_16, + BT_UUID_TYPE_128, +}; + +/** @brief This is a 'tentative' type and should be used as a pointer only */ +struct bt_uuid { + uint8_t type; +}; + +struct bt_uuid_16 { + struct bt_uuid uuid; + uint16_t val; +}; + +struct bt_uuid_128 { + struct bt_uuid uuid; + uint8_t val[16]; +}; + +#define BT_UUID_INIT_16(value) \ +{ \ + .uuid.type = BT_UUID_TYPE_16, \ + .val = (value), \ +} + +#define BT_UUID_INIT_128(value...) \ +{ \ + .uuid.type = BT_UUID_TYPE_128, \ + .val = { value }, \ +} + +#define BT_UUID_DECLARE_16(value) \ + ((struct bt_uuid *) (&(struct bt_uuid_16) BT_UUID_INIT_16(value))) +#define BT_UUID_DECLARE_128(value...) \ + ((struct bt_uuid *) (&(struct bt_uuid_128) BT_UUID_INIT_128(value))) + +#define BT_UUID_16(__u) CONTAINER_OF(__u, struct bt_uuid_16, uuid) +#define BT_UUID_128(__u) CONTAINER_OF(__u, struct bt_uuid_128, uuid) + +/** @def BT_UUID_GAP + * @brief Generic Access + */ +#define BT_UUID_GAP BT_UUID_DECLARE_16(0x1800) +#define BT_UUID_GAP_VAL 0x1800 +/** @def BT_UUID_GATT + * @brief Generic Attribute + */ +#define BT_UUID_GATT BT_UUID_DECLARE_16(0x1801) +#define BT_UUID_GATT_VAL 0x1801 +/** @def BT_UUID_CTS + * @brief Current Time Service + */ +#define BT_UUID_CTS BT_UUID_DECLARE_16(0x1805) +#define BT_UUID_CTS_VAL 0x1805 +/** @def BT_UUID_DIS + * @brief Device Information Service + */ +#define BT_UUID_DIS BT_UUID_DECLARE_16(0x180a) +#define BT_UUID_DIS_VAL 0x180a +/** @def BT_UUID_HRS + * @brief Heart Rate Service + */ +#define BT_UUID_HRS BT_UUID_DECLARE_16(0x180d) +#define BT_UUID_HRS_VAL 0x180d +/** @def BT_UUID_BAS + * @brief Battery Service + */ +#define BT_UUID_BAS BT_UUID_DECLARE_16(0x180f) +#define BT_UUID_BAS_VAL 0x180f +/** @def BT_UUID_RSCS + * @brief Running Speed and Cadence Service + */ +#define BT_UUID_RSCS BT_UUID_DECLARE_16(0x1814) +#define BT_UUID_RSCS_VAL 0x1814 +/** @def BT_UUID_CSC + * @brief Cycling Speed and Cadence Service + */ +#define BT_UUID_CSC BT_UUID_DECLARE_16(0x1816) +#define BT_UUID_CSC_VAL 0x1816 +/** @def BT_UUID_ESS + * @brief Environmental Sensing Service + */ +#define BT_UUID_ESS BT_UUID_DECLARE_16(0x181a) +#define BT_UUID_ESS_VAL 0x181a +/** @def BT_UUID_IPSS + * @brief IP Support Service + */ +#define BT_UUID_IPSS BT_UUID_DECLARE_16(0x1820) +#define BT_UUID_IPSS_VAL 0x1820 +/** @def BT_UUID_LNS + * @brief Location and Navigation Support Service + */ +#define BT_UUID_LNS BT_UUID_DECLARE_16(0x1819) +#define BT_UUID_LNS_VAL 0x1819 +/** @def BT_UUID_GATT_PRIMARY + * @brief GATT Primary Service + */ +#define BT_UUID_GATT_PRIMARY BT_UUID_DECLARE_16(0x2800) +#define BT_UUID_GATT_PRIMARY_VAL 0x2800 +/** @def BT_UUID_GATT_SECONDARY + * @brief GATT Secondary Service + */ +#define BT_UUID_GATT_SECONDARY BT_UUID_DECLARE_16(0x2801) +#define BT_UUID_GATT_SECONDARY_VAL 0x2801 +/** @def BT_UUID_GATT_INCLUDE + * @brief GATT Include Service + */ +#define BT_UUID_GATT_INCLUDE BT_UUID_DECLARE_16(0x2802) +#define BT_UUID_GATT_INCLUDE_VAL 0x2802 +/** @def BT_UUID_GATT_CHRC + * @brief GATT Characteristic + */ +#define BT_UUID_GATT_CHRC BT_UUID_DECLARE_16(0x2803) +#define BT_UUID_GATT_CHRC_VAL 0x2803 +/** @def BT_UUID_GATT_CEP + * @brief GATT Characteristic Extended Properties + */ +#define BT_UUID_GATT_CEP BT_UUID_DECLARE_16(0x2900) +#define BT_UUID_GATT_CEP_VAL 0x2900 +/** @def BT_UUID_GATT_CUD + * @brief GATT Characteristic User Description + */ +#define BT_UUID_GATT_CUD BT_UUID_DECLARE_16(0x2901) +#define BT_UUID_GATT_CUD_VAL 0x2901 +/** @def BT_UUID_GATT_CCC + * @brief GATT Client Characteristic Configuration + */ +#define BT_UUID_GATT_CCC BT_UUID_DECLARE_16(0x2902) +#define BT_UUID_GATT_CCC_VAL 0x2902 +/** @def BT_UUID_GATT_SCC + * @brief GATT Server Characteristic Configuration + */ +#define BT_UUID_GATT_SCC BT_UUID_DECLARE_16(0x2903) +#define BT_UUID_GATT_SCC_VAL 0x2903 +/** @def BT_UUID_GATT_CPF + * @brief GATT Characteristic Presentation Format + */ +#define BT_UUID_GATT_CPF BT_UUID_DECLARE_16(0x2904) +#define BT_UUID_GATT_CPF_VAL 0x2904 +/** @def BT_UUID_VALID_RANGE + * @brief Valid Range Descriptor + */ +#define BT_UUID_VALID_RANGE BT_UUID_DECLARE_16(0x2906) +#define BT_UUID_VALID_RANGE_VAL 0x2906 +/** @def BT_UUID_ES_CONFIGURATION + * @brief Environmental Sensing Configuration Descriptor + */ +#define BT_UUID_ES_CONFIGURATION BT_UUID_DECLARE_16(0x290b) +#define BT_UUID_ES_CONFIGURATION_VAL 0x290b +/** @def BT_UUID_ES_MEASUREMENT + * @brief Environmental Sensing Measurement Descriptor + */ +#define BT_UUID_ES_MEASUREMENT BT_UUID_DECLARE_16(0x290c) +#define BT_UUID_ES_MEASUREMENT_VAL 0x290c +/** @def BT_UUID_ES_TRIGGER_SETTING + * @brief Environmental Sensing Trigger Setting Descriptor + */ +#define BT_UUID_ES_TRIGGER_SETTING BT_UUID_DECLARE_16(0x290d) +#define BT_UUID_ES_TRIGGER_SETTING_VAL 0x290d +/** @def BT_UUID_GAP_DEVICE_NAME + * @brief GAP Characteristic Device Name + */ +#define BT_UUID_GAP_DEVICE_NAME BT_UUID_DECLARE_16(0x2a00) +#define BT_UUID_GAP_DEVICE_NAME_VAL 0x2a00 +/** @def BT_UUID_GAP_APPEARANCE + * @brief GAP Characteristic Appearance + */ +#define BT_UUID_GAP_APPEARANCE BT_UUID_DECLARE_16(0x2a01) +#define BT_UUID_GAP_APPEARANCE_VAL 0x2a01 +/** @def BT_UUID_GAP_PPCP + * @brief GAP Characteristic Peripheral Preferred Connection Parameters + */ +#define BT_UUID_GAP_PPCP BT_UUID_DECLARE_16(0x2a04) +#define BT_UUID_GAP_PPCP_VAL 0x2a04 +/** @def BT_UUID_BAS_BATTERY_LEVEL + * @brief BAS Characteristic Battery Level + */ +#define BT_UUID_BAS_BATTERY_LEVEL BT_UUID_DECLARE_16(0x2a19) +#define BT_UUID_BAS_BATTERY_LEVEL_VAL 0x2a19 +/** @def BT_UUID_DIS_SYSTEM_ID + * @brief DIS Characteristic System ID + */ +#define BT_UUID_DIS_SYSTEM_ID BT_UUID_DECLARE_16(0x2a23) +#define BT_UUID_DIS_SYSTEM_ID_VAL 0x2a23 +/** @def BT_UUID_DIS_MODEL_NUMBER + * @brief DIS Characteristic Model Number String + */ +#define BT_UUID_DIS_MODEL_NUMBER BT_UUID_DECLARE_16(0x2a24) +#define BT_UUID_DIS_MODEL_NUMBER_VAL 0x2a24 +/** @def BT_UUID_DIS_SERIAL_NUMBER + * @brief DIS Characteristic Serial Number String + */ +#define BT_UUID_DIS_SERIAL_NUMBER BT_UUID_DECLARE_16(0x2a25) +#define BT_UUID_DIS_SERIAL_NUMBER_VAL 0x2a25 +/** @def BT_UUID_DIS_FIRMWARE_REVISION + * @brief DIS Characteristic Firmware Revision String + */ +#define BT_UUID_DIS_FIRMWARE_REVISION BT_UUID_DECLARE_16(0x2a26) +#define BT_UUID_DIS_FIRMWARE_REVISION_VAL 0x2a26 +/** @def BT_UUID_DIS_HARDWARE_REVISION + * @brief DIS Characteristic Hardware Revision String + */ +#define BT_UUID_DIS_HARDWARE_REVISION BT_UUID_DECLARE_16(0x2a27) +#define BT_UUID_DIS_HARDWARE_REVISION_VAL 0x2a27 +/** @def BT_UUID_DIS_SOFTWARE_REVISION + * @brief DIS Characteristic Software Revision String + */ +#define BT_UUID_DIS_SOFTWARE_REVISION BT_UUID_DECLARE_16(0x2a28) +#define BT_UUID_DIS_SOFTWARE_REVISION_VAL 0x2a28 +/** @def BT_UUID_DIS_MANUFACTURER_NAME + * @brief DIS Characteristic Manufacturer Name String + */ +#define BT_UUID_DIS_MANUFACTURER_NAME BT_UUID_DECLARE_16(0x2a29) +#define BT_UUID_DIS_MANUFACTURER_NAME_VAL 0x2a29 +/** @def BT_UUID_DIS_PNP_ID + * @brief DIS Characteristic PnP ID + */ +#define BT_UUID_DIS_PNP_ID BT_UUID_DECLARE_16(0x2a50) +#define BT_UUID_DIS_PNP_ID_VAL 0x2a50 +/** @def BT_UUID_RSC_MEASUREMENT + * @brief RSC Characteristic measurement ID + */ +#define BT_UUID_RSC_MEASUREMENT BT_UUID_DECLARE_16(0x2a53) +#define BT_UUID_RSC_MEASUREMENT_VAL 0x2a53 +/** @def BT_UUID_RSC_FEATURE + * @brief RSC Characteristic feature ID + */ +#define BT_UUID_RSC_FEATURE BT_UUID_DECLARE_16(0x2a54) +#define BT_UUID_RSC_FEATURE_VAL 0x2a54 +/** @def BT_UUID_CTS_CURRENT_TIME + * @brief CTS Characteristic Current Time + */ +#define BT_UUID_CTS_CURRENT_TIME BT_UUID_DECLARE_16(0x2a2b) +#define BT_UUID_CTS_CURRENT_TIME_VAL 0x2a2b +/** @def BT_UUID_MAGN_DECLINATION + * @brief Magnetic Declination Characteristic + */ +#define BT_UUID_MAGN_DECLINATION BT_UUID_DECLARE_16(0x2a2c) +#define BT_UUID_MAGN_DECLINATION_VAL 0x2a2c +/** @def BT_UUID_HRS_MEASUREMENT + * @brief HRS Characteristic Measurement Interval + */ +#define BT_UUID_HRS_MEASUREMENT BT_UUID_DECLARE_16(0x2a37) +#define BT_UUID_HRS_MEASUREMENT_VAL 0x2a37 +/** @def BT_UUID_HRS_BODY_SENSOR + * @brief HRS Characteristic Body Sensor Location + */ +#define BT_UUID_HRS_BODY_SENSOR BT_UUID_DECLARE_16(0x2a38) +#define BT_UUID_HRS_BODY_SENSOR_VAL 0x2a38 +/** @def BT_UUID_HRS_CONTROL_POINT + * @brief HRS Characteristic Control Point + */ +#define BT_UUID_HRS_CONTROL_POINT BT_UUID_DECLARE_16(0x2a39) +#define BT_UUID_HRS_CONTROL_POINT_VAL 0x2a39 +/** @def BT_UUID_CSC_MEASUREMENT + * @brief CSC Measurement Characteristic + */ +#define BT_UUID_CSC_MEASUREMENT BT_UUID_DECLARE_16(0x2a5b) +#define BT_UUID_CSC_MEASUREMENT_VAL 0x2a5b +/** @def BT_UUID_CSC_FEATURE + * @brief CSC Feature Characteristic + */ +#define BT_UUID_CSC_FEATURE BT_UUID_DECLARE_16(0x2a5c) +#define BT_UUID_CSC_FEATURE_VAL 0x2a5c +/** @def BT_UUID_SENSOR_LOCATION + * @brief Sensor Location Characteristic + */ +#define BT_UUID_SENSOR_LOCATION BT_UUID_DECLARE_16(0x2a5d) +#define BT_UUID_SENSOR_LOCATION_VAL 0x2a5d +/** @def BT_UUID_SC_CONTROL_POINT + * @brief SC Control Point Characteristic + */ +#define BT_UUID_SC_CONTROL_POINT BT_UUID_DECLARE_16(0x2a55) +#define BT_UUID_SC_CONTROL_POINT_VAl 0x2a55 +/** @def BT_UUID_LNS_CONTROL_POINT + * @brief LNS Control Point Characteristic + */ +#define BT_UUID_LNS_CONTROL_POINT BT_UUID_DECLARE_16(0x2a6B) +#define BT_UUID_LNS_CONTROL_POINT_VAL 0x2a6B +/** @def BT_UUID_LNS_LOCATION_SPEED + * @brief LNS Characteristic Location and Speed + */ +#define BT_UUID_LNS_LOCATION_SPEED BT_UUID_DECLARE_16(0x2a67) +#define BT_UUID_LNS_LOCATION_SPEED_VAL 0x2a67 +/** @def BT_UUID_LNS_FEATURE + * @brief LNS Characteristic Feature + */ +#define BT_UUID_LNS_FEATURE BT_UUID_DECLARE_16(0x2a6a) +#define BT_UUID_LNS_FEATURE_VAL 0x2a6a +/** @def BT_UUID_ELEVATION + * @brief Elevation Characteristic + */ +#define BT_UUID_ELEVATION BT_UUID_DECLARE_16(0x2a6c) +#define BT_UUID_ELEVATION_VAL 0x2a6c +/** @def BT_UUID_PRESSURE + * @brief Pressure Characteristic + */ +#define BT_UUID_PRESSURE BT_UUID_DECLARE_16(0x2a6d) +#define BT_UUID_PRESSURE_VAL 0x2a6d +/** @def BT_UUID_TEMPERATURE + * @brief Temperature Characteristic + */ +#define BT_UUID_TEMPERATURE BT_UUID_DECLARE_16(0x2a6e) +#define BT_UUID_TEMPERATURE_VAL 0x2a6e +/** @def BT_UUID_HUMIDITY + * @brief Humidity Characteristic + */ +#define BT_UUID_HUMIDITY BT_UUID_DECLARE_16(0x2a6f) +#define BT_UUID_HUMIDITY_VAL 0x2a6f +/** @def BT_UUID_TRUE_WIND_SPEED + * @brief True Wind Speed Characteristic + */ +#define BT_UUID_TRUE_WIND_SPEED BT_UUID_DECLARE_16(0x2a70) +#define BT_UUID_TRUE_WIND_SPEED_VAL 0x2a70 +/** @def BT_UUID_TRUE_WIND_DIR + * @brief True Wind Direction Characteristic + */ +#define BT_UUID_TRUE_WIND_DIR BT_UUID_DECLARE_16(0x2a71) +#define BT_UUID_TRUE_WIND_DIR_VAL 0x2a71 +/** @def BT_UUID_APPARENT_WIND_SPEED + * @brief Apparent Wind Speed Characteristic + */ +#define BT_UUID_APPARENT_WIND_SPEED BT_UUID_DECLARE_16(0x2a72) +#define BT_UUID_APPARENT_WIND_SPEED_VAL 0x2a72 +/** @def BT_UUID_APPARENT_WIND_DIR + * @brief Apparent Wind Direction Characteristic + */ +#define BT_UUID_APPARENT_WIND_DIR BT_UUID_DECLARE_16(0x2a73) +#define BT_UUID_APPARENT_WIND_DIR_VAL 0x2a73 +/** @def BT_UUID_GUST_FACTOR + * @brief Gust Factor Characteristic + */ +#define BT_UUID_GUST_FACTOR BT_UUID_DECLARE_16(0x2a74) +#define BT_UUID_GUST_FACTOR_VAL 0x2a74 +/** @def BT_UUID_POLLEN_CONCENTRATION + * @brief Pollen Concentration Characteristic + */ +#define BT_UUID_POLLEN_CONCENTRATION BT_UUID_DECLARE_16(0x2a75) +#define BT_UUID_POLLEN_CONCENTRATION_VAL 0x2a75 +/** @def BT_UUID_UV_INDEX + * @brief UV Index Characteristic + */ +#define BT_UUID_UV_INDEX BT_UUID_DECLARE_16(0x2a76) +#define BT_UUID_UV_INDEX_VAL 0x2a76 +/** @def BT_UUID_IRRADIANCE + * @brief Irradiance Characteristic + */ +#define BT_UUID_IRRADIANCE BT_UUID_DECLARE_16(0x2a77) +#define BT_UUID_IRRADIANCE_VAL 0x2a77 +/** @def BT_UUID_RAINFALL + * @brief Rainfall Characteristic + */ +#define BT_UUID_RAINFALL BT_UUID_DECLARE_16(0x2a78) +#define BT_UUID_RAINFALL_VAL 0x2a78 +/** @def BT_UUID_WIND_CHILL + * @brief Wind Chill Characteristic + */ +#define BT_UUID_WIND_CHILL BT_UUID_DECLARE_16(0x2a79) +#define BT_UUID_WIND_CHILL_VAL 0x2a79 +/** @def BT_UUID_HEAT_INDEX + * @brief Heat Index Characteristic + */ +#define BT_UUID_HEAT_INDEX BT_UUID_DECLARE_16(0x2a7a) +#define BT_UUID_HEAT_INDEX_VAL 0x2a7a +/** @def BT_UUID_DEW_POINT + * @brief Dew Point Characteristic + */ +#define BT_UUID_DEW_POINT BT_UUID_DECLARE_16(0x2a7b) +#define BT_UUID_DEW_POINT_VAL 0x2a7b +/** @def BT_UUID_DESC_VALUE_CHANGED + * @brief Descriptor Value Changed Characteristic + */ +#define BT_UUID_DESC_VALUE_CHANGED BT_UUID_DECLARE_16(0x2a7d) +#define BT_UUID_DESC_VALUE_CHANGED_VAL 0x2a7d +/** @def BT_UUID_MAGN_FLUX_DENSITY_2D + * @brief Magnetic Flux Density - 2D Characteristic + */ +#define BT_UUID_MAGN_FLUX_DENSITY_2D BT_UUID_DECLARE_16(0x2aa0) +#define BT_UUID_MAGN_FLUX_DENSITY_2D_VAL 0x2aa0 +/** @def BT_UUID_MAGN_FLUX_DENSITY_3D + * @brief Magnetic Flux Density - 3D Characteristic + */ +#define BT_UUID_MAGN_FLUX_DENSITY_3D BT_UUID_DECLARE_16(0x2aa1) +#define BT_UUID_MAGN_FLUX_DENSITY_3D_VAL 0x2aa1 +/** @def BT_UUID_BAR_PRESSURE_TREND + * @brief Barometric Pressure Trend Characteristic + */ +#define BT_UUID_BAR_PRESSURE_TREND BT_UUID_DECLARE_16(0x2aa3) +#define BT_UUID_BAR_PRESSURE_TREND_VAL 0x2aa3 + +/** @brief Compare Bluetooth UUIDs. + * + * Compares 2 Bluetooth UUIDs, if the types are different both UUIDs are + * first converted to 128 bits format before comparing. + * + * @param u1 First Bluetooth UUID to compare + * @param u2 Second Bluetooth UUID to compare + * + * @return negative value if @a u1 < @a u2, 0 if @a u1 == @a u2, else positive + */ +int bt_uuid_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2); + +#if defined(CONFIG_BLUETOOTH_DEBUG) +/** @brief Convert Bluetooth UUID to string. + * + * Converts Bluetooth UUID to string. UUID has to be in 16 bits or 128 bits + * format. + * + * @param uuid Bluetooth UUID + * @param str pointer where to put converted string + * @param len length of str + * + * @return N/A + */ +void bt_uuid_to_str(const struct bt_uuid *uuid, char *str, size_t len); + +/** @brief Convert Bluetooth UUID to string in place. + * + * Converts Bluetooth UUID to string in place. UUID has to be in 16 bits or + * 128 bits format. + * + * @param uuid Bluetooth UUID + * + * @return String representation of the UUID given + */ +const char *bt_uuid_str(const struct bt_uuid *uuid); +#endif /* CONFIG_BLUETOOTH_DEBUG */ + +#ifdef __cplusplus +} +#endif + +#endif /* __BT_UUID_H */ diff --git a/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c b/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c index a497b6cf..b2f6c08f 100644 --- a/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c +++ b/system/libarc32_arduino101/drivers/ipc_uart_ns16550.c @@ -49,14 +49,38 @@ #define IPC_UART_HDR_REQUEST_LEN (IPC_HEADER_LEN+sizeof(uint32_t)) /* ipc header + request len */ -struct ipc_uart_channels channels[IPC_UART_MAX_CHANNEL] = { {0, 0, NULL},}; -static uint16_t send_counter = 0; -static uint16_t received_counter = 0; -static uint8_t * ipc_uart_tx = NULL; -static uint8_t * ipc_uart_rx = NULL; -static uint8_t ipc_uart_tx_state = 0; +enum { + STATUS_TX_IDLE = 0, + STATUS_TX_BUSY, + STATUS_TX_DONE, +}; + +enum { + STATUS_RX_IDLE = 0, + STATUS_RX_HDR, + STATUS_RX_DATA +}; + +struct ipc_uart { + uint8_t *tx_data; + uint8_t *rx_ptr; + struct ipc_uart_channels channels[IPC_UART_MAX_CHANNEL]; + struct ipc_uart_header tx_hdr; + struct ipc_uart_header rx_hdr; + uint16_t send_counter; + uint16_t rx_size; + uint8_t tx_state; + uint8_t rx_state; + uint8_t uart_enabled; + /* protect against multiple wakelock and wake assert calls */ + uint8_t tx_wakelock_acquired; + /* TODO: remove once IRQ will take a parameter */ + //struct td_device *device; + void (*tx_cb)(bool wake_state, void *); /*!< Callback to be called to set wake state when TX is starting or ending */ + void *tx_cb_param; /*!< tx_cb function parameter */ +}; -static void * ble_cfw_channel; +static struct ipc_uart ipc = {}; static const struct uart_init_info uart_dev_info[] = { { @@ -79,187 +103,246 @@ static const struct uart_init_info uart_dev_info[] = { }, }; -void uart_ipc_close_channel(int channel_id) +void ipc_uart_close_channel(int channel_id) { - channels[channel_id].state = IPC_CHANNEL_STATE_CLOSED; - channels[channel_id].cb = NULL; - channels[channel_id].index = channel_id; + ipc.channels[channel_id].state = IPC_CHANNEL_STATE_CLOSED; + ipc.channels[channel_id].cb = NULL; + ipc.channels[channel_id].index = channel_id; + + ipc.uart_enabled = 0; + ipc.tx_wakelock_acquired = 0; } -void uart_ipc_disable(int num) +void ipc_uart_ns16550_disable(int num) { int i; for (i = 0; i < IPC_UART_MAX_CHANNEL; i++) - uart_ipc_close_channel(i); + ipc_uart_close_channel(i); + if (ipc.tx_cb) + ipc.tx_cb(0, ipc.tx_cb_param); + UART_IRQ_TX_DISABLE(num); UART_IRQ_RX_DISABLE(num); } -void uart_ipc_init(int num) +void ipc_uart_init(int num) { int i; (void)num; - uint8_t c; for (i = 0; i < IPC_UART_MAX_CHANNEL; i++) - uart_ipc_close_channel(i); + ipc_uart_close_channel(i); - pr_info(LOG_MODULE_IPC, "uart_ipc_init(nr: %d), baudrate %d, options:" - "0x%x, irq: %d",IPC_UART, + pr_info(LOG_MODULE_IPC, "%s(nr: %d), baudrate %d, options:" + "0x%x, irq: %d",__FUNCTION__, IPC_UART, uart_dev_info[IPC_UART].baud_rate, uart_dev_info[IPC_UART].options, uart_dev_info[IPC_UART].irq); uart_init(IPC_UART, &uart_dev_info[IPC_UART]); - /* Drain RX FIFOs (no need to disable IRQ at this stage) */ - while (uart_poll_in(IPC_UART, &c) != -1); - uart_int_connect(IPC_UART, uart_ipc_isr, NULL, NULL); - - UART_IRQ_RX_ENABLE(IPC_UART); + + ipc.uart_enabled = 0; + ipc.tx_wakelock_acquired = 0; + + /* Initialize the reception pointer */ + ipc.rx_size = sizeof(ipc.rx_hdr); + ipc.rx_ptr = (uint8_t *)&ipc.rx_hdr; + ipc.rx_state = STATUS_RX_IDLE; } -void uart_ipc_set_channel(void * ipc_channel) +static void ipc_uart_push_frame(uint16_t len, uint8_t *p_data) { - ble_cfw_channel = ipc_channel; -} - -void * uart_ipc_get_channel(void) -{ - return ble_cfw_channel; -} - -void uart_ipc_push_frame(void) { - void * frame; - OS_ERR_TYPE error = E_OS_OK; - - if (NULL == ipc_uart_rx) - return; - int len = IPC_FRAME_GET_LEN(ipc_uart_rx); - int channel = IPC_FRAME_GET_CHANNEL(ipc_uart_rx); - uint8_t cpu_id = IPC_FRAME_GET_SRC(ipc_uart_rx); - - pr_debug(LOG_MODULE_IPC, "%s: received frame: len %d, channel %d, src " - "%d", __func__, len, channel, cpu_id); - - if (channels[channel].cb != NULL) { - frame = balloc(len, &error); - if (error != E_OS_OK) { - pr_error(LOG_MODULE_IPC, "NO MEM: error: %d size: %d", - error, len); - } else { - memcpy(frame, &ipc_uart_rx[IPC_HEADER_LEN], len); - - channels[channel].cb(cpu_id, channel, len, frame); - } + //pr_debug(LOG_MODULE_IPC, "push_frame: received:frame len: %d, 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]); + + if ((ipc.rx_hdr.channel < IPC_UART_MAX_CHANNEL) && + (ipc.channels[ipc.rx_hdr.channel].cb != NULL)) { + ipc.channels[ipc.rx_hdr.channel].cb(ipc.rx_hdr.channel, + IPC_MSG_TYPE_MESSAGE, + len, + p_data); + } else { + bfree(p_data); + pr_error(LOG_MODULE_IPC, "uart_ipc: bad channel %d", + ipc.rx_hdr.channel); } - if (ipc_uart_rx) - bfree(ipc_uart_rx); - ipc_uart_rx = NULL; } -void uart_ipc_isr() +void ipc_uart_isr() { - uint8_t *p_rx; + /* 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(); + if (UART_BREAK_CHECK(IPC_UART)) { + panic(-1); } UART_POLL_IN(IPC_UART, &c); - } else if (UART_IRQ_RX_READY(IPC_UART)) { - int received; - if (received_counter < 2) { - if (NULL == ipc_uart_rx) - ipc_uart_rx = - balloc(IPC_UART_MAX_PAYLOAD, NULL); - p_rx = ipc_uart_rx; - received = UART_FIFO_READ(IPC_UART, - &p_rx[received_counter], - 1); - received_counter += received; - } else { - p_rx = ipc_uart_rx; - received = UART_FIFO_READ(IPC_UART, - &p_rx[received_counter], - IPC_FRAME_GET_LEN(p_rx) + - IPC_HEADER_LEN - - received_counter); - received_counter += received; - if (received_counter == IPC_FRAME_GET_LEN(p_rx) - + IPC_HEADER_LEN) { + } + 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); + + //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 - for(int i = 0; i < received_counter; i++) { - pr_debug(LOG_MODULE_IPC, "%s: %d byte is %d", __func__, 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 - received_counter = 0; - uart_ipc_push_frame(); + + 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; + } } } - } else if (UART_IRQ_TX_READY(IPC_UART)) { - int transmitted; - if (ipc_uart_tx_state == STATUS_TX_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); - - pr_debug(LOG_MODULE_IPC, "ipc_isr_tx: disable TXint, LSR: 0x%2x\n", - lsr); + /* 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_uart_tx){ - pr_warning(LOG_MODULE_IPC, "%s: Bad Tx data",__func__); + if (NULL == ipc.tx_data) { + pr_warning(LOG_MODULE_IPC, + "ipc_uart_isr: Bad Tx data"); return; } - p_tx = ipc_uart_tx; - transmitted = UART_FIFO_FILL(IPC_UART, &p_tx[send_counter], - IPC_FRAME_GET_LEN(p_tx) + - IPC_HEADER_LEN - send_counter); - send_counter += transmitted; - if (send_counter == IPC_FRAME_GET_LEN(p_tx) + - IPC_HEADER_LEN) { - send_counter = 0; -#ifdef IPC_UART_DBG_TX - pr_debug(LOG_MODULE_IPC, "%s: sent IPC FRAME " - "len %d", __func__, - IPC_FRAME_GET_LEN(p_tx)); - for (int i = 0; i < send_counter; i++) { - pr_debug(LOG_MODULE_IPC, "%s: %d sent " - "byte is %d", - __func__, i, p_tx[i]); + + 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); #endif - bfree(ipc_uart_tx); - ipc_uart_tx = NULL; - ipc_uart_tx_state = STATUS_TX_IDLE; + 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); - pr_info(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 } - } else { - pr_warning(LOG_MODULE_IPC, "%s: Unknown ISR src", - __func__); } + } } -void *uart_ipc_channel_open(int channel_id, - void (*cb) (uint8_t, int, int, void *)) +void *ipc_uart_channel_open(int channel_id, + int (*cb)(int, int, int, void *)) { struct ipc_uart_channels *chan; + uint8_t c; - if (channel_id > IPC_UART_MAX_CHANNEL - 1) + if (channel_id > (IPC_UART_MAX_CHANNEL - 1)) return NULL; - chan = &channels[channel_id]; + chan = &ipc.channels[channel_id]; if (chan->state != IPC_CHANNEL_STATE_CLOSED) return NULL; @@ -267,74 +350,48 @@ void *uart_ipc_channel_open(int channel_id, chan->state = IPC_CHANNEL_STATE_OPEN; chan->cb = cb; + ipc.uart_enabled = 1; + ipc.tx_wakelock_acquired = 0; + + pr_debug(LOG_MODULE_IPC, "%s: open chan success", __FUNCTION__); + + /* Drain RX FIFOs (no need to disable IRQ at this stage) */ + while (uart_poll_in(IPC_UART, &c) != -1); + uart_int_connect(IPC_UART, ipc_uart_isr, NULL, NULL); + + UART_IRQ_RX_ENABLE(IPC_UART); + return chan; } -int uart_ipc_send_message(void *handle, int len, void *p_data) +int ipc_uart_ns16550_send_pdu(void *handle, int len, void *p_data) { - struct ipc_uart_channels *chan = (struct ipc_uart_channels *) handle; - - int flags = interrupt_lock(); - if (ipc_uart_tx_state == STATUS_TX_BUSY) { - interrupt_unlock(flags); - return IPC_UART_ERROR_WRONG_STATE; - } - ipc_uart_tx_state = STATUS_TX_BUSY; - interrupt_unlock(flags); - - uint8_t *p_tx = ipc_uart_tx = balloc(len + IPC_UART_HDR_REQUEST_LEN, - NULL); + struct ipc_uart_channels *chan = (struct ipc_uart_channels *)handle; - /* Adding size of the message request field*/ - int size = len + sizeof(uint32_t); + pr_debug(LOG_MODULE_IPC, "%s: %d", __FUNCTION__, ipc.tx_state); - /* length = cfw_message size + message request field*/ - IPC_FRAME_SET_LEN(p_tx, size); - IPC_FRAME_SET_CHANNEL(p_tx, chan->index); - IPC_FRAME_SET_SRC(p_tx, get_cpu_id()); - IPC_FRAME_SET_REQUEST(p_tx, IPC_MSG_TYPE_MESSAGE); - - /* IPC_HEADER + request_ID + cfw_message */ - /* copy cfw_message within IPC frame*/ - memcpy(IPC_FRAME_DATA(p_tx), p_data, len); + if (ipc.tx_state == STATUS_TX_BUSY) { + return IPC_UART_TX_BUSY; + } + + /* It is eventually possible to be in DONE state (sending last bytes of previous message), + * so we move immediately to BUSY and configure the next frame */ + ipc.tx_state = STATUS_TX_BUSY; - pr_debug(LOG_MODULE_IPC, "%s: tx: channel %d, len %d, request 0x%x", - __func__, p_tx[2], len, p_tx[4]); + ipc.tx_hdr.len = len; + ipc.tx_hdr.channel = chan->index; + ipc.tx_hdr.src_cpu_id = 0; + ipc.tx_data = p_data; + /* Enable the interrupt (ready will expire if it was disabled) */ UART_IRQ_TX_ENABLE(IPC_UART); return IPC_UART_ERROR_OK; } -int uart_ipc_send_sync_resp(int channel, int request_id, int param1, int param2, - void * ptr) +void ipc_uart_ns16550_set_tx_cb(void (*cb)(bool, void *), void *param) { - if (ipc_uart_tx_state == STATUS_TX_BUSY) - return IPC_UART_ERROR_WRONG_STATE; - ipc_uart_tx_state = STATUS_TX_BUSY; - - uint8_t *p_tx = ipc_uart_tx = balloc(IPC_UART_HDR_REQUEST_LEN + 12, - NULL); - - IPC_FRAME_SET_LEN(p_tx, 16); - IPC_FRAME_SET_CHANNEL(p_tx, channel); - IPC_FRAME_SET_SRC(ipc_uart_tx, get_cpu_id()); - - IPC_FRAME_SET_REQUEST(p_tx, request_id); - SYNC_FRAME_SET_PARAM1(p_tx, param1); - SYNC_FRAME_SET_PARAM2(p_tx, param2); - SYNC_FRAME_SET_PTR(p_tx, ptr); - -#ifdef IPC_UART_DBG_SYNC_RESP - for (int i = 0; i < 20; i++) { - pr_debug(LOG_MODULE_IPC, "%s: IPC sync resp %d byte : %d", - __func__,i, p_tx[i]); - } - pr_debug(LOG_MODULE_IPC, "%s: tx: channel %d, request %xh", __func__, - p_tx[2], p_tx[4]); -#endif - - UART_IRQ_TX_ENABLE(IPC_UART); - - return IPC_UART_ERROR_OK; + ipc.tx_cb = cb; + ipc.tx_cb_param = param; } + diff --git a/system/libarc32_arduino101/drivers/ipc_uart_ns16550.h b/system/libarc32_arduino101/drivers/ipc_uart_ns16550.h index 7fc04670..d71a61b9 100644 --- a/system/libarc32_arduino101/drivers/ipc_uart_ns16550.h +++ b/system/libarc32_arduino101/drivers/ipc_uart_ns16550.h @@ -33,19 +33,71 @@ #define IPC_UART 0 -enum { - STATUS_TX_IDLE = 0, - STATUS_TX_BUSY, -}; -enum { +/** IPC UART return codes */ +enum IPC_UART_RESULT_CODES { IPC_UART_ERROR_OK = 0, - IPC_UART_ERROR_WRONG_STATE, IPC_UART_ERROR_DATA_TO_BIG, + IPC_UART_TX_BUSY /**< A transmission is already ongoing, message is NOT sent */ +}; + + +/** + * Channel list + */ +enum ipc_channels { + RPC_CHANNEL=0, /**< RPC channel */ + IPC_UART_MAX_CHANNEL = 4 +}; + +/** + * Channel state + */ +enum ipc_channel_state { + IPC_CHANNEL_STATE_CLOSED = 0, + IPC_CHANNEL_STATE_OPEN +}; + +/** + * Definitions valid for NONE sync IPC UART headers + * |len|channel|cpu_id|request|payload| + * + * len = len(request)+len(payload) + */ + +/** + * @note this structure must be self-aligned and self-packed + */ +struct ipc_uart_header { + uint16_t len; /**< Length of IPC message, (request + payload) */ + uint8_t channel; /**< Channel number of IPC message. */ + uint8_t src_cpu_id; /**< CPU id of IPC sender. */ }; -void uart_ipc_isr(); -void uart_ipc_push_frame(void); -void uart_ipc_close_channel(int channel_id); +/** + * IPC channel description + */ +struct ipc_uart_channels { + uint16_t index; /**< Channel number */ + uint16_t state; /**< @ref ipc_channel_state */ + int (*cb)(int chan, int request, int len, void *data); + /**< Callback of the channel. + * @param chan Channel index used + * @param request Request id (defined in ipc_requests.h) + * @param len Payload size + * @param data Pointer to data + */ +}; + +void ipc_uart_init(int num); +void ipc_uart_isr(); +//static void ipc_uart_push_frame(uint16_t len, uint8_t *p_data); +void ipc_uart_ns16550_disable(int num); +void ipc_uart_close_channel(int channel_id); +void ipc_uart_ns16550_set_tx_cb(void (*cb)(bool, void *), void *param); +int ipc_uart_ns16550_send_pdu(void *handle, int len, void *p_data); +void *ipc_uart_channel_open(int channel_id, + int (*cb)(int, int, int, void *)); + #endif /* _IPC_UART_NS16550_H_ */ diff --git a/system/libarc32_arduino101/drivers/rpc/rpc.h b/system/libarc32_arduino101/drivers/rpc/rpc.h new file mode 100644 index 00000000..5d45d463 --- /dev/null +++ b/system/libarc32_arduino101/drivers/rpc/rpc.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RPC_H_ +#define RPC_H_ + +#include + +/** Identifiers of the signature supported by the RPC */ +enum { + SIG_TYPE_NONE = 1, + SIG_TYPE_S, + SIG_TYPE_P, + SIG_TYPE_S_B, + SIG_TYPE_B_B_P, + SIG_TYPE_S_P, + SIG_TYPE_S_B_P, + SIG_TYPE_S_B_B_P +}; + +/** + * RPC memory allocation function, must be implemented by the user of the RPC. + * + * This function is called by the RPC mechanism to allocate a buffer for transmission + * of a serialized function. The function should not fail. + * + * @param length Length of the buffer to allocate + * + * @return Pointer to the allocated buffer, the allocation shall not fail, error must + * be handled internally + */ +uint8_t * rpc_alloc_cb(uint16_t length); + +/** + * RPC transmission function, must be implemented by the user of the RPC. + * + * @param p_buf Pointer to the buffer allocated for transmission by @ref rpc_alloc_cb + * @param length Length of the buffer to transmit + */ +void rpc_transmit_cb(uint8_t * p_buf, uint16_t length); + +/** + * RPC serialization function to serialize a function that does not require any parameter. + * + * @param fn_index Index of the function + */ +void rpc_serialize_none(uint8_t fn_index); + +/** + * RPC serialization function to serialize a function that expects a structure as parameter. + * + * @param fn_index Index of the function + * @param struct_data Pointer to the structure to serialize + * @param struct_length Length of the structure to serialize + */ +void rpc_serialize_s(uint8_t fn_index, const void * struct_data, uint8_t struct_length); + +/** + * RPC serialization function to serialize a function that expects a structure as parameter. + * + * @param fn_index Index of the function + * @param struct_data Pointer to the structure to serialize + * @param struct_length Length of the structure to serialize + * @param p_priv Pointer to serialize + */ +void rpc_serialize_s_p(uint8_t fn_index, const void * struct_data, uint8_t struct_length, void * p_priv); + +/** + * RPC serialization function to serialize a function that expects a pointer as parameter. + * + * @param fn_index Index of the function + * @param p_priv Pointer to serialize + */ +void rpc_serialize_p(uint8_t fn_index, void * p_priv); + +/** + * RPC serialization function to serialize a function that expects a structure + * and a buffer as parameters. + * + * @param fn_index Index of the function + * @param struct_data Pointer to the structure to serialize + * @param struct_length Length of the structure to serialize + * @param vbuf Pointer to the buffer to serialize + * @param vbuf_length Length of the buffer to serialize + */ +void rpc_serialize_s_b(uint8_t fn_index, const void * struct_data, uint8_t struct_length, const void * vbuf, uint16_t vbuf_length); + +/** + * RPC serialization function to serialize a function that expects a structure + * and a buffer as parameters. + * + * @param fn_index Index of the function + * @param vbuf1 Pointer to the buffer1 to serialize + * @param vbuf1_length Length of the buffer1 to serialize + * @param vbuf2 Pointer to the buffer2 to serialize + * @param vbuf2_length Length of the buffer2 to serialize + * @param p_priv Pointer to serialize + */ +void rpc_serialize_b_b_p(uint8_t fn_index, const void * vbuf1, uint16_t vbuf1_length, + const void * vbuf2, uint16_t vbuf2_length, void * p_priv); + +/** + * RPC serialization function to serialize a function that expects a structure + * and a buffer as parameters. + * + * @param fn_index Index of the function + * @param struct_data Pointer to the structure to serialize + * @param struct_length Length of the structure to serialize + * @param vbuf Pointer to the buffer to serialize + * @param vbuf_length Length of the buffer to serialize + * @param p_priv Pointer to serialize + */ +void rpc_serialize_s_b_p(uint8_t fn_index, const void * struct_data, uint8_t struct_length, + const void * vbuf, uint16_t vbuf_length, void * p_priv); + +/** + * RPC serialization function to serialize a function that expects a structure + * and a buffer as parameters. + * + * @param fn_index Index of the function + * @param struct_data Pointer to the structure to serialize + * @param struct_length Length of the structure to serialize + * @param vbuf1 Pointer to the buffer1 to serialize + * @param vbuf1_length Length of the buffer1 to serialize + * @param vbuf2 Pointer to the buffer2 to serialize + * @param vbuf2_length2 Length of the buffer2 to serialize + * @param p_priv Pointer to serialize + */ +void rpc_serialize_s_b_b_p(uint8_t fn_index, const void * struct_data, uint8_t struct_length, + const void * vbuf1, uint16_t vbuf1_length, const void * vbuf2, uint16_t vbuf2_length, void * p_priv); + +/** RPC deserialization function, shall be invoked when a buffer is received over the transport interface. + * + * @param p_buf Pointer to the received buffer + * @param length Length of the received buffer + */ +void rpc_deserialize(const uint8_t * p_buf, uint16_t length); + +#endif /* RPC_H_*/ diff --git a/system/libarc32_arduino101/drivers/rpc/rpc_deserialize.c b/system/libarc32_arduino101/drivers/rpc/rpc_deserialize.c new file mode 100644 index 00000000..e3f568d0 --- /dev/null +++ b/system/libarc32_arduino101/drivers/rpc/rpc_deserialize.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "rpc.h" + +extern void panic(int err); + +/* Include the functions offered */ +#if defined(CONFIG_QUARK_SE_BLE_CORE) +#include "rpc_functions_to_ble_core.h" +#elif defined(CONFIG_SOC_QUARK_SE) +#include "rpc_functions_to_quark.h" +#elif defined(LINUX_HOST_RUNTIME) +// for the host compilation (to simulate connection to BLE controller) +#include "rpc_functions_to_ble_core.h" +#else +#error "File is compiled but should not" +#endif + +/* Build the list of prototypes and check that list are made only of matching signatures */ +#define FN_SIG_NONE(__fn) void __fn(void); +LIST_FN_SIG_NONE +#undef FN_SIG_NONE + +#define FN_SIG_S(__fn, __s) void __fn(__s p_s); +LIST_FN_SIG_S +#undef FN_SIG_S + +#define FN_SIG_P(__fn, __type) void __fn(__type p_priv); +LIST_FN_SIG_P +#undef FN_SIG_P + +#define FN_SIG_S_B(__fn, __s, __type, __length) void __fn(__s p_s, __type p_buf, __length length); +LIST_FN_SIG_S_B +#undef FN_SIG_S_B + +#define FN_SIG_B_B_P(__fn, __type1, __length1, __type2, __length2, __type3) \ + void __fn(__type1 p_buf1, __length1 length1, __type2 p_buf2, __length2 length2, __type3 p_priv); +LIST_FN_SIG_B_B_P +#undef FN_SIG_B_B_P + +#define FN_SIG_S_P(__fn, __s, __type) void __fn(__s p_s, __type p_priv); +LIST_FN_SIG_S_P +#undef FN_SIG_S_P + +#define FN_SIG_S_B_P(__fn, __s, __type, __length, __type_ptr) \ + void __fn(__s p_s, __type p_buf, __length length, __type_ptr p_priv); +LIST_FN_SIG_S_B_P +#undef FN_SIG_S_B_P + +#define FN_SIG_S_B_B_P(__fn, __s, __type1, __length1, __type2, __length2, __type_ptr) \ + void __fn(__s p_s, __type1 p_buf1, __length1 length1, __type2 p_buf2, __length2 length2, __type_ptr p_priv); +LIST_FN_SIG_S_B_B_P +#undef FN_SIG_S_B_B_P + + + +/* 1 - define the size check arrays */ +#define FN_SIG_NONE(__fn) + +#define FN_SIG_S(__fn, __s) sizeof(*((__s)0)), + +#define FN_SIG_P(__fn, __type) + +#define FN_SIG_S_B(__fn, __s, __type, __length) sizeof(*((__s)0)), + +#define FN_SIG_B_B_P(__fn, __type1, __length1, __type2, __length2, __type3) sizeof(*((__s)0)), + +#define FN_SIG_S_P(__fn, __s, __type) sizeof(*((__s)0)), + +#define FN_SIG_S_B_P(__fn, __s, __type, __length, __type_ptr) sizeof(*((__s)0)), + +#define FN_SIG_S_B_B_P(__fn, __s, __type1, __length1, __type2, __length2, __type3) sizeof(*((__s)0)), + +static uint8_t m_size_s[] = { LIST_FN_SIG_S }; +static uint8_t m_size_s_b[] = { LIST_FN_SIG_S_B }; +static uint8_t m_size_s_p[] = { LIST_FN_SIG_S_P }; +static uint8_t m_size_s_b_p[] = { LIST_FN_SIG_S_B_P }; +static uint8_t m_size_s_b_b_p[] = { LIST_FN_SIG_S_B_B_P }; + +#undef FN_SIG_NONE +#undef FN_SIG_S +#undef FN_SIG_P +#undef FN_SIG_S_B +#undef FN_SIG_B_B_P +#undef FN_SIG_S_P +#undef FN_SIG_S_B_P +#undef FN_SIG_S_B_B_P + +/* 2- build the enumerations list */ +#define FN_SIG_NONE(__fn) fn_index_##__fn, +#define FN_SIG_S(__fn, __s) FN_SIG_NONE(__fn) +#define FN_SIG_P(__fn, __type) FN_SIG_NONE(__fn) +#define FN_SIG_S_B(__fn, __s, __type, __length) FN_SIG_NONE(__fn) +#define FN_SIG_B_B_P(__fn, __type1, __length1, __type2, __length2, __type3) FN_SIG_NONE(__fn) +#define FN_SIG_S_P(__fn, __s, __type) FN_SIG_NONE(__fn) +#define FN_SIG_S_B_P(__fn, __s, __type, __length, __type_ptr) FN_SIG_NONE(__fn) +#define FN_SIG_S_B_B_P(__fn, __s, __type1, __length1, __type2, __length2, __type3) FN_SIG_NONE(__fn) + +/* Build the list of function indexes in the deserialization array */ +enum { LIST_FN_SIG_NONE fn_none_index_max }; +enum { LIST_FN_SIG_S fn_s_index_max }; +enum { LIST_FN_SIG_P fn_p_index_max }; +enum { LIST_FN_SIG_S_B fn_s_b_index_max }; +enum { LIST_FN_SIG_B_B_P fn_b_b_p_index_max }; +enum { LIST_FN_SIG_S_P fn_s_p_index_max }; +enum { LIST_FN_SIG_S_B_P fn_s_b_p_index_max }; +enum { LIST_FN_SIG_S_B_B_P fn_s_b_b_p_index_max }; + +#undef FN_SIG_NONE +#undef FN_SIG_S +#undef FN_SIG_P +#undef FN_SIG_S_B +#undef FN_SIG_B_B_P +#undef FN_SIG_S_P +#undef FN_SIG_S_B_P +#undef FN_SIG_S_B_B_P + +/* 3- build the array */ +#define FN_SIG_NONE(__fn) [fn_index_##__fn] = (void*)__fn, +#define FN_SIG_S(__fn, __s) FN_SIG_NONE(__fn) +#define FN_SIG_P(__fn, __type) FN_SIG_NONE(__fn) +#define FN_SIG_S_B(__fn, __s, __type, __length) FN_SIG_NONE(__fn) +#define FN_SIG_B_B_P(__fn, __type1, __length1, __type2, __length2, __type3) FN_SIG_NONE(__fn) +#define FN_SIG_S_P(__fn, __s, __type) FN_SIG_NONE(__fn) +#define FN_SIG_S_B_P(__fn, __s, __type, __length, __type_ptr) FN_SIG_NONE(__fn) +#define FN_SIG_S_B_B_P(__fn, __s, __type1, __length1, __type2, __length2, __type3) FN_SIG_NONE(__fn) + +static void (*m_fct_none[])(void) = { LIST_FN_SIG_NONE }; +static void (*m_fct_s[])(void * structure) = { LIST_FN_SIG_S }; +static void (*m_fct_p[])(void * pointer) = { LIST_FN_SIG_P }; +static void (*m_fct_s_b[])(void * structure, void * buffer, uint8_t length) = { LIST_FN_SIG_S_B }; +static void (*m_fct_b_b_p[])(void * buffer1, uint8_t length1, void * buffer2, uint8_t length2, void * pointer) = { LIST_FN_SIG_B_B_P }; +static void (*m_fct_s_p[])(void * structure, void * pointer) = { LIST_FN_SIG_S_P }; +static void (*m_fct_s_b_p[])(void * structure, void * buffer, uint8_t length, void * pointer) = { LIST_FN_SIG_S_B_P }; +static void (*m_fct_s_b_b_p[])(void * structure, void * buffer1, uint8_t length1, void * buffer2, uint8_t length2, void * pointer) = { LIST_FN_SIG_S_B_B_P }; + +static const uint8_t * deserialize_struct(const uint8_t *p, const uint8_t **pp_struct, uint8_t *p_struct_length) { + uint8_t struct_length; + + struct_length = *p++; + *pp_struct = p; + *p_struct_length = struct_length; + + return p + struct_length; +} + +static const uint8_t * deserialize_buf(const uint8_t *p, const uint8_t **pp_buf, uint16_t *p_buflen) { + uint8_t b; + uint16_t buflen; + + /* Get the current byte */ + b = *p++; + buflen = b & 0x7F; + if (b & 0x80) { + /* Get the current byte */ + b = *p++; + buflen += (uint16_t)b << 7; + } + + /* Return the values */ + *pp_buf = p; + *p_buflen = buflen; + p += buflen; + return p; +} + +static void deserialize_none(uint8_t fn_index, const uint8_t * p_buf, uint16_t length) { + (void)p_buf; + if (length != 0) + panic(-1); + m_fct_none[fn_index](); +} + +static void deserialize_s(uint8_t fn_index, const uint8_t * p_buf, uint16_t length) { + const uint8_t *p_struct_data; + uint8_t struct_length; + const uint8_t *p; + + p = deserialize_struct(p_buf, &p_struct_data, &struct_length); + + if ((length != (p - p_buf)) || + (struct_length != m_size_s[fn_index])) + panic(-1); + + { + /* Always align structures on word boundary */ + uintptr_t struct_data[(struct_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + + memcpy(struct_data, p_struct_data, struct_length); + + m_fct_s[fn_index](struct_data); + } +} + +static void deserialize_p(uint8_t fn_index, const uint8_t * p_buf, uint16_t length) { + uintptr_t p_priv; + + if (length != 4) + panic(-1); + + /* little endian conversion */ + p_priv = p_buf[0] | (p_buf[1] << 8) | (p_buf[2] << 16) | (p_buf[3] << 24); + + m_fct_p[fn_index]((void *)p_priv); +} + +static void deserialize_s_b(uint8_t fn_index, const uint8_t * p_buf, uint16_t length) { + const uint8_t *p_struct_data; + uint8_t struct_length; + const uint8_t *p_vbuf; + uint16_t vbuf_length; + const uint8_t *p; + + p = deserialize_struct(p_buf, &p_struct_data, &struct_length); + p = deserialize_buf(p, &p_vbuf, &vbuf_length); + + if ((length != (p - p_buf)) || + (struct_length != m_size_s_b[fn_index])) + panic(-1); + + { + /* Always align structures on word boundary */ + uintptr_t struct_data[(struct_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + uintptr_t vbuf[(vbuf_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + void * buf = NULL; + + memcpy(struct_data, p_struct_data, struct_length); + + if (vbuf_length) { + memcpy(vbuf, p_vbuf, vbuf_length); + buf = vbuf; + } + + m_fct_s_b[fn_index](struct_data, buf, vbuf_length); + } +} + +static void deserialize_b_b_p(uint8_t fn_index, const uint8_t * p_buf, uint16_t length) { + const uint8_t *p_vbuf1; + uint16_t vbuf1_length; + const uint8_t *p_vbuf2; + uint16_t vbuf2_length; + uintptr_t p_priv; + const uint8_t *p; + + p = deserialize_buf(p_buf, &p_vbuf1, &vbuf1_length); + p = deserialize_buf(p, &p_vbuf2, &vbuf2_length); + p += 4; + + if (length != (p - p_buf)) + panic(-1); + + { + /* Always align structures on word boundary */ + uintptr_t vbuf1[(vbuf1_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + uintptr_t vbuf2[(vbuf2_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + void * buf1 = NULL; + void * buf2 = NULL; + + if (vbuf1_length) { + memcpy(vbuf1, p_vbuf1, vbuf1_length); + buf1 = vbuf1; + } + + if (vbuf2_length) { + memcpy(vbuf2, p_vbuf2, vbuf2_length); + buf2 = vbuf2; + } + p = p_vbuf2 + vbuf2_length; + + /* little endian conversion */ + p_priv = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); + + m_fct_b_b_p[fn_index](buf1, vbuf1_length, buf2, vbuf2_length, (void *)p_priv); + } +} + +static void deserialize_s_p(uint8_t fn_index, const uint8_t * p_buf, uint16_t length) +{ + const uint8_t *p_struct_data; + uint8_t struct_length; + uintptr_t p_priv; + const uint8_t *p; + + p = deserialize_struct(p_buf, &p_struct_data, &struct_length); + p += 4; + + if ((length != (p - p_buf)) || + (struct_length != m_size_s_p[fn_index])) + panic(-1); + + { + /* Always align structures on word boundary */ + uintptr_t struct_data[(struct_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + + memcpy(struct_data, p_struct_data, struct_length); + p = p_struct_data + struct_length; + + /* little endian conversion */ + p_priv = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); + + m_fct_s_p[fn_index](struct_data, (void *)p_priv); + } +} + +static void deserialize_s_b_p(uint8_t fn_index, const uint8_t * p_buf, uint16_t length) +{ + const uint8_t *p_struct_data; + uint8_t struct_length; + const uint8_t *p_vbuf; + uint16_t vbuf_length; + uintptr_t p_priv; + const uint8_t *p; + + p = deserialize_struct(p_buf, &p_struct_data, &struct_length); + p = deserialize_buf(p, &p_vbuf, &vbuf_length); + p += 4; + + if ((length != (p - p_buf)) || + (struct_length != m_size_s_b_p[fn_index])) + panic(-1); + + { + /* Always align structures on word boundary */ + uintptr_t struct_data[(struct_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + uintptr_t vbuf[(vbuf_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + void * buf = NULL; + + memcpy(struct_data, p_struct_data, struct_length); + + if (vbuf_length) { + memcpy(vbuf, p_vbuf, vbuf_length); + buf = vbuf; + } + p = p_vbuf + vbuf_length; + + /* little endian conversion */ + p_priv = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); + + m_fct_s_b_p[fn_index](struct_data, buf, vbuf_length, (void *)p_priv); + } +} + +static void deserialize_s_b_b_p(uint8_t fn_index, const uint8_t * p_buf, uint16_t length) { + const uint8_t *p_struct_data; + uint8_t struct_length; + const uint8_t *p_vbuf1; + uint16_t vbuf1_length; + const uint8_t *p_vbuf2; + uint16_t vbuf2_length; + uintptr_t p_priv; + const uint8_t *p; + + p = deserialize_struct(p_buf, &p_struct_data, &struct_length); + p = deserialize_buf(p, &p_vbuf1, &vbuf1_length); + p = deserialize_buf(p, &p_vbuf2, &vbuf2_length); + p += 4; + if ((length != (p - p_buf)) || + (struct_length != m_size_s_b_b_p[fn_index])) + panic(-1); + + { + /* Always align structures on word boundary */ + uintptr_t struct_data[(struct_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + uintptr_t vbuf1[(vbuf1_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + uintptr_t vbuf2[(vbuf2_length + (sizeof(uintptr_t) - 1))/(sizeof(uintptr_t))]; + void * buf1 = NULL; + void * buf2 = NULL; + + memcpy(struct_data, p_struct_data, struct_length); + + if (vbuf1_length) { + memcpy(vbuf1, p_vbuf1, vbuf1_length); + buf1 = vbuf1; + } + if (vbuf2_length) { + memcpy(vbuf2, p_vbuf2, vbuf2_length); + buf2 = vbuf2; + } + + p = p_vbuf2 + vbuf2_length; + + /* little endian conversion */ + p_priv = p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); + + m_fct_s_b_b_p[fn_index](struct_data, buf1, vbuf1_length, buf2, vbuf2_length, (void *)p_priv); + } +} + +void rpc_deserialize(const uint8_t * p_buf, uint16_t length) { + + uint8_t fn_index; + uint8_t sig_type; + + if (NULL != p_buf) { + sig_type = p_buf[0]; + fn_index = p_buf[1]; + + p_buf += 2; + length -= 2; + + switch(sig_type) { + case SIG_TYPE_NONE: + if (sizeof(m_fct_none)) + deserialize_none(fn_index, p_buf, length); + break; + case SIG_TYPE_S: + if (sizeof(m_fct_s)) + deserialize_s(fn_index, p_buf, length); + break; + case SIG_TYPE_P: + if (sizeof(m_fct_p)) + deserialize_p(fn_index, p_buf, length); + break; + case SIG_TYPE_S_B: + if (sizeof(m_fct_s_b)) + deserialize_s_b(fn_index, p_buf, length); + break; + case SIG_TYPE_B_B_P: + if (sizeof(m_fct_b_b_p)) + deserialize_b_b_p(fn_index, p_buf, length); + break; + case SIG_TYPE_S_P: + if (sizeof(m_fct_s_p)) + deserialize_s_p(fn_index, p_buf, length); + break; + case SIG_TYPE_S_B_P: + if (sizeof(m_fct_s_b_p)) + deserialize_s_b_p(fn_index, p_buf, length); + break; + case SIG_TYPE_S_B_B_P: + if (sizeof(m_fct_s_b_b_p)) + deserialize_s_b_b_p(fn_index, p_buf, length); + break; + default: + panic(-1); + break; + } + } +} diff --git a/system/libarc32_arduino101/drivers/rpc/rpc_functions_to_ble_core.h b/system/libarc32_arduino101/drivers/rpc/rpc_functions_to_ble_core.h new file mode 100644 index 00000000..5f742208 --- /dev/null +++ b/system/libarc32_arduino101/drivers/rpc/rpc_functions_to_ble_core.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RPC_FUNCTIONS_TO_BLE_CORE_H_ +#define RPC_FUNCTIONS_TO_BLE_CORE_H_ + +#include "gap_internal.h" +#include "gatt_internal.h" + +/* declare the list of functions sorted by signature */ +#define LIST_FN_SIG_NONE \ + FN_SIG_NONE(nble_gap_start_adv_req) \ + FN_SIG_NONE(nble_gap_stop_scan_req) + +#define LIST_FN_SIG_S \ + FN_SIG_S(nble_gap_set_adv_data_req, \ + struct nble_gap_ad_data_params *) \ + FN_SIG_S(nble_gap_set_adv_params_req, \ + struct nble_gap_adv_params *) \ + FN_SIG_S(nble_gap_start_scan_req, \ + const struct nble_gap_scan_params *) \ + FN_SIG_S(nble_gap_sm_config_req, \ + const struct nble_gap_sm_config_params *) \ + FN_SIG_S(nble_gap_sm_passkey_reply_req, \ + const struct nble_gap_sm_key_reply_req_params *) \ + FN_SIG_S(nble_gap_sm_bond_info_req, \ + const struct nble_gap_sm_bond_info_param *) \ + FN_SIG_S(nble_gap_sm_security_req, \ + const struct nble_gap_sm_security_params *) \ + FN_SIG_S(nble_gap_sm_clear_bonds_req, \ + const struct nble_gap_sm_clear_bond_req_params *) \ + FN_SIG_S(nble_set_bda_req, const struct nble_set_bda_params *) \ + FN_SIG_S(nble_gap_conn_update_req, \ + const struct nble_gap_connect_update_params *) \ + FN_SIG_S(nble_gattc_discover_req, \ + const struct nble_discover_params *) \ + FN_SIG_S(nble_gatts_wr_reply_req, \ + const struct nble_gatts_wr_reply_params *) \ + FN_SIG_S(nble_uas_rssi_calibrate_req, \ + const struct nble_uas_rssi_calibrate *) \ + FN_SIG_S(nble_gap_service_write_req, \ + const struct nble_gap_service_write_params *) \ + FN_SIG_S(nble_gap_disconnect_req, \ + const struct nble_gap_disconnect_req_params *) \ + FN_SIG_S(nble_gattc_read_req, \ + const struct ble_gattc_read_params *) \ + FN_SIG_S(nble_gap_tx_power_req, \ + const struct nble_gap_tx_power_params *) \ + FN_SIG_S(nble_get_version_req, \ + const struct nble_gap_get_version_param *) + +#define LIST_FN_SIG_P \ + FN_SIG_P(nble_gap_dtm_init_req, void *) \ + FN_SIG_P(nble_gap_read_bda_req, void *) \ + FN_SIG_P(nble_gap_stop_adv_req, void *) \ + FN_SIG_P(nble_gap_cancel_connect_req, void *) + +#define LIST_FN_SIG_S_B \ + FN_SIG_S_B(nble_gatt_register_req, \ + const struct nble_gatt_register_req *, \ + uint8_t *, uint16_t) \ + FN_SIG_S_B(nble_gatt_send_notif_req, \ + const struct nble_gatt_send_notif_params *, \ + const uint8_t *, uint16_t) \ + FN_SIG_S_B(nble_gatt_send_ind_req, \ + const struct nble_gatt_send_ind_params *, \ + const uint8_t *, uint8_t) \ + FN_SIG_S_B(nble_gatts_rd_reply_req, \ + const struct nble_gatts_rd_reply_params *, \ + uint8_t *, uint16_t) \ + FN_SIG_S_B(nble_gattc_write_req, \ + const struct ble_gattc_write_params *, \ + const uint8_t *, uint8_t) \ + FN_SIG_S_B(nble_gattc_read_multiple_req, \ + const struct ble_gattc_read_multiple_params *, \ + const uint16_t *, uint16_t) + +#define LIST_FN_SIG_B_B_P + +#define LIST_FN_SIG_S_P \ + FN_SIG_S_P(nble_gap_connect_req, \ + const struct nble_gap_connect_req_params *, void *) \ + FN_SIG_S_P(nble_gap_set_rssi_report_req, \ + const struct nble_rssi_report_params *, void *) \ + FN_SIG_S_P(nble_gap_dbg_req, \ + const struct nble_debug_params *, \ + void *) + +#define LIST_FN_SIG_S_B_P + +#define LIST_FN_SIG_S_B_B_P + +#endif /* RPC_FUNCTIONS_TO_BLE_CORE_H_ */ diff --git a/system/libarc32_arduino101/drivers/rpc/rpc_functions_to_quark.h b/system/libarc32_arduino101/drivers/rpc/rpc_functions_to_quark.h new file mode 100644 index 00000000..b08005e6 --- /dev/null +++ b/system/libarc32_arduino101/drivers/rpc/rpc_functions_to_quark.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RPC_FUNCTIONS_TO_QUARK_H_ +#define RPC_FUNCTIONS_TO_QUARK_H_ + +#include "gap_internal.h" +#include "gatt_internal.h" +#include "gap_internal.h" + +/* declare the list of functions sorted by signature */ +#define LIST_FN_SIG_NONE \ + FN_SIG_NONE(on_nble_up) + +#define LIST_FN_SIG_S \ + FN_SIG_S(on_nble_get_version_rsp, \ + const struct nble_version_response *) \ + FN_SIG_S(on_nble_gap_connect_evt, \ + const struct nble_gap_connect_evt *) \ + FN_SIG_S(on_nble_gap_disconnect_evt, \ + const struct nble_gap_disconnect_evt *) \ + FN_SIG_S(on_nble_gap_conn_update_evt, \ + const struct nble_gap_conn_update_evt *) \ + FN_SIG_S(on_nble_gap_sm_status_evt, \ + const struct nble_gap_sm_status_evt *) \ + FN_SIG_S(on_nble_gap_sm_passkey_display_evt, \ + const struct nble_gap_sm_passkey_disp_evt *) \ + FN_SIG_S(on_nble_gap_sm_passkey_req_evt, \ + const struct nble_gap_sm_passkey_req_evt *) \ + FN_SIG_S(on_nble_gap_rssi_evt, \ + const struct nble_gap_rssi_evt *) \ + FN_SIG_S(on_nble_common_rsp, \ + const struct nble_response *) \ + FN_SIG_S(on_nble_gap_connect_rsp, \ + const struct nble_response *) \ + FN_SIG_S(on_nble_gap_cancel_connect_rsp, \ + const struct nble_response *) \ + FN_SIG_S(on_nble_gap_read_bda_rsp, \ + const struct nble_service_read_bda_response *) \ + FN_SIG_S(on_nble_gap_sm_config_rsp, \ + struct nble_gap_sm_config_rsp *) \ + FN_SIG_S(on_nble_gap_sm_common_rsp, \ + const struct nble_gap_sm_response *) \ + FN_SIG_S(on_nble_set_bda_rsp, \ + const struct nble_set_bda_rsp *) \ + FN_SIG_S(on_nble_gap_set_rssi_report_rsp, \ + const struct nble_response *) \ + FN_SIG_S(on_nble_gap_dbg_rsp, \ + const struct nble_debug_resp *) \ + FN_SIG_S(on_nble_gap_dir_adv_timeout_evt, \ + const struct nble_gap_dir_adv_timeout_evt *) \ + FN_SIG_S(on_nble_gatts_send_notif_rsp, \ + const struct nble_gatt_notif_rsp *) \ + FN_SIG_S(on_nble_gatts_send_ind_rsp, \ + const struct nble_gatt_ind_rsp *) \ + FN_SIG_S(on_nble_gap_start_advertise_rsp, \ + const struct nble_response *) \ + FN_SIG_S(on_nble_gap_scan_start_stop_rsp, \ + const struct nble_response *) \ + FN_SIG_S(on_nble_gatts_read_evt, \ + const struct nble_gatt_rd_evt *) \ + FN_SIG_S(on_nble_gatts_write_exec_evt, \ + const struct nble_gatt_wr_exec_evt *) \ + FN_SIG_S(on_nble_uas_bucket_change, \ + const struct nble_uas_bucket_change *) \ + FN_SIG_S(on_nble_gattc_write_rsp, \ + const struct ble_gattc_write_rsp *) \ + FN_SIG_S(on_nble_gap_tx_power_rsp, \ + const struct nble_response *) + +#define LIST_FN_SIG_P \ + FN_SIG_P(on_nble_gap_dtm_init_rsp, void *) + +#define LIST_FN_SIG_S_B \ + FN_SIG_S_B(nble_log, const struct nble_log_s *, char *, \ + uint8_t) \ + FN_SIG_S_B(on_nble_gattc_value_evt, \ + const struct ble_gattc_value_evt *, \ + uint8_t *, uint8_t) \ + FN_SIG_S_B(on_nble_gatts_write_evt, \ + const struct nble_gatt_wr_evt *, \ + const uint8_t *, uint8_t) \ + FN_SIG_S_B(on_nble_gatt_register_rsp, \ + const struct nble_gatt_register_rsp *, \ + const struct nble_gatt_attr_handles *, \ + uint8_t) \ + FN_SIG_S_B(on_nble_gattc_discover_rsp, \ + const struct nble_gattc_discover_rsp *, \ + const uint8_t *, uint8_t) \ + FN_SIG_S_B(on_nble_gap_adv_report_evt, \ + const struct nble_gap_adv_report_evt *, \ + const uint8_t *, uint8_t) \ + FN_SIG_S_B(on_nble_gap_sm_bond_info_rsp, \ + const struct nble_gap_sm_bond_info_rsp*, \ + const bt_addr_le_t *, uint16_t) \ + FN_SIG_S_B(on_nble_gattc_read_rsp, \ + const struct ble_gattc_read_rsp *, \ + uint8_t *, uint8_t) \ + FN_SIG_S_B(on_nble_gattc_read_multiple_rsp, \ + const struct ble_gattc_read_rsp *, \ + uint8_t *, uint8_t) + +#define LIST_FN_SIG_B_B_P + +#define LIST_FN_SIG_S_P + +#define LIST_FN_SIG_S_B_P + +#define LIST_FN_SIG_S_B_B_P + +#endif /* RPC_FUNCTIONS_TO_QUARK_H_ */ diff --git a/system/libarc32_arduino101/drivers/rpc/rpc_serialize.c b/system/libarc32_arduino101/drivers/rpc/rpc_serialize.c new file mode 100644 index 00000000..e2144ee3 --- /dev/null +++ b/system/libarc32_arduino101/drivers/rpc/rpc_serialize.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "rpc.h" + +/* Include the functions called */ +#if defined(CONFIG_QUARK_SE_BLE_CORE) +#include "rpc_functions_to_quark.h" +#elif defined(CONFIG_SOC_QUARK_SE) +#include "rpc_functions_to_ble_core.h" +#elif defined(LINUX_HOST_RUNTIME) +// for the host compilation (to simulate connection to BLE controller) +#include "rpc_functions_to_ble_core.h" +#else +#error "File is compiled but should not" +#endif + +/* Build the functions exposed */ +/* Define the functions identifiers per signature */ +#define FN_SIG_NONE(__fn) fn_index_##__fn, +#define FN_SIG_S(__fn, __s) FN_SIG_NONE(__fn) +#define FN_SIG_P(__fn, __type) FN_SIG_NONE(__fn) +#define FN_SIG_S_B(__fn, __s, __type, __length) FN_SIG_NONE(__fn) +#define FN_SIG_B_B_P(__fn, __type1, __length1, __type2, __length2,__type3) FN_SIG_NONE(__fn) +#define FN_SIG_S_P(__fn, __s, __type) FN_SIG_NONE(__fn) +#define FN_SIG_S_B_P(__fn, __s, __type, __length, __type_ptr) FN_SIG_NONE(__fn) +#define FN_SIG_S_B_B_P(__fn, __s, __type1, __length1, __type2, __length2,__type3) FN_SIG_NONE(__fn) + +/* Build the list of function indexes -> this should match the array at deserialization */ +enum { LIST_FN_SIG_NONE fn_none_index_max }; +enum { LIST_FN_SIG_S fn_s_index_max }; +enum { LIST_FN_SIG_P fn_p_index_max }; +enum { LIST_FN_SIG_S_B fn_s_b_index_max }; +enum { LIST_FN_SIG_B_B_P fn_b_b_p_index_max }; +enum { LIST_FN_SIG_S_P fn_s_p_index_max }; +enum { LIST_FN_SIG_S_B_P fn_s_b_p_index_max }; +enum { LIST_FN_SIG_S_B_B_P fn_s_b_b_p_index_max }; + +/* Implement the functions using serialization API */ +#undef FN_SIG_NONE +#undef FN_SIG_S +#undef FN_SIG_P +#undef FN_SIG_S_B +#undef FN_SIG_B_B_P +#undef FN_SIG_S_P +#undef FN_SIG_S_B_P +#undef FN_SIG_S_B_B_P + +#define FN_SIG_NONE(__fn) \ + void __fn(void) { \ + rpc_serialize_none(fn_index_##__fn); \ + } \ + +#define FN_SIG_S(__fn, __s) \ + void __fn(__s p_s) { \ + rpc_serialize_s(fn_index_##__fn, p_s, sizeof(*p_s)); \ + } \ + +#define FN_SIG_P(__fn, __type) \ + void __fn(__type p_priv) { \ + rpc_serialize_p(fn_index_##__fn, p_priv); \ + } \ + +#define FN_SIG_S_B(__fn, __s, __type, __length) \ + void __fn(__s p_s, __type p_buf, __length length) { \ + rpc_serialize_s_b(fn_index_##__fn, p_s, sizeof(*p_s), p_buf, length); \ + } \ + +#define FN_SIG_B_B_P(__fn, __type1, __length1, __type2, __length2, __type3) \ + void __fn(__type1 p_buf1, __length1 length1, __type2 p_buf2, __length2 length2, __type3 p_priv) { \ + rpc_serialize_b_b_p(fn_index_##__fn, p_buf1, length1, p_buf2, length2, p_priv); \ + } \ + +#define FN_SIG_S_P(__fn, __s, __type) \ + void __fn(__s p_s, __type p_priv) { \ + rpc_serialize_s_p(fn_index_##__fn, p_s, sizeof(*p_s), p_priv); \ + } \ + +#define FN_SIG_S_B_P(__fn, __s, __type, __length, __type_ptr) \ + void __fn(__s p_s, __type p_buf, __length length, __type_ptr p_priv) { \ + rpc_serialize_s_b_p(fn_index_##__fn, p_s, sizeof(*p_s), p_buf, length, p_priv); \ + } + +#define FN_SIG_S_B_B_P(__fn, __s, __type1, __length1, __type2, __length2, __type3) \ + void __fn(__s p_s, __type1 p_buf1, __length1 length1, __type2 p_buf2, __length2 length2, __type3 p_priv) { \ + rpc_serialize_s_b_b_p(fn_index_##__fn, p_s, sizeof(*p_s), p_buf1, length1, p_buf2, length2, p_priv); \ + } \ + + +/* Build the functions */ +LIST_FN_SIG_NONE +LIST_FN_SIG_S +LIST_FN_SIG_P +LIST_FN_SIG_S_B +LIST_FN_SIG_B_B_P +LIST_FN_SIG_S_P +LIST_FN_SIG_S_B_P +LIST_FN_SIG_S_B_B_P + +#define SIG_TYPE_SIZE 1 +#define FN_INDEX_SIZE 1 +#define POINTER_SIZE 4 + +static void _send(uint8_t *buf, uint16_t length) { + rpc_transmit_cb(buf, length); +} + +static uint16_t encoded_structlen(uint8_t structlen) { + return 1 + structlen; +} + +static uint8_t *serialize_struct(uint8_t *p, const uint8_t *struct_data, uint8_t struct_length) { + *p++ = struct_length; + memcpy(p, struct_data, struct_length); + p += struct_length; + return p; +} + +static uint16_t encoded_buflen(const uint8_t *buf, uint16_t buflen) { + if (NULL == buf) + return 1; + else { + if (buflen < (1 << 7)) { + return 1 + buflen; + } + else + return 2 + buflen; + } +} + +static uint8_t *serialize_buf(uint8_t *p, const uint8_t *buf, uint16_t buflen) { + uint16_t varint; + + if (NULL == buf) + buflen = 0; + + varint = buflen; + + *p = varint & 0x7F; + if (varint >= (1 << 7)) { + *p |= 0x80; + p++; + *p = varint >> 7; + } + p++; + memcpy(p, buf, buflen); + p += buflen; + return p; +} + +static uint8_t *serialize_p(uint8_t *p, uintptr_t priv) { + *p++ = priv; + *p++ = (priv >> 8); + *p++ = (priv >> 16); + *p++ = (priv >> 24); + return p; +} + +void rpc_serialize_none(uint8_t fn_index) { + uint16_t length; + uint8_t * buf; + uint8_t * p; + + length = SIG_TYPE_SIZE + FN_INDEX_SIZE; + + p = buf = rpc_alloc_cb(length); + + *p++ = SIG_TYPE_NONE; + *p = fn_index; + + _send(buf, length); +} + +void rpc_serialize_s(uint8_t fn_index, const void * struct_data, uint8_t struct_length) { + uint16_t length; + uint8_t * buf; + uint8_t * p; + + length = SIG_TYPE_SIZE + FN_INDEX_SIZE + encoded_structlen(struct_length); + + p = buf = rpc_alloc_cb(length); + + *p++ = SIG_TYPE_S; + *p++ = fn_index; + p = serialize_struct(p, struct_data, struct_length); + + _send(buf, length); +} + + +void rpc_serialize_p(uint8_t fn_index, void * p_priv) { + uint16_t length; + uint8_t * buf; + uint8_t * p; + uintptr_t priv = (uintptr_t) p_priv; + + length = SIG_TYPE_SIZE + FN_INDEX_SIZE + POINTER_SIZE; + + p = buf = rpc_alloc_cb(length); + + *p++ = SIG_TYPE_P; + *p++ = fn_index; + p = serialize_p(p, priv); + + _send(buf, length); +} + +void rpc_serialize_s_b(uint8_t fn_index, const void * struct_data, uint8_t struct_length, const void * vbuf, uint16_t vbuf_length) { + uint16_t length; + uint8_t * buf; + uint8_t * p; + + length = SIG_TYPE_SIZE + FN_INDEX_SIZE + encoded_structlen(struct_length) + + encoded_buflen(vbuf, vbuf_length); + + p = buf = rpc_alloc_cb(length); + + *p++ = SIG_TYPE_S_B; + *p++ = fn_index; + p = serialize_struct(p, struct_data, struct_length); + p = serialize_buf(p, vbuf, vbuf_length); + + _send(buf, length); +} + +void rpc_serialize_b_b_p(uint8_t fn_index, const void * vbuf1, uint16_t vbuf1_length, const void * vbuf2, uint16_t vbuf2_length, void * p_priv) { + uint16_t length; + uint8_t * buf; + uint8_t * p; + uintptr_t priv = (uintptr_t) p_priv; + + length = SIG_TYPE_SIZE + FN_INDEX_SIZE + encoded_buflen(vbuf1, vbuf1_length) + + encoded_buflen(vbuf2, vbuf2_length) + POINTER_SIZE; + + p = buf = rpc_alloc_cb(length); + + *p++ = SIG_TYPE_B_B_P; + *p++ = fn_index; + p = serialize_buf(p, vbuf1, vbuf1_length); + p = serialize_buf(p, vbuf2, vbuf2_length); + p = serialize_p(p, priv); + + _send(buf, length); +} + +void rpc_serialize_s_p(uint8_t fn_index, const void * struct_data, uint8_t struct_length, void * p_priv) { + uint16_t length; + uint8_t * buf; + uint8_t * p; + uintptr_t priv = (uintptr_t) p_priv; + + length = SIG_TYPE_SIZE + FN_INDEX_SIZE + encoded_structlen(struct_length) + + POINTER_SIZE; + + p = buf = rpc_alloc_cb(length); + + *p++ = SIG_TYPE_S_P; + *p++ = fn_index; + p = serialize_struct(p, struct_data, struct_length); + p = serialize_p(p, priv); + + _send(buf, length); +} + +void rpc_serialize_s_b_p(uint8_t fn_index, const void * struct_data, uint8_t struct_length, + const void * vbuf, uint16_t vbuf_length, void * p_priv) { + uint16_t length; + uint8_t * buf; + uint8_t * p; + uintptr_t priv = (uintptr_t) p_priv; + + length = SIG_TYPE_SIZE + FN_INDEX_SIZE + encoded_structlen(struct_length) + + encoded_buflen(vbuf, vbuf_length) + POINTER_SIZE; + + p = buf = rpc_alloc_cb(length); + + *p++ = SIG_TYPE_S_B_P; + *p++ = fn_index; + p = serialize_struct(p, struct_data, struct_length); + p = serialize_buf(p, vbuf, vbuf_length); + p = serialize_p(p, priv); + + _send(buf, length); +} + +void rpc_serialize_s_b_b_p(uint8_t fn_index, const void * struct_data, uint8_t struct_length, + const void * vbuf1, uint16_t vbuf1_length, const void * vbuf2, uint16_t vbuf2_length, void * p_priv) { + + uint16_t length; + uint8_t * buf; + uint8_t * p; + uintptr_t priv = (uintptr_t) p_priv; + + length = SIG_TYPE_SIZE + FN_INDEX_SIZE + encoded_structlen(struct_length) + + encoded_buflen(vbuf1, vbuf1_length) + + encoded_buflen(vbuf2, vbuf2_length) + POINTER_SIZE; + + p = buf = rpc_alloc_cb(length); + + *p++ = SIG_TYPE_S_B_B_P; + *p++ = fn_index; + p = serialize_struct(p, struct_data, struct_length); + p = serialize_buf(p, vbuf1, vbuf1_length); + p = serialize_buf(p, vbuf2, vbuf2_length); + p = serialize_p(p, priv); + + _send(buf, length); +} diff --git a/system/libarc32_arduino101/framework/include/cfw/cfw.h b/system/libarc32_arduino101/framework/include/cfw/cfw.h index 9c9f040f..311dcf7b 100644 --- a/system/libarc32_arduino101/framework/include/cfw/cfw.h +++ b/system/libarc32_arduino101/framework/include/cfw/cfw.h @@ -113,7 +113,7 @@ typedef struct svc_client_handle_ { * Passed in the conn field of struct cfw_message for request messages */ void * server_handle; -} svc_client_handle_t; +} svc_client_handle_t, cfw_service_conn_t; struct cfw_message * cfw_alloc_message(int size, OS_ERR_TYPE * err); diff --git a/system/libarc32_arduino101/framework/include/cfw/cfw_client.h b/system/libarc32_arduino101/framework/include/cfw/cfw_client.h index 2a1ae2ba..2154208f 100644 --- a/system/libarc32_arduino101/framework/include/cfw/cfw_client.h +++ b/system/libarc32_arduino101/framework/include/cfw/cfw_client.h @@ -40,22 +40,6 @@ * @{ */ -/** - * Create a handle to the component framework. - * This handle is to be used for all other requests - * to the component framework - * - * Implementation is different in the master and the slave contexts. - * The master context will be pseudo-synchronous, while the slave - * implementation will actually pass a message to the master context - * in order to register a new client. - * - * \param queue pointer to service queue - * \param cb the callback that will be called for each message reception - * \param param the param passed along with the message to the callback - */ -cfw_handle_t cfw_init(void * queue, handle_msg_cb_t cb, void * param); - /** * Allocate a request message for a service. diff --git a/system/libarc32_arduino101/framework/include/cfw_platform.h b/system/libarc32_arduino101/framework/include/cfw_platform.h index 4e361961..aa5cb29e 100644 --- a/system/libarc32_arduino101/framework/include/cfw_platform.h +++ b/system/libarc32_arduino101/framework/include/cfw_platform.h @@ -39,7 +39,6 @@ extern "C" { #endif void cfw_platform_init(void); -void cfw_platform_nordic_init(void); T_QUEUE cfw_get_service_queue(void); #ifdef __cplusplus diff --git a/system/libarc32_arduino101/framework/include/infra/ipc_uart.h b/system/libarc32_arduino101/framework/include/infra/ipc_uart.h index 5f5cbe76..6c8eab4f 100644 --- a/system/libarc32_arduino101/framework/include/infra/ipc_uart.h +++ b/system/libarc32_arduino101/framework/include/infra/ipc_uart.h @@ -78,25 +78,6 @@ /* optional sync frame payload */ #define SYNC_FRAME_DATA(_frame_) ((unsigned char *)&(_frame_)[20]) -#define IPC_CHANNEL_STATE_CLOSED 0 -#define IPC_CHANNEL_STATE_OPEN 1 - -#define IPC_UART_MAX_CHANNEL 4 - -struct ipc_uart_channels { - uint16_t index; - uint16_t state; - void (*cb) (uint8_t cpu_id, int chan, int len, void * data); -}; - -void * uart_ipc_channel_open(int channel, void(*cb)(uint8_t cpu_id, int chan, int len, void * data)); -int uart_ipc_send_message(void * handle, int len, void *p_data); -void uart_ipc_set_channel(void * ipc_channel); -void * uart_ipc_get_channel(void); -int uart_ipc_send_sync_resp(int channel, int request_id, int param1, int param2, void * ptr); -void uart_ipc_init(int num); -void uart_ipc_disable(int num); - /** @} */ #endif /* _IPC_UART_H_ */ diff --git a/system/libarc32_arduino101/framework/include/infra/log.h b/system/libarc32_arduino101/framework/include/infra/log.h index 528b0d99..07848310 100644 --- a/system/libarc32_arduino101/framework/include/infra/log.h +++ b/system/libarc32_arduino101/framework/include/infra/log.h @@ -36,6 +36,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /** * @defgroup infra_log Log * @ingroup infra @@ -260,7 +264,11 @@ void log_resume(); * @param module the ID of the log module related to this message * @param format the printf-like string format */ -#define pr_debug(module, format,...) pr_debug_ ## module(format, ##__VA_ARGS__) +#define pr_debug(module, format,...) log_printk(LOG_LEVEL_DEBUG, module, format, ##__VA_ARGS__) + +#ifdef __cplusplus +} +#endif /** @} */ diff --git a/system/libarc32_arduino101/framework/include/log_modules b/system/libarc32_arduino101/framework/include/log_modules index c7246635..401bbae9 100644 --- a/system/libarc32_arduino101/framework/include/log_modules +++ b/system/libarc32_arduino101/framework/include/log_modules @@ -25,3 +25,4 @@ DEFINE_LOGGER_MODULE(LOG_MODULE_DRV, "DRV", 0) DEFINE_LOGGER_MODULE(LOG_MODULE_CUNIT, "CUNIT", 0) DEFINE_LOGGER_MODULE(LOG_MODULE_CFW, "CFW", 0) DEFINE_LOGGER_MODULE(LOG_MODULE_GPIO_SVC, "GPIO_SVC", 0) +DEFINE_LOGGER_MODULE(LOG_MODULE_APP, "APP", 0) diff --git a/system/libarc32_arduino101/framework/include/panic_api.h b/system/libarc32_arduino101/framework/include/panic_api.h index 70aea89c..5a579280 100644 --- a/system/libarc32_arduino101/framework/include/panic_api.h +++ b/system/libarc32_arduino101/framework/include/panic_api.h @@ -29,5 +29,5 @@ */ // TODO - replace with a proper implementation of panic() -#define panic(x) _do_fault(); +//#define panic(x) _do_fault(); #define force_panic() panic(-1) diff --git a/system/libarc32_arduino101/framework/include/services/ble/ble_service.h b/system/libarc32_arduino101/framework/include/services/ble/ble_service.h index fa14941b..8917e61c 100644 --- a/system/libarc32_arduino101/framework/include/services/ble/ble_service.h +++ b/system/libarc32_arduino101/framework/include/services/ble/ble_service.h @@ -33,29 +33,205 @@ #include +/* For MSG_ID_BLE_SERVICE_BASE */ +#include "services/services_ids.h" + +/* For bt_uuid */ +#include "bluetooth/gatt.h" + +#include "bluetooth/bluetooth.h" + +#include "cfw/cfw.h" + +/* Forward declarations */ +struct _ble_service_cb; +struct bt_conn; + /** - * @addtogroup ble_service + * @cond + * @defgroup ble_service BLE Service + * + * Bluetooth Low Energy (BLE) application level service. + * + * This service provides BLE service, abstracting most of the complexity of the underlying BLE services/profiles. + * + * @ingroup services + * * @{ + */ + +/* + * CFW Message ID base definitions for BLE services. + */ + +/* BLE Service Message ID definitions. */ +#define MSG_ID_BLE_SERVICE_RSP (MSG_ID_BLE_SERVICE_BASE + 0x40) +#define MSG_ID_BLE_SERVICE_EVT (MSG_ID_BLE_SERVICE_BASE + 0x80) + +/** BLE High level Message IDs used for request, response, events. */ +enum BLE_MSG_ID { + MSG_ID_BLE_ENABLE_RSP = MSG_ID_BLE_SERVICE_RSP, /**< Message ID for enable response, of type @ref ble_enable_rsp */ + MSG_ID_BLE_INIT_SVC_RSP, /**< Message ID for init service response, of type @ref ble_init_svc_rsp */ + + /* BLE direct test mode command */ + MSG_ID_BLE_DBG_RSP, /**< Message ID for DTM command response, of type @ref ble_dbg_req_rsp */ + + MSG_ID_BLE_SERVICE_RSP_LAST, + + /* events */ + MSG_ID_BLE_ADV_TO_EVT = MSG_ID_BLE_SERVICE_EVT, /**< Message ID for struct @ref ble_adv_to_evt */ + MSG_ID_BLE_SERVICE_EVT_LAST +}; + +/** Macro to convert milliseconds to a specific unit */ +#define MSEC_TO_1_25_MS_UNITS(TIME) (((TIME) * 1000) / 1250) +#define MSEC_TO_10_MS_UNITS(TIME) ((TIME) / 10) + +#define BLE_GAP_SEC_RAND_LEN 8 /**< Random Security number length (64 bits) */ +#define BLE_GAP_SEC_MAX_KEY_LEN 16 /**< Maximum security key len (LTK, CSRK) */ + +/** + * Advertisement options. + */ +enum BLE_GAP_ADV_OPTIONS { + BLE_GAP_OPT_ADV_DEFAULT = 0, /**< no specific option */ + BLE_GAP_OPT_ADV_WHITE_LISTED = 0x02 /**< use white list and only report whitelisted devices */ +}; + +/** + * LE security modes. * + * see BT spec PART C, 10.2 + * + * - Security mode 1 + * - Level 1: No security at all (service may use data signing) + * - Level 2: Unauthenticated (no MITM protection pairing with encryption + * - Level 3: Authenticated (MITM protection) pairing with encryption + * - Level 4: Authenticated (MITM protection) LE Secure Connection wi + * + * - Security mode 2 (data signing) + * - Level 1: Unauthenticated pairing with data signing + * - Level 2: Authenticated (MITM protection) with data signing */ +enum BLE_GAP_SEC_MODES { + GAP_SEC_NO_PERMISSION = 0, /**< No access permitted. */ + GAP_SEC_LEVEL_1, + GAP_SEC_LEVEL_2, + GAP_SEC_LEVEL_3, + GAP_SEC_LEVEL_4, + GAP_SEC_MODE_1 = 0x10, + GAP_SEC_MODE_2 = 0x20 /**< only used for data signing, level 1 or 2 */ +}; -/** BLE response/event status codes. */ -enum BLE_STATUS { - BLE_STATUS_SUCCESS = 0, /**< General BLE Success code */ - 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_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_GAP_BASE = 0x100, /**< GAP specific error base */ - BLE_STATUS_GATT_BASE = 0x200, /**< GATT specific Error base */ +/** + * Security manager passkey type. + */ +enum BLE_GAP_SM_PASSKEY_TYPE { + BLE_GAP_SM_PK_NONE = 0, /**< No key (may be used to reject). */ + BLE_GAP_SM_PK_PASSKEY, /**< Security data is a 6-digit passkey. */ + BLE_GAP_SM_PK_OOB, /**< Security data is 16 bytes of OOB data */ }; -typedef uint16_t ble_status_t; /**< Response and event BLE service status type @ref BLE_STATUS */ +/** + * Connection Parameter update request event. + */ +struct ble_gap_conn_param_update_req_evt { + struct bt_le_conn_param param; +}; + +/* - BLE_SERVICE_GAP_API.H */ + + +/** Generic BLE status response message. */ +struct ble_rsp { + struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ + int status; /**< Response status */ +}; + +/** Generic BLE response with connection reference and status. */ +struct ble_conn_rsp { + struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ + struct bt_conn *conn; /**< Connection reference */ + int status; /**< Status */ +}; + +/** BLE Enable configuration options. */ +struct ble_enable_config { + bt_addr_le_t * p_bda; /**< Optional BT device address. If NULL, internal unique static random will be used */ + struct bt_le_conn_param central_conn_params; /**< Central supported range */ +}; + +/** Parameters of MSG_ID_BLE_ENABLE_RSP. */ +struct ble_enable_rsp { + struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ + int status; /**< Response status */ + uint8_t enable; /**< Enable state: 0:Disabled, 1:Enabled */ + bt_addr_le_t bd_addr; +}; + +/** + * Attribute handle range definition. + */ +struct ble_gatt_handle_range { + uint16_t start_handle; + uint16_t end_handle; +}; + +/** Parameters of the current connection. */ +struct ble_connection_values { + uint16_t interval; /**< Connection interval (unit 1.25 ms) */ + uint16_t latency; /**< Connection latency (unit interval) */ + uint16_t supervision_to; /**< Connection supervision timeout (unit 10ms)*/ +}; + +/** Parameters for @ref MSG_ID_BLE_INIT_SVC_RSP. */ +struct ble_init_svc_rsp { + struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ + int status; +}; + +/** Authentication data. */ +struct ble_auth_data { + union { + uint8_t passkey[6]; /**< 6 digit key (000000 - 999999) */ + uint8_t obb_data[16]; /**< 16 byte of OBB data */ + }; + uint8_t type; /**< @ref BLE_GAP_SM_PASSKEY_TYPE */ +}; + +/** Parameters for @ref MSG_ID_BLE_ADV_TO_EVT. */ +struct ble_adv_to_evt { + struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ +}; + +/** + * BLE debug rsp message. + */ + +/* + * BLE debug req message. + */ +struct ble_dbg_req_rsp { + struct cfw_message header; + uint32_t u0; + uint32_t u1; +}; + +/** Enable/Disable BLE stack. To be called before any BLE service related call. + * + * @param p_service_conn client service connection (cfw service connection) + * @param enable 1: enable BLE stack 0: disable BLE stack + * @param p_config configuration parameters when enabling BLE. shall be null in case of BLE disable. @ref ble_enable_config + * @param p_priv pointer to private structure returned in a response + * + * @return @ref OS_ERR_TYPE + * @note Expected notification: + * - Message with @ref MSG_ID_BLE_ENABLE_RSP and type @ref ble_enable_rsp. + */ +int ble_service_enable(cfw_service_conn_t * p_service_conn, uint8_t enable, + const struct ble_enable_config * p_config, + void *p_priv); + +/** @endcond */ /** @}*/ #endif diff --git a/system/libarc32_arduino101/framework/include/services/ble/ble_service_gap_api.h b/system/libarc32_arduino101/framework/include/services/ble/ble_service_gap_api.h deleted file mode 100644 index 6fd62cec..00000000 --- a/system/libarc32_arduino101/framework/include/services/ble/ble_service_gap_api.h +++ /dev/null @@ -1,1036 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __BLE_SERVICE_GAP_H__ -#define __BLE_SERVICE_GAP_H__ - -#include "cfw/cfw.h" -#include "cfw/cfw_client.h" -#include "infra/version.h" -#include "ble_service_msg.h" -#include "ble_service.h" - -/** - * @defgroup ble_core_service BLE Core Service GAP/GATT APIs - * - * BLE Core service GAP/GATT APIs used by BLE service. - * - * @ingroup ble_service - * @{ - */ - -/** Macro to convert milliseconds to a specific unit */ -#define MSEC_TO_0_625_MS_UNITS(TIME) (((TIME) * 1000) / 625) -#define MSEC_TO_1_25_MS_UNITS(TIME) (((TIME) * 1000) / 1250) -#define MSEC_TO_10_MS_UNITS(TIME) ((TIME) / 10) - -/** - * BLE GAP Status return codes. - */ -enum BLE_SVC_GAP_STATUS_CODES { - BLE_SVC_GAP_STATUS_SUCCESS = BLE_STATUS_SUCCESS, /**< GAP success */ - BLE_SVC_GAP_STATUS_ERROR = BLE_STATUS_GATT_BASE, /**< Generic GAP error */ - BLE_SVC_GAP_STATUS_INVALID_UUID_LIST, /**< Invalid UUID list provided (e.g. advertisement) */ - /* TODO: add more status codes */ -}; - -/** - * BLE GAP addr types. - * - * BLE GAP supported address types - */ -enum BLE_ADDR_TYPES { - BLE_ADDR_PUBLIC = 0, /**< BD address assigned by IEEE */ - BLE_ADDR_PRIVATE_RANDOM_STATIC, /**< Random address */ - BLE_ADDR_RRIVATE_RANDOM_PRIVATE_RESOLVABLE, /**< Resolvable Private Random address */ - BLE_ADDR_PRIVATE_RANDOM_PRIVATE_NONRESOLVABLE /**< Non-resolvable Private Random address */ -}; - -/** - * BT/BLE address Length. - */ -#define BLE_ADDR_LEN 6 - -#define BLE_GAP_SEC_RAND_LEN 8 /**< Random Security number length (64 bits) */ -#define BLE_GAP_SEC_MAX_KEY_LEN 16 /**< Maximum security key len (LTK, CSRK) */ - -#define BLE_SVC_GAP_HANDLE_INVALID 0xffff /**< Invalid GAP connection handle */ - -/** - * Device GAP name characteristic write permission. - * - * If the characteristic shall be writable, use a combination of the values - * defined in @ref BLE_GAP_SEC_MODES - */ -#define BLE_DEVICE_NAME_WRITE_PERM GAP_SEC_NO_PERMISSION - -typedef struct { - uint8_t type; /**< BLE Address type @ref BLE_ADDR_TYPES */ - uint8_t addr[BLE_ADDR_LEN]; - /**< BD address, little endian format */ -} ble_addr_t; - -/** - * GAP device roles. - */ -enum BLE_ROLES { - BLE_ROLE_INVALID = 0, - BLE_ROLE_PERIPHERAL = 0x01, - BLE_ROLE_CENTRAL = 0x02 -}; - -typedef uint8_t ble_role_t; - -/** - * BLE core (GAP, GATT) Message IDs used for request, response, events and indications. - */ -enum BLE_GAP_MSG_ID { - MSG_ID_BLE_GAP_WR_CONF_REQ = MSG_ID_BLE_GAP_BASE, - MSG_ID_BLE_GAP_RD_BDA_REQ, - MSG_ID_BLE_GAP_WR_ADV_DATA_REQ, - MSG_ID_BLE_GAP_WR_WHITE_LIST_REQ, - MSG_ID_BLE_GAP_CLR_WHITE_LIST_REQ, - MSG_ID_BLE_GAP_ENABLE_ADV_REQ, - MSG_ID_BLE_GAP_DISABLE_ADV_REQ, - MSG_ID_BLE_GAP_CONN_UPDATE_REQ, - MSG_ID_BLE_GAP_DISCONNECT_REQ, - MSG_ID_BLE_GAP_SERVICE_WRITE_REQ, - MSG_ID_BLE_GAP_SERVICE_READ_REQ, - MSG_ID_BLE_GAP_SM_CONFIG_REQ, - MSG_ID_BLE_GAP_SM_PAIRING_REQ, - MSG_ID_BLE_GAP_SM_PASSKEY_REQ, - MSG_ID_BLE_GAP_SET_RSSI_REPORT_REQ, - MSG_ID_BLE_GAP_SCAN_START_REQ, - MSG_ID_BLE_GAP_SCAN_STOP_REQ, - MSG_ID_BLE_GAP_CONNECT_REQ, - MSG_ID_BLE_GAP_CONNECT_CANCEL_REQ, - MSG_ID_BLE_GAP_SET_OPTIONS_REQ, - MSG_ID_BLE_GAP_GENERIC_CMD_REQ, - MSG_ID_BLE_GAP_GET_VERSION_REQ, - MSG_ID_BLE_GAP_DTM_INIT_REQ, - MSG_ID_BLE_CTRL_LOG_REQ, - MSG_ID_BLE_GAP_REQ_LAST, - - /** BLE GAP Response Messages IDs. */ - MSG_ID_BLE_GAP_WR_CONF_RSP = MSG_ID_BLE_GAP_RSP, - /**< Write controller config: own Bluetooth Device Address, tx power */ - MSG_ID_BLE_GAP_RD_BDA_RSP, /**< Read own Bluetooth Device Address */ - MSG_ID_BLE_GAP_WR_ADV_DATA_RSP, /**< Write Advertising Data and Scan response data */ - MSG_ID_BLE_GAP_WR_WHITE_LIST_RSP, /**< Write white list to controller */ - MSG_ID_BLE_GAP_CLR_WHITE_LIST_RSP, /**< Clear current white list */ - MSG_ID_BLE_GAP_ENABLE_ADV_RSP, /**< Enable Advertising */ - MSG_ID_BLE_GAP_DISABLE_ADV_RSP, /**< Disable Advertising */ - MSG_ID_BLE_GAP_CONN_UPDATE_RSP, /**< Update Connection */ - MSG_ID_BLE_GAP_DISCONNECT_RSP, /**< Disconnect */ - MSG_ID_BLE_GAP_SERVICE_WRITE_RSP, /**< Write GAP Service specific like device name, appearance and PPCPparameters */ - MSG_ID_BLE_GAP_SERVICE_READ_RSP, /**< Read GAP Service specific like device name, appearance and PPCPparameters */ - MSG_ID_BLE_GAP_SM_CONFIG_RSP, /**< Response to @ref ble_gap_sm_config */ - MSG_ID_BLE_GAP_SM_PAIRING_RSP, /**< Response to @ref ble_gap_sm_pairing_req */ - MSG_ID_BLE_GAP_SM_PASSKEY_RSP, /**< Response to @ref ble_gap_sm_passkey_reply */ - MSG_ID_BLE_GAP_SET_RSSI_REPORT_RSP, /**< Enable/Disable reporting of changes in RSSI */ - MSG_ID_BLE_GAP_SCAN_START_RSP, /**< Start Scanning */ - MSG_ID_BLE_GAP_SCAN_STOP_RSP, /**< Stop Scanning */ - MSG_ID_BLE_GAP_CONNECT_RSP, /**< Start Connection procedure */ - MSG_ID_BLE_GAP_CONNECT_CANCEL_RSP, /**< Cancel ongoing connection procedure */ - MSG_ID_BLE_GAP_SET_OPTIONS_RSP, /**< Set gap options (e.g. co-ex, master/central role) */ - MSG_ID_BLE_GAP_GENERIC_CMD_RSP, /**< Generic non connection related requests */ - MSG_ID_BLE_GAP_GET_VERSION_RSP, - MSG_ID_BLE_GAP_DTM_INIT_RSP, - MSG_ID_BLE_CTRL_LOG_RSP, /**< BLE controller logging message */ - MSG_ID_BLE_GAP_RSP_LAST, - - /** GAP related events. */ - MSG_ID_BLE_GAP_CONNECT_EVT = MSG_ID_BLE_GAP_EVT, /**< Connection established */ - MSG_ID_BLE_GAP_DISCONNECT_EVT, /**< Disconnect from peer */ - MSG_ID_BLE_GAP_CONN_UPDATE_EVT, /**< Connection Parameters update event (in central, they have been updated, in peripheral, also includes the status of the request) */ - MSG_ID_BLE_GAP_SM_PAIRING_STATUS_EVT, /**< Pairing request status event */ - MSG_ID_BLE_GAP_SM_PASSKEY_REQ_EVT, /**< Pairing passkey request (6 digits or 16 byte OOB data) */ - MSG_ID_BLE_GAP_TO_EVT, /**< GAP Timeout event */ - MSG_ID_BLE_GAP_ADV_DATA_EVT, /**< Advertising raw data event (central role) */ - MSG_ID_BLE_GAP_RSSI_EVT, /**< Signal strength change event */ - MSG_ID_BLE_GAP_GENERIC_CMD_EVT, /**< Generic command request event */ - MSG_ID_BLE_CTRL_LOG_EVT, /**< BLE Controller Logging Events */ - MSG_ID_BLE_GAP_EVT_LAST, -}; - -/** - * Generic BLE Status Response. - * Short status response for commands not returning any additional data - */ -struct ble_rsp { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< Response status @ref BLE_STATUS */ -}; - -/** - * Connection requested parameters. - */ -struct ble_gap_connection_params { - uint16_t interval_min; /**< minimal connection interval: range 0x0006 to 0x0c80 (unit 1.25ms) */ - uint16_t interval_max; /**< maximum connection interval: range 0x0006 to 0x0c80 must be bigger then min! */ - uint16_t slave_latency; /**< maximum connection slave latency: 0x0000 to 0x01f3 */ - uint16_t link_sup_to; /**< link supervision timeout: 0x000a to 0x0c80 (unit 10ms) */ -}; - -/** - * Connection values. - */ -struct ble_gap_connection_values { - uint16_t interval; /**< Connection interval (unit 1.25 ms) */ - uint16_t latency; /**< Connection latency (unit interval) */ - uint16_t supervision_to; /**< Connection supervision timeout (unit 10ms)*/ -}; - -/** - * Initial GAP configuration - */ -struct ble_wr_config { - ble_addr_t *p_bda; - uint8_t *p_name; /**< GAP Device name, NULL terminated! */ - uint16_t appearance; /**< see BLE spec */ - int8_t tx_power; - struct ble_gap_connection_params peripheral_conn_params; /**< Peripheral preferred */ - struct ble_gap_connection_params central_conn_params; /**< Central supported range */ -}; - -/** Read BD address response. */ -typedef struct { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< Response status @ref BLE_STATUS */ - ble_addr_t bd; /**< if status ok @ref ble_addr_t */ -} ble_bda_rd_rsp_t; - -struct ble_gap_adv_rsp_data { - uint8_t *p_data; /**< max 31 bytes! */ - uint8_t len; -}; - -/** - * Advertising types, see BT spec vol 6, Part B, chapter 2.3. - */ -enum GAP_ADV_TYPES { - ADV_IND = 0x00, /**< Connectable undirected advertising */ - ADV_DIRECT_IND = 0x01, /**< Connectable high duty cycle advertising */ - ADV_NONCONN_IND = 0x02, /**< Non connectable undirected advertising */ - ADV_SCAN_IND = 0x06, /**< Scannable undirected advertising */ - ADV_SCAN_RSP = 0x81, /**< Scan response, only a return value in @ref ble_gap_adv_data_evt_t */ - ADV_RESERVED /* keep last */ -}; - -typedef struct { - uint8_t irk[BLE_GAP_SEC_MAX_KEY_LEN]; - /**< Identity Resolving Key (IRK) */ -} ble_gap_irk_info_t; - -struct ble_gap_whitelist_info { - ble_addr_t **pp_bd; /**< list of bd addresses */ - ble_gap_irk_info_t **pp_key; /**< list of irk keys (for address resolution offload) */ - uint8_t bd_count; /**< number of bd addresses */ - uint8_t key_count; /**< number of keys */ -}; - -/** - * Advertisement options. - */ -enum BLE_GAP_ADV_OPTIONS { - BLE_GAP_OPT_ADV_DEFAULT = 0, /**< no specific option */ - BLE_GAP_OPT_ADV_WHITE_LISTED = 0x02 /**< use white list and only report whitelisted devices */ -}; - -/** - * Advertisement parameters. - */ -typedef struct { - uint16_t timeout; - uint16_t interval_min; /**< min interval 0xffff: use default 0x0800 */ - uint16_t interval_max; /**< max interval 0xffff: use default 0x0800 */ - uint8_t type; /**< advertisement types @ref GAP_ADV_TYPES */ - uint8_t filter_policy; /**< filter policy to apply with white list */ - ble_addr_t *p_peer_bda; /**< bd address of peer device in case of directed advertisement */ - uint8_t options; /**< options see @ref BLE_GAP_ADV_OPTIONS (to be ORed) */ -} ble_gap_adv_param_t; - -/** - * Generic BLE Status. Response - * Short status response for commands not returning any additional data - */ -typedef struct { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< Response status @ref BLE_STATUS */ - uint32_t wl_handle; /**< reference handle. to be used for clearing it later */ -} ble_gap_wr_white_list_rsp_t; - - -/** - * Appearance read response message. - */ -typedef struct { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< Response status @ref BLE_STATUS */ - uint16_t uuid; /**< value of GAP appearance characteristic */ -} ble_rd_appearance_rsp_t; - -/** - * LE security modes. - * - * see BT spec PART C, 10.2 - * - * - Security mode 1 - * - Level 1: No security at all (service may use data signing) - * - Level 2: Unauthenticated (no MITM protection pairing with encryption - * - Level 3: Authenticated (MITM protection) pairing with encryption - * - Level 4: Authenticated (MITM protection) LE Secure Connection wi - * - * - Security mode 2 (data signing) - * - Level 1: Unauthenticated pairing with data signing - * - Level 2: Authenticated (MITM protection) with data signing - */ -enum BLE_GAP_SEC_MODES { - GAP_SEC_NO_PERMISSION = 0, /**< No access permitted. */ - GAP_SEC_LEVEL_1, - GAP_SEC_LEVEL_2, - GAP_SEC_LEVEL_3, - GAP_SEC_LEVEL_4, - GAP_SEC_MODE_1 = 0x10, - GAP_SEC_MODE_2 = 0x20 /**< only used for data signing, level 1 or 2 */ -}; - -struct ble_gap_svc_local_name { - uint8_t sec_mode; /**< security mode for writing device name, @ref BLE_GAP_SEC_MODES */ - uint8_t authorization; /**< 0: no authorization, 1: authorization required */ - uint8_t len; /**< device name length (0-248) */ - const uint8_t *p_name; /**< name to to write */ -}; - -enum BLE_GAP_SVC_ATTR_TYPE { - GAP_SVC_ATTR_NAME = 0, /**< Device Name, UUID 0x2a00 */ - GAP_SVC_ATTR_APPEARANCE, /**< Appearance, UUID 0x2a01 */ - GAP_SVC_ATTR_PPCP = 4, /**< Peripheral Preferred Connection Parameters (PPCP), UUID 0x2a04 */ - GAP_SVC_ATTR_CAR = 0xa6, /**< Central Address Resolution (CAR), UUID 0x2aa6, BT 4.2 */ -}; - -struct ble_gap_service_write_params { - uint16_t attr_type; /**< GAP Characteristics attribute type @ref BLE_GAP_SVC_ATTR_TYPE */ - union { - struct ble_gap_svc_local_name name; - uint16_t appearance; /**< Appearance UUID */ - struct ble_gap_connection_params conn_params; - /**< Preferred Peripheral Connection Parameters */ - uint8_t car; /**< Central Address Resolution support 0: no, 1: yes */ - }; -}; - -struct ble_gap_service_read_rsp { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< status of read operation @ref BLE_STATUS, in case failure union shall be empty */ - uint16_t attr_type; /**< type of attribute returned (valid even in failure case! */ - union { - struct ble_gap_svc_local_name name; - uint16_t appearance; /**< Appearance UUID */ - struct ble_gap_connection_params conn_params; /**< Preferred Peripheral Connection Parameters */ - uint8_t car; /** Central Address Resolution support 0: no, 1: yes */ - }; -}; - -/** - * GAP security manager options for bonding/authentication procedures, see Vol 3: Part H, 3.5. - */ -enum BLE_GAP_SM_OPTIONS { - BLE_GAP_BONDING = 0x01, /**< SMP supports bonding */ - BLE_GAP_MITM = 0x04, /**< SMP requires Man In The Middle protection */ - BLE_GAP_OOB = 0x08 /**< SMP supports Out Of Band data */ -}; - -/** - * IO capabilities, see Vol 3: PART H, 3.5. - */ -enum BLE_GAP_IO_CAPABILITIES { - BLE_GAP_IO_DISPLAY_ONLY = 0, - BLE_GAP_IO_DISPLAY_YESNO = 1, - BLE_GAP_IO_KEYBOARD_ONLY = 2, - BLE_GAP_IO_NO_INPUT_NO_OUTPUT = 3, - BLE_GAP_IO_KEYBOARD_DISPLAY = 4 -}; - -/** - * Security manager configuration parameters. - * - * options and io_caps will define there will be a passkey request or not. - * It is assumed that io_caps and options are compatible. - */ -struct ble_gap_sm_config_params { - uint8_t options; /**< Security options (@ref BLE_GAP_SM_OPTIONS) */ - uint8_t io_caps; /**< I/O Capabilities to allow passkey exchange (@ref BLE_GAP_IO_CAPABILITIES) */ - uint8_t key_size; /**< Maximum encryption key size (7-16) */ -}; - -/** - * Security manager pairing parameters. - */ -struct ble_gap_sm_pairing_params { - uint8_t auth_level; /**< authentication level see @ref BLE_GAP_SM_OPTIONS */ -}; - -/** - * Security manager passkey type. - */ -enum BLE_GAP_SM_PASSKEY_TYPE { - BLE_GAP_SM_PASSKEY = 0, /**< Security data is a passkey. */ - BLE_GAP_SM_OBB, /**< Security data is 16 bytes of OOB data */ -}; -/** - * Security reply to incoming security request. - */ -struct ble_gap_sm_passkey { - uint8_t type; /**< Security data type in this reply @ref BLE_GAP_SM_PASSKEY_TYPE */ - union { - uint8_t passkey[6]; /**< 6 digits (string) */ - uint8_t oob[16]; /**< 16 bytes of OOB security data */ - }; -}; - -/** - * RSSI operation definition. - */ -enum BLE_GAP_RSSI_OPS { - BLE_GAP_RSSI_DISABLE_REPORT = 0, - BLE_GAP_RSSI_ENABLE_REPORT -}; - -enum BLE_GAP_SCAN_OPTIONS { - BLE_GAP_SCAN_DEFAULT = 0, /**< no specific option */ - BLE_GAP_SCAN_ACTIVE = 0x01, /**< do an active scan (request scan response */ - BLE_GAP_SCAN_WHITE_LISTED = 0x02 /**< Use white list and only report whitelisted devices */ -}; - -enum BLE_GAP_SET_OPTIONS { - BLE_GAP_SET_CH_MAP = 0, /**< Set channel map */ -}; - -typedef struct { - uint16_t conn_handle; /**< connection on which to change channel map */ - uint8_t map[5]; /**< 37 bits are used of the 40 bits (LSB) */ -} ble_gap_channel_map_t; - -/** - * GAP option data structure. - */ -typedef union { - ble_gap_channel_map_t ch_map; /**< BLE channel map to set see BT spec */ -} ble_gap_option_t; - -/** - * Scan parameters. - * - * @note Check BT core spec for high low duty cycle interval & window size! - */ -typedef struct { - uint16_t timeout; /**< scan timeout in s, 0 never */ - uint16_t interval; /**< interval: 0x4 - 0x4000. (unit: 0.625ms), use default: 0xffff (0x0010) */ - uint16_t window; /**< Window: 0x4 - 0x4000. (unit: 0.625ms), use default 0xffff (= 0x0010) */ - uint8_t options; /**< scan options, ORed options from @ref BLE_GAP_SCAN_OPTIONS */ -} ble_gap_scan_param_t; - -/** - * Connect event @ref MSG_ID_BLE_GAP_CONNECT_EVT. - */ -struct ble_gap_connect_evt { - struct ble_gap_connection_values conn_values; /**< Connection values */ - uint8_t role; /**< role in this connection @ref */ - ble_addr_t peer_bda; /**< address of peer device */ -}; - -/** - * Disconnect event @ref MSG_ID_BLE_GAP_DISCONNECT_EVT. - */ -struct ble_gap_disconnected_evt { - uint8_t hci_reason; /**< HCI disconnect reason */ -}; - -/** - * Updated connection event @ref MSG_ID_BLE_GAP_CONN_UPDATE_EVT. - */ -struct ble_gap_conn_update_evt { - struct ble_gap_connection_values conn_values; -}; - -/** - * Security manager pairing status event @ref MSG_ID_BLE_GAP_SM_PAIRING_STATUS_EVT. - */ -struct ble_gap_sm_pairing_status_evt { - uint16_t conn_handle; - uint16_t status; -}; - -/** - * Security manager passkey request event @ref MSG_ID_BLE_GAP_SM_PASSKEY_REQ_EVT. - */ -struct ble_gap_sm_passkey_req_evt { - uint8_t dummy; -}; - -/** - * GAP/SMP security result status code. - * see Vol 3: Part H, chapter 3.5.5. - */ -enum BLE_GAP_SEC_RESULT_STATUS { - BLE_GAP_SEC_STATUS_SUCCESS = 0, - /**< bonding/pairing completed successfully */ - BLE_GAP_SEC_STATUS_PASSKEY_ENTRY_FAILED,/**< passkey entry failed */ - BLE_GAP_SEC_STATUS_OOB_NOT_AVAILABLE, /**< Out of Band data is not available */ - BLE_GAP_SEC_STATUS_AUTH_REQUIREMENTS, /**< Authentication requirements not met due to IO cap */ - BLE_GAP_SEC_STATUS_CONFIRM_VALUE, /**< Confirm value does not match calculated value */ - BLE_GAP_SEC_STATUS_PAIRING_NOT_SUPPORTED, - /**< Pairing not supported by the device */ - BLE_GAP_SEC_STATUS_ENC_KEY_SIZE, /**< Encryption key size insufficient */ - BLE_GAP_SEC_STATUS_SMP_CMD_UNSUPPORTED, /**< Unsupported SMP command on this device */ - BLE_GAP_SEC_STATUS_UNSPECIFIED, /**< Failure due to unspecified reason */ - BLE_GAP_SEC_STATUS_REPEATED_ATTEMPTS, /**< Pairing/authent disallowed due to too little time elapsed since last attempt */ - BLE_GAP_SEC_STATUS_INVALID_PARAMS, /**< Invalid parameters due to length or parameters */ - /* 4.2 spec only ? */ - BLE_GAP_SEC_STATUS_DHKEY_CHECK_FAILED, /**< Remote device indicates that DHKey does not match local calculated key */ - BLE_GAP_SEC_STATUS_NUMERIC_COMP_FAILED, /**< values in numeric key comparison protocol do not match */ - BLE_GAP_SEC_STATUS_BREDR_PAIRING_INPROGRESS,/**< Failure due to BR/EDR pairing request */ - BLE_GAP_SEC_STATUS_CROSS_TSPRT_KEY_GEN_DIS, - /**< BR/EDR link key generation can not be use for LE keys handling */ -}; - -enum BLE_SVC_GAP_TIMEOUT_REASON { - BLE_SVC_GAP_TO_ADV, /**< Advertisement Stopped. */ - BLE_SVC_GAP_TO_SEC_REQ, /**< Security Request took too long. */ - BLE_SVC_GAP_TO_SCAN, /**< Scanning stopped. */ - BLE_SVC_GAP_TO_CONN, /**< Connection Link timeout. */ -}; - -/** - * GAP timeout event (e.g. protocol error) MSG_ID_BLE_GAP_TO_EVT. - */ -struct ble_gap_timout_evt { - int reason; /**< reason for timeout @ref BLE_SVC_GAP_TIMEOUT_REASON */ -}; - -/** - * Advertisement data structure (central role) @ref MSG_ID_BLE_GAP_ADV_DATA_EVT. - */ -struct ble_gap_adv_data_evt { - ble_addr_t remote_bda; /**< address of remote device */ - int8_t rssi; /**< signal strength compared to 0 dBm */ - uint8_t type; /**< type of advertisement data or scan response @ref GAP_ADV_TYPES */ - uint8_t len; /**< length of advertisement data or scap response data */ - uint8_t data[]; /**< Advertisement or scan response data */ -}; - -/** - * Connection Parameter update request event @ref MSG_ID_BLE_GAP_CONN_PARAM_UPDATE_REQ_EVT. - * - * @note reply with @ref ble_gap_conn_update_params - */ -struct ble_gap_conn_param_update_req_evt { - struct ble_gap_connection_params param; -}; - -/** - * RSSI signal strength event @ref MSG_ID_BLE_GAP_RSSI_EVT. - */ -struct ble_gap_rssi_evt { - int8_t rssi_lvl; /**< RSSI level (compared to 0 dBm) */ -}; - -/** - * RSSI report parameters @ref MSG_ID_BLE_GAP_SET_RSSI_REPORT_REQ. - */ -struct rssi_report_params { - uint16_t conn_hdl; /**< Connection handle */ - uint8_t op; /**< RSSI operation @ref BLE_GAP_RSSI_OPS */ - uint8_t delta_dBm; /**< minimum RSSI dBm change to report a new RSSI value */ - uint8_t min_count; /**< number of delta_dBm changes before sending a new RSSI report */ -}; - -/** Test Mode opcodes. */ -enum TEST_OPCODE { - BLE_TEST_INIT_DTM = 0x01, /**< Put BLE controller in HCI UART DTM test mode */ - BLE_TEST_START_DTM_RX = 0x1d, /**< LE receiver test HCI opcode */ - BLE_TEST_START_DTM_TX = 0x1e, /**< LE transmitter test HCI opcode */ - BLE_TEST_END_DTM = 0x1f, /**< End LE DTM TEST */ - /* vendor specific commands start at 0x80 */ - BLE_TEST_SET_TXPOWER = 0x80, /**< Set Tx power. To be called before start of tx test */ - BLE_TEST_START_TX_CARRIER, /**< Start Tx Carrier Test */ -}; - -/** - * Rx direct test mode data structure. - */ -struct ble_dtm_rx_test { - uint8_t freq; /**< rf channel 0x00 - 0x27, resulting F = 2402 MHz + [freq * 2 MHz] */ -}; - -/** - * Tx direct test mode data structure - */ -struct ble_dtm_tx_test { - uint8_t freq; /**< rf channel 0x00 - 0x27 where resulting F = 2402 + [freq * 2 MHz] */ - uint8_t len; /**< length of test data payload for each packet */ - uint8_t pattern; /**< packet payload pattern type, 0x00 - 0x02 mandatory */ -}; - -/** - * Tx power settings data structure. - */ -struct ble_set_txpower { - int8_t dbm; /**< Tx power level to set (e.g. -30: -30 dBm). Depends on BLE Controller */ -}; - -/** - * RX test result data. - */ -struct ble_dtm_test_result { - uint16_t mode; - uint16_t nb; -}; - -/** - * Direct Test mode command params - */ -struct ble_test_cmd { - uint8_t mode; /**< test mode to execute @ref TEST_OPCODE */ - union { - struct ble_dtm_rx_test rx; - struct ble_dtm_tx_test tx; - struct ble_set_txpower tx_pwr; /**< Tx power to use for Tx tests. */ - }; -}; - -/** - * BLE GAP event structure. - */ -struct ble_gap_event { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - uint16_t conn_handle; /**< connection handle */ - union { - struct ble_gap_connect_evt connected; /**< connected event parameters */ - struct ble_gap_disconnected_evt disconnected; /**< disconnected reason */ - struct ble_gap_conn_update_evt conn_updated; /**< connection updated */ - struct ble_gap_sm_pairing_status_evt sm_pairing_status; /**< Security Manager pairing status */ - struct ble_gap_sm_passkey_req_evt sm_passkey_req; /**< Security Manager passkey request */ - /**< connection related security update */ - struct ble_gap_timout_evt timeout; /**< gap timeout occurred */ - struct ble_gap_adv_data_evt adv_data; /**< advertisement data */ - struct ble_gap_conn_param_update_req_evt conn_param_req; - /**< update request from remote for connection parameters */ - struct ble_gap_rssi_evt rssi; /**< new rssi level if rssi reporting is enabled */ - }; -}; - -/** Generic request op codes. - * This allows to access some non connection related commands like DTM. - */ -enum BLE_GAP_GEN_OPS { - DUMMY_VALUE = 0, /**< Not used now. */ -}; - -/** - * Generic command parameters. - * - * @note Independent of connection! - */ -struct ble_gap_gen_cmd_params { - uint8_t op_code; /**< @ref BLE_GAP_GEN_OPS */ -}; - -struct ble_version_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; - struct version_header version; /**< Nordic version header */ -}; - -struct ble_dtm_init_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; -}; - -struct ble_dtm_result_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; - struct ble_dtm_test_result result; /**< Result data of DTM RX test */ -}; - -/** - * Generic request message response or event. - */ -struct ble_generic_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; - uint8_t op_code; /**< Opcode to which this message is applicable @ref BLE_GAP_GEN_OPS */ -}; - -/** - * Set Enable configuration parameters (BD address, etc). - * - * This shall put the controller stack into a usable (enabled) state. - * Hence this should be called first! - * - * @param p_svc_handle service handle - * @param p_config BLE write configuration - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG_ID_BLE_GAP_WR_CONF_RSP @ref ble_rsp, TODO: return maybe more info? - */ -int ble_gap_set_enable_config(svc_client_handle_t * p_svc_handle, - const struct ble_wr_config * p_config, void *p_priv); - -/** - * Read BD address from Controller. - * - * - * @param p_svc_handle service handle - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: @ref MSG_ID_BLE_GAP_RD_BDA_RSP @ref ble_bda_rd_rsp_t - */ -int ble_gap_read_bda(svc_client_handle_t * p_svc_handle, void *p_priv); - -/** - * Write Advertisement data to BLE controller. - * - * Store advertisement data in BLE controller. It needs to be done BEFORE starting advertisement - * - * @param p_svc_handle service handle - * @param p_adv_data adv data to store in BLE controller - * @param p_scan_data scan response data to store in controller, can be NULL - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_WR_ADV_DATA_RSP @ref ble_rsp - */ -int ble_gap_wr_adv_data(svc_client_handle_t * p_svc_handle, - const struct ble_gap_adv_rsp_data * p_adv_data, - const struct ble_gap_adv_rsp_data * p_scan_data, - void *p_priv); - -/** - * Write white list to the BLE controller. - * - * Store white in BLE controller. It needs to be done BEFORE starting advertisement or - * start scanning - * - * @param p_svc_handle service handle - * @param p_white_list white list to store in the controller - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_WR_WHITE_LIST @ref ble_gap_wr_white_list_rsp_t - */ -int ble_gap_wr_white_list(svc_client_handle_t * p_svc_handle, - const struct ble_gap_whitelist_info * p_white_list, - void *p_priv); - -/** - * Clear previously stored white list. - * - * @param p_svc_handle service handle - * @param wl_handle handle to the white list previously stored - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GAP_CLR_WHITE_LIST @ref ble_rsp - */ -int ble_gap_clr_white_list(svc_client_handle_t * p_svc_handle, - uint32_t wl_handle, void *p_priv); - -/** - * Start advertising. - * - * @param p_svc_handle service handle - * @param p_adv_param advertisement - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GAP_ENABLE_ADV @ref ble_rsp - */ -int ble_gap_start_advertise(svc_client_handle_t * p_svc_handle, - const ble_gap_adv_param_t * p_adv_param, - void *p_priv); - -/** - * Stop advertising. - * - * @param p_svc_handle service handle - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GAP_DISABLE_ADV @ref ble_rsp - */ -int ble_gap_stop_advertise(svc_client_handle_t * p_svc_handle, void *p_priv); - -/** - * Update connection. - * - * This function's behavior depends on the role of the connection: - * - in peripheral mode, it sends an L2CAP signaling connection parameter - * update request based the values in @ref p_conn_param - * and the action can be taken by the central at link layer - * - in central mode, it will send a link layer command to change the - * connection values based on the values in @ref p_conn_param where the - * connection interval is interval_min - * - * When the connection is updated, the event @ref MSG_ID_BLE_GAP_CONN_UPDATE_EVT will - * be received. - * - * @param conn_handle Connection handle - * @param p_conn_param Connection parameters - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: @ref MSG_ID_BLE_GAP_CONN_UPDATE_RSP @ref ble_rsp - */ -int ble_gap_conn_update(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const struct ble_gap_connection_params * p_conn_param, - void *p_priv); - -/** - * Disconnect connection (peripheral or central role). - * - * @param p_svc_handle service handle - * @param conn_hhdl connection to terminate - * @param reason HCI reason for connection termination, most often 0x16 (connection terminated by local host) - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GAP_DISCONNECT @ref ble_rsp, MSG_ID_BLE_GAP_DISCONNECT_EVT @ref ble_gap_disconnected_evt_t - */ -int ble_gap_disconnect(svc_client_handle_t * p_svc_handle, - uint16_t conn_hhdl, uint8_t reason, - void *p_priv); -/** - * Write GAP Service Attribute Characteristics. - * - * @param p_svc_handle service handle - * @param p_params data of characteristic to write - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GAP_SERVICE_WRITE_RSP @ref ble_rsp - */ -int ble_gap_service_write(svc_client_handle_t * p_svc_handle, - const struct ble_gap_service_write_params * p_params, - void *p_priv); - -/** - * Read GAP Service Characteristics. - * - * @param p_svc_handle service handle - * @param type type of GAP service data characteristic to read @ref BLE_GAP_SVC_ATTR_TYPE - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_SERVICE_READ_RSP @ref ble_gap_service_read_rsp - */ -int ble_gap_service_read(svc_client_handle_t * p_svc_handle, - uint16_t type, void * p_priv); - -/** - * Function for configuring the security manager. - * - * @param h Service client - * @param p_params local authentication/bonding parameters - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * - * @note Upon completion of the procedure, the client will receive - * a message @ref MSG_ID_BLE_GAP_SM_CONFIG_RSP - */ -int ble_gap_sm_config(const svc_client_handle_t * h, - const struct ble_gap_sm_config_params * p_params, - void *p_priv); - -/** - * Initiate the bonding procedure (central). - * - * @param h Service client - * @param conn_handle connection on which bonding procedure is executed - * @param p_params local authentication/bonding parameters - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * - * @note Upon completion of the procedure, the client receives - * @ref MSG_ID_BLE_GAP_SM_PAIRING_RSP - */ -int ble_gap_sm_pairing_req(const svc_client_handle_t * h, - uint16_t conn_handle, - const struct ble_gap_sm_pairing_params * p_params, - void *p_priv); - -/** - * Reply to an incoming passkey request event (@ref MSG_ID_BLE_GAP_SM_PASSKEY_REQ_EVT). - * - * @param p_svc_handle service handle - * @param conn_handle connection on which bonding is going on - * @param p_params bonding security reply - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * - * @note Upon completion of the procedure, the client receives - * @ref MSG_ID_BLE_GAP_SM_PASSKEY_RSP - */ -int ble_gap_sm_passkey_reply(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const struct ble_gap_sm_passkey * p_params, - void *p_priv); - -/** - * Enable disable the reporting of the RSSI value. - * - * @param p_svc_handle service handle - * @param conf RSSI report parameters @ref MSG_ID_BLE_GAP_SET_RSSI_REPORT_REQ - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_SET_RSSI_REPORT_RSP @ref ble_rsp - */ -int ble_gap_set_rssi_report(svc_client_handle_t * p_svc_handle, - const struct rssi_report_params *params, - void *p_priv); - -/** - * Start scanning for BLE devices doing advertisement. - * - * @param p_svc_handle service handle - * @param p_scan_params scan parameters to use @ref ble_gap_scan_param_t - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_SCAN_START_RSP @ref ble_rsp - */ -int ble_gap_start_scan(svc_client_handle_t * p_svc_handle, - const ble_gap_scan_param_t * p_scan_params, - void *p_priv); - -/** - * Stop scanning. - * - * @param p_svc_handle service handle - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_STOP_START_RSP @ref ble_rsp - */ -int ble_gap_stop_scan(svc_client_handle_t * p_svc_handle, void *p_priv); - -/** - * Connect to a Remote Device. - * - * @param p_svc_handle service handle - * @param p_bd bd to connect to. shall be null if BLE_GAP_SCAN_WHITE_LISTED option is set in @ref ble_gap_scan_param_t - * @param p_scan_params scan parameters - * @param p_conn_params connection parameters - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_CONNECT_RSP @ref ble_rsp, - * @return MSG: MSG_ID_BLE_GAP_CONNECT_EVT @ref ble_gap_connect_evt_t - */ -int ble_gap_connect(svc_client_handle_t * p_svc_handle, const ble_addr_t * p_bd, - const ble_gap_scan_param_t * p_scan_params, - const struct ble_gap_connection_params * p_conn_params, - void *p_priv); - -/** - * Cancel an ongoing connection attempt. - * - * @param p_svc_handle service handle - * @param p_bd bd address of device for which the connection shall be canceled - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_CONNECT @ref ble_rsp - */ -int ble_gap_cancel_connect(svc_client_handle_t * p_svc_handle, - const ble_addr_t * p_bd, void *p_priv); - -/** - * Set a gap option (channel map etc) on a connection. - * - * @param p_svc_handle service handle - * @param op option to set @ref BLE_GAP_SET_OPTIONS - * @param p_opt bd address of device for which the connection shall be canceled ble_gap_option_t - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_SET_OPTIONS @ref ble_rsp - */ -int ble_gap_set_option(svc_client_handle_t * p_svc_handle, uint8_t op, - const ble_gap_option_t * p_opt, void *p_priv); - -/** - * Set a gap option (channel map etc) on a connection. - * - * @param p_svc_handle service handle - * @param p_params bd address of device for which the connection shall be canceled ble_gap_option_t - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_GENERIC_CMD_RSP @ref ble_rsp or @ref ble_generic_msg - */ -int ble_gap_generic_cmd_req(svc_client_handle_t * p_svc_handle, - const struct ble_gap_gen_cmd_params *p_params, - void *p_priv); - -/** - * Get nordic version. - * - * @param p_svc_handle service handle - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_GET_VERSION_RSP @ref ble_rsp or @ref ble_generic_msg - */ -int ble_gap_get_version_req(svc_client_handle_t * p_svc_handle, - void *p_priv); - -/** - * Init dtm mode. - * - * @param p_svc_handle service handle - * @param p_params bd address of device for which the connection shall be canceled ble_gap_option_t - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GAP_DTM_INIT_RSP @ref ble_rsp or @ref ble_generic_msg - */ -int ble_gap_dtm_init_req(svc_client_handle_t * p_svc_handle, - void *p_priv); -/** @} */ - -#endif /* __BLE_SVC_API_H__ */ diff --git a/system/libarc32_arduino101/framework/include/services/ble/ble_service_gatt.h b/system/libarc32_arduino101/framework/include/services/ble/ble_service_gatt.h deleted file mode 100644 index 23c65b73..00000000 --- a/system/libarc32_arduino101/framework/include/services/ble/ble_service_gatt.h +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __BLE_SERVICE_GATT_H__ -#define __BLE_SERVICE_GATT_H__ - -#include "ble_service.h" -#include "ble_service_gap_api.h" - -/** GATT common definitions. - * - * @ingroup ble_core_service - * - * @addtogroup ble_core_service_gatt BLE core service common GATT definitions - * @{ - */ - -/** - * GATT Success code and error codes. - */ -enum BLE_SVC_GATT_STATUS_CODES { - BLE_SVC_GATT_STATUS_SUCCESS = BLE_STATUS_SUCCESS, /**< GATT success @ref BLE_STATUS_SUCCESS */ - BLE_SVC_GATT_STATUS_ENCRYPTED_MITM = BLE_SVC_GATT_STATUS_SUCCESS, - BLE_SVC_GATT_STATUS_INVALID_HANDLE = BLE_STATUS_GATT_BASE + 0x01,/**< 0x01 see BT Spec Vol 3: Part F (ATT), chapter 3.4.1.1 */ - BLE_SVC_GATT_STATUS_READ_NOT_PERMIT, - BLE_SVC_GATT_STATUS_WRITE_NOT_PERMIT, - BLE_SVC_GATT_STATUS_INVALID_PDU, - BLE_SVC_GATT_STATUS_INSUF_AUTHENTICATION, - BLE_SVC_GATT_STATUS_REQ_NOT_SUPPORTED, - BLE_SVC_GATT_STATUS_INVALID_OFFSET, - BLE_SVC_GATT_STATUS_INSUF_AUTHORIZATION, - BLE_SVC_GATT_STATUS_PREPARE_Q_FULL, - BLE_SVC_GATT_STATUS_NOT_FOUND, - BLE_SVC_GATT_STATUS_NOT_LONG, - BLE_SVC_GATT_STATUS_INSUF_KEY_SIZE, - BLE_SVC_GATT_STATUS_INVALID_ATTR_LEN, - BLE_SVC_GATT_STATUS_ERR_UNLIKELY, - BLE_SVC_GATT_STATUS_INSUF_ENCRYPTION, - BLE_SVC_GATT_STATUS_UNSUPPORT_GRP_TYPE, - BLE_SVC_GATT_STATUS_INSUF_RESOURCE, - - /**< TODO: maybe be not needed, to be covered by generic GAP status */ - BLE_SVC_GATT_STATUS_NO_RESOURCES = BLE_STATUS_GATT_BASE | 0x80, - BLE_SVC_GATT_STATUS_INTERNAL_ERROR, - BLE_SVC_GATT_STATUS_WRONG_STATE, - BLE_SVC_GATT_STATUS_DB_FULL, - BLE_SVC_GATT_STATUS_BUSY, - BLE_SVC_GATT_STATUS_ERROR, - BLE_SVC_GATT_STATUS_CMD_STARTED, - BLE_SVC_GATT_STATUS_ILLEGAL_PARAMETER, - BLE_SVC_GATT_STATUS_PENDING, - BLE_SVC_GATT_STATUS_AUTH_FAIL, - BLE_SVC_GATT_STATUS_MORE, - BLE_SVC_GATT_STATUS_INVALID_CFG, - BLE_SVC_GATT_STATUS_SERVICE_STARTED, - BLE_SVC_GATT_STATUS_ENCRYPTED_NO_MITM, - BLE_SVC_GATT_STATUS_NOT_ENCRYPTED, - BLE_SVC_GATT_STATUS_CONGESTED, -}; - -/** - * GATT Server Message ID definitions. - */ -enum BLE_GATTS_MSG_ID { - /**< GATT Server Requests */ - MSG_ID_BLE_GATTS_ADD_SERVICE_REQ = MSG_ID_BLE_GAP_REQ_LAST, - MSG_ID_BLE_GATTS_ADD_INCL_SVC_REQ, - MSG_ID_BLE_GATTS_ADD_CHARACTERISTIC_REQ, - MSG_ID_BLE_GATTS_ADD_DESCRIPTOR_REQ, - MSG_ID_BLE_GATTS_START_SERVICE_REQ, - MSG_ID_BLE_GATTS_REMOVE_SERVICE_REQ, - MSG_ID_BLE_GATTS_INDICATE_SERVICE_CHANGE_REQ, - MSG_ID_BLE_GATTS_SET_ATTRIBUTE_VALUE_REQ, - MSG_ID_BLE_GATTS_GET_ATTRIBUTE_VALUE_REQ, - MSG_ID_BLE_GATTS_SEND_NOTIF_REQ, - MSG_ID_BLE_GATTS_SEND_IND_REQ, - MSG_ID_BLE_GATTS_SEND_RW_AUTHORIZATION_REQ, - MSG_ID_BLE_GATTS_WR_CONN_ATTRIBUTES_REQ, - MSG_ID_BLE_GATTS_RD_CONN_ATTRIBUTES_REQ /* 37 */ , - MSG_ID_BLE_GATTS_REQ_LAST, - - /**< GATT Server Requests */ - MSG_ID_BLE_GATTS_ADD_SERVICE_RSP = MSG_ID_BLE_GAP_RSP_LAST, - /**< create new service */ - MSG_ID_BLE_GATTS_ADD_INCL_SVC_RSP, - MSG_ID_BLE_GATTS_ADD_CHARACTERISTIC_RSP, - MSG_ID_BLE_GATTS_ADD_DESCRIPTOR_RSP, - MSG_ID_BLE_GATTS_START_SERVICE_RSP, /**< enable created service */ - MSG_ID_BLE_GATTS_REMOVE_SERVICE_RSP, /**< stop and remove service */ - MSG_ID_BLE_GATTS_INDICATE_SERVICE_CHANGE_RSP, /**< indicate a service change */ - MSG_ID_BLE_GATTS_SET_ATTRIBUTE_VALUE_RSP, - MSG_ID_BLE_GATTS_GET_ATTRIBUTE_VALUE_RSP, - MSG_ID_BLE_GATTS_SEND_NOTIF_RSP, /**< send notification */ - MSG_ID_BLE_GATTS_SEND_IND_RSP, /**< send indication */ - MSG_ID_BLE_GATTS_SEND_RW_AUTHORIZATION_RSP, /**< authorize a R/W request from remote */ - MSG_ID_BLE_GATTS_WR_CONN_ATTRIBUTES_RSP, /**< write connection related attributes (previously bonded!) */ - MSG_ID_BLE_GATTS_RD_CONN_ATTRIBUTES_RSP /* 37 */ , /**< read connection related attributes (only for bonded connections!*/ - MSG_ID_BLE_GATTS_RSP_LAST, - - /**< GATT Server Events */ - MSG_ID_BLE_GATTS_WRITE_EVT = MSG_ID_BLE_GAP_EVT_LAST, /**< remote client write happened */ - MSG_ID_BLE_GATTS_RW_AUTHORIZATION_REQ_EVT, /**< remote client R/W authorization request */ - MSG_ID_BLE_GATTS_CONN_ATTRIB_MISSING_EVT, /**< connection related attributes have not been set, access pending */ - MSG_ID_BLE_GATTS_INDICATION_CONF_EVT, /**< indication confirmation event */ - MSG_ID_BLE_GATTS_SVC_CHG_CONF_EVT, /**< confirmation of service change indication (no params) */ - MSG_ID_BLE_GATTS_TO_EVT, /**< GATTS timeout indication */ - MSG_ID_BLE_GATTS_EVT_LAST -}; - -/** - * GATT Client Message ID definitions. - */ -enum BLE_GATTC_MSG_ID { - /**< GATT Client Requests, responses, events and indications */ - MSG_ID_BLE_GATTC_DISCOVER_PRIMARY_SERVICE_REQ = - MSG_ID_BLE_GATTS_REQ_LAST, - MSG_ID_BLE_GATTC_DISCOVER_INCLUDED_SERVICES_REQ, - MSG_ID_BLE_GATTC_DISCOVER_CHAR_REQ, - MSG_ID_BLE_GATTC_DISCOVER_DESCRIPTOR_REQ, - MSG_ID_BLE_GATTC_RD_CHARS_REQ, - MSG_ID_BLE_GATTC_WR_OP_REQ, - MSG_ID_BLE_GATTC_SEND_HANDLE_VALUE_REQ /* 44 */ , - - /** GATT Client Requests, responses, events and indications */ - MSG_ID_BLE_GATTC_DISCOVER_PRIMARY_SERVICE_RSP = MSG_ID_BLE_GATTS_RSP_LAST, /**< discover primary service */ - MSG_ID_BLE_GATTC_DISCOVER_INCLUDED_SERVICES_RSP,/**< find included service procedure */ - MSG_ID_BLE_GATTC_DISCOVER_CHAR_RSP, /**< discover characteristics of a service */ - MSG_ID_BLE_GATTC_DISCOVER_DESCRIPTOR_RSP, /**< discover descriptor of a characteristic */ - MSG_ID_BLE_GATTC_RD_CHARS_RSP, /**< read characteristic or long characteristics */ - MSG_ID_BLE_GATTC_WR_OP_RSP, /**< different types of write operations */ - MSG_ID_BLE_GATTC_SEND_HANDLE_VALUE_RSP /* 44 */ , /**< send attribute handle to server */ - - /** GATT Client Events */ - MSG_ID_BLE_GATTC_DISC_PRIM_SVC_EVT = MSG_ID_BLE_GATTS_EVT_LAST, /**< primary service discovery response */ - MSG_ID_BLE_GATTC_DISC_INCL_SVC_EVT, /**< include service discovery response */ - MSG_ID_BLE_GATTC_DISC_CHAR_EVT, /**< characteristic discovery response */ - MSG_ID_BLE_GATTC_DISC_DESCR_EVT, /**< descriptor discovery response */ - MSG_ID_BLE_GATTC_RD_EVT, /**< data read response */ - MSG_ID_BLE_GATTC_WR_EVT, /**< data write response */ - MSG_ID_BLE_GATTC_HDL_NOTIF_EVT, /**< handle indication/notification event */ - MSG_ID_BLE_GATTC_TO_EVT, /**< GATT Client timeout event */ - MSG_ID_BLE_GATTC_LAST -}; - -/** - * Maximum UUID size - 16 bytes, and structure to hold any type of UUID. - */ -#define MAX_UUID_SIZE 16 - -#define BLE_GATT_INVALID_HANDLE 0x0000 /**< reserved invalid attribute handle */ -#define BLE_GATT_MAX_HANDLE 0xffff /**< maximum handle in a BLE server */ -#define BLE_GATT_START_HANDLE_DISCOVER 0x0001 /**< Value of start handle during discovery. */ - -/** BT uuid types defined as length. */ -enum BT_UUID_TYPES { - BT_UUID16 = 2, /**< 16 bit UUID type */ - BT_UUID32 = 4, /**< 32 bit UUID type */ - BT_UUID128 = 16 /**< 128 bit UUID type */ -}; - -/** - * Generic uuid structure specific to BT/BLE. - */ -struct bt_uuid { - uint8_t type; /**< UUID type (encoded as length of the union element) @ref BT_UUID_TYPES */ - union { - uint16_t uuid16; - uint32_t uuid32; - uint8_t uuid128[MAX_UUID_SIZE]; - }; -}; - -/** - * UUID and Handle combination for services and characteristics - * - * Make sure this is 32 bit aligned! - */ -struct bt_uuid_handle_tuple { - void *p_priv; /**< Service private reference handle. */ - uint16_t handle; /** Service or characteristic handle. */ -}; - -/** - * GATT service types, primary versus secondary/included one. - */ -enum BLE_GATT_SVC_TYPES { - BLE_GATT_SVC_PRIMARY = 0, /**< primary service */ - BLE_GATT_SVC_INCLUDED /**< include service (must be referenced by a primary) */ -}; - -/** - * Characteristic properties. - */ -enum BLE_GATT_CHAR_PROPS { - BLE_GATT_CHAR_PROP_BIT_NONE = 0, - BLE_GATT_CHAR_PROP_BIT_BROADCAST = 0x01, - BLE_GATT_CHAR_PROP_BIT_READ = 0x02, - BLE_GATT_CHAR_PROP_BIT_WRITE_NR = 0x04, - BLE_GATT_CHAR_PROP_BIT_WRITE = 0x08, - BLE_GATT_CHAR_PROP_BIT_NOTIFY = 0x10, - BLE_GATT_CHAR_PROP_BIT_INDICATE = 0x20, - BLE_GATT_CHAR_PROP_BIT_AUTH = 0x40, - BLE_GATT_CHAR_PROP_BIT_EXTEND = 0x80/**< if set the extend property @ref BLE_GATT_CHAR_EXT_PROPS is present! */ -}; - -/** - * Extended characteristic properties. - */ -enum BLE_GATT_CHAR_EXT_PROPS { - BLE_GATT_CHAR_EXT_PROP_BIT_NONE = 0, - BLE_GATT_CHAR_EXT_PROP_RELIABLE_WR = 0x0001, /**< Reliable write procedure is supported */ - BLE_GATT_CHAR_EXT_PROP_WR_AUX = 0x0002, /**< User Descriptor Writes are permitted */ -}; - -struct ble_gatt_char_properties { - uint8_t props; /**< properties, @ref BLE_GATT_CHAR_PROPS */ - uint16_t ext_props; /**< extended properties, @ref BLE_GATT_CHAR_EXT_PROPS, valid if BLE_GATT_CHAR_PROP_BIT_EXTEND set */ -}; - -/** - * Format of the value of a characteristic, enumeration type. - */ -enum BLE_GATT_FORMATS { - BLE_GATT_FORMAT_RES, /* rfu */ - BLE_GATT_FORMAT_BOOL, /* 0x01 boolean */ - BLE_GATT_FORMAT_2BITS, /* 0x02 2 bit */ - BLE_GATT_FORMAT_NIBBLE, /* 0x03 nibble */ - BLE_GATT_FORMAT_UINT8, /* 0x04 uint8 */ - BLE_GATT_FORMAT_UINT12, /* 0x05 uint12 */ - BLE_GATT_FORMAT_UINT16, /* 0x06 uint16 */ - BLE_GATT_FORMAT_UINT24, /* 0x07 uint24 */ - BLE_GATT_FORMAT_UINT32, /* 0x08 uint32 */ - BLE_GATT_FORMAT_UINT48, /* 0x09 uint48 */ - BLE_GATT_FORMAT_UINT64, /* 0x0a uint64 */ - BLE_GATT_FORMAT_UINT128,/* 0x0B uint128 */ - BLE_GATT_FORMAT_SINT8, /* 0x0C signed 8 bit integer */ - BLE_GATT_FORMAT_SINT12, /* 0x0D signed 12 bit integer */ - BLE_GATT_FORMAT_SINT16, /* 0x0E signed 16 bit integer */ - BLE_GATT_FORMAT_SINT24, /* 0x0F signed 24 bit integer */ - BLE_GATT_FORMAT_SINT32, /* 0x10 signed 32 bit integer */ - BLE_GATT_FORMAT_SINT48, /* 0x11 signed 48 bit integer */ - BLE_GATT_FORMAT_SINT64, /* 0x12 signed 64 bit integer */ - BLE_GATT_FORMAT_SINT128,/* 0x13 signed 128 bit integer */ - BLE_GATT_FORMAT_FLOAT32,/* 0x14 float 32 */ - BLE_GATT_FORMAT_FLOAT64,/* 0x15 float 64 */ - BLE_GATT_FORMAT_SFLOAT, /* 0x16 IEEE-11073 16 bit SFLOAT */ - BLE_GATT_FORMAT_FLOAT, /* 0x17 IEEE-11073 32 bit SFLOAT */ - BLE_GATT_FORMAT_DUINT16,/* 0x18 IEEE-20601 format */ - BLE_GATT_FORMAT_UTF8S, /* 0x19 UTF-8 string */ - BLE_GATT_FORMAT_UTF16S, /* 0x1a UTF-16 string */ - BLE_GATT_FORMAT_STRUCT, /* 0x1b Opaque structure */ - BLE_GATT_FORMAT_MAX /* 0x1c or above reserved */ -}; - -/** - * GATT characteristic user description. - */ -struct ble_gatt_char_user_desc { - uint8_t *buffer; /**< Pointer to a UTF-8 string. */ - uint8_t len; /**< The size in bytes of the user description. */ -}; - -/** - * GATT characteristic presentation format description. - */ -struct ble_gatt_pf_desc { - uint16_t unit; /**< as UUIUD defined by SIG */ - uint16_t descr; /**< as UUID as defined by SIG */ - uint8_t format; /**< @ref BLE_GATT_FORMATS */ - int8_t exp; /**< see Unit from Bluetooth Assigned Numbers, https://developer.bluetooth.org/gatt/units/Pages/default.aspx */ - uint8_t name_spc; /**< name space of the description */ -} ; - -/** - * GATT indication types. - */ -enum BLE_GATT_IND_TYPES { - BLE_GATT_IND_TYPE_NONE = 0, - BLE_GATT_IND_TYPE_NOTIFICATION, - BLE_GATT_IND_TYPES_INDICATION, -}; - -/** - * GATT Write operation types - * - * (BT spec Vol 3, Part G, chapter. 4.9) - * @note long char write, Prepare & Exe request are handled internally to the controller stack - */ -enum BLE_GATT_WR_OP_TYPES { - BLE_GATT_WR_OP_NOP = 0, /**< normally not used except to cancel BLE_GATT_WR_OP_REQ long char write procedure */ - BLE_GATT_WR_OP_CMD, /**< Write Command, (no response) */ - BLE_GATT_WR_OP_REQ, /**< Write Request, Write response is received , if length is longer then ATT MTU, Prepare write procedure */ - BLE_GATT_WR_OP_SIGNED_CMD, /**< Signed Write Command */ -}; - -/** @} */ - -#endif diff --git a/system/libarc32_arduino101/framework/include/services/ble/ble_service_gattc_api.h b/system/libarc32_arduino101/framework/include/services/ble/ble_service_gattc_api.h deleted file mode 100644 index d7b42df9..00000000 --- a/system/libarc32_arduino101/framework/include/services/ble/ble_service_gattc_api.h +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __BLE_SERVICE_GATTC_H__ -#define __BLE_SERVICE_GATTC_H__ - -#include "ble_service.h" -#include "ble_service_gap_api.h" -#include "ble_service_gatt.h" - -/** - * @defgroup ble_core_service_gattc BLE Core Service GATTC - * @ingroup ble_core_service - * - * BLE Core Service GATTC APIs used to implement GATT Clients. - * - * This is typically only used to add new client services to BLE service. - * - * It provides the following services: - * - Discover remote \b primary services or a specific service - * - Discover remote characteristics - * - Discover remote descriptors - * - read/write remote characteristics - * - Getting notified on characteristic changes - * - * @{ - */ - -/** - * Generic GATTC response message. - */ -struct ble_gattc_rsp { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; - uint16_t conn_handle; /**< GAP connection handle */ -}; - -/** - * Generic GATTC error event. - */ -struct ble_gattc_err_rsp_evt { - uint16_t err_handle; /**< handle of char attribute causing the failure */ -}; - -/** - * Handle range for a service operation. - */ -struct ble_gattc_handle_range { - uint16_t start_handle; - uint16_t end_handle; -}; - -typedef struct { - struct ble_gattc_handle_range handle_range; /**< range of characteristic handles within a service */ - struct bt_uuid uuid; /**< service uuid */ -} ble_gattc_svc_t; - -/** - * Primary Service discovery Indication message @ref MSG_ID_BLE_GATTC_DISC_PRIM_SVC_EVT. - */ -typedef struct { - uint16_t svc_count; /**< number of service included into this indication */ - ble_gattc_svc_t service_found[];/**< array on fouTnd services */ -} ble_gattc_primary_svc_disc_evt_t; - -/** - * Included service. - */ -typedef struct { - uint16_t incl_handle; /**< handle of included service */ - ble_gattc_svc_t svc; /**< included service */ -} ble_gattc_incl_svc_t; - -/** - * Discovered included services @ref MSG_ID_BLE_GATTC_DISC_INCL_SVC_EVT. - */ -typedef struct { - uint16_t incl_count; /**< Number of included services */ - ble_gattc_incl_svc_t included[]; /**< Array on found services */ -} ble_gattc_incl_svc_disc_evt_t; - -typedef struct { - struct ble_gatt_char_properties char_properties; /**< characteristic properties */ - uint16_t decl_handle; /**< Characteristic declaration handle */ - uint16_t value_handle; /**< Char's value handle */ - struct bt_uuid uuid; /**< Characteristic's UUID */ -} ble_gattc_characteristic_t; - -/** - * Discovered characteristics indication @ref MSG_ID_BLE_GATTC_DISC_CHAR_EVT. - */ -typedef struct { - uint16_t char_count; /**< number of characteristics in this message */ - ble_gattc_characteristic_t chars[]; /**< characteristics data as per char_count */ -} ble_gattc_char_disc_evt_t; - -/** - * GATTC descriptor. - */ -typedef struct { - uint16_t handle; /**< descriptor handle */ - struct bt_uuid uuid; /**< uuid of the descriptor */ -} ble_gattc_descriptor_t; - -/** - * Descriptor discover indication. - */ -typedef struct { - uint16_t desc_count; /**< number of descriptors in this message */ - ble_gattc_descriptor_t descs[]; /**< found descriptors */ -} ble_gattc_desc_disc_evt_t; - -enum BLE_GATTC_RD_CHAR_TYPES { - BLE_GATTC_RD_CHAR_BY_UUID = 0, /**< Read characteristic by UUID */ - BLE_GATTC_RD_CHAR, /**< Read (Long) characteristic or (Long) descriptor. Maybe called multiple times in case of long */ - BLE_GATTC_RD_CHAR_MULTIPLE /**< Read multiple characteristic attributes */ -}; - -/** - * Characteristic read by using UUID. - */ -typedef struct { - struct ble_gattc_handle_range handle_range; /**< characteristic or descriptor handle range */ - struct bt_uuid *p_uuid; /**< uuid of characteristic to read */ -} ble_gattc_rd_char_by_uuid_t; - -/** - * Characteristic or descriptor read. - * - * Maybe used for long too. - */ -typedef struct { - uint16_t handle; /**< attribute handle for reading */ - uint16_t offset; /**< offset into attribute data to read */ -} ble_gattc_rd_char_t; - -/** - * Read multiple characteristics values. - */ -typedef struct { - uint16_t handle_count; /**< number of handles in this structure */ - uint16_t handle[]; /**< handles of attributes to read from */ -} ble_gattc_rd_multi_char_t; - -typedef struct { - union { - ble_gattc_rd_char_by_uuid_t char_by_uuid; - ble_gattc_rd_char_t char_desc; /**< (Long) characteristic or descriptor to read */ - ble_gattc_rd_multi_char_t multi_char; - /**< read multiple characteristics */ - }; -} ble_gattc_rd_characteristic_t; - -typedef struct { - uint16_t char_handle; /**< handle of characteristic */ - uint16_t len; /**< if len is bigger then ATT MTU size, the controller fragment buffer itself */ - uint8_t *p_value; /**< characteristic value to write */ - uint8_t wr_type; /**< type of write operation @ref BLE_GATT_WR_OP_TYPES */ -} ble_gattc_wr_characteristic_t; - -/** - * Read characteristic response indication (@ref MSG_ID_BLE_GATTC_RD_EVT). - */ -typedef struct { - uint16_t handle; /**< handle of characteristic attribute read */ - uint16_t offset; /**< offset of data returned */ - uint16_t len; /**< length of data returned */ - uint8_t data[]; /**< characteristic attribute data */ -} ble_gattc_rd_char_evt_t; - -/** - * Characteristic write response indication @ref MSG_ID_BLE_GATTC_WR_EVT. - */ -typedef struct { - uint16_t char_handle; - uint16_t len; -} ble_gattc_wr_char_evt_t; - -/** - * Handle value indication or notification indication/event (@ref MSG_ID_BLE_GATTC_HDL_NOTIF_EVT). - */ -typedef struct { - uint16_t handle; /**< handle of characteristic being notified/indicated */ - uint16_t len; /**< length of value included into this indication */ - uint8_t type; /**< notification versus indication, @ref BLE_GATT_IND_TYPES */ - uint8_t data[]; /**< value data received */ -} ble_gattc_value_evt_t; - -/** - * GATT timeout reason. - */ -typedef struct { - uint16_t reason; /**< GATT timeout reason */ -} ble_gattc_to_evt_t; - -/** - * GATTC indication or response message structure applicable to most indications/events/responses. - */ -struct ble_gattc_evt_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; - uint16_t conn_handle; - union { - struct ble_gattc_err_rsp_evt err_rsp; /**< returned only if status != BLE_GATT_STATUS_SUCCESS */ - ble_gattc_primary_svc_disc_evt_t prim_svc_disc; - /**< primary service discovery indication event */ - ble_gattc_incl_svc_disc_evt_t incl_svc_disc; /**< included services discovered */ - ble_gattc_char_disc_evt_t char_disc; /**< characteristic discover event/indication */ - ble_gattc_desc_disc_evt_t desc_disc; /**< discovered descriptors indication/event */ - ble_gattc_rd_char_evt_t char_rd; /**< read characteristic indication/event */ - ble_gattc_wr_char_evt_t char_wr; /**< characteristic write indication event */ - ble_gattc_value_evt_t val_ind; /**< value indication or notification */ - ble_gattc_to_evt_t timeout_ind; /**< gattc timeout protocol error */ - }; /**< in case for responses, union is not used! */ -}; - -/** - * Discover primary service. - * - * @param p_svc_handle service handle - * @param conn_handle connection to use - * @param p_svc_uuid points to service UUID. if NULL, all services found are returned - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTC_DISCOVER_PRIMARY_SERVICE_RSP @ref ble_gattc_rsp - * @return EVT: MSG_ID_BLE_GATTC_DISC_PRIM_SVC_EVT @ref ble_gattc_primary_svc_disc_evt_t - */ -int ble_gattc_discover_primary_service(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const struct bt_uuid * p_svc_uuid, - void *p_priv); - -/** - * Discover included services on a previously discovered primary service. - * - * @param p_svc_handle service handle - * @param conn_handle connection to use - * @param p_handle_range handle range previously returned by @ref ble_gattc_primary_svc_disc_evt_t - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTC_DISCOVER_INCLUDED_SERVICES_RSP @ref ble_gattc_rsp - */ -int ble_gattc_discover_included_service(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const struct ble_gattc_handle_range * - p_handle_range, void *p_priv); - -/** - * Discover characteristics on a service. - * - * May be called several times if not all characteristics have been discovered. - * In this case a new handle range needs to be provided. - * - * @param p_svc_handle service handle - * @param conn_handle connection to use - * @param p_handle_range handle range - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTC_DISCOVER_CHAR_RSP @ref ble_gattc_rsp - * @ref MSG_ID_BLE_GATTC_DISC_CHAR_EVT @ref ble_gattc_char_disc_evt_t - */ -int ble_gattc_discover_characteristic(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const struct ble_gattc_handle_range * p_handle_range, - void *p_priv); - -/** - * Discover characteristics on a service. - * - * May be called several times if not all characteristics have been discovered. - * In this case a new handle range needs to be provided. - * - * @param p_svc_handle service handle - * @param conn_handle connection to use - * @param p_handle_range handle range - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTC_DISCOVER_DESCRIPTOR_RSP @ref ble_gattc_rsp - * @ref MSG_ID_BLE_GATTC_DISC_DESCR_EVT @ref ble_gattc_desc_disc_evt_t - */ -int ble_gattc_discover_descriptor(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const struct ble_gattc_handle_range * - p_handle_range, void *p_priv); - -/** - * Read characteristic on remote server. - * - * @param p_svc_handle service handle - * @param conn_handle connection to use - * @param type type of read to execute @ref BLE_GATTC_RD_CHAR_TYPES - * @param p_rd_char_param read type specific characteristic read parameter - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTC_RD_CHARS_RSP @ref ble_gattc_rsp - * @return EVT: MSG_ID_BLE_GATTC_RD_EVT @ref ble_gattc_rd_char_evt_t - */ -int ble_gattc_read_characteristic(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - uint8_t type, - const ble_gattc_rd_characteristic_t * p_rd_char_param, - void *p_priv); - -/** - * Write characteristic on server. - * - * @param p_svc_handle service handle - * @param conn_handle connection to use - * @param p_wr_char_param write characteristic on remote service - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GATTC_WR_OP_RSP @ref ble_gattc_rsp - */ -int ble_gattc_write_char_op(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const ble_gattc_wr_characteristic_t * - p_wr_char_param, - void *p_priv); - - -/** - * Write characteristic on server. - * - * @param p_svc_handle service handle - * @param conn_handle connection to use - * @param val_handle handle to confirm and received by Handle Value Indication (@ref MSG_ID_BLE_GATTC_HDL_NOTIF_EVT) - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE, - * @return MSG: MSG_ID_BLE_GATTC_SEND_HANDLE_VALUE_RSP @ref ble_gattc_rsp - */ -int ble_gattc_send_confirm_handle_value(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - uint16_t val_handle, - void *p_priv); - -/** @} */ - -#endif diff --git a/system/libarc32_arduino101/framework/include/services/ble/ble_service_gatts_api.h b/system/libarc32_arduino101/framework/include/services/ble/ble_service_gatts_api.h deleted file mode 100644 index 76bc3149..00000000 --- a/system/libarc32_arduino101/framework/include/services/ble/ble_service_gatts_api.h +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __BLE_SERVICE_GATTS_H__ -#define __BLE_SERVICE_GATTS_H__ - -#include "ble_service.h" -#include "ble_service_gap_api.h" -#include "ble_service_gatt.h" - -/** @defgroup ble_core_service_gatts BLE Core Service GATTS - * @ingroup ble_core_service - * - * BLE Core GATTS Service APIs to implement GATT Servers. - * - * This API should only be used by BLE service to implement additional BLE profiles/services. - * - * Those the GATT server APIs provide the following services: - * - Create an new (server) BLE service - * - Add characteristics to the service - * - Write local server characteristics - * - Receive data when updated by client - * - * @note If a service is based on a 128 bit UUID (vendor service), all the characteristic - * need to use the same 128 bit UUID base and only vary octets 12-13 of base UUID. - * - * @{ - */ - -/** - * BLE GATTS max attribute length. - * @note BLE controller dependent - */ -#define BLE_SVC_GATTS_FIX_ATTR_LEN_MAX 510 /**< Maximum length for fixed length Attribute Values. */ -#define BLE_SVC_GATTS_VAR_ATTR_LEN_MAX 512 /**< Maximum length for variable length Attribute Values. */ - -/* forward declaration for callback handlers */ -struct _ble_service_cb; -struct ble_gatts_add_svc_rsp; -struct ble_gatts_add_char_rsp; -struct ble_gatts_add_desc_rsp; -struct ble_gatts_notif_ind_rsp_msg; - -/** - * Generic GATTS response message. - */ -typedef struct { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; - uint16_t conn_handle; /**< GAP connection handle */ -} ble_gatts_rsp_t; - -/** - * Add Service callback handler. - */ -typedef int (* ble_gatts_add_svc_cback_t)(struct ble_gatts_add_svc_rsp * rsp, - struct _ble_service_cb * p_cb); - -/** - * Add service response message. - */ -struct ble_gatts_add_svc_rsp { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< status of service creation */ - ble_gatts_add_svc_cback_t cback; /**< Callback function to execute on reception of this message */ - uint16_t svc_handle; /**< Handle of newly allocated service (only valid in case of success. */ -}; - -/** - * Include service response. - */ -typedef struct { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< status of service creation */ - uint16_t svc_handle; /**< updated handle of included service (only valid in case of success */ -} ble_gatts_incl_svc_rsp_t; - -/** - * ATT attribute permission - */ -struct ble_gatts_permissions { - uint8_t rd; /**< Read permissions, @ref BLE_GAP_SEC_MODES */ - uint8_t wr; /**< Write permissions @ref BLE_GAP_SEC_MODES */ -}; - -/** - * GATT characteristic. - */ -struct ble_gatts_characteristic { - struct bt_uuid * p_uuid; /**< Pointer to the characteristic UUID. */ - struct ble_gatts_permissions perms; /**< Characteristic value attribute permissions */ - struct ble_gatt_char_properties props; /**< Characteristic Properties. @ref ble_gatt_char_properties */ - uint16_t max_len; /**< Maximum characteristic value length in bytes, see @ref BLE_SVC_GATTS_FIX_ATTR_LEN_MAX or @ref BLE_SVC_GATTS_VAR_ATTR_LEN_MAX. */ - uint16_t init_len; /**< Initial characteristic value length in bytes. */ - uint8_t * p_value; /**< Pointer to the characteristic initialization value */ - // optional descriptors - struct ble_gatt_char_user_desc * p_user_desc; /**< Optional user description of the characteristic, NULL if not required */ - struct ble_gatt_pf_desc *p_char_pf_desc; /**< Pointer to a presentation format structure or NULL if the descriptor is not required. */ -}; - -/** - * GATT generic descriptor. - */ -struct ble_gatts_descriptor { - struct bt_uuid * p_uuid; /**< Pointer to the descriptor UUID. */ - uint8_t * p_value; /**< Value of the descriptor */ - uint16_t length; /**< Length of the descriptor value */ - struct ble_gatts_permissions perms; /**< Descriptor attribute permissions */ -}; - -struct ble_gatts_char_handles { - uint16_t value_handle; /**< Handle to the characteristic value. */ - uint16_t cccd_handle; /**< Handle to the Client Characteristic Configuration Descriptor, or BLE_GATT_HANDLE_INVALID if not present. */ - uint16_t sccd_handle; /**< Handle to the Server Characteristic Configuration Descriptor, or BLE_GATT_HANDLE_INVALID if not present. */ -}; - -/** - * Add Service callback handler. - */ -typedef int (* ble_gatts_add_char_cback_t)(struct ble_gatts_add_char_rsp * rsp, - struct _ble_service_cb * p_cb); - -/** - * Add characteristic response message. - */ -struct ble_gatts_add_char_rsp { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< Status of the operation. */ - ble_gatts_add_char_cback_t cback; /**< Callback function to call on reception of this message */ - struct ble_gatts_char_handles char_h; /**< Handles of the created characteristic */ -}; - -/** - * Add Service callback handler. - */ -typedef int (* ble_gatts_add_desc_cback_t)(struct ble_gatts_add_desc_rsp * rsp, - struct _ble_service_cb * p_cb); - -/** - * Add descriptor response message. - */ -struct ble_gatts_add_desc_rsp { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< Status of the operation. */ - ble_gatts_add_desc_cback_t cback; /**< Callback function to call on reception of this message */ - uint16_t handle; /**< Handle of the created descriptor */ -}; - -/** - * Set attribute response message. - */ -struct ble_gatts_set_attr_rsp_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; - uint16_t value_handle; -}; - -/** - * Notification/Indication callback. - */ -typedef int (* ble_gatts_notif_ind_cback_t)(struct ble_gatts_notif_ind_rsp_msg * rsp, - struct _ble_service_cb * p_cb); - -/** - * Notification/Indication response message. - */ -struct ble_gatts_notif_ind_rsp_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; - uint16_t conn_handle; - ble_gatts_notif_ind_cback_t cback; /**< Callback function to call on reception of this message */ - uint16_t handle; /**< Characteristic value handle */ -}; - -/** - * Shortened attribute type definitions. - * See BT Spec r Vol 3, PART G, chapter 3 - */ -enum BLE_SVC_GATTS_ATTR_TYPES { - BLE_SVC_GATTS_ATTR_TYPE_NONE = 0, - BLE_SVC_GATTS_ATTR_TYPE_PRIMARY_SVC_DECL, - /**< primary service attribute declaration (chpt 3.1) */ - BLE_SVC_GATTS_ATTR_TYPE_SECONDARY_SVC_DECL, - /**< secondary service attribute declaration (chpt 3.1) */ - BLE_SVC_GATTS_ATTR_TYPE_INCLUDE_DECL, /**< include attribute declaration (3.2) */ - BLE_SVC_GATTS_ATTR_TYPE_CHAR_DECL, /**< characteristic declaration (3.3.1) */ - BLE_SVC_GATTS_ATTR_TYPE_CHAR_VALUE_DECL,/**< Characteristic value declaration */ - BLE_SVC_GATTS_ATTR_TYPE_DESC_DECL, /**< descriptor declaration */ -}; - -/** - * GATT server write ops. - */ -enum BLE_GATTS_WR_OPS { - BLE_GATTS_OP_NONE = 0, - BLE_GATTS_OP_WR, /**< 3.4.5.1 Write Request (Attribute), expects write response */ - BLE_GATTS_OP_WR_CMD, /**< 3.4.5.3 Write Command (Attribute) NO response sent */ - BLE_GATTS_OP_WR_CMD_SIGNED, /**< 3.4.5.4 Write Command Signed (Attribute), NO response sent */ - BLE_GATTS_OP_WR_PREP_REQ, /**< 3.4.6.1 Write Prepare Request, expects a prepare write request response */ - BLE_GATTS_OP_WR_EXE_REQ_CANCEL, /**< 3.4.6.3 Cancel Executed Write Request, cancel and clear queue (flags = 0) */ - BLE_GATTS_OP_WR_EXE_REQ_IMM /**< 3.4.6.3 Immediately Execute Write Request */ -}; - -/** - * Write authorization context data structure. - */ -typedef struct { - uint16_t attr_handle; /**< handle of attribute to write */ - uint16_t offset; - uint16_t len; - uint8_t op; /**< @ref BLE_GATTS_WR_OPS */ - uint8_t data[]; -} ble_gatt_wr_evt_t; - -/** - * Read authorization context data structure. - */ -typedef struct { - uint16_t attr_handle; /**< handle of attribute to been read */ - uint16_t offset; -} ble_gatt_rd_evt_t; - -/** - * Connection attribute data is missing @ref MSG_ID_BLE_GATTS_CONN_ATTRIB_MISSING_EVT. - */ -typedef struct { - uint16_t miss_type; /**< missing connection attribute type */ -} ble_gatts_conn_attrib_missing_evt_t; - -/** - * Handle Value Confirmation, @ref MSG_ID_BLE_GATTS_INDICATION_CONF_EVT in response an handle value indication. - */ -typedef struct { - uint16_t handle; /**< attribute handle of indication value sent */ -} ble_gatts_handle_value_conf_evt_t; - -/** - * Read attribute value rsp message. - */ -typedef struct { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; - uint16_t conn_handle; /**< GAP connection handle */ - uint16_t val_handle; /**< handle of attribute value */ - uint16_t len; /**< length of value returned */ - uint16_t offset; /**< offset in the value. the same as in the rd request! */ - uint8_t value[]; /**< value data of length \ref len */ -} ble_gatts_rd_attrib_val_rsp_t; - -/** - * Indication or notification. - */ -typedef struct { - uint16_t val_handle; /**< handle of attribute value */ - uint16_t len; /**< len of attribute data value to indicate */ - uint8_t *p_data; /**< data to indicate (maybe NULL if currently stored shall be used) */ - uint16_t offset; /**< optional offset into attribute value data */ -} ble_gatts_ind_params_t; - -/** - * Connection related attribute data parameters (stack specific). - */ -typedef struct { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< result for request, if not success, the data afterwards may not be valid */ - uint16_t conn_handle; /**< connection handle of the retrieved connection attribute data */ - uint16_t len; /**< length of the following connection attribute data */ - uint8_t conn_attr_data[]; -} ble_gatts_rd_conn_attr_rsp_t; - -/** - * GATTS timeout @ref MSG_ID_BLE_GATTS_TO_EVT. - */ -typedef struct { - uint16_t reason; /**< reason for timeout */ -} ble_gatts_timeout_evt_t; - -/** - * BLE GATTS Indication Data structure. - */ -struct ble_gatts_evt_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_status_t status; /**< result for request, if not success, the data afterwards may not be valid */ - uint16_t conn_handle; - union { - ble_gatt_wr_evt_t wr; /**< write indication */ - ble_gatts_conn_attrib_missing_evt_t conn_attr; - /**< connection related attribute missing */ - ble_gatts_handle_value_conf_evt_t handle_val_conf; - /**< value confirmation (confirmation of an indication) */ - ble_gatts_timeout_evt_t timeout; /**< GATTS timeout occurred */ - }; -}; - -/** - * Create an new service, primary or include. - * - * @param p_svc_handle service handle - * @param p_uuid UUID of new service - * @param type primary versus included @ref BLE_GATT_SVC_TYPES - * @param cback Callback function to be called on reception of add service response - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_ADD_SERVICE_RSP @ref ble_gatts_add_svc_rsp - */ -int ble_gatts_add_service(svc_client_handle_t * p_svc_handle, - const struct bt_uuid * p_uuid, - uint8_t type, - ble_gatts_add_svc_cback_t cback, - void *p_priv); - -/** - * Include a (secondary) service into a primary service. - * - * @param p_svc_handle service handle - * @param svc_handle service to which to add the included service - * @param svc_handle_to_include the previously created includable service - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_ADD_INCL_SVC @ref ble_gatts_incl_svc_rsp_t - */ -int ble_gatts_add_included_svc(svc_client_handle_t * p_svc_handle, - uint16_t svc_handle, - uint16_t svc_handle_to_include, - void *p_priv); - -/** - * Add a characteristic to service. - * - * @note this may called with the same UUID. the returned handle will be different in this case to - * distinguish multiple instances of the same char - * - * @param p_svc_handle service handle - * @param svc_handle service to which to add the characteristic - * @param p_char meta data for characteristic - * @param cback Callback function called on reception of response message - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_ADD_CHARACTERISTIC @ref ble_gatts_add_char_rsp - */ -int ble_gatts_add_characteristic(svc_client_handle_t * p_svc_handle, - uint16_t svc_handle, - const struct ble_gatts_characteristic * p_char, - ble_gatts_add_char_cback_t cback, - void *p_priv); - -/** - * Add a descriptor to the last added characteristic. - * - * @note The descriptor is automatically added to the latest - * added characteristic - * - * @param p_svc_handle service handle - * @param p_desc description of the descriptor - * @param cback Callback function called on reception of response message - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_ADD_DESCRIPTOR @ref ble_gatts_add_desc_rsp - */ -int ble_gatts_add_descriptor(svc_client_handle_t * p_svc_handle, - const struct ble_gatts_descriptor * p_desc, - ble_gatts_add_desc_cback_t cback, - void * p_priv); - -/** - * Start BLE Service setup before. - * - * @param p_svc_handle service handle - * @param svc_handle service to start - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_START_SERVICE @ref ble_rsp - */ -int ble_gatts_start_service(svc_client_handle_t * p_svc_handle, - uint16_t svc_handle, - void *p_priv); - -/** - * Stop and remove service. - * - * @note Not yet supported - * - * @param p_svc_handle service handle - * @param svc_handle handle of characteristic to which to add the descriptor - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_REMOVE_SERVICE_RSP @ref ble_rsp - */ -int ble_gatts_remove_service(svc_client_handle_t * p_svc_handle, - uint16_t svc_handle, - void *p_priv); - -/** - * Send a service change indication. - * - * @note Not yet supported - * - * @param p_svc_handle service handle - * @param conn_handle handle of the connection affected by the service layout change - * @param start_handle start handle of changed attribute handle range - * @param end_handle end handle of changed attribute handle range - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_INDICATE_SERVICE_CHANGE @ref ble_gatts_rsp_t - */ -int ble_gatts_send_svc_changed(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - uint16_t start_handle, - uint16_t end_handle, - void *p_priv); - -/** - * Set an attribute value. - * - * @param p_svc_handle service handle - * @param value_handle handle of value to change - * @param len length of attribute value to write - * @param p_value attribute value data to write - * @param offset optional offset from which on to write the attribute value data - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_WR_ATTRIBUTE_VALUE ble_gatts_wr_attr_rsp_msg - */ -int ble_gatts_set_attribute_value(svc_client_handle_t * p_svc_handle, - uint16_t value_handle, - uint16_t len, - const uint8_t * p_value, - uint16_t offset, - void *p_priv); - -/** - * Get an attribute value. - * - * @param p_svc_handle service handle - * @param value_handle handle of the attribute value to retrieve - * @param len length of the attribute value to get - * @param offset optional offset from which on to get the attribute value - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_RD_ATTRIBUTE_VALUE @ref ble_gatts_rd_attrib_val_rsp_t - */ -int ble_gatts_get_attribute_value(svc_client_handle_t * p_svc_handle, - uint16_t value_handle, - uint16_t len, - uint16_t offset, - void *p_priv); - -/** - * Send notification. - * - * @param p_svc_handle service handle - * @param conn_handle handle of the connection affected by the service layout change - * @param p_ind_params length of attribute value to write - * @param cback callback function to be called on reception of reception of notif response - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_SEND_NOTIF_RSP @ref ble_gatts_notif_ind_rsp_msg - */ -int ble_gatts_send_notif(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const ble_gatts_ind_params_t * p_params, - ble_gatts_notif_ind_cback_t cback, - void *p_priv); - -/** - * Send indication. - * - * @param p_svc_handle service handle - * @param conn_handle handle of the connection affected by the service layout change - * @param p_ind_params length of attribute value to write - * @param cback callback function to be called on reception of reception of ind response - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_SEND_NOTIF_RSP @ref ble_gatts_notif_ind_rsp_msg - */ -int ble_gatts_send_ind(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const ble_gatts_ind_params_t * p_params, - ble_gatts_notif_ind_cback_t cback, - void *p_priv); - -/** - * Write stack specific data of a previously bonded connection. - * - * @note this data is typically stored in NVRAM in relation ship to the bonded device! - * (e.g. CCD) - * - * @param p_svc_handle service handle - * @param conn_handle handle of the connection - * @param p_data data blob specific to stack to write - * @param len length of above byte stream (little endian) - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_WR_CONN_ATTRIBUTES @ref ble_gatts_rsp_t - */ -int ble_gatts_write_conn_attributes(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const uint8_t * p_data, - uint16_t len, - void *p_priv); - -/** - * Read stack specific data of the bonded connection. - * - * @note this data is typically stored in NVRAM in relation ship to the bonded device! - * - * @param p_svc_handle service handle - * @param conn_handle handle of the connection - * @param p_priv pointer to private data - * - * @return @ref OS_ERR_TYPE - * @return MSG: MSG_ID_BLE_GATTS_RD_CONN_ATTRIBUTES @ref ble_gatts_rd_conn_attr_rsp_t - */ -int ble_gatts_read_conn_attributes(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - void *p_priv); - -/** @} */ - -#endif diff --git a/system/libarc32_arduino101/framework/include/services/services_ids.h b/system/libarc32_arduino101/framework/include/services/services_ids.h index ccf2260c..c153a468 100644 --- a/system/libarc32_arduino101/framework/include/services/services_ids.h +++ b/system/libarc32_arduino101/framework/include/services/services_ids.h @@ -57,6 +57,7 @@ enum { CFW_LAST_SERVICE_ID = 17 }; +#define MSG_ID_BLE_SERVICE_BASE (BLE_SERVICE_ID << 10) #define BLE_SERVICE_MSG_BASE (BLE_SERVICE_ID << 10) #define BLE_SERVICE_GAP_MSG_BASE (BLE_CORE_SERVICE_ID << 10) #define MSG_ID_GPIO_BASE (SOC_GPIO_SERVICE_ID << 10) diff --git a/system/libarc32_arduino101/framework/include/util/misc.h b/system/libarc32_arduino101/framework/include/util/misc.h new file mode 100644 index 00000000..ea2cdff4 --- /dev/null +++ b/system/libarc32_arduino101/framework/include/util/misc.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __MISC_H__ +#define __MISC_H__ + +/* required for offsetof */ +#include + +//#include "util/compiler.h" + +/** Generate a build time error if a condition is met + * + * @param condition The condition to be tested + * + * @note This function should be used inside a code block + */ +#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2 * !!(condition)])) + +/** Generate a build time error if a condition is met or return 0 + * + * @param condition The condition to be tested + * + * @return 0 + */ +#define BUILD_BUG_ON_ZERO(e) (sizeof(char[1 - 2 * !!(e)]) - 1) + +/** + * Similar to BUILD_BUG_ON_ZERO + */ +#define STATIC_ASSERT(EXPR) typedef char __unused static_assert_failed[(EXPR) ? \ + 1 : -1] + +/** Generate a build time error if the parameter is not an array otherwise return 0 + * + * @param A The array to be checked. + * + * @return 0 + */ +#define __must_be_array(A) \ + BUILD_BUG_ON_ZERO (HAVE_SAME_TYPE(A, &A[0])) + +/** + * Check if the integer provided is a power of two. + * + * @param A Number to be tested. + * + * @return true if value is power of two else false. + */ +#define IS_POWER_OF_TWO(A) ((A) != 0 && (((A)-1) & (A)) == 0) + +/** + * Given a pointer on a member, return the pointer on the containing struct + * + * @param ptr Pointer to the member + * @param type Containing struct + * @param member Member name + * + * @return Casted pointer to the struct + * + * @note Use if possible CONTAINER_OF() defined in Zephyr include + */ +#define container_of(ptr, type, member) ({ \ + (type *)((char *)(ptr) - \ + offsetof(type, member)); }) + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +/** + * Concatenate 4 ASCII characters into an integer + * + * @param key_1 first ASCII char + * @param key_2 second ASCII char + * @param key_3 third ASCII char + * @param key_4 fourth ASCII char + * + * @return the integer representation of the four-digit string + */ +#define GEN_KEY(key_1, key_2, key_3, key_4) \ + (key_1 << 24) | (key_2 << 16) | (key_3 << 8) | key_4 + +#endif /* __MISC_H__ */ diff --git a/system/libarc32_arduino101/framework/src/cfw/service_api.c b/system/libarc32_arduino101/framework/src/cfw/service_api.c index 936fa3f9..28b5bfe5 100644 --- a/system/libarc32_arduino101/framework/src/cfw/service_api.c +++ b/system/libarc32_arduino101/framework/src/cfw/service_api.c @@ -122,54 +122,6 @@ struct cfw_message * cfw_alloc_internal_msg(int msg_id, int size, void * priv) { return evt; } -void default_msg_handler(struct cfw_message * msg, void *data) { - pr_error(LOG_MODULE_CFW, "Bug: %s should not be called data: %p", __func__, data); - cfw_dump_message(msg); -} - -void client_handle_message(struct cfw_message * msg, void *param) { - _cfw_handle_t * h = (_cfw_handle_t*)param; - switch(CFW_MESSAGE_ID(msg)) { - case MSG_ID_CFW_OPEN_SERVICE: - { - cfw_open_conn_rsp_msg_t * cnf = (cfw_open_conn_rsp_msg_t *) msg; - /** Make client handle point to server handle */ - ((svc_client_handle_t*)cnf->client_handle)->server_handle = cnf->svc_server_handle; - /** Initialize service port. */ - ((svc_client_handle_t*)cnf->client_handle)->port = cnf->port; -#ifndef CONFIG_INFRA_IS_MASTER - /* Set local port and cpu id */ - if (get_cpu_id() != cnf->cpu_id) { - port_set_port_id(cnf->port); - port_set_cpu_id(cnf->port, cnf->cpu_id); - } -#endif - break; - } - case MSG_ID_CFW_CLOSE_SERVICE: - { - /* Free client-side conn */ - bfree(msg->conn); - break; - } - default: - //Nothing to do - break; - } - h->handle_msg(msg, h->data); -} - -cfw_handle_t cfw_init(void * queue, handle_msg_cb_t cb, void *cb_data) { - _cfw_handle_t * handle = (_cfw_handle_t*)balloc(sizeof(*handle), NULL); - handle->handle_msg = cb; - handle->data = cb_data; - - handle->client_port_id = port_alloc(queue); - - cfw_port_set_handler(handle->client_port_id, client_handle_message, handle); - - return (cfw_handle_t) handle; -} int _cfw_send_message(struct cfw_message * message) { diff --git a/system/libarc32_arduino101/framework/src/cfw_platform.c b/system/libarc32_arduino101/framework/src/cfw_platform.c index b2da0288..5daa0951 100644 --- a/system/libarc32_arduino101/framework/src/cfw_platform.c +++ b/system/libarc32_arduino101/framework/src/cfw_platform.c @@ -42,10 +42,11 @@ #include "cfw/cfw_messages.h" -#include "nordic_interface.h" +#include "nble_driver.h" /* FIXME: Service manager API */ extern void _cfw_init(void *); +extern void ble_cfw_service_init(int service_id, T_QUEUE queue); extern void *services; @@ -81,21 +82,6 @@ static void free_message_ipc(struct message *msg) { extern "C" { #endif - -void cfw_platform_nordic_init(void) -{ - /* Setup UART0 for BLE communication, HW flow control required */ - SET_PIN_MODE(18, QRK_PMUX_SEL_MODEA); /* UART0_RXD */ - SET_PIN_MODE(19, QRK_PMUX_SEL_MODEA); /* UART0_TXD */ - SET_PIN_MODE(40, QRK_PMUX_SEL_MODEB); /* UART0_CTS_B */ - SET_PIN_MODE(41, QRK_PMUX_SEL_MODEB); /* UART0_RTS_B */ - - /* Reset the nordic to force sync - Warning: not working everytime */ - nordic_interface_init(service_mgr_queue); - uart_ipc_init(0); - uart_ipc_set_channel(uart_ipc_channel_open(SYNC_CHANNEL, uart_ipc_message_cback)); -} - static void cfw_platform_mbx_int_enable(void) { /* Set up mailbox interrupt handler */ @@ -116,8 +102,6 @@ void cfw_platform_init(void) set_cpu_id(CPU_ID_ARC); set_cpu_message_sender(ipc_remote_cpu, send_message_ipc); set_cpu_free_handler(ipc_remote_cpu, free_message_ipc); - set_cpu_message_sender(CPU_ID_BLE, send_message_ipc_uart); - set_cpu_free_handler(CPU_ID_BLE, free_message_ipc_uart); service_mgr_queue = queue_create(IPC_QUEUE_DEPTH, NULL); @@ -131,6 +115,7 @@ void cfw_platform_init(void) shared_data->services, shared_data->service_mgr_port_id); #else _cfw_init(service_mgr_queue); + ble_cfw_service_init(BLE_SERVICE_ID, service_mgr_queue); /* Initialized shared structure. */ shared_data->ports = port_get_port_table(); diff --git a/system/libarc32_arduino101/framework/src/infra/log.c b/system/libarc32_arduino101/framework/src/infra/log.c index c0d0ab7a..7e21decf 100644 --- a/system/libarc32_arduino101/framework/src/infra/log.c +++ b/system/libarc32_arduino101/framework/src/infra/log.c @@ -62,10 +62,12 @@ void log_init() uint8_t i; for (i = 0; i < LOG_MODULE_NUM; i++) { - global_infos.modules_filter[i].status = 1; - global_infos.modules_filter[i].log_level = LOG_LEVEL_INFO; + global_infos.modules_filter[i].status = true; + global_infos.modules_filter[i].log_level = LOG_LEVEL_DEBUG; } - global_infos.log_level_limit = LOG_LEVEL_INFO; + global_infos.log_level_limit = LOG_LEVEL_DEBUG; + + global_infos.modules_filter[LOG_MODULE_MAIN].log_level = LOG_LEVEL_INFO; log_impl_init(); } diff --git a/system/libarc32_arduino101/framework/src/infra/log_impl_printk.c b/system/libarc32_arduino101/framework/src/infra/log_impl_printk.c index ed8f1e99..958b7521 100644 --- a/system/libarc32_arduino101/framework/src/infra/log_impl_printk.c +++ b/system/libarc32_arduino101/framework/src/infra/log_impl_printk.c @@ -32,13 +32,14 @@ #include "log_impl.h" #include "infra/log_backend.h" -extern int printk(const char * format, ...); +//extern int printk(const char * format, ...); +extern void printk(const char *fmt, va_list args); uint32_t log_write_msg(uint8_t level, uint8_t module, const char *format, va_list args) { // TODO - implement printk -// printk(format, args); + printk(format, args); return 0; } diff --git a/system/libarc32_arduino101/framework/src/nordic_interface.c b/system/libarc32_arduino101/framework/src/nordic_interface.c deleted file mode 100644 index daee0f12..00000000 --- a/system/libarc32_arduino101/framework/src/nordic_interface.c +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "os/os.h" -#include "cfw/cfw.h" -#include "cfw/cfw_debug.h" -#include "cfw/cfw_internal.h" -#include "cfw/cfw_messages.h" -#include "infra/ipc_uart.h" -#include "infra/ipc_requests.h" -#include "infra/log.h" -#include "infra/message.h" -#include "infra/time.h" -#include "drivers/soc_gpio.h" -#include "platform.h" - -#include "portable.h" - -/* Macro definition for reset pin */ -#define BLE_SW_CLK_PIN 27 -#define BLE_SWDIO_PIN 6 -#define RESET_PIN BLE_SWDIO_PIN -#define ATP_BLE_INT 5 - -static T_QUEUE service_mgr_queue; - -/* When we set val to 1 it will wake up the Nordic, setting it to 0 - * will set it back to sleep. */ -static int nordic_wake_assert(bool val) -{ - uint8_t ret = soc_gpio_write(SOC_GPIO_32_ID, ATP_BLE_INT, val); - if (ret != DRV_RC_OK) - pr_error(LOG_MODULE_IPC, "Error setting ATP_BLE_INT %d", val); - return ret; -} - -void nordic_wake_deassert(void* ignored) -{ - nordic_wake_assert(0); -} - -int send_message_ipc_uart(struct cfw_message * message) { - int ret = uart_ipc_send_message(uart_ipc_get_channel(), - CFW_MESSAGE_LEN(message), message); - message_free((struct message *)message); - return ret; -} - -void free_message_ipc_uart(void * ptr) { - bfree(ptr); -} - -/** - * IPC CFW message format is the following (little endian): - * ------------------------------------- - * | len: 2 bytes | chan 1 byte: sender cpu id: 1 byte | - * ------------------------------------- - * | REQUEST_ID | payload | - * ------------------------------------- - * - * For TYPE_MESSAGE request, the payload is the message copy. - * For TYPE_FREE is not valid (this ipc is not shared mem based) - */ -void uart_ipc_message_cback(uint8_t cpu_id, int channel, int len, void * p_data) -{ - struct cfw_message * msg; - unsigned int request = *(uint32_t*)p_data; - - switch (request) { - case IPC_MSG_TYPE_MESSAGE: - { - OS_ERR_TYPE error = E_OS_OK; - int size = len - sizeof(uint32_t); - msg = (struct cfw_message *)message_alloc(size, &error); - if (error != E_OS_OK) - pr_error(LOG_MODULE_IPC, "NO MEM: error: %d size: %d", error, size); - else { - memcpy(msg, (uint8_t *)p_data + sizeof(uint32_t), size); - handle_ipc_sync_request(cpu_id, request, 0, 0, msg); - } - break; - } - case IPC_REQUEST_ALLOC_PORT: - { - unsigned int ret; - unsigned int error; - uint16_t port_id = port_alloc(NULL); - port_set_cpu_id(port_id, cpu_id); - ret = port_id; - pr_info(LOG_MODULE_IPC, "%s return port_id %d", __func__, ret); - error = uart_ipc_send_sync_resp(channel, request, ret, 0, NULL); - if (error) - pr_error(LOG_MODULE_IPC, "%s returned error from ipc uart sync resp %d", __func__, error); - break; - } - default: - { - unsigned int error; - int32_t * p = ((int32_t *) p_data) + 1; - int32_t param0 = *p++; - int32_t param1 = *p++; - void * ptr = (void *) *p; - pr_info(LOG_MODULE_IPC, "%s request %xh, param1 %d, param2 %d", __func__, request, param0, param1); - handle_ipc_sync_request(cpu_id, request, param0, param1, ptr); - error = uart_ipc_send_sync_resp(channel, request, 0, 0, NULL); - if (error) - pr_error(LOG_MODULE_IPC, "%s returned error from ipc uart sync resp %d", __func__, error); - break; - } - } - bfree(p_data); - - /* Dequeue and process any new messages received */ - while(queue_process_message(service_mgr_queue) != 0); -} - -/* Nordic reset is achieved by asserting low the SWDIO pin. - * However, the Nordic chip can be in SWD debug mode, and NRF_POWER->RESET = 0 due to, - * other constraints: therefore, this reset might not work everytime, especially after - * flashing or debugging. - */ -static int nordic_reset(void) -{ - /* RESET_PIN depends on the board and the local configuration: check top of file */ - uint32_t delay_until; - gpio_cfg_data_t pin_cfg = { .gpio_type = GPIO_OUTPUT }; - - soc_gpio_set_config(SOC_GPIO_32_ID, RESET_PIN, &pin_cfg); - - /* Reset hold time is 0.2us (normal) or 100us (SWD debug) */ - soc_gpio_write(SOC_GPIO_32_ID, RESET_PIN, 0); - /* Wait for ~1ms */ - delay_until = get_uptime_32k() + 32768; - while (get_uptime_32k() < delay_until); - /* De-assert the reset */ - soc_gpio_write(SOC_GPIO_32_ID, RESET_PIN, 1); - - /* Set back GPIO to input to avoid interfering with external debugger */ - pin_cfg.gpio_type = GPIO_INPUT; - soc_gpio_set_config(SOC_GPIO_32_ID, RESET_PIN, &pin_cfg); - - return 0; -} - -int nordic_interface_init(T_QUEUE queue) -{ - uint8_t ret; - gpio_cfg_data_t config; - - service_mgr_queue = queue; - - config.gpio_type = GPIO_OUTPUT; - ret = soc_gpio_set_config(SOC_GPIO_32_ID, ATP_BLE_INT, &config); - if (ret != DRV_RC_OK) - return -1; - ret = nordic_wake_assert(1); - if (ret != DRV_RC_OK) - return -1; - ret = nordic_reset(); - if (ret != DRV_RC_OK) - return -1; - - return 0; -} diff --git a/system/libarc32_arduino101/framework/src/os/os.c b/system/libarc32_arduino101/framework/src/os/os.c index 63aa31f6..ed84eb8b 100644 --- a/system/libarc32_arduino101/framework/src/os/os.c +++ b/system/libarc32_arduino101/framework/src/os/os.c @@ -34,6 +34,7 @@ /************************* MEMORY *************************/ #ifdef TRACK_ALLOCS +#include "infra/log.h" int alloc_count = 0; #endif @@ -45,6 +46,7 @@ void * cfw_alloc(int size, OS_ERR_TYPE * err) { (*(int*) ptr) = size; #ifdef TRACK_ALLOCS alloc_count++; + pr_info(0, "alloc_count - %d", alloc_count); #endif interrupt_unlock(flags); return ptr; @@ -56,6 +58,7 @@ void cfw_free(void * ptr, OS_ERR_TYPE * err) { int flags = interrupt_lock(); #ifdef TRACK_ALLOCS alloc_count--; + pr_info(0, "alloc_countf - %d", alloc_count); #endif free(ptr); interrupt_unlock(flags); diff --git a/system/libarc32_arduino101/framework/src/os/panic.c b/system/libarc32_arduino101/framework/src/os/panic.c new file mode 100644 index 00000000..9fe88ef9 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/os/panic.c @@ -0,0 +1,16 @@ + +#include "os/os.h" + +extern void _do_fault(); +void panic(int x) +{ + _do_fault(); +} + + +void __assert_fail() +{ + panic(-10); +} + + diff --git a/system/libarc32_arduino101/framework/src/services/ble/ble_protocol.h b/system/libarc32_arduino101/framework/src/services/ble/ble_protocol.h deleted file mode 100644 index 460e7458..00000000 --- a/system/libarc32_arduino101/framework/src/services/ble/ble_protocol.h +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __BLE_PROTOCOL_H__ -#define __BLE_PROTOCOL_H__ - -/** - * @defgroup ble_protocol BLE protocol definitions - * - * BT Spec definitions. - * @ingroup ble_service - * @{ - * - * Bluetooth SIG defined macros and enum extracted from Bluetooth Spec 4.2 - */ -#define BLE_MAX_DEVICE_NAME 20 /**< Max BLE device name length, spec size: 248 */ -#define BLE_MAX_ADV_SIZE 31 - -/** Advertising Data Type. */ -#define BLE_ADV_TYPE_FLAGS 0x01 /* Flags */ -#define BLE_ADV_TYPE_INC_16_UUID 0x02 /* Incomplete List of 16-bit Service Class UUIDs */ -#define BLE_ADV_TYPE_COMP_16_UUID 0x03 /* Complete List of 16-bit Service Class UUIDs */ -#define BLE_ADV_TYPE_INC_32_UUID 0x04 /* Incomplete List of 32-bit Service Class UUIDs */ -#define BLE_ADV_TYPE_COMP_32_UUID 0x05 /* Complete List of 32-bit Service Class UUIDs */ -#define BLE_ADV_TYPE_INC_128_UUID 0x06 /* Incomplete List of 128-bit Service Class UUIDs */ -#define BLE_ADV_TYPE_COMP_128_UUID 0x07 /* Complete List of 128-bit Service Class UUIDs */ -#define BLE_ADV_TYPE_SHORT_LOCAL_NAME 0x08 /* Shortened Local Name */ -#define BLE_ADV_TYPE_COMP_LOCAL_NAME 0x09 /* Complete Local Name */ -#define BLE_ADV_TYPE_TX_POWER 0x0A /* Tx Power Level */ -#define BLE_ADV_TYPE_CLASS 0x0D /* Class of Device */ -#define BLE_ADV_TYPE_PAIRING_C 0x0E /* Simple Pairing Hash C */ -#define BLE_ADV_TYPE_PAIRING_C_192 0x0E /* Simple Pairing Hash C-192 */ -#define BLE_ADV_TYPE_PAIRING_R_192 0x0F /* Simple Pairing Randomizer R-192 */ -#define BLE_ADV_TYPE_DEVICE_ID 0x10 /* Device ID */ -#define BLE_ADV_TYPE_TK_VALUE 0x10 /* Security Manager TK Value */ -#define BLE_ADV_TYPE_OOB_FLAGS 0x11 /* Security Manager Out of Band Flags */ -#define BLE_ADV_TYPE_CONN_INTERVAL_RANGE 0x12 /* Slave Connection Interval Range */ -#define BLE_ADV_TYPE_SERVICE_SOLICITATION_16_UUID 0x14 /* List of 16-bit Service Solicitation UUIDs */ -#define BLE_ADV_TYPE_SERVICE_SOLICITATION_32_UUID 0x1F /* List of 32-bit Service Solicitation UUIDs */ -#define BLE_ADV_TYPE_SERVICE_SOLICITATION_128_UUID 0x15 /* List of 128-bit Service Solicitation UUIDs */ -#define BLE_ADV_TYPE_SERVICE_DATA 0x16 /* Service Data */ -#define BLE_ADV_TYPE_SERVICE_DATA_16_UUID 0x16 /* Service Data - 16-bit UUID */ -#define BLE_ADV_TYPE_SERVICE_DATA_32_UUID 0x20 /* Service Data - 32-bit UUID */ -#define BLE_ADV_TYPE_SERVICE_DATA_128_UUID 0x21 /* Service Data - 128-bit UUID */ -#define BLE_ADV_TYPE_SEC_CONF_VALUE 0x22 /* LE Secure Connections Confirmation Value */ -#define BLE_ADV_TYPE_SEC_RANDOM_VALUE 0x23 /* LE Secure Connections Random Value */ -#define BLE_ADV_TYPE_PUB_TARGET_ADDR 0x17 /* Public Target Address */ -#define BLE_ADV_TYPE_APPEARANCE 0x19 /* Appearance */ -#define BLE_ADV_TYPE_ADV_INTERVAL 0x1A /* Advertising Interval */ -#define BLE_ADV_TYPE_DEVICE_ADDR 0x1B /* LE Bluetooth Device Address */ -#define BLE_ADV_TYPE_ROLE 0x1C /* LE Role */ -#define BLE_ADV_TYPE_PAIRING_C_256 0x1D /* Simple Pairing Hash C-256 */ -#define BLE_ADV_TYPE_PAIRING_R_256 0x1E /* Simple Pairing Randomizer R-256 */ -#define BLE_ADV_TYPE_3D 0x3D /* 3D Information Data */ -#define BLE_ADV_TYPE_MANUFACTURER 0xFF /* Manufacturer Specific Data */ - -/** BLE Service UUID Definitions. */ -#define BLE_SVC_UUID_IMMEDIATE_ALERT_SERVICE 0x1802 /**< Immediate Alert service UUID. */ -#define BLE_SVC_UUID_LINK_LOSS_SERVICE 0x1803 /**< Link Loss service UUID. */ -#define BLE_SVC_UUID_TX_POWER_SERVICE 0x1804 /**< TX Power service UUID. */ -#define BLE_SVC_UUID_CURRENT_TIME_SERVICE 0x1805 /**< Current Time service UUID. */ -#define BLE_SVC_UUID_REFERENCE_TIME_UPDATE_SERVICE 0x1806 /**< Reference Time Update service UUID. */ -#define BLE_SVC_UUID_NEXT_DST_CHANGE_SERVICE 0x1807 /**< Next Dst Change service UUID. */ -#define BLE_SVC_UUID_GLUCOSE_SERVICE 0x1808 /**< Glucose service UUID. */ -#define BLE_SVC_UUID_HEALTH_THERMOMETER_SERVICE 0x1809 /**< Health Thermometer service UUID. */ -#define BLE_SVC_UUID_DEVICE_INFORMATION_SERVICE 0x180A /**< Device Information service UUID. */ -#define BLE_SVC_UUID_HEART_RATE_SERVICE 0x180D /**< Heart Rate service UUID. */ -#define BLE_SVC_UUID_PHONE_ALERT_STATUS_SERVICE 0x180E /**< Phone Alert Status service UUID. */ -#define BLE_SVC_UUID_BATTERY_SERVICE 0x180F /**< Battery service UUID. */ -#define BLE_SVC_UUID_BLOOD_PRESSURE_SERVICE 0x1810 /**< Blood Pressure service UUID. */ -#define BLE_SVC_UUID_ALERT_NOTIFICATION_SERVICE 0x1811 /**< Alert Notification service UUID. */ -#define BLE_SVC_UUID_HUMAN_INTERFACE_DEVICE_SERVICE 0x1812 /**< Human Interface Device service UUID. */ -#define BLE_SVC_UUID_SCAN_PARAMETERS_SERVICE 0x1813 /**< Scan Parameters service UUID. */ -#define BLE_SVC_UUID_RUNNING_SPEED_AND_CADENCE 0x1814 /**< Running Speed and Cadence service UUID. */ -#define BLE_SVC_UUID_CYCLING_SPEED_AND_CADENCE 0x1816 /**< Cycling Speed and Cadence service UUID. */ - -#define BLE_SVC_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE (0x01) /**< LE Limited Discoverable Mode. */ -#define BLE_SVC_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE (0x02) /**< LE General Discoverable Mode. */ -#define BLE_SVC_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED (0x04) /**< BR/EDR not supported. */ -#define BLE_SVC_GAP_ADV_FLAG_LE_BR_EDR_CONTROLLER (0x08) /**< Simultaneous LE and BR/EDR, Controller. */ -#define BLE_SVC_GAP_ADV_FLAG_LE_BR_EDR_HOST (0x10) /**< Simultaneous LE and BR/EDR, Host. */ -#define BLE_SVC_GAP_ADV_FLAGS_LE_ONLY_LIMITED_DISC_MODE (BLE_SVC_GAP_ADV_FLAG_LE_LIMITED_DISC_MODE | BLE_SVC_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) /**< LE Limited Discoverable Mode, BR/EDR not supported. */ -#define BLE_SVC_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE (BLE_SVC_GAP_ADV_FLAG_LE_GENERAL_DISC_MODE | BLE_SVC_GAP_ADV_FLAG_BR_EDR_NOT_SUPPORTED) /**< LE General Discoverable Mode, BR/EDR not supported. */ - - -/** - * Characteristics UUID definitions. - */ - -/* GAP */ -#define BLE_GAP_DEVICE_NAME 0x2A00 -#define BLE_GAP_APPEARANCE 0x2A01 -#define BLE_GAP_PER_PRIVACY_FLAG 0x2A02 -#define BLE_GAP_RECONN_ADDR 0x2A03 -#define BLE_GAP_PREF_CONN_PARAM 0x2A04 - -/* DIS */ -#define BLE_DIS_MANUFACTURER_NAME 0x2A29 -#define BLE_DIS_MODEL_NB 0x2A24 -#define BLE_DIS_SERIAL_NB 0x2A25 -#define BLE_DIS_FW_REV 0x2A26 -#define BLE_DIS_HW_REV 0x2A27 -#define BLE_DIS_SW_REV 0x2A28 -#define BLE_DIS_SYS_ID 0x2A23 -#define BLE_DIS_CERTIF_DATA_LIST 0x2A2A -#define BLE_DIS_PNP_ID 0x2A50 - -/* BATTERY */ -#define BLE_BAT_BAT_LEVEL 0x2A19 - -/* HR */ -#define BLE_HEART_RATE_MEASUREMENT 0x2A37 -#define BLE_HEART_RATE_SENSOR_LOCATION 0x2A38 -#define BLE_HEART_RATE_CONTROL_POINT 0x2A39 - -/* RSC */ -#define BLE_RSC_MEASUREMENT 0x2A53 -#define BLE_RSC_SENSOR_LOCATION 0x2A5D -#define BLE_RSC_SUPPORTED_FEATURE 0x2A54 -#define BLE_RSC_CONTROL_POINT 0x2A55 - -/*CSC */ -#define BLE_CSC_MEASUREMENT 0x2A5B -#define BLE_CSC_SENSOR_LOCATION BLE_RSC_SENSOR_LOCATION -#define BLE_CSC_SUPPORTED_FEATURE 0x2A5C -#define BLE_CSC_CONTROL_POINT 0x2A55 - -/* CP */ -#define BLE_CP_MEASUREMENT 0x2A63 -#define BLE_CP_SENSOR_LOCATION BLE_RSC_SENSOR_LOCATION -#define BLE_CP_SUPPORTED_FEATURE 0x2A65 -#define BLE_CP_POWER_VECTOR 0x2A64 -#define BLE_CP_CONTROL_POINT 0x2A66 - -/* HCI status (error) codes as per BT spec */ -#define HCI_REMOTE_USER_TERMINATED_CONNECTION 0x13 -#define HCI_REMOTE_DEV_TERMINATION_DUE_TO_LOW_RESOURCES 0x14 -#define HCI_REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF 0x15 -#define HCI_LOCAL_HOST_TERMINATED_CONNECTION 0x16 - -/** BLE GAP Appearance Characteristic definitions. - * - * See http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml - */ -#define BLE_GAP_APPEARANCE_TYPE_UNKNOWN 0 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_PHONE 64 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_COMPUTER 128 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_WATCH 192 -#define BLE_GAP_APPEARANCE_TYPE_WATCH_SPORTS_WATCH 193 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_CLOCK 256 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_DISPLAY 320 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_REMOTE_CONTROL 384 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_EYE_GLASSES 448 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_TAG 512 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_KEYRING 576 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_MEDIA_PLAYER 640 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_BARCODE_SCANNER 704 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_THERMOMETER 768 -#define BLE_GAP_APPEARANCE_TYPE_THERMOMETER_EAR 769 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_HEART_RATE_SENSOR 832 -#define BLE_GAP_APPEARANCE_TYPE_HEART_RATE_SENSOR_HEART_RATE_BELT 833 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_BLOOD_PRESSURE 896 -#define BLE_GAP_APPEARANCE_TYPE_BLOOD_PRESSURE_ARM 897 -#define BLE_GAP_APPEARANCE_TYPE_BLOOD_PRESSURE_WRIST 898 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_HID 960 -#define BLE_GAP_APPEARANCE_TYPE_HID_KEYBOARD 961 -#define BLE_GAP_APPEARANCE_TYPE_HID_MOUSE 962 -#define BLE_GAP_APPEARANCE_TYPE_HID_JOYSTICK 963 -#define BLE_GAP_APPEARANCE_TYPE_HID_GAMEPAD 964 -#define BLE_GAP_APPEARANCE_TYPE_HID_DIGITIZERSUBTYPE 965 -#define BLE_GAP_APPEARANCE_TYPE_HID_CARD_READER 966 -#define BLE_GAP_APPEARANCE_TYPE_HID_DIGITAL_PEN 967 -#define BLE_GAP_APPEARANCE_TYPE_HID_BARCODE 968 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_GLUCOSE_METER 1024 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_RUNNING_WALKING_SENSOR 1088 -#define BLE_GAP_APPEARANCE_TYPE_RUNNING_WALKING_SENSOR_IN_SHOE 1089 -#define BLE_GAP_APPEARANCE_TYPE_RUNNING_WALKING_SENSOR_ON_SHOE 1090 -#define BLE_GAP_APPEARANCE_TYPE_RUNNING_WALKING_SENSOR_ON_HIP 1091 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_CYCLING 1152 -#define BLE_GAP_APPEARANCE_TYPE_CYCLING_CYCLING_COMPUTER 1153 -#define BLE_GAP_APPEARANCE_TYPE_CYCLING_SPEED_SENSOR 1154 -#define BLE_GAP_APPEARANCE_TYPE_CYCLING_CADENCE_SENSOR 1155 -#define BLE_GAP_APPEARANCE_TYPE_CYCLING_POWER_SENSOR 1156 -#define BLE_GAP_APPEARANCE_TYPE_CYCLING_SPEED_CADENCE_SENSOR 1157 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_PULSE_OXIMETER 3136 -#define BLE_GAP_APPEARANCE_TYPE_PULSE_OXIMETER_FINGERTIP 3137 -#define BLE_GAP_APPEARANCE_TYPE_PULSE_OXIMETER_WRIST_WORN 3138 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_WEIGHT_SCALE 3200 -#define BLE_GAP_APPEARANCE_TYPE_GENERIC_OUTDOOR_SPORTS_ACT 5184 -#define BLE_GAP_APPEARANCE_TYPE_OUTDOOR_SPORTS_ACT_LOC_DISP 5185 -#define BLE_GAP_APPEARANCE_TYPE_OUTDOOR_SPORTS_ACT_LOC_AND_NAV_DISP 5186 -#define BLE_GAP_APPEARANCE_TYPE_OUTDOOR_SPORTS_ACT_LOC_POD 5187 -#define BLE_GAP_APPEARANCE_TYPE_OUTDOOR_SPORTS_ACT_LOC_AND_NAV_POD 5188 - -/** - * DTM commands, opcodes, indexes. - */ -#define DTM_HCI_CMD 0x01 -#define DTM_HCI_OPCODE2 0x20 - -#define DTM_HCI_STATUS_IDX 6 -#define DTM_HCI_LE_END_IDX (DTM_HCI_STATUS_IDX + 1) - -/** @} */ - -#endif diff --git a/system/libarc32_arduino101/framework/src/services/ble/ble_service_core_int.h b/system/libarc32_arduino101/framework/src/services/ble/ble_service_core_int.h deleted file mode 100644 index d2b9311d..00000000 --- a/system/libarc32_arduino101/framework/src/services/ble/ble_service_core_int.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __BLE_SERVICE_CORE_INT_H__ -#define __BLE_SERVICE_CORE_INT_H__ - -#include -#include "services/ble/ble_service_gap_api.h" -#include "services/ble/ble_service_gattc_api.h" - -/* Forward declarations */ -struct _ble_service_cb; -struct ble_enable_req_msg; - -/** - * BLE common structures. - */ - -struct ble_svc_string { - uint8_t *p_string; /**< String utf8 encoded */ - uint8_t len; /**< length of string */ -}; - -struct ble_svc_sec_mode { - uint8_t rd_perm; - uint8_t wr_perm; -}; - -struct ble_svc_cccd_sec_mode { - uint8_t cccd_wr_perm; - uint8_t rd_perm; /**< Read permissions. */ - uint8_t wr_perm; /**< Write permissions. */ -}; - -struct ble_svc_report_reference { - uint8_t report_id; /**< Non-zero value if these is more than one instance of the same Report Type */ - uint8_t report_type; /**< Type of Report characteristic */ -}; - -struct ble_gap_write_config_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - uint16_t appearance; - uint8_t bda_len; - uint8_t name_len; - int8_t tx_power; - struct ble_gap_connection_params peripheral_conn_params; - struct ble_gap_connection_params central_conn_params; - uint8_t data[]; -}; - -struct ble_gap_wr_adv_data_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - uint8_t adv_len; - uint8_t scan_rsp_len; - uint8_t data[]; - /* adv_data, - * scan_rsp_dat */ -}; - -struct ble_gap_start_advertise_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - uint16_t timeout; - uint16_t interval_min; /**< min interval 0xffff: use default 0x0800 */ - uint16_t interval_max; /**< max interval 0xffff: use default 0x0800 */ - uint8_t type; /**< advertisement types @ref GAP_ADV_TYPES */ - uint8_t filter_policy; /**< filter policy to apply with white list */ - uint8_t options; /**< options see @ref BLE_GAP_ADV_OPTIONS (to be ORed) */ - uint8_t bda_len; /**< non 0 if peer_bda is present */ - uint8_t peer_bda[]; /**< format ble_addr_t */ -}; - -struct ble_gap_conn_update_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - uint16_t conn_handle; - struct ble_gap_connection_params conn_params; -}; - -struct ble_gap_svc_local_name_req { - uint8_t sec_mode; /**< security mode for writing device name, @ref BLE_GAP_SEC_MODES (GAP_SEC_NO_PERMISSION: write forbidden) */ - uint8_t authorization; /**< 0: no authorization, 1: authorization required */ - uint8_t len; /**< device name length (0-248) */ - uint8_t name_array[]; /**< name to to write */ -}; - -struct ble_gap_service_write_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - uint16_t attr_type; /**< GAP Characteristics attribute type @ref BLE_GAP_SVC_ATTR_TYPE */ - union { - struct ble_gap_svc_local_name_req name; - uint16_t appearance; /**< Appearance UUID */ - struct ble_gap_connection_params conn_params; - /**< Preferred Peripheral Connection Parameters */ - uint8_t car; /**< Central Address Resolution support 0: no, 1: yes */ - }; -}; - -struct ble_gap_disconnect_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - uint16_t conn_handle; /**< Connection handle*/ - uint8_t reason; /**< Reason of the disconnect*/ -}; - -struct ble_gap_sm_config_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - struct ble_gap_sm_config_params params; -}; - -struct ble_gap_sm_pairing_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - struct ble_gap_sm_pairing_params params; - uint16_t conn_handle; -}; - -struct ble_dtm_test_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - struct ble_test_cmd params; -}; - -struct ble_gap_set_rssi_report_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - struct rssi_report_params params; /**< RSSI report config @ref rssi_report_cfg */ -}; - -struct ble_gattc_discover_primary_service_req_msg { - struct cfw_message header; - uint16_t conn_handle; /**< Connection handle */ - uint8_t data[]; /**< Variable length data of the request (UUID) */ -}; - -struct ble_gattc_discover_included_service_req_msg { - struct cfw_message header; - uint16_t conn_handle; - struct ble_gattc_handle_range handle_range; -}; - -struct ble_gattc_discover_characteristic_req_msg { - struct cfw_message header; - uint16_t conn_handle; - struct ble_gattc_handle_range handle_range; -}; - -struct ble_gattc_discover_descriptor_req_msg { - struct cfw_message header; - uint16_t conn_handle; - struct ble_gattc_handle_range handle_range; -}; - -struct ble_gattc_read_characteristic_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - uint16_t conn_handle; /**< Connection handle*/ - uint16_t char_handle; /**< handle of the attribute to be read */ - uint16_t offset; /**< offset into the attribute value to be read */ -}; - -struct _ble_gattc_wr_characteristic { - uint16_t char_handle; /**< handle of characteristic */ - uint16_t len; /**< if len is bigger then ATT MTU size, the controller fragment buffer itself */ - uint8_t wr_type; /**< type of write operation @ref BLE_GATT_WR_OP_TYPES */ - uint8_t value[]; /**< characteristic value to write */ -}; - -struct ble_gattc_write_char_op_req_msg { - struct cfw_message header; - uint16_t conn_handle; - struct _ble_gattc_wr_characteristic wr_char_param; -}; - -/** - * Generic BLE controller commands. - */ -struct ble_generic_cmd_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ -}; - - -void ble_core_resume_enable(struct ble_enable_req_msg *req, struct _ble_service_cb * p_cb, uint8_t * p_name); - -void ble_core_delete_conn_params_timer(void); - -#endif diff --git a/system/libarc32_arduino101/framework/src/services/ble/ble_service_gap_api.c b/system/libarc32_arduino101/framework/src/services/ble/ble_service_gap_api.c deleted file mode 100644 index 6b772864..00000000 --- a/system/libarc32_arduino101/framework/src/services/ble/ble_service_gap_api.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include "services/ble/ble_service_gap_api.h" -#include "ble_protocol.h" -#include "ble_service_core_int.h" -#include "infra/log.h" - -int ble_gap_set_enable_config(svc_client_handle_t * p_svc_handle, - const struct ble_wr_config * p_config, - void * p_priv) -{ - struct ble_gap_write_config_req_msg *msg; - int total_len = sizeof(*msg); - int str_len = 0; - - if (p_config->p_bda) - total_len += sizeof(ble_addr_t); - if (p_config->p_name) { - str_len = strlen((char *)p_config->p_name); - if (str_len > BLE_MAX_DEVICE_NAME) - return -2; - total_len += str_len; - } - msg = (struct ble_gap_write_config_req_msg *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_WR_CONF_REQ, - total_len, - p_priv); - msg->name_len = str_len; - msg->appearance = p_config->appearance; - msg->tx_power = p_config->tx_power; - msg->central_conn_params = p_config->central_conn_params; - msg->peripheral_conn_params = p_config->peripheral_conn_params; - if (p_config->p_bda) { - msg->bda_len = sizeof(ble_addr_t); - memcpy(msg->data, p_config->p_bda, msg->bda_len); - } else - msg->bda_len = 0; - if (p_config->p_name) - strcpy((char *)&msg->data[msg->bda_len], (char *)p_config->p_name); - - return cfw_send_message(msg); -} - -int ble_gap_read_bda(svc_client_handle_t * p_svc_handle, void * p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_RD_BDA_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_wr_adv_data(svc_client_handle_t * p_svc_handle, - const struct ble_gap_adv_rsp_data * p_adv_data, - const struct ble_gap_adv_rsp_data * p_scan_data, - void * p_priv) -{ - int data_len = sizeof(struct ble_gap_wr_adv_data_req_msg); - struct ble_gap_wr_adv_data_req_msg *msg; - - if (NULL != p_adv_data) - data_len += p_adv_data->len; - if (NULL != p_scan_data) - data_len += p_scan_data->len; - - msg = (struct ble_gap_wr_adv_data_req_msg *)cfw_alloc_message_for_service( - p_svc_handle, MSG_ID_BLE_GAP_WR_ADV_DATA_REQ, - data_len, p_priv); - - if (NULL != p_adv_data) { - int i; - msg->adv_len = p_adv_data->len; - for (i = 0; i < msg->adv_len; i++) - msg->data[i] = p_adv_data->p_data[i]; - } else - msg->adv_len = 0; - - if (NULL != p_scan_data) { - int i; - msg->scan_rsp_len = p_scan_data->len; - for (i = 0; i < msg->adv_len; i++) - msg->data[msg->adv_len + i] = p_scan_data->p_data[i]; - } else - msg->scan_rsp_len = 0; - - return cfw_send_message(msg); -} - - -int ble_gap_wr_white_list(svc_client_handle_t * p_svc_handle, - const struct ble_gap_whitelist_info * p_white_list, - void * p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_WR_WHITE_LIST_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_clr_white_list(svc_client_handle_t * p_svc_handle, - uint32_t wl_hdl, void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_CLR_WHITE_LIST_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_start_advertise(svc_client_handle_t * p_svc_handle, - const ble_gap_adv_param_t * p_adv_param, - void *p_priv) -{ - struct ble_gap_start_advertise_req_msg *msg; - int data_len = sizeof(struct ble_gap_start_advertise_req_msg); - - if ((NULL != p_adv_param) && (NULL != p_adv_param->p_peer_bda)) { - data_len += sizeof(ble_addr_t); - } - msg = (struct ble_gap_start_advertise_req_msg *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_ENABLE_ADV_REQ, - data_len, - p_priv); - if (NULL != p_adv_param) { - uint8_t i; - msg->filter_policy = p_adv_param->filter_policy; - msg->interval_max = p_adv_param->interval_max; - msg->interval_min = p_adv_param->interval_min; - msg->options = p_adv_param->options; - msg->timeout = p_adv_param->timeout; - msg->type = p_adv_param->type; - if (NULL != (p_adv_param->p_peer_bda)) - { - msg->peer_bda[0] = p_adv_param->p_peer_bda->type; - for (i = 1; i <= BLE_ADDR_LEN; i++) - { - msg->peer_bda[i] = p_adv_param->p_peer_bda->addr[i-1]; - } - msg->bda_len = i; - } else - msg->bda_len = 0; - } - return cfw_send_message(msg); -} - -int ble_gap_stop_advertise(svc_client_handle_t * p_svc_handle, void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_DISABLE_ADV_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_conn_update(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const struct ble_gap_connection_params * p_conn_param, - void *p_priv) -{ - CFW_ALLOC_FOR_SVC(struct ble_gap_conn_update_req_msg, msg, p_svc_handle, - MSG_ID_BLE_GAP_CONN_UPDATE_REQ, 0, p_priv); - - msg->conn_handle = conn_handle; - msg->conn_params = *p_conn_param; - - return cfw_send_message(msg); -} - -int ble_gap_disconnect(svc_client_handle_t * p_svc_handle, - uint16_t conn_hdl, uint8_t reason, - void *p_priv) -{ - struct ble_gap_disconnect_req_msg *msg; - - msg = (struct ble_gap_disconnect_req_msg*)cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_DISCONNECT_REQ, - sizeof(*msg), - p_priv); - - msg->reason = reason; - msg->conn_handle = conn_hdl; - - return cfw_send_message(msg); -} - -int ble_gap_service_write(svc_client_handle_t * p_svc_handle, - const struct ble_gap_service_write_params * p_params, - void *p_priv) -{ - struct ble_gap_service_write_req_msg *msg; - int total_len = sizeof(*msg); - if ((p_params->attr_type == GAP_SVC_ATTR_NAME) && (p_params->name.len)) { - total_len += p_params->name.len; - } - msg = (struct ble_gap_service_write_req_msg *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_SERVICE_WRITE_REQ, - total_len, - p_priv); - msg->attr_type = p_params->attr_type; - - switch (p_params->attr_type) { - case GAP_SVC_ATTR_NAME: - msg->name.authorization = p_params->name.authorization; - msg->name.len = p_params->name.len; - msg->name.sec_mode = p_params->name.sec_mode; - if (msg->name.len) - strcpy((char *)&msg->name.name_array[0], (char *)p_params->name.p_name); - break; - case GAP_SVC_ATTR_APPEARANCE: - msg->appearance = p_params->appearance; - break; - case GAP_SVC_ATTR_PPCP: - msg->conn_params = p_params->conn_params; - break; - case GAP_SVC_ATTR_CAR: - msg->car = p_params->car; - break; - default: - pr_warning(LOG_MODULE_BLE, "ble_gap_srv_wr: Attr " - "not supported : 0x%x", p_params->attr_type); - } - - return cfw_send_message(msg); -} - -int ble_gap_service_read(svc_client_handle_t * p_svc_handle, - uint16_t type, void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_SERVICE_READ_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_sm_config(const svc_client_handle_t * h, - const struct ble_gap_sm_config_params * p_params, - void *p_priv) -{ - CFW_ALLOC_FOR_SVC(struct ble_gap_sm_config_req_msg, msg, h, MSG_ID_BLE_GAP_SM_CONFIG_REQ, 0, p_priv); - - msg->params = *p_params; - - return cfw_send_message(msg); -} - -int ble_gap_sm_pairing_req(const svc_client_handle_t * h, - uint16_t conn_handle, - const struct ble_gap_sm_pairing_params * p_params, - void *p_priv) -{ - CFW_ALLOC_FOR_SVC(struct ble_gap_sm_pairing_req_msg, msg, h, MSG_ID_BLE_GAP_SM_PAIRING_REQ, 0, p_priv); - - msg->params = *p_params; - msg->conn_handle = conn_handle; - - return cfw_send_message(msg); -} - -int ble_gap_set_rssi_report(svc_client_handle_t * p_svc_handle, - const struct rssi_report_params *params, - void *p_priv) -{ - CFW_ALLOC_FOR_SVC(struct ble_gap_set_rssi_report_req_msg, msg, p_svc_handle, MSG_ID_BLE_GAP_SET_RSSI_REPORT_REQ, 0, p_priv); - msg->params = *params; - return cfw_send_message(msg); -} - -int ble_gap_start_scan(svc_client_handle_t * p_svc_handle, - const ble_gap_scan_param_t * p_scan_params, void * p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_SCAN_START_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_stop_scan(svc_client_handle_t * p_svc_handle, void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_SCAN_STOP_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_connect(svc_client_handle_t * p_svc_handle, const ble_addr_t * p_bd, - const ble_gap_scan_param_t * p_scan_params, - const struct ble_gap_connection_params * p_conn_params, - void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_CONNECT_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_cancel_connect(svc_client_handle_t * p_svc_handle, - const ble_addr_t * p_bd, void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_CONNECT_CANCEL_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_set_option(svc_client_handle_t * p_svc_handle, uint8_t op, - const ble_gap_option_t * p_opt, void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_SET_OPTIONS_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_generic_cmd_req(svc_client_handle_t * p_svc_handle, - const struct ble_gap_gen_cmd_params *p_params, - void *p_priv) -{ - struct ble_generic_cmd_req_msg *msg = (struct ble_generic_cmd_req_msg *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_GENERIC_CMD_REQ, - sizeof(*msg), - p_priv); - - return cfw_send_message(msg); -} - -int ble_gap_get_version_req(svc_client_handle_t * p_svc_handle, void * p_priv) -{ - struct cfw_message *msg = (struct cfw_message *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_GET_VERSION_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gap_dtm_init_req(svc_client_handle_t * p_svc_handle, void * p_priv) -{ - struct cfw_message *msg = (struct cfw_message *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GAP_DTM_INIT_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} diff --git a/system/libarc32_arduino101/framework/src/services/ble/ble_service_gatt_int.h b/system/libarc32_arduino101/framework/src/services/ble/ble_service_gatt_int.h deleted file mode 100644 index 3f42b09f..00000000 --- a/system/libarc32_arduino101/framework/src/services/ble/ble_service_gatt_int.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef __BLE_SERVICE_GATT_INT_H__ -#define __BLE_SERVICE_GATT_INT_H__ - -#include "services/ble/ble_service_gatt.h" -#include "services/ble/ble_service_gatts_api.h" - -/** - * BLE GATT related shared internal structures between master and BLE controller. - */ -struct ble_gatts_add_service_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_gatts_add_svc_cback_t cback; /**< Callback function to be returnedn in response message */ - uint8_t type; /**< Primary or secondary \ref BLE_GATT_SVC_TYPES. */ - uint8_t data[]; /**< Variable length data of the request (UUID) */ -}; - -/** - * Component framework message to add a characteristic to a service - */ -struct ble_gatts_add_char_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_gatts_add_char_cback_t cback; /**< Callback function, to be returned to sender! */ - uint16_t svc_handle; /**< Handle of the service to which the characteristic should be added */ - struct ble_gatts_permissions perms; - struct ble_gatt_char_properties props; - uint16_t max_len; /**< Maximum length of the characteristic value */ - uint16_t init_len; /**< Length of the initialization value */ - uint8_t ud_len; /**< Length of the characteristic User Description */ - uint8_t pf_len; - - uint8_t data[]; /**< Variable length data of the request (UUID, init value, user descriptor) */ -}; - -/** - * Component framework message to add a descriptor to a characteristic - */ -struct ble_gatts_add_desc_req_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_gatts_add_desc_cback_t cback; /**< Callback function, to be returned to sender! */ - uint16_t length; - struct ble_gatts_permissions perms; /**< Read/Write permissions for descriptor */ - uint8_t data[]; /**< Variable length data of the request (descriptor value) */ -}; - -struct ble_gatts_start_service_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - uint16_t svc_handle; -}; - -struct ble_gatts_set_attribute_value_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - uint16_t value_handle; /* mandatory */ - uint16_t len; - uint16_t offset; /* by default 0 */ - uint8_t data[]; /* value to update */ -}; - -struct ble_gatts_notif_ind_params { - uint16_t conn_handle; - uint16_t val_handle; - uint16_t len; /* may be 0, in this case already stored data is sent */ - uint16_t offset; - uint8_t data[]; -}; - -struct ble_gatts_send_notif_ind_msg { - struct cfw_message header; /**< Component framework message header (@ref cfw), MUST be first element of structure */ - ble_gatts_notif_ind_cback_t cback; /**< Callback function to be returned in response message */ - struct ble_gatts_notif_ind_params params; /* parameters for notification/indication */ -}; - -#endif diff --git a/system/libarc32_arduino101/framework/src/services/ble/ble_service_gatts_api.c b/system/libarc32_arduino101/framework/src/services/ble/ble_service_gatts_api.c deleted file mode 100644 index 17345bde..00000000 --- a/system/libarc32_arduino101/framework/src/services/ble/ble_service_gatts_api.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (c) 2015, Intel Corporation. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "services/ble/ble_service_gatt.h" -#include "services/ble/ble_service_gatts_api.h" -#include "ble_service_core_int.h" -#include "ble_service_gatt_int.h" -#include "ble_service_utils.h" -#include - -int ble_gatts_add_service(svc_client_handle_t * p_svc_handle, - const struct bt_uuid * p_uuid, - uint8_t type, - ble_gatts_add_svc_cback_t cback, - void *p_priv) -{ - struct ble_gatts_add_service_req_msg *msg = - (struct ble_gatts_add_service_req_msg *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_ADD_SERVICE_REQ, - sizeof(*msg) + ble_sizeof_bt_uuid(p_uuid), - p_priv); - msg->cback = cback; - msg->type = type; - uint8_t *p = (uint8_t *)&msg->data; - ble_encode_bt_uuid(p_uuid, p); - return cfw_send_message(msg); -} - -int ble_gatts_add_included_svc(svc_client_handle_t * p_svc_handle, - uint16_t svc_handle, - uint16_t svc_handle_to_include, - void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_ADD_INCL_SVC_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gatts_add_characteristic(svc_client_handle_t * p_svc_handle, - uint16_t svc_handle, - const struct ble_gatts_characteristic * p_char, - ble_gatts_add_char_cback_t cback, - void *p_priv) -{ - struct ble_gatts_add_char_req_msg *msg; - int data_len = sizeof(struct ble_gatts_add_char_req_msg); - uint8_t * p; - - // Sanity check - if (!p_char || !p_char->p_uuid) - return E_OS_ERR; - - // compute the variable length part of the message - data_len += ble_sizeof_bt_uuid(p_char->p_uuid); - data_len += p_char->init_len; - if (p_char->p_user_desc) - data_len += p_char->p_user_desc->len; - if (p_char->p_char_pf_desc) - data_len += sizeof(struct ble_gatt_pf_desc); - - msg = (struct ble_gatts_add_char_req_msg *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_ADD_CHARACTERISTIC_REQ, - data_len, p_priv); - msg->cback = cback; - p = msg->data; - p = ble_encode_bt_uuid(p_char->p_uuid, p); - msg->svc_handle = svc_handle; - msg->perms = p_char->perms; - msg->props = p_char->props; - msg->max_len = p_char->max_len; - msg->init_len = p_char->init_len; - memcpy(p, p_char->p_value, p_char->init_len); - p += p_char->init_len; - if (p_char->p_user_desc) { - msg->ud_len = p_char->p_user_desc->len; - memcpy(p, p_char->p_user_desc->buffer, p_char->p_user_desc->len); - p += p_char->p_user_desc->len; - } - if (p_char->p_char_pf_desc) { - msg->pf_len = sizeof(struct ble_gatt_pf_desc); - memcpy(p, p_char->p_char_pf_desc, msg->pf_len); - p += msg->pf_len; - } - return cfw_send_message(msg); -} - -int ble_gatts_add_descriptor(svc_client_handle_t * p_svc_handle, - const struct ble_gatts_descriptor * p_desc, - ble_gatts_add_desc_cback_t cback, - void * p_priv) -{ - struct ble_gatts_add_desc_req_msg *req; - uint8_t * p; - int data_len = sizeof(struct ble_gatts_add_desc_req_msg); - - /* Sanity check */ - if (!p_desc || !p_desc->length || !p_desc->p_value || !p_desc->p_uuid) - return E_OS_ERR; - - // compute the variable length part of the message - data_len += ble_sizeof_bt_uuid(p_desc->p_uuid); - data_len += p_desc->length; - - req = (struct ble_gatts_add_desc_req_msg *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_ADD_DESCRIPTOR_REQ, - data_len, p_priv); - req->cback = cback; - p = req->data; - p = ble_encode_bt_uuid(p_desc->p_uuid, p); - req->length = p_desc->length; - req->perms = p_desc->perms; - memcpy(p, p_desc->p_value, p_desc->length); - - return cfw_send_message(req); -} - - -int ble_gatts_start_service(svc_client_handle_t * p_svc_handle, - uint16_t svc_handle, - void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_START_SERVICE_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gatts_remove_service(svc_client_handle_t * p_svc_handle, - uint16_t svc_handle, - void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_REMOVE_SERVICE_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gatts_send_svc_changed(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - uint16_t start_handle, - uint16_t end_handle, - void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_INDICATE_SERVICE_CHANGE_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gatts_set_attribute_value(svc_client_handle_t * p_svc_handle, - uint16_t value_handle, - uint16_t len, - const uint8_t * p_value, - uint16_t offset, - void *p_priv) -{ - int i; - struct ble_gatts_set_attribute_value_msg *msg = - (struct ble_gatts_set_attribute_value_msg *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_SET_ATTRIBUTE_VALUE_REQ, - sizeof(*msg)+len, - p_priv); - msg->value_handle = value_handle; - msg->len = len; - msg->offset = offset; - - for (i = 0; i < len; i++) - msg->data[i] = p_value[i]; - return cfw_send_message(msg); -} - -int ble_gatts_get_attribute_value(svc_client_handle_t * p_svc_handle, - uint16_t value_handle, - uint16_t len, - uint16_t offset, - void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_GET_ATTRIBUTE_VALUE_REQ, sizeof(*msg), - p_priv); - - return cfw_send_message(msg); -} - -int ble_gatts_send_notif(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const ble_gatts_ind_params_t * p_params, - ble_gatts_notif_ind_cback_t cback, - void *p_priv) -{ - struct ble_gatts_send_notif_ind_msg *msg = - (struct ble_gatts_send_notif_ind_msg *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_SEND_NOTIF_REQ, - sizeof(*msg) + p_params->len, - p_priv); - - msg->cback = cback; - msg->params.conn_handle = conn_handle; - msg->params.val_handle = p_params->val_handle; - msg->params.len = p_params->len; - msg->params.offset = p_params->offset; - memcpy(msg->params.data, p_params->p_data, msg->params.len); - return cfw_send_message(msg); -} - -int ble_gatts_send_ind(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const ble_gatts_ind_params_t * p_params, - ble_gatts_notif_ind_cback_t cback, - void *p_priv) -{ - struct ble_gatts_send_notif_ind_msg *msg = - (struct ble_gatts_send_notif_ind_msg *) - cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_SEND_IND_REQ, - sizeof(*msg) + p_params->len, - p_priv); - - msg->cback = cback; - msg->params.conn_handle = conn_handle; - msg->params.val_handle = p_params->val_handle; - msg->params.len = p_params->len; - msg->params.offset = p_params->offset; - memcpy(msg->params.data, p_params->p_data, msg->params.len); - return cfw_send_message(msg); -} - -int ble_gatts_write_conn_attributes(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - const uint8_t * p_data, - uint16_t len, - void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_WR_CONN_ATTRIBUTES_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} - -int ble_gatts_read_conn_attributes(svc_client_handle_t * p_svc_handle, - uint16_t conn_handle, - void *p_priv) -{ - struct cfw_message *msg = cfw_alloc_message_for_service(p_svc_handle, - MSG_ID_BLE_GATTS_RD_CONN_ATTRIBUTES_REQ, - sizeof(*msg), - p_priv); - return cfw_send_message(msg); -} diff --git a/system/libarc32_arduino101/framework/src/services/ble/conn.c b/system/libarc32_arduino101/framework/src/services/ble/conn.c new file mode 100644 index 00000000..c0aa9014 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/conn.c @@ -0,0 +1,927 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +#include "hci_core.h" +#include "conn_internal.h" +#include "gap_internal.h" +#include "l2cap_internal.h" +#include "smp.h" + +/* #define BT_GATT_DEBUG 1 */ + +extern void on_nble_curie_log(char *fmt, ...); +extern void __assert_fail(void); +#ifdef BT_GATT_DEBUG +#define BT_DBG(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ERR(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) +#else +#define BT_DBG(fmt, ...) do {} while (0) +#define BT_ERR(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) +#endif + +#if defined(CONFIG_BLUETOOTH_SMP) || defined(CONFIG_BLUETOOTH_BREDR) +const struct bt_conn_auth_cb *bt_auth; +#endif /* CONFIG_BLUETOOTH_SMP || CONFIG_BLUETOOTH_BREDR */ + +static struct bt_conn conns[CONFIG_BLUETOOTH_MAX_CONN]; +static struct bt_conn_cb *callback_list; + +static void notify_connected(struct bt_conn *conn) +{ + struct bt_conn_cb *cb; + + for (cb = callback_list; cb; cb = cb->_next) { + if (cb->connected) { + cb->connected(conn, conn->err); + } + } +} + +static void notify_disconnected(struct bt_conn *conn) +{ + struct bt_conn_cb *cb; + + for (cb = callback_list; cb; cb = cb->_next) { + if (cb->disconnected) { + cb->disconnected(conn, conn->err); + } + } +} + +void notify_le_param_updated(struct bt_conn *conn) +{ + struct bt_conn_cb *cb; + + for (cb = callback_list; cb; cb = cb->_next) { + if (cb->le_param_updated) { + cb->le_param_updated(conn, conn->le.interval, + conn->le.latency, + conn->le.timeout); + } + } +} + +#if defined(CONFIG_BLUETOOTH_SMP) + +void bt_conn_security_changed(struct bt_conn *conn) +{ + struct bt_conn_cb *cb; + + for (cb = callback_list; cb; cb = cb->_next) { + if (cb->security_changed) { + cb->security_changed(conn, conn->sec_level); + } + } +} + +static int start_security(struct bt_conn *conn) +{ + switch (conn->role) { +#if defined(CONFIG_BLUETOOTH_CENTRAL) + case BT_HCI_ROLE_MASTER: + { +#ifdef NOT_APPLICABLE_NBLE + if (!conn->keys) { + conn->keys = bt_keys_find(BT_KEYS_LTK_P256, + &conn->le.dst); + if (!conn->keys) { + conn->keys = bt_keys_find(BT_KEYS_LTK, + &conn->le.dst); + } + } + + if (!conn->keys || + !(conn->keys->keys & (BT_KEYS_LTK | BT_KEYS_LTK_P256))) { + return bt_smp_send_pairing_req(conn); + } + + if (conn->required_sec_level > BT_SECURITY_MEDIUM && + !atomic_test_bit(&conn->keys->flags, + BT_KEYS_AUTHENTICATED)) { + return bt_smp_send_pairing_req(conn); + } + + if (conn->required_sec_level > BT_SECURITY_HIGH && + !atomic_test_bit(&conn->keys->flags, + BT_KEYS_AUTHENTICATED) && + !(conn->keys->keys & BT_KEYS_LTK_P256)) { + return bt_smp_send_pairing_req(conn); + } + + /* LE SC LTK and legacy master LTK are stored in same place */ + return bt_conn_le_start_encryption(conn, conn->keys->ltk.rand, + conn->keys->ltk.ediv, + conn->keys->ltk.val, + conn->keys->enc_size); +#endif + return bt_smp_send_pairing_req(conn); + } +#endif /* CONFIG_BLUETOOTH_CENTRAL */ +#if defined(CONFIG_BLUETOOTH_PERIPHERAL) + case BT_HCI_ROLE_SLAVE: + return bt_smp_send_security_req(conn); +#endif /* CONFIG_BLUETOOTH_PERIPHERAL */ + default: + return -EINVAL; + } +} + +int bt_conn_security(struct bt_conn *conn, bt_security_t sec) +{ + int err; + + if (conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + +#if defined(CONFIG_BLUETOOTH_SMP_SC_ONLY) + if (sec < BT_SECURITY_FIPS) { + return -EOPNOTSUPP; + } +#endif/* CONFIG_BLUETOOTH_SMP_SC_ONLY */ + + /* nothing to do */ + if (conn->sec_level >= sec || conn->required_sec_level >= sec) { + return 0; + } + + conn->required_sec_level = sec; + + err = start_security(conn); + + /* reset required security level in case of error */ + if (err) { + conn->required_sec_level = conn->sec_level; + } + return err; +} +#endif /* CONFIG_BLUETOOTH_SMP */ + +void bt_conn_cb_register(struct bt_conn_cb *cb) +{ + cb->_next = callback_list; + callback_list = cb; +} + +static struct bt_conn *conn_new(void) +{ + struct bt_conn *conn = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + conn = &conns[i]; + break; + } + } + + if (!conn) { + return NULL; + } + + memset(conn, 0, sizeof(*conn)); + + atomic_set(&conn->ref, 1); + + return conn; +} + +struct bt_conn *bt_conn_add_le(const bt_addr_le_t *peer) +{ + struct bt_conn *conn = conn_new(); + + if (!conn) { + return NULL; + } + + bt_addr_le_copy(&conn->le.dst, peer); +#if defined(CONFIG_BLUETOOTH_SMP) + conn->sec_level = BT_SECURITY_LOW; + conn->required_sec_level = BT_SECURITY_LOW; +#endif /* CONFIG_BLUETOOTH_SMP */ + conn->type = BT_CONN_TYPE_LE; + conn->le.interval_min = BT_GAP_INIT_CONN_INT_MIN; + conn->le.interval_max = BT_GAP_INIT_CONN_INT_MAX; + + return conn; +} + +#if defined(CONFIG_BLUETOOTH_BREDR) +struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + + if (conns[i].type != BT_CONN_TYPE_BR) { + continue; + } + + if (!bt_addr_cmp(peer, &conns[i].br.dst)) { + return bt_conn_ref(&conns[i]); + } + } + + return NULL; +} + +struct bt_conn *bt_conn_add_br(const bt_addr_t *peer) +{ + struct bt_conn *conn = conn_new(); + + if (!conn) { + return NULL; + } + + bt_addr_copy(&conn->br.dst, peer); + conn->type = BT_CONN_TYPE_BR; + + return conn; +} +#endif + +void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state) +{ + bt_conn_state_t old_state; + + BT_DBG("conn state %d -> %d, err: %d", conn->state, state, conn->err); + + if (conn->state == state) { + BT_DBG("no transition"); + return; + } + + old_state = conn->state; + conn->state = state; + + /* Actions needed for exiting the old state */ + switch (old_state) { + case BT_CONN_DISCONNECTED: + /* Take a reference for the first state transition after + * bt_conn_add_le() and keep it until reaching DISCONNECTED + * again. + */ + bt_conn_ref(conn); + break; + case BT_CONN_CONNECT: +#if 0 + if (conn->timeout) { + fiber_delayed_start_cancel(conn->timeout); + conn->timeout = NULL; + + /* Drop the reference taken by timeout fiber */ + bt_conn_unref(conn); + } +#endif + break; + default: + break; + } + + /* Actions needed for entering the new state */ + switch (conn->state) { + case BT_CONN_CONNECTED: + bt_l2cap_connected(conn); + notify_connected(conn); + break; + case BT_CONN_DISCONNECTED: + /* Notify disconnection and queue a dummy buffer to wake + * up and stop the tx fiber for states where it was + * running. + */ + if (old_state == BT_CONN_CONNECTED || + old_state == BT_CONN_DISCONNECT) { + bt_l2cap_disconnected(conn); + notify_disconnected(conn); + } else if (old_state == BT_CONN_CONNECT) { + /* conn->err will be set in this case */ + notify_connected(conn); + } + + /* Release the reference we took for the very first + * state transition. + */ + bt_conn_unref(conn); + + break; + case BT_CONN_CONNECT_SCAN: + break; + case BT_CONN_CONNECT: + break; + case BT_CONN_DISCONNECT: + break; + default: + + break; + } +} + +struct bt_conn *bt_conn_lookup_handle(uint16_t handle) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + /* We only care about connections with a valid handle */ + if (conns[i].state != BT_CONN_CONNECTED && + conns[i].state != BT_CONN_DISCONNECT) { + continue; + } + + if (conns[i].handle == handle) { + return bt_conn_ref(&conns[i]); + } + } + + return NULL; +} + +struct bt_conn *bt_conn_lookup_addr_le(const bt_addr_le_t *peer) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + + if (conns[i].type != BT_CONN_TYPE_LE) { + continue; + } + + if (!bt_addr_le_cmp(peer, &conns[i].le.dst)) { + return bt_conn_ref(&conns[i]); + } + } + + return NULL; +} + +struct bt_conn *bt_conn_lookup_state_le(const bt_addr_le_t *peer, + const bt_conn_state_t state) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + if (!atomic_get(&conns[i].ref)) { + continue; + } + + if (conns[i].type != BT_CONN_TYPE_LE) { + continue; + } + + if (bt_addr_le_cmp(peer, BT_ADDR_LE_ANY) && + bt_addr_le_cmp(peer, &conns[i].le.dst)) { + continue; + } + + if (conns[i].state == state) { + return bt_conn_ref(&conns[i]); + } + } + + return NULL; +} + +struct bt_conn *bt_conn_ref(struct bt_conn *conn) +{ + atomic_inc(&conn->ref); + + return conn; +} + +void bt_conn_unref(struct bt_conn *conn) +{ + atomic_dec(&conn->ref); +} + +const bt_addr_le_t *bt_conn_get_dst(const struct bt_conn *conn) +{ + return &conn->le.dst; +} + +int bt_conn_get_info(const struct bt_conn *conn, struct bt_conn_info *info) +{ + info->type = conn->type; + info->role = conn->role; + + switch (conn->type) { + case BT_CONN_TYPE_LE: + if (conn->role == BT_HCI_ROLE_MASTER) { +#if 0 + info->le.src = &conn->le.init_addr; + info->le.dst = &conn->le.resp_addr; +#else + info->le.dst = &conn->le.dst; +#endif + } else { +#if 0 + info->le.src = &conn->le.resp_addr; + info->le.dst = &conn->le.init_addr; +#else + info->le.src = &conn->le.dst; +#endif + } + info->le.interval = conn->le.interval; + info->le.latency = conn->le.latency; + info->le.timeout = conn->le.timeout; + + return 0; +#if defined(CONFIG_BLUETOOTH_BREDR) + case BT_CONN_TYPE_BR: + info->br.dst = &conn->br.dst; + return 0; +#endif + } + + return -EINVAL; +} + +static int bt_hci_disconnect(struct bt_conn *conn, uint8_t reason) +{ + struct nble_gap_disconnect_req_params ble_gap_disconnect; + + ble_gap_disconnect.conn_handle = conn->handle; + ble_gap_disconnect.reason = reason; + nble_gap_disconnect_req(&ble_gap_disconnect); + + bt_conn_set_state(conn, BT_CONN_DISCONNECT); + return 0; +} + +static int bt_hci_connect_le_cancel(struct bt_conn *conn) +{ + nble_gap_cancel_connect_req(conn); + return 0; +} + +void on_nble_gap_cancel_connect_rsp(const struct nble_response *params) +{ + struct bt_conn *conn = params->user_data; + + conn->err = BT_HCI_ERR_INSUFFICIENT_RESOURCES; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); +} + +int bt_conn_disconnect(struct bt_conn *conn, uint8_t reason) +{ +#if defined(CONFIG_BLUETOOTH_CENTRAL) + /* Disconnection is initiated by us, so auto connection shall + * be disabled. Otherwise the passive scan would be enabled + * and we could send LE Create Connection as soon as the remote + * starts advertising. + */ + if (conn->type == BT_CONN_TYPE_LE) { + bt_le_set_auto_conn(&conn->le.dst, NULL); + } +#endif + + switch (conn->state) { + case BT_CONN_CONNECT_SCAN: + conn->err = BT_HCI_ERR_INSUFFICIENT_RESOURCES; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + /* scan update not yet implemented */ + return 0; + case BT_CONN_CONNECT: + return bt_hci_connect_le_cancel(conn); + case BT_CONN_CONNECTED: + return bt_hci_disconnect(conn, reason); + case BT_CONN_DISCONNECT: + return 0; + case BT_CONN_DISCONNECTED: + default: + return -ENOTCONN; + } +} + +static bool valid_adv_params(const struct nble_gap_adv_params *params) +{ + if (params->type == BT_LE_ADV_DIRECT_IND) { + /* If high duty, ensure interval is 0 */ + if (params->interval_max != 0) + return false; + + if (params->timeout != 0) + return false; + } else if (params->type == BT_LE_ADV_DIRECT_IND_LOW_DUTY) { + if (params->interval_min < 0x20) + return false; + } else { + return false; + } + + if (params->interval_min > params->interval_max) + return false; + + if (params->interval_max > 0x4000) + return false; + + return true; +} + +struct bt_conn *bt_conn_create_slave_le(const bt_addr_le_t *peer, + const struct bt_le_adv_param *param) +{ + struct bt_conn *conn; + /* Timeout is handled by application timer */ + /* forced to none currently (no whitelist support) */ + struct nble_gap_adv_params params = { + .interval_max = param->interval_max, + .interval_min = param->interval_min, + .type = param->type, + .timeout = 0, + .filter_policy = 0 + }; + + bt_addr_le_copy(¶ms.peer_bda, peer); + + if (!valid_adv_params(¶ms)) { + return NULL; + } + + if (param->type == BT_LE_ADV_DIRECT_IND_LOW_DUTY) { + params.type = BT_LE_ADV_DIRECT_IND; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + return NULL; + } + + conn = bt_conn_add_le(¶ms.peer_bda); + + if (!conn) { + return NULL; + } + + bt_conn_set_state(conn, BT_CONN_CONNECT); + + nble_gap_set_adv_params_req(¶ms); + nble_gap_start_adv_req(); + + return conn; +} + +#if defined(CONFIG_BLUETOOTH_CENTRAL) +static int hci_le_create_conn(struct bt_conn *conn) +{ + struct nble_gap_connect_req_params conn_params; + + conn_params.bda = conn->le.dst; + conn_params.conn_params.interval_min = conn->le.interval_min; + conn_params.conn_params.interval_max = conn->le.interval_max; + conn_params.conn_params.slave_latency = conn->le.latency; + conn_params.conn_params.link_sup_to = conn->le.timeout; + + conn_params.scan_params.interval = sys_cpu_to_le16(BT_GAP_SCAN_FAST_INTERVAL); + conn_params.scan_params.window = conn_params.scan_params.interval; + conn_params.scan_params.selective = 0; + conn_params.scan_params.active = 1; + conn_params.scan_params.timeout = 0; + + nble_gap_connect_req(&conn_params, conn); + + return 0; +} + +void on_nble_gap_connect_rsp(const struct nble_response *params) +{ + struct bt_conn *conn = params->user_data; + + /* If the connection request was not issued successfully */ + if (params->status) { + conn->err = BT_HCI_ERR_UNACCEPT_CONN_PARAMS; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + } +} + +struct bt_conn *bt_conn_create_le(const bt_addr_le_t *peer, + const struct bt_le_conn_param *param) +{ + struct bt_conn *conn; + + if (!bt_le_conn_params_valid(param->interval_min, param->interval_max, + param->latency, param->timeout)) { + return NULL; + } + + /* if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) */ + /* return NULL; */ + + conn = bt_conn_lookup_addr_le(peer); + if (conn) { + switch (conn->state) { + case BT_CONN_CONNECT_SCAN: + bt_conn_set_param_le(conn, param); + return conn; + case BT_CONN_CONNECT: + case BT_CONN_CONNECTED: + return conn; + default: + bt_conn_unref(conn); + return NULL; + } + } + + conn = bt_conn_add_le(peer); + if (!conn) { + return NULL; + } +#if 0 + bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); + + bt_le_scan_update(true); +#endif + + bt_addr_le_copy(&conn->le.dst, peer); + + bt_conn_set_param_le(conn, param); + + /* for the time being, the implementation bypassed the scan procedure */ + if (hci_le_create_conn(conn)) { + goto done; + } + + bt_conn_set_state(conn, BT_CONN_CONNECT); + +done: + return conn; +} +#else + +void on_nble_gap_connect_rsp(const struct nble_response *params) +{ +} + +#endif /* CONFIG_BLUETOOTH_CENTRAL */ + +int bt_conn_le_param_update(struct bt_conn *conn, const struct bt_le_conn_param *param) +{ + return bt_conn_update_param_le(conn, param); +} + +#if defined(CONFIG_BLUETOOTH_SMP) || defined(CONFIG_BLUETOOTH_BREDR) +uint8_t bt_conn_enc_key_size(struct bt_conn *conn) +{ + return 0; +} + +int bt_conn_auth_cb_register(const struct bt_conn_auth_cb *cb) +{ + if (!cb) { + bt_auth = NULL; + return 0; + } + + /* cancel callback should always be provided */ + if (!cb->cancel) { + return -EINVAL; + } + + if (bt_auth) { + return -EALREADY; + } + + bt_auth = cb; + return 0; +} + +#if defined(CONFIG_BLUETOOTH_BREDR) +static int pin_code_neg_reply(const bt_addr_t *bdaddr) +{ + struct bt_hci_cp_pin_code_neg_reply *cp; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_NEG_REPLY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + bt_addr_copy(&cp->bdaddr, bdaddr); + + return bt_hci_cmd_send_sync(BT_HCI_OP_PIN_CODE_NEG_REPLY, buf, NULL); +} + +static int pin_code_reply(struct bt_conn *conn, const char *pin, uint8_t len) +{ + struct bt_hci_cp_pin_code_reply *cp; + struct net_buf *buf; + + BT_DBG(""); + + buf = bt_hci_cmd_create(BT_HCI_OP_PIN_CODE_REPLY, sizeof(*cp)); + if (!buf) { + return -ENOBUFS; + } + + cp = net_buf_add(buf, sizeof(*cp)); + + bt_addr_copy(&cp->bdaddr, &conn->br.dst); + cp->pin_len = len; + strncpy(cp->pin_code, pin, sizeof(cp->pin_code)); + + return bt_hci_cmd_send_sync(BT_HCI_OP_PIN_CODE_REPLY, buf, NULL); +} + +int bt_conn_auth_pincode_entry(struct bt_conn *conn, const char *pin) +{ + size_t len; + + if (!bt_auth) { + return -EINVAL; + } + + if (conn->type != BT_CONN_TYPE_BR) { + return -EINVAL; + } + + len = strlen(pin); + if (len > 16) { + return -EINVAL; + } + + if (conn->required_sec_level == BT_SECURITY_HIGH && len < 16) { + BT_WARN("PIN code for %s is not 16 bytes wide", + bt_addr_str(&conn->br.dst)); + return -EPERM; + } + + return pin_code_reply(conn, pin, len); +} + +void bt_conn_pin_code_req(struct bt_conn *conn) +{ + if (bt_auth && bt_auth->pincode_entry) { + bool secure = false; + + if (conn->required_sec_level == BT_SECURITY_HIGH) { + secure = true; + } + + bt_auth->pincode_entry(conn, secure); + } else { + pin_code_neg_reply(&conn->br.dst); + } + +} +#endif /* CONFIG_BLUETOOTH_BREDR */ + +int bt_conn_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey) +{ + if (!bt_auth) { + return -EINVAL; + } +#if defined(CONFIG_BLUETOOTH_SMP) + if (conn->type == BT_CONN_TYPE_LE) { + return bt_smp_auth_passkey_entry(conn, passkey); + } +#endif /* CONFIG_BLUETOOTH_SMP */ + + return -EINVAL; +} + +int bt_conn_auth_passkey_confirm(struct bt_conn *conn, bool match) +{ + if (!bt_auth) { + return -EINVAL; + }; +#if defined(CONFIG_BLUETOOTH_SMP) + if (conn->type == BT_CONN_TYPE_LE) { + return bt_smp_auth_passkey_confirm(conn, match); + } +#endif /* CONFIG_BLUETOOTH_SMP */ + + return -EINVAL; +} + +int bt_conn_auth_cancel(struct bt_conn *conn) +{ + if (!bt_auth) { + return -EINVAL; + } +#if defined(CONFIG_BLUETOOTH_SMP) + if (conn->type == BT_CONN_TYPE_LE) { + return bt_smp_auth_cancel(conn); + } +#endif /* CONFIG_BLUETOOTH_SMP */ +#if defined(CONFIG_BLUETOOTH_BREDR) + if (conn->type == BT_CONN_TYPE_BR) { + return pin_code_neg_reply(&conn->br.dst); + } +#endif /* CONFIG_BLUETOOTH_BREDR */ + + return -EINVAL; +} + +int bt_conn_remove_info(const bt_addr_le_t *addr) +{ + struct bt_conn *conn; + + /* TODO: implement address specific removal */ + if (bt_addr_le_cmp(addr, BT_ADDR_LE_ANY)) + return -EINVAL; + + do { + conn = bt_conn_lookup_state_le(addr, BT_CONN_CONNECTED); + if (conn) { + bt_conn_unref(conn); + bt_conn_disconnect(conn, + BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + } while(conn); + + return bt_smp_remove_info(addr); +} +#endif /* CONFIG_BLUETOOTH_SMP || CONFIG_BLUETOOTH_BREDR */ + +int bt_conn_init(void) +{ + int err; +#if NOT_USED_FOR_NOW + + net_buf_pool_init(frag_pool); + net_buf_pool_init(dummy_pool); + + bt_att_init(); +#endif + + err = bt_smp_init(); + if (err) { + return err; + } + +#if NOT_USED_FOR_NOW + bt_l2cap_init(); + + background_scan_init(); +#endif + return 0; +} + +int bt_conn_le_conn_update(struct bt_conn *conn, + const struct bt_le_conn_param *param) +{ + struct nble_gap_connect_update_params ble_gap_connect_update; +#if 0 + struct hci_cp_le_conn_update *conn_update; + struct net_buf *buf; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_CONN_UPDATE, + sizeof(*conn_update)); + if (!buf) { + return -ENOBUFS; + } + + conn_update = net_buf_add(buf, sizeof(*conn_update)); + memset(conn_update, 0, sizeof(*conn_update)); + conn_update->handle = sys_cpu_to_le16(conn->handle); + conn_update->conn_interval_min = sys_cpu_to_le16(param->interval_min); + conn_update->conn_interval_max = sys_cpu_to_le16(param->interval_max); + conn_update->conn_latency = sys_cpu_to_le16(param->latency); + conn_update->supervision_timeout = sys_cpu_to_le16(param->timeout); +#endif + ble_gap_connect_update.conn_handle = conn->handle; + ble_gap_connect_update.params.interval_min = param->interval_min; + ble_gap_connect_update.params.interval_max = param->interval_max; + ble_gap_connect_update.params.slave_latency = param->latency; + ble_gap_connect_update.params.link_sup_to = param->timeout; + + nble_gap_conn_update_req(&ble_gap_connect_update); + + return 0; +} diff --git a/system/libarc32_arduino101/framework/src/services/ble/conn_internal.h b/system/libarc32_arduino101/framework/src/services/ble/conn_internal.h new file mode 100644 index 00000000..04c08839 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/conn_internal.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +typedef enum { + BT_CONN_DISCONNECTED, + BT_CONN_CONNECT_SCAN, + BT_CONN_CONNECT, + BT_CONN_CONNECTED, + BT_CONN_DISCONNECT, +} bt_conn_state_t; + +/* bt_conn flags: the flags defined here represent connection parameters */ +enum { + BT_CONN_AUTO_CONNECT, +}; + +struct bt_conn_le { + bt_addr_le_t dst; + +#if 0 + bt_addr_le_t init_addr; + bt_addr_le_t resp_addr; +#endif + uint16_t interval; + uint16_t interval_min; + uint16_t interval_max; + + uint16_t latency; + uint16_t timeout; +#if 0 + uint8_t features[8]; +#endif +}; + +#if defined(CONFIG_BLUETOOTH_BREDR) +struct bt_conn_br { + bt_addr_t dst; +}; +#endif + +struct bt_conn { + uint16_t handle; + uint8_t type; + uint8_t role; + +#if defined(CONFIG_BLUETOOTH_SMP) + uint8_t encrypt; + bt_security_t sec_level; + bt_security_t required_sec_level; +#endif /* CONFIG_BLUETOOTH_SMP */ + + atomic_t ref; + + /* Connection error or reason for disconnect */ + uint8_t err; + + bt_conn_state_t state; + union { + struct bt_conn_le le; +#if defined(CONFIG_BLUETOOTH_BREDR) + struct bt_conn_br br; +#endif + }; +}; + +/* Add a new LE connection */ +struct bt_conn *bt_conn_add_le(const bt_addr_le_t *peer); + +#if defined(CONFIG_BLUETOOTH_BREDR) +/* Add a new BR/EDR connection */ +struct bt_conn *bt_conn_add_br(const bt_addr_t *peer); + +/* Look up an existing connection by BT address */ +struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer); +#endif + +/* Look up an existing connection */ +struct bt_conn *bt_conn_lookup_handle(uint16_t handle); + +/* Look up a connection state. For BT_ADDR_LE_ANY, returns the first connection + * with the specific state + */ +struct bt_conn *bt_conn_lookup_state_le(const bt_addr_le_t *peer, + const bt_conn_state_t state); + +/* Set connection object in certain state and perform action related to state */ +void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state); + +void bt_conn_set_param_le(struct bt_conn *conn, + const struct bt_le_conn_param *param); + +int bt_conn_update_param_le(struct bt_conn *conn, + const struct bt_le_conn_param *param); + +int bt_conn_le_conn_update(struct bt_conn *conn, + const struct bt_le_conn_param *param); + +void notify_le_param_updated(struct bt_conn *conn); + +#if defined(CONFIG_BLUETOOTH_SMP) +/* rand and ediv should be in BT order */ +int bt_conn_le_start_encryption(struct bt_conn *conn, uint64_t rand, + uint16_t ediv, const uint8_t *ltk, size_t len); + +/* Notify higher layers that RPA was resolved */ +void bt_conn_identity_resolved(struct bt_conn *conn); + +/* Notify higher layers that connection security changed */ +void bt_conn_security_changed(struct bt_conn *conn); +#endif /* CONFIG_BLUETOOTH_SMP */ +/* Initialize connection management */ +int bt_conn_init(void); diff --git a/system/libarc32_arduino101/framework/src/services/ble/dtm_tcmd.c b/system/libarc32_arduino101/framework/src/services/ble/dtm_tcmd.c new file mode 100644 index 00000000..4a0c5b0c --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/dtm_tcmd.c @@ -0,0 +1,12 @@ + +#include +#include "uart.h" +#include "ipc_uart_ns16550.h" + +void on_nble_gap_dtm_init_rsp(void *user_data) +{ +#ifdef CONFIG_UART_NS16550 + uart_ipc_disable(); +#endif +} + diff --git a/system/libarc32_arduino101/framework/src/services/ble/gap.c b/system/libarc32_arduino101/framework/src/services/ble/gap.c new file mode 100644 index 00000000..4cf71d47 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/gap.c @@ -0,0 +1,912 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include "gap_internal.h" +#include "conn_internal.h" + +#include "hci_core.h" + +#if defined(CONFIG_BLUETOOTH_SMP) +#include "smp.h" +#endif /* CONFIG_BLUETOOTH_SMP */ + +/* #define BT_GATT_DEBUG 1 */ + +extern void on_nble_curie_log(char *fmt, ...); +extern void __assert_fail(void); +#ifdef BT_GATT_DEBUG +#define BT_DBG(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ERR(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) +#else +#define BT_DBG(fmt, ...) do {} while (0) +#define BT_ERR(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) +#endif + +static bt_ready_cb_t bt_ready_cb; +static bt_le_scan_cb_t *scan_dev_found_cb; +static rssi_report_t rssi_report_cb; + +struct bt_dev bt_dev; + +static int set_advertise_enable(void) +{ +#if 0 + struct net_buf *buf; + int err; +#endif + if (atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + return 0; + } +#if 0 + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_u8(buf, BT_HCI_LE_ADV_ENABLE); + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL); + if (err) { + return err; + } +#endif + nble_gap_start_adv_req(); + + return 0; +} + +static int set_advertise_disable(void) +{ +#if 0 + struct net_buf *buf; + int err; +#endif + if (!atomic_test_bit(bt_dev.flags, BT_DEV_ADVERTISING)) { + return 0; + } +#if 0 + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1); + if (!buf) { + return -ENOBUFS; + } + + net_buf_add_u8(buf, BT_HCI_LE_ADV_DISABLE); + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL); + if (err) { + return err; + } +#endif + nble_gap_stop_adv_req(NULL); + + atomic_clear_bit(bt_dev.flags, BT_DEV_ADVERTISING); + return 0; +} + +void ble_gap_get_bonding_info(ble_bond_info_cb_t func, void *user_data, + bool include_bonded_addrs) +{ + struct nble_gap_sm_bond_info_param params; + + params.cb = func; + params.user_data = user_data; + params.include_bonded_addrs = include_bonded_addrs; + + nble_gap_sm_bond_info_req(¶ms); +} + +void on_nble_gap_start_advertise_rsp(const struct nble_response *params) +{ + if (params->status == 0) + atomic_set_bit(bt_dev.flags, BT_DEV_ADVERTISING); + else + BT_WARN("start advertise failed with %d", params->status); +} + +void on_nble_gap_disconnect_evt(const struct nble_gap_disconnect_evt *evt) +{ + struct bt_conn *conn; + +#if 0 + /* Nordic has no disconnection error */ + if (evt->status) { + return; + } +#endif + + conn = bt_conn_lookup_handle(evt->conn_handle); + if (!conn) { + BT_DBG("Unable to look up conn with handle %u", + evt->conn_handle); + return; + } +#if 0 + /* Check stacks usage (no-ops if not enabled) */ + stack_analyze("rx stack", rx_fiber_stack, sizeof(rx_fiber_stack)); + stack_analyze("cmd rx stack", rx_prio_fiber_stack, + sizeof(rx_prio_fiber_stack)); + stack_analyze("cmd tx stack", cmd_tx_fiber_stack, + sizeof(cmd_tx_fiber_stack)); + stack_analyze("conn tx stack", conn->stack, sizeof(conn->stack)); + +#endif + + conn->err = evt->hci_reason; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + conn->handle = 0; + +#if 0 + /* Only LE supported */ + if (conn->type != BT_CONN_TYPE_LE) { + bt_conn_unref(conn); + return; + } + /* TODO enabled when autoconn is supported */ + if (atomic_test_bit(conn->flags, BT_CONN_AUTO_CONNECT)) { + bt_conn_set_state(conn, BT_CONN_CONNECT_SCAN); + bt_le_scan_update(false); + } +#endif + + bt_conn_unref(conn); + if (atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) { + set_advertise_enable(); + } +} + +void on_nble_gap_connect_evt(const struct nble_gap_connect_evt *evt) +{ + struct bt_conn *conn; + + /* Make lookup to check if there's a connection object in CONNECT state + * associated with passed peer LE address. + */ + conn = bt_conn_lookup_state_le(&evt->peer_bda, BT_CONN_CONNECT); + +#if 0 + /* Nordic has no connection error */ + if (evt->status) { + if (!conn) { + return; + } + + conn->err = BT_HCI_ERR_UNACCEPT_CONN_PARAMS; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + + /* Drop the reference got by lookup call in CONNECT state. + * We are now in DISCONNECTED state since no successful LE + * link been made. + */ + bt_conn_unref(conn); + + return; + } +#endif + /* + * clear advertising even if we are not able to add connection object + * to keep host in sync with controller state + */ + if (evt->role_slave == BT_CONN_ROLE_SLAVE) { + atomic_clear_bit(bt_dev.flags, BT_DEV_ADVERTISING); + } + + if (!conn) { + conn = bt_conn_add_le(&evt->peer_bda); + } + + if (!conn) { + BT_DBG("Unable to add new conn for handle %u", + evt->conn_handle); + return; + } + + conn->handle = evt->conn_handle; + bt_addr_le_copy(&conn->le.dst, &evt->peer_bda); + conn->le.interval = evt->conn_values.interval; + conn->le.latency = evt->conn_values.latency; + conn->le.timeout = evt->conn_values.supervision_to; + conn->role = evt->role_slave; + +#if 0 + src.type = BT_ADDR_LE_PUBLIC; + memcpy(src.val, bt_dev.bdaddr.val, sizeof(bt_dev.bdaddr.val)); + + /* use connection address (instead of identity address) as initiator + * or responder address + */ + if (conn->role == BT_HCI_ROLE_MASTER) { + bt_addr_le_copy(&conn->le.init_addr, &src); + bt_addr_le_copy(&conn->le.resp_addr, &evt->peer_addr); + } else { + bt_addr_le_copy(&conn->le.init_addr, &evt->peer_addr); + bt_addr_le_copy(&conn->le.resp_addr, &src); + } +#endif + bt_conn_set_state(conn, BT_CONN_CONNECTED); + + /* Note: Connection update removed because Windows interop and BT spec recommendations */ + + bt_conn_unref(conn); +#if 0 + bt_le_scan_update(false); +#endif + +} + +void on_nble_gap_adv_report_evt(const struct nble_gap_adv_report_evt *evt, + const uint8_t *buf, uint8_t len) +{ +#if TODO_IMPLEMENT_CONNECTION + uint8_t num_reports = buf->data[0]; + struct bt_hci_ev_le_advertising_info *info; + + BT_DBG("Adv number of reports %u", num_reports); + + info = net_buf_pull(buf, sizeof(num_reports)); + + while (num_reports--) { + int8_t rssi = info->data[info->length]; + const bt_addr_le_t *addr; + + BT_DBG("%s event %u, len %u, rssi %d dBm", + bt_addr_le_str(&info->addr), + info->evt_type, info->length, rssi); + + addr = find_id_addr(&info->addr); +#endif + + BT_DBG("nble gap: event:%u, len %u", evt->adv_type, len); + + if (scan_dev_found_cb) { + scan_dev_found_cb(&evt->addr, evt->rssi, evt->adv_type, + buf, len); + } +#if TODO_IMPLEMENT_CONNECTION +#if defined(CONFIG_BLUETOOTH_CONN) + check_pending_conn(addr, &info->addr, info->evt_type); +#endif /* CONFIG_BLUETOOTH_CONN */ + /* Get next report iteration by moving pointer to right offset + * in buf according to spec 4.2, Vol 2, Part E, 7.7.65.2. + */ + info = net_buf_pull(buf, sizeof(*info) + info->length + + sizeof(rssi)); + } +#endif +} + +void on_nble_gap_conn_update_evt(const struct nble_gap_conn_update_evt *evt) +{ + struct bt_conn *conn; + uint16_t handle, interval; + + handle = evt->conn_handle; + interval = evt->conn_values.interval; + +/* BT_DBG("status %u, handle %u", evt->status, handle); */ + + conn = bt_conn_lookup_handle(handle); + if (!conn) { +/* BT_ERR("Unable to lookup conn for handle %u", handle); */ + return; + } + +/* if (!evt->status) { */ + conn->le.interval = interval; + conn->le.latency = evt->conn_values.latency; + conn->le.timeout = evt->conn_values.supervision_to; + notify_le_param_updated(conn); +/* } */ + + + bt_conn_unref(conn); +} + +void bt_conn_set_param_le(struct bt_conn *conn, + const struct bt_le_conn_param *param) +{ + conn->le.interval_min = param->interval_min; + conn->le.interval_max = param->interval_max; + conn->le.latency = param->latency; + conn->le.timeout = param->timeout; +} +int bt_conn_update_param_le(struct bt_conn *conn, + const struct bt_le_conn_param *param) +{ +#if 0 + BT_DBG("conn %p features 0x%x params (%d-%d %d %d)", conn, + conn->le.features[0], param->interval_min, param->interval_max, + param->latency, param->timeout); +#endif + /* Check if there's a need to update conn params */ + if (conn->le.interval >= param->interval_min && + conn->le.interval <= param->interval_max) { + return -EALREADY; + } +#if 0 + if ((conn->role == BT_HCI_ROLE_SLAVE) && + !(bt_dev.le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC)) { + return bt_l2cap_update_conn_param(conn, param); + } + + if ((conn->le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC) && + (bt_dev.le.features[0] & BT_HCI_LE_CONN_PARAM_REQ_PROC)) { +#endif + return bt_conn_le_conn_update(conn, param); +#if 0 + } + return -EBUSY; +#endif +} + +void on_nble_gap_scan_start_stop_rsp(const struct nble_response *rsp) +{ + if (rsp->status) + BT_INFO("scan start/stop failed: %d", rsp->status); + /* TODO: clear scanning bit atomic_clear_bit(bt_dev.flags, BT_DEV_SCANNING) */ +} + +static int bt_hci_stop_scanning(void) +{ +#ifdef NOT_USED_FOR_NOW + struct net_buf *buf, *rsp; + struct bt_hci_cp_le_set_scan_enable *scan_enable; + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { + return -EALREADY; + } + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE, + sizeof(*scan_enable)); + if (!buf) { + return -ENOBUFS; + } + + scan_enable = net_buf_add(buf, sizeof(*scan_enable)); + memset(scan_enable, 0, sizeof(*scan_enable)); + scan_enable->filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_DISABLE; + scan_enable->enable = BT_HCI_LE_SCAN_DISABLE; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, &rsp); + if (err) { + return err; + } + + /* Update scan state in case of success (0) status */ + err = rsp->data[0]; + if (!err) { + atomic_clear_bit(bt_dev.flags, BT_DEV_SCANNING); + } + + net_buf_unref(rsp); + + return err; +#endif + + nble_gap_stop_scan_req(); + + return 0; +} + +#if defined(CONFIG_BLUETOOTH_CENTRAL) +int bt_le_set_auto_conn(bt_addr_le_t *addr, + const struct bt_le_conn_param *param) +{ + return -EINVAL; +} +#endif /* CONFIG_BLUETOOTH_CENTRAL */ + + +static int start_le_scan(uint8_t scan_type, uint16_t interval, uint16_t window, + uint8_t filter_dup) +{ + struct nble_gap_scan_params params = { + .interval = interval, + .window = window, + .scan_type = scan_type, + }; + +#ifdef NOT_USED_FOR_NOW + struct net_buf *buf, *rsp; + struct bt_hci_cp_le_set_scan_params *set_param; + struct bt_hci_cp_le_set_scan_enable *scan_enable; + int err; + + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_PARAMS, + sizeof(*set_param)); + if (!buf) { + return -ENOBUFS; + } + + + set_param = net_buf_add(buf, sizeof(*set_param)); + memset(set_param, 0, sizeof(*set_param)); + set_param->scan_type = scan_type; + + /* for the rest parameters apply default values according to + * spec 4.2, vol2, part E, 7.8.10 + */ + set_param->interval = sys_cpu_to_le16(interval); + set_param->window = sys_cpu_to_le16(window); + set_param->filter_policy = 0x00; + + if (scan_type == BT_HCI_LE_SCAN_ACTIVE) { + err = le_set_nrpa(); + if (err) { + net_buf_unref(buf); + return err; + } + + set_param->addr_type = BT_ADDR_LE_RANDOM; + } else { + set_param->addr_type = BT_ADDR_LE_PUBLIC; + } + + bt_hci_cmd_send(BT_HCI_OP_LE_SET_SCAN_PARAMS, buf); + buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_SCAN_ENABLE, + sizeof(*scan_enable)); + if (!buf) { + return -ENOBUFS; + } + + scan_enable = net_buf_add(buf, sizeof(*scan_enable)); + memset(scan_enable, 0, sizeof(*scan_enable)); + scan_enable->filter_dup = filter_dup; + scan_enable->enable = BT_HCI_LE_SCAN_ENABLE; + + err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_SCAN_ENABLE, buf, &rsp); + if (err) { + return err; + } + /* Update scan state in case of success (0) status */ + err = rsp->data[0]; + if (!err) { + atomic_set_bit(bt_dev.flags, BT_DEV_SCANNING); + } + + net_buf_unref(rsp); +#endif + + nble_gap_start_scan_req(¶ms); + + return 0; +} + +#if NOT_USED_FOR_NOW +/* Used to determine whether to start scan and which scan type should be used */ +int bt_le_scan_update(bool fast_scan) +{ +#if defined(CONFIG_BLUETOOTH_CENTRAL) + uint16_t interval, window; + struct bt_conn *conn; +#endif /* CONFIG_BLUETOOTH_CENTRAL */ + + if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return 0; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { + int err; + + err = bt_hci_stop_scanning(); + if (err) { + return err; + } + } + +#if defined(CONFIG_BLUETOOTH_CENTRAL) + conn = bt_conn_lookup_state_le(NULL, BT_CONN_CONNECT_SCAN); + if (!conn) { + return 0; + } + + bt_conn_unref(conn); + + if (fast_scan) { + interval = BT_GAP_SCAN_FAST_INTERVAL; + window = BT_GAP_SCAN_FAST_WINDOW; + } else { + interval = BT_GAP_SCAN_SLOW_INTERVAL_1; + window = BT_GAP_SCAN_SLOW_WINDOW_1; + } + + return start_le_scan(BT_HCI_LE_SCAN_PASSIVE, interval, window, 0x01); +#else + return 0; +#endif /* CONFIG_BLUETOOTH_CENTRAL */ +} +#endif + +static int bt_init(void) +{ +#if NOT_USED_FOR_NOW + struct bt_driver *drv = bt_dev.drv; +#endif + int err = 0; + +#if NOT_USED_FOR_NOW + err = drv->open(); + if (err) { + BT_ERR("HCI driver open failed (%d)", err); + return err; + } + + err = hci_init(); +#endif + + if (!err) { + err = bt_conn_init(); + } + + scan_dev_found_cb = NULL; + if (!err) { + atomic_set_bit(bt_dev.flags, BT_DEV_READY); +#if 0 + bt_le_scan_update(false); +#endif + } + + return err; +} + +void on_nble_up(void) +{ + BT_DBG("%s", __FUNCTION__); + if (bt_ready_cb) + bt_ready_cb(bt_init()); +} + +extern void on_nble_curie_init(void); + +int bt_enable(bt_ready_cb_t cb) +{ + bt_ready_cb = cb; + + on_nble_curie_init(); + + if (!cb) { + return bt_init(); + } + + return 0; +} + + +static bool valid_adv_param(const struct bt_le_adv_param *param) +{ + switch (param->type) { + case BT_LE_ADV_IND: + case BT_LE_ADV_SCAN_IND: + case BT_LE_ADV_NONCONN_IND: + break; + default: + return false; + } + +#if 0 + /* checks done in Nordic */ + switch (param->addr_type) { + case BT_LE_ADV_ADDR_IDENTITY: + case BT_LE_ADV_ADDR_NRPA: + break; + default: + return false; + } + + if (param->interval_min > param->interval_max || + param->interval_min < 0x0020 || param->interval_max > 0x4000) { + return false; + } +#endif + + return true; +} + +static int set_ad(struct bt_eir_data *p_ad_data, + const struct bt_data *ad, size_t ad_len) +{ + int i; + + for (i = 0; i < ad_len; i++) { + /* Check if ad fit in the remaining buffer */ + if (p_ad_data->len + ad[i].data_len + 2 > 31) { + return -EINVAL; + } + + p_ad_data->data[p_ad_data->len++] = ad[i].data_len + 1; + p_ad_data->data[p_ad_data->len++] = ad[i].type; + + memcpy(&p_ad_data->data[p_ad_data->len], ad[i].data, + ad[i].data_len); + p_ad_data->len += ad[i].data_len; + } + + return 0; +} + +int bt_le_adv_start(const struct bt_le_adv_param *param, + const struct bt_data *ad, size_t ad_len, + const struct bt_data *sd, size_t sd_len) +{ + int err; + struct nble_gap_adv_params set_param = {0}; + struct nble_gap_ad_data_params data; + + if (!valid_adv_param(param)) { + return -EINVAL; + } + + memset(&data, 0, sizeof(data)); + + if (atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) { + return -EALREADY; + } + + err = set_advertise_disable(); + if (err) { + return err; + } + err = set_ad(&data.ad, ad, ad_len); + if (err) { + return err; + } + + /* + * Don't bother with scan response if the advertising type isn't + * a scannable one. + */ + if (param->type == BT_LE_ADV_IND || param->type == BT_LE_ADV_SCAN_IND) { + err = set_ad(&data.sd, sd, sd_len); + if (err) { + return err; + } + } + nble_gap_set_adv_data_req(&data); + + /* Timeout is handled by application timer */ + set_param.timeout = 0; + /* forced to none currently (no whitelist support) */ + set_param.filter_policy = 0; + set_param.interval_max = param->interval_max; + set_param.interval_min = param->interval_min; + set_param.type = param->type; + nble_gap_set_adv_params_req(&set_param); + +#if 0 + if (param->addr_type == BT_LE_ADV_ADDR_NRPA) { + err = le_set_nrpa(); + if (err) { + net_buf_unref(buf); + return err; + } + + set_param->own_addr_type = BT_ADDR_LE_RANDOM; + } else { + set_param->own_addr_type = BT_ADDR_LE_PUBLIC; + } + + bt_hci_cmd_send(BT_HCI_OP_LE_SET_ADV_PARAMETERS, buf); +#endif + + err = set_advertise_enable(); + if (err) { + return err; + } + + atomic_set_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING); + + return 0; +} + +void on_nble_gap_dir_adv_timeout_evt(const struct nble_gap_dir_adv_timeout_evt *p_evt) +{ + struct bt_conn *conn = bt_conn_lookup_state_le(BT_ADDR_LE_ANY, BT_CONN_CONNECT); + + if (conn) { + atomic_clear_bit(bt_dev.flags, BT_DEV_ADVERTISING); + conn->err = p_evt->error; + bt_conn_set_state(conn, BT_CONN_DISCONNECTED); + bt_conn_unref(conn); + } +} + +int bt_le_adv_stop(void) +{ + int err; + + if (!atomic_test_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING)) { + return -EALREADY; + } + + err = set_advertise_disable(); + if (err) { + return err; + } + atomic_clear_bit(bt_dev.flags, BT_DEV_KEEP_ADVERTISING); + + return 0; +} + +static bool valid_le_scan_param(const struct bt_le_scan_param *param) +{ + if (param->type != BT_HCI_LE_SCAN_PASSIVE && + param->type != BT_HCI_LE_SCAN_ACTIVE) { + return false; + } + + if (/* param->filter_dup != BT_HCI_LE_SCAN_FILTER_DUP_DISABLE */ + /* && nble always filters duplicates */ + param->filter_dup != BT_HCI_LE_SCAN_FILTER_DUP_ENABLE) { + return false; + } + + if (param->interval < 0x0004 || param->interval > 0x4000) { + return false; + } + + if (param->window < 0x0004 || param->window > 0x4000) { + return false; + } + + if (param->window > param->interval) { + return false; + } + + return true; +} + +int bt_le_scan_start(const struct bt_le_scan_param *param, bt_le_scan_cb_t cb) +{ + + int err; + + /* Check that the parameters have valid values */ + if (!valid_le_scan_param(param)) { + return -EINVAL; + } +#if NOT_USED_FOR_NOW + /* Return if active scan is already enabled */ + if (atomic_test_and_set_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return -EALREADY; + } + + if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) { + err = bt_hci_stop_scanning(); + if (err) { + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); + return err; + } + } +#endif + + err = start_le_scan(param->type, param->interval, param->window, + param->filter_dup); + + + if (err) { +#if NOT_USED_FOR_NOW + atomic_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN); +#endif + return err; + } + + scan_dev_found_cb = cb; + + return 0; +} + +int bt_le_scan_stop(void) +{ +#if NOT_USED_FOR_NOW + /* Return if active scanning is already disabled */ + if (!atomic_test_and_clear_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) { + return -EALREADY; + } +#endif + scan_dev_found_cb = NULL; + +#if NOT_USED_FOR_NOW + return bt_le_scan_update(false); +#else + return bt_hci_stop_scanning(); +#endif +} + +/* Temporary RSSI patch for UAS: RPC need definition if UAS not compiled */ +__attribute__((weak)) +void on_nble_uas_bucket_change(const struct nble_uas_bucket_change *p_params) +{ +} + +void ble_gap_set_rssi_report(struct nble_rssi_report_params *params, + struct bt_conn *conn, + rssi_report_resp_t resp_cb, rssi_report_t evt_cb) +{ + rssi_report_cb = evt_cb; + + params->conn_handle = conn->handle; + + nble_gap_set_rssi_report_req(params, resp_cb); +} + +void on_nble_gap_set_rssi_report_rsp(const struct nble_response *params) +{ + rssi_report_resp_t resp_cb = params->user_data; + + if (resp_cb) + resp_cb(params->status); +} + +void on_nble_gap_rssi_evt(const struct nble_gap_rssi_evt *event) +{ + if (rssi_report_cb) + rssi_report_cb(event->rssi_data); +} + +void ble_gap_set_tx_power(int8_t tx_power) +{ + struct nble_gap_tx_power_params params = { + .tx_power = tx_power, + }; + nble_gap_tx_power_req(¶ms); +} + +void on_nble_gap_tx_power_rsp(const struct nble_response *params) +{ +} + +void ble_gap_get_version(ble_get_version_cb_t func) +{ + struct nble_gap_get_version_param params; + + params.cb = func; + + nble_get_version_req(¶ms); +} + +void on_nble_get_version_rsp(const struct nble_version_response *par) +{ + struct nble_gap_get_version_param param = par->params; + ble_get_version_cb_t cb = param.cb; + + if (cb) { + cb(&par->ver); + } +} + +void bt_le_set_device_name(char *device_name, int len) +{ + struct nble_gap_service_write_params gap_service_params; + if (len > 20) + len = 20; + memset(&gap_service_params, 0, sizeof(gap_service_params)); + gap_service_params.attr_type = NBLE_GAP_SVC_ATTR_NAME; + gap_service_params.name.len = len; + gap_service_params.name.sec_mode = 0x11;// GAP_SEC_LEVEL_1 | GAP_SEC_MODE_1; + memcpy(gap_service_params.name.name_array, device_name, len); + nble_gap_service_write_req(&gap_service_params); +} + diff --git a/system/libarc32_arduino101/framework/src/services/ble/gatt.c b/system/libarc32_arduino101/framework/src/services/ble/gatt.c new file mode 100644 index 00000000..d3ef4c09 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/gatt.c @@ -0,0 +1,1561 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include + +#include +#include "gatt_internal.h" +#include "hci_core.h" +#include "conn_internal.h" + +/* #define BT_GATT_DEBUG 1 */ + +extern void on_nble_curie_log(char *fmt, ...); +extern void __assert_fail(void); +#ifdef BT_GATT_DEBUG +#define BT_DBG(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ERR(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) +#else +#define BT_DBG(fmt, ...) do {} while (0) +#define BT_ERR(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) +#endif + +#define N_BLE_BUF_SIZE 512 + +struct ble_gatt_service { + struct bt_gatt_attr *attrs; /* Pointer to the array of attributes */ + uint16_t attr_count; /* Number of attributes in the array */ +}; + +struct ble_gatts_flush_all { + struct bt_conn *conn; + int status; + uint8_t flag; +}; + +static struct ble_gatt_service db[CONFIG_BT_GATT_BLE_MAX_SERVICES]; + +static uint8_t db_cnt; + +#if defined(CONFIG_BLUETOOTH_GATT_CLIENT) +static struct bt_gatt_subscribe_params *subscriptions; +#endif + +/** + * Copy a UUID in a buffer using the smallest memory length + * @param buf Pointer to the memory where the UUID shall be copied + * @param uuid Pointer to the UUID to copy + * @return The length required to store the UUID in the memory + */ +static uint8_t bt_gatt_uuid_memcpy(uint8_t *buf, + const struct bt_uuid *uuid) +{ + uint8_t *ptr = buf; + + /* Store the type of the UUID */ + *ptr = uuid->type; + ptr++; + + /* 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(ptr, &le16, sizeof(le16)); + ptr += sizeof(le16); + } else { + memcpy(ptr, BT_UUID_128(uuid)->val, + sizeof(BT_UUID_128(uuid)->val)); + ptr += sizeof(BT_UUID_128(uuid)->val); + } + return ptr - buf; +} + +/* These attributes need the value to be read */ +static struct bt_uuid *whitelist[] = { + BT_UUID_GATT_PRIMARY, + BT_UUID_GATT_SECONDARY, + BT_UUID_GATT_INCLUDE, + BT_UUID_GATT_CHRC, + BT_UUID_GATT_CEP, + BT_UUID_GATT_CUD, + BT_UUID_GATT_CPF, + BT_UUID_GAP_DEVICE_NAME, + BT_UUID_GAP_APPEARANCE, + BT_UUID_GAP_PPCP +}; + +static int attr_read(struct bt_gatt_attr *attr, uint8_t *data, size_t len) +{ + uint8_t i; + int data_size; + + if (!data || len < 0) { + return -ENOMEM; + } + + data_size = bt_gatt_uuid_memcpy(data, attr->uuid); + + for (i = 0; i < ARRAY_SIZE(whitelist); i++) { + if (!bt_uuid_cmp(attr->uuid, whitelist[i])) { + int read; + + read = attr->read(NULL, attr, data + data_size, len, + 0); + if (read < 0) { + return read; + } + + data_size += read; + break; + } + } + + return data_size; +} + +int bt_gatt_register(struct bt_gatt_attr *attrs, size_t count) +{ + size_t attr_table_size, i; + struct nble_gatt_register_req param; + /* TODO: Replace the following with net_buf */ + uint8_t attr_table[N_BLE_BUF_SIZE]; + + if (!attrs || !count) { + return -EINVAL; + } + BT_ASSERT(db_cnt < ARRAY_SIZE(db)); + + db[db_cnt].attrs = attrs; + db[db_cnt].attr_count = count; + db_cnt++; + param.attr_base = attrs; + param.attr_count = count; + + attr_table_size = 0; + + for (i = 0; i < count; i++) { + struct bt_gatt_attr *attr = &attrs[i]; + struct ble_gatt_attr *att; + int data_size; + + if (attr_table_size + sizeof(*att) > sizeof(attr_table)) { + return -ENOMEM; + } + + att = (void *)&attr_table[attr_table_size]; + att->perm = attr->perm; + + /* Read attribute data */ + data_size = attr_read(attr, att->data, + sizeof(attr_table) - + (attr_table_size + sizeof(*att))); + if (data_size < 0) { + return data_size; + } + att->data_size = data_size; + + BT_DBG("table size = %u attr data_size = %u", attr_table_size, + att->data_size); + + /* Compute the new element size and align it on upper 4 bytes + * boundary. + */ + attr_table_size += (sizeof(*att) + att->data_size + 3) & ~3; + } + + nble_gatt_register_req(¶m, attr_table, attr_table_size); + return 0; +} + +void on_nble_gatt_register_rsp(const struct nble_gatt_register_rsp *rsp, + const struct nble_gatt_attr_handles *handles, uint8_t len) +{ + + if (rsp->status != 0) { + BT_ERR("failure registering table: %d - %u - %p", rsp->status, + rsp->attr_count, rsp->attr_base); + } +#ifdef BT_GATT_DEBUG + BT_DBG("register rsp : s=%d - b=%p - c=%u", rsp->status, + rsp->attr_base, rsp->attr_count); + { + int i; + + for (i = 0; i < rsp->attr_count; i++) { + if (handles[i].handle != 0) { + 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); + } + } + } +#endif +} + +ssize_t bt_gatt_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, + void *buf, uint16_t buf_len, uint16_t offset, + const void *value, uint16_t value_len) +{ + uint16_t len; + + if (offset > value_len) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + len = min(buf_len, value_len - offset); + + BT_DBG("handle 0x%04x offset %u length %u", attr->handle, offset, + len); + + memcpy(buf, value + offset, len); + + return len; +} + +ssize_t bt_gatt_attr_read_service(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + 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); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, + &uuid16, 2); + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, + BT_UUID_128(uuid)->val, 16); +} + +struct gatt_incl { + uint16_t start_handle; + uint16_t end_handle; + uint16_t uuid16; +} __packed; + +ssize_t bt_gatt_attr_read_included(struct bt_conn *conn, + const struct bt_gatt_attr *attr, + void *buf, uint16_t len, uint16_t offset) +{ + struct bt_gatt_include *incl = attr->user_data; + struct gatt_incl pdu; + uint8_t value_len; + + pdu.start_handle = sys_cpu_to_le16(incl->start_handle); + pdu.end_handle = sys_cpu_to_le16(incl->end_handle); + value_len = sizeof(pdu.start_handle) + sizeof(pdu.end_handle); + + /* + * Core 4.2, Vol 3, Part G, 3.2, + * The Service UUID shall only be present when the UUID is a 16-bit + * Bluetooth UUID. + */ + if (incl->uuid->type == BT_UUID_TYPE_16) { + pdu.uuid16 = sys_cpu_to_le16(BT_UUID_16(incl->uuid)->val); + value_len += sizeof(pdu.uuid16); + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &pdu, value_len); +} + +struct gatt_chrc { + uint8_t properties; + uint16_t value_handle; + union { + uint16_t uuid16; + uint8_t uuid[16]; + }; +} __packed; + +ssize_t bt_gatt_attr_read_chrc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + struct bt_gatt_chrc *chrc = attr->user_data; + struct gatt_chrc pdu; + uint8_t value_len; + + pdu.properties = chrc->properties; + /* BLUETOOTH SPECIFICATION Version 4.2 [Vol 3, Part G] page 534: + * 3.3.2 Characteristic Value Declaration + * The Characteristic Value declaration contains the value of the + * characteristic. It is the first Attribute after the characteristic + * declaration. All characteristic definitions shall have a + * Characteristic Value declaration. + */ +#if 0 + next = bt_gatt_attr_next(attr); + if (!next) { + BT_WARN("No value for characteristic at 0x%04x", attr->handle); + pdu.value_handle = 0x0000; + } else { + pdu.value_handle = sys_cpu_to_le16(next->handle); + } +#else + pdu.value_handle = 0x0000; +#endif + 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); + value_len += 2; + } else { + memcpy(pdu.uuid, BT_UUID_128(chrc->uuid)->val, 16); + value_len += 16; + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &pdu, value_len); +} + +void bt_gatt_foreach_attr(uint16_t start_handle, uint16_t end_handle, + bt_gatt_attr_func_t func, void *user_data) +{ + + const struct bt_gatt_attr *attr; + + BT_ASSERT(start_handle == 1 && end_handle == 0xFFFF); + + for (attr = db[0].attrs; attr; attr = bt_gatt_attr_next(attr)) { +#if 0 + /* Check if attribute handle is within range */ + if (attr->handle < start_handle || attr->handle > end_handle) { + continue; + } +#endif + + if (func(attr, user_data) == BT_GATT_ITER_STOP) { + break; + } + } +} + +struct bt_gatt_attr *bt_gatt_attr_next(const struct bt_gatt_attr *attr) +{ + struct ble_gatt_service *svc, *svc_last; + + svc_last = &db[db_cnt]; + + for (svc = db; svc < svc_last; svc++) { + if (attr >= svc->attrs && attr < &svc->attrs[svc->attr_count]) { + int index = attr - &svc->attrs[0]; + + if (index < (svc->attr_count - 1)) { + return (struct bt_gatt_attr *)&attr[1]; + } else if ((svc + 1) < svc_last) { + return (svc + 1)->attrs; + } else { + return NULL; + } + } + } + /* Normally, we should not reach here */ + return NULL; +} + +ssize_t bt_gatt_attr_read_ccc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + return BT_GATT_ERR(BT_ATT_ERR_NOT_SUPPORTED); + +#if 0 + struct _bt_gatt_ccc *ccc = attr->user_data; + uint16_t value; + size_t i; + + for (i = 0; i < ccc->cfg_len; i++) { + if (bt_addr_le_cmp(&ccc->cfg[i].peer, &conn->le.dst)) { + continue; + } + + value = sys_cpu_to_le16(ccc->cfg[i].value); + break; + } + + /* Default to disable if there is no cfg for the peer */ + if (i == ccc->cfg_len) { + value = 0x0000; + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &value, + sizeof(value)); +#endif +} + +static void gatt_ccc_changed(struct _bt_gatt_ccc *ccc) +{ + int i; + uint16_t value = 0x0000; + + for (i = 0; i < ccc->cfg_len; i++) { + if (ccc->cfg[i].value > value) { + value = ccc->cfg[i].value; + } + } + + BT_DBG("ccc %p value 0x%04x", ccc, value); + + if (value != ccc->value) { + ccc->value = value; + if (ccc->cfg_changed) + ccc->cfg_changed(value); + } +} + +#if defined(CONFIG_BLUETOOTH_GATT_CLIENT) +static bool is_bonded(const bt_addr_le_t *addr) +{ +#if defined(CONFIG_BLUETOOTH_SMP) + struct bt_conn *conn = bt_conn_lookup_addr_le(addr); + + /* + * this is a temporary workaround. if encrypt is set, we know we are + * paired. nble does not yet report if device is bonded. + */ + if (conn) { + uint8_t encrypt = conn->encrypt; + + bt_conn_unref(conn); + + return encrypt; + } + return false; +#else + return false; +#endif /* defined(CONFIG_BLUETOOTH_SMP) */ +} +#endif + +ssize_t bt_gatt_attr_write_ccc(struct bt_conn *conn, + const struct bt_gatt_attr *attr, const void *buf, + uint16_t len, uint16_t offset) +{ + struct _bt_gatt_ccc *ccc = attr->user_data; + const uint16_t *data = buf; + size_t i; + + //BT_DBG("%s", __FUNCTION__); + + if (offset > sizeof(*data)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + //BT_DBG("%s1", __FUNCTION__); + + if (offset + len > sizeof(*data)) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + //BT_DBG("%s2", __FUNCTION__); + + for (i = 0; i < ccc->cfg_len; i++) { + /* Check for existing configuration */ + if (!bt_addr_le_cmp(&ccc->cfg[i].peer, &conn->le.dst)) { + break; + } + } + + //BT_DBG("%s3", __FUNCTION__); + + if (i == ccc->cfg_len) { + for (i = 0; i < ccc->cfg_len; i++) { + /* Check for unused configuration */ + if (!ccc->cfg[i].valid) { + bt_addr_le_copy(&ccc->cfg[i].peer, &conn->le.dst); +#if NOT_USED_FOR_THE_TIME_BEING + /* Only set valid if bonded */ + ccc->cfg[i].valid = is_bonded(&conn->le.dst); +#endif + break; + } + } + + if ((i == ccc->cfg_len) && (ccc->cfg_len)) { + BT_WARN("No space to store CCC cfg"); + return -ENOMEM; + } + } + + //BT_DBG("%s len-%p", __FUNCTION__, ccc); + + ccc->cfg[i].value = sys_le16_to_cpu(*data); + //BT_DBG("%s 5len-%d", __FUNCTION__, len); + + //BT_DBG("handle 0x%04x value %u", attr->handle, *data); + + /* Update cfg if don't match */ + if (ccc->value != *data) { + gatt_ccc_changed(ccc); + } + + return len; +} + +ssize_t bt_gatt_attr_read_cep(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + struct bt_gatt_cep *value = attr->user_data; + uint16_t props = sys_cpu_to_le16(value->properties); + + return bt_gatt_attr_read(conn, attr, buf, len, offset, &props, + sizeof(props)); +} + +ssize_t bt_gatt_attr_read_cud(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + char *value = attr->user_data; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, strlen(value)); +} + +ssize_t bt_gatt_attr_read_cpf(struct bt_conn *conn, + const struct bt_gatt_attr *attr, void *buf, + uint16_t len, uint16_t offset) +{ + struct bt_gatt_cpf *value = attr->user_data; + + return bt_gatt_attr_read(conn, attr, buf, len, offset, value, + sizeof(*value)); +} + +struct notify_data { + uint16_t state; + uint16_t type; + const struct bt_gatt_attr *attr; + const void *data; + uint16_t len; + bt_gatt_notify_sent_func_t notify_cb; + struct bt_gatt_indicate_params *params; +}; + +static int att_notify(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *data, size_t len, + bt_gatt_notify_sent_func_t cb) +{ + struct nble_gatt_send_notif_params notif; + + notif.conn_handle = conn->handle; + notif.params.attr = attr; + notif.params.offset = 0; + notif.cback = cb; + + nble_gatt_send_notif_req(¬if, data, len); + + return 0; +} + +void on_nble_gatts_send_notif_rsp(const struct nble_gatt_notif_rsp *rsp) +{ + struct bt_conn *conn; + + conn = bt_conn_lookup_handle(rsp->conn_handle); + + if (conn) { + if (rsp->cback) { + rsp->cback(conn, rsp->attr, rsp->status); + } + bt_conn_unref(conn); + } +} + +void on_nble_gatts_send_ind_rsp(const struct nble_gatt_ind_rsp *rsp) +{ + struct bt_conn *conn; + + conn = bt_conn_lookup_handle(rsp->conn_handle); + + if (conn) { + if (rsp->cback) { + rsp->cback(conn, rsp->attr, rsp->status); + } + bt_conn_unref(conn); + } +} + +static int att_indicate(struct bt_conn *conn, + struct bt_gatt_indicate_params *params) +{ + struct nble_gatt_send_ind_params ind; + + ind.conn_handle = conn->handle; + ind.cback = params->func; + ind.params.attr = params->attr; + ind.params.offset = 0; + + nble_gatt_send_ind_req(&ind, params->data, params->len); + + return 0; +} + +static uint8_t notify_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct notify_data *data = user_data; + struct _bt_gatt_ccc *ccc; + size_t i; + + /* Check if the attribute was reached */ + if (data->state == 0) { + if (attr == data->attr) + data->state = 1; + return BT_GATT_ITER_CONTINUE; + } + + if (bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CCC)) { + /* Stop if we reach the next characteristic */ + if (!bt_uuid_cmp(attr->uuid, BT_UUID_GATT_CHRC)) { + return BT_GATT_ITER_STOP; + } + return BT_GATT_ITER_CONTINUE; + } + + /* Check attribute user_data must be of type struct _bt_gatt_ccc */ + if (attr->write != bt_gatt_attr_write_ccc) { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* Notify all peers configured */ + for (i = 0; i < ccc->cfg_len; i++) { + struct bt_conn *conn; + int err; + + if (ccc->value != data->type) { + continue; + } + + conn = bt_conn_lookup_addr_le(&ccc->cfg[i].peer); + if (!conn || conn->state != BT_CONN_CONNECTED) {//Bug here + continue; + } + + if (data->type == BT_GATT_CCC_INDICATE) { + err = att_indicate(conn, data->params); + + } else { + err = att_notify(conn, data->attr, data->data, + data->len, data->notify_cb); + } + + bt_conn_unref(conn); + + if (err < 0) { + return BT_GATT_ITER_STOP; + } + } + + return BT_GATT_ITER_CONTINUE; +} + +int bt_gatt_notify(struct bt_conn *conn, const struct bt_gatt_attr *attr, + const void *data, uint16_t len, + bt_gatt_notify_sent_func_t cb) +{ + struct notify_data nfy; + + if (!attr) { + return -EINVAL; + } + + if (conn) { + return att_notify(conn, attr, data, len, cb); + } + + nfy.state = 0; + nfy.attr = attr; + nfy.type = BT_GATT_CCC_NOTIFY; + nfy.data = data; + nfy.len = len; + nfy.notify_cb = cb; + + bt_gatt_foreach_attr(1, 0xffff, notify_cb, &nfy); + + return 0; +} + +int bt_gatt_indicate(struct bt_conn *conn, + struct bt_gatt_indicate_params *params) +{ + struct notify_data nfy; + + if (!params || !params->attr) { + return -EINVAL; + } + + if (conn) { + return att_indicate(conn, params); + } + + nfy.state = 0; + nfy.type = BT_GATT_CCC_INDICATE; + nfy.params = params; + + bt_gatt_foreach_attr(1, 0xffff, notify_cb, &nfy); + + return 0; +} + +static uint8_t connected_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct bt_conn *conn = user_data; + struct _bt_gatt_ccc *ccc; + size_t i; + + /* Check attribute user_data must be of type struct _bt_gatt_ccc */ + if (attr->write != bt_gatt_attr_write_ccc) { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* If already enabled skip */ + if (ccc->value) { + return BT_GATT_ITER_CONTINUE; + } + + for (i = 0; i < ccc->cfg_len; i++) { + /* Ignore configuration for different peer */ + if (bt_addr_le_cmp(&conn->le.dst, &ccc->cfg[i].peer)) { + continue; + } + + if (ccc->cfg[i].value) { + gatt_ccc_changed(ccc); + return BT_GATT_ITER_CONTINUE; + } + } + + return BT_GATT_ITER_CONTINUE; +} + +static uint8_t disconnected_cb(const struct bt_gatt_attr *attr, void *user_data) +{ + struct bt_conn *conn = user_data; + struct _bt_gatt_ccc *ccc; + size_t i; + + /* Check attribute user_data must be of type struct _bt_gatt_ccc */ + if (attr->write != bt_gatt_attr_write_ccc) { + return BT_GATT_ITER_CONTINUE; + } + + ccc = attr->user_data; + + /* If already disabled skip */ + if (!ccc->value) { + return BT_GATT_ITER_CONTINUE; + } + + for (i = 0; i < ccc->cfg_len; i++) { + /* Ignore configurations with disabled value */ + if (!ccc->cfg[i].value) { + continue; + } + + if (bt_addr_le_cmp(&conn->le.dst, &ccc->cfg[i].peer)) { + struct bt_conn *tmp; + + /* Skip if there is another peer connected */ + tmp = bt_conn_lookup_addr_le(&ccc->cfg[i].peer); + if (tmp) { + if (tmp->state == BT_CONN_CONNECTED) { + bt_conn_unref(tmp); + return BT_GATT_ITER_CONTINUE; + } + + bt_conn_unref(tmp); + } + } + } + + /* Reset value while disconnected */ + memset(&ccc->value, 0, sizeof(ccc->value)); + + if (ccc->cfg_changed) { + ccc->cfg_changed(ccc->value); + } + + BT_DBG("ccc %p reseted", ccc); + + return BT_GATT_ITER_CONTINUE; +} + +void on_nble_gatts_write_evt(const struct nble_gatt_wr_evt *evt, + const uint8_t *buf, uint8_t buflen) +{ + const struct bt_gatt_attr *attr = evt->attr; + struct bt_conn *conn = bt_conn_lookup_handle(evt->conn_handle); + struct nble_gatts_wr_reply_params reply_data; + + //BT_DBG("write_evt %p", attr); + + /* Check for write support and flush support in case of prepare */ + if (!attr->write || + ((evt->flag & NBLE_GATT_WR_FLAG_PREP) && !attr->flush)) { + reply_data.status = BT_GATT_ERR(BT_ATT_ERR_WRITE_NOT_PERMITTED); + + goto reply; + } + //BT_DBG("%s", __FUNCTION__); + + reply_data.status = attr->write(conn, attr, buf, buflen, evt->offset); + if (reply_data.status < 0) { + + //BT_DBG("%s1-1", __FUNCTION__); + + goto reply; + } + + //BT_DBG("%s1", __FUNCTION__); + + /* Return an error if not all data has been written */ + if (reply_data.status != buflen) { + reply_data.status = BT_GATT_ERR(BT_ATT_ERR_UNLIKELY); + + goto reply; + } + + //BT_DBG("%s2", __FUNCTION__); + + if (attr->flush && !(evt->flag & NBLE_GATT_WR_FLAG_PREP)) + reply_data.status = attr->flush(conn, attr, BT_GATT_FLUSH_SYNC); + + //BT_DBG("%s3", __FUNCTION__); + +reply: + //BT_DBG("%s4", __FUNCTION__); + if (evt->flag & NBLE_GATT_WR_FLAG_REPLY) { + reply_data.conn_handle = evt->conn_handle; + + nble_gatts_wr_reply_req(&reply_data); + } + + if (conn) + bt_conn_unref(conn); +} + +static uint8_t flush_all(const struct bt_gatt_attr *attr, void *user_data) +{ + struct ble_gatts_flush_all *flush_data = user_data; + + if (attr->flush) { + int status = attr->flush(flush_data->conn, attr, flush_data->flag); + + if (status < 0 && flush_data->status == 0) + flush_data->status = status; + } + + return BT_GATT_ITER_CONTINUE; +} + +void on_nble_gatts_write_exec_evt(const struct nble_gatt_wr_exec_evt *evt) +{ + BT_DBG("write_exec_evt"); + + struct ble_gatts_flush_all flush_data = { + .conn = bt_conn_lookup_handle(evt->conn_handle), + .flag = evt->flag, + .status = 0, + }; + + bt_gatt_foreach_attr(0x0001, 0xFFFF, flush_all, &flush_data); + + struct nble_gatts_wr_reply_params reply_data = { + .conn_handle = evt->conn_handle, + .status = flush_data.status, + }; + nble_gatts_wr_reply_req(&reply_data); + + bt_conn_unref(flush_data.conn); +} + +void on_nble_gatts_read_evt(const struct nble_gatt_rd_evt *evt) +{ + struct nble_gatts_rd_reply_params reply_data; + const struct bt_gatt_attr *attr; + /* The length of the value sent back in the response is unknown because + * of NRF API limitation, so we use the max possible one: ATT_MTU-1 */ + uint8_t data[BLE_GATT_MTU_SIZE - 1] = { 0 }; + int len; + + attr = evt->attr; + + BT_DBG("read_evt %p", attr); + + memset(&reply_data, 0, sizeof(reply_data)); + + if (attr->read) { + struct bt_conn *conn = bt_conn_lookup_handle(evt->conn_handle); + + len = attr->read(conn, attr, data, sizeof(data), evt->offset); + + if (conn) + bt_conn_unref(conn); + + } else { + len = BT_GATT_ERR(BT_ATT_ERR_READ_NOT_PERMITTED); + } + + /* status >= 0 is considered as success by nble */ + reply_data.status = len; + + if (len < 0) { + len = 0; + } + + reply_data.conn_handle = evt->conn_handle; + + /* offset is needed by nble even in error case */ + reply_data.offset = evt->offset; + + nble_gatts_rd_reply_req(&reply_data, data, len); +} + +#if defined(CONFIG_BLUETOOTH_GATT_CLIENT) +void on_nble_gattc_value_evt(const struct ble_gattc_value_evt *evt, + uint8_t *data, uint8_t length) +{ + struct bt_gatt_subscribe_params *params; + struct bt_conn *conn; + + conn = bt_conn_lookup_handle(evt->conn_handle); + + //BT_DBG("FUNC %s", __FUNCTION__); + + if (conn) { + for (params = subscriptions; params; params = params->_next) { + if (evt->handle != params->value_handle) { + continue; + } + + if (params->notify(conn, params, data, length) == + BT_GATT_ITER_STOP) { + bt_gatt_unsubscribe(conn, params); + } + } + bt_conn_unref(conn); + } +} + +static void gatt_subscription_remove(struct bt_conn *conn, + struct bt_gatt_subscribe_params *prev, + struct bt_gatt_subscribe_params *params) +{ + /* Remove subscription from the list*/ + if (!prev) { + subscriptions = params->_next; + } else { + prev->_next = params->_next; + } + + params->notify(conn, params, NULL, 0); +} + +static void remove_subscribtions(struct bt_conn *conn) +{ + struct bt_gatt_subscribe_params *params, *prev; + + /* Lookup existing subscriptions */ + for (params = subscriptions, prev = NULL; params; + prev = params, params = params->_next) { + if (bt_addr_le_cmp(¶ms->_peer, &conn->le.dst)) { + continue; + } + + /* Remove subscription */ + gatt_subscription_remove(conn, prev, params); + } +} + +int bt_gatt_exchange_mtu(struct bt_conn *conn, bt_gatt_rsp_func_t func) +{ + return -EINVAL; +} + +void on_nble_gattc_discover_rsp(const struct nble_gattc_discover_rsp *rsp, + const uint8_t *data, uint8_t data_len) +{ + size_t i; + uint8_t attr_count; + uint16_t last_handle; + int status = BT_GATT_ITER_STOP; + struct bt_gatt_discover_params *params; + struct bt_gatt_service svc_value; + struct bt_gatt_include inc_value; + struct bt_conn *conn = bt_conn_lookup_handle(rsp->conn_handle); + + BT_ASSERT(conn); + + params = rsp->user_data; + + BT_DBG("disc_rsp: s=%d", rsp->status); + + if (rsp->status) + goto complete; + + if (rsp->type == BT_GATT_DISCOVER_PRIMARY) { + attr_count = (data_len / sizeof(struct nble_gattc_primary)); + } else if (rsp->type == BT_GATT_DISCOVER_INCLUDE) { + attr_count = (data_len / sizeof(struct nble_gattc_included)); + } else if (rsp->type == BT_GATT_DISCOVER_CHARACTERISTIC) { + attr_count = (data_len / sizeof(struct nble_gattc_characteristic)); + } else if (rsp->type == BT_GATT_DISCOVER_DESCRIPTOR) { + attr_count = (data_len / sizeof(struct nble_gattc_descriptor)); + } else + goto complete; + BT_DBG("disc_rsp: c=%d", attr_count); + last_handle = params->end_handle; + for (i = 0; i < attr_count; i++) { + struct bt_gatt_attr *attr = NULL; + + if (rsp->type == BT_GATT_DISCOVER_PRIMARY) { + const struct nble_gattc_primary *gattr = + (void *)&data[i * sizeof(*gattr)]; + if ((gattr->range.start_handle < params->start_handle) && + (gattr->range.end_handle > params->end_handle)) { + /* + * Only the attributes with attribute handles + * between and including the Starting + * Handle and the Ending Handle is returned + */ + goto complete; + } + svc_value.end_handle = gattr->range.end_handle; + svc_value.uuid = params->uuid; + attr = (&(struct bt_gatt_attr)BT_GATT_PRIMARY_SERVICE(&svc_value)); + attr->handle = gattr->handle; + last_handle = svc_value.end_handle; + } else if (rsp->type == BT_GATT_DISCOVER_INCLUDE) { + const struct nble_gattc_included *gattr = + (void *)&data[i * sizeof(*gattr)]; + + inc_value.start_handle = gattr->range.start_handle; + inc_value.end_handle = gattr->range.end_handle; + /* + * 4.5.1 If the service UUID is a 16-bit Bluetooth UUID + * it is also returned in the response. + */ + switch (gattr->uuid.uuid.type) { + case BT_UUID_TYPE_16: + inc_value.uuid = &gattr->uuid.uuid; + break; + case BT_UUID_TYPE_128: + /* Data is not available at this point */ + break; + } + attr = (&(struct bt_gatt_attr) + BT_GATT_INCLUDE_SERVICE(&inc_value)); + attr->handle = gattr->handle; + last_handle = gattr->handle; + } else if (rsp->type == BT_GATT_DISCOVER_CHARACTERISTIC) { + const struct nble_gattc_characteristic *gattr = + (void *)&data[i * sizeof(*gattr)]; + attr = (&(struct bt_gatt_attr) + BT_GATT_CHARACTERISTIC(&gattr->uuid.uuid, + gattr->prop)); + attr->handle = gattr->handle; + last_handle = gattr->handle; + /* Skip if UUID is set but doesn't match */ + if (params->uuid && bt_uuid_cmp(&gattr->uuid.uuid, params->uuid)) { + continue; + } + } else if (rsp->type == BT_GATT_DISCOVER_DESCRIPTOR) { + const struct nble_gattc_descriptor *gattr = + (void *)&data[i * sizeof(*gattr)]; + + attr = (&(struct bt_gatt_attr) + BT_GATT_DESCRIPTOR(&gattr->uuid.uuid, 0, NULL, NULL, NULL)); + attr->handle = gattr->handle; + last_handle = gattr->handle; + } else { + /* Error case */ + goto complete; + } + status = params->func(conn, attr, params); + if (status == BT_GATT_ITER_STOP) { + /* Not required to call complete */ + goto done; + } + } + if (last_handle < UINT16_MAX) { + last_handle++; + } + BT_DBG("disc_rsp: l=%d", last_handle); + params->start_handle = last_handle; + if (params->start_handle < params->end_handle) { + if (!bt_gatt_discover(conn, params)) + goto not_done; + } + +complete: + /* Indicate that there are no more attributes found */ + params->func(conn, NULL, params); + +done: + BT_DBG("disc_rsp: done"); + +not_done: + bt_conn_unref(conn); +} + +int bt_gatt_discover(struct bt_conn *conn, + struct bt_gatt_discover_params *params) +{ + struct nble_discover_params discover_params; + + if (!conn || !params || !params->func || !params->start_handle || + !params->end_handle || params->start_handle > params->end_handle) { + return -EINVAL; + } + + if (conn->state != BT_CONN_CONNECTED) { + return -EINVAL; + } + + BT_DBG("disc: %d", params->start_handle); + + memset(&discover_params, 0, sizeof(discover_params)); + + switch (params->type) { + case BT_GATT_DISCOVER_PRIMARY: + case BT_GATT_DISCOVER_CHARACTERISTIC: + if (params->uuid) { + /* Always copy a full 128 bit UUID */ + discover_params.uuid = *BT_UUID_128(params->uuid); + discover_params.flags = DISCOVER_FLAGS_UUID_PRESENT; + } + break; + + case BT_GATT_DISCOVER_INCLUDE: + case BT_GATT_DISCOVER_DESCRIPTOR: + break; + default: + return -EINVAL; + } + + discover_params.conn_handle = conn->handle; + discover_params.type = params->type; + discover_params.handle_range.start_handle = params->start_handle; + discover_params.handle_range.end_handle = params->end_handle; + + discover_params.user_data = params; + + nble_gattc_discover_req(&discover_params); + + return 0; +} + +void on_nble_gattc_read_multiple_rsp(const struct ble_gattc_read_rsp *rsp, + uint8_t *pdu, uint8_t length) +{ + struct bt_gatt_read_params *params; + struct bt_conn *conn = bt_conn_lookup_handle(rsp->conn_handle); + + BT_ASSERT(conn); + + params = rsp->user_data; + + BT_DBG("err 0x%02x", rsp->status); + + if (rsp->status) { + params->func(conn, rsp->status, params, NULL, 0); + bt_conn_unref(conn); + return; + } + + params->func(conn, 0, params, pdu, length); + + /* mark read as complete since read multiple is single response */ + params->func(conn, 0, params, NULL, 0); + + bt_conn_unref(conn); +} + +void on_nble_gattc_read_rsp(const struct ble_gattc_read_rsp *rsp, + uint8_t *pdu, uint8_t length) +{ + struct bt_gatt_read_params *params; + struct bt_conn *conn = bt_conn_lookup_handle(rsp->conn_handle); + + BT_ASSERT(conn); + + params = rsp->user_data; + + if (rsp->status) { + params->func(conn, rsp->status, params, NULL, 0); + bt_conn_unref(conn); + return; + } + + if (params->func(conn, 0, params, pdu, length) == BT_GATT_ITER_STOP) { + bt_conn_unref(conn); + return; + } + + /* + * Core Spec 4.2, Vol. 3, Part G, 4.8.1 + * If the Characteristic Value is greater than (ATT_MTU – 1) octets + * in length, the Read Long Characteristic Value procedure may be used + * if the rest of the Characteristic Value is required. + */ + if (length < BLE_GATT_MTU_SIZE - 1) { + params->func(conn, 0, params, NULL, 0); + bt_conn_unref(conn); + return; + } + + params->single.offset += length; + + /* Continue reading the attribute */ + if (bt_gatt_read(conn, params)) { + params->func(conn, BT_ATT_ERR_UNLIKELY, params, NULL, 0); + } + bt_conn_unref(conn); +} + +int bt_gatt_read(struct bt_conn *conn, struct bt_gatt_read_params *params) +{ + + struct ble_gattc_read_params single_req; + struct ble_gattc_read_multiple_params mutiple_req; + + if (!conn || conn->state != BT_CONN_CONNECTED || !params || + params->handle_count == 0 || !params->func) { + return -EINVAL; + } + + single_req.conn_handle = conn->handle; + + if (1 == params->handle_count) { + single_req.handle = params->single.handle; + single_req.offset = params->single.offset; + single_req.user_data = params; + + nble_gattc_read_req(&single_req); + } else { + mutiple_req.conn_handle = conn->handle; + mutiple_req.user_data = params; + + nble_gattc_read_multiple_req(&mutiple_req, params->handles, 2 * params->handle_count); + } + return 0; +} + +static void on_write_no_rsp_complete(struct bt_conn *conn, uint8_t err, + const void *data) +{ +} + +static void on_write_complete(struct bt_conn *conn, uint8_t err, + const struct bt_gatt_write_params *wr_params) +{ + bt_gatt_write_rsp_func_t func = wr_params->user_data[0]; + const void *data = wr_params->user_data[1]; + + BT_ASSERT(func); + func(conn, err, data); +} + +static int _bt_gatt_write(struct bt_conn *conn, uint16_t handle, bool with_resp, + uint16_t offset, const void *data, uint16_t length, + struct bt_gatt_write_params *wr_params) +{ + struct ble_gattc_write_params req; + + req.conn_handle = conn->handle; + req.handle = handle; + req.offset = offset; + req.with_resp = with_resp; + req.wr_params = *wr_params; + + nble_gattc_write_req(&req, data, length); + + return 0; +} + +void on_nble_gattc_write_rsp(const struct ble_gattc_write_rsp *rsp) +{ + + struct bt_conn *conn = bt_conn_lookup_handle(rsp->conn_handle); + + BT_ASSERT(conn); + + if (rsp->wr_params.func) { + rsp->wr_params.func(conn, rsp->status, &rsp->wr_params); + } + + bt_conn_unref(conn); +} + + +int bt_gatt_write_without_response(struct bt_conn *conn, uint16_t handle, + const void *data, uint16_t length, bool sign) +{ + return bt_gatt_write(conn, handle, 0, data, length, + on_write_no_rsp_complete); +} + +int bt_gatt_write(struct bt_conn *conn, uint16_t handle, uint16_t offset, + const void *data, uint16_t length, + bt_gatt_write_rsp_func_t func) +{ + struct bt_gatt_write_params wr_params; + + if (!conn || conn->state != BT_CONN_CONNECTED || !handle || !func) { + return -EINVAL; + } + + wr_params.func = on_write_complete; + wr_params.user_data[0] = func; + wr_params.user_data[1] = (void *)data; + + return _bt_gatt_write(conn, handle, + (func == on_write_no_rsp_complete) + ? false : true, + offset, data, length, &wr_params); +} + +static void gatt_subscription_add(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params) +{ + bt_addr_le_copy(¶ms->_peer, &conn->le.dst); + + /* Prepend subscription */ + params->_next = subscriptions; + subscriptions = params; +} + +static void att_write_ccc_rsp(struct bt_conn *conn, uint8_t err, + const struct bt_gatt_write_params *wr_params) +{ + struct bt_gatt_subscribe_params *params = wr_params->user_data[0]; + + /* if write to CCC failed we remove subscription and notify app */ + if (err) { + struct bt_gatt_subscribe_params *cur, *prev; + + for (cur = subscriptions, prev = NULL; cur; + prev = cur, cur = cur->_next) { + + if (cur == params) { + gatt_subscription_remove(conn, prev, params); + break; + } + } + } +} + +static int gatt_write_ccc(struct bt_conn *conn, uint16_t handle, uint16_t value, + bt_att_func_t func, + struct bt_gatt_subscribe_params *params) +{ + struct bt_gatt_write_params wr_params; + + wr_params.func = func; + wr_params.user_data[0] = params; + + BT_DBG("handle 0x%04x value 0x%04x", handle, value); + + return _bt_gatt_write(conn, handle, true, 0, &value, sizeof(value), + &wr_params); +} + +int bt_gatt_subscribe(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params) +{ + struct bt_gatt_subscribe_params *tmp; + bool has_subscription = false; + + if (!conn || conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + if (!params || !params->notify || + !params->value || !params->ccc_handle) { + return -EINVAL; + } + + /* Lookup existing subscriptions */ + for (tmp = subscriptions; tmp; tmp = tmp->_next) { + /* Fail if entry already exists */ + if (tmp == params) { + return -EALREADY; + } + + /* Check if another subscription exists */ + if (!bt_addr_le_cmp(&tmp->_peer, &conn->le.dst) && + tmp->value_handle == params->value_handle && + tmp->value >= params->value) { + has_subscription = true; + } + } + + /* Skip write if already subscribed */ + if (!has_subscription) { + int err; + + err = gatt_write_ccc(conn, params->ccc_handle, params->value, + att_write_ccc_rsp, params); + if (err) { + return err; + } + } + + /* + * Add subscription before write complete as some implementation were + * reported to send notification before reply to CCC write. + */ + gatt_subscription_add(conn, params); + + return 0; +} + +int bt_gatt_unsubscribe(struct bt_conn *conn, + struct bt_gatt_subscribe_params *params) +{ + struct bt_gatt_subscribe_params *tmp; + bool has_subscription = false, found = false; + + if (!conn || conn->state != BT_CONN_CONNECTED) { + return -ENOTCONN; + } + + if (!params) { + return -EINVAL; + } + + /* Check head */ + if (subscriptions == params) { + subscriptions = params->_next; + found = true; + } + + /* Lookup existing subscriptions */ + for (tmp = subscriptions; tmp; tmp = tmp->_next) { + /* Remove subscription */ + if (tmp->_next == params) { + tmp->_next = params->_next; + found = true; + } + + /* Check if there still remains any other subscription */ + if (!bt_addr_le_cmp(&tmp->_peer, &conn->le.dst) && + tmp->value_handle == params->value_handle) { + has_subscription = true; + } + } + + if (!found) { + return -EINVAL; + } + + if (has_subscription) { + return 0; + } + + params->value = 0; + + return gatt_write_ccc(conn, params->ccc_handle, params->value, NULL, + NULL); +} + +static void add_subscriptions(struct bt_conn *conn) +{ + struct bt_gatt_subscribe_params *params, *prev; + + /* Lookup existing subscriptions */ + for (params = subscriptions, prev = NULL; params; + prev = params, params = params->_next) { + if (bt_addr_le_cmp(¶ms->_peer, &conn->le.dst)) { + continue; + } + + /* Force write to CCC to workaround devices that don't track + * it properly. + */ + gatt_write_ccc(conn, params->ccc_handle, params->value, + att_write_ccc_rsp, params); + } +} +#else + +void on_nble_gattc_discover_rsp(const struct nble_gattc_discover_rsp *rsp, + const uint8_t *data, uint8_t data_len) +{ + +} +void on_nble_gattc_write_rsp(const struct ble_gattc_write_rsp *rsp) +{ +} + +void on_nble_gattc_value_evt(const struct ble_gattc_value_evt *evt, + uint8_t *buf, uint8_t buflen) +{ +} + +void on_nble_gattc_read_rsp(const struct ble_gattc_read_rsp *rsp, + uint8_t *data, uint8_t data_len) +{ +} + +void on_nble_gattc_read_multiple_rsp(const struct ble_gattc_read_rsp *rsp, + uint8_t *data, uint8_t data_len) +{ +} + +#endif + +void bt_gatt_connected(struct bt_conn *conn) +{ + BT_DBG("conn %p", conn); + bt_gatt_foreach_attr(0x0001, 0xffff, connected_cb, conn); +#if defined(CONFIG_BLUETOOTH_GATT_CLIENT) + add_subscriptions(conn); +#endif /* CONFIG_BLUETOOTH_GATT_CLIENT */ +} + +void bt_gatt_disconnected(struct bt_conn *conn) +{ + BT_DBG("conn %p", conn); + bt_gatt_foreach_attr(0x0001, 0xffff, disconnected_cb, conn); + +#if defined(CONFIG_BLUETOOTH_GATT_CLIENT) + /* If bonded don't remove subscriptions */ + if (is_bonded(&conn->le.dst)) { + return; + } + + remove_subscribtions(conn); +#endif /* CONFIG_BLUETOOTH_GATT_CLIENT */ +} diff --git a/system/libarc32_arduino101/framework/src/services/ble/hci_core.h b/system/libarc32_arduino101/framework/src/services/ble/hci_core.h new file mode 100644 index 00000000..dc7374b0 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/hci_core.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* State tracking for the local Bluetooth controller */ +struct bt_dev { + atomic_t flags[1]; +}; +extern struct bt_dev bt_dev; + +#if defined(CONFIG_BLUETOOTH_SMP) || defined(CONFIG_BLUETOOTH_BREDR) +extern const struct bt_conn_auth_cb *bt_auth; +#endif /* CONFIG_BLUETOOTH_SMP || CONFIG_BLUETOOTH_BREDR */ + +static inline bool bt_addr_le_is_rpa(const bt_addr_le_t *addr) +{ + if (addr->type != BT_ADDR_LE_RANDOM) + return false; + + if ((addr->val[5] & 0xc0) == 0x40) + return true; + + return false; +} + +static inline bool bt_addr_le_is_identity(const bt_addr_le_t *addr) +{ + if (addr->type == BT_ADDR_LE_PUBLIC) + return true; + + /* Check for Random Static address type */ + if ((addr->val[5] & 0xc0) == 0xc0) + return true; + + return false; +} + +static inline bool bt_le_conn_params_valid(uint16_t min, uint16_t max, + uint16_t latency, uint16_t timeout) +{ + if (min > max || min < 6 || max > 3200) { + return false; + } + + /* Limits according to BT Core spec 4.2 [Vol 2, Part E, 7.8.12] */ + if (timeout < 10 || timeout > 3200) { + return false; + } + + /* Limits according to BT Core spec 4.2 [Vol 6, Part B, 4.5.1] */ + if (latency > 499 || ((latency + 1) * max) > (timeout * 4)) { + return false; + } + + return true; +} + + diff --git a/system/libarc32_arduino101/framework/src/services/ble/l2cap.c b/system/libarc32_arduino101/framework/src/services/ble/l2cap.c new file mode 100644 index 00000000..488cfd0e --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/l2cap.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include + +#include "conn_internal.h" +#include "gatt_internal.h" +#include "smp.h" + +/* #define BT_GATT_DEBUG 1 */ + +extern void on_nble_curie_log(char *fmt, ...); +extern void __assert_fail(void); +#ifdef BT_GATT_DEBUG +#define BT_DBG(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ERR(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) +#else +#define BT_DBG(fmt, ...) do {} while (0) +#define BT_ERR(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) +#endif + +void bt_l2cap_connected(struct bt_conn *conn) +{ + /* TODO Add support for fixed channels on BR/EDR transport */ + if (conn->type != BT_CONN_TYPE_LE) { + return; + } +#if defined(CONFIG_BLUETOOTH_SMP) + bt_smp_connected(conn); +#endif + bt_gatt_connected(conn); +} + +void bt_l2cap_disconnected(struct bt_conn *conn) +{ +#if defined(CONFIG_BLUETOOTH_SMP) + bt_smp_disconnected(conn); +#endif + bt_gatt_disconnected(conn); +} diff --git a/system/libarc32_arduino101/framework/src/services/ble/l2cap_internal.h b/system/libarc32_arduino101/framework/src/services/ble/l2cap_internal.h new file mode 100644 index 00000000..2f593fa5 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/l2cap_internal.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef L2CAP_INTERNAL_H_ +#define L2CAP_INTERNAL_H_ + +/* Notify L2CAP channels of a new connection */ +void bt_l2cap_connected(struct bt_conn *conn); + +/* Notify L2CAP channels of a disconnect event */ +void bt_l2cap_disconnected(struct bt_conn *conn); + + +#endif /* L2CAP_INTERNAL_H_ */ diff --git a/system/libarc32_arduino101/framework/src/services/ble/smp.h b/system/libarc32_arduino101/framework/src/services/ble/smp.h new file mode 100644 index 00000000..4f52c002 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/smp.h @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define BT_SMP_ERR_PASSKEY_ENTRY_FAILED 0x01 +#define BT_SMP_ERR_OOB_NOT_AVAIL 0x02 +#define BT_SMP_ERR_AUTH_REQUIREMENTS 0x03 +#define BT_SMP_ERR_CONFIRM_FAILED 0x04 +#define BT_SMP_ERR_PAIRING_NOTSUPP 0x05 +#define BT_SMP_ERR_ENC_KEY_SIZE 0x06 +#define BT_SMP_ERR_CMD_NOTSUPP 0x07 +#define BT_SMP_ERR_UNSPECIFIED 0x08 +#define BT_SMP_ERR_REPEATED_ATTEMPTS 0x09 +#define BT_SMP_ERR_INVALID_PARAMS 0x0a +#define BT_SMP_ERR_DHKEY_CHECK_FAILED 0x0b +#define BT_SMP_ERR_NUMERIC_COMP_FAILED 0x0c +#define BT_SMP_ERR_BREDR_PAIRING_IN_PROGRESS 0x0d +#define BT_SMP_ERR_CROSS_TRANSP_NOT_ALLOWED 0x0e + +#define BT_SMP_IO_DISPLAY_ONLY 0x00 +#define BT_SMP_IO_DISPLAY_YESNO 0x01 +#define BT_SMP_IO_KEYBOARD_ONLY 0x02 +#define BT_SMP_IO_NO_INPUT_OUTPUT 0x03 +#define BT_SMP_IO_KEYBOARD_DISPLAY 0x04 + +#define BT_SMP_OOB_NOT_PRESENT 0x00 +#define BT_SMP_OOB_PRESENT 0x01 + +#define BT_SMP_MIN_ENC_KEY_SIZE 7 +#define BT_SMP_MAX_ENC_KEY_SIZE 16 + +#define BT_SMP_DIST_ENC_KEY 0x01 +#define BT_SMP_DIST_ID_KEY 0x02 +#define BT_SMP_DIST_SIGN 0x04 +#define BT_SMP_DIST_LINK_KEY 0x08 + +#define BT_SMP_DIST_MASK 0x0f + +#define BT_SMP_AUTH_NONE 0x00 +#define BT_SMP_AUTH_BONDING 0x01 +#define BT_SMP_AUTH_MITM 0x04 +#define BT_SMP_AUTH_SC 0x08 + +#if 0 +bool bt_smp_irk_matches(const uint8_t irk[16], const bt_addr_t *addr); +#endif +int bt_smp_send_pairing_req(struct bt_conn *conn); +int bt_smp_send_security_req(struct bt_conn *conn); +#if 0 +void bt_smp_update_keys(struct bt_conn *conn); +bool bt_smp_get_tk(struct bt_conn *conn, uint8_t *tk); + +void bt_smp_dhkey_ready(const uint8_t *dhkey); +void bt_smp_pkey_ready(void); +#endif + +int bt_smp_init(void); + +int bt_smp_auth_passkey_entry(struct bt_conn *conn, unsigned int passkey); +int bt_smp_auth_passkey_confirm(struct bt_conn *conn, bool match); +int bt_smp_auth_cancel(struct bt_conn *conn); + +int bt_smp_remove_info(const bt_addr_le_t *addr); + +#ifdef CONFIG_BLUETOOTH_SMP +void bt_smp_connected(struct bt_conn *conn); +void bt_smp_disconnected(struct bt_conn *conn); +#endif + +#if 0 +/** brief Verify signed message + * + * @param conn Bluetooth connection + * @param buf received packet buffer with message and signature + * + * @return 0 in success, error code otherwise + */ +int bt_smp_sign_verify(struct bt_conn *conn, struct net_buf *buf); + +/** brief Sign message + * + * @param conn Bluetooth connection + * @param buf message buffer + * + * @return 0 in success, error code otherwise + */ +int bt_smp_sign(struct bt_conn *conn, struct net_buf *buf); +#endif diff --git a/system/libarc32_arduino101/framework/src/services/ble/smp_null.c b/system/libarc32_arduino101/framework/src/services/ble/smp_null.c new file mode 100644 index 00000000..cdc3a4d5 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/smp_null.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include + +#include "smp.h" + +/* nble internal APIs */ +#include "gap_internal.h" + +/* #define BT_GATT_DEBUG 1 */ + +extern void on_nble_curie_log(char *fmt, ...); +extern void __assert_fail(void); +#ifdef BT_GATT_DEBUG +#define BT_DBG(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ERR(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) +#else +#define BT_DBG(fmt, ...) do {} while (0) +#define BT_ERR(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_WARN(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_INFO(fmt, ...) on_nble_curie_log(fmt, ##__VA_ARGS__) +#define BT_ASSERT(cond) ((cond) ? (void)0 : __assert_fail()) +#endif + +/** + * Compile time IO capabilities and OOB support settings for nble. + * + * This compile options must match the application registered callbacks + * (bt_conn_auth_cb_register) + */ +#define NBLE_SMP_IO_CAPS BT_SMP_IO_NO_INPUT_OUTPUT +#define NBLE_SMP_AUTH_OPTIONS 0 +#define NBLE_SMP_OBB_PRESENT BT_SMP_OOB_NOT_PRESENT + +#if NOT_USED_FOR_NOW +int bt_smp_sign_verify(struct bt_conn *conn, struct net_buf *buf) +{ + return -ENOTSUP; +} + +int bt_smp_sign(struct bt_conn *conn, struct net_buf *buf) +{ + return -ENOTSUP; +} +#endif + +void on_nble_gap_sm_bond_info_rsp(const struct nble_gap_sm_bond_info_rsp *rsp, + const bt_addr_le_t *peer_addr, uint16_t len) +{ + /* stub */ +} + +void on_nble_gap_sm_passkey_req_evt(const struct nble_gap_sm_passkey_req_evt * p_evt) +{ + /* stub */ +} + +void on_nble_gap_sm_passkey_display_evt( + const struct nble_gap_sm_passkey_disp_evt *p_evt) +{ + /* stub */ +} + +void on_nble_gap_sm_status_evt(const struct nble_gap_sm_status_evt *evt) +{ + /* stub */ + BT_INFO("nble_gap_sm_status_evt: %d, gap_status: %d", + evt->evt_type, evt->status); +} + +int bt_smp_init(void) +{ + struct nble_gap_sm_config_params params = { + .options = NBLE_SMP_AUTH_OPTIONS, + .io_caps = NBLE_SMP_IO_CAPS, + .key_size = BT_SMP_MAX_ENC_KEY_SIZE, + .oob_present = NBLE_SMP_OBB_PRESENT, + }; + + nble_gap_sm_config_req(¶ms); + + return 0; +} + +void on_nble_gap_sm_config_rsp(struct nble_gap_sm_config_rsp *p_params) +{ + if (p_params->status) { + BT_ERR("sm_config failed: %d", p_params->status); + } +} + +void on_nble_gap_sm_common_rsp(const struct nble_gap_sm_response *rsp) +{ + if (rsp->status) { + BT_WARN("gap sm request failed: %d", rsp->status); + } +} diff --git a/system/libarc32_arduino101/framework/src/services/ble/uuid.c b/system/libarc32_arduino101/framework/src/services/ble/uuid.c new file mode 100644 index 00000000..dbacfd94 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble/uuid.c @@ -0,0 +1,135 @@ +/* uuid.c - Bluetooth UUID handling */ + +/* + * Copyright (c) 2015 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#include +//#include +#include +#include +#include +#include +#include + +#include + +#if defined(CONFIG_BLUETOOTH_DEBUG) +#include +#endif /* CONFIG_BLUETOOTH_DEBUG */ + +#define UUID_16_BASE_OFFSET 12 + +/* TODO: Decide whether to continue using BLE format or switch to RFC 4122 */ + +/* Base UUID : 0000[0000]-0000-1000-8000-00805F9B34FB -> + * { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + * 0x00, 0x10, 0x00, 0x00, [0x00, 0x00], 0x00, 0x00 } + * 0x2800 : 0000[2800]-0000-1000-8000-00805F9B34FB -> + * { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + * 0x00, 0x10, 0x00, 0x00, [0x00, 0x28], 0x00, 0x00 } + * little endian 0x2800 : [00 28] -> no swapping required + * big endian 0x2800 : [28 00] -> swapping required + */ +static const struct bt_uuid_128 uuid128_base = { + .uuid.type = BT_UUID_TYPE_128, + .val = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +}; + +static inline void u16_to_uuid128(void *dst, uint16_t u16) +{ +#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + u16 = bswap_16(u16); +#endif + memcpy(dst, &u16, 2); +} + +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); + return; + case BT_UUID_TYPE_128: + memcpy(dst, src, sizeof(*dst)); + return; + } +} + +static int uuid128_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2) +{ + struct bt_uuid_128 uuid1, uuid2; + + uuid_to_uuid128(u1, &uuid1); + uuid_to_uuid128(u2, &uuid2); + + return memcmp(uuid1.val, uuid2.val, 16); +} + +int bt_uuid_cmp(const struct bt_uuid *u1, const struct bt_uuid *u2) +{ + /* Convert to 128 bit if types don't match */ + if (u1->type != u2->type) + return uuid128_cmp(u1, u2); + + switch (u1->type) { + case BT_UUID_TYPE_16: + return (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); + } + + return -EINVAL; +} + +#if defined(CONFIG_BLUETOOTH_DEBUG) +void bt_uuid_to_str(const struct bt_uuid *uuid, char *str, size_t len) +{ + uint32_t tmp1, tmp5; + uint16_t tmp0, tmp2, tmp3, tmp4; + + switch (uuid->type) { + case BT_UUID_TYPE_16: + snprintf(str, len, "%.4x", BT_UUID_16(uuid)->val); + break; + case BT_UUID_TYPE_128: + memcpy(&tmp0, &BT_UUID_128(uuid)->val[0], sizeof(tmp0)); + memcpy(&tmp1, &BT_UUID_128(uuid)->val[2], sizeof(tmp1)); + memcpy(&tmp2, &BT_UUID_128(uuid)->val[6], sizeof(tmp2)); + memcpy(&tmp3, &BT_UUID_128(uuid)->val[8], sizeof(tmp3)); + memcpy(&tmp4, &BT_UUID_128(uuid)->val[10], sizeof(tmp4)); + memcpy(&tmp5, &BT_UUID_128(uuid)->val[12], sizeof(tmp5)); + + snprintf(str, len, "%.8x-%.4x-%.4x-%.4x-%.8x%.4x", + tmp5, tmp4, tmp3, tmp2, tmp1, tmp0); + break; + default: + memset(str, 0, len); + return; + } +} + +const char *bt_uuid_str(const struct bt_uuid *uuid) +{ + static char str[37]; + + bt_uuid_to_str(uuid, str, sizeof(str)); + + return str; +} +#endif /* CONFIG_BLUETOOTH_DEBUG */ diff --git a/system/libarc32_arduino101/framework/include/services/ble/ble_service_msg.h b/system/libarc32_arduino101/framework/src/services/ble_service/ble_protocol.h similarity index 70% rename from system/libarc32_arduino101/framework/include/services/ble/ble_service_msg.h rename to system/libarc32_arduino101/framework/src/services/ble_service/ble_protocol.h index 6893731a..51109f9d 100644 --- a/system/libarc32_arduino101/framework/include/services/ble/ble_service_msg.h +++ b/system/libarc32_arduino101/framework/src/services/ble_service/ble_protocol.h @@ -28,23 +28,30 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BLE_SERVICE_MSG_H__ -#define __BLE_SERVICE_MSG_H__ +#ifndef __BLE_PROTOCOL_H__ +#define __BLE_PROTOCOL_H__ -#include "services/services_ids.h" - -/* - * CFW Message ID base definitions for BLE services. +/** + * @defgroup ble_protocol BLE protocol definitions + * + * BT Spec definitions. + * @ingroup ble_service + * @{ + * + * Bluetooth SIG defined macros and enum extracted from Bluetooth Spec 4.2 */ +#define BLE_MAX_DEVICE_NAME 20 /**< Max BLE device name length 20 + NULL, spec size: 248 */ +#define BLE_MAX_ADV_SIZE 31 + +#define BLE_GATT_MTU_SIZE 23 /**< Default MTU size */ + +/** Manufacturer IDs */ +#define INTEL_MANUFACTURER 0x0002 -/* BLE Service Message ID definitions. */ -#define MSG_ID_BLE_BASE BLE_SERVICE_MSG_BASE -#define MSG_ID_BLE_RSP (BLE_SERVICE_MSG_BASE + 0x40) -#define MSG_ID_BLE_EVT (BLE_SERVICE_MSG_BASE + 0x80) +/* HCI status (error) codes as per BT spec */ +#define HCI_REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF 0x15 +#define HCI_LOCAL_HOST_TERMINATED_CONNECTION 0x16 -/* BLE Core Service Message ID base (GAP/GATT) */ -#define MSG_ID_BLE_GAP_BASE BLE_SERVICE_GAP_MSG_BASE -#define MSG_ID_BLE_GAP_RSP (BLE_SERVICE_GAP_MSG_BASE + 0x40) -#define MSG_ID_BLE_GAP_EVT (BLE_SERVICE_GAP_MSG_BASE + 0x80) +/** @} */ #endif 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 new file mode 100644 index 00000000..405c984a --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ble_service.h" +#include "os/os.h" + +//#include "util/assert.h" +#include +//#include +#include "cfw/cfw_service.h" +#include "ble_protocol.h" +#include "ble_service_int.h" +#include "ble_service_internal.h" +#include "infra/log.h" + +#include +#include +#include +#include +#include "gap_internal.h" + +#include "nble_driver.h" + +#include "rpc.h" + +#include "util/misc.h" + +struct _ble_service_cb _ble_cb = { 0 }; +volatile uint8_t ble_inited = false; + + +static void ble_client_connected(conn_handle_t *instance); +static void ble_client_disconnected(conn_handle_t *instance); + +#if defined(CONFIG_BLUETOOTH_SMP) +static const struct bt_conn_auth_cb auth_callbacks; +#endif + +static service_t ble_service = { + .service_id = BLE_SERVICE_ID, + .client_connected = ble_client_connected, + .client_disconnected = ble_client_disconnected, +}; + +static void ble_is_not_enabled_rsp(struct cfw_message *msg, int status) +{ + struct ble_enable_rsp *resp = + (struct ble_enable_rsp *)cfw_alloc_rsp_msg(msg, + /* translate msg from req to rsp */ + (CFW_MESSAGE_ID(msg) ^ MSG_ID_BLE_SERVICE_BASE) + | MSG_ID_BLE_SERVICE_RSP, + sizeof(*resp)); + resp->status = status; + cfw_send_message(resp); +} + +#ifdef CONFIG_TCMD_BLE_DEBUG +static void handle_msg_id_ble_dbg(struct cfw_message *msg) +{ + struct nble_debug_params params; + struct ble_dbg_req_rsp *resp = (void *) + cfw_alloc_rsp_msg(msg, MSG_ID_BLE_DBG_RSP, sizeof(*resp)); + struct ble_dbg_req_rsp *req = (struct ble_dbg_req_rsp *) msg; + + params.u0 = req->u0; + params.u1 = req->u1; + + nble_gap_dbg_req(¶ms, resp); +} +#endif /* CONFIG_TCMD_BLE_DEBUG */ + +void on_nble_gap_dbg_rsp(const struct nble_debug_resp *params) +{ +#ifdef CONFIG_TCMD_BLE_DEBUG + struct ble_dbg_req_rsp *resp = params->user_data; + if (!resp) + return; + resp->u0 = params->u0; + resp->u1 = params->u1; + cfw_send_message(resp); +#endif /* CONFIG_TCMD_BLE_DEBUG */ +} + + +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 */ + rpc_deserialize(rpc->p_data, rpc->len); + bfree(rpc->p_data); + message_free(msg); +} + +static void ble_set_bda_cb(int status, void *user_data) +{ + struct ble_enable_req *req = user_data; + + if (!req) + return; + + struct ble_enable_rsp *resp = (void *)cfw_alloc_rsp_msg(&req->header, + MSG_ID_BLE_ENABLE_RSP, sizeof(*resp)); + resp->status = status; + + if (status == 0) { + resp->enable = 1; + + nble_gap_read_bda_req(resp); + } else { + /* error case */ + resp->enable = 0; + cfw_send_message(resp); + } + bfree(req); +} + + +void on_nble_gap_read_bda_rsp(const struct nble_service_read_bda_response *params) +{ + struct cfw_message *rsp = params->user_data; + + if (rsp) { + struct ble_enable_rsp *r = container_of(rsp, struct ble_enable_rsp, header); + r->bd_addr = params->bd; + cfw_send_message(rsp); + } +} + +static void handle_ble_enable(struct ble_enable_req *req, + struct _ble_service_cb *p_cb) +{ + pr_info(LOG_MODULE_BLE, "ble_enable: state %d", p_cb->ble_state); + + p_cb->ble_state = BLE_ST_ENABLED; + + if (req->bda_present) { + struct nble_set_bda_params params; + + params.cb = ble_set_bda_cb; + params.user_data = req; + params.bda = req->bda; + + nble_set_bda_req(¶ms); + } else { + ble_set_bda_cb(0, req); + } +} + +void on_nble_set_bda_rsp(const struct nble_set_bda_rsp *params) +{ + if (params->cb) { + params->cb(params->status, params->user_data); + } +} + +static void handle_ble_disable(struct ble_enable_req *req, struct _ble_service_cb *p_cb) +{ + struct ble_enable_rsp *resp; + + pr_debug(LOG_MODULE_BLE, "ble_disable"); + p_cb->ble_state = BLE_ST_DISABLED; + + bt_le_adv_stop(); + + resp = (void *)cfw_alloc_rsp_msg(&req->header, + MSG_ID_BLE_ENABLE_RSP, + sizeof(*resp)); + cfw_send_message(resp); + +} + +static void ble_service_message_handler(struct cfw_message *msg, void *param) +{ + bool free_msg = true; + struct _ble_service_cb *p_cb = param; + uint16_t msg_id = CFW_MESSAGE_ID(msg); + + if (p_cb->ble_state < BLE_ST_ENABLED && + msg_id != MSG_ID_BLE_ENABLE_REQ) { + ble_is_not_enabled_rsp(msg, -ENODEV); + goto out; + } + + switch (msg_id) { + case MSG_ID_BLE_ENABLE_REQ: { + struct ble_enable_req *req = + container_of(msg, struct ble_enable_req, header); + if (p_cb->ble_state) { + if (req->enable) { + handle_ble_enable(req, p_cb); + free_msg = false; + } else + handle_ble_disable(req, p_cb); + } else { + pr_debug(LOG_MODULE_BLE, "ble_hdl_msg: core service not opened!"); + /* core service is not yet up */ + struct ble_enable_rsp *resp = (void *)cfw_alloc_rsp_msg(msg, + MSG_ID_BLE_ENABLE_RSP, sizeof(*resp)); + resp->status = -EINPROGRESS; + resp->enable = 0; + cfw_send_message(resp); + } + } + break; +#ifdef CONFIG_TCMD_BLE_DEBUG + case MSG_ID_BLE_DBG_REQ: + handle_msg_id_ble_dbg(msg); + break; +#endif + default: + pr_warning(LOG_MODULE_BLE, "unsupported %d", msg_id); + break; + } +out: + if (free_msg) + cfw_msg_free(msg); +} + +static void ble_client_connected(conn_handle_t *instance) +{ + if (_ble_cb.ble_state == BLE_ST_NOT_READY) + pr_warning(LOG_MODULE_BLE, "BLE_CORE service is not registered"); +} + +static void ble_client_disconnected(conn_handle_t *instance) +{ +} + +void ble_bt_rdy(int err) +{ + _ble_cb.ble_state = BLE_ST_DISABLED; + ble_inited = true; + + /* register BLE service */ + if (cfw_register_service(_ble_cb.queue, &ble_service, + ble_service_message_handler, &_ble_cb) == -1) { + panic(0xb1eb1e); + } +} + +void ble_cfw_service_init(int service_id, T_QUEUE queue) +{ + _ble_cb.queue = queue; + _ble_cb.ble_state = BLE_ST_NOT_READY; + +#ifdef CONFIG_IPC_UART_NS16550 + nble_driver_configure(queue, handle_msg_id_ble_rpc_callin); +#endif + + ble_inited = false; + + bt_enable(ble_bt_rdy); + do{} + while (ble_inited == false); +} + +void nble_log(const struct nble_log_s *param, char *buf, uint8_t buflen) +{ + pr_info(LOG_MODULE_BLE, buf, param->param0, param->param1, param->param2, param->param3); +} + +void on_nble_common_rsp(const struct nble_response *params) +{ + struct ble_rsp *resp = params->user_data; + + if (!resp) + return; + resp->status = params->status; + cfw_send_message(resp); +} diff --git a/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_api.c b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_api.c new file mode 100644 index 00000000..e70b15c8 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_api.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ble_service.h" + +#include +#include +//#include +//#include "util/assert.h" + +#include "ble_service_int.h" +#include "ble_service_internal.h" +#include "cfw/cfw_service.h" +#include "cfw/cfw_client.h" + +#include +#include + +#include "ble_protocol.h" +#include "ble_service_utils.h" + +int ble_service_enable(cfw_service_conn_t * p_service_conn, uint8_t enable, + const struct ble_enable_config * p_config, + void *p_priv) +{ + struct ble_enable_req * msg = + (void *) cfw_alloc_message_for_service(p_service_conn, + MSG_ID_BLE_ENABLE_REQ, + sizeof(*msg), p_priv); + msg->central_conn_params = p_config->central_conn_params; + msg->enable = enable; + + if (p_config->p_bda) { + msg->bda_present = 1; + msg->bda = *p_config->p_bda; + } + + return cfw_send_message(msg); +} diff --git a/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_int.h b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_int.h new file mode 100644 index 00000000..857270c7 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_int.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BLE_SERVICE_INT_H_ +#define BLE_SERVICE_INT_H_ + +#include + +/* For cfw_service_conn_t, cfw_client_t, T_QUEUE */ +#include "cfw/cfw.h" + +/* Forward declarations */ +struct ble_init_svc_req; +struct ble_init_svc_rsp; +struct bt_conn_cb; +struct bt_gatt_attr; + +enum BLE_STATE { + BLE_ST_NOT_READY = 0, + BLE_ST_DISABLED, + BLE_ST_ENABLED, + BLE_ST_DTM +}; + +struct _ble_service_cb { + T_QUEUE queue; /* Queue for the messages */ + uint8_t ble_state; +}; + +extern struct _ble_service_cb _ble_cb; + +/** Send advertisement timeout event to application */ +void ble_gap_advertisement_timeout(void); + +#ifdef CONFIG_BLE_CORE_TEST +void test_ble_service_init(void); +T_QUEUE get_service_queue(void); +#endif + +#endif /* BLE_SERVICE_INT_H_ */ diff --git a/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_internal.h b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_internal.h new file mode 100644 index 00000000..013043a5 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_internal.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __BLE_SERVICE_INTERNAL_H__ +#define __BLE_SERVICE_INTERNAL_H__ + +// For MSG_ID_BLE_SERVICE_BASE +#include "services/services_ids.h" + +/* BLE High level Message IDs used for request, response, events. */ +enum BLE_MSG_ID_REQ { + MSG_ID_BLE_ENABLE_REQ = MSG_ID_BLE_SERVICE_BASE, /* Message ID for enable request */ + MSG_ID_BLE_SET_NAME_REQ, /* Message ID for set name request */ + MSG_ID_BLE_START_SCAN_REQ, /* Message ID for start scan request */ + MSG_ID_BLE_STOP_SCAN_REQ, /* Message ID for stop scan request */ + MSG_ID_BLE_INIT_SVC_REQ, /* Message ID for init service request */ + + /* BLE debug command */ + MSG_ID_BLE_DBG_REQ, /* Message ID for BLE debug command request */ + + MSG_ID_BLE_REQ_LAST, +}; + +/* Parameters of MSG_ID_BLE_ENABLE_REQ. */ +struct ble_enable_req { + struct cfw_message header; /* Component framework message header, MUST be first element of structure */ + struct bt_le_conn_param central_conn_params; + uint8_t enable; + uint8_t bda_present; + bt_addr_le_t bda; +}; + +struct ble_disconnect_req { + struct cfw_message header; /* Component framework message header, MUST be first element of structure */ + struct bt_conn *conn; /* Connection reference */ + uint8_t reason; /* Reason of the disconnect*/ +}; + +struct ble_connect_req { + struct cfw_message header; /* Component framework message header, MUST be first element of structure */ + struct bt_le_conn_param conn_params; + bt_addr_le_t bd_addr; +}; + +struct ble_init_svc_req { + struct cfw_message header; /* Component framework message header, MUST be first element of structure */ + int (*init_svc)(struct ble_init_svc_req *msg, struct _ble_service_cb *p_cb); + void (*init_svc_complete)(struct ble_init_svc_req *req); + uint8_t status; +}; + +#endif /* __BLE_SERVICE_INTERNAL_H__ */ diff --git a/system/libarc32_arduino101/framework/src/services/ble/ble_service_utils.c b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_utils.c similarity index 50% rename from system/libarc32_arduino101/framework/src/services/ble/ble_service_utils.c rename to system/libarc32_arduino101/framework/src/services/ble_service/ble_service_utils.c index d40c1b72..0275e79b 100644 --- a/system/libarc32_arduino101/framework/src/services/ble/ble_service_utils.c +++ b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_utils.c @@ -31,77 +31,48 @@ #include #include "os/os.h" #include "ble_protocol.h" -#include "services/ble/ble_service_gap_api.h" +#include "ble_service.h" +#include "ble_service_int.h" #include "ble_service_utils.h" -uint8_t ble_sizeof_bt_uuid(const struct bt_uuid * p_uuid) +void uint8_to_ascii(uint8_t in, uint8_t *p) { - return p_uuid->type + 1; + uint8_t hi = (in & 0xF0) >> 4; + uint8_t lo = in & 0x0F; + + if (hi < 0x0A) + *p = '0' + hi; + else + *p = 'A' + (hi - 0x0A); + + p++; + + if (lo < 10) + *p = '0' + lo; + else + *p = 'A' + (lo - 0x0A); } -uint8_t * ble_encode_bt_uuid(const struct bt_uuid * p_uuid, uint8_t * p_data) +void uint8buf_to_ascii(uint8_t * dst, const uint8_t * src, int len) { - // start with the type (length) - UINT8_TO_LESTREAM(p_data, p_uuid->type); - switch (p_uuid->type) { - case BT_UUID16: - UINT16_TO_LESTREAM(p_data, p_uuid->uuid16); - break; - case BT_UUID32: - UINT32_TO_LESTREAM(p_data, p_uuid->uuid32); - break; - case BT_UUID128: - memcpy(p_data, p_uuid->uuid128, MAX_UUID_SIZE); - p_data += MAX_UUID_SIZE; - break; + int i; + for (i = 0; i < len; ++i) { + uint8_to_ascii(src[i], dst); + dst += 2; } - return p_data; } -uint8_t ble_cpy_bt_uuid(uint8_t * p_uuid_dst, - const struct bt_uuid * p_uuid_src, - uint8_t type) +size_t adv_data_len(const struct bt_data *ad, size_t count) { - uint8_t *p = p_uuid_dst; - uint8_t res_type = (p_uuid_src->type >= type) ? type : 0; + int i; + size_t data_len = 0; - switch (res_type) { - case BT_UUID16: - switch (p_uuid_src->type) { - case BT_UUID16: - UINT16_TO_LESTREAM(p, p_uuid_src->uuid16); - break; - case BT_UUID32: - UINT16_TO_LESTREAM(p, p_uuid_src->uuid16); - break; - case BT_UUID128: { - uint16_t uuid16; - p = (uint8_t *)&p_uuid_src->uuid128[BLE_BASE_UUID_OCTET_OFFSET]; - LESTREAM_TO_UINT16(p, uuid16); - p = p_uuid_dst; - UINT16_TO_LESTREAM(p, p_uuid_src->uuid16); - break; - } - } - break; - case BT_UUID32: - switch (p_uuid_src->type) { - case BT_UUID32: - UINT32_TO_LESTREAM(p, p_uuid_src->uuid32); - break; - case BT_UUID128: { - uint32_t uuid32; - p = (uint8_t *)&p_uuid_src->uuid128[BLE_BASE_UUID_OCTET_OFFSET]; - LESTREAM_TO_UINT32(p, uuid32); - p = p_uuid_dst; - UINT32_TO_LESTREAM(p, uuid32); - break; - } - } - break; - case BT_UUID128: - memcpy(p_uuid_dst, p_uuid_src->uuid128, MAX_UUID_SIZE); - break; + if (ad == NULL) + return 0; + + for (i = 0; i < count ; i++) { + data_len += ad[i].data_len; } - return res_type; + + return data_len; } diff --git a/system/libarc32_arduino101/framework/src/services/ble/ble_service_utils.h b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_utils.h similarity index 66% rename from system/libarc32_arduino101/framework/src/services/ble/ble_service_utils.h rename to system/libarc32_arduino101/framework/src/services/ble_service/ble_service_utils.h index a51f8dcd..8d5d9378 100644 --- a/system/libarc32_arduino101/framework/src/services/ble/ble_service_utils.h +++ b/system/libarc32_arduino101/framework/src/services/ble_service/ble_service_utils.h @@ -28,20 +28,19 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BLE_SERVICE_UTILS_H__ -#define __BLE_SERVICE_UTILS_H__ +#ifndef BLE_SERVICE_UTILS_H_ +#define BLE_SERVICE_UTILS_H_ -#include "services/ble/ble_service_gap_api.h" -#include "services/ble/ble_service_gatt.h" -//#include "ble_service_int.h" +#include -#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) + __must_be_array(a) +// For HAVE_SAME_TYPE +#include "compiler.h" #define IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #define UINT8_TO_LESTREAM(p, i) \ do { *(p)++ = (uint8_t)(i); } \ - while (sizeof(char[1 - 2 * !(__builtin_types_compatible_p(__typeof__(p), uint8_t *))]) - 1) + while (sizeof(char[1 - 2 * !(HAVE_SAME_TYPE(p, uint8_t *))]) - 1) #define UINT16_TO_LESTREAM(p, i) UINT8_TO_LESTREAM(p, i); UINT8_TO_LESTREAM(p, (i)>>8) #define UINT32_TO_LESTREAM(p, i) UINT16_TO_LESTREAM(p, i); UINT16_TO_LESTREAM(p, (i)>>16) @@ -59,8 +58,8 @@ #define LESTREAM_TO_UINT8(p, i) \ do { i = *p; p++; } \ - while (sizeof(char[1 - 2 * !(__builtin_types_compatible_p(__typeof__(p), const uint8_t *) || \ - __builtin_types_compatible_p(__typeof__(p), uint8_t *))]) - 1) + while (sizeof(char[1 - 2 * !(HAVE_SAME_TYPE(p, const uint8_t *) || \ + HAVE_SAME_TYPE(p, uint8_t *))]) - 1) #define LESTREAM_TO_UINT16(p, i) \ do { uint16_t temp16; LESTREAM_TO_UINT8(p, i); LESTREAM_TO_UINT8(p, temp16); i |= (temp16 << 8); } \ while (0) @@ -90,53 +89,38 @@ #define BESTREAM_TO_INT32(p, i) \ do {uint32_t __i; BESTREAM_TO_UINT32(p, __i); i = (int32_t)__i; } while (0) -#define between(a, b, c) (((a) >= (b)) || ((a) <= (c))) - -#define strict_between(a, b, c) (((a) > (b)) || ((a) < (c))) - /** * BLE helper functions. */ -#define BLE_BASE_UUID_OCTET_OFFSET 12 -/** - * Compute the size required to encode a UUID in a buffer +/**@brief Represent an unsigned 8 bit value in ASCII hexadecimal + * + * @param[in] in The value to represent + * @param[inout] p Pointer to the buffer to fill * - * @param p_uuid Pointer to the UUID + * @note The representation requires 2 bytes */ -uint8_t ble_sizeof_bt_uuid(const struct bt_uuid * p_uuid); +void uint8_to_ascii(uint8_t in, uint8_t * p); -/** - * Encode a UUID in a buffer +/** Converts buffer from hex to ascii. * - * @param p_uuid Pointer to the UUID to encode - * @param p_data Pointer to the buffer to store the encoded UUID + * @param dst buffer converted + * @param src buffer which is converted + * @param len length of src (dst shall be large enough) * - * @return The pointer to the buffer after the encoded UUID + * @return None */ -uint8_t * ble_encode_bt_uuid(const struct bt_uuid * p_uuid, uint8_t * p_data); +void uint8buf_to_ascii(uint8_t * dst, const uint8_t * src, int len); -/** Copy BT/BLE address and eventually reduce size. - * - * Copies a uuid from src to dst. type may modify the resulting uuid type. only - * going from bigger to small type is supported. typically 128 bits to 16 bits - * If it is different from src type, a transformation is applied: - * IN: BT_UUID128: dd97c415-fed9-4766-b18f-ba690d24a06a (stored in little endian) - * OUT: BT_UUID16: c415 - * OUT: BT_UUID32: dd97c415 - * or - * IN: BT_UUID32: dd97c415 - * OUT: BT_UUID16: c415 - * - * @param p_uuid_src source uuid - * @param[out] p_uuid_dst: destination to copy uuid to (excluding type). little endian - * @param type output type +struct bt_data; +/** + * Get advertisement data length. * - * \return size/type of copied uuid: BT_UUID16, BT_UUID32, BT_UUID128 or 0 for failure! + * @param ad advertisement data + * @param count array length + * @return length of advertisement data * */ -uint8_t ble_cpy_bt_uuid(uint8_t * p_uuid_dst, - const struct bt_uuid * p_uuid_src, - uint8_t type); +size_t adv_data_len(const struct bt_data *ad, size_t count); -#endif +#endif /* BLE_SERVICE_UTILS_H_ */ diff --git a/system/libarc32_arduino101/framework/src/services/ble_service/gap_internal.h b/system/libarc32_arduino101/framework/src/services/ble_service/gap_internal.h new file mode 100644 index 00000000..333ad36e --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble_service/gap_internal.h @@ -0,0 +1,629 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GAP_INTERNAL_H_ +#define GAP_INTERNAL_H_ + +#include +/* For bt_addr_le_t */ +#include +/* For bt_security_t */ +#include + +/* Directed advertisement timeout error after 1.28s */ +#define ERR_DIRECTED_ADVERTISING_TIMEOUT 0x3C + +enum NBLE_GAP_SM_PASSKEY_TYPE { + NBLE_GAP_SM_REJECT = 0, + NBLE_GAP_SM_PK_PASSKEY, + NBLE_GAP_SM_PK_OOB, +}; + +enum NBLE_GAP_SM_EVT { + NBLE_GAP_SM_EVT_START_PAIRING, + NBLE_GAP_SM_EVT_BONDING_COMPLETE, + NBLE_GAP_SM_EVT_LINK_ENCRYPTED, + NBLE_GAP_SM_EVT_LINK_SECURITY_CHANGE, +}; + +enum NBLE_GAP_RSSI_OPS { + NBLE_GAP_RSSI_DISABLE_REPORT = 0, + NBLE_GAP_RSSI_ENABLE_REPORT +}; + +enum NBLE_TEST_OPCODES { + NBLE_TEST_INIT_DTM = 0x01, + NBLE_TEST_START_DTM_RX = 0x1d, + NBLE_TEST_START_DTM_TX = 0x1e, + NBLE_TEST_END_DTM = 0x1f, + /* vendor specific commands start at 0x80 */ + /* Set Tx power. To be called before start of tx test */ + NBLE_TEST_SET_TXPOWER = 0x80, + NBLE_TEST_START_TX_CARRIER, +}; + +/* DTM commands, opcodes, indexes. */ +#define H4_CMD 0x01 +#define HCI_OGF_LE_CMD 0x20 + +#define DTM_HCI_STATUS_IDX 6 +#define DTM_HCI_LE_END_IDX (DTM_HCI_STATUS_IDX + 1) + + +struct nble_response { + int status; + void *user_data; +}; + +struct nble_gap_device_name { + /* Security mode for writing device name, @ref BLE_GAP_SEC_MODES */ + uint8_t sec_mode; + /* 0: no authorization, 1: authorization required */ + uint8_t authorization; + /* Device name length (0-248) */ + uint8_t len; + uint8_t name_array[20]; +}; + +struct nble_gap_connection_values { + /* Connection interval (unit 1.25 ms) */ + uint16_t interval; + /* Connection latency (unit interval) */ + uint16_t latency; + /* Connection supervision timeout (unit 10ms)*/ + uint16_t supervision_to; +}; + + +enum NBLE_GAP_SVC_ATTR_TYPE { + /* Device Name, UUID 0x2a00 */ + NBLE_GAP_SVC_ATTR_NAME = 0, + /* Appearance, UUID 0x2a01 */ + NBLE_GAP_SVC_ATTR_APPEARANCE, + /* Peripheral Preferred Connection Parameters (PPCP), UUID 0x2a04 */ + NBLE_GAP_SVC_ATTR_PPCP = 4, + /* Central Address Resolution (CAR), UUID 0x2aa6, BT 4.2 */ + NBLE_GAP_SVC_ATTR_CAR = 0xa6, +}; + +struct nble_gap_connection_params { + /* minimal connection interval: range 0x0006 to 0x0c80 (unit 1.25ms) */ + uint16_t interval_min; + /* maximum connection interval: range 0x0006 to 0x0c80 must be bigger then min! */ + uint16_t interval_max; + /* maximum connection slave latency: 0x0000 to 0x01f3 */ + uint16_t slave_latency; + /* link supervision timeout: 0x000a to 0x0c80 (unit 10ms) */ + uint16_t link_sup_to; +}; + +struct nble_gap_scan_parameters { + /* If 1, perform active scanning (scan requests). */ + uint8_t active; + /* If 1, ignore unknown devices (non whitelisted). */ + uint8_t selective; + /* Scan interval between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */ + uint16_t interval; + /* Scan window between 0x0004 and 0x4000 in 0.625ms units (2.5ms to 10.24s). */ + uint16_t window; + /* Scan timeout between 0x0001 and 0xFFFF in seconds, 0x0000 disables timeout. */ + uint16_t timeout; +}; + +struct nble_gap_service_write_params { + /* GAP Characteristics attribute type @ref BLE_GAP_SVC_ATTR_TYPE */ + uint16_t attr_type; + union { + struct nble_gap_device_name name; + /* Appearance UUID */ + uint16_t appearance; + /* Preferred Peripheral Connection Parameters */ + struct nble_gap_connection_params conn_params; + /* Central Address Resolution support 0: no, 1: yes */ + uint8_t car; + }; +}; + +struct nble_service_read_bda_response { + int status; + /* If @ref status ok */ + bt_addr_le_t bd; + void *user_data; +}; + +struct nble_service_write_response { + int status; + /* GAP Characteristics attribute type @ref BLE_GAP_SVC_ATTR_TYPE */ + uint16_t attr_type; + void *user_data; +}; + +struct nble_gap_service_read_params { + /* Type of GAP data characteristic to read @ref BLE_GAP_SVC_ATTR_TYPE */ + uint16_t attr_type; +}; + +struct nble_debug_params { + uint32_t u0; + uint32_t u1; +}; + +struct nble_debug_resp { + int status; + uint32_t u0; + uint32_t u1; + void *user_data; +}; + +typedef void (*nble_set_bda_cb_t)(int status, void *user_data); + +struct nble_set_bda_params { + bt_addr_le_t bda; + nble_set_bda_cb_t cb; + void *user_data; +}; + +struct nble_set_bda_rsp { + nble_set_bda_cb_t cb; + void *user_data; + int status; +}; + +struct bt_eir_data { + uint8_t len; + uint8_t data[31]; +}; + +struct nble_gap_adv_params { + uint16_t timeout; + /* min interval 0xffff: use default 0x0800 */ + uint16_t interval_min; + /* max interval 0xffff: use default 0x0800 */ + uint16_t interval_max; + /* advertisement types @ref GAP_ADV_TYPES */ + uint8_t type; + /* filter policy to apply with white list */ + uint8_t filter_policy; + /* bd address of peer device in case of directed advertisement */ + bt_addr_le_t peer_bda; +}; + +struct nble_gap_ad_data_params { + /* Advertisement data, maybe 0 (length) */ + struct bt_eir_data ad; + /* Scan response data, maybe 0 (length) */ + struct bt_eir_data sd; +}; + +struct nble_log_s { + uint8_t param0; + uint8_t param1; + uint8_t param2; + uint8_t param3; +}; + +/* bt_dev flags: the flags defined here represent BT controller state */ +enum { + BT_DEV_READY, + + BT_DEV_ADVERTISING, + BT_DEV_KEEP_ADVERTISING, + BT_DEV_SCANNING, + BT_DEV_EXPLICIT_SCAN, + +#if defined(CONFIG_BLUETOOTH_BREDR) + BT_DEV_ISCAN, + BT_DEV_PSCAN, +#endif /* CONFIG_BLUETOOTH_BREDR */ +}; + +void nble_log(const struct nble_log_s *param, char *buf, uint8_t buflen); + +void on_nble_up(void); + +void nble_gap_service_write_req(const struct nble_gap_service_write_params *params); + +void on_nble_gap_read_bda_rsp(const struct nble_service_read_bda_response *params); + +void nble_gap_dbg_req(const struct nble_debug_params *params, void *user_data); + +void on_nble_gap_dbg_rsp(const struct nble_debug_resp *params); + +void on_nble_set_bda_rsp(const struct nble_set_bda_rsp *params); + +void nble_set_bda_req(const struct nble_set_bda_params *params); + +void nble_gap_set_adv_data_req(struct nble_gap_ad_data_params *ad_data_params); + +void nble_gap_set_adv_params_req(struct nble_gap_adv_params *adv_params); + +void nble_gap_start_adv_req(void); + +void on_nble_gap_start_advertise_rsp(const struct nble_response *params); + +void nble_gap_stop_adv_req(void *user_data); + +void nble_gap_read_bda_req(void *priv); + +struct nble_gap_irk_info { + /* Identity Resolving Key (IRK) */ + uint8_t irk[16]; +}; + +void on_nble_common_rsp(const struct nble_response *params); + +struct nble_gap_connect_update_params { + uint16_t conn_handle; + struct nble_gap_connection_params params; +}; + +void nble_gap_conn_update_req(const struct nble_gap_connect_update_params *params); + +void on_nble_gap_conn_update_rsp(const struct nble_response *params); + +struct nble_gap_connect_req_params { + bt_addr_le_t bda; + struct nble_gap_connection_params conn_params; + struct nble_gap_scan_parameters scan_params; +}; + +struct nble_gap_disconnect_req_params { + uint16_t conn_handle; + uint8_t reason; +}; + +void nble_gap_disconnect_req(const struct nble_gap_disconnect_req_params *params); + +struct nble_gap_sm_config_params { + /* Security options (@ref BLE_GAP_SM_OPTIONS) */ + uint8_t options; + /* I/O Capabilities to allow passkey exchange (@ref BLE_GAP_IO_CAPABILITIES) */ + uint8_t io_caps; + /* Maximum encryption key size (7-16) */ + uint8_t key_size; + uint8_t oob_present; +}; + +void nble_gap_sm_config_req(const struct nble_gap_sm_config_params *params); + +struct nble_gap_sm_config_rsp { + void *user_data; + int status; + bool sm_bond_dev_avail; +}; + +void on_nble_gap_sm_config_rsp(struct nble_gap_sm_config_rsp *params); + + +struct nble_gap_sm_pairing_params { + /* authentication level see @ref BLE_GAP_SM_OPTIONS */ + uint8_t auth_level; +}; + +struct nble_gap_sm_security_params { + struct bt_conn *conn; + uint16_t conn_handle; + /* Local authentication/bonding parameters */ + struct nble_gap_sm_pairing_params params; +}; + +void nble_gap_sm_security_req(const struct nble_gap_sm_security_params * + params); + +struct nble_gap_sm_passkey { + uint8_t type; + union { + uint32_t passkey; + uint8_t oob[16]; + uint8_t reason; + }; +}; + +struct nble_gap_sm_key_reply_req_params { + struct bt_conn *conn; + uint16_t conn_handle; + struct nble_gap_sm_passkey params; +}; + +void nble_gap_sm_passkey_reply_req(const struct nble_gap_sm_key_reply_req_params + *params); + +struct nble_gap_sm_clear_bond_req_params { + bt_addr_le_t addr; +}; + +void nble_gap_sm_clear_bonds_req(const struct nble_gap_sm_clear_bond_req_params + *params); + +struct nble_gap_sm_response { + int status; + struct bt_conn *conn; +}; + +void on_nble_gap_sm_common_rsp(const struct nble_gap_sm_response *rsp); + +/** + * Callback for rssi event. + */ +typedef void (*rssi_report_t)(const int8_t *rssi_data); + +/** + * Callback for rssi report response. + */ +typedef void (*rssi_report_resp_t)(int status); + +struct nble_rssi_report_params { + uint16_t conn_handle; + /* RSSI operation @ref NBLE_GAP_RSSI_OPS */ + uint8_t op; + /* Channel for RSSI enabling */ + uint8_t channel; + /* minimum RSSI dBm change to report a new RSSI value */ + uint8_t delta_dBm; + /* number of delta_dBm changes before sending a new RSSI report */ + uint8_t min_count; +}; + +void ble_gap_set_rssi_report(struct nble_rssi_report_params *params, + struct bt_conn *conn, + rssi_report_resp_t resp_cb, rssi_report_t evt_cb); + +void nble_gap_set_rssi_report_req(const struct nble_rssi_report_params *params, + void *user_data); + +void on_nble_gap_set_rssi_report_rsp(const struct nble_response *params); + +struct nble_gap_scan_params { + uint16_t interval; + uint16_t window; + uint8_t scan_type; + uint8_t use_whitelist; +}; + +void nble_gap_start_scan_req(const struct nble_gap_scan_params *params); + +void nble_gap_stop_scan_req(void); + +void on_nble_gap_scan_start_stop_rsp(const struct nble_response *rsp); + +void nble_gap_connect_req(const struct nble_gap_connect_req_params *params, + void *user_data); + +void on_nble_gap_connect_rsp(const struct nble_response *params); + +void nble_gap_cancel_connect_req(void *priv); + +void on_nble_gap_cancel_connect_rsp(const struct nble_response *params); + +enum BLE_GAP_SET_OPTIONS { + BLE_GAP_SET_CH_MAP = 0, +}; + +struct nble_gap_channel_map { + /* connection on which to change channel map */ + uint16_t conn_handle; + /* 37 bits are used of the 40 bits (LSB) */ + uint8_t map[5]; +}; + + +struct nble_gap_set_option_params { + /* Option to set @ref BLE_GAP_SET_OPTIONS */ + uint8_t op; + union { + struct nble_gap_channel_map ch_map; + }; +}; + +/* + * Generic request op codes. + * This allows to access some non connection related commands like DTM. + */ +enum BLE_GAP_GEN_OPS { + /* Not used now. */ + DUMMY_VALUE = 0, +}; + +struct nble_gap_gen_cmd_params { + /* @ref BLE_GAP_GEN_OPS */ + uint8_t op_code; +}; + +/* Temporary patch: RSSI processing for UAS */ +struct nble_uas_rssi_calibrate { + float distance; +}; +void nble_uas_rssi_calibrate_req(const struct nble_uas_rssi_calibrate *p_struct); + +/* Temporary patch: RSSI processing for UAS */ +struct nble_uas_bucket_change { + uint8_t distance; +}; +void on_nble_uas_bucket_change(const struct nble_uas_bucket_change *p_params); + +struct nble_version { + uint8_t version; + uint8_t major; + uint8_t minor; + uint8_t patch; + char version_string[20]; + uint8_t hash[4]; +}; + +typedef void (*ble_get_version_cb_t)(const struct nble_version *ver); + +struct nble_gap_get_version_param { + ble_get_version_cb_t cb; +}; + +struct nble_version_response { + struct nble_gap_get_version_param params; + struct nble_version ver; +}; + +void nble_get_version_req(const struct nble_gap_get_version_param *params); + +void on_nble_get_version_rsp(const struct nble_version_response *params); + +void nble_gap_dtm_init_req(void *user_data); + +void on_nble_gap_dtm_init_rsp(void *user_data); + +struct nble_gap_tx_power_params { + int8_t tx_power; +}; + +void nble_gap_tx_power_req(const struct nble_gap_tx_power_params *params); + +void on_nble_gap_tx_power_rsp(const struct nble_response *params); + +struct nble_gap_connect_evt { + uint16_t conn_handle; + struct nble_gap_connection_values conn_values; + /* 0 if connected as master, otherwise as slave */ + uint8_t role_slave; + /* Address of peer device */ + bt_addr_le_t peer_bda; +}; + +void on_nble_gap_connect_evt(const struct nble_gap_connect_evt *evt); + +struct nble_gap_disconnect_evt { + uint16_t conn_handle; + uint8_t hci_reason; +}; + +void on_nble_gap_disconnect_evt(const struct nble_gap_disconnect_evt *evt); + +struct nble_gap_conn_update_evt { + uint16_t conn_handle; + struct nble_gap_connection_values conn_values; +}; + +void on_nble_gap_conn_update_evt(const struct nble_gap_conn_update_evt *evt); + +struct nble_gap_adv_report_evt { + bt_addr_le_t addr; + int8_t rssi; + uint8_t adv_type; +}; + +void on_nble_gap_adv_report_evt(const struct nble_gap_adv_report_evt *evt, + const uint8_t *buf, uint8_t len); + +struct nble_gap_dir_adv_timeout_evt { + uint16_t conn_handle; + uint16_t error; +}; + +void on_nble_gap_dir_adv_timeout_evt(const struct nble_gap_dir_adv_timeout_evt *p_evt); + +#define BLE_GAP_RSSI_EVT_SIZE 32 +struct nble_gap_rssi_evt { + uint16_t conn_handle; + int8_t rssi_data[BLE_GAP_RSSI_EVT_SIZE]; +}; + +void on_nble_gap_rssi_evt(const struct nble_gap_rssi_evt *evt); + +struct nble_gap_timout_evt { + uint16_t conn_handle; + /* reason for timeout @ref BLE_SVC_GAP_TIMEOUT_REASON */ + int reason; +}; + +struct nble_gap_sm_passkey_req_evt { + uint16_t conn_handle; + uint8_t key_type; +}; + +void on_nble_gap_sm_passkey_req_evt(const struct nble_gap_sm_passkey_req_evt *evt); + +struct nble_gap_sm_passkey_disp_evt { + uint16_t conn_handle; + uint32_t passkey; +}; + +void on_nble_gap_sm_passkey_display_evt(const struct nble_gap_sm_passkey_disp_evt *evt); + +struct nble_link_sec { + bt_security_t sec_level; + uint8_t enc_size; +}; + +struct nble_gap_sm_status_evt { + uint16_t conn_handle; + uint8_t evt_type; + int status; + struct nble_link_sec enc_link_sec; +}; + +void on_nble_gap_sm_status_evt(const struct nble_gap_sm_status_evt *evt); + +void on_nble_set_bda_rsp(const struct nble_set_bda_rsp *params); + +enum BLE_INFO_REQ_TYPES { + BLE_INFO_BONDING = 1, /* Get bonding database related information */ + BLE_INFO_LAST /* Keep last */ +}; + +struct ble_gap_bonded_dev_info +{ + uint8_t addr_count; /* Count of le_addr in array. */ + uint8_t irk_count; /* IRK count */ + bt_addr_le_t le_addr[]; /* Bonded device address */ +}; + +struct ble_get_info_rsp { + int status; /* Response status */ + uint8_t info_type; /* Requested information type */ + struct ble_gap_bonded_dev_info info_params; +}; + +struct nble_gap_sm_bond_info; +typedef void (*ble_bond_info_cb_t)(const struct nble_gap_sm_bond_info *info, + const bt_addr_le_t *addr, uint16_t len, + void *user_data); + +struct nble_gap_sm_bond_info_param { + ble_bond_info_cb_t cb; + void *user_data; + bool include_bonded_addrs; +}; + +void nble_gap_sm_bond_info_req(const struct nble_gap_sm_bond_info_param *params); + +struct nble_gap_sm_bond_info { + int err; + uint8_t addr_count; + uint8_t irk_count; +}; + +struct nble_gap_sm_bond_info_rsp { + ble_bond_info_cb_t cb; + void *user_data; + struct nble_gap_sm_bond_info info; +}; + +void on_nble_gap_sm_bond_info_rsp(const struct nble_gap_sm_bond_info_rsp *rsp, + const bt_addr_le_t *peer_addr, uint16_t len); + +void ble_gap_get_bonding_info(ble_bond_info_cb_t func, void *user_data, + bool include_bonded_addrs); + +void ble_gap_get_version(ble_get_version_cb_t func); + +#endif /* GAP_INTERNAL_H_ */ diff --git a/system/libarc32_arduino101/framework/src/services/ble_service/gatt_internal.h b/system/libarc32_arduino101/framework/src/services/ble_service/gatt_internal.h new file mode 100644 index 00000000..25ac4da8 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble_service/gatt_internal.h @@ -0,0 +1,308 @@ +/* + * Copyright (c) 2016 Intel Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GATT_INTERNAL_H_ +#define GATT_INTERNAL_H_ + +#include +#include + +/* Forward declarations */ +struct nble_response; + +#define BLE_GATT_MTU_SIZE 23 + +/* + * Internal APIs used between host and BLE controller + * Typically they are required if gatt.h APIs can not be mapped 1:1 onto controller API + */ + +enum NBLE_GATT_IND_TYPES { + NBLE_GATT_IND_TYPE_NONE = 0, + NBLE_GATT_IND_TYPE_NOTIFICATION, + NBLE_GATT_IND_TYPE_INDICATION, +}; + +struct nble_gatt_register_req { + /* Base address of the attribute table in the Quark mem space */ + struct bt_gatt_attr *attr_base; + /* Number of of attributes in this service */ + uint8_t attr_count; +}; + +struct nble_gatt_register_rsp { + int status; + struct bt_gatt_attr *attr_base; + /* Number of attributes successfully added */ + uint8_t attr_count; +}; + +enum nble_gatt_wr_flag { + NBLE_GATT_WR_FLAG_REPLY = 1, + NBLE_GATT_WR_FLAG_PREP = 2, +}; + +struct nble_gatt_wr_evt { + struct bt_gatt_attr *attr; + uint16_t conn_handle; + uint16_t offset; + uint8_t flag; /* Cf. enum nble_gatt_wr_flag */ +}; + +struct nble_gatt_wr_exec_evt { + uint16_t conn_handle; + uint8_t flag; +}; + +struct nble_gatt_rd_evt { + struct bt_gatt_attr *attr; + uint16_t conn_handle; + uint16_t offset; +}; + +struct nble_gatts_rd_reply_params { + uint16_t conn_handle; + uint16_t offset; + int32_t status; +}; + +struct nble_gatts_wr_reply_params { + uint16_t conn_handle; + int32_t status; +}; + +struct nble_gatt_notif_ind_params { + const struct bt_gatt_attr *attr; + uint16_t offset; +}; + +struct nble_gatt_send_notif_params { + /* Function to be invoked when buffer is freed */ + bt_gatt_notify_sent_func_t cback; + uint16_t conn_handle; + struct nble_gatt_notif_ind_params params; +}; + +struct nble_gatt_notif_rsp { + bt_gatt_notify_sent_func_t cback; + int status; + uint16_t conn_handle; + struct bt_gatt_attr *attr; +}; + +struct nble_gatt_send_ind_params { + /* Function to be invoked when buffer is freed */ + bt_gatt_indicate_func_t cback; + uint16_t conn_handle; + struct nble_gatt_notif_ind_params params; +}; + +struct nble_gatt_ind_rsp { + bt_gatt_indicate_func_t cback; + int status; + uint16_t conn_handle; + struct bt_gatt_attr *attr; +}; + +struct nble_gatt_handle_range { + uint16_t start_handle; + uint16_t end_handle; +}; + +struct nble_gattc_primary { + uint16_t handle; + struct nble_gatt_handle_range range; + struct bt_uuid_128 uuid; +}; + +struct nble_gattc_included { + uint16_t handle; + struct nble_gatt_handle_range range; + struct bt_uuid_128 uuid; +}; + +struct nble_gattc_characteristic { + uint16_t handle; + uint8_t prop; + uint16_t value_handle; + struct bt_uuid_128 uuid; +}; + +struct nble_gattc_descriptor { + uint16_t handle; + struct bt_uuid_128 uuid; +}; + +struct nble_gattc_discover_rsp { + int32_t status; + void *user_data; + uint16_t conn_handle; + uint8_t type; +}; + +struct nble_gatts_attribute_rsp { + int32_t status; + struct bt_gatt_attr *attr; + void *user_data; +}; + +void nble_gatts_rd_reply_req(const struct nble_gatts_rd_reply_params *, uint8_t *, uint16_t); + +void nble_gatts_wr_reply_req(const struct nble_gatts_wr_reply_params *p_params); + +void nble_gatt_register_req(const struct nble_gatt_register_req *p_param, + uint8_t *p_buf, + uint16_t len); + +struct nble_gatt_attr_handles { + uint16_t handle; +}; + +void on_nble_gatt_register_rsp(const struct nble_gatt_register_rsp *p_param, + const struct nble_gatt_attr_handles *p_attrs, uint8_t len); + +void on_nble_gatts_write_evt(const struct nble_gatt_wr_evt *p_evt, + const uint8_t *p_buf, uint8_t buflen); + +void on_nble_gatts_write_exec_evt(const struct nble_gatt_wr_exec_evt *evt); + +void on_nble_gatts_read_evt(const struct nble_gatt_rd_evt *p_evt); + +void nble_gatt_send_notif_req(const struct nble_gatt_send_notif_params *p_params, + const uint8_t *p_value, uint16_t length); + +void nble_gatt_send_ind_req(const struct nble_gatt_send_ind_params *p_params, + const uint8_t *p_value, uint8_t length); + +void on_nble_gatts_send_notif_rsp(const struct nble_gatt_notif_rsp *rsp); + +void on_nble_gatts_send_ind_rsp(const struct nble_gatt_ind_rsp *rsp); + +#define DISCOVER_FLAGS_UUID_PRESENT 1 + +struct nble_discover_params { + void *user_data; + struct bt_uuid_128 uuid; + struct nble_gatt_handle_range handle_range; + uint16_t conn_handle; + uint8_t type; + uint8_t flags; +}; + +void nble_gattc_discover_req(const struct nble_discover_params *req); + +void on_nble_gattc_discover_rsp(const struct nble_gattc_discover_rsp *rsp, + const uint8_t *data, uint8_t data_len); + +/* + * GATT Attribute stream structure. + * + * This structure is a "compressed" copy of @ref bt_gatt_attr. + * UUID pointer and user_data pointer are used as offset into buffer itself. + * The offset is from the beginning of the buffer. therefore a value of 0 + * means that UUID or user_data is not present. + */ +struct ble_gatt_attr { + /* Attribute permissions */ + uint16_t perm; + /* Attribute variable data size */ + uint16_t data_size; + /* Attribute variable data: always starts with the UUID and data follows */ + uint8_t data[]; +}; + +struct ble_gattc_read_params { + void *user_data; + uint16_t conn_handle; + uint16_t handle; + uint16_t offset; +}; + +struct ble_gattc_read_multiple_params { + void *user_data; + uint16_t conn_handle; +}; + +struct ble_gattc_read_rsp { + int status; + void *user_data; + uint16_t conn_handle; + uint16_t handle; + uint16_t offset; +}; + +/* forward declaration */ +struct bt_gatt_write_params; + +typedef void (*bt_att_func_t)(struct bt_conn *conn, uint8_t err, + const struct bt_gatt_write_params *wr_params); + +struct bt_gatt_write_params { + /* Function invoked upon write response */ + bt_att_func_t func; + /* User specific data */ + void *user_data[2]; +}; + +struct ble_gattc_write_params { + uint16_t conn_handle; + uint16_t handle; + uint16_t offset; + /* different than 0 if response required */ + uint8_t with_resp; + struct bt_gatt_write_params wr_params; +}; + +struct ble_gattc_write_rsp { + uint16_t conn_handle; + int status; + uint16_t handle; + uint16_t len; + struct bt_gatt_write_params wr_params; +}; + +void nble_gattc_read_req(const struct ble_gattc_read_params *params); + +void on_nble_gattc_read_rsp(const struct ble_gattc_read_rsp *rsp, + uint8_t *data, uint8_t data_len); + +void nble_gattc_read_multiple_req( + const struct ble_gattc_read_multiple_params *params, + const uint16_t *data, uint16_t data_len); + +void on_nble_gattc_read_multiple_rsp(const struct ble_gattc_read_rsp *rsp, + uint8_t *data, uint8_t data_len); + +void nble_gattc_write_req(const struct ble_gattc_write_params *params, + const uint8_t *buf, uint8_t buflen); + +void on_nble_gattc_write_rsp(const struct ble_gattc_write_rsp *rsp); + +void bt_gatt_connected(struct bt_conn *conn); +void bt_gatt_disconnected(struct bt_conn *conn); + + +struct ble_gattc_value_evt { + uint16_t conn_handle; + int status; + uint16_t handle; + uint8_t type; +}; + +void on_nble_gattc_value_evt(const struct ble_gattc_value_evt *evt, + uint8_t *buf, uint8_t buflen); + +#endif /* GATT_INTERNAL_H_ */ diff --git a/system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.c b/system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.c new file mode 100644 index 00000000..4a128f13 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include "nble_driver.h" + +#include "portable.h" + +/* ipc/rpc uart interface */ +#include "os/os.h" +#include "infra/ipc_requests.h" +#include "infra/port.h" +#include "infra/log.h" +#include "drivers/soc_gpio.h" +#include "drivers/ipc_uart_ns16550.h" + +#include "platform.h" + +#include "rpc.h" + +#include "util/misc.h" + +#include "infra/time.h" + +#include "infra/ipc_uart.h" + + + +/* + * Macro definition for reset pin + * Curie and other board - Everything should be working out of the box + */ +#define BLE_SW_CLK_PIN 27 +#define BLE_SWDIO_PIN 6 +#define RESET_PIN BLE_SWDIO_PIN +#define QRK_BLE_INT 5 + +static uint16_t rpc_port_id; +static list_head_t m_rpc_tx_q; + +extern void on_nble_curie_log(char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + log_vprintk(LOG_LEVEL_INFO, LOG_MODULE_BLE, fmt, args); + va_end(args); +} + +/* + * When we set val to 1 it will wake up the remote (BLE Core), setting it to 0 + * will allow remote to sleep. + */ +static int nble_wake_assert(bool val) +{ + val = true; + uint8_t ret = soc_gpio_write(SOC_GPIO_32, QRK_BLE_INT, val); + + //pr_debug(LOG_MODULE_BLE, "BLE wake up: %d", val); + if (ret != DRV_RC_OK) { + pr_debug(LOG_MODULE_BLE, "Error setting QRK_BLE_INT %d", ret); + } + return ret; +} + +/** + * Function registered to be invoked when the IPC starts or ends + * a transmission session. + * + * This sets the wake state of ble core to either keep it awake at start of a tx + * or to allow sleep at the end of a TX. + * + * @param wake_state false if remote may sleep (end of transmission). + * true, wake remote to start a transmission session. + * @param ignored not used + */ +static void nble_ipc_tx_wake_cb(bool wake_state, void *ignored) +{ + nble_wake_assert(wake_state); +} + +static int nble_interface_init(void) +{ + DRIVER_API_RC ret = DRV_RC_OK; + /* setup IPC UART reference */ + ipc_uart_ns16550_set_tx_cb(nble_ipc_tx_wake_cb, NULL); + + /* Configure GPIO as output and high by default */ + gpio_cfg_data_t config; + config.gpio_type = GPIO_OUTPUT; + ret = soc_gpio_set_config(SOC_GPIO_32, QRK_BLE_INT, &config); + if (ret != DRV_RC_OK) + return -1; + + list_init(&m_rpc_tx_q); + + ret = nble_wake_assert(1); + return ret; +} + +void uart_ipc_disable(void) +{ + ipc_uart_ns16550_disable(SYNC_CHANNEL); +} + +static void *m_rpc_channel; + +struct rpc_tx_elt { + list_t l; + uint16_t length; + uint8_t data[0]; +}; + +/** + * Try to send a element of the RPC waiting list + * + * @param l Pointer to the list element to try to send on the UART + * @return + */ +static int uart_rpc_try_tx(list_t *l) +{ + struct rpc_tx_elt *p_elt; + + /* Retrieve the RPC TX element from the list pointer */ + p_elt = container_of(l, struct rpc_tx_elt, l); + + /* Try to send the element payload */ + return ipc_uart_ns16550_send_pdu(m_rpc_channel, + p_elt->length, + p_elt->data); +} + +/** + * Try to send an RPC TX queue element during the free operation + * of the previous message. This is invoked under interrupt context + * and therefore does not require protection. It is also expected + * that the tx operation can not fail. + */ +static void uart_rpc_try_tx_on_free(void) +{ + list_t *l; + int ret; + + /* Get next element in tx q */ + l = list_get(&m_rpc_tx_q); + if (l) { + ret = uart_rpc_try_tx(l); + + /* It is not possible to fail when called on free event */ + assert(ret == IPC_UART_ERROR_OK); + } +} + +/** + * Try to send an RPC TX queue element after enqueuing an element to the + * RPC TX queue. At this point the state of the UART driver is not known + * and there could be a transmission in progress, so the procedure is to + * protect from interruption, pick (not get) the first element and only + * get it out of the queue if the tx operation was successful. + */ +static void uart_rpc_try_tx_on_add(void) +{ + list_t *l; + int ret; + + int flags = interrupt_lock(); + + /* Pick next element in tx q */ + l = m_rpc_tx_q.head; + if (l) { + ret = uart_rpc_try_tx(l); + + /* If it was sent correctly, remove it from the queue */ + if (ret == IPC_UART_ERROR_OK) { + l = list_get(&m_rpc_tx_q); + } + } + + interrupt_unlock(flags); +} + +/** + * Function handling the events from the UART IPC (irq). + * + * @param channel Channel on which the event applies + * @param request IPC_MSG_TYPE_MESSAGE when a new message was received, + * IPC_MSG_TYPE_FREE when the previous message was sent and can be freed. + * @param len Length of the data + * @param p_data Pointer to the data + * @return 0 + */ +static int uart_ipc_rpc_cback(int channel, int request, int len, void *p_data) +{ + switch (request) { + case IPC_MSG_TYPE_MESSAGE: { +#ifdef CONFIG_RPC_IN + /* if BLE service is available, handle it in BLE service context */ + struct ble_rpc_callin *rpc = (void *) message_alloc( + sizeof(*rpc), NULL); +if (NULL == rpc) +{ + panic(-1); +} + MESSAGE_ID(&rpc->msg) = 0; + MESSAGE_LEN(&rpc->msg) = sizeof(*rpc); + MESSAGE_SRC(&rpc->msg) = rpc_port_id; + MESSAGE_DST(&rpc->msg) = rpc_port_id; + MESSAGE_TYPE(&rpc->msg) = TYPE_INT; + rpc->p_data = p_data; + rpc->len = len; + if (port_send_message(&rpc->msg) != E_OS_OK) + panic(-1); +#endif + } + break; + case IPC_MSG_TYPE_FREE: + { + /* Free the message */ + struct rpc_tx_elt *p_elt; + + p_elt = container_of(p_data, struct rpc_tx_elt, data); + bfree(p_elt); + + //pr_debug(LOG_MODULE_IPC, "%s:%p", __FUNCTION__, p_elt); + + /* Try to send another message immediately */ + uart_rpc_try_tx_on_free(); + break; + } + default: + /* Free the message */ + bfree(p_data); + pr_error(LOG_MODULE_BLE, "Unsupported RPC request"); + break; + } + return 0; +} + +uint8_t *rpc_alloc_cb(uint16_t length) +{ + struct rpc_tx_elt *p_elt; + + //pr_info(0, "%s", __FUNCTION__); + + p_elt = balloc(length + offsetof(struct rpc_tx_elt, data), NULL); + //pr_debug(LOG_MODULE_IPC, "%s:%p", __FUNCTION__, p_elt); + assert(p_elt != NULL); + + /* Save the length of the buffer */ + p_elt->length = length; + + return p_elt->data; +} + +/* called under NON-interrupt context */ +void rpc_transmit_cb(uint8_t *p_buf, uint16_t length) +{ + struct rpc_tx_elt *p_elt; + + p_elt = container_of(p_buf, struct rpc_tx_elt, data); + + list_add(&m_rpc_tx_q, &p_elt->l); + + uart_rpc_try_tx_on_add(); +} + +/* nble reset is achieved by asserting low the SWDIO pin. + * However, the BLE Core chip can be in SWD debug mode, and NRF_POWER->RESET = 0 due to, + * other constraints: therefore, this reset might not work everytime, especially after + * flashing or debugging. + */ +void nble_driver_init(void) +{ + uint32_t delay_until; + + nble_interface_init(); + /* Setup UART0 for BLE communication, HW flow control required */ + SET_PIN_MODE(18, QRK_PMUX_SEL_MODEA); /* UART0_RXD */ + SET_PIN_MODE(19, QRK_PMUX_SEL_MODEA); /* UART0_TXD */ + SET_PIN_MODE(40, QRK_PMUX_SEL_MODEB); /* UART0_CTS_B */ + SET_PIN_MODE(41, QRK_PMUX_SEL_MODEB); /* UART0_RTS_B */ + + ipc_uart_init(0); + + //while (1) + //{} + /* RESET_PIN depends on the board and the local configuration: check top of file */ + gpio_cfg_data_t pin_cfg = { .gpio_type = GPIO_OUTPUT }; + + soc_gpio_set_config(SOC_GPIO_32, RESET_PIN, &pin_cfg); + //soc_gpio_set_config(SOC_GPIO_32_ID, BLE_SW_CLK_PIN, &pin_cfg); + /* Reset hold time is 0.2us (normal) or 100us (SWD debug) */ + soc_gpio_write(SOC_GPIO_32, RESET_PIN, 0); + /* Wait for ~1ms */ + delay_until = get_uptime_32k() + 32768; + + /* Open the UART channel for RPC while Nordic is in reset */ + m_rpc_channel = ipc_uart_channel_open(RPC_CHANNEL, uart_ipc_rpc_cback); + + while (get_uptime_32k() < delay_until); + + /* De-assert the reset */ + soc_gpio_write(SOC_GPIO_32, RESET_PIN, 1); + + + /* Set back GPIO to input to avoid interfering with external debugger */ + pin_cfg.gpio_type = GPIO_INPUT; + soc_gpio_set_config(SOC_GPIO_32, RESET_PIN, &pin_cfg); +} + + +void nble_driver_configure(T_QUEUE queue, void (*handler)(struct message*, void*)) +{ + rpc_port_id = port_alloc(queue); + assert(rpc_port_id != 0); + port_set_handler(rpc_port_id, handler, NULL); + pr_debug(LOG_MODULE_BLE, "%s: done port: %d", __func__, rpc_port_id); +} + + +void on_nble_curie_init(void) +{ + nble_driver_init(); +} + + diff --git a/system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.h b/system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.h new file mode 100644 index 00000000..7ea15b44 --- /dev/null +++ b/system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, Intel Corporation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NBLE_DRIVER_H_ +#define NBLE_DRIVER_H_ + +#include "os/os.h" +#include "infra/message.h" + +struct ble_rpc_callin { + struct message msg; /**< Message header, MUST be first element of structure */ + uint8_t *p_data; /**< RPC buffer, must be freed after deserializing */ + uint16_t len; /**< length of above buffer */ +}; + +/** + * This resets and initializes the uart/ipc mechanism of nble. + * + * This this will trigger the call to @ref on_nble_up indicating that rpc + * mechanism is up and running. + */ +void nble_driver_init(void); + +void nble_driver_configure(T_QUEUE queue, void (*handler)(struct message*, void*)); + +void uart_ipc_disable(void); + +#endif /* NBLE_DRIVER_H_ */ diff --git a/variants/arduino_101/variant.cpp b/variants/arduino_101/variant.cpp index d9b0bf05..c20dc4c3 100644 --- a/variants/arduino_101/variant.cpp +++ b/variants/arduino_101/variant.cpp @@ -22,6 +22,11 @@ #include "cfw_platform.h" #include "platform.h" +// Add for debug corelib +#ifdef CONFIGURE_DEBUG_CORELIB_ENABLED +#include +#endif + /* * Arduino 101 @@ -236,6 +241,11 @@ void initVariant( void ) variantAdcInit(); cfw_platform_init(); + + // Add for debug corelib + #ifdef CONFIGURE_DEBUG_CORELIB_ENABLED + log_init(); + #endif } #ifdef __cplusplus From e7238f02c945faf6a05eca38ae590d520ec2af62 Mon Sep 17 00:00:00 2001 From: lianggao Date: Fri, 9 Sep 2016 08:25:31 +0800 Subject: [PATCH 002/125] Jira Tickets and Bug fix 1. Jira 541 Peripheral Start fails on X Number of Attributes -Change the interface and add return value 2. Fix BLE Peripheral is not advertising -Add a delay after register the profile. 3. Modify some comments based on code review 4. Jira 544 BLE peripheral disconnect and end functions -The state not aligned and make disconnect failed. Update when connect and disconnect event received 5. Delete the duplicated code that base class has implemented 6. Fix Jira 665 BLECentral Preview -- compile issue when instantiating BLE descriptor -Fix the build error. Note: i . The current code only support one descriptor on characteristic. ii . The central discover logic need more twist. iii. Now is so long and can't process CCCD and another characteristic. iv . The library will support one other descriptor except CCCD. 7. Improve the discover logic and support another descriptor i. Improve the discover logic ii. Support another the descriptor and CCCD at same time. 8. Modify the comments and delete the unused API 9. Fix Jira 670 A compile error occurs with two BLE methods with the same name i. Add method uuid_cstr in BLEAttribute class. ii. Remove the duplicate file. 10. Fix Jira 672 BLE documentation in code needs to specify units i. Change the scan and connection interval's unit to ms. ii. Unify the advertising interval unit as millisecond iii. Delete some unused code. 11. Fix Jria 671 Support update connection interval in central/peripheral i. Central can update the connection interval. ii. Unify the interval unit to ms at user API. 12. Adjust the example comments and functions i. Fix Jira 675 - BLE callbacks should use passed arguments instead of global variables ii. Adjust the code struture to align with Arduino's requirement 13. Fix Jira 680 LED and LED Central sketches need some work -Add a delay to make sure profile register process success. -The UART send too fast may makes the profile registration failed. 14. Fix Jira 673 BLE Api should pass arguments that are typedef 15. Fix Jira 671 Support update connection interval in central/peripheral i. Add peripheral update the connection interval feature. ii. Update the library. 16. Fix Jira 687 Edit the keyword.txt of CurieBLE to reflect Library changes 17. Make example function ready. But need to resolve Jira 675 18. Fix Jira 676 Review Edit Update BLE User Manual with China Flex 19. Fix Jira 685 BLE Peripheral sketches shall state which associated Central sketch to use 20. Fix Jira 588 - BLE Corrupted Long Write i. Modify the BLECharacteristic to implement the Long write feature. 21. Fix Jira 683 Typecasting and Pointers should be avoided in sketch i. Add write in Characteristic template ii. Add method in BLE Role class to avoid typecaste iii. Modify the advertise address parameter and related example sketches 22. Fix Jira 704 Type Characteristic should be documented 23. Fix Jira 684 Documentation about the central/peripheral --- .../BatteryMonitor/BatteryMonitor.ino | 9 +- .../CurieBLE/examples/ButtonLED/ButtonLED.ino | 15 +- .../examples/CallbackLED/CallbackLED.ino | 21 +- .../examples/IMUBleCentral/IMUBleCentral.ino | 145 +++--- .../IMUBleNotification/IMUBleNotification.ino | 93 ++-- libraries/CurieBLE/examples/LED/LED.ino | 12 +- .../examples/LEDCentral/LEDCentral.ino | 161 ++++--- .../CurieBLE/examples/MIDIBLE/MIDIBLE.ino | 37 +- .../CurieBLE/examples/Scanning/Scanning.ino | 141 +++--- libraries/CurieBLE/keywords.txt | 33 +- libraries/CurieBLE/src/BLEAttribute.cpp | 32 +- libraries/CurieBLE/src/BLEAttribute.h | 74 +++- libraries/CurieBLE/src/BLECentral.cpp | 42 +- libraries/CurieBLE/src/BLECentral.h | 58 ++- libraries/CurieBLE/src/BLECentralHelper.cpp | 52 --- libraries/CurieBLE/src/BLECentralHelper.h | 14 - libraries/CurieBLE/src/BLECentralRole.cpp | 44 +- libraries/CurieBLE/src/BLECentralRole.h | 72 +-- libraries/CurieBLE/src/BLECharacteristic.cpp | 79 +++- libraries/CurieBLE/src/BLECharacteristic.h | 101 +++-- libraries/CurieBLE/src/BLECommon.h | 71 ++- libraries/CurieBLE/src/BLEDescriptor.cpp | 33 +- libraries/CurieBLE/src/BLEDescriptor.h | 36 +- libraries/CurieBLE/src/BLEHelper.cpp | 87 ++-- libraries/CurieBLE/src/BLEHelper.h | 128 +++++- libraries/CurieBLE/src/BLEPeripheral.cpp | 56 ++- libraries/CurieBLE/src/BLEPeripheral.h | 134 +++--- .../CurieBLE/src/BLEPeripheralHelper.cpp | 16 +- libraries/CurieBLE/src/BLEPeripheralHelper.h | 98 ++++- libraries/CurieBLE/src/BLEPeripheralRole.cpp | 66 ++- libraries/CurieBLE/src/BLEPeripheralRole.h | 102 +++-- libraries/CurieBLE/src/BLEProfile.cpp | 413 +++++++++++++----- libraries/CurieBLE/src/BLEProfile.h | 67 ++- libraries/CurieBLE/src/BLERoleBase.cpp | 24 +- libraries/CurieBLE/src/BLERoleBase.h | 34 +- libraries/CurieBLE/src/BLEService.cpp | 12 +- libraries/CurieBLE/src/BLEService.h | 10 +- .../CurieBLE/src/BLETypedCharacteristic.h | 96 ++++ .../CurieBLE/src/BLETypedCharacteristics.h | 143 ++++++ libraries/CurieBLE/src/internal/ble_client.c | 76 ++-- libraries/CurieBLE/src/internal/ble_client.h | 4 +- .../drivers/bluetooth/conn_internal.h | 125 ------ 42 files changed, 2019 insertions(+), 1047 deletions(-) delete mode 100644 system/libarc32_arduino101/drivers/bluetooth/conn_internal.h diff --git a/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino b/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino index 7386087f..0facfe09 100644 --- a/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino +++ b/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino @@ -6,7 +6,10 @@ #include /* - This sketch example partially implements the standard Bluetooth Low-Energy Battery service. + 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 */ @@ -15,9 +18,9 @@ BLEPeripheral blePeripheral; // BLE Peripheral Device (the board you're pr BLEService batteryService("180F"); // BLE Battery Service // BLE Battery Level Characteristic" -BLEUnsignedCharCharacteristic batteryLevelChar("2A19", // standard 16-bit characteristic UUID +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 + // 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 diff --git a/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino b/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino index b4cd873a..9cb38a2c 100644 --- a/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino +++ b/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino @@ -3,19 +3,28 @@ * See the bottom of this file for the license terms. */ + /* 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 connect 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 +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); @@ -32,6 +41,7 @@ void setup() { blePeripheral.addAttribute(ledCharacteristic); blePeripheral.addAttribute(buttonCharacteristic); + // set initial values for led and button characteristic ledCharacteristic.setValue(0); buttonCharacteristic.setValue(0); @@ -59,10 +69,13 @@ void loop() { 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); } diff --git a/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino b/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino index 26946cbf..3f516ca5 100644 --- a/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino +++ b/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino @@ -2,13 +2,24 @@ * 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 + // BLECoonected, 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 + // Writng 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 +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); @@ -45,10 +56,14 @@ void loop() { 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(central.address()); + Serial.println(bleCentral1->address()); } void blePeripheralDisconnectHandler(BLEHelper& central) { @@ -57,6 +72,8 @@ void blePeripheralDisconnectHandler(BLEHelper& 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: "); diff --git a/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino b/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino index 0523ed55..e14753db 100644 --- a/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino +++ b/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino @@ -1,31 +1,28 @@ /* - 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 -*/ + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ #include /* - This sketch example partially implements the standard Bluetooth Low-Energy Battery service. - For more information: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx + This sketch example works with IMUBleNotification.ino + IMUBleNotification.ino will send notification to this sketch. + This sketch will receive the notifications and out put 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 -struct bt_le_conn_param conn_param = {0x18, 0x28, 0, 400}; +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]; @@ -38,19 +35,64 @@ 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) { - BLEPeripheralHelper&peripheral = *(BLEPeripheralHelper*)(&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(); + peripheral->discover(); } -void bleImuCharacteristicWritten(BLEHelper& central, BLECharacteristic& characteristic) +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: "); @@ -64,32 +106,32 @@ void bleImuCharacteristicWritten(BLEHelper& central, BLECharacteristic& characte } bool adv_found(uint8_t type, - const uint8_t *data, + const uint8_t *dataPtr, uint8_t data_len, - void *user_data) + const bt_addr_le_t *addrPtr) { - bt_addr_le_t *addr = (bt_addr_le_t *)user_data; 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 % MAX_UUID_SIZE != 0) + if (data_len % UUID_SIZE_128 != 0) { Serial.println("AD malformed"); return true; } - struct bt_uuid * serviceuuid = bleImuService.uuid(); - for (i = 0; i < data_len; i += MAX_UUID_SIZE) + for (i = 0; i < data_len; i += UUID_SIZE_128) { - if (memcmp (((struct bt_uuid_128*)serviceuuid)->val, &data[i], MAX_UUID_SIZE) != 0) + if (bleImuService.uuidCompare(dataPtr + i, UUID_SIZE_128) == false) { continue; } @@ -102,7 +144,7 @@ bool adv_found(uint8_t type, } Serial.println("Connecting"); // Connect to peripheral - bleCentral.connect(addr, &conn_param); + bleCentral.connect(addrPtr, &conn_param); return false; } } @@ -111,33 +153,20 @@ bool adv_found(uint8_t type, return true; } -void setup() { - Serial.begin(115200); // initialize serial communication - pinMode(13, OUTPUT); // initialize the LED on pin 13 to indicate when a central is connected - - bleImuChar.setEventHandler(BLEWritten, bleImuCharacteristicWritten); - - /* 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 */ - bleCentral.addAttribute(bleImuService); // Add the BLE IMU service - bleCentral.addAttribute(bleImuChar); // Add the BLE IMU characteristic - - /* Setup callback */ - 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(); -} +/* + 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. -void loop() -{ - delay(2000); -} + 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/IMUBleNotification/IMUBleNotification.ino b/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino index 4a179e2a..81ca3dec 100644 --- a/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino +++ b/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino @@ -1,27 +1,15 @@ /* - 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 -*/ + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ #include #include /* - This sketch example partially implements the standard Bluetooth Low-Energy Battery service. - For more information: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx + This sketch example works with IMUBleCentral.ino + This sketch will read IMU data from sensor and send notification to IMUBleCentral.ino + IMUBleCentral.ino will receive the Notifications and output the received data. */ #define MAX_IMU_RECORD 1 @@ -31,6 +19,7 @@ typedef struct { unsigned int slot[3]; } imuFrameType; +// Buffer to hold IMU data imuFrameType imuBuf[MAX_IMU_RECORD]; unsigned seqNum = 0; @@ -40,8 +29,8 @@ BLEService bleImuService("F7580001-153E-D4F6-F26D-43D8D98EEB13"); // Tx IMU data 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 -void setup() { - +void setup() +{ Serial.begin(9600); // initialize serial communication pinMode(13, OUTPUT); // initialize the LED on pin 13 to indicate when a central is connected @@ -52,35 +41,21 @@ void setup() { 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 BLE Battery service - blePeripheral.addAttribute(bleImuChar); // add the battery level characteristic + blePeripheral.addAttribute(bleImuService); // Add the Imu service + blePeripheral.addAttribute(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(); } -void recordImuData(int index) { - /* Read IMU data. - */ - int ax, ay, az; - int gx, gy, gz; - - imuBuf[index].index = seqNum++; - CurieIMU.readMotionSensor(ax, ay, az, gx, gy, gz); - - 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)); - -} - - -void loop() { +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(); // if a central is connected to peripheral: @@ -109,7 +84,7 @@ void loop() { sentTime = millis(); bleImuChar.setValue((unsigned char *)&(imuBuf[0]), sizeof(imuBuf)); } - } // while + } // end of while loop // when the central disconnects, turn off the LED: digitalWrite(13, LOW); @@ -118,3 +93,39 @@ void loop() { } } +// This function records the IMU data that we send to the central +void recordImuData(int index) +{ + /* Read IMU data. + */ + int ax, ay, az; + int gx, gy, gz; + + imuBuf[index].index = seqNum++; + CurieIMU.readMotionSensor(ax, ay, az, gx, gy, gz); + + // Encode the data into the buffer + 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/LED/LED.ino b/libraries/CurieBLE/examples/LED/LED.ino index 263c7d5b..2c2c261d 100644 --- a/libraries/CurieBLE/examples/LED/LED.ino +++ b/libraries/CurieBLE/examples/LED/LED.ino @@ -2,7 +2,17 @@ * 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 wriiten 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 + // Writng a byte value of 0x00 should turn off the LED + #include BLEPeripheral blePeripheral; // BLE Peripheral Device (the board you're programming) diff --git a/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino b/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino index 04cd0971..b9b533b3 100644 --- a/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino +++ b/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino @@ -1,40 +1,88 @@ /* - 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 -*/ + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ -// This example can work with CallbackLED to show the profile read/write operation in central #include -struct bt_le_conn_param conn_param = {0x18, 0x28, 0, 400}; +/* + This example can work with CallbackLED and LED + 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; +BLEPeripheralHelper *blePeripheral1 = NULL; // peer peripheral device -BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // create service +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 = (BLEPeripheralHelper *)(&peripheral); + blePeripheral1 = bleCentral.getPeerPeripheralBLE(peripheral); Serial.print("Connected event, peripheral: "); - Serial.println(peripheral.address()); + Serial.println(blePeripheral1->address()); // Start discovery the profiles in peripheral device blePeripheral1->discover(); } @@ -66,11 +114,10 @@ void switchCharacteristicWritten(BLEHelper& peripheral, BLECharacteristic& chara } bool adv_found(uint8_t type, - const uint8_t *data, + const uint8_t *dataPtr, uint8_t data_len, - void *user_data) + const bt_addr_le_t *addrPtr) { - bt_addr_le_t *addr = (bt_addr_le_t *)user_data; int i; Serial.print("[AD]:"); @@ -83,15 +130,14 @@ bool adv_found(uint8_t type, case BT_DATA_UUID128_SOME: case BT_DATA_UUID128_ALL: { - if (data_len % MAX_UUID_SIZE != 0) + if (data_len % UUID_SIZE_128 != 0) { Serial.println("AD malformed"); return true; } - struct bt_uuid * serviceuuid = ledService.uuid(); - for (i = 0; i < data_len; i += MAX_UUID_SIZE) + for (i = 0; i < data_len; i += UUID_SIZE_128) { - if (memcmp (((struct bt_uuid_128*)serviceuuid)->val, &data[i], MAX_UUID_SIZE) != 0) + if (ledService.uuidCompare(dataPtr + i, UUID_SIZE_128) == false) { continue; } @@ -104,7 +150,7 @@ bool adv_found(uint8_t type, } Serial.println("Connecting"); // Connect to peripheral - bleCentral.connect(addr, &conn_param); + bleCentral.connect(addrPtr, &conn_param); return false; } } @@ -113,45 +159,20 @@ bool adv_found(uint8_t type, return true; } -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); +/* + Copyright (c) 2016 Intel Corporation. All rights reserved. - // advertise the service - bleCentral.setAdvertiseHandler(adv_found); + 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. - // assign event handlers for characteristic - switchChar.setEventHandler(BLEWritten, switchCharacteristicWritten); + 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. - 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, (unsigned char *)(&ledstate), sizeof (ledstate)); - } - } -} + 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 index 4892f017..7d78df27 100644 --- a/libraries/CurieBLE/examples/MIDIBLE/MIDIBLE.ino +++ b/libraries/CurieBLE/examples/MIDIBLE/MIDIBLE.ino @@ -1,7 +1,8 @@ /* 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 @@ -25,6 +26,9 @@ 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 @@ -98,6 +102,21 @@ void setup() { 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 @@ -124,22 +143,6 @@ void BLESetup() midiDevice.begin(); } -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 midiDeviceConnectHandler(BLEHelper& central) { // central connected event handler Serial.print("Connected event, central: "); diff --git a/libraries/CurieBLE/examples/Scanning/Scanning.ino b/libraries/CurieBLE/examples/Scanning/Scanning.ino index d2d7d0e2..97ea222d 100644 --- a/libraries/CurieBLE/examples/Scanning/Scanning.ino +++ b/libraries/CurieBLE/examples/Scanning/Scanning.ino @@ -1,37 +1,76 @@ /* - Copyright (c) 2016 Intel Corporation. All rights reserved. + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ - 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. +#include - 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 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 + This sketch is meaningful if one or more BLE peripheral devices (any of the peripheral examples will do) + are present. */ -#include -#define BLE_SCANING_DEVICE_MAX_CNT 5 + +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[BLE_SCANING_DEVICE_MAX_CNT]; +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 >= BLE_SCANING_DEVICE_MAX_CNT) + if (list_index >= bleScanMaxCnt) { return false; } @@ -72,58 +111,40 @@ void adv_list_clear() // Process the Advertisement data bool adv_found(uint8_t type, - const uint8_t *data, + const uint8_t *dataPtr, uint8_t data_len, - void *user_data) + const bt_addr_le_t *addrPtr) { - bt_addr_le_t *addr = (bt_addr_le_t *)user_data; ble_device_info_t device; - bt_addr_le_to_str (addr, device.macaddr, sizeof (device.macaddr)); + 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, data, data_len); - device.loacalname[data_len] = '\0'; - adv_list_update(device); - break; + 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; } -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..."); -} +/* + Copyright (c) 2016 Intel Corporation. All rights reserved. -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(); -} + 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/keywords.txt b/libraries/CurieBLE/keywords.txt index 85eccea2..bb0f37ea 100644 --- a/libraries/CurieBLE/keywords.txt +++ b/libraries/CurieBLE/keywords.txt @@ -13,7 +13,9 @@ BLEDescriptor KEYWORD1 BLEPeripheral KEYWORD1 BLEService KEYWORD1 BLETypedCharacteristic KEYWORD1 -BLEUuid KEYWORD1 +BLEHelper KEYWORD1 +BLECentralHelper KEYWORD1 +BLEPeripheralHelper KEYWORD1 BLECharCharacteristic KEYWORD1 BLEUnsignedCharCharacteristic KEYWORD1 @@ -31,13 +33,18 @@ BLEDoubleCharacteristic KEYWORD1 ####################################### uuid KEYWORD2 -type 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 @@ -49,11 +56,14 @@ written KEYWORD2 subscribed KEYWORD2 begin KEYWORD2 -getAdvertisingLength KEYWORD2 -getAdvertising KEYWORD2 + +stopAdvertising KEYWORD2 +setConnectable KEYWORD2 +startAdvertising KEYWORD2 setAdvertisedServiceUuid KEYWORD2 setAdvertisedServiceData KEYWORD2 setLocalName KEYWORD2 +setDeviceName KEYWORD2 setAppearance KEYWORD2 setConnectionInterval KEYWORD2 addAttribute KEYWORD2 @@ -64,10 +74,9 @@ valueLE KEYWORD2 setValueBE KEYWORD2 valueBE KEYWORD2 -str KEYWORD2 -data KEYWORD2 -length KEYWORD2 - +getPeerPeripheralBLE KEYWORD2 +getPeerCentralBLE KEYWORD2 +uuidCompare KEYWORD2 ####################################### # Constants (LITERAL1) @@ -77,7 +86,6 @@ BLETypeService LITERAL1 BLETypeCharacteristic LITERAL1 BLETypeDescriptor LITERAL1 -BLEBroadcast LITERAL1 BLERead LITERAL1 BLEWriteWithoutResponse LITERAL1 BLEWrite LITERAL1 @@ -86,7 +94,14 @@ BLEIndicate LITERAL1 BLEConnected LITERAL1 BLEDisconnected LITERAL1 +BLEUpdateParam LITERAL1 BLEWritten LITERAL1 BLESubscribed LITERAL1 BLEUnsubscribed LITERAL1 + +ble_conn_param_t LITERAL1 +bt_uuid_t LITERAL1 +bt_uuid_16_t LITERAL1 +bt_uuid_128_t LITERAL1 +bt_addr_le_t LITERAL1 diff --git a/libraries/CurieBLE/src/BLEAttribute.cpp b/libraries/CurieBLE/src/BLEAttribute.cpp index 478c2e00..dc49158f 100644 --- a/libraries/CurieBLE/src/BLEAttribute.cpp +++ b/libraries/CurieBLE/src/BLEAttribute.cpp @@ -21,7 +21,7 @@ unsigned char BLEAttribute::_numAttributes = 0; -BLEAttribute::BLEAttribute(const char* uuid, enum BLEAttributeType type) : +BLEAttribute::BLEAttribute(const char* uuid, BLEAttributeType type) : _uuid_cstr(uuid), _type(type), _handle(0) @@ -54,7 +54,7 @@ BLEAttribute::BLEAttribute(const char* uuid, enum BLEAttributeType type) : { uint16_t temp = (_uuid.val[1] << 8)| _uuid.val[0]; _uuid.uuid.type = BT_UUID_TYPE_16; - ((struct bt_uuid_16*)(&_uuid.uuid))->val = temp; + ((bt_uuid_16_t*)(&_uuid.uuid))->val = temp; } else { @@ -67,13 +67,18 @@ BLEAttribute::uuid() const { return _uuid_cstr; } -struct bt_uuid *BLEAttribute::uuid(void) +const char* +BLEAttribute::uuid_cstr() const { + return _uuid_cstr; +} + +bt_uuid_t *BLEAttribute::uuid(void) { - return (struct bt_uuid *)&_uuid; + return (bt_uuid_t *)&_uuid; } -enum BLEAttributeType +BLEAttributeType BLEAttribute::type() const { return this->_type; } @@ -99,4 +104,19 @@ 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 index 71a19fca..1de33613 100644 --- a/libraries/CurieBLE/src/BLEAttribute.h +++ b/libraries/CurieBLE/src/BLEAttribute.h @@ -22,11 +22,12 @@ #include "BLECommon.h" -enum BLEAttributeType { - BLETypeService = 0x2800, - BLETypeCharacteristic = 0x2803, - BLETypeDescriptor = 0x2900 -}; +/// 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; @@ -42,33 +43,72 @@ class BLEAttribute { * @return const char* string representation of the Attribute */ const char* uuid(void) const; - struct bt_uuid *uuid(void); + + /** + * 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(struct bt_conn *conn, - const struct bt_gatt_attr *attr, + 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(struct bt_conn *conn, - const struct bt_gatt_attr *attr, + friend ssize_t profile_read_process(bt_conn_t *conn, + const bt_gatt_attr_t *attr, void *buf, uint16_t len, uint16_t offset); - BLEAttribute(const char* uuid, enum BLEAttributeType type); + 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; - bt_uuid btUuid(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(struct bt_gatt_discover_params *params) = 0; - virtual void discover(const struct bt_gatt_attr *attr, - struct bt_gatt_discover_params *params) = 0; + 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 * @@ -86,9 +126,9 @@ class BLEAttribute { static unsigned char _numAttributes; const char* _uuid_cstr; - struct bt_uuid_128 _uuid; + bt_uuid_128_t _uuid; - enum BLEAttributeType _type; + BLEAttributeType _type; uint16_t _handle; }; diff --git a/libraries/CurieBLE/src/BLECentral.cpp b/libraries/CurieBLE/src/BLECentral.cpp index 6b4afcf5..728b222c 100644 --- a/libraries/CurieBLE/src/BLECentral.cpp +++ b/libraries/CurieBLE/src/BLECentral.cpp @@ -20,15 +20,17 @@ #include "BLECentralRole.h" #include "BLECentral.h" +#include "internal/ble_client.h" bool BLECentral::startScan() { return BLECentralRole::instance()->startScan(); } -bool BLECentral::startScan(const struct bt_le_scan_param &scan_param) +bool BLECentral::startScan(float interval, float window) { - return BLECentralRole::instance()->startScan(scan_param); + setScanParam(interval, window); + return BLECentralRole::instance()->startScan(); } bool BLECentral::stopScan() @@ -36,9 +38,22 @@ bool BLECentral::stopScan() return BLECentralRole::instance()->stopScan(); } -bool BLECentral::connect(const bt_addr_le_t *addr, const struct bt_le_conn_param *param) +bool BLECentral::connect(const bt_addr_le_t *addr, const ble_conn_param_t *param) { - return BLECentralRole::instance()->connect(addr, 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) @@ -56,14 +71,19 @@ void BLECentral::setAdvertiseHandler(ble_advertise_handle_cb_t advcb) BLECentralRole::instance()->setAdvertiseHandler(advcb); } -void BLECentral::setScanParam(const struct bt_le_scan_param &scan_param) +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); } -void BLECentral::addAttribute(BLEAttribute& attribute) +BleStatus BLECentral::addAttribute(BLEAttribute& attribute) { - BLECentralRole::instance()->addAttribute(attribute); + return BLECentralRole::instance()->addAttribute(attribute); } bool BLECentral::begin(void) @@ -76,8 +96,8 @@ bool BLECentral::begin(void) } // Start scan - const struct bt_le_scan_param *scan_param = BLECentralRole::instance()->getScanParam(); - struct bt_le_scan_param zero_param; + 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))) { @@ -96,5 +116,9 @@ bool BLECentral::begin(void) return retval; } +BLEPeripheralHelper *BLECentral::getPeerPeripheralBLE(BLEHelper& peripheral) +{ + return (BLEPeripheralHelper *)(&peripheral); +} diff --git a/libraries/CurieBLE/src/BLECentral.h b/libraries/CurieBLE/src/BLECentral.h index 8327f5d6..09379b2b 100644 --- a/libraries/CurieBLE/src/BLECentral.h +++ b/libraries/CurieBLE/src/BLECentral.h @@ -25,6 +25,11 @@ class BLEAttribute; +/** + * @brief A class defining the BLE central function + * + * This class abstract the BLE central. + */ class BLECentral{ public: /** @@ -41,13 +46,15 @@ class BLECentral{ /** * @brief Start scan with scan parameter * - * @param none + * @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(const struct bt_le_scan_param &scan_param); + bool startScan(float interval, float window); /** * @brief Stop scan @@ -63,20 +70,20 @@ class BLECentral{ /** * @brief Schedule a connect request to peripheral to establish a connection * - * @param addr The MAC address of peripheral device that want to establish connection + * @param[in] addr The MAC address of peripheral device that want to establish connection * - * @param param The connetion parameters + * @param[in] param The connetion parameters * * @return bool Indicate the success or error * * @note none */ - bool connect(const bt_addr_le_t *addr, const struct bt_le_conn_param *param); + bool connect(const bt_addr_le_t *addr, const ble_conn_param_t *param); /** * @brief Discover the peripheral device profile * - * @param peripheral The Peripheral that need to discover the profile + * @param[in] peripheral The Peripheral that need to discover the profile * * @return none * @@ -87,37 +94,47 @@ class BLECentral{ /** * @brief Set the scan parameter * - * @param scan_param The scan parameter want to be set + * @param[in] interval The scan interval in ms + * + * @param[in] window The scan window in ms * * @return none * - * @note 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(const struct bt_le_scan_param &scan_param); + void setScanParam(float interval, float window); /** * @brief Add an attribute to the BLE Central Device * - * @param attribute Attribute to add to Central + * @param[in] attribute Attribute to add to Central * - * @return none + * @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 */ - void addAttribute(BLEAttribute& attribute); + BleStatus addAttribute(BLEAttribute& attribute); /** * Provide a function to be called when events related to this Device are raised * - * @param event Event type for callback - * @param callback Pointer to callback function to invoke when an event occurs. + * @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 advcb Pointer to callback function to invoke when advertisement received + * @param[in] advcb Pointer to callback function to invoke when advertisement received * * @return none * @@ -126,11 +143,20 @@ class BLECentral{ void setAdvertiseHandler(ble_advertise_handle_cb_t advcb); /** - * Setup attributes and start scan + * @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); protected: private: diff --git a/libraries/CurieBLE/src/BLECentralHelper.cpp b/libraries/CurieBLE/src/BLECentralHelper.cpp index 99e92de9..e48426aa 100644 --- a/libraries/CurieBLE/src/BLECentralHelper.cpp +++ b/libraries/CurieBLE/src/BLECentralHelper.cpp @@ -28,24 +28,6 @@ BLECentralHelper::BLECentralHelper(BLEPeripheralRole* peripheral) : clearAddress(); } -BLECentralHelper::operator bool() const { - bt_addr_le_t zero; - - memset(&zero, 0, sizeof(zero)); - - return (memcmp(&_address, &zero, sizeof(_address)) != 0); -} - -bool -BLECentralHelper::operator==(const BLECentralHelper& rhs) const { - return (memcmp(&_address, &rhs._address, sizeof(_address)) == 0); -} - -bool -BLECentralHelper::operator!=(const BLECentralHelper& rhs) const { - return !(*this == rhs); -} - bool BLECentralHelper::connected() { poll(); @@ -53,31 +35,6 @@ BLECentralHelper::connected() { return (*this && *this == _peripheral->central()); } -const char* -BLECentralHelper::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; -} - void BLECentralHelper::poll() { _peripheral->poll(); @@ -92,12 +49,3 @@ BLECentralHelper::disconnect() { return false; } -void -BLECentralHelper::setAddress(bt_addr_le_t address) { - _address = address; -} - -void -BLECentralHelper::clearAddress() { - memset(&_address, 0x00, sizeof(_address)); -} diff --git a/libraries/CurieBLE/src/BLECentralHelper.h b/libraries/CurieBLE/src/BLECentralHelper.h index 465d9cab..4a563d97 100644 --- a/libraries/CurieBLE/src/BLECentralHelper.h +++ b/libraries/CurieBLE/src/BLECentralHelper.h @@ -36,13 +36,6 @@ class BLECentralHelper: public BLEHelper{ * @return boolean_t true if the central is connected, otherwise false */ bool connected(void); - - /** - * Get the address of the Central in string form - * - * @return const char* address of the Central in string form - */ - const char* address(void) const; /** * Disconnect the central if it is connected @@ -55,18 +48,11 @@ class BLECentralHelper: public BLEHelper{ */ void poll(void); - operator bool(void) const; - bool operator==(const BLECentralHelper& rhs) const; - bool operator!=(const BLECentralHelper& rhs) const; - protected: BLECentralHelper(BLEPeripheralRole* peripheral); - void setAddress(bt_addr_le_t address); - void clearAddress(); private: BLEPeripheralRole* _peripheral; - bt_addr_le_t _address; }; #endif diff --git a/libraries/CurieBLE/src/BLECentralRole.cpp b/libraries/CurieBLE/src/BLECentralRole.cpp index 1595e24d..011a4031 100644 --- a/libraries/CurieBLE/src/BLECentralRole.cpp +++ b/libraries/CurieBLE/src/BLECentralRole.cpp @@ -75,7 +75,7 @@ const BLECentralHelper *BLECentralRole::central(void) const return &_central; } -bool BLECentralRole::connect(const bt_addr_le_t *addr, const struct bt_le_conn_param *param) +bool BLECentralRole::connect(const bt_addr_le_t *addr, const bt_le_conn_param_t *param) { BLEPeripheralHelper* temp = NULL; BLEPeripheralHelper* unused = NULL; @@ -108,7 +108,7 @@ bool BLECentralRole::connect(const bt_addr_le_t *addr, const struct bt_le_conn_p if (!link_existed) { // Send connect request - struct bt_conn* conn = bt_conn_create_le(addr, param); + bt_conn_t* conn = bt_conn_create_le(addr, param); if (NULL != conn) { unused->setAddress(*addr); @@ -121,27 +121,27 @@ bool BLECentralRole::connect(const bt_addr_le_t *addr, const struct bt_le_conn_p bool BLECentralRole::startScan() { - int err = bt_le_scan_start(&_scan_param, ble_central_device_found); - if (err) + 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; - } + pr_info(LOG_MODULE_BLE, "Scanning failed to start (err %d)\n", err); + return false; + } return true; } -bool BLECentralRole::startScan(const struct bt_le_scan_param &scan_param) +bool BLECentralRole::startScan(const bt_le_scan_param_t &scan_param) { setScanParam(scan_param); return startScan(); } -void BLECentralRole::setScanParam(const struct bt_le_scan_param &scan_param) +void BLECentralRole::setScanParam(const bt_le_scan_param_t &scan_param) { memcpy(&_scan_param, &scan_param, sizeof (_scan_param)); } -const struct bt_le_scan_param* BLECentralRole::getScanParam() +const bt_le_scan_param_t* BLECentralRole::getScanParam() { return &_scan_param; } @@ -153,12 +153,12 @@ bool BLECentralRole::stopScan() if (err) { pr_info(LOG_MODULE_BLE, "Stop LE scan failed (err %d)\n", err); - return false; + return false; } return true; } -BLEPeripheralHelper* BLECentralRole::peripheral(struct bt_conn *conn) +BLEPeripheralHelper* BLECentralRole::peripheral(bt_conn_t *conn) { BLEPeripheralHelper* temp = NULL; const bt_addr_le_t *addr = bt_conn_get_dst(conn); @@ -207,7 +207,7 @@ void BLECentralRole::handleDeviceFound(const bt_addr_le_t *addr, return; } - if (!_adv_event_handle(data[1], &data[2], len - 1, (void *)addr)) + if (!_adv_event_handle(data[1], &data[2], len - 1, addr)) { return; } @@ -219,7 +219,7 @@ void BLECentralRole::handleDeviceFound(const bt_addr_le_t *addr, } } -void BLECentralRole::handleConnectEvent(struct bt_conn *conn, uint8_t err) +void BLECentralRole::handleConnectEvent(bt_conn_t *conn, uint8_t err) { if (_event_handlers[BLEConnected]) { @@ -228,7 +228,7 @@ void BLECentralRole::handleConnectEvent(struct bt_conn *conn, uint8_t err) } } -void BLECentralRole::handleDisconnectEvent(struct bt_conn *conn, uint8_t reason) +void BLECentralRole::handleDisconnectEvent(bt_conn_t *conn, uint8_t reason) { if (_event_handlers[BLEDisconnected]) { @@ -238,15 +238,15 @@ void BLECentralRole::handleDisconnectEvent(struct bt_conn *conn, uint8_t reason) } } -void BLECentralRole::handleParamUpdated(struct bt_conn *conn, +void BLECentralRole::handleParamUpdated(bt_conn_t *conn, uint16_t interval, uint16_t latency, uint16_t timeout) { if (_event_handlers[BLEUpdateParam]) { - // Fix me Add parameter proc BLEPeripheralHelper *temp = peripheral(conn); + temp->setConnectionParameters(interval, interval, latency, timeout); _event_handlers[BLEUpdateParam](*temp); } } @@ -265,12 +265,18 @@ void BLECentralRole::setAdvertiseHandler(ble_advertise_handle_cb_t advcb) _adv_event_handle = advcb; } -void BLECentralRole::addAttribute(BLEAttribute& attribute) +BleStatus BLECentralRole::addAttribute(BLEAttribute& attribute) { + BleStatus err = BLE_STATUS_SUCCESS; for (int i = 0; i < BLE_MAX_CONN_CFG; i++) { - _peripherial[i]->addAttribute(attribute); + err = _peripherial[i]->addAttribute(attribute); + if (err != BLE_STATUS_SUCCESS) + { + break; + } } + return err; } bool BLECentralRole::begin() diff --git a/libraries/CurieBLE/src/BLECentralRole.h b/libraries/CurieBLE/src/BLECentralRole.h index d3757fae..0ea6c008 100644 --- a/libraries/CurieBLE/src/BLECentralRole.h +++ b/libraries/CurieBLE/src/BLECentralRole.h @@ -46,7 +46,7 @@ class BLECentralRole: public BLERoleBase { * * @note none */ - bool startScan(const struct bt_le_scan_param &scan_param); + bool startScan(const bt_le_scan_param_t &scan_param); /** * @brief Stop scan @@ -62,37 +62,37 @@ class BLECentralRole: public BLERoleBase { /** * @brief Schedule a connect request to peripheral to establish a connection * - * @param addr The MAC address of peripheral device that want to establish connection + * @param[in] addr The MAC address of peripheral device that want to establish connection * - * @param param The connetion parameters + * @param[in] param The connetion parameters * * @return bool Indicate the success or error * * @note none */ - bool connect(const bt_addr_le_t *addr, const struct bt_le_conn_param *param); + bool connect(const bt_addr_le_t *addr, const bt_le_conn_param_t *param); /** * @brief Set the scan parameter * - * @param scan_param The scan parameter want to be set + * @param[in] scan_param The scan parameter want to be set * * @return none * * @note none */ - void setScanParam(const struct bt_le_scan_param &scan_param); + void setScanParam(const bt_le_scan_param_t &scan_param); /** * @brief Get the scan parameter * * @param none * - * @return const struct bt_le_scan_param* The scan parameter that current used + * @return const bt_le_scan_param_t* The scan parameter that current used * * @note none */ - const struct bt_le_scan_param* getScanParam(); + const bt_le_scan_param_t* getScanParam(); /** * @brief Discover the peripheral device profile @@ -108,26 +108,26 @@ class BLECentralRole: public BLERoleBase { /** * @brief Add an attribute to the BLE Central Device * - * @param attribute Attribute to add to Central + * @param[in] attribute Attribute to add to Central * - * @return none + * @return BleStatus indicating success or error * * @note The attribute will used for discover the peripheral handler */ - void addAttribute(BLEAttribute& attribute); + BleStatus addAttribute(BLEAttribute& attribute); /** - * Provide a function to be called when events related to this Device are raised + * @brief Provide a function to be called when events related to this Device are raised * - * @param event Event type for callback - * @param callback Pointer to callback function to invoke when an event occurs. + * @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 advcb Pointer to callback function to invoke when advertisement received + * @param[in] advcb Pointer to callback function to invoke when advertisement received * * @return none * @@ -138,13 +138,13 @@ class BLECentralRole: public BLERoleBase { /** * @brief Get BLE peripheral helper by conntion * - * @param conn The connection object + * @param[in] conn The connection object * * @return BLEPeripheralHelper* The BLE peripheral helper * * @note none */ - BLEPeripheralHelper* peripheral(struct bt_conn *conn); + BLEPeripheralHelper* peripheral(bt_conn_t *conn); /** * @brief Get BLE central helper that for APP use @@ -197,60 +197,60 @@ class BLECentralRole: public BLERoleBase { /** * @brief Handle the connected event * - * @param conn The object that established the connection + * @param[in] conn The object that established the connection * - * @param err The code of the process + * @param[in] err The code of the process * * @return none * * @note none */ - void handleConnectEvent(struct bt_conn *conn, uint8_t err); + void handleConnectEvent(bt_conn_t *conn, uint8_t err); /** * @brief Handle the disconnected event * - * @param conn The object that lost the connection + * @param[in] conn The object that lost the connection * - * @param reason The link lost reason + * @param[in] reason The link lost reason * * @return none * * @note none */ - void handleDisconnectEvent(struct bt_conn *conn, uint8_t reason); + void handleDisconnectEvent(bt_conn_t *conn, uint8_t reason); /** * @brief Handle the conntion update request * - * @param conn The connection object that need to process the update request + * @param[in] conn The connection object that need to process the update request * - * @param interval The connection interval + * @param[in] interval The connection interval (N*1.25)ms * - * @param latency The connection latency + * @param[in] latency The connection latency * - * @param timeout The connection timeout + * @param[in] timeout The connection timeout (N*10)ms * * @return none * * @note none */ - void handleParamUpdated(struct bt_conn *conn, + void handleParamUpdated(bt_conn_t *conn, uint16_t interval, - uint16_t latency, - uint16_t timeout); + uint16_t latency, + uint16_t timeout); /** * @brief Handle the advertisement * - * @param addr The device's MAC address that send out ADV + * @param[in] addr The device's MAC address that send out ADV * - * @param rssi The antenna's RSSI + * @param[in] rssi The antenna's RSSI * - * @param type The advertise type + * @param[in] type The advertise type * - * @param ad The advertisement RAW data + * @param[in] ad The advertisement RAW data * - * @param len The RAW data's length + * @param[in] len The RAW data's length * * @return none * @@ -266,7 +266,7 @@ class BLECentralRole: public BLERoleBase { ~BLECentralRole(); BLEPeripheralHelper* _peripherial[BLE_MAX_CONN_CFG]; BLECentralHelper _central; - struct bt_le_scan_param _scan_param; + bt_le_scan_param_t _scan_param; static BLECentralRole* _ble_central_ins; ble_advertise_handle_cb_t _adv_event_handle; diff --git a/libraries/CurieBLE/src/BLECharacteristic.cpp b/libraries/CurieBLE/src/BLECharacteristic.cpp index 5c810a5f..3e46e0a1 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.cpp +++ b/libraries/CurieBLE/src/BLECharacteristic.cpp @@ -21,24 +21,25 @@ #include "BLEPeripheralHelper.h" #include "internal/ble_client.h" -uint8_t profile_notify_process (struct bt_conn *conn, - struct bt_gatt_subscribe_params *params, - const void *data, uint16_t length); -uint8_t profile_read_rsp_process(struct bt_conn *conn, int err, - struct bt_gatt_read_params *params, +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; -struct bt_uuid_16 BLECharacteristic::_gatt_chrc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CHRC_VAL}; -struct bt_uuid_16 BLECharacteristic::_gatt_ccc_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_CCC_VAL}; +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), @@ -46,8 +47,12 @@ BLECharacteristic::BLECharacteristic(const char* uuid, _attr_chrc_value(NULL), _attr_cccd(NULL) { - _value_size = maxLength > BLE_MAX_ATTR_DATA_LEN ? BLE_MAX_ATTR_DATA_LEN : maxLength; + _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)); @@ -246,12 +251,12 @@ BLECharacteristic::numNotifyAttributes(void) { return _numNotifyAttributes; } -struct _bt_gatt_ccc* BLECharacteristic::getCccCfg(void) +_bt_gatt_ccc_t* BLECharacteristic::getCccCfg(void) { return &_ccc_value; } -struct bt_gatt_chrc* BLECharacteristic::getCharacteristicAttValue(void) +bt_gatt_chrc_t* BLECharacteristic::getCharacteristicAttValue(void) { return &_gatt_chrc; } @@ -270,32 +275,32 @@ uint8_t BLECharacteristic::getPermission(void) return perm; } -struct bt_uuid* BLECharacteristic::getCharacteristicAttributeUuid(void) +bt_uuid_t* BLECharacteristic::getCharacteristicAttributeUuid(void) { - return (struct bt_uuid*) &_gatt_chrc_uuid; + return (bt_uuid_t*) &_gatt_chrc_uuid; } -struct bt_uuid* BLECharacteristic::getClientCharacteristicConfigUuid(void) +bt_uuid_t* BLECharacteristic::getClientCharacteristicConfigUuid(void) { - return (struct bt_uuid*) &_gatt_ccc_uuid; + return (bt_uuid_t*) &_gatt_ccc_uuid; } -void BLECharacteristic::addCharacteristicDeclaration(struct bt_gatt_attr *gatt_attr) +void BLECharacteristic::addCharacteristicDeclaration(bt_gatt_attr_t *gatt_attr) { _attr_chrc_declaration = gatt_attr; } -void BLECharacteristic::addCharacteristicValue(struct bt_gatt_attr *gatt_attr) +void BLECharacteristic::addCharacteristicValue(bt_gatt_attr_t *gatt_attr) { _attr_chrc_value = gatt_attr; } -void BLECharacteristic::addCharacteristicConfigDescriptor(struct bt_gatt_attr *gatt_attr) +void BLECharacteristic::addCharacteristicConfigDescriptor(bt_gatt_attr_t *gatt_attr) { _attr_cccd = gatt_attr; } -void BLECharacteristic::discover(struct bt_gatt_discover_params *params) +void BLECharacteristic::discover(bt_gatt_discover_params_t *params) { params->type = BT_GATT_DISCOVER_CHARACTERISTIC; params->uuid = this->uuid(); @@ -306,8 +311,8 @@ void BLECharacteristic::discover(struct bt_gatt_discover_params *params) } -void BLECharacteristic::discover(const struct bt_gatt_attr *attr, - struct bt_gatt_discover_params *params) +void BLECharacteristic::discover(const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) { if (!attr) { @@ -340,7 +345,7 @@ void BLECharacteristic::discover(const struct bt_gatt_attr *attr, } } -struct bt_gatt_subscribe_params *BLECharacteristic::getSubscribeParams() +bt_gatt_subscribe_params_t *BLECharacteristic::getSubscribeParams() { return &_sub_params; } @@ -348,7 +353,7 @@ struct bt_gatt_subscribe_params *BLECharacteristic::getSubscribeParams() bool BLECharacteristic::read(BLEPeripheralHelper &peripheral) { int retval = 0; - struct bt_conn* conn = NULL; + bt_conn_t* conn = NULL; if (_reading) { // Already in reading state @@ -387,7 +392,7 @@ bool BLECharacteristic::write(BLEPeripheralHelper &peripheral, uint16_t length) { int retval = 0; - struct bt_conn* conn = NULL; + bt_conn_t* conn = NULL; conn = bt_conn_lookup_addr_le(peripheral.bt_le_address()); if (NULL == conn) @@ -403,4 +408,32 @@ bool BLECharacteristic::write(BLEPeripheralHelper &peripheral, return (0 == retval); } +void BLECharacteristic::setBuffer(BLEHelper& blehelper, + const uint8_t value[], + uint16_t length, + uint16_t offset) +{ + if (length + offset > _value_size) { + // Ignore the data + return; + } + + memcpy(_value_buffer + offset, value, length); +} + +void BLECharacteristic::syncupBuffer2Value(BLEHelper& blehelper) +{ + setValue(blehelper, _value_buffer, _value_size); +} + +void BLECharacteristic::discardBuffer() +{ + memcpy(_value_buffer, _value, _value_size); +} + +bool BLECharacteristic::longCharacteristic() +{ + return (_value_size > BLE_MAX_ATTR_DATA_LEN); +} + diff --git a/libraries/CurieBLE/src/BLECharacteristic.h b/libraries/CurieBLE/src/BLECharacteristic.h index a867e296..58274f67 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.h +++ b/libraries/CurieBLE/src/BLECharacteristic.h @@ -65,9 +65,9 @@ class BLECharacteristic : public BLEAttribute { /** * Constructor for BLE Characteristic * - * @param uuid 16-bit or 128-bit UUID (in string form) defined by BLE standard - * @param properties Characteristic property mask - * @param maxLength Maximum data length required for characteristic value (<= BLE_MAX_ATTR_DATA_LEN) + * @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) */ BLECharacteristic(const char* uuid, const unsigned char properties, @@ -76,9 +76,9 @@ class BLECharacteristic : public BLEAttribute { /** * Constructor for BLE Characteristic * - * @param uuid 16-bit or 128-bit UUID (in string form) defined by BLE standard - * @param properties Characteristic property mask - * @param value String value for characteristic (string length (<= BLE_MAX_ATTR_DATA_LEN)) + * @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)) */ BLECharacteristic(const char* uuid, const unsigned char properties, @@ -89,8 +89,8 @@ class BLECharacteristic : public BLEAttribute { /** * Set the current value of the Characteristic * - * @param value New value to set, as a byte array. Data is stored in internal copy. - * @param length Length, in bytes, of valid data in the array to write. + * @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 @@ -100,9 +100,9 @@ class BLECharacteristic : public BLEAttribute { /** * Set the current value of the Characteristic * - * @param central The central device that update the value. - * @param value New value to set, as a byte array. Data is stored in internal copy. - * @param length Length, in bytes, of valid data in the array to write. + * @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 @@ -156,8 +156,8 @@ class BLECharacteristic : public BLEAttribute { /** * Provide a function to be called when events related to this Characteristic are raised * - * @param event Event type to set event handler for - * @param callback Pointer to callback function to invoke when the event occurs. + * @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); @@ -175,7 +175,7 @@ class BLECharacteristic : public BLEAttribute { /** * @brief Schedule the read request to read the characteristic in peripheral * - * @param peripheral The peripheral device that want to read. + * @param[in] peripheral The peripheral device that want to read. * * @return bool Indicate the success or error * @@ -186,9 +186,9 @@ class BLECharacteristic : public BLEAttribute { /** * @brief Schedule the write request to update the characteristic in peripheral * - * @param peripheral The peripheral device that want to be updated - * @param value New value to set, as a byte array. Data is stored in internal copy. - * @param length Length, in bytes, of valid data in the array to write. + * @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 @@ -201,10 +201,26 @@ class BLECharacteristic : public BLEAttribute { 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(struct bt_gatt_attr *gatt_attr); - void addCharacteristicValue(struct bt_gatt_attr *gatt_attr); - void addCharacteristicConfigDescriptor(struct bt_gatt_attr *gatt_attr); + void addCharacteristicDeclaration(bt_gatt_attr_t *gatt_attr); + void addCharacteristicValue(bt_gatt_attr_t *gatt_attr); + void addCharacteristicConfigDescriptor(bt_gatt_attr_t *gatt_attr); + + bool longCharacteristic(); + + void setBuffer(BLEHelper& blehelper, + const uint8_t value[], + uint16_t length, + uint16_t offset); + void discardBuffer(); + void syncupBuffer2Value(BLEHelper& blehelper); /** * @brief Get the characteristic value handle @@ -233,10 +249,10 @@ class BLECharacteristic : public BLEAttribute { void setUserDescription(BLEDescriptor *descriptor); void setPresentationFormat(BLEDescriptor *descriptor); - struct _bt_gatt_ccc* getCccCfg(void); - struct bt_gatt_chrc* getCharacteristicAttValue(void); - static struct bt_uuid* getCharacteristicAttributeUuid(void); - static struct bt_uuid* getClientCharacteristicConfigUuid(void); + _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 characteristic permission @@ -252,38 +268,38 @@ class BLECharacteristic : public BLEAttribute { /** * @brief For central to discover the peripherial profile * - * @param attr The discover response + * @param[in] attr The discover response * - * @param params The discover parameter that need to fill + * @param[in] params The discover parameter that need to fill * * @return none * * @note Only for central */ - void discover(const struct bt_gatt_attr *attr, - struct bt_gatt_discover_params *params); + void discover(const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); /** * @brief For central to discover the peripherial profile * - * @param params The discover parameter that need to fill + * @param[in] params The discover parameter that need to fill * * @return none * * @note Only for central */ - void discover(struct bt_gatt_discover_params *params); + void discover(bt_gatt_discover_params_t *params); /** * @brief Get the subscribe parameter * * @param none * - * @return struct bt_gatt_subscribe_params * the subscribe parameter + * @return bt_gatt_subscribe_params_t * the subscribe parameter * * @note Only for central */ - struct bt_gatt_subscribe_params* getSubscribeParams(); + bt_gatt_subscribe_params_t* getSubscribeParams(); private: void _setValue(const uint8_t value[], uint16_t length); @@ -291,31 +307,32 @@ class BLECharacteristic : public BLEAttribute { private: static unsigned char _numNotifyAttributes; - static struct bt_uuid_16 _gatt_chrc_uuid; - static struct bt_uuid_16 _gatt_ccc_uuid; + 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; - struct bt_gatt_ccc_cfg _ccc_cfg; - struct _bt_gatt_ccc _ccc_value; - struct bt_gatt_chrc _gatt_chrc; + 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; - struct bt_gatt_attr *_attr_chrc_declaration; - struct bt_gatt_attr *_attr_chrc_value; - struct bt_gatt_attr *_attr_cccd; + bt_gatt_attr_t *_attr_chrc_declaration; + bt_gatt_attr_t *_attr_chrc_value; + bt_gatt_attr_t *_attr_cccd; // For central device to subscribe the Notification/Indication - struct bt_gatt_subscribe_params _sub_params; + bt_gatt_subscribe_params_t _sub_params; bool _reading; - struct bt_gatt_read_params _read_params; + bt_gatt_read_params_t _read_params; BLECharacteristicEventHandler _event_handlers[BLECharacteristicEventLast]; }; diff --git a/libraries/CurieBLE/src/BLECommon.h b/libraries/CurieBLE/src/BLECommon.h index 00c31342..9c26bad3 100644 --- a/libraries/CurieBLE/src/BLECommon.h +++ b/libraries/CurieBLE/src/BLECommon.h @@ -35,14 +35,16 @@ #define BLE_ADDR_LEN 6 -#define MAX_UUID_SIZE 16 - +#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, * we will just limit this to 20 bytes for now, which will fit in a single packet */ -#define BLE_MAX_ATTR_DATA_LEN 20 +#define BLE_MAX_ATTR_DATA_LEN 20 +#define BLE_MAX_ATTR_LONGDATA_LEN 512 /* Default device name prefix, applied only if user does not provide a name * If a factory-configured MAC address is defined, the last 2 bytes of the @@ -54,19 +56,20 @@ /** BLE response/event status codes. */ enum BLE_STATUS { - BLE_STATUS_SUCCESS = 0, /**< General BLE Success code */ - 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_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_GAP_BASE = 0x100, /**< GAP specific error base */ - BLE_STATUS_GATT_BASE = 0x200, /**< GATT specific Error base */ + BLE_STATUS_SUCCESS = 0, /**< General BLE Success code */ + 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_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_GAP_BASE = 0x100, /**< GAP specific error base */ + BLE_STATUS_GATT_BASE = 0x200, /**< GATT specific Error base */ }; typedef uint16_t ble_status_t; /**< Response and event BLE service status type @ref BLE_STATUS */ @@ -75,7 +78,37 @@ typedef ble_status_t BleStatus; #define BLE_MAX_CONN_CFG 2 -typedef bool (*ble_advertise_handle_cb_t)(uint8_t type, const uint8_t *data, - uint8_t data_len, void *user_data); - +typedef bool (*ble_advertise_handle_cb_t)(uint8_t type, const uint8_t *dataPtr, + uint8_t data_len, const bt_addr_le_t *addrPtr); + + +typedef struct ble_conn_param { + float interval_min; // millisecond 7.5 - 4000ms + float interval_max; // millisecond 7.5 - 4000ms + uint16_t latency; // 0x0000 - 0x01F4 + uint16_t timeout; // millisecond 100 - 32000ms +}ble_conn_param_t; +#ifdef __cplusplus +extern "C" { +#endif + +/// Define the structure for app +typedef struct bt_uuid bt_uuid_t; +typedef struct bt_uuid_16 bt_uuid_16_t; +typedef struct bt_uuid_128 bt_uuid_128_t; +typedef struct bt_conn bt_conn_t; +typedef struct bt_gatt_attr bt_gatt_attr_t; +typedef struct bt_gatt_discover_params bt_gatt_discover_params_t; +typedef struct bt_le_scan_param bt_le_scan_param_t; +typedef struct bt_le_conn_param bt_le_conn_param_t; +typedef struct bt_gatt_subscribe_params bt_gatt_subscribe_params_t; +typedef struct bt_gatt_read_params bt_gatt_read_params_t; +typedef struct _bt_gatt_ccc _bt_gatt_ccc_t; +typedef struct bt_gatt_chrc bt_gatt_chrc_t; +typedef struct bt_gatt_ccc_cfg bt_gatt_ccc_cfg_t; +typedef struct bt_data bt_data_t; + +#ifdef __cplusplus +} +#endif #endif // _BLE_COMMON_H_INCLUDED diff --git a/libraries/CurieBLE/src/BLEDescriptor.cpp b/libraries/CurieBLE/src/BLEDescriptor.cpp index d611d471..889cd58d 100644 --- a/libraries/CurieBLE/src/BLEDescriptor.cpp +++ b/libraries/CurieBLE/src/BLEDescriptor.cpp @@ -46,7 +46,7 @@ BLEDescriptor::BLEDescriptor(const char* uuid, const char* value) : } const unsigned char* -BLEDescriptor::BLEDescriptor::value() const +BLEDescriptor::value() const { return _value; } @@ -63,3 +63,34 @@ BLEDescriptor::operator[] (int offset) const return _value[offset]; } +void BLEDescriptor::discover(const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) +{ + 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; + } + +} + + +void BLEDescriptor::discover(bt_gatt_discover_params_t *params) +{ + params->type = BT_GATT_DISCOVER_DESCRIPTOR; + params->uuid = this->uuid(); + // Start discovering + _discoverying = true; +} + + diff --git a/libraries/CurieBLE/src/BLEDescriptor.h b/libraries/CurieBLE/src/BLEDescriptor.h index 498035a0..95f75ed5 100644 --- a/libraries/CurieBLE/src/BLEDescriptor.h +++ b/libraries/CurieBLE/src/BLEDescriptor.h @@ -30,9 +30,9 @@ class BLEDescriptor : public BLEAttribute { /** * Constructor for BLE Descriptor * - * @param uuid 16-bit UUID (in string form) defined by BLE standard - * @param value Value of descriptor, as a byte array. Data is stored in internal copy. - * @param valueLength Data length required for descriptor value (<= BLE_MAX_ATTR_DATA_LEN) + * @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); @@ -41,8 +41,8 @@ class BLEDescriptor : public BLEAttribute { /** * Constructor for BLE Descriptor * - * @param uuid 16-bit UUID (in string form) defined by BLE standard - * @param value String value of descriptor. Data is stored in internal copy. + * @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); @@ -62,6 +62,32 @@ class BLEDescriptor : public BLEAttribute { unsigned short valueLength(void) const; + /** + * @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); + + unsigned char operator[] (int offset) const; protected: diff --git a/libraries/CurieBLE/src/BLEHelper.cpp b/libraries/CurieBLE/src/BLEHelper.cpp index b756f5d4..e3e1caa6 100644 --- a/libraries/CurieBLE/src/BLEHelper.cpp +++ b/libraries/CurieBLE/src/BLEHelper.cpp @@ -16,29 +16,7 @@ BLEHelper::BLEHelper() BLEHelper::~BLEHelper() { - #if 0 - if (NULL != _conn) - { - bt_conn_unref(_conn); - } - #endif -} - -#if 0 -void BLEHelper::setConn(struct bt_conn *conn) -{ - if (conn == _conn) - { - return; - } - - if (NULL != _conn) - { - bt_conn_unref(_conn); - } - _conn = conn; } -#endif BLEHelper::operator bool() const { @@ -106,8 +84,8 @@ BLEHelper::poll() { } void -BLEHelper::setAddress(bt_addr_le_t address) { - _address = address; +BLEHelper::setAddress(const bt_addr_le_t &address) { + memcpy(&_address, &address, sizeof(bt_addr_le_t)); } void @@ -115,21 +93,68 @@ BLEHelper::clearAddress() { memset(&_address, 0x00, sizeof(_address)); } -const struct bt_le_conn_param *BLEHelper::getConnParams() +void BLEHelper::getConnParams(ble_conn_param_t &user_conn_params) { - return &_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::setConnParames(uint16_t intervalmin, - uint16_t intervalmax, - uint16_t latency, - uint16_t timeout) +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 index f844eea6..f0c6bc21 100644 --- a/libraries/CurieBLE/src/BLEHelper.h +++ b/libraries/CurieBLE/src/BLEHelper.h @@ -31,12 +31,21 @@ class BLEHelper { virtual bool connected(void) = 0; /** - * Get the address of the Central in string form + * Get the address of the BLE in string format * - * @return const char* address of the Central in string form + * @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 @@ -53,25 +62,118 @@ class BLEHelper { bool operator==(const BLEHelper& rhs) const; bool operator==(const bt_addr_le_t& rhs) const; bool operator!=(const BLEHelper& rhs) const; - void operator=(const BLEHelper& rhs); - void setConn(struct bt_conn *conn); - const struct bt_le_conn_param *getConnParams(); - void setConnParames(uint16_t intervalmin, - uint16_t intervalmax, - uint16_t latency, - uint16_t timeout); + /** + * @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(bt_addr_le_t address); + void setAddress(const bt_addr_le_t &address); void clearAddress(); BLEHelper(); virtual ~BLEHelper(); private: - bt_addr_le_t _address; - //struct bt_conn *_conn; - struct bt_le_conn_param _conn_params; + 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 ca40137b..efbb77f6 100644 --- a/libraries/CurieBLE/src/BLEPeripheral.cpp +++ b/libraries/CurieBLE/src/BLEPeripheral.cpp @@ -30,9 +30,7 @@ BLEPeripheral::BLEPeripheral(void) : memset(_adv_data, 0x00, sizeof(_adv_data)); // Default Advertising parameter - setAdvertisingParam(BT_LE_ADV_IND , - 0xA0, - 0xF0); + setConnectable(true); } BLEPeripheral::~BLEPeripheral(void) @@ -70,7 +68,7 @@ BLEPeripheral::end() } void -BLEPeripheral::setAdvertisedServiceUuid(const struct bt_uuid* advertisedServiceUuid) +BLEPeripheral::setAdvertisedServiceUuid(const bt_uuid_t* advertisedServiceUuid) { _advertise_service_uuid = advertisedServiceUuid; } @@ -82,7 +80,7 @@ BLEPeripheral::setLocalName(const char* localName) } void -BLEPeripheral::setAdvertisedServiceData(const struct bt_uuid* serviceDataUuid, +BLEPeripheral::setAdvertisedServiceData(const bt_uuid_t* serviceDataUuid, uint8_t* serviceData, uint8_t serviceDataLength) { @@ -91,13 +89,30 @@ BLEPeripheral::setAdvertisedServiceData(const struct bt_uuid* serviceDataUuid, _service_data_length = serviceDataLength; } -void BLEPeripheral::setAdvertisingParam(uint8_t type, - uint16_t interval_min, - uint16_t interval_max) +void +BLEPeripheral::setAdvertisingInterval(float interval_min, + float interval_max) { - BLEPeripheralRole::instance()->setAdvertisingParam(type, - interval_min, - interval_max); + 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); +} + +void +BLEPeripheral::setAdvertisingInterval(float advertisingInterval) +{ + setAdvertisingInterval(advertisingInterval, advertisingInterval); +} + +void +BLEPeripheral::setConnectable(bool connectable) +{ + uint8_t type = BT_LE_ADV_IND; + if (connectable == false) + { + type = BT_LE_ADV_NONCONN_IND; + } + BLEPeripheralRole::instance()->setAdvertisingType(type); } void @@ -143,9 +158,10 @@ BLEPeripheral::connected() return BLEPeripheralRole::instance()->connected(); } -void BLEPeripheral::addAttribute(BLEAttribute& attribute) +BleStatus +BLEPeripheral::addAttribute(BLEAttribute& attribute) { - BLEPeripheralRole::instance()->addAttribute(attribute); + return BLEPeripheralRole::instance()->addAttribute(attribute); } @@ -172,14 +188,14 @@ BLEPeripheral::_advDataInit(void) if (BT_UUID_TYPE_16 == _advertise_service_uuid->type) { //UINT16_TO_LESTREAM(adv_tmp, uuid.uuid16); - data = (uint8_t *)&(((struct bt_uuid_16 *)_advertise_service_uuid)->val); - length = sizeof(uint16_t); + 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 = ((struct bt_uuid_128 *)_advertise_service_uuid)->val; - length = MAX_UUID_SIZE; + data = ((bt_uuid_128_t *)_advertise_service_uuid)->val; + length = UUID_SIZE_128; type = BT_DATA_UUID128_ALL; } if (NULL != data) @@ -232,7 +248,7 @@ BLEPeripheral::_advDataInit(void) uint8_t *adv_tmp = _service_data_buf; - UINT16_TO_LESTREAM(adv_tmp, (((struct bt_uuid_16 *)_service_data_uuid)->val)); + UINT16_TO_LESTREAM(adv_tmp, (((bt_uuid_16_t *)_service_data_uuid)->val)); memcpy(adv_tmp, _service_data, _service_data_length); lengthTotal += block_len; @@ -272,3 +288,7 @@ BLEPeripheral::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 67b6f07b..33b8bef7 100644 --- a/libraries/CurieBLE/src/BLEPeripheral.h +++ b/libraries/CurieBLE/src/BLEPeripheral.h @@ -47,17 +47,17 @@ class BLEPeripheral{ /** * Set the service UUID that the BLE Peripheral Device advertises * - * @param advertisedServiceUuid 16-bit or 128-bit UUID to advertis + * @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 struct bt_uuid* advertisedServiceUuid); + void setAdvertisedServiceUuid(const bt_uuid_t* advertisedServiceUuid); /** * Set the local name that the BLE Peripheral Device advertises * - * @param localName local name to advertise + * @param[in] localName local name to advertise * * @note This method must be called before the begin method */ @@ -66,14 +66,14 @@ class BLEPeripheral{ /** * Set the Service Data that the BLE Peripheral Device advertises * - * @param serviceDataUuid 16-bit Service UUID for this Service Data + * @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 serviceData binary array of Service Data. + * @param[in] serviceData binary array of Service Data. * - * @param serviceDataLength length (bytes) of serviceData[] + * @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. @@ -85,29 +85,53 @@ class BLEPeripheral{ * the service data will silently not be copied * into the advertising block. */ - void setAdvertisedServiceData(const struct bt_uuid* serviceDataUuid, + 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); + /** - * Set the ADV parameters about the ADV-Type and interval + * @brief Set advertising interval + * + * @param[in] interval_min Minimum Advertising Interval (millisecond) * - * @param type Advertising types + * @param[in] interval_max Maximum Advertising Interval (millisecond) * - * @param interval_min Minimum Advertising Interval (N * 0.625) + * @return none * - * @param interval_max Maximum Advertising Interval (N * 0.625) + * @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 * - * @note none + * @return none + * + * @note none */ - void setAdvertisingParam(uint8_t type, - uint16_t interval_min, - uint16_t interval_max); + 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 device User-defined name string for this device. Truncated if + * @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 @@ -120,7 +144,7 @@ class BLEPeripheral{ * See https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml * for available options. * - * @param appearance Appearance category identifier as defined by BLE Standard + * @param[in] appearance Appearance category identifier as defined by BLE Standard * * @return BleStatus indicating success or error * @@ -131,8 +155,8 @@ class BLEPeripheral{ /** * Set the min and max connection interval BLE Peripheral Device * - * @param minConnInterval Minimum connection interval (1.25 ms units), minimum 0x0006 (7.5ms) - * @param maxConnInterval Maximum connection interval (1.25 ms units), maximum 0x095f (2998.75ms) + * @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 */ @@ -141,19 +165,20 @@ class BLEPeripheral{ /** * Add an attribute to the BLE Peripheral Device * - * @param attribute Attribute to add to Peripheral + * @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 */ - void addAttribute(BLEAttribute& attribute); + BleStatus addAttribute(BLEAttribute& attribute); /** * Provide a function to be called when events related to this Device are raised * - * @param event Event type for callback - * @param callback Pointer to callback function to invoke when an event occurs. + * @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); @@ -195,35 +220,38 @@ class BLEPeripheral{ */ bool connected(void); - /** - * @brief Init the ADV data and start send advertisement - * - * @param none - * - * @return BleStatus 0 - Success. Others - error code - * - * @note none - */ + /** + * @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); - + + /** + * @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: - void handleConnectEvent(struct bt_conn *conn, uint8_t err); - void handleDisconnectEvent(struct bt_conn *conn, uint8_t reason); - void handleParamUpdated(struct bt_conn *conn, - uint16_t interval, - uint16_t latency, - uint16_t timeout); private: @@ -234,17 +262,17 @@ class BLEPeripheral{ private: const char* _local_name; - const struct bt_uuid* _service_data_uuid; + 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 struct bt_uuid* _advertise_service_uuid; + const bt_uuid_t* _advertise_service_uuid; uint8_t _adv_type; - struct bt_data _adv_data[4]; + bt_data_t _adv_data[4]; size_t _adv_data_idx; }; diff --git a/libraries/CurieBLE/src/BLEPeripheralHelper.cpp b/libraries/CurieBLE/src/BLEPeripheralHelper.cpp index 65d54c3d..2a13c03c 100644 --- a/libraries/CurieBLE/src/BLEPeripheralHelper.cpp +++ b/libraries/CurieBLE/src/BLEPeripheralHelper.cpp @@ -24,19 +24,19 @@ BLEAttribute *BLEPeripheralHelper::attribute(uint16_t handle) return _profile.attribute(handle); } -BLEAttribute *BLEPeripheralHelper::attribute(struct bt_gatt_subscribe_params *params) +BLEAttribute *BLEPeripheralHelper::attribute(bt_gatt_subscribe_params_t *params) { return _profile.attribute(params); } -void BLEPeripheralHelper::discover(const struct bt_gatt_attr *attr) +uint8_t BLEPeripheralHelper::discover(const bt_gatt_attr_t *attr) { // Not allow to call the discover if (NULL == _central) { - return; + return BT_GATT_ITER_STOP; } - _profile.discover(attr); + return _profile.discover(attr); } void BLEPeripheralHelper::discover() @@ -62,7 +62,7 @@ BLEPeripheralHelper::~BLEPeripheralHelper() bool BLEPeripheralHelper::disconnect(void) { int err = 0; - struct bt_conn* conn = bt_conn_lookup_addr_le(this->bt_le_address()); + bt_conn_t* conn = bt_conn_lookup_addr_le(this->bt_le_address()); if (NULL == conn) { return false; @@ -75,7 +75,7 @@ bool BLEPeripheralHelper::disconnect(void) bool BLEPeripheralHelper::connected(void) { - struct bt_conn* conn = bt_conn_lookup_addr_le(this->bt_le_address()); + bt_conn_t* conn = bt_conn_lookup_addr_le(this->bt_le_address()); if (NULL == conn) { return false; @@ -94,9 +94,9 @@ void BLEPeripheralHelper::linkLost(void) } } -void BLEPeripheralHelper::addAttribute(BLEAttribute& attribute) +BleStatus BLEPeripheralHelper::addAttribute(BLEAttribute& attribute) { - _profile.addAttribute(attribute); + return _profile.addAttribute(attribute); } int BLEPeripheralHelper::registerProfile() diff --git a/libraries/CurieBLE/src/BLEPeripheralHelper.h b/libraries/CurieBLE/src/BLEPeripheralHelper.h index 2d29a210..bdb7c719 100644 --- a/libraries/CurieBLE/src/BLEPeripheralHelper.h +++ b/libraries/CurieBLE/src/BLEPeripheralHelper.h @@ -47,28 +47,110 @@ class BLEPeripheralHelper : public BLEHelper { /** * Add an attribute to the BLE Peripheral helper * - * @param attribute Attribute to add to Peripheral + * @param[in] attribute Attribute to add to Peripheral + * + * @return BleStatus indicating success or error * * @note This method must be called before the begin method */ - void addAttribute(BLEAttribute& attribute); + BleStatus addAttribute(BLEAttribute& attribute); - BLEAttribute *attribute(struct bt_gatt_subscribe_params *params); + /** + * @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); /** - * For central to discover the profile + * @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(); - void discover(const struct bt_gatt_attr *attr); + + /** + * @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); - // For peripheral to register the tree + /** + * @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); - // Get value handle. - // 0 is invalid + /** + * @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: diff --git a/libraries/CurieBLE/src/BLEPeripheralRole.cpp b/libraries/CurieBLE/src/BLEPeripheralRole.cpp index dc13b566..ff5baad6 100644 --- a/libraries/CurieBLE/src/BLEPeripheralRole.cpp +++ b/libraries/CurieBLE/src/BLEPeripheralRole.cpp @@ -43,6 +43,11 @@ BLEPeripheralRole::BLEPeripheralRole(void) : { 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) @@ -64,9 +69,10 @@ bool BLEPeripheralRole::begin() // Set device name setDeviceName(); + delay(4); // Register profile _peripheral.registerProfile(); - delay(2); // Temp solution for send data fast will makes ADV data set failed + delay(8); // Temp solution for send data fast will makes ADV data set failed return true; } @@ -128,14 +134,16 @@ bool BLEPeripheralRole::disconnect() { BleStatus status = BLE_STATUS_WRONG_STATE; + int err; if (BLE_PERIPH_STATE_CONNECTED == _state) { - struct bt_conn *central_conn = bt_conn_lookup_addr_le(_central.bt_le_address()); + bt_conn_t *central_conn = bt_conn_lookup_addr_le(_central.bt_le_address()); if (NULL != central_conn) { - status = bt_conn_disconnect (central_conn, - BT_HCI_ERR_REMOTE_USER_TERM_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); } } @@ -158,9 +166,10 @@ BLEPeripheralRole::connected() return _central; } -void BLEPeripheralRole::addAttribute(BLEAttribute& attribute) +BleStatus +BLEPeripheralRole::addAttribute(BLEAttribute& attribute) { - _peripheral.addAttribute(attribute); + return _peripheral.addAttribute(attribute); } BleStatus @@ -183,9 +192,9 @@ BLEPeripheralRole::stopAdvertising() } BleStatus -BLEPeripheralRole::startAdvertising(const struct bt_data *ad, +BLEPeripheralRole::startAdvertising(const bt_data_t *ad, size_t ad_len, - const struct bt_data *sd, + const bt_data_t *sd, size_t sd_len) { int ret; @@ -204,16 +213,24 @@ BLEPeripheralRole::startAdvertising(const struct bt_data *ad, return BLE_STATUS_SUCCESS; } -void BLEPeripheralRole::setAdvertisingParam(uint8_t type, - uint16_t interval_min, - uint16_t interval_max) +void BLEPeripheralRole::setAdvertisingInterval(uint16_t advertisingInterval) +{ + setAdvertisingInterval(advertisingInterval, advertisingInterval); +} + +void BLEPeripheralRole::setAdvertisingInterval(uint16_t interval_min, + uint16_t interval_max) { - _adv_param.addr_type = _local_bda.type; - _adv_param.type = type; _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) { @@ -235,7 +252,7 @@ BLEPeripheralRole::stop(void) return BLE_STATUS_SUCCESS; } -void BLEPeripheralRole::handleConnectEvent(struct bt_conn *conn, uint8_t err) +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); @@ -245,12 +262,15 @@ void BLEPeripheralRole::handleConnectEvent(struct bt_conn *conn, uint8_t 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(struct bt_conn *conn, uint8_t reason) +void BLEPeripheralRole::handleDisconnectEvent(bt_conn_t *conn, uint8_t reason) { - struct bt_conn *central_conn = bt_conn_lookup_addr_le(_central.bt_le_address()); + 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); @@ -262,17 +282,23 @@ void BLEPeripheralRole::handleDisconnectEvent(struct bt_conn *conn, uint8_t reas { bt_conn_unref(central_conn); } + + if (BLE_PERIPH_STATE_CONNECTED == _state) + _state = BLE_PERIPH_STATE_ADVERTISING; } -void BLEPeripheralRole::handleParamUpdated(struct bt_conn *conn, - uint16_t interval, - uint16_t latency, - uint16_t timeout) +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 index 0fcbd40e..87c311b5 100644 --- a/libraries/CurieBLE/src/BLEPeripheralRole.h +++ b/libraries/CurieBLE/src/BLEPeripheralRole.h @@ -46,7 +46,7 @@ class BLEPeripheralRole: public BLERoleBase{ * * If device name is not set, a default name will be used instead * - * @param deviceName User-defined name string for this device. Truncated if + * @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 @@ -56,8 +56,8 @@ class BLEPeripheralRole: public BLERoleBase{ /** * Set the min and max connection interval BLE Peripheral Device * - * @param minConnInterval Minimum connection interval (1.25 ms units), minimum 0x0006 (7.5ms) - * @param maxConnInterval Maximum connection interval (1.25 ms units), maximum 0x095f (2998.75ms) + * @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 */ @@ -66,19 +66,19 @@ class BLEPeripheralRole: public BLERoleBase{ /** * Add an attribute to the BLE Peripheral Device * - * @param attribute Attribute to add to Peripheral + * @param[in] attribute Attribute to add to Peripheral * * @return BleStatus indicating success or error * * @note This method must be called before the begin method */ - void addAttribute(BLEAttribute& attribute); + BleStatus addAttribute(BLEAttribute& attribute); /** * Provide a function to be called when events related to this Device are raised * - * @param event Event type for callback - * @param callback Pointer to callback function to invoke when an event occurs. + * @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); @@ -123,50 +123,70 @@ class BLEPeripheralRole: public BLERoleBase{ /** * @brief Start peripheral advertising * - * @param ad The ADV data array + * @param[in] ad The ADV data array * - * @param ad_len The ADV data array length + * @param[in] ad_len The ADV data array length * - * @param sd The Scan response data array + * @param[in] sd The Scan response data array * - * @param sd_len The Scan response data array length + * @param[in] sd_len The Scan response data array length * * @return BleStatus * * @note none */ - BleStatus startAdvertising(const struct bt_data *ad, + BleStatus startAdvertising(const bt_data_t *ad, size_t ad_len, - const struct bt_data *sd, + const bt_data_t *sd, size_t sd_len); - - /** - * @brief Stop send advertisement - * - * @param none - * - * @return none - * - * @note none - */ + + /** + * @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 type Advertising type + * @param[in] interval_min Minimum Advertising Interval (N * 0.625) + * + * @param[in] interval_max Maximum Advertising Interval (N * 0.625) * - * @param interval_min Minimum Advertising Interval (N * 0.625) + * @return none + * + * @note none + */ + void setAdvertisingInterval(uint16_t interval_min, + uint16_t interval_max); + + /** + * @brief Set advertising type * - * @param interval_max Maximum Advertising Interval (N * 0.625) + * @param[in] type Advertising type + * BT_LE_ADV_IND, BT_LE_ADV_NONCONN_IND * * @return none * * @note none */ - void setAdvertisingParam(uint8_t type, - uint16_t interval_min, - uint16_t interval_max); + void setAdvertisingType(uint8_t type); /** * @brief Get BLE Peripheral instance. @@ -184,45 +204,45 @@ class BLEPeripheralRole: public BLERoleBase{ /** * @brief Handle the connected event * - * @param conn The object that established the connection + * @param[in] conn The object that established the connection * - * @param err The code of the process + * @param[in] err The code of the process * * @return none * * @note none */ - void handleConnectEvent(struct bt_conn *conn, uint8_t err); + void handleConnectEvent(bt_conn_t *conn, uint8_t err); /** * @brief Handle the disconnected event * - * @param conn The object that lost the connection + * @param[in] conn The object that lost the connection * - * @param reason The link lost reason + * @param[in] reason The link lost reason * * @return none * * @note none */ - void handleDisconnectEvent(struct bt_conn *conn, uint8_t reason); + void handleDisconnectEvent(bt_conn_t *conn, uint8_t reason); /** * @brief Handle the conntion update request * - * @param conn The connection object that need to process the update request + * @param[in] conn The connection object that need to process the update request * - * @param interval The connection interval + * @param[in] interval The connection interval (N*1.25)ms * - * @param latency The connection latency + * @param[in] latency The connection latency * - * @param timeout The connection timeout + * @param[in] timeout The connection timeout (N*10)ms * * @return none * * @note none */ - void handleParamUpdated(struct bt_conn *conn, + void handleParamUpdated(bt_conn_t *conn, uint16_t interval, uint16_t latency, uint16_t timeout); diff --git a/libraries/CurieBLE/src/BLEProfile.cpp b/libraries/CurieBLE/src/BLEProfile.cpp index 186bf4fa..10f0c91e 100644 --- a/libraries/CurieBLE/src/BLEProfile.cpp +++ b/libraries/CurieBLE/src/BLEProfile.cpp @@ -24,27 +24,54 @@ #include "BLEPeripheralRole.h" // Only for peripheral -ssize_t profile_read_process(struct bt_conn *conn, - const struct bt_gatt_attr *attr, +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(); - if (BLETypeCharacteristic != type) + BLECentralHelper central = BLEPeripheralRole::instance()->central(); + if ((BLETypeCharacteristic != type) || 0 != offset) { return 0; } + blecharacteritic = (BLECharacteristic*)bleattr; - pvalue = blecharacteritic->value(); - return bt_gatt_attr_read(conn, attr, buf, len, offset, pvalue, - blecharacteritic->valueLength()); + blecharacteritic->setValue(*((BLEHelper *)¢ral), (const uint8_t *) buf, len); + + return len; } -// Only for peripheral -ssize_t profile_write_process(struct bt_conn *conn, +ssize_t profile_longwrite_process(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset) @@ -54,21 +81,50 @@ ssize_t profile_write_process(struct bt_conn *conn, BLECharacteristic* blecharacteritic; BLEAttributeType type = bleattr->type(); BLECentralHelper central = BLEPeripheralRole::instance()->central(); - if ((BLETypeCharacteristic != type) || 0 != offset) + if (BLETypeCharacteristic != type) { return 0; } blecharacteritic = (BLECharacteristic*)bleattr; - blecharacteritic->setValue(*((BLEHelper *)¢ral), (const uint8_t *) buf, len); + 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 (struct bt_conn *conn, - struct bt_gatt_subscribe_params *params, +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 @@ -82,18 +138,17 @@ uint8_t profile_notify_process (struct bt_conn *conn, } // Only for central -uint8_t profile_discover_process(struct bt_conn *conn, - const struct bt_gatt_attr *attr, - struct bt_gatt_discover_params *params) +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 - peripheral->discover(attr); - return BT_GATT_ITER_STOP; + return peripheral->discover(attr); } // Only for central -uint8_t profile_read_rsp_process(struct bt_conn *conn, int err, - struct bt_gatt_read_params *params, +uint8_t profile_read_rsp_process(bt_conn_t *conn, int err, + bt_gatt_read_params_t *params, const void *data, uint16_t length) { @@ -140,24 +195,56 @@ BLEProfile::~BLEProfile (void) } } -void BLEProfile::addAttribute (BLEAttribute& attribute) +BleStatus +BLEProfile::addAttribute (BLEAttribute& attribute) { - struct bt_gatt_attr *start; + 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 = (struct bt_gatt_attr *)malloc((BLEAttribute::numAttributes() + BLECharacteristic::numNotifyAttributes()) * sizeof(struct bt_gatt_attr)); - memset(_attr_base, 0x00, ((BLEAttribute::numAttributes() + BLECharacteristic::numNotifyAttributes()) * sizeof(struct bt_gatt_attr))); + _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 = (struct bt_gatt_subscribe_params *)malloc((BLECharacteristic::numNotifyAttributes()) * sizeof(struct bt_gatt_subscribe_params)); - memset(_sub_param, 0x00, ((BLECharacteristic::numNotifyAttributes()) * sizeof(struct bt_gatt_subscribe_params))); + _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; @@ -174,34 +261,46 @@ void BLEProfile::addAttribute (BLEAttribute& attribute) BLECharacteristic* characteritic = (BLECharacteristic*) &attribute; // Characteristic - memset(start, 0, sizeof(struct bt_gatt_attr)); + 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); + + pr_info(LOG_MODULE_BLE, "chrc-%p, uuid type-%d", start, start->uuid->type); + start++; _attr_index++; // Descriptor - memset(start, 0, sizeof(struct bt_gatt_attr)); + memset(start, 0, sizeof(bt_gatt_attr_t)); start->uuid = characteritic->uuid(); start->perm = characteritic->getPermission(); - start->read = profile_read_process; - start->write = profile_write_process; start->user_data = (void*)&attribute; characteritic->addCharacteristicValue(start); - pr_info(LOG_MODULE_BLE, "desc-%p, uuid: 0x%x", start, ((struct bt_uuid_16*) start->uuid)->val); + 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()) { - pr_info(LOG_MODULE_BLE, "cccd-%p", start); // Descriptor - memset(start, 0, sizeof(struct bt_gatt_attr)); + 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; @@ -209,27 +308,42 @@ void BLEProfile::addAttribute (BLEAttribute& attribute) start->user_data = characteritic->getCccCfg(); characteritic->addCharacteristicConfigDescriptor(start); + pr_info(LOG_MODULE_BLE, "cccd-%p", start); + start++; _attr_index++; } } else if (BLETypeService == type) { - pr_info(LOG_MODULE_BLE, "service-%p", start); 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; @@ -244,117 +358,194 @@ int BLEProfile::registerProfile() delay(1000); // End for debug +#endif ret = bt_gatt_register(_attr_base, _attr_index); - pr_info(LOG_MODULE_APP, "%s: ret, %d", __FUNCTION__, ret); + pr_debug(LOG_MODULE_APP, "%s: ret, %d", __FUNCTION__, ret); return ret; } -void BLEProfile::discover(const struct bt_gatt_attr *attr) +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) { - BLEAttribute* attribute = NULL; 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++) { - attribute = _attributes[i]; - if (attribute->discovering()) + // Find the discovering attribute + attribute_tmp = _attributes[i]; + if (attribute_tmp->discovering()) { - if (NULL != attr) + if (NULL == attr) + { + attribute_tmp->discover(attr, &_discover_params); + break; + } + // Discover success + switch (_discover_params.type) { - // Discover success - switch (_discover_params.type) + case BT_GATT_DISCOVER_CHARACTERISTIC: { - case BT_GATT_DISCOVER_CHARACTERISTIC: - { - struct bt_gatt_attr *attr_dec = declarationAttr(attribute); - attr_dec++; - attr_dec->handle = attr->handle + 1; - break; - } - case BT_GATT_DISCOVER_DESCRIPTOR: - { - BLECharacteristic *chrc = (BLECharacteristic *)attribute; - struct bt_gatt_attr *attr_dec = declarationAttr(attribute); - struct bt_gatt_attr *attr_chrc = attr_dec + 1; - struct bt_gatt_attr *attr_cccd = attr_dec + 2; - struct bt_gatt_subscribe_params *sub_param_tmp = chrc->getSubscribeParams(); - struct bt_gatt_subscribe_params *sub_param = _sub_param + _sub_param_idx; - struct bt_conn *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(struct bt_gatt_subscribe_params)); - 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); - } - break; - } - case BT_GATT_DISCOVER_PRIMARY: - default: - { - // Do nothing - break; - } + 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; } } - attribute->discover(attr, &_discover_params); break; } } - // Send discover - if (attribute->discovering()) - { - send_discover = true; - } - else + // Find next attribute to discover + if (attribute_tmp->discovering() == false) { // Current attribute complete discovery - // Find next attribute to discover i++; - if (i < _num_attributes) + while (i < _num_attributes) { - attribute = _attributes[i]; - attribute->discover(&_discover_params); - send_discover = true; + 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; + } - if (send_discover) + // Send the discover request if necessary + if (send_discover && attribute_tmp->discovering()) { - struct bt_conn *conn = bt_conn_lookup_addr_le(_peripheral->bt_le_address()); + 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; + 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; + return ret; } } + return ret; } @@ -362,7 +553,7 @@ void BLEProfile::discover() { int err; BLEService *serviceattr = (BLEService *)_attributes[0]; - struct bt_conn *conn = bt_conn_lookup_addr_le(_peripheral->bt_le_address()); + bt_conn_t *conn = bt_conn_lookup_addr_le(_peripheral->bt_le_address()); if (NULL == conn) { @@ -384,12 +575,12 @@ void BLEProfile::discover() } } -BLEAttribute *BLEProfile::attribute(struct bt_gatt_subscribe_params *params) +BLEAttribute *BLEProfile::attribute(bt_gatt_subscribe_params_t *params) { return attribute(params->value_handle); } -BLEAttribute *BLEProfile::attribute(const struct bt_uuid* uuid) +BLEAttribute *BLEProfile::attribute(const bt_uuid_t* uuid) { int i; BLEAttribute *attr_tmp = NULL; @@ -424,7 +615,7 @@ BLEAttribute *BLEProfile::attribute(const struct bt_uuid* uuid) BLEAttribute *BLEProfile::attribute(uint16_t handle) { int i; - struct bt_gatt_attr *attr_gatt = NULL; + bt_gatt_attr_t *attr_gatt = NULL; for (i = 0; i < _attr_index; i++) { attr_gatt = _attr_base + i; @@ -475,7 +666,7 @@ BLEAttribute *BLEProfile::attribute(uint16_t handle) void BLEProfile::clearHandles(void) { int i; - struct bt_gatt_attr *attr = NULL; + 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. @@ -489,10 +680,10 @@ void BLEProfile::clearHandles(void) } } -struct bt_gatt_attr* BLEProfile::declarationAttr(BLEAttribute *attr) +bt_gatt_attr_t* BLEProfile::declarationAttr(BLEAttribute *attr) { int i; - struct bt_gatt_attr *attr_gatt = NULL; + bt_gatt_attr_t *attr_gatt = NULL; for (i = 0; i < _attr_index; i++) { @@ -510,7 +701,7 @@ struct bt_gatt_attr* BLEProfile::declarationAttr(BLEAttribute *attr) uint16_t BLEProfile::valueHandle(BLEAttribute *attr) { uint16_t handle = 0; - struct bt_gatt_attr *attr_gatt = declarationAttr(attr); + bt_gatt_attr_t *attr_gatt = declarationAttr(attr); attr_gatt++; if (attr_gatt->uuid == attr->uuid()) { @@ -522,7 +713,7 @@ uint16_t BLEProfile::valueHandle(BLEAttribute *attr) uint16_t BLEProfile::cccdHandle(BLEAttribute *attr) { uint16_t handle = 0; - struct bt_gatt_attr *attr_gatt = declarationAttr(attr); + bt_gatt_attr_t *attr_gatt = declarationAttr(attr); attr_gatt+= 2; if (attr_gatt->uuid == BLECharacteristic::getClientCharacteristicConfigUuid()) { diff --git a/libraries/CurieBLE/src/BLEProfile.h b/libraries/CurieBLE/src/BLEProfile.h index 33b06c75..300d54e8 100644 --- a/libraries/CurieBLE/src/BLEProfile.h +++ b/libraries/CurieBLE/src/BLEProfile.h @@ -34,13 +34,13 @@ class BLEProfile{ /** * @brief Add an attribute to the BLE Peripheral Device * - * @param attribute Attribute to add to Peripheral + * @param[in] attribute Attribute to add to Peripheral * * @return BleStatus indicating success or error * * @note This method must be called before the begin method */ - void addAttribute(BLEAttribute& attribute); + BleStatus addAttribute(BLEAttribute& attribute); /** * @brief Register the profile to Nordic BLE stack @@ -56,19 +56,19 @@ class BLEProfile{ /** * @brief Get BLEAttribute by subscribe parameter * - * @param struct bt_gatt_subscribe_params * Subscribe parameter + * @param[in] params Subscribe parameter * * @return BLEAttribute * NULL - Not found * Not NULL - The BLEAttribute object * * @note none */ - BLEAttribute *attribute(struct bt_gatt_subscribe_params *params); + BLEAttribute *attribute(bt_gatt_subscribe_params_t *params); /** * @brief Get BLEAttribute by characteristic handle * - * @param uint16_t The characteristic handle + * @param[in] handle The characteristic handle * * @return BLEAttribute * NULL - Not found * Not NULL - The BLEAttribute object @@ -81,13 +81,14 @@ class BLEProfile{ * @brief Process the discover response and * discover the BLE peripheral profile * - * @param const struct bt_gatt_attr * The gatt attribute response + * @param[in] const bt_gatt_attr_t * The gatt attribute response * - * @return none + * @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. */ - void discover(const struct bt_gatt_attr *attr); + uint8_t discover(const bt_gatt_attr_t *attr); /** * @brief Discover the BLE peripheral profile @@ -119,7 +120,7 @@ class BLEProfile{ /** * @brief Get the characteristic value handle * - * @param none + * @param[in] attr Attribute object * * @return uint16_t The value handle * 0 is invalid handle @@ -131,7 +132,7 @@ class BLEProfile{ /** * @brief Get characteristic configuration descriptor value handle * - * @param none + * @param[in] attr Attribute object * * @return uint16_t The value handle * 0 is invalid handle @@ -140,15 +141,15 @@ class BLEProfile{ */ uint16_t cccdHandle(BLEAttribute *attr); protected: - friend ssize_t profile_write_process(struct bt_conn *conn, - const struct bt_gatt_attr *attr, + 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 const struct bt_uuid* The UUID + * @param[in] const bt_uuid_t* The UUID * * @return BLEAttribute * NULL - Not found * Not NULL - The BLEAttribute object @@ -157,32 +158,58 @@ class BLEProfile{ * Because the uuid pointer in bt_gatt_attr is got from BLEAttribute * So set this as private. */ - BLEAttribute *attribute(const struct bt_uuid* uuid); + BLEAttribute *attribute(const bt_uuid_t* uuid); /** * @brief Get bt_gatt_attr by BLEAttribute class * - * @param BLEAttribute * The BLEAttribute object + * @param[in] BLEAttribute * The BLEAttribute object * - * @return struct bt_gatt_attr* NULL - Not found + * @return bt_gatt_attr_t* NULL - Not found * Not NULL - The bt_gatt_attr in the stack * * @note none */ - struct bt_gatt_attr* declarationAttr(BLEAttribute *attr); + 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; - struct bt_gatt_attr *_attr_base; + bt_gatt_attr_t *_attr_base; int _attr_index; BLEAttribute** _attributes; uint16_t _num_attributes; - struct bt_gatt_subscribe_params *_sub_param; + bt_gatt_subscribe_params_t *_sub_param; int _sub_param_idx; - struct bt_gatt_discover_params _discover_params; + bt_gatt_discover_params_t _discover_params; }; #endif diff --git a/libraries/CurieBLE/src/BLERoleBase.cpp b/libraries/CurieBLE/src/BLERoleBase.cpp index 6ab9def5..0285b857 100644 --- a/libraries/CurieBLE/src/BLERoleBase.cpp +++ b/libraries/CurieBLE/src/BLERoleBase.cpp @@ -21,7 +21,7 @@ #include "BLERoleBase.h" -void bleConnectEventHandler(struct bt_conn *conn, +void bleConnectEventHandler(bt_conn_t *conn, uint8_t err, void *param) { @@ -31,7 +31,7 @@ void bleConnectEventHandler(struct bt_conn *conn, } -void bleDisconnectEventHandler(struct bt_conn *conn, +void bleDisconnectEventHandler(bt_conn_t *conn, uint8_t reason, void *param) { @@ -42,7 +42,7 @@ void bleDisconnectEventHandler(struct bt_conn *conn, p->handleDisconnectEvent(conn, reason); } -void bleParamUpdatedEventHandler(struct bt_conn *conn, +void bleParamUpdatedEventHandler(bt_conn_t *conn, uint16_t interval, uint16_t latency, uint16_t timeout, @@ -64,15 +64,15 @@ void BLERoleBase::setTxPower (int8_t 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++; + // 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; } diff --git a/libraries/CurieBLE/src/BLERoleBase.h b/libraries/CurieBLE/src/BLERoleBase.h index 4db80c48..b8fc5169 100644 --- a/libraries/CurieBLE/src/BLERoleBase.h +++ b/libraries/CurieBLE/src/BLERoleBase.h @@ -54,7 +54,7 @@ class BLERoleBase{ /** * Set TX output power * - * @param tx_power The antenna TX power + * @param[in] tx_power The antenna TX power * * @return boolean_t true if established connection, otherwise false */ @@ -62,13 +62,13 @@ class BLERoleBase{ protected: virtual BleStatus _init(void); - friend void bleConnectEventHandler(struct bt_conn *conn, + friend void bleConnectEventHandler(bt_conn_t *conn, uint8_t err, void *param); - friend void bleDisconnectEventHandler(struct bt_conn *conn, + friend void bleDisconnectEventHandler(bt_conn_t *conn, uint8_t reason, void *param); - friend void bleParamUpdatedEventHandler(struct bt_conn *conn, + friend void bleParamUpdatedEventHandler(bt_conn_t *conn, uint16_t interval, uint16_t latency, uint16_t timeout, @@ -77,48 +77,48 @@ class BLERoleBase{ /** * @brief Handle the connected event * - * @param conn The object that established the connection + * @param[in] conn The object that established the connection * - * @param err The code of the process + * @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(struct bt_conn *conn, uint8_t err) = 0; + virtual void handleConnectEvent(bt_conn_t *conn, uint8_t err) = 0; /** * @brief Handle the disconnected event * - * @param conn The object that lost the connection + * @param[in] conn The object that lost the connection * - * @param reason The link lost reason + * @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(struct bt_conn *conn, uint8_t reason) = 0; + virtual void handleDisconnectEvent(bt_conn_t *conn, uint8_t reason) = 0; /** * @brief Handle the conntion update request * - * @param conn The connection object that need to process the update request + * @param[in] conn The connection object that need to process the update request * - * @param interval The connection interval + * @param[in] interval The connection interval (N*1.25)ms * - * @param latency The connection latency + * @param[in] latency The connection latency * - * @param timeout The connection timeout + * @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 (struct bt_conn *conn, + virtual void handleParamUpdated (bt_conn_t *conn, uint16_t interval, - uint16_t latency, - uint16_t timeout) = 0; + uint16_t latency, + uint16_t timeout) = 0; char _device_name[BLE_MAX_DEVICE_NAME+1]; bt_addr_le_t _local_bda; diff --git a/libraries/CurieBLE/src/BLEService.cpp b/libraries/CurieBLE/src/BLEService.cpp index 2e25f161..906dc17d 100644 --- a/libraries/CurieBLE/src/BLEService.cpp +++ b/libraries/CurieBLE/src/BLEService.cpp @@ -20,10 +20,10 @@ #include "internal/ble_client.h" #include "BLEService.h" -struct bt_uuid_16 BLEService::_gatt_primary_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_PRIMARY_VAL}; -struct bt_uuid *BLEService::getPrimayUuid(void) +bt_uuid_16_t BLEService::_gatt_primary_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_PRIMARY_VAL}; +bt_uuid_t *BLEService::getPrimayUuid(void) { - return (struct bt_uuid *)&_gatt_primary_uuid; + return (bt_uuid_t *)&_gatt_primary_uuid; } BLEService::BLEService(const char* uuid) : @@ -32,7 +32,7 @@ BLEService::BLEService(const char* uuid) : } -void BLEService::discover(struct bt_gatt_discover_params *params) +void BLEService::discover(bt_gatt_discover_params_t *params) { params->type = BT_GATT_DISCOVER_PRIMARY; @@ -41,8 +41,8 @@ void BLEService::discover(struct bt_gatt_discover_params *params) _discoverying = true; } -void BLEService::discover(const struct bt_gatt_attr *attr, - struct bt_gatt_discover_params *params) +void BLEService::discover(const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params) { params->start_handle = attr->handle + 1; diff --git a/libraries/CurieBLE/src/BLEService.h b/libraries/CurieBLE/src/BLEService.h index 2d4c4999..24f468ef 100644 --- a/libraries/CurieBLE/src/BLEService.h +++ b/libraries/CurieBLE/src/BLEService.h @@ -35,18 +35,18 @@ class BLEService : public BLEAttribute { /** * Constructor for BLE Service * - * @param uuid 16-bit or 128-bit UUID (in string form) defined by BLE standard + * @param[in] uuid 16-bit or 128-bit UUID (in string form) defined by BLE standard */ BLEService(const char* uuid); protected: friend BLEPeripheral; friend BLEProfile; - void discover(const struct bt_gatt_attr *attr, - struct bt_gatt_discover_params *params); - void discover(struct bt_gatt_discover_params *params); + void discover(const bt_gatt_attr_t *attr, + bt_gatt_discover_params_t *params); + void discover(bt_gatt_discover_params_t *params); - static struct bt_uuid *getPrimayUuid(void); + static bt_uuid_t *getPrimayUuid(void); private: static bt_uuid_16 _gatt_primary_uuid; }; diff --git a/libraries/CurieBLE/src/BLETypedCharacteristic.h b/libraries/CurieBLE/src/BLETypedCharacteristic.h index 77828aae..65842d0f 100644 --- a/libraries/CurieBLE/src/BLETypedCharacteristic.h +++ b/libraries/CurieBLE/src/BLETypedCharacteristic.h @@ -27,18 +27,110 @@ template class BLETypedCharacteristic : public BLECharacteristic { public: + /** + * @brief The constructor of the template BLE 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 + */ BLETypedCharacteristic(const char* uuid, unsigned char properties); + /** + * @brief Set the characteristic value + * + * @param[in] value New value to set + * + * @return bool true - set value success, + * false - on error + * + * @note none + */ bool setValue(T value); + /** + * @brief Get the value of the Characteristic + * + * @param none + * + * @return T The value of characteristic + * + * @note none + */ T value(void); + /** + * @brief Set the characteristic value in Little Endian + * + * @param[in] value New value to set + * + * @return bool true - set value success, + * false - on error + * + * @note none + */ bool setValueLE(T value); + /** + * @brief Get the value of the Characteristic in Little Endian + * + * @param none + * + * @return T The value of characteristic + * + * @note none + */ T valueLE(void); + /** + * @brief Set the characteristic value in Big Endian + * + * @param[in] value New value to set + * + * @return bool true - set value success, + * false - on error + * + * @note none + */ bool setValueBE(T value); + /** + * @brief Get the value of the Characteristic in Big Endian + * + * @param none + * + * @return T The value of characteristic + * + * @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: + /** + * @brief Swap the bytes + * + * @param value The typed value + * + * @return T The swapped value + * + * @note none + */ T byteSwap(T value); }; @@ -79,6 +171,10 @@ 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.h b/libraries/CurieBLE/src/BLETypedCharacteristics.h index fb76cfce..1ecd2a6c 100644 --- a/libraries/CurieBLE/src/BLETypedCharacteristics.h +++ b/libraries/CurieBLE/src/BLETypedCharacteristics.h @@ -24,56 +24,199 @@ class BLEBoolCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a bool Typed Characteristic. + * Default constructor for BLE bool 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 + */ BLEBoolCharacteristic(const char* uuid, unsigned char properties); }; class BLECharCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a Char Typed Characteristic. + * Default constructor for BLE Char 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 + */ BLECharCharacteristic(const char* uuid, unsigned char properties); }; class BLEUnsignedCharCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a Unsigned Char Typed Characteristic. + * Default constructor for BLE Unsigned Char 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 + */ BLEUnsignedCharCharacteristic(const char* uuid, unsigned char properties); }; class BLEShortCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a Short Typed Characteristic. + * Default constructor for BLE short 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 + */ BLEShortCharacteristic(const char* uuid, unsigned char properties); }; class BLEUnsignedShortCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a Unsigned Short Typed Characteristic. + * Default constructor for BLE Unsigned Short 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 + */ BLEUnsignedShortCharacteristic(const char* uuid, unsigned char properties); }; class BLEIntCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a Int Typed Characteristic. + * Default constructor for BLE Int 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 + */ BLEIntCharacteristic(const char* uuid, unsigned char properties); }; class BLEUnsignedIntCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a Unsigned Int Typed Characteristic. + * Default constructor for BLE Unsigned Int 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 + */ BLEUnsignedIntCharacteristic(const char* uuid, unsigned char properties); }; class BLELongCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a Long Typed Characteristic. + * Default constructor for BLE Long 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 + */ BLELongCharacteristic(const char* uuid, unsigned char properties); }; class BLEUnsignedLongCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a Unsigned Long Typed Characteristic. + * Default constructor for BLE Unsigned Long 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 + */ BLEUnsignedLongCharacteristic(const char* uuid, unsigned char properties); }; class BLEFloatCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a Float Typed Characteristic. + * Default constructor for BLE Float 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 + */ BLEFloatCharacteristic(const char* uuid, unsigned char properties); }; class BLEDoubleCharacteristic : public BLETypedCharacteristic { public: + /** + * @brief Instantiate a Double Typed Characteristic. + * Default constructor for BLE Double 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 + */ BLEDoubleCharacteristic(const char* uuid, unsigned char properties); }; diff --git a/libraries/CurieBLE/src/internal/ble_client.c b/libraries/CurieBLE/src/internal/ble_client.c index 5731a75f..78afa966 100644 --- a/libraries/CurieBLE/src/internal/ble_client.c +++ b/libraries/CurieBLE/src/internal/ble_client.c @@ -27,7 +27,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ - + #include #include @@ -77,7 +77,7 @@ static void *ble_client_update_param_event_param; extern "C" { #endif -static void on_connected(struct bt_conn *conn, uint8_t err) +static void on_connected(bt_conn_t *conn, uint8_t err) { if (ble_client_connect_event_cb) { @@ -85,7 +85,7 @@ static void on_connected(struct bt_conn *conn, uint8_t err) } } -static void on_disconnected(struct bt_conn *conn, uint8_t reason) +static void on_disconnected(bt_conn_t *conn, uint8_t reason) { if (ble_client_disconnect_event_cb) { @@ -93,8 +93,8 @@ static void on_disconnected(struct bt_conn *conn, uint8_t reason) } } -static void on_le_param_updated(struct bt_conn *conn, uint16_t interval, - uint16_t latency, uint16_t timeout) +static void on_le_param_updated(bt_conn_t *conn, uint16_t interval, + uint16_t latency, uint16_t timeout) { if (ble_client_update_param_event_cb) { @@ -107,9 +107,9 @@ static void on_le_param_updated(struct bt_conn *conn, uint16_t interval, } static struct bt_conn_cb conn_callbacks = { - .connected = on_connected, - .disconnected = on_disconnected, - .le_param_updated = on_le_param_updated + .connected = on_connected, + .disconnected = on_disconnected, + .le_param_updated = on_le_param_updated }; @@ -167,12 +167,12 @@ void ble_client_get_factory_config(bt_addr_le_t *bda, char *name) *suffix++ = '-'; 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 + *suffix = 0; /* NULL-terminate the string. Note the macro BYTE_TO_STR automatically move the pointer */ } else { - /* This code segment will be only reached if Curie module was not + /* This code segment will be only reached if Curie module was not provisioned properly with a BLE MAC address*/ *suffix++ = 0; /* NULL-terminate the string */ } @@ -200,33 +200,35 @@ void ble_client_init(ble_client_connect_event_cb_t connect_cb, void* connect_par BleStatus errorno_to_ble_status(int err) { - BleStatus err_code; - err = 0 - err; - - switch(err) { - case 0: - err_code = BLE_STATUS_SUCCESS; - break; - case EIO: - err_code = BLE_STATUS_WRONG_STATE; - break; - case EBUSY: - err_code = BLE_STATUS_TIMEOUT; - break; - case EFBIG: - case ENOTSUP: - err_code = BLE_STATUS_NOT_SUPPORTED; - break; - case EPERM: - case EACCES: - err_code = BLE_STATUS_NOT_ALLOWED; - break; - case ENOMEM: // No memeory - default: - err_code = BLE_STATUS_ERROR; - break; - } - return err_code; + BleStatus err_code; + err = 0 - err; + + switch(err) { + case 0: + err_code = BLE_STATUS_SUCCESS; + break; + case EIO: + err_code = BLE_STATUS_WRONG_STATE; + break; + case EBUSY: + err_code = BLE_STATUS_TIMEOUT; + break; + case EFBIG: + case ENOTSUP: + err_code = BLE_STATUS_NOT_SUPPORTED; + break; + case EPERM: + case EACCES: + err_code = BLE_STATUS_NOT_ALLOWED; + break; + case ENOMEM: // No memeory + err_code = BLE_STATUS_NO_MEMORY; + break; + default: + err_code = BLE_STATUS_ERROR; + break; + } + return err_code; } diff --git a/libraries/CurieBLE/src/internal/ble_client.h b/libraries/CurieBLE/src/internal/ble_client.h index b7495867..30a33a98 100644 --- a/libraries/CurieBLE/src/internal/ble_client.h +++ b/libraries/CurieBLE/src/internal/ble_client.h @@ -40,12 +40,13 @@ enum { }; #define MSEC_TO_UNITS(TIME, RESOLUTION) (((TIME) * 1000) / (RESOLUTION)) +#define UNITS_TO_MSEC(TIME, RESOLUTION) (((TIME) * RESOLUTION) / 1000) /* Connection parameters used for Peripheral Preferred Connection Parameterss (PPCP) and update request */ #define DEFAULT_MIN_CONN_INTERVAL MSEC_TO_UNITS(80, UNIT_1_25_MS) #define DEFAULT_MAX_CONN_INTERVAL MSEC_TO_UNITS(150, UNIT_1_25_MS) #define MIN_CONN_INTERVAL 0x0006 -#define MAX_CONN_INTERVAL 0x095f +#define MAX_CONN_INTERVAL 0x0C80 #define SLAVE_LATENCY 0 #define CONN_SUP_TIMEOUT MSEC_TO_UNITS(6000, UNIT_10_MS) @@ -108,6 +109,7 @@ 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); + #ifdef __cplusplus } #endif diff --git a/system/libarc32_arduino101/drivers/bluetooth/conn_internal.h b/system/libarc32_arduino101/drivers/bluetooth/conn_internal.h deleted file mode 100644 index 04c08839..00000000 --- a/system/libarc32_arduino101/drivers/bluetooth/conn_internal.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -typedef enum { - BT_CONN_DISCONNECTED, - BT_CONN_CONNECT_SCAN, - BT_CONN_CONNECT, - BT_CONN_CONNECTED, - BT_CONN_DISCONNECT, -} bt_conn_state_t; - -/* bt_conn flags: the flags defined here represent connection parameters */ -enum { - BT_CONN_AUTO_CONNECT, -}; - -struct bt_conn_le { - bt_addr_le_t dst; - -#if 0 - bt_addr_le_t init_addr; - bt_addr_le_t resp_addr; -#endif - uint16_t interval; - uint16_t interval_min; - uint16_t interval_max; - - uint16_t latency; - uint16_t timeout; -#if 0 - uint8_t features[8]; -#endif -}; - -#if defined(CONFIG_BLUETOOTH_BREDR) -struct bt_conn_br { - bt_addr_t dst; -}; -#endif - -struct bt_conn { - uint16_t handle; - uint8_t type; - uint8_t role; - -#if defined(CONFIG_BLUETOOTH_SMP) - uint8_t encrypt; - bt_security_t sec_level; - bt_security_t required_sec_level; -#endif /* CONFIG_BLUETOOTH_SMP */ - - atomic_t ref; - - /* Connection error or reason for disconnect */ - uint8_t err; - - bt_conn_state_t state; - union { - struct bt_conn_le le; -#if defined(CONFIG_BLUETOOTH_BREDR) - struct bt_conn_br br; -#endif - }; -}; - -/* Add a new LE connection */ -struct bt_conn *bt_conn_add_le(const bt_addr_le_t *peer); - -#if defined(CONFIG_BLUETOOTH_BREDR) -/* Add a new BR/EDR connection */ -struct bt_conn *bt_conn_add_br(const bt_addr_t *peer); - -/* Look up an existing connection by BT address */ -struct bt_conn *bt_conn_lookup_addr_br(const bt_addr_t *peer); -#endif - -/* Look up an existing connection */ -struct bt_conn *bt_conn_lookup_handle(uint16_t handle); - -/* Look up a connection state. For BT_ADDR_LE_ANY, returns the first connection - * with the specific state - */ -struct bt_conn *bt_conn_lookup_state_le(const bt_addr_le_t *peer, - const bt_conn_state_t state); - -/* Set connection object in certain state and perform action related to state */ -void bt_conn_set_state(struct bt_conn *conn, bt_conn_state_t state); - -void bt_conn_set_param_le(struct bt_conn *conn, - const struct bt_le_conn_param *param); - -int bt_conn_update_param_le(struct bt_conn *conn, - const struct bt_le_conn_param *param); - -int bt_conn_le_conn_update(struct bt_conn *conn, - const struct bt_le_conn_param *param); - -void notify_le_param_updated(struct bt_conn *conn); - -#if defined(CONFIG_BLUETOOTH_SMP) -/* rand and ediv should be in BT order */ -int bt_conn_le_start_encryption(struct bt_conn *conn, uint64_t rand, - uint16_t ediv, const uint8_t *ltk, size_t len); - -/* Notify higher layers that RPA was resolved */ -void bt_conn_identity_resolved(struct bt_conn *conn); - -/* Notify higher layers that connection security changed */ -void bt_conn_security_changed(struct bt_conn *conn); -#endif /* CONFIG_BLUETOOTH_SMP */ -/* Initialize connection management */ -int bt_conn_init(void); From 84fc3dbbfb900d0d5cf5b8b2a9c7e734accb5771 Mon Sep 17 00:00:00 2001 From: lianggao Date: Fri, 9 Sep 2016 08:28:50 +0800 Subject: [PATCH 003/125] Create BLE Example sketches 1. Fix Jira 664 demonstrates changing the ADV data 2. Fix Jira 671 Support update connection interval in central/peripheral --- .../BatteryAdvChange/BatteryAdvChange.ino | 113 ++++++++++ .../BatteryMonitor/BatteryMonitor.ino | 33 +++ .../UpdateConnectionInterval.ino | 205 ++++++++++++++++++ 3 files changed, 351 insertions(+) create mode 100644 libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino create mode 100644 libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino diff --git a/libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino b/libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino new file mode 100644 index 00000000..44a8055a --- /dev/null +++ b/libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino @@ -0,0 +1,113 @@ +/* Please see code cpyright at the bottom of this example code */ +/* + 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 index 0facfe09..96969888 100644 --- a/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino +++ b/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino @@ -66,6 +66,14 @@ void loop() { 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: @@ -90,6 +98,31 @@ void updateBatteryLevel() { } } +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. diff --git a/libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino b/libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino new file mode 100644 index 00000000..a25c2195 --- /dev/null +++ b/libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino @@ -0,0 +1,205 @@ +/* + * 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 + to show how to control and response the connection intelval 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 +*/ From 0304de781d54d0158c070d2d9cc750ab295c0712 Mon Sep 17 00:00:00 2001 From: Calvin Sangbin Park Date: Fri, 9 Sep 2016 10:55:17 -0700 Subject: [PATCH 004/125] Rebuilt libarc32drv --- variants/arduino_101/libarc32drv_arduino101.a | Bin 536328 -> 782772 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/variants/arduino_101/libarc32drv_arduino101.a b/variants/arduino_101/libarc32drv_arduino101.a index ff809d808f9a43ca3dbce2f609201934bd3a88db..2d62036cb637f949522b45d05a75c6129d571e4f 100644 GIT binary patch literal 782772 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%0A~oRohrPTK9nQyz!19Xc7L}pq6Gn7`6~CAjg+x;k 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

PUM_&nXA|?` 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 Date: Mon, 29 Aug 2016 14:01:05 -0700 Subject: [PATCH 005/125] Update expected BLE version for V3 Signed-off-by: Brian Baltz --- platform.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform.txt b/platform.txt index ecac9280..e0ff63ce 100644 --- a/platform.txt +++ b/platform.txt @@ -5,7 +5,7 @@ # https://github.com/arduino/Arduino/wiki/Arduino-IDE-1.5---3rd-party-Hardware-specification name=Intel Curie (32-bit) Boards -version=1.0.7 +version=2.0.0 # Arduino 101 compile variables # ---------------------- @@ -93,8 +93,8 @@ recipe.size.regex=^(?:text|ctors|rodata|datas)\s+([0-9]+).* # BLE firmware check # ------------------- -tools.arduino101load.ble.fw.string="ATP1BLE000-1541C5635" -tools.arduino101load.ble.fw.position=141312 +tools.arduino101load.ble.fw.string="ATP1BLE00R-1631C4439" +tools.arduino101load.ble.fw.position=169984 # Arc Uploader/Programmers tools # ------------------- From d2b7d1d732ca2ace5eb3f3da2e5cf3371e104f54 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 12 Sep 2016 10:33:51 -0600 Subject: [PATCH 006/125] JIRA-685, Peripheral sketches shall state associated Central sketch. --- .../BatteryAdvChange/BatteryAdvChange.ino | 21 +++++++++++-------- .../BatteryMonitor/BatteryMonitor.ino | 11 +++++----- .../CurieBLE/examples/ButtonLED/ButtonLED.ino | 14 +++++++------ .../examples/CallbackLED/CallbackLED.ino | 19 ++++++++++------- .../examples/IMUBleCentral/IMUBleCentral.ino | 12 +++++------ .../IMUBleNotification/IMUBleNotification.ino | 7 ++++--- libraries/CurieBLE/examples/LED/LED.ino | 20 ++++++++++-------- .../examples/LEDCentral/LEDCentral.ino | 7 ++++--- .../CurieBLE/examples/Scanning/Scanning.ino | 11 +++++----- .../UpdateConnectionInterval.ino | 5 +++-- 10 files changed, 71 insertions(+), 56 deletions(-) diff --git a/libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino b/libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino index 44a8055a..8b977a39 100644 --- a/libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino +++ b/libraries/CurieBLE/examples/BatteryAdvChange/BatteryAdvChange.ino @@ -1,13 +1,16 @@ -/* Please see code cpyright at the bottom of this example code */ +/* Please see code copyright at the bottom of this example code */ + /* - 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. + 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 diff --git a/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino b/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino index 96969888..e3a297cf 100644 --- a/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino +++ b/libraries/CurieBLE/examples/BatteryMonitor/BatteryMonitor.ino @@ -6,11 +6,12 @@ #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 + 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 */ /* */ diff --git a/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino b/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino index 9cb38a2c..7a46c164 100644 --- a/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino +++ b/libraries/CurieBLE/examples/ButtonLED/ButtonLED.ino @@ -3,12 +3,14 @@ * See the bottom of this file for the license terms. */ - /* 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 connect 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. - */ +/* + 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 diff --git a/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino b/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino index 3f516ca5..2a661189 100644 --- a/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino +++ b/libraries/CurieBLE/examples/CallbackLED/CallbackLED.ino @@ -3,14 +3,17 @@ * 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 - // BLECoonected, 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 - // Writng a byte value of 0x00 should turn off the LED + /* + 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 diff --git a/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino b/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino index e14753db..a5359509 100644 --- a/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino +++ b/libraries/CurieBLE/examples/IMUBleCentral/IMUBleCentral.ino @@ -6,12 +6,12 @@ #include /* - This sketch example works with IMUBleNotification.ino - IMUBleNotification.ino will send notification to this sketch. - This sketch will receive the notifications and out put 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 - + 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 diff --git a/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino b/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino index 81ca3dec..ee57d295 100644 --- a/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino +++ b/libraries/CurieBLE/examples/IMUBleNotification/IMUBleNotification.ino @@ -7,9 +7,10 @@ #include /* - This sketch example works with IMUBleCentral.ino - This sketch will read IMU data from sensor and send notification to IMUBleCentral.ino - IMUBleCentral.ino will receive the Notifications and output the received data. + This sketch example works with IMUBleCentral.ino. + + This sketch will read IMU data from sensor and send notifications to IMUBleCentral.ino. + IMUBleCentral.ino will receive the notifications and output the received data. */ #define MAX_IMU_RECORD 1 diff --git a/libraries/CurieBLE/examples/LED/LED.ino b/libraries/CurieBLE/examples/LED/LED.ino index 2c2c261d..34c26a8c 100644 --- a/libraries/CurieBLE/examples/LED/LED.ino +++ b/libraries/CurieBLE/examples/LED/LED.ino @@ -3,15 +3,17 @@ * 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 wriiten 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 - // Writng a byte value of 0x00 should turn off the LED + /* + 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 diff --git a/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino b/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino index b9b533b3..e26e9451 100644 --- a/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino +++ b/libraries/CurieBLE/examples/LEDCentral/LEDCentral.ino @@ -6,9 +6,10 @@ #include /* - This example can work with CallbackLED and LED - 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 + 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 diff --git a/libraries/CurieBLE/examples/Scanning/Scanning.ino b/libraries/CurieBLE/examples/Scanning/Scanning.ino index 97ea222d..966bd3b4 100644 --- a/libraries/CurieBLE/examples/Scanning/Scanning.ino +++ b/libraries/CurieBLE/examples/Scanning/Scanning.ino @@ -6,11 +6,12 @@ #include /* - 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 - This sketch is meaningful if one or more BLE peripheral devices (any of the peripheral examples will do) - are present. + 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. */ diff --git a/libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino b/libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino index a25c2195..852b8e3b 100644 --- a/libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino +++ b/libraries/CurieBLE/examples/UpdateConnectionInterval/UpdateConnectionInterval.ino @@ -6,8 +6,9 @@ #include /* - This example can work with BatteryMonitor - to show how to control and response the connection intelval request. + This example can work with BatteryMonitor. + + Show how to control and response the connection interval request. */ // set up connection params From 2e56b81a264c8760f095a12f650381b8ea8de831 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Wed, 28 Sep 2016 12:02:32 -0700 Subject: [PATCH 007/125] stdlib_noniso.cpp: Handle rounding corner case for dtostrf Rounding must be done before separating the integral and decimal portions. --- cores/arduino/WString.cpp | 4 ++-- cores/arduino/stdlib_noniso.cpp | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cores/arduino/WString.cpp b/cores/arduino/WString.cpp index d577e002..3b20b5ad 100644 --- a/cores/arduino/WString.cpp +++ b/cores/arduino/WString.cpp @@ -138,7 +138,7 @@ String::String(float value, unsigned char decimalPlaces) if(decimalPlaces) totalSize += 1 + ((int)decimalPlaces & 0x0FF); - char buf[totalSize+1]; + char buf[totalSize + 2]; *this = dtostrf(value, 0, decimalPlaces, buf); } @@ -149,7 +149,7 @@ String::String(double value, unsigned char decimalPlaces) if(decimalPlaces) totalSize += 1 + ((int)decimalPlaces & 0x0FF); - char buf[totalSize+1]; + char buf[totalSize + 2]; *this = dtostrf(value, 0, decimalPlaces, buf); } diff --git a/cores/arduino/stdlib_noniso.cpp b/cores/arduino/stdlib_noniso.cpp index 7b8a107c..14318e85 100644 --- a/cores/arduino/stdlib_noniso.cpp +++ b/cores/arduino/stdlib_noniso.cpp @@ -215,6 +215,12 @@ char *dtostrf(double number, signed char width, unsigned char prec, char *s) number = -number; } + // rounding up to the precision + rounding = 0.5; + for (i = 0; i < prec; ++i) + rounding /= 10.0; + number += rounding; + // seperate integral and fractional parts integer = (unsigned long long) number; fraction = (double) (number - integer); @@ -231,12 +237,6 @@ char *dtostrf(double number, signed char width, unsigned char prec, char *s) out += before; if (!prec) goto end; - // rounding up to the precision - rounding = 0.5; - for (i = 0; i < prec; ++i) - rounding /= 10.0; - fraction += rounding; - // generate chars for each digit of the fractional part *out++ = '.'; for (i = 0; i < prec; ++i) { From 1a5e3f7b2d65cf2b81417f85bd24ca455bdaa1fc Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Thu, 29 Sep 2016 15:17:07 -0700 Subject: [PATCH 008/125] Make g_APinDescription const This has the effect of moving g_APinDescription out of .data (SRAM), and into .rodata (flash). Pros: sketches now have an additional 1160 bytes of SRAM available Cons: state of pins is no longer tracked; e.g. when calling digitalWrite, the full configuration is done each time. See changes in wiring_digital.c and wiring_analog.c for details. There is inevitably a performance impact for digitalRead/Write & analogRead/Write, though it is unlikely to be a noticable one. --- cores/arduino/Arduino.h | 2 +- cores/arduino/wiring_analog.c | 24 ++++++--------- cores/arduino/wiring_digital.c | 47 ++++++++++++----------------- libraries/CurieI2S/src/CurieI2S.cpp | 6 ---- libraries/SPI/src/SPI.cpp | 3 -- 5 files changed, 30 insertions(+), 52 deletions(-) diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index cc6cb593..dc557e36 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -60,7 +60,7 @@ typedef void (*voidFuncPtr)( void ) ; /* Types used for the tables below */ /* TODO - consider using smaller types to optimise storage space */ -typedef struct _PinDescription +typedef const struct _PinDescription { uint32_t ulGPIOId; // GPIO port pin uint32_t ulGPIOPort; // GPIO port ID diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index 6e0c84ae..332e07d4 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -97,16 +97,13 @@ void analogWrite(uint8_t pin, int val) offset = ((p->ulPwmChan * QRK_PWM_N_REGS_LEN) + QRK_PWM_N_LOAD_COUNT1); MMIO_REG_VAL(QRK_PWM_BASE_ADDR + offset) = lcnt; - if (p->ulPinMode != PWM_MUX_MODE) { - /* start the PWM output */ - offset = ((p->ulPwmChan * QRK_PWM_N_REGS_LEN) + QRK_PWM_N_CONTROL); - SET_MMIO_MASK(QRK_PWM_BASE_ADDR + offset, QRK_PWM_CONTROL_ENABLE); - - /* Disable pull-up and set pin mux for PWM output */ - SET_PIN_PULLUP(p->ulSocPin, 0); - SET_PIN_MODE(p->ulSocPin, PWM_MUX_MODE); - p->ulPinMode = PWM_MUX_MODE; - } + /* start the PWM output */ + offset = ((p->ulPwmChan * QRK_PWM_N_REGS_LEN) + QRK_PWM_N_CONTROL); + SET_MMIO_MASK(QRK_PWM_BASE_ADDR + offset, QRK_PWM_CONTROL_ENABLE); + + /* Disable pull-up and set pin mux for PWM output */ + SET_PIN_PULLUP(p->ulSocPin, 0); + SET_PIN_MODE(p->ulSocPin, PWM_MUX_MODE); } } uint32_t analogRead(uint32_t pin) @@ -120,11 +117,8 @@ uint32_t analogRead(uint32_t pin) PinDescription *p = &g_APinDescription[pin]; /* Disable pull-up and set pin mux for ADC output */ - if (p->ulPinMode != ADC_MUX_MODE) { - SET_PIN_MODE(p->ulSocPin, ADC_MUX_MODE); - p->ulPinMode = ADC_MUX_MODE; - SET_PIN_PULLUP(p->ulSocPin,0); - } + SET_PIN_MODE(p->ulSocPin, ADC_MUX_MODE); + SET_PIN_PULLUP(p->ulSocPin,0); /* Reset sequence pointer */ SET_ARC_MASK(ADC_CTRL, ADC_SEQ_PTR_RST); diff --git a/cores/arduino/wiring_digital.c b/cores/arduino/wiring_digital.c index 73c4fa76..67390f2d 100644 --- a/cores/arduino/wiring_digital.c +++ b/cores/arduino/wiring_digital.c @@ -31,7 +31,6 @@ void pinMode( uint8_t pin, uint8_t mode ) PinDescription *p = &g_APinDescription[pin]; if (mode == OUTPUT) { - p->ulInputMode = OUTPUT_MODE; if (p->ulGPIOType == SS_GPIO) { uint32_t reg = p->ulGPIOBase + SS_GPIO_SWPORTA_DDR; SET_ARC_BIT(reg, p->ulGPIOId); @@ -41,7 +40,6 @@ void pinMode( uint8_t pin, uint8_t mode ) SET_MMIO_BIT(reg, p->ulGPIOId); } } else { - p->ulInputMode = INPUT_MODE; if (p->ulGPIOType == SS_GPIO) { uint32_t reg = p->ulGPIOBase + SS_GPIO_SWPORTA_DDR; CLEAR_ARC_BIT(reg, p->ulGPIOId); @@ -54,10 +52,7 @@ void pinMode( uint8_t pin, uint8_t mode ) /* Set SoC pin mux configuration */ SET_PIN_PULLUP(p->ulSocPin, (mode == INPUT_PULLUP) ? 1 : 0); - if (p->ulPinMode != GPIO_MUX_MODE) { - SET_PIN_MODE(p->ulSocPin, GPIO_MUX_MODE); - p->ulPinMode = GPIO_MUX_MODE; - } + SET_PIN_MODE(p->ulSocPin, GPIO_MUX_MODE); } void digitalWrite( uint8_t pin, uint8_t val ) @@ -66,27 +61,25 @@ void digitalWrite( uint8_t pin, uint8_t val ) PinDescription *p = &g_APinDescription[pin]; - if (!p->ulInputMode) { - if (p->ulGPIOType == SS_GPIO) { - uint32_t reg = p->ulGPIOBase + SS_GPIO_SWPORTA_DR; - if (val) - SET_ARC_BIT(reg, p->ulGPIOId); - else - CLEAR_ARC_BIT(reg, p->ulGPIOId); - } - else if (p->ulGPIOType == SOC_GPIO) { - uint32_t reg = p->ulGPIOBase + SOC_GPIO_SWPORTA_DR; - if (val) - SET_MMIO_BIT(reg, p->ulGPIOId); - else - CLEAR_MMIO_BIT(reg, p->ulGPIOId); - } - } else { - if (val) - SET_PIN_PULLUP(p->ulSocPin,1); - else - SET_PIN_PULLUP(p->ulSocPin,0); - } + if (p->ulGPIOType == SS_GPIO) { + uint32_t reg = p->ulGPIOBase + SS_GPIO_SWPORTA_DR; + if (val) + SET_ARC_BIT(reg, p->ulGPIOId); + else + CLEAR_ARC_BIT(reg, p->ulGPIOId); + } + else if (p->ulGPIOType == SOC_GPIO) { + uint32_t reg = p->ulGPIOBase + SOC_GPIO_SWPORTA_DR; + if (val) + SET_MMIO_BIT(reg, p->ulGPIOId); + else + CLEAR_MMIO_BIT(reg, p->ulGPIOId); + } + + if (val) + SET_PIN_PULLUP(p->ulSocPin,1); + else + SET_PIN_PULLUP(p->ulSocPin,0); } int digitalRead( uint8_t pin ) diff --git a/libraries/CurieI2S/src/CurieI2S.cpp b/libraries/CurieI2S/src/CurieI2S.cpp index 6179b34e..92785ee6 100644 --- a/libraries/CurieI2S/src/CurieI2S.cpp +++ b/libraries/CurieI2S/src/CurieI2S.cpp @@ -419,9 +419,6 @@ void Curie_I2S::muxRX(bool enable) SET_PIN_MODE(49, mux_mode); //I2S_RXD SET_PIN_MODE(51, mux_mode); //I2S_RWS SET_PIN_MODE(50, mux_mode); //I2S_RSCK - g_APinDescription[I2S_RXD].ulPinMode = mux_mode; - g_APinDescription[I2S_RWS].ulPinMode = mux_mode; - g_APinDescription[I2S_RSCK].ulPinMode = mux_mode; } void Curie_I2S::muxTX(bool enable) @@ -436,9 +433,6 @@ void Curie_I2S::muxTX(bool enable) SET_PIN_MODE(g_APinDescription[I2S_TXD].ulSocPin, mux_mode); SET_PIN_MODE(g_APinDescription[I2S_TWS].ulSocPin, mux_mode); SET_PIN_MODE(g_APinDescription[I2S_TSCK].ulSocPin, mux_mode); - g_APinDescription[I2S_TXD].ulPinMode = mux_mode; - g_APinDescription[I2S_TWS].ulPinMode = mux_mode; - g_APinDescription[I2S_TSCK].ulPinMode = mux_mode; } void Curie_I2S::initRX() diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index 57644358..29c6da47 100755 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -94,9 +94,6 @@ void SPIClass::begin() SET_PIN_MODE(g_APinDescription[MOSI].ulSocPin, SPI_MUX_MODE); SET_PIN_MODE(g_APinDescription[MISO].ulSocPin, SPI_MUX_MODE); SET_PIN_MODE(g_APinDescription[SCK].ulSocPin, SPI_MUX_MODE); - g_APinDescription[MOSI].ulPinMode = SPI_MUX_MODE; - g_APinDescription[MISO].ulPinMode = SPI_MUX_MODE; - g_APinDescription[SCK].ulPinMode = SPI_MUX_MODE; } initialized++; /* reference count */ From c396a3ad4f0ffb9163b0a2b270aa4584848764f3 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Tue, 4 Oct 2016 16:59:36 -0700 Subject: [PATCH 009/125] Shrink oversized objects in SRAM 824 bytes is gained for use by sketches. Two new defines are added; CDCACM_BUFFER_SIZE and UART_BUFFER_SIZE. The CDC-ACM Serial class and the UART driver had been using the same #define for buffer size-- SERIAL_BUFFER_SIZE-- and separating them means that the CDC-ACM buffer can stay the same size, while the UART buffer is shrunk. --- cores/arduino/CDCSerialClass.cpp | 8 ++++---- cores/arduino/RingBuffer.cpp | 4 ++-- cores/arduino/RingBuffer.h | 4 ++-- cores/arduino/UARTClass.cpp | 10 +++++----- .../framework/include/infra/port.h | 2 +- .../framework/include/platform.h | 4 ++-- variants/arduino_101/libarc32drv_arduino101.a | Bin 782772 -> 782628 bytes 7 files changed, 16 insertions(+), 16 deletions(-) diff --git a/cores/arduino/CDCSerialClass.cpp b/cores/arduino/CDCSerialClass.cpp index 56c50e89..5cd0083d 100644 --- a/cores/arduino/CDCSerialClass.cpp +++ b/cores/arduino/CDCSerialClass.cpp @@ -83,7 +83,7 @@ void CDCSerialClass::end( void ) int CDCSerialClass::available( void ) { -#define SBS SERIAL_BUFFER_SIZE +#define SBS CDCACM_BUFFER_SIZE if (!_shared_data->device_open) return (0); @@ -100,7 +100,7 @@ int CDCSerialClass::availableForWrite(void) int tail = _tx_buffer->tail; if (head >= tail) - return SERIAL_BUFFER_SIZE - head + tail - 1; + return CDCACM_BUFFER_SIZE - head + tail - 1; return tail - head - 1; } @@ -118,7 +118,7 @@ int CDCSerialClass::read( void ) return -1; uint8_t uc = _rx_buffer->data[_rx_buffer->tail]; - _rx_buffer->tail = (_rx_buffer->tail + 1) % SERIAL_BUFFER_SIZE; + _rx_buffer->tail = (_rx_buffer->tail + 1) % CDCACM_BUFFER_SIZE; return uc; } @@ -138,7 +138,7 @@ size_t CDCSerialClass::write( const uint8_t uc_data ) return(0); do { - int i = (uint32_t)(_tx_buffer->head + 1) % SERIAL_BUFFER_SIZE; + int i = (uint32_t)(_tx_buffer->head + 1) % CDCACM_BUFFER_SIZE; // if we should be storing the received character into the location // just before the tail (meaning that the head would advance to the // current location of the tail), we're about to overflow the buffer diff --git a/cores/arduino/RingBuffer.cpp b/cores/arduino/RingBuffer.cpp index 14ecc1e8..1cb74bbb 100644 --- a/cores/arduino/RingBuffer.cpp +++ b/cores/arduino/RingBuffer.cpp @@ -23,14 +23,14 @@ RingBuffer::RingBuffer( void ) { - memset( _aucBuffer, 0, SERIAL_BUFFER_SIZE ) ; + memset( _aucBuffer, 0, UART_BUFFER_SIZE ) ; _iHead=0 ; _iTail=0 ; } void RingBuffer::store_char( uint8_t c ) { - int i = (uint32_t)(_iHead + 1) % SERIAL_BUFFER_SIZE ; + int i = (uint32_t)(_iHead + 1) % UART_BUFFER_SIZE ; // if we should be storing the received character into the location // just before the tail (meaning that the head would advance to the diff --git a/cores/arduino/RingBuffer.h b/cores/arduino/RingBuffer.h index e391ace4..a844e738 100644 --- a/cores/arduino/RingBuffer.h +++ b/cores/arduino/RingBuffer.h @@ -23,12 +23,12 @@ // using a ring buffer (I think), in which head is the index of the location // to which to write the next incoming character and tail is the index of the // location from which to read. -#define SERIAL_BUFFER_SIZE 256 +#define UART_BUFFER_SIZE 64 class RingBuffer { public: - uint8_t _aucBuffer[SERIAL_BUFFER_SIZE] ; + uint8_t _aucBuffer[UART_BUFFER_SIZE] ; int _iHead ; int _iTail ; bool _buffer_overflow ; diff --git a/cores/arduino/UARTClass.cpp b/cores/arduino/UARTClass.cpp index 922cf74d..000985f2 100644 --- a/cores/arduino/UARTClass.cpp +++ b/cores/arduino/UARTClass.cpp @@ -134,7 +134,7 @@ uint32_t UARTClass::getInterruptPriority() int UARTClass::available( void ) { - return (uint32_t)(SERIAL_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % SERIAL_BUFFER_SIZE; + return (uint32_t)(UART_BUFFER_SIZE + _rx_buffer->_iHead - _rx_buffer->_iTail) % UART_BUFFER_SIZE; } int UARTClass::availableForWrite(void) @@ -143,7 +143,7 @@ int UARTClass::availableForWrite(void) return(0); int head = _tx_buffer->_iHead; int tail = _tx_buffer->_iTail; - if (head >= tail) return SERIAL_BUFFER_SIZE - 1 - head + tail; + if (head >= tail) return UART_BUFFER_SIZE - 1 - head + tail; return tail - head - 1; } @@ -162,7 +162,7 @@ int UARTClass::read( void ) return -1; uint8_t uc = _rx_buffer->_aucBuffer[_rx_buffer->_iTail]; - _rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % SERIAL_BUFFER_SIZE; + _rx_buffer->_iTail = (unsigned int)(_rx_buffer->_iTail + 1) % UART_BUFFER_SIZE; return uc; } @@ -182,7 +182,7 @@ size_t UARTClass::write( const uint8_t uc_data ) if (_tx_buffer->_iTail != _tx_buffer->_iHead) { // If busy we buffer - int l = (_tx_buffer->_iHead + 1) % SERIAL_BUFFER_SIZE; + int l = (_tx_buffer->_iHead + 1) % UART_BUFFER_SIZE; while (_tx_buffer->_iTail == l) ; // Spin locks if we're about to overwrite the buffer. This continues once the data is sent @@ -215,7 +215,7 @@ void UARTClass::IrqHandler( void ) { 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) % SERIAL_BUFFER_SIZE; + _tx_buffer->_iTail = (unsigned int)(_tx_buffer->_iTail + 1) % UART_BUFFER_SIZE; } else { diff --git a/system/libarc32_arduino101/framework/include/infra/port.h b/system/libarc32_arduino101/framework/include/infra/port.h index 8c7e52ff..e1c9ef5d 100644 --- a/system/libarc32_arduino101/framework/include/infra/port.h +++ b/system/libarc32_arduino101/framework/include/infra/port.h @@ -41,7 +41,7 @@ * @{ */ -#define MAX_PORTS 50 +#define MAX_PORTS 6 #ifndef CONFIG_INFRA_IS_MASTER diff --git a/system/libarc32_arduino101/framework/include/platform.h b/system/libarc32_arduino101/framework/include/platform.h index 0bd3830b..583fcf71 100644 --- a/system/libarc32_arduino101/framework/include/platform.h +++ b/system/libarc32_arduino101/framework/include/platform.h @@ -39,12 +39,12 @@ #define CPU_ID_HOST 3 #define NUM_CPU 4 -#define SERIAL_BUFFER_SIZE 256 +#define CDCACM_BUFFER_SIZE 256 struct cdc_ring_buffer { /** Ring buffer data */ - uint8_t data[SERIAL_BUFFER_SIZE]; + uint8_t data[CDCACM_BUFFER_SIZE]; /** Ring buffer head pointer, modified by producer */ int head; /** Ring buffer head pointer, modified by consumer */ diff --git a/variants/arduino_101/libarc32drv_arduino101.a b/variants/arduino_101/libarc32drv_arduino101.a index 2d62036cb637f949522b45d05a75c6129d571e4f..e0cb6b8ebca696bb969e406b16df05c24693f4f2 100644 GIT binary patch delta 87834 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\m&zlE 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%0D>d2i^!LYQIw1as>2)avsQUDRXAwuJS>c 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 F{ufdFnGpa0 From 39ef9ef12c50366c4b77f494ae53361bbaf2118c Mon Sep 17 00:00:00 2001 From: Calvin Park Date: Fri, 7 Oct 2016 11:07:44 -0700 Subject: [PATCH 010/125] Don't save unstripped elf for debugging (#314) The current save mechanism breaks when a user has deleted system32 from PATH environment variable. Comment out the copying so that it won't break for such users, but do not delete so that advanced users can use the unstripped elfs to debug. --- platform.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform.txt b/platform.txt index e0ff63ce..f222a263 100644 --- a/platform.txt +++ b/platform.txt @@ -70,9 +70,9 @@ recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compil ## Combine gc-sections, archives, and objects recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" "-L{build.variant.path}" -Wl,--whole-archive "-l{build.variant_system_lib}" -Wl,--no-whole-archive -Wl,--start-group "-l{build.variant_system_lib}" -lnsim -lc -lm -lgcc {object_files} "{build.path}/{archive_file}" -## Save output with debug symbols (.debug.elf file) -recipe.hooks.objcopy.preobjcopy.1.pattern=cp -f "{build.path}/{build.project_name}.elf" "{build.path}/../arduino101_sketch.debug.elf" -recipe.hooks.objcopy.preobjcopy.1.pattern.windows=cmd /C copy /y "{build.path}\{build.project_name}.elf" "{build.path}\..\arduino101_sketch.debug.elf" +## Save output with debug symbols (.debug.elf file). Uncomment if you wish to use OpenOCD to debug. +#recipe.hooks.objcopy.preobjcopy.1.pattern=cp -f "{build.path}/{build.project_name}.elf" "{build.path}/../arduino101_sketch.debug.elf" +#recipe.hooks.objcopy.preobjcopy.1.pattern.windows=cmd /C copy /y "{build.path}\{build.project_name}.elf" "{build.path}\..\arduino101_sketch.debug.elf" ## Create output (.bin file) recipe.objcopy.bin.pattern="{compiler.path}{compiler.elf2bin.cmd}" {compiler.elf2bin.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin" From 27a68decb8e450f52b7c4c9dc254a1b0cb045b17 Mon Sep 17 00:00:00 2001 From: Biagio Montaruli Date: Wed, 7 Sep 2016 13:15:41 +0200 Subject: [PATCH 011/125] Update sketches that use serial communication of different libraries Improve sketches of CurieTime, CurieTimerOne, CurieSoftwareSerial, SPI and SerialFlash libraries that use Serial communication. Since Arduino/Genuino 101 uses USB native port, wait for the Serial port to open to not lose serial data already sent to the Serial monitor. Signed-off-by: Biagio Montaruli --- .../examples/SoftwareSerialExample/SoftwareSerialExample.ino | 2 +- libraries/CurieTime/examples/ReadTest/ReadTest.ino | 4 ++-- libraries/CurieTime/examples/SetTime/SetTime.ino | 4 ++-- .../examples/CurieTimer1BlinkSpeed/CurieTimer1BlinkSpeed.ino | 3 ++- .../examples/CurieTimer1Interrupt/CurieTimer1Interrupt.ino | 4 ++-- .../BarometricPressureSensor/BarometricPressureSensor.ino | 3 ++- .../SerialFlash/examples/CopyFromSerial/CopyFromSerial.ino | 3 ++- 7 files changed, 13 insertions(+), 10 deletions(-) diff --git a/libraries/CurieSoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino b/libraries/CurieSoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino index 14e88720..581f3c82 100644 --- a/libraries/CurieSoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino +++ b/libraries/CurieSoftwareSerial/examples/SoftwareSerialExample/SoftwareSerialExample.ino @@ -34,7 +34,7 @@ void setup() // Open serial communications and wait for port to open: Serial.begin(115200); while (!Serial) { - ; // wait for serial port to connect. Needed for Leonardo only + ; // wait for serial port to connect. Needed for Native USB only } diff --git a/libraries/CurieTime/examples/ReadTest/ReadTest.ino b/libraries/CurieTime/examples/ReadTest/ReadTest.ino index 4483268f..12593f73 100644 --- a/libraries/CurieTime/examples/ReadTest/ReadTest.ino +++ b/libraries/CurieTime/examples/ReadTest/ReadTest.ino @@ -6,8 +6,8 @@ #include void setup() { - while (!Serial); - Serial.begin(9600); + Serial.begin(9600); // initialize Serial communication + while (!Serial); // wait for serial port to connect. Serial.println("CurieTime Read Test"); Serial.println("-------------------"); diff --git a/libraries/CurieTime/examples/SetTime/SetTime.ino b/libraries/CurieTime/examples/SetTime/SetTime.ino index 97dbefc6..42d1a611 100644 --- a/libraries/CurieTime/examples/SetTime/SetTime.ino +++ b/libraries/CurieTime/examples/SetTime/SetTime.ino @@ -6,8 +6,8 @@ #include void setup() { - while (!Serial); - Serial.begin(9600); + Serial.begin(9600); // initialize Serial communication + while(!Serial) ; // wait for serial port to connect. // set the current time to 14:27:00, December 14th, 2015 setTime(14, 27, 00, 14, 12, 2015); diff --git a/libraries/CurieTimerOne/examples/CurieTimer1BlinkSpeed/CurieTimer1BlinkSpeed.ino b/libraries/CurieTimerOne/examples/CurieTimer1BlinkSpeed/CurieTimer1BlinkSpeed.ino index 04147cf0..88f2ee7e 100644 --- a/libraries/CurieTimerOne/examples/CurieTimer1BlinkSpeed/CurieTimer1BlinkSpeed.ino +++ b/libraries/CurieTimerOne/examples/CurieTimer1BlinkSpeed/CurieTimer1BlinkSpeed.ino @@ -5,7 +5,8 @@ const int blinkPin = 13; void setup(void) { CurieTimerOne.initialize(50000); - Serial.begin(9600); + Serial.begin(9600); // initialize Serial communication + while(!Serial) ; // wait for serial port to connect. } void loop(void) diff --git a/libraries/CurieTimerOne/examples/CurieTimer1Interrupt/CurieTimer1Interrupt.ino b/libraries/CurieTimerOne/examples/CurieTimer1Interrupt/CurieTimer1Interrupt.ino index 30750399..a7284b5b 100644 --- a/libraries/CurieTimerOne/examples/CurieTimer1Interrupt/CurieTimer1Interrupt.ino +++ b/libraries/CurieTimerOne/examples/CurieTimer1Interrupt/CurieTimer1Interrupt.ino @@ -38,8 +38,8 @@ void timedBlinkIsr() // callback function when interrupt is asserted void setup() { #ifdef SERIAL_PORT_LOG_ENABLE - Serial.begin(115200); - while (!Serial); // wait for the serial monitor to open + Serial.begin(115200); // initialize Serial communication + while (!Serial); // wait for the serial monitor to open #endif // Initialize pin 13 as an output - onboard LED. diff --git a/libraries/SPI/examples/BarometricPressureSensor/BarometricPressureSensor.ino b/libraries/SPI/examples/BarometricPressureSensor/BarometricPressureSensor.ino index 8104fcbc..4e5a2734 100644 --- a/libraries/SPI/examples/BarometricPressureSensor/BarometricPressureSensor.ino +++ b/libraries/SPI/examples/BarometricPressureSensor/BarometricPressureSensor.ino @@ -38,7 +38,8 @@ const int dataReadyPin = 6; const int chipSelectPin = 7; void setup() { - Serial.begin(9600); + Serial.begin(9600); // initialize Serial communication + while(!Serial) ; // wait for serial port to connect. // start the SPI library: SPI.begin(); diff --git a/libraries/SerialFlash/examples/CopyFromSerial/CopyFromSerial.ino b/libraries/SerialFlash/examples/CopyFromSerial/CopyFromSerial.ino index d7c4f376..6d1af440 100644 --- a/libraries/SerialFlash/examples/CopyFromSerial/CopyFromSerial.ino +++ b/libraries/SerialFlash/examples/CopyFromSerial/CopyFromSerial.ino @@ -82,7 +82,8 @@ #define CSPIN 21 void setup(){ - Serial.begin(9600); //Teensy serial is always at full USB speed and buffered... the baud rate here is required but ignored + Serial.begin(9600); // initialize Serial communication + while(!Serial) ; // wait for serial port to connect. pinMode(13, OUTPUT); From d6b2ecaa47bd543dbb35a33b36dfa2fb9037de86 Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Wed, 28 Sep 2016 12:54:37 -0700 Subject: [PATCH 012/125] pwm improvements -add capability to change pwm signal frequency with analogWriteFrequency -improve pwm signal frequency accuracy --- cores/arduino/Arduino.h | 2 ++ cores/arduino/wiring_analog.c | 30 +++++++++++++++--------------- cores/arduino/wiring_analog.h | 11 ++++++++++- variants/arduino_101/variant.cpp | 1 + 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index dc557e36..204a1b5f 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -95,6 +95,8 @@ typedef struct _PinDescription /* Pins table to be instanciated into variant.cpp */ extern PinDescription g_APinDescription[] ; +extern uint32_t pwmPeriod[4]; + #ifdef __cplusplus } // extern "C" diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index 332e07d4..03089cf5 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -27,11 +27,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /* Standard Arduino PWM resolution */ static int _writeResolution = 8; static int _readResolution = 10; - +uint32_t maxResolutionValue = 0xFF; void analogWriteResolution(int res) { _writeResolution = res; + maxResolutionValue = 0xFFFFFFFF >> (32-res); } void analogReadResolution(int res) @@ -49,7 +50,7 @@ static inline uint32_t mapResolution(uint32_t value, uint32_t from, uint32_t to) return value << (to-from); } -void analogWrite(uint8_t pin, int val) +void analogWrite(uint8_t pin, uint32_t val) { if (! digitalPinHasPWM(pin)) { @@ -75,20 +76,12 @@ void analogWrite(uint8_t pin, int val) } else { /* PWM for everything in between */ PinDescription *p = &g_APinDescription[pin]; - uint32_t hcnt = mapResolution(val, _writeResolution, PWM_RESOLUTION); - uint32_t lcnt = PWM_MAX_DUTY_CYCLE - hcnt; - uint32_t offset; - - /* For Arduino Uno compatibilty, we scale up frequency on certain pins */ - hcnt >>= p->ulPwmScale; - lcnt >>= p->ulPwmScale; - - /* Each count must be > 0 */ - if (hcnt < PWM_MIN_DUTY_CYCLE) - hcnt = PWM_MIN_DUTY_CYCLE; - if (lcnt < PWM_MIN_DUTY_CYCLE) - lcnt = PWM_MIN_DUTY_CYCLE; + uint32_t offset; + + uint32_t hcnt = (val/(float)maxResolutionValue) * pwmPeriod[p->ulPwmChan]; + uint32_t lcnt = pwmPeriod[p->ulPwmChan] - hcnt; + /* Set the high count period (duty cycle) */ offset = ((p->ulPwmChan * QRK_PWM_N_LCNT2_LEN) + QRK_PWM_N_LOAD_COUNT2); MMIO_REG_VAL(QRK_PWM_BASE_ADDR + offset) = hcnt; @@ -139,6 +132,13 @@ uint32_t analogRead(uint32_t pin) } +void analogWriteFrequency(uint8_t pin, uint32_t freq) +{ + //convert frequency to period in clock ticks + PinDescription *p = &g_APinDescription[pin]; + pwmPeriod[p->ulPwmChan] = F_CPU / freq; +} + #ifdef __cplusplus } #endif diff --git a/cores/arduino/wiring_analog.h b/cores/arduino/wiring_analog.h index c30aa5ff..5777d534 100644 --- a/cores/arduino/wiring_analog.h +++ b/cores/arduino/wiring_analog.h @@ -33,6 +33,8 @@ typedef enum _eAnalogReference AR_DEFAULT, } eAnalogReference ; +#define PWM_PERIOD 65306 //490 Hz + /* * \brief Configures the reference voltage used for analog input (i.e. the value used as the top of the input range). * This function is kept only for compatibility with existing AVR based API. @@ -47,7 +49,7 @@ extern void analogReference( eAnalogReference ulMode ) ; * \param pin * \param val */ -extern void analogWrite( uint8_t pin, int val ) ; +extern void analogWrite( uint8_t pin, uint32_t val ) ; /* * \brief Reads the value from the specified analog pin. @@ -72,6 +74,13 @@ extern void analogReadResolution(int res); */ extern void analogWriteResolution(int res); +/* + * \brief Set the frequency of analogWrite parameters. + * + * \param res + */ +extern void analogWriteFrequency(uint8_t pin, uint32_t freq); + #ifdef __cplusplus } #endif diff --git a/variants/arduino_101/variant.cpp b/variants/arduino_101/variant.cpp index c20dc4c3..6829e3b0 100644 --- a/variants/arduino_101/variant.cpp +++ b/variants/arduino_101/variant.cpp @@ -98,6 +98,7 @@ PinDescription g_APinDescription[]= { 3, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_AON_BASE_ADDR, 3, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO28 } ; +uint32_t pwmPeriod[] = {PWM_PERIOD, PWM_PERIOD/2, PWM_PERIOD/2, PWM_PERIOD}; #ifdef __cplusplus } #endif From 3d3c1f92c2c15724725f71542bc564754ba5d6e3 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Wed, 12 Oct 2016 15:42:50 -0700 Subject: [PATCH 013/125] I2SDMA: Do not modify g_APinDescription The g_APinDesctiption is now 'const', so cannot be modified. --- libraries/CurieI2S/src/CurieI2SDMA.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/libraries/CurieI2S/src/CurieI2SDMA.cpp b/libraries/CurieI2S/src/CurieI2SDMA.cpp index 9ac8de50..3840ecde 100644 --- a/libraries/CurieI2S/src/CurieI2SDMA.cpp +++ b/libraries/CurieI2S/src/CurieI2SDMA.cpp @@ -110,9 +110,6 @@ void Curie_I2SDMA::muxTX(bool enable) SET_PIN_MODE(g_APinDescription[I2S_TXD].ulSocPin, mux_mode); SET_PIN_MODE(g_APinDescription[I2S_TWS].ulSocPin, mux_mode); SET_PIN_MODE(g_APinDescription[I2S_TSCK].ulSocPin, mux_mode); - g_APinDescription[I2S_TXD].ulPinMode = mux_mode; - g_APinDescription[I2S_TWS].ulPinMode = mux_mode; - g_APinDescription[I2S_TSCK].ulPinMode = mux_mode; } void Curie_I2SDMA::muxRX(bool enable) @@ -127,9 +124,6 @@ void Curie_I2SDMA::muxRX(bool enable) SET_PIN_MODE(49, mux_mode); //I2S_RXD SET_PIN_MODE(51, mux_mode); //I2S_RWS SET_PIN_MODE(50, mux_mode); //I2S_RSCK - g_APinDescription[I2S_RXD].ulPinMode = mux_mode; - g_APinDescription[I2S_RWS].ulPinMode = mux_mode; - g_APinDescription[I2S_RSCK].ulPinMode = mux_mode; } int Curie_I2SDMA::beginTX(uint16_t sample_rate,uint8_t resolution,uint8_t master,uint8_t mode) From 1c8de0d7cc0ecf93abeb539bd97b0fee2b09eb97 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Tue, 11 Oct 2016 10:11:29 -0700 Subject: [PATCH 014/125] Add functions to check if new IMU data is available accelDataReady() and gyroDataReady() read the drdy_acc and drdy_gyr bits, respectively, of the BMI160's sensor status register. This removes the burden of syncronising sensor reads from the user. (adapted from descampsa/corelibs-arduino101@773a800) --- libraries/CurieIMU/src/BMI160.cpp | 16 ++++++++++++++++ libraries/CurieIMU/src/BMI160.h | 3 +++ 2 files changed, 19 insertions(+) diff --git a/libraries/CurieIMU/src/BMI160.cpp b/libraries/CurieIMU/src/BMI160.cpp index 746e8123..d1bf2a22 100644 --- a/libraries/CurieIMU/src/BMI160.cpp +++ b/libraries/CurieIMU/src/BMI160.cpp @@ -2386,3 +2386,19 @@ uint8_t BMI160Class::getRegister(uint8_t reg) { void BMI160Class::setRegister(uint8_t reg, uint8_t data) { reg_write(reg, data); } + +/** Check if new gyroscope data is available + * @return True if new data is available, else false. + */ +bool BMI160Class::gyroDataReady() +{ + return reg_read_bits(BMI160_RA_STATUS, BMI160_STATUS_DRDY_GYR, 1); +} + +/** Check if new accelerometer data is available + * @return True if new data is available, else false. + */ +bool BMI160Class::accelDataReady() +{ + return reg_read_bits(BMI160_RA_STATUS, BMI160_STATUS_DRDY_ACC, 1); +} diff --git a/libraries/CurieIMU/src/BMI160.h b/libraries/CurieIMU/src/BMI160.h index b452965a..6834ace6 100644 --- a/libraries/CurieIMU/src/BMI160.h +++ b/libraries/CurieIMU/src/BMI160.h @@ -653,6 +653,9 @@ class BMI160Class { void setInterruptLatch(uint8_t latch); void resetInterrupt(); + bool gyroDataReady(); + bool accelDataReady(); + protected: virtual int serial_buffer_transfer(uint8_t *buf, unsigned tx_cnt, unsigned rx_cnt); From 4e2232e3e55ab68da0e125f59da2817f45b7e06d Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Wed, 19 Oct 2016 15:26:19 -0700 Subject: [PATCH 015/125] Expose dccm area -expose dccm area giving the user an extra 8k of memory -simple implementation of a malloc like function for dccm memory area --- cores/arduino/Arduino.h | 1 + cores/arduino/dccm/dccm_alloc.c | 42 +++++++++++++++++++++++++++++++++ cores/arduino/dccm/dccm_alloc.h | 39 ++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 cores/arduino/dccm/dccm_alloc.c create mode 100644 cores/arduino/dccm/dccm_alloc.h diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index 204a1b5f..8060307c 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -117,5 +117,6 @@ extern uint32_t pwmPeriod[4]; #include "wiring_analog.h" #include "wiring_shift.h" #include "WInterrupts.h" +#include "dccm/dccm_alloc.h" #endif // Arduino_h diff --git a/cores/arduino/dccm/dccm_alloc.c b/cores/arduino/dccm/dccm_alloc.c new file mode 100644 index 00000000..d679c8b3 --- /dev/null +++ b/cores/arduino/dccm/dccm_alloc.c @@ -0,0 +1,42 @@ +/* +Copyright (c) 2016 Intel Corporation. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "dccm_alloc.h" + +uint16_t dccm_index = 0; + +#ifdef __cplusplus + extern "C" { +#endif + +void* dccm_malloc(uint16_t size) +{ + if((size + dccm_index) > DCCM_SIZE) + { + return 0; + } + + void* addr = (void*)(DCCM_START + dccm_index); + dccm_index += size; + return addr; +} + +#ifdef __cplusplus +} +#endif diff --git a/cores/arduino/dccm/dccm_alloc.h b/cores/arduino/dccm/dccm_alloc.h new file mode 100644 index 00000000..bddc21d3 --- /dev/null +++ b/cores/arduino/dccm/dccm_alloc.h @@ -0,0 +1,39 @@ +/* +Copyright (c) 2016 Intel Corporation. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include + +#define DCCM_START 0x80000000 +#define DCCM_SIZE 8192 + +#ifndef _DCCM_ALLOC_ +#define _DCCM_ALLOC_ + +#ifdef __cplusplus + extern "C" { +#endif + +void* dccm_malloc(uint16_t size); + +#ifdef __cplusplus +} +#endif + + +#endif \ No newline at end of file From 8619f2b3e088c28c389550a19ecf2e2e3029543e Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Thu, 20 Oct 2016 11:35:08 -0700 Subject: [PATCH 016/125] Move buffers for Wire library to DCCM area --- libraries/Wire/src/Wire.cpp | 3 ++- libraries/Wire/src/Wire.h | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/Wire/src/Wire.cpp b/libraries/Wire/src/Wire.cpp index 2f8601f7..3315fe8e 100644 --- a/libraries/Wire/src/Wire.cpp +++ b/libraries/Wire/src/Wire.cpp @@ -33,7 +33,8 @@ TwoWire::TwoWire(I2C_CONTROLLER _controller_id) : rxBufferIndex(0), rxBufferLength(0), init_status(-1), controller_id(_controller_id) { - // Empty + rxBuffer = (uint8_t*)dccm_malloc(BUFFER_LENGTH); + txBuffer = (uint8_t*)dccm_malloc(BUFFER_LENGTH); } void TwoWire::begin(void) diff --git a/libraries/Wire/src/Wire.h b/libraries/Wire/src/Wire.h index a18e1f19..c7f270e4 100644 --- a/libraries/Wire/src/Wire.h +++ b/libraries/Wire/src/Wire.h @@ -58,13 +58,13 @@ class TwoWire : public Stream { private: // RX Buffer - uint8_t rxBuffer[BUFFER_LENGTH]; + uint8_t *rxBuffer; uint8_t rxBufferIndex; uint8_t rxBufferLength; // TX Buffer uint8_t txAddress; - uint8_t txBuffer[BUFFER_LENGTH]; + uint8_t *txBuffer; uint8_t txBufferLength; int init_status; From 41f98f9c459a0ed7175b34c9c334c03c352c0706 Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Thu, 20 Oct 2016 15:14:47 -0700 Subject: [PATCH 017/125] Allocate RingBuffer buffer into DCCM area -this buffer is used by the uart. Moving it into the DCCM area would save us 128 bytes of SRAM --- cores/arduino/RingBuffer.cpp | 1 + cores/arduino/RingBuffer.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/cores/arduino/RingBuffer.cpp b/cores/arduino/RingBuffer.cpp index 1cb74bbb..c3ec50c4 100644 --- a/cores/arduino/RingBuffer.cpp +++ b/cores/arduino/RingBuffer.cpp @@ -23,6 +23,7 @@ RingBuffer::RingBuffer( void ) { + _aucBuffer = (uint8_t*)dccm_malloc(UART_BUFFER_SIZE); memset( _aucBuffer, 0, UART_BUFFER_SIZE ) ; _iHead=0 ; _iTail=0 ; diff --git a/cores/arduino/RingBuffer.h b/cores/arduino/RingBuffer.h index a844e738..0e87b7ce 100644 --- a/cores/arduino/RingBuffer.h +++ b/cores/arduino/RingBuffer.h @@ -18,6 +18,7 @@ #define _RING_BUFFER_ #include +#include "dccm/dccm_alloc.h" // Define constants and variables for buffering incoming serial data. We're // using a ring buffer (I think), in which head is the index of the location @@ -28,7 +29,7 @@ class RingBuffer { public: - uint8_t _aucBuffer[UART_BUFFER_SIZE] ; + uint8_t *_aucBuffer; int _iHead ; int _iTail ; bool _buffer_overflow ; From 6ee828b737b4bd08dda78b0769bb9043c64d2c4c Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Thu, 20 Oct 2016 15:53:53 -0700 Subject: [PATCH 018/125] Move SoftwareSerial buffers into DCCM area --- libraries/CurieSoftwareSerial/src/SoftwareSerial.cpp | 4 ++-- libraries/CurieSoftwareSerial/src/SoftwareSerial.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/CurieSoftwareSerial/src/SoftwareSerial.cpp b/libraries/CurieSoftwareSerial/src/SoftwareSerial.cpp index a72ee892..bba162fe 100644 --- a/libraries/CurieSoftwareSerial/src/SoftwareSerial.cpp +++ b/libraries/CurieSoftwareSerial/src/SoftwareSerial.cpp @@ -31,14 +31,13 @@ Rx does not work for pin 13 // // Includes // -#include #include // // Statics // SoftwareSerial *SoftwareSerial::active_object = 0; -char SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF]; +char *SoftwareSerial::_receive_buffer; volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0; volatile uint8_t SoftwareSerial::_receive_buffer_head = 0; @@ -231,6 +230,7 @@ SoftwareSerial::SoftwareSerial(uint32_t receivePin, uint32_t transmitPin, bool i _transmitPin = transmitPin; setRX(receivePin); _receivePin = receivePin; + _receive_buffer = (char*)dccm_malloc(_SS_MAX_RX_BUFF); } // diff --git a/libraries/CurieSoftwareSerial/src/SoftwareSerial.h b/libraries/CurieSoftwareSerial/src/SoftwareSerial.h index 8e97cf79..1934d4e2 100644 --- a/libraries/CurieSoftwareSerial/src/SoftwareSerial.h +++ b/libraries/CurieSoftwareSerial/src/SoftwareSerial.h @@ -27,7 +27,7 @@ Rx does not work for pin 13 #include #include - +#include /****************************************************************************** * Definitions ******************************************************************************/ @@ -56,7 +56,7 @@ class SoftwareSerial : public Stream bool _inverse_logic = false; // static data - static char _receive_buffer[_SS_MAX_RX_BUFF]; + static char *_receive_buffer; static volatile uint8_t _receive_buffer_tail; static volatile uint8_t _receive_buffer_head; static SoftwareSerial *active_object; From c353f4caa7e5d9a4287410f2826f8e75683ab13c Mon Sep 17 00:00:00 2001 From: Calvin Park Date: Thu, 27 Oct 2016 15:57:39 -0700 Subject: [PATCH 019/125] Use our own version of copy.exe so that we can copy without cmd.exe --- platform.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform.txt b/platform.txt index f222a263..89747de3 100644 --- a/platform.txt +++ b/platform.txt @@ -71,8 +71,8 @@ recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compil recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" "-L{build.variant.path}" -Wl,--whole-archive "-l{build.variant_system_lib}" -Wl,--no-whole-archive -Wl,--start-group "-l{build.variant_system_lib}" -lnsim -lc -lm -lgcc {object_files} "{build.path}/{archive_file}" ## Save output with debug symbols (.debug.elf file). Uncomment if you wish to use OpenOCD to debug. -#recipe.hooks.objcopy.preobjcopy.1.pattern=cp -f "{build.path}/{build.project_name}.elf" "{build.path}/../arduino101_sketch.debug.elf" -#recipe.hooks.objcopy.preobjcopy.1.pattern.windows=cmd /C copy /y "{build.path}\{build.project_name}.elf" "{build.path}\..\arduino101_sketch.debug.elf" +recipe.hooks.objcopy.preobjcopy.1.pattern=cp -f "{build.path}/{build.project_name}.elf" "{build.path}/../arduino101_sketch.debug.elf" +recipe.hooks.objcopy.preobjcopy.1.pattern.windows={runtime.tools.arduino101load.path}/arduino101load/arduino101copy.exe "{build.path}\{build.project_name}.elf" "{build.path}\..\arduino101_sketch.debug.elf" ## Create output (.bin file) recipe.objcopy.bin.pattern="{compiler.path}{compiler.elf2bin.cmd}" {compiler.elf2bin.flags} {compiler.elf2hex.extra_flags} "{build.path}/{build.project_name}.elf" "{build.path}/{build.project_name}.bin" From 249fb3b7b6a7ebf85a202688c960de9f256a8018 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Mon, 14 Nov 2016 16:20:09 -0800 Subject: [PATCH 020/125] README.md: add link to ICS for product support --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b437314b..3ab29563 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ # corelibs-arduino101 +For Intel Community Support, product questions, or help troubleshooting, visit +ICS: [https://communities.intel.com/community/tech/intel-curie](https://communities.intel.com/community/tech/intel-curie) + +| The contents of this repo is distributed through releases in Arduino IDE. `Tools > Board > Boards Manager > Intel Curie Boards by Intel` From 5279509cc2f6d161e1607245f15024e5d578c10e Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Mon, 14 Nov 2016 10:12:39 -0800 Subject: [PATCH 021/125] CurieTime.cpp: check gmtime() return pointer for NULL-ness --- libraries/CurieTime/src/CurieTime.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/libraries/CurieTime/src/CurieTime.cpp b/libraries/CurieTime/src/CurieTime.cpp index 8f6bdd00..197fd543 100644 --- a/libraries/CurieTime/src/CurieTime.cpp +++ b/libraries/CurieTime/src/CurieTime.cpp @@ -50,7 +50,7 @@ int year(unsigned long t) { struct tm* tm = tTm(t); - return (tm->tm_year + YEAR_OFFSET); + return (tm) ? (tm->tm_year + YEAR_OFFSET) : -1; } int month() @@ -64,7 +64,7 @@ int month(unsigned long t) { struct tm* tm = tTm(t); - return (tm->tm_mon + MONTH_OFFSET); + return (tm) ? (tm->tm_mon + MONTH_OFFSET) : -1; } int day() @@ -78,7 +78,7 @@ int day(unsigned long t) { struct tm* tm = tTm(t); - return tm->tm_mday; + return (tm) ? tm->tm_mday : -1; } int hour() @@ -92,7 +92,7 @@ int hour(unsigned long t) { struct tm* tm = tTm(t); - return tm->tm_hour; + return (tm) ? tm->tm_hour : -1; } int minute() @@ -106,7 +106,7 @@ int minute(unsigned long t) { struct tm* tm = tTm(t); - return tm->tm_min; + return (tm) ? tm->tm_min : -1; } int second() @@ -120,7 +120,7 @@ int second(unsigned long t) { struct tm* tm = tTm(t); - return tm->tm_sec; + return (tm) ? tm->tm_sec : -1; } void setTime(unsigned long t) From 205c1e40852f356a4819a16260086e8f3c719592 Mon Sep 17 00:00:00 2001 From: Adrien Descamps Date: Thu, 11 Aug 2016 23:13:02 +0200 Subject: [PATCH 022/125] Enable interrupt mode of UART tx Previous version of write will never use the interrupt mode transmission, because it fails to check if holding register is empty. This commit fix that, and several bugs related to interrupt mode serial transmission. When possible, it uses the tx FIFO, in order to reduce the number of interrupts called and the overhead. --- cores/arduino/UARTClass.cpp | 37 +++++++++++------- system/libarc32_arduino101/drivers/ns16550.c | 21 ++++++++-- system/libarc32_arduino101/drivers/uart.h | 4 ++ variants/arduino_101/libarc32drv_arduino101.a | Bin 782628 -> 782810 bytes 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/cores/arduino/UARTClass.cpp b/cores/arduino/UARTClass.cpp index 000985f2..57d3f99e 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 != _tx_buffer->_iTail); //wait for transmit data to be sent + while (_tx_buffer->_iHead != *(volatile int*)&(_tx_buffer->_iTail)); //wait for transmit data to be sent // Wait for transmission to complete while(!uart_tx_complete(CONFIG_UART_CONSOLE_INDEX)); } @@ -179,12 +179,11 @@ size_t UARTClass::write( const uint8_t uc_data ) return(0); // Is the hardware currently busy? - if (_tx_buffer->_iTail != _tx_buffer->_iHead) + if (_tx_buffer->_iTail != _tx_buffer->_iHead || !uart_tx_ready(CONFIG_UART_CONSOLE_INDEX)) { // If busy we buffer int l = (_tx_buffer->_iHead + 1) % UART_BUFFER_SIZE; - while (_tx_buffer->_iTail == l) - ; // Spin locks if we're about to overwrite the buffer. This continues once the data is sent + while (*(volatile int*)&(_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; @@ -201,21 +200,29 @@ size_t UARTClass::write( const uint8_t uc_data ) void UARTClass::IrqHandler( void ) { - 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); + 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; 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); + } } - // Do we need to keep sending data? - if (!uart_irq_tx_ready(CONFIG_UART_CONSOLE_INDEX)) + // if irq is Transmitter Holding Register + else if(uart_irq_tx_ready(CONFIG_UART_CONSOLE_INDEX)) { - 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; + 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; } else { diff --git a/system/libarc32_arduino101/drivers/ns16550.c b/system/libarc32_arduino101/drivers/ns16550.c index 386c6b22..f32d5d32 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_TEMT) == 0) + while ((INBYTE(LSR(which)) & LSR_THRE) == 0) ; OUTBYTE(THR(which), outChar); @@ -352,6 +352,8 @@ 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 */ @@ -362,8 +364,8 @@ int uart_fifo_fill(int which, /* UART on which to send */ { int i; - for (i = 0; i < size && (INBYTE(LSR(which)) & - LSR_BOTH_EMPTY) != 0; i++) { + for (i = 0; i < size ; i++) + { OUTBYTE(THR(which), txData[i]); } return i; @@ -640,6 +642,19 @@ 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/drivers/uart.h b/system/libarc32_arduino101/drivers/uart.h index ed737780..456c244b 100644 --- a/system/libarc32_arduino101/drivers/uart.h +++ b/system/libarc32_arduino101/drivers/uart.h @@ -54,6 +54,9 @@ extern "C" { /* options for uart init */ #define UART_OPTION_AFCE 0x01 +/* Size of the FIFO in bytes */ +#define UART_FIFO_SIZE 16 + /* generic UART info structure */ struct uart_init_info { int baud_rate; @@ -92,6 +95,7 @@ int uart_break_check(int port); void uart_break_send(int port, int delay); void uart_disable(int port); uint8_t uart_tx_complete(int which); +uint8_t uart_tx_ready(int which); void uart_loop_enable(int which); void uart_loop_disable(int which); diff --git a/variants/arduino_101/libarc32drv_arduino101.a b/variants/arduino_101/libarc32drv_arduino101.a index e0cb6b8ebca696bb969e406b16df05c24693f4f2..2f8ce4f076a5fadf399bc63c203cf2f33a93eca6 100644 GIT binary patch delta 90725 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 delta 93642 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 DG?SEd From 2bb7d4b58e4152fde9b87153f71618475f54c42a Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Fri, 21 Oct 2016 16:20:37 -0700 Subject: [PATCH 023/125] README.md: remove Serial1 debug information --- README.md | 18 ------------------ libraries/CurieBLE/README.md | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 libraries/CurieBLE/README.md diff --git a/README.md b/README.md index 3ab29563..a86d8af5 100644 --- a/README.md +++ b/README.md @@ -57,21 +57,3 @@ them to the [support forum](https://forum.arduino.cc/index.php?board=103). > "How do I use this library?" > "I can't get this example sketch to work. What am I doing wrong?" - -# Enable debug interface on Serail1 - -* Default disable the debug interface. - -If you want to enable debug trace on Serial1 to debug corelib, follow these instructions. - -1. Shut down the IDE -2. Go to Arduino15 directory - * Windows: `C:\Users\\AppData\Roaming\Arduino15` - * OS X: `~/Library/Arduino15` - * Linux: `~/.arduino15` -3. Modify the platform.txt - * Find `compiler.c.flags` and add `-DCONFIGURE_DEBUG_CORELIB_ENABLED` at the end of this line - * Find `compiler.cpp.flags` and add `-DCONFIGURE_DEBUG_CORELIB_ENABLED` at the end of this line -4. Initial Serial1 in your sketch - * Add `Serial1.begin(115200);` in your `setup()` -5. Adjust the output level at log_init function in log.c diff --git a/libraries/CurieBLE/README.md b/libraries/CurieBLE/README.md new file mode 100644 index 00000000..3642943d --- /dev/null +++ b/libraries/CurieBLE/README.md @@ -0,0 +1,18 @@ +# Enable debug interface on Serail1 + +* Default disable the debug interface. + +If you want to enable debug trace on Serial1 to debug corelib, follow these instructions. + +1. Shut down the IDE +2. Go to Arduino15 directory + * Windows: `C:\Users\\AppData\Roaming\Arduino15` + * OS X: `~/Library/Arduino15` + * Linux: `~/.arduino15` +3. Modify the platform.txt + * Find `compiler.c.flags` and add `-DCONFIGURE_DEBUG_CORELIB_ENABLED` at the end of this line + * Find `compiler.cpp.flags` and add `-DCONFIGURE_DEBUG_CORELIB_ENABLED` at the end of this line +4. Initial Serial1 in your sketch + * Add `Serial1.begin(115200);` in your `setup()` +5. Adjust the output level at log_init function in log.c + From de208be5c42a305624468d9a1a5208f60d0b5bdb Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Tue, 29 Nov 2016 10:35:10 -0800 Subject: [PATCH 024/125] README.md: add steps for updating intel-arduino-tools --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a86d8af5..abbca1e8 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,10 @@ If you wish to use the latest **untested** changes, follow these instructions. 6. Delete the content of the directory from step 5, and replace it with the content of the "corelibs-arduino101-master" folder in the zip from step 2. +Note: your [tools](https://github.com/01org/intel-arduino-tools), found in +`Arduino15/packages/Intel/tools/arduino101load//`, may also need to +be updated to the latest snapshot. + Future upgrades may fail since the internal contents were modified by hand. In order to recover, shut down the IDE, delete the entire `Arduino15` directory, then restart the IDE. From e870ba255646afa47debfc0b1cb493cc055520dd Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Tue, 29 Nov 2016 13:27:38 -0800 Subject: [PATCH 025/125] Ensure RTC clock scaling is set to 1Hz -needed to make sure that the RTC scaling is consitent with the factory firmware and CODK-M based firmware --- variants/arduino_101/variant.cpp | 7 ++++++- variants/arduino_101/variant.h | 8 ++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/variants/arduino_101/variant.cpp b/variants/arduino_101/variant.cpp index 6829e3b0..e96570e8 100644 --- a/variants/arduino_101/variant.cpp +++ b/variants/arduino_101/variant.cpp @@ -240,7 +240,12 @@ void initVariant( void ) variantGpioInit(); variantPwmInit(); variantAdcInit(); - + + //set RTC clock divider to 32768(1 Hz) + *SYS_CLK_CTL |= RTC_DIV_1HZ_MASK; + *SYS_CLK_CTL &= ~(1 << CCU_RTC_CLK_DIV_EN); + *SYS_CLK_CTL |= 1 << CCU_RTC_CLK_DIV_EN; + cfw_platform_init(); // Add for debug corelib diff --git a/variants/arduino_101/variant.h b/variants/arduino_101/variant.h index ed53a179..4ee93f05 100644 --- a/variants/arduino_101/variant.h +++ b/variants/arduino_101/variant.h @@ -146,6 +146,14 @@ extern "C"{ #define ADC_RESOLUTION 12 #define ADC_CLOCK_GATE (1 << 31) +/* + * Clocking + */ + +#define SYS_CLK_CTL (volatile int*)0xB0800038 +#define CCU_RTC_CLK_DIV_EN 2 +#define RTC_DIV_1HZ_MASK 0x00000078 + #define digitalPinToBitMask(P) (1 << g_APinDescription[P].ulGPIOId) //static uint8_t __unused_var_POR; From 0d321db5cc619dc9a4451a6710dabd38fee492bf Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Tue, 29 Nov 2016 11:32:50 -0800 Subject: [PATCH 026/125] CurieIMU.cpp: Don't lock interrupts for SPI transfers This is unnecessary- nothing else is contending for access to this particular SPI controller --- libraries/CurieIMU/src/CurieIMU.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/libraries/CurieIMU/src/CurieIMU.cpp b/libraries/CurieIMU/src/CurieIMU.cpp index 94dc4f80..8b24c5a5 100644 --- a/libraries/CurieIMU/src/CurieIMU.cpp +++ b/libraries/CurieIMU/src/CurieIMU.cpp @@ -1778,20 +1778,10 @@ bool CurieIMUClass::stepsDetected() int CurieIMUClass::serial_buffer_transfer(uint8_t *buf, unsigned tx_cnt, unsigned rx_cnt) { - int flags, status; - if (rx_cnt) /* For read transfers, assume 1st byte contains register address */ buf[0] |= (1 << BMI160_SPI_READ_BIT); - /* Lock interrupts here to - * - avoid concurrent access to the SPI bus - * - avoid delays in SPI transfer due to unrelated interrupts - */ - flags = interrupt_lock(); - status = ss_spi_xfer(SPI_SENSING_1, buf, tx_cnt, rx_cnt); - interrupt_unlock(flags); - - return status; + return ss_spi_xfer(SPI_SENSING_1, buf, tx_cnt, rx_cnt); } /** Interrupt handler for interrupts from PIN1 on the BMI160 From 16227f53f2591eaa47e551b8000878855294eb35 Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Tue, 13 Dec 2016 16:15:51 -0800 Subject: [PATCH 027/125] Ensure RTC clock scaling is set to 1Hz (#350) -needed to make sure that the RTC scaling is consitent with the factory firmware and CODK-M based firmware From 431c7812e71c904b0e8212a0234d520ffbbe2b0d Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Tue, 13 Dec 2016 16:16:17 -0800 Subject: [PATCH 028/125] check mux mode when using digitalWrite (#335) -check and make sure that the pin is in GPIO_MUX_MODE when doing a digitalWrite() -this has a slight performance impact of digitalWrite -it also comsumes an extra byte in SRAM per pin -it does however give us the ability to track the muxing state of each pin --- cores/arduino/Arduino.h | 4 ++++ cores/arduino/wiring_analog.c | 12 ++++++++---- cores/arduino/wiring_digital.c | 12 +++++++++++- libraries/CurieSoftwareSerial/src/SoftwareSerial.cpp | 4 ++-- libraries/SPI/src/SPI.cpp | 4 +++- variants/arduino_101/variant.cpp | 3 +++ 6 files changed, 31 insertions(+), 8 deletions(-) diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index 8060307c..e656d4ee 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -26,6 +26,8 @@ #include #include +#include "pins_arduino.h" + #include "binary.h" //#include "itoa.h" @@ -97,6 +99,8 @@ extern PinDescription g_APinDescription[] ; extern uint32_t pwmPeriod[4]; +extern uint8_t pinmuxMode[NUM_DIGITAL_PINS]; + #ifdef __cplusplus } // extern "C" diff --git a/cores/arduino/wiring_analog.c b/cores/arduino/wiring_analog.c index 03089cf5..c2917f89 100644 --- a/cores/arduino/wiring_analog.c +++ b/cores/arduino/wiring_analog.c @@ -93,10 +93,14 @@ void analogWrite(uint8_t pin, uint32_t val) /* start the PWM output */ offset = ((p->ulPwmChan * QRK_PWM_N_REGS_LEN) + QRK_PWM_N_CONTROL); SET_MMIO_MASK(QRK_PWM_BASE_ADDR + offset, QRK_PWM_CONTROL_ENABLE); - - /* Disable pull-up and set pin mux for PWM output */ - SET_PIN_PULLUP(p->ulSocPin, 0); - SET_PIN_MODE(p->ulSocPin, PWM_MUX_MODE); + + if(pinmuxMode[pin] != PWM_MUX_MODE) + { + /* Disable pull-up and set pin mux for PWM output */ + SET_PIN_PULLUP(p->ulSocPin, 0); + SET_PIN_MODE(p->ulSocPin, PWM_MUX_MODE); + pinmuxMode[pin] = PWM_MUX_MODE; + } } } uint32_t analogRead(uint32_t pin) diff --git a/cores/arduino/wiring_digital.c b/cores/arduino/wiring_digital.c index 67390f2d..e895a822 100644 --- a/cores/arduino/wiring_digital.c +++ b/cores/arduino/wiring_digital.c @@ -53,6 +53,10 @@ void pinMode( uint8_t pin, uint8_t mode ) /* Set SoC pin mux configuration */ SET_PIN_PULLUP(p->ulSocPin, (mode == INPUT_PULLUP) ? 1 : 0); SET_PIN_MODE(p->ulSocPin, GPIO_MUX_MODE); + if(pinmuxMode[pin] != GPIO_MUX_MODE) + { + pinmuxMode[pin] = GPIO_MUX_MODE; + } } void digitalWrite( uint8_t pin, uint8_t val ) @@ -60,7 +64,13 @@ void digitalWrite( uint8_t pin, uint8_t val ) if (pin >= NUM_DIGITAL_PINS) return; PinDescription *p = &g_APinDescription[pin]; - + + if(pinmuxMode[pin] != GPIO_MUX_MODE) + { + pinmuxMode[pin] = GPIO_MUX_MODE; + SET_PIN_MODE(p->ulSocPin, GPIO_MUX_MODE); + } + if (p->ulGPIOType == SS_GPIO) { uint32_t reg = p->ulGPIOBase + SS_GPIO_SWPORTA_DR; if (val) diff --git a/libraries/CurieSoftwareSerial/src/SoftwareSerial.cpp b/libraries/CurieSoftwareSerial/src/SoftwareSerial.cpp index bba162fe..8be77370 100644 --- a/libraries/CurieSoftwareSerial/src/SoftwareSerial.cpp +++ b/libraries/CurieSoftwareSerial/src/SoftwareSerial.cpp @@ -280,8 +280,8 @@ void SoftwareSerial::begin(long speed) { _isSOCGpio = true; } - //toggling a pin takes about 62 ticks - _tx_delay = _bit_delay - 62; + //toggling a pin takes about 68 ticks + _tx_delay = _bit_delay - 68; //reading a pin takes about 70 ticks _rx_delay_intrabit = _bit_delay - 70; //it takes about 272 ticks from when the start bit is received to when the ISR is called diff --git a/libraries/SPI/src/SPI.cpp b/libraries/SPI/src/SPI.cpp index 29c6da47..628756a8 100755 --- a/libraries/SPI/src/SPI.cpp +++ b/libraries/SPI/src/SPI.cpp @@ -94,7 +94,9 @@ void SPIClass::begin() SET_PIN_MODE(g_APinDescription[MOSI].ulSocPin, SPI_MUX_MODE); SET_PIN_MODE(g_APinDescription[MISO].ulSocPin, SPI_MUX_MODE); SET_PIN_MODE(g_APinDescription[SCK].ulSocPin, SPI_MUX_MODE); - + pinmuxMode[MISO] = SPI_MUX_MODE; + pinmuxMode[MOSI] = SPI_MUX_MODE; + pinmuxMode[SCK] = SPI_MUX_MODE; } initialized++; /* reference count */ interrupt_unlock(flags); diff --git a/variants/arduino_101/variant.cpp b/variants/arduino_101/variant.cpp index e96570e8..6993d2f5 100644 --- a/variants/arduino_101/variant.cpp +++ b/variants/arduino_101/variant.cpp @@ -99,6 +99,8 @@ PinDescription g_APinDescription[]= } ; uint32_t pwmPeriod[] = {PWM_PERIOD, PWM_PERIOD/2, PWM_PERIOD/2, PWM_PERIOD}; + +uint8_t pinmuxMode[]; #ifdef __cplusplus } #endif @@ -173,6 +175,7 @@ void variantGpioInit(void) for (uint8_t pin = 0; pin < NUM_DIGITAL_PINS; pin++) { PinDescription *p = &g_APinDescription[pin]; SET_PIN_MODE(p->ulSocPin, p->ulPinMode); + pinmuxMode[pin] = GPIO_MUX_MODE; } } From ade23f0c2c064b733a1c3d264eb29e1a608f8745 Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 17 Oct 2016 16:11:18 +0800 Subject: [PATCH 029/125] 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 zcmc${2YeO9{x-g|=OmmoNP&bTl#oCYS{k7v1c-F$DkzeK9%=|tP>_Rw3J8KOFe)gh zs8~UxMg;|X7Yi!3Yr%U(L7D~cweWwQ+3$t}>h<@(_xHZukJj?n&e5k^B>kp1Ge?{K6CYJg)huz{bXN}4UZX9RX|H<)P zq-Fof;p_f}^*=p=MsV^%%jX?)U$A`t?s%_>t0ye~g!TW)5&Tb-{$ri6ewmgz zDvw)X3*&JF`Sbtq2qN+Dy_fv29d$;ou*@-Ku~lbDpT9hU{}ZAAiAFNj zcTB#_3Yo6rGV=oKFOTt; zSdsG=y2r9-tjIq)f`3HZ`Gytw+P_XeKEKS0{DUJZJ;(ZQj$o~FRWB>*ua1r9Sy4Rh zS@@qHfB(XYa*vW3W#ww7qxxT6_mYW~bINDUESWN~Y{JA!r!;0;b)CqCDo{1Aq!Oe{ z^&+jBSN93DR(jYsz8qcpr7zlfre3OkWS}oi|1{8-;5=G)SoFlIlJV1Ljh|LBxvZ-C z)~MaenH{m-d9cn^j+&X`tg6%839q-cMadQ^kaw(~{G8+CE6Z&JT{PLplQd8qOIy4PEg@oAHhn~N)|%4bY0X;(1K3OSuI$C^HCvL1RO zGSS(Sc8`;uR$q@h5gG4X)2w-*ykdMw`ONYvU3Oz+L+8A-!PVQEWh&=*`mA;pWi!ji zTO}oBbLLE}tSXsQRzBStKWT1B*>n`zph=YzCtBm&^Km?J7B;_D4{HTCI zIHfJ3oT?T*8eUvgK7CHktlY?P(LR+NpORyKL!oZS91t0qp*t(rA!`kdUd%JFR{PM_4S zAU8j!Lr!OXG$}gkzq6qF2O2x8TL-IGwrQaB1Lxb}XXLO`M6O%yImqx?D#7_|a^vbZ z+b;IkZDm-iLVOv&fA&mkq5IhdJv*7l?MS@dMHGKuI1RgB-GT?Is zM?rqP;NzfA5sb*XSa2K2TxnSyOkSqpWQ8#5AYLc<2`JwqI05)k4?7*(%?%EK)^Jc+ znv;kx5}XeD6v1yJo+CIJ+6x8mN4#9{3y8N0?hV~H1aCt8uHXfre=PWQ#0XB?!c}T^ z^^U?9l^WzM=`h4T5asOcaEaRM4D2{W6*?R6JIFcMv9((1Bz4MA6P&J{vcld9v#iz5 zeoz@|9?HwHG%33wNfR0h{Iod^7Me zv2!i(7Rzd7jIthg3gbO6o*_oHGrW$22~To149HOB&aMGT>T&1&0lD_SEGIB9J--~e z3qr|X&$1>%_)20ZECKx*;s~0tthNvI zNr`f~bA-zlyQo(31nl$zcH57bl_549~ z?EY6n*(fkINTJU$nhNv5y-ouaB+v_=n zBQ62y-VsC80_W!uL+pIXar%#Lomf#;HGYcq$G68hW1^hvKl<8v_kuOQw{Tr{ zAkUe2VO7|SvdQJ+b;T~bkIq{VSyzAZMIfk);=mnsQLp6Ds3p@;qhid}d0P*; zJTfVGF|K!(kk|{h5u5~U?lP!N2QCtNHt^YkUqST@7u*k+N%6v83_Mowc*su^%r;sn zxEgp7ZV#hyCr;3V8{8EN*9pD~_-4VkK;~}2Y}$_qo(250U_|a+iA;=y?gv7T2mZU@ z^B{jr@G{lavVN75DC7+7un&Zxy=I1Q$6d18a$92?=SW#r_%FB=DVF|nx!pYD-}ku%9Xq${ z;B4xgrDAJFtg!cG23q>rDhK}FrDzs|YrcC*9o>C}uR+)U*0s*buRUhp==(iSau|r1GYfu2!5p|16L#9m zb-i77q8|68y-lxv%3c&Re!B5hC&J^KG)p(%Wv|hHyC|Yj&D*=|PBAUQP=3>*Ti~IO zzR0v_;EPc0$-I~umX)Sg->8y;U*LM>k_7gRxNaQ;e}L*gOYo1N7Yk;u++T1kdL4|g z7~Lx&f1%)F*fh@#*t`u%&Jg->WaTo!>`kvQts6pdFlzcuVBS8aMFXD$e7h(-2W8W` zL9a$)+l0O!*tB%eJAt-C=)({{Ex2=-WxXhPF=F#vg8T zw*>v~g6km@hXlWfCVN8gNyI)Bg0*-qVm@h!+4rXkE<|kZ7|@%;P7&!roTMSKK_WC2 z@hHJJql%^p-T_N<1%C*md^jl>{ ziAv z+6beogt0TsvThK}s@fv>pD?st@K>n1oq{uA=q15#LwB!W7T`0%%;+J(EWl5Knb9yG zu0L}$9Ln)Xgg6O8O$0Xqy_Mj)Q0yRhCR+Yz!7R;0!2!@`3qA?m%LShc`m2K3zrQc| z)*uqwFDC;b^rPT#&~4ns%+O{eP**T?trWqXAe$+833Lkt-+>zHF8CVI2MD$xf1cp0 zQ2KGeK_=iJv6;f?35Cl9A3+JO@-Rw&z2JJF-y(P%GIx*QQNYg#o(%an1V4_%J`~JL z^bf%WHlA+B<>X~#;y1xbC}kuPVdhE@Ckk$l1kwfHhB!y?UX-kh;2lVykKnPY3_;ZxprfH{~W_!IrrKIGu@fNuVQ zeMxXPBdk68tOVZx>u$$Jtz%SX23-y`Wyu zJetDXoGpcd_oB0r7={@>Ax14S&~xx3cMNnf`JTsc7BM{(goVV?u1x2TH#r7BO6ITc z;w0h*#!-Vse|p7_Ro&9BDjtB=@2RF!_8(ITC7 z)E;6V>aOPEz+YHKU}1@GJdT{ z|HSyEp1R+6i1+r?H!wcbQy=&acT$lq`5x8=>22TJb73^=2Yar)dx+lr1B5;sqmMG) ze335t(Qc*M>S;fsNe|Vlf3&m0Gh4&KiO@TLwA1Z(3-xC|+L@}Mu5-f9LRNDTg-66@3=;*H10{Y+l*aUmCsiXk_!<6(cJmn_Eq%&)(4cm5fMVVy0C=O>(xT zS@yKdMdo|}=>5|13^w{IZl&Mfd(pi=9@8r(sGJ)AL{;DJ_fw;O)jeMboSm3i*Xa1K zWg2!M&DzkrLFPGg7Hu!}^UQzk9Yy}MMfH9|2t%wC+SDD)SAb(gmM)s4>gxrQRl`92 zWgB`Ir&$5LXtL^KFO1Qf*V?0M(x#|9e{hPT=gH6yhuZirbVp5is!nm-^KwNVLsR~! zsYX)`Z&q%p8MRhYx-xMD+$xrutr~K0EU~W;Evu2=!hMp;#2VuWTI%vz-2qhDxE6dj z#Zw$N#f6GIHO%D(6`wo|%)#PXD+yyIG2B0vTBk^iW#bL!S#kq zvua};GVUgDeXYq1PYDv1TCetp>HL{0#a^$ps#FQObf${xi0caG<`7d$YE5mfHFbLK zZdd2(yqPN1zWtP?xH0$FO1oKlpw`YQj^!rD!AUp2YGWFDTQ}!V)EYQF=TE8QZgNkZ zN-nCCYl)7iXX*M{OE1-0Iz8jBstI-6I(YSzskn>Ytrd53{$#D8Qyj_p7a{58qJwsG z{za{U({uiJr2=kxe?OI;mv#E#Ue<4|HFc!c)ahA2uI7lT$4ZJ%mzqLWyw)5z$YSF4j~bfzYU>bRFA%VxRI zdm)XEc#ykkWYt=7?}}`5u7)))pIB+B*5)j{(h8hR*>rjxwzhX5(Zrd0^;DG@-o`$R zGq&Fb`nB0Ap;-h{XpTFntR$1Llp3+R7o~q4Lx7s{4n=xU$ zrF!ULl`6^K!(J0G33;zg*6X^Y?1jr>feji23_r54%!7$dmX`51Vv$whA4SR9&VS7+53IvwCLxjL(i zPM6i8I_Wg7ZZXBAl-l@ct^6&u#cXb_!)9HtN;U9rwwFb_*WotrIz+W~?K}`-rvpmq zru)DdcDC!)w6om~#JF}IsptoM38mfN!=_}OL5o}TnjcWQYR^yd3v}d5sZFq{< z)~~;t#i$)m>#|fu{rBT(tCp%GtqdMv0@!ng$OAq#FnDwBKsz!dg`Gz4W{B^H<(pyoOT-%SH)p87 zY$Vq}@+ebS0dV3Bk@t|onF;JSYCm2+z_-HA@dbapJjZtpz){54*{7G`s1f}dqS{6Z z`vGn_L*&;#NMVJam9z)a5862x<_{^IHmqmBdWx(_%-_^rk-H6VJ9uv*KiiR?x9yyM zzIy@o+2$gJ=Y;#VULNS?`w+PZHT$Gd-3Zk$?VNny+RG>T4xf?H*Zru9JiBg4Wg z@NwVC1p|Cn_;PadPcwPcuQuI0uzb;!z*(nVpG9FT+y~IP%V$b(ssO7D>ou?*K9hBe zVeJ6xd0&oi$ATQ+i-;dykki8VhVS%Bf53>|3(+e<%sKfT+PBZk8E;k1 zD=w=lvp8dD&6+-8(8Nn7PS;0Ps^9e3UsbIBZIzmrz>d35UH=9(I3{&!WK5%vilSWV6dDT(%RFFMOJmycf8$Sjo2vmIL zjPrNSIR8q{`J{=ra;%?DJI}MiO@H9_>G7Baw#n2 zQUWiaI6U1@%yM{iCX)&mtUF>itsoFWCXQ5iE_sMUl|wP~j3LrO>7n@lw+z$wau;go z&+wlmfboM`JA5mT>xCZ#7A&r}fmR z9}?UGiM=A2kLmXWpN)9G;6sRy2!FPe~2Bs@X;2L4%gYmR5nt}0x(6d2j z7s14K1DgRSDEEL2-?m8S;Cw2o)t|2CZBV(%U|$aQ43hAk%$|s|nPBhUppwYu1*{hK zbl}%q_EBN8`u-{Gg}`REAYG?zR9#tNU6GGFkfuKzJcNgdL1*E15hI0W$lG&5r_6gI z&l}|%k>R}U_d-tu-4A{0t_MAi7&iMM#_&Sz;9IlS!eC+@h@rq6ftjMh_xk6JDlG`= zr=VUb)LVhq5c38`*d%mX;Xx}L6x=3sCi#TW(`*z==oyIL6M9e3KP86EUa|3h|#+-qzRpgwkA%;05cTwggy`S?t+8QKbkyNDy19W2`rIy3Y%G2}0RycyAl3@_Llq&GL>*55>C zK4cCMLxvZRGeCZMcEpF{zGh;-fRP*yERW7?V$hjbJ7Ofp*3(U7nAlLEzYF>WLT3WA zi6w!h#FMp|^u36Yx#hqEMTTM} zLZ?i*(3!v@Vkx~t3_BA+zlFFS0K)x3hi=e%j3-E7KMXx34E74o6Vnkv*n3LB`c&lE zV;m3}X6O)cbNA8wi^#NqOgLIS?J#qVi6wJ+#PBQ3EN@4i$k2Bm!*EMKj2M|43qu!( z3=^9v^dRW-gw6z36H5ZO5u^0af_^`7JOIK@p|j>*4DtjCJO)Fr3u7@F)IMSvbN?zt z;kd|u0QsLqh8c>&Ek-;40zIA>33GO@F|mx-w-GuM?kMs>wr(@U10fn3DvXh!pDzmA zp)i)1odklJ|3LyYG*9GLLH=@)p9`7WM4mS9ByR2w@;)l^99G;N!RI=RawO{`GNkIh z7NM^obc7f(^v~+z9wugI(t=pzμK)*(a1#Jm;=BZSV3jwMD7MWSt(i7DXLb#VRX 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

2T8Gr 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�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 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 030/125] 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); +} From 34d0e50135f9e0baff349fe116f92d049e9f97c9 Mon Sep 17 00:00:00 2001 From: Sidney Leung Date: Tue, 20 Dec 2016 13:11:21 -0800 Subject: [PATCH 031/125] I2S example sketches failed compliation, Jira 786. The latest BLE library consumes more heap space that causes these sketches ran out of heap space for their temporary buffers. For now, the size of the tempporary is reduced. For the near future, there are 3 improvements to address this issue. The BLE library will be making use of the DCCM memory instead of heap. The I2S library buffering space is going to the DCCM memory too. The heap space will be increased to maximize the usage of the entire SRAM sapce. --- .../examples/I2SDMA_RXCallBack/I2SDMA_RXCallBack.ino | 4 ++-- .../examples/I2SDMA_TXCallBack/I2SDMA_TXCallBack.ino | 2 +- .../CurieI2S/examples/I2S_RxCallback/I2S_RxCallback.ino | 6 ++++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/libraries/CurieI2S/examples/I2SDMA_RXCallBack/I2SDMA_RXCallBack.ino b/libraries/CurieI2S/examples/I2SDMA_RXCallBack/I2SDMA_RXCallBack.ino index ed099fa1..188800a1 100644 --- a/libraries/CurieI2S/examples/I2SDMA_RXCallBack/I2SDMA_RXCallBack.ino +++ b/libraries/CurieI2S/examples/I2SDMA_RXCallBack/I2SDMA_RXCallBack.ino @@ -22,8 +22,8 @@ **/ #include -#define BUFF_SIZE 128 -#define OFFSET 2 +const int BUFF_SIZE=64; +const int OFFSET=2; uint32_t dataBuff[BUFF_SIZE+OFFSET]; // extra 2 buffers are for the padding zero /** diff --git a/libraries/CurieI2S/examples/I2SDMA_TXCallBack/I2SDMA_TXCallBack.ino b/libraries/CurieI2S/examples/I2SDMA_TXCallBack/I2SDMA_TXCallBack.ino index 9ce55501..8cfc478e 100644 --- a/libraries/CurieI2S/examples/I2SDMA_TXCallBack/I2SDMA_TXCallBack.ino +++ b/libraries/CurieI2S/examples/I2SDMA_TXCallBack/I2SDMA_TXCallBack.ino @@ -9,7 +9,7 @@ #include -#define BUFF_SIZE 128 +const int BUFF_SIZE=64; boolean blinkState = true; // state of the LED uint32_t dataBuff[BUFF_SIZE]; uint32_t loop_count = 0; diff --git a/libraries/CurieI2S/examples/I2S_RxCallback/I2S_RxCallback.ino b/libraries/CurieI2S/examples/I2S_RxCallback/I2S_RxCallback.ino index 9550ad1f..06b4db8e 100644 --- a/libraries/CurieI2S/examples/I2S_RxCallback/I2S_RxCallback.ino +++ b/libraries/CurieI2S/examples/I2S_RxCallback/I2S_RxCallback.ino @@ -17,7 +17,9 @@ **/ #include -uint32_t dataBuff[256]; +const int BUFF_SIZE=128; + +uint32_t dataBuff[BUFF_SIZE]; volatile int count = 0; void setup() @@ -54,6 +56,6 @@ void rxDataReceived() while(CurieI2S.available()) { dataBuff[count++] = CurieI2S.requestdword(); - count %= 256; //prevent buffer overflow and just write data in front of the buffer. + count %= BUFF_SIZE; //prevent buffer overflow and just write data in front of the buffer. } } From 2391f48210995aa34fc20e62745811aeaaa0bd7d Mon Sep 17 00:00:00 2001 From: Sidney Leung Date: Wed, 21 Dec 2016 00:48:55 -0800 Subject: [PATCH 032/125] Moving balloc static memory buffer to DCCM memory balloc() is used by interrupt context code for memory allocation. The memory space is statically allocated at initialization. In order not to take up SRAM memory, this buffer is moved to the DCCM memory space. --- cores/arduino/dccm/dccm_alloc.c | 9 +++++++++ cores/arduino/dccm/dccm_alloc.h | 4 +++- system/libarc32_arduino101/Makefile | 2 +- system/libarc32_arduino101/bootcode/c_init.c | 3 +++ .../framework/include/os/os.h | 9 +++++++++ .../framework/src/os/balloc.c | 16 +++++++++++----- variants/arduino_101/libarc32drv_arduino101.a | Bin 799666 -> 797726 bytes 7 files changed, 36 insertions(+), 7 deletions(-) diff --git a/cores/arduino/dccm/dccm_alloc.c b/cores/arduino/dccm/dccm_alloc.c index d679c8b3..b34dd020 100644 --- a/cores/arduino/dccm/dccm_alloc.c +++ b/cores/arduino/dccm/dccm_alloc.c @@ -37,6 +37,15 @@ void* dccm_malloc(uint16_t size) return addr; } +void *dccm_memalign(uint16_t size) +{ + if ((dccm_index +3) > DCCM_SIZE) + return 0; + + dccm_index = (dccm_index + 3) & ~((uint16_t)0x3); /* 4 byte addr alignment */ + return dccm_malloc(size); +} + #ifdef __cplusplus } #endif diff --git a/cores/arduino/dccm/dccm_alloc.h b/cores/arduino/dccm/dccm_alloc.h index bddc21d3..a25ca9c7 100644 --- a/cores/arduino/dccm/dccm_alloc.h +++ b/cores/arduino/dccm/dccm_alloc.h @@ -31,9 +31,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA void* dccm_malloc(uint16_t size); +void *dccm_memalign(uint16_t size); + #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/system/libarc32_arduino101/Makefile b/system/libarc32_arduino101/Makefile index 2e947bc9..a9ef7d07 100644 --- a/system/libarc32_arduino101/Makefile +++ b/system/libarc32_arduino101/Makefile @@ -39,7 +39,7 @@ CFGFLAGS+=-DCONFIG_BLUETOOTH_MAX_CONN=2 CFGFLAGS+=-DCONFIG_BT_GATT_BLE_MAX_SERVICES=10 CFGFLAGS+=-DCONFIG_BLUETOOTH_GATT_CLIENT CFGFLAGS+=-DCONFIG_BLUETOOTH_CENTRAL -DCONFIG_BLUETOOTH_PERIPHERAL -INCLUDES=-I. -Icommon -Idrivers -Ibootcode -Iframework/include -Iframework/include/services/ble -Iframework/src/services/ble_service +INCLUDES=-I. -Icommon -Idrivers -Ibootcode -Iframework/include -Iframework/include/services/ble -Iframework/src/services/ble_service -I../../cores/arduino/dccm #-Iframework/src/services/ble -Iframework/include/services/ble INCLUDES+= -Idrivers/rpc -Iframework/src EXTRA_CFLAGS=-D__CPU_ARC__ -DCLOCK_SPEED=32 -std=c99 -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 diff --git a/system/libarc32_arduino101/bootcode/c_init.c b/system/libarc32_arduino101/bootcode/c_init.c index 243622d6..a6256c45 100644 --- a/system/libarc32_arduino101/bootcode/c_init.c +++ b/system/libarc32_arduino101/bootcode/c_init.c @@ -21,6 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #include "interrupt.h" #include "arcv2_timer0.h" +#include "os/os.h" /* Application main() function prototype */ extern int main (void); @@ -60,6 +61,8 @@ static void _exec_ctors (void) interrupt_unit_device_init(); /* Start the system's virtual 64-bit Real Time Counter */ timer0_driver_init(); + /* Initialize the memory buffer for balloc() calls. */ + os_abstraction_init_malloc(); /* Jump to application main() */ main (); /* Never reached */ diff --git a/system/libarc32_arduino101/framework/include/os/os.h b/system/libarc32_arduino101/framework/include/os/os.h index 12b9a11a..7f5103d9 100644 --- a/system/libarc32_arduino101/framework/include/os/os.h +++ b/system/libarc32_arduino101/framework/include/os/os.h @@ -132,6 +132,15 @@ extern void queue_get_message (T_QUEUE queue, T_QUEUE_MESSAGE* message, int time */ extern void queue_send_message(T_QUEUE queue, T_QUEUE_MESSAGE message, OS_ERR_TYPE* err ); +/** + * \brief 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. + */ +extern void os_abstraction_init_malloc(void); + /** * \brief Reserves a block of memory * diff --git a/system/libarc32_arduino101/framework/src/os/balloc.c b/system/libarc32_arduino101/framework/src/os/balloc.c index 1122ec4a..aa2906b3 100644 --- a/system/libarc32_arduino101/framework/src/os/balloc.c +++ b/system/libarc32_arduino101/framework/src/os/balloc.c @@ -9,6 +9,7 @@ #include "os/os.h" #include "infra/log.h" +#include "dccm_alloc.h" #ifdef CONFIG_MEMORY_POOLS_BALLOC_TRACK_OWNER #include "misc/printk.h" @@ -59,7 +60,6 @@ typedef struct { #include "memory_pool_list.def" - /** Pool descriptor definition */ T_POOL_DESC mpool[] = { @@ -100,21 +100,19 @@ T_POOL_DESC mpool[] = /** 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.start */ 0, \ +/* T_POOL_DESC.end */ 0, \ /* T_POOL_DESC.count */ count, \ /* T_POOL_DESC.size */ size \ }, @@ -333,6 +331,14 @@ static void print_pool(int method, void *ctx) */ void os_abstraction_init_malloc(void) { + int indx; + uint32_t bufSize; + + for (indx=0; indx < NB_MEMORY_POOLS; indx++) { + bufSize = mpool[indx].count * mpool[indx].size; + mpool[indx].start = (uint32_t)dccm_memalign((uint16_t)bufSize); + mpool[indx].end = mpool[indx].start + bufSize; + } } /** diff --git a/variants/arduino_101/libarc32drv_arduino101.a b/variants/arduino_101/libarc32drv_arduino101.a index fc6fa9920598992207aad417d72d3fc9aaddd3e5..e739392b68fe6bcc004fd02643674f32668ec6b9 100644 GIT binary patch delta 89674 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

=7OK99fI}5=ZgqDZ}9nEu1i}M5-*dzme=uy>C=3 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 Date: Wed, 21 Dec 2016 17:10:09 -0800 Subject: [PATCH 033/125] Modified SensorTag example and added examples derived from 1.0.7 --- .../BatteryMonitor_Central.ino | 125 ++++++++++++++ .../sensortag_button/sensortag_button.ino | 82 +++++++-- .../BatteryMonitor_Notification.ino | 147 ++++++++++++++++ .../examples/peripheral/MIDIBLE/MIDIBLE.ino | 161 ++++++++++++++++++ 4 files changed, 504 insertions(+), 11 deletions(-) create mode 100644 libraries/CurieBLE/examples/central/BatteryMonitor_Central/BatteryMonitor_Central.ino create mode 100644 libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino create mode 100644 libraries/CurieBLE/examples/peripheral/MIDIBLE/MIDIBLE.ino diff --git a/libraries/CurieBLE/examples/central/BatteryMonitor_Central/BatteryMonitor_Central.ino b/libraries/CurieBLE/examples/central/BatteryMonitor_Central/BatteryMonitor_Central.ino new file mode 100644 index 00000000..89469631 --- /dev/null +++ b/libraries/CurieBLE/examples/central/BatteryMonitor_Central/BatteryMonitor_Central.ino @@ -0,0 +1,125 @@ +#include + +/* + This sketch example works with BatteryMonitor_Notification.ino + + BatteryMonitor_Notification 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 +*/ + +#define LED_PIN 13 + + +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.print("My Address is: "); + Serial.println(BLE.address()); + + BLE.scanForName("BatteryMonitorSketch"); + Serial.println("Scan Started"); +} + + +void loop() +{ + BLEDevice peripheral = BLE.available(); + + if (peripheral) + { + Serial.print("Found "); + Serial.print(peripheral.address()); + Serial.print(" '"); + Serial.print(peripheral.localName()); + Serial.print("' "); + Serial.print(peripheral.advertisedServiceUuid()); + Serial.println(); + if ( peripheral.localName() == "BatteryMonitorSketch") { + BLE.stopScan(); + processNotification(peripheral); + + delay (1000); + } + + // central connected to peripheral + + //delay (4000); + // BLE.scan();//("BatteryMonitorSketch"); + } +} + +void processNotification(BLEDevice peripheral) { + if (peripheral.connect()) { + Serial.print("Connected: "); + Serial.println(peripheral.address()); + // light pin to indicate connection + digitalWrite(LED_PIN, HIGH); + } 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(); + digitalWrite(LED_PIN, LOW); + return; + } + BLECharacteristic batteryLevelChar = peripheral.characteristic("2A19"); + if (!batteryLevelChar) { + peripheral.disconnect(); + Serial.println("Peripheral does not have battery level characteristic!"); + digitalWrite(LED_PIN, LOW); + delay(1000); + return; + } + + if (!batteryLevelChar.subscribe()) { + Serial.println("subscription failed!"); + peripheral.disconnect(); + return; + } else { + Serial.println("Subscribed"); + } + + while (peripheral.connected()) + { + if (batteryLevelChar.valueUpdated()) { + + printData(batteryLevelChar.value(), batteryLevelChar.valueLength()); + Serial.println("%"); + + } + + } + Serial.print("Disconnected"); + Serial.println(peripheral.address()); + digitalWrite(LED_PIN, LOW); +} + + +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); + } +} diff --git a/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino b/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino index aa47cbe8..7f3f1aa2 100644 --- a/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino +++ b/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino @@ -19,13 +19,33 @@ #include +const int NUM_OF_SERVICE = 10; + +char *serviceUUIDArray[NUM_OF_SERVICE] = + + // These are the various services that are included in the CC2650 Sensor Tag + // If you uncomment them you can see the various services +{ //"f000aa00-0451-4000-b000-000000000000", + // "f000aa20-0451-4000-b000-000000000000", + // "f000aa40-0451-4000-b000-000000000000", + // "f000aa70-0451-4000-b000-000000000000", + // "f000aa80-0451-4000-b000-000000000000", + // "f000aa64-0451-4000-b000-000000000000", + // "f000ac00-0451-4000-b000-000000000000", + // "f000ccc0-0451-4000-b000-000000000000", + // "f000ffc0-0451-4000-b000-000000000000", + "0000ffe0-0000-1000-8000-00805f9b34fb" +}; + void setup() { Serial.begin(9600); + while (!Serial); // initialize the BLE hardware BLE.begin(); Serial.println("BLE Central - SensorTag button"); + Serial.println("Make sure to turn on the device."); // start scanning for peripheral BLE.scan(); @@ -45,8 +65,26 @@ void loop() { Serial.print(peripheral.advertisedServiceUuid()); Serial.println(); - // see if peripheral is a SensorTag - if (peripheral.localName() == "SensorTag") { + /*see if peripheral is a SensorTag + The localName SensorTag is in the Scan Response data packet + In this release we do not have the feature that gets the scan response data and hence + the local name in the scan is blank + We have to explicitly find the BLE mac address + Please use another deviice like nrfConnect app to discover the Bluetooth Address + */ + //if (peripheral.localName() == "SensorTag") { + + + /****************************************************** + * ATTENTION: + * Change to the mac address according to your device! + * Use a central app that can display the BT MAC address + * ****************************************************** + */ + + if (peripheral.address() == "24:71:89:07:27:80") + + { // stop scanning BLE.stopScan(); @@ -58,7 +96,11 @@ void loop() { } } -void monitorSensorTagButtons(BLEDevice peripheral) { +void monitorSensorTagButtons(BLEDevice peripheral) +{ + static bool getAllServices = true; + static int serviceIndx = 0; + // connect to the peripheral Serial.println("Connecting ..."); if (peripheral.connect()) { @@ -68,18 +110,34 @@ void monitorSensorTagButtons(BLEDevice peripheral) { return; } - // discover peripheral attributes - Serial.println("Discovering attributes ..."); - if (peripheral.discoverAttributes()) { - Serial.println("Attributes discovered"); + if (getAllServices) { + // discover peripheral attributes + Serial.println("Discovering attributes ..."); + if (peripheral.discoverAttributes()) { + Serial.println("Attributes discovered"); + } else { + getAllServices = false; + Serial.println("Attribute discovery failed."); + peripheral.disconnect(); + return; + } } else { - Serial.println("Attribute discovery failed!"); - peripheral.disconnect(); - return; + int tmp = serviceIndx; + Serial.print("Discovering Service: "); + Serial.println(serviceUUIDArray[tmp]); + if (++serviceIndx >= NUM_OF_SERVICE) + serviceIndx = 0; + if (peripheral.discoverAttributesByService(serviceUUIDArray[tmp]) == false) { + Serial.println("Can't find the Service."); + peripheral.disconnect(); + return; + } else { + Serial.println("Service discovered."); + } } // retrieve the simple key characteristic - BLECharacteristic simpleKeyCharacteristic = peripheral.characteristic("ffe1"); + BLECharacteristic simpleKeyCharacteristic = peripheral.characteristic("0000ffe1-0000-1000-8000-00805f9b34fb"); // subscribe to the simple key characteristic Serial.println("Subscribing to simple key characteristic ..."); @@ -97,6 +155,7 @@ void monitorSensorTagButtons(BLEDevice peripheral) { return; } else { Serial.println("Subscribed"); + Serial.println("Press the right and left buttons on your Sensor Tag."); } while (peripheral.connected()) { @@ -118,4 +177,5 @@ void monitorSensorTagButtons(BLEDevice peripheral) { } } } + } diff --git a/libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino b/libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino new file mode 100644 index 00000000..2325f351 --- /dev/null +++ b/libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino @@ -0,0 +1,147 @@ +/* + Copyright (c) 2016 Intel Corporation. All rights reserved. + See the bottom of this file for the license terms. +*/ + +#include + +/* + This sketch can work with BatteryMonitor_Central. + + 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 +*/ + + + +BLEService batteryService("180F"); // BLE Battery Service + +// BLE Battery Level Characteristic" +BLEUnsignedCharCharacteristic batteryLevelChar("2A19", BLERead | BLENotify); // standard 16-bit characteristic UUID defined in the URL above + // 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() { + BLE.begin(); + 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 + If you want to make this work with the BatteryMonitor_Central sketch, do not modufy the name. + */ + BLE.setLocalName("BatteryMonitorSketch"); + BLE.setAdvertisedServiceUuid(batteryService.uuid()); // add the service UUID + BLE.addService(batteryService); // Add the BLE Battery service + batteryService.addCharacteristic(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 + */ + + BLE.advertise(); + Serial.println("Bluetooth device active, waiting for connections..."); +} + +void loop() { + // listen for BLE peripherals to connect: + BLEDevice central = BLE.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.writeUnsignedChar(batteryLevel); // and update the battery level characteristic + oldBatteryLevel = batteryLevel; // save the level for next comparison + } +} + +void updateIntervalParams(BLEDevice central) { + // 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/peripheral/MIDIBLE/MIDIBLE.ino b/libraries/CurieBLE/examples/peripheral/MIDIBLE/MIDIBLE.ino new file mode 100644 index 00000000..13d12eea --- /dev/null +++ b/libraries/CurieBLE/examples/peripheral/MIDIBLE/MIDIBLE.ino @@ -0,0 +1,161 @@ +/* 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 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. + + 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(); + // advertise the service + + Serial.println(("Bluetooth device active, waiting for connections...")); +} + +void BLESetup() +{ + BLE.begin(); + // set the local name peripheral advertises + BLE.setLocalName("Auxren"); + + // set the UUID for the service this peripheral advertises + BLE.setAdvertisedServiceUuid(midiSvc.uuid()); + + // add service and characteristic + + midiSvc.addCharacteristic(midiChar); + BLE.addService(midiSvc); + + // assign event handlers for connected, disconnected to peripheral + BLE.setEventHandler(BLEConnected, midiDeviceConnectHandler); + BLE.setEventHandler(BLEDisconnected, midiDeviceDisconnectHandler); + + // assign event handlers for characteristic + midiChar.setEventHandler(BLEWritten, midiCharacteristicWritten); + // set an initial value for the characteristic + midiChar.setValue(midiData, 5); + + +} + +void loop() { + + /*Simple randome note player to test MIDI output + Plays random note every 400ms + */ + BLE.poll(); + 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 midiDeviceConnectHandler(BLEDevice central) { + // central connected event handler + Serial.print("Connected event, central: "); + Serial.println(central.address()); +} + +void midiDeviceDisconnectHandler(BLEDevice central) { + // central disconnected event handler + Serial.print("Disconnected event, central: "); + Serial.println(central.address()); +} + +void midiCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) { + // central wrote new value to characteristic, update LED + Serial.print("Characteristic event, written: "); +} From 4ae5af7f8a63dfaa9d44b35c0c820d93a4a6980e Mon Sep 17 00:00:00 2001 From: Noel Paz Date: Thu, 22 Dec 2016 13:24:24 -0800 Subject: [PATCH 034/125] Corrected the MIDIBLE example so it advertises correctly --- libraries/CurieBLE/examples/peripheral/MIDIBLE/MIDIBLE.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/CurieBLE/examples/peripheral/MIDIBLE/MIDIBLE.ino b/libraries/CurieBLE/examples/peripheral/MIDIBLE/MIDIBLE.ino index 13d12eea..1ae7ebc8 100644 --- a/libraries/CurieBLE/examples/peripheral/MIDIBLE/MIDIBLE.ino +++ b/libraries/CurieBLE/examples/peripheral/MIDIBLE/MIDIBLE.ino @@ -96,7 +96,7 @@ void setup() { BLESetup(); // advertise the service - + BLE.advertise(); Serial.println(("Bluetooth device active, waiting for connections...")); } From 5d91dc66fe0f49ab5cb9d5dc7459a9abba5ea7de Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Thu, 29 Dec 2016 17:57:17 -0800 Subject: [PATCH 035/125] libarc32_arduino101/Makefile: Add 'strip' target The file libarc32drv_arduino101.a is built with debugging symbols by default, however the version we ship needs to be stripped. The new "strip" target makes it easy for us to strip when committing, and the default target still behaves the same way, so the library can be re-compiled for debugging purposes --- system/libarc32_arduino101/Makefile | 4 ++++ variants/arduino_101/libarc32drv_arduino101.a | Bin 797726 -> 190802 bytes 2 files changed, 4 insertions(+) diff --git a/system/libarc32_arduino101/Makefile b/system/libarc32_arduino101/Makefile index a9ef7d07..bccd2ab3 100644 --- a/system/libarc32_arduino101/Makefile +++ b/system/libarc32_arduino101/Makefile @@ -17,6 +17,7 @@ C_SRC+=$(wildcard $(PWD)/framework/src/os/*.c) ARCH=ARC CC=arc-elf32-gcc AS=arc-elf32-as +STRIP=arc-elf32-strip RM=rm -f INSTALL=install @@ -56,6 +57,9 @@ $(TARGET_LIB): $(C_OBJ) $(ASM_OBJ) @echo "Link $@" @$(AR) rcs $@ $^ +strip: $(TARGET_LIB) + @$(STRIP) --strip-unneeded $^ + %.o: %.S @echo "Assembling $<" $(CC) -c $(CFLAGS) $< -o $@ diff --git a/variants/arduino_101/libarc32drv_arduino101.a b/variants/arduino_101/libarc32drv_arduino101.a index e739392b68fe6bcc004fd02643674f32668ec6b9..af098ec4401f2ef6f736eea7d93191a21cdd229d 100644 GIT binary patch delta 49343 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 Klzz?tk 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

    0v`NG^!@ZSy$l}m$ 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%0D9%%AEONBG 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 vAme>BDLi_|HxqhV{QD}8*`&NUP!T_QgcBq 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
    5O)#^$Xh8V-wcRmEQRs!Tp12@C^ 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 z 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
    +-RabWQwMR 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%0D#l!un^rebIviza;Ph~JLRL10WAD9zmiDf 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 }LBUVFB02*2`gNOnv!i2iIaOhx zE&Yf#xA&lR8GTsOS09NJ0Fg2UeBI4@8`Uqea_1)_c9u>;y)Q|K_XRexT?BU^>0A$x zAF)>(a>ShyW`?b}Z`Znc!^0jyOyiLGpj^zrfzEdC0TnTKi1gG@0C7N$zwg1Sc>Rf5 zN(xm^H4TNp(`X%yd(l##&Tz1|0lZs{!jtq!@Ze)NS`0j76xAIBFx3DQWGFZ(H$a-> zE_?N%-g)qT+%ugxyyV0Q!~RGn~!za>;l>UOs6UW z4glTS0Y!kSAui~`@4~gYC)3|YLk_-z(!@^DH=)<&e8Tz<-G-89FmuSNLD>_y>*WI7 zM*KS=-MUQ2zlQ^N7J<(=TIvo0rfLWvBW65+0`mC6LuDw4l1?~|m5&OVj-27Il^Lb< zgM$jwc`9Aw7&!G6+t_rd3Aph{$!0(d=^JUY!ana3O?;=92CTsblZGG^qBR~UZUeh* zqE7&}b0s;;L4?ebXbv7DqPwHjkH#P6rTLiASh3innnhd}Xsg)(b2%SA1AdDmf`<;A zJLQjlit>rTqi(q-`TM=Fh*uua#sx(i`YIj?9un$fOas%?XWx$~Dj|Xt^lIb!Nb_Oz zJ?Xep^Oa24u4dIyg>~4NNtV=YFo0sAkX1e#Xa)$i-#7R^9R&G!04vwVT+bBC?LXzY z8Fq`j4^+*d8L`r5&O<9NxtLKnRh8GyQjV=usErviHm%B}N>|u6a0uq6ERLEssUZ$V z1GB@kl!kIpjH8TR&3GGDEL}|6&uwHv*Z|&|%7hG=Rj`f6xCuOK!vRuU6j+rxw6%4_ zU}n`Qy{x4>edKvIwuric0|s(K_BG9$uX1xm5z%QhkByzB88`-mLxn*In!e2Q^j=U% zzBJ>)7*@Z^^USL2V+ncQ01}oq5tgg#svj|fZwz!zYon#)0~=|SS_mcsxuHfC^?l8Y zTj+bC1xA+;sX+sR-X8)6p`Y?+MAVAZwlH4?jp&9RFCh&rA6 zG--w|iawfy;Nd=+gQV`zxZk~P*8Y;;Et*kTh?g0{GT@M1Y`IBjBha?i<-Uwb5U7|{ zPF*<0bl4DD1IFeqjcIDjoV@Su#3&ImBlV}}3eIp3mP-q=jYo70e2_JGPL)psOw38j zY=N18h%N+c;t0uf;l^L>J z#E6=KHdZ_~jP5j_I75SFoEQ&KcD=_CN3A;e&OG{*26k(Ex2HX_W% zrnZ?Ml|RO5C&t2zo3_%i_%0h$YMaKHrA3R;`ZSc_pk5|52m-2U2?i9Nk%l{Mh7Dgc z^+Dr`FI^w)CPpJ0GK}Hba9-GW!Ie!jG7V9Pea3-kLWoW>wlxHuUr7Xu$AmnWpnxPA zuH4x&G|DEI-r$d$LfU|ro)O@FnB0FdU9uH3!v{CwY{^Dut(`W0^R*W=qX0%6(K{;u zm6kL?*^9QXHRnqM5?%rpO-8uNw4?|TB(}$hL=?>2Ic(aa%V}`pd>U>y_9OZQ8Ccd(3-kOJVs8Z0QaTUJeuih{t=>T7B8JzW4<$7j27dxvOoRr>&fD25sCf-4nuV zAdQjZ_^Pn&*rP3(@K%A!gm-5VV3#@23)e4Q z78wr@nr|i8RZ>~>O`N$tMwf$k!ZkwFgpQ%t7DtFJ&Aq7ng|ZY&D9PR^K$DcFiuBb8 zyM99h8iogyX>43%M3WOOPes`&7qr}~2Z^_n5-3>MfN8=$0(wcY+jXb`G}(|&bGXdQ zYTu!jA zeIk*2Yg*bGC>y`BC9^eSg2lvz3G!O|lG3+8vs_!_#aQ+qH9>eo^>$;saow>+Ljo$# zhLQ1BHjiGudI`!_vcL`bFPV$gGg>MX--}k;sWMPTU19$&^!UbU;9BfR6hyDTsLE*GsWxwtP%YlkBG0#z@{f zXk3rJXhktw#ISFpKXMfmM6$>!aE-njB+L_@gTD1Hesb|AqP;IVI@fExyS?1KB2T%~ zhe2E5XkXQLYp2NEKdjrd7s&MtzC?YND?a?u#`A70rkd*%uK^nUuP@AP!fTzpXVrE! zI3daz9#i_`to#g;M9U{0mB9l1BIaykd5}EvN_pPtV@1U#*3D91LV8a>31{TLJ!efQ zp(<`u_@P>QCn%z2t3!*k>dwFYmSy#L3-mG%s9x?n0>i{k+(<-`SYJ12P^jxhKfyR3 zle=K@x{idFx|#GcwBeq9LBJHK=L|VAW)SP+Q6273$GESjQP6KrE8PKrSvVR951~Vj zfxLLyH1BOFARTA~P$PKxn7$<=h~MAbQ`K-lNc}Jmh*VTuQBfd6@qoH@@)TW?^C*s{ zb;IAFsi)=7dN&B8oiWBDSHubZh%*ul%om+O1_vlup?opmdg>`Sy5uS8(BY>Z>9;B? zeDIW905| z>zxN-B6riZm0`xvkZ>k&9d-;HL`%kaB~6qO)qy(b0^~;EVi>TwyGLY9kbsz1Esqi2 zL2mgM+((R3p0}|R1D%=@?~!BHp<~dDtNy0zjZml`@=+_uVcr1kG4b7!BP#M9XbYi?1@S87nHpG46VDz#jj{QesY^|&84@dG<%r|GcO|%U4{Qe= zgMGD9fM=HUJ|Ut-oPVQy28C5n)Hb71uKs= zBV!EI?5)HCjt2=2Fxo%l;){!z2kM)y9-4El7*5ujBgjh|*%zO=5D`@4{0OK;y&eqHk)|3yf3ZuErNBll{VY)}=UM#UbLW;)~HZ|ANv|)|L5@(b?x(t>S zefk*Rm%Es|&D)RX2iC~QmETtCN7sm{$zhFC77Q*-UUoSq^j+$aE}wtLT+1VaNaI6!g9~2DVN= zbo-WJU_JcVl>V^YA(l3+#SJz=tm`>(xx}q|;AML$)4_zbZFAi(5YQZUe+@vR4@Ukx zPHSC<2w&*w$VVtk;5yn?z|{7KS;qfnq#M>C7ITfIoMEk97nLe@X8qczt+vHjjmO#X zGQWd1)WWfuxf=T0HP3AS`?y2bKjg@+`kkb)yn z>>~wn$kotqAD;Jt#lGqGgPXmPN|6Azxw}YRr>2|ByE$xC zaDw-{j0X))e#m|+v}XcH5hm6VKYo@Li8d!j5{vMA4zO-+m1z_ppK%ML`_Locz-D-} z`fJ6{)Bg;76nMGv7ngyD!y z?+d4su8&EMW1aLv#rzVGW+>ju;EiMN=c*pd8ai-ns(ffZ06Ch+5s5K;bw*`C99!y0 zjBR|Q=qIqyF?0B9dl-E--7BJktp=%xtp+W&8FbGU5Ui!t(mjy>@F## zeAwRsk5f45*Zpl1K?p(M?j67P@qObWrTX{*HORBvD1a~e`%`8+5h4F6oa-Up5PJZ6 zevu~Fn`g@j(oyW`l!bvwN+(6)+}aBPR02glGG@?I8x(jtwlOe;@xU z{0N^QgrcV2vXM@{DCq?3BY#JlO43)KDe+?WAsfJ!hi4f%VTGMO{P-66vflUf&pMGq zVzJN(k>7n$*A5 ztn=n7-%=ozEauK>hM*>chFkkh{ZLeLD(V2C3RL3~^iHjrK#-EsCJ91rq22y#lPqu| zQ}(r=eImr~Au|Mra;)$Pq^h!~#w4hAq8aotCc*AH1PDO1zsQ5iX2*~yG87kA;_@q% zMUg9=NGik_LwStCH<+Md+LR~+X(;2!A%%O0se&a1s=O+#+YsMHeJqfsR3k^}2vKkt z=G=Jv5G5E3B$m{Yk^}Mv<5+>vR!A)mawOSEkRU!x^NN{)FsMqar9r7EI2Xu$boc_6 zmO4nC2tkz+I4T)y*}JvWg-Is|p;(nQ3&D<_gjko@(XW7)#Cj z1{Nv18x#gy6B!F>E$1p#a2ZDnl#vPQ1FpvX6ChIp6p`PX3-+~h;jEU-M{BLq8kSzQ z_3nAOHO+x91!cgeLj2*AHI^6Y3e%VV>oQL#7r|l71%=q9ensDFv7c+;vFHMP6RKNa zv^Dj%O)Ke<2CxAPi`7t~j)c!33c;G~L{K4-^}K0C z-lH~GNX}+_W~0CfyEI+IN$30U-y>z9J2ZBA6h->j3Z+c3io)thi=*ySfSM)RKd{}& zoeM65#J390nq>FovbiJ1JiKpk@3%y`u>x#MbbpEcbXU%S7m6j2``yT^lHPR}nSG+L zal9-aXC;UWgIJNvgEJ_vV zw~GF+roWJsTG1>vXdz?5iSS_67s_iEw#NZF^LMlPl7@RL##Sg#`}IfcHm4#zi)Z6k#OiD8H_1K7 zLRg@!<=INBW9(FaI&RbW3cA?+#XfYr9t9s`#;HKOW0l=D@79#kU)8CVx>{qMez9(R z>tvNixSb*yzRg>FAK5d=>w)9Gd#&Hr$=y%0{Z*y4YGRk&M>U`rtFN9%C+{>X^_Vz0 zs6&q7h-SH3A)7Y8`NA}Uy@B#x2+nhR?^mN(4xq(^afVYZ_Kp`qYXP>TUX?I$~3`eyLW+e1@xBa#2@#tAY)k^N<1F^Uk#W-o zxtmgPjK5APSA8P3Fm5Ns)chL)T-XZpq5kZEYbm;gF5VX z3@)9HSgndN2$*%{=$v@xK(t#dIygUW6d5x#2?puuG*Rh02GDe9*u+-48FW^@5tKS& zbXZ=aQq7>5@kK1c?L95bn?xM!NR2a&P!kYf{EuY*wpVd&@8a|vU+hs{;S{X^i!O7|R2YB1c0IGy;Pv^B+WP=9+wA804Wg_ zK2*MhK+)+JT`vI##V}s*QcF03r8|^T=oOQiZwMfKVl4rgIQ-0DP++1CT_eI6s3L&T zP$yg*m*GUS*~f%1GMHTC0uulM=<5=a$g257-||&x9};8?8Ap=DB1zvxCX#5~bVEFc zsC>tLiO7j7*d(Q)>MxxUQ+bb`2?z|Qd zA}?#jUr}Sf$QxJYF9eIVAK%7H^qb7H2=Qae1aMJeg&6Xv>l&qo^}w=zI231kuj29s z=9}uCIQYLw{`nRB1sj7c3jVr9qhXzv>0zGO7{uG(zBo%_Rd|V|y{Av)bK1^i<)7zE zxWc`2>3Qfo9$1zAz;82Eicbyol(H*Yby=%GgGHESOGlZ;2l%~rzIF?t1+g>socmR- zjZm{ztCg$uS_}JI-*2^UIS~#J3h(Kf4Ut2SL&r>se(Ps`H_J+!lKUS!JQ#++Uy z7PnSXV^p#FDJq|o-Cfd~Gbc{OQ^I~#eRP8+eWDL>#%@ol3|dW3msP71>0Fs%wn#P6 zqBN*gN}EQlO#E{N-1z+GIqU^h>P&iT{+Tu>%?Xsp^pmY+$#*?BU-g%Wk|-zW`FtQ5 zzg2=ztCQw}S$aJ#fz6k4=0egcP4bu3d@-3rK^0nkI#nLGT~*@U?eL_tq;d|X!+_EK z!lX_|kMd8P(CX4ZwR2gODz$o93Yni_dVhAHX*P4J({I(ryRUvOcjBDoHqhC`m*|nK z?m2%E@{6|d{wZOYf47QVv?TVI#*CD5!rIcy0d{WZQFVOp*o05Zb=w`Swp>BcoV>^= zT)K&`uQ1uobH=80`cLk2WT}3kO;gdSc-x!F-j0YD<=a)MN*Z*`2DBZp28DJ4PN=;c zf@}j@zz;zBXPqJ>eF-2iH~c3b%DP^Dw?o<7>$AELV+RBhw9OGHbVqEk$n#wCaKrCs#i+tZ7fAY+SJkBl2zU9k* zG0lxoa$QcK{gVAR=7y<@^TLb#IOXd?zB6`l*DTzu!{?3CqtrNwPdMVQXDSy7l8qaB z!~Cd?bIFw)WYIg;?_$U(adN~EDRqS@a&|N>QQ$=Jq_u8@Qw zL?%7aksS0x|H0A6c;&M(@kEG*I^quqI!|Zw5R=B}@I^<23-##d=d4i#7QBF6Fv;Ll(beuuP&p3i$M@bDR`OO~z;3puw6kxvkpbHN7abAnr2b!*#$uTslzM`sP}n zNDFXsCr%1l8~HU;OKH{BrARL&9V>Wv0z~l^^4sz%9q9~bpo2}Aa)N#s{Fx3qgYr^| zxBIb7;V~trO#@cOI(iMPW1T_OUax5l5@DCIBivx1u&~^pfx$pyxjq91gl-_9SReM@ z%Whx8GGf$PT=nJb*Uf$kXGoI4P=}xNLA#>&o+vheZ-z5UAkQO!^r)wykGvpRDK5v! z3$u;KK4psgcYgd3naCL|(i-RaHR$K0fZ|OQEQvcxD*@+*hq?oUqd9ZXZu)@;NgpFY zsIZ^x&eG|5E`5fdaDhRbK&f(8kCX6*nV03$AtUaOhg(5VlhfWDvBTGOYX%Zh#g3aH;XN}3S> zfE4ztLgf!Qj}%ZP{P)U6|GtHxM!`Vpf!%?DQ~LOVAQHs1%1i`zSL`{WCR+bxa#zSA zD;iE}Cq$!6pX|;a9mR@SUPS@8zzUzy zWA*}BS!kN}q5iv zA)?BC?o@UxRJ77PE1a4Ys%&au&a=SMyD>8WdtGrJ@fw?!YLHPDhaEqmhXjJJV!S_N zPMJ3~j!0XDzWPGLpeiNhv*_l42IriGr-12Kfo$J6JV$9k>NveqlBlURW%GGf;(K~Z=2ef&lBx%-NgU#WEP3BNBn8wW&h|{oE?lzR}yn{4fOvbrtg2y1i8Ghi5(L0;Q7PIxmz|f3c z$bS|DIteINY%YA`4IxdPHH_u`Ip(PyHflFB(}xa&nv7K1nGQ`flj#sQIM&GXZR~7k zg;NFu8W_}Ywiyn2;7$8QY+;7SYeS5SJq+x@*udl_)weuehsw1v^%+EpsF1Oq4)U`& zf%A+oM}k zNu%gCJF2ExG#6kD0b&{!l-@K$8Y!wVWVzezz%&F~auSDg$Wjs(# z^l0Fq1cFwIh^HC-#crSzvKC3!8dF>Nw``=TZLlyHIg=(TfPmGmKU_1$*4my{{q&i4h}L;D9LC#MuD1=;ezM1#+6JGaR({tOugJM?kgO6kN=7tfW~A zq~H`iH>el`x!Yjr2HE(mZjcE}z#t&FeN^c1DFTfcFFpkea=zmh!V>>d1qc+aN0H%) zOG;c<;46NY70_rxg)8yxiCi~w? zuj?QJwY7D10K*5Tt@DxV#CXo@1mdji=DcnP$Bw0ywx|vtQBEj@ys-w~Lmd!eR2K3V zDM4P{gPqnEZKOYSQYhHneL6;kpb)sfPJzIH??Xwwd%^^XTD4mb-PcXn9S-#ydb z{q6~VHYVpX7wuzhxTS}WLAO&{=HYZ$k3LYIdmA9@a^K!>AL?U@DmFC?67W3L9UItT zTC(U@nZkd%9(oM6{ZK!s_UthPu{{34<0I0i#D9is4TFo?A%TOh9(Eq-F55WxRebTF zJ@yzg$YZ=u#%W;3U_*!C*}BJEkJwNhJh0{PG;}<~jW`VjX2@xqxj$GZgHk(FIW@yq zGbD((uY7b({#X@mVH3+?f73Deo69>(E*;c7vXWJ z(zxE^+dQ@`3Fi#^zKntAG`oa&jJ1Z+3bDrI+8C96*f;PfZU8_082rZ8Wobsx52(H! zOWKb&(pH~b@yztxi!+!+^JA~UGdAR_RKvORXLCT7!*hjWm~(}*ulj;xI+Iw3N?Pq1 z*Zhz%$cOqpbdAagc4rYOU>A5^&kX5}Vwp>TD{oe}J9EikS%Y`VIiWNPUHqXFEh=W& z5CC|5CUgRdq+yZr1rOw_qV2+6hZPelsjc|dA_H+*I+BbwhE58x4Ls&f)H0TnxI6-- zuGW90$$HCd3Od+WN z^2f&?gLxgFTj6!Ki^F4q=cDnOc_--aO~lV1WMPpXg9cB$KG+*Ot`cYSbt(niZ9t4M`*ji6BWxBEu&dv%uFjJJAxmM*<#0Y#&S5-#l%;V}Y>rbRw8Aw5=E< z!jhv_+nJ>4+{=g&YxE?E5DK`YWqJq**HYuL%kp6Cu0*N!yTf^lix+V#&)Ii4zqVJ^ zRhDl!Um;i7y=1vpUJSQqfQuhgLCRz^{dYar|5?p&{oIih005DY0RqqpsNVp;q1`~z zy-EDXE57Mc$q0SKEzDikjlNxpY&azx(XbqVz@XiOyb6z$@DA~h2=Q3Jl@B6-2mm68 z@YaJ5ApbFEF%B|8SkIVuvdSkBC(%ICAZf@nXf#YXQ0f$hiov3O?!JyUNS65=0Qh>- z{m0h_?_*%&#dd3&C^seF9-bneJgJ~=l-tU9^Y4%|E)N-n3+clKZ@)dIC>>5At#2)( z0e8oPJ40OvxJ@N{xg;GMGJlwTcv6+HofFsZ!1DTIwC?H7^eJn|jRYqjlH{hIc0YMe zz_1f<@VESaLXz0nx9r_s*iR=V)>DDPgC3B6yMCOUN}!b_2GozACkMaoA=~!A@c6L= z@hKC@8Rw_R2|2!PjD!3?K~Et5H6QR0koBkT2^7Dee>b508lNbjF#BukM;#Pte}zu< z69x*rzXc~$1ft;iYc&A(_lotuE}byz=PLb3ekY`c=030Qe5%e{BZn{(dk!I!>2-+2 zNNqYDg5RkK_8T_258!2X?(Y0u+Ltc`yokrLXvzm)yp-)V*7=40O?LIe;AzilX|k~> zP^5d=P?1sv?DF=`(7hh2!C2>hstcRaD8i_AX;wTTGx5J(H|h`gW@yz=s8=X8%c~kl z!oAG$0}GR|M#~q;&04#6?^3WM5f&w8socDB?5pj31H5VOU3S;8oh@d+A#gR2*0$~L z-D(wr0PR&a%>u5=p4%@iuuC@yf3$4u*?ZQ{hK&|Kq_~GeSz9=sy+EIypxC8r_;Phn zo2g|Ja7HN%bQJctYgWbS4z*Ql zT>A9tVI-1Ot5Ye)sf+x&)Vl4w@$KBuc~KHLAmE+VD$OdlW0_fgsmBkr!vxxR-g^95 zn?I$JbzPZmm@e1P76RYO$}38ccPqH6O--|SrYc{PEYF!Q>H68wCQoQy%ZrwoO?0A$ zvu?U<;o^m>N~VJ9Hk0P^UG#A5>2B)#;wouRp5T zYu}lw)a)nf)pGuwKUbG^raXGO?dFs!d*2#gvDmK@D%tiee`Z9^b{_g!dTpkBbl5-B#1v&Iayd>u#G& z+`jU;ygq?|+qr2o*GrT|gJCw9F7PtSww$28MQzpk;O)!o-MaqI=tn>K?A@NCRZ%iL z&kBxP#on=PGUhfFY?gT~Y1Er`Ej${D{-sD$vSzrxmLml%BgVJ3YE}8zqtq<+{YHhT z-wD(2E}30x(i_*5b?sB?xBi~b%%4(`&qVv~n_050Om>gu-OT%t!NXew4(;KI`ky-} zQQp3|TT*UUd7HMIZ}--qIHq=m_PQcxrBd{1K4%T zF^XEAYpUpJ|}+pkSX-H8!>gLx{--f z{TSn=p1GU%iR^z^huMF7NFmHbI&P0DDx{l5mfMmsB=6We7R88geyj zw2vR&zqWJi6a!A|Z5uX*4S&PBmre>l`7dwI7xfl%{BFto;G(MuEbL=vtIzt_r?2?> zgWMO7MX$8ch4y4Wkpg9pefWB#KkjGy4zNya*c@PnFBur_Rf9~(A@4GrkShOl9K z+lCGK!(D#kMB}vF)H{yi*nzgtKNCtgv0-ohu)AZd5)40gP(+v`;X#`-#^Hum{KAe5 z>s86sPXbr0!h;3yhbh(r?+J-h?2>gfsn(lLq}Z@NY|PCt;EoT1EdYRq4PnFEunl|r zhlj`N#wp~&`=4MZs@@I2?~060e_^x6x!qljwvn(9Xhsiz)~JeU(o!ezeU#WFNwAoI z*zczKw@xN=If?c`)Z07;eGC&~^sK(f~lkC!{t z{SQd5iYd>gfZ0xW0n)670czpIz*;qRf&Q}rW!AQW0W#q;_S6cB4qEdc2C;|U1Gn*X zAcedb)OFtndhShN@bKzTmic3YJDY=r!12y>f$2Wc1H|F|_m~i%?!x!M>0#AiZ9Mp( zeH!-T>ir|n!6)*zxO8BzoD2>@e+z_j-+6gzreEqp=&fO~r=d5vF}i?je5r$i>0{wy z;tmoCCwg&S99ntM0a)OMjnrIhCtmIDk5ZmodM+Oaa|8l`rIe?ySHW+ zTFE_}?3kfQ}6$u6@#~#y><15V-5|>jpNPiAd zFSjjnU;w{+CAcR_1j*GW0=!sf0px>Mz(j=ANnkEojNrJJY&%Tif-!*MULC>myJFzP zYHow0z5Pgxx0gqG-pc|8XIIm@x9yCNAemFV!7*Vqj-m<<`Kep)1cn5-(5TQnIL^FE z`z61F(0zjKhNMANg3i5rK|aNh6Z+@|WHt4r0{C*S1tbO7EFf@NC9y_!dl48FYr*}x zS2Ke5a0+O-l@;-z=G87BF2XaA;rlc;NQZ{7#vk3c6QA9`s@T6GK_I*MBwGv_`+9>uz-l7Nln1`}6kFazt0-wBO+A;~y|lP}QUyy7 zI|9^3O9f_*GdRUy(0J3ZwCK*^$efaAj{p?XTfylM;&U|6IQd-amoeU^C)N{jK=uBb z6z9(7piDZoz0X~@ZHS&_8=Ko}eD+gqWHc`KrstoA6tByS=#k%j5~Rr&+SMty0wBuI zIpcu0ZdTWRRYB?EFL1hWQb@gjDvrH>1;?1pg1d2P0zU305Ng~y8{p&Q^-A6EyA~}J z-WwGoKCkmBBjES>iBr}Se5>=_>BIE$6V!mmG(VVY`ZJ|>QU7>%+s5?l(*xZ;y`sY_ zw+(y4hOl95*cJfOT4xyRUPw6m82W(Ehw#GCf6Knw*RXFiN$4rDOGR*?cG<+qDy z4PUE{unt$D=!t^2F3`UD)S8U_mDVoc2rHi^dIM36#^9+k#$#O<2 zRpVqC{%dq|&j!tM?qmiRS>a$_w?m-MW`{&{c#Qp z7)m0@%AQKL(SCu(>kKpzut493v3BU?sT-BZ4-q7}#WQLOlq`(EJutfS)ik*cG8i-Sr85gLAyNd+^^z)4j8-gZNi4H!JLub^3?%}+f|KX9{T$v00gT5z=_DEMeSHU&x>loJa~1i8ZmI5$qPGvB3t?>>?$Z(3mPQ zC}Us@@VcmK#Gs~O`x9nUF#qTtVL9`amkC&5Tr*VyBOp@hH6E2P<7cRgC?bOmM1H<) zGF2BAk~|ozSiso>Mstf3M=DH2E}zY9DjU|)S6euFwvofdM*`(wHfhur3lub^dV`O4 zo;Fil5xOY6ym4Kx8T!8^7Zqh5S>Rvz`KO)sqy=$+>x09ZUxqRXGXwl`fMa8-YK~y0 z))gVJPeX{^Ppfy%s0^B5;u_9X9Cg&A!^6^1d$of}^KlJJgc(2{ehZt8=EE|eWmqF= z#$Yp;@>NU6K!2Eo2b3$zmoMyellhhwgO`?%2}oLV!P$a?AhspL_j-?oxVV1dBA(AZ z2oIJ@!UzSsX*xD;J4G=(>2Z9S0-kuxdW+dS>!i3aYv_}nKPOb_YD|M%xsLj!BZv`| zsnrZcUSYaE!>dByo1|+bPsZln@pDV&Fa`}dd-w)t%^I=bAeY<G&6Ic z6-LF;^4uje)vEJ^s6G=sTARCUj}jtexkm}-{6Ac7+kI>pMDfl?g6W2EXkF+;Vo<{T z&Z^TAQU@`C0T%Fh7Du2GNyiM{)6Q^Rgq2p-GTjWNBM6>Ug$_!bCxN8Iu+J0>tGajj z#|(sl3_8}2mAhuqtfFv1@Cc{BgAfAnVY?d;9T4*F!Ulo5Fiyxna}JoqO^0YUf+z}t ztqry^hLzY*h&u9(M~EK}(Kr)-mIpF5hpOyoV%F>+J}kPrMlJ&DC5oE;xiC`^Lth#- z#pHz}=D!dZgr6Y8ks-xdXx-Etpvq9~53J%O8^VeK|A7d3lP3m!q`1N4U+Ny~AR~|} zAPJDwRRm3%LNq$KfR?6u7hSY)uYfovTUT6kD2QMi(QE{;5y(bG7fkEx9Mn#=Q|c5S z6^|T&vJ@nx3TNTM&$0J|;0+UMQ{uaL57;7L_nU4&cNkTv0ZLhw{$AHUN^U0{s5nwk zUza(#od+ovAm}PVpvF?3Hk0enJxsTBfMx~HWXt64`)F#i$BiiX5k*lIY+SISpo?gF zo4D3|qzgKYlMWM!B@AjxUQaVMw^T%aT}`|?wP zOy~!b$vZvqqNOtTGqQZmBwXhaO+Um{My%Yq(`D5Othj~1}`Ufciv*yJxY zjTi{as~Oo1tY)X~G~~b@)pk3BjHSw{(eRV})Mhi$h{Zcr6j`$gC>BR2bG?t@dniUm z^6i?QZd3C*$EsTK7FgT;qlmOS(NLRG>C$BEqU{>4;!;B&ryeIsCW^X0!6|Pz zx9>i1#5G1UWz|#dGP#*{sjVz84J{3QCQfKo+>i6(z0sK)Jt6|Ajy`ufL(FGT+$Hh| z2nBuwtl^3Y57W+LG`qqS9YipCr^xeksm`IEfix^AxrGk3O33=8^TRqEf|HLPA6SMk z=zMip#?JfEwaWVws>Si?rx7HA_c#z|vOPe#D@STobI+C#dOMH+uUwH0mE2E_{Pzr! zV*PP3HA=kF0UG&&SPr6Q16MrHwAj9eDv~UUbSw9SZcy<=i=k+8qXkL$OHWNW7=wyt zmSl~vNJ&K_O6){1&k>{-^mXKeSX80EpHIJHKJq(7F%-#!@OVxlIq;i-iAIDvDHu?J zWK3N+6Q~!#sC=qyFt4I1Z#bW^s;xB8y76214x2U`q*D_yHo#j{h9z#my4-A{Ob5f2 zWceWAF)RmQ6!QniRwGr*OS`!o_G?EVL+IG?UyiLiSfUZUj;5$zvgpq_N=^!R%cSeJ z*g322)V6Lnls#ybVAxf%CVMnIaQqr$dDjZHj=yOrD1>XqjPO? zRkR7V)4i!xYEvQY{c>iOM{#_$%lj}Loi(8qM$>2sBkN?9c_S;7p`~~NAV+yfk`#^B zcxe$jh$sN7^gdJPKB8lumX^vbVeyj9W`c4ywg?al zygoKawbmpqD=qlvh%{k*{tUfowMF5}V%JAZn2JIGJgkC*y%K4kF!J;9?BF{X>biac zW?QDJO!y&R+SXhme}s{^BzXAHQ48rjv(BipY&m)6nOhmk71)TlmiC&TQKQ-s23Vi&NneK#nkh8D^GU+QG z_~4c+o%u&ZJhyDQxTR5}^qqB%LGgsJj7IyOie6k&x9Q)R!CO)13r*s3-)Gv#k#eJqyI8?tD{q8PoWyn$f0O3eEFd2l zV>y8!RDV;93``(Q`MIpeemzB5fTpbpo4U}ZQZJ&-gD)tWD>^p!Dhg*{BXy0<@MB^I zDW@G}2YbHsK4i7an%w>qr$yp$H>9W4z5d>*y=n)lDGxf0At-ZdjbLz1czJ1I$gIc~ zS%}42Ym)#yK*GPNjoAAW4Ej*tdh7rX8LUbb+<4tV^<85lnZlU#7zyZQXHk{VkVCG= zhr+PbnTnwB)cAiN2FnPqyw3}7N;>;gHkZ)o&cQFCt`ZUQ!%|nWi>yA+ORQDiB2XLI zmFDOhQ(E3^qS5-t)WjkW$M>@avnE4;@asf3anh7d_&tbBVBwUb2W(Nc;X`jUKc;k~8G0_xAq z|1XLnrGEM%`eO0l3;PIb1+qKMaiiqQ!LYoQM2GgXov?}s;W{f1+X_0Fl>o;&KP?vn z2cJ`%bNGSkBVRG^YNyyeSR$e%2)!Rp`*oq$n?nC7Y{a7L!Ux@MFK1ZWt)cr*rs`t< z_SBOXc%K9nu30tizsupSwpKVgX8LelPkKMq8o%=4kR*5rhmd&B=pk>jK7QDOfg!ru zL-V2LC&dXrk|itV(;E)ZuPUT&X3pnUrTblwVvj`AtL?|gWJBi zr?v5e#aihwvk4LBA7;o}z42^K4=HesFb$fd6-U$p^*xd=p*rdKU=_yJ@9X#>2mPaD@8&$T=y{Ln2B|yQxX{FUtOOb*S0z-<# zVo{*uNv@M5l_!hzqUbb9)4((^!iFJzGP}b}33p7o`P~Wokh|GLnb`q=fz?;8{A)~PcN#e@3RE#0A{QV0H~+{17_8JIKJ@30LWyxDP!%wst~^n zkiP~fHbAd=NB#Thy{`D*zkTbv{ap`N8tvcKRfPe_qc79=so112PANkLChfSZUMOZ- zLIHqmykFuPG1nd{YYI*M@j?qq4Q85sP+y%;gryqPn3Cca=^MeRgH~#JR(Ri_+)S_) z4>tv~4Qr@$Pz203;p`0++JDSDZAE8wgWcHV4*-$s3jhbJa5`G|4s8ZE52DoVEk7CB z)=?kMc8ris)o8sVq^Ud&{|Pfuh$W;~NaJ(14S-(m?b^}C|JsrVQ6vD^TX$jghB zwA{b11stb?28Vw4LF;tD`~R4Dlr$%VgoG9AG#C@s?RbWLLD{>@&4OdBhOoLTb|dH0r);(=lfqkKDihuy9Xc`gzkZXtG65w9M@Y2Ag{Lr1!3X^ zA<1rYf#6%Wo}&P6yo8|gEiqxx%enZ>YYBA5pw9KS!xHM37CAW{b6}V7vOMA%%MmfS z2%U(573gm$Ig|Sq0&4C@NI1>$9hVc*HU`Q6{cD$FMwzloqyq`M%Qqm9uAqp(E`YKQ z_GR-+mXkG@>X9Ddup=N3`1h1YH`pGq+`cZPT#cE;K<5FE3Cy<;XGP2hSjdP)aO-uK z#FQjnMMIO-Dp%S?4U6W@GAyueIqzaf=RL_9T0p_!amoSbw`f!yy_R?dSAVE(G^iL_ z*`PRZao|eb3_7eFRV<+*;g!kV8lURgcZoWJXppWD#BqPL*(El%yRf$ zh$xRhgTaQXT7D{wB*xG;Y%Gg7g%y#OoNP0O(i6t4{3Tl{3=tzHy6OmtP5*-&A_0P+ zjZJ32UMwgK#ID4U!_GkUV!+4Nh@}fc)!Y$qWO?X0P~>~d>WYcYvpA7ygyH8yMW!Ym zAz*R9Ns}PPB11r-whs6#j*%Fj{v&=!u2|oBqd26Y9ROj6cG!m<*kK)Ze~0hO=ENM^ zm^=}H&ULqRi$6s8w7J=f^s-q-+aX6VVULyV0`4@RKV7skh?3J1~;zf+LgXy7d|X{|66m391Lq$zS8!k$of{MYhury z+;)Gz%h2GUBw##k%u838rSh;|9>@%#f9TAvZX*qR_uZRh-QY0qI{W9o-uupnlnU^- zp}d;oOCu%~*I7o2B8_51=Luc){z_Mq-n`P;H`mjh+i?Z`EmbD?DeRMnk5BJX>Mou_ zO+f-b=X(u2o&mMi)e?Zd_kLMeZ!`3&J)VrS?YoJboUe!BSXHn?LA5u_^aVX*rl)O- z0Oz?fgy{JfG<3yDrN%AEs`R08QZ&fgpf+rljnMU(G9Axd`w|>8x$J7*=%B^iMPVNp z1yYeLmQ$o(CbhUNkcH~0f`%_I-FLCBG9i5?^>PciO@0)Fm{rmyYDVpHgKL6B+Q{Z` zd!z?J-LJx80_d0aRNtlqI|UN6-wEpsrcbOkGeRjyW7ywSKY%Xyk@{a^I$LcS@7}(z zaDZeVlTkZrxdXT^$OFL<$gbduYU-fa!rb@umT1z;h*(sQE-{6S<=NVv;Z8wLjtsG! zGVO4g7sJqh>kIX+-@1#geQf89tf~hWyAftfQ4EV_%Tk-?i!5SJTIo78Ta~{5)jGUb zD_6CfB-t`vStj@F8PP<(^r_jR#hlEMNk}B2jYvV%HZMU#j4+l>VmEQpLRm%0>#~V_21to$V}@N5qpFOPBSUipmqMOE6^JHTuo-PY4*PYFoO<&R| z^E4%_D{Hk-n`+Cvz#?|doCDO*5Mact=E54Ifs4+tg#oGtQ=5XR)$kG3hx7$LcZbSK z_tWB=iNy{(&@%*xA;dKBKAq}(W|L^jZ&^5-Vb)AB|$~-E(@hQ--fYUT*0fA7%&(?;ldYQTv7YeBH zgI3{U6h$r-;WC(}W??me?q3l%sKtWGnAZF-&-vf1Gw=>b#*!>_BQ*6P+UKltN zm3u+u04;FTZ@~>6Ui;?P-u9Ib(eAxbz>!T7WkFlC{gXqz7MEokmY-P1KZdXZ{Wo>o!txLahdsdwrXvl})6B4mrt zD`48(zgyA^e<|~p0@JVE2_npTh81eVxH7KPV>vWA(MXx15WRWXjW6v4#o~|PEEia< z(NQ{5cwGs5LL&O{AtH<#AyUZQ0+^wv6^tz?%$;*&M@9B+E80Ho`tx`iT#q&9@T!Y7w_PU5IQw}A~!CbS4CuQUh5}nAd2thunu#GN1 zk|CpggpxsnJ-YWbz8v>@2;+Q<2uO zyEjAqbf@Lg<6~W+3=;nO(d{XXNd-XA)GP+24*Kr+r)C;Y zo}IB`(xRockQ;Q}Ql~RyPBU9gtYar;Cdo~WrW-mHCyk_0H0nj23Q6MeyAaaEys~b} zSBZQ8wsEpABbnCuXd{<5#4}n0q0%8^8bTkXyK!EsEcWSL_yi*c+ew!LF0p=St5GFN zPIVKYJit_H?Ws3)YH5Y~xY~7U-3HdN(?3tAo*F(iI!(o8c>N@w_Uum_)!u5zvh}Q0 z5hqkn6cKIT(n*yofaG589GG=Vz_F zd0udE3{TkRiq|=_B?r=HsJk>fRraLvuM$<}N{AYKbKHwO0YXv=Ur4zDofv~UjBk0Y zer%BQN%jh9(~sE~UZ!&Cmn(HL#h5a+p7>)-T-rVi$9<)lbc~P5vdE=YW$t7+rc3L{ za>a=_hJ%C*A@~H|>y2yVd0V2BvH@}VQ|_QM-q_#ba8zyV6YlaW{lqk#2S>C3ua>W; zj>}@0zx#@XTz=i@EBuT);KimvB zKd4hhi6~`M=LpzH5>Ct&{Ito{|FhZbZ}{ncYZS`flV<6w31(59=RAN+=;+76pD$8mb&g{QTPR}l^P#WaKr!V59Cu3fi1A1h2dIlX-Ys^ zNu+K92L_O)*HHQEG{}7;1vT7}wSB?K8OZaq28(bK|8SeJ6;jvdOD&910^{T(j~62L zD;3E^N0M1j;a$hazoIiHA0^ZAj}H%-Fvd8|)`5=X(Ookd+K`7sN(@N*->=r>x^2^* z7zSn%PvfTXqCf;T{?S7)j^?_3FI%E!9z2(vmX%CZ3algP;!s(dOKavwS2(5d_8vb*cCmaX35pNVYFsx|-Uevd>SlWZ090*~_MP4ZT*$SOhP_e$~XXvkgEm@!GB zmVeifyppj|9@MTxq(lbvt7O~@$73qs^j327j9gLZ{%saPN5^8YvQXsOM7(LTSfpW_ zMHiR^B9O&d1Hmj5cCXbsg7*uV;T#n<<7Vk{6P!Y`1x$)j)MzDM=|et;w;1Q;T>;By zmmI9mY@m*~kE1HmzS7?8`S~P|hpyL}*>Ek~yNRt~_-E9Od`k~sNM(?H44fsZ>fp$VJdKp6J>-?rzQqFB=`S+ z&e1zntvd|ymW}$1dcet%y1$>+pa-4GQBJDaa-4HxM02;aW!{x|70T6NgEMT0*;IS4 zs!35gtaH3MHgF(qb!Ag=4(MIZ*(aZ0`O)*J7b>OTS#&}wQfU6&-5!!&;jXD5^(i#_ zOM8E@(ov&&-+s{q9JiFi{Lu%UIK|0HrR1KIm~ZK9YhE=c+$?G7u}Q<$5dXFCoDH>e z{@BA~7BILr`n;Jkc+#n4hhyt!$kI>5EqLXUw*NPh7FtIQ^g`G-Hzh?m6cM3)wHP_R z&i z(k*XsLh~-;vAUZjYh>hSj^vTbE=U(;iVe{43O8!=x~J_%j`M^hng` z->=DUFN-#wo#EsK z_2dskV%OXm8cjl{BoIR>9lMHN1K);7sy;P}k$n}_$|POQeiYfvtI>Gg&SZbRNB@G# zF#u!lZ>Z$P!pUkv$$?L%46H@oXKl zBpFmCk9a+i5TA8P!D(ITTl>5MeX3E3gx&sV#XC`iz+IvsaBFyeXKfI zCs8@HC)c~4;1LneqK{BDM;&fm)JsDCs=#^_D#b?u^cUjtw7l=|3+bvEMlf#ZU*&2+ z(K>$0+&54nNmDl36NpG~YY>mVZ8!g8ZO%l`?;O9vuj0<#--=zueg`lHi^tpI+Q!~B zoN4D$=RQpc*k`WWh4Gt=*43r_2yDA%gSw7aaxhWMLCF(G=BV@wH4M&mt95FM&g*8_ zuLjoV2S#^Q{`)cj)G!0&7+LLC@TESHC z8g*X(s;+94e6$AfBiMLU9tE~E&fu$H1=a0WQ;vIK>Oo@L;&jt}-_+PJ>?&uca2g** zaSM(vE1d=W%Ctw5>YuU$^K?N0IzZC}u~IBaY4A}HZRkHkfVkX-7|9!jumI7oC`2^v zDus&7cWFk-o|{qGslN(*&8(o%NQvTkWN+>Jh<#{+NRZ;*Qfy2igt>$9paTAxQQ%;dv-)7 zGr92t(fSC99j0H1{I8{4?E7zOOoiWXv_MoYM+$>0obmeIWdCm1wG~e@JVvyJxAF@! zpsEP*o0vV^EB>*lcGEv=Qx*6?y<$Bu6}cR9lgWbG9F4exqITMmz);bG&LkS<0#ntHrLn;c1`F71HRc_*Fqx*ZAA{hI` zR)RkcHDo9T|-=jnxuO&2kUCVVzl`b zq2U58kCsh|B@_FK5B}MuuyfhZ?00xSCokbCXb|eAEK|Ma%8{Egz;q9rd zgIrX10JZ(yfGe$SL@oI>;x0@Zt&i}AN$Iu*KzV_!cNX8 z2x-9Rb>+9im}H@F#^!Av4>`yte*nAg*e|q`c~}pI=*8qeS0ZsFaAQc9PNeY|~5fbAM=rsHm*0%&N?$F%31Q#^J^R!!2ZzaGL^|A9+X@IZOzfh6Cq^`hMT|oW1oE zjSECE33zrQafs<_C_C7SB9eJ}?O=wM2g_Cy(V!QlaUW0#X7@v_H}+_2xMhb%sK}4b z1vjDkuBx-MlG83JS9CmEN+J}{8ed2@zhk3-Q9m(1y;wG6ab7Qw#y2>BSaGQdf5$Tc z0YA?;3G2XN(U8^|l^s1mJ>T!w9_>bJ=6pVnzx~LZ=3bs(nMsbjyiBaDu5!9$b;&Ak zmYlL&LdaV-7Qqw=AWB$}#EuoPvKoUFAk+o4s|Xa-UPPdvbT8t150?K-Hoza-33Sj5*&E!*y9ypUl79-_{MviNUzsb!#dZ6)tb)mY6TfEsNYuI4H|O$xmcdq#yc;yU z@RI5U`|BV>^Y4sDmdT~h4}<}Hd(>-K9sYRx5CgIH=G?Xa(yhm>(KgL5`QTB^(t$<9 zlxUIPkS`vA(8EwWmjD@>^aDhzZRp$vQ)$;!nbVh+N;7t9NAxoVTc#R|<R4^-XeU|kT|2yqXf~!= zT!vOO;xU=r*vxcKL`dAeUld=BPF5trU5?O9U(6lNy8^^2`1^x=;2Ap%+`_FoO@1z4 z3oFN9=AJ7UDx`km+4ZZnS?JzAoSuQZ?WIJ3 zz>RJ;(9*B(vXIU^1=HKusivT#nwRD|miID(i3f!uPwT!Mq8rk)S4eZ@r{<0vhPC}L zp_fkQMvb_#4J4y9HGYrNnlGkx2!8}to~B=9=jG<|Nor7Wj(`G0S`U5mU#cfPETa-T|#BSF$=&hU!pHyRE@VU*QRR4}jY9guuPJn8cu;BPYYj8n!2wzT{p z4QYA}4}K0#ir`1TIIcJJ5yCh7&7GDl3ja6N^KB}lYNvd|5b$h`bqKJ47bT%~R3ojE zTUXJ4?>#WaY38P;l)%dm=tKmq*~L@4f4lWCYP)o=SGAk|DKi0jY;iW)5;3GjB(Qdm zx)_l&`PFd9nH{5mCT@^G19+h>rc~pEL*SOD2kA_Gt$>Fa7E6`{wSLk%%=-;a(kiq< zZ?zu&o4D&oU_Da6;dXrri|_9mH-070EvdICssDG5cBj&K9pR&$SpS%bp@{zjDAT8Y5HIf8|b+tGpl$D&gqs~;v7unGC6fT_-}fScZe z?4&@cvrC9vSA;7id0Cc?c2S+E%hO?2C6S%Joj1H>kGJn46;nf|JapFbAn65x0zlu| ztDOP{i1SCc4Az-cZIY+_ig}dFa)ZB*NE$S;?S0MM+XZM$r=QRU zJGjJ_aY$;W4E?Zrrb<~hN}}hr_x`tO`}J4FQen4OVI!*>Ny3S?3gMv@SE&)@J_q*O z(r;r6{Y?0V;B51=?^ni4#*UagEKjWTIsex8E_MbGHvp+S(VPg2V#(+F=fI6k1+Mw# zYsPYHMZ)}S;nALVdS2v9<+b8z#y1!Fmv*VQ%44re8o#BDIJG(vfcM{3hV#NiS%zSX z7Cb;ZbLj!>`+P`9LuQf3UIrkyAH|Q!Cy&^Y#7YW^76w>5_8gTkKl+pYRa0onjHQ_=ux=U8#i4DuOP)saF60hrd3@p`>uHkviB!!*lW#bz zRxcuVEWs*KVd5IU!g|1S4=1+P%#BMt-`SLHpvqB@HMd$$Z9VSLPqpnh#IKl^hWhfY zxm6G+C3dbeL`w?HYGXt5L*ydw;!{f65nd>Y$UdXr#k4B!n>=D9*v{Qa#Ufb+9DN-c z(dnfjz3as#N^p{|^R4zK^jv0;r{qm*nTEH*H=-}11b@WR(;w^NZ03_#)O|>#x(pFv z&B0pw?VG$9j=E$Aed`epsq<6jX4ePbQ{Zxo}&qbToHy zb8(1G)GBmi*`+4Sv}=u*F)uC|Ot_-%*6l0enklfJe6hVggeke?EO3(>8+2l{rU*Gk z3SIU^vSen4&@DzVRH@d|kaJV=qev`i1OQ2sx@DJk0K?YG`shF*pv^)xNTXh^#swg# z25Tia=%3x^ua>FsDsjS#OnZoA`1Pq6PYNnVfJSVzQ@lMnok*t(@@OHZWiOK*0~mWH z!5{#h1E)?k`~~W$uc^9=z^eFe|MqgLG*;7#LYSr!_FS&k+Km0=JBk*# zDEXRk1XLSJ+PW0nEH+e^8vfrKMs=wQ@k$O=P8L|n4T4K z0EFu*R)r7jYrzlTVl_H&xYvy4i>xD7yXCFMuYj=P7ujZB(%qH^ztxqDKkzKRQ4j55;1AhV@cfcI@ zXyJWbeb5v_Gk1QDo-`hFuq;SzRWVoWZ}0y4deo_*YpFDN2s`MGP=y^M8_lc|+SJk$ ztQu<|Hz|#i^t7K_nN}4Y?UTxq6HU^b0j4aTNP@M#7#wZ+&GuSoD_g}v)eWjz+f+Mw zn@PfoGZDIOwpiQl#1`BE88wC=l?$DDH=hC9VmkMK*ESV$_*xAigwN$lRn1uO%k;h!2 zYjHAPI=SxD&8-M))?vV@^GAYMr*5ZX_`xH*rUNtjLIt&Me*TThCZFWXnSW-g(PS*O;L==k$ixw@BvdrE%*mR&;4 zYQ&8&qE|)^a3-T@c~r?|foW~%Suxk86!)PUPEvq~DIZEX^<-HkG;IR)dz*vyZHomo(*`7z?bbj7R6z$Obv)+EHv;$Bc}Rha5CGAH5bxis~Zp~e5r39KBZ z91>>jDzWrv)diNrS&cairt8jDEaLVY5E3w9Y&|(6c|7Pm4?rq;o$`na5Fox3s6D-) zzRI(PXK3R3LYgC6wYA`qoB9I#H^k;QX)NkIEod&aqjD}C$T%3zJ~#ar^Y!O{=E{ zZhpt|QtE5zy36t{r&uSHSnTtG_1hR}chHgUN#j4~wO-WxKk!hAct6Qyj^`7j#Guh0 zWO3i~h+9pDy;W4&{55orZc?3dCyX+jp)>@ns;iJfRoKW^a4eQgJ51npsZ+E6vI@=I zPcPN}UV3%@kZ7kQz3c|Ke23{=)7-1+FYCr6El$U}kzv`31;DVXi`k$8t(X2)d{%C~ z9$WV}s1?)VX;?+1qt%2+qAVVS<;aS6sfL%zL1?-PNXZ!$TGNRY;&>+^(+F?&YNi<& z%9z?cX|d17q-v(Bt@FVSj7hg>Q4G7>L$FNS>9RjVoS#E-G)B~qD^s}+&Px#!{y4@T zl!}D7!QRQBWiL{_a{KVAZ$A}p1??p`<^RwiSd9G)MupYY5d(KvR@VH_EdTP0yxj)j zNRlO%L3HO!HwoDnF?xn)l(qtQ(J@CcOCN3VD%~-Gi5~DxY9;=}B->FdD zq)^GLH$T`Wf1hjNQwJT!p#6zyW2U%E5!kMbeW%r!@m|y0PHSD6|KtHn?JDJZPo$?g zUumc2N5l~Jk&L1x1O{z#m*(7?y=s3AC?Y#%RI`uhaMmtYb)pbH4$RhE?5@cW?=C-y z)09Bcaa0|eO4U+yub|&WMN1v=MlB-vleA$*2UIvJ5G@5MN5jOf8$x#?o-FjXt}%zE zUzK*51ZGvVBLxg>2GClusksiexjXEGmLVR%+NqEot9`T z3O@|AIclst#W1kf@5NzMVR*hsy`}6#%+|sdR82)=NBiw`6K)Q*TJXgmyR)Mt|yL<}C$(<5qe6-?oOub*Z+I`NwgFUNQdg{oq zc2vxQ%lR2n!&e>KecCmwOc$7)TvumN%C1W4I8Q7m#xFq@dfVs!+#vJ4I48|PTN`$) zVaY61_pF9gV%lg{PUJN#PqwQ*;4-6I;_i9@Eav5u-u%WYd^GUw0qDmC31JuFBHSiX z76IhpT zB%%2EI+O2MFTPw@ap2{|TAdlX{MaXwWN;Xg97PZX{UW5T1&MyeGMPx+B|eBZJ}yb* zMxHfCQtwG;_aP(8?YE)<`Zjf7TGl_{O8zeA(rV;k^9=+RBi^L1OG8v1bRmu0a8XMp zht-9W8gjms?+R7g;7!1*wiK+~vz#usXaz$nObcFHN4pWDMA}wgh_7Sk#SG!6_OIe^ zX$zxr0idnvV?sKOIYwLtfI=Q^%^ zOM#$=E1?J~nw1x`4c7k=hGimJmMthi8r>qz%!$;$?AzzGOn2ynfb8Npd*c7s+@4u2Zt~s7;;+$X!9JIdV%e>Qf-)UQbHWyL*8wc%etFX zhlZS6z3LsclQ?sZ4yiJa!o^@GBDuf5Fvtq8#0aoNeQ`+hEW~Fy7+Z1Zoh3naW4Xc` zm|!T{ebB(mY(m;HL7GAKbeL@T*?m2%UjEM9kVvxgs&|RP;XBGwe#SAJSBB$Ib~$iI zFjg<_OM+gE1&Dw6%Lhe0N+k>_oJ%k+3TH-dT$*<(W8OY^28EjNOkY*>BM1ZmOkdu( z>|GqD^ zD`icMYa~w$Oy`67#|gA`)O3p?Ly^mBgIQwJZL0-tvgTx`iuifUyL4rPs1>V+DTDu)1P!y?g(m3ZJh$-b@ z5A|sQOwXqq>1kTuQN*iD-i|Ipd9Wuc+7;QCu;Qnm4I}F!-YNLBNrQC6H3r9cz%n1B z(;13Gez`oLjx8q|fKKI*D`R{!9Ouaq8z)qsz8^%=<~&5|Xs8;&Ia>JL{%&JJ2Qe!4 zurqvkT~omRgZT#rNpKSc2OC@?4qAcsazv<2p?}!=LQ%D+!hGUz&%l5}IcFG50O+i* z5eY%;@=g>Ua`vR|jz(epT#v!7MT03cS1ZaJ_=f`Mj&98+iYsltf8$ph&}T;kuM=Ua zQ3v3aJc!iFB|5V3^2e%Jlr^&V^=bKAe=RJ8Hx)L_%b`Tope_r$d{{+uR7_P4)qDlM zIR+`onZm)!868r;6i=}7u|3IR_`MppeAijI$?2D2Kr5Nzu6<_=n0t6z$*~Ir!z%xE zUDyyF-mZyBMO38XRWZR<9t{^w%5Zj7QJOsdx}xmPG9{n17PnCjPb!+R+~$c&j&nO) z9P%SLQGf!0Dxxk#J~s;0D#daPa1p5aaFsdPer95hYCH}IrqOJv~SD*lh zpeYV&ijrFEZx+|C5Xg$}G>V5%mlRvwSh(x;Y75A`~P&L~hF$&7^i@NHDg>0s1C)}%v> z!gx(M9M>-#L--{1?;%7IOIV7VO(yT6;Ecvm8TmqyLN0FMa#*jMiSPeZWVzO?3Jle` zpL$(Ld>>s_*7R|U79kc5q`|~zFFA;^Fq?lE7PIWaH{lVsNZt-;N(r{9(op-`k!wB0 zfvGNr8gSeBFJq>5tQX-=qIeW3=f;?~(SE&_`sbVjxFv6qQjfzStnh=4i)7)8R?C1w z{QGXZOdzAQcY(ejcpH9W<tg;h4{$377egz{g*EDUowvDs z`)lxNLmNohB}Q278b26Co~8zFo=&Uc5Ic#YGTm72teB~gdRyVf!9vb6 zxu!iCOD=Fn`Sw%9#~%*2<7cts2pf}54XlryU8s!;O#G*5#9Pe7~rnYwj(a#f{@W*ByA}M3YftxvLh+7Mt4ga zrIJR7dU7xX%xGmj<9IykOse&^2le-+-a5?r`VOLR3<<%pe$v0AJOT$vzz@Sh)X7BCwpdVVst?g6bf{2+v`pe1M3~7b65{602`^u1jT<{lMdb=}BD# zPVEZgLiEx5d-#>}AB!w0BioPIk;zNTicm2IVEOsx0X8dOQ(df1rivb6rotyKn#?^S zVr4nAxlTAmwZLn$MY1PBgvy;JMNV@#a|9ZbKcU#C&F7dGR`)kh@ zJ{e$sFRN2=Fm0sIFIVffE;v__ma1Be1Mlt+{InfaMn^Q7I9fh1B&~rS;`;g!9-$lV ztXJs9h_Z@Jj7H{Bd03{#4~qnW!`h*UVSwZ_`y~b!nJKdkVl3mpwFjBSQ|CczmcHsZAU@`#%sEzH zVVhSbiP$g!d~3IuGqZRq*j&*AQ>$`On>qIx7~PP8X7Dl^(kVSq#%&QOQ_}n@K|RvQ zj`V#I8m=;G=b0QJC#rhdDg@vop^!!zR-+yl=RorJ4zIA+kX6} z+EM?{WDHCH?&eah?ARK&)_xBS^v~JvV+Z;t-jR3uS|Ha}{1kpv#@`bB{XY-&1GRH? zWY*&6uo4(en4-TwHE+-p+ozWSMOxf6`SvVd8W4;oe)q|&es zn;xYOr|zYI5xu@YAne>dE%EW!b&8nlX?1dGwW2KXo{Y?@HKKKUGH*jV$vmX>2PqRa z-wQ!RXw~uZ>Lnc!upu%fq@t$JKDzI4=vGvx>j){Wwo zS(UKC@lLdR%lV?P3^FF+TPk#55?7&3UU~;-TXD8TxY~6 z4hU4h*-RtN?pTo>n?o}IC2ue2qR#sOc70~XL9(U6nfPU%a?wA*&NUY>jY(?2FEffV z3XpH~IHG;ff|d&k_yLiN!VBr}lBMdL#zufiEjnOsG}SoGPgS|;jb7$#CHu4oV8mHf;__$!xZMNL^725vW|_9{jEWdx0i-SOGzh~pBbZuTRUlS;v;2)N1yzb{*LsdfDvJt|47G+QSXD#%f$Y=f0V*$p zdt08;&D(^qxT^wnmx_60=}$hZh=9^rxc;m6j7={6!}38Hh(Ro8Yym+y2GC5Tk~B}u zP%%u*M!2&1$(fNJ&=ydyLs{(OejpR3QqDevMD-r&pem?4s?EWm;nygQV0JS&1W1c= zqAL^w4qBaJz*@}k9yI*H#^48*9gH*R)%F=$b-j_6H~F7rU1W9jgTg?X0{j!kcg$9G zYgNoaI}?dR#AoFQR8>GV=;T!vN=-l(1>S6it=yNv=ScL3`uTW;${;}=9Gt_I%#SHj zEkwm9i}bzrVL@D*o!Ag{L!~nQgIWt6qB8-v$@o*UV~x~!60{mKsZta_K{JFRqcf3D zesj=SF=HKNg?m?JS}CIes}}n4{q)9UCknzyHKAlMGfZmrgBO3L1$t{+1l-ym>n6V{ z849B-Gf=MZfxen$iu^hplYH?t97t`3bpQ&_V@ph6lF8|S047u9$A!ldg-%E;-X4MZ z^~do6AiRZ?4;Sg8SfuZHr$wPo77)PE#;>XN89wGI5U_NakWJY$noJy1EWcuPn!Pj* zj4$MfRDUjIk@g^5*cD=~rBe@+2Hi~q4W5n(nAo1w?<_1Ih-dKEzzoS9U!`pZ(=&*7J0kuJ^^r^()&z)iG;jmOaHSAqN+;k53TAZY>?QOOW3kHwD~ zD5SzNqdc6_K07c5?t|*ZaSb`%_=3-YyQ6v)zvOhHyWev|@;Bl?XTnl}$NnEOlN`XS zReskFDO2U$$UYA13`--#`lm+Alo0@IY7q*betNG07MdW-m4N!&#d1_EFGPi`(SOTD zY$f3$7@xT?s|7JM6b!?`FOgL&2lQ^s`NpdxO|@|K#rT8pyIW2!HnuPEm)LsOwukR1 z=)~a68(%VSVp8~E0jE{yRsL z|FQgPJVyjOjhXNV)pVZS>v;D=f4TSxNv6ZzSA|A79nCoWLFSy`|6rgUeEMGo%j{|x z2ayL*k7Rp}gPH0um_JfMq_>qdjTv>ujsu5%OZvVq zd(M~w$}gVy_EGZe=y1YgHCcGld%x9sYl*yX4Gw^*kw0~A3Mud_WT3ks8Svw7OL#9DqA05 z?iBt%e@v|QBt(CmmX%Vs-L^TBDE9Vn|4cuEk8V}G-VVt0GQs#D+f`d-^>Y>;B*{w( zcZ2eCM&8TEHn}~^iaTv~%5?e}>p!>S!c!w({PfP8ql1PV7(!rbk(w!1T#JaA;d%4I zUZag|tC~q)~fZkM4(>n>fsj6XvX~omfqE~yG@gl z6*Oafa%g6~a6bkG92Le7-e_+4OSL|+Ovm*>c(OH_Xzcz-DhQ6A0(k>JqA446tCR0~ z_Go{H18#PsP~6&r&6v+3UGjdnjupH}>HJ$;>Tf6#y4Lm^-xL2xLP~FlPi%jb)Nuk~ z`0Kyzm)#JG(ci4h&mbBS*HacZ_eVv<_z|XjnO#Y+Y%l8VYFrH0;o46?Q^MvCwAy zg5P5My@w^DUF)~53E3K&b|2+d)nu3`dx*RW+wrS%cKP}og2g;=%(Y>R#PrcmL5+^^`=;~!Lbr)U&--ou5yZB)5xMR4CemTv z-nW+%a`DBbp`Do3IJMFn)Yg*L_s*~kW{1p9d*0nQkJ%5;(~dnWgIpcsUq5c_r z(#b*vsAANRMun@3mzV-wMKwx3o!x*nm2lAd#sqQ1`iEH-j_Wr6+l?_)iyOIbxkAOh zB)8s2vvUOQ{?r1Vej!BH9_h{VyuXy&r*QL}Z1f24)7!gQkQ^{lVq{Lb1;u(J zhi-m)h*7X3A8;TABiRl8uy0^x(Bn+}D01ZJgdRU+$jdQ)AShDM_&~=6c>Q$pgz(|~ z1I~o@^Xc=%;(1AYnf^NmR3ETKIF59Ri?Xvj{U;X10KhKbESNSA0D$r&^(6O`|AXIW zvf^3LwL!dL33O38tpYaYFnQe|wj z;%$E09yt}1xHYehVw5pU(o!+dOBNbp0D?e$zXEyBwG=&j@ImF?-kW}l859+z%~F~* z7KWDkw1YsFQNO(ZN1rpCURsWyWRJ(#``i1%bVlt8?(q5laZhqq z<5AgJTTnjj_3nv~28cV|eJ`J1mg0Oe_bz$qWC`Ac)2t`SpCtD2`su{U;>Fc&lb$SV z4aMvHIIX^;KenUww%0_hTKQSHP|~NDwD}TqL7ni{Bd}r^Nf@myYGRAdkUqFnFRQ!f z;7EZouc_L^RG=Oo?Qc!r@g~lcv4ReUg$QE=Hth@kRth&4PT1D((0vD0p36AOj~$nh zpd=e%d}cp#R03V{lmDaG{C`3n)ljbKM4R12a(n$c+2VI^b=$-ye!%PQP3;?>_+Q#8 zW0JW!2?lGPcbbAFJwtqcE6L}4O*|*NSo{}e~99?w*ve}IP5A{+D)GG2zqz^Wv(4XJye_*h>#$*TIFdn zxn!h`{YjPcDEm`nC=C#IwsWKUyZPaMc!R{j{${Hx9hj1A@dBB+3flqX1>guVm1JQJk+(CsY?}B1nR3YfuL|I=ePq@1uR5EY-zHp2qh`} z!)gu%?-rEK&;;2-*(8~8x~y(!?ch8G+>64o&$pwz!RdMq+Jf5<;J|ShQkxrb11&kUrjhYr{g7;?Ph;6&ukD|%-J?o zP7-!7W!B*X$^uti$!)vD9ITsI(uW(v+G&H<*};SeoqNriv%Ydx)2P`5C2fjw zwKJSBE36;@+kH=dMv}cQcJXAC2)en$BG%7}{c3(-;C&&QpZc!kFv#4=oF}OR`PyS}Z3Jm)hM{8NYWh*|ldtTE|n;cxvp0C3ouJ z>xaYgQe+Q@NhC+?n&gITT|z1^L)U|KD8S^c{Ps&eq9R8n->9b`9q2FzXLlloe%;q4 zhbOLO3ZGpt;$^7R%pi5f%TZswo$HoMz0@XNT@I2o9E1#+K>t$2E!f1%a+)EcE6P=4 ztc(yk0*npW>#Ir}2;7%<3W|n6d>~DoZa$C`Mbxcc!?obkBX`jS_dxN)jo{eZxCx>) zE>8*f5OgPjQA>s)x8S*+*Y2)m`dwphYOAheNgh0tW}TGt>d}7hhdw!4S(+oNXyTO_ zMuAdTnWh*xml3h19>OjqG+!Mjle0%1UA-5QaC%(78WJ z8#(0m#tkSX%O3N!6U z>b3}s*lLnV@UID~BsF;v34qY7Qbh!`DWGDIbe&e(gK31pHe`CR3l9kh6l_?jGF@3$ zD^MHS7W1mOISP(@Yf-LqG`*yXl%F|kcK#Q>b+z;>d34FKhLxT<$nBa>+Le_A&X6U1 zV4i?b!>e6@mR3C2vK7jqHIN(VRFOsrsH<&x4>#@oB_O+3S$R7 zcbc9?{dUsU!|9%Zr z1ll{9kH+KzKLZF{npRzRa`I4F%=!&@^UHTx@?p^ma zfqA?^Lo+$tkf^l+!TWO=^rltLNw3@>eEwPr-q#y~uMyvOlov zVUeqqXXK}dg)lC%s-{CO>=uCWkXa02kTe;AuKsLq#iSOg?oJIYC>N+UKo7Lkr%~Jt zsbCJ12a=TWqH7|813+&OkgLqkdWlFJv-ynP5-?$n^DJ#JPSXbm9>xP5IiDV zO?z|faX-s->v)OVbUqdBBc;Zvn@9WW6Y06N;Zw`(a*Y|CL}oQVKzy`oyoB>CqJuL4 z(;?8;hMIifLN}i{Zyolrcrg;%0wZE$V!Sq<-ZEzq?kOp8RNL7)kPx(%9k#S#|ge{-m>k56ggG?s~t_tcrmv<@?Gbd58D((fjIm)_2T z8?;vq)2d$2jQVc4$BGv{36riz1LxObMBywBQTD%$_ac14{5ELEy2z>!XJQ!!V5uz^ zrQWLgFoBJgF8JHumHd7o$Huc9Ft4glIVx%}E8&#(%s{^O_+S_+M<}p?er+~`nR)c3c7!AtzggFQ)mWv3KN`z2A zW)f3G)uR`itY5hKX<_X$=3*@{p+^GyZ0W}E#;?vCYk9^*H{t@2J*SG7Us9$W67<)# zb-V9u5u`_izHOzXSgjfiFZE%Vyzi#KOvW?Q+&X?}=`EuHcrKVR81iY>Z)k{Tk!dD& z4w*oXGdw?9-sFgkTz8KZH}&3Z>)#{tW#sAF=rz*zYMA7%!{gq?p-xU@*aZ9D6Fdgs zn5gCb!7i}y>#$f+dt4JMleRnOOv3=OQV;in?XsVzkz296YO-BRb~#N`S;S>m1ty~e z)!Z;|Tw3nA4@M=9TC)4u31rWQGjQ_2%-a)RrA7@cOBksw3kEYX+R+K&UUo~A@G8_` zn>D43Jp+cWSS-P_qhU6tt9@^>VWbihHwineB#BN^S&(Dwpi%Z0*GqMg%GsKGIM6K7 z&QyMoktgf2;iCT++R*mwLb$iowrji)xA{PSQNtteifEB?{Zm2Em7jub|85`z*L{2Y zXUB~Y_v1N-Y&}`^Wc-u8&k{(8cUiqA|98c<*zRrS;qCVO`_KRJ!@nDQ6<9#6Gu);~ zgpKe401N>DgnHt2SCO2b-qGFdS=rt2LjbD^XBvw88e|X!altSv@dG%98~{ZFgbjW1 z@9BiXxD+yHCpdpbwcC7rBrJ?4!P!MHS8}(G&c#_5aCqjJ>*(i>nxo+6+o6#nqJ%uc zgMgbJd8AZ@^P&f`g3(n}v=;hMeR;r2)9=GEOpoBZgrR7OKWYMysw#vM01ms_x$Y|NbG3u+>-@ zfE8WU6nMb`GXXJrM5c%c5!V?&t61ZF38b_0;cL@b6jgpQf4MX;-`JOxO8#=NrBI=Yto%(olE4`J|%{cBHbebn_ce zzkAk<7r)Ht8)mxpeS8{H>-n3yKk{uEee|dQ=F{Qte)X}l9(&l4pFGfOmU`N6M$p{} zcuUth@`-n!`kJnU^*TCg_UnJYzw_Vyo<7sQ|4cV``k0%q_uc_h`;eFU>Y-O z<%n0_?LQ}d^L*B$zce-O_@e7x>APExebj*yJmPnLc0;XQ?NX~7)VDl&(d&M?-1kqD zx_2x4WldK6A9vd6*8j*Co%!$wz25tyf4^yH`HerC^Ye7jr3*iPt7cEV&ng;D_4BE- z-Dt$#-z?O3*9txLc|o1$qrbYMVfDDfT>IuRi!=RZ%HJ9E-tYc%`4gYBp6_>5g>bxxc=cPMPMM_T|x+{&cn{zct|`58~$;m^b_CU{U{j)v+ym`tGDqKVIt56EA-3 zn8SV4LVmN=`>wO2@119_y=lhf|IYgNqx;=>r@Hp#H$G>_Pk(*zTA%stnVa4Du{oc7 z*X%mI-#D=CAFI9e&u#WK2kvQidh=qhUCgY$SGV8C{m^|+G}OA^JME0C9r`-04;ky< zjD5}SiTTX~-n6qndHtf*E(Wkui)sYT;p%1;h z@=I5`?dE#og&8J$u4PHS@??hgzu9x4=icmrF8epnX@2B%MyT65=4*fdo%4V9JYZD% zUfay^Odmb}Gg?1sTxOuRoE+85A-sJZLdS{uwh14-(q#wH|9jqTPrvNzgU_$M?4_@d z)%Nj2T|Z&j#Ws9>xn+mC?9`uM_6`b|k4kr|RyErGUxcFxEO5@IcqnF=uYGD^_H0U} zTo?XDN1EyeeMhHy&AwrlSsPDH{_d)()A#%VC;+*qf1DmEP#e9XrD|GV1hkJ|)c&P{ z2|YgV*BT}doP6ZZeEsOq2d~+?y@RHhOyfKH0XWnI^@AUGFb2k}_4RYP&PwMt=cai7 zA`>M{!9F#m|J(XR@GtwNwUPD^I}wy2Gz1t3noocepA#_s&M&1CQHjRH;6m@=b}&0( zl<+!aouEmOnJ}G4Pwd?7uteoH?032wVdg4MeG^@YrwN!e=U>_rkP}Q3ONp}yq(s*f zXA?Z_#qXrPj-8qbr9{^P>xnK2nMBhB%-Zv=U8N2zt5)x8yaN@x=jPsghq0xoQ+*t;6JiokCtwny zCmZmNpR2lyx33dnS=p&cfSItJm`^nKN3gqW2g3S}jiSwoIYiig(|FhINI~VD=^nF7 z>H7IdOjDI+Qieu&81$>YINNZN0dIQPwNx= zL^MG>!JbG@L?Jfo?x4BT?ajo`#GC})2#_X}C-a2WwC&FDPIRJII~zPKhV+6ZC|1BJYkzWzl8f zGyglXfLw)Bq)5A1nhc&iO*l`YC!-U^edjOD#Lq-z1Va;o64EEyiTi{!K|Il(fWv2h z=4wMaW%psUG}#WF&`w|@wx5_MP!n(oy+q?g)ME7BbYbzm&&;K|U6Xh}0ZdEzr8Ln$ z@lOOM0$-ce9k<)s3DN}Xgfjy5iPl}$mqF}}VRJ9Mb4qt$TNf&Bc&qiqcXQ8rqp{c8 z1K}x9!EHU6;e?&P8uMlv-k+DeQz@;a8rlid0(mfewrTKo#}zpsTvVpgfbIw5kF6D-*#|B)1vNic7iB~G$*_fQBQF9 zLBEtIEE94y6EyESx0t!zeXK8+ z_t0wXyj|B_oL94&ZkX-oi7xy@=8(5)rQOa>*eBcx)d{2v{&pyB$4V^D4(dMJm%H6P zsQNd&9k*K(m5I*pFpg01TJ-oR@0=!iC$|&Y3HpRIfheI*B4k886Ezd36XOYXUAz-c ziNfd$dOTTw&wZKA4X!fkb@2SP+Dt&+d(*5p`|aa)@Y?yk&{V602V!_b1Tzz;6EF!d6VVf$ zear$p2dUP_#w04Gq1 z)`Z?7@d0@pKRQpLComIi)9ttknLgyg&f1QhXin%S>L--l4o<))>=S$wO^L-sY`=@P2KYq#FzvAeS++r zox})uBTAoXC+!o{1pEX%A)WBT8~##Eh(`1}k&f_oVmo1-XiwB968xE;N!rj?xpzL< z!uCTVWQ0zMp7^L=I)wS6?_z0Tb^<8`P{hy@Vkcq{PM+|=bNo`BXioem;EC4nHn|j& zz$npn!aKp5pqgNxc;J~(!J9q$z21JL?X^-4Z+ZvvpdLNIIjTqXfWX|d-~G^Gwmz+u z#(b)S_q){RJb$}!iuBs%-pjbN;`@#_J=T1pQSXZ`xg*q{!}0TRMC6zD)~Ddy!PDoE2+)+4u*A3yL9AI_`o4Mx_|n* z<07x=$FcnJgxeE+kCf9UMT{N9I_eF-KMuEBC~x$*d&-fU{zvjP-nrnJ<3OI2KEpLx zlU*8bDjqxb(-9X2(@o(teLqp)8&wc;b zbb%-UhRBUlG-h@skyLe#BB}oy94|W6WO5EXhpj`^nVdyw%~?}grNKO+op+Yb`mU?Y z)F8Eb-kzd4q`uh$;2p*h5~P>5R$Njs;^x%L1fJFI(Gpa$@b^TRDSvG#%S2eVK6!U+ zYCShwY=Gw9m!S)5%r3Se(A3Rdco1ZA7R19sx!<-%+S;J^V|TsW<-P%w|IM8p^RhMV z%^VV`RzLLEeQ6B#!R4+C0q35Um#j0*=dJAW@VaF)IQsyS7g-m;>*K6On`4-xHqqO|Vs zCF73fj2+m$-owtPUUO}B=J&}1_vbv!%xtYr)tq4f2f2QK9}25wPN~M!L^Z>Fuwc4~ zxzI2)v~1F_xs(U6X57p&BNCF7UL^`2S$k_ezLz^K7c^M;{$jkDfvI?HTgs?NGzn3K z6ctq_0RCh|^a9*3vY66=?MtWn?ClIee{=bE@qdXynahM#U}Xc&w6(RNk(j9*r3oGm zuM8hClW*R}m^s5kfXgQbmFgs(AuS7 zmUM>a_MX~-@AP(jTzmLpg2K=;&5sQyuq`{&AWx#$nV;tj(&z5qZtRP1%;hAE%8iVc zbCFgMuZ-t)EP3iQ+(a}dsb7uw^&N0xr5@M>c-${``e4@^V_y@Y%a~s=A2hJ#ijzDO zEprFN(ek+l`_8*|r;9f7K;$`J42>>S*(rNM3{POduj=T-LG$9Kv^E7&30alr86#SO zx+HqF=e1h&EqphOFHI!oG}TiUK6-z|X;1 zO?sc*v^XN16W!k3@LvERAYv*20000000aO_0K2=pySux)ySux)ySux)ySux)ySux) zyA8OQl>q>inam6*lnPlUh)BXgC51^9c0gHRdn@D=Hb49)q!!kjAixOeioAsR=YZ4(2mk;80A>L1A^yIv&L)d|K6O1iW>9;t+fw@VPFfH_ ziCc$r%b3j(?1IMalP+9a3D!trMNj(1`_qLtbj0p%JI>Dh=TK@(h|j|AiUhvPK=a00 zEr*Wr4Y#Peq=Azz8&EShe|lo=Qe09GoNjsf&$(fb3GCyv6S6scXl3MUf+s`9(03p1 zrxCH$OjBPji3E|gw5Z!A7ebjAwmEe3)UTzv7t6c9a5?blbj0kF9z5LF9{au_HjB5D zqFP(+9`DwmgEmYwSi18qgTy9;P$Tl#TBDKe3;$e224yF#W*Zz3mx*A!GsfN`Hj9ZRVGlR9DeVwSsiZj9$u7HvaHr6^e1C`^54pL% zv!`I(8!BC;Y+9>?9{b{-u6Xa%BQxp46j?c=UDrfBL)%+kB&j>TsOcLQIj|{>H!Y6l zj-i@p$ei_{ju_i`)V58@3k0~9O^D%|GBiPm2d%LSXy^em5=}LO5a5Qr61j;%MhKZN z?>ievi7kIDysDcDT6Rf>T9TQ()w8c5PFaQ4?kX>(&qG_M6Zt$6*>)asyK$6*`cD!D2kzB;ELzWUseznDIQjC{t-RhRRQw)px&<4WAE;%rS& z(Y&{|hT*!C;1Ot|YGN3Tx6+82SaT`jXwN=>#)LXI;I-vrv_1_T1eAoLppJ_Xx2R7^ zRWp|#yCWM?UxH++Sx)<%8{P8McLh&z+OF{@vN?9%N-S2yjeZm}DUxildKvWfl})hq zk(=DhDjcCY?Q-ThpS>5Z$1*!(y>wA~tmyQGf6Lm3&x}T43CC|c5~{XO*Zn-bi^(WG zu2fMLGh0tAJyk?*nYTqrkkt)_=kga^jj0PsW@lwUY<1}PEBb6}9_MxDcYQ63)8#^s zuC)fepHBxOdMMN!=InwvUSXSqoIP)tjnKvzFtRP!UFy9j+r4MA)I<|O1h#&))$+Lk za;H)n{w{iCO!Pdkp4_o5z#9h1acTej3SW&ZS+{q-(v$c1y!CW9jYhVJ2c&9{v< z=^h31Vcao>0hJD zr=!i(IA%K>=`h8^%Irah^ZueOz8|@y8laPd-TpWRRbc+^5MN_9wNz_zcGqzManMm~ zj_q2Ra=2X9`L$tTlV@7q-IG+eB(=cyhIES!Hk09J9~!9Ak&*u?x|JqG@dSmXF%2R-;!y*(S=k5G4IVX38iYg`nD!0_cs1d^*i`^%C-^x}o4!2haV z*{#IbQiRZ2l72cQjri?Q2)fU}577jbq&()|ao6_&tGHy~`NLM`-$YL_uu+0sW>|1A z$`ygt43hB=b|G&WO?M$TJbWi+Pei#Pm$Ms=x}P*pkw;;J+Hau=YZkQ+ z5h#U6um4%VTSA*01qVh>ac%L0v&%c587^_v z2ND#*R5_pgBFp)ay5H+y*=VOxpu@*7ns4!rG7^S(n~Rv7B|!mTWXRp71F`R+4X8Os zQRDMi`{K#c@w6#2Rfybl6gp~Q`G!8Ug7AFYS5bW10Fh`N(BcR^`iSyMHX#M%hq+oG zcia-Sh`6Bs6FQ&hj^&Ry^=3f-A*?`C(FhI6Jy7wLE0WRo1P&0ITlKd~0)Z7Tq^NX3 zVAv%eH_@6*&I00V7m|$pWhjDwx#$8%Qw-;Rb(F@T*Nj*<{ye#ElajKh0E)1+T;)rU z-4`)rtT@ISRF}`^{J^}NknVE#M7F&|tW8Yk`1xx__Xu$=`UjSVnxi#{sf;i;!NYZC zyJ`t_=v|rNKKe4X^teL2kH|2E?TNasMxM~kK2#sd^SzQ3vpp9~O%S6?|%)3aX%j0|aU!`~3fcCp3cx?p@aq5GUd#~xTNJpEJ0+ch4Y?OMveI7(F) z7RkwlMY$%Uo!R<`qtkL(IO2Ac;gq$yx4u)I(g8)j2^RZ32z~qG4hZh9msviVQvECA z>%MyjCrkjHGv?Zw+1Mlzw(<95WRr$g$K^F(f4w@RKz)ctt`8-< z{E03ecJT@yndnja67E?Ol5@e@bSDG-IP@434u7g5juGfqh8(6~xCoqci%n_94(39o zH7J$k(t#f~4&w!W2iX|wmo#nhs1wt_6m>569$kU(Z{$*Yx_LNKEdzHPF9$-R=4P~W2$Du?gH;sE$FkCB3m`PLAjTOT)_#RjBLg&e zfRU6>jK6>h9$~g<2#C*&#@iq_#Kyhokr|GJN&ZHacY)vWm?g}hNO%ds&&}cZayKh$ zlTfQvsAzHHM{8ku0nr;zR3aRM8uua#sTjxsE2KmmlD;J@1n|DK#{ zR*V|&q%-)HyTgu?t9yS#n|P^ombFc}1yrs5oi;p0n9v(!0JX6SiuX?1dInAAC`V$R&e%y7(TcM%#N&~f?fWL{(Rhvg`AC&EIg$A$`K zgF3l}W*%M_+vG70%~kTJ=P>>;MOr_)IIjb$Yw?NliQQpg+Eh+ zJwWKq^@WP`m>fN7)Yf)`^9S~19UMhNA%Af}vlu_(gRd*akMe@~_!%G1k1<4}QgM5M zW^9Aa(GI7j#t+dhB$5ey&MceleGiIcJriCtg1!p#U)~$#j%Q0fk?G7nooZVCHx@*q z195@G?2RcebD)Bvs#=8Tc;^>zhKZ2erpFY_skPfi${Yu!n8Y)Lyr%kF|GNNv zVb5ePRtTo+ryYms?KTWdiCk`_<@MeCMmrkn+-zxhy3q%n`7mxK;*r`myZ@!4O3 z4Z;aOmE=M$9X1gTUr&0#YEwz|#%G(s%rKvg7!z2CCb*ogPm^#jjgA?~!qI3+K5447 zoycSDTTTXjjUH`S6cO`lDmjlZFQ@I;uvT`JoH2(Fw3f8niLyA|gBh#2)Z<^f!j%@J zWH^$i%X1skb=p^c^W6I*lZg&~v}F``B#|*x0!lR2sK?47lSc@plA|S8_-&u+4NXv_ zG-5%{$5u7a5&^yl2hOJ)u7^Yjpp8_fpl5E)}d^(E<9S`TFWWCw-1A^W(&SMvK_GcH5)|7O z@^{`#+Wa&^isT?H9}p@^7?iUrfbEVyQi~CyKcJ*WFR_pdBmyG8zDq&)3`%T;mZaaI zG9GyeC!Au4-XYpS%EA+2cz&3NqoIvNS!1U%gKGDXgV1WPlEJ;8hsI$_`C&i$t}vks zZex3ee)VFJ?Ri<8|MURRJ+3+qgbHTb-uYbN>O=AQSwvJF6TR{J#kazgpRT_`yh z2z6ogjLC5krU<&uWs2j-@0Jct1hbAMe{Rm7z+c3DSNMEyILLLlFX$fJSiR72hi!=} z1=6%{aE7~eiBR@r(^l?G_Xk7u@QD{CT#lp`*VOp!(LH6-Ed+gPQ2^F~j{cT811PAE z@9kHBFv&MQxa%nZjn6+KqOUEH;99*vqu2|eQ8FffD{h@;MP&`O>GV;I^v^x&kzFt^ z&-TNekMDTMfjV3fUXI*@yELZgcOYzLG3-0~_y)??;l(3t!Oy5jQco-Z zxN;hNn%JW?+*{@*B;a9Pk!}W34XxQdlIlKn9{4YJWo&XonK$_5x}UW6!uZo`Okbg+ zAX}B8z&NI{ixm~%28fzd=0W)y;}#|g2!sS#CNF-g1smuG3OM$e@?j8an)R`C$K!)f zvR)2xELLZsvkv+d8=MJtb>fHrn|hl}WrTS&7~ZNMshQuZ=t#d$_h6MHHOQLk5%j;8 zlh5lnNeIxCS_8KW5+VhuN^MBbbkC{dm8#cMGa2RkI|Ax_ct(6Lga$|if`KdqJ2w-MKg2Vrb+&6~25+g&~3aE7lHv88z79?M>E0GRO?NGg|8IW=s7H5^& z1Uzg!F^94YgH;W*Q5U=65RMt9s=Ki-pVj+Q zN74oMyrxLLdRPaz+C2BedMPBh#wmL|+aN)>>BU7qXF;9OIuLi3WE-^GFf}|ZMouPr z7Ht8UIBDbRd+P&I=pK`2{0AtecA`Y~=%68E7vTyCNt9_+S58X#9MiBd#gk9(dN95ts0+t3f^ zkg25?QVSEF7-%v9)1)|N7pzMTm=D(PyZJB=N!lwP7@*l?m;-mzc%3x=8GC8bQ4Q2< ziaARsAG_HFe31s8oSmbgPnCbejCdhPCS^ut?_GSCI&U+0mQSilyaIwxF7kI=e1;{r zU`sPsAoBHQ>(tbB+N_B*xWy$QKmCqcQ_tFWd1-srvN;abE(G99e!ujZH0!r;X(nMW zviXcn76h(~8(w{9vl5@klVfCbepQ`5BII|FQZm@~9USIT3{grQHk7#aV(B2n4r$!I zqCrxQoo&LrMqg!GkrOUKPHUy0J@bkY)qb+s=WaLE+FSYVzGw4f0wk%rhh$0E$MBZ&PIOz?Kqe2 zUbCTa-xd1YUz7@b*mTHnJtYM(CV(p&HCH%{aX#rAR#44h2&kA)!y2F5$J1o!6q8L8 zycZsr;knobDkzgFSA9j}y`_X=a(CWtl=X5>U=C~6-;QWDm`EEMEul)KCI7(J}N3H zoIwIdthVmxl@?Fu1V>!{%ai-|UgM~#s~Wn<3R_waVTA2V6?B~y->VB7i=f1RUH$4Q z-aqJ~fy@?lb*9W^l`K={k}d;rFidt3bA#FRW@X543RwVZ=65S=#yvKM_az}(@)jvkzENWJl;Ui0(syj<;Y zb3{NlpWItJ_Q?<>wE!tiDovF$?~NuN%f~lNE0&Rd^a4ily^{4gJ8ow6a63m0%MiS) z89u+7$=oK${f7~LE5gw4lH^{zKYSmZEAd}HbpV=iU=|Li5~SzVk6vkLj7kc#mb``x zdE`KQ0WoE-(Okv1v)EhP>|GFww zOR)bxLM=1IQHV;?c`HBi5$L;#as#{FE@ygXoLKCG$JeMN(}&2)o)Z{iD~ycBsCMw{ zmDSz?AnIvlcjbry;mSYzr84l}BQt9T-R zC(|M(Du9YDh7Ao8e$hX6H86uCAGOJ83H3CaB+ql$RcU}ecx|(CTd0~1Z$W23^x_(=B^#x*i%0jl~p&fPq-9X_kP!{`!ZU7*>%7)+%o_cYc3m`uaooC z9IrHV@p7aoP%@;jend`Ix9Icl-YI|*LrDLU(tVM%XC!m!Ok&K6O;=ej)#q~&+uwyZ zU$r z@WwXpl!L>t>JERHJUnyp)*SMuxo0EqS)$zkD$Qz*z;{nbZsjS}2Aw`ZM_>lpB3|-4 zNbFZtq1iI(ySTDCGrp(~>fy8rKF~8sn%3Aih0}AeC>=WbxN)sac!nC)D!gZ!^}Pm& zX^c4|$^dG=M0Po>EgUW5V>>}^Ux_7~aaWmxY3R+AKl`c+AY&DTxgv0+_RMxsA~00_ zvN|0C+fVo}JK2pQOW@*hFx?)k+@5IJy-LxN)LYxU9Z@z(tH)sEje2utf`4M~1cf`P8Sf&@~6JP`Zod3$^U zzLUR3eVZCEPmVqjBiOHM{J=64zl%OcZ{p+LUnhlD_~=8$k|>u8K%AfjeHIB#_(Z{1JTrajy0_wFoNA6`=b)hnmuT z#;`Ub@K*TVQ7H#&wV_o+za-icF4~NaPdm6aW3NP3T+XCC4ZcadesQk3$D5^qBOqKU z*8CR41!RO&za1w+0yfpN&>(qtt`Cg25TTY)eiS~s>)D@xW9uJN2-K0s3o`OW$t*G} z`{@7TV8-OsWijAOolMSFHFD@9At^ZdN?IBbv%(4Wzq+%(`Cxs~2Q7KD4h7J2_*r#*ysW^GBMJMTPuF?y{mVDg?y@D^FF7RinxuR#-c_ZuR=$aZ^$yqC3as28U+S@Sj&H^ z<#$tuy(S#N2|N~+@=ER}ODn`W2}e2r*nooDCt4KPXZl$=5U@{HZ3*@r$~(AvyjyOJ zh@Fn6ejRle{2)=Totz8xUUO>lI%h?6rdKJQDdPbt9d$BOULRH|oksE=$jH=dZJSLL z^F_AX;DxSSV=x|klp*p>N%D``es#X(jr>M0`7_WD-wqKAzA=62(>~c8DC|KSXPzrS zWd6;XjfDj`N6Q4@d1Mme+kKoAQH>-;G zhqiW^bNGb4e^i<%>iEnMcNha49??iEe-LTdhqWDtTl?VMrPRF-oUU=ia911B)jD{l*(v1is*vIox0{_Y?7`2q0m_bQxAj7?Qd?ORW1z`gpa9Rxb;WYJW zJ=DmZ@274r`GfPg$Qm(Hwp*1GvRo%f$YLdML<7()f;@iznU*Z(1Z z2&7ts@pF?*;MNfVHhw-AO1GS;WPeiZ$iy*g3jt0B_hDp*K6@%Qj_|BnxbnRZQhr*} z6^A~b^87R($>u{ZC%k?ha!zk*{NXHIC&PF@W2k9(7ah0q<9$uc*v@tt2X@8F7`8-E80d&sPit{gHD)|afke+0V-4+hlcC^;byy!#HX;un=XCnHonJ{hNdCSLimYW!}YW*R<~c^(Iz z)y0G(%D2JU93x^$d!+eVrsZWKi?QSfT^bcjGD>kPSUUJx^IXh}-_6~U#>fK>Vb#IW zE3HGjsNFe}(6y`Y%C}rTuR6Ch%@RtVC?u!CC%+&grC5FHxnK+6nqXXo<7g5bBd+!v zsQoTp9|putv^*+I6rL&*>i+JFX1i`MtL&FYH|>dys%j&>s;_J`te4tEcm+q$G^Zro zpH6!(9w?a(P(aeLvAJFBP?Fwfs!DaAV#Wbrxp7WB1;B}eu(_yeO*LRHcx|9Wy>S3CM9RM zqyhX9NUN%D5U?V;Qy!*d#QX53$w!Ca;>PcnHAmKr;M{bz&OEu+aLbH4h85_(x8gxbD(FaWYRVB71({BbGwn zW3Q>>`Vq(~{T2iyA0$gp@h>CXHMJ^yB$=H=?<*FNqqtCx3HHk-BW&rvEWEkrOR=xO z&VW5A%3yl?URv(fZ}I{m^K!vo{V*c&+TlIU-`ri$SAG!Q!@KUN#-kj5zt~R6BT@h@ zwQB}Zu%``9z2iWvf2spPL)JQ-R0U;`by6xS0-)tkEpLK)f|$x~AEN0FibB~(>$EhJ zcM;S=-tu~(0}1a-D5_bzy#6D%?*Qf2AIYX6c#3Q?)lzi~t+^K%10dJ{zpHRp`zmua zG?~(XypBZs@`E5WopbCQum9)p3~CTzhM7WSznk@3Kp z16N9_w14-z1v21%7V;|K=4O9|dp#uJ6?9-cnJfuSHs)Z&TW83SH#$x;tGSZ9YpfXa z<|54eY%72B8yGM8Dou{u;ZMvsQJC76Q?rLQjYJlpRlV$D|4z&rq_BHXM?3=f^vM$- z3@8oM_*E9NutX;P+F-x&$TDtpLS^~}il zS%#)2M5MN(X42~CKnQH(w5$oW&XVm2+oW*zif<|c1XidK!^)x*^;)A5Ze`_s)E8x^ z8g9hC0w4ks;oihEI3%;KN8|REkwllu>0yzCG^R7I?tSOOcF5m5GV0C_sl{aujceQ+ zBPLEr2M)cNYYI8Wkw&jy+j@UuIW(tFLLP+)W6_TMxLOhTLvavRd&FT;uL#HilhIya>@Cnw3gkK;B9R0gMb7(d3OKX;?D(i z)nsmB$Xb=%wmkiFHt*m0Poi*8W%ggnr%g@m*}hO-^dKC&r&9V`3_2TX>iMl{xyU~u^wmR0?$1f;kB&D;(hjwmY$n4G>nS(A-wX&er z#lw;@@2Y#(3)rhvS0mV5J3XYidLa4adafXjvl%>?UN6vqF$*sN15r)_1xp}IRk`%| z(#8vh8CeVtGF!y>H@{SwIJB}>7az|48DY5FKusc-2v{A_wq;!IxaKGyCS&LjYVMYZ zjIhaIh@b=jL_oX0_w#Vr=WBZX(}>ooe#S5%@}|?LWY_NTw~=c@gxm(nURlR-#X!5AYQE_NMw z(^RUQa_?^CUTSF*wmHf>Q}x4^{ni@#nXxn{) zBZG>t{}ML{SH=fhRviHTkxAEFS2A{VY2+sP<`lyN@U-`w#`-e{5k=l+aLA4iOn*;~ zL~(fnvlR~uZ~xkVp44TN5@qki&UO$m5F?NMC~Dxk@JAWkoDFmlDh;k4MLL$i1tz0Qs)6Nbh83zVt@yHELwwihRqj1a7Zj7p4WD-|N5VF;0}@4r31F1ADk~@X+C1@H*Lk>EUYagr zq>grJi3k0s;tKRut*jG!@v&(PH*H~li)x?1T%;5_s7o&(m2NRYc+q+FE?c4%#x2ZmH50mTo`&q=ri}9ruON7}Ic> zJLIN3I0i9}Q6S=&7}ytBVu{C;Xp_#Xh5m>i-0Y=SP~D<#3!v`g8d|MGSLU zMw94ufgmI>gjTwZ4!)-pB>(VJcIt$`A$ZmDxb7fBI-<&K5XloDIdIO&Z;F~oRxwoTT$|>_@XmHBiK9`kUL`ZN?tAk zg#YxIXFEdf_954Y)}#{UuTFkJyGn5CE0S{ySFTMIz=cSlWB>`xVB%CMYpLLF|B|co zxLiPU*mxKfOk?PSZyj@fQC_$6*y@3;x73UpK0m6ze4Mz2t5?+t{Lhy|2Xf%qwDJNJ zrM23JzO#~(PRKprtKQjbr#%JaTBFyVtiY0U!+4OV7t74>_YQ!44<`4j1HHWR*Crbqh{tpM}+Pqpi>yE?h+POko2>n^pe_)0Q{elx3D+N ziEfv{gOgTHsySKdA;QN%dE80(Wmq~BH^mI0sAoUW!QdQ{rfco_%AVxo{V+Qf0(%7N zoB$73bnUn+Xb{>mh6=<04l(#yfx;K5EOKnSfHZ}65F?~AcSO1?ZsL@Fz-LuFqx^#F z+#FdT@C5F_th&YEBfsRA`l$*1--#(tyFPGpCiontS(&@_AptXd6e!l13Bi z+V`?>np{Ze8BmwK061K|_j!kG>g#4V^H+;Yi$v+v3VeSJFoR@(ySRs;3UBMiYWUIB zr0t`iIqT*FamW(?_un=5tsQc6z|e*)Kv{gp6s<%6mS)#!aTt7J^J|uMPcvxLX>RVw zX5YbQbj#;!<_)=K z32_dfKo0@oo|IZbS~|Y)M%HYCuD!x0=u28R%{1Av})XIT*~}B9ixd2aESPiq7@>;>&NVifwj*PA@91H>)=4O@WAO;i^Dv6*{6S{+ zM9D{)0cbyd>544HsHQY#QH6GKvQp`Pk6Efj0==4Yw@G15fecc)h2+CO2z)t((* zk{5lRJxFW*%K!hk2_G~gqZU@MWy6e$TH(6Or+-bQ7>onBZu+T6myKMb5YHMXGw|tv zuV(qB?@AGu>>-A(s(u&jX1(HBXBY6zc+B!-_Kiw}?v~IV$i^h^t&ja&UgNB(Wyur`n5zpb}8K6hTB}a+O&g!Ajzn4Xv>8K1W;zWEd3k7XxX!T%>+tcWV`M z4A;~%h0k;OgCuu(I(7~%qlPis&feP!JF4IWE+z`iu@Nh5SXjTk#p$k{dP z86#qs%37yi;w`-LFX8jSbvC`~gEI->KmI2!hA>BRiiuJ%2Tc{1F8Z!t*SR&EW8{Sy}Ai4MH4-671wuRv$hxv zWss|v3~)3Vk#8L25iVhWJ>5fAU4;1io^o+e4CeaRQ{mMyQ^ae>ewIdD+mQa>iM(TV z5^i&T9NcqB()txFT#gQH@8ileZXa7hEt*dw4l~?JnDT_S$Yw>N)u?c{uW(Hm7PCE`pa25s4j1;SUdIe@?3B!zxn-IpG z%<-3THE1_eAfQLPNBT16Amf?3{DTx%FPZWQnW4yp)001r@D4_;kVIdAW7C8*W~@}2 zF@sE*z3I%SzKXegevl43M;a&3!aEFeq1t+os1)y93;o+be6(y6bZsng{-K{`F9bz)$REJez z#T}AGEl)0xstjcZ7Bh;Np#;V=#cz)!ow8$&t7FD$f+!;vf5p*n#DGI!elThHEZLYr zlAL(WATpUx57?~8fZ(X4G{NPfRU~AU9U@uy3c}V|qUPv{W}CNt=bmM_98=HM9&N)( zFCG|5_jQt0f(DB~V)4MS!!sYzib||I` z=pgtLwtuG}96lPriyozUH~$nlM?%k_jE#VD*}-o8#lRy{6~C6LjB-;P9mFq^Pb;9Z+IwLqb<0xynE| zrHBk+rI)*5_y!Rx;zp9x+_HrM+mX3F^XD>w9Ae56B;oB2Gng^r3oRl^Z_b4OOZqh! z$ikSw&C?&ihuN8cLT2#?;RYm_Vi@!^nq84}oCxFn@}W6t@B|0quw%(}#EO6dYn9)j z^>x2j0Y4*F;zBIalrU#eV+>Asjeuq71O$f%<0nuyls?HOYKp!{9OI@=sSP9JPC1@B zvnX!@wm|UF=*x`=QQ4#{v^3r49q^$>4UYZZ-?Ol|ax65|y~+Al|b3$Z`Zi!L|2uJAQi$`^H_ zAO3Qymh>RJn4wH6wW$#!8kYrCD<6?a8QQf&>(Sm1Z*U+w4O-l^2F})Q5S|oae}R|A zeC*Sq9)2E@N5W7w?IHD2&9O{B7^e&55d;W^(qxQ6c?%fYG9X6 zf?a|JV0G{1+G4X(t8<-4ZuHzXtE0h6gsTGo1;GIV)x?}Z64i2VR3TBBg);&0E3uaj zk2u%T@U!MNlx^HpWd4W^k)8uyZB@D%0MI#PqJt`%&dtCdp0O# zvv0UIoRDO&xbAfM05I*j*juS%18W2hd!SJ>d#mVM*)UGf2T17Z*08$*E0(lgj zzf!;T`Y9WQwf4ey0$9AUdD@n>Y_SdwbYRULhe2?l-w)EA^8S-A90XYU@HKh3DwLbh(I;BW-xm`;cW~^0-S+R0!6lq%#z^_G*9;@h27N>c$ zcWUz)Nf>ieDMI$9Z$=Wslzp4LO{H_BB=3v*-)kA<*cmKT{=;e$Tm+DszB0*hWo9`1RBv2gA%pM{Dyh{wH^RAF26wAn_v?z@ag})!tp_4|eVvF~%!E@&(ZI9OwWS=TjVDksP)!ROUgw-MW4Vs*oY; zZ;UX5qc;V(^>(RHlAwec`<~R)uAtQ=AzK11?#)!*s;*W3^=diCdD8{u!LLAi)^H@r zk{Lu7O@mi7N4fKjH3;f+k5W0=9G#JWSAPKn@!fXl?(wpPzn5~57xv5q zh_%qj!DBqlZh@8N~f0A2$zWa4>^0sFTz0if~|m}{#^@zSE^>A@&- zc;xEx1x~~H@CWlk7_SpeSC6F1nq{CLR|uV_Ow2b>`sK&gl%C*TxW+h+6Xnz1Uoo4% zsQ}^cL?(K$&VyNa%H*mk_kPx#fkDT|T|>P&m%V(gB?KpBl_u=(QJ5MA)D2K(z~$tD z%+_X$GH0jedRi+SMZg;9PcxR)bE;G8p4B9cEG=s%2Z?TzYJRg$BO)#YK05@; z?=!KxC3*&mmin*yAS2N|ED~4+^|!qsG7bOzzl8|8gk;Gi0Sz|75mi9GM5iWGYzAGz`>U;4f6y>b}HQRNSqzK2FNQPffq}V|5PS z#1qk!2=UoGU?!jRXZugkV%Ad@W;`Y#d(4a8N7ju>M;ThNaG${dU4RcZ<5~P2Np@!V zhfg|8beFDhfUSXLjGI@8eaqB5sXr+EG^(0fX1ow{BS!6YU0!nhw;DNZhYE!*P|d;i zGD)XXAP)hZ$elSKR|MGr9~mzlf_EiuLmwc9dK5M%W`0UGU(AD-7Kt;YJ_K4x$MgPS zS&dKKb%EUN#&xPkOXV?$aVuTTWnn#E8(9Ua^op{TaC1YpZ0GyNx$4BZcrM(Bguk@6 z#sJZA`6(y>h)ATDums$_$8*_fqGE4NItS;jseRm@%4VXjYp-j*Dtmj{@2*1{YsqZ7~H3K3Oh}5W`3jyrnQF$xs4G zX-yNr0Kw|gIm&-R{>9ohV_2#KyN%9PwLcl((D=sF4@Zt67I~^t(*~~xFoGTM5d{8> z9yw%5C02=!>UIpl@;b7^dl>(cxQrA$IJ=Cl2Y|7nt7uy1OAyt>hGR3X4~&qwA=$yS z4w|zrj;6$MigMjYwb-aluG~|%NsjDNUp-E4ei^eZQKEOY*yYBq;=MyTZZSlg6^k41 zDcqkY6v=XIi%_!A{h@7KQ952uI4wO?DhM6k6O$(N911-!1F}5}bY+QkqX7^yQ$8or z%%_13u1clRPvE)^h<1xBw9AX(u_NWCeov?Z32v zlnF%bMB#_8(O|A_Vk~D`u~!y5{ei}LqEbL)? z{{@SIh&TU`#&5};-1#I7Prg0%js2m7kVgOH#P7ODLf7xIFV zoQ4}ts>*Na#AQ~gir3vdAVD>*0$j)w)P5#?j3Q|(KiQ;~=V~y-mUQeZk7#2t)Kxw? z>#SYKcy|kjVEX)4x2J2wdsYtTvN#U-5uE51L0b zPcj3Ko`_*lbXmIoY-YsEENwu#j}XG1&ovP4yqpCm-0mCwt9+;$q27o(f_)bJ2&2zD z&fICTt`rb_I*lh1@Z@l8R19d)Hiz2MMkHK+nRH{$z##t8Yv;||F`C-32Mk^Oa>8H+ z79@Qvo8;0C_!|`$xpC2peOd2Yf#8W&SCZ+Gy(*URf9i3RTf4MJOSnveNgVe0Q{O_r zT)zC*SLKv#2NyG61nVbsz;%sAF*aViJAQJ%U{g%1wu;mM0Uzd3044--P1r0_;P{!3>4g`Xeix-x9G@8+rwJIr#nBp^HBP-p#$C8Q7?(2UI zPD-h9!7T}VQ#H00eB#rlH?J<)q33LSe%omvM{z-)LM-z~wNs=&CLWhUk%~kCx(~oA z?_E}pe%Hs{c33AXH=3G%cUKtYhCY)wfbyJ@O)rN>v0)DQk9jM*pD*~oKTpU1%>2{_ z-LR&)4bJH~#K)!ff*(tqz19*Rv|z8e(sVmKCkX&v0U4-k%h?|f-dp06gkg~o{T2dd zL0O|ZUwn0nNVa$!cQIP+1wG&B?7uMS+i#!BSkZ`AT;I~+C{s6xth%!VB3l0J7o0O-}}Gy~aaG{RTR zTgi@Ey~3=kW@#(vo4(!k3s;=YBzY+GBCNj2nii~@*`iB_emu`7Ul3cQ^T_VjKVC8S z$M1ra{->2Y8JiFP4F3CH=Y%OJ$D3B0rgDXW`D~8_Ju9YB)VAczb3CzuZ>qrH@@m`;;syToby|W1iUJ#+i~sK~;e+vJUjXFb5D8@3jQA$_V=KxSwBS@P z{{%(M1QZ+d(9peayiQ3|IK3^dRIyJj2YRAn@BKsP41+H9`j z@k!f_l-hC%oSMS=w+7h9h_ngAY2@Q-`sqG_awI>MCjCw93YByqLl zWh^EcAyc+)un?~&!bQ3pPNqhg&U6f@(jo28Fqz%XTh|r;Y_lsB4wzz_$P&?ffLZ~j zdhbcE=}WlLmrI&nxU6?NA?^GQJ{{j-4 zC^C_r2^Bf*0(++IY-y>jqEX++9V>ov6;tZtbB+a{eHqEdu$G=syitN4hOv}V2Jl7v zov=VkS7Xf0FmQ-qlXrWY1n+Z=6fP$J35Kl=Cy@QDQY9Sf7n`hRm|xT=+K)QAa&eqq z#;W}Z#kBynxpm!j4Cstdr2(aJVhwe9!9>#UrNk#?p+nltP1b5qSjlU@Y&0?c0nJa- zVnJ}La}Sll*6m~I*Q51d3?oaQy97_0v{#7EOi1AWQ!7>#luTS3~tlZTP>mEX3c|k%b zoCs^1!Rl5FhFqELFu}=(Px}onAHQdq>2dMQyr#fdHNbG8JIb$K2-$?aUDv z7QFXG&Jwl>&+qlTCR5+gXq=uh8AT4@)Y?Bg1=VPTTK5VwEblL08DihWAYR~BkH-PZ{hq(dm41uLfK zKsA2H+(4aXZCQ`)PW2(nKIpKUBu6oU<4lt29N^u}Br&hm0>)!OM!*Go!~=V!v2N?F zQn$N-ZkC-(vmFw5g=mn11aSI@cFsGUGP9TbB_*lqe2xSzJWfTFkNqU(FAwU20g$e6 z-t)*-{9_PNL@yC6s1CuEN`zEO2j#)TRc}20Lx;zb0q+>GzXK^wn}x--7}K(B@nU(e zg9<{pkNUGX{T-BMyCov2w~l`<;_l*9FVSQaF3;h`2mGoq2F(1AdZdLm?IA0&Nf_*? z(`GcXBoBPYu!GiQdU+d6Pw8iy;Gf|aOgn{)(Tp9kR*MaLYXx?lYvq|c3S*ceziU5a z4ES5;(Zjyy%XnZn^aU6(yn;I#nv%qb1>gD8neV|^-|I=f30FzjpaU)dvWFLVtddD{ z2eB&$2l$9zwlTW%-(I=zfu&1Pa2!LFGmo}aZ^n@t0#dAJc&s4^jOq?uB4W>VtANVk zjQ!zXr1WdNj_TUJx}_sOJyci_X#%{%Mk9Dg&%tXy{3`2Mj3zhXz{c(M4e?*?SQ8hh zG=Yn-9j!rx@0k=c9ZnQeQ{SQTHnB^058@XZBK95T8TQetXkxAerHKc-#R`HTsrsY4 zJYUIhCKe3}ZKIOdA&*>=FKHU8F+PimK;yTIN}GY+ zcJdpeDwzRfe6`3(@Z8H;NQ=lU9*zJ2lH{Fw7MFhjM#psou4d&a@6I=QkL|@SAM6y) zq-Zlp8a4$}lFegKg~*hxyvAv!8`cjtxJH^K;<91UCjX6pSPW7zQClmGe!_SS1gt~O zEJ?EiZAB8;Jgj5JlNZ|U4@gxO6u{sQ|EVASJTx48(n%G5K>3p;qM&YYT;qwCvnkKv z*HooO3&5-^2kgB9u@;LWM7~+HU>F%%At?y63a&s;&Bb#OQSh>@p1fJR^a147Yw~&`+cbX3Rv)Ool|238#~rV*S;G7h zI-&}qXv;80Xv$W)0l(fNv5K_RW#u2USoD8mm>udHRwi-0ScCSY{W@j>8<>x1UMyvw z2E9I`zbt2ci!K|mYLuGuwkWs>K-JranK1p}WY%VuWldFE`nAuYkT_JydLE2#q&wr| z&A}xKNx}|__?0ksn3;!jZ0j~g?UeNO8OSJEUDrG_$=1yDzNydo5`c)JSQ=M9sGk5p z2bjnaIcoVPn`JN55mc8-2r$`lt|nCQMUS<(hZ&fyqW^MnlZFw*8I+*+0gpo{$uqQi zo+ZgSybVIKPPZ|Cmon5yWI7E18wED^(w|Gh8{n64&)I)|eEFR=FtT~G$Fojz|>(y;?%~u*%hK^Z^>XZX?)l>3MyV0Sx zCv_kjr~-`y8MV3SO)+%f`~=kCR_lbc7*_#MpQ~D|>VGI-pBQKuZ?xbb8@DS3ZEuek zr!{FxjkNt`(+Ob3kngS`7{CMV7W@t87{ae_@W8%*J0;9d<(qSq7GN$7qD+c4^64<{_t2>tT2M=gL6k&3*DC4<!K8(XLP5V&O(c%CgttX92duTZ9<3QXGLjpk`REOd0{SxO5$stDf4vJ%eV*< zW@^p58Re%%Wu$2$$qg53G$(j!RNV7xYNij(no&7tq=3$*4!-nYvi=LEdE4I#0BNxG z+SKXs9boeh~6HUcY0OG>Yb!7{V+soVHAnv?=R8f=5ty6 zOQ7KO8jT1cnKpGOSUD!|hCIDThW5vt`hFNRtsV3w|7Aa{zS8gwlcvLzEXL#%=luAa zqDvhzK&r(+2=Z^^CLpZHVC;DsJ0F$5f>RH!CcMYCK3GMTxJkrTv~XPqs((x(7a4TC z1)LoMn>IPxs^YQ_PlJK-o3$m7I;^9?0Ph+i9hr9_pA@wm@Fsx8F2+B-=PF&EPV?P5D@u1E ziyBT&K9jIML#nRW8z-`F?_%B+0Mvti`F5AX?I`7afIeyo z9bM=r{N_6i8>hiY^>)NhK;f-VE~BMNzme%obQiWUtPfcwr$ET_JbpTo%oq2%@V2*= z5AqssnP9ETTweGX=rvjG<7|wtS~dtZ@ER)9q+ZxVSPEcd6Sm*r#sIy$P^3`7g0=+w z+Q`io@da)v707iejO*>bi^_h*R6on=9G1_Pcl&_8)2+AUk=BTL)<9DppWc`B!1o&EKlc#D*Y^^TSt-IkRf*n7EgU>3J~MP1r7y{V#W{))0kJ#F!JP-^ZX!T z>isEAk!t{;Q>w{_jan6-ymmUsAG_x7Vxn~9d9xa)j7$ZtC6p`EpbDynF~C^q2!-+l z!mpqnQ*$Aw7o0#pbPA3eT4ZEO4x-UPs$L)xo%D>exVx??Ik@&%!q#ZyOzF{C1yLd^ z^UB^bu3^&@*4N)Ez&GEAd#*r(K~qj1TG5!*;RPILN8iwwx)U^S={J`T%@7A4;mFxB z=6^dsMJVX3by_VtM;l;hj)ipP4!?tj6*X0SuNRStsN+<;)>&u=9)n$KKzT%O9vZwiwO!7SOm zY!W*VK=B?h86YXVdyeDY2W5U#NH(< zx?C`>WSaz1LSo#|0ne^XRlz7Yf3jGQmi=a^TJZNMk?Irhxk#lJuhFZo>Bla&pXn5Z z6G;i0vzDs1_n*bU^bHp7@@$Fs9Znmh7S-fO?gdn=?2{-yH^}9@fQV`wXpI&}jMWtY zE^A9yI3;2kZs7433{eRw;WoamGS%)X$BsG9g=0O4WIFmbYX|2@LK-Qx2Tp1%v+r=g zVqvgh`g`5>Vkr)F_A~6n#6UoTx42iWjH9az)wO0v?cZ8aWf<}i7a|5JYq_H^vT!wOBMMn~ zx}o=-k;FhAZ8x@Hsn(65BLg+i&U0W}Lrun17-D)Zg`@RoP~cd0Hfxj(nwu>Gj_wrw z{9et09F-b9BlHWw%a=FVV_CW9-sN51&+MML(n~2`Bvoi?UZkM=QO0fhqcaXUwoCYY z_Gbiz+?)KjA>I&!mRHhyV;e9KIKUI^^`31Xcrel+UGXkHLv)%qRh;ded@84UvA2>v zSi@o03v5yKYA`ZPdkT*fmh(S<`}_F)2PDLmqd4COhW1%j66eF>lZEC;P`9v2CTx{; zZM8T@YzyfoV9V^Y8iy=`HFFCb&x}P>}c#Tzo2NYOA^ z6&iMj2+;-6AEaY^kP^jsiDbkY2ZW)4i?}G&czJ3h(7- z>^q@Ev1-D2mJ%-NaV<3e+tAk_hEKwSfAC0)G70%UfX_4G_v7BV`K?odcO;>X(Vy&W z?sfPv7l997t{cR&u&m4-7_=U@*E&;S6H9VaK zv)Nm9E+H!C1}fDEk&zO*fXh89&d1PijiRV$3jh}%RzSBfqT-~8?ny=J)xU% zM6?^Ct+l5Ol$G1s?WxZ0b;S5~y?ps{O1U`2H`w`Pm?e$o+)oqS{OxyWrhjGNJGx+SKm2AD3nU&D$UArh02k{)Fk{%S)Ht?Sp$eFOsQ z+hn4b0`gM3mm4MCzu_U9M_yr6Z}FHNo&9#cc@|5}Pcc}mEU%elfeNQlCObfb7eZ=P zwP;M3XpmInyUiN_9208SOL+*wd%TyaFZHT~FUIO?0l$|Zh{o$dl5L#_>9TBd{NhO3 z#s*EjpQm$2JHoU_j`wFFHeL95q<_B4D{p>n>EyUk1f=)zuTSJShr0@yHe@PHt))uP zdaT-WFBMGeiaBByg@AuZ&JG%2tT&^)MVoQ(tTSU&;fVj{aCZD%d9}Dy@;tlDMbt>0 zjQ?O}tW%=k3SG2fOqzJu=fdtb@qQrQNK!3B?@>`B%4WZn<4C9|_2p%>4Hgx>gV0X? z6r|xRyh(wmTP3nGSigq?o{jbGGzD}$`uX*)DN(hHQI9|$XX$T)jq&(?CZP_o=$+hD zxDr*&i3u}5WOcTy_z`vo9Hd($t$n+Wt(4-QLj)qx9bXiNgjEpp2NXj%KsQUP(}p)( zYo2;*fN1CVWaBsV_J#z{2mdvEUCqy?*PU7@Ua5D4zjc2-)3FBsWEzaaD!?9yIoZ_HhCj%Br@)wQ9J7ZIw?6io7>jk?OCHs9*Ma7{Pw;2&W5lNiTC!~DS{WTom32%s}%aIC&7R2rAL69 z`Kmubi4dMW2a9-)ANb9ViadRz1x#RtXrhl3_cD0O%M<76D)v#I$FPRx^Wd?lJ{3{|?zYGRh~RgAh^(u=nAxE2PS?Hh z!6|D_yDWxC%$1U(m{@nrgE8{N=V^J<-_Ap15|Ob(SS{VkJa54*(vZ~I)_#q*nVg(X zp_un}{RZAl!vX`LwWCW~DBk4N8Dev>Fiai2ulPH}e_|z`)p>v}Ge>CnHGkuLv2_(s zS#9`zFt{?s$8x~ur+q$N!P(s#1YbcGqq|>j#CH*pW|O9Ft8|Jjas2!f)d~2QBjnE$TlHK;i2;!XV?QK+XAt~lD41i6A zYs_Yl%w$qMNq6wn4c2B=F1%|L+sBib%0tt%1ewC2abh)<3$zFHDyfiOWam29-@ z)+=C?qi3=H*{Ini#E3P^RI6K07P5BRX$KO;&Bp#MC}BIR7cuOdPLgo}Vm5%AiWJ~y zMSf3kID<(SUiY+hhCHHUNP2D<_@d5bhdLHkS+m1g&BU9nTWllqmAmKVs1 z(xi$a<)MtRdTFC{t#UzU*mrz+fyMG2JsibP)t-2Qd#%rbFlhb_VM82Y?O#9`^+G|q z=a+FvEx($NMu!A&y~C6)4q(^OHdJ2I$}|g|CbCM{XZ$LW`{e1flJL{{4Gx4uZai>> z{|cxpTdxUo)iO*vuD8RXeAiZhTXeaAv805;GcuKU*8}5CL?)AE$(9)f;!RCBhyxUA zHnC#LKTz5d0(K;uhAw&g_yJ#f#&DP}g`JPzpIJ>FAMv?F!b)Z+K=xas`Q;0tK`fjMX32|4?8`XAZkA@#o@a( ztvq+9|AAGE+pL&CRAXl4Gz>M_{u(R26yY#}6wwS7bg(Uo(-ofY1~j*>5ILY3;Lu+> zMkbVgsYeG%Nfa6#%-2I<#1;UJA9Dl0Wxg5{B-K?zv)YCTDN7yUzx}{Ja9?6k^X^c@ z@LP;fy5f~ad7S5Dx}OJuvFWFsDIG@JK@IbpCaU%8@=ACn!?YXBZwknD71DzJ)$c5M zi62M;ADa_n9}yD<*6leHdKC3n?RqkmqUQ+B@fm6H}xl1VRjZ5o1PuxP63b8h2AfotE;GG(`?>XwfdKVuevf;&uTPY^F`4oX0!%M zdXL1|1X^4g2iqM74(ZSYmra#Q`jjRkWQ%1&nfV5NgQ!yK=`Xw4pJXmq?9w?UB02u6 z8-uNHRDy6IB`d!>c3G(4BC#06QCp`1XkjYYXJ7=i>GSVF9tfmIdNP(5)W&}rsp ziyT>0?UBNvn1mA04{GrR4bZv#^|~V_x}{(XENxGpb<*bj?A?ei13Ua)B5Cm$HBxcp z*Nn&mGn3FF2c>y=b8V4RcC6Z&?t=;H&{5T&=@s64emDPJ@mCdLLScX5^%wY61f`Pv zGi0TIm1dQ6lU8k=M7L^_yz= zFw=i?sVC-cjQ&)@RB0IjHNnS%PlT_W|B^wo0!>HU$2!Lp#1+|=*V_Sru)yF2#3#(8 z=cCO3i{p^tfivTd=ON@I^V{~<0PwH@02(2vqcQqo9PkFX z+JN6+UvTHc;lZHTI9@m%FB_ZwF8`PBtmob%)*)T+TW}tDAGq&R=Zoi73qr6aQhQU?=^0&}z;!_+GS9h=tb5!{^Ig?U%@P=SS4xF>UkQK+i zkM5Jd5AQFY=@=cbgRYRo$d`9WZB~O9ugG@-^Dz~s=Q&B{Ntb0xd~ho=&KG3I|5-F> zfoZZ)eYQrq$l%vXFm6GKF$72faP1`?2*3SCg;L&7aQQQtNxF2o&*-?_%N!>-q$vul zX64U8m@H#e$WnR4RE!{(4t+@pVPD2D8@ub5VI4HY5EopB_zjBwZNuv3S?O$C>Pyg` zE|Dxb2H#O%#4yvjC8vhb;pg_!l0)BdI%$ZZt&o2VWO3WNX^9~%5bTTncubzIGL7#| z8qpK4h4H?;jCFYf$hw7Qj&{rfu5yDge`%dy88pPCrG#b%;?*C@anKkY1fakpi_Znj zag+Hsx6q|Z@>43MPqh^G5A8nyvxAzIhK|;M*#uugzHMVCaF|I3@<7^4m=J!YIK#=z zr#(;&!B7vy1XSHH?|0JHJe@G6cEvIcUF$S@lzLl)^sBv(D;%nlfgy}p%ZP=9tNTBh zw#P3mHo8@YO=XU!624>1PwwZKr5y2A?W~d?{ubm-yxQHJZqtVm!0Gp&O_HFrF*CssRo?G#zyhH#L52*vq`u2Acja zOXy=E`Ez`}5AioEKV91(brZ~tasM`|SXW$BGPW`6$yU127y9mH|DP$YtWM_i%>IZ_ z153R@+722Tnzfd?lQd^G6lAlb4?2w!f(y?c{=c+R}cYh zuc`vj=8T1tvs4hqOgKleW(JN$n{cS&0UgP6y5VW^eX@Eo34F1Ndwx6X1j!>ha`047 z*agGAY3?Dr{KrJEaIa>fNbPo1i9JdjqD!3XmQ|_)v*9=ifj$eYq)v!mA#VczZkJdT z^~;B&^^OGaSkKS4i>kXWkWxI+FbpJ4zE|81U9rif;k_=4cpsB3gf=2C`I?Qs5nIbN zTC6TTxGF86P=5wNZVu?eA^TCxOD%saWc98vuEqX8T&&gqAB!;Ys=t=68px;)e?r3j z+c%(V!f4D6KUd$E$4PEVBVTt0cV%q5PP~0H(T?(7nU~bi^qS%*sRWK7)Z35=|Ac~+y=Z^VT50pI{ z8S?86r)$8!zcUS)y~-nVU6^GG195K2$if@-mL!LzsPz1cD>GFh^$P55cDFFIe?V~@ zn!RkR#{jl*Q_7KRd0*wxZ&9!0KWJ#U1!e<*$fvgra@`4JR|$rnx)<-|yah@|on_Nvr6&kqSjVmw5B zorvbgj?4jH>gaL+b@N8$z&R5A3>-@*e4YIeAQt!#A{H?QF%42qAd*X^B3wGHA-+&U zeP-5MLbiW77GbX$VhEfq9JD*3M^WuW^V?VGii&N(6ize7!W4`SBys?JyL~ zza7E4TRIOjSJCBmL!n*s>`XAc7?==EHFg5~t7jG>pm z3Hikc>fwX6|L_Gl_e-^jSW%;EAC|hL4hU5d^MG34Pe@u8dZt$qLgdvsheg(Cy9g~<-3CsJT(!PRG}b~n4GywvWAU8_mk z_f`}rp@PL70FQ(DQqO}{3f3|a7%e5ur*6WFb$3j-F!c8|CPO@SP9EogFZ zOa4t@ivsFvukWmh+5l$@OK7dU-%7iBI5RGt)+Dzp<$D4skJZx8L^@J@xb8B>r_SA5 zCvwfhd>^scyA2TOFpLAPQ+$Cuk~vH36?_DvpmQwxgH}E+S>MMWa{2sMaNoXXxvN1l z=G*)Ia2Pa&rYFe-K$CL=I?mlkZ`bt!n&7E%0)N^0MmQAj}-%%)`2Ms|aat z6I?1Fd1)vn5(;XF*VWQ;b>WKcdRb>k5*Kh~KwG{!cR5^D%806{>$zs(K2&_o|szuw06bBTf- z#Ku2xR83&jzgp8v@ThTM^N>6%T^XIdhZ)9`ro26ci}*ow5Ig{k+Fz4B_s*csIO&cA z1~043U6SKYaHe@lCmrsC^h<^1ygW|AVI#rpw{&aVICVV6`XhxW$W**vHk$vS4M;+AwFZ-EBzlu0PwF80*;?RrS^(p#8BR1fq^4h8dM8rtn7C<35kj zY6;vhBw)2C{qcOVv-SI|`@5EwsTf2|WHm-oLryb)vvV*ojv#)3jnz>PFkv##v^An1 z;|Ry1R(t=|CDrv_r*wE@DaDo?m^o;+H#hu6`b_q) z12VK*WVsic-ft#;ADi0IV4T&AD9&!RZ{=5Z2c*SmyMRE~4{yX;=`>VV2vyR;EGKkiw_pB#W(s{E zERGcKSx0@+R_?dXcKYZW8cs?cbJq4*&nO(jfZFG7*xN2D$#$iUB+*&!j>)_36_tIR zDb1l=ZW~QlalHN$vK9ZqkD~SfF~u#VxN~n3BRuJ5tQNS^s7^0B)au-9;(P=H7KCVR z23iyo*uh`yS;3aMqcxw%U~XMPbbEbDQ@0HF(aAX)k$gSqn%oOBrg6)b9k&%h|sE4pf`MKw1R?LfJ@ zkoSXvDY%XSKyy0vD<#SgAeHH~8=+k7xPJ8-`3e#LFIab8G}C8Y8C-#$mZRib({Yur zUpimzCwLa=`&q?*OzNhXQH$b8m}JU#$T|ZTO%6m?zrAM#wJu$I?R4P`%##(Qg3K8& zR6;%glwBlBjX&Tb>!OiHo_oXH6a@zWa!BB!r>z0cQ^T1wkshAg-32ZJI_T_OSp@Xy zhpzO(wH2fxJ+5fT$T z607qdX=gA}jo4B>ctW+T1FGl$E?}WJMyC`lr`dhanq>T2PRk?uwE`FnnNvzy zI}QqKgW!=*^BnvuR0A>^C3x79x7v>H={MTqQkEUn+hr|YK7pZjdoJAJR2>DaC6`GZ zU??dTM*x4~8F>OijEE|hTNKtGsgQkeVV}f?e3FN)DCgGW-TFrySJ}3d%096ug$bNy zTDyvVsJ%gtV@2+P0Ez4DB`Yh|`4YRP9OZjJsx>4nnwQPDWio9B08A>ZQ1R*}h1$rW{&OTu(W{kt9bY@an zaD8<*iO@;lsSerf!ReE-!Sa)>jda0GQk?IZ2dNT6e0;MyyD$Ztk5#mkaOMMsbHcc+ z&L+z%lrbvK9dioaTxbGQaHhL_Gt(ro;uFYy;v^iG1O9=4IB7i&<34~MQ{oKd=u`gC z4gkUeI4Q3jf&T&L#1sE4jh{thQmoDdxV&nXcIw>2q-~K%);Ps3*GDio0b?7Fd%v`56YJU=8 zN$j3BYP5ha`EpUUkX`ZeIAan?I(G=jQ>K!M_Z4lxJj!u`(@qk8PTu$LH?B=0^*$xm zXGR? z$yh}9nAYx+-36(*>zj%X%|Azx`xGxI11Nl*moLM8v-?!in+WG1JJh!Haq$Nmh@mEB zF9RxUKvYCtZ>(wKe|~i>59YH#2V?Ln7P?jD3v61rR`7>K|L9^YEDe+ms6{{qAVeY& zoi=OxQ5w0mb`Q<Z#wX zx4nD`Ngzf!7?Kp?7<)}3P7VS%j|+5RTLHF5LI0X@R*Z^Hb+Lc6p9>idj@@2P_1ZNw zGl3vZ&VHIL-mP}zOh_gY(UZk1uBxYh2L{M~gSAXBp}9X&&U8w;e=_&2xaCzH* z#YgWbiY@q-u$(*Ux^N~iN-&|(6hbm$O(FTZP~?=LINBNX)o{Q}$K8bb3_W}t5Iz;; z^jervZ%@B8`jc+h7AuzNCWHp7JWu z`;1~B&bl@eK^31%S;GVbM#?TMs}lUSd4X<>5$fq{OD8E#ymw%R=9g0vw-^&!hEks6+8o=!IUk_-uJ=;vAvlVAkDbFU`FwQ8D;unPcbCt-o0DH!OBvK<6b<4o!#}vIXEnLJj z-dI{)?4_1`jxUz1vSa@`@vk}^aev=ZM|jG<*NKZjwNj)-wYV(*&;uawk+ITTWfaRp z5dpegevum*7APdJM*j@A2FY7~j|1lkv;JnsszSH_%T7aP4qAvvi~C$MO#Fm+BTqxr)Q8#Ks_TY^kiDe4u>8WGwpf3`~3IR)z2Z#f!u~bGa>>=Q=Zj`1hgIz zYYrmko<-7zOlBS+{u|aFHlcqsVE#M*KldS#_W&C|z`Os*S18=}$mAaq2VfKo00Y3p z5NLtOA6d$f1IskWUF64`UiY&*Gzqz&m#hK1$WAhm3^U&OWm&KMrnseCAy~xBTipCE z5{X%kd+#=cm6dZTgla$ufK2L2X6XgX$zRam{3H?s4CKJyTNS%6TrEa`m-?f2HE2;w z?InFxS(1xgb%_kK%9`b?Rc~~~qMW+paoB!aL0<8SE@3^;t*avyIa4}QF?Kl?*p2fjVN__!eFWk-@u zcXIm&UY%)kaIYNGL-#t#MefqljZ6L%?xedqkgW7mk9;E!RA*;M9$#L99rAf0DbES0 z$Xl&|n$cWt?2n1YHG}b=QI)dtz})DswWSb^P$hhvIIKFjw$5B_|1M|2`n){? zmG`imXw1h*S;2Mxf{@tt#GCh<`5DTn!$)U9-o-GNX9r#$+p9U3X78REasFwta2$y` zX~EJJuK`lAScCBsPd3s5IZDK+#ma~sc8eoH6nD?CuCx92M?WK>mmYK zh{%4=DSe+=^1OK)8Q!CC?JH0Fe*g&CJ?DKa$M$5=Jg3M~3HA!eAG0eayxU}N#xije zUGN4fc#aen$vAH$w#)>h!h0;>y(E^%ldCd~=0Y~?WxIe@@Hmt}BsSE6op(>&5?s8x z$^=Q_*HRk0=Hm2?!}I^$o{%2#m7DI1m)L*HGED+xq)RpsgoCDn>tWt;ylC?z!ASiP zse9HfpLjt$a-9og4uk5nwGst(OYr65d3L ztNxkhBXeHL%ZgDGarA7ItWbdTbIsFIfX~wR-v=2JwI0it$98G~F#I7imv&A%kVv3RjvYHuXHvj>cY9z7=Zrg-LgtrMyW z6kwAqLvOJyqoCBSFy3%|-^s(egT-+H8@sNb5^lz1T}Zo-UN=FbD|&X@D^wvIc^@yD zRz05AQ}ChZVMbWaxlNl+U{h{ z0mGDUx2zpKzn)>XZoPXiWgJNsN$!11(hr!h5BoHhBLV(;;&_|w_+i7G_6>T#N9G!-J+j+yC5MI4rv<^|C}P-dCbIiSt6EHjXg*D zMZ)b$a?a@00)`9=4Re`K$p5LsjOTG1rpHGX{pY8^^X0mZ@2<~itXO>XjhsJm7`(CL zrsTAI-|jlL4-+}~SqJ&Io);*AH&FE)U!)d85h?^=BN~}dILX3#;}JLODHtM<8NGXg zvlNmX!N7TQD*1NdJ&BZ|TL*+?`$Y>@@bB<&_Wcy@*M$Baz6aOBm(^BT)MMn(3#ZyH z3z;Q$YruJsw(~)OuLf>|L{W$08xA+;sqf=PIb*Gd+(^!>7C$uNz<8xRBIvd;-$nZi z3idXdx^%lCG~fWK!;s2;K#q+C5BF1$Dzb*=ML*oJSNPgYWs8%ZpPReRc$9~s4&VRf zNQu~fFxHkFD`6&F*K#_F|5aUo>^QME4h01iBWd6i1UNZG08r zCJ|E^21v8#wjP9XFb69y}rb(ji8tt#&p^tV1rStvKZj5_5sVvyJGSo(nNYttpz z0)x`51)wltcRB6Xj!>KWIJg#l?9l@6skrs7gl7c2=Dr2|Ge!2%dMsM%hlcJUj-?+Z zIMI+C<{ds^;aW;niJDMqg-9N881zS$&}nqixt5+0H^)vuszygO(Iz>S(Z@-VS_VG5 z>xtv#F|J>k6w6UY{p@~QIh7W03U0FRqwVg>r5^o~0c;XxHHaoIr9HUqQLH)Jn4f%I zDV)kDoi-ox%td~!fW@i^>WZm!59TV=e?sy@@fa6^4pNh^`0=OrPeCTPUUyTPg+7k zwi4RWMpOvF+4)g*xyF#;8%vR6rmrp=FLw_Aapp7yG8^pbSdf~!k=ICOIqWnp1SZS25p-sp<>n{Nuxw(k+$jZER(#Y{3c4RAYA&4h^ zgJ38m9t9H1I$zS)8U9gIz?{~ZQbkCLRO~`?=VJ?;p}$AV+~K>guCExNyB_XklQ4CA zEX&&lB#(zS*(AvSxFFs%x`A`0OWoIFL|MbnT*S6}(jI)c0UOP}msLL}EO&{GUb3@Z z__{|}s$}Sx81@)-UV{NDeQp@9Xf?5*XDB}5lTE(9ZjUnHdhs<6j-^*Hm%Zi_pc+b%Lj z*{u`n;Rf5nqj(;c{|flyqwfgX1#*ZEd(j{xnv)u`V2Wx7BlTKV3ZZTLIWZOBNy(h7QR*sNuS z5YMbcv?(r4 z3Jw@danxggj1{g2fdytM-XB*jxQ?__y^9f~zmgt$;xiT=DL;Ud-)6EHMvjL1GW(C> zgsp85$ZwWeQsTByCr7vU8?|1ln)Z*#m@w$|orNDpftGFK{@7<6GLW*4S=*~`-CVPz zhgO$^3#9L@)3wKPEW8XDKH)`yk^XsrT3t@2l*Q&qW z)RShOV)c-qp=9BPT^)S^AsUoCwi8eOs7R(q%|hf?CJq^`en^6+^mz;pzNvbNHrrFl zK$`Bg?q|MVMP+u}I+k1VXWmkc(qq~7q(+vZB$Gk&u72$rd|Yi)!OIqFUI^KNkBLRm7^`C)O}|h!xGD zfJr4e^XD6tW8~T? zm~-y#l!0}--&RrGq2%7zQ)OKeWGh?JesY`KI5{pi?h@Kq7!cDfX1-@Tk?L&OnB5B1 z-5J`^`(!HpZSD$*e3Zkanc5Vnu8Gn zV8QC0AyY{*KSSIIQ&9>jDfD0c1q}@B%)Y|6GE)L~Vd5}wXh4sS6=n<+87BGJubH-~ zqS128E=H9r?$d2P`yYO9+*3e?e@8VAjy;X)(HLHoueROXS6rT5hacv??3!Dcm>$rA zV~)`4e#?4L&`?3T6oLOoaG9AW#OmRXiNhYU{j(GpA6La9QKPVrWX?8hFn8atxh$qn zCONPYC9TxT$A9Qf2hOk+H+U^?RFFp~^b=~Ti6|+rEAwXD3d9+jJ6z8@kp|-6$}*Cwe?_`Y)B(&^j-c=`S277ro$3Ir@}tcm@XcfAS-4Lu)WJP{ zEO`cmD$Vt4t`qVPpLxH5@>MS8>JQCVBUK^0pCeM9;av7_Soj)ERv z0DUvnv=1I&dSImMg&J`mOwke?k^0wvVK0geJencbT0}AfY3+1G?jKOmN<0pXlvGGQ zASVtN2a1i@paDoT0(q!XN@k6_yyP1(8Z!HWaPYc}=A ziT)9Ng|L%j8+G2?gup(x2hzX zvn-F{>tk>>+-y$GvWMzY6{nX*F#qD2S2s2uHBk7rGA|+?AxT&Qq)ET&IKbjYq6VKU z217G^SJ0w&rKY&QOAQ44jZ7MfB53Hs+)BQZ-~b{t79NC=^@&^{l@P0VQef?vhS6)G^7-(pIJn2p5~5J1SO$mleEF zi27MQF}_4*Re4v@4+38j0kI)5-nYQ82C|}4?cK6(SCeh5=G(fdg$II_%@~4AW{Zy2 z)2h?E`A>Z)9e>-_TBpxq6{B@vGRjKg&eN(1qk`}#U6D+3%c}MT< zXgv)SqjX#aQ5(Fq_Hj5mMp2gBiwt2Xlhn!EJ5L_7x2k1w{2(ffw;9C~gLg+l612n&oP3*lcN6ywm{=upW~(#yFJk{&VH*gH4f9bjSUsmU_X1EBm;MmJrvcUnXh3pUvyR6ol4v8o+l;e|BZP&8{K^h zHpySJ<4%(4?fJ;!W}NdHZ3c5IzFhm)3Jz5yLdR99m@cGI z+qY|aDM$K-s+|e-In=zBn9Z-Jk}$L(UUs{OUceVCP+r@1;!HVn5AN2d=XRPtZtc9a zI`Aq4xpMmb*RIToeuAK#!Swe@E+lU`r$CdUMPo;3AB43S{gHScT)d+?gLD@3Pnp+( zb`vi)My+0lte5RAS-Z>Dz{JYI0J1cav+|(IkD9me;g)Q1ym*;1+l*!?3LNRS8$u}; z*FxqP`D5u2S2t*FBN>R3@vy;ijb_P_okJn&fo$)Uw zT37yMqhB9$bSiLRcaIXz^AqVEukOhvYAz9fb|59>33DCGB6 zjt3t{TeTWc9=B3${$p@iPCfZCu>T~^z>>%?;K^Y{LOwQ)NOrN`o~Blew^uLAj?;L~ zIly3+umL-Y%3n!jtT}?oohl;vqvzl>l|SANvt-3ykNq=!tGU9cC_wzEILMg!ASPsr z5Ui8co0+-F(G^os(+?8axp3bnZpyt-hpXUOt7ET{Dx)wS<0%@}1~ny`jF5vjOw67D z;Z((lQ9qr-Zzp~YJdf@XeHigt3)`2aAxn_8b%Z%JZ~DPBtJf$4#qYB(-=R=UG+V&N~`K) zi1Ya*ylSaB!)d=9Y2+JhrYOlC6QoCxUCdW#oQWs8mtAxmcYAWGIoMQIpng(H+hv5| z5o0p1EL;%=uK-aY^cbxq_s0I-?1$*ts2o_66JlfMyk=v_Pmq!gF!7+h#c)j6ll#4s zd@}fJP_(sq52q-kpjoj4yw2KD-$DO5z zRizt42#VB4UQTYI=`w~7d=!d#6VGw}?w>XsXONJa7!d3W@TmD!VtRB}Q`UgG3~Kp> ze0j42Bkz7`8H(VEVqXbS;kF>!$V7k(Rqex&SN>F$qwk(BcR}+xy3G(;vjharPmyY( z$aqbdhzAL_z3aw{?aTC9r0OsGN)!00QIEzpZ@9E=mXEX7jI#E-?pUTnLp$v8fE}vH>YaTY7BTXxjVl;VmMI%g}pnpSVZU9MDNT z&67bi%C|p6KRljRgWVjBsG>m;rM$lIWVg(Dg=@wzGp#OCewE+JG$p{#iUCa-8{z_B z_|V8d^(3kCa-KfQx{;7O2hjQCAZIQp)7I!%;4bRMDl*Tt0ARDrH5Hknt)&O)#!2Wo zk_Y#0D%oq&4O%YB7rPuvsXk7(WEqZeP?X|yDlYMVVOxct_We)%c>rqW`6G?%I$ON0sVU!+!WGWj_C)#lo(Ct4eaAZ zDLp?&8#}`*=F-DY?WL!0NZm6FsV@oBy7r=X$&+|o;T79x#IZ;Yol*Yh}Q^q}JLWa9Y zk(qOS-k{9hYyzLnnCNZ84g3U19&?npAH=%E0Pa$ zLeWEpY%^?O$D!fi2{?6%ybS4W{19sm?5Vu;r|N6QT5QkghD-96G&1TrzS%4~I76B^ zm?~|Zn48l~ETF#T&={d)VB2OlOE`iyB|6*^+aZ$MZ>KU@w2i1z3z?(($&Zv2my2#2 z;7Lt)&J%!(vmADry{l(2&)6eK$c*W(Q}?GWX?BAI_^noUQUS%{)c|xb_>{}yJT5Z6 z@UABQsMRmo-57d2$mm*-xEh$6EQUo#ade3(`$5UUR-mJGuuTDD9nX_6?{SW%IyR5&E` zr)FuhusI%&p7olwC=DpuCkGasI!dg4TA$$X+q|c`&?q7mTnP_sh)HrWl-L(X z6PAc+BIJ13r+1R&&+t-;k&Ad@ypd#Ew*};asKYa1AZL+4551%e$TwBPlCmqe!GBaS z=N9y2zw3a$syFACFrt=q1Dac?<;Wfm99-U#`vW1$3}>^1a&0mTbr%lZlT2TmCAp}$ zDrzDNWAV+JE(O=z+y`z_3*JhyMGkdURk5-`q8|RDJ~lWWfp6j(&Ka5GGiO71#g%J= zeCVOG4>^L)%WD9TL769N*;aJHfRQ+I#S;+Q&QFYWz=dQw6Y$1?ei9*bpAHY*;G?>_ zc_q1=La72gGy+Lt#x`74ca)z5Dg43Mm5tmj)lkT-isjQO!Hke40Dr2?X{L3a>W2*E z$q6Qofb4JincDVhhbG49wy+H^zzM@yV$H+lL#23pViKOSb^iU^$2}W=`jy2XeOv`} zQ1#QZN@caBZegq3b)^}g7U_elDpnO+|g4p{8>%`Rk;xAfFi_F0799!uKDG&)sL~Leu?OAk0?9zgQx9(1 zqS|P?dJMn$r$|r--lgA5S>Xp{J!p+yc0QxIPG4xqGg_rZ0$1Hvt9C1!q~;1UJ8Q zjB?5sa?Cztr^t`_eSFUqp=6iMiIIjJV$d^!;ska2vS=5)2ng+eP!;?TtD5?a)TA#t z5pHKWGVB~Q)5EVXhfp`BjOl2m1XdlQdRUNn<0_WEnsirFdHC9VH+XD%l{(KurB$=f z7AH|na!Bu3b$NX}jY9&ve|hq(l`;HUPsTp~!MFmK>5WKw5^@C%f1^EwJafjf12_QY zbl7xNI6*ygpvg^C*^fAYvL{$VY?ftkp%7}mZ|b>TS(;4JGJLHKK?Ij*aSRK3`oOAM{G1_Oyo0 z5lK)n(!+h5L;>c31_0Cm~OB_{>5_T>aF!}TFwrfjh?x-2)V}z+EhuHk^!s0a! zGlfw?sR8rhJY|`bzks!qwnxuHl2y49o-_sD%o+> z0sq0kx{zv0F?RWZ&91YLr+#Yl-4s=U(Fr?r!Op=QX8jEx!sM`OtB?01cMFE z&ev=ggB(xIP1N#@mH%(ZJxMc(twCSTNL;y@kg1AJy+ZuTZ8;u zfo^LfT}^Kw))o#hKa7bFs_Pv^Xs?YgT|{0Vooi=hj%9xx)=N9tr&H!%?Fp9n_|&W( zsB1_>dj1*`pAIW2Y)7%{))Rbkw5@o~m{`lug+dGk*ds$zHr?}k5zP})p)A$UaK-fT z8~v4STR%;Mtux2E;&_H}OOh_~T6BT=-+Caxcp+eExy{g+&K9?%uy>8EuXs_4Zl`}) zAxn%6wW`nOO?&Qk85MKQ=9l*`jR@mP8_S!XNK`9nitF{aM6zNWXhqYnryYiav$Eg( zP~>iBz36AoY~2+ep^|PjaIvnm4!d}QkLbE^0%TUrp{ISQJYL0yJwFU3F8{+r{*O{gE+r=mg?m-m$D9$gUmH(EK)(FE~I6?~@JcA_LnhNMK+M@Un z6_oA8zvR&I@?P1yh7exr?ND9&Nv-kl?Z`~nSrjp6gP z!&3U+_*y-dbd7!8*&fM3&r*b)*ViLhh0On8_q)5qR6RW*o+EYsy{|rQDO1sS7qHQ0 zL!=U~l_tk=aQAlP>`y8r9&T%Z6Av7Aqb=1t1E!-^6N|PfXSz!Z!9n%MOHA6egXyhk z_D(~LI0wNEXM@J1b&^=f#86!zAC*KdM%?u}L$5`La<(BSigl=4b+8K*ji)^tXpKpr zMOFq9g-RKBKYPp;C{_(utHFKlrAHjhfg#E3ts!Pdg`3IoUD(J>d>@XY<`5eGsmD%f zsZ56zu)bgs3V2HJWOLE?FmPuQ(gn|XDn^s9gWK0gHPLhlYg{pQD=NbHkRUf_I+Bwm ze0NfFuvpb&rQJTioY^u?Mt+*?&8cXdjN&RELJTqvTY>qU*{sX@q2A-R5YwB4PHlel z^CTq8UgucCs5cPXkdb@8ljNPW(oP=vA~gOUOq{1OY$&Pguao^n2!=H3)^`TZ$yt_h zK1ZaqD)w9}I)->^Xbjp3X}JSrctF7fZ26e&K=koEB&T6ZIYp_2is-`qXP{twSOw1s zv_P0}te4GRv(}CGQ8CU&Km{WzXL1qpIWC#h;a$-iIHlEZSZ**RePhk}<&S8DgtNK+ z)?kkwE5M^-XHC!iO3o8RB{eAs;W_BTJ(?JZ9>`XFcrzIx*mN9Uwq%mu{jeQ$U2-iY zr8*ES*5&W^jgqF?*Wr>ErU*8RJB*%m;ZVcb9#X-=0cZBzI{*e7H&uO~-~cgfe}zQn zGdeWSRk|oe5{9!fa9!p_;fHg^q#7%x3=>vM{r#kx&nLIzGqdZDJ8Cij`Y=p`-a~TF z=Z(oKJ)T^&QD^dn4|xmQ35uiXa{H1Mz`JL%FBnm*pv#8UAVG+)n zm*Sq%>rRC)p> zi2b_0G#J|_0`-cUOmE21HYUjKx9HK{h`1kA^Pt`53jW(>ow&$51Gd4d_CnNBpm3E0{IMM2<8%lLdnnXB8UX@3k z&|$u$sg*Ru_T2EbG@Bb_XSYuD16r{$EgXvU33}lOS2UCThe34f@KU0it9lj*AT&Tm zlpP?EazoD=O3jC46q#`4PXK)L%t=&HwYbnep!33$CS~j3 zr4FGWg9=BxNwH1PmO)MdOkg9BzK??Hi)G&x_g7jT;2F7*Rwb#NnAm86thx#M|(YynpvMso2-TPfO#rL zQ!VozOjxF(k&~+Vj_DY?z=toHEWvh?lJBH{7QJW#F zK3b3Z(lu~gku(p6p}4o@sN3_-w#8A7q=U+ckY(w~*C)4|%V0iGkSZ&gm zAg^1ScmK#EE|YG^m6e#b468Qudy0$TFW{l4TwUWri2(ZEqHvjv#sW0Z2<5Ocw?K0S zc+FRMI1kRia&SM5zLWr&aaw-)T5MWASS#yF;*%t7p3wWMHFhe=$OnM=p>ak_25&e2+-7YX1689ayoCdvEVVH1u#XG@KTQ(kORB z6VUN%b1~#)O~hp?6z4b21pfn0a?poS$t3B0=9asA*i zlGE6STw5kx9AT@*-`l#ys+ub?|Cl3{QpH7Z5-B zh{G=ON*V93eyb13>HCy6Itd3PL`a3HglT+mbCfs)xG(ke*s;)4S}Kj-zk>d_W=ICa0Nkz1rTetMJU1p%?yH-ud2Gvpqzctg1JfOW;F zo+c{b#|=L{fz|v=(B7*iY&DwIV$0+0%TY~r5lC4~Yb;+UeL2OV=&DxU_naJ(n*zmc zZ%p%ZNY#^B#_yTj8F<(MIuzqe3L%XccnfGM|3W8nD~TkMfl*r%?1x205? zNJR9=+x_A3@PG~E3Yb6jBTEPb{dL*du8=pU0X{)+O$5K*%Wz=<9uxpV;+Hk(Ut%AF2zgMf%SJ2(ikx0BEPJ1Ae$i)&{rt{r)E~#ZLSB=Wp(>u0 zo-jT+NiBLXE+oJqMvxIF9X^}sC-0gWa!W*b5N%+IlUAi=NoN@8NFdL^C7hMWITBd4 zhpl1w>}!iy4TbW!CE%^y9qQEGJ8eVOK{Y!-whc90cr>yC#NxbHdfa+6^mULAvmF`0 zJ_+-Z?i$*AX-3tkV^>4Se@awcTBbU(w`{cRB!-Bs&1KR?g3%-fbR?h+h%G2GocwDT zP=+|G3NbgBBuR^ztWRc70a7DKkFT==zWm$ufl?*vVaQ(IX&m&bG2gD8etR(qy&$D7 zrknQ7x;TT<<87eXg2We1p8)HuHo z!fK>3SkJX^S_uoDv}GU2M@VOHl&N;hjmOMdrv+=+EaQ|kvLrOLk%ko=Zc4yrocXilKg_9jwl^a>_3YCYLQ(>myd9$94*cd)B5fawjfmlelb4*9I8;3 z$G1S(yE4lX#|PU)zvJqS#H{AfUMHeeBE7D$2s=zIqHPcWU$6NIeb=MDXWla18948> zfurxA;|rRV~G2=1kLJjA>n{lhnw^14r1ZU)UN}E0!?b z5D`*U_`$-oyRw{54Vn4dFE>1YV>^zm6tpTO1|knsh7>Qk0YY4d2oWsjONR7%Azps~uR_aQr%pdFAjY ziu++9KR7s;RvsvNyBVHm9O$4C!M(fbUFwG%$uSG&t^0{rfJYRCUV3{KT-OX){QFC{UNM%tv@n?1-Rl*`{$}aUQoxMxw4ztinil1mu3B z{n&M;wJ|QK7CkE|q~%i*7xB5yl9Hxra&th z7p&Bm1uQZyHlGR|$b1O`@?GMndq;^OZ~}_+aOY!L@25<-BQ(WLkGC~L*`teH0muj% zD9&WQP0QFkT4b(QUe(`$**qEO!{@`7prj!Na>GhIvwT;lWBMht z+Q*E}YTw>jr~Eee2rv(BGFdY701i7422yR61*@BvMQKzcaVYLX%`GRC5>lHa&WG?aqRjEPURaVp95tUT_B1%nwDFdHJ%gk^Ps4*_%n$DE zGfIe9uctgfV|vVr!=b*8ri4a@6dEX$i)VWo0yA`1be&4EmE1VHY&94KFZs2<3$0qULIvD6x~ly_(QUS0fwy;%n+tu1|QMxT%@WO6R7)G9^SJqR5+P z#FT#Yu*WnnLDeoJlkVL_4x%==7Q5%p_OgyT=oHe``Yuq}5{ZTnC%H4PV_8UC~ULq1cTlHcKjt zI=H?}5DR_v9p0c@YpOFNN2=Oam_v?ej^6VXq#pytVkAA@dpkbr3Y>3PO}sW`>D~{X zd@KE6vLrsymf3!cR?@uX9`w1;5Zab`@t1QNhv3_-?^FeP2A9dCrQ z`I%PqOZ}*tOcrY))9luJ$Z%8*wtshRV$D=EvcV#gXF|-UQad&>(^jhCV&YmE^v+Bh zax?-=Xvz2-ad2K`z%&7|{-wf7KJl~FF^D(sAy;9%fB=>OZ%-frQ48N7bI#axlBsdg z_CPwP0l!2l;ljw%ie)sl60SzlsT_alsXjTSqa20f6c=4o0(h_^)9RR(Dl69I(`2zm zY2>jprR~WFksn2FPZBSU6RIO4s!x*{xiYaDb98IFV-=eEU?#>rNU;y6M z@Odv!1cNo*c73Jznr7#~O|(hvc3HU}rL=#ZKW8Irch&;{mo?uEl2nY@(3IqBa<5+! zAX5(-UC~lYn}@E&&EG}{pecdPsNRQwjym3VBF+5i3os}Cf@zCG`_1I0bAiKTNs$b0 zCZ}T9b5{p=BTRLwRdL}RpE5Rf6T>!DZ#GtTQCH6_o>JUeu+bCKF6z5T1XP%yI>9>I zPgLs^Oh6_7-r#cqAzxMZ`Ov19vE$+5fjnejm5^NqpHiz>!+s|ceuQjL#Vdv|;+TZ| zHzQTOT!bA}-JPpH<3Dr6%lPKa!>sGTRZ6MXq<7m3n9r;LNip!9h4DPzc5>gP@VGDM zDq}^6H)k-M$ns%PSwftllXP)KPaO;ds(Y^nd^J;lMk7)+TAVnPOeo>7>RA|m!Sc<3 z#*6}^{eY(J{BFts)Fsold{}jk{xIZWiFA=E1W9vWoY_?7&|0knjKz4}u+`kDyKlCo z9%Oz6chFmkyb7LB4!>Er3mAa)Hg}V$Po2FDwXyRkwjSpalF;bRJ*=_7!3sbM;9X15c94=#{aR?(7>1e7E?hwfHgiDvDA4KMp>>Zu z`W}Y$CPoT4wCIGkAQJQM=V(H#hsgMenz(CgSbwd#b5MN>p|%^eCW7?m;bX&Pqu?g!N2MI)(w6T-VDFv?&BJhT9*JDglYjgdR+Ch(nC$4V&( z%h?mZlmLdoQaBE9%WpwhIJb|7X$ER^MQiIC=jIK4Q<||HXMd~y+8lf1mmVsp;2BeU z^BRW_rd4BWe4*C`+ESLG`1o5eN*|4bFR@v-Wx4<*^@nyTS(@XmLi*_-`78KDFBK^x z?i53}u5BI;orP!WY-@<3C=1shqh5keiZMm9hK;c<7K1FyiNj&}h6vUT^xpG6a`r7- zASMvov`#zpN-%Zhh>x}qG)`QFoRr_JnbPX5Db8zRV?5W@0&nwxdwIu%Z})A1C|+7TiSP!T_~KmIjSk?VI}7 z#3|8oL}*KD5B3xYzYZn0qlKYULM43*#XLA^icG8dxC(e_3WpL#=f|#sm??wckQeo4 z*r<}?x!7anTUOA_#P)fqP1r0B{=0z{Yu9eO?N~rC(J<$|pPI*`ySgKJ#eTBRm>t! zfx^_uL*A$YDv68+Yr_d=$nW? zYO=>T)F~r@owvKL8jNX%C16jjfHe=3f__JC&_9vZ4w0=Aq7ca`ZDrD3v_d=F1xvbO z$|K{cNyXLjD=ayBd^5`@Sl7?H#H9m!NB!Z-#gH*7$xjO8R-v`p{K7|gdZSdZ=s|;| z5e843y{<4^8R`){tqjui_@InBC+iS{Zf~8$+a@fOGjXiWeFjsJJG}1LQP&OFUSTM@ z$Xi|ht?qp{W0*RoX2*(J+|%k=6H5CVf*F(g9|?|y>o@1{=R{x&O6a#>d~!+=WINsr zmq4AGTu7!BtNf^aT10EXwo|Fmv$lro1@Ygs$hp%JYHP4ZlfLby2QT5MxKHq3vtj>`^aOFdwv)+a-)EY7C)pSJYLYdG5)YUG&s< zj{YCWC-tWv8JB6|`0VXfSlFnDRRfdWdHHIK^}=lH?~p~bdG+|YisKDDvt$6 zGSLhNATW=gSAk)P>F<-`s(F*+X}jTO5UXe?0V|}TA23Cke0^O%vSumRe!g_Uw1;$~ z(ZMtr+p}%;TaL z2D=x>`I>knvDqOJKagBs<_)|Kju;YbR?Rw2l5lI10$@A*E-XC)x&q%W%?7~%t*CXR z*sPQ6NsQ9?Z{P?gcRXdFcs$ZVQ2uz$W=7+tRHS}jDGuuLwbWKEtzRjprkRkdFDoeq z2M-qDS&YP(`V%kQge#0@?@xdbMWl9EK=h!mD=KLwCD>T0t+Bmi!jFTL+*C&UYH zHA$){Qo9h!yAnQuJ@B-Ucy0x&>sMwU zz6aJQrUZ-IVP@IZsD7~?+qse(dPr8+sUEbFchld!WuK5mx8^$g)n1vej9SYBXXVeA`nf%;tl<{#o%Fu%iga^_kWwM<5U^mP$) z*Hg>%VI`}VPEa(vg=E^xiI&ud$CS!;;`+%u9jpw%TX9Q^LZIf|EA}=UbfN|8r>O7} zO?YT9p7faov>`JnzCx1d{p?pwn@L&!(H^Mk*Mrn!Zkl`b+En)R-fN-n+YgYj1|BP4 zLv%CPDMvz7->31r$}sqmh;3{N+a3Y58jC*|{Ie=A9dHkL#g7zJrQ!1jfnwN_e40-_7isA`giW&FioR#M}%RTEU5p zYlxkcU!MCnyhH)<5O2Cc9DD?Y^#$#uPdKuR{CUkD^vix#p%a;IYr4v;^EQ_i3jm*a zL=IQ@6(5@$1bFg<06Q(mUd2mgPUpOE(s?@{6BGKR!bD&Syi1wZp7(n1S1-AYrv_L+ z_oEgzzg@vRI^7(k@}3@KB(>XR6cJzAmY;Dw0`}%+lY_HC&kiW8!Ja{_)PJ35^LeK7 z>4W_hrwuVCi`i|op}zT5mnX4KTAOJ{$kLY=zlsvEnddN{MS4Z+$5mtL5%H5#B3yU4 zb1gyK`rusxEw|-VI$j(3#2gaATw@>0-{2RD5Y7sL;=S`A8sdNBuP|@*)@c zy`~H=M&-jtl7O*r`Wq(`uoF>@1`RV0(MGC+#{$x^U9cyb?V7Q3ZbD;a9%@<<>q-O> zFRwo470XyXOuP>&*ZQ5j&_!zR>lL=LO~xGHDNrg}Z?z1wp%}Ms)@vbjdu?tEj=2nk zwFf(fjumFaqZ+0@1!rjK>2PYAxWk?xS^!z>kU5qX@8>U-(FE;Tp4=ZuvWG@lx`C{+_-{J7XZ$$2TkJsuu z(|IC;oL_@%k;4mY0X;r8@y~&sOst@Oh(S(um(7xDUrruv`fKo|!YDgw*I$uTUdaxF zuL3OW>>ns-KpYNf(%ccWMd5QP^X^1gSfPV%A2uVukvh3~?z{%W{<-SVy*aB#WFU@i z7XOtn9UU}@$s*ZP-a9ZwT=$UMkqLCw$U5zq@l@w~y8C)8pG>GjXEadWL0@n2Pp~ zu7^7Ij)hf>d?W60i*^QQidgP|b!cd?E(ScMnyjnw4$PxweoVOObvc&s&r!2|YZGiP- zse(3I`P+a75vfS4_^LtnvREfsy$-~st267_I_i zPp$|Rl`4f!z}Fyi(CSq4@*EPtn8R+8JJ6K;+1ND;!*YK0QVLr8Rk8_1!GFjidRr(k zI%R*?qw3y8zSvm_0M&2~0&%SmsG(NW;1|$a&!4?|7?I&&8UrQ7RU!2jNUd74| zuK+EDu8~rN%FP=hqikC5tWY{r^2)ft_8m-%@u4{#n< zuuAAjQhor|JDMM|>4~P2pX;NdJU4WLx8&@Erx`2NyaZ|=#EIQ$=~$$J#ARO&x_6sG z?RRX8nj}Bxmh=s6zG(innd$gi%I3L}wvl znjj+}t&AiIS=M8C((Jie&$%}A%k1|8s>6IAnP~t?34Tg3fV|f9u_SS9s_lDb%eo86ns6I^gAIK822wiD2rOt*7iWhdk`;SGY?D9aQy>waB zEuhj$UB;x=DulHVq3ZL!?vN)k{>0v1)SKBN8)S#X2_PP>7+N(yyi2-zJCKSPhikxmhrn1cuH#eSBT6RZ0FUNun*xJo-biA9Sh4GGG%}q9k%w>G)??P`2Dci{Uu-#jbdhJH2%;mN<)`*Ly}) z>jO=Q)?wguHd>3@kWm_!_WxY!(C(AARf*>MePc6(jG_xOh|G-DPC8p2#=?^som#Uk z-3UCUKmK#m-OCx+Xe^|Q>p!#}E{-mVHrZMgI)uTNhJf^x{ar-Ww`?h5|p6>=kQYuAX_e1(;q2Lsd=vmFSc^ zBI3TIxmCPlLIqX)o(7HwmU+z7MdN(Kn~8cHNmlA$u5bdyJq`>~F_!u8ET?lw@b_kS zv+WJbEYrUwjfTBCfK?fX=-@|wqkEm?S=XU$MvoxW>Sa)l%_-Ljx6s6GLt#SZ$&0{U zI0d$9ZvG!dKTWrkIPwk5fHFmzUV|Dy2qp|TO7K5PYn#+mr1KPJe|V{S8BfZ1Tfw(0 z_8~48ZZ(H7l=<*t%~IMJ&y@dB(C@T(n9vg(Ms{iCRBn$tBmhX%aR!c_g9-0#l z6LY`d=DWOW&xf&^m01#aWlgU5Ic z{@JZJYaq_ePT?I7xi{c~B%(G>KpKd|#g}BZcF&0DZx)F3jFIeAotlp}Tc4m3zijlq z>ytPaQv4QoPyRMGXVs;og@j!E4k-D9x&}Q3oIqqvQH=@MZE)dQPXg`iM_^*+-c(wX zAO6Zg6*TjmZ;YIGOx)Z@6#%2A?*m`@mfUv()3Gk&B|)u?Sk*kqlnK&=NXwT1$PCQd zykQc`DGC(?C<5cpBq2XfmWkx|^2$0!RHy=|=<|tRB}}RFk!{GDz6}22QMs-@rnI-lT~k601smcaRtq5emHo@Modlb}F7yR)J`}!%0P{i6v~py= zE6%V|z*NaT6`dhg#oGCjEiNmlB;8h}mn|_WFVs#TR^G=xMT$~-FQfb zp9mWOJSZ2ydV%bt8&z;xw>bvqsIf7PVvThKPUXuwjQjG>azgUz!zBnQ`G>_waEt!) zz7bY@QRch1X0s)aFwCf(FrlR80@>ZYC&JjfWo@6HSrh{mD;Qj@SdX_#bVV&lcn-V_ zHo5Ci!$p-AX4s03dWqfAf*ybT__# zTvEG4qK!hDnMK3*$j0^znvJKa!?AG4d@aBqZ0vo>p@f;`2&17NzgOhT% z{P%fUlW!?dh@*^&F_fu5ClOJT`{buqtE7>sKAVmObjGALv>9B$7fglQUhW z^aO)_(_~neahx(lcp;cFav2(66Ifw}%{6EUtTL)sQz$$aZV!a?qf!|J4J!Rs1K%Ww!b?<(Os1xxMfpquMOSCi3(8=f?WU%9+ecl z>()UY!Ojc{Q1;zn+|x;U-cXMoLP@OhhWJGF7|^63i_qMa4y?XAh{ zBf6f1GR8_5uLQh+Td)%D*RC^OrQ~VK)9~R>#nJylxSS!R2NV{+37)!#G3qEe6`x1~ z|E5brXr#?j$y|s&c-sK*zM>)O4<^R0zIm&6iDAOy-6K{jLUNJM+{3ifN_w|+foNxl zM)cMbB?Uph{`?QS0|JK5_GvzcuE(X1)c^t`C+{B0GV1{(C z2}rbY^pYlPWK-39AB9Ae_|h5L*9sksus#n0{eV{+u(zryH}MHhPdTvEzn7RBnS^t~ z^-3HcISJ)U!^*Q|?UukO#juT0+JvjP1%RIX8aT%#=$18gb{r8A z93X=E@Zajxt4Ue8lwONpu!DpFu6ECc`r}-Bz-P0x8rnw`Oqk# z^b-?(w!_27UoB_Tb%&?w1}4tXE>~W!wQlFt96fmS0jHnaYPp|p>v(RL=?>PyCpY%B zPOGXu!4PDQk2c?HlHyk8_G5(o(GFf0$)pwikhgTbtfr?>4L_nQbuqKhM<6y_s(s}t z91;&(0{xNR{O!gH(nx`VZn|IZmigk#(+cMdg|#Rzr2t~*A`}y8%~~N7i|gn9uIU!ND{yEU+bc5@h)GglTb&>q z+OHD}M*!%8E~)zzGe$#BiA5Be7v%@jm67qo76GQnIyHOxhZT}79Wx7UfbN$=u6Sev zK3Y)kPZ!WDPVzcROhebE8g#;&|HET&5Mg%BF^oQ$n2x|X4f+-8UsF-rU)oJRdIL|D ztE!3|DwTLXwnZG-LI!$mhvnR8@MjDUg-#erTj5JjPgb{jnB9ah`<@kuK!~PSf8!?M z&(_fU%4@}Hnp!nJKm6&}u#EH&e1R+La>BHGd6yOpbgC8CroF@U!cA%~EEO6hmoZ;E z7CURWzq2)PW~oFSCxjj~l|k3!V1j8^l*JkaRA#5e?^I2PoYy%a@Zu#7hdZ zyP2;=nS_Q~1+lkvxXiT!CTS<-Unkt;`D6_|GSNkYvuUMF+Ce&CM{PpAL83Nd?7dfO zgro!8KRb_%%+=Q#+1MRdR!H%l!Y;nD7q#&YmZO(7E&e1wxq|@%@S6*m(+2p`eAHG>2}H{}Umg13PZ0ydeI z1^F$3#dkdx3w?((a%H1~Bo6~ZKi7opHD-lJitV0dia z6tg>6=bKTiY&2m^E}<4&vOFMrShWZL`OH4Y?nI)On;e8#8jOJmzWd{4bNsF`{7Bqx zsDC0g*}FxutLLMtH)>`VK5#j(x4PMzGrn?s7P|P#P{h+BjvbK6dF1n2#)q0a*^3Fh zZmH90dy{_iIofC=g@gNK9m0?smY_=+cz209tJPZBxD+2s$CMYU@&t@S#MGkho#O2(2* zs?xeF-a=Esf=XnxcNd@LUbvt9r@x9h?vjQ~k7g3Gz0ea@;2G*#_~GrHiBnu+Me`(t zu~`FHbe)9oF?#Qn_2Wzjg#pYL9yGm>|5}c4&)HY4xcnthoGiNs;?0%ip>39Zp&RXp z2ggL0h>_gk=*~pQ{TUA~Mu2&-_zJI4db@qOt4#ZPuF$13?pNUZ-(kW#UGG9PpyL(Z9pYITyRmdcNkk#^NlUi{o)I&Tj1b%A zKTi!k%hbXq;M+EMyMDZQJC{2??oVCBz}Z=V&zNUh?C-?W;vB4$*k+@2PU14mL?@0~ z4C)=Jn5qu@3eyVPWQQfM(ijlg5-x&p8MS zXG?w@(ll(yaqiu%TW9M7hpM^#EJqnj#T>)_iE`{&gxaWZq6R})EJD#TJAa_ z&wZ6%2XyU>vqN~=cGjK&sb&NxrFfh(OFuaa%D9v^Fsx~@w*5D#IG-4(r{Lv}ZXNc8 zYFMt=a`ey6vOTm`mV-L{w3ns3gS_W&UMf7-shFU~?h{3^{kN;xMsO3)s6#Tw_sUPq z-T!VJo3jWDF)jfA`*_THH|g%A^Zx9t7fxp{vK%~*z|FzeCY$G1Y~KbDE1vyb-5wTb zhs}}~b;=YMUH1kG$bhXK;*1}% zFJ(Wk9gQ6!c~(7*T60g2g_I(&Y4geqh#A4(<+RYNea;RC&p+1~ZFEu}(})zdni6aG z(XE$kibV385fo|2^%KfVDjSFG-4)$f%Q{D2Y)i_7%EcRfzVld+kkv}%%PGVx=)C|3 zv21q4QTanSN+f;*!`|^vpP%)gU*iLO3>S4Jk$RIIgXy0T9w5jw+05>%_PSI!&G(Ba-kdXiZCp!V6Uj^9yccU=ipHq7J+ zXmU-2ES#`!m&fa6OUN;gEH{H2a~+t4*HPwTqok^Ty8@=1i1FUqV_$E~qu_9gMJbJv ztD-ZAZ`;1RRVQ=hNiZWh2nV13z6?3kQjs1aIln+9G#Y6H{Z;}KgWTE})R3E1=F8y2uk|csjV4al3wV;z zNB_V9-EKTQaF1nlPlkHorWDAD09sv~YndG|r&OT;geLmAvms67;o)Gf@0cYzQ@QjK z+r7H9lP;}p%#lhCn(#tCDGcpEdkJ6q_zb_BPZi!1PZ|M(twgpDCU1k)ne9i zuJl=WFTB(C0Xv{TI!(-mR!s{K{v?WW9jaXstAjzXq;pbJng^_}h+$GKQTGz_0=iL= ziPeWcVEQ=gZ-~NweQ!xmLY!^ z{Qa)>Cz$PJ@v;M*Sd>iHf|haX3C%lmORg62iN;wd#8IW~t99 zgX2V|a#P6z?m#9>oN{01Br0!heUxjyN^A(OI^b$s1wV>I9mz%U+ZtJxw7MNg)}KS` zCbQR2G4METm&j@y!Jj@xSyWCio_K6kZ{nfqeGl8=h#ZK2OP#xa@mupO9QxB0s3p4+ zD%BEq)x9KCC|A*+YowNiHz7l73V!_!IaBUG>v~AqnC)0v8q{a!7X4O?h#>AxF0Pz{ zuto6RuqeE9qZ(bAkciNTSrW^{$XF$Xx9yZyro2E(HYS%N(7ZPrt%!G*Cdv3!ObY+^ zGEfL|k-E|XY<8DbvM@UWcJacXt7dGabnCh%l7o%k%8?LlI_=1Oz(qrwk)X=MPRT46G^!2AI#XLbw!p&cK z{`V-S_>Q?cE+i#ho^9s@a<1$KDP^vdm#9AdQHxmd)B91mo?N9Q`Snz+NPavE@Xj&W zy^VVxMCQBZe1vqR^)JI(oy$9^n*^?k0cBykM_KiVI8OZ^T{*&OYxSP~1rEjDEo=%F z0n`^2&wL0pG+tgXA|p6UN!A^}9yH@imCfH@*giOQ{`gtj&%h(YuOpWQpb3&z%cbT!W{{>!l-aBiq1}nr=c|D zhcU|Dv}NVyCzU~doHS+bTN0--t%_-sxV5sd6+W;~FKg>^+;;6kFVeta_ox+B%{r1e zSb`}~7>W)(8aCcc1v)dlcy(>H^s;1J++wsG+kWbZQ-_C7)LMi-jkYp9PNQbc-7~Xvew-XScM}G-+bO6^BNZ zPchX>KO68&5lgvm7IuNLeqedmWDoW#E|dPa9!%ARHZt?rd@<_ThAm^B=kRc_h;LQQ zW~y16zR^+`wj2>B*nXl_nU6BByQB%<&%~d8v@13-(BkxHhDFxAP+NBVHNU!8Ud0_) zo_fCUTUghAI;6RQxzK=L5v*G+^YPnwA*y9Ezb|xLVOO=mviiHnsmV@czGBFE}CaAzPo-jDha#<#+qzj1rX<`fDeXNN6 zV!8LbeHH8~=f3=1kSz8Vc(qn7LYa3g-cVe!LR+&;4`okTB~KX2!^nl$^xwm)Vo!^? zTZjQB5ns3mYL^Ix0X-HC7^@JK2-{Xg9u^X@v@Ch679x#lKgx_62c)=L{GY!q|sH@0+(*Y*l@xHsuO;I@wGm zNyI!M92=xO+(gwN$hh~ zEW!wm9&ttg5ukeuwLRp`M=+A1lOA}ZJdxcJVAEy)oMP{>)D6~tb1EHf{_5harz!=! z5X=KX?!}XqtTVk?3P9C?))7_klJ z5GX`lTKH%P9qvbo)rZ3(iEEOy-f!OSHCcA7zy7@9K~mEj;cCGQh?P<_%UR$ph8FEe zP}3SrK|cJ!F+v_%h&lX2u{y>@E-EURUA^@P{w1E~mu59uBvbsDx)aJTND4C*>3gN^ z{>&J86kCa%?M~j-_eYil6Kh9)rvKgRR~;)ENlGc5#&>c6QM?M)JTsB)HC8{T@wRqH zR!oGChCin*2e#*SQ#b%dM$YHR?JL4&>me+R3X=R-NO_3Kr}Up>AhpOU{v1WXh%`%t zq9T$kB!PLPA`1C2`3`A*`O#h)Mcm9=!zs+?LZ@IftD{u}A_4tK+Tm@IRR2)}JFi%| z1g&mxUdZbK%8B$dIxDe7&rquukR%DFT*aC$z}W$nwR~3nq?0pv4ffS|GGOMwUM|%^ zwUStd`W~k{k(M{^87h8!x>IDJwGL01wn-^!&a%*n4OcR)x&{HSePG5VrfqKnl?1WF zk5tbrJ1WZd6hZj4{{ZlHmMxjV%cy!<&TXea@g{3i-!*B&ZNeRFN_qD0z)me{&V}~j z0}8uB3~KrYrJw5NU&c+(V^#?f(`=aPpdCW{+Rdbnxa8!X_U>ITDXx=~1>BRfOJziLsD1lJV0hGf7YzdYSf9Cm6eaBj~FN{&Y^d znN;^0A#5R9G`a0U=W~|Ih;96-zYa&w3siNk3b3O747zRhMm%CR{0fTRAwn}6&_HZNtZuojAiEuUr~1iNpbYV$B|&_R zUeJ7wYW6~ey_%FNOiYRW*~3;N5m+UhMl02;y@|4pScpm)>01Blt~>z z=!uS=`4YaHBhi z8W>(@eAF4K+rj!DEM1P2PillfyBstNDI7Q9)T3Mu1kSXEZAE^(1|1BU?;4TY+E~>FU(S_&`stB{^EU)vE)Q~VcAZnPBPVHGieV^ zvO^oSbwM{@yIcoRC6!OqE}==n0rBGu$T`!Kgkk5_mhR)3 zHu|w^t9N9!6b%p;T9Hldb;f`_GkcB#Nu91wPhl>_oSF%zt48|KwdVg z++zy0-~gvFq_X{y{Laly*-}(0oV!esXx{hkE=M<>LoZ|0)564h}If;{2_V~+&d zAX3sx=oVV+H?2W%fa3kxy)F=DG+!C&fHqyRrS-!&Kd*^~IpoFK{@qruQ)9+gFVGer zcp>gI2|$zW{MQEHP*iRqxqVZ5uL>0#e%O-Gd>$jFq%U_y*fkRvG9=hq@n6J-z`y%( zLi2Lfdh3fv1GBBfrBlAJ=Xf$mpWd3>zE3?(DZ&ZHAz^scHxX)LrVBY8*t&0E?$BX> zh>Z**>_O)Aq0?B0=;t3j1AR`Sm(lx4T)Dxo{V|oOH(Q z=vj%9YNM;>u)KQQP-yJ0L>v3X_T;9MN7GML(aFHAJ6DhTUB25&atZIGF)N0*fOP{R zhZM9L>yuQMExId-XFCvvU6i2aQ}Cn3evfGDz!p*{tM()f=2C%LC}Qs#)h7ZIGOk;N zPKTj2_M~^BzZ`cAu97e7qT{=+D)JZ_ge!=f#UYMWr(g^31;aMa;J_6Q;D^w8hVK`Z z>PeCCPrJJ+m8M^FWj0ZF%1^}igV$rt!6x-uK~^PDfd?l^mzom&<#K~SBQa!ik7<#o zwc9Kqg!DTLXX2B#^b;Oyo&pz`yB=$dGsH(8qxP>0C>B&^>8n?uVw8R;qwX%T6} zEq)%Xdc-Y5H1bEwoo1>UnqLtdz{_yGaTD;{3${q|!*jrJ;>MDsihzN6wj?^29@wF~ z_=Z^Y>75xmUX;`c=^JF~R*(;Dtn42(o%-EXr`zfXJ0oj=qKdRSe4}yUXdCFJ)jKu< zQ;LEcuE&B3rN{nsLjsAKMblt(fr(?`vVJX&G$teuWak&lNngT5 zUc!`AJ3QH~rN0yGZxa`ZYGyDwg)gwAA=scM$J-0`YNS0LF!V7qVIQX$$o{{t+1bGj z_RtLV9ADW&%jiW+3}@vy+|oU_4?-r>(niQg3*{YPZG*6fjg1~r+ybUgoHertb)f=- ziSfaI+`6;T9Zyqn|E(iqFx`wWH1~shW>AK1?h~T#guPHPAkHOr=!V$B_{(5MFiA4= z-Emx%MX1oDKKZ%@3Bl30=xHTB8x*grAy!VkFt~e(jaF2z9u~g=(FW5IR;(<7= zRF?}oH=6lpb-s{=5lopM<4LHxJ&1zX@U1nT4x_3xv;S&D`_;4;x!?zjPy8TLuQ<+X zL8R@yFID{`*gC+GZila*e-zQOMVQX{NH2&EhzCdr;|G!E0rU~lLDCOM+!Z8HcR#ZR zTR;eyP%Kv*?{@JPw0WV=a+|li^;jazMhSrdRIDL@(p7Bd^I@v^ZnWUnS5<-%6jr(Q zRSG(4@NHBQ8iiD|X-HTRYtpO%n_YcN2p0A$u$W*+o310u7aSiTZ8ZIhQ@rsquT93V zw&uVPrQ-cnkH zQRN=5XDYH)RZLyc>)t!19=93I6|mj|1PEOf*edMx4DqF|?V72ZLSxRa zwPkz((IhruC?c{`JQh+Dxx4XUBL0RjJa)y($Z}C1!V?VOt!lZ>Eg$&-4@RgKnr-Ej zk_cTi{}Wcrb*Uy#nnWTCD;`YaCw#GZygwLAg!Ge{swAf*g2P})=S6pRaPwEF!ztZQ z6%mT?F=v^T=|9TqDY0zTOQwbrLr=#|E*|Q%vMbx@r}$`r*TQBul=?<%FZ{XnM2xRd zO-?iCMuMn6Q{7Y0tP00gDHr>ShXT7Zq6$-X6?=_zGazjpn0RTb^aWgOIf!5Hq2~Bf z!b^p27)*mkk5O)RinQO{#Z^+RMoSO?NW5?_Bd=af*|K;I?a);{9_B@f0%lx@noVfc zVMnFWF`a=kcwd-E8~C!M(^QiX=|*_L)&^mXa!UfGo66|mR7yR%=$9%Q0iLd3Cp{ZB z`$S0>efA)n{_2|5_+4L^p7d9Ae{d;_llNDvVcmimZdy$ij_}>0^W^P7;eBo{Q0_*A z>Y2= z4wKl346s8bNe>12F0vN@?dbziMhs5&-qT9{1c8`bZKOnD-S=M=VXiDuk8sTM+hKC7 z3$BDDl(6sx>FcMD=-c%up&A-WE?rK_RX)4kz7E)VOxgDpMdJN7!WZG}bd#4QQNyo( z?>)-K3NU|WY*O6Gv0TpEDJwTdspDwsg69-Sr;ba`;d1dW=2+~?Y@*ai$x)77zGK)i zNkiTFBOJu!*%SG27?rm%>ndJw@8cg6S-5anG^mNkQS8iwPs%F#z^b|z z#DDUVXYwy$V|3N(qZejcKg_IZ0Zp3c38lX6;~U3Hr^M7R(j3uTuN0jCz7f5h0_20g z0<70fnpDUU$55@-Ep4k{9UXk4aO;YxU&Uu0g-^??tF@x@c_Z^xkEYwFFWUAvRKe@D z{M3EM7l!R`RJ5j7vq=`hznknF0hIkxe1flU$ftU?9#CRcK4~wH4=xWr4Ne38Ed`PP^gSNF z0<7sK3p@KQ_9aC6^^#WE0{d%$0a|L(B}ZumQU%wyVvj3h+y`fij7BK?O>0^WWM8+0 zR4CqJep7K2M^@ab2c3vJN;U0N&?j62DW=Ba1&G~De;>=clM+R-P@*G9pTXLUio<1F zC_nYiF{cW(`aaWRsXepP-$5m-W9n|cT)ZtssG4KoiEh*?$wfqJ%5il;J7paee-zSP z!U)q86PL8oL2iagXZt5p*RCpnX;X<*+!LDi2qJe8kgK#IhUX+fta;=a^Q1R(;>(%J z+s1-6A*gzdE@V1Cyv(8~rWN@B5PQuZgNjcaB>AC=cdH)BU42pMeW^Ig0Syx zj1YMF2y}70P?pM6$_%qRmMu;kxNxsuy~Hp(&6DM_E(k@)qQRwjje8wyOpb!F05Bz; z%;b+Qx(sa#O6u;x!v@SkdTn2P15N&CA7;lLUq&{T&mjK}pE1AAM2`6rFk^DtXmbd@ zgiRCiEVD|SCtR}rE)^+yd2IZGYKy&i=`}*3(`@yv|0~-$d1c(9p|R~H_i#fb5*nd; z)@$=Ixm=wS?fZ#n?Th2$AMD=uMwl!T4*s*!6On9}?){^-g4Hu)+^Xsplgu}LzSX3Z zZD5B(rQp+mgfgEQ#18*+(^${T(aKV>sg_7krs!4_lFsL~UqAdG8oK-GrV6BU!t@Lr>!HP50KeGpqT(Vogk({!>;2< z|0`8^Fgu@8V!M#)=22I?FVCv)pQK2DiTd#sY;vxn55D(e&bJ{X8p<6>5wcb_LrCgK z1m*Zg6rEQ4)iW$1*=lzIC37s+L+-o&_hPfFJ5m6tH{G7K9{eVY4SJg85|{{X>H!p< z3T-#vK&5A&%v%tpT*P+<8eo+#Q%iH(JgWOQ@z^u7Z<+p1Gv~+&-RS6aI6H??7 zaZ_jw2#x@N=m7g<5`X}Js~D+w+5^?Fm(QyaCWo4WjM2Liyp(T5lq z(PfcqTonbS`X%#=K%KXn43irr>whP4v;wJt1iinO5n!BFhzfZ^oG2zo3iu$F-%UWr zu3uJed!3ltz^bsy%+V-O7`Ca~eE7l)zF%iIZ~_tY4(X0U8|h4+P_|Kltwj5_v72@* zOm1t)4^ruV5)GYY=7ISXhl0y`X3yxSz-8a*eVjlG!!jV@->6+^XTm#rK1r}X0z66X zhmRsXtOoj)c?f_`rG}v}$VL?XC2{9p4yUZ zK$4_Mo+=;k=!1HRB*^!UhFc_r2Y_cLK_&A$7khLLKG`oq6*=W*dj;Z?+Lu}*9z9T1 z&~wTViv>;DR12f7xKOr_HzpY9M|poAhNJF_j?ISMA@KwY(JzM$<@L#qr(Zj~HIi&a zT3~1fNk@)O<<8{Hp;4`FwvnZJ^*`TEFoNSCqE1MMt5_?Yfajtvxxr`iv7t6uLLrCZ zz4^B=1gD^rJZW`h5VVC8C9Ijmo48xD3IvkP;X48V)L61U4*L6G{JP?X!|7w=)k;?> z9kIn;K`|I&cDE`J7+$*3zFs~#lYt=sn#wnht4W*Sc&e{cA4rq1D@mcbib&_Zqb4E( zqyn$dq*fiJX#2A+KNYKG%R@8q`_YzUWotCN4-5b0K(Wk}e0G9k0=Us;19%c=y=o=R zD10MGLXYGQtnk{&MGU`9Gc**LpiH4{*qk|@OsJ>5VOZ+-TQm@&h*N+U!jIAsK_ByR zA|@&c)T*Q))Hz3}$ES}*!YFrh<8UE)kf;&b^Ea-e|J+1gx5gY2{gLyww#R=QCc!tE ze62^M=h2!KPkKJMasE~@i{-@_zCuRZ7dK78Sjw&&04G4$zmEg2Wpi68apX@oN1dqJ zW)rle_rFJ{E8;lDQY`s!DVQF{UE(XMlPJ_^Es)|J+$%r4nqNh z1$G@P2baf$VNdnnm9K}wH2woOLB_PExAvSCOz;#d43Nn231{3Y0<2sZ(C$i}!n^3` zut=qLnOOv)K;EYwE%k(k*ibTw-%&f6$yCg}V4+j@tJ1n}c(j_rxd4?25Wf6~Q{>@_=wL82RqV7doK zLWOb6%@lO(l&23^w1gYkF8_1ul;LanBK8;?);u?mg5s#0z*&1+Ct!@I4{a=e0iGi) zLpgGF^U=e~ipuc2azBC)Qa(pK4NMW51w%qH$;(s0_N_Wc{ZlMfBF>P3F|dD&S>9a% zu#SwCU27eM4vTw5(6LQ4^dnrYrF?pX$sUoUpl~P4{l5uDd@i!UuP1g&iZdiSS7QEk z<%NzY=$Xn1m~B~PGvp^X@}RxmB$vWxydJmsm_1;Es|hWeI73Qqt4z5>Z9$9b1E%W2 zJ-nS&21T-p_H;8hN6ln6WAhPbDpG|!w;wnp^)iILaa*KVlUP8Tg4%Vkrrk^gN?uPP z;4c%jS(OxagH9H(n^*X^bdA8g-JMFj35O6#dUjpjsI*59zOSx|>zLwHD0!3Bk~Xu2 zUu@W8MWcH)Xo9Va4pylYWyk{~OMTqZ5Ovsd&jp5|7iL7O7VtVAcB2oDD$pGTJa+6# zm@(PBUK~f}Dxq>jmzW8^8!vHbLI(=K&i`nJY{n2@A}AJ4PVQm%C??Y6L&Lq(Fo=eO zMp@vDo@0} zy6YYm5@kCkyavl<>^hrnyEwX3E?To_V%VR8LrQMHr=4!ra?2aWuHBu>YoP z{GPEJgy`95RB}~jBhpv$oAjr|(z<%7$Yz}$G||QsJ#}K5Fpoz5x4U?1dCt=!G5{KAFG$~Tr?6S+ZN5!liRD z&T911CBz{q$l=7Zy-x8lTFwpeaYsniB}+!f*}OwRHVn?w7Ldps%@*Z9lCRvYRN)^- z&l2N|6NN`otXZEJCQs*%5=NiDRQ)7vbMeZFV=i+IcnI zV5FM=BUV$Hojjd&g-;IwWwqa5i?ac*?y9o(SCO@S;>8%mG}~M3 zr(8~yVNHpd0YwG38bo1P4Bz&aAN&=ms7d$2HN+X>;Ss|@4vKEEctN40GA^8vzmF^f zI(_zP>=iBT^G~qKnd>(oRjKt^v@0fam+-LoQtCA@VYz{BhgGAJm0WMG>R7$?ci%iD z(_UBb1`$zHKHub3ek{r5e7fw8#Kqvp8*5ny_CR)dx};t&fSvKwYSgug9NbQ6PqJ3) zT73Te(}~hl9X;FAl&5n6LmIYflp4L%&i@jCvJiin-6BTSKZbicvrZus04M{5RqeX? zs#ol4FoEG(@8aSxRHo*D?c9!romrH2wR-BwYe{%`Jz+=ZhfyYjrSK+140v{DW^QUL ziiNP}M`&#wB@a=*#PE+~f@KnhqXdr`ABDC3PCq!^w||HrA6>(mfcqh`FVfO*N5Uf} z0Vp(ei8fq>9~B{&XkdScXGo2SQCh^LxbPCc4WxTaOJPgGMivrmEaob@hA zt$jcOq`SxqQ_dKSFF1&}5n^+Y>4hwGW`* zS=6L+v62SkQzl~*UJ5SdiN`jslK9;O%ElSSDJ<56+?)@5l^8&Mh`yw7Gk;uktNnx$ zD!=;{|Rl4==a3sppoa`qOe?%LJY2}&} z)(k!(6pKAEZmy}gsF;r%YRRc!9kbNDBNBfv#@O0HeE}~lCw4*v`4AjLvF-~l2VxzP zc3txbX6~ohMyct_M+l7$D^J?#S(+&99T0|T0R-m?g82xvM7K+lC_vuE7NDz9Y68GRk5M&GIv6Rt^Hr%PO%7@&u;YfnQ|~$g`v% zVp>*8b^%2-0k~K`DcU{IrBvKBVm%Q%F_V?|QP$M;i3hYbnxx#6u9c-l&?Y@Do*Jm> z(P3QpT8zQ#vpHxLiTDz3!2}S=BsPgRPxX)IFZ5h{ps*Dj?ekA6^_KN4$84ti6)V>e z#Cb>06VQnElp!6Np{(T_!HsqA2Bvy4L%3kvY)b_+$-YKn%T#uK_RmVWH3q#k_mDlQ z?jxq;cb~GSzl~5<7(&6bdWWkvHa}Upe(zNieDh@ORUiJTOrHWr0XBE`HlQ4jzX(lE z;3J(WU&jc(`0)>INg@hgH%3&FuL+%%0uEoRbb67Ko#3+DWtY*4@qEX!>@61(rNtdM6Kc))dLAx-Hg zyt|PUel+|Bfx2Eb^ggnuL7TU zHzkLN3Ntp61=g;C+TcQxLVqISdd|SYuzYRFyq3 zT){Dh$2A$q0aZ$Mggslxj=T6Z=U#{rN2fyAYwLG{4osEKtrl|^UWv(G8P{S8IXeMm zE+rhQ;uxo@ucl+fu&|tYVc%$G2bBuIe-OHJq}u=Hm`!hpk*#M51UoARR_&@0xt%>krR%Ywm*^il^DS}y5Vne8VWTho z6)46Rqzne4C|4XHGcfkgxNC(zg>%!?`+fH+jd=X|aeYfWo*cew*93^yL`Ho&_!1iJ zbBNbV1=>H_7}r%+QwQ@Vq_uj4l-Dez_e*ij*z)V4GE`A)s;JWJ_nqnjY({tdLSgag z7;CA(!lbf4m6Rl#L>!z~11=k76n7QHTLF)1X7{ijM7v#Ko#!%5E>DCS?MxqHr?BZ4 zNGG2HOKW(-0i6{r%j;Y0qD8V?F+r*FwUQ<68U;>+fMv%+r1A-5VLLVb8Uoh&F)|uh z>&MaG*xhHuss+k$-57wYe%PeJ0n@yO4jDjuG+S#L@6msK24KCg9 zr=CC30V~he7Tmm&MB5zk{Mrr@Rn*s86d^Oy_$Qe3dZ@`ph-WrYA%jM_&9M|EHAyrS zBC##I{t46Bjqy*swRbN)&`kOCoV$XgEo!yMia~!`l;_j8Ph0&T*3*5~A*)p)0a>A} z=*L~sKT8w5cN-A$H3)|nPzk&{{c{F{Qk+K1=m ztaM=AJ!zPX(W%HmLrYc_E2#*IFfc7tVYCuR<43yxc?B zvs~z%#)n6TE;i>1#BEHE6t84ru{67JuKkkREvsBocit(w zw%P03){Lhsun%oy)#S^*l$RBc$?`pv0G-&|oL{XATQxUj{b(Yt9SDwKcVt^#`;>Pg z7l+WxQ13-Y4Q+?`Wiv!2q{fL@gumKCzxad%_^OTn_slzK&5qCZx|G&37E0fj3{cE~ zmtBfN@r$)xul{1<8)nw%8~>7tOFEgQaUxosp&FeE(O85Rq2pcVi^j{qOo-+^doo-p zfb;;F#R1OB+(sP^V;elgueh|k1UIFqN!8?9SQ&0wyjJ8^Vp>8G%&NBFXI^zdV&0ya zms{7*PCjWoK~)&*NtsIq+=*ytPXLY~=KU7n|1^=1cx#Rb&6Dx$WByr01@vBo&BcW- zU>>L>OP%+3tCcG;Yjc`7u7dXYbaF4d(hK`>BnNJp+0lJmQA$!|nWy;)E>CI~bleD0 z_IAl67B!zmVIK;3gYn)?BVMbeh*if}yboCjAYzvsFJj;{VPwbztQF;KmvHE57Hi|_m#1GqRsLX$TFG#3 zb}uw}aqe+BwWsmFzp(O{{8@H=%!{I&KbUdxe$^v9$)w_vKqfRF^dnhGSM?WE@{vl4 zHwI~w(62NGbl~8dlX1wpQbDR8GsvOl>t}S>>DlHJqi{6#AiAd2-~?Gw`|@cxIU_qQ z|34eVUsu#iWNcGfhuelKVd6OlK0s@{*GDut8G(1O_xiJrhvFi1Mk3c1{9-=e87)na z#jF1Y;?gk5U)&PLWU)GOQctt9efi6!1i-%*)BE5lz#y*Yd$Q@*$m_4`YD;F{M!oO; zxOa7@BgMG*(Fg*8ppYn82~#tpBdOluC?is3969RWuzIsQVH1Y-kbAvuZ$NW}UR*Yoa`b9QiEJ7~1HbkWun zy7xc#C={iEeaP587k9R$B0*|?7#t%=RUia~H>MlZ1xopI@iK-+IC>3Z@cTmRIu{yN zt5tMH3Y(8@G>~B6s7h*Jh#ADP$cG3B7E75;cy&s4bxry|e0ZS}hU5ZsaCY}0ooS9$ zL~Pr#XU+-wp>l;0s^LbfnW+!#a-zgZdOyES8LvY4b@CC2hnX~+kay!E(0uh&vK-+v_ zGSullwUMveJ!xcWS(gkMqwv~p>V2dFEh}uT36zDv9%@_9_+nHc(YcdP zCL~;i?n4Kcw4D%Hvd|srcHt=A?#c)1x%3-u^%^lQ<<_GcbXMR}WE`V9>sTEzuj36q>a+*=#i#Lz_?1{)_9 zlFVf#KI=JzPEgxPq+4|`X+JH@?o+?iZ~+ZZ{pA`?N1h`b(dF<^qos-fVI>NUV6jf4 z%%chq0=KrXmwb>Tm=Cn(Kf&^ZN&cH`i4yCsZ&w70@GF#0b2=DTCbU|akMLfu(5v9^ znH5*dp=Q)F=YIedc9x2Vh3rsF#M80w-bNUm8tejtb>;n@!u3ZRK?TH&?VK?7x>UGvL2DgwUn7$N_iwoZjmG3-qz<{N83255B z4?k=0N=^igku}9YgGJe0Hp6O!8U{OL4%izvV>{YaT1qxJ)R-T*i2mxX(&A`ij?Q;9 zi4WqGSv!c9!zIC`Zdru9Ma*S-9oMStd=wpA9~Y$DBp*bemh!Xjm5-fN*NugH{r1xp zW?&g1%zCUz_R)uJn$`Abr{H#5uoN~@3DP}GlX#&B7)P6J%z$bPW~}6I)B7aE*mciq zIIXQN=N-MxR`^I3B(HgywE!6_D=;?^eBNCnaWj*SUH%djBxkAx1rQMw;4LgVYP;fa zBC)4!IBAm7Z$vC}xv-^2^`1?YWSC$_{HO6dY}iFR|aGX+O-F#8VB|Ka8G*^38?ww28jvar02%h;go z3n||{LD>GD(|uo%FRz@Z<02y1f>E-7UWd26%N?xMZ&HI7jTI5o*niLe#`bdW8G@fs zFo+!Cy{zskAojSG&>oLMs}F}pI(6Gy8ED!EY#`4=fe-`j@bI3Z21EITF#9JZ{LTl& z*n(GLvZBFSo<|1(Zlmp38Q{LZs{SPw0$2&pje#6;&{ft+ zmEe&}4pKS>4lscLOu7lb(_?CgF1JuNG_T}7-o^Db2zI-vM09A%zd1S4 zS(&~eoayzE*}FRJFw=Jz)aF9(l|%e-qD)dDoQ1*%K9Ll9t^CFRQJ$fq6pi>oyuX*( z*6PPLBwCZuM>i04=*&!5On-q$PpiD2QIUtmoLviIAN?#+UjwY@B$CUSpZSp-ZzrKK z76m8*Mg##e0000I1^@sP0{{RU<WR@34S}nuSF}$t7zEr*S_;%q=rR%;n!wE0j zf!pGeRg1sdm`;e29(2Al<*{8u6u7Rj_CY9+tk0x*6EfD{-Z4BOv*x3vT^Xe^5M&^b zG-=3?Nwd(IAe4at|3;<|6JdCQ*si$r$iOJrzVG$Q5sfXWi%^b6ICU)vPfgGqFL(^$ zg+J6BAa=Px)q0K158GoZY4twScaPAGoZf-Bpw>xj-T5YVG?}sVMP48aXh5ZCsFn;R z3m`5uxRjuMV6YXH7>Q?@SAP`<#c$vYlpfGRltt4Du1SAj~3w)!6<3}9UIUw2gm_;gUq`qPW4N1>2 z0&Go$ITED^EiUpnx-NY5jA;xaAmHyy>O~#5(XN*fwwb1Mj3);M5k*5$(MvK>A`1RS zjCI*tp$`F++XmGNxumsWyrZen8>7Y*6&x$__Z0yU_N_H|Q&7x*d&?HRI0||vU6L5% zxojAHN^zB9f1jQi$eV{E<6WWt5}Ba~oUv0~A8u_-Ve|9tEqm*9vwv10 zb@?o-H?4LzZ)v8CQVBW!()+Ey6znS8Qh}&%SJIFHs!>oSP|(FvI`bV@nuYhi6})uT z0Xe_SW^^QRoj5_c1RPq>BA|3BmDib=gM`03<%0ZW!5lM|@*GXdX4LGWbHR|5zSOt` zl}kz=yh^5Fgv@|h7n%2)OLVO!HNtu{juUD$&IjlP%#LMM8nd4D22fVqnX^)m;U7pV zwkwtBD9=VF*-rD`qg1GiOs9~YW9r(#RP=Cx@!$-=;|jMZL3HJuc04eLCf#i}`LXz; z%GRLl1#Nj~#0)0?{1$BBJ)kMH4V&}@7>ovTHXcxT&=KtezfjWtxolFRPHa`>47^P# zLaT4H8<)UrOQro-d8arnof1K9P0Op6z3ooI(cD5Unr=n?*pUy%siimDR9uw*Ot3cfod zsLq)GMV3#;1rQeroGyRyQ<6eo<(o&=6ku}S-xMZpsczKzL@90I z{}_l$wYkbnW}_XJ$yl;+ZxdGLKnns3#@&$Jry|};BaiI3MQ22FCQPOVwj)PiHdjQW z#=TFR5eYOJjS35WHzkx%(mr*qHV7GL^>4eSk{!`7Uf(7ExP{V^GZL}9H8;`kGtPK^ zT63-@WJ91%n)NOw?b86t+!t`(dL@qOg#zaHxOmcF_0-tv?S2R0KQOlsIS+c6kd0gZ zKg8POSstqL=}57mca#$kyRQ|qsaIHKIIYp2UlQXtbh|I?covyos~_vqQelYc^l#5@ zzS;z`P2sX7uOw%=z@XV$?t_5d;kP~o7BGLko#A&HX|^6wl}o+wkZ88o;m#z#^RV-b z4ssF;Kcp&nZXL{^=+*)VRkM51GZHPtGFjQ^f<>DV4Y-d+OP%rwx%Wjdl!Ykb)5~)M z^V>f&OerzBj9l@3whBzm3d3Z{PUHRy9wlR>f?yVto)Gcd!nNAYvU&1&eyyf4!tJ=L z5v%%QqPV#hp$%}?bb!@TSwVRNrjWu0xg-gTf8;WvqxCtA;={kPAV3ec$7 zzH?S!^+(u3u2fl|D;{7=LuHZOC|Gv{wqeO->OIJk{musyIv767ENa9oa(vJO6? zT17|tRXJ3OpB`Guc1jVWHSGKoUN&$oPy>X5ah0IL ztZdLB{9X0g0Tyyw`yK#fO+8tKNV3A|aGshAlIPU&>&~Fk%vCw?$jm2X*ol&rS)>d- z>^P2{69Y#et@_?{i=tf5j+@&#;&Z(jiWK4=UI1BUE1ULLS2|j*rUE02JSR>{Z4MLj z{N~7Xey+(!=nCaI*RZP44ckuGm*<8ELUH6^yJs|kbA0%e3aNsLwTt6eL~lhFk{-bY zONGWW>S*qlQXn-m37VY4DA{A;k1VC=#;%(Bu3do>u*w;Es#W1ND)vn>wt81!(Vd@S zezNGy$_m*wY;jy3l|EG=dkI=|dQ%`rA>TIk0JA7a(rXYcIM)t3c*l2pwF z@ps2mSMhGVPWch#7Ue#qVNWGDrZz9j@Q!(cOchIO0tR#nn;JzcmJb^>vwylf+|H8m zs(;R9uLLl9UP4!bCRSDOx7RPm*6!gnb$e*HSnml3LVmrl!4$!xl(PPud_kO*512gwmys(UC^GVTC_ivL|1nH>$4s~c64 zZ^NSU<_{W3f1|O3ZTgjX+S{{`IwoSf)@~i3G~fPIZ_L-AB>_qB?h^}QfAuY05gyA^ z*`MzBN-MVYMR~o?4YWN^2wWfvoa?2_h)HnLRTkf}$e~0` z@u9~iRc*Fp%y-hI*d>-WElU|L6nBN3cZ+GXzq;NU*iK`z*Rfov>{u!pn=cj88@bb6 z+8jKcrP`9+j1q5kWY`ShEDlg|ZwsN{)UPMOo$sERy$}g(dl)E zI$eC4iF;%|O$^wny!|MVb8_6vaWA=tKx(~}4wGylw-nSQCQK4!i$F94N+Vi}7jB%+ z!ccS8oKi3B)6B#_PQWiGb2HfwA!yDyGiIp0AyZFsz5xEze*0ZfCxzI|yyeW3ZQ9PC ze*3ZAduj5Dj8aSXsAoPpRb|ZSlubj(By+#KP$>C65sGz6>Ky-kO{;V;Rf6C>0~|7Uw^nBIS;@-3S3a#+9E!o(k#(1OGlw6p zgZm-Wv8E*zJ^{+RzGQL8)Jb5d!&jxd0Ig-!L14Q)>3BB`+b-~kTv*5zhKOC6TvlA@ zp2As@niDE~;C%=~G}IdujNz~w7FMVaRnU<^cVtU_Bhqp! z<*EqcaqhDnR8dEC6SH{GO1vKsh9w{78rGzNjTxm`nA)hMJK9{xhlaZI%9b(H8&54D z+>Pt+5IQ;e0YcYf&)G7m-FvC42gkcOH_>+T6LybMB0X_4{K+Ie2o*Peu7#qWz&7aq1h%;R605*8X^@X=?wLvz2>B4 zlgEUWDv9SgR|vmwZ#X@|nF`4i+Og&LP8(4dQA4CK{5RU}r(f3swX}bhzHW+VG0~rQ zXvO$i>eW68wf`}_hmf9L&6w=;2hz&Hg0((0z^tPb6t&?kwyA|bm&9-d*!2KHL}?W$ zoDUzF6NlM5yuvq2hPu-~@ijX#{pxbV|3H-zwIK(Y z+H-+(&AwH{E;E<9jY3BRa8*Vjr2qFgGwCAt;huY z@MKvHeKvLKqSd3D4a)H@XRJMsj!L1bOHPEZEWRKOnz{X3ei}KkLZdQF@*_mZOPYg; z8&Zko-GbYZ=Tq@uY*WtD25MLXT}cvPgjyT?*o=X0Z(Q&zD4a+@)DU;m!`oIw=G8Z_ z%5sK4A3M&8wF3T3VZN4*S)k21W+i|jC)&n5*x93A#GxW0ugl~ao{Kyd#6=CG$%q{3 zTl22Z3g&0;4vMkg5p}$F7yIduf!g9-VrI)65JG)`XX7KZKjvv$G9LVV^+;c9D4m7t zp~FkN+Xq9S8!HQnE{4AtaXt~|yeA-Ehe1(DV@uu`p!yEZ`-l7-y_YRED2}i`*RaAZ z`YR;XGa}v<`Z+gAI-6obSrMzmF7vV7{DIp9H{^4mKFha-kfGQr;!v8&6}lalfX6*& zbjE+jj0h8-A=bU1x9Q*1mk4oGg!0BTAvmTc8zUl}4)B6#_Rg z<@`n)=5|o`%IgsqTUooTR(JnzQq^jBkt^I9RoiVmkNVVSG9ANTWa8%F`6c+@SA_3i zor|)u8JU!f6{=&p;FXkiv#ODnzi^VV+<3mM#!UFClnQcOV@~oab59l#7@Zx8^WGa4 zAy@-9OshWbMzVL0i=0umi9On%mCy#H9V}d<{^Y(vOt&l6LI^i*jsw@@Y~k2ntTJr_ zx3oTPX`fQ&dAL~L`sX!dL|VHScnb=qo+>y|0|SW$5)347NLi96L>uxy&2rgwo#WAA z_VN+P<==eh^lyu&e4p}){u;(IBI6tVPV{1>vjn#zJIQIEIAqLcEzb!8%-Izka@a8P zd9CRzkxa=+`r-k}Br9M2XUK-kG{}lF23Fbn;^XqdGToSJjfb*D<_L1*0_tksHH3Sm zT9)X8JN!c{RO4jy|0(!;2kPmEDl+<9zD$m$5bR zA*1{jAv1`8Sl(@F@Lc(6=75PWOP+!gPYT$uW1oae;hi@q{}9C(P+LB8X>pCo@n{>I zWhtuC_~Safm$T|F&)vLBeiqEAccp2j+Ya|n#913SB&SLp3iS66pz|??o746Ra8)2o z$#-wLp8IP&6*zqOpN60nByN1fzAR4ug$`8F=@})zTN*yW6qm?6vX7WOZ*JM#*3=Njm3(3 z(<$GG^vI3o2+ziU+ycJl*ge*t~^^d#9sGWTD?O=&J1C?U5+7Zu3w zq}J~&9>pNDD*wGp#aBp0Nl^VXofP<*`zWxpt7IG0%-Dk1X*Ix0eXn2t+=EWh;=h{K z49XHIQv0)y@&^*5eFNJ=WQbbD2$ZKePW{758(@S--#qp=1>0q@H;JB$&f}->mht;d z#BWzcCf0ApWI`a@PqA@A5B}uZN<37DFmRS)CTL+Ik#ANJ4%bP90vRA|5V!lj=?<+c zS_?F~?!!dm6a@Q7Xlwyu-~kAx9N3gm>|E5)G}yW|80#0dA3molmf!1sRsswL;B5>f zl^+9ifLu=u&TSF)k5f!9^UUx6C)^R_vrMYy=|Ahg#IzxV(ss-*q5kUAst{!%xwCe~ zzlsQ;(w2rFrK4=wT1J6MPmK3? zZgq<-Ljpm9ULbTnm8d6PC_Chc>@Rz^G*yi`qycOLia<~Y=`Z58JwfsdJ+;f)^wUxW zJ<}Udzl0}|4q4S)(R3h-txv4v=Egl8Mh5-sjQG%7Vq~pA<$0Z|d?9_y$91>&IT0D; zfOQ5oGS;4>kAkQcD?CF&2@Q5g{Qn8_s~8d~pdDM)>)*9MDiyMA|5z}~gxY4^LQ`|L z00aT7c{m;Dck9pPnn5zTMT;Q;u|q4smz@F`si8kslPdSSR{Gs+enEijN@H{G_VE>m zSj(RYz~v_V+VLYE?*}lW_-G#?AqUqQF3RP4q%Y*z0tY_CN7?@e@f|xmEd>V{#33TB ze0N|&ymSH!l6E6V5AuhlxrRUybeBF!B9z9uT*ZfGjRA(IcSIz%`aceRF|jl9ltrGp zJKE|$#RHzP!bU*0hkkltY3&{idkp?u9_l1JgW^cxrQz^pV5aj!a=kQ?j7{vC=|7^ZBA=-5G&J>ePvOgc;3 zNPNoFn&bq9q(0OB_)mr|cZKHl7o5yGmekGwM}lP`$}isS}hv?3kSf@YJoH> zAs@2KCi0Rvfo{J(eQD4;xA?^;4ML)sDhDYfO?@F;fE)2#djk{Wn{Q1!ZfY&VblPae zsfj<0$l}j;F;#9Doql-@VnTu=bR|uiY>?@+8DJmI6sG-lRxM~ac9BE4o)>LOn+Wvt z?ojnn0{ZDRZ^K^-v&z0`!5IIsnS$V4T#b(CUMRch=< z$m(ik>IE5#qq*8EV2ezHa{z5h{2eNOASEN6>2f6ienH3>;m5ZJQ;y{LLS~<(xc2AK zRzEnv>0Q32jnymCh)x80Ab>d|?0@vu9{hctS0&nuLqk#|o&WB*(rh(F6il7fRp`n) z=f&|*#3s#b(s4}PRo60gs`gS!zKXTos}0>_2;fb4D+a~py3vhsjflOsdq`3;?dzGW zt6#I3nuUFwhuJfus$c#W3`ZH!dlZ*X-TKq)eL&_x%b#Shv~zLam0hzc#rs9tx`<)h ztE=Vt(lXP85Kgm&3bwxFDk)nzT)=x0!&My2TntY^;AY-gwGJ=j96N%EhM8rCtPYyJ zZ&Rq(%pYclo)X7G*r6WrEl9I_ScxjxXd=J5aXETG@(PDNPNF-(Pnn?*xQQN(ksW~n z$I#At;V$x`?{*jz!!xEZ&EQbzme@fw5&53m$h?x`n25O!HTE;okvR?u8Vg-w)LAAATAJ_B!Se-|lZGX*bM^(hGmD@^Fu(vHOmTR6uo#4Gl zbuQq+3sd1GZgD^AV<-l1DO!}qGvxa^ioum}EoH|zefB=oMxBug$fYw@RvJy9J z^3}ZJQee9P8Wzs!ABsv2U4p`CS5n(uJ)Qc?L52arimJEmxM^57Cy?zv5vK00jcaSk zYreEA;dSMqTyOA0v@`;;aQd;*fS4dz@cd|O*d}x_62_oJ0F#LzN6bsyEG~Z64G1BH zY@DY4jieC^YgEWq?7w5FSJUaR<@h0}<{_MpT#yl_Y$z`_WWzLYoia>Hdy$>M05uIOWUGD(~ zkXL)Ng7Q4nZ!_bRucj2k1VDS?_=>5o#Tc`B)N;HK%5&3TXzhPG`@3 zv&+b>+t~~hMP|PH6`w78*uwI)Vr-i=LCd^R69QLXt^! z6w~C$0`8d_jjA@|r@u;C7O6dcnyy0%9e;JfqH{iiEFgDF;${aahQw5r}r*aND4~!B6}(%1VF9KBG`?K z>eCld2^G|U_Uy?AQUjin&1s!w?zpW&E}CRcog!q}M$`gM#X$_=rL2x0ACt7>WU*-& zm?7l9V8z4L=3lGVy+%$L2$C?k7S)3*gOx2j;7XIQt!Q3HgqLMs;`hn;pZeKQs1%=F zWa^(_OHYfeLbsBWyCN*8&yTll52^7P^tMu}}O zF)(y@edEX8K9nUqwZxKI_(N50UvKR37ufNuGF z`Zs)MiwyXj-!pCI5LcgosBk$f45bl{!?V=IA#b~wN{>^}EIajS4k_PUGd;X|CQg`C zrr0sjz6q#(LA0i$Zh}7Q3dwo((JEj1i1f>44mWomB3p~W((*X03%$=91Iu921c%`k zcraH42HpFZ+%P+wRAbZ!4L`?dSpp@cCWT5Xb#^NM3NHE~yi2xbb$&9pmRVW6Py+tT zSDFCHLmKhBFta!aB{`T4V$GKL5!tlHW;#!$uuv!X#^g7Iu|1BB0eK`A{HTccHm^1M zN{5?|6Iz8aOA)c?8m|2A6W^+y4ASB|v8#`g#x$;B5K(51dkiV&O0`HE2BHQPCFsFZ z3KrD{<*)+@r<2hz81!(X6;A%nGA?oH1;(yd%}im;&Ji~^dKCX?@8t6*k-fKgId`H@ zrdg}&xQB@*3)r7vgMb46iHvvCw=orRm;2&A>@P1;z|XHF;R97j>a~k|%j{3kX+)?- zq#h;j+!8V>v-fm4ck4Jy>sYq8_!lr<|kK;|5qkU{8}xN_8x=A<{7F zm!}~m?yACO#p#Q8c-50?M3jY8;)qe8c7KZE#tK_jMjX1S$tk3rU>@lDBCprj2VAHN z5R_9!urnoUxOs5nv4^24NM=tFaMpIWFHMaf~jk$Jip0OEzC zK$LceB6m(-NoE+Eu%MK;M-5`N@U4bHoQW8!uTZeHE5HUWbq#nAqsf=+sFWo z-E%Ppj%=?|d+0UdhVWtMEwp~Ql&Lh)ueSay^r-3QZ2c{x}$%H-Ez5ji!i&Y0-@K~Z9Uku{Qw zR*Ps+O`qcK)Y5RHu~SY(34#1*CBu3E|B0Y9S9X=HTcRrT8=zT8_pG{s`u{BIA^&&Q zdhvew$t6@`us>~0V;YClMeNpn=wJ>QxmgymE{DXahr4Huuw>T8h1E3O`W-V&z3L>a z%Jk&1v{o$V(ul@GBDK4ExHRC_MREX6F61+mL&98&5TWU`f+1HP43TVApaC?V4wk);)dP9%I)HWAQsWecC+W(w~g9 z4Q*$e5BzwFE|$(+x+1rJg|t3gN^R-&YvZcDE-+n*rFWfwmoBf@HLZ|d%d@(F>1pgP zsB-!ysN?i-{`UMUMR(1o{=e=AEp0~mrGY~9Bt7MW#f&@p%hPXFPCvlU;mnOCPvTwh zf{a;ZTd1BYpN!i^soTAn{wvKBd(6}~OR2QW2q)7wH2Yx={-s{Dl9BpU>B``%_bj&* z)x2p2HUPYoMNxOnSlV3FzQUCz%b4gU<@oNa))Y0}Z-hN{56la^mU?+Mu`JJ*gIP?E zi6JTkGk)YhtP@@jU1$Aq&}s31I5Xc5gX1{m-5dO};2!R+yhOfd`U^;gXA7Eh!5dVK z+Z`lHZ?dJ&zE_s1^R%%?lZ0zWd=YoTR+;39M9wm+z~Fs(sMkVN-E?J6l zc-V(kRb~Fk{XxoTlb0h$&1HeCvF+*Kr+R$9IfMI|xe}XC10kt_f?^ zhVAc$?L(~b-T_1W)@31k>$(*FzQMF*U4S*a!Hq6xrCMbwfGhRvj*^`ee+e_GZhu3R8KHH77 z*Ro>Y^Egow?`=AF4vv-ocEqA}pUsv8w#=Aj05L$$zYEZ0wq8_R5%`}2(Vec|74E!r zMfozSU7C0*%Omfq=hX3|G5 zXCYCFSc(BMkH~FG!3OSU>3*3_&XqI+nO^IahPuC3yPS*5$TyKQzB{A+EEjOw)zBNl`u}75yt|tJ!hauw z@+u*^B5U^_XVT8kEq6V~EMR>zhV+4Ev*Mgh`3RA>v?n#`yW>C|(7>iV3my=c8_ra z!uj!5(v$77Y@MsXK+E1XMv1a1_jB#lXaAPSm&wY*`m{z^46>M(0{hUfBmTMg-$5E- z>F!+YLo`)|RPVcxle6?ld!)}ve}jf2NW zNLjz*2Ir;eUEt%cUw1xq_n3{l)Z)4Q17>Z7gT+Wk0Qpqm`}R7gs+Qt>1pR-~GmQn$ zT3%(nF-zFgi=yd9YN6=%RA!A*T8Xu%_9+FT(5gkLX*Sc3o=&RrmP#ljB9E(jr>bL0 zy(`gfQ33@j5fw>+|0HBUrgzK5E#ABGrqq~HYjfwwFdn@KNUCE>CBR8p0&7oIWR_%k z9kBNu|IF4Wu-|6t7Z;s-QkZmwJJP>p8pOj2uD!n4-$qRwfQqkyi*XcReV@k5pNXqZ zy6T1RymV)yhX9mb)AqVGLw(^|A%;-+~F(Ug~_&r9<}KPL4sA?-3K zP@rI1Y8(@l3dAK1@SVR2i~AmA!qj*sK)E7{7nL8_eAorOXl(^|VPIqfdVt=|obvk((&QR^<0z=d|9U zZ;K)h=Hn3f7BVCe13sQ-m?Ig+GjeZgRBT0_X+Gc?%n^Gp$O;~K2>P>(D zD5j=(n!AM3Io}xD=UNub89CX2j*`hgA9be{yR+n6&_|r zP0FQfBbD>C$~#J3fzl}rP?rW&-2WVz>AI|5=l0}7f# z9^3ZN+K+wf--Ww<&eb$B?7^du^H9->Tyg4na4>VoP5pq+04mcRcT_ zD)Mw;jy$h5AZq6W;#o<1>;u#YWLgRd;5s#hZS~Q891)DcCDBt2oxiPKd0#qf&Xuz&ZJ6%1ZUuI>C)zg04$Lc z`xQ={<%Q9w#ybFXqwZz>5M~8f4%G(RB5t_NZnX-zdx5*?b03{)T+e7R$gE61 zk^@dFp4>hI9lzFS`<-VlZTu8NYr5^7Ej>#FGjTg9GPNNt&E^v$2s@Kg8FyhIdrZd& zh1cO8_QvsZ*zpZA?QH*XM!U!Xsv2=wVx+O#VMkZh1^m z-A9rNX9P8COQXHx!Anft_S3WBR;*SkP+33WXQ1yD!|}2I+djZ7*)39cL;-r-KdjgE zwJO*pn{3L#IHlW(i~z`uqrhh(sB>shzY&Q4B;Tg@_P+}DX-3tnEL(Fr?Bl{DKMBy` z07Dsur6dM@l{d+-t|iUY=vX?If^U{Tt=G>YD{v>r#*H7q*0fv4&Iu(o4f0F>If4F= z1z*AsR#L1q3(W?79j?Y^-T*ycuyJ)ysar)@PL$=?K?Bd! zFQ)~DRrR+9+3RVW)n-b+XO@|kM>f-&mkW3=0-gXgXToZQ&sjcF*i85~{vP3Ak3|V2 zTmR5}3S0k9>g99NV|GuUEOETkZW}CY+BQ>@{_Is1SYsps-GuS8@iv}K1ze1y`KMop zzh&N8Oe|S*zNUyTj;OT=Q6uhM>!;;s+bwgRdKTL6jQA#o2kX~2DX7MA#Us57(70tW zQVZmqxatFIFg37C4C-(Aa8eOVK(*^IMUaDcL!im~+){}Jf;qhm@^ikIw ze~Vt|rO9}QRIVD2{Sg20P9nGsQ+Ny2S|E)JNuG0=o&P@1`DLEawxN@*gZXESjnvRT z)EzK;C%6J2`hYpI$Z>HDdj7!Gwk4PaEqcl3!{Uv` zlgk(M=t|R8&JtWe7PXm{FaT2_m>I-Df%xuoJHjTGmMiglvE3Vg*sTX0LmyW>k16-L zp2Sb#+&ah^nLl&M%zTD-L!_ggTh`$9zo{_=fT_oBBxTH6wIJl)qq@rgSlz5J;at{A z=?(b7-4&H?Lsrx8TT0tf$k*?K?c?~v#zGQ8YlVw$&OI>4jXj%wF;44N_A|}yjqbtx z9~K@Mz#it^L|x%fo#|uhXmCzA`jIPXe)IJccEJ4Y=84(JRj&X=x@ME6H^U zkr1%{ z{3)@lTvrPrO0)QXsfO5DL!Y#PBTWc|B;K+e6XHaizBrHzLpa06Oq^&Y4*Ou(_)BFu z>ja>0H|+$Tfnvza3k)fcB1xmcyQrF+SJ>ZbcldVHlFR_G-B-$2mTV;Op%d4MLF$_+ zwtAVE@3(^eKfm;-Oq`@h6ewj15H)nFncjmn6RGrG7GMp?A{xQ!ucQkV3uy+S;E>u=h$o06^T@8O zvxF6&t*4UBV4-?+a66EHo=6IOXs?+sh`=S2ERojy{9m;zErTHNvj%v%;9u=KGo#bW z{)NEc+tYowTFEJbrLXjYX}D06&3e(Cj^}7#;BO4goln?&Q<8e;E7zQM!)}-?AFSJp z;XdtVib9~6k|CJ9-$&y66Yc*6%b)DNZJ94Pq@uQy~pT^ zkPw(`SD-Kwow1m~GFZv5&nmg1V(iumu4Ss?f=OG5Mc;-qe55QS=imMMgWk`rOMw_e z+MPrO;{3!5Z^PcMi+F2KMvJja0_%n21(jHD^rmI6P(HDndV~Illxq|d^bH_AyoHRV z?d_p>t@sSLA@d12f;E1R+ciwTuz3`#jyuOE`*&qFH?_Yl3sC6M`lzHY8qL_4MOV>@ zs1X!)E?^o^m}^OpxVT;Lkr>#K!KcWhg@=D;z@l}xxd0>9NJS}VX@CZ!MGa-zc!6Qi zE|MCcgEry@A?LoP3i}IX4Hcj=EW-#`^k_2-c14>8=)2%3h*l?Q91J*%g_R-XfQHz* z)o3&f(nU=}@>S9lD;FXSgW+MJqhTCy_t#J{s;><#@Cm;NiU09>u@WI%e5PCv(LzH_ zeM}e`whMwv0o>w7Vd~_j5_~Rp8h`<5@lJtbFr+FfBrI+;+5>Z}3K*jEHqp2@h*6NO zG}9RjRtiBH21CtaA!PtbBU6QxQJ<{fagoqa(#x>HfUaiJ z3icE?O5iQC2oVcq>9}X%hM~GxX|Q@ZGlkIwO#?6#0?j0lLuz-Cn^*LkuqMjXbYnJJ z4jgKQK)a&!2hrUFAbOK7=+8;_O+ybcnCL{%-8b#^Z@OthVeL_ISMbs4!5q`+G0N$* zR@7j}!o<4#9=vuZ9M@;91zo>iT}y3$_F!N3_rM+VWReHgP%cI!Dycny<`Dq}s~!Ts z6e)dj7_=K$N8#!;*&x{j737`PGWVWQI6e@et{1kNbi{(~ z($yKsqG~y{NKAU@C2h8`C%aU|J^|VE^z-)j*vH)x zM)+}tCnA@v*U#(C1i$0*78ukY{rXDv#nE%#x@StFC8wa*w8wvgKIB*&5Am>yDQEMO zd+rXHQ@iMl49KS^n0VReC*(1-ZzxiyghC~6%q!`piH*_rxfd!4l)C!dFY<_ zQPk+Vbf2nVqf6ZMo|SZ-Pw)08CLCP!57{6NkG-GLxfkadGEa)7Kl$gH=3e=`rT5!f zikerq*!VnN(8i7(mISG{CN+r%*uTq8sRp#?<=9V62Ta`kB+7T&Q8HM((94`PT_57)Z9>dLqPdRA+d1{U*AKS%l=@ANn zQ`x`Yyolf9Kp6Ew?D#$}5%5npgVCza5c7s!L-*qIu*5a6{1a-Q)7_XT*e4d8-FK3W zzkM|Tgmt1EA-9vo*RO)NCt`v`osgSlt6?1}Dq$x`$J!(R0r|P|)t&oszLe2Cr~h}! z7u7moM;XMtw}rZ}L+v%$iBK`1Bzz@PRq-FBlBTO#c)EM*l0&$78}eA#YF_tdTFJIjF>-gxZt34pMPd8r{dxEkXC*Yyo|t5I8S9U zF&Orr{NO)JjFoV$GL2UGtS)B`DieQ%TlxdE4f%;WQ_Z6U@XsLGiO2el;MqdW9v*e6 zAL?@PWHTxeR-S!sR%UqjI*%g2D|`dugEvTnE23>^?k(yYWRSyA1|CWFUc}^y8})Zy zP>bPHMPMTRJ8ih6VQ4d+a1f+11H^+JcFa#CTj7y<{DLifUshy&+TD%{9KufUAmIW` z+Z&irQIgg@R#fI#jXa5l^!tm;Mh>B9e=*g|x;VRIx{qx0j|dL03~Y&iO)NzfD9$mg z^0}p>Zcb@iegdQa$(iy0{;>oeSnhWp<8}r2TQ+Uj1!pd+FqMg7A|+c*IuBNwth(Yu zLOe9q<;g>3z{sq^zot*_4}n5%UCDYr(}MYGqZAj&%Z_E9WyaFlEF~U4vhC%fb;wYP zmkybCo@#0AFU=wr2MmKs13vi^cr9ARI%_gRAr+91uf3RjzTtFenz0N^F!fr8pjMi0 zMViA80*u%{E@o|nhN9mKfrhLHa0kaPUSuk@=Gjjoztxk0sjRnYe=zg1Plo>| zD9g2$T!#!%x3b&C?wPW}%?R$(H2EsVw#vHYWoiy{K}?Qm-mX(bmFv={3G~?J9XOVg z)52eGViqDE+m63b0sr^~#fl!RHeTb0_g1y>I-U3(J~}X|+>{`a9vIv(IJ6&yyQV`2 z{D70kL-k$$J|=-6fXDlhliL9KmJ%&6<;u^+6}D($f%A3xy7^%mIyf|z5Zuc zw*O=j098GWX;YP%i> z+d0f6Ulo6j0<%h%JP3;FKLOd*3>Q@ za-&OImPIH5f8|h4mxj^5y>|V{`!F{rME^L#oN<4pP2KV#Hp`wm`Hht@)g(i`FA4kh zbGF?ZSzf~H%FndWCqX!Rjm_}c8?{e2<>Fx{e?l7Nqkj!c3X(Pc{-@p@EHL6#(Hx@X z8Mx|%?(&jqUKdkLFVxMZd4_hU@l~FJ$)xY;Q>)t}@n|2L&HaM9R_z%S-T0!1 zZ&1_}+ZA%s8(49ep6EVb+KQHq>Z7S5H zl_ah;c@2f7T_f3LcoO??p0u|;(#z^moFwFRjY)c%N^Dv-{~-h{y!A|yDIR5F?MC6f zT0w3?g2%)+ca^F5o*A=Rd48z6dx4lJx7+oJ?n8Qje8CX?{|RNAQ@o>^r~I|~LuB>v z2A+I{w>n-Y-s;xy$)1e5wST9;t~9UFX{Ly_wt0FUq-^uBQF7}B^csHJUZ_>mjMKHe zaw&D2jpC`ZBq~Ymr+7Dhk`m?bGv#SnXmGL1pZAll6Kr5gL~}cl`QrIO+j9Ls#1-}s z^iL2k0jXi7HyDdfFq-$ig7;}n9x9tGYy3(>&xnbHtAD1+dG174=_^k>ua8i{ecQ)5 zyG#-cK~e%H2ToJ(UO$;#iPmQiLHz+&XHu!FXJ)gU?-*sTEd()N`I_E_i8K}&KTdbs z!xt=0YVZEYDo#G*j9%&AiL|4WXvG9qL&aptV_b5gTKAFn@#k;eEZve zODPOU+1$6qp~Jzwu{ayy+Qndc$nxLd%=0Q$Y~L+wX5;qoG|c%+L%`DrgiI>K#;_EH z@WefoGxNKWAlrafwH8&VH?$#G(of1LzjNeKnnQ~bw@PdSx>Vk0fed23l+s+N1?(kq z6GNYDK*k6Ufgg|g68o&ncS&qNVGcgXwt81oMSDdyC$Vb6A1-IfB?*ca8dzj@>Binq znt{O3Bd)-mDWbzMxPod@Yy}CqL=R`FE{lRixN9ryGXl|!k+^{V$xYI8R)()Sa%8j1 ztK}w|##m$AtjPyAr%8Ag0-K$HJv?;qrttBIp?+FukY*jzoxg;y_A?!Y%6IiT)WYMz z5hbR;`&R-obYYUn^C#hlS$7-rSxL#Up|@gdS+6`s#7a-}4F4xASGN~4tB{e#n6I6! z4~IAm6i$FTx%){K_TCBEbzq!(5mWTG>Wjb4maPP;2iq2*tL~-=L_#jUys`5#>!%TV z$o~lh6magy)8B~u6R4_ac;X;{ku$W`!vnXYVQDxr7R6iTk89pgKb-nbC+;0mlknq& zxRmAlLmQXhqGZpGQ`>9Tdyql>(^8D55f>)>RD8FCC+WGVGk1uMq?jH2?9|J!7$>gXajcX$?aun?vOvpm3?q$%Z zT6+u>(J`Yq5LtL?Qyy?U@f6?@&PI5{<193l@T>6xUECnBrIO#~QCDjo2#KA$-x&kZ zpvBPhYo;BIM*7_Ep>0JY+Rvuv+ZR8Dk9>bn06}ecezhidF2%5BicX6U*mAmA5D*nHIViUtlu-1HY|N`MRYGS#uc#>%W$y%O zqn}T8__96WTfJxC3eL(U(uM!?nZZ7Lug_<#6ZPDer ztJeMGT(AT_5C!3|&m`W0jopnT^3K4)Sk&QcMY0sbbOK9l*#_XJ4tv!dceeSV(AS4&3yT9nVQmeUHnJvGK66idDR zp7o-g%f(dA*|Nh!6wBlHM8(_?060eOhrT+Zt1*^D{ydIv(N5&e9it>OrCkn5en@8R zzas~EYD}blP#;kwLM^5UYA_q7l7KS=&qC2eU{`oL9uaD(8a1Lx8Qm;pWyjlKQ=Nl3$=xvgK+3m-?tZ;mVw^cX59Zwo39)6225uo}hED_7M11PU#zXV&-lldyK?JGQ6BBs? zm}r$?38+cG?^O}7T)t?bBJz#!Sp`*(Stu?LkW7jzFJwp;-X=mWTCC5P+Qw^V?oOHH zMwpsR9$kom>iEbuwX3sTCwSK2dpthKJJadA zjQH1EjxbKq27K7o-3o^UEe(|Sb0*#=Dz91}ItmOWnKSh-)O)fY1zEcj2Ro@JPq+<5 z@47X-RNoYTLIu2BL77C` zbPwk@ydmJkb1sUE4a|5pe;8l86e~3B4U0FK@d6##{YOVlfN}dhfYa;Ud8Q{($gm*^ z6h%g8^!`Yy&QOJTTq+2%d&UNg-!&cJCy2GHes4rx>9CMoQ?P5`D~Io>*rj&6F1mPr7Y)#*fiShZX%NJM#R{~6JF zF(OIF9PKCiV#Pw`*HLwGfDLiYjCyn<=8-w88q&C6L~h{X!pQzY$YsyQEoBDauEsk>VX$nW084M{{B)m+d+gm|! zYt;oJ_1hdNLp2P>xUH%TVSys#V6d8{6}g9_hjV+^=8Ir0rK!S;uVQS6Ky`2~cg36i zy*7OJZ<;rE)4s=jhL~N5yVU%Cl%c^e9C`@MU>Fp=6lVScW4!?5^x~HB(N)%^E<4w` z6?!lBy$s+&^W>Bg(p4-G79)P22t!W|9}gIB=mDFeTuvs82^~^av-d>HsIHUsmXcFK zMOQ9kS-t&}^;DbMnk-}hkOo;bMradUQu>-i0zbuWzSA*N*Ot7YQ@op?QBBReVwl;f z)WvZL`1fG1=G}|}34O#~OyY1>zm_BQ5UUR^;o5pRNP~IAE~>kP7;7Fk;(bgDnV416S+nVP2ojJUCU>NVGfxczJ4(>GD46&RM z{0XW#pKD?1&Fz>u!C}QuHdP82c ze9MlD!?vp$C-g;d$jyU50^`sM0pF83HQ!$A#U5Ia^h9>71w%y&e-P zq_W~H{DPmFqLw6qW>UFt$QDje`8Em5W14qip7l1Yp2ykx0si z;z#}@Qj!Jh1bimIv!N>43wJZh1OkH=+r;rh z335E6Q*bPLubPEE8JCJ%h+aV_3as#y8^JFj$kwg1T{tyomR6yX;;_l$a#Y@g1jLA3@-j_!ks}?-<1~jF>y6L!neHS^uYE#8(5IFV z)H6t3oe1pYV{P%S>7*)=_?HV{hM{y?;R^Outn*qO1(kX>Ms*S*X|58ZRF%(M_teg9 z?W|J&={!yR&wrQ{&-|{k|0Yew^$R0>b7r(S^XE*8P9i-{jVPDZ)6;XhRJ(`hNXLs; z!Fsf=V2CgUPVw<|q`~d=IvQ`Ljm&tTT)8!1MU&Igp~6?DgxgL-k%K*Ma4lT`Ec2K= zC)bcH?e1NsG>f7N$n&ugsq7+WxBReW8lqXIsq^2arzc+bq@ER3ldKl^h{&D{4gDJv zX86wtQiK=J6F@9gyXtLn-{`>iM%)p!r*_i7As~Op7AEzL0a7$DSB2-v@A}dAs|O(Hi1NTv2_xu(d3!zd zLA9XpLlwFXyllbLdPLIstE4od&t$fqZ5c|9qU~1TRkgUgISS-re0q(60UTADiJZ;l zkzWY3^j^Ot4k$y6_6YHC9Dq0JjTV|3^t?DvH7a7NA~2dK7;Djb7!qumEB}bt$uZTX zw7UVBU5pRh>NNY+OMKnjkopI#=Q+pwL6v}!=u{xgNr%YCn?rsx7i}$ zc{aCgb+wr5V4JM+mFtZw*zsT{6@D{bo1Xpg^#mO%`>F;&1P4`VwzA zOOg}veW?)ETAnyUBA_eZx;^!Y@71kkU>W4(d04tgW||`{*a7uKsoD4G?TZn<+gu$- z_ZU1t0~_^&$T(~D@R&A+kN6XOl;6{kZqnaumQt5;8-GSudKC~JxXEKdh%YyHo2gOG zZb~tKGE8td6`lU7GEUUPuEZ}zAA!1V(9I-#64e;5xL0L_i2kmIiRc&ZAPpKWT^2rb zU6?*|`?>aZz5hfS>jk|pxGMUGNzlzcWH1eJDN=9t8i(ZSdzxI?43{bWGwf0;Z7V37 zTuS6z@Jd_->PGK9v*N0Ty=!+G-&>B`x-zuYL3q!+|0Hv*$`MEJr~)Zj3cr4lCZ58` zZu{EERnxtO#6bSJFwUZ06%#}ACXg?v#UXW{%W9MbT=eGqA4lxqyo+=lJKF!vjSDfX z(fQ~A2=bQqllW@Ei|5bJUtQaCyI>^=WsUbF4v2fBqg<2p>J$H3(FnrD=CKX&=zH8Pu>8FHHL(fyIAfQXhMEh-48XO*n|rbH=xAVtrfhe zSx^IR7m)AZ2A?pXH|gk=NXoDNn#z)F2AukAh-IMZ84!o-*48`zzhs6{%E_Zx+gdZ| z59KzCXKtAb_MxKieNn?;RTDt|5BPCfwi9tIep>qbY9lvv zHq+a$ImXtYXPn^#>m8Oc@@@Qq2JT|@)Pu~#V%`_Gr#OE^ZmZFPL6H4*G&2_p2R``^ z7(lDW^M3Ax@qzr5^dk>0}Mp`#YYg_(h*DGu@FKF5nu8url7DL5IR^)DtG}M>kPB z(_W1!mIJxbMAed!sf@z19-cy!p~UdBl2xVw74Vy;(<#9VN5~rwI-<=v9SBAd#7R1M z?mIN*T~8l(%__SB*6eY&fdgLeM7@1jzj~US2>gmO;vH=YnBZtqsWF&FF8JiJUSP)FLB?(Ee9}wyzs=!6=CAu z(eiEYJSUuuwyJq}ZlN4^9$wu9sq@6#1Z z@gw6NI?dYcVcwE`c_RIO9b`UYH|eNj%{eBYeICg}h^PItA2FMV>!6Xy&@a3*H`|H) zOMXXoSfWv7t6O&;n$Yk*ZuLcWKexNeUrQk};f7W9iyac;_0U#v@3L=jA!@ z24k$d+EVkge)H`m(U6QDYI=LnOuqo!v3o1pr z=5X+}{JlbhO8zgYTS>sG*-i~_fLUR`_=~ge;sm-u%MVV7!M-qF-s+iSdi3+9 zIpO+sl|6&+P6tPpLx1y0my-$Ch#1c)I=6(0sl3Qzw??AhvDl?1xIK)zlCU>{Zs`qXL#6!;CO7fA6J2 zESUll>NZ1KmYY>%Zl3Hg4Hsdq(WUcSs*b0VWz$8K(SZ@VZsoaXaY@IF(*f9~c#1$y zmU$*dtMsOo;X|RImn_d-kNAv# z#@hDt*_2}%eZOmoTf$u@T>jY@)1f!cCWx)=IO0U5?ZEDzyAw@o06ac}QG24%+u-X? zGeNqv4y5`uZ%(b;wfM%E+IE`yk$_GbXMz(%i9}JSdfsrmRo$o=`)Z<-ZcjKD2rfd; z4iThMCunXh^`dFJ@Xw5%A4wPC0x{xatVKgB`AS%t69nW7y?G~!N% zs1qFw!iWZtm2Tfbp#4=#|CywJ;BO1e-(I~};sB9S`W|j(0ts$jwZBnAxap!Nc~q3q zYsfQ~_y7b8-N*PMvkYPW+qB?sjd8z1X>g!gY1#7^0b9YsWExfh(`n-T%>N^nU1O}< z-z-r|STvYh15dxHpbu)1x-OYi$9~mG#(9q2;6IjKdbL1}^}1JK4g6`T@gF~Kj|k)e zcj;VwKaWkOZJ{Y!eTlEMsy3M_->vk2PO5*Ofrhqq@>NRAAwRlSgW;o+J}cr=n6u;` zWH5Zt+lZAZ$d9nfnCmpw9R9b_rsuayKBws&Z205@ehRL$-0j?((jK@-??{KfZS2_E zv8IXam3KroKtAUbE2WrN^W&Au&pF<0Dq#2a!ie~)ly$!6Bdb=85Ma0FfdebczBvL8 zTvKf9;h$*y+ZCp`RL}-gdZh^JZ?m#=q(9!b_J}3j{$>MzFPBtUt#Os(taE!=DWgj4 z7^?V7WX5t=Tq*+^D`kr2A5@z#E5P|9I@Ml6dXLO}3YM0oEIi9DwJn=kz! zo~6_b?$Q4JL5Opld%Y1j%r8wBkj}>Kips|bltw@ux16%VrSKb!Elsit<2e&Kc%uv z#uU}Q3PCy!^%l0L8Va}x_!M^!E=U~GnyW4s!PjukG%Z5KGcTr+nk$$@4Lib6h)&*k zX4=;<$x`IgjD6*_Pmb(stBgpu44aan{NEfHRtf3L>oT6wDm*-8kBU4O4~xUu_e!3} zI7$gB$=CnS{sP9AZyx;>p@R5+f^6AAyTS`Q-x@Trbp-QRk!$~y>j+&4rnH~fZYtj7 zI(sRvpqoWu=nX!~SPvBk3y|7XCe>Gl%J5Z9uLF8{n~#Oz)Bl(| zUjJE^5>_BK@Y~a1EQX<+!0MClObdN(Adk|vVZo+ti2?6Dhd|+pn4Z4(qMdOsD(m&S zPrJz;5NYMisrT>imc*0AO(op(BHP|uPanu;0akiK8KK+}`CqcgtT|hhAr5rq=}_XR z9%ips&Q2TC?N0b5Gtoj8z&GnEEn3BS)`=%wln8lsrUjF;?u&57clfh^y;09ny(;#Y z<)&VvlismP#FFNDb~=PBB!lUO6vBbV&xXwgN_|j=#2ud}w6nGFg1ewU)a_}CogM@y z8n88(Yk=9{h=9Zmpc5w+a>3@qflSa##*0;AHNgzeCdZW^$?4Ltpx5(De_GovKEkrJ z-?Z8l({5qSScX?0w2OAK-94Qkws+8tGwVxd$x4Ulj-p5TZ^PQwAUU=WVFw)3_^0(& zjhN0GOP;X#Y{#`UV>bvRR6V+{Y0pIWXaO+;+4sddouK1P5oXO1re})0Uvxx#T6?Z@ z6cn7U&=k?NTWcp*2omVlnGG2P83d&rexXF%yuid^hGPP*L6|R*%Yp6jb*W+> z=$hh4qT35e!V5y@Q^qR>6o9B9AbWv_mAe*Z5&l?o>>lQ|&zJsUc1D@jFF99Ciay+q zrW|-;HE^rXhU=%teU^T!Syr}pz`|%yl^PzFg*avX+M?jL3P1LpJl+kdDYi|}*hKgf zt>VNNQci|_=pT=q9y=b}9^(_~7?!MV_z;U?>jRe0>R`Dl<&e&!gmASOp~x>r8GA5q zG*Z^&IvMgfN1v)NihvG_*X6kVFqSgoZ9^q*Q;IU({Tba0EGm{p5#9OiW1{8livW)v zW-Oi2yO_-Ck%8(OSxc~c(Vls$9U9y8=j5F7Dl{orwX>7Y>WvsfsV!DPS|DZ^$fx|A zZBh^J^q(_xgoe5HOakNhOT{#){(~f+_BGSU>LNZ&TvX2(fScY;Jd+uzChP;(p-}nR zJkM^S?1^Bq2Is`?O=3DZMe9YE*0}b&?JJB1}N?llOVG$ zaE0CRo@>}#2N7-uxG}X4#JT#?!)UPQy`9Fm^uVBfq`7as7cR6ui`stvkQf) zLLH;(IBwZjw+7m++=@-Zi3LZNYj<$_Wf<*6gKWg1xUcU3HUh#+@X_-F8bp;HwNi!O zs0!v?M;|u0@bD_uFp(jQTf5<3B3pg>#-QVx!e5TOk!sEb)Yfk23SD%tq3pXI>U1oZ z(39HJUq_4;Ga4I6L2x^WST}K}l{J-vWq6<4oIYj!A6xdH6ASWN-;T79VkP%W_><4xBo)KZ z@Oq=+0Du61ZTGkAH+zfctQN7VOuhX2BYi0`L{_@@Wu(4-(6#Ce7>K$bJQnc1yF`z2 zEi*~Ty)k65nNIAyPN>1(@clno$!xHep5AK9B;MKSmDsmfPd`qg9j;%@R@l2Qbz}v> zGpcs4=N>11VvFodv?%@r?-$+-cJsAP1b=i}X8c}ghxbG1&dSaC;8KZEzd@5$3+%^) zh)BB07+SV(gMjh&P#EVPFIZ$0OwA&b4?=-^PmWVF?pCKfU#6kT-*0mMvulVU53)SuVecy zA>=V`!kqF^P__oAmAp=yMd=aVN;h9|oMo8{f$prl@|>XV4!z^7i`cb47L{J$K*1S` zjGgqb90UsX1J7rgB2y&m`O-~~UBBhl>etpxi9M^8dmMYX+OOHawKWzt;%shOS7PJPhG&PZQ~?jqs4Z#c9ED|#v@ zFROaH#bKV$Fp@tDe{#(^$r9c)0@chdKSa`}=@`9)!(eD%Fp)(Tbw7O3QRoX&iPH`8(4#{;>iw6!qa>?zk zJ6~Kv`Yp@Ky**8qW{-IY@;z^@ZENQoQE?usuntfulB$EvLhioe_o)%zFph~G&QgK{ ze~#{|T@=XFv=xtOOC+m3FJ5LA3nPVO%;ES>VC_CXuVC4-|1N(F{B|;whA-=Cxp0yW zF5r^gN4!qy7j!rby1U9?*1Cj5Z)?N3@C;*8WN=K#vXtzBA^)49M#-E;B{_L~7X--% zFRt^C&_xzb=hs9rwQ^=_Y`xjMquM~tGh{`-{4)T6Qq#dz-VL*$TW09rU7Un`1q2HCSE*9)wXNCI5WuQ5+i>~o{DMvuRKQZih{D|6t4v9lr&96*SaXZ+IPXpqW?K&Ik zI{Y#?=IEN(93KhQk=jFjmKnd-s8_T$P(ZR=?|@(iDh2z-TM1$a=A@tWI(td>9P2ho z55cZT6m}1@yBp(`NArFKmQgt`HZVAi4!_Lal5)mKuvM2911?l~YxaH2hDx7XN zR3B8uQKU*rAa~H+5CyF?z_+VWX!f3IJT?S0dLk=WXJ`ODK*GO9m^9RE+Jq})B7`Agz@)Mz zBLlXK=dIM1$v*~J4iVPb;Z*(Sh+(Kx%K#IOtA>kf3rMl@C!T7_>)yX4O>he}jxlod zI`5%}dwWuX`Y2|c1yO)fK0i=(rea6#Mu{xkSdvPcD98*>o{mQ$E+$1dh@)KyuHk>rPA&@c9 z*)T(2Z53-F1TA&_xWs-VG#Y06SKtj#jWYI`AsT_$K+|Blsb((cG&c51Z{s3{Nzv3X zQC{*kG7*-6iZ=ETa0YtOz=zN_5HZx@VGs=?Y63w67~gvE$|qHL?H2{lvd;d3voNK- zzB-5E$&&W1wbjN#UC_yR-G9SG-Tt4A%jmC4CyhL}(vbWsKeE>XvVJ6To%0F`iRr<7 zd|zaeZYc}kwB~8G^j$PhxF+a!(BbZYz9w%!xATh=a(sXob2jo0PoTISD0l_@2G?ft zk%`!|cAsE#EPTD59sHaq1b`7iNQ0yXR0hsqc_5AfJ%Wq~9y91^fYe}x!HJcDhYb7? zfM%f6z%l{U1`7tIG^uLbH0FZ)FAg1F26SiIx(B}9kxLchy-W^%W7r-7ERZ>QE&PzL zD!9}|dJ;98O`2KA=U$1jWna#z{q}+0Tl9oXpw4NZb~DPqCuEyFyS7B*%Z+WK1s83U z_qXq_`L(ytEK|SniNZQ1Be>h5=Zj+tu2R?Qsr^phn^*MklyOp9*ey<@N5h)9{3mpd zmUywp^SVDwtsds-5w;1(+xr5Xs>=VCZfb$|UMtH09-4K5J1G6Up@3F~B&GU=uNPOG z#ouR<#T*$hY9Sxv+`blW0$7cPyrKcBjH&zSJK|O0{{ME(b$wNA{f2kZx$`=H{>86# zCa|nwD_isjX&W|R;`cL$!mIyub|W!IwI2~Pe57#SRyHiVRmKhXj<3`hNU;O#@Vz6D z4W!VdH`YfR#qWFWe=b2IF0+m-5$P2t*Q>W}CN_)3GceJv@1oV0 zKaEj_C(a*tps(Lhkk^-TKrD@p2x{w|>zo20d{424!-J<{Kv(+1BoFjI(EUL41JDmZ zKjZ)T(+ErF-!cFPYHa*2_+Cb6!L;9zpxwLK9S*huc1uhA!rxBlPsM%S0Z{ens{Td> zbbDJp`Vy;lGLS{Wpq2$#;fB#lLmAesBXPdVY$NO9*sqgi4IJBpsfl7ErEfz0zB;4Z z?52~4FR1hIl0!La`MpeaogXGefaE`-=|kbtt@_HNB0g|I6T0BdvK#*FwOpbWY^$~0 z8bhCz2R3A_$)J(_vQ0rZ3rl%eUB~_^|7$R8CEkPFjwt(Hct4c)X zf3#tjRv(`FOOa<^Z(S3V5Gq~7{$^(#rknGAx>+ZC+~of`J(~Ek22bOi{Mc6q#$YUX zyL!)4>Dn&`EQhy7&j{aw59rcx&1`rzD4xv{q8gztbEh{Ti+33aq0s!g61C?h)+1sy zvfd(|4-9T1-Cy|lac3>5zn8EbXAn{h!!k(bG)edM{zN_46@sl2 z|0FD)J{AEA=Kp)v3yW_A->v6%qZuVZxV=RLV96R!Jd_X;?&$e(O_KR zjJn_+Rgm%8!v-X!bMByWLyi;<%E6RB#j|AAMQdzMH<_cG~N zKp2t?meo6li%9LVHfXZ^=~wO4~(1In&Mm*Ho*I49_0~4k+(^1O-4% zx|2eUWQe&)c_Jr7pfo9W_NV?>OMhglET`_n$P`&qn##z$zDBhQ0R}Z6)ilfbo^2g{5fqL_Okxc zwR}q#jDCy*eBxiMdAn2XQG-69e-F=1rG@XT>K*lG?Oa464}*Yt4lG%^jz9JK1}J}a zIA_K>NIl^1Sb^vgMvQL!cS%juOv~{8#Sa;4O_>`zy6QCN=e6htX-6HzBlCjnVPWvQ zqW{9Fn&UF#v|){}f$sLVieIw@i@~`#&Z6M9gO=oqOy^2dpu`*~|Gtyp3@UOxjb~ppv#nC;pnITtmsOYV|lh z!?NgKMat32IOR5}cLk+Wn{sQSQK!%e2Y*Nti2h@bfBueU_&haSEvl7IBx)5*?se-X zn@iTPBSl>clFz?SVGNVpw}aVt4$M98rICtJQ4(hfu6f#X_G<$Zt(hvgCc+Bx($Ta0 zUg)WY?zIT?8d&q&ZoHqzXHfx*G6AtX%2&`oxGTwdDDO8eIlJgZRnWire`xf|BI@~2 z=CUSZ%)=Ze)kM1H(EKF^Ql#EYV?jW*4;S{@a4Oee(17e*d^}YDbtPO4 z=U$ECOw_J_DOD`-Ee{$MHQMVroEK=Kwn_LSuVEFoqV>o!VXp)Ntu%%W5|0mY7yg?U z!Y4Z7($Vb092k`dW~2A;Hx@mZFbFhwZ?g|qxmH0PDJNNQ|0WIl;K6Q7J7TW*{lr%^pLl6xrGf6#zki(Wwq<;+3ANl9 zxIF!-=hPqZ^bjvuH|(IA_0rv|LV)}zm;7u77vk+d3t=LGnA2LXo-VLDV$j*wirsuj zqy06Q7zKsJiC|d0rS26!$=S#2JhUG3_@7nt)<|w-A34{`f!)D^m!DY~f%^jDP?StQ zGA4@eUP?~5B|fEs{p6!CP(WN%619WXy1U@_U#CSqrqu(ZeLheey@n&o8kuwrSD#ye zeyt*yxTjzC9JFL;>0>+_zYggY;qQjjt@Y4MrOs4Azj6;h+ZYZLr!0 zR#wuKN=yHrTjqysZpyHx`lYIgk6I+@4oi`zfCsP)S-=+Y_-k8sjq-7SJGYi7iBH_E z_Q7gtpW%A10;?>@7`m8N%`P5oIoZ?1dit>%2+BzrV zSk0SqhN-J|K~*4cuU$R{4oi*doqfx*GY*on`V`X*U4X^2P=H49D238YZzRfNg2s99AAmcCu zMxj3#perg#lV#4`XEf(@cgNbxv}DORs|j;Y{2O6&X)+590c}8SG|mm65h0CR;{h$K z3?OaGZ7GTy5gTPjaUn24wy2xv#mF&A&NE47gxRPFxD6Z)lq0p;8M+wF&`M8kcA5dO z4TgX=r0l11%4mdl#3+YnxiY1q0Yk$=05m9M2xBxG2pL=g{(%o;o+&-V zf6_KX!T-Y4QhI!&InzR?&$Ou5&JtHx1Gz1zA}uvEqP)5~RIlIW`V0hb2wZmVlD?AB zq)Zt92Np;>$93+$cKlBrAQD?vMcbr_Nlr#;!kIOJTdO{hX{(%$Ll$Q-$Sc zRg}nWaBLVcFaesKeT5I38R$31!TpRsws5rH`WzDc5ARHN*-@7v#C8Z6{M*&NAcYX% zfgnPl20ym{ES4{N`Rv>)SEdF7wE4Vjx=bqfKdd@UFj<8%jheYV%vqiE>L>EA<#-_1ro z@+)Oob%5UAt8omqcBcZWvP-0^594mqw9KK6m8a6fn$BXeD?fL%XfnJTU;^s>;E>nO z1%#)tqzehSBZQTSq)!%k6#X%69ax(4k(-Cw4BU51%;S|_p^v{8-(>vQUsMI?F>yVd z4Pa+UExkY|`%HJE0v7RCT&dsX=qZF9A$1O&Hiwi8ET3q)={~5`YviB52Hh5?bj29~ zhdwqdt8V*8#(LLa&o!>$O+z9*KlqyXl59e4Ppmc7b7ZqKTzBD-_0Gzc4R1GxYR_P|~*rNuW1s^srIEq`Cle&X6-@KGYlK(}))6tvUqDXhmWFpve;EVA<+u1VF)b5>tz49S~@9{~UVKLABQd_;Uiej`5;9}^!uDm<6tX19UdkM0Ne zBl{s)nS?b}@cxZBz8yabACDgb7B~bk5JB)!@L}+B@TcX({W_v{I*2im(*v8EL7<|i z4c~8VT>pwj8*Ad!d7bxu%itM)+12t7;u|*`I3q_jXT_Fy(atqh?e)1ko zjv)dTtcD5-+gexkMj<+ll!B%@#(s283ATcO4cBKa8i`cE5@h@hB+!PTutQ6L@tF{d z74o?v9 zOfWM=*TBObT(IqM%m-8vI&Jgcsg=yFuMV(4z2+&dlcsz2{Ru;yDCAi=aMxdJpwpH5 zWwc%VUHfHFAG-$J_!e?UrP?g<7xR}*S-P)xZs%S(eXOIe%qyq7=-j%UYu)X$qRUXh zHLMr+VwKil{_g_gDDi(Z{ifFZyH27yCZ+8#g`d5dYx(H@k2+;*KE^W(>+SeQvVE~Lk=>hLzM||e_FXffZ1}u=j+T9HrHOQ zFs!*^Wh^bf#tnnyFkiI6azpkdngnL&v4iKnp^d3wMQl^$&MZOti4Un3P zwG6h|3zx}{%dp>j7JazD{%q0I-bkKwI=F2r_1(RqAk910C=OKY(Yk#Ye$jBCv7y8u zj2njVVDs`2;i?CJqm8mNEK5@><-eR%#BL9I0Y8{#>vU^dzlHjiIy-B_7y1`K$8r6d?UY#k@2)wQv4e00 z{vtSdu?frPf8uWv-;(W98-iDcBmfmUK0HxdPz*OZH5hkHnV5>!kf16Uk7^UJUJQlv<(mUv4I>YDDtW5~@~M1{O1~Mh z=IXf)k>%t6jjdX65I}MD;Jsx4#Q?Uhb|03gg;;^E2^M)Sd>N;z-F#GO*em!8(@F4Y69=$fCd5#1Q-Y~5MUtTL#3KxlISq_ z%wP^L7hQ-(p05cbWnbM1n&LJ5mfyUul#Q=5pW)^BBKFKw(sJQmSNzVvOZq$99R6`v zWnIdvu|0esCw?VQCsIeKD%5et;NeVX_BjZsHr}~%?mgXomaUGGnXV-$FTp#-- zf1yru@?dBF7rff(xBQ1p-jw5BTYf*O%Vk;rC$5<_la_L0JWRFgi)!u$>Luc|D2ZHrbz zeFH1e>bcKkti$jAv1vz|Fr;e$-iehnIR3GN*?2FwK-Awml3FbV(kvLdv4rf3xTl zdCZ|2sy^BD+W#A_2)A9YEM{zg_pBQ+1RB~~# z9T^-VRw?8ZKlnFrk4UI7e&EXMT4C7B?{7hKTywzpc!UWi?kgtXJgzuk-d8yaZM%QI zIoJU!==!@3p{e9mYP!gC?o|%^QESLC7ct+ZwW`cBhEVEck}WF*Rn3!dYf6C(rE@7l z*At5Am<$!?Q(t}!CIH&Wd5eQM5AEN4IImhG?cKgY-w%;xR6l;2)+4;QGX?3PeC2pPft3X;KPn3g z#!+9!RH))iLF^c7pbZOo{||yGx(>JZ^^TS7Z~|bE4q^J6qK1Gc&zTSKT^&@f%|>y- zV_Ed4=-xFB0{WmFW*s{>pmxzPiIIF1A)=>7pint5eI>!$Bd)0V(A(VbQQ+lu?ya+34RRdJ%JC$>7wLtTv};h3AXEdGlrdCm2JmFep)<&TeYH;eB8NMPRUybOw2rY z6x+8l5^}ps?&<_aVgvlX+OLn>eO%ji8$Z5f>b*-5J*>=HteeZK*w>~n`cKlc*s7kB zWF|@G68dAE+sM1Ux@4LH*t8S`dgczw&!{<1S1mRZC%C{l36CAXax7^Ict5td(Waiq z(-l}1)T15RMi5l2c|uNv+c%P%mQJ-VLC%<*Lztv|Bb4!|`pg=1mfs)RA|Yp*{jrQO zK*_cb0$yDLDVFayuu3l8NX}U~(RdFe1+2Y96Vy&e*rBui`oVq;c|cz!yho*wBY|mL zWV$lIANhTIpNgjU@Irb*V_i}O;sb*|tqZd>E=d^FMJ!jEWtK4~B#OpO1W8r^C)Tc% z2HI8pbI}*KC6&!TR|-U8j^Sh5Le$nHU#Aqp!Bytex#V7>I?WSG9~-b!aD&wcIYSsiyLT+vjuF#HnvcY~YJF>7}#KijZP+l)pyTLhsbQ3RE?vpbUQ zbCrk&&xX^&*cD2gv;6>Zugr<8xm-ynqUO@6Z+vkjj%!E=k4(vNg3GSE0#RH|EPfB0 z+b@0v73aysH53$G4(@-Qt$6q&b@%6~zp}B6axiY#H} z9VQ;>eu+HqOHA zx_SgcnHXUHAwwjDpSA66@Q>={U=wmuw`*BBoWxQ0(r!V{HN-n?u4rSoosVZeHkD{~ znthf34bb(*Jzu%$4e^bAazH9~icFeEYVYhZPB&50XugE|A5Znm+oZu@0T{Wq{ct!T zfmJVTb0TGd7Nb#<@GmA3fRPV|jT$?qOVNrf8=4uJCq5b+PyMWpkRTT1V+_XZ1Q9;H z*bgcdFkHqY)fg$V&fmUdBL)pL8{8RSHVY4KeUEwuuBKRF zqjTaZgouHK{fr%AN}@PzVQ{i?FZ7lZo(F(HW6wsBlK&tZ8@d@tGstF~+eDbxKAy=W zUctNw0{M9s!U&M_Mb)+0>&c|eg~KF~

    qwFqkk$4(#IA1pBg&Zu*k>g%LA@K-}sp?))6f##K7XMbV6aQ zsvwk0y=wdEp&GG-{)7;$1ML@x5%kA`YJ;p|W)uj)NEw8%tWF6888M5I5j?bKIV`b1 z26n3bF|gD8eBn%-`ItWHn{m!5H&4CYQinS5<}+AE&woSOQqXp}63B7x7l|U+`iOkQ z20O`z)b=tUReHBC>2;!e{yX572{}eD zYur$V^A*cIDQKe=o7J+Pe!jmzHr_y@Luhrxn7J16%iJYf#ovr#MG>KK2raDi z(XospC4Em&d5&(9LwT;EGaRHMJSx!R+*tBz1dfbxpRC2hE ztgEA+tF{$Aq)I~u1TpiPxtFq5eSW=^t~}0ghS@HHfn*fK34` zL=8eW2k^oG7NX{UFVJTWsmmwWi1zNOa{{t?YVDL788!}5FsBClCL-^q0=!Pq_@*K6<^sHL zTy;->HbCd;h`I3e&V`l$(b@Y2#;dr+L5G>Pc2T+iGu+Zxhldna6WDzI+vrjF>(LtY2QQ2q z_0#^&uMNG0FQdpe7+W)m&g8}2nQfJLt+=DNC8rM#`Bi&D zqaEg_J@3i#%C@TVwX52y4jedebz3$59yqXnbz3za$a=-C#V=M?R#v~`>dV9L+r6sf z%rCE=UpYVgC)N6q%G5RCpI>oRRe9Cb2O8TdE9Y0PX{&5(Yip|v--u5smO80?I^?yr zjm^>W^MgqbhP|&;J@LfVA6XdQwW{h*SAXQg;SsH?#X)>^d*ywhnG^0S`SqUF7qzWk zQ<;;KDWW)0sw!_xr7Blew(V)V?LgZ{Zu`-J{_ya7FRHq_o&P)b?{BMWPpw71Q~fZ+ zts5$HWPK^(^=nys8ftTL{Ak4caOKAK{o(di;VHRl-&W*Z#H7etS=AWcxhg!oBF6$>IwHqMi@nwbTHn>Wyeo=V z_lMIJ;fYuEr%tX8cU~2qoGbF#BCjO+Rc52(snq<+RQ1`FRSQ>Fg?CnzrTMqtax84b$<%h3V$=)dQD|cy?Ct1`*I`B)i_1*WRdr$J%+UQoHg-H z>z2h=)+|%m?q)V9zQlvZ5YHqNEh@7!-m^2_k?c~bX8u-M@Sc*qhh!sVI}$sRar47w zh@v1czGmsB%?%qB1YC^wr!t+1%r;fOaZ7yTviQ1lHpka3kFRN1!z8qP{2WiVC(+ZJ z)p3_2t_B;=i?7=PDb(!ZObYK|HTAZ(=Dt&9mB=KUcf{Eh_<7-b4?USgSGE;7>g+~` zL&Y{bGMii5a-*cWQuqOCogE#4`A>C%CU%N%YFxUdA--l^eM8}7vgu^9Md`Sb+hyz4 zZr-@Av9V!eeC68sIhz`^7E9~vHySa_o7Dqf9hF!c8FMslH(8|$?CMEoRlKgdyMy&-Q1!CLShpU+H?Lf` zmdT=4t~o!>vmLAhD>gYJ-%%e`F}}G+=bKe?dmYhR8whQ$`r(oM}q9udhc+YP0rHPeV zQ+S86C7JGNS1sLLNt=MvOsV$LWy=~iZCY8^*uXm0+@!kj;w4)TZC2P|zc zo4O>)S!$uzh=qSAhMIzno*l41mIalw zGaTY{x)i->^U}@dY?2W|54-HdR5sPtm28P>wf$~G_lt~liXOWPyn2nn0_9PM5rs1r zq@~M-1E1*`WG=uKjkns-_c5_#Hk;~dGxLLg{7cL@?}i10`z>aFj}vsRtnKfc;wMa} zEysr@^kE9n>A2#yzj6Am(2CKew}kPgbLbNO(rNuq>%Lt--pk?V0X5+-eOGw-=(2Z* zZiyg9IPHy^R{!JCk>%g{wwpdz?l%49KVC74VMhvg_2Y^^(665Q;fGkm$9j%}cJ&U? zdS^hVS3hd%a=foyMWQ(F77UgZ+G!NGxiv@SY#i<^42~17k=*)`X4we1a~HzK(Z3tp z9{g^st52@&ZQepxyq-SX(Xg`L74qY3&5z^Sgu5QL3_4(w*9&vY<^m^PXp4FI-PNDF zCt8=~qoeOCsUJP-mfoYGu4K|_;TJ% z2w`&|+y*0@tv7Ik-_h5vw)hNT|4Z106xLrh@`E97V&DP#$G^QjJx|_@Jik7R>eIq% zwrUU`&%k-`yDqHexL)WtFVC+}fcdnrIV9@%^;s6juMc=R{%Ri+gw=vPe~nt>P|AQ4 z;%8sQZ-Z~k@sIGo_07I$9Lnoj3FnRRKb+_Pr1-f@ISxJ2SH#c$XB-Zqe^>nMTgIV0 ze<6O}DDO|=XFoCyqvk6q)J;@t$tnYOybX2||HIfK9`h15pjVtL86O1B#sy$+X9rWLZJ}B*hYPr&77! zSjpwFvmZ;%*#
    4HIe(e8)jZD|ywb7CV|ki~`d=1(W%SUd!FT%vOAx@XBK-1E4m4#&H!xbvsxEIkH1MsH^-buDn2G=27fQ;)?TIQDjBh)ZYb zEx<0Fb#SV56O)J~E=d+ICzg)6(V=GqQ|2AG#!7ecDdLmx2kvoPmi`p6&}~|b8^vYQ zDkqQRYbYM}Vq+`t2kvxSmY?6QvzW)F{A4*R?!5O;yiNuRvz2;9nq5S;0HOzf?lTZ;5}3xStdJN8Ilbd@Jr8U&!+t@PA$KPRMyu z@GYSKMDPc2|ApX}pu_J3^P3j03cee0`Su@W{sng)G$h`MJL@y?`QYU^M9jCCP8Pfy zwpt|kcfcD3Q}%^|KZ*NAfWaRPBg3kv2o8aY0mv0eK2Y%ONm|(sPG)izB^znj=VcRK! zAH#jR-~sT@7R>QgfA@N(QfGlyE%fcM$ti*_$Ndb!=iy#2n4RPX!QIH$g@PYMx-Ejg z4XnRsJQD)pb_xA7#N93UIOubY;Bx5mVZkZb?IVIYbNZy=x({RHrOp-+N*ekX-8 zTR?9Y%(v$H1b+`a?-V>2HrXpUhP>P^nDytNVAh%Y1hbwzEO;B_JTCbE!dA}+z8x~3 z7tA{Rvf#&fN1mzM+dwZ9{2s`eCYa-7tzf>ybC%%iq1zV0Cx9m*xES|7!B^vc4KdQE zyG`h<7oQP48fhI8d_U;lA%<+apAjE#@L{3PgZJ-3=lB+aZ1R)NF`WEo0qfs|0Nx0E z9O+R2xH-a;1jeCQo%e45FBklu&}XCIXMt0KSx*n`}p?^K}`JvE1hrIqw=tuC1S|o zm@@)$i8Y0VTm)0$6=w8oKQ^`1Ns`lHvw-Xj+#?E=L-Kx5Zg)&`J9JdDVS|% zukb7d{wOgU7u@HBP95$ih7Mna{I3c9N6_KhLjOp{ zTQVcj2QcrHKZzLfF9QEmp?5>(9HDcpIa%m;Kn~yHCjW0iUnTTkgZ~`jNk;z`p;Lah z@J~dW?h!g(ZBV;~z7F&~Lg#NFy+`P2&~Fv|9^g+4{xR_9iKAxyaSt)_ay^oMPhmkXWt+#vMNfWAfOv}Z!-&w}16bdJ~Cg?=jV4x!U-R|>rgbbgPUdd?2vyWB#* z4fF$o+15WnjC}1wzP=*#KZ5?CLw{QEr@`|V!LI=0FqV#62RxY=X?+ZNJb`#Jh;TJR zXIY;?3|q0!s1rKpHEV=E2lS0XKNER*yU_U^^(LV+@0~(#2R$Qn=KXS^-vs(q#L$6i z+%G&(T|G?9g%{itLZ@y&Cx&i^5ck(Yr*3}~`p-cBi_obX4gi^QN4ptDjJ#8~aYA1L z`Y}SMZZn0R1br?sbcjNSjPUTcvaTSOarSDVe-?G=LxP`zKA#Z$JnAr=ojW1A#t^SR z96>rUhb7{haA)(gn8^_D#l2kcJlwfvC;fWdIj<QJJTjV@w)|Ii~Fs_CPHPfsb*Y0gd~h7LO8q7x?b?< z!0#oNdU&(YS*Chz3;rFz2ZW~uSg&)1{t4k{{y3MibFKRYv)Vr7;D-fM2hNl1JnAQc zStoz#;NJ-D1N~19=K7y;uLAv52Xnnd`ujlV+{?=6w|j|afgW}6EWv%C>$Na=i1lwk ziG0r07&itwznefz9?r4s9BhkV>i>2JCj@ivVVi@y1uq7Dhl4K>%sqI{+pK)PY^vv7 zkiXZ#`vg<|M;-hz!KZ@$83%t}@adp){zsV=kogV4l=E$e|9cKU=Y`~F|De~}z;^?4 z4rk|hFAJUk`fCmjqc0;5`{z*(9xE7Kv6}4QX@Z#+=Y>{&jl-jVj}bB%_e`Njfg2pW zQt)ihHwiuo_*}v43psbRbIWal*&H$s-XWN2>2*Ekjl_F|UIl!EgWn^V{VnI9R{nrs z%Kwyu_1a%#-Yay<|B7JB|GI5gHwW;Zq~sU3ud}kIQS~TOqX+Xo9)zRWRjqjc3<@e3nSedd2UQTg*KsVwUe52cIN( zDd_wv6M2Xi3g$Czt`$kATP>LKH#&HWV9I~HgA;;j=XM8o2&R0lIj#KNf+_zR2Xmj- z>i=E`e?TyG=9<~+zh5xrbM0#7e_k-<-|OJ72&VpDcks6aQ~qNPeq1o+a}8|e|5`BR z|K7oW6ioU5>EOQ$ru;}rpugU8gUwfgK1S%o;{~(a^JJbKSi@aKenE9iR95Olgng-(5bAb1`ypTSVhBH*7on0xM|7b9Kn zI}q;!&r5<|0oHqr?C9X|iE_QJXL5W#&ow^r1i@tEK7!qUILYCucCg-`1T*=$zhL=S zJ3Jd5yhSkix$j{4I|Ot6l6COKf~ngT4%T~{k}mfttem}qF@;tiaqvf-xSw$Frv+0V z?pfG9jfVtJ2mMh8e^+o6bnazX{$C2_nESkg^BUGcG=;qSGawD>w?wy%MXB-aAFQ=zdkb(1}+#ao0I`lVHl>-inoz5={G~9o!?B zI$Y}D-GV6xUA~HNu!duK*ymZ4#eE67C@!wq>E_|G&rLWlx6ect6JuRW7sF-O!Yhce zW~5t>%dY9pBbL4Kt;8%8xOQB2UYaJRk>PgYvh$5TVj2qWT3l>LbT{Ji>liU?YaR2# z_88#>OnplP*W+F)_gZ%ao#h-ofuLuLrB0~gDP z^fa-QQIBBmjqD_5qlUW-7csqk#8P&91iue=ZSyh!xOd`WnR3m3Be9g}O@eE2XC7?% z?h{Pfeqz{=?si5@l^ zh@7C4HFaLwPoPXBx|B0+fZTB^LT( zVzK{vhrXFu$|CL1dx)i6c;3Rwxs!OZk^hiGf0)?zV-?w5ojnQsI&-!sWA5!1@h6_~ z4OPDQSkaWVKk;nO4dx#DtkAy)ZNq}2pXMi_asqh@)FB6lJ_``5T}~_AISQJataIAa zk!7R#2R{ph<(vT>GTfg3EWl9UDCm*6^r_H7OnC=HkHmtrqJ>z@ORoh&@=uTs&0-FU z4jrz~jIwI*!;N^o4pk(R>`1`Z)Z1o$*idkysVReR@w&nPbACvBDw+tIsGhg5wbb&D z;}}La0~c|a9=M_S<|3>F*OBI?I;?!R4C<5Bn9{Y#rRQ$Avs=9kmn)C`iF8*8F*10+Sn&U2?@a)!s;>RvbMCnb z7YLac1R1=6ggFxiL`A)sAcR0-2u`hrkOYW^BqTw>0m7uzic)K>uTTLMO9%U?wJlC9 zidu&bwzZbFwo>g=uxiobtF2bP-*4}=&)N49r2Ss|-uJ%$e<$ah-(Gv|wbvfbKKl%J ztr#N++aQnA4&|{OSQh1d2AeJKkrT+fN%F*SWO%@mhh?TO?_OYA-t&;hwTB6&zB@s% z>-z)9TL~KVqh3b8?*ZHLm>|_}j7!F_~j$vacJ6EsqSS~Nq zOk??f!Dh?jGg7X7Ofd3}f?&&A3V9qGl*h}vkv9x^ZFxs<4YtRskv9lTw!A%-e!N^6 zd3@(#%j2`N-p1e51bHu5@_0Ej@)jn@&dA%CAa6R> z7QNNCCqdr$^MY~8%b}6CJ3$_w0re*D?gV);OCIMGBk#Ecd3@%>wWJBAzE35{yU3C^ z63j*(-{;}qpRYcIJocptM&7{$c^!~f2PU@PY1oXs2+kW@9-j;F{58SI^T0o}valI>;}hhKhrB4#O)&DtB*=T-lEq1+Deu^wZwnQ|8<$V)||^f!~bC9f-iZx}lNGn}X-m~z_^%B`A*`!Jli zEO!DnQ||f%d0)xV_b-X^Rwu~2r(S>aO?i_nd7n>^SBpv>G{SH+`rVo!Z_`5bHE5KV zi_Pfwl>~WH#^Ku|CQ2~!b|uJrdXb(JDK8(Jk@szp$8($EUMQc5zB7UHxILD@w-+a6 ziD42R^8Hx&OwxzoI|!NyM%8CQBaTVJjz)Zo2m#dZbZkaH&aGCrJ2kk_zGTT`nG8Hy zWap(ba6gKSIH+I8Q68@+teP$F+OzTfj1ht~1M>@_ap7cs>kFqB6%?r)C-;+hZ84q; zLJ2*ujmP*K>uCQspM-gl~7a?Oyju@2^2-;?xaG`8^C$;$^EVV;Gtydmq; zQ#$sSFqM>kznn6vY*2Vq&!DNJ9v}4Y?-ap*oGx=tsCoE}{Ef)9yx^47jDe|BGvm5o<8>X_15gda9c%;<53tXjV| z{3cqaGxZ(aGX37s-ln3yPs6H?A!Bv^;%{onH+2ljN^w#~I2l_@oo`MXw$3>NoADuL z`(lffYwgs2x!QNCeV3LrZ*9^Wx}8Yf8QU2PeE+#l)VXV)_N_;Ma4Mf3jq#FHFTw*N z!c86ha(dFzoLI>ZmRF98_3QUbU8XKgOQ`DJA0zMNj*It98(Jb>6wd%E-Z2$><5O-$ zWT30-GGy!k)ovm90@I$zbPw))>??;_muKy2_|~*jA9v0;HH2}bQ?xd!ysqGU_)BE% zEj&vw^3({aQnhK-b?Vp|OFD9J4M)uJzTC9DqJcL#=cHVeu07;!US2dj4Yia6T`FEK0C}G@}GsI z<~CY&Xp45OXvCdp7s@z-XGWZJv3I#wI|4hX2|F~p05p#;lyTs1ByiA==RAH1AoF@C z??NWHT#V?MK2+F3N(d`3Ln*q36#ab5i}P`2h&l}(vBLL)wNG-`lA0WT2(-w3*!6`k zwAXOdD+#rC)jp*5p4x}W7ULIZDysLru&LA}wNF-iIu$`dYTsAw`>B0@wNFudzM#Nk zTeVM9`*gME%L-hg)t(=BWBIA}gVlbB+MlBKe3gM8PO1H=>cdL>YUZ4Gp)MDWPWaqU z@u2Z}JgxNeys(<8Bbr`ZMV0~RZYNwqJV4V+iBmN$BTiFKnDJP3YzBH&l^NF_Cb?i@ zd~(<%OfQX3aJ9zaq*;8#Hh?C1k}57kNm}D17^Xt6D-Dt=i9MYml2pUza5~|9D?=@D zXh7p4CuzR=TFgmf0}!SYSi3tBb&|uB(>I))pNea>RU^J`!HY4qzfSG1ReQdY!2@r# zU#0dd)qdL=JzkS%Jgo$}MMd!fWyqi`_pIvPlW;cSoOfgV#7R}nEhZFPqP~36^wN(& z*S+VKu}#NodO7iAjc2kc3N)TYJVoP*D&QiGRr^lWShX*|1jl(?MV>P>uFk+@0O-!Cagzg>gJmA6XXZH^3G(?kCHBV8P zC#L$~(cQKNvxT}%a`z|(B&dg~oG>)~Q8l_jKXnr1GH_|>{^36m8(t!$;rxt?VFY*I zMPwws4yi9JoRR1~tSQhs03+R6F4;80StH#V7qeEBgj%G&uX2*6GF+)Lz$1sgH&fy& zq1k8{zWg@ZC-mtxD(~%JEJA{or}BPP=LLrplEp$k#5TsS^ZTZ5Dy;-*--nPft$4WW zen9yQQSP3Ew9okkDAKP$;+I8QywAlCi8Lvj8KrQQ%=#Zv$%$=Hs2lFjlDAM7%HL_w zX`z;(nwFmev1z9SO{;kaV`NYBc~x-}L7E*;IuE2z#*^qmuiYo9rfUI7^v+=&+{?M= z8zpz!U0@x{sCzjZ&#lomGu&>{a4!fJ{=8~j+lpn5+ilu+47_MvX(Cawa^St@7weP$ zA|z=6a-~2VwV9$TO44aUI$bwgoY;TYg`CyUi3+>&cknG1NG*Gw|-&fkz**GUVYTtaIfoR4r7F6sv_;tAnam zCsbbQ&vTVO!bAo9^EJvczJ*$y2C1k&8vzb_66&bN>(7JnnjGegsVYuwRK*875q{~R zE8gx`uLAPjZ4V%25{q)%{U({LCeMH~Fu!4ur4+C^ zH@TeZ-I_vGh9snxRnZh-nLS#A93DP5id1h zRq5f&$jZJ`GK4sXL{67l@RtyiluMbylT))#WlFe~lboHunq2+Rm^#0YP;xq`5mrYR z{|Rz(lC!g4LL#R*n|Lvaoak)g1tfB+vx)ObBJ~_Mx z`v^NuXZs|_rOKAR15B!SbQkILIu02vmX}l~e7=r2y?KPP-9UMPd#JELoIFYN{RB0P z3#dNYn^R=0GkYM{UzQ`Me#T!^|9H1hDe-Qh;Hu2{VxqsN)UfNN22Q8a=wfR2Ko`RC zaWTutFiw6ljgy~&#tqe=z}-?*xfmKOE{6Iu;}G#wq$2PPb>1-TWb0hGe+#r8H!RMb6MlVe`>eu`{Y|5?8soJ9arQKUb(}pA4>M4d z27&tylg;)LV!M?HIa9U=Jf~Z@$j2t#(&m4#5Uc;-20~Z?NS5 z$LoB;QmurbRDV=Z^}w@)H6YYdM`gezTtZY9M_e2;a0Se;QXkanija#926Z#0;lF&p zAG(E{#kd~*|IcZ{ao!SORT?zsczS!6^`Ne;t8m)XsRdlR;#1x3CfHodUDDVH&jx6# z*7haZMFOAs0fEJT!e=rnYI~P5blU|UjD&VM;tH$=gLj|ndYLbJZHv8A^Sp6+`5PQ~ zGVo$~IE1!y*Ic!6{UbZPv)6gecdE3;yht$t+#OtAy}sq)M|RDb?WI?G-&&Km{fh-R zEb+eYM51%5+WxeD3E5g6zH82-OA59hC~TQrm4ErecWtPsnp{wLMfnvIF0b z-?F348?Y|U+Wi}jJ13I0t*~IuoH!%>M;-T}!pMs4OPAz-F)pFUb#I7V8OlCo-^A-D zRxg^DKPP0H%Ku5o9kS^B4c=9i%e+;KVfw!?KeupB6%~w}7s)!hbdGmdPBZNJD|M=>XVDb=f~xZR1+(kt zUrlYN)*45XR&tFhgr)>MqcVMZw%c(_DX+`n8dF54sVR{re9fp`y zP4Iz|{vb)kI$)z5QD?T12-E$_u(eemn2~q7I$=S-V+Dd#5buBFy`;|2Y%3Io+ znpZlOh?@BoP_njM+htZ((BmxNi;ZFwKD(@{Jg&*jr)GXPtmweg@n$8xs{DfTs`|3> zk_EHu(k_@?QmbVsL*(k?EZ8H~W|g&3d!w?QZT@^UFiaAYcE#)<9E%!z!rIa zQ)kPS&7EEO$4wQD9ED3^u9=OgtE-yb*1LK>t>3rVq`AejVUnLF@qLM+zkY?G8dHC_ z*c5c1GRj_6Rehm7fXnCB)LrZZ9i&Ewks64XxvgGVq3&o}5!Z)ei&A$X&1}=Tg&Lk5 zmPRv-Eo;aW*VNe2n)%w##TCu?T<(hchQ{S9S~{C`4&5$rwV?~e2X+lt;FV2t;}wpa zh06TDCA_BM;`;gXtPGrL+1hD9*w-`=Q_bSW1W*Up}C2>_RnIAMWKF{oGHm{>>jThA6 z88PM*_2x*=Dwe?c@IX>i8pl7s#E#Mk|Dwg6&CNkk{5rLRP8}>cr4@0pnHO?+c63|A z%!~tb+FF^Zfp6O1j35jJ`n}Na*Zw7T{(0uZEPT*odfqB4t}CvutFAABGY=Wvu-~ty zA2cIRIc@!%BO^#>E*aX?Tg_zTkQZ|Q=UDz%=dzk!7q_&w26G5~$;`wQ5{#pc6(9(;4(eLfRZlll7`4lF`81tx902&d z3+Bz4SAF3;-BocH9si1g5z@+mgfJzqoH+LkVuH@l9LkJjwJZs9)#w>A7Cz;)bf zPlE-0yt?rNZ z!#~4*ye|xZ_TcT{sV@`*%0F)ZJw3<_ZK$w>**qH2ik)lD}+>)KG04^{z2-u zVkE|}xxO?~D1Wf}=9@T*%{|VZeEjY9$?eOZN>P6B^y?)nKFY@go+>pUdCuq{qSEaV zLH;r7H^yY*xsZw5#|y0-Y`rB3@v9QzpH7JHOo-o~5Z{{+e=;Hdi-h>$g!uaj@nZ?` zBx`_D|MY}-RzjTLSDQfovV=H)uf)LfJ`>}!UE(~~Y42XYRUrq;!nRG)d2VOo>w+B# z@@R{Qf#)>s;qOaG|4Blezj;aR)F5#($Fl|dRy42h%))62 zl*|h%wiYSd+(P;} z{Y!^W!KFd&oT))@Qzolxz3{Z|ja5ebhS!#4F4c+_tU%(-y-z7n=HC6*h+j7QWOM0^ z6De0eL-U!{Le-C%OT79bOsi+DRD6BW7IM}0bDBaw?oO!}X80yj7k3VwjMF%J^w=HF z0foR>g_zei&KMa{>=5#>Wn?L+=D`e^USgz=(exS|(qx*sfOPe88@88;WpZ6iJPtbq z{*Kg?yBauSJgM4#v7vt#@r((I?7V`5p}$M|L`}zJ>kOC4onNM9Ow#mGq)WZVlP(u* z+K|dnFHUi8S#+N7hQH0iD~TbWUp-t)ev!Xhc&PUM7XK6EVS^zYu;_1Cn4fH!vQp3) zrY`&qW4d~2tG`boPXuy!wKDv?K4lbOhrntZ%qxb$Q43Q)lXn{FrtZM$oZJvthROT) z$Y=MnO{U|hZ^J17p$VI@w|_bD8Km-Ibf5#9(&$qJoN*?02+7zCo!IqM&7wE|>GM!t)W1rwe`yjenNlBFHQjd?9d!;2~kyp%Q!> z-a=d=_>;(6FL)EmZ57N<7?ulu569Jl@z~VaD45r;&kCmOF9<$_FGBVRK8kuhE%-6u z9|@)%?iU2pUdyip{}eJ03jQVZ{G;ICBQNz}yU}*b2ZCw84G&uteLQsQBlsJTuWS{9 z|3zS~pP4tAZ7UceoYMq10OttixBrs_({59tVA>2jLvSU^I!ACfj-`U{0e!Y$e8#P9 z8=~B!$h$!3cLS^6Qi1+$)UiS6Gf>tN!FZ?aEECM~y9C$vgpxAa8&jHk(aiv&k_14 zv}dW{v(R_S?jLy01-(Y-{N(17f?q>hEfst*+NWFawV?M1&P85jw;ysIg3fmco#(Ey z+YdTzv@6?vz>O%EHe^{ZeysVlVAkmc!B60LP;d(PmEC^u-wK&oD4TivA+NHb2i%D^ zDHS^Bq-wzvQ7(Ny7@G{rrUCfR0KJX$Q^1U%Y~rcUJTX=zFX!Ovg@^bSk;D1sE}_o? zR`%{7rx|#U@EieF_U=#)?-!mHdO!5h&xr?th`{!xZX1A={W{<$fcxS|dJI_EpaY%> zJXPqI0W14*h!2I#i%3Vkwn5$^V(7CTJWGV1>yFhz-wC?19fvq=uHG&@Gl0J=cpmU0 zf)@clEqER9FNvW)*CB_6eh2993w{>3KgI<0XCEm${J=bSm3=l~E}6~{9=2_z;B$d5 z72E`@Y^;Hw*OMO7QAf7x^~Bg1J}2~T7%z7VUWhzjCdRm6c$^q*@+|O=1#`ZCL3r35 z%I+EXIWH+&XTVWlW#0^VEN~P@%H;YbU+@gzQo-|p7Z5|w&p?My5aaw~=q82^|)7!={uxDeg)`H6APXGKd9$Y(0@k^o?76)3g)_LFle+#%@8HV#<@}1r-J?$K~H6q zs-I$Un#lutCi#e=|C!LONAM-U8-<7S#ulN|*UIOF&U)<-I`i%#hWxX@|1IJ9DRfje z_VG~|!vUecjj{SC;&e?{_Vb|+pAqoK6~?Z}C}PmFF=kE|d;_v5J5Hc;oqm4}N*1@H4=C zo+L^SSgn&GXC7#3JuLD$UMU}^r(O>W z&Ja5LN3DlJCmt#EC~%I2CkWufk9Xc(1rC{p6+QPh^F)ww#(ZV+ij)H!>g})$}<#HZ0^CbJ5m}B-^7Jg9hji7U` zH2gmn%_nb2su69};{u@EN^nsP!{2r(d;x7F;1b6`*rIHT=rg6?o1By;3#D@_xp`pR@9AxA5J9DJL15vAM)4nCB+LP;6%X5GBUgV;GOk zjHLo%v=75{Y^Gi@Voq8TD3jwY%ng`hEFzfWftNSZvvD*!bO4i&bgti`f^krMr1N>W z(ub8mn2e1&Pr*^?COQ|9F6WNYlZ7L2UBbGs-($p57q&I=jW|{iOFLHyCas28>R2b3 zby4?^(2ap>JJW`viAA5YiA4uB9;Dn`NSCs{MJ)1FJ8&_Hp!!ARE8T>ybdz>y0-v!% zxRhARz0IQEK`c5vV$t^zi#`s|BazSNbw)R?l?{C|vFYo7#HN-qn567u`A^Axu?9`!)Cw}iN7yS*_O>HZ_!k7qKEZu2_Z`FTd(@sIXRW4mxoX!4NteN^^8Od}y~(mt2K_Zz&b zzrPp0E#R}uePlY;bRe)?t~;5=a(5-jy9F0q`}{EF?nsbVaE{|FM1IQS<;=+A+SB9# z*?AfA77|D>X%8gG+X8tlt}JNRW3P8twbN)c5-d@_O)4$DF@WOCH}z;otur zCK-)EyU`{vjoYgUe4S(TJC{VhcZAQ-9{^vB0wk~=3~z!)9AgB5_RM(hv%cBbOnv!I z#lvR8wdZLcIwo#J^Jq1La}+}OtaCi*ru}%nuxhrvd%!T+%4p=t^*FCRKdQpX>;p}i zdgjz=iu2_5fvPUSeFBuw^Z0n&8<)k|2im!I{LHyG4;ia&cJT@*Jgmkm+nsuIYRA@6 z_sgMSp>Y1VW8s6>M}}mk|E`$UM@nKhjy~6^y03V=>qhUJyf=KGrWa3ivP$wZGDa5{ zPpFHMb6v;&z57On($j8Exo`5#{SO>`@Y(1q{nGEdG5V1U%NU!(u#J;H_E?y>0|wFUmM*WjTt^l zS$laMYE!kmwd2{*{XCPiUv^Ii7(-W#?dd>AIhAOGlq2Di+iu*D?G8}*}IM2MoZleOy7`Iniix}7sC@m zYFV_$T^a51?e&(D|;`SX=PC- ztW)FbU4^vjtE)$^cXvM4y1TXG!{Ph(d^phClh(h~;jwA?pWmt4eN9!{icc&~jdTYB}cRyw<4@G8%H`I4e6IVFxGw#A01(I~zxSfBh=8~oZN z=GFDx-`~{s;L589=(!PlHC37Uif4--Li%nj@SqclT1gQxafWtOZrGW51q@1j_*^~@5qw?)puQgs$A}}Er?>(*rH{5VLp(s! zONmo8E+bBJQ~?q96~4_?3GS;D*`cK(cqW&vrniah2+Xrg*o4o}$D~QDV^{ z$irul>ZQ56?New%E*U{oEd!*Z?=yuVjXU=PW_KHTRdU=9RT)4!O+|;SbqaIVIYl{Z zoROpdoU_*XbI$NTJKHn5mvu-6w;^*`6Mi)IH)_l!%xbX%uFVi4V?Ki38a% z3~V_!)8*^dA$*p}aLO!zo;8logha<3=ziK}8cL=FmO<{gzOJKeCT`rLr}u-6$N-f<_mwPa|EtA^5XvdgFBO8PLZ z%{@O} zu^H|kA#jLNjM+|&Q#BAw$@ij7du~ZCKh|4y9haSdz@;9 zhia)yTz_ap*&Z+oA!RQ1C0C(bciV_0Q)ZB&%Ut3z%{^ahwA9a+1zRRp__6qgSmx5^ zPEcK6D^O_@!`CVpNoWzir5(<5K{v}azVJ$=;!#V_mo&!6Qrx$>oaL;N;=1TI zH_B5g&H7Pt@|f^;zq@pacUmRV3A!%vPQS$Xy6tqY`j`@To}@(G`d{^Bvn+Sp^M0v8 ziZsVwOSuwUhO}~feF5>!@rXMJJSS?7M^2)gZjPs{68XMNm-v)lVtjKvY~$3aWE^CL#NgT8?H=6Kbmjh+)J_v%TM)6MagRpNfX#JBts zxBudH9l*Q65p?v9N({(s~V#fP~#u@0xtJOkK=0t z_lnSu)nXA(DH=MLIBsi*mUI}jZp-}mcoNI7GJF^e6WsG*-|KvB=;;y=Co;8z>&8hV z!YpobSXX_{m8E-62L?A#T4`(w(#=OpFlzJl^6qsTn&Tyy`T zSJZD(7j2^|pL&L(?1}=OKLx5=H8q?4QowVeKrN~GJl}x{Q+1v>y;(pCj2?NmF(lgp zQoJ>z1iftH!0B-$t%q-_-8God1f28>`tsZd!)I)#9} zFq8nFDyTSyzilZAE2`nzGSmoTFlwY<4cG;mJuuEP5ZuHnBSvCT=rk(|RimnCfGp8j z*hbqD7xD@tJ-8+IUy(~Fu*i+sxmHR>?Vn5v71z=>`zUwtUFDFK_ytC zV#OzE3s{r2VXRzZohr{*rwR-q!Ge*tQv?f0G)PtGE3Q&FHYJ?Y(vD?Z##O(_LBik1WvXWB@v%){@%65p#o#$zt-hYU6m%j1yt=92IoGc71;kqwGAwrA zTNbQLvgdRLEn+i<=5=oU0BY)kq@lw|g^7Wfk*Ju1GST zwE8V=PE84F7|Y{|Afy*EQGwt&}~y6NX=J zOgX>9xNa{UQ?7 z3ljeBvQMg9e9@5;6&)n>QnYpb?|sIys!WFS{Zay9{?&vGfmynM!S4q$%DX*ck2^N;x3#aCHG&Ek( zu%x*we>P0WwdU3}cXj7?x3{-;`W55T`_(5k$Na;Wvuf_M!g98ZYSn;=13P zP?YD>pi??-3=3v>t&Ve1Gf^sKkzP>UvNYELm_wI7rpIkD!o_mBfdM=EcRY=@(Rj} z;!F{~5ON1?TU>xA4X(F-O{F^i4eTGSuJks!dH8{CAndO{A=i6-b)NS{sAA{*=ma?rNY33ZCAi*!)mnqyt~%z=i<5Gz#|nKPnmy>| z;h5(=gyUm4?t`YumZsYk=R@Fp%FWC79su}Z0?XH63Gs_CycpMFD%*B&{|m5^{fEao!5fw^_hV0PiKRywFPlzf;-XzYc91%Jbg8F3P!z$~Y$ofhsuh6^wy2aj`33@&kMoSljQmy_u zfLsrHYmw_1YF8L1@$Im?er8mMo2&6Q}4gRobtvt@Q5o66&x84?Di-J%gn0 zczHPHc@N_FIF9?g_@>>VI3EP(_aVf)AK-am=_sYODzRK)O!ECJZ5sv?h$=L&0`e~? z)Uw^?TLg9r-aO7UU|>$GrKPj90X7TGJnOWt=(c(YYC)zc2Qq75x>Q-8JI(~1PiSa@ z5j%gT_7$?LV+000qO7_3X3`otMrO@h5G{?W%Rw}ES!+vocWZMlt)aokRPM62_S`Oh z)eZ;)q37W0(!4C1yCj-h-4)Hnh2fmWGtZ3XE(Tj?b9*Q3y5%lj(blNkS81~9m!+NU zZS5<%a#yrn1>>Q)@W0*KjQq^jzO1D?SJ{ut?Pyok(e$R~#Sqrf-hn7B7sB|Mve2ee z5o^36x1+P2Mi|>W$s@()c7a0gS)fsIKOS_SWf0EqQp-k;ZC@?u1HaLrV_3}-j$jn2 zn5u?tQ6~^jdzwy1d#7FGiZ(@6m5VE|du18Dpeuul?X68!%~v+JTB^dfZF^@+_bQkH zr8qOKsM6!?1gQv=3)(!Q-nQcD<&-65a){|7KSE%*;CxWgBiLN+{9B;p}wYq4?=Boo>tK9fK;jY{2j; zB>y96N-oUS&HD%QeO!6}oicg1Wpt@Xr~}*3q|xS{-KI0Fw59 zadm_}z^yPEIGkmwHL-?N+e=pZwY zr@pKYNS^vy%#Pc=Pk!~L+Gp_8ua^8M((&UBD|r5|2sF{t?ty#`4_~~?3v>0`6*ume zOe}Az&r9Gb@4f-aQ@;SU<7WjC#m}=O2%fSKWyfPd1jtiXqXLrW6vzFhCLKxU=?J+P z&n7_Nd`mltb_+25Tqa6j+&nGeTuZxw^^&IJ5oejiIj7Q2;I$Ix97?-@pO!d#ns)#0 zjEko`_s7LEmE97OH2)yy7jfxB)bGrL^r6n*;?hsW8%j;k`A0ZoVowfXp>w!Ypvow#De3Q=sC1GhP z&!5a)OX-jiv(W=+%7m9mAX>!EYQ~pW}R*pyb&^a{bE_Pp?9xf+P>oTg!G?)=UKr& z1N}w8@8I~VVA`Adqu^BV^g*0?AHk7!aELj-M+F~)KD>UAeh|1q@Q;A!3w{O1dci*f zz1^bo+QGa7QPyV#-wvLCBTj-M2)l*;Yu1H0OVht2bbjaln9!NT?|><@1oWQ?eh2tB z!gC&Ys1tdfL|MvSN0z>>XQMw@F2`Dtg_SK1&|{!0yLRBe4tSaH@Y=qX81y@V`NbaP zaGc&FblRBtrqEk~e<1XifL|1PC$O?#2RZBq+Kr-|)xeq1mGVoL~^P3!U=6LJaxG!2j<;XPJ)*J_ko-I}SWi$e|5N^3MZ)nHVy$QgHqx zboTS#gdPKZK$7NP2RgonQ+a7OWjrzJcnx??C&v8DP$G2dHkTM>Jq`X^p??|lg@UQu z6~c2EJhX91e*Q*g9Wi85x7&q24fGvCpAPySp??DO$BA>aU7u%&`%xR%(GoiKd6gLQ zuLl2{!o$8kLOe#Xs~;ATXDfKp9uXU@EjnmNbrx1jTiKVP2 zV#r}%FB2ZJtrhySpnqEElz$ho$p02GT{FTr70j7mo*3w3SGkq2ys%TZB#<$r@%5`%~OVvAlxE!%K3uu-v|EfLLYe)jK znG+#%i{LrH|0ev@^FE<-jDJVyJdb`vEM@(U7-fw^A1NDp`0SYB1EI65WXvBd>t4u7 zAqM{qpr1lafe2%TPX0pS|33K75*`lwN}<#4@%h5vjdCv{=0Xo)iO?yhQ~0wmj@AjD z3w*2KHsH?-KWz-(CG=ZB{}M4|j)Kfb1m^)iDYyvu05R%9o2IW4v*Qrn6*_hJ8!_s> z2Qrf}&yk01JA@c?+H@RA3^^N7muzAh$U!(==#+D=@b|;GSRg!;x=0-bvw6{+icZX_lRRI*5$gKP(EEQafBR5m-1%kVAyv)Lj1amESxrN&VZ@}?7!OXW-a0`x`iKQ>@ z6gqkC5=@@26AKU5n$(9p-xExp=ZJ;pC84+Ccu;UBj%xjnyu^PJI_vU*g^vkdi6hse zENeAzreM~E>qxWC%n^)X>nPh_n74=vggzFSV~hNl0-TwGd74*SSlv^AXB6n_oRtwPoO0Uspx}Jr znF#uH3(v6Ra2;&q)LJ~s?i}PWud+iZ<*NH2;3=Rl7ydF}u8%3_TwrB;5SVmjV^HY- zCOk3F)x8qv#Jh!F0<7%XflmCO&?$#&XtTyv_fwc-*giiKI=9r=)_5wcgRCIT#Fky!v&LPjD>k^GyGhm6SIBJ5;X2-|^A8ASSub1oRl&^5^}XT$i^ZevAEn#@Va-E1>Ru9)H}j4pU3j>@H|zf? zg4uUxTA24y?1DJb+K7!dk_?4KV6K4u$ z-fUtnmJrlEEHL%V7do-Jp9P)#Ji>HoQ^t{xfHwb3lWfqUJoi6ta>fRQZWqpb~ zf;S6hUfw5|`-{6Rp05*2x!b3k9<*b?=P(5#mKc z=QV}*8-{Ruc74lHZ0(1{-r%(;vAA%_2_7SC^p zrQFvn`Wu3o_dhKj-m6d!ufP2Tv;8wIJVY?_@;=1er{oEyZc{CMrr;>(yjLMVhkCVO zUiU8%%yKWY_|<(n84#8Wo${|GmU?Zp=%2FkDm(MS!+RLoNN4zpV2-zMSXkZTgGY^3 z(y0u>bAnm!Pc8hSVDj_c#_+#s@%))sbYKUPhjMsLCT8ArV&UOEkGUtx5sYExOtx@= z;PIfJVd1j{*MiRbB_oIC4v1OAMHZ%S8`8lY2o>1Ob-#vKuIU#M^JWuaAvSaETS`nt5IV4#YsX4r%<~LA*v#C%iI@W(VGA}h zr`mI<_Y(6$fUpmn>C>l)rJVs)9jP`h-3n9Hh+wM6IfCs# zl~@exOMZ$VCVx~g`LhL+zknDFq{BN#8=uMnbp^8!`30 zgILDTHo=uR?jS}RGTeiWm}7o7v5c#)2_}6HaSC<_4`4HE$i2ie-X0N5{(Z!31cWEB z5tIHju{@{!fnd^~CuTz-ynv0E^aI4Q7J5lA=?96~mNB1gA=Mp|9M> z5myt-7+6It{l1M@`g#ws^zW~TrBDAxEd7|ldlu=tvx%j@_`cTk5v`4xe%VYceX*NZ z+F#uhNt-8!L6>$OMl5YRl~~%7{U~J^k+XxU#RgQ zbTuBNU#iF>^crI6BlRpu=;~RJ^w$Q9e-p9v-42U>53!tUeCKWC?<1CT?SMspiCFsg zs72?y5YyL#IDSmMh^628U6P@zcg->esw{d9v5beM7QK~N#>Q@56H@ zNyf%dq!0d{X$)D|h;eyQ;Y57nkjkn2WPF9-Ni27plB{=O!IN<$j!7IghSVCeT
    W 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.FZ+TDzDk>Th==>81e_-u+ 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ΝeD7nHoI0~4aIg%%^l*wT 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 Z7(NH=HYksnd9r&8D0&4K5vVbF{|^*@%2faW From 17d2e2fb22ba0cd239c46973cca0006704534195 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Mon, 9 Jan 2017 10:55:34 -0800 Subject: [PATCH 036/125] Rebuild libarc32drv_arduino101.a --- variants/arduino_101/libarc32drv_arduino101.a | Bin 190802 -> 190802 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/variants/arduino_101/libarc32drv_arduino101.a b/variants/arduino_101/libarc32drv_arduino101.a index af098ec4401f2ef6f736eea7d93191a21cdd229d..c3fc2f474992a2b910fb4489b33188904f4941f7 100644 GIT binary patch delta 3015 zcmcJQZA@Eb6oB9N(3W>%j)!hk-NAmLxxqIJFIpQubF?KIJS<6^anZxX=93x5?+^F=Hdv|uF~ImT*Nu1Q_X@_B1{uK35XwV?kY{Z_gImOpGq@Xu zI0m=a+s5EddgK}IXH5(q6pmU3&1Ezv_A}_{V(#VJn&=B2K`0*&Gq``03_MB8pk9$< z@&$YT7gO;J*2=7g!3sE{IE7~#cHy{c6TTaHJksW}RMt0D)>*2{T{dY(^)Sa5)V&f{ z_Hm5b_=WU#go0yf_-FV`lnZ}Wzk?SxzKN&0voKG+O&W{(Axt_G_c|A|I=)3J-`oJ= zQWt+n`X*);#HLZcLs+)*Z6a`kAHsvW13M~QmgeS~DobU9omJG^EKZxFrKZwmu{D+l z!#`8OC620BgdqaVs7uJi*mUa%!sh#*lst&(Q>p*LI zI6j`P#p?6|&B`xEug}iT+mmg`30~6Y=^x3dyme2@o z3WTH4OFFZ64sga!xzU#zw_s5(rKI{^MGTgdlFn6ncr$)|g>+Z1#OSbKEQZ>Gb?i_q z78}VryKyc-EN$SF^rWfzH5EFBC@Fn7)E|vEe!D^%jG^RzvEo+ru0;1X@G?u0Cv@P`34 z1<#EzXwziu-jn~=3|5ngFu8RE~Kr!5CSP;w4Juo?QjXi;U3z))&pLRsCCe`+X0y} zQPfJ?`c^247K5F%o#=#inJ^B});s_uTH$S>?R*PVhlx+UKz;+0UYJpeqRVt`{beYQ e6eSmF>$(Vjl^FKXcG3rJa&dN^w%^V}i}DYvR}+T- delta 3028 zcmc)LeN0dZ6^@HMUmSwPLTowa0S?0FjB*rCGCvh4x{vdXu3ob7oL5(+g zZqobfIrsFQHjPFTqS1stlhR@CuAX~10h0A2NpJBBjiH)%1oRkWJ#WRZ~AJKkmdSihLAe;3|GV4j+XFHYJB{Bz8# zv^qYoCa_#z#y`*Uu`|k3Y-`eo(jCoF%8JS!W1Xs0mbr7i^o&|g`6Vq~!+TSxo(-g| zXU}N{n3&v^q>~Q?~)I&pOS*og_JuomZLZEMiaLi#$blB(;2g4Zg#{Jj51nx|@O^E3$JD^i)h1=y(Z6MiuRa@Y=25<8OF z$1C$d$C^wpr`CJjLaRH_?h)OB+vlqHvgE98cFk1If7=bKr5Cf`r!1WHL8`|u3azjC zL_u_Uz4b1!nIHQYvSr?uM&20)1N+E4#K4@+!{PYE%{h6@VD?Fea|$Sn=C&C9E}usv zg5YY6-zzBWae2H%u)Es4L1xH1#I~-x#xCadvbuat%KQbn{2nIeKdGDh$1$5XTP&9N z*;;Cr&gCCbVAq|SVk+!pVYcVuh_}d!cpLYF1@Ko8K0bGoKx8Z`yrp zc)5mRG2+xvDS^qHY1nEuIF)HEFog}2-kLh5Wlh`A*0*gE*wi4})Tf?LXO0TAbymbw zZ0I1`#t)`tveJ9#I&1HJqhHtXs^GDTg)K$q__4X9tQgs_Z)o0u?^@*QEG?Atj#7N)6QwY?iW@9=ecS?-D(<u%z;>?U+5xN!onTP7f?;X`A19Up_L OBtCo_ueWYPyZU!#qZ0}M From 7c97231c087929a90ae880486b3c0ce539a9b326 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Tue, 10 Jan 2017 10:31:56 -0800 Subject: [PATCH 037/125] CurieIMU.cpp: initialize saved accel/gyro ranges in begin() This ensures that "read[Accelerometer/Gyro]Scaled()" will work as expected even if a range has not been explicitly set using the API --- libraries/CurieIMU/src/CurieIMU.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/CurieIMU/src/CurieIMU.cpp b/libraries/CurieIMU/src/CurieIMU.cpp index 8b24c5a5..0def7306 100644 --- a/libraries/CurieIMU/src/CurieIMU.cpp +++ b/libraries/CurieIMU/src/CurieIMU.cpp @@ -47,7 +47,12 @@ bool CurieIMUClass::begin() * MakgetGyroRatee sure the device is connected and responds as expected. * @return True if connection is valid, false otherwise */ - return (CURIE_IMU_CHIP_ID == getDeviceID()); + if (CURIE_IMU_CHIP_ID != getDeviceID()) + return false; + + accel_range = (float) getAccelerometerRange(); + gyro_range = (float) getGyroRange(); + return true; } void CurieIMUClass::end() From 90e8bc65828a7e65f5a59d9a64c3db7df185e0a3 Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Tue, 10 Jan 2017 09:55:59 -0800 Subject: [PATCH 038/125] shared memory structure changes for CODK-M -added struct for ipm using shared memory -added cdc-acm buffer space on shared memory which is used by the new firmware --- .../framework/include/platform.h | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/system/libarc32_arduino101/framework/include/platform.h b/system/libarc32_arduino101/framework/include/platform.h index 583fcf71..3fe25184 100644 --- a/system/libarc32_arduino101/framework/include/platform.h +++ b/system/libarc32_arduino101/framework/include/platform.h @@ -40,6 +40,7 @@ #define NUM_CPU 4 #define CDCACM_BUFFER_SIZE 256 +#define SHARED_BUFFER_SIZE 64 struct cdc_ring_buffer { @@ -62,6 +63,30 @@ struct cdc_acm_shared_data { int device_open; }; +struct shared_ring_buffer +{ + /** Ring buffer data */ + volatile uint8_t data[SHARED_BUFFER_SIZE]; + /** Ring buffer head index, modified by producer */ + volatile int head; + /** Ring buffer tail index, modified by consumer */ + volatile int tail; + + /** Buffer status + * 0 - locked by X86 core + * 1 - locked by arc core + * 2 - available to be taken by any core + **/ + volatile int flag; +}; + +struct ipm_shared_data +{ + struct shared_ring_buffer *quark_buffer; + struct shared_ring_buffer *arc_buffer; +}; + + /** * LMT / ARC global shared structure. This structure lies in the beginning of * the RAM. @@ -109,6 +134,18 @@ struct platform_shared_block_ { * The ARC core counts on QRK to find valid pointers in place. */ struct cdc_acm_shared_data * cdc_acm_buffers; + + struct cdc_acm_shared_data cdc_acm_buffers_obj; + + struct cdc_ring_buffer cdc_acm_shared_rx_buffer; + struct cdc_ring_buffer cdc_acm_shared_tx_buffer; + + struct ipm_shared_data *ipm_shared_data_ptr; + + struct ipm_shared_data ipm_shared_data_obj; + + struct shared_ring_buffer quark_to_ARC; + struct shared_ring_buffer ARC_to_quark; }; #define RAM_START 0xA8000000 From cd3215bd5929dd83004cdf3b94e32726ff2eba2e Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Tue, 10 Jan 2017 10:17:13 -0800 Subject: [PATCH 039/125] expose all AON-GPIOs -expose AON-GPIOs since they are available on the TinyTILE --- variants/arduino_101/pins_arduino.h | 2 +- variants/arduino_101/variant.cpp | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/variants/arduino_101/pins_arduino.h b/variants/arduino_101/pins_arduino.h index e447277c..8be94f07 100644 --- a/variants/arduino_101/pins_arduino.h +++ b/variants/arduino_101/pins_arduino.h @@ -25,7 +25,7 @@ #ifndef Pins_Arduino_h #define Pins_Arduino_h -#define NUM_DIGITAL_PINS 29 +#define NUM_DIGITAL_PINS 32 #define NUM_ANALOG_INPUTS 6 #define NUM_PWM 4 #define NUM_UARTS 1 diff --git a/variants/arduino_101/variant.cpp b/variants/arduino_101/variant.cpp index 6993d2f5..9e9b52ce 100644 --- a/variants/arduino_101/variant.cpp +++ b/variants/arduino_101/variant.cpp @@ -87,15 +87,18 @@ PinDescription g_APinDescription[]= { 5, SS_GPIO_8B0, SS_GPIO, SS_GPIO_8B0_BASE_ADDR, 13, GPIO_MUX_MODE, INVALID, INVALID, 13, INPUT_MODE }, // Arduino IO17 { 6, SS_GPIO_8B0, SS_GPIO, SS_GPIO_8B0_BASE_ADDR, 14, GPIO_MUX_MODE, INVALID, INVALID, 14, INPUT_MODE }, // Arduino IO18 { 1, SS_GPIO_8B0, SS_GPIO, SS_GPIO_8B0_BASE_ADDR, 9, GPIO_MUX_MODE, INVALID, INVALID, 9, INPUT_MODE }, // Arduino IO19 - { 0, SS_GPIO_8B0, SS_GPIO, SS_GPIO_8B0_BASE_ADDR, 8, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO20 + { 0, SS_GPIO_8B0, SS_GPIO, SS_GPIO_8B0_BASE_ADDR, 8, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO20 { 24, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_BASE_ADDR, 58, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO21 { 12, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_BASE_ADDR, 46, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO22 { 13, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_BASE_ADDR, 47, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO23 { 14, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_BASE_ADDR, 48, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO24 { 26, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_BASE_ADDR, 60, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO25 - { 1, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_AON_BASE_ADDR, 1, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO26 - { 2, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_AON_BASE_ADDR, 2, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO27 - { 3, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_AON_BASE_ADDR, 3, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO28 + { 0, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_AON_BASE_ADDR, 0, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO26 *soft reset button + { 1, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_AON_BASE_ADDR, 1, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO27 + { 2, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_AON_BASE_ADDR, 2, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO28 + { 3, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_AON_BASE_ADDR, 3, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO29 + { 4, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_AON_BASE_ADDR, 4, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO30 *imu_int + { 5, SOC_GPIO_32, SOC_GPIO, SOC_GPIO_AON_BASE_ADDR, 5, GPIO_MUX_MODE, INVALID, INVALID, INVALID, INPUT_MODE }, // Arduino IO31 *ble_int } ; uint32_t pwmPeriod[] = {PWM_PERIOD, PWM_PERIOD/2, PWM_PERIOD/2, PWM_PERIOD}; From 29daefb64ae643a0cb2bcb15b586e90a206bf7e8 Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Tue, 10 Jan 2017 10:24:15 -0800 Subject: [PATCH 040/125] add support for Power Management library --- system/libarc32_arduino101/drivers/soc_gpio.c | 3 +++ system/libarc32_arduino101/drivers/soc_gpio.h | 1 + .../drivers/ss_gpio_iface.c | 3 +++ .../drivers/ss_gpio_iface.h | 1 + .../framework/include/platform.h | 10 ++++++++++ variants/arduino_101/libarc32drv_arduino101.a | Bin 190802 -> 190830 bytes 6 files changed, 18 insertions(+) diff --git a/system/libarc32_arduino101/drivers/soc_gpio.c b/system/libarc32_arduino101/drivers/soc_gpio.c index c06f14e4..382f8c61 100644 --- a/system/libarc32_arduino101/drivers/soc_gpio.c +++ b/system/libarc32_arduino101/drivers/soc_gpio.c @@ -34,6 +34,7 @@ #include "soc_register.h" #include "portable.h" +#include "platform.h" #define GPIO_CLKENA_POS (31) #define GPIO_LS_SYNC_POS (0) @@ -429,6 +430,8 @@ static void soc_gpio_ISR_proc( uint32_t dev_id ) uint32_t status = MMIO_REG_VAL_FROM_BASE(dev->reg_base, SOC_GPIO_INTSTATUS); // Mask the pending interrupts MMIO_REG_VAL_FROM_BASE(dev->reg_base, SOC_GPIO_INTMASK) |= status; + // Save a copy of the INTSTATUS register + shared_data->pm_int_status = status; // Clear interrupt flag (write 1 to clear) MMIO_REG_VAL_FROM_BASE(dev->reg_base, SOC_GPIO_PORTA_EOI) = status; diff --git a/system/libarc32_arduino101/drivers/soc_gpio.h b/system/libarc32_arduino101/drivers/soc_gpio.h index 401011cb..a2623f2d 100644 --- a/system/libarc32_arduino101/drivers/soc_gpio.h +++ b/system/libarc32_arduino101/drivers/soc_gpio.h @@ -43,6 +43,7 @@ #include "data_type.h" #include "gpio.h" +#include "platform.h" // soc gpio 32 bit count #if defined(CONFIG_SOC_GPIO_32) diff --git a/system/libarc32_arduino101/drivers/ss_gpio_iface.c b/system/libarc32_arduino101/drivers/ss_gpio_iface.c index 9d8543b6..fad7743e 100644 --- a/system/libarc32_arduino101/drivers/ss_gpio_iface.c +++ b/system/libarc32_arduino101/drivers/ss_gpio_iface.c @@ -37,6 +37,7 @@ #include "io_config.h" #include "eiaextensions.h" #include "portable.h" +#include "platform.h" /* EIA GPIO device registers */ #define SWPORTA_DR (0x00) /* GPIO Port A Data Register*/ @@ -355,6 +356,8 @@ static void ss_gpio_ISR_proc( uint32_t dev_id ) uint32_t status = REG_READ( INTSTATUS ); /* Mask the pending IRQ in order to avoid a storm of interrupts */ REG_WRITE(INTMASK, REG_READ(INTMASK) | status); + // Save a copy of the INTSTATUS register + shared_data->pm_int_status = status; // Clear interrupt flag (write 1 to clear) REG_WRITE( PORTA_EOI, status ); diff --git a/system/libarc32_arduino101/drivers/ss_gpio_iface.h b/system/libarc32_arduino101/drivers/ss_gpio_iface.h index 86630387..58acfc7b 100644 --- a/system/libarc32_arduino101/drivers/ss_gpio_iface.h +++ b/system/libarc32_arduino101/drivers/ss_gpio_iface.h @@ -41,6 +41,7 @@ #include "data_type.h" #include "gpio.h" +#include "platform.h" #define SS_GPIO_8B0_BITS (8) #define SS_GPIO_8B1_BITS (8) diff --git a/system/libarc32_arduino101/framework/include/platform.h b/system/libarc32_arduino101/framework/include/platform.h index 3fe25184..71c7e7f2 100644 --- a/system/libarc32_arduino101/framework/include/platform.h +++ b/system/libarc32_arduino101/framework/include/platform.h @@ -146,6 +146,16 @@ struct platform_shared_block_ { struct shared_ring_buffer quark_to_ARC; struct shared_ring_buffer ARC_to_quark; + + uint32_t arc_cpu_context[33]; + + uint32_t pm_status; + + void* arc_restore_addr; + + void* quark_restore_addr; + + uint32_t pm_int_status; }; #define RAM_START 0xA8000000 diff --git a/variants/arduino_101/libarc32drv_arduino101.a b/variants/arduino_101/libarc32drv_arduino101.a index c3fc2f474992a2b910fb4489b33188904f4941f7..db6e5b4591162ed7abd8d50f81b0b7d077847d32 100644 GIT binary patch delta 5559 zcmcgv32>9g72bW4EE^kJ@*!hHY=g1o1M9LZ+Z@H=PNBvr45UC1@&PusWg#0dgozx; z023%sHp6BDB&J->Q7$$9UCcCe!Zi&Oj?l!@WD;W1b{Z~YQbIZ@^sOW}stdnK+Gc0I zx4ZAXeQ*EWx4ZAZcro(E#mFsswar|RS7^2z9`+jbmJ!nN9niUukiO_zNr)V7|Co@6 z!HWVKQSzTi$RIR*+y5Qj|Jq6E%Y+KdF@7I-A3UhZO6W5qe*kzxob0|q=noC=gB)JH zN2owI#>T_&^)f>59VMWM?jH$92=rp`Cj>&^4efy0O8$j{mkB%w;}#O=57UqpITnEz*@+kY@%7dt$FW)oY&>{J4$&|RD_TUgdPTDr+6}?n;+$Y~>KuKp_5-j8qS-YH^J- z$axkyT75wJx?dV-|5osa1lnC8{2sx7EWjrettkU-CAPJ~JeFj^2WG1=jR|^j`8fe; zQ*e|pzA9u?YfvW>`A7V1)>4z06twU~P%3Qn1f=I{(!_U#BPSN2hpn}vD6rABGrTyd zLSzY3dQ=wWM~L1DPQtccA-II#Ye=*Pf0N9_N*{Ds7Hp5eIy%@EuEaO?>IffV0jjVT z_Vp?xSXgx3dORhf#3_S6%S@OEcX-Zx^0(7xf8dj_;B`GRq%Kedm(hP~5n zpl8`tq(fNTZwwHxV%^<3?V3J{nXDXzqg8CnEuD5(AEAx;P*|m63ohxjC;AABnc}Dc zcBokMMV2PK`9n@C&U$EspJluKQg(S_hH6zLA$InvBMpN@-(gT4Ld`OE zZeJps-w`7pi3NC+L?pI#bPSP;=?$HKkC3-yv)cUEJ1*9_uAvJ<19kiO{Z-lN#c|5U z?4gA0U6F(I+mKC~l9Ns}*WJoZNf@@;K4E(Y!!|HZgs#w3#_MfSALD9~%T4UPpQrj3 zU)iCiE}!~pje^b39!{q*enZ6D+eqE)FG$;2%uNGb#R`y~VGgA0**c`7*+HZ)vJa7V zvinHenW5W28`<=31CR*T)SW?3uvd{4#ZDs~&Tb+d#1d~A(9Kw+!`ULFJJ@EVW+ov$ z!#=%bfHO+5t1P)kgI-KM26~sx_ET?OJ(%WcdZNm~HGAomtYP zYgD0>zmTm~aUn}vyXlp1Ij@_)rrG_aVdxdlo&@)4WIwr+t-#{n(OC+Z8!PAb-2{m3 z$8EmNEIt-mV%YhvcxkjA)+%B=m>)T7YwOD$2wyxvn;TV2+W>-1DCMS;a^$#vHuhi9fhiPV?^QkX_O;#gim=+&6c@KG6% zMqlEN3^36{d~*hrYJxX;Mo{OxbTb1gs3w>y7=!G~ygU=q27HO#X@aGawq(Ly3I%z5 zXf`B3k%c=BLV{GD4RI7bEYE@IbQM1>surF&0;@OqB2l$-NmM7eIv1t`iQ-S?LJ6J8 z_hXli;9rUA5T9&>9FbP=wMNj;v%KDjYjHrdKM%A&FhV>H=U0v56;W}G2{N!|mp{<) z5))_yZvpbyE1pbxj2|?CfllEcimH|0H$guK-BO5mvMVQ3@|Yn-Ew|Zk6u^-I@{yf~ z`MXH%ck+-BC2_@s3N%A3zL%~2pNv+ zZAbL}iA)gdAHmOi@$^OTF9X$}W*CLNnazG?6ZXW=Yp5!Q_YM^~ISxJBjPAmD>T{4b z5Vb^V?9bf8q|)bLE{*TIDVBWveS2*+n7I2OD5aj&a9j~B-s@oGkV^`YVa`WZ=xXBO ziigBl@jvu$Y=t;XI<5F05=MHH_Ro3DI!KgC)`5b`#dXbkuq)*hIyS;jHS(v(VhOwt zvv$Iwes1b~Tb}=>4*MnKB*?j)DuG8K@75Yjsid$DkizA4^YRM)PgvLqFbtFrMyrh- gPtTT?U4VlTax)zJ5xmgPTVk~Yl0GT+Gbj%KCs&ydwEzGB delta 5467 zcmcgw3sBU@8Q=ZxjspSx1M)n=Q#jy+dvL%Z#^N(p9kkVn78_3#6$AkfocJ1##7^p1 zd<>g$GotovMXj2N_9A}+PGX~}8I!7^PU6JcSRJi{L=Y|0s1y6`JuGANkLe>j^Sj;s z?Qi$n|F_>h;C5$lXJ_zMoi?}7W;5j$97*{db(Rv+@jY;H9wATSs)Gj<9JGNipG=Q6Q9KqcwSrzC*a4G zHUeJQmz7o4kjk0{HgRSw8)jVUo}hgtAWX*+q%-%OANktK(GBiO$PG6 z_mr8dDmvCTLYJ>qjM3+9Jzte7zAF%!aD-0PTwO#}Y;^TZCr-+Slo(2feh_I4!au=D zSk|cpOUM*NnBG$)voO>73@-4MM`AwIR~DwkH~KZghHyXy=3;yuPD~^=blKQ6-#8|$ z`}I~Vhlv3(1t4@PeqtkxWj)I@tbN%HFz2)LHPK+RvE4Ohp4Lyz{BjuGkgsV7cZnP;|fb2rVThuDwyCBL|7n3zRt@x%C!+j<+b z8(YR-UzP1@E&fy8U6*=O^iYj7Y}n?A)`=&69K7;s5|EFU|Km<;adu-s@;TMl?$&*+ zLG-8YsYi;`bPls;rP2^~@TeY|h{rXo>!?Ic>;W=6e9WjeiH>Gd+w_`4Xt$c%k3cG& z%vz9$LDy{Hx)z#BXR%vtdg@{kvYxJFMY0|)0C$~*D7IRb=zjJqWY(&--K0_M3=*HR zOGw;PaTA~E6>rh=wQKLPhC&5=~-N$XJoVC1wYZ8Ob`3nXlo8W6{WcWNa+BT~FU+x%fMQ&287i zDb%Y_zr9@&W$7}F3eU!P!7n2s7)3gzVOJ)mYZ`(H$!EW|r-lY%IJ!`jiH*_xkr5JE zNrzr39=3FJ#VfC}CC}nnW%q{{KM7KnS}nQk4{4cHVqaWMF5TjZhwGkrXc>RHA}hTy zVnjt&N-_CpwE=%)CmYg<^md(kOLQp4#?X?8>l0nR*l5MrxL&Mo$V$d7mb_Udl3_q*p^JFcbWZr!aa6~Fow`n;r5qQ&gs?Z?ru%_V&bMY@O`U(+%uaj~+gy+@+yWyksm$7~Dm@dZrM!J1G%lJ z6S2^ay4A>ij@)t2re;I>Bzm0b`t&%V@%Y=#s`~WM?QJ~JCy64idBFT2Lf)pVac?RM zzN@D*Snk~{rFDMwZdas2ySyfdz8^fu>YAT2ta3u_&V zsw)@NXPZst?8UW?Dn2d|O62B5I2s`No*28ZvrbD$1D~Ca2liIJCLN4)KR=ibGbC}H zVoFB{&Bt=E9?GfYyX13x^hNH>fYc$~Se+&)wR|W8T4`*4ftlnuYRk(S%5v=X#VcwS z+U+^!{6Z`LNga)pommh;(I7LxblSkLiR>+&I})=!e7(pH^FNF1I8V)n>G02B-jEF? zbQ(X2)u0ifc*ml-WPa|iG?9t*T=taE zVsz(xd?%!!#VB^puA?rlGl9|}Alpq)qE+7HmIChsSMD!>^+S}kxfbsmT>fwzoDNZb zgxd=7Ra9fhkUOWtEG-{18)A6#9Eg!$o(-cZ+maZ=A%;4b>1L8V`e%5`W5&V=n&pLAJ$V1uPcLSs*yL80RyV#@#QcR8|%t3nuc(fsHyqAa%iE!^43LQ zP{lnN8J0pzA<0=#U!TKwFNP>u&$}xiNoC73d*X#}eFuWMu@Z)@YuaB2d$?mcjG(hPUk-YHV>!5$9hYC2pX71e zRR5*MkvBsowxR;w|2e} z*CmkOY{b#EeDrI8?-J~cVeWZE2g%j1!CcBOzYf{Fe+o!yv$fD72d;-ds+?eQnm*+gOVT8`V=Y-Z@UW_`?$NxSSI^2AZlL#wOUE$MVKRj|U-Soc3<)yy_6;<{) zp30uT2j#X6pdI7{x0-$0fK8Ap4RSA-xv86G$&Q_{WRPL=te!`gobnqmC|>bPWN@gI zKgVM6O);ed#1Sghzh8ol8l}7I(G{o-{1*~9 B64(F$ From b60860237540cde903b81615c3324f47f03cd756 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Tue, 10 Jan 2017 15:18:27 -0800 Subject: [PATCH 041/125] Add new library MemoryFree This is the MemoryFree libary from http://playground.arduino.cc/code/AvailableMemory, rewritten specifically for Arduino 101 and tinyTILE. It provides some functions for querying available stack & heap space. --- libraries/MemoryFree/README.md | 41 +++++++++++++ .../examples/freeMemory/freeMemory.ino | 46 +++++++++++++++ libraries/MemoryFree/keywords.txt | 11 ++++ libraries/MemoryFree/library.properties | 10 ++++ libraries/MemoryFree/src/MemoryFree.cpp | 58 +++++++++++++++++++ libraries/MemoryFree/src/MemoryFree.h | 48 +++++++++++++++ 6 files changed, 214 insertions(+) create mode 100644 libraries/MemoryFree/README.md create mode 100644 libraries/MemoryFree/examples/freeMemory/freeMemory.ino create mode 100644 libraries/MemoryFree/keywords.txt create mode 100644 libraries/MemoryFree/library.properties create mode 100644 libraries/MemoryFree/src/MemoryFree.cpp create mode 100644 libraries/MemoryFree/src/MemoryFree.h diff --git a/libraries/MemoryFree/README.md b/libraries/MemoryFree/README.md new file mode 100644 index 00000000..d08aa930 --- /dev/null +++ b/libraries/MemoryFree/README.md @@ -0,0 +1,41 @@ +## MemoryFree library for Arduino/Genuino101 and tinyTILE + +This library is a re-write of http://playground.arduino.cc/Code/AvailableMemory +specifically for Curie-based devices. This library defines the same header file +`MemoryFree.h`, and provides the function `freeMemory()`. In addition, two extra +functions are provided; `freeHeap()`, for heap space only, and `freeStack()`, +for free stack space. `freeStack()` can also be used to detect a stack overflow. + +The rewrite is necessary for two reasons: + +* AVR-based boards use a different implementation of `malloc()` than Curie-based + boards, and in order to determine the amount of free heap space, we depend + on specific symbols defined by the `malloc()` implementation. The `malloc()` + implementation used by Curie-based devices + [can be seen here](https://github.com/foss-for-synopsys-dwc-arc-processors/glibc). + +* Curie-based boards have a different memory layout than AVR-based boards. See + the [linker script](https://github.com/01org/corelibs-arduino101/blob/master/variants/arduino_101/linker_scripts/flash.ld) + for Arduino/Genuino 101 for details of the memory layout + for Curie-based devices. + +## Functions + + +`int freeHeap (void)` + +Returns the number of bytes free in the heap, i.e. the number of bytes free +to be allocated using `malloc()`. + +`int freeStack (void)` + +Returns the number of bytes free in the stack, i.e. the space between the +current stack frame and the end of the stack area. This function will return +a negative number in the event of a stack overflow, representing the size of +the overflow; for example, a return value of -20 means that the current stack +frame is 20 bytes past the end of the stack area. + +`int freeMemory (void)` + +Returns the number of bytes free in both the stack and the heap. If a stack +overflow has occurred, only the number of bytes free in the heap is returned. diff --git a/libraries/MemoryFree/examples/freeMemory/freeMemory.ino b/libraries/MemoryFree/examples/freeMemory/freeMemory.ino new file mode 100644 index 00000000..724f81ac --- /dev/null +++ b/libraries/MemoryFree/examples/freeMemory/freeMemory.ino @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +#include + +void setup () { + Serial.begin(9600); + while(!Serial); +} + +void loop() { + char *p; + + Serial.println("Free memory: " + String(freeMemory())); + Serial.println("Allocating 24 bytes ..."); + + p = (char *)malloc(24); + Serial.println("Free memory: " + String(freeMemory())); + + Serial.println("Freeing 24 bytes ..."); + free(p); + Serial.println("Free memory: " + String(freeMemory())); + + delay(2000); +} + +/* + * Copyright (c) 2017 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/MemoryFree/keywords.txt b/libraries/MemoryFree/keywords.txt new file mode 100644 index 00000000..9da291a1 --- /dev/null +++ b/libraries/MemoryFree/keywords.txt @@ -0,0 +1,11 @@ +####################################### +# Syntax Coloring Map For MemoryFree +####################################### + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +freeStack KEYWORD2 +freeHeap KEYWORD2 +freeMemory KEYWORD2 diff --git a/libraries/MemoryFree/library.properties b/libraries/MemoryFree/library.properties new file mode 100644 index 00000000..1acd530e --- /dev/null +++ b/libraries/MemoryFree/library.properties @@ -0,0 +1,10 @@ +name=MemoryFree +version=1.0 +author=Erik Nyquist +maintainer=Erik Nyquist +sentence=Determines the amount of available memory in the heap +paragraph=Determines the amount of memory, in bytes, that is available for allocation using malloc() +category=Uncategorized +url= +architectures=arc32 + diff --git a/libraries/MemoryFree/src/MemoryFree.cpp b/libraries/MemoryFree/src/MemoryFree.cpp new file mode 100644 index 00000000..8fbba7ee --- /dev/null +++ b/libraries/MemoryFree/src/MemoryFree.cpp @@ -0,0 +1,58 @@ +/* + * MemoryFree.cpp: taken from http://playground.arduino.cc/Code/AvailableMemory, + * re-written for the Arduino 101 which uses a different malloc implementation. + * + * Arduino 101 malloc source: + * https://github.com/foss-for-synopsys-dwc-arc-processors/glibc + * + * mallinfo() struct details: + * http://man7.org/linux/man-pages/man3/mallinfo.3.html + * + * Copyright (c) 2017 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 "MemoryFree.h" + +extern char __start_heap; +extern char __end_heap; +extern char __stack_size; +extern char __stack_start; + +int freeStack() { + int stack_end; + int mark; + + stack_end = ((int)&__stack_start) - ((int)&__stack_size); + return ((int)&mark) - stack_end; +} + +int freeHeap (void) { + int hsize; + struct mallinfo mi; + + mi = mallinfo(); + hsize = (int)&__end_heap - (int)&__start_heap; + return (hsize - mi.arena) + mi.fordblks; +} + +int freeMemory (void) { + int heap = freeHeap(); + int stack = freeStack(); + return (stack < 0) ? heap : stack + heap; +} diff --git a/libraries/MemoryFree/src/MemoryFree.h b/libraries/MemoryFree/src/MemoryFree.h new file mode 100644 index 00000000..bf532bc2 --- /dev/null +++ b/libraries/MemoryFree/src/MemoryFree.h @@ -0,0 +1,48 @@ +/* + * 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 MEMORYFREE_H +#define MEMORYFREE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* freeHeap: returns the size (in bytes) of unused space on the heap, + * i.e. the number of bytes available for allocation by 'malloc()' */ +int freeHeap(void); + +/* freeStack: returns the size (in bytes) of remaining free space in the stack, + * i.e. the difference between our current position in the stack, and the end + * of usable stack space. + * + * NOTE: This function will return a negative number to indicate a stack + * overflow, i.e. a return value of -20 means you have overrun the allocated + * stack area by 20 bytes. */ +int freeStack(void); + +/* freeMemory: returns the combined free memory in both the stack and heap, + * except in the case where a stack overflow has occurred (i.e. freeStack + * returns a negative number). In this case, only the amount of free heap + * space will be returned. */ +int freeMemory(void); + +#ifdef __cplusplus +} +#endif + +#endif From 001cab44592510cf9ea799d7a6f34d237d5b149c Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Wed, 1 Feb 2017 11:36:59 -0800 Subject: [PATCH 042/125] freeMemory.ino: add introductory comment --- libraries/MemoryFree/examples/freeMemory/freeMemory.ino | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/MemoryFree/examples/freeMemory/freeMemory.ino b/libraries/MemoryFree/examples/freeMemory/freeMemory.ino index 724f81ac..15c08d28 100644 --- a/libraries/MemoryFree/examples/freeMemory/freeMemory.ino +++ b/libraries/MemoryFree/examples/freeMemory/freeMemory.ino @@ -1,4 +1,8 @@ /* + * freeMemory.ino: This sketch demonstrates the use of the freeMemory() + * function to measure the amount of free memory available in the system, + * before and after using 'malloc' to allocate some memory. + * * Copyright (c) 2016 Intel Corporation. All rights reserved. * See the bottom of this file for the license terms. */ From 78c3d0ad0a5475f7809d31dfd1c2d75cbcf4a58c Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Thu, 2 Feb 2017 13:49:50 -0800 Subject: [PATCH 043/125] EEPROM library: move function definitions into separate .cpp files --- libraries/EEPROM/src/EEPROM.cpp | 125 ++++++++++++++++++++++++++++++++ libraries/EEPROM/src/EEPROM.h | 113 +++-------------------------- 2 files changed, 134 insertions(+), 104 deletions(-) create mode 100644 libraries/EEPROM/src/EEPROM.cpp diff --git a/libraries/EEPROM/src/EEPROM.cpp b/libraries/EEPROM/src/EEPROM.cpp new file mode 100644 index 00000000..8295c064 --- /dev/null +++ b/libraries/EEPROM/src/EEPROM.cpp @@ -0,0 +1,125 @@ +/* + EEPROM.h - EEPROM library + Original Copyright (c) 2006 David A. Mellis. All right reserved. + New version by Christopher Andrews 2015. + Curie porting by Intel and Arduino LLC - 2016 + 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 + +EEPROMClass EEPROM; + +void CurieClear() +{ + //erase the 2k bytes of the eeprom section inside the otp area + *(uint32_t*)(ROM_WR_CTRL) = 0x4002; + //wait for erase to be complete + #if 0 + // TODO: wait for FLASH_STTS.ER_DONE to be set to 1 + while(((*(uint32_t*)FLASH_STTS) & 0x01) == 0) { + delay(1); + } + #endif + delay(5); +} + +void CurieRestoreMemory(uint32_t* buffer, uint32_t size) +{ + uint32_t rom_wr_ctrl = 0; + uint32_t address; + + for (uint32_t i=0; i 0x7FF)) + { + return 0; + } + int offset = address%4; + uint32_t value = *(uint32_t*)(EEPROM_ADDR+(address/4)*4); + value = (value >> ((3-offset)*8)) & 0xFF; + return (uint8_t)value; +} + +uint32_t CurieRead32(uint32_t address) +{ + if((address > 0x7FF)) + { + return 0; + } + uint32_t value = *(uint32_t*)(EEPROM_ADDR+(address/4)*4); + return value; +} + +void CurieWrite8(uint32_t address, uint8_t data) +{ + //make sure address is valid + if((address > 0x7FF)) + { + return; + } + + uint8_t currentValue = CurieRead8(address); + //only do something if value is different from what is currently stored + if(currentValue==data) + { + return; + } + + uint32_t currentDword = CurieRead32(address); + + int offset = address%4; + + uint32_t data32 = (currentDword & ~(uint32_t)(0xFF << ((3-offset)*8))); + data32 = data32 | (data << ((3-offset)*8)); + + if (currentValue != 0xFF) { + uint32_t dump[EEPROM_SIZE/4]; + memcpy(dump, (uint32_t *)EEPROM_ADDR, EEPROM_SIZE); + dump[(address >> 2)] = data32; + CurieClear(); + CurieRestoreMemory((uint32_t *)dump, EEPROM_SIZE/sizeof(uint32_t)); + return; + } + + uint32_t rom_wr_ctrl = 0; + + //store data into ROM_WR_DATA register + *(uint32_t*)(ROM_WR_DATA) = data32; + address = ((address >> 2) << 2) + EEPROM_OFFSET; + //shift left 2 bits to store offset into bits 19:2 (WR_ADDR) + rom_wr_ctrl = (address)<<2; + rom_wr_ctrl |= 0x00000001; //set (WR_REQ) bit + *(uint32_t*)(ROM_WR_CTRL) = rom_wr_ctrl; + + delay(3); //give it enough time to finish writing +} diff --git a/libraries/EEPROM/src/EEPROM.h b/libraries/EEPROM/src/EEPROM.h index c52737c7..ac5e05f4 100644 --- a/libraries/EEPROM/src/EEPROM.h +++ b/libraries/EEPROM/src/EEPROM.h @@ -16,8 +16,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#ifndef EEPROM_h -#define EEPROM_h +#ifndef EEPROM_H +#define EEPROM_H #define ROM_WR_CTRL 0xb0100004 #define ROM_WR_DATA 0xb0100008 @@ -28,112 +28,17 @@ #define EEPROM_SIZE 2048 //EEPROM size in bytes - #include #include "Arduino.h" /* Curie specific implementation of "atomic" read8 and write8 on OTP flash storage */ -void CurieClear() -{ - //erase the 2k bytes of the eeprom section inside the otp area - *(uint32_t*)(ROM_WR_CTRL) = 0x4002; - //wait for erase to be complete - #if 0 - while(((*(uint32_t*)FLASH_STTS) & 0x01) == 0) { // TODO: wait for FLASH_STTS.ER_DONE to be set to 1 - delay(1); - } - #endif - delay(5); -} - -void CurieRestoreMemory(uint32_t* buffer, uint32_t size) -{ - uint32_t rom_wr_ctrl = 0; - uint32_t address; - - for (uint32_t i=0; i 0x7FF)) - { - return 0; - } - int offset = address%4; - uint32_t value = *(uint32_t*)(EEPROM_ADDR+(address/4)*4); - value = (value >> ((3-offset)*8)) & 0xFF; - return (uint8_t)value; -} - -uint32_t CurieRead32(uint32_t address) -{ - if((address > 0x7FF)) - { - return 0; - } - uint32_t value = *(uint32_t*)(EEPROM_ADDR+(address/4)*4); - return value; -} - -void CurieWrite8(uint32_t address, uint8_t data) -{ - //make sure address is valid - if((address > 0x7FF)) - { - return; - } - - uint8_t currentValue = CurieRead8(address); - //only do something if value is different from what is currently stored - if(currentValue==data) - { - return; - } - - uint32_t currentDword = CurieRead32(address); - - int offset = address%4; - - uint32_t data32 = (currentDword & ~(uint32_t)(0xFF << ((3-offset)*8))); - data32 = data32 | (data << ((3-offset)*8)); - - if (currentValue != 0xFF) { - uint32_t dump[EEPROM_SIZE/4]; - memcpy(dump, (uint32_t *)EEPROM_ADDR, EEPROM_SIZE); - dump[(address >> 2)] = data32; - CurieClear(); - CurieRestoreMemory((uint32_t *)dump, EEPROM_SIZE/sizeof(uint32_t)); - return; - } - - uint32_t rom_wr_ctrl = 0; - - //store data into ROM_WR_DATA register - *(uint32_t*)(ROM_WR_DATA) = data32; - address = ((address >> 2) << 2) + EEPROM_OFFSET; - rom_wr_ctrl = (address)<<2; //shift left 2 bits to store offset into bits 19:2 (WR_ADDR) - rom_wr_ctrl |= 0x00000001; //set (WR_REQ) bit - *(uint32_t*)(ROM_WR_CTRL) = rom_wr_ctrl; +void CurieClear(); +void CurieRestoreMemory(uint32_t* buffer, uint32_t size); - delay(3); //give it enough time to finish writing -} +uint8_t CurieRead8(uint32_t address); +uint32_t CurieRead32(uint32_t address); +void CurieWrite8(uint32_t address, uint8_t data); /*** EERef class. @@ -252,5 +157,5 @@ struct EEPROMClass{ } }; -static EEPROMClass EEPROM; -#endif \ No newline at end of file +extern EEPROMClass EEPROM; +#endif From 40b0c43967187137c9bde6bf88f57732a8a30aa4 Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 9 Jan 2017 10:50:13 +0800 Subject: [PATCH 044/125] Jira 797, Central can scan with MAC address, git 376 New feature: - Add the capability for a Central to scan for a Peripheral using its MAC address. Code Modifications: - Added the API for MAC address scanning. - For Device Manager, added provision for holding the MAC address of a Peripheral. - For scan discovery handler, added the checking for MAC address comparison, if MAC address scanning is requested. File changes: 1. libraries/CurieBLE/src/BLEDevice.cpp: - Added API for MAC address scanning. 2. libraries/CurieBLE/src/BLEDevice.h: - API definition. 3. libraries/CurieBLE/src/internal/BLEDeviceManager.cpp: - Added provision for storing the MAC address. - Added MAC address comparison in the scan discovery handle if MAC address scanning is selected. 4. libraries/CurieBLE/src/internal/BLEDeviceManager.h: - Prototyping. --- libraries/CurieBLE/src/BLEDevice.cpp | 6 ++++++ libraries/CurieBLE/src/BLEDevice.h | 3 ++- .../CurieBLE/src/internal/BLEDeviceManager.cpp | 14 ++++++++++++++ libraries/CurieBLE/src/internal/BLEDeviceManager.h | 2 ++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/libraries/CurieBLE/src/BLEDevice.cpp b/libraries/CurieBLE/src/BLEDevice.cpp index e59b5462..d96b70c2 100644 --- a/libraries/CurieBLE/src/BLEDevice.cpp +++ b/libraries/CurieBLE/src/BLEDevice.cpp @@ -275,6 +275,12 @@ void BLEDevice::scanForUuid(String uuid, bool withDuplicates) startScan(withDuplicates); } +void BLEDevice::scanForAddress(String macaddr, bool withDuplicates) +{ + BLEDeviceManager::instance()->setAdvertiseCritical(macaddr.c_str()); + startScan(withDuplicates); +} + void BLEDevice::stopScan() { BLEDeviceManager::instance()->stopScanning(); diff --git a/libraries/CurieBLE/src/BLEDevice.h b/libraries/CurieBLE/src/BLEDevice.h index 590d933a..6d3315fe 100644 --- a/libraries/CurieBLE/src/BLEDevice.h +++ b/libraries/CurieBLE/src/BLEDevice.h @@ -420,8 +420,9 @@ class BLEDevice * @note option to filter out duplicate addresses for Arduino. * The current only support fileter duplicate mode. */ - void scanForUuid(String uuid, bool withDuplicates); + void scanForUuid(String uuid, bool withDuplicates); + void scanForAddress(String macaddr, bool withDuplicates = true); /** * @brief Stop scanning for peripherals * diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp index 930e9faf..14f7a4c6 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp @@ -77,6 +77,7 @@ BLEDeviceManager::BLEDeviceManager(): 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(&_adv_accept_device, 0, sizeof(_adv_accept_device)); memset(_peer_peripheral, 0, sizeof(_peer_peripheral)); memset(_peer_peripheral_adv_data, 0, sizeof(_peer_peripheral_adv_data)); @@ -562,6 +563,7 @@ bool BLEDeviceManager::stopScanning() void BLEDeviceManager::clearAdvertiseCritical() { memset(&_adv_accept_critical, 0, sizeof(_adv_accept_critical)); + memset(&_adv_accept_device, 0, sizeof(_adv_accept_device)); //memset(&_adv_critical_service_uuid, 0, sizeof(_adv_critical_service_uuid)); } @@ -598,6 +600,11 @@ void BLEDeviceManager::setAdvertiseCritical(BLEService& service) _adv_accept_critical.data = data; } +void BLEDeviceManager::setAdvertiseCritical(const char* macaddress) +{ + BLEUtils::macAddressString2BT(macaddress, _adv_accept_device); +} + bool BLEDeviceManager::hasLocalName(const BLEDevice* device) const { if (BLEUtils::isLocalBLE(*device) == true) @@ -1248,6 +1255,13 @@ void BLEDeviceManager::handleDeviceFound(const bt_addr_le_t *addr, if (type == BT_LE_ADV_IND || type == BT_LE_ADV_DIRECT_IND) { //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + // Filter address + if (BLEUtils::macAddressValid(_adv_accept_device) == true && + (memcmp(addr->val, _adv_accept_device.val, sizeof (addr->val)) != 0)) + { + return; + } + while (data_len > 1) { uint8_t len = data[0]; diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.h b/libraries/CurieBLE/src/internal/BLEDeviceManager.h index 3ad80a41..2e9d93b0 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.h +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.h @@ -305,6 +305,7 @@ class BLEDeviceManager void clearAdvertiseCritical(); void setAdvertiseCritical(String name); void setAdvertiseCritical(BLEService& service); + void setAdvertiseCritical(const char* macaddress); bool startScanning(); // start scanning for peripherals bool startScanningWithDuplicates(); // start scanning for peripherals, and report all duplicates bool stopScanning(); // stop scanning for peripherals @@ -378,6 +379,7 @@ class BLEDeviceManager 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 _adv_accept_device; bt_addr_le_t _wait_for_connect_peripheral; uint8_t _wait_for_connect_peripheral_adv_data[BLE_MAX_ADV_SIZE]; From 0eac6a00a766e424b87aecbbb734eb76bcb71694 Mon Sep 17 00:00:00 2001 From: lianggao Date: Fri, 13 Jan 2017 22:07:08 +0800 Subject: [PATCH 045/125] Jira 794 Inconsistent writeInt() result with different Peripherals, git 385 Root cause: - As a Central, the write() operation can produce two different commands to a Peripheral depending on the Characteristic. - One command is a simple write without aknowledgement from Peripheral and the other requires a complete handshake for each write operation. - Root cause of the issue was that the function, BLECharacteristicImp::write(), did not check the characteristic property to determine if the caller requires a response. It just treats all calls do not require a response. Feature added: - For write with respond from Peripheral, it is now a blocking call (it also satisfied Arduino's request). Modifications: 1. libraries/CurieBLE/src/internal/BLECallbacks.cpp: - ble_on_write_no_rsp_complete: CB to process Peripheral Response to complete a write operation. 2. libraries/CurieBLE/src/internal/BLECallbacks.h: - Function definition added. 3. libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp: - In function write(), check the characteristic property to determine whether the call requires a response. - Added a flag to signal the waiting on Peripheral Response for a write operation. - Write with response is now a blocking call. 4. BLECharacteristicImp.h: - Prototyping. --- .../CurieBLE/src/internal/BLECallbacks.cpp | 5 +++ .../CurieBLE/src/internal/BLECallbacks.h | 3 ++ .../src/internal/BLECharacteristicImp.cpp | 39 +++++++++++++++---- .../src/internal/BLECharacteristicImp.h | 5 +++ 4 files changed, 45 insertions(+), 7 deletions(-) diff --git a/libraries/CurieBLE/src/internal/BLECallbacks.cpp b/libraries/CurieBLE/src/internal/BLECallbacks.cpp index b6234750..568c54f8 100644 --- a/libraries/CurieBLE/src/internal/BLECallbacks.cpp +++ b/libraries/CurieBLE/src/internal/BLECallbacks.cpp @@ -223,4 +223,9 @@ void ble_central_device_found(const bt_addr_le_t *addr, ad, len); } +void ble_on_write_no_rsp_complete(struct bt_conn *conn, uint8_t err, + const void *data) +{ + BLECharacteristicImp::writeResponseReceived(conn, err, data); +} diff --git a/libraries/CurieBLE/src/internal/BLECallbacks.h b/libraries/CurieBLE/src/internal/BLECallbacks.h index 882c134a..213bd794 100644 --- a/libraries/CurieBLE/src/internal/BLECallbacks.h +++ b/libraries/CurieBLE/src/internal/BLECallbacks.h @@ -74,5 +74,8 @@ uint8_t profile_service_read_rsp_process(bt_conn_t *conn, const void *data, uint16_t length); +void ble_on_write_no_rsp_complete(struct bt_conn *conn, uint8_t err, + const void *data); + #endif diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp index a5a60c57..91c46a8a 100644 --- a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp @@ -27,6 +27,7 @@ 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}; +volatile bool BLECharacteristicImp::_gattc_writing = false; BLECharacteristicImp::BLECharacteristicImp(const bt_uuid_t* uuid, unsigned char properties, @@ -637,13 +638,20 @@ bool BLECharacteristicImp::read() return _reading; } +void BLECharacteristicImp::writeResponseReceived(struct bt_conn *conn, + uint8_t err, + const void *data) +{ + _gattc_writing = false; +} + bool BLECharacteristicImp::write(const unsigned char value[], uint16_t length) { int retval = 0; bt_conn_t* conn = NULL; - if (true == BLEUtils::isLocalBLE(_ble_device)) + if (true == BLEUtils::isLocalBLE(_ble_device) || true == _gattc_writing) { // GATT server can't write return false; @@ -655,12 +663,29 @@ bool BLECharacteristicImp::write(const unsigned char value[], return false; } - // Send read request - retval = bt_gatt_write_without_response(conn, - _value_handle, - value, - length, - false); + // Send write request + if (_gatt_chrc.properties | BT_GATT_CHRC_WRITE_WITHOUT_RESP) + { + retval = bt_gatt_write_without_response(conn, + _value_handle, + value, + length, + false); + } + else if (_gatt_chrc.properties | BT_GATT_CHRC_WRITE) + { + _gattc_writing = true; + retval = bt_gatt_write(conn, + _value_handle, + 0, + value, + length, + ble_on_write_no_rsp_complete); + while (_gattc_writing) + { + delay(2); + } + } bt_conn_unref(conn); return (0 == retval); } diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.h b/libraries/CurieBLE/src/internal/BLECharacteristicImp.h index c6cee5ab..1066992c 100644 --- a/libraries/CurieBLE/src/internal/BLECharacteristicImp.h +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.h @@ -168,6 +168,10 @@ class BLECharacteristicImp: public BLEAttribute{ */ bool write(const unsigned char value[], uint16_t length); + + static void writeResponseReceived(struct bt_conn *conn, + uint8_t err, + const void *data); int descriptorCount() const; uint8_t discoverResponseProc(bt_conn_t *conn, @@ -325,6 +329,7 @@ class BLECharacteristicImp: public BLEAttribute{ bool _subscribed; bool _reading; + static volatile bool _gattc_writing; bt_gatt_read_params_t _read_params; // GATT read parameter typedef LinkNode BLEDescriptorLinkNodeHeader; From fe14b8bd2600544330e95ccf22f7f91c114467c0 Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 6 Feb 2017 14:13:37 +0800 Subject: [PATCH 046/125] Fix #380 characteristic written returns incorrect value after first connect 1. Not set the flage when local device try to set the value. 2. Changed file libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp --- libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp index 91c46a8a..415aa5ff 100644 --- a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp @@ -260,6 +260,7 @@ bool BLECharacteristicImp::setValue(const unsigned char value[], uint16_t length) { _setValue(value, length, 0); + _value_updated = true; if (BLEUtils::isLocalBLE(_ble_device) == true) { // GATT server @@ -555,7 +556,6 @@ BLECharacteristicImp::_setValue(const uint8_t value[], uint16_t length, uint16_t } } - _value_updated = true; memcpy(_value + offset, value, length); _value_length = length; } From 547ecd723e39da4044cb636d6bdbb8956cb2f2f2 Mon Sep 17 00:00:00 2001 From: lianggao Date: Fri, 13 Jan 2017 09:04:29 +0800 Subject: [PATCH 047/125] Sketch crash using both Firmata and BLE library, gitHub issue #377 1. The BLEStream object adds profile when boot but the HW doesn't be initialized and makes APP crash. 2. Change the BLE initial process not depend on HW. Buffer the attributes if HW not initialized. 3. Changed files libraries/CurieBLE/src/BLEDevice.h - expose constructor libraries/CurieBLE/src/BLEPeripheral.cpp - not call HW init function libraries/CurieBLE/src/internal/BLEDeviceManager.cpp - change init order libraries/CurieBLE/src/internal/BLEProfileManager.cpp - change constructor --- libraries/CurieBLE/src/BLEDevice.h | 20 +++++----- libraries/CurieBLE/src/BLEPeripheral.cpp | 37 +------------------ .../src/internal/BLEDeviceManager.cpp | 12 ++++-- .../src/internal/BLEProfileManager.cpp | 2 +- 4 files changed, 20 insertions(+), 51 deletions(-) diff --git a/libraries/CurieBLE/src/BLEDevice.h b/libraries/CurieBLE/src/BLEDevice.h index 6d3315fe..56742f1b 100644 --- a/libraries/CurieBLE/src/BLEDevice.h +++ b/libraries/CurieBLE/src/BLEDevice.h @@ -59,6 +59,16 @@ class BLEDevice */ BLEDevice(const BLEDevice* bledevice); BLEDevice(const BLEDevice& bledevice); + /** + * @brief The BLE device constructure + * + * @param[in] bleaddress BLE device address + * + * @return none + * + * @note none + */ + BLEDevice(const bt_addr_le_t* bleaddress); virtual ~BLEDevice(); @@ -654,16 +664,6 @@ class BLEDevice 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(); diff --git a/libraries/CurieBLE/src/BLEPeripheral.cpp b/libraries/CurieBLE/src/BLEPeripheral.cpp index 84b85c88..760d374f 100644 --- a/libraries/CurieBLE/src/BLEPeripheral.cpp +++ b/libraries/CurieBLE/src/BLEPeripheral.cpp @@ -55,67 +55,37 @@ BLEPeripheral::~BLEPeripheral(void) void BLEPeripheral::setAdvertisedServiceUuid(const char* advertisedServiceUuid) { - if (!_initCalled) { - init(); - } - BLE.setAdvertisedServiceUuid(advertisedServiceUuid); } + void BLEPeripheral::setLocalName(const char* localName) { - if (!_initCalled) { - init(); - } - BLE.setLocalName(localName); } - void BLEPeripheral::setDeviceName(const char *deviceName) { - if (!_initCalled) { - init(); - } - BLE.setDeviceName(deviceName); } void BLEPeripheral::setAppearance(const unsigned short appearance) { - if (!_initCalled) { - init(); - } - BLE.setAppearance(appearance); } void BLEPeripheral::setConnectionInterval(const unsigned short minConnInterval, const unsigned short maxConnInterval) { - if (!_initCalled) { - init(); - } - BLE.setConnectionInterval(minConnInterval, maxConnInterval); } void BLEPeripheral::addAttribute(BLEService& service) { - if (!_initCalled) - { - init(); - } - BLE.addService(service); _lastService = &service; } void BLEPeripheral::addAttribute(BLECharacteristic& characteristic) { - if (!_initCalled) - { - init(); - } - if (_lastService) { _lastService->addCharacteristic(characteristic); @@ -125,11 +95,6 @@ void BLEPeripheral::addAttribute(BLECharacteristic& characteristic) void BLEPeripheral::addAttribute(BLEDescriptor& descriptor) { - if (!_initCalled) - { - init(); - } - if (_lastCharacteristic) { _lastCharacteristic->addDescriptor(descriptor); diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp index 14f7a4c6..e21c40f5 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp @@ -98,11 +98,11 @@ BLEDeviceManager::~BLEDeviceManager() bool BLEDeviceManager::begin(BLEDevice *device) { - if (NULL == _local_ble && false == *device) + if (NULL == _local_ble) { _local_ble = device; - _local_ble->setAddress(_local_bda); bt_le_set_mac_address(_local_bda); + // Set device name setDeviceName(); _state = BLE_PERIPH_STATE_READY; @@ -134,7 +134,8 @@ void BLEDeviceManager::poll() } void BLEDeviceManager::end() -{} +{ +} bool BLEDeviceManager::connected(const BLEDevice *device) const { @@ -288,7 +289,10 @@ void BLEDeviceManager::setDeviceName(const char* deviceName) if (len > BLE_MAX_DEVICE_NAME) len = BLE_MAX_DEVICE_NAME; memcpy(_device_name, deviceName, len); - setDeviceName(); + if (NULL != _local_ble) + { + setDeviceName(); + } } } diff --git a/libraries/CurieBLE/src/internal/BLEProfileManager.cpp b/libraries/CurieBLE/src/internal/BLEProfileManager.cpp index c3b9cd58..df34c996 100644 --- a/libraries/CurieBLE/src/internal/BLEProfileManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEProfileManager.cpp @@ -25,7 +25,7 @@ #include "BLECallbacks.h" #include "BLEUtils.h" -BLEDevice BLE; +BLEDevice BLE(BLEUtils::bleGetLoalAddress()); BLEProfileManager* BLEProfileManager::_instance = NULL; From 0285190f5d94dd2228a33ec5ef91d0e3cecfed56 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Thu, 9 Feb 2017 10:18:37 -0800 Subject: [PATCH 048/125] EEPROM.cpp: Use double quotes for include file works in the Arduino IDE, but it's not actually in a system include location so it makes non-IDE (i.e. Klocwork) builds fail --- libraries/EEPROM/src/EEPROM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/EEPROM/src/EEPROM.cpp b/libraries/EEPROM/src/EEPROM.cpp index 8295c064..327061fc 100644 --- a/libraries/EEPROM/src/EEPROM.cpp +++ b/libraries/EEPROM/src/EEPROM.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "EEPROM.h" EEPROMClass EEPROM; From d6c68f26914bf9508e8b76081d383a5cd432c7b6 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Tue, 14 Feb 2017 12:40:25 -0800 Subject: [PATCH 049/125] flash.ld: fix mis-aligned .data section The end of the .data section in flash must be explicitly word-aligned, otherwise corruption can occur in a .data section that happens to contain non-word aligned data. --- variants/arduino_101/linker_scripts/flash.ld | 1 + 1 file changed, 1 insertion(+) diff --git a/variants/arduino_101/linker_scripts/flash.ld b/variants/arduino_101/linker_scripts/flash.ld index 03344a54..335f7ef6 100644 --- a/variants/arduino_101/linker_scripts/flash.ld +++ b/variants/arduino_101/linker_scripts/flash.ld @@ -113,6 +113,7 @@ SECTIONS __data_ram_start = .; *(.data) *(".data.*") + . = ALIGN(4); } > SRAM __data_ram_end = .; From ff32c97b547b42e8afa3e423c7ad9ddf4057b4dd Mon Sep 17 00:00:00 2001 From: lianggao Date: Tue, 20 Dec 2016 18:21:23 +0800 Subject: [PATCH 050/125] BLE Peripheral cannot discover Central attributes 1. Add test sketches discoveratperipheral.ino and profileatcentral.ino 2. Modify BLEDeviceManager.cpp to add central to profile buffer in peripheral role. --- .../BatteryMonitor_Central.ino | 51 ++++++- .../central/CentralDouble/CentralDouble.ino | 126 ++++++++++++++++++ .../central/IMUBleCentral/IMUBleCentral.ino | 61 +++++---- .../central/led_control/led_control.ino | 58 +++++--- .../peripheral_explorer.ino | 53 +++++--- .../CurieBLE/examples/central/scan/scan.ino | 50 ++++--- .../central/scan_callback/scan_callback.ino | 63 ++++++--- .../sensortag_button/sensortag_button.ino | 51 ++++--- .../BatteryMonitor_Notification.ino | 26 ++-- .../IMUBleNotification/IMUBleNotification.ino | 55 +++++--- .../PeripheralDouble/PeripheralDouble.ino | 95 +++++++++++++ .../CurieBLE/examples/peripheral/led/led.ino | 49 ++++--- .../peripheral/led_callback/led_callback.ino | 43 +++--- .../AccelerometerBLE/AccelerometerBLE.ino | 94 ------------- .../Genuino101CurieBLEHeartRateMonitor.ino | 52 +++++--- .../StandardFirmataBLE/StandardFirmataBLE.ino | 56 +++++--- .../StarterKit101_BLE/StarterKit101_BLE.ino | 37 ----- .../examples/test/central/central.ino | 86 ------------ .../discoveratperipheral.ino | 124 +++++++++++++++++ .../test/notification/notification.ino | 78 ----------- .../test/notifycentral/notifycentral.ino | 89 ------------- .../profileatcentral/profileatcentral.ino | 91 +++++++++++++ .../CurieBLE/examples/test/test/test.ino | 71 ---------- .../src/internal/BLEDeviceManager.cpp | 5 +- 24 files changed, 894 insertions(+), 670 deletions(-) create mode 100644 libraries/CurieBLE/examples/central/CentralDouble/CentralDouble.ino create mode 100644 libraries/CurieBLE/examples/peripheral/PeripheralDouble/PeripheralDouble.ino delete mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/AccelerometerBLE/AccelerometerBLE.ino delete mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/StarterKit101_BLE/StarterKit101_BLE.ino delete mode 100644 libraries/CurieBLE/examples/test/central/central.ino create mode 100644 libraries/CurieBLE/examples/test/discoveratperipheral/discoveratperipheral.ino delete mode 100644 libraries/CurieBLE/examples/test/notification/notification.ino delete mode 100644 libraries/CurieBLE/examples/test/notifycentral/notifycentral.ino create mode 100644 libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.ino delete mode 100644 libraries/CurieBLE/examples/test/test/test.ino diff --git a/libraries/CurieBLE/examples/central/BatteryMonitor_Central/BatteryMonitor_Central.ino b/libraries/CurieBLE/examples/central/BatteryMonitor_Central/BatteryMonitor_Central.ino index 89469631..90a39dfb 100644 --- a/libraries/CurieBLE/examples/central/BatteryMonitor_Central/BatteryMonitor_Central.ino +++ b/libraries/CurieBLE/examples/central/BatteryMonitor_Central/BatteryMonitor_Central.ino @@ -1,13 +1,27 @@ -#include +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ /* - This sketch example works with BatteryMonitor_Notification.ino + * Sketch: BatteryMonitor_Central.ino + * + * Description: + * This sketch will receive the notifications and output the received + * data in the serial monitor. It also illustrates using a non-typed + * characteristic. + * + * Notes: + * + * - Set the baud rate to 115200 on the serial monitor to accomodate + * the speed of constant data updates + * - Expected Peripheral name: BatteryMonitorSketch + * - Expected Peripheral Characteristic: 2A19 + * - Expected Peripheral sketch: BatteryMonitor_Notification.ino + * + */ - BatteryMonitor_Notification 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 -*/ +#include #define LED_PIN 13 @@ -123,3 +137,26 @@ void printData(const unsigned char data[], int length) { Serial.print(b); } } + +/* + 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/CentralDouble/CentralDouble.ino b/libraries/CurieBLE/examples/central/CentralDouble/CentralDouble.ino new file mode 100644 index 00000000..45089df4 --- /dev/null +++ b/libraries/CurieBLE/examples/central/CentralDouble/CentralDouble.ino @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: CentralDouble.ino. + * + * Description: + * This is a simple BLE sketch that initiates the + * Arduino platform as a Central. It reads a double value from + * a connected Peripheral. The sketch exercises: Scanning for + * a specific Peripheral, connecting to it, discover its Attributes, + * and exercise a specific Characteristic to read a double value + * from the Peripheral. + * + * Notes: + * Expected Peripheral name: DataTest + * Expected Peripheral Characteristic: 19b20001e8f2537e4f6cd104768a1214 + * Expected Characteristic read value: double. + */ + +#include "CurieBLE.h" + + +// LED pin +#define LED_PIN 13 + +void setup() { + Serial.begin(9600); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial.println(BLE.address()); + + BLE.scanForName("DataTest"); +} + +void loop() { + BLEDevice peripheral = BLE.available(); + if (peripheral) + { + Serial.println(peripheral.address()); + BLE.stopScan(); + // central connected to peripheral + controlLogic(peripheral); + BLE.scanForName("DataTest"); + } +} + + +void controlLogic(BLEDevice &peripheral) +{ + // 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; + } + + if (peripheral.discoverAttributes() == false) + { + Serial.println("Discover failed, Disconnecting..."); + peripheral.disconnect(); + return; + } + + BLECharacteristic doubleCharacteristic = peripheral.characteristic("19b20001e8f2537e4f6cd104768a1214"); + + if (!doubleCharacteristic) + { + peripheral.disconnect(); + Serial.println("Peripheral does not have test double characteristic!"); + delay(5000); + return; + } + doubleCharacteristic.subscribe(); + + while (peripheral.connected()) + { + doubleCharacteristic.read(); + delay(1000); + if (doubleCharacteristic.valueUpdated()) + { + Serial.print("Double characteristic value: "); + Serial.println(doubleCharacteristic.doubleValue()); + } + delay(1000); + } + Serial.print("Disconnected"); + Serial.println(peripheral.address()); +} + + + +/* + Arduino BLE Peripheral Double read/write test 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 +*/ + + diff --git a/libraries/CurieBLE/examples/central/IMUBleCentral/IMUBleCentral.ino b/libraries/CurieBLE/examples/central/IMUBleCentral/IMUBleCentral.ino index 60748578..739a8b05 100644 --- a/libraries/CurieBLE/examples/central/IMUBleCentral/IMUBleCentral.ino +++ b/libraries/CurieBLE/examples/central/IMUBleCentral/IMUBleCentral.ino @@ -1,30 +1,24 @@ /* - 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 + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ /* - This sketch example works with IMUBleNotification.ino + * Sketch: IMUBleCentral.ino + * + * Description: + * This sketch will receive the notifications and output the + * received data in the serial monitor. It also illustrates + * using a non-typed characteristic. + * + * Notes: + * + * - Expected Peripheral name: Imu + * - Expected Peripheral Characteristic: F7580003-153E-D4F6-F26D-43D8D98EEB13 + * - Expected Peripheral sketch: 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. -*/ +#include #define LED_PIN 13 #define MAX_IMU_RECORD 1 @@ -123,3 +117,24 @@ void controlImu(BLEDevice peripheral) Serial.print("Disconnected"); Serial.println(peripheral.address()); } + + +/* + 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 +*/ + + diff --git a/libraries/CurieBLE/examples/central/led_control/led_control.ino b/libraries/CurieBLE/examples/central/led_control/led_control.ino index 8983a2ef..fd8ce3c2 100644 --- a/libraries/CurieBLE/examples/central/led_control/led_control.ino +++ b/libraries/CurieBLE/examples/central/led_control/led_control.ino @@ -1,22 +1,24 @@ /* - 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 -*/ + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ +/* + * Sketch: led_control.ino + * + * Description: + * This is a Central sketch that looks for a particular Sevice with a + * certain Characteristic from a Peripheral. Upon succesful discovery, + * it reads the state of a button and write that value to the + * Peripheral Characteristic. + * + * Notes: + * + * - Expected Peripheral Service: 19b10000-e8f2-537e-4f6c-d104768a1214 + * - Expected Peripheral Characteristic: 19b10001-e8f2-537e-4f6c-d104768a1214 + * - Expected Peripheral sketch: + * + */ #include @@ -125,3 +127,25 @@ void controlLed(BLEDevice peripheral) { } } } + +/* + 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 +*/ + + + diff --git a/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino b/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino index 5014b7ee..45429655 100644 --- a/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino +++ b/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino @@ -1,21 +1,21 @@ /* - 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. + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ - 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 -*/ +/* + * Sketch: peripheral_explorer.ino + * + * Description: + * This is a Central sketch demonstrating the discovery process + * of a Peripheral. The discovered Attributes are being + * display onto the serial output. + * + * Notes: + * + * - Expected Peripheral name: LED + * + */ #include @@ -150,3 +150,24 @@ void printData(const unsigned char data[], int length) { } } + +/* + 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 +*/ + + diff --git a/libraries/CurieBLE/examples/central/scan/scan.ino b/libraries/CurieBLE/examples/central/scan/scan.ino index 32c77c97..d191d033 100644 --- a/libraries/CurieBLE/examples/central/scan/scan.ino +++ b/libraries/CurieBLE/examples/central/scan/scan.ino @@ -1,21 +1,19 @@ /* - 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. + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ - 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 -*/ +/* + * Sketch: scan.ino + * + * Description: + * This is a Central sketch that performs scanning of Peripherals + * only. It does not perform connection. The Advertized info + * of a Peripheral are display. + * + * Notes: + * + */ #include @@ -68,3 +66,23 @@ void loop() { } } + +/* + 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 +*/ + diff --git a/libraries/CurieBLE/examples/central/scan_callback/scan_callback.ino b/libraries/CurieBLE/examples/central/scan_callback/scan_callback.ino index ddb8b14c..fe853cd6 100644 --- a/libraries/CurieBLE/examples/central/scan_callback/scan_callback.ino +++ b/libraries/CurieBLE/examples/central/scan_callback/scan_callback.ino @@ -1,21 +1,30 @@ /* - 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. + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ - 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 -*/ +/* + * Sketch: scan_callback.ino + * + * Description: + * This is a Central Sketch that scan for Peripherals without + * performing connection. It displays the Adv info for each + * Peripheral scanned. Notice that this sketch makes use of + * the Event Handler, a call back routine, to display the + * info onto the serial output. + * + * Notes: + * + * - It is highly recommended not to use the Event Handler to + * dump information to the serial monitor. Please note + * that Event Handler executes in interrupt context and it + * is expected to perform short execution task. It is due + * to the fact that the entire system timing is impact by the + * execution time of the Event Handler. Accessing the serial + * monitor is relatively time consuming, it is highly desirable + * to perform that in the background. + * + */ #include @@ -70,3 +79,25 @@ void bleCentralDiscoverHandler(BLEDevice peripheral) { Serial.println(); } + + + +/* + 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 +*/ + diff --git a/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino b/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino index 7f3f1aa2..786ff051 100644 --- a/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino +++ b/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino @@ -1,21 +1,17 @@ /* - 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. + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ - 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 -*/ +/* + * Sketch: sensortag_button.ino + * + * Description: + * This Central sketch scan for a Peripheral called the SensorTag. + * It looks for particular Service, discovers all its attributes, + * and them on the serial monitor. + * + */ #include @@ -179,3 +175,26 @@ void monitorSensorTagButtons(BLEDevice peripheral) } } + + + +/* + 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 +*/ + + diff --git a/libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino b/libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino index 2325f351..a44cd6b6 100644 --- a/libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino +++ b/libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino @@ -3,18 +3,24 @@ See the bottom of this file for the license terms. */ -#include - /* - This sketch can work with BatteryMonitor_Central. - - 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 -*/ - + * Sketch: BatteryMonitor_Notification.ino + * + * Description: + * 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 + * + * Notes: + * + * - Expected to work with BatteryMonitor_Central sketch. + * You can also use an android or IOS app that supports notifications. + * + */ +#include BLEService batteryService("180F"); // BLE Battery Service diff --git a/libraries/CurieBLE/examples/peripheral/IMUBleNotification/IMUBleNotification.ino b/libraries/CurieBLE/examples/peripheral/IMUBleNotification/IMUBleNotification.ino index c3ac0ff0..e8ad758b 100644 --- a/libraries/CurieBLE/examples/peripheral/IMUBleNotification/IMUBleNotification.ino +++ b/libraries/CurieBLE/examples/peripheral/IMUBleNotification/IMUBleNotification.ino @@ -1,30 +1,24 @@ /* - 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. + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ - 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. +/* + * Sketch: IMUBleNotification.ino + * + * Description: + * This is a Peripheral sketch that reads IMU data from sensor and sends + * via notifications to a connected Central. + * + * Notes: + * + * - This sketch is inteneded to pair up with IMUBleCentral.ino. + * + */ - 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 -/* - This sketch example works with IMUBleCentral.ino. - - This sketch will read IMU data from sensor and send notifications to IMUBleCentral.ino. - IMUBleCentral.ino will receive the notifications and output the received data. -*/ - #define MAX_IMU_RECORD 1 typedef struct { @@ -125,3 +119,22 @@ void recordImuData(int index) imuBuf[index].slot[2] = (unsigned int)((gy << 16) | (gz & 0x0FFFF)); } + +/* + 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 +*/ + diff --git a/libraries/CurieBLE/examples/peripheral/PeripheralDouble/PeripheralDouble.ino b/libraries/CurieBLE/examples/peripheral/PeripheralDouble/PeripheralDouble.ino new file mode 100644 index 00000000..658822e2 --- /dev/null +++ b/libraries/CurieBLE/examples/peripheral/PeripheralDouble/PeripheralDouble.ino @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: PeripheralDouble.ino + * + * Description: + * This is a Peripheral sketch that has a Characteristic of double. + * It demonstrates the usage of double in a BLE exchange. + * + * Notes: + * + * - Peripheral Characteristic: 19b20001e8f2537e4f6cd104768a1214 + */ + +#include + +// LED pin +#define LED_PIN 13 + +// create service +BLEService dataService("19b20000e8f2537e4f6cd104768a1214"); + +// create data characteristic +BLEDoubleCharacteristic doubleCharacteristic("19b20001e8f2537e4f6cd104768a1214", BLERead | BLEWrite); + +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("DataTest"); + + dataService.addCharacteristic(doubleCharacteristic); + + // add service and characteristic + BLE.addService(dataService); + + BLE.advertise(); + + Serial.println(F("BLE Test Double Peripheral")); +} + +void loop() { + BLEDevice central = BLE.central(); + double test_value = 0.1; + + if (central) { + // central connected to peripheral + Serial.print(F("Connected to central: ")); + Serial.println(central.address()); + + while (central.connected()) + { + // central still connected to peripheral + doubleCharacteristic.writeDouble(test_value); + test_value += 0.3; + delay(2000); + } + + // central disconnected + Serial.print(F("Disconnected from central: ")); + Serial.println(central.address()); + } +} + + +/* + Arduino BLE Peripheral Double read/write test 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 +*/ + + diff --git a/libraries/CurieBLE/examples/peripheral/led/led.ino b/libraries/CurieBLE/examples/peripheral/led/led.ino index 55948fd1..4cbb25dd 100644 --- a/libraries/CurieBLE/examples/peripheral/led/led.ino +++ b/libraries/CurieBLE/examples/peripheral/led/led.ino @@ -1,21 +1,16 @@ /* - 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. + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ - 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 -*/ +/* + * Sketch: led.ino + * + * Description: + * This is a Peripheral sketch that works with a connected Central. + * It allows the Central to write a value and set/reset the led + * accordingly. + */ #include @@ -82,3 +77,25 @@ void loop() { Serial.println(central.address()); } } + + +/* + 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 +*/ + + diff --git a/libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino b/libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino index e00bf419..54a5c659 100644 --- a/libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino +++ b/libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino @@ -1,21 +1,7 @@ /* - 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 -*/ + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ // Import libraries #include @@ -88,3 +74,26 @@ void switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteri digitalWrite(LED_PIN, LOW); } } + + + +/* + 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 +*/ + + diff --git a/libraries/CurieBLE/examples/test/CommunitySketches/AccelerometerBLE/AccelerometerBLE.ino b/libraries/CurieBLE/examples/test/CommunitySketches/AccelerometerBLE/AccelerometerBLE.ino deleted file mode 100644 index 0e660cb6..00000000 --- a/libraries/CurieBLE/examples/test/CommunitySketches/AccelerometerBLE/AccelerometerBLE.ino +++ /dev/null @@ -1,94 +0,0 @@ -#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 index b7062f34..5428572f 100644 --- a/libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino +++ b/libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino @@ -1,25 +1,17 @@ /* - 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 -*/ + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ /* - 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 -*/ + * Sketch: Genuino101CurieBLEHeartRateMonitor.ino. + * + * Description: + * 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 @@ -97,4 +89,24 @@ void updateHeartRate() { heartRateChar.setValue(heartRateCharArray, 2); // and update the heart rate measurement characteristic oldHeartRate = heartRate; // save the level for next comparison } -} +} + +/* + 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 +*/ + + diff --git a/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino index cf1e3a74..398993f9 100644 --- a/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino +++ b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino @@ -1,21 +1,20 @@ /* - 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 -*/ + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: StandardFirmataBLE.ino. + * + * Description: + * 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. + * + * Notes: + * - Please use the link stated at the end of this file to + * download a host s/w package. + */ #include #include @@ -830,4 +829,25 @@ void loop() #ifdef FIRMATA_SERIAL_FEATURE serialFeature.update(); #endif -} +} + +/* + 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 +*/ + + diff --git a/libraries/CurieBLE/examples/test/CommunitySketches/StarterKit101_BLE/StarterKit101_BLE.ino b/libraries/CurieBLE/examples/test/CommunitySketches/StarterKit101_BLE/StarterKit101_BLE.ino deleted file mode 100644 index 641af931..00000000 --- a/libraries/CurieBLE/examples/test/CommunitySketches/StarterKit101_BLE/StarterKit101_BLE.ino +++ /dev/null @@ -1,37 +0,0 @@ -#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 deleted file mode 100644 index 5bc13524..00000000 --- a/libraries/CurieBLE/examples/test/central/central.ino +++ /dev/null @@ -1,86 +0,0 @@ - -#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/discoveratperipheral/discoveratperipheral.ino b/libraries/CurieBLE/examples/test/discoveratperipheral/discoveratperipheral.ino new file mode 100644 index 00000000..8de71767 --- /dev/null +++ b/libraries/CurieBLE/examples/test/discoveratperipheral/discoveratperipheral.ino @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: discoveratperipheral.ino. + * + * Description: + * This is a BLE Central sketch that looks for a particular + * Characteristic in a connected Peripheral to write to. The + * Peripheral with the special Characteristic will blink its + * LED. + * + * Notes: + * - This sketch is Arduino BLE Peripheral LED example. + * Please see licensing at the bottom of this file. + * - Expected Peripheral Characteristic: 19b10000e8f2537e4f6cd104768a1214 + */ + +#include + +// LED pin +#define LED_PIN 13 + +// create service +BLEService ledService("19b10000e8f2537e4f6cd104768a1214"); + +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.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()); + + controlLed(central); + // central disconnected + Serial.print(F("Disconnected from central: ")); + Serial.println(central.address()); + } +} + +void controlLed(BLEDevice ¢ral) +{ + if (central.discoverAttributes() == false) + { + Serial.println("Discover failed, Disconnecting..."); + central.disconnect(); + return; + } + + BLECharacteristic ledCharacteristic = central.characteristic("19b10101-e8f2-537e-4f6c-d104768a1214"); + + if (!ledCharacteristic) + { + central.disconnect(); + //while(1) + { + Serial.println("Central does not have LED characteristic!"); + delay(5000); + } + return; + } + + ledCharacteristic.subscribe(); + + unsigned char ledstate = 0; + + while (central.connected()) + { + if (ledstate == 1) + { + ledstate = 0; + } + else + { + ledstate = 1; + } + ledCharacteristic.write(&ledstate, sizeof(ledstate)); + delay(5000); + } + Serial.print("Disconnected"); + Serial.println(central.address()); +} + + +/* + 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 +*/ + diff --git a/libraries/CurieBLE/examples/test/notification/notification.ino b/libraries/CurieBLE/examples/test/notification/notification.ino deleted file mode 100644 index 542102bb..00000000 --- a/libraries/CurieBLE/examples/test/notification/notification.ino +++ /dev/null @@ -1,78 +0,0 @@ - -#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 deleted file mode 100644 index 47086b18..00000000 --- a/libraries/CurieBLE/examples/test/notifycentral/notifycentral.ino +++ /dev/null @@ -1,89 +0,0 @@ - -#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/profileatcentral/profileatcentral.ino b/libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.ino new file mode 100644 index 00000000..2fe20520 --- /dev/null +++ b/libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.ino @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: profileatcentral.ino + * + * Description: + * This is a BLE Central sketch that demostrates the setting of the + * BLE descriptor. + * + * Notes: + * - This sketch is based on the Arduino BLE Peripheral LED example. + * Please refer to licensing agreement at the bottom of this file. + */ + +#include "CurieBLE.h" +#include +// LED pin +#define LED_PIN 13 +BLEService ledService("19b10100e8f2537e4f6cd104768a1214"); + +BLECharacteristic switchCharacteristic("19b10101e8f2537e4f6cd104768a1214", BLERead | BLEWrite | BLENotify, 1); + +BLEDescriptor switchDescriptor("2901", "switch"); + +void setup() { + Serial.begin(115200); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial.println(BLE.address()); + ledService.addCharacteristic(switchCharacteristic); + switchCharacteristic.addDescriptor(switchDescriptor); + BLE.addService(ledService); + + BLE.scanForName("LED"); +} + + +void loop() { + BLEDevice peripheral = BLE.available(); + if (peripheral) + { + Serial.println(peripheral.address()); + + BLE.stopScan(); + + if (peripheral.connect()) + { + Serial.print("Connected: "); + Serial.println(peripheral.address()); + while (peripheral.connected()) + { + delay (1000); + } + } + else + { + Serial.println("Failed to connect!"); + } + delay (4000); + BLE.scanForName("LED"); + } +} + + +/* + 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 +*/ + + diff --git a/libraries/CurieBLE/examples/test/test/test.ino b/libraries/CurieBLE/examples/test/test/test.ino deleted file mode 100644 index bd4da60c..00000000 --- a/libraries/CurieBLE/examples/test/test/test.ino +++ /dev/null @@ -1,71 +0,0 @@ - -#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); -} diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp index e21c40f5..5162f593 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp @@ -1062,11 +1062,12 @@ void BLEDeviceManager::handleConnectEvent(bt_conn_t *conn, uint8_t err) } else { + // Peripheral has established the connection with this Central device 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)); } + // The peripheral and central can work as GATT server. Reserve one buffer for peer device + BLEProfileManager::instance()->handleConnectedEvent(bt_conn_get_dst(conn)); if (NULL != _device_events[BLEConnected]) { From f61091473e5888337098a103daccf321e3fe328c Mon Sep 17 00:00:00 2001 From: lianggao Date: Wed, 8 Feb 2017 10:59:09 +0800 Subject: [PATCH 051/125] Jira 804 BLE Characteristic initialization issue, git 366 Issue: - Created Characteristic failed to reflect the initialized value. Root cause: - During initialization, the length field was incorrectly skipped over. --- libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp index 415aa5ff..23e55f20 100644 --- a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp @@ -171,6 +171,7 @@ BLECharacteristicImp::BLECharacteristicImp(BLECharacteristic& characteristic, if (NULL != characteristic._value) { memcpy(_value, characteristic._value, _value_size); + _value_length = _value_size; } // Update BLE device object From fb578000afad9bc5cdf74358e8c3acbc7a04f9e8 Mon Sep 17 00:00:00 2001 From: Sidney Leung Date: Wed, 15 Feb 2017 22:28:08 -0800 Subject: [PATCH 052/125] Jira 792, Support addition UUID types, git PR 386. Features added: 1. Add the support for two addition UUID types, UUID16_SOME and UUID128_SOME. These types, practically, behaves the same as their related ALL types. Sandeep found that the Apple devices made use of these additional types and requested for their support. File mods: 1. BLEDeviceManager.cpp: - Add UUID16_SOME and UUID128_SOME at places checking for UUID types. --- .../src/internal/BLEDeviceManager.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp index 5162f593..b3e74278 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp @@ -742,8 +742,13 @@ int BLEDeviceManager::advertisedServiceUuidCount(const BLEDevice* device) const return service_cnt; } + /* Sid, 2/15/2017. Sandeep reported that Apple devices may use + BT_DATA_UUID16_SOME and BT_DATA_UUID128_SOME in addition to ALL. + Practically, these types are same as ALL. */ if (type == BT_DATA_UUID16_ALL || - type == BT_DATA_UUID128_ALL) + type == BT_DATA_UUID128_ALL || + type == BT_DATA_UUID16_SOME || + type == BT_DATA_UUID128_SOME) { service_cnt++; } @@ -791,7 +796,10 @@ String BLEDeviceManager::localName(const BLEDevice* device) const return temp; } - if (type == BT_DATA_NAME_COMPLETE) + /* Sid, 2/15/2017. Support both forms of data name. + */ + if (type == BT_DATA_NAME_COMPLETE || + type == BT_DATA_NAME_SHORTENED) { if (len >= BLE_MAX_ADV_SIZE) { @@ -862,14 +870,17 @@ String BLEDeviceManager::advertisedServiceUuid(const BLEDevice* device, int inde } if (type == BT_DATA_UUID16_ALL || - type == BT_DATA_UUID128_ALL) + type == BT_DATA_UUID128_ALL || + type == BT_DATA_UUID16_SOME || + type == BT_DATA_UUID128_SOME) { service_cnt++; } if (index < service_cnt) { - if (type == BT_DATA_UUID16_ALL) + if (type == BT_DATA_UUID16_ALL || + type == BT_DATA_UUID16_SOME) { service_uuid.uuid.type = BT_UUID_TYPE_16; memcpy(&BT_UUID_16(&service_uuid.uuid)->val, &adv_data[2], 2); From ee08a03b21d5c141020b328974c2392a8484dfa9 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Fri, 27 Jan 2017 16:28:38 -0800 Subject: [PATCH 053/125] Use single dataReady() method, instead of accelDataReady/gyroDataReady This method is a bit more flexible. There is a version with no parameters, which will return true if all enabled sensors have new data. There is one overload, that takes a single 'flags' parameter, which returns true if all enabled *and* selected sensors have new data. Sensors are selectable through bit flags. For example; CurieIMU.dataReady() : this will return true if both the accel and gyro are enabled, and both have new data ready. CurieIMU.dataReady(ACCEL | GYRO) : this will return true if both the accel and gyro are enabled, and both have new data ready. CurieIMU.dataReady(GYRO) : this will return true if the gyro is enabled and has new data ready. CurieIMU.dataReady(ACCEL) : this will return true if the accel is enabled and has new data ready. This also involved changing the 'begin' method to allow selective enabling of sensors, e.g. CurieIMU.begin() : same as it always has, initializes IMU, enabling both accel and gyro CurieIMU.begin(ACCEL | GYRO) : initializes IMU, enabling both accel and gyro CurieIMU.begin(ACCEL) : initializes IMU, enabling accel only --- libraries/CurieIMU/keywords.txt | 4 ++ libraries/CurieIMU/src/BMI160.cpp | 78 ++++++++++++++++----------- libraries/CurieIMU/src/BMI160.h | 20 +++++-- libraries/CurieIMU/src/CurieIMU.cpp | 83 +++++++++++++++++++++++------ libraries/CurieIMU/src/CurieIMU.h | 8 +-- 5 files changed, 138 insertions(+), 55 deletions(-) diff --git a/libraries/CurieIMU/keywords.txt b/libraries/CurieIMU/keywords.txt index 1c64688b..05b1cb2b 100644 --- a/libraries/CurieIMU/keywords.txt +++ b/libraries/CurieIMU/keywords.txt @@ -14,6 +14,7 @@ CurieIMUClass KEYWORD1 begin KEYWORD1 +dataReady KEYWORD1 getGyroRate KEYWORD1 setGyroRate KEYWORD1 getAccelerometerRate KEYWORD1 @@ -84,6 +85,9 @@ CurieIMU KEYWORD2 # Constants (LITERAL1) ####################################### +ACCEL LITERAL1 +GYRO LITERAL1 + X_AXIS LITERAL1 Y_AXIS LITERAL1 Z_AXIS LITERAL1 diff --git a/libraries/CurieIMU/src/BMI160.cpp b/libraries/CurieIMU/src/BMI160.cpp index d1bf2a22..c8945b38 100644 --- a/libraries/CurieIMU/src/BMI160.cpp +++ b/libraries/CurieIMU/src/BMI160.cpp @@ -78,6 +78,11 @@ uint8_t BMI160Class::reg_read_bits(uint8_t reg, unsigned pos, unsigned len) return b; } +int BMI160Class::isBitSet(uint8_t value, unsigned bit) +{ + return value & (1 << bit); +} + /******************************************************************************/ /** Power on and prepare for general usage. @@ -85,8 +90,10 @@ uint8_t BMI160Class::reg_read_bits(uint8_t reg, unsigned pos, unsigned len) * after start-up). This function also sets both the accelerometer and the gyroscope * to default range settings, namely +/- 2g and +/- 250 degrees/sec. */ -void BMI160Class::initialize() +void BMI160Class::initialize(unsigned int flags) { + sensors_enabled = 0; + /* Issue a soft-reset to bring the device into a clean state */ reg_write(BMI160_RA_CMD, BMI160_CMD_SOFT_RESET); delay(1); @@ -95,26 +102,36 @@ void BMI160Class::initialize() reg_read(0x7F); delay(1); - /* Power up the accelerometer */ - reg_write(BMI160_RA_CMD, BMI160_CMD_ACC_MODE_NORMAL); - delay(1); - /* Wait for power-up to complete */ - while (0x1 != reg_read_bits(BMI160_RA_PMU_STATUS, + if (flags & ACCEL) { + /* Power up the accelerometer */ + reg_write(BMI160_RA_CMD, BMI160_CMD_ACC_MODE_NORMAL); + delay(1); + + /* Wait for power-up to complete */ + while (0x1 != reg_read_bits(BMI160_RA_PMU_STATUS, BMI160_ACC_PMU_STATUS_BIT, BMI160_ACC_PMU_STATUS_LEN)) + delay(1); + + sensors_enabled |= ACCEL; + } + + if (flags & GYRO) { + /* Power up the gyroscope */ + reg_write(BMI160_RA_CMD, BMI160_CMD_GYR_MODE_NORMAL); delay(1); - /* Power up the gyroscope */ - reg_write(BMI160_RA_CMD, BMI160_CMD_GYR_MODE_NORMAL); - delay(1); - /* Wait for power-up to complete */ - while (0x1 != reg_read_bits(BMI160_RA_PMU_STATUS, + /* Wait for power-up to complete */ + while (0x1 != reg_read_bits(BMI160_RA_PMU_STATUS, BMI160_GYR_PMU_STATUS_BIT, BMI160_GYR_PMU_STATUS_LEN)) - delay(1); + delay(1); + + sensors_enabled |= GYRO; + } - setFullScaleGyroRange(BMI160_GYRO_RANGE_250); - setFullScaleAccelRange(BMI160_ACCEL_RANGE_2G); + setFullScaleGyroRange(BMI160_GYRO_RANGE_250, 250.0f); + setFullScaleAccelRange(BMI160_ACCEL_RANGE_2G, 2.0f); /* Only PIN1 interrupts currently supported - map all interrupts to PIN1 */ reg_write(BMI160_RA_INT_MAP_0, 0xFF); @@ -131,6 +148,17 @@ uint8_t BMI160Class::getDeviceID() { return reg_read(BMI160_RA_CHIP_ID); } +/* Checks if the specified sensors are enabled (sensors are specified using + * bit flags defined by CurieIMUSensor enum). If 0 is passed, checks if *any* + * sensors are enabled */ +bool BMI160Class::isEnabled(unsigned int sensors) +{ + if (sensors == 0) + return sensors_enabled > 0; + + return (sensors_enabled & sensors) > 0; +} + /** Verify the SPI connection. * Make sure the device is connected and responds as expected. * @return True if connection is valid, false otherwise @@ -330,10 +358,11 @@ uint8_t BMI160Class::getFullScaleGyroRange() { * @param range New full-scale gyroscope range value * @see getFullScaleGyroRange() */ -void BMI160Class::setFullScaleGyroRange(uint8_t range) { +void BMI160Class::setFullScaleGyroRange(uint8_t range, float real) { reg_write_bits(BMI160_RA_GYRO_RANGE, range, BMI160_GYRO_RANGE_SEL_BIT, BMI160_GYRO_RANGE_SEL_LEN); + gyro_range = real; } /** Get full-scale accelerometer range. @@ -362,10 +391,11 @@ uint8_t BMI160Class::getFullScaleAccelRange() { * @see getFullScaleAccelRange() * @see BMI160AccelRange */ -void BMI160Class::setFullScaleAccelRange(uint8_t range) { +void BMI160Class::setFullScaleAccelRange(uint8_t range, float real) { reg_write_bits(BMI160_RA_ACCEL_RANGE, range, BMI160_ACCEL_RANGE_SEL_BIT, BMI160_ACCEL_RANGE_SEL_LEN); + accel_range = real; } /** Get accelerometer offset compensation enabled value. @@ -2386,19 +2416,3 @@ uint8_t BMI160Class::getRegister(uint8_t reg) { void BMI160Class::setRegister(uint8_t reg, uint8_t data) { reg_write(reg, data); } - -/** Check if new gyroscope data is available - * @return True if new data is available, else false. - */ -bool BMI160Class::gyroDataReady() -{ - return reg_read_bits(BMI160_RA_STATUS, BMI160_STATUS_DRDY_GYR, 1); -} - -/** Check if new accelerometer data is available - * @return True if new data is available, else false. - */ -bool BMI160Class::accelDataReady() -{ - return reg_read_bits(BMI160_RA_STATUS, BMI160_STATUS_DRDY_ACC, 1); -} diff --git a/libraries/CurieIMU/src/BMI160.h b/libraries/CurieIMU/src/BMI160.h index 1384f1c4..dfdc7531 100644 --- a/libraries/CurieIMU/src/BMI160.h +++ b/libraries/CurieIMU/src/BMI160.h @@ -267,6 +267,12 @@ THE SOFTWARE. #define BMI160_RA_CMD 0x7E +/* Bit flags for selecting individual sensors */ +typedef enum { + GYRO = 0x1, + ACCEL = 0x2 +} CurieIMUSensor; + /** * Interrupt Latch Mode options * @see setInterruptLatch() @@ -471,9 +477,10 @@ typedef enum { class BMI160Class { public: - void initialize(); + void initialize(unsigned int flags); bool testConnection(); + bool isEnabled(unsigned int sensors); uint8_t getGyroRate(); void setGyroRate(uint8_t rate); @@ -487,9 +494,9 @@ class BMI160Class { void setAccelDLPFMode(uint8_t bandwidth); uint8_t getFullScaleGyroRange(); - void setFullScaleGyroRange(uint8_t range); + void setFullScaleGyroRange(uint8_t range, float real); uint8_t getFullScaleAccelRange(); - void setFullScaleAccelRange(uint8_t range); + void setFullScaleAccelRange(uint8_t range, float real); void autoCalibrateGyroOffset(); bool getGyroOffsetEnabled(); @@ -640,6 +647,7 @@ class BMI160Class { uint8_t getDeviceID(); + int isBitSet(uint8_t value, unsigned bit); uint8_t getRegister(uint8_t reg); void setRegister(uint8_t reg, uint8_t data); @@ -653,8 +661,10 @@ class BMI160Class { void setInterruptLatch(uint8_t latch); void resetInterrupt(); - bool gyroDataReady(); - bool accelDataReady(); + /* Use a bitmask to track which sensors are enabled */ + unsigned sensors_enabled; + float accel_range; + float gyro_range; protected: virtual int serial_buffer_transfer(uint8_t *buf, unsigned tx_cnt, unsigned rx_cnt) = 0; diff --git a/libraries/CurieIMU/src/CurieIMU.cpp b/libraries/CurieIMU/src/CurieIMU.cpp index 0def7306..e9e64760 100644 --- a/libraries/CurieIMU/src/CurieIMU.cpp +++ b/libraries/CurieIMU/src/CurieIMU.cpp @@ -32,7 +32,8 @@ * on the Curie module, before calling BMI160::initialize() to activate the * BMI160 accelerometer and gyroscpoe with default settings. */ -bool CurieIMUClass::begin() + +bool CurieIMUClass::configure_imu(unsigned int sensors) { ss_spi_init(SPI_SENSING_1, 2000, SPI_BUSMODE_0, SPI_8_BIT, SPI_SE_1); @@ -41,7 +42,7 @@ bool CurieIMUClass::begin() serial_buffer_transfer(&dummy_reg, 1, 1); /* The SPI interface is ready - now invoke the base class initialization */ - BMI160Class::initialize(); + initialize(sensors); /** Verify the SPI connection. * MakgetGyroRatee sure the device is connected and responds as expected. @@ -50,16 +51,66 @@ bool CurieIMUClass::begin() if (CURIE_IMU_CHIP_ID != getDeviceID()) return false; - accel_range = (float) getAccelerometerRange(); - gyro_range = (float) getGyroRange(); return true; } +bool CurieIMUClass::begin() +{ + return configure_imu(GYRO | ACCEL); +} + +bool CurieIMUClass::begin(unsigned int sensors) +{ + return configure_imu(sensors); +} + void CurieIMUClass::end() { ss_spi_disable(SPI_SENSING_1); } +bool CurieIMUClass::dataReady() +{ + uint8_t stat; + + /* If no sensors are enabled */ + if (!isEnabled(0)) + return false; + + /* Read status register */ + stat = getRegister(BMI160_RA_STATUS); + + if (isEnabled(GYRO) && !isBitSet(stat, BMI160_STATUS_DRDY_GYR)) + return false; + + if (isEnabled(ACCEL) && !isBitSet(stat, BMI160_STATUS_DRDY_ACC)) + return false; + + return true; +} + +bool CurieIMUClass::dataReady(unsigned int sensors) +{ + uint8_t stat; + + /* If no sensors enabled, or no data requested */ + if (sensors == 0 || !isEnabled(0)) + return false; + + /* Read status register */ + stat = getRegister(BMI160_RA_STATUS); + + if ((sensors & GYRO) && isEnabled(GYRO) && + !isBitSet(stat, BMI160_STATUS_DRDY_GYR)) + return false; + + if ((sensors & ACCEL) && isEnabled(ACCEL) && + !isBitSet(stat, BMI160_STATUS_DRDY_ACC)) + return false; + + return true; +} + int CurieIMUClass::getGyroRate() { int rate; @@ -227,25 +278,26 @@ int CurieIMUClass::getGyroRange() void CurieIMUClass::setGyroRange(int range) { BMI160GyroRange bmiRange; + float real; if (range >= 2000) { bmiRange = BMI160_GYRO_RANGE_2000; - gyro_range = 2000.0f; + real = 2000.0f; } else if (range >= 1000) { bmiRange = BMI160_GYRO_RANGE_1000; - gyro_range = 1000.0f; + real = 1000.0f; } else if (range >= 500) { bmiRange = BMI160_GYRO_RANGE_500; - gyro_range = 500.0f; + real = 500.0f; } else if (range >= 250) { bmiRange = BMI160_GYRO_RANGE_250; - gyro_range = 250.0f; + real = 250.0f; } else { bmiRange = BMI160_GYRO_RANGE_125; - gyro_range = 125.0f; + real = 125.0f; } - setFullScaleGyroRange(bmiRange); + setFullScaleGyroRange(bmiRange, real); } int CurieIMUClass::getAccelerometerRange() @@ -277,22 +329,23 @@ int CurieIMUClass::getAccelerometerRange() void CurieIMUClass::setAccelerometerRange(int range) { BMI160AccelRange bmiRange; + float real; if (range <= 2) { bmiRange = BMI160_ACCEL_RANGE_2G; - accel_range = 2.0f; + real = 2.0f; } else if (range <= 4) { bmiRange = BMI160_ACCEL_RANGE_4G; - accel_range = 4.0f; + real = 4.0f; } else if (range <= 8) { bmiRange = BMI160_ACCEL_RANGE_8G; - accel_range = 8.0f; + real = 8.0f; } else { bmiRange = BMI160_ACCEL_RANGE_16G; - accel_range = 16.0f; + real = 16.0f; } - setFullScaleAccelRange(bmiRange); + setFullScaleAccelRange(bmiRange, real); } void CurieIMUClass::autoCalibrateGyroOffset() diff --git a/libraries/CurieIMU/src/CurieIMU.h b/libraries/CurieIMU/src/CurieIMU.h index 8056b135..3a7a0dc4 100644 --- a/libraries/CurieIMU/src/CurieIMU.h +++ b/libraries/CurieIMU/src/CurieIMU.h @@ -94,9 +94,13 @@ class CurieIMUClass : public BMI160Class { friend void bmi160_pin1_isr(void); public: + bool begin(unsigned int sensors); bool begin(void); void end(void); + bool dataReady(); + bool dataReady(unsigned int sensors); + // supported values: 25, 50, 100, 200, 400, 800, 1600, 3200 (Hz) int getGyroRate(); void setGyroRate(int rate); @@ -207,11 +211,9 @@ class CurieIMUClass : public BMI160Class { void detachInterrupt(void); private: + bool configure_imu(unsigned int sensors); int serial_buffer_transfer(uint8_t *buf, unsigned tx_cnt, unsigned rx_cnt); - float accel_range; - float gyro_range; - float getFreefallDetectionThreshold(); void setFreefallDetectionThreshold(float threshold); float getShockDetectionThreshold(); From bc5650068db85e166241919d0b6724703138e4d9 Mon Sep 17 00:00:00 2001 From: lianggao Date: Wed, 18 Jan 2017 15:23:13 +0800 Subject: [PATCH 054/125] Jira 795, read() is blocking selectable, git 383 Mods: 1. Add an input parameter to BLECharacteristic::read() for blocking call selection. 2. By default, read() is a blocking call. File changes: 1. libraries/CurieBLE/src/BLECharacteristic.cpp: - Method read() added blocking selection. 2. libraries/CurieBLE/src/BLECharacteristic.h: - Prototype for read(), default is blocking. 3. libraries/CurieBLE/src/internal/BLECallbacks.cpp: - Added parameter checking in profile_read_rsp_process(). 4. libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp - The implementation of read() added waiting for resp if blocking is selected. 5. libraries/CurieBLE/src/internal/BLECharacteristicImp.h: - prototyping. --- libraries/CurieBLE/src/BLECharacteristic.cpp | 4 ++-- libraries/CurieBLE/src/BLECharacteristic.h | 3 ++- .../CurieBLE/src/internal/BLECallbacks.cpp | 2 +- .../src/internal/BLECharacteristicImp.cpp | 18 +++++++++++++++--- .../src/internal/BLECharacteristicImp.h | 10 +++++----- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/libraries/CurieBLE/src/BLECharacteristic.cpp b/libraries/CurieBLE/src/BLECharacteristic.cpp index f8432b11..62d8a777 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.cpp +++ b/libraries/CurieBLE/src/BLECharacteristic.cpp @@ -367,14 +367,14 @@ bool BLECharacteristic::canUnsubscribe() return retVar; } -bool BLECharacteristic::read() +bool BLECharacteristic::read(bool blocked) { bool retVar = false; BLECharacteristicImp *characteristicImp = getImplementation(); if (NULL != characteristicImp) { - retVar = characteristicImp->read(); + retVar = characteristicImp->read(blocked); } return retVar; } diff --git a/libraries/CurieBLE/src/BLECharacteristic.h b/libraries/CurieBLE/src/BLECharacteristic.h index c1887c73..0ca0cf53 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.h +++ b/libraries/CurieBLE/src/BLECharacteristic.h @@ -320,8 +320,9 @@ class BLECharacteristic: public BLEAttributeWithValue * @return bool true - Success, false - Failed * * @note Only for GATT client. Schedule read request to the GATT server + * Arduino requests to have read, by default, be blocking. */ - virtual bool read(); + virtual bool read(bool blocked = true); /** * @brief Write the charcteristic value diff --git a/libraries/CurieBLE/src/internal/BLECallbacks.cpp b/libraries/CurieBLE/src/internal/BLECallbacks.cpp index 568c54f8..be86552c 100644 --- a/libraries/CurieBLE/src/internal/BLECallbacks.cpp +++ b/libraries/CurieBLE/src/internal/BLECallbacks.cpp @@ -145,7 +145,7 @@ uint8_t profile_read_rsp_process(bt_conn_t *conn, const void *data, uint16_t length) { - if (NULL == data) + if (NULL == data && 0 != length) { return BT_GATT_ITER_STOP; } diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp index 23e55f20..71318bc9 100644 --- a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp @@ -595,9 +595,10 @@ bt_uuid_t* BLECharacteristicImp::getClientCharacteristicConfigUuid(void) return (bt_uuid_t*) &_gatt_ccc_uuid; } -bool BLECharacteristicImp::read() +bool BLECharacteristicImp::read(bool blocked) { int retval = 0; + bool ret_bool = false; bt_conn_t* conn = NULL; if (true == BLEUtils::isLocalBLE(_ble_device)) @@ -631,12 +632,23 @@ bool BLECharacteristicImp::read() // Send read request retval = bt_gatt_read(conn, &_read_params); - bt_conn_unref(conn); if (0 == retval) { _reading = true; + ret_bool = true; + + // Block the call + if (blocked == true) + { + while (_reading == true && ret_bool) + { + delay(5); + ret_bool = _ble_device.connected(); + } + } } - return _reading; + bt_conn_unref(conn); + return ret_bool; } void BLECharacteristicImp::writeResponseReceived(struct bt_conn *conn, diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.h b/libraries/CurieBLE/src/internal/BLECharacteristicImp.h index 1066992c..d690d600 100644 --- a/libraries/CurieBLE/src/internal/BLECharacteristicImp.h +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.h @@ -146,18 +146,18 @@ class BLECharacteristicImp: public BLEAttribute{ /** * @brief Schedule the read request to read the characteristic in peripheral * - * @param[in] none + * @param[in] blocked Flag the call is blocked or un-blocked * * @return bool Indicate the success or error * - * @note Only for central device + * @note Only for GATT client + * Default it is block call as per Arduino request */ - bool read(); + bool read(bool blocked = true); /** * @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. @@ -328,7 +328,7 @@ class BLECharacteristicImp: public BLEAttribute{ bt_gatt_subscribe_params_t _sub_params; bool _subscribed; - bool _reading; + volatile bool _reading; static volatile bool _gattc_writing; bt_gatt_read_params_t _read_params; // GATT read parameter From a2df1795ac275ac48485329dc0697ab2324715bf Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 20 Feb 2017 09:39:50 +0800 Subject: [PATCH 055/125] Jira 857, BLEPeripheral::connected() always returns false, git 444 Root casue: - The stack need use connected device to get the state. - The current code used local address to get the link and get an error. Code mods: 1. BLEPeripheral.cpp: - Use central's address to get connection state. --- libraries/CurieBLE/src/BLEPeripheral.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/CurieBLE/src/BLEPeripheral.cpp b/libraries/CurieBLE/src/BLEPeripheral.cpp index 760d374f..9fa572e2 100644 --- a/libraries/CurieBLE/src/BLEPeripheral.cpp +++ b/libraries/CurieBLE/src/BLEPeripheral.cpp @@ -146,7 +146,8 @@ BLECentral BLEPeripheral::central(void) bool BLEPeripheral::connected(void) { - return BLE.connected(); + BLEDevice centralBle = BLE.central(); + return centralBle.connected(); } void BLEPeripheral::init() From 6d22f3da7eace1640a8a217e200e19560fb5a780 Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 13 Feb 2017 09:18:48 +0800 Subject: [PATCH 056/125] Jira 832 Sketch cannot exercise BLE broadcast, git 420 Root Cause: Need to restart advertising after setting the broadcast option in Characteristic. Otherwise, broadcasting would not take effect. Code mods: 1. libraries/CurieBLE/src/BLECharacteristic.cpp: - At the processing of the Boardcast option in the characteristic, stop and restart advertising if advertising has already started. --- libraries/CurieBLE/src/BLECharacteristic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/CurieBLE/src/BLECharacteristic.cpp b/libraries/CurieBLE/src/BLECharacteristic.cpp index 62d8a777..89a8e41d 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.cpp +++ b/libraries/CurieBLE/src/BLECharacteristic.cpp @@ -294,8 +294,8 @@ bool BLECharacteristic::broadcast() if (BLEDeviceManager::instance()->advertising()) { BLEDeviceManager::instance()->stopAdvertising(); - BLEDeviceManager::instance()->startAdvertising(); } + BLEDeviceManager::instance()->startAdvertising(); return _broadcast; } From 07f4de28e6aa57dab1f35ce730d12961774a7090 Mon Sep 17 00:00:00 2001 From: lianggao Date: Fri, 23 Dec 2016 10:26:18 +0800 Subject: [PATCH 057/125] Jira 842 UART improvement, PR 266, broke BLE interface. Root causes: 1. The uart_fifo_fill doesn't check the FIFO's length and failed to send out all data. 2. Check the FIFO status register before write data in FIFO. 3. Ring buffer pointer variables are not volatile and polling mechanism failed to examine the actual pointer value being update by foreground task. File changes: 1. RingBuffer.h: Make pointer as volatile. 2. UARTClass.cpp: Added checking of FIFO size for data Tx. 3. ns16550.c: Added routines to h/w status register checking. --- cores/arduino/RingBuffer.h | 6 ++-- cores/arduino/UARTClass.cpp | 35 ++++++++++++-------- system/libarc32_arduino101/drivers/ns16550.c | 23 ++++++++++--- 3 files changed, 43 insertions(+), 21 deletions(-) diff --git a/cores/arduino/RingBuffer.h b/cores/arduino/RingBuffer.h index 0e87b7ce..04d41b5a 100644 --- a/cores/arduino/RingBuffer.h +++ b/cores/arduino/RingBuffer.h @@ -30,9 +30,9 @@ class RingBuffer { public: uint8_t *_aucBuffer; - int _iHead ; - int _iTail ; - bool _buffer_overflow ; + volatile int _iHead ; + volatile int _iTail ; + volatile bool _buffer_overflow ; RingBuffer( void ) ; void store_char( uint8_t c ) ; diff --git a/cores/arduino/UARTClass.cpp b/cores/arduino/UARTClass.cpp index 000985f2..b4c31004 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 != _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)); } @@ -183,8 +183,7 @@ size_t UARTClass::write( const uint8_t uc_data ) { // If busy we buffer int l = (_tx_buffer->_iHead + 1) % UART_BUFFER_SIZE; - while (_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; @@ -201,21 +200,29 @@ size_t UARTClass::write( const uint8_t uc_data ) void UARTClass::IrqHandler( void ) { - 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); + 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; 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); + } } - // Do we need to keep sending data? - if (!uart_irq_tx_ready(CONFIG_UART_CONSOLE_INDEX)) + // if irq is Transmitter Holding Register + if(uart_irq_tx_ready(CONFIG_UART_CONSOLE_INDEX)) { - 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; + 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); + l = uart_fifo_fill(CONFIG_UART_CONSOLE_INDEX, _tx_buffer->_aucBuffer+_tx_buffer->_iTail, l); + _tx_buffer->_iTail = (_tx_buffer->_iTail+l)%UART_BUFFER_SIZE; } else { diff --git a/system/libarc32_arduino101/drivers/ns16550.c b/system/libarc32_arduino101/drivers/ns16550.c index f4e529e5..575d43af 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_TEMT) == 0) + while ((INBYTE(LSR(which)) & LSR_THRE) == 0) ; OUTBYTE(THR(which), outChar); @@ -352,6 +352,8 @@ 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 */ @@ -362,8 +364,9 @@ int uart_fifo_fill(int which, /* UART on which to send */ { int i; - for (i = 0; i < size && (INBYTE(LSR(which)) & - LSR_BOTH_EMPTY) != 0; i++) { + for (i = 0; i < size && (INBYTE(LSR(which)) & + LSR_BOTH_EMPTY) != 0; i++) + { OUTBYTE(THR(which), txData[i]); } return i; @@ -622,7 +625,6 @@ 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)); @@ -641,6 +643,19 @@ 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 From f4a18802a50acaafb091691b0eff207bc74942ec Mon Sep 17 00:00:00 2001 From: lianggao Date: Thu, 23 Feb 2017 13:55:34 +0800 Subject: [PATCH 058/125] Jira 802, BLE Central scan() to filter Peripheral adv, git #369 New feature: - Request from Arduino to make our Central BLE library to scan Peripheral similar to an Apple device. - When filter is set, advertisement from a Peripheral will only show up once (until scan is stop and start again) with the available() call. Otherwise, a Peripheral will appear as often as the Central can detect its advertisement. - Unlike an Apple device, the 101 operates under memory constraints. Thus, the maximum number of filter entries is limited to 20. In other words, if ther are more than 20 Peripherals, the filter will NOT be able to filter out all the advertisement all the time. A Peripherla WILL show up multiple times when available() is called. Code mods: 1. BLECommon.h: - Definition of the filter entries size. 2. BLEDeviceManager.cpp: - Peripheral filtering initialization at startScanningWithDuplicates(). - Added deviceInDuplicateFilterBuffer() for search a newly detected Peripheral against a recorded list of devices. - Added updateDuplicateFilter() to record any newly detected Peripoheral. - At available(), check for duplicated Peripheral prior to return it to caller. Discard any reported Peripheral. Record any newly detected one prior to return it to caller. --- libraries/CurieBLE/src/BLECommon.h | 1 + .../src/internal/BLEDeviceManager.cpp | 57 ++++++++++++++++++- .../CurieBLE/src/internal/BLEDeviceManager.h | 6 ++ 3 files changed, 61 insertions(+), 3 deletions(-) diff --git a/libraries/CurieBLE/src/BLECommon.h b/libraries/CurieBLE/src/BLECommon.h index 76e6b804..37ac49df 100644 --- a/libraries/CurieBLE/src/BLECommon.h +++ b/libraries/CurieBLE/src/BLECommon.h @@ -103,6 +103,7 @@ typedef ble_status_t BleStatus; #define BLE_MAX_CONN_CFG 2 #define BLE_MAX_ADV_BUFFER_CFG 3 +#define BLE_MAX_ADV_FILTER_SIZE_CFG 20 typedef bool (*ble_advertise_handle_cb_t)(uint8_t type, const uint8_t *dataPtr, uint8_t data_len, const bt_addr_le_t *addrPtr); diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp index b3e74278..643e6b97 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp @@ -47,7 +47,10 @@ BLEDeviceManager::BLEDeviceManager(): _local_name(""), _state(BLE_PERIPH_STATE_NOT_READY), _local_ble(NULL), - _peer_peripheral_index(0) + _peer_peripheral_index(0), + _duplicate_filter_header(0), + _duplicate_filter_tail(0), + _adv_duplicate_filter_enabled(false) { memset(&_local_bda, 0, sizeof(_local_bda)); memset(&_wait_for_connect_peripheral, 0, sizeof(_wait_for_connect_peripheral)); @@ -530,7 +533,8 @@ BLEDevice BLEDeviceManager::peripheral() bool BLEDeviceManager::startScanning() { - _scan_param.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE;//BT_HCI_LE_SCAN_FILTER_DUP_DISABLE; + _adv_duplicate_filter_enabled = false; + _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) { @@ -542,6 +546,10 @@ bool BLEDeviceManager::startScanning() bool BLEDeviceManager::startScanningWithDuplicates() { + _adv_duplicate_filter_enabled = true; + memset(_peer_duplicate_address_buffer, 0, sizeof(_peer_duplicate_address_buffer)); + _duplicate_filter_header = _duplicate_filter_tail = 0; + _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) @@ -549,7 +557,7 @@ bool BLEDeviceManager::startScanningWithDuplicates() pr_info(LOG_MODULE_BLE, "Scanning failed to start (err %d)\n", err); return false; } - return false; + return true; } bool BLEDeviceManager::stopScanning() @@ -1164,6 +1172,38 @@ bool BLEDeviceManager::advertiseDataProc(uint8_t type, return false; } +bool BLEDeviceManager::deviceInDuplicateFilterBuffer(const bt_addr_le_t* addr) +{ + bool retVal = false; + for (uint8_t i = 0; + i < (sizeof(_peer_duplicate_address_buffer) / sizeof(bt_addr_le_t)); + i++) + { + if (0 == bt_addr_le_cmp(addr, &_peer_duplicate_address_buffer[i])) + { + retVal = true; + break; + } + } + return retVal; +} + +void BLEDeviceManager::updateDuplicateFilter(const bt_addr_le_t* addr) +{ + uint8_t i = (_duplicate_filter_header + 1) % (ARRAY_SIZE(_peer_duplicate_address_buffer)); + if (deviceInDuplicateFilterBuffer(addr)) + { + return; + } + bt_addr_le_copy(&_peer_duplicate_address_buffer[_duplicate_filter_header], + addr); + if (i == _duplicate_filter_tail) + { + _duplicate_filter_tail = (_duplicate_filter_tail + 1) % (ARRAY_SIZE(_peer_duplicate_address_buffer)); + } + _duplicate_filter_header = i; +} + BLEDevice BLEDeviceManager::available() { BLEDevice tempdevice; @@ -1179,6 +1219,13 @@ BLEDevice BLEDeviceManager::available() temp = &_peer_adv_buffer[i]; if ((timestamp_delta <= 2000) && (max_delta < timestamp_delta)) { + // Eable the duplicate filter + if (_adv_duplicate_filter_enabled && + true == deviceInDuplicateFilterBuffer(temp)) + { + _peer_adv_mill[i] -= 2000; // Invalid the item + continue; + } max_delta = timestamp_delta; index = i; } @@ -1198,6 +1245,10 @@ BLEDevice BLEDeviceManager::available() 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 + if (_adv_duplicate_filter_enabled) + { + updateDuplicateFilter(temp); + } } } return tempdevice; diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.h b/libraries/CurieBLE/src/internal/BLEDeviceManager.h index 2e9d93b0..e09bfe6c 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.h +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.h @@ -362,6 +362,8 @@ class BLEDeviceManager const uint8_t* &adv_data, uint8_t &adv_len) const; bool disconnectSingle(const bt_addr_le_t *peer); + void updateDuplicateFilter(const bt_addr_le_t* addr); + bool deviceInDuplicateFilterBuffer(const bt_addr_le_t* addr); private: uint16_t _min_conn_interval; @@ -432,6 +434,10 @@ class BLEDeviceManager 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]; + bt_addr_le_t _peer_duplicate_address_buffer[BLE_MAX_ADV_FILTER_SIZE_CFG]; + uint8_t _duplicate_filter_header; + uint8_t _duplicate_filter_tail; + bool _adv_duplicate_filter_enabled; BLEDeviceEventHandler _device_events[BLEDeviceLastEvent]; }; From a03fa3c40e1c3043913eb6af25b6e34762a5064f Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Thu, 29 Dec 2016 15:40:59 -0500 Subject: [PATCH 059/125] Fix and simplify SensorTag button example --- .../sensortag_button/sensortag_button.ino | 59 ++++--------------- 1 file changed, 10 insertions(+), 49 deletions(-) diff --git a/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino b/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino index 786ff051..de85c6a8 100644 --- a/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino +++ b/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino @@ -15,24 +15,6 @@ #include -const int NUM_OF_SERVICE = 10; - -char *serviceUUIDArray[NUM_OF_SERVICE] = - - // These are the various services that are included in the CC2650 Sensor Tag - // If you uncomment them you can see the various services -{ //"f000aa00-0451-4000-b000-000000000000", - // "f000aa20-0451-4000-b000-000000000000", - // "f000aa40-0451-4000-b000-000000000000", - // "f000aa70-0451-4000-b000-000000000000", - // "f000aa80-0451-4000-b000-000000000000", - // "f000aa64-0451-4000-b000-000000000000", - // "f000ac00-0451-4000-b000-000000000000", - // "f000ccc0-0451-4000-b000-000000000000", - // "f000ffc0-0451-4000-b000-000000000000", - "0000ffe0-0000-1000-8000-00805f9b34fb" -}; - void setup() { Serial.begin(9600); while (!Serial); @@ -77,10 +59,7 @@ void loop() { * Use a central app that can display the BT MAC address * ****************************************************** */ - - if (peripheral.address() == "24:71:89:07:27:80") - - { + if (peripheral.address() == "68:C9:0B:06:BC:81") { // stop scanning BLE.stopScan(); @@ -94,9 +73,6 @@ void loop() { void monitorSensorTagButtons(BLEDevice peripheral) { - static bool getAllServices = true; - static int serviceIndx = 0; - // connect to the peripheral Serial.println("Connecting ..."); if (peripheral.connect()) { @@ -106,34 +82,18 @@ void monitorSensorTagButtons(BLEDevice peripheral) return; } - if (getAllServices) { - // discover peripheral attributes - Serial.println("Discovering attributes ..."); - if (peripheral.discoverAttributes()) { - Serial.println("Attributes discovered"); - } else { - getAllServices = false; - Serial.println("Attribute discovery failed."); - peripheral.disconnect(); - return; - } + // discover peripheral attributes + Serial.println("Discovering attributes of service 0xffe0 ..."); + if (peripheral.discoverAttributesByService("ffe0")) { + Serial.println("Attributes discovered"); } else { - int tmp = serviceIndx; - Serial.print("Discovering Service: "); - Serial.println(serviceUUIDArray[tmp]); - if (++serviceIndx >= NUM_OF_SERVICE) - serviceIndx = 0; - if (peripheral.discoverAttributesByService(serviceUUIDArray[tmp]) == false) { - Serial.println("Can't find the Service."); - peripheral.disconnect(); - return; - } else { - Serial.println("Service discovered."); - } + Serial.println("Attribute discovery failed."); + peripheral.disconnect(); + return; } // retrieve the simple key characteristic - BLECharacteristic simpleKeyCharacteristic = peripheral.characteristic("0000ffe1-0000-1000-8000-00805f9b34fb"); + BLECharacteristic simpleKeyCharacteristic = peripheral.characteristic("ffe1"); // subscribe to the simple key characteristic Serial.println("Subscribing to simple key characteristic ..."); @@ -174,6 +134,7 @@ void monitorSensorTagButtons(BLEDevice peripheral) } } + Serial.println("SensorTag disconnected!"); } From e9984d9e88a851edcce5e7c348d0110f8c75628c Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 26 Sep 2016 12:39:20 +0200 Subject: [PATCH 060/125] [TEMP] remove PaulStoffregen's SerialFlash fork --- libraries/SerialFlash/SerialFlash.h | 132 ----- libraries/SerialFlash/SerialFlashChip.cpp | 523 ------------------ .../SerialFlash/SerialFlashDirectory.cpp | 395 ------------- .../EraseEverything/EraseEverything.ino | 80 --- .../examples/FileWrite/FileWrite.ino | 50 -- .../examples/ListFiles/ListFiles.ino | 63 --- .../RawHardwareTest/RawHardwareTest.ino | 502 ----------------- libraries/SerialFlash/keywords.txt | 9 - .../util/SerialFlash_directwrite.h | 204 ------- 9 files changed, 1958 deletions(-) delete mode 100644 libraries/SerialFlash/SerialFlash.h delete mode 100644 libraries/SerialFlash/SerialFlashChip.cpp delete mode 100644 libraries/SerialFlash/SerialFlashDirectory.cpp delete mode 100644 libraries/SerialFlash/examples/EraseEverything/EraseEverything.ino delete mode 100755 libraries/SerialFlash/examples/FileWrite/FileWrite.ino delete mode 100644 libraries/SerialFlash/examples/ListFiles/ListFiles.ino delete mode 100644 libraries/SerialFlash/examples/RawHardwareTest/RawHardwareTest.ino delete mode 100644 libraries/SerialFlash/keywords.txt delete mode 100644 libraries/SerialFlash/util/SerialFlash_directwrite.h diff --git a/libraries/SerialFlash/SerialFlash.h b/libraries/SerialFlash/SerialFlash.h deleted file mode 100644 index e56df624..00000000 --- a/libraries/SerialFlash/SerialFlash.h +++ /dev/null @@ -1,132 +0,0 @@ -/* SerialFlash Library - for filesystem-like access to SPI Serial Flash memory - * https://github.com/PaulStoffregen/SerialFlash - * Copyright (C) 2015, Paul Stoffregen, paul@pjrc.com - * - * Development of this library was funded by PJRC.COM, LLC by sales of Teensy. - * Please support PJRC's efforts to develop open source software by purchasing - * Teensy or other genuine PJRC products. - * - * 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, development funding notice, and this permission - * notice shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#ifndef SerialFlash_h_ -#define SerialFlash_h_ - -#include -#include - -class SerialFlashFile; - -class SerialFlashChip -{ -public: - static bool begin(uint8_t pin = 6); - static uint32_t capacity(const uint8_t *id); - static uint32_t blockSize(); - static void sleep(); - static void wakeup(); - static void readID(uint8_t *buf); - static void readSerialNumber(uint8_t *buf); - static void read(uint32_t addr, void *buf, uint32_t len); - static bool ready(); - static void wait(); - static void write(uint32_t addr, const void *buf, uint32_t len); - static void eraseAll(); - static void eraseBlock(uint32_t addr); - - static SerialFlashFile open(const char *filename); - static bool create(const char *filename, uint32_t length, uint32_t align = 0); - static bool createErasable(const char *filename, uint32_t length) { - return create(filename, length, blockSize()); - } - static bool exists(const char *filename); - static bool remove(const char *filename); - static bool remove(SerialFlashFile &file); - static void opendir() { dirindex = 0; } - static bool readdir(char *filename, uint32_t strsize, uint32_t &filesize); -private: - static uint16_t dirindex; // current position for readdir() - static uint8_t flags; // chip features - static uint8_t busy; // 0 = ready - // 1 = suspendable program operation - // 2 = suspendable erase operation - // 3 = busy for realz!! -}; - -extern SerialFlashChip SerialFlash; - - -class SerialFlashFile -{ -public: - SerialFlashFile() : address(0) { - } - operator bool() { - if (address > 0) return true; - return false; - } - uint32_t read(void *buf, uint32_t rdlen) { - if (offset + rdlen > length) { - if (offset >= length) return 0; - rdlen = length - offset; - } - SerialFlash.read(address + offset, buf, rdlen); - offset += rdlen; - return rdlen; - } - uint32_t write(const void *buf, uint32_t wrlen) { - if (offset + wrlen > length) { - if (offset >= length) return 0; - wrlen = length - offset; - } - SerialFlash.write(address + offset, buf, wrlen); - offset += wrlen; - return wrlen; - } - void seek(uint32_t n) { - offset = n; - } - uint32_t position() { - return offset; - } - uint32_t size() { - return length; - } - uint32_t available() { - if (offset >= length) return 0; - return length - offset; - } - void erase(); - void flush() { - } - void close() { - } - uint32_t getFlashAddress() { - return address; - } -protected: - friend class SerialFlashChip; - uint32_t address; // where this file's data begins in the Flash, or zero - uint32_t length; // total length of the data in the Flash chip - uint32_t offset; // current read/write offset in the file - uint16_t dirindex; -}; - - -#endif diff --git a/libraries/SerialFlash/SerialFlashChip.cpp b/libraries/SerialFlash/SerialFlashChip.cpp deleted file mode 100644 index 4e467f57..00000000 --- a/libraries/SerialFlash/SerialFlashChip.cpp +++ /dev/null @@ -1,523 +0,0 @@ -/* SerialFlash Library - for filesystem-like access to SPI Serial Flash memory - * https://github.com/PaulStoffregen/SerialFlash - * Copyright (C) 2015, Paul Stoffregen, paul@pjrc.com - * - * Development of this library was funded by PJRC.COM, LLC by sales of Teensy. - * Please support PJRC's efforts to develop open source software by purchasing - * Teensy or other genuine PJRC products. - * - * 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, development funding 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 "SerialFlash.h" -#include "util/SerialFlash_directwrite.h" - -#define CSASSERT() DIRECT_WRITE_LOW(cspin_basereg, cspin_bitmask) -#define CSRELEASE() DIRECT_WRITE_HIGH(cspin_basereg, cspin_bitmask) -#define SPICONFIG SPISettings(50000000, MSBFIRST, SPI_MODE0) - -#if defined(__arc__) -// Use SPI1 on Arduino 101 (accesses chip already on the board) -#define SPIPORT SPI1 -#elif 0 -// Add cases here, if you wish to use other SPI ports... -#else -// Otherwise, use the normal SPI port. -#define SPIPORT SPI -#endif - -uint16_t SerialFlashChip::dirindex = 0; -uint8_t SerialFlashChip::flags = 0; -uint8_t SerialFlashChip::busy = 0; - -static volatile IO_REG_TYPE *cspin_basereg; -static IO_REG_TYPE cspin_bitmask; - -#define FLAG_32BIT_ADDR 0x01 // larger than 16 MByte address -#define FLAG_STATUS_CMD70 0x02 // requires special busy flag check -#define FLAG_DIFF_SUSPEND 0x04 // uses 2 different suspend commands -#define FLAG_MULTI_DIE 0x08 // multiple die, don't read cross 32M barrier -#define FLAG_256K_BLOCKS 0x10 // has 256K erase blocks -#define FLAG_DIE_MASK 0xC0 // top 2 bits count during multi-die erase - -void SerialFlashChip::wait(void) -{ - uint32_t status; - //Serial.print("wait-"); - while (1) { - SPIPORT.beginTransaction(SPICONFIG); - CSASSERT(); - if (flags & FLAG_STATUS_CMD70) { - // some Micron chips require this different - // command to detect program and erase completion - SPIPORT.transfer(0x70); - status = SPIPORT.transfer(0); - CSRELEASE(); - SPIPORT.endTransaction(); - //Serial.printf("b=%02x.", status & 0xFF); - if ((status & 0x80)) break; - } else { - // all others work by simply reading the status reg - SPIPORT.transfer(0x05); - status = SPIPORT.transfer(0); - CSRELEASE(); - SPIPORT.endTransaction(); - //Serial.printf("b=%02x.", status & 0xFF); - if (!(status & 1)) break; - } - } - busy = 0; - //Serial.println(); -} - -void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) -{ - uint8_t *p = (uint8_t *)buf; - uint8_t b, f, status, cmd; - - memset(p, 0, len); - f = flags; - SPIPORT.beginTransaction(SPICONFIG); - b = busy; - if (b) { - // read status register ... chip may no longer be busy - CSASSERT(); - if (flags & FLAG_STATUS_CMD70) { - SPIPORT.transfer(0x70); - status = SPIPORT.transfer(0); - if ((status & 0x80)) b = 0; - } else { - SPIPORT.transfer(0x05); - status = SPIPORT.transfer(0); - if (!(status & 1)) b = 0; - } - CSRELEASE(); - if (b == 0) { - // chip is no longer busy :-) - busy = 0; - } else if (b < 3) { - // TODO: this may not work on Spansion chips - // which apparently have 2 different suspend - // commands, for program vs erase - CSASSERT(); - SPIPORT.transfer(0x06); // write enable (Micron req'd) - CSRELEASE(); - delayMicroseconds(1); - cmd = 0x75; //Suspend program/erase for almost all chips - // but Spansion just has to be different for program suspend! - if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x85; - CSASSERT(); - SPIPORT.transfer(cmd); // Suspend command - CSRELEASE(); - if (f & FLAG_STATUS_CMD70) { - // Micron chips don't actually suspend until flags read - CSASSERT(); - SPIPORT.transfer(0x70); - do { - status = SPIPORT.transfer(0); - } while (!(status & 0x80)); - CSRELEASE(); - } else { - CSASSERT(); - SPIPORT.transfer(0x05); - do { - status = SPIPORT.transfer(0); - } while ((status & 0x01)); - CSRELEASE(); - } - } else { - // chip is busy with an operation that can not suspend - SPIPORT.endTransaction(); // is this a good idea? - wait(); // should we wait without ending - b = 0; // the transaction?? - SPIPORT.beginTransaction(SPICONFIG); - } - } - do { - uint32_t rdlen = len; - if (f & FLAG_MULTI_DIE) { - if ((addr & 0xFE000000) != ((addr + len - 1) & 0xFE000000)) { - rdlen = 0x2000000 - (addr & 0x1FFFFFF); - } - } - CSASSERT(); - // TODO: FIFO optimize.... - if (f & FLAG_32BIT_ADDR) { - SPIPORT.transfer(0x03); - SPIPORT.transfer16(addr >> 16); - SPIPORT.transfer16(addr); - } else { - SPIPORT.transfer16(0x0300 | ((addr >> 16) & 255)); - SPIPORT.transfer16(addr); - } - SPIPORT.transfer(p, rdlen); - CSRELEASE(); - p += rdlen; - addr += rdlen; - len -= rdlen; - } while (len > 0); - if (b) { - CSASSERT(); - SPIPORT.transfer(0x06); // write enable (Micron req'd) - CSRELEASE(); - delayMicroseconds(1); - cmd = 0x7A; - if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x8A; - CSASSERT(); - SPIPORT.transfer(cmd); // Resume program/erase - CSRELEASE(); - } - SPIPORT.endTransaction(); -} - -void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) -{ - const uint8_t *p = (const uint8_t *)buf; - uint32_t max, pagelen; - - //Serial.printf("WR: addr %08X, len %d\n", addr, len); - do { - if (busy) wait(); - SPIPORT.beginTransaction(SPICONFIG); - CSASSERT(); - // write enable command - SPIPORT.transfer(0x06); - CSRELEASE(); - max = 256 - (addr & 0xFF); - pagelen = (len <= max) ? len : max; - //Serial.printf("WR: addr %08X, pagelen %d\n", addr, pagelen); - delayMicroseconds(1); // TODO: reduce this, but prefer safety first - CSASSERT(); - if (flags & FLAG_32BIT_ADDR) { - SPIPORT.transfer(0x02); // program page command - SPIPORT.transfer16(addr >> 16); - SPIPORT.transfer16(addr); - } else { - SPIPORT.transfer16(0x0200 | ((addr >> 16) & 255)); - SPIPORT.transfer16(addr); - } - addr += pagelen; - len -= pagelen; - do { - SPIPORT.transfer(*p++); - } while (--pagelen > 0); - CSRELEASE(); - busy = 4; - SPIPORT.endTransaction(); - } while (len > 0); -} - -void SerialFlashChip::eraseAll() -{ - if (busy) wait(); - uint8_t id[5]; - readID(id); - //Serial.printf("ID: %02X %02X %02X\n", id[0], id[1], id[2]); - if (id[0] == 0x20 && id[2] >= 0x20 && id[2] <= 0x22) { - // Micron's multi-die chips require special die erase commands - // N25Q512A 20 BA 20 2 dies 32 Mbyte/die 65 nm transitors - // N25Q00AA 20 BA 21 4 dies 32 Mbyte/die 65 nm transitors - // MT25QL02GC 20 BA 22 2 dies 128 Mbyte/die 45 nm transitors - uint8_t die_count = 2; - if (id[2] == 0x21) die_count = 4; - uint8_t die_index = flags >> 6; - //Serial.printf("Micron die erase %d\n", die_index); - flags &= 0x3F; - if (die_index >= die_count) return; // all dies erased :-) - uint8_t die_size = 2; // in 16 Mbyte units - if (id[2] == 0x22) die_size = 8; - SPIPORT.beginTransaction(SPICONFIG); - CSASSERT(); - SPIPORT.transfer(0x06); // write enable command - CSRELEASE(); - delayMicroseconds(1); - CSASSERT(); - // die erase command - SPIPORT.transfer(0xC4); - SPIPORT.transfer16((die_index * die_size) << 8); - SPIPORT.transfer16(0x0000); - CSRELEASE(); - //Serial.printf("Micron erase begin\n"); - flags |= (die_index + 1) << 6; - } else { - // All other chips support the bulk erase command - SPIPORT.beginTransaction(SPICONFIG); - CSASSERT(); - // write enable command - SPIPORT.transfer(0x06); - CSRELEASE(); - delayMicroseconds(1); - CSASSERT(); - // bulk erase command - SPIPORT.transfer(0xC7); - CSRELEASE(); - SPIPORT.endTransaction(); - } - busy = 3; -} - -void SerialFlashChip::eraseBlock(uint32_t addr) -{ - uint8_t f = flags; - if (busy) wait(); - SPIPORT.beginTransaction(SPICONFIG); - CSASSERT(); - SPIPORT.transfer(0x06); // write enable command - CSRELEASE(); - delayMicroseconds(1); - CSASSERT(); - if (f & FLAG_32BIT_ADDR) { - SPIPORT.transfer(0xD8); - SPIPORT.transfer16(addr >> 16); - SPIPORT.transfer16(addr); - } else { - SPIPORT.transfer16(0xD800 | ((addr >> 16) & 255)); - SPIPORT.transfer16(addr); - } - CSRELEASE(); - SPIPORT.endTransaction(); - busy = 2; -} - - -bool SerialFlashChip::ready() -{ - uint32_t status; - if (!busy) return true; - SPIPORT.beginTransaction(SPICONFIG); - CSASSERT(); - if (flags & FLAG_STATUS_CMD70) { - // some Micron chips require this different - // command to detect program and erase completion - SPIPORT.transfer(0x70); - status = SPIPORT.transfer(0); - CSRELEASE(); - SPIPORT.endTransaction(); - //Serial.printf("ready=%02x\n", status & 0xFF); - if ((status & 0x80) == 0) return false; - } else { - // all others work by simply reading the status reg - SPIPORT.transfer(0x05); - status = SPIPORT.transfer(0); - CSRELEASE(); - SPIPORT.endTransaction(); - //Serial.printf("ready=%02x\n", status & 0xFF); - if ((status & 1)) return false; - } - busy = 0; - if (flags & 0xC0) { - // continue a multi-die erase - eraseAll(); - return false; - } - return true; -} - - -#define ID0_WINBOND 0xEF -#define ID0_SPANSION 0x01 -#define ID0_MICRON 0x20 -#define ID0_MACRONIX 0xC2 -#define ID0_SST 0xBF - -//#define FLAG_32BIT_ADDR 0x01 // larger than 16 MByte address -//#define FLAG_STATUS_CMD70 0x02 // requires special busy flag check -//#define FLAG_DIFF_SUSPEND 0x04 // uses 2 different suspend commands -//#define FLAG_256K_BLOCKS 0x10 // has 256K erase blocks - -bool SerialFlashChip::begin(uint8_t pin) -{ - uint8_t id[5]; - uint8_t f; - uint32_t size; - - cspin_basereg = PIN_TO_BASEREG(pin); - cspin_bitmask = PIN_TO_BITMASK(pin); - SPIPORT.begin(); - pinMode(pin, OUTPUT); - CSRELEASE(); - readID(id); - f = 0; - size = capacity(id); - if (size > 16777216) { - // more than 16 Mbyte requires 32 bit addresses - f |= FLAG_32BIT_ADDR; - SPIPORT.beginTransaction(SPICONFIG); - if (id[0] == ID0_SPANSION) { - // spansion uses MSB of bank register - CSASSERT(); - SPIPORT.transfer16(0x1780); // bank register write - CSRELEASE(); - } else { - // micron & winbond & macronix use command - CSASSERT(); - SPIPORT.transfer(0x06); // write enable - CSRELEASE(); - delayMicroseconds(1); - CSASSERT(); - SPIPORT.transfer(0xB7); // enter 4 byte addr mode - CSRELEASE(); - } - SPIPORT.endTransaction(); - if (id[0] == ID0_MICRON) f |= FLAG_MULTI_DIE; - } - if (id[0] == ID0_SPANSION) { - // Spansion has separate suspend commands - f |= FLAG_DIFF_SUSPEND; - if (!id[4]) { - // Spansion chips with id[4] == 0 use 256K sectors - f |= FLAG_256K_BLOCKS; - } - } - if (id[0] == ID0_MICRON) { - // Micron requires busy checks with a different command - f |= FLAG_STATUS_CMD70; // TODO: all or just multi-die chips? - } - flags = f; - readID(id); - return true; -} - -// chips tested: https://github.com/PaulStoffregen/SerialFlash/pull/12#issuecomment-169596992 -// -void SerialFlashChip::sleep() -{ - if (busy) wait(); - SPIPORT.beginTransaction(SPICONFIG); - CSASSERT(); - SPIPORT.transfer(0xB9); // Deep power down command - CSRELEASE(); -} - -void SerialFlashChip::wakeup() -{ - SPIPORT.beginTransaction(SPICONFIG); - CSASSERT(); - SPIPORT.transfer(0xAB); // Wake up from deep power down command - CSRELEASE(); -} - -void SerialFlashChip::readID(uint8_t *buf) -{ - if (busy) wait(); - SPIPORT.beginTransaction(SPICONFIG); - CSASSERT(); - SPIPORT.transfer(0x9F); - buf[0] = SPIPORT.transfer(0); // manufacturer ID - buf[1] = SPIPORT.transfer(0); // memory type - buf[2] = SPIPORT.transfer(0); // capacity - if (buf[0] == ID0_SPANSION) { - buf[3] = SPIPORT.transfer(0); // ID-CFI - buf[4] = SPIPORT.transfer(0); // sector size - } - CSRELEASE(); - SPIPORT.endTransaction(); - //Serial.printf("ID: %02X %02X %02X\n", buf[0], buf[1], buf[2]); -} - -void SerialFlashChip::readSerialNumber(uint8_t *buf) //needs room for 8 bytes -{ - if (busy) wait(); - SPIPORT.beginTransaction(SPICONFIG); - CSASSERT(); - SPIPORT.transfer(0x4B); - SPIPORT.transfer16(0); - SPIPORT.transfer16(0); - for (int i=0; i<8; i++) { - buf[i] = SPIPORT.transfer(0); - } - CSRELEASE(); - SPIPORT.endTransaction(); -// Serial.printf("Serial Number: %02X %02X %02X %02X %02X %02X %02X %02X\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); -} - -uint32_t SerialFlashChip::capacity(const uint8_t *id) -{ - uint32_t n = 1048576; // unknown chips, default to 1 MByte - - if (id[2] >= 16 && id[2] <= 31) { - n = 1ul << id[2]; - } else - if (id[2] >= 32 && id[2] <= 37) { - n = 1ul << (id[2] - 6); - } else - if ((id[0]==0 && id[1]==0 && id[2]==0) || - (id[0]==255 && id[1]==255 && id[2]==255)) { - n = 0; - } - //Serial.printf("capacity %lu\n", n); - return n; -} - -uint32_t SerialFlashChip::blockSize() -{ - // Spansion chips >= 512 mbit use 256K sectors - if (flags & FLAG_256K_BLOCKS) return 262144; - // everything else seems to have 64K sectors - return 65536; -} - - - - -/* -Chip Uniform Sector Erase - 20/21 52 D8/DC - ----- -- ----- -W25Q64CV 4 32 64 -W25Q128FV 4 32 64 -S25FL127S 64 -N25Q512A 4 64 -N25Q00AA 4 64 -S25FL512S 256 -SST26VF032 4 -*/ - - - -// size sector busy pgm/erase chip -// Part Mbyte kbyte ID bytes cmd suspend erase -// ---- ---- ----- -------- --- ------- ----- -// Winbond W25Q64CV 8 64 EF 40 17 -// Winbond W25Q128FV 16 64 EF 40 18 05 single 60 & C7 -// Winbond W25Q256FV 32 64 EF 40 19 -// Spansion S25FL064A 8 ? 01 02 16 -// Spansion S25FL127S 16 64 01 20 18 05 -// Spansion S25FL128P 16 64 01 20 18 -// Spansion S25FL256S 32 64 01 02 19 05 60 & C7 -// Spansion S25FL512S 64 256 01 02 20 -// Macronix MX25L12805D 16 ? C2 20 18 -// Macronix MX66L51235F 64 C2 20 1A -// Numonyx M25P128 16 ? 20 20 18 -// Micron M25P80 1 ? 20 20 14 -// Micron N25Q128A 16 64 20 BA 18 -// Micron N25Q512A 64 ? 20 BA 20 70 single C4 x2 -// Micron N25Q00AA 128 64 20 BA 21 single C4 x4 -// Micron MT25QL02GC 256 64 20 BA 22 70 C4 x2 -// SST SST25WF010 1/8 ? BF 25 02 -// SST SST25WF020 1/4 ? BF 25 03 -// SST SST25WF040 1/2 ? BF 25 04 -// SST SST25VF016B 1 ? BF 25 41 -// SST26VF016 ? BF 26 01 -// SST26VF032 ? BF 26 02 -// SST25VF032 4 64 BF 25 4A -// SST26VF064 8 ? BF 26 43 -// LE25U40CMC 1/2 64 62 06 13 - -SerialFlashChip SerialFlash; diff --git a/libraries/SerialFlash/SerialFlashDirectory.cpp b/libraries/SerialFlash/SerialFlashDirectory.cpp deleted file mode 100644 index 116c14ec..00000000 --- a/libraries/SerialFlash/SerialFlashDirectory.cpp +++ /dev/null @@ -1,395 +0,0 @@ -/* SerialFlash Library - for filesystem-like access to SPI Serial Flash memory - * https://github.com/PaulStoffregen/SerialFlash - * Copyright (C) 2015, Paul Stoffregen, paul@pjrc.com - * - * Development of this library was funded by PJRC.COM, LLC by sales of Teensy. - * Please support PJRC's efforts to develop open source software by purchasing - * Teensy or other genuine PJRC products. - * - * 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, development funding 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 "SerialFlash.h" - -/* On-chip SerialFlash file allocation data structures: - - uint32_t signature = 0xFA96554C; - uint16_t maxfiles - uint16_t stringssize // div by 4 - uint16_t hashes[maxfiles] - struct { - uint32_t file_begin - uint32_t file_length - uint16_t string_index // div4 - } fileinfo[maxfiles] - char strings[stringssize] - -A 32 bit signature is stored at the beginning of the flash memory. -If 0xFFFFFFFF is seen, the entire chip should be assumed blank. -If any value other than 0xFA96554C is found, a different data format -is stored. This could should refuse to access the flash. - -The next 4 bytes store number of files and size of the strings -section, which allow the position of every other item to be found. -The string section size is the 16 bit integer times 4, which allows -up to 262140 bytes for string data. - -An array of 16 bit filename hashes allows for quick linear search -for potentially matching filenames. A hash value of 0xFFFF indicates -no file is allocated for the remainder of the array. - -Following the hashes, and array of 10 byte structs give the location -and length of the file's actual data, and the offset of its filename -in the strings section. - -Strings are null terminated. The remainder of the chip is file data. -*/ - -#define DEFAULT_MAXFILES 600 -#define DEFAULT_STRINGS_SIZE 25560 - - -static uint32_t check_signature(void) -{ - uint32_t sig[2]; - - SerialFlash.read(0, sig, 8); - //Serial.printf("sig: %08X %08X\n", sig[0], sig[1]); - if (sig[0] == 0xFA96554C) return sig[1]; - if (sig[0] == 0xFFFFFFFF) { - sig[0] = 0xFA96554C; - sig[1] = ((uint32_t)(DEFAULT_STRINGS_SIZE/4) << 16) | DEFAULT_MAXFILES; - SerialFlash.write(0, sig, 8); - while (!SerialFlash.ready()) ; // TODO: timeout - SerialFlash.read(0, sig, 8); - if (sig[0] == 0xFA96554C) return sig[1]; - } - return 0; -} - -static uint16_t filename_hash(const char *filename) -{ - // http://isthe.com/chongo/tech/comp/fnv/ - uint32_t hash = 2166136261; - const char *p; - - for (p=filename; *p; p++) { - hash ^= *p; - hash *= 16777619; - } - hash = (hash % (uint32_t)0xFFFE) + 1; // all values except 0000 & FFFF - return hash; -} - -static bool filename_compare(const char *filename, uint32_t straddr) -{ - unsigned int i; - const char *p; - char buf[16]; - - p = filename; - while (1) { - SerialFlash.read(straddr, buf, sizeof(buf)); - straddr += sizeof(buf); - for (i=0; i < sizeof(buf); i++) { - if (*p++ != buf[i]) return false; - if (buf[i] == 0) return true; - } - } -} - -#if 0 -void pbuf(const void *buf, uint32_t len) -{ - const uint8_t *p = (const uint8_t *)buf; - do { - Serial.printf("%02X ", *p++); - } while (--len > 0); - Serial.println(); -} -#endif - -SerialFlashFile SerialFlashChip::open(const char *filename) -{ - uint32_t maxfiles, straddr; - uint16_t hash, hashtable[8]; - uint32_t i, n, index=0; - uint32_t buf[3]; - SerialFlashFile file; - - maxfiles = check_signature(); - //Serial.printf("sig: %08X\n", maxfiles); - if (!maxfiles) return file; - maxfiles &= 0xFFFF; - hash = filename_hash(filename); - //Serial.printf("hash %04X for \"%s\"\n", hash, filename); - while (index < maxfiles) { - n = 8; - if (n > maxfiles - index) n = maxfiles - index; - SerialFlash.read(8 + index * 2, hashtable, n * 2); - //Serial.printf(" read %u: ", 8 + index * 2); - //pbuf(hashtable, n * 2); - for (i=0; i < n; i++) { - if (hashtable[i] == hash) { - //Serial.printf(" hash match at index %u\n", index+i); - buf[2] = 0; - SerialFlash.read(8 + maxfiles * 2 + (index+i) * 10, buf, 10); - - //Serial.printf(" maxf=%d, index=%d, i=%d\n", maxfiles, index, i); - //Serial.printf(" read %u: ", 8 + maxfiles * 2 + (index+i) * 10); - //pbuf(buf, 10); - straddr = 8 + maxfiles * 12 + buf[2] * 4; - //Serial.printf(" straddr = %u\n", straddr); - if (filename_compare(filename, straddr)) { - //Serial.printf(" match!\n"); - //Serial.printf(" addr = %u\n", buf[0]); - //Serial.printf(" len = %u\n", buf[1]); - file.address = buf[0]; - file.length = buf[1]; - file.offset = 0; - file.dirindex = index + i; - return file; - } - } else if (hashtable[i] == 0xFFFF) { - return file; - } - } - index += n; - } - return file; -} - -bool SerialFlashChip::exists(const char *filename) -{ - SerialFlashFile file = open(filename); - return (bool)file; -} - -bool SerialFlashChip::remove(const char *filename) -{ - SerialFlashFile file = open(filename); - return remove(file); -} - -bool SerialFlashChip::remove(SerialFlashFile &file) -{ - // To "remove" a file, we simply zero its hash in the lookup - // table, so it can't be found by open(). The space on the - // flash memory is not freed. - if (!file) return false; - uint16_t hash; - SerialFlash.read(8 + file.dirindex * 2, &hash, 2); - //Serial.printf("remove hash %04X at %d index\n", hash, file.dirindex); - hash ^= 0xFFFF; // write zeros to all ones - SerialFlash.write(8 + file.dirindex * 2, &hash, 2); - while (!SerialFlash.ready()) ; // wait... TODO: timeout - SerialFlash.read(8 + file.dirindex * 2, &hash, 2); - if (hash != 0) { - //Serial.printf("remove failed, hash %04X\n", hash); - return false; - } - file.address = 0; - file.length = 0; - return true; -} - -static uint32_t find_first_unallocated_file_index(uint32_t maxfiles) -{ - uint16_t hashtable[8]; - uint32_t i, n, index=0; - - do { - n = 8; - if (index + n > maxfiles) n = maxfiles - index; - SerialFlash.read(8 + index * 2, hashtable, n * 2); - for (i=0; i < n; i++) { - if (hashtable[i] == 0xFFFF) return index + i; - } - index += n; - } while (index < maxfiles); - return 0xFFFFFFFF; -} - -static uint32_t string_length(uint32_t addr) -{ - char buf[16]; - const char *p; - uint32_t len=0; - - while (1) { - SerialFlash.read(addr, buf, sizeof(buf)); - for (p=buf; p < buf + sizeof(buf); p++) { - len++; - if (*p == 0) return len; - } - addr += sizeof(buf); - } -} - -// uint32_t signature = 0xFA96554C; -// uint16_t maxfiles -// uint16_t stringssize // div by 4 -// uint16_t hashes[maxfiles] -// struct { -// uint32_t file_begin -// uint32_t file_length -// uint16_t string_index // div 4 -// } fileinfo[maxfiles] -// char strings[stringssize] - -bool SerialFlashChip::create(const char *filename, uint32_t length, uint32_t align) -{ - uint32_t maxfiles, stringsize; - uint32_t index, buf[3]; - uint32_t address, straddr, len; - SerialFlashFile file; - - // check if the file already exists - if (exists(filename)) return false; - - // first, get the filesystem parameters - maxfiles = check_signature(); - if (!maxfiles) return false; - stringsize = (maxfiles & 0xFFFF0000) >> 14; - maxfiles &= 0xFFFF; - - // find the first unused slot for this file - index = find_first_unallocated_file_index(maxfiles); - if (index >= maxfiles) return false; - //Serial.printf("index = %u\n", index); - // compute where to store the filename and actual data - straddr = 8 + maxfiles * 12; - if (index == 0) { - address = straddr + stringsize; - } else { - buf[2] = 0; - SerialFlash.read(8 + maxfiles * 2 + (index-1) * 10, buf, 10); - address = buf[0] + buf[1]; - straddr += buf[2] * 4; - straddr += string_length(straddr); - straddr = (straddr + 3) & 0x0003FFFC; - } - //Serial.printf("straddr = %u\n", straddr); - //Serial.printf("address = %u\n", address); - //Serial.printf("length = %u\n", length); - if (align > 0) { - // for files aligned to sectors, adjust addr & len - address += align - 1; - address /= align; - address *= align; - //Serial.printf("align address = %u\n", address); - length += align - 1; - length /= align; - length *= align; - //Serial.printf("align length = %u\n", length); - } else { - // always align every file to a page boundary - // for predictable write latency and to guarantee - // write suspend for reading another file can't - // conflict on the same page (2 files never share - // a write page). - address = (address + 255) & 0xFFFFFF00; - } - //Serial.printf("address = %u\n", address); - // last check, if enough space exists... - len = strlen(filename); - // TODO: check for enough string space for filename - - // 5 bytes, to allow for extra 2 bytes in Spansion device IDs - uint8_t id[5]; - SerialFlash.readID(id); - if (address + length > SerialFlash.capacity(id)) return false; - - SerialFlash.write(straddr, filename, len+1); - buf[0] = address; - buf[1] = length; - buf[2] = (straddr - (8 + maxfiles * 12)) / 4; - SerialFlash.write(8 + maxfiles * 2 + index * 10, buf, 10); - //Serial.printf(" write %u: ", 8 + maxfiles * 2 + index * 10); - //pbuf(buf, 10); - while (!SerialFlash.ready()) ; // TODO: timeout - - buf[0] = filename_hash(filename); - //Serial.printf("hash = %04X\n", buf[0]); - SerialFlash.write(8 + index * 2, buf, 2); - while (!SerialFlash.ready()) ; // TODO: timeout - return true; -} - -bool SerialFlashChip::readdir(char *filename, uint32_t strsize, uint32_t &filesize) -{ - uint32_t maxfiles, index, straddr; - uint32_t i, n; - uint32_t buf[2]; - uint16_t hash; - char str[16], *p=filename; - - filename[0] = 0; - maxfiles = check_signature(); - if (!maxfiles) return false; - maxfiles &= 0xFFFF; - index = dirindex; - while (1) { - if (index >= maxfiles) return false; - //Serial.printf("readdir, index = %u\n", index); - SerialFlash.read(8 + index * 2, &hash, 2); - if (hash != 0) break; - index++; // skip deleted entries - } - dirindex = index + 1; - buf[1] = 0; - SerialFlash.read(8 + 4 + maxfiles * 2 + index * 10, buf, 6); - if (buf[0] == 0xFFFFFFFF) return false; - filesize = buf[0]; - straddr = 8 + maxfiles * 12 + buf[1] * 4; - //Serial.printf(" length = %u\n", buf[0]); - //Serial.printf(" straddr = %u\n", straddr); - - while (strsize) { - n = strsize; - if (n > sizeof(str)) n = sizeof(str); - SerialFlash.read(straddr, str, n); - for (i=0; i < n; i++) { - *p++ = str[i]; - if (str[i] == 0) { - //Serial.printf(" name = %s\n", filename); - return true; - } - } - strsize -= n; - straddr += n; - } - *(p - 1) = 0; - //Serial.printf(" name(overflow) = %s\n", filename); - return true; -} - - -void SerialFlashFile::erase() -{ - uint32_t i, blocksize; - - blocksize = SerialFlash.blockSize(); - if (address & (blocksize - 1)) return; // must begin on a block boundary - if (length & (blocksize - 1)) return; // must be exact number of blocks - for (i=0; i < length; i += blocksize) { - SerialFlash.eraseBlock(address + i); - } -} - diff --git a/libraries/SerialFlash/examples/EraseEverything/EraseEverything.ino b/libraries/SerialFlash/examples/EraseEverything/EraseEverything.ino deleted file mode 100644 index d3b3f4d7..00000000 --- a/libraries/SerialFlash/examples/EraseEverything/EraseEverything.ino +++ /dev/null @@ -1,80 +0,0 @@ -//SerialFlash library API: https://github.com/PaulStoffregen/SerialFlash - -#include -#include - -const int FlashChipSelect = 21; // digital pin for flash chip CS pin - -SerialFlashFile file; - -const unsigned long testIncrement = 4096; - -void setup() { - //uncomment these if using Teensy audio shield - //SPI.setSCK(14); // Audio shield has SCK on pin 14 - //SPI.setMOSI(7); // Audio shield has MOSI on pin 7 - - //uncomment these if you have other SPI chips connected - //to keep them disabled while using only SerialFlash - //pinMode(4, INPUT_PULLUP); - //pinMode(10, INPUT_PULLUP); - - Serial.begin(9600); - - // wait up to 10 seconds for Arduino Serial Monitor - unsigned long startMillis = millis(); - while (!Serial && (millis() - startMillis < 10000)) ; - delay(100); - - SerialFlash.begin(FlashChipSelect); - unsigned char id[5]; - SerialFlash.readID(id); - unsigned long size = SerialFlash.capacity(id); - - if (size > 0) { - Serial.print("Flash Memory has "); - Serial.print(size); - Serial.println(" bytes."); - Serial.println("Erasing ALL Flash Memory:"); - // Estimate the (lengthy) wait time. - Serial.print(" estimated wait: "); - int seconds = (float)size / eraseBytesPerSecond(id) + 0.5; - Serial.print(seconds); - Serial.println(" seconds."); - Serial.println(" Yes, full chip erase is SLOW!"); - SerialFlash.eraseAll(); - unsigned long dotMillis = millis(); - unsigned char dotcount = 0; - while (SerialFlash.ready() == false) { - if (millis() - dotMillis > 1000) { - dotMillis = dotMillis + 1000; - Serial.print("."); - dotcount = dotcount + 1; - if (dotcount >= 60) { - Serial.println(); - dotcount = 0; - } - } - } - if (dotcount > 0) Serial.println(); - Serial.println("Erase completed"); - unsigned long elapsed = millis() - startMillis; - Serial.print(" actual wait: "); - Serial.print(elapsed / 1000ul); - Serial.println(" seconds."); - } -} - -float eraseBytesPerSecond(const unsigned char *id) { - if (id[0] == 0x20) return 152000.0; // Micron - if (id[0] == 0x01) return 500000.0; // Spansion - if (id[0] == 0xEF) return 419430.0; // Winbond - if (id[0] == 0xC2) return 279620.0; // Macronix - return 320000.0; // guess? -} - - -void loop() { - -} - diff --git a/libraries/SerialFlash/examples/FileWrite/FileWrite.ino b/libraries/SerialFlash/examples/FileWrite/FileWrite.ino deleted file mode 100755 index dbeb438a..00000000 --- a/libraries/SerialFlash/examples/FileWrite/FileWrite.ino +++ /dev/null @@ -1,50 +0,0 @@ -//SerialFlash library API: https://github.com/PaulStoffregen/SerialFlash - -#include -#include - -#define FSIZE 256 - -const char *filename = "myfile.txt"; -const char *contents = "0123456789ABCDEF"; - -const int FlashChipSelect = 21; // digital pin for flash chip CS pin - -void setup() { - Serial.begin(9600); - - // wait for Arduino Serial Monitor - while (!Serial) ; - delay(100); - - // Init. SPI Flash chip - if (!SerialFlash.begin(FlashChipSelect)) { - Serial.println("Unable to access SPI Flash chip"); - } - - SerialFlashFile file; - - // Create the file if it doesn't exist - if (!create_if_not_exists(filename)) { - Serial.println("Not enough space to create file " + String(filename)); - return; - } - - // Open the file and write test data - file = SerialFlash.open(filename); - file.write(contents, strlen(contents) + 1); - Serial.println("String \"" + String(contents) + "\" written to file " + String(filename)); -} - -bool create_if_not_exists (const char *filename) { - if (!SerialFlash.exists(filename)) { - Serial.println("Creating file " + String(filename)); - return SerialFlash.create(filename, FSIZE); - } - - Serial.println("File " + String(filename) + " already exists"); - return true; -} - -void loop() { -} diff --git a/libraries/SerialFlash/examples/ListFiles/ListFiles.ino b/libraries/SerialFlash/examples/ListFiles/ListFiles.ino deleted file mode 100644 index bc15b89b..00000000 --- a/libraries/SerialFlash/examples/ListFiles/ListFiles.ino +++ /dev/null @@ -1,63 +0,0 @@ -// SerialFlash library API: https://github.com/PaulStoffregen/SerialFlash - -#include -#include - -const int FlashChipSelect = 21; // digital pin for flash chip CS pin - -void setup() { - //uncomment these if using Teensy audio shield - //SPI.setSCK(14); // Audio shield has SCK on pin 14 - //SPI.setMOSI(7); // Audio shield has MOSI on pin 7 - - //uncomment these if you have other SPI chips connected - //to keep them disabled while using only SerialFlash - //pinMode(4, INPUT_PULLUP); - //pinMode(10, INPUT_PULLUP); - - Serial.begin(9600); - - // wait for Arduino Serial Monitor - while (!Serial) ; - delay(100); - Serial.println("All Files on SPI Flash chip:"); - - if (!SerialFlash.begin(FlashChipSelect)) { - error("Unable to access SPI Flash chip"); - } - - SerialFlash.opendir(); - unsigned int count = 0; - while (1) { - char filename[64]; - unsigned long filesize; - - if (SerialFlash.readdir(filename, sizeof(filename), filesize)) { - Serial.print(" "); - Serial.print(filename); - spaces(20 - strlen(filename)); - Serial.print(" "); - Serial.print(filesize); - Serial.print(" bytes"); - Serial.println(); - } else { - break; // no more files - } - } -} - -void spaces(int num) { - for (int i=0; i < num; i++) { - Serial.print(" "); - } -} - -void loop() { -} - -void error(const char *message) { - while (1) { - Serial.println(message); - delay(2500); - } -} diff --git a/libraries/SerialFlash/examples/RawHardwareTest/RawHardwareTest.ino b/libraries/SerialFlash/examples/RawHardwareTest/RawHardwareTest.ino deleted file mode 100644 index 74afa10a..00000000 --- a/libraries/SerialFlash/examples/RawHardwareTest/RawHardwareTest.ino +++ /dev/null @@ -1,502 +0,0 @@ -// SerialFlash library API: https://github.com/PaulStoffregen/SerialFlash -// -// RawHardwareTest - Check if a SPI Flash chip is compatible -// with SerialFlash by performing many read and write tests -// to its memory. -// -// The chip should be fully erased before running this test. -// Use the EraseEverything to do a (slow) full chip erase. -// -// Normally you should NOT access the flash memory directly, -// as this test program does. You should create files and -// read and write the files. File creation allocates space -// with program & erase boundaries within the chip, to allow -// reading from any other files while a file is busy writing -// or erasing (if created as erasable). -// -// If you discover an incompatible chip, please report it here: -// https://github.com/PaulStoffregen/SerialFlash/issues -// You MUST post the complete output of this program, and -// the exact part number and manufacturer of the chip. - - -#include -#include - -const int FlashChipSelect = 21; // digital pin for flash chip CS pin - -SerialFlashFile file; - -const unsigned long testIncrement = 4096; - -void setup() { - - //uncomment these if using Teensy audio shield - //SPI.setSCK(14); // Audio shield has SCK on pin 14 - //SPI.setMOSI(7); // Audio shield has MOSI on pin 7 - - //uncomment these if you have other SPI chips connected - //to keep them disabled while using only SerialFlash - //pinMode(4, INPUT_PULLUP); - //pinMode(10, INPUT_PULLUP); - - Serial.begin(9600); - - while (!Serial) ; - delay(100); - - Serial.println("Raw SerialFlash Hardware Test"); - SerialFlash.begin(FlashChipSelect); - - if (test()) { - Serial.println(); - Serial.println("All Tests Passed :-)"); - Serial.println(); - Serial.println("Test data was written to your chip. You must run"); - Serial.println("EraseEverything before using this chip for files."); - } else { - Serial.println(); - Serial.println("Tests Failed :{"); - Serial.println(); - Serial.println("The flash chip may be left in an improper state."); - Serial.println("You might need to power cycle to return to normal."); - } -} - - -bool test() { - unsigned char buf[256], sig[256], buf2[8]; - unsigned long address, count, chipsize, blocksize; - unsigned long usec; - bool first; - - // Read the chip identification - Serial.println(); - Serial.println("Read Chip Identification:"); - SerialFlash.readID(buf); - Serial.print(" JEDEC ID: "); - Serial.print(buf[0], HEX); - Serial.print(" "); - Serial.print(buf[1], HEX); - Serial.print(" "); - Serial.println(buf[2], HEX); - Serial.print(" Part Nummber: "); - Serial.println(id2chip(buf)); - Serial.print(" Memory Size: "); - chipsize = SerialFlash.capacity(buf); - Serial.print(chipsize); - Serial.println(" bytes"); - if (chipsize == 0) return false; - Serial.print(" Block Size: "); - blocksize = SerialFlash.blockSize(); - Serial.print(blocksize); - Serial.println(" bytes"); - - - // Read the entire chip. Every test location must be - // erased, or have a previously tested signature - Serial.println(); - Serial.println("Reading Chip..."); - memset(buf, 0, sizeof(buf)); - memset(sig, 0, sizeof(sig)); - memset(buf2, 0, sizeof(buf2)); - address = 0; - count = 0; - first = true; - while (address < chipsize) { - SerialFlash.read(address, buf, 8); - //Serial.print(" addr = "); - //Serial.print(address, HEX); - //Serial.print(", data = "); - //printbuf(buf, 8); - create_signature(address, sig); - if (is_erased(buf, 8) == false) { - if (equal_signatures(buf, sig) == false) { - Serial.print(" Previous data found at address "); - Serial.println(address); - Serial.println(" You must fully erase the chip before this test"); - Serial.print(" found this: "); - printbuf(buf, 8); - Serial.print(" correct: "); - printbuf(sig, 8); - return false; - } - } else { - count = count + 1; // number of blank signatures - } - if (first) { - address = address + (testIncrement - 8); - first = false; - } else { - address = address + 8; - first = true; - } - } - - - // Write any signatures that were blank on the original check - if (count > 0) { - Serial.println(); - Serial.print("Writing "); - Serial.print(count); - Serial.println(" signatures"); - memset(buf, 0, sizeof(buf)); - memset(sig, 0, sizeof(sig)); - memset(buf2, 0, sizeof(buf2)); - address = 0; - first = true; - while (address < chipsize) { - SerialFlash.read(address, buf, 8); - if (is_erased(buf, 8)) { - create_signature(address, sig); - //Serial.printf("write %08X: data: ", address); - //printbuf(sig, 8); - SerialFlash.write(address, sig, 8); - while (!SerialFlash.ready()) ; // wait - SerialFlash.read(address, buf, 8); - if (equal_signatures(buf, sig) == false) { - Serial.print(" error writing signature at "); - Serial.println(address); - Serial.print(" Read this: "); - printbuf(buf, 8); - Serial.print(" Expected: "); - printbuf(sig, 8); - return false; - } - } - if (first) { - address = address + (testIncrement - 8); - first = false; - } else { - address = address + 8; - first = true; - } - } - } else { - Serial.println(" all signatures present from prior tests"); - } - - - // Read all the signatures again, just to be sure - // checks prior writing didn't corrupt any other data - Serial.println(); - Serial.println("Double Checking All Signatures:"); - memset(buf, 0, sizeof(buf)); - memset(sig, 0, sizeof(sig)); - memset(buf2, 0, sizeof(buf2)); - count = 0; - address = 0; - first = true; - while (address < chipsize) { - SerialFlash.read(address, buf, 8); - create_signature(address, sig); - if (equal_signatures(buf, sig) == false) { - Serial.print(" error in signature at "); - Serial.println(address); - Serial.print(" Read this: "); - printbuf(buf, 8); - Serial.print(" Expected: "); - printbuf(sig, 8); - return false; - } - count = count + 1; - if (first) { - address = address + (testIncrement - 8); - first = false; - } else { - address = address + 8; - first = true; - } - } - Serial.print(" all "); - Serial.print(count); - Serial.println(" signatures read ok"); - - - // Read pairs of adjacent signatures - // check read works across boundaries - Serial.println(); - Serial.println("Checking Signature Pairs"); - memset(buf, 0, sizeof(buf)); - memset(sig, 0, sizeof(sig)); - memset(buf2, 0, sizeof(buf2)); - count = 0; - address = testIncrement - 8; - first = true; - while (address < chipsize - 8) { - SerialFlash.read(address, buf, 16); - create_signature(address, sig); - create_signature(address + 8, sig + 8); - if (memcmp(buf, sig, 16) != 0) { - Serial.print(" error in signature pair at "); - Serial.println(address); - Serial.print(" Read this: "); - printbuf(buf, 16); - Serial.print(" Expected: "); - printbuf(sig, 16); - return false; - } - count = count + 1; - address = address + testIncrement; - } - Serial.print(" all "); - Serial.print(count); - Serial.println(" signature pairs read ok"); - - - // Write data and read while write in progress - Serial.println(); - Serial.println("Checking Read-While-Write (Program Suspend)"); - address = 256; - while (address < chipsize) { // find a blank space - SerialFlash.read(address, buf, 256); - if (is_erased(buf, 256)) break; - address = address + 256; - } - if (address >= chipsize) { - Serial.println(" error, unable to find any blank space!"); - return false; - } - for (int i=0; i < 256; i += 8) { - create_signature(address + i, sig + i); - } - Serial.print(" write 256 bytes at "); - Serial.println(address); - Serial.flush(); - SerialFlash.write(address, sig, 256); - usec = micros(); - if (SerialFlash.ready()) { - Serial.println(" error, chip did not become busy after write"); - return false; - } - SerialFlash.read(0, buf2, 8); // read while busy writing - while (!SerialFlash.ready()) ; // wait - usec = micros() - usec; - Serial.print(" write time was "); - Serial.print(usec); - Serial.println(" microseconds."); - SerialFlash.read(address, buf, 256); - if (memcmp(buf, sig, 256) != 0) { - Serial.println(" error writing to flash"); - Serial.print(" Read this: "); - printbuf(buf, 256); - Serial.print(" Expected: "); - printbuf(sig, 256); - return false; - } - create_signature(0, sig); - if (memcmp(buf2, sig, 8) != 0) { - Serial.println(" error, incorrect read while writing"); - Serial.print(" Read this: "); - printbuf(buf2, 256); - Serial.print(" Expected: "); - printbuf(sig, 256); - return false; - } - Serial.print(" read-while-writing: "); - printbuf(buf2, 8); - Serial.println(" test passed, good read while writing"); - - - - // Erase a block and read while erase in progress - if (chipsize >= 262144 + blocksize + testIncrement) { - Serial.println(); - Serial.println("Checking Read-While-Erase (Erase Suspend)"); - memset(buf, 0, sizeof(buf)); - memset(sig, 0, sizeof(sig)); - memset(buf2, 0, sizeof(buf2)); - SerialFlash.eraseBlock(262144); - usec = micros(); - delayMicroseconds(50); - if (SerialFlash.ready()) { - Serial.println(" error, chip did not become busy after erase"); - return false; - } - SerialFlash.read(0, buf2, 8); // read while busy writing - while (!SerialFlash.ready()) ; // wait - usec = micros() - usec; - Serial.print(" erase time was "); - Serial.print(usec); - Serial.println(" microseconds."); - // read all signatures, check ones in this block got - // erased, and all the others are still intact - address = 0; - first = true; - while (address < chipsize) { - SerialFlash.read(address, buf, 8); - if (address >= 262144 && address < 262144 + blocksize) { - if (is_erased(buf, 8) == false) { - Serial.print(" error in erasing at "); - Serial.println(address); - Serial.print(" Read this: "); - printbuf(buf, 8); - return false; - } - } else { - create_signature(address, sig); - if (equal_signatures(buf, sig) == false) { - Serial.print(" error in signature at "); - Serial.println(address); - Serial.print(" Read this: "); - printbuf(buf, 8); - Serial.print(" Expected: "); - printbuf(sig, 8); - return false; - } - } - if (first) { - address = address + (testIncrement - 8); - first = false; - } else { - address = address + 8; - first = true; - } - } - Serial.print(" erase correctly erased "); - Serial.print(blocksize); - Serial.println(" bytes"); - // now check if the data we read during erase is good - create_signature(0, sig); - if (memcmp(buf2, sig, 8) != 0) { - Serial.println(" error, incorrect read while erasing"); - Serial.print(" Read this: "); - printbuf(buf2, 256); - Serial.print(" Expected: "); - printbuf(sig, 256); - return false; - } - Serial.print(" read-while-erasing: "); - printbuf(buf2, 8); - Serial.println(" test passed, good read while erasing"); - - } else { - Serial.println("Skip Read-While-Erase, this chip is too small"); - } - - - - - return true; -} - - -void loop() { - // do nothing after the test -} - -const char * id2chip(const unsigned char *id) -{ - if (id[0] == 0xEF) { - // Winbond - if (id[1] == 0x40) { - if (id[2] == 0x14) return "W25Q80BV"; - if (id[2] == 0x15) return "W25Q16DV"; - if (id[2] == 0x17) return "W25Q64FV"; - if (id[2] == 0x18) return "W25Q128FV"; - if (id[2] == 0x19) return "W25Q256FV"; - } - } - if (id[0] == 0x01) { - // Spansion - if (id[1] == 0x02) { - if (id[2] == 0x16) return "S25FL064A"; - if (id[2] == 0x19) return "S25FL256S"; - if (id[2] == 0x20) return "S25FL512S"; - } - if (id[1] == 0x20) { - if (id[2] == 0x18) return "S25FL127S"; - } - } - if (id[0] == 0xC2) { - // Macronix - if (id[1] == 0x20) { - if (id[2] == 0x18) return "MX25L12805D"; - } - } - if (id[0] == 0x20) { - // Micron - if (id[1] == 0xBA) { - if (id[2] == 0x20) return "N25Q512A"; - if (id[2] == 0x21) return "N25Q00AA"; - } - if (id[1] == 0xBB) { - if (id[2] == 0x22) return "MT25QL02GC"; - } - } - if (id[0] == 0xBF) { - // SST - if (id[1] == 0x25) { - if (id[2] == 0x02) return "SST25WF010"; - if (id[2] == 0x03) return "SST25WF020"; - if (id[2] == 0x04) return "SST25WF040"; - if (id[2] == 0x41) return "SST25VF016B"; - if (id[2] == 0x4A) return "SST25VF032"; - } - if (id[1] == 0x25) { - if (id[2] == 0x01) return "SST26VF016"; - if (id[2] == 0x02) return "SST26VF032"; - if (id[2] == 0x43) return "SST26VF064"; - } - } - return "(unknown chip)"; -} - -void print_signature(const unsigned char *data) -{ - Serial.print("data="); - for (unsigned char i=0; i < 8; i++) { - Serial.print(data[i]); - Serial.print(" "); - } - Serial.println(); -} - -void create_signature(unsigned long address, unsigned char *data) -{ - data[0] = address >> 24; - data[1] = address >> 16; - data[2] = address >> 8; - data[3] = address; - unsigned long hash = 2166136261ul; - for (unsigned char i=0; i < 4; i++) { - hash ^= data[i]; - hash *= 16777619ul; - } - data[4] = hash; - data[5] = hash >> 8; - data[6] = hash >> 16; - data[7] = hash >> 24; -} - -bool equal_signatures(const unsigned char *data1, const unsigned char *data2) -{ - for (unsigned char i=0; i < 8; i++) { - if (data1[i] != data2[i]) return false; - } - return true; -} - -bool is_erased(const unsigned char *data, unsigned int len) -{ - while (len > 0) { - if (*data++ != 255) return false; - len = len - 1; - } - return true; -} - - -void printbuf(const void *buf, uint32_t len) -{ - const uint8_t *p = (const uint8_t *)buf; - do { - unsigned char b = *p++; - Serial.print(b >> 4, HEX); - Serial.print(b & 15, HEX); - //Serial.printf("%02X", *p++); - Serial.print(" "); - } while (--len > 0); - Serial.println(); -} - diff --git a/libraries/SerialFlash/keywords.txt b/libraries/SerialFlash/keywords.txt deleted file mode 100644 index 1e473961..00000000 --- a/libraries/SerialFlash/keywords.txt +++ /dev/null @@ -1,9 +0,0 @@ -SerialFlash KEYWORD1 -SerialFlashFile KEYWORD1 -createWritable KEYWORD2 -erase KEYWORD2 -eraseAll KEYWORD2 -ready KEYWORD2 -create KEYWORD2 -createWritable KEYWORD2 -getAddress KEYWORD2 diff --git a/libraries/SerialFlash/util/SerialFlash_directwrite.h b/libraries/SerialFlash/util/SerialFlash_directwrite.h deleted file mode 100644 index d323a055..00000000 --- a/libraries/SerialFlash/util/SerialFlash_directwrite.h +++ /dev/null @@ -1,204 +0,0 @@ -#ifndef SerialFlash_directwrite_h -#define SerialFlash_directwrite_h - -#include - -// Adapted from OneWire.h - -#if ARDUINO >= 100 -#include "Arduino.h" // for delayMicroseconds, digitalPinToBitMask, etc -#else -#include "WProgram.h" // for delayMicroseconds -#include "pins_arduino.h" // for digitalPinToBitMask, etc -#endif - -// Platform specific I/O definitions - -#if defined(__AVR__) -#define PIN_TO_BASEREG(pin) (portInputRegister(digitalPinToPort(pin))) -#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) -#define IO_REG_TYPE uint8_t -#define IO_REG_ASM asm("r30") -#define DIRECT_READ(base, mask) (((*(base)) & (mask)) ? 1 : 0) -#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) &= ~(mask)) -#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+1)) |= (mask)) -#define DIRECT_WRITE_LOW(base, mask) ((*((base)+2)) &= ~(mask)) -#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+2)) |= (mask)) - -#elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__) -#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) -#define PIN_TO_BITMASK(pin) (1) -#define IO_REG_TYPE uint8_t -#define IO_REG_ASM -#define DIRECT_READ(base, mask) (*((base)+512)) -#define DIRECT_MODE_INPUT(base, mask) (*((base)+640) = 0) -#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+640) = 1) -#define DIRECT_WRITE_LOW(base, mask) (*((base)+256) = 1) -#define DIRECT_WRITE_HIGH(base, mask) (*((base)+128) = 1) - -#elif defined(__MKL26Z64__) -#define PIN_TO_BASEREG(pin) (portOutputRegister(pin)) -#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) -#define IO_REG_TYPE uint8_t -#define IO_REG_ASM -#define DIRECT_READ(base, mask) ((*((base)+16) & (mask)) ? 1 : 0) -#define DIRECT_MODE_INPUT(base, mask) (*((base)+20) &= ~(mask)) -#define DIRECT_MODE_OUTPUT(base, mask) (*((base)+20) |= (mask)) -#define DIRECT_WRITE_LOW(base, mask) (*((base)+8) = (mask)) -#define DIRECT_WRITE_HIGH(base, mask) (*((base)+4) = (mask)) - -#elif defined(__SAM3X8E__) -#define PIN_TO_BASEREG(pin) (&(digitalPinToPort(pin)->PIO_PER)) -#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) -#define IO_REG_TYPE uint32_t -#define IO_REG_ASM -#define DIRECT_READ(base, mask) (((*((base)+15)) & (mask)) ? 1 : 0) -#define DIRECT_MODE_INPUT(base, mask) ((*((base)+5)) = (mask)) -#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+4)) = (mask)) -#define DIRECT_WRITE_LOW(base, mask) ((*((base)+13)) = (mask)) -#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+12)) = (mask)) -#ifndef PROGMEM -#define PROGMEM -#endif -#ifndef pgm_read_byte -#define pgm_read_byte(addr) (*(const uint8_t *)(addr)) -#endif - -#elif defined(__PIC32MX__) -#define PIN_TO_BASEREG(pin) (portModeRegister(digitalPinToPort(pin))) -#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) -#define IO_REG_TYPE uint32_t -#define IO_REG_ASM -#define DIRECT_READ(base, mask) (((*(base+4)) & (mask)) ? 1 : 0) //PORTX + 0x10 -#define DIRECT_MODE_INPUT(base, mask) ((*(base+2)) = (mask)) //TRISXSET + 0x08 -#define DIRECT_MODE_OUTPUT(base, mask) ((*(base+1)) = (mask)) //TRISXCLR + 0x04 -#define DIRECT_WRITE_LOW(base, mask) ((*(base+8+1)) = (mask)) //LATXCLR + 0x24 -#define DIRECT_WRITE_HIGH(base, mask) ((*(base+8+2)) = (mask)) //LATXSET + 0x28 - -#elif defined(ARDUINO_ARCH_ESP8266) -#define PIN_TO_BASEREG(pin) ((volatile uint32_t*) GPO) -#define PIN_TO_BITMASK(pin) (1 << pin) -#define IO_REG_TYPE uint32_t -#define IO_REG_ASM -#define DIRECT_READ(base, mask) ((GPI & (mask)) ? 1 : 0) //GPIO_IN_ADDRESS -#define DIRECT_MODE_INPUT(base, mask) (GPE &= ~(mask)) //GPIO_ENABLE_W1TC_ADDRESS -#define DIRECT_MODE_OUTPUT(base, mask) (GPE |= (mask)) //GPIO_ENABLE_W1TS_ADDRESS -#define DIRECT_WRITE_LOW(base, mask) (GPOC = (mask)) //GPIO_OUT_W1TC_ADDRESS -#define DIRECT_WRITE_HIGH(base, mask) (GPOS = (mask)) //GPIO_OUT_W1TS_ADDRESS - -#elif defined(__SAMD21G18A__) -#define PIN_TO_BASEREG(pin) portModeRegister(digitalPinToPort(pin)) -#define PIN_TO_BITMASK(pin) (digitalPinToBitMask(pin)) -#define IO_REG_TYPE uint32_t -#define IO_REG_ASM -#define DIRECT_READ(base, mask) (((*((base)+8)) & (mask)) ? 1 : 0) -#define DIRECT_MODE_INPUT(base, mask) ((*((base)+1)) = (mask)) -#define DIRECT_MODE_OUTPUT(base, mask) ((*((base)+2)) = (mask)) -#define DIRECT_WRITE_LOW(base, mask) ((*((base)+5)) = (mask)) -#define DIRECT_WRITE_HIGH(base, mask) ((*((base)+6)) = (mask)) - -#elif defined(RBL_NRF51822) -#define PIN_TO_BASEREG(pin) (0) -#define PIN_TO_BITMASK(pin) (pin) -#define IO_REG_TYPE uint32_t -#define IO_REG_ASM -#define DIRECT_READ(base, pin) nrf_gpio_pin_read(pin) -#define DIRECT_WRITE_LOW(base, pin) nrf_gpio_pin_clear(pin) -#define DIRECT_WRITE_HIGH(base, pin) nrf_gpio_pin_set(pin) -#define DIRECT_MODE_INPUT(base, pin) nrf_gpio_cfg_input(pin, NRF_GPIO_PIN_NOPULL) -#define DIRECT_MODE_OUTPUT(base, pin) nrf_gpio_cfg_output(pin) - -#elif defined(__arc__) /* Arduino101/Genuino101 specifics */ - -#include "scss_registers.h" -#include "portable.h" -#include "avr/pgmspace.h" - -#define GPIO_ID(pin) (g_APinDescription[pin].ulGPIOId) -#define GPIO_TYPE(pin) (g_APinDescription[pin].ulGPIOType) -#define GPIO_BASE(pin) (g_APinDescription[pin].ulGPIOBase) -#define DIR_OFFSET_SS 0x01 -#define DIR_OFFSET_SOC 0x04 -#define EXT_PORT_OFFSET_SS 0x0A -#define EXT_PORT_OFFSET_SOC 0x50 - -/* GPIO registers base address */ -#define PIN_TO_BASEREG(pin) ((volatile uint32_t *)g_APinDescription[pin].ulGPIOBase) -#define PIN_TO_BITMASK(pin) pin -#define IO_REG_TYPE uint32_t -#define IO_REG_ASM - -static inline __attribute__((always_inline)) -IO_REG_TYPE directRead(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) -{ - IO_REG_TYPE ret; - if (SS_GPIO == GPIO_TYPE(pin)) { - ret = READ_ARC_REG(((IO_REG_TYPE)base + EXT_PORT_OFFSET_SS)); - } else { - ret = MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, EXT_PORT_OFFSET_SOC); - } - return ((ret >> GPIO_ID(pin)) & 0x01); -} - -static inline __attribute__((always_inline)) -void directModeInput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) -{ - if (SS_GPIO == GPIO_TYPE(pin)) { - WRITE_ARC_REG(READ_ARC_REG((((IO_REG_TYPE)base) + DIR_OFFSET_SS)) & ~(0x01 << GPIO_ID(pin)), - ((IO_REG_TYPE)(base) + DIR_OFFSET_SS)); - } else { - MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) &= ~(0x01 << GPIO_ID(pin)); - } -} - -static inline __attribute__((always_inline)) -void directModeOutput(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) -{ - if (SS_GPIO == GPIO_TYPE(pin)) { - WRITE_ARC_REG(READ_ARC_REG(((IO_REG_TYPE)(base) + DIR_OFFSET_SS)) | (0x01 << GPIO_ID(pin)), - ((IO_REG_TYPE)(base) + DIR_OFFSET_SS)); - } else { - MMIO_REG_VAL_FROM_BASE((IO_REG_TYPE)base, DIR_OFFSET_SOC) |= (0x01 << GPIO_ID(pin)); - } -} - -static inline __attribute__((always_inline)) -void directWriteLow(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) -{ - if (SS_GPIO == GPIO_TYPE(pin)) { - WRITE_ARC_REG(READ_ARC_REG(base) & ~(0x01 << GPIO_ID(pin)), base); - } else { - MMIO_REG_VAL(base) &= ~(0x01 << GPIO_ID(pin)); - } -} - -static inline __attribute__((always_inline)) -void directWriteHigh(volatile IO_REG_TYPE *base, IO_REG_TYPE pin) -{ - if (SS_GPIO == GPIO_TYPE(pin)) { - WRITE_ARC_REG(READ_ARC_REG(base) | (0x01 << GPIO_ID(pin)), base); - } else { - MMIO_REG_VAL(base) |= (0x01 << GPIO_ID(pin)); - } -} - -#define DIRECT_READ(base, pin) directRead(base, pin) -#define DIRECT_MODE_INPUT(base, pin) directModeInput(base, pin) -#define DIRECT_MODE_OUTPUT(base, pin) directModeOutput(base, pin) -#define DIRECT_WRITE_LOW(base, pin) directWriteLow(base, pin) -#define DIRECT_WRITE_HIGH(base, pin) directWriteHigh(base, pin) - -#else -#define PIN_TO_BASEREG(pin) (0) -#define PIN_TO_BITMASK(pin) (pin) -#define IO_REG_TYPE unsigned int -#define IO_REG_ASM -#define DIRECT_READ(base, pin) digitalRead(pin) -#define DIRECT_WRITE_LOW(base, pin) digitalWrite(pin, LOW) -#define DIRECT_WRITE_HIGH(base, pin) digitalWrite(pin, HIGH) -#define DIRECT_MODE_INPUT(base, pin) pinMode(pin,INPUT) -#define DIRECT_MODE_OUTPUT(base, pin) pinMode(pin,OUTPUT) - -#endif - -#endif From 1c643fb0f00ab5b42de55cdf9a8f835f53da930c Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 26 Sep 2016 12:39:31 +0200 Subject: [PATCH 061/125] Add CurieSerialFlash wrapper over SerialFlash library The examples declare the correct CS pin and use the overloaded begin() method, passing SPI1 as port The link in the last line of the header opens the library manager to allow easy import of the father library --- libraries/CurieSerialFlash/CurieSerialFlash.h | 4 + .../CopyFromSerial/CopyFromSerial.ino | 268 ++++++++++ .../EraseEverything/EraseEverything.ino | 80 +++ .../examples/FileWrite/FileWrite.ino | 50 ++ .../examples/ListFiles/ListFiles.ino | 63 +++ .../RawHardwareTest/RawHardwareTest.ino | 504 ++++++++++++++++++ 6 files changed, 969 insertions(+) create mode 100644 libraries/CurieSerialFlash/CurieSerialFlash.h create mode 100644 libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino create mode 100644 libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino create mode 100755 libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino create mode 100644 libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino create mode 100644 libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino diff --git a/libraries/CurieSerialFlash/CurieSerialFlash.h b/libraries/CurieSerialFlash/CurieSerialFlash.h new file mode 100644 index 00000000..47ce73d9 --- /dev/null +++ b/libraries/CurieSerialFlash/CurieSerialFlash.h @@ -0,0 +1,4 @@ +#if !defined (__arc__) +#error These examples only work with onboard SPI flash on Arduino/Genuino 101 board +#endif +#include "SerialFlash.h" diff --git a/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino b/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino new file mode 100644 index 00000000..3f3a9e4d --- /dev/null +++ b/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino @@ -0,0 +1,268 @@ +/* + * This is free and unencumbered software released into the public domain. + * + * Anyone is free to copy, modify, publish, use, compile, sell, or + * distribute this software, either in source code form or as a compiled + * binary, for any purpose, commercial or non-commercial, and by any + * means. + * + * In jurisdictions that recognize copyright laws, the author or authors + * of this software dedicate any and all copyright interest in the + * software to the public domain. We make this dedication for the benefit + * of the public at large and to the detriment of our heirs and + * successors. We intend this dedication to be an overt act of + * relinquishment in perpetuity of all present and future rights to this + * software under copyright law. + * + * 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 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. + * + * For more information, please refer to + * ------------------------------------------------------------------------- + * + * This is example code to 1) format an SPI Flash chip, and 2) copy raw + * audio files (mono channel, 16 bit signed, 44100Hz) to it using the + * SerialFlash library. The audio can then be played back using the + * AudioPlaySerialflashRaw object in the Teensy Audio library. + * + * To convert a .wav file to the proper .RAW format, use sox: + * sox input.wav -r 44100 -b 16 --norm -e signed-integer -t raw OUTPUT.RAW remix 1,2 + * + * Note that the OUTPUT.RAW filename must be all caps and contain only the following + * characters: A-Z, 0-9, comma, period, colon, dash, underscore. (The SerialFlash + * library converts filenames to caps, so to avoid confusion we just enforce it here). + * + * It is a little difficult to see what is happening; aswe are using the Serial port + * to upload files, we can't just throw out debug information. Instead, we use the LED + * (pin 13) to convey state. + * + * While the chip is being formatted, the LED (pin 13) will toggle at 1Hz rate. When + * the formatting is done, it flashes quickly (10Hz) for one second, then stays on + * solid. When nothing has been received for 3 seconds, the upload is assumed to be + * completed, and the light goes off. + * + * Use the 'rawfile-uploader.py' python script (included in the extras folder) to upload + * the files. You can start the script as soon as the Teensy is turned on, and the + * USB serial upload will just buffer and wait until the flash is formatted. + * + * This code was written by Wyatt Olson (originally as part + * of Drum Master http://drummaster.digitalcave.ca and later modified into a + * standalone sample). + * + * Enjoy! + * + * SerialFlash library API: https://github.com/PaulStoffregen/SerialFlash + * + * This example depens on http://librarymanager/all#SerialFlash&SPI (clickme!) + */ + +#include +#include + +//Buffer sizes +#define USB_BUFFER_SIZE 128 +#define FLASH_BUFFER_SIZE 4096 + +//Max filename length (8.3 plus a null char terminator) +#define FILENAME_STRING_SIZE 13 + +//State machine +#define STATE_START 0 +#define STATE_SIZE 1 +#define STATE_CONTENT 2 + +//Special bytes in the communication protocol +#define BYTE_START 0x7e +#define BYTE_ESCAPE 0x7d +#define BYTE_SEPARATOR 0x7c + +#define CSPIN 21 + +void setup(){ + Serial.begin(9600); //Teensy serial is always at full USB speed and buffered... the baud rate here is required but ignored + + pinMode(13, OUTPUT); + + SerialFlash.begin(SPI1, CSPIN); + + //We start by formatting the flash... + uint8_t id[5]; + SerialFlash.readID(id); + SerialFlash.eraseAll(); + + //Flash LED at 1Hz while formatting + while (!SerialFlash.ready()) { + delay(500); + digitalWrite(13, HIGH); + delay(500); + digitalWrite(13, LOW); + } + + //Quickly flash LED a few times when completed, then leave the light on solid + for(uint8_t i = 0; i < 10; i++){ + delay(100); + digitalWrite(13, HIGH); + delay(100); + digitalWrite(13, LOW); + } + digitalWrite(13, HIGH); + + //We are now going to wait for the upload program + while(!Serial.available()); + + SerialFlashFile flashFile; + + uint8_t state = STATE_START; + uint8_t escape = 0; + uint8_t fileSizeIndex = 0; + uint32_t fileSize = 0; + char filename[FILENAME_STRING_SIZE]; + + char usbBuffer[USB_BUFFER_SIZE]; + uint8_t flashBuffer[FLASH_BUFFER_SIZE]; + + uint16_t flashBufferIndex = 0; + uint8_t filenameIndex = 0; + + uint32_t lastReceiveTime = millis(); + + //We assume the serial receive part is finished when we have not received something for 3 seconds + while(Serial.available() || lastReceiveTime + 3000 > millis()){ + uint16_t available = Serial.readBytes(usbBuffer, USB_BUFFER_SIZE); + if (available){ + lastReceiveTime = millis(); + } + + for (uint16_t usbBufferIndex = 0; usbBufferIndex < available; usbBufferIndex++){ + uint8_t b = usbBuffer[usbBufferIndex]; + + if (state == STATE_START){ + //Start byte. Repeat start is fine. + if (b == BYTE_START){ + for (uint8_t i = 0; i < FILENAME_STRING_SIZE; i++){ + filename[i] = 0x00; + } + filenameIndex = 0; + } + //Valid characters are A-Z, 0-9, comma, period, colon, dash, underscore + else if ((b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '.' || b == ',' || b == ':' || b == '-' || b == '_'){ + filename[filenameIndex++] = b; + if (filenameIndex >= FILENAME_STRING_SIZE){ + //Error name too long + flushError(); + return; + } + } + //Filename end character + else if (b == BYTE_SEPARATOR){ + if (filenameIndex == 0){ + //Error empty filename + flushError(); + return; + } + + //Change state + state = STATE_SIZE; + fileSizeIndex = 0; + fileSize = 0; + + } + //Invalid character + else { + //Error bad filename + flushError(); + return; + } + } + //We read 4 bytes as a uint32_t for file size + else if (state == STATE_SIZE){ + if (fileSizeIndex < 4){ + fileSize = (fileSize << 8) + b; + fileSizeIndex++; + } + else if (b == BYTE_SEPARATOR){ + state = STATE_CONTENT; + flashBufferIndex = 0; + escape = 0; + + if (SerialFlash.exists(filename)){ + SerialFlash.remove(filename); //It doesn't reclaim the space, but it does let you create a new file with the same name. + } + + //Create a new file and open it for writing + if (SerialFlash.create(filename, fileSize)) { + flashFile = SerialFlash.open(filename); + if (!flashFile) { + //Error flash file open + flushError(); + return; + } + } + else { + //Error flash create (no room left?) + flushError(); + return; + } + } + else { + //Error invalid length requested + flushError(); + return; + } + } + else if (state == STATE_CONTENT){ + //Previous byte was escaped; unescape and add to buffer + if (escape){ + escape = 0; + flashBuffer[flashBufferIndex++] = b ^ 0x20; + } + //Escape the next byte + else if (b == BYTE_ESCAPE){ + //Serial.println("esc"); + escape = 1; + } + //End of file + else if (b == BYTE_START){ + //Serial.println("End of file"); + state = STATE_START; + flashFile.write(flashBuffer, flashBufferIndex); + flashFile.close(); + flashBufferIndex = 0; + } + //Normal byte; add to buffer + else { + flashBuffer[flashBufferIndex++] = b; + } + + //The buffer is filled; write to SD card + if (flashBufferIndex >= FLASH_BUFFER_SIZE){ + flashFile.write(flashBuffer, FLASH_BUFFER_SIZE); + flashBufferIndex = 0; + } + } + } + } + + //Success! Turn the light off. + digitalWrite(13, LOW); +} + +void loop(){ + //Do nothing. +} + +void flushError(){ + uint32_t lastReceiveTime = millis(); + char usbBuffer[USB_BUFFER_SIZE]; + //We assume the serial receive part is finished when we have not received something for 3 seconds + while(Serial.available() || lastReceiveTime + 3000 > millis()){ + if (Serial.readBytes(usbBuffer, USB_BUFFER_SIZE)){ + lastReceiveTime = millis(); + } + } +} diff --git a/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino b/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino new file mode 100644 index 00000000..b6d1ebc7 --- /dev/null +++ b/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino @@ -0,0 +1,80 @@ +// This example depens on http://librarymanager/all#SerialFlash&SPI (clickme!) + +#include +#include + +const int FlashChipSelect = 21; // digital pin for flash chip CS pin + +SerialFlashFile file; + +const unsigned long testIncrement = 4096; + +void setup() { + //uncomment these if using Teensy audio shield + //SPI.setSCK(14); // Audio shield has SCK on pin 14 + //SPI.setMOSI(7); // Audio shield has MOSI on pin 7 + + //uncomment these if you have other SPI chips connected + //to keep them disabled while using only SerialFlash + //pinMode(4, INPUT_PULLUP); + //pinMode(10, INPUT_PULLUP); + + Serial.begin(9600); + + // wait up to 10 seconds for Arduino Serial Monitor + unsigned long startMillis = millis(); + while (!Serial && (millis() - startMillis < 10000)) ; + delay(100); + + SerialFlash.begin(SPI1, FlashChipSelect); + unsigned char id[5]; + SerialFlash.readID(id); + unsigned long size = SerialFlash.capacity(id); + + if (size > 0) { + Serial.print("Flash Memory has "); + Serial.print(size); + Serial.println(" bytes."); + Serial.println("Erasing ALL Flash Memory:"); + // Estimate the (lengthy) wait time. + Serial.print(" estimated wait: "); + int seconds = (float)size / eraseBytesPerSecond(id) + 0.5; + Serial.print(seconds); + Serial.println(" seconds."); + Serial.println(" Yes, full chip erase is SLOW!"); + SerialFlash.eraseAll(); + unsigned long dotMillis = millis(); + unsigned char dotcount = 0; + while (SerialFlash.ready() == false) { + if (millis() - dotMillis > 1000) { + dotMillis = dotMillis + 1000; + Serial.print("."); + dotcount = dotcount + 1; + if (dotcount >= 60) { + Serial.println(); + dotcount = 0; + } + } + } + if (dotcount > 0) Serial.println(); + Serial.println("Erase completed"); + unsigned long elapsed = millis() - startMillis; + Serial.print(" actual wait: "); + Serial.print(elapsed / 1000ul); + Serial.println(" seconds."); + } +} + +float eraseBytesPerSecond(const unsigned char *id) { + if (id[0] == 0x20) return 152000.0; // Micron + if (id[0] == 0x01) return 500000.0; // Spansion + if (id[0] == 0xEF) return 419430.0; // Winbond + if (id[0] == 0xC2) return 279620.0; // Macronix + return 320000.0; // guess? +} + + +void loop() { + +} + diff --git a/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino b/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino new file mode 100755 index 00000000..d87438b2 --- /dev/null +++ b/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino @@ -0,0 +1,50 @@ +// This example depens on http://librarymanager/all#SerialFlash&SPI (clickme!) + +#include +#include + +#define FSIZE 256 + +const char *filename = "myfile.txt"; +const char *contents = "0123456789ABCDEF"; + +const int FlashChipSelect = 21; // digital pin for flash chip CS pin + +void setup() { + Serial.begin(9600); + + // wait for Arduino Serial Monitor + while (!Serial) ; + delay(100); + + // Init. SPI Flash chip + if (!SerialFlash.begin(SPI1, FlashChipSelect)) { + Serial.println("Unable to access SPI Flash chip"); + } + + SerialFlashFile file; + + // Create the file if it doesn't exist + if (!create_if_not_exists(filename)) { + Serial.println("Not enough space to create file " + String(filename)); + return; + } + + // Open the file and write test data + file = SerialFlash.open(filename); + file.write(contents, strlen(contents) + 1); + Serial.println("String \"" + String(contents) + "\" written to file " + String(filename)); +} + +bool create_if_not_exists (const char *filename) { + if (!SerialFlash.exists(filename)) { + Serial.println("Creating file " + String(filename)); + return SerialFlash.create(filename, FSIZE); + } + + Serial.println("File " + String(filename) + " already exists"); + return true; +} + +void loop() { +} diff --git a/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino b/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino new file mode 100644 index 00000000..9822aafe --- /dev/null +++ b/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino @@ -0,0 +1,63 @@ +// This example depens on http://librarymanager/all#SerialFlash&SPI (clickme!) + +#include +#include + +const int FlashChipSelect = 21; // digital pin for flash chip CS pin + +void setup() { + //uncomment these if using Teensy audio shield + //SPI.setSCK(14); // Audio shield has SCK on pin 14 + //SPI.setMOSI(7); // Audio shield has MOSI on pin 7 + + //uncomment these if you have other SPI chips connected + //to keep them disabled while using only SerialFlash + //pinMode(4, INPUT_PULLUP); + //pinMode(10, INPUT_PULLUP); + + Serial.begin(9600); + + // wait for Arduino Serial Monitor + while (!Serial) ; + delay(100); + Serial.println("All Files on SPI Flash chip:"); + + if (!SerialFlash.begin(SPI1, FlashChipSelect)) { + error("Unable to access SPI Flash chip"); + } + + SerialFlash.opendir(); + unsigned int count = 0; + while (1) { + char filename[64]; + unsigned long filesize; + + if (SerialFlash.readdir(filename, sizeof(filename), filesize)) { + Serial.print(" "); + Serial.print(filename); + spaces(20 - strlen(filename)); + Serial.print(" "); + Serial.print(filesize); + Serial.print(" bytes"); + Serial.println(); + } else { + break; // no more files + } + } +} + +void spaces(int num) { + for (int i=0; i < num; i++) { + Serial.print(" "); + } +} + +void loop() { +} + +void error(const char *message) { + while (1) { + Serial.println(message); + delay(2500); + } +} diff --git a/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino b/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino new file mode 100644 index 00000000..f912c0a3 --- /dev/null +++ b/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino @@ -0,0 +1,504 @@ +// SerialFlash library API: https://github.com/PaulStoffregen/SerialFlash +// +// RawHardwareTest - Check if a SPI Flash chip is compatible +// with SerialFlash by performing many read and write tests +// to its memory. +// +// The chip should be fully erased before running this test. +// Use the EraseEverything to do a (slow) full chip erase. +// +// Normally you should NOT access the flash memory directly, +// as this test program does. You should create files and +// read and write the files. File creation allocates space +// with program & erase boundaries within the chip, to allow +// reading from any other files while a file is busy writing +// or erasing (if created as erasable). +// +// If you discover an incompatible chip, please report it here: +// https://github.com/PaulStoffregen/SerialFlash/issues +// You MUST post the complete output of this program, and +// the exact part number and manufacturer of the chip. +// +// This example depens on http://librarymanager/all#SerialFlash&SPI (clickme!) + + +#include +#include + +const int FlashChipSelect = 21; // digital pin for flash chip CS pin + +SerialFlashFile file; + +const unsigned long testIncrement = 4096; + +void setup() { + + //uncomment these if using Teensy audio shield + //SPI.setSCK(14); // Audio shield has SCK on pin 14 + //SPI.setMOSI(7); // Audio shield has MOSI on pin 7 + + //uncomment these if you have other SPI chips connected + //to keep them disabled while using only SerialFlash + //pinMode(4, INPUT_PULLUP); + //pinMode(10, INPUT_PULLUP); + + Serial.begin(9600); + + while (!Serial) ; + delay(100); + + Serial.println("Raw SerialFlash Hardware Test"); + SerialFlash.begin(SPI1, FlashChipSelect); + + if (test()) { + Serial.println(); + Serial.println("All Tests Passed :-)"); + Serial.println(); + Serial.println("Test data was written to your chip. You must run"); + Serial.println("EraseEverything before using this chip for files."); + } else { + Serial.println(); + Serial.println("Tests Failed :{"); + Serial.println(); + Serial.println("The flash chip may be left in an improper state."); + Serial.println("You might need to power cycle to return to normal."); + } +} + + +bool test() { + unsigned char buf[256], sig[256], buf2[8]; + unsigned long address, count, chipsize, blocksize; + unsigned long usec; + bool first; + + // Read the chip identification + Serial.println(); + Serial.println("Read Chip Identification:"); + SerialFlash.readID(buf); + Serial.print(" JEDEC ID: "); + Serial.print(buf[0], HEX); + Serial.print(" "); + Serial.print(buf[1], HEX); + Serial.print(" "); + Serial.println(buf[2], HEX); + Serial.print(" Part Nummber: "); + Serial.println(id2chip(buf)); + Serial.print(" Memory Size: "); + chipsize = SerialFlash.capacity(buf); + Serial.print(chipsize); + Serial.println(" bytes"); + if (chipsize == 0) return false; + Serial.print(" Block Size: "); + blocksize = SerialFlash.blockSize(); + Serial.print(blocksize); + Serial.println(" bytes"); + + + // Read the entire chip. Every test location must be + // erased, or have a previously tested signature + Serial.println(); + Serial.println("Reading Chip..."); + memset(buf, 0, sizeof(buf)); + memset(sig, 0, sizeof(sig)); + memset(buf2, 0, sizeof(buf2)); + address = 0; + count = 0; + first = true; + while (address < chipsize) { + SerialFlash.read(address, buf, 8); + //Serial.print(" addr = "); + //Serial.print(address, HEX); + //Serial.print(", data = "); + //printbuf(buf, 8); + create_signature(address, sig); + if (is_erased(buf, 8) == false) { + if (equal_signatures(buf, sig) == false) { + Serial.print(" Previous data found at address "); + Serial.println(address); + Serial.println(" You must fully erase the chip before this test"); + Serial.print(" found this: "); + printbuf(buf, 8); + Serial.print(" correct: "); + printbuf(sig, 8); + return false; + } + } else { + count = count + 1; // number of blank signatures + } + if (first) { + address = address + (testIncrement - 8); + first = false; + } else { + address = address + 8; + first = true; + } + } + + + // Write any signatures that were blank on the original check + if (count > 0) { + Serial.println(); + Serial.print("Writing "); + Serial.print(count); + Serial.println(" signatures"); + memset(buf, 0, sizeof(buf)); + memset(sig, 0, sizeof(sig)); + memset(buf2, 0, sizeof(buf2)); + address = 0; + first = true; + while (address < chipsize) { + SerialFlash.read(address, buf, 8); + if (is_erased(buf, 8)) { + create_signature(address, sig); + //Serial.printf("write %08X: data: ", address); + //printbuf(sig, 8); + SerialFlash.write(address, sig, 8); + while (!SerialFlash.ready()) ; // wait + SerialFlash.read(address, buf, 8); + if (equal_signatures(buf, sig) == false) { + Serial.print(" error writing signature at "); + Serial.println(address); + Serial.print(" Read this: "); + printbuf(buf, 8); + Serial.print(" Expected: "); + printbuf(sig, 8); + return false; + } + } + if (first) { + address = address + (testIncrement - 8); + first = false; + } else { + address = address + 8; + first = true; + } + } + } else { + Serial.println(" all signatures present from prior tests"); + } + + + // Read all the signatures again, just to be sure + // checks prior writing didn't corrupt any other data + Serial.println(); + Serial.println("Double Checking All Signatures:"); + memset(buf, 0, sizeof(buf)); + memset(sig, 0, sizeof(sig)); + memset(buf2, 0, sizeof(buf2)); + count = 0; + address = 0; + first = true; + while (address < chipsize) { + SerialFlash.read(address, buf, 8); + create_signature(address, sig); + if (equal_signatures(buf, sig) == false) { + Serial.print(" error in signature at "); + Serial.println(address); + Serial.print(" Read this: "); + printbuf(buf, 8); + Serial.print(" Expected: "); + printbuf(sig, 8); + return false; + } + count = count + 1; + if (first) { + address = address + (testIncrement - 8); + first = false; + } else { + address = address + 8; + first = true; + } + } + Serial.print(" all "); + Serial.print(count); + Serial.println(" signatures read ok"); + + + // Read pairs of adjacent signatures + // check read works across boundaries + Serial.println(); + Serial.println("Checking Signature Pairs"); + memset(buf, 0, sizeof(buf)); + memset(sig, 0, sizeof(sig)); + memset(buf2, 0, sizeof(buf2)); + count = 0; + address = testIncrement - 8; + first = true; + while (address < chipsize - 8) { + SerialFlash.read(address, buf, 16); + create_signature(address, sig); + create_signature(address + 8, sig + 8); + if (memcmp(buf, sig, 16) != 0) { + Serial.print(" error in signature pair at "); + Serial.println(address); + Serial.print(" Read this: "); + printbuf(buf, 16); + Serial.print(" Expected: "); + printbuf(sig, 16); + return false; + } + count = count + 1; + address = address + testIncrement; + } + Serial.print(" all "); + Serial.print(count); + Serial.println(" signature pairs read ok"); + + + // Write data and read while write in progress + Serial.println(); + Serial.println("Checking Read-While-Write (Program Suspend)"); + address = 256; + while (address < chipsize) { // find a blank space + SerialFlash.read(address, buf, 256); + if (is_erased(buf, 256)) break; + address = address + 256; + } + if (address >= chipsize) { + Serial.println(" error, unable to find any blank space!"); + return false; + } + for (int i=0; i < 256; i += 8) { + create_signature(address + i, sig + i); + } + Serial.print(" write 256 bytes at "); + Serial.println(address); + Serial.flush(); + SerialFlash.write(address, sig, 256); + usec = micros(); + if (SerialFlash.ready()) { + Serial.println(" error, chip did not become busy after write"); + return false; + } + SerialFlash.read(0, buf2, 8); // read while busy writing + while (!SerialFlash.ready()) ; // wait + usec = micros() - usec; + Serial.print(" write time was "); + Serial.print(usec); + Serial.println(" microseconds."); + SerialFlash.read(address, buf, 256); + if (memcmp(buf, sig, 256) != 0) { + Serial.println(" error writing to flash"); + Serial.print(" Read this: "); + printbuf(buf, 256); + Serial.print(" Expected: "); + printbuf(sig, 256); + return false; + } + create_signature(0, sig); + if (memcmp(buf2, sig, 8) != 0) { + Serial.println(" error, incorrect read while writing"); + Serial.print(" Read this: "); + printbuf(buf2, 256); + Serial.print(" Expected: "); + printbuf(sig, 256); + return false; + } + Serial.print(" read-while-writing: "); + printbuf(buf2, 8); + Serial.println(" test passed, good read while writing"); + + + + // Erase a block and read while erase in progress + if (chipsize >= 262144 + blocksize + testIncrement) { + Serial.println(); + Serial.println("Checking Read-While-Erase (Erase Suspend)"); + memset(buf, 0, sizeof(buf)); + memset(sig, 0, sizeof(sig)); + memset(buf2, 0, sizeof(buf2)); + SerialFlash.eraseBlock(262144); + usec = micros(); + delayMicroseconds(50); + if (SerialFlash.ready()) { + Serial.println(" error, chip did not become busy after erase"); + return false; + } + SerialFlash.read(0, buf2, 8); // read while busy writing + while (!SerialFlash.ready()) ; // wait + usec = micros() - usec; + Serial.print(" erase time was "); + Serial.print(usec); + Serial.println(" microseconds."); + // read all signatures, check ones in this block got + // erased, and all the others are still intact + address = 0; + first = true; + while (address < chipsize) { + SerialFlash.read(address, buf, 8); + if (address >= 262144 && address < 262144 + blocksize) { + if (is_erased(buf, 8) == false) { + Serial.print(" error in erasing at "); + Serial.println(address); + Serial.print(" Read this: "); + printbuf(buf, 8); + return false; + } + } else { + create_signature(address, sig); + if (equal_signatures(buf, sig) == false) { + Serial.print(" error in signature at "); + Serial.println(address); + Serial.print(" Read this: "); + printbuf(buf, 8); + Serial.print(" Expected: "); + printbuf(sig, 8); + return false; + } + } + if (first) { + address = address + (testIncrement - 8); + first = false; + } else { + address = address + 8; + first = true; + } + } + Serial.print(" erase correctly erased "); + Serial.print(blocksize); + Serial.println(" bytes"); + // now check if the data we read during erase is good + create_signature(0, sig); + if (memcmp(buf2, sig, 8) != 0) { + Serial.println(" error, incorrect read while erasing"); + Serial.print(" Read this: "); + printbuf(buf2, 256); + Serial.print(" Expected: "); + printbuf(sig, 256); + return false; + } + Serial.print(" read-while-erasing: "); + printbuf(buf2, 8); + Serial.println(" test passed, good read while erasing"); + + } else { + Serial.println("Skip Read-While-Erase, this chip is too small"); + } + + + + + return true; +} + + +void loop() { + // do nothing after the test +} + +const char * id2chip(const unsigned char *id) +{ + if (id[0] == 0xEF) { + // Winbond + if (id[1] == 0x40) { + if (id[2] == 0x14) return "W25Q80BV"; + if (id[2] == 0x15) return "W25Q16DV"; + if (id[2] == 0x17) return "W25Q64FV"; + if (id[2] == 0x18) return "W25Q128FV"; + if (id[2] == 0x19) return "W25Q256FV"; + } + } + if (id[0] == 0x01) { + // Spansion + if (id[1] == 0x02) { + if (id[2] == 0x16) return "S25FL064A"; + if (id[2] == 0x19) return "S25FL256S"; + if (id[2] == 0x20) return "S25FL512S"; + } + if (id[1] == 0x20) { + if (id[2] == 0x18) return "S25FL127S"; + } + } + if (id[0] == 0xC2) { + // Macronix + if (id[1] == 0x20) { + if (id[2] == 0x18) return "MX25L12805D"; + } + } + if (id[0] == 0x20) { + // Micron + if (id[1] == 0xBA) { + if (id[2] == 0x20) return "N25Q512A"; + if (id[2] == 0x21) return "N25Q00AA"; + } + if (id[1] == 0xBB) { + if (id[2] == 0x22) return "MT25QL02GC"; + } + } + if (id[0] == 0xBF) { + // SST + if (id[1] == 0x25) { + if (id[2] == 0x02) return "SST25WF010"; + if (id[2] == 0x03) return "SST25WF020"; + if (id[2] == 0x04) return "SST25WF040"; + if (id[2] == 0x41) return "SST25VF016B"; + if (id[2] == 0x4A) return "SST25VF032"; + } + if (id[1] == 0x25) { + if (id[2] == 0x01) return "SST26VF016"; + if (id[2] == 0x02) return "SST26VF032"; + if (id[2] == 0x43) return "SST26VF064"; + } + } + return "(unknown chip)"; +} + +void print_signature(const unsigned char *data) +{ + Serial.print("data="); + for (unsigned char i=0; i < 8; i++) { + Serial.print(data[i]); + Serial.print(" "); + } + Serial.println(); +} + +void create_signature(unsigned long address, unsigned char *data) +{ + data[0] = address >> 24; + data[1] = address >> 16; + data[2] = address >> 8; + data[3] = address; + unsigned long hash = 2166136261ul; + for (unsigned char i=0; i < 4; i++) { + hash ^= data[i]; + hash *= 16777619ul; + } + data[4] = hash; + data[5] = hash >> 8; + data[6] = hash >> 16; + data[7] = hash >> 24; +} + +bool equal_signatures(const unsigned char *data1, const unsigned char *data2) +{ + for (unsigned char i=0; i < 8; i++) { + if (data1[i] != data2[i]) return false; + } + return true; +} + +bool is_erased(const unsigned char *data, unsigned int len) +{ + while (len > 0) { + if (*data++ != 255) return false; + len = len - 1; + } + return true; +} + + +void printbuf(const void *buf, uint32_t len) +{ + const uint8_t *p = (const uint8_t *)buf; + do { + unsigned char b = *p++; + Serial.print(b >> 4, HEX); + Serial.print(b & 15, HEX); + //Serial.printf("%02X", *p++); + Serial.print(" "); + } while (--len > 0); + Serial.println(); +} + From 057b9a1271f3ccdfc5b8ef5144f754e75a30650e Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 4 Oct 2016 15:05:39 +0200 Subject: [PATCH 062/125] Make onboard flash port and cs pin explicit --- .../examples/CopyFromSerial/CopyFromSerial.ino | 4 +--- .../examples/EraseEverything/EraseEverything.ino | 4 +--- libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino | 4 +--- libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino | 4 +--- .../examples/RawHardwareTest/RawHardwareTest.ino | 4 +--- variants/arduino_101/variant.h | 6 ++++++ 6 files changed, 11 insertions(+), 15 deletions(-) diff --git a/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino b/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino index 3f3a9e4d..85723894 100644 --- a/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino +++ b/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino @@ -81,14 +81,12 @@ #define BYTE_ESCAPE 0x7d #define BYTE_SEPARATOR 0x7c -#define CSPIN 21 - void setup(){ Serial.begin(9600); //Teensy serial is always at full USB speed and buffered... the baud rate here is required but ignored pinMode(13, OUTPUT); - SerialFlash.begin(SPI1, CSPIN); + SerialFlash.begin(ONBOARD_FLASH_SPI_PORT, ONBOARD_FLASH_CS_PIN); //We start by formatting the flash... uint8_t id[5]; diff --git a/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino b/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino index b6d1ebc7..c79bf5fa 100644 --- a/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino +++ b/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino @@ -3,8 +3,6 @@ #include #include -const int FlashChipSelect = 21; // digital pin for flash chip CS pin - SerialFlashFile file; const unsigned long testIncrement = 4096; @@ -26,7 +24,7 @@ void setup() { while (!Serial && (millis() - startMillis < 10000)) ; delay(100); - SerialFlash.begin(SPI1, FlashChipSelect); + SerialFlash.begin(ONBOARD_FLASH_SPI_PORT, ONBOARD_FLASH_CS_PIN); unsigned char id[5]; SerialFlash.readID(id); unsigned long size = SerialFlash.capacity(id); diff --git a/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino b/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino index d87438b2..3e10bc84 100755 --- a/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino +++ b/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino @@ -8,8 +8,6 @@ const char *filename = "myfile.txt"; const char *contents = "0123456789ABCDEF"; -const int FlashChipSelect = 21; // digital pin for flash chip CS pin - void setup() { Serial.begin(9600); @@ -18,7 +16,7 @@ void setup() { delay(100); // Init. SPI Flash chip - if (!SerialFlash.begin(SPI1, FlashChipSelect)) { + if (!SerialFlash.begin(ONBOARD_FLASH_SPI_PORT, ONBOARD_FLASH_CS_PIN)) { Serial.println("Unable to access SPI Flash chip"); } diff --git a/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino b/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino index 9822aafe..21888537 100644 --- a/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino +++ b/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino @@ -3,8 +3,6 @@ #include #include -const int FlashChipSelect = 21; // digital pin for flash chip CS pin - void setup() { //uncomment these if using Teensy audio shield //SPI.setSCK(14); // Audio shield has SCK on pin 14 @@ -22,7 +20,7 @@ void setup() { delay(100); Serial.println("All Files on SPI Flash chip:"); - if (!SerialFlash.begin(SPI1, FlashChipSelect)) { + if (!SerialFlash.begin(ONBOARD_FLASH_SPI_PORT, ONBOARD_FLASH_CS_PIN)) { error("Unable to access SPI Flash chip"); } diff --git a/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino b/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino index f912c0a3..453bb123 100644 --- a/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino +++ b/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino @@ -25,8 +25,6 @@ #include #include -const int FlashChipSelect = 21; // digital pin for flash chip CS pin - SerialFlashFile file; const unsigned long testIncrement = 4096; @@ -48,7 +46,7 @@ void setup() { delay(100); Serial.println("Raw SerialFlash Hardware Test"); - SerialFlash.begin(SPI1, FlashChipSelect); + SerialFlash.begin(ONBOARD_FLASH_SPI_PORT, ONBOARD_FLASH_CS_PIN); if (test()) { Serial.println(); diff --git a/variants/arduino_101/variant.h b/variants/arduino_101/variant.h index 4ee93f05..2736252f 100644 --- a/variants/arduino_101/variant.h +++ b/variants/arduino_101/variant.h @@ -113,6 +113,12 @@ extern "C"{ * ADC */ +/* + * ONBOARD SPI FLASH + */ +#define ONBOARD_FLASH_SPI_PORT SPI1 +#define ONBOARD_FLASH_CS_PIN 21 + /* EAI ADC device registers */ #define ADC_SET (0x80015000) #define ADC_DIVSEQSTAT (0x80015001) From d5bce042321f2fe10ddf87044d7016aad87a9558 Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 20 Feb 2017 09:37:36 +0800 Subject: [PATCH 063/125] Jira 858, Can't trigger BLEConnected, BLEDisconnected events, git 445 Root casue: - The callback regiter before the begin and the begin reset the callback. Code mods: 1. BLEPeripheral.cpp: - Remove clearing of callback register array at begin(). The array is cleared upon system initialization. --- libraries/CurieBLE/src/BLEPeripheral.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/CurieBLE/src/BLEPeripheral.cpp b/libraries/CurieBLE/src/BLEPeripheral.cpp index 9fa572e2..2bcee466 100644 --- a/libraries/CurieBLE/src/BLEPeripheral.cpp +++ b/libraries/CurieBLE/src/BLEPeripheral.cpp @@ -155,7 +155,6 @@ void BLEPeripheral::init() if (!_initCalled) { BLE.begin(); - memset(m_eventHandlers, 0, sizeof(m_eventHandlers)); _initCalled = true; } } From d818a3a5fa1ede20004487ffc5c95e6008b59a8d Mon Sep 17 00:00:00 2001 From: lianggao Date: Wed, 15 Feb 2017 15:26:45 +0800 Subject: [PATCH 064/125] Jira 793 Support Characteristic UUID that is not subset of Service, git 384 Root cause: - Nordic stack does not return long UUID for Characteristic discovery. It is expecting Characteristic UUID is a subset of the Service UUID (eg. a sequential increament of the Service UUID). - In order for the Central mode (to behave like Apple) to discover long Characteristic UUID, the process is now done in two steps (similar to the Service discovery). First, initiate a read of the Characteristic and then discover the long UUID upon successful read. Code Mods: 1. BLECallbacks.cpp: - Added a call back to handle the read Characteristic completion event. 2. BLECallbacks.h: - Prototyping. 3. BLEProfileManager.cpp: - Added getServiceBySubHandle() to enable the call back to locate the Service of a discovered Characteristic. 4. BLEProfileManager.h: - Prototyping. 5. BLEServiceImp.cpp: - This is where the two steps Characteristic discovery is implemented. At discoverAttributes(), reading of Characteristic is first initiated. When the stack completes the operation, extract the UUID thereafter. - To support this two steps process, additional functions, discoverNextCharacteristic and readCharacteristic, are added. 6. BLEServiceImp.h: - Prototyping. - Change the discover process. - Add read operation after discover returned invalid UUID. --- .../CurieBLE/src/internal/BLECallbacks.cpp | 17 ++ .../CurieBLE/src/internal/BLECallbacks.h | 5 + .../src/internal/BLEProfileManager.cpp | 34 +++ .../CurieBLE/src/internal/BLEProfileManager.h | 1 + .../CurieBLE/src/internal/BLEServiceImp.cpp | 200 ++++++++++++++---- .../CurieBLE/src/internal/BLEServiceImp.h | 16 ++ 6 files changed, 234 insertions(+), 39 deletions(-) diff --git a/libraries/CurieBLE/src/internal/BLECallbacks.cpp b/libraries/CurieBLE/src/internal/BLECallbacks.cpp index be86552c..ee89e916 100644 --- a/libraries/CurieBLE/src/internal/BLECallbacks.cpp +++ b/libraries/CurieBLE/src/internal/BLECallbacks.cpp @@ -172,6 +172,23 @@ uint8_t profile_service_read_rsp_process(bt_conn_t *conn, return ret; } +uint8_t profile_characteristic_read_rsp_process(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length) +{ + BLEDevice bleDevice(bt_conn_get_dst(conn)); + BLEServiceImp* service_imp = BLEProfileManager::instance()->getServiceBySubHandle(bleDevice, params->single.handle); + + uint8_t ret = service_imp->characteristicReadRspProc(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, diff --git a/libraries/CurieBLE/src/internal/BLECallbacks.h b/libraries/CurieBLE/src/internal/BLECallbacks.h index 213bd794..7eefaaa6 100644 --- a/libraries/CurieBLE/src/internal/BLECallbacks.h +++ b/libraries/CurieBLE/src/internal/BLECallbacks.h @@ -76,6 +76,11 @@ uint8_t profile_service_read_rsp_process(bt_conn_t *conn, void ble_on_write_no_rsp_complete(struct bt_conn *conn, uint8_t err, const void *data); +uint8_t profile_characteristic_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/BLEProfileManager.cpp b/libraries/CurieBLE/src/internal/BLEProfileManager.cpp index df34c996..8dd33244 100644 --- a/libraries/CurieBLE/src/internal/BLEProfileManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEProfileManager.cpp @@ -507,6 +507,40 @@ BLEServiceImp* BLEProfileManager::service(const BLEDevice &bledevice, int index) return serviceImp; } +BLEServiceImp* BLEProfileManager::getServiceBySubHandle(const BLEDevice &bledevice, uint16_t handle) const +{ + BLEServiceImp* serviceImp = NULL; + uint16_t start_handle; + uint16_t end_handle; + + const BLEServiceLinkNodeHeader* serviceHeader = getServiceHeader(bledevice); + if (NULL == serviceHeader) + { + // Doesn't find the service + return NULL; + } + BLEServiceNodePtr node = serviceHeader->next; + + while (node != NULL) + { + serviceImp = node->value; + start_handle = serviceImp->startHandle(); + end_handle = serviceImp->endHandle(); + if (handle >= start_handle && handle <= end_handle) + { + break; + } + node = node->next; + } + + if (NULL == node) + { + serviceImp = NULL; + } + + return serviceImp; +} + void BLEProfileManager::handleConnectedEvent(const bt_addr_le_t* deviceAddr) { int index = getUnusedIndex(); diff --git a/libraries/CurieBLE/src/internal/BLEProfileManager.h b/libraries/CurieBLE/src/internal/BLEProfileManager.h index 02c42c9c..a83ba431 100644 --- a/libraries/CurieBLE/src/internal/BLEProfileManager.h +++ b/libraries/CurieBLE/src/internal/BLEProfileManager.h @@ -98,6 +98,7 @@ class BLEProfileManager{ 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; + BLEServiceImp* getServiceBySubHandle(const BLEDevice &bledevice, uint16_t handle) const; int serviceCount(const BLEDevice &bledevice) const; int characteristicCount(const BLEDevice &bledevice) const; diff --git a/libraries/CurieBLE/src/internal/BLEServiceImp.cpp b/libraries/CurieBLE/src/internal/BLEServiceImp.cpp index 4f4d3dc1..ba479b90 100644 --- a/libraries/CurieBLE/src/internal/BLEServiceImp.cpp +++ b/libraries/CurieBLE/src/internal/BLEServiceImp.cpp @@ -26,6 +26,7 @@ #include "BLECharacteristicImp.h" bt_uuid_16_t BLEServiceImp::_gatt_primary_uuid = {BT_UUID_TYPE_16, BT_UUID_GATT_PRIMARY_VAL}; +bt_gatt_read_params_t BLEServiceImp::_read_params; bt_uuid_t *BLEServiceImp::getPrimayUuid(void) { @@ -36,6 +37,7 @@ BLEServiceImp::BLEServiceImp(BLEService& service): BLEAttribute(service.uuid(), BLETypeService), _start_handle(0), _end_handle(0xFFFF), + _reading(false), _cur_discover_chrc(NULL) { memset(&_characteristics_header, 0, sizeof(_characteristics_header)); @@ -46,6 +48,7 @@ BLEServiceImp::BLEServiceImp(const bt_uuid_t* uuid): BLEAttribute(uuid, BLETypeService), _start_handle(0), _end_handle(0xFFFF), + _reading(false), _cur_discover_chrc(NULL) { memset(&_characteristics_header, 0, sizeof(_characteristics_header)); @@ -242,10 +245,17 @@ BLECharacteristicImp* BLEServiceImp::characteristic(const char* uuid) bool BLEServiceImp::discovering() { - return (_cur_discover_chrc != NULL); + return (_cur_discover_chrc != NULL || _reading); } bool BLEServiceImp::discoverAttributes(BLEDevice* device) +{ + return discoverAttributes(device, _start_handle, _end_handle); +} + +bool BLEServiceImp::discoverAttributes(BLEDevice* device, + uint16_t start_handle, + uint16_t end_handle) { pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); int err; @@ -273,8 +283,8 @@ bool BLEServiceImp::discoverAttributes(BLEDevice* device) return false; } temp = &_discover_params; - temp->start_handle = _start_handle; - temp->end_handle = _end_handle; + temp->start_handle = start_handle; + temp->end_handle = end_handle; temp->uuid = NULL; temp->type = BT_GATT_DISCOVER_CHARACTERISTIC; temp->func = profile_discover_process; @@ -309,21 +319,34 @@ uint8_t BLEServiceImp::discoverResponseProc(bt_conn_t *conn, //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); + const bt_uuid_t* chrc_uuid = psttemp->uuid; - //pr_debug(LOG_MODULE_BLE, "%s-%d:handle-%d:%d", __FUNCTION__, __LINE__,attr->handle, chrc_handle); - if (BLE_STATUS_SUCCESS != retval) + uint16_t le16; + memcpy(&le16, &BT_UUID_16(chrc_uuid)->val, sizeof(le16)); + if (chrc_uuid->type == BT_UUID_TYPE_16 && + le16 == 0) { - pr_error(LOG_MODULE_BLE, "%s-%d: Error-%d", - __FUNCTION__, __LINE__, retval); - errno = ENOMEM; + // Read the UUID + readCharacteristic(device, chrc_handle); + retVal = BT_GATT_ITER_CONTINUE; } else { - retVal = BT_GATT_ITER_CONTINUE; + int retval = (int)addCharacteristic(device, + psttemp->uuid, + chrc_handle, + psttemp->properties); + + 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; @@ -335,8 +358,8 @@ uint8_t BLEServiceImp::discoverResponseProc(bt_conn_t *conn, if (NULL != _cur_discover_chrc) { retVal = _cur_discover_chrc->discoverResponseProc(conn, - attr, - params); + attr, + params); } break; } @@ -347,7 +370,7 @@ uint8_t BLEServiceImp::discoverResponseProc(bt_conn_t *conn, } } - pr_debug(LOG_MODULE_BLE, "%s-%d:ret-%d",__FUNCTION__, __LINE__, retVal); + //pr_debug(LOG_MODULE_BLE, "%s-%d:ret-%d",__FUNCTION__, __LINE__, retVal); if (retVal == BT_GATT_ITER_STOP) { if (errno == ENOMEM) @@ -355,36 +378,135 @@ uint8_t BLEServiceImp::discoverResponseProc(bt_conn_t *conn, _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) + if (false == _reading) { - chrcCurImp = node->value; - - if (NULL == _cur_discover_chrc) + discoverNextCharacteristic(device); + } + } + return retVal; +} + +void BLEServiceImp::discoverNextCharacteristic(BLEDevice &bledevice) +{ + 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(&bledevice); + pr_debug(LOG_MODULE_BLE, "%s-%d",__FUNCTION__, __LINE__); + if (result == true) { - 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; - } + // Record the current discovering service + _cur_discover_chrc = chrcCurImp; + break; } - else if (_cur_discover_chrc == chrcCurImp) + } + else if (_cur_discover_chrc == chrcCurImp) + { + // Find next discoverable service + _cur_discover_chrc = NULL; + } + node = node->next; + } +} + +bool BLEServiceImp::readCharacteristic(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) + { + return false; + } + + _read_params.func = profile_characteristic_read_rsp_process; + _read_params.handle_count = 1; + _read_params.single.handle = handle - 1; + _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) + { + _reading = true; + } + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return _reading; +} + +uint8_t BLEServiceImp::characteristicReadRspProc(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length) +{ + _reading = false; + 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 + 3) + { + const uint8_t* rspdata = (const uint8_t*) data; + bt_uuid_128_t uuid_tmp; + uint16_t chrc_handle = rspdata[1] | (rspdata[2] << 8); + uuid_tmp.uuid.type = BT_UUID_TYPE_128; + memcpy(uuid_tmp.val, &rspdata[3], UUID_SIZE_128); + int retval = (int)addCharacteristic(bleDevice, + (const bt_uuid_t*)&uuid_tmp, + chrc_handle, + rspdata[0]); + + if (BLE_STATUS_SUCCESS != retval) + { + pr_error(LOG_MODULE_BLE, "%s-%d: Error-%d", + __FUNCTION__, __LINE__, retval); + errno = ENOMEM; + } + else + { + if (false == discovering()) { - // Find next discoverable service - _cur_discover_chrc = NULL; + if (false == discoverAttributes(&bleDevice, chrc_handle + 1, _end_handle)) + { + discoverNextCharacteristic(bleDevice); + } } - node = node->next; } } - return retVal; + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + + return BT_GATT_ITER_STOP; } - diff --git a/libraries/CurieBLE/src/internal/BLEServiceImp.h b/libraries/CurieBLE/src/internal/BLEServiceImp.h index d6c63389..a2ef1571 100644 --- a/libraries/CurieBLE/src/internal/BLEServiceImp.h +++ b/libraries/CurieBLE/src/internal/BLEServiceImp.h @@ -71,6 +71,12 @@ class BLEServiceImp: public BLEAttribute{ uint8_t discoverResponseProc(bt_conn_t *conn, const bt_gatt_attr_t *attr, bt_gatt_discover_params_t *params); + + uint8_t characteristicReadRspProc(bt_conn_t *conn, + int err, + bt_gatt_read_params_t *params, + const void *data, + uint16_t length); bool discovering(); static bt_uuid_t *getPrimayUuid(void); @@ -81,6 +87,13 @@ class BLEServiceImp: public BLEAttribute{ int updateProfile(bt_gatt_attr_t *attr_start, int& index); + +private: + void discoverNextCharacteristic(BLEDevice &bledevice); + bool readCharacteristic(const BLEDevice &bledevice, uint16_t handle); + bool discoverAttributes(BLEDevice* device, + uint16_t start_handle, + uint16_t end_handle); private: typedef LinkNode BLECharacteristicLinkNodeHeader; typedef LinkNode* BLECharacteristicNodePtr; @@ -89,6 +102,9 @@ class BLEServiceImp: public BLEAttribute{ uint16_t _start_handle; uint16_t _end_handle; + static bt_gatt_read_params_t _read_params; + bool _reading; + void releaseCharacteristic(); BLECharacteristicImp *_cur_discover_chrc; From 3ec83508e9c63b44068db86b72ecdbe3690bef36 Mon Sep 17 00:00:00 2001 From: Sidney Leung Date: Wed, 1 Mar 2017 22:57:07 -0800 Subject: [PATCH 065/125] Jira 867, BLE setValue(), add support for strings, git #210 Feature added: - This is directly pulled from Arduino community PR #210. Overloading the setValue() method to take in a string as the only input parameter, instead of a pointer and its size. --- libraries/CurieBLE/src/BLECharacteristic.cpp | 5 +++++ libraries/CurieBLE/src/BLECharacteristic.h | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/libraries/CurieBLE/src/BLECharacteristic.cpp b/libraries/CurieBLE/src/BLECharacteristic.cpp index 89a8e41d..96866c00 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.cpp +++ b/libraries/CurieBLE/src/BLECharacteristic.cpp @@ -248,6 +248,11 @@ bool BLECharacteristic::setValue(const unsigned char value[], unsigned short len return writeValue(value, (int)length); } +bool BLECharacteristic::setValue(const char* value) +{ + return this->setValue((const unsigned char *)value, strlen(value)); +} + bool BLECharacteristic::writeValue(const byte value[], int length) { return writeValue(value, length, 0); diff --git a/libraries/CurieBLE/src/BLECharacteristic.h b/libraries/CurieBLE/src/BLECharacteristic.h index 0ca0cf53..79fbfed3 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.h +++ b/libraries/CurieBLE/src/BLECharacteristic.h @@ -180,6 +180,15 @@ class BLECharacteristic: public BLEAttributeWithValue */ bool setValue(const unsigned char value[], unsigned short length); + /** + * Set the current value of the Characteristic with a String + * + * @param value New string value to set, strings exceeding maxLength will be truncated + * + * @return bool true set value success, false on error + */ + bool setValue(const char* value); + /** * @brief Write the value of the characteristic * From 59d03ae9ccfb8fd0ab84e3245a11647d9cdd353e Mon Sep 17 00:00:00 2001 From: lianggao Date: Wed, 8 Feb 2017 10:25:34 +0800 Subject: [PATCH 066/125] Jira 859. Support BLE descriptor processing. Feature added: For Central mode, added the support for processing BLE descriptor from a connected Peripheral. Enable the user sketch to access to descriptor value, length info. Code modifications: 1. peripheral_explorer.ino: - Example sketch with added descriptor processing. 2. BLECharacteristic.cpp: - Bug fixed in handling multi-descriptors. 3. BLEDescriptor.cpp: - For constructors, added initialization of descriptor storage and info. - For copy constructor and assignment, take into the account of descripter value and info. - Added API's for readning and returning descriptor info, eg. value, size. 4. BLEDescriptor.h: - Prototyping. 5. BLEDevice.h: - Added descriptor storage declaration. 6. BLECallbacks.cpp: - Added call back event for decriptor arrival. 7. BLECallbacks.h: - Prototyping. 8. BLECharacteristicImp.cpp: - The processing of the descriptor. 9. BLECharacteristicImp.h: - Prototyping. 10. BLEServiceImp.cpp: - Added descriptor info to constructor. - Made copy constructor to be aware of descriptor info. 11. BLEServiceImp.h: - Prototyping. --- .../peripheral_explorer.ino | 23 ++- libraries/CurieBLE/src/BLECharacteristic.cpp | 11 +- libraries/CurieBLE/src/BLEDescriptor.cpp | 117 ++++++++----- libraries/CurieBLE/src/BLEDescriptor.h | 99 ++++++----- libraries/CurieBLE/src/BLEDevice.h | 6 + .../CurieBLE/src/internal/BLECallbacks.cpp | 25 +++ .../CurieBLE/src/internal/BLECallbacks.h | 7 + .../src/internal/BLECharacteristicImp.cpp | 20 +++ .../src/internal/BLECharacteristicImp.h | 2 + .../src/internal/BLEDescriptorImp.cpp | 164 ++++++++++++++---- .../CurieBLE/src/internal/BLEDescriptorImp.h | 77 +++++++- .../src/internal/BLEProfileManager.cpp | 24 +++ .../CurieBLE/src/internal/BLEProfileManager.h | 1 + .../CurieBLE/src/internal/BLEServiceImp.cpp | 23 +++ .../CurieBLE/src/internal/BLEServiceImp.h | 2 + 15 files changed, 480 insertions(+), 121 deletions(-) diff --git a/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino b/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino index 45429655..4c558922 100644 --- a/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino +++ b/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino @@ -133,9 +133,30 @@ void exploreCharacteristic(BLECharacteristic characteristic) { printData(characteristic.value(), characteristic.valueLength()); } } - Serial.println(); + // loop the descriptors of the characteristic and explore each + for (int i = 0; i < characteristic.descriptorCount(); i++) { + BLEDescriptor descriptor = characteristic.descriptor(i); + + exploreDescriptor(descriptor); + } +} + +void exploreDescriptor(BLEDescriptor descriptor) { + // print the UUID of the descriptor + Serial.print("\t\tDescriptor "); + Serial.print(descriptor.uuid()); + + // read the descriptor value + descriptor.read(); + delay(1000); + + // print out the value of the descriptor + Serial.print(", value 0x"); + printData(descriptor.value(), descriptor.valueLength()); + + Serial.println(); } void printData(const unsigned char data[], int length) { diff --git a/libraries/CurieBLE/src/BLECharacteristic.cpp b/libraries/CurieBLE/src/BLECharacteristic.cpp index 96866c00..278eb1d4 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.cpp +++ b/libraries/CurieBLE/src/BLECharacteristic.cpp @@ -444,15 +444,20 @@ int BLECharacteristic::addDescriptor(BLEDescriptor& descriptor) else if (BLEUtils::isLocalBLE(_bledev) == true) { // Only support the GATT server that create the service in local device. - _chrc_local_imp = new BLECharacteristicImp(*this, _bledev); + // Consider to add multi-descriptor if (NULL == _chrc_local_imp) { - retVar = BLE_STATUS_NO_MEMORY; + _chrc_local_imp = new BLECharacteristicImp(*this, _bledev); } - else + + if (NULL != _chrc_local_imp) { retVar = _chrc_local_imp->addDescriptor(descriptor); } + else + { + retVar = BLE_STATUS_NO_MEMORY; + } } return retVar; } diff --git a/libraries/CurieBLE/src/BLEDescriptor.cpp b/libraries/CurieBLE/src/BLEDescriptor.cpp index 31c4ba7e..eb624532 100644 --- a/libraries/CurieBLE/src/BLEDescriptor.cpp +++ b/libraries/CurieBLE/src/BLEDescriptor.cpp @@ -35,28 +35,19 @@ BLEDescriptor::BLEDescriptor(BLEDescriptorImp* descriptorImp, const BLEDevice *bleDev): _bledev(bleDev), _value_size(0), - _value(NULL) + _value(NULL), + _internal(descriptorImp) { _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): - _bledev() + _bledev(), + _internal(NULL) { bt_uuid_128_t uuid_tmp; memset(_uuid_cstr, 0, sizeof (_uuid_cstr)); @@ -65,15 +56,16 @@ BLEDescriptor::BLEDescriptor(const char* uuid, _bledev.setAddress(*BLEUtils::bleGetLoalAddress()); - _value_size = valueLength > BLE_MAX_ATTR_LONGDATA_LEN ? BLE_MAX_ATTR_LONGDATA_LEN : valueLength; + _value_size = (valueLength > BLE_MAX_ATTR_LONGDATA_LEN) ? BLE_MAX_ATTR_LONGDATA_LEN : valueLength; _value = (unsigned char*)malloc(_value_size); - if (NULL == _value) + if (NULL != _value) { memcpy(_value, value, _value_size); } else { errno = ENOMEM; + _value_size = 0; } } @@ -82,22 +74,27 @@ BLEDescriptor::BLEDescriptor(const char* uuid, BLEDescriptor(uuid, (const unsigned char*)value, strlen(value)) {} -BLEDescriptor::BLEDescriptor(const BLEDescriptor& rhs) +BLEDescriptor::BLEDescriptor(const BLEDescriptor& rhs): + _bledev(&rhs._bledev), + _properties(rhs._properties), + _value_size(0), + _value(NULL), + _internal(rhs._internal) { - _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 + memcpy(_uuid_cstr, rhs._uuid_cstr, sizeof(_uuid_cstr)); + if (NULL == _internal && rhs._value_size > 0) { - _value_size = 0; - errno = ENOMEM; + _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 + { + errno = ENOMEM; + } } - memcpy(_uuid_cstr, rhs._uuid_cstr, sizeof(_uuid_cstr)); - _properties = rhs._properties; - _bledev = BLEDevice(&rhs._bledev); } BLEDescriptor& BLEDescriptor::operator= (const BLEDescriptor& rhs) @@ -107,23 +104,27 @@ BLEDescriptor& BLEDescriptor::operator= (const BLEDescriptor& rhs) memcpy(_uuid_cstr, rhs._uuid_cstr, sizeof(_uuid_cstr)); _properties = rhs._properties; _bledev = BLEDevice(&rhs._bledev); - if (_value_size < rhs._value_size) + _internal = rhs._internal; + if (NULL == _internal && rhs._value_size > 0) { - _value_size = rhs._value_size; + 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) - free(_value); - _value = (unsigned char*)malloc(_value_size); - } - - if (NULL != _value) - { - memcpy(_value, rhs._value, rhs._value_size); - } - else - { - _value_size = 0; - errno = ENOMEM; + { + memcpy(_value, rhs._value, rhs._value_size); + } + else + { + _value_size = 0; + errno = ENOMEM; + } } } return *this; @@ -145,12 +146,22 @@ const char* BLEDescriptor::uuid() const const byte* BLEDescriptor::value() const { - return _value; + const byte* ret = _value; + if (NULL != _internal) + { + ret = _internal->value(); + } + return ret; } int BLEDescriptor::valueLength() const { - return _value_size; + int ret = _value_size; + if (NULL != _internal) + { + ret = _internal->valueLength(); + } + return ret; } BLEDescriptor::operator bool() const @@ -166,6 +177,22 @@ unsigned char BLEDescriptor::properties() const int BLEDescriptor::valueSize() const { - return _value_size; + int ret = _value_size; + if (NULL != _internal) + { + ret = _internal->valueSize(); + } + return ret; +} + +bool BLEDescriptor::read() +{ + bool retVar = false; + + if (NULL != _internal) + { + retVar = _internal->read(); + } + return retVar; } diff --git a/libraries/CurieBLE/src/BLEDescriptor.h b/libraries/CurieBLE/src/BLEDescriptor.h index e1a79f2a..9e95de7d 100644 --- a/libraries/CurieBLE/src/BLEDescriptor.h +++ b/libraries/CurieBLE/src/BLEDescriptor.h @@ -37,73 +37,92 @@ class BLEDescriptor virtual ~BLEDescriptor(); + /** + * @brief Get the descriptor's UUID string + * + * @param none + * + * @return const char* The UUID string + * + * @note none + */ const char* uuid() 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) - - 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 Write the value of the descriptor + * @brief Get the value of descriptor * - * @param value The value buffer that want to write to descriptor + * @param none * - * @param length The value buffer's length + * @return const byte* The value buffer * - * @return bool true - Success, false - Failed + * @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 none */ - //virtual bool writeValue(const byte value[], int length); + virtual int valueLength() const; /** - * @brief Write the value of the descriptor + * @brief Is the descriptor valid + * + * @param none * - * @param value The value buffer that want to write to descriptor + * @return bool true/false * - * @param length The value buffer's length + * @note none + */ + virtual operator bool() const; + + /** + * @brief Read the descriptor value * - * @param offset The offset in the descriptor's data + * @param none * * @return bool true - Success, false - Failed * + * @note Only for GATT client. Schedule read request to the GATT server + */ + bool read(); + + /** + * @brief Get the property mask of the descriptor + * + * @param none + * + * @return unsigned char The property mask of the descriptor + * * @note none */ - //bool writeValue(const byte value[], int length, int offset); + unsigned char properties() const; /** - * @brief Write the value of the descriptor + * @brief Get the maximum size of the value * - * @param value The value string that want to write to descriptor + * @param none * - * @return bool true - Success, false - Failed + * @return int The maximum size of the value * * @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(); + 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 + BLEDescriptorImp *_internal; // The real implementation of Descriptor }; #endif diff --git a/libraries/CurieBLE/src/BLEDevice.h b/libraries/CurieBLE/src/BLEDevice.h index 56742f1b..6c1090d6 100644 --- a/libraries/CurieBLE/src/BLEDevice.h +++ b/libraries/CurieBLE/src/BLEDevice.h @@ -644,6 +644,7 @@ class BLEDevice void setEventHandler(BLEDeviceEvent event, BLEDeviceEventHandler eventHandler); // set an event handler (callback) protected: + friend class BLEDescriptorImp; friend class BLECharacteristicImp; friend class BLEServiceImp; friend class BLEDeviceManager; @@ -659,6 +660,11 @@ class BLEDevice bt_gatt_read_params_t *params, const void *data, uint16_t length); + friend uint8_t profile_descriptor_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); diff --git a/libraries/CurieBLE/src/internal/BLECallbacks.cpp b/libraries/CurieBLE/src/internal/BLECallbacks.cpp index ee89e916..edfcaeec 100644 --- a/libraries/CurieBLE/src/internal/BLECallbacks.cpp +++ b/libraries/CurieBLE/src/internal/BLECallbacks.cpp @@ -161,6 +161,31 @@ uint8_t profile_read_rsp_process(bt_conn_t *conn, return BT_GATT_ITER_STOP; } +uint8_t profile_descriptor_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; + } + BLEDescriptorImp *descriptor = NULL; + BLEDevice bleDevice(bt_conn_get_dst(conn)); + + // Get characteristic by handle params->single.handle + descriptor = BLEProfileManager::instance()->descriptor(bleDevice, params->single.handle); + + //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + if (descriptor) + { + descriptor->writeValue((const unsigned char *)data, length, params->single.offset); + } + //pr_debug(LOG_MODULE_BLE, "%s-%d: desc len-%d", __FUNCTION__, __LINE__, descriptor->valueLength()); + return BT_GATT_ITER_STOP; +} + uint8_t profile_service_read_rsp_process(bt_conn_t *conn, int err, bt_gatt_read_params_t *params, diff --git a/libraries/CurieBLE/src/internal/BLECallbacks.h b/libraries/CurieBLE/src/internal/BLECallbacks.h index 7eefaaa6..deffe92a 100644 --- a/libraries/CurieBLE/src/internal/BLECallbacks.h +++ b/libraries/CurieBLE/src/internal/BLECallbacks.h @@ -28,6 +28,13 @@ uint8_t profile_read_rsp_process(bt_conn_t *conn, int err, bt_gatt_read_params_t *params, const void *data, uint16_t length); + +uint8_t profile_descriptor_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); diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp index 71318bc9..35567130 100644 --- a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp @@ -538,6 +538,26 @@ BLECharacteristicImp::valueHandle() return handle; } +BLEDescriptorImp* BLECharacteristicImp::descriptor(uint16_t handle) +{ + BLEDescriptorImp* descriptorImp = NULL; + BLEDescriptorNodePtr node = link_node_get_first(&_descriptors_header); + while (NULL != node) + { + descriptorImp = node->value; + if (handle == descriptorImp->valueHandle()) + { + break; + } + node = node->next; + } + if (NULL == node) + { + descriptorImp = NULL; + } + return descriptorImp; +} + void BLECharacteristicImp::_setValue(const uint8_t value[], uint16_t length, uint16_t offset) { diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.h b/libraries/CurieBLE/src/internal/BLECharacteristicImp.h index d690d600..af5e9839 100644 --- a/libraries/CurieBLE/src/internal/BLECharacteristicImp.h +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.h @@ -188,6 +188,8 @@ class BLECharacteristicImp: public BLEAttribute{ friend class BLEProfileManager; friend class BLEServiceImp; friend class BLECharacteristic; + + BLEDescriptorImp* descriptor(uint16_t handle); /** * Constructor for BLE Characteristic * diff --git a/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp b/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp index 0586bc09..42ca6a24 100644 --- a/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp +++ b/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp @@ -23,11 +23,15 @@ #include "internal/ble_client.h" #include "BLECallbacks.h" +#include "BLEUtils.h" BLEDescriptorImp::BLEDescriptorImp(BLEDevice& bledevice, BLEDescriptor &descriptor): - BLEAttribute(descriptor.uuid(), BLETypeDescriptor), - _value_handle(0) + BLEAttribute(descriptor.uuid(), BLETypeDescriptor), + _value_handle(0), + _bledev(bledevice), + _reading(false), + _attr_desc_value(NULL) { _properties = descriptor.properties(); @@ -45,21 +49,22 @@ BLEDescriptorImp::BLEDescriptorImp(const bt_uuid_t* uuid, uint16_t handle, BLEDevice& bledevice): BLEAttribute(uuid, BLETypeDescriptor), + _value_length(0), _value_handle(handle), - _properties(properties) + _value(NULL), + _properties(properties), + _bledev(bledevice), + _reading(false), + _attr_desc_value(NULL) { - _value_length = BLE_MAX_ATTR_DATA_LEN; - _value = (unsigned char*)malloc(_value_length); - - if (_value) - memset(_value, 0, _value_length); - else - _value_length = 0; + memset(&_read_params, 0, sizeof(_read_params)); } BLEDescriptorImp::BLEDescriptorImp(const BLEDescriptorImp& rhs) : - BLEAttribute(rhs) + BLEAttribute(rhs), + _reading(false), + _attr_desc_value(rhs._attr_desc_value) { _value_length = rhs._value_length; _value = (unsigned char *)malloc(_value_length); @@ -70,30 +75,29 @@ BLEDescriptorImp::BLEDescriptorImp(const BLEDescriptorImp& rhs) : _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); + 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; + _bledev = BLEDevice(&that._bledev); + _attr_desc_value = that._attr_desc_value; } return *this; } @@ -136,6 +140,24 @@ int BLEDescriptorImp::updateProfile(bt_gatt_attr_t *attr_start, int& index) return 1; } +uint16_t +BLEDescriptorImp::valueHandle() const +{ + uint16_t handle = 0; + if (NULL != _attr_desc_value) + { + //GATT server + handle = _attr_desc_value->handle; + } + else + { + // GATT client + handle = _value_handle; + } + + return handle; +} + unsigned char BLEDescriptorImp::properties() const { return _properties; @@ -146,4 +168,88 @@ int BLEDescriptorImp::valueSize() const return _value_length; } +bool BLEDescriptorImp::read() +{ + int retval = 0; + bt_conn_t* conn = NULL; + + if (true == BLEUtils::isLocalBLE(_bledev)) + { + // GATT server can't read + return false; + } + + if (_reading) + { + // Already in reading state + return false; + } + + _read_params.func = profile_descriptor_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(_bledev.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 BLEDescriptorImp::writeValue(const byte value[], + int length, + int offset) +{ + bool ret = true; + int total_length = length + offset; + int write_len = length; + if (total_length > BLE_MAX_ATTR_DATA_LEN) + { + return false; + } + + if (NULL == _value) + { + _value_length = length + offset; + _value = (unsigned char*)malloc(_value_length); + + if (NULL != _value) + { + memset(_value, 0, _value_length); + } + else + { + _value_length = 0; + ret = false; + } + } + + if (_value_length < total_length) + { + write_len = _value_length - offset; + } + + if (NULL != _value) + { + memcpy(_value + offset, value, write_len); + } + return ret; +} + diff --git a/libraries/CurieBLE/src/internal/BLEDescriptorImp.h b/libraries/CurieBLE/src/internal/BLEDescriptorImp.h index 32fceb1a..701f3f83 100644 --- a/libraries/CurieBLE/src/internal/BLEDescriptorImp.h +++ b/libraries/CurieBLE/src/internal/BLEDescriptorImp.h @@ -59,12 +59,80 @@ class BLEDescriptorImp: public BLEAttribute{ * @return unsigned short size of Descriptor value in bytes */ unsigned short valueLength(void) const; - + + /** + * @brief Fill the attribute for profile register structure + * + * @param bt_gatt_attr_t * The start pointer of the profile register structure array + * + * @param int& The current index in the profile structure array + * + * @return int Filled structure counter + * + * @note none + */ int updateProfile(bt_gatt_attr_t *attr_start, int& index); unsigned char operator[] (int offset) const; + + /** + * @brief Get the property mask of the descriptor + * + * @param none + * + * @return unsigned char The property mask of the descriptor + * + * @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 descriptor value handle + * + * @param none + * + * @return none + * + * @note none + */ + uint16_t valueHandle() const; + + /** + * @brief Write the value of the descriptor + * + * @param value The value buffer that want to write to descriptor + * + * @param length The value buffer's length + * + * @param offset The offset in the descriptor's data + * + * @return bool true - Success, false - Failed + * + * @note none + */ + bool writeValue(const byte value[], int length, int offset); + + /** + * @brief Read the descriptor value + * + * @param none + * + * @return bool true - Success, false - Failed + * + * @note Only for GATT client. Schedule read request to the GATT server + */ + bool read(); protected: @@ -75,9 +143,12 @@ class BLEDescriptorImp: public BLEAttribute{ unsigned char* _value; unsigned char _properties; // The characteristic property - bt_uuid_128 _descriptor_uuid; - BLEDevice _bledev; + + bool _reading; + bt_gatt_read_params_t _read_params; // GATT read parameter + + bt_gatt_attr_t *_attr_desc_value; // GATT server only }; #endif // _BLE_DESCRIPTOR_H_INCLUDED diff --git a/libraries/CurieBLE/src/internal/BLEProfileManager.cpp b/libraries/CurieBLE/src/internal/BLEProfileManager.cpp index 8dd33244..aba7981b 100644 --- a/libraries/CurieBLE/src/internal/BLEProfileManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEProfileManager.cpp @@ -337,6 +337,30 @@ void BLEProfileManager::clearProfile(BLEServiceLinkNodeHeader* serviceHeader) } } +BLEDescriptorImp* BLEProfileManager::descriptor(const BLEDevice &bledevice, uint16_t handle) +{ + BLEDescriptorImp* descriptorImp = 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; + descriptorImp = service->descriptor(handle); + if (NULL != descriptorImp) + { + break; + } + node = node->next; + } + return descriptorImp; +} + BLECharacteristicImp* BLEProfileManager::characteristic(const BLEDevice &bledevice, int index) { BLECharacteristicImp* characteristicImp = NULL; diff --git a/libraries/CurieBLE/src/internal/BLEProfileManager.h b/libraries/CurieBLE/src/internal/BLEProfileManager.h index a83ba431..1c05fc99 100644 --- a/libraries/CurieBLE/src/internal/BLEProfileManager.h +++ b/libraries/CurieBLE/src/internal/BLEProfileManager.h @@ -73,6 +73,7 @@ class BLEProfileManager{ inline bool hasRegisterProfile(){return _profile_registered;} + BLEDescriptorImp* descriptor(const BLEDevice &bledevice, uint16_t handle); /** * @brief Get the BLE's Characteristic implementation object by uuid and index * diff --git a/libraries/CurieBLE/src/internal/BLEServiceImp.cpp b/libraries/CurieBLE/src/internal/BLEServiceImp.cpp index ba479b90..d5d7afe6 100644 --- a/libraries/CurieBLE/src/internal/BLEServiceImp.cpp +++ b/libraries/CurieBLE/src/internal/BLEServiceImp.cpp @@ -177,6 +177,29 @@ void BLEServiceImp::releaseCharacteristic() } +BLEDescriptorImp* BLEServiceImp::descriptor(uint16_t handle) +{ + BLEDescriptorImp* descriptorImp = NULL; + + BLECharacteristicImp* characteristicImp = NULL; + BLECharacteristicNodePtr node = link_node_get_first(&_characteristics_header); + while (NULL != node) + { + characteristicImp = node->value; + descriptorImp = characteristicImp->descriptor(handle); + if (descriptorImp != NULL) + { + break; + } + node = node->next; + } + if (NULL == node) + { + descriptorImp = NULL; + } + return descriptorImp; +} + BLECharacteristicImp* BLEServiceImp::characteristic(int index) { BLECharacteristicImp* characteristicImp = NULL; diff --git a/libraries/CurieBLE/src/internal/BLEServiceImp.h b/libraries/CurieBLE/src/internal/BLEServiceImp.h index a2ef1571..f40ffd36 100644 --- a/libraries/CurieBLE/src/internal/BLEServiceImp.h +++ b/libraries/CurieBLE/src/internal/BLEServiceImp.h @@ -57,6 +57,8 @@ class BLEServiceImp: public BLEAttribute{ uint16_t handle, unsigned char properties); int getCharacteristicCount(); + + BLEDescriptorImp* descriptor(uint16_t handle); BLECharacteristicImp* characteristic(const bt_uuid_t* uuid); BLECharacteristicImp* characteristic(const char* uuid); From 3612e08ec902c391e38d862b26d0017e30bcbc28 Mon Sep 17 00:00:00 2001 From: lianggao Date: Wed, 4 Jan 2017 16:18:37 +0800 Subject: [PATCH 067/125] =?UTF-8?q?Jira=20836,=20Enumerate=20non=20connect?= =?UTF-8?q?able=20Peripherals=20in=20Central=20scan=20=20=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added feature: - In Central mode, the BLE library should enumerate Peripheral that are non-connectable (eg beacon). Code Mods: 1. BLEDevice.cpp: - Add the call to get Manufacturer Data info from a Peripheral. Non-connectable device may only broadcast this info. 2. BLEDevice.h: - Prototyping. 3. BLEDeviceManager.cpp: - Delete the filter about non-connectable advertisement. - Added APIs, hasManufacturerData(), setManufacturerData(), hasManufacturerData(), to gain access to adv info of non-connectable Peripherals. --- libraries/CurieBLE/src/BLEDevice.cpp | 11 + libraries/CurieBLE/src/BLEDevice.h | 5 + .../src/internal/BLEDeviceManager.cpp | 721 ++++++++++++------ .../CurieBLE/src/internal/BLEDeviceManager.h | 53 +- 4 files changed, 566 insertions(+), 224 deletions(-) diff --git a/libraries/CurieBLE/src/BLEDevice.cpp b/libraries/CurieBLE/src/BLEDevice.cpp index d96b70c2..2c5a3189 100644 --- a/libraries/CurieBLE/src/BLEDevice.cpp +++ b/libraries/CurieBLE/src/BLEDevice.cpp @@ -133,6 +133,17 @@ void BLEDevice::setManufacturerData(const unsigned char manufacturerData[], BLEDeviceManager::instance()->setManufacturerData(manufacturerData, manufacturerDataLength); } +bool BLEDevice::getManufacturerData (unsigned char* manu_data, + unsigned char& manu_data_len) const +{ + return BLEDeviceManager::instance()->getManufacturerData(this, manu_data, manu_data_len); +} + +bool BLEDevice::hasManufacturerData() const +{ + return BLEDeviceManager::instance()->hasManufacturerData(this); +} + void BLEDevice::setLocalName(const char *localName) { BLEDeviceManager::instance()->setLocalName(localName); diff --git a/libraries/CurieBLE/src/BLEDevice.h b/libraries/CurieBLE/src/BLEDevice.h index 6c1090d6..76e94f14 100644 --- a/libraries/CurieBLE/src/BLEDevice.h +++ b/libraries/CurieBLE/src/BLEDevice.h @@ -191,6 +191,11 @@ class BLEDevice void setManufacturerData(const unsigned char manufacturerData[], unsigned char manufacturerDataLength); + bool getManufacturerData (unsigned char* manu_data, + unsigned char& manu_data_len) const; + + bool hasManufacturerData() const; + /** * Set the local name that the BLE Peripheral Device advertises * diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp index 643e6b97..56e2e960 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp @@ -33,9 +33,15 @@ BLEDeviceManager* BLEDeviceManager::_instance; BLEDeviceManager::BLEDeviceManager(): _min_conn_interval(0), _max_conn_interval(0), + _peer_temp_dev_index(0), _adv_critical_local_name(""), + _wait_for_connect_peripheral_adv_data_len(0), + _wait_for_connect_peripheral_scan_rsp_data_len(0), _wait_for_connect_peripheral_adv_rssi(0), + _available_for_connect_peripheral_adv_data_len(0), + _available_for_connect_peripheral_scan_rsp_data_len(0), _available_for_connect_peripheral_adv_rssi(0), + _available_for_connect_peripheral_connectable(false), _connecting(false), _has_service_uuid(false), _has_service_solicit_uuid(false), @@ -44,6 +50,7 @@ BLEDeviceManager::BLEDeviceManager(): _service_data_length(0), _adv_type(0), _adv_data_idx(0), + _scan_rsp_data_idx(0), _local_name(""), _state(BLE_PERIPH_STATE_NOT_READY), _local_ble(NULL), @@ -53,14 +60,7 @@ BLEDeviceManager::BLEDeviceManager(): _adv_duplicate_filter_enabled(false) { 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)); @@ -78,20 +78,51 @@ BLEDeviceManager::BLEDeviceManager(): memset(_peer_adv_buffer, 0, sizeof(_peer_adv_buffer)); memset(_peer_adv_mill, 0, sizeof(_peer_adv_mill)); + memset(_peer_adv_data, 0, sizeof(_peer_adv_data)); + memset(_peer_adv_data_len, 0, sizeof(_peer_adv_data_len)); + memset(_peer_scan_rsp_data, 0, sizeof(_peer_scan_rsp_data)); + memset(_peer_scan_rsp_data_len, 0, sizeof(_peer_scan_rsp_data_len)); + memset(_peer_adv_rssi, 0, sizeof(_peer_adv_rssi)); + + memset(_peer_adv_connectable, 0, sizeof(_peer_adv_connectable)); + + memset(_peer_temp_adv_buffer, 0, sizeof(_peer_temp_adv_buffer)); + memset(_peer_temp_adv_data, 0, sizeof(_peer_temp_adv_data)); + memset(_peer_temp_adv_data_len, 0, sizeof(_peer_temp_adv_data_len)); + memset(_peer_temp_adv_connectable, 0, sizeof(_peer_adv_connectable)); + memset(&_adv_accept_critical, 0, sizeof(_adv_accept_critical)); memset(&_adv_critical_service_uuid, 0, sizeof(_adv_critical_service_uuid)); memset(&_adv_accept_device, 0, sizeof(_adv_accept_device)); + memset(&_wait_for_connect_peripheral, 0, sizeof(_wait_for_connect_peripheral)); + memset(&_wait_for_connect_peripheral_adv_data, 0, sizeof(_wait_for_connect_peripheral_adv_data)); + memset(&_wait_for_connect_peripheral_scan_rsp_data, 0, sizeof(_wait_for_connect_peripheral_scan_rsp_data)); + + memset(&_available_for_connect_peripheral_adv_data, 0, sizeof(_available_for_connect_peripheral_adv_data)); + memset(&_available_for_connect_peripheral_scan_rsp_data, 0, sizeof(_available_for_connect_peripheral_scan_rsp_data)); + + 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(_manufacturer_data, 0, sizeof(_manufacturer_data)); + + 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(_scan_rsp_data, 0, sizeof(_scan_rsp_data)); + 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_scan_rsp_data, 0, sizeof(_peer_peripheral_scan_rsp_data)); + memset(_peer_peripheral_scan_rsp_data_len, 0, sizeof(_peer_peripheral_scan_rsp_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() @@ -311,109 +342,132 @@ void BLEDeviceManager::setAppearance(unsigned short appearance) _appearance = appearance; } +BLE_STATUS_T +BLEDeviceManager::setAdvertiseData(uint8_t type, const uint8_t* data, uint8_t length) +{ + uint8_t lengthOfAdv = 0; // Flags data length + uint8_t lengthOfScanRsp = 0; // Flags data length + bt_data_t *fill_area = NULL; + + // Get the length of the Advertisement + for (uint8_t i = 0; i < _adv_data_idx; i++) + { + lengthOfAdv += _adv_data[i].data_len + 2; + } + + for (uint8_t i = 0; i < _scan_rsp_data_idx; i++) + { + lengthOfAdv += _scan_rsp_data[i].data_len + 2; + } + + + if (((length + lengthOfAdv) < BLE_MAX_ADV_SIZE) && + (_adv_data_idx < ARRAY_SIZE(_adv_data))) + { + fill_area = &_adv_data[_adv_data_idx]; + _adv_data_idx++; + } + else if ((length + lengthOfScanRsp) < BLE_MAX_ADV_SIZE && + (_scan_rsp_data_idx < ARRAY_SIZE(_scan_rsp_data))) + { + fill_area = &_scan_rsp_data[_scan_rsp_data_idx]; + _scan_rsp_data_idx++; + } + else + { + // Service data block is too large. + return BLE_STATUS_ERROR_PARAMETER; + } + + if (fill_area) + { + fill_area->type = type; + fill_area->data = data; + fill_area->data_len = length; + + pr_info(LOG_MODULE_BLE, "ADV type %d Len - %d",type, length); + } + return BLE_STATUS_SUCCESS; +} + BLE_STATUS_T BLEDeviceManager::_advDataInit(void) { - uint8_t lengthTotal = 2; // Flags data length + BLE_STATUS_T ret = BLE_STATUS_SUCCESS; + // Clear the indexs _adv_data_idx = 0; + _scan_rsp_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++; + ret = setAdvertiseData (BT_DATA_FLAGS, &_adv_type, sizeof(_adv_type)); - if (_has_service_uuid) + if (_has_service_solicit_uuid && + (BLE_STATUS_SUCCESS == ret)) { 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) + 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_uuid)->val); + data = (uint8_t *)&(((bt_uuid_16_t *)&_service_solicit_uuid)->val); length = UUID_SIZE_16; - type = BT_DATA_UUID16_ALL; + type = BT_DATA_SOLICIT16; } - else // Sid. KW, default is BT_UUID_TYPE_128 + else // Sid. KW, default is BT_UUID_TYPE_128 { - data = _service_uuid.val; + data = _service_solicit_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); + type = BT_DATA_SOLICIT128; } + + ret = setAdvertiseData(type, data, length); } - if (_has_service_solicit_uuid) + if (_has_service_uuid && + (BLE_STATUS_SUCCESS == ret)) { 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) + 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_solicit_uuid)->val); + data = (uint8_t *)&(((bt_uuid_16_t *)&_service_uuid)->val); length = UUID_SIZE_16; - type = BT_DATA_SOLICIT16; + type = BT_DATA_UUID16_ALL; } - else // Sid. KW, default is BT_UUID_TYPE_128 + else // Sid. KW, default is BT_UUID_TYPE_128 { - data = _service_solicit_uuid.val; + data = _service_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); + type = BT_DATA_UUID128_ALL; } + ret = setAdvertiseData(type, data, length); } - - if (_local_name.length() > 0) + + if (_manufacturer_data_length > 0 && + (BLE_STATUS_SUCCESS == ret)) { - /* 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()); + ret = setAdvertiseData (BT_DATA_MANUFACTURER_DATA, + _manufacturer_data, + _manufacturer_data_length); } - - if (_manufacturer_data_length > 0) + + if (_local_name.length() > 0 && + (BLE_STATUS_SUCCESS == ret)) { - // 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; + uint8_t length = _local_name.length(); + ret = setAdvertiseData (BT_DATA_NAME_COMPLETE, + (const uint8_t*)_local_name.c_str(), + length); } - if (_service_data_length > 0) + if (_service_data_length > 0 && + (BLE_STATUS_SUCCESS == ret)) { /* Add Service Data (if it will fit) */ @@ -431,29 +485,18 @@ BLEDeviceManager::_advDataInit(void) 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++; + ret = setAdvertiseData (BT_DATA_SVC_DATA16, + _service_data_buf, + block_len); 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; + + return ret; } BLE_STATUS_T BLEDeviceManager::startAdvertising() @@ -470,7 +513,9 @@ BLE_STATUS_T BLEDeviceManager::startAdvertising() 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); + ret = bt_le_adv_start(&_adv_param, + _adv_data, _adv_data_idx, + _scan_rsp_data, _scan_rsp_data_idx); if (0 != ret) { pr_error(LOG_MODULE_APP, "[ADV] Start failed. Error: %d", ret); @@ -617,48 +662,119 @@ void BLEDeviceManager::setAdvertiseCritical(const char* macaddress) BLEUtils::macAddressString2BT(macaddress, _adv_accept_device); } -bool BLEDeviceManager::hasLocalName(const BLEDevice* device) const +bool BLEDeviceManager::getDataFromAdvertiseByType(const BLEDevice* device, + const uint8_t eir_type, + const uint8_t* &data, + uint8_t &data_len) const { - if (BLEUtils::isLocalBLE(*device) == true) - { - return (_local_name.length() != 0); - } - const uint8_t* adv_data = NULL; uint8_t adv_data_len = 0; + bool retval = false; + bool scan_response_proced = false; + 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) + while (NULL != adv_data) + { + while (adv_data_len > 1) { - return false; - } + uint8_t len = adv_data[0]; + uint8_t type = adv_data[1]; - 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; - } + /* Check for early termination */ + if ((len == 0) || ((len + 1) > adv_data_len)) { + break; + } + + if (type == eir_type) + { + if (len >= BLE_MAX_ADV_SIZE) + { + len = BLE_MAX_ADV_SIZE-1; + } + data = &adv_data[2]; + data_len = len - 1; + retval = true; + break; + } - if (type == BT_DATA_NAME_COMPLETE) + adv_data_len -= len + 1; + adv_data += len + 1; + } + if (retval == true || scan_response_proced == true) { - return true; + break; } + getDeviceScanResponseBuffer(device->bt_le_address(), + adv_data, + adv_data_len); + scan_response_proced = true; + } + return retval; +} - adv_data_len -= len + 1; - adv_data += len + 1; + +bool BLEDeviceManager::hasLocalName(const BLEDevice* device) const +{ + if (BLEUtils::isLocalBLE(*device) == true) + { + return (_local_name.length() != 0); } - return false; + + const uint8_t* local_name = NULL; + uint8_t local_name_len = 0; + bool retval = getDataFromAdvertiseByType(device, + BT_DATA_NAME_COMPLETE, + local_name, + local_name_len); + if (false == retval) + { + retval = getDataFromAdvertiseByType(device, + BT_DATA_NAME_SHORTENED, + local_name, + local_name_len); + } + return retval; +} + +bool BLEDeviceManager::hasManufacturerData(const BLEDevice* device) const +{ + if (BLEUtils::isLocalBLE(*device) == true) + { + return (_manufacturer_data_length != 0); + } + + const uint8_t* manufactgurer_data = NULL; + uint8_t manufactgurer_data_len = 0; + return getDataFromAdvertiseByType(device, + BT_DATA_MANUFACTURER_DATA, + manufactgurer_data, + manufactgurer_data_len); +} + +bool BLEDeviceManager::getManufacturerData (const BLEDevice* device, + uint8_t* manu_data, + uint8_t&manu_data_len) const +{ + if (BLEUtils::isLocalBLE(*device) == true) + { + return (_manufacturer_data_length != 0); + } + + const uint8_t* manufactgurer_data = NULL; + uint8_t manufactgurer_data_len = 0; + bool retval = getDataFromAdvertiseByType(device, + BT_DATA_MANUFACTURER_DATA, + manufactgurer_data, + manufactgurer_data_len); + if (retval) + { + memcpy (manu_data, manufactgurer_data, manufactgurer_data_len); + manu_data_len = manufactgurer_data_len; + } + return retval; } bool BLEDeviceManager::hasAdvertisedServiceUuid(const BLEDevice* device) const @@ -713,6 +829,41 @@ void BLEDeviceManager::getDeviceAdvertiseBuffer(const bt_addr_le_t* addr, return; } +void BLEDeviceManager::getDeviceScanResponseBuffer(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_scan_rsp_data[i]; + adv_len = _peer_peripheral_scan_rsp_data_len[i]; + return; + } + } + + // Connecting device + if (bt_addr_le_cmp(&_wait_for_connect_peripheral, addr) == 0) + { + adv_data = _wait_for_connect_peripheral_scan_rsp_data; + adv_len = _wait_for_connect_peripheral_scan_rsp_data_len; + return; + } + + // Available device + if (bt_addr_le_cmp(&_available_for_connect_peripheral, addr) == 0) + { + adv_data = _available_for_connect_peripheral_scan_rsp_data; + adv_len = _available_for_connect_peripheral_scan_rsp_data_len; + return; + } + return; +} + int BLEDeviceManager::advertisedServiceUuidCount(const BLEDevice* device) const { const uint8_t* adv_data = NULL; @@ -740,7 +891,7 @@ int BLEDeviceManager::advertisedServiceUuidCount(const BLEDevice* device) const uint8_t type = adv_data[1]; /* Check for early termination */ - if (len == 0) + if (len == 0 || ((len + 1) > adv_data_len)) { return service_cnt; } @@ -773,57 +924,34 @@ String BLEDeviceManager::localName(const BLEDevice* device) const { 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; + const uint8_t* local_name = NULL; + uint8_t local_name_len = 0; + String temp(""); + char local_name_buff[BLE_MAX_ADV_SIZE]; + bool retval = getDataFromAdvertiseByType(device, + BT_DATA_NAME_COMPLETE, + local_name, + local_name_len); + if (false == retval) + { + retval = getDataFromAdvertiseByType(device, + BT_DATA_NAME_SHORTENED, + local_name, + local_name_len); } - while (adv_data_len > 1) + if (true == retval) { - 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; - } - - /* Sid, 2/15/2017. Support both forms of data name. - */ - if (type == BT_DATA_NAME_COMPLETE || - type == BT_DATA_NAME_SHORTENED) + if (local_name_len >= BLE_MAX_ADV_SIZE) { - 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; + local_name_len = BLE_MAX_ADV_SIZE - 1; } - - adv_data_len -= len + 1; - adv_data += len + 1; + memcpy(local_name_buff, local_name, local_name_len); + local_name_buff[local_name_len] = '\0'; + temp = local_name_buff; } - - String temp(localname_string); + return temp; } @@ -867,14 +995,9 @@ String BLEDeviceManager::advertisedServiceUuid(const BLEDevice* device, int inde uint8_t type = adv_data[1]; /* Check for early termination */ - if (len == 0) + if (len == 0 || ((len + 1) > adv_data_len)) { - 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); + break; } if (type == BT_DATA_UUID16_ALL || @@ -947,7 +1070,9 @@ bool BLEDeviceManager::connect(BLEDevice &device) 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); + memcpy(_wait_for_connect_peripheral_scan_rsp_data, _available_for_connect_peripheral_scan_rsp_data, BLE_MAX_ADV_SIZE); _wait_for_connect_peripheral_adv_data_len = _available_for_connect_peripheral_adv_data_len; + _wait_for_connect_peripheral_scan_rsp_data_len = _available_for_connect_peripheral_scan_rsp_data_len; _wait_for_connect_peripheral_adv_rssi = _available_for_connect_peripheral_adv_rssi; startScanning(); @@ -1016,6 +1141,10 @@ bool BLEDeviceManager::connectToDevice(BLEDevice &device) _wait_for_connect_peripheral_adv_data, BLE_MAX_ADV_SIZE); _peer_peripheral_adv_data_len[i] = _wait_for_connect_peripheral_adv_data_len; + memcpy(_peer_peripheral_scan_rsp_data[i], + _wait_for_connect_peripheral_scan_rsp_data, + BLE_MAX_ADV_SIZE); + _peer_peripheral_scan_rsp_data_len[i] = _wait_for_connect_peripheral_scan_rsp_data_len; _peer_peripheral_adv_rssi[i] = _wait_for_connect_peripheral_adv_rssi; } } @@ -1240,10 +1369,12 @@ BLEDevice BLEDeviceManager::available() 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); + memcpy(_available_for_connect_peripheral_scan_rsp_data, _peer_scan_rsp_data[index], BLE_MAX_ADV_SIZE); + _available_for_connect_peripheral_scan_rsp_data_len = _peer_scan_rsp_data_len[index]; _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()); + _available_for_connect_peripheral_connectable = _peer_adv_connectable[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 if (_adv_duplicate_filter_enabled) { @@ -1257,7 +1388,8 @@ BLEDevice BLEDeviceManager::available() bool BLEDeviceManager::setAdvertiseBuffer(const bt_addr_le_t* bt_addr, const uint8_t *ad, uint8_t data_len, - int8_t rssi) + int8_t rssi, + bool connectable) { bt_addr_le_t* temp = NULL; uint64_t timestamp = millis(); @@ -1303,12 +1435,163 @@ bool BLEDeviceManager::setAdvertiseBuffer(const bt_addr_le_t* bt_addr, _peer_adv_rssi[index] = rssi; // Update the timestamp _peer_adv_mill[index] = timestamp; + _peer_adv_connectable[index] = connectable; + retval = true; + } + + return retval; +} + +bool BLEDeviceManager::setScanRespBuffer(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; + bool retval = false; + //pr_debug(LOG_MODULE_BLE, "%s-%d-1", __FUNCTION__, __LINE__); + for (i = 0; i < BLE_MAX_ADV_BUFFER_CFG; i++) + { + temp = &_peer_adv_buffer[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", __FUNCTION__, __LINE__); + if (index < BLE_MAX_ADV_BUFFER_CFG) + { + if (data_len > BLE_MAX_ADV_SIZE) + { + data_len = BLE_MAX_ADV_SIZE; + } + memcpy(_peer_scan_rsp_data[index], ad, data_len); + _peer_scan_rsp_data_len[index] = data_len; + //_peer_adv_rssi[index] = rssi; + // Update the timestamp + _peer_adv_mill[index] = timestamp; retval = true; } return retval; } + +uint8_t BLEDeviceManager::getTempAdvertiseIndexFromBuffer(const bt_addr_le_t* bt_addr) +{ + bt_addr_le_t* temp = NULL; + uint8_t i = 0; + + for (i = 0; i < BLE_MAX_ADV_BUFFER_CFG; i++) + { + temp = &_peer_temp_adv_buffer[i]; + + if (bt_addr_le_cmp(temp, bt_addr) == 0) + { + // The device alread in the buffer + break; + } + } + + return i; +} + +void BLEDeviceManager::setTempAdvertiseBuffer(const bt_addr_le_t* bt_addr, + int8_t rssi, + const uint8_t *ad, + uint8_t data_len, + bool connectable) +{ + bt_addr_le_t* temp = NULL; + uint8_t i = getTempAdvertiseIndexFromBuffer(bt_addr); + if (i >= BLE_MAX_ADV_BUFFER_CFG) + { + _peer_temp_dev_index = (_peer_temp_dev_index + 1) % BLE_MAX_ADV_BUFFER_CFG; + i = _peer_temp_dev_index; + } + + temp = &_peer_temp_adv_buffer[i]; + memcpy(temp, bt_addr, sizeof (bt_addr_le_t)); + if (data_len > BLE_MAX_ADV_SIZE) + { + data_len = BLE_MAX_ADV_SIZE; + } + + memcpy(_peer_temp_adv_data[i], ad, data_len); + _peer_temp_adv_data_len[i] = data_len; + _peer_temp_adv_connectable[i] = connectable; + + return; +} + +void BLEDeviceManager::advertiseAcceptHandler(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + const uint8_t *ad, + uint8_t data_len) +{ + 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 + { + const uint8_t *adv_data = ad; + uint8_t adv_data_len = data_len; + bool connectable = (BT_LE_ADV_NONCONN_IND != type); + bool update_advertise_data = true; + // The critical is accepted + // Find the oldest and expired buffer + if (BT_LE_ADV_SCAN_RSP == type) + { + update_advertise_data = false; + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + if (false == setScanRespBuffer(addr, ad, data_len, rssi)) + { + // Find the device in the ADV temp buffer + uint8_t tempIndex = getTempAdvertiseIndexFromBuffer(addr); + if (tempIndex < BLE_MAX_ADV_BUFFER_CFG) + { + adv_data = _peer_temp_adv_data[tempIndex]; + adv_data_len = _peer_temp_adv_data_len[tempIndex]; + connectable = _peer_temp_adv_connectable[tempIndex]; + update_advertise_data = true; + } + } + } + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + + if (true == update_advertise_data) + { + if (false == setAdvertiseBuffer(addr, + adv_data, + adv_data_len, + rssi, + connectable)) + { + pr_info(LOG_MODULE_BLE, "No buffer to store the ADV\n"); + } + else if (BT_LE_ADV_SCAN_RSP == type) + { + setScanRespBuffer(addr, ad, data_len, rssi); + } + } + } +} + void BLEDeviceManager::handleDeviceFound(const bt_addr_le_t *addr, int8_t rssi, uint8_t type, @@ -1319,60 +1602,52 @@ void BLEDeviceManager::handleDeviceFound(const bt_addr_le_t *addr, 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__); + // Filter address + if (BLEUtils::macAddressValid(_adv_accept_device) == true && + (memcmp(addr->val, _adv_accept_device.val, sizeof (addr->val)) != 0)) + { + pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); + return; + } + + while (data_len > 1) { - //pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); - // Filter address - if (BLEUtils::macAddressValid(_adv_accept_device) == true && - (memcmp(addr->val, _adv_accept_device.val, sizeof (addr->val)) != 0)) + uint8_t len = data[0]; + + /* Check for early termination */ + if (len == 0) { return; } - - 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; - } + if ((len + 1) > data_len) { // Sid. KW, cannot be (data_len < 2) + pr_info(LOG_MODULE_BLE, "AD malformed\n"); + return; + } - data_len -= len + 1; - data += len + 1; + if (true == advertiseDataProc(data[1], &data[2], len - 1)) + { + advertiseAcceptHandler(addr, rssi, type, ad, real_adv_len); + //pr_debug(LOG_MODULE_BLE, "%s-%d: Done", __FUNCTION__, __LINE__); + return; } - //pr_debug(LOG_MODULE_BLE, "%s: done", __FUNCTION__); + + data_len -= len + 1; + data += len + 1; + } + //pr_debug(LOG_MODULE_BLE, "%s: done", __FUNCTION__); + // Doesn't accept the ADV/scan data + // Check it in the buffer + if (BT_LE_ADV_SCAN_RSP == type) + { + // Find the ADV and set response + setScanRespBuffer(addr, ad, real_adv_len, rssi); + } + else + { + // Add advertise into buffer + setTempAdvertiseBuffer(addr, rssi, ad, real_adv_len, BT_LE_ADV_NONCONN_IND != type); } } diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.h b/libraries/CurieBLE/src/internal/BLEDeviceManager.h index e09bfe6c..118c4c11 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.h +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.h @@ -140,6 +140,10 @@ class BLEDeviceManager */ void setManufacturerData(const unsigned char manufacturerData[], unsigned char manufacturerDataLength); + bool getManufacturerData (const BLEDevice* device, + uint8_t* manu_data, + uint8_t&manu_data_len) const; + bool hasManufacturerData(const BLEDevice* device) const; /** * Set the local name that the BLE Peripheral Device advertises @@ -350,6 +354,9 @@ class BLEDeviceManager protected: private: + BLE_STATUS_T setAdvertiseData (uint8_t type, + const uint8_t* data, + uint8_t length); BLE_STATUS_T _advDataInit(void); bool advertiseDataProc(uint8_t type, const uint8_t *dataPtr, @@ -357,13 +364,36 @@ class BLEDeviceManager bool setAdvertiseBuffer(const bt_addr_le_t* bt_addr, const uint8_t *ad, uint8_t data_len, - int8_t rssi); + int8_t rssi, + bool connectable); void getDeviceAdvertiseBuffer(const bt_addr_le_t* addr, const uint8_t* &adv_data, uint8_t &adv_len) const; + bool setScanRespBuffer(const bt_addr_le_t* bt_addr, + const uint8_t *ad, + uint8_t data_len, + int8_t rssi); + void getDeviceScanResponseBuffer(const bt_addr_le_t* addr, + const uint8_t* &adv_data, + uint8_t &adv_len) const; + bool getDataFromAdvertiseByType(const BLEDevice* device, + const uint8_t eir_type, + const uint8_t* &data, + uint8_t &data_len) const; bool disconnectSingle(const bt_addr_le_t *peer); void updateDuplicateFilter(const bt_addr_le_t* addr); bool deviceInDuplicateFilterBuffer(const bt_addr_le_t* addr); + void advertiseAcceptHandler(const bt_addr_le_t *addr, + int8_t rssi, + uint8_t type, + const uint8_t *ad, + uint8_t data_len); + void setTempAdvertiseBuffer(const bt_addr_le_t* bt_addr, + int8_t rssi, + const uint8_t *ad, + uint8_t data_len, + bool connectable); + uint8_t getTempAdvertiseIndexFromBuffer(const bt_addr_le_t* bt_addr); private: uint16_t _min_conn_interval; @@ -377,7 +407,19 @@ class BLEDeviceManager 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]; + uint8_t _peer_scan_rsp_data[BLE_MAX_ADV_BUFFER_CFG][BLE_MAX_ADV_SIZE]; + uint8_t _peer_scan_rsp_data_len[BLE_MAX_ADV_BUFFER_CFG]; int8_t _peer_adv_rssi[BLE_MAX_ADV_BUFFER_CFG]; + bool _peer_adv_connectable[BLE_MAX_ADV_BUFFER_CFG]; + + // The accept critical may include in scan response + bt_addr_le_t _peer_temp_adv_buffer[BLE_MAX_ADV_BUFFER_CFG]; + uint8_t _peer_temp_dev_index; + uint8_t _peer_temp_adv_data[BLE_MAX_ADV_BUFFER_CFG][BLE_MAX_ADV_SIZE]; + uint8_t _peer_temp_adv_data_len[BLE_MAX_ADV_BUFFER_CFG]; + bool _peer_temp_adv_connectable[BLE_MAX_ADV_BUFFER_CFG]; + + // The critical for central scan bt_data_t _adv_accept_critical; // The filters for central device String _adv_critical_local_name; bt_uuid_128_t _adv_critical_service_uuid; @@ -386,12 +428,17 @@ class BLEDeviceManager 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; + uint8_t _wait_for_connect_peripheral_scan_rsp_data[BLE_MAX_ADV_SIZE]; + uint8_t _wait_for_connect_peripheral_scan_rsp_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; + uint8_t _available_for_connect_peripheral_scan_rsp_data[BLE_MAX_ADV_SIZE]; + uint8_t _available_for_connect_peripheral_scan_rsp_data_len; int8_t _available_for_connect_peripheral_adv_rssi; + bool _available_for_connect_peripheral_connectable; volatile bool _connecting; // For peripheral @@ -412,6 +459,8 @@ class BLEDeviceManager uint8_t _adv_type; bt_data_t _adv_data[6]; // KW: fount _advDataInit() can use 6 slots. size_t _adv_data_idx; + bt_data_t _scan_rsp_data[6]; + size_t _scan_rsp_data_idx; String _local_name; // Peripheral states @@ -433,6 +482,8 @@ class BLEDeviceManager 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_scan_rsp_data[BLE_MAX_CONN_CFG][BLE_MAX_ADV_SIZE]; + uint8_t _peer_peripheral_scan_rsp_data_len[BLE_MAX_CONN_CFG]; uint8_t _peer_peripheral_adv_rssi[BLE_MAX_CONN_CFG]; bt_addr_le_t _peer_duplicate_address_buffer[BLE_MAX_ADV_FILTER_SIZE_CFG]; uint8_t _duplicate_filter_header; From c9129264b89dc92bcb17233b864550660b25200a Mon Sep 17 00:00:00 2001 From: lianggao Date: Thu, 2 Mar 2017 15:18:23 +0800 Subject: [PATCH 068/125] Jira 868, BLE Peripheral stack crash with long write, git456 Root cause: - Upon processing the write event, The received user data was casted incorrectly using the base class. Resulted in using an incorrect offset that caused memcpy to use incorrect address and length. Code Mods: 1. BLECallbacks.cpp: Adjust the typecast order, from base class to child class in profile_longwrite_process() and profile_longflush_process(). --- libraries/CurieBLE/src/internal/BLECallbacks.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/libraries/CurieBLE/src/internal/BLECallbacks.cpp b/libraries/CurieBLE/src/internal/BLECallbacks.cpp index edfcaeec..328c196b 100644 --- a/libraries/CurieBLE/src/internal/BLECallbacks.cpp +++ b/libraries/CurieBLE/src/internal/BLECallbacks.cpp @@ -77,7 +77,13 @@ ssize_t profile_longwrite_process(struct bt_conn *conn, const void *buf, uint16_t len, uint16_t offset) { - BLECharacteristicImp *blecharacteritic = (BLECharacteristicImp*)attr->user_data; + BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; + BLEAttributeType type = bleattr->type(); + if (BLETypeCharacteristic != type) + { + return 0; + } + BLECharacteristicImp *blecharacteritic = (BLECharacteristicImp*)bleattr; blecharacteritic->setBuffer((const uint8_t *) buf, len, offset); @@ -88,7 +94,13 @@ int profile_longflush_process(struct bt_conn *conn, const struct bt_gatt_attr *attr, uint8_t flags) { - BLECharacteristicImp *blecharacteritic = (BLECharacteristicImp*)attr->user_data; + BLEAttribute *bleattr = (BLEAttribute *)attr->user_data; + BLEAttributeType type = bleattr->type(); + if (BLETypeCharacteristic != type) + { + return 0; + } + BLECharacteristicImp *blecharacteritic = (BLECharacteristicImp*)bleattr; switch (flags) { From 17d642b7324c7610b6c5a669fc4ffc3e4230d9e0 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 31 Jan 2017 09:46:55 +0100 Subject: [PATCH 069/125] Fix arduino101load argument passing if binary path contains spaces --- platform.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform.txt b/platform.txt index 308ce967..e60f5ceb 100644 --- a/platform.txt +++ b/platform.txt @@ -103,7 +103,7 @@ tools.arduino101load.cmd.path={runtime.tools.arduino101load.path}/arduino101load tools.arduino101load.upload.params.verbose=verbose tools.arduino101load.upload.params.quiet=quiet -tools.arduino101load.upload.pattern="{cmd.path}" "{runtime.tools.arduino101load.path}/x86/bin" {build.path}/{build.project_name}.bin {serial.port} "{upload.verbose}" {ble.fw.string} {ble.fw.position} +tools.arduino101load.upload.pattern="{cmd.path}" "{runtime.tools.arduino101load.path}/x86/bin" "{build.path}/{build.project_name}.bin" {serial.port} "{upload.verbose}" {ble.fw.string} {ble.fw.position} # This is needed to avoid an error on unexistent fields tools.arduino101load.erase.params.verbose= From 037654751d5e65a03882f8837f60962cb7fe3559 Mon Sep 17 00:00:00 2001 From: Sidney Leung Date: Fri, 3 Mar 2017 17:00:43 -0800 Subject: [PATCH 070/125] Jira 870, Change sensortag_button.ino to use localName Feature added: - Since the BLE library now support the Scan Response Data processing, update the sketch to look for the sensorTag localName instead of its specific unit address. Cod mods: 1. sensortag_button.ino: - Replace MAC address comparison with localName string comparison. --- .../sensortag_button/sensortag_button.ino | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino b/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino index de85c6a8..3209012f 100644 --- a/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino +++ b/libraries/CurieBLE/examples/central/sensortag_button/sensortag_button.ino @@ -43,23 +43,12 @@ void loop() { Serial.print(peripheral.advertisedServiceUuid()); Serial.println(); - /*see if peripheral is a SensorTag - The localName SensorTag is in the Scan Response data packet - In this release we do not have the feature that gets the scan response data and hence - the local name in the scan is blank - We have to explicitly find the BLE mac address - Please use another deviice like nrfConnect app to discover the Bluetooth Address - */ - //if (peripheral.localName() == "SensorTag") { - - - /****************************************************** - * ATTENTION: - * Change to the mac address according to your device! - * Use a central app that can display the BT MAC address - * ****************************************************** - */ - if (peripheral.address() == "68:C9:0B:06:BC:81") { + /* see if peripheral is a SensorTag + * The localName, CC2650 SensorTag, is in the Scan Response Data packet. + * If this is not the expected name, please change the following + * if-statement accordingly. + */ + if (peripheral.localName() == "CC2650 SensorTag") { // stop scanning BLE.stopScan(); From 76f7d75160e04ec37eda3e189b705f9a5a4f6b74 Mon Sep 17 00:00:00 2001 From: Noel F Paz Date: Wed, 8 Mar 2017 17:29:03 -0800 Subject: [PATCH 071/125] Modify comments to point to Paul Stoffregen's Serial Flash library in github --- .../examples/CopyFromSerial/CopyFromSerial.ino | 3 ++- .../examples/EraseEverything/EraseEverything.ino | 3 ++- libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino | 3 ++- libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino | 3 ++- .../examples/RawHardwareTest/RawHardwareTest.ino | 4 ++-- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino b/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino index 85723894..68a30af3 100644 --- a/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino +++ b/libraries/CurieSerialFlash/examples/CopyFromSerial/CopyFromSerial.ino @@ -58,7 +58,8 @@ * * SerialFlash library API: https://github.com/PaulStoffregen/SerialFlash * - * This example depens on http://librarymanager/all#SerialFlash&SPI (clickme!) + * This example depends on Paul Stoffregen's SerialFlash library. + * Download at https://github.com/PaulStoffregen/SerialFlash. */ #include diff --git a/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino b/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino index c79bf5fa..43425324 100644 --- a/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino +++ b/libraries/CurieSerialFlash/examples/EraseEverything/EraseEverything.ino @@ -1,4 +1,5 @@ -// This example depens on http://librarymanager/all#SerialFlash&SPI (clickme!) +// This example depends on Paul Stoffregen's SerialFlash library +// Download at https://github.com/PaulStoffregen/SerialFlash #include #include diff --git a/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino b/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino index 3e10bc84..e9f5ab9a 100755 --- a/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino +++ b/libraries/CurieSerialFlash/examples/FileWrite/FileWrite.ino @@ -1,4 +1,5 @@ -// This example depens on http://librarymanager/all#SerialFlash&SPI (clickme!) +// This example depends on Paul Stoffregen's SerialFlash library +// Download at https://github.com/PaulStoffregen/SerialFlash #include #include diff --git a/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino b/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino index 21888537..6dfda8b7 100644 --- a/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino +++ b/libraries/CurieSerialFlash/examples/ListFiles/ListFiles.ino @@ -1,4 +1,5 @@ -// This example depens on http://librarymanager/all#SerialFlash&SPI (clickme!) +// This example depends on Paul Stoffregen's SerialFlash library +// Download at https://github.com/PaulStoffregen/SerialFlash #include #include diff --git a/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino b/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino index 453bb123..a124010f 100644 --- a/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino +++ b/libraries/CurieSerialFlash/examples/RawHardwareTest/RawHardwareTest.ino @@ -19,8 +19,8 @@ // You MUST post the complete output of this program, and // the exact part number and manufacturer of the chip. // -// This example depens on http://librarymanager/all#SerialFlash&SPI (clickme!) - +// This example depends on Paul Stoffregen's SerialFlash library +// Download at https://github.com/PaulStoffregen/SerialFlash #include #include From 9c079d15befd3faf2701ab81fdf85d2cf3b9c428 Mon Sep 17 00:00:00 2001 From: Erik Nyquist Date: Wed, 8 Mar 2017 14:01:07 -0800 Subject: [PATCH 072/125] SerialFlash: fix compile errors Old SerialFlash directory was still there-- delete it --- .../CopyFromSerial/CopyFromSerial.ino | 267 ------------------ 1 file changed, 267 deletions(-) delete mode 100644 libraries/SerialFlash/examples/CopyFromSerial/CopyFromSerial.ino diff --git a/libraries/SerialFlash/examples/CopyFromSerial/CopyFromSerial.ino b/libraries/SerialFlash/examples/CopyFromSerial/CopyFromSerial.ino deleted file mode 100644 index 6d1af440..00000000 --- a/libraries/SerialFlash/examples/CopyFromSerial/CopyFromSerial.ino +++ /dev/null @@ -1,267 +0,0 @@ -/* - * This is free and unencumbered software released into the public domain. - * - * Anyone is free to copy, modify, publish, use, compile, sell, or - * distribute this software, either in source code form or as a compiled - * binary, for any purpose, commercial or non-commercial, and by any - * means. - * - * In jurisdictions that recognize copyright laws, the author or authors - * of this software dedicate any and all copyright interest in the - * software to the public domain. We make this dedication for the benefit - * of the public at large and to the detriment of our heirs and - * successors. We intend this dedication to be an overt act of - * relinquishment in perpetuity of all present and future rights to this - * software under copyright law. - * - * 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 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. - * - * For more information, please refer to - * ------------------------------------------------------------------------- - * - * This is example code to 1) format an SPI Flash chip, and 2) copy raw - * audio files (mono channel, 16 bit signed, 44100Hz) to it using the - * SerialFlash library. The audio can then be played back using the - * AudioPlaySerialflashRaw object in the Teensy Audio library. - * - * To convert a .wav file to the proper .RAW format, use sox: - * sox input.wav -r 44100 -b 16 --norm -e signed-integer -t raw OUTPUT.RAW remix 1,2 - * - * Note that the OUTPUT.RAW filename must be all caps and contain only the following - * characters: A-Z, 0-9, comma, period, colon, dash, underscore. (The SerialFlash - * library converts filenames to caps, so to avoid confusion we just enforce it here). - * - * It is a little difficult to see what is happening; aswe are using the Serial port - * to upload files, we can't just throw out debug information. Instead, we use the LED - * (pin 13) to convey state. - * - * While the chip is being formatted, the LED (pin 13) will toggle at 1Hz rate. When - * the formatting is done, it flashes quickly (10Hz) for one second, then stays on - * solid. When nothing has been received for 3 seconds, the upload is assumed to be - * completed, and the light goes off. - * - * Use the 'rawfile-uploader.py' python script (included in the extras folder) to upload - * the files. You can start the script as soon as the Teensy is turned on, and the - * USB serial upload will just buffer and wait until the flash is formatted. - * - * This code was written by Wyatt Olson (originally as part - * of Drum Master http://drummaster.digitalcave.ca and later modified into a - * standalone sample). - * - * Enjoy! - * - * SerialFlash library API: https://github.com/PaulStoffregen/SerialFlash - */ - -#include -#include - -//Buffer sizes -#define USB_BUFFER_SIZE 128 -#define FLASH_BUFFER_SIZE 4096 - -//Max filename length (8.3 plus a null char terminator) -#define FILENAME_STRING_SIZE 13 - -//State machine -#define STATE_START 0 -#define STATE_SIZE 1 -#define STATE_CONTENT 2 - -//Special bytes in the communication protocol -#define BYTE_START 0x7e -#define BYTE_ESCAPE 0x7d -#define BYTE_SEPARATOR 0x7c - -#define CSPIN 21 - -void setup(){ - Serial.begin(9600); // initialize Serial communication - while(!Serial) ; // wait for serial port to connect. - - pinMode(13, OUTPUT); - - SerialFlash.begin(CSPIN); - - //We start by formatting the flash... - uint8_t id[5]; - SerialFlash.readID(id); - SerialFlash.eraseAll(); - - //Flash LED at 1Hz while formatting - while (!SerialFlash.ready()) { - delay(500); - digitalWrite(13, HIGH); - delay(500); - digitalWrite(13, LOW); - } - - //Quickly flash LED a few times when completed, then leave the light on solid - for(uint8_t i = 0; i < 10; i++){ - delay(100); - digitalWrite(13, HIGH); - delay(100); - digitalWrite(13, LOW); - } - digitalWrite(13, HIGH); - - //We are now going to wait for the upload program - while(!Serial.available()); - - SerialFlashFile flashFile; - - uint8_t state = STATE_START; - uint8_t escape = 0; - uint8_t fileSizeIndex = 0; - uint32_t fileSize = 0; - char filename[FILENAME_STRING_SIZE]; - - char usbBuffer[USB_BUFFER_SIZE]; - uint8_t flashBuffer[FLASH_BUFFER_SIZE]; - - uint16_t flashBufferIndex = 0; - uint8_t filenameIndex = 0; - - uint32_t lastReceiveTime = millis(); - - //We assume the serial receive part is finished when we have not received something for 3 seconds - while(Serial.available() || lastReceiveTime + 3000 > millis()){ - uint16_t available = Serial.readBytes(usbBuffer, USB_BUFFER_SIZE); - if (available){ - lastReceiveTime = millis(); - } - - for (uint16_t usbBufferIndex = 0; usbBufferIndex < available; usbBufferIndex++){ - uint8_t b = usbBuffer[usbBufferIndex]; - - if (state == STATE_START){ - //Start byte. Repeat start is fine. - if (b == BYTE_START){ - for (uint8_t i = 0; i < FILENAME_STRING_SIZE; i++){ - filename[i] = 0x00; - } - filenameIndex = 0; - } - //Valid characters are A-Z, 0-9, comma, period, colon, dash, underscore - else if ((b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '.' || b == ',' || b == ':' || b == '-' || b == '_'){ - filename[filenameIndex++] = b; - if (filenameIndex >= FILENAME_STRING_SIZE){ - //Error name too long - flushError(); - return; - } - } - //Filename end character - else if (b == BYTE_SEPARATOR){ - if (filenameIndex == 0){ - //Error empty filename - flushError(); - return; - } - - //Change state - state = STATE_SIZE; - fileSizeIndex = 0; - fileSize = 0; - - } - //Invalid character - else { - //Error bad filename - flushError(); - return; - } - } - //We read 4 bytes as a uint32_t for file size - else if (state == STATE_SIZE){ - if (fileSizeIndex < 4){ - fileSize = (fileSize << 8) + b; - fileSizeIndex++; - } - else if (b == BYTE_SEPARATOR){ - state = STATE_CONTENT; - flashBufferIndex = 0; - escape = 0; - - if (SerialFlash.exists(filename)){ - SerialFlash.remove(filename); //It doesn't reclaim the space, but it does let you create a new file with the same name. - } - - //Create a new file and open it for writing - if (SerialFlash.create(filename, fileSize)) { - flashFile = SerialFlash.open(filename); - if (!flashFile) { - //Error flash file open - flushError(); - return; - } - } - else { - //Error flash create (no room left?) - flushError(); - return; - } - } - else { - //Error invalid length requested - flushError(); - return; - } - } - else if (state == STATE_CONTENT){ - //Previous byte was escaped; unescape and add to buffer - if (escape){ - escape = 0; - flashBuffer[flashBufferIndex++] = b ^ 0x20; - } - //Escape the next byte - else if (b == BYTE_ESCAPE){ - //Serial.println("esc"); - escape = 1; - } - //End of file - else if (b == BYTE_START){ - //Serial.println("End of file"); - state = STATE_START; - flashFile.write(flashBuffer, flashBufferIndex); - flashFile.close(); - flashBufferIndex = 0; - } - //Normal byte; add to buffer - else { - flashBuffer[flashBufferIndex++] = b; - } - - //The buffer is filled; write to SD card - if (flashBufferIndex >= FLASH_BUFFER_SIZE){ - flashFile.write(flashBuffer, FLASH_BUFFER_SIZE); - flashBufferIndex = 0; - } - } - } - } - - //Success! Turn the light off. - digitalWrite(13, LOW); -} - -void loop(){ - //Do nothing. -} - -void flushError(){ - uint32_t lastReceiveTime = millis(); - char usbBuffer[USB_BUFFER_SIZE]; - //We assume the serial receive part is finished when we have not received something for 3 seconds - while(Serial.available() || lastReceiveTime + 3000 > millis()){ - if (Serial.readBytes(usbBuffer, USB_BUFFER_SIZE)){ - lastReceiveTime = millis(); - } - } -} From 80cf4edb532693c2dcad4f6bbbcbaa23dd655e16 Mon Sep 17 00:00:00 2001 From: Yashaswini Hanji Date: Fri, 10 Mar 2017 13:02:56 -0800 Subject: [PATCH 073/125] [TEMP] remove test direcyory from CurieBLE examples --- .../examples/test/CallbackLED/CallbackLED.ino | 90 -- .../Genuino101CurieBLEHeartRateMonitor.ino | 112 --- .../StandardFirmataBLE/BLEStream.h | 243 ----- .../StandardFirmataBLE/StandardFirmataBLE.ino | 853 ------------------ .../StandardFirmataBLE/bleConfig.h | 112 --- .../discoveratperipheral.ino | 124 --- .../profileatcentral/profileatcentral.ino | 91 -- 7 files changed, 1625 deletions(-) delete mode 100644 libraries/CurieBLE/examples/test/CallbackLED/CallbackLED.ino delete mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino delete mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/BLEStream.h delete mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino delete mode 100644 libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/bleConfig.h delete mode 100644 libraries/CurieBLE/examples/test/discoveratperipheral/discoveratperipheral.ino delete mode 100644 libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.ino diff --git a/libraries/CurieBLE/examples/test/CallbackLED/CallbackLED.ino b/libraries/CurieBLE/examples/test/CallbackLED/CallbackLED.ino deleted file mode 100644 index 7cbcd21b..00000000 --- a/libraries/CurieBLE/examples/test/CallbackLED/CallbackLED.ino +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino b/libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino deleted file mode 100644 index 5428572f..00000000 --- a/libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -/* - * Sketch: Genuino101CurieBLEHeartRateMonitor.ino. - * - * Description: - * 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 - } -} - -/* - 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 -*/ - - diff --git a/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/BLEStream.h b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/BLEStream.h deleted file mode 100644 index 87256762..00000000 --- a/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/BLEStream.h +++ /dev/null @@ -1,243 +0,0 @@ -/* - 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 deleted file mode 100644 index 398993f9..00000000 --- a/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino +++ /dev/null @@ -1,853 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -/* - * Sketch: StandardFirmataBLE.ino. - * - * Description: - * 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. - * - * Notes: - * - Please use the link stated at the end of this file to - * download a host s/w package. - */ - -#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 -} - -/* - 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 -*/ - - diff --git a/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/bleConfig.h b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/bleConfig.h deleted file mode 100644 index aeb96a81..00000000 --- a/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/bleConfig.h +++ /dev/null @@ -1,112 +0,0 @@ -/*================================================================================================== - * 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/discoveratperipheral/discoveratperipheral.ino b/libraries/CurieBLE/examples/test/discoveratperipheral/discoveratperipheral.ino deleted file mode 100644 index 8de71767..00000000 --- a/libraries/CurieBLE/examples/test/discoveratperipheral/discoveratperipheral.ino +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -/* - * Sketch: discoveratperipheral.ino. - * - * Description: - * This is a BLE Central sketch that looks for a particular - * Characteristic in a connected Peripheral to write to. The - * Peripheral with the special Characteristic will blink its - * LED. - * - * Notes: - * - This sketch is Arduino BLE Peripheral LED example. - * Please see licensing at the bottom of this file. - * - Expected Peripheral Characteristic: 19b10000e8f2537e4f6cd104768a1214 - */ - -#include - -// LED pin -#define LED_PIN 13 - -// create service -BLEService ledService("19b10000e8f2537e4f6cd104768a1214"); - -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.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()); - - controlLed(central); - // central disconnected - Serial.print(F("Disconnected from central: ")); - Serial.println(central.address()); - } -} - -void controlLed(BLEDevice ¢ral) -{ - if (central.discoverAttributes() == false) - { - Serial.println("Discover failed, Disconnecting..."); - central.disconnect(); - return; - } - - BLECharacteristic ledCharacteristic = central.characteristic("19b10101-e8f2-537e-4f6c-d104768a1214"); - - if (!ledCharacteristic) - { - central.disconnect(); - //while(1) - { - Serial.println("Central does not have LED characteristic!"); - delay(5000); - } - return; - } - - ledCharacteristic.subscribe(); - - unsigned char ledstate = 0; - - while (central.connected()) - { - if (ledstate == 1) - { - ledstate = 0; - } - else - { - ledstate = 1; - } - ledCharacteristic.write(&ledstate, sizeof(ledstate)); - delay(5000); - } - Serial.print("Disconnected"); - Serial.println(central.address()); -} - - -/* - 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 -*/ - diff --git a/libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.ino b/libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.ino deleted file mode 100644 index 2fe20520..00000000 --- a/libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.ino +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2017 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -/* - * Sketch: profileatcentral.ino - * - * Description: - * This is a BLE Central sketch that demostrates the setting of the - * BLE descriptor. - * - * Notes: - * - This sketch is based on the Arduino BLE Peripheral LED example. - * Please refer to licensing agreement at the bottom of this file. - */ - -#include "CurieBLE.h" -#include -// LED pin -#define LED_PIN 13 -BLEService ledService("19b10100e8f2537e4f6cd104768a1214"); - -BLECharacteristic switchCharacteristic("19b10101e8f2537e4f6cd104768a1214", BLERead | BLEWrite | BLENotify, 1); - -BLEDescriptor switchDescriptor("2901", "switch"); - -void setup() { - Serial.begin(115200); - - // set LED pin to output mode - pinMode(LED_PIN, OUTPUT); - - // begin initialization - BLE.begin(); - Serial.println(BLE.address()); - ledService.addCharacteristic(switchCharacteristic); - switchCharacteristic.addDescriptor(switchDescriptor); - BLE.addService(ledService); - - BLE.scanForName("LED"); -} - - -void loop() { - BLEDevice peripheral = BLE.available(); - if (peripheral) - { - Serial.println(peripheral.address()); - - BLE.stopScan(); - - if (peripheral.connect()) - { - Serial.print("Connected: "); - Serial.println(peripheral.address()); - while (peripheral.connected()) - { - delay (1000); - } - } - else - { - Serial.println("Failed to connect!"); - } - delay (4000); - BLE.scanForName("LED"); - } -} - - -/* - 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 -*/ - - From 752d8726009ef02ce59728c51e946ef6babc0c71 Mon Sep 17 00:00:00 2001 From: Yashaswini Hanji Date: Fri, 10 Mar 2017 14:19:21 -0800 Subject: [PATCH 074/125] Add test directory for CurieBLE examples --- .../examples/test/CallbackLED/CallbackLED.ino | 90 ++ .../Genuino101CurieBLEHeartRateMonitor.ino | 112 +++ .../StandardFirmataBLE/BLEStream.h | 243 +++++ .../StandardFirmataBLE/StandardFirmataBLE.ino | 853 ++++++++++++++++++ .../StandardFirmataBLE/bleConfig.h | 112 +++ .../discoveratperipheral.ino | 124 +++ .../profileatcentral/profileatcentral.ino | 91 ++ 7 files changed, 1625 insertions(+) create mode 100644 libraries/CurieBLE/examples/test/CallbackLED/CallbackLED.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/discoveratperipheral/discoveratperipheral.ino create mode 100644 libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.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/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino b/libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino new file mode 100644 index 00000000..5428572f --- /dev/null +++ b/libraries/CurieBLE/examples/test/CommunitySketches/Genuino101CurieBLEHeartRateMonitor/Genuino101CurieBLEHeartRateMonitor.ino @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: Genuino101CurieBLEHeartRateMonitor.ino. + * + * Description: + * 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 + } +} + +/* + 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 +*/ + + 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..398993f9 --- /dev/null +++ b/libraries/CurieBLE/examples/test/CommunitySketches/StandardFirmataBLE/StandardFirmataBLE.ino @@ -0,0 +1,853 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: StandardFirmataBLE.ino. + * + * Description: + * 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. + * + * Notes: + * - Please use the link stated at the end of this file to + * download a host s/w package. + */ + +#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 +} + +/* + 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 +*/ + + 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/discoveratperipheral/discoveratperipheral.ino b/libraries/CurieBLE/examples/test/discoveratperipheral/discoveratperipheral.ino new file mode 100644 index 00000000..8de71767 --- /dev/null +++ b/libraries/CurieBLE/examples/test/discoveratperipheral/discoveratperipheral.ino @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: discoveratperipheral.ino. + * + * Description: + * This is a BLE Central sketch that looks for a particular + * Characteristic in a connected Peripheral to write to. The + * Peripheral with the special Characteristic will blink its + * LED. + * + * Notes: + * - This sketch is Arduino BLE Peripheral LED example. + * Please see licensing at the bottom of this file. + * - Expected Peripheral Characteristic: 19b10000e8f2537e4f6cd104768a1214 + */ + +#include + +// LED pin +#define LED_PIN 13 + +// create service +BLEService ledService("19b10000e8f2537e4f6cd104768a1214"); + +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.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()); + + controlLed(central); + // central disconnected + Serial.print(F("Disconnected from central: ")); + Serial.println(central.address()); + } +} + +void controlLed(BLEDevice ¢ral) +{ + if (central.discoverAttributes() == false) + { + Serial.println("Discover failed, Disconnecting..."); + central.disconnect(); + return; + } + + BLECharacteristic ledCharacteristic = central.characteristic("19b10101-e8f2-537e-4f6c-d104768a1214"); + + if (!ledCharacteristic) + { + central.disconnect(); + //while(1) + { + Serial.println("Central does not have LED characteristic!"); + delay(5000); + } + return; + } + + ledCharacteristic.subscribe(); + + unsigned char ledstate = 0; + + while (central.connected()) + { + if (ledstate == 1) + { + ledstate = 0; + } + else + { + ledstate = 1; + } + ledCharacteristic.write(&ledstate, sizeof(ledstate)); + delay(5000); + } + Serial.print("Disconnected"); + Serial.println(central.address()); +} + + +/* + 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 +*/ + diff --git a/libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.ino b/libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.ino new file mode 100644 index 00000000..2fe20520 --- /dev/null +++ b/libraries/CurieBLE/examples/test/profileatcentral/profileatcentral.ino @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: profileatcentral.ino + * + * Description: + * This is a BLE Central sketch that demostrates the setting of the + * BLE descriptor. + * + * Notes: + * - This sketch is based on the Arduino BLE Peripheral LED example. + * Please refer to licensing agreement at the bottom of this file. + */ + +#include "CurieBLE.h" +#include +// LED pin +#define LED_PIN 13 +BLEService ledService("19b10100e8f2537e4f6cd104768a1214"); + +BLECharacteristic switchCharacteristic("19b10101e8f2537e4f6cd104768a1214", BLERead | BLEWrite | BLENotify, 1); + +BLEDescriptor switchDescriptor("2901", "switch"); + +void setup() { + Serial.begin(115200); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial.println(BLE.address()); + ledService.addCharacteristic(switchCharacteristic); + switchCharacteristic.addDescriptor(switchDescriptor); + BLE.addService(ledService); + + BLE.scanForName("LED"); +} + + +void loop() { + BLEDevice peripheral = BLE.available(); + if (peripheral) + { + Serial.println(peripheral.address()); + + BLE.stopScan(); + + if (peripheral.connect()) + { + Serial.print("Connected: "); + Serial.println(peripheral.address()); + while (peripheral.connected()) + { + delay (1000); + } + } + else + { + Serial.println("Failed to connect!"); + } + delay (4000); + BLE.scanForName("LED"); + } +} + + +/* + 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 +*/ + + From 312278182c64d015f00fa5cba9a6e39788e3cd44 Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Tue, 7 Mar 2017 15:01:31 -0800 Subject: [PATCH 075/125] -add error functions -add error functions that sets an error_code variable in shared memory and halt the ARC core -this will be used by CODK-M based firmware to toggle the fault led on the Arduino 101 --- cores/arduino/Arduino.h | 1 + cores/arduino/error.cpp | 49 +++++++++++++++++++ cores/arduino/error.h | 37 ++++++++++++++ .../framework/include/platform.h | 2 + 4 files changed, 89 insertions(+) create mode 100644 cores/arduino/error.cpp create mode 100644 cores/arduino/error.h diff --git a/cores/arduino/Arduino.h b/cores/arduino/Arduino.h index e656d4ee..a4cce47d 100644 --- a/cores/arduino/Arduino.h +++ b/cores/arduino/Arduino.h @@ -110,6 +110,7 @@ extern uint8_t pinmuxMode[NUM_DIGITAL_PINS]; #include "WMath.h" #include "HardwareSerial.h" #include "wiring_pulse.h" +#include "error.h" #endif // __cplusplus diff --git a/cores/arduino/error.cpp b/cores/arduino/error.cpp new file mode 100644 index 00000000..7371415e --- /dev/null +++ b/cores/arduino/error.cpp @@ -0,0 +1,49 @@ +/* +Copyright (c) 2017 Intel Corporation. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#include "error.h" + +void error_continue() +{ + error_continue(1); +} + +void error_continue(uint8_t error_code) +{ + 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); + shared_data->error_code = error_code; +} + +void error_halt() +{ + error_halt(1); +} + +void error_halt(uint8_t error_code) +{ + error_continue(error_code); + __asm__("flag 0x01") ; /* Halt the CPU */ +} diff --git a/cores/arduino/error.h b/cores/arduino/error.h new file mode 100644 index 00000000..9ca0be1b --- /dev/null +++ b/cores/arduino/error.h @@ -0,0 +1,37 @@ +/* +Copyright (c) 2017 Intel Corporation. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef _Error_h +#define _Error_h + +#include +#include "os/os.h" +#include "infra/log.h" +#include "aux_regs.h" +#include "platform.h" + +extern void error_halt(); + +extern void error_halt(uint8_t error_code); + +extern void error_continue(); + +extern void error_continue(uint8_t error_code); + +#endif diff --git a/system/libarc32_arduino101/framework/include/platform.h b/system/libarc32_arduino101/framework/include/platform.h index 71c7e7f2..801f87f5 100644 --- a/system/libarc32_arduino101/framework/include/platform.h +++ b/system/libarc32_arduino101/framework/include/platform.h @@ -156,6 +156,8 @@ struct platform_shared_block_ { void* quark_restore_addr; uint32_t pm_int_status; + + uint8_t error_code; }; #define RAM_START 0xA8000000 From dea61b83818d92c6d11cd80d03368f5886f3f4f1 Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Wed, 22 Feb 2017 14:08:30 -0800 Subject: [PATCH 076/125] CDC-ACM CODK-M firmware compatibility changes -compatibility changes for CDC-ACM usb serial to work with upcoming CODK-M based firmware -use a fixed delay between writes instead of trying to mimick UART throttling --- cores/arduino/CDCSerialClass.cpp | 8 +- tatus | 4812 ++++++++++++++++++++++++++++++ 2 files changed, 4816 insertions(+), 4 deletions(-) create mode 100644 tatus diff --git a/cores/arduino/CDCSerialClass.cpp b/cores/arduino/CDCSerialClass.cpp index 5cd0083d..878cbac6 100644 --- a/cores/arduino/CDCSerialClass.cpp +++ b/cores/arduino/CDCSerialClass.cpp @@ -30,6 +30,7 @@ #include "wiring_digital.h" #include "variant.h" +#define CDCACM_FIXED_DELAY 64 extern void CDCSerial_Handler(void); extern void serialEventRun1(void) __attribute__((weak)); @@ -132,7 +133,7 @@ void CDCSerialClass::flush( void ) size_t CDCSerialClass::write( const uint8_t uc_data ) { - uint32_t retries = 1; + uint32_t retries = 2; if (!_shared_data->device_open || !_shared_data->host_open) return(0); @@ -147,9 +148,8 @@ size_t CDCSerialClass::write( const uint8_t uc_data ) _tx_buffer->data[_tx_buffer->head] = uc_data; _tx_buffer->head = i; - // Mimick the throughput of a typical UART by throttling the data - // flow according to the configured baud rate - delayMicroseconds(_writeDelayUsec); + // Just use a fixed delay to make it compatible with the CODK-M based firmware + delayMicroseconds(CDCACM_FIXED_DELAY); break; } } while (retries--); diff --git a/tatus b/tatus new file mode 100644 index 00000000..0bf5a18c --- /dev/null +++ b/tatus @@ -0,0 +1,4812 @@ +commit 178bb5f58570f9e71b7423940c11e4d6d97e8f1a +Author: Dino Tinitigan +Date: Wed Feb 22 14:08:30 2017 -0800 + + CDC-ACM CODK-M firmware compatibility changes + + -compatibility changes for CDC-ACM usb serial to work with upcoming + CODK-M based firmware + -use a fixed delay between writes instead of trying to mimick UART + throttling + +commit ee08a03b21d5c141020b328974c2392a8484dfa9 +Author: Erik Nyquist +Date: Fri Jan 27 16:28:38 2017 -0800 + + Use single dataReady() method, instead of accelDataReady/gyroDataReady + + This method is a bit more flexible. There is a version with no + parameters, which will return true if all enabled sensors have new + data. There is one overload, that takes a single 'flags' parameter, + which returns true if all enabled *and* selected sensors have new data. + Sensors are selectable through bit flags. For example; + + CurieIMU.dataReady() : this will return true if both the accel and gyro + are enabled, and both have new data ready. + + CurieIMU.dataReady(ACCEL | GYRO) : this will return true if both the + accel and gyro are enabled, and both have new data ready. + + CurieIMU.dataReady(GYRO) : this will return true if the gyro is + enabled and has new data ready. + + CurieIMU.dataReady(ACCEL) : this will return true if the accel is + enabled and has new data ready. + + This also involved changing the 'begin' method to allow selective + enabling of sensors, e.g. + + CurieIMU.begin() : same as it always has, initializes IMU, enabling + both accel and gyro + + CurieIMU.begin(ACCEL | GYRO) : initializes IMU, enabling both accel + and gyro + + CurieIMU.begin(ACCEL) : initializes IMU, enabling accel only + +commit fb578000afad9bc5cdf74358e8c3acbc7a04f9e8 +Author: Sidney Leung +Date: Wed Feb 15 22:28:08 2017 -0800 + + Jira 792, Support addition UUID types, git PR 386. + + Features added: + + 1. Add the support for two addition UUID types, + UUID16_SOME and UUID128_SOME. These types, + practically, behaves the same as their related + ALL types. Sandeep found that the Apple devices + made use of these additional types and requested + for their support. + + File mods: + + 1. BLEDeviceManager.cpp: + - Add UUID16_SOME and UUID128_SOME at places + checking for UUID types. + +commit f61091473e5888337098a103daccf321e3fe328c +Author: lianggao +Date: Wed Feb 8 10:59:09 2017 +0800 + + Jira 804 BLE Characteristic initialization issue, git 366 + + Issue: + - Created Characteristic failed to reflect the initialized + value. + + Root cause: + - During initialization, the length field was incorrectly + skipped over. + +commit ff32c97b547b42e8afa3e423c7ad9ddf4057b4dd +Author: lianggao +Date: Tue Dec 20 18:21:23 2016 +0800 + + BLE Peripheral cannot discover Central attributes + + 1. Add test sketches discoveratperipheral.ino and profileatcentral.ino + 2. Modify BLEDeviceManager.cpp to add central to profile buffer in peripheral + role. + +commit d6c68f26914bf9508e8b76081d383a5cd432c7b6 +Author: Erik Nyquist +Date: Tue Feb 14 12:40:25 2017 -0800 + + flash.ld: fix mis-aligned .data section + + The end of the .data section in flash must be explicitly word-aligned, + otherwise corruption can occur in a .data section that happens to contain + non-word aligned data. + +commit 0285190f5d94dd2228a33ec5ef91d0e3cecfed56 +Author: Erik Nyquist +Date: Thu Feb 9 10:18:37 2017 -0800 + + EEPROM.cpp: Use double quotes for include file + + works in the Arduino IDE, but it's not actually in a system + include location so it makes non-IDE (i.e. Klocwork) builds fail + +commit 547ecd723e39da4044cb636d6bdbb8956cb2f2f2 +Author: lianggao +Date: Fri Jan 13 09:04:29 2017 +0800 + + Sketch crash using both Firmata and BLE library, gitHub issue #377 + + 1. The BLEStream object adds profile when boot but the HW doesn't be + initialized and makes APP crash. + 2. Change the BLE initial process not depend on HW. Buffer the + attributes if HW not initialized. + 3. Changed files + libraries/CurieBLE/src/BLEDevice.h - expose constructor + libraries/CurieBLE/src/BLEPeripheral.cpp - not call HW init function + libraries/CurieBLE/src/internal/BLEDeviceManager.cpp - change init order + libraries/CurieBLE/src/internal/BLEProfileManager.cpp - change constructor + +commit fe14b8bd2600544330e95ccf22f7f91c114467c0 +Author: lianggao +Date: Mon Feb 6 14:13:37 2017 +0800 + + Fix #380 characteristic written returns incorrect value after first connect + + 1. Not set the flage when local device try to set the value. + 2. Changed file + libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp + +commit 0eac6a00a766e424b87aecbbb734eb76bcb71694 +Author: lianggao +Date: Fri Jan 13 22:07:08 2017 +0800 + + Jira 794 Inconsistent writeInt() result with different Peripherals, git 385 + + Root cause: + - As a Central, the write() operation can produce two different + commands to a Peripheral depending on the Characteristic. + - One command is a simple write without aknowledgement from + Peripheral and the other requires a complete handshake for + each write operation. + - Root cause of the issue was that the function, + BLECharacteristicImp::write(), did not check the + characteristic property to determine if the caller requires + a response. It just treats all calls do not require a + response. + + Feature added: + - For write with respond from Peripheral, it is now a + blocking call (it also satisfied Arduino's request). + + Modifications: + 1. libraries/CurieBLE/src/internal/BLECallbacks.cpp: + - ble_on_write_no_rsp_complete: CB to process Peripheral + Response to complete a write operation. + 2. libraries/CurieBLE/src/internal/BLECallbacks.h: + - Function definition added. + 3. libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp: + - In function write(), check the characteristic property to + determine whether the call requires a response. + - Added a flag to signal the waiting on Peripheral + Response for a write operation. + - Write with response is now a blocking call. + 4. BLECharacteristicImp.h: + - Prototyping. + +commit 40b0c43967187137c9bde6bf88f57732a8a30aa4 +Author: lianggao +Date: Mon Jan 9 10:50:13 2017 +0800 + + Jira 797, Central can scan with MAC address, git 376 + + New feature: + - Add the capability for a Central to scan for a + Peripheral using its MAC address. + + Code Modifications: + - Added the API for MAC address scanning. + - For Device Manager, added provision for holding the + MAC address of a Peripheral. + - For scan discovery handler, added the checking + for MAC address comparison, if MAC address scanning + is requested. + + File changes: + + 1. libraries/CurieBLE/src/BLEDevice.cpp: + - Added API for MAC address scanning. + 2. libraries/CurieBLE/src/BLEDevice.h: + - API definition. + 3. libraries/CurieBLE/src/internal/BLEDeviceManager.cpp: + - Added provision for storing the MAC address. + - Added MAC address comparison in the scan discovery + handle if MAC address scanning is selected. + 4. libraries/CurieBLE/src/internal/BLEDeviceManager.h: + - Prototyping. + +commit 78c3d0ad0a5475f7809d31dfd1c2d75cbcf4a58c +Author: Erik Nyquist +Date: Thu Feb 2 13:49:50 2017 -0800 + + EEPROM library: move function definitions into separate .cpp files + +commit 001cab44592510cf9ea799d7a6f34d237d5b149c +Author: Erik Nyquist +Date: Wed Feb 1 11:36:59 2017 -0800 + + freeMemory.ino: add introductory comment + +commit b60860237540cde903b81615c3324f47f03cd756 +Author: Erik Nyquist +Date: Tue Jan 10 15:18:27 2017 -0800 + + Add new library MemoryFree + + This is the MemoryFree libary from + http://playground.arduino.cc/code/AvailableMemory, rewritten specifically + for Arduino 101 and tinyTILE. It provides some functions for querying + available stack & heap space. + +commit 29daefb64ae643a0cb2bcb15b586e90a206bf7e8 +Author: Dino Tinitigan +Date: Tue Jan 10 10:24:15 2017 -0800 + + add support for Power Management library + +commit cd3215bd5929dd83004cdf3b94e32726ff2eba2e +Author: Dino Tinitigan +Date: Tue Jan 10 10:17:13 2017 -0800 + + expose all AON-GPIOs + + -expose AON-GPIOs since they are available on the TinyTILE + +commit 90e8bc65828a7e65f5a59d9a64c3db7df185e0a3 +Author: Dino Tinitigan +Date: Tue Jan 10 09:55:59 2017 -0800 + + shared memory structure changes for CODK-M + + -added struct for ipm using shared memory + -added cdc-acm buffer space on shared memory which is used by the new firmware + +commit 7c97231c087929a90ae880486b3c0ce539a9b326 +Author: Erik Nyquist +Date: Tue Jan 10 10:31:56 2017 -0800 + + CurieIMU.cpp: initialize saved accel/gyro ranges in begin() + + This ensures that "read[Accelerometer/Gyro]Scaled()" will work as + expected even if a range has not been explicitly set using the API + +commit 17d2e2fb22ba0cd239c46973cca0006704534195 +Author: Erik Nyquist +Date: Mon Jan 9 10:55:34 2017 -0800 + + Rebuild libarc32drv_arduino101.a + +commit 5d91dc66fe0f49ab5cb9d5dc7459a9abba5ea7de +Author: Erik Nyquist +Date: Thu Dec 29 17:57:17 2016 -0800 + + libarc32_arduino101/Makefile: Add 'strip' target + + The file libarc32drv_arduino101.a is built with debugging symbols by + default, however the version we ship needs to be stripped. The new + "strip" target makes it easy for us to strip when committing, and + the default target still behaves the same way, so the library can be + re-compiled for debugging purposes + +commit 4ae5af7f8a63dfaa9d44b35c0c820d93a4a6980e +Author: Noel Paz +Date: Thu Dec 22 13:24:24 2016 -0800 + + Corrected the MIDIBLE example so it advertises correctly + +commit 621949108d0c3def12722b626b57176d10425863 +Author: noelpaz +Date: Wed Dec 21 17:10:09 2016 -0800 + + Modified SensorTag example and added examples derived from 1.0.7 + +commit 2391f48210995aa34fc20e62745811aeaaa0bd7d +Author: Sidney Leung +Date: Wed Dec 21 00:48:55 2016 -0800 + + Moving balloc static memory buffer to DCCM memory + + balloc() is used by interrupt context code for memory allocation. The memory space is statically allocated at + initialization. In order not to take up SRAM memory, this buffer is moved to the DCCM memory space. + +commit 34d0e50135f9e0baff349fe116f92d049e9f97c9 +Author: Sidney Leung +Date: Tue Dec 20 13:11:21 2016 -0800 + + I2S example sketches failed compliation, Jira 786. + + The latest BLE library consumes more heap space that causes these sketches ran out of heap space for their temporary + buffers. For now, the size of the tempporary is reduced. For the near future, there are 3 improvements to address + this issue. The BLE library will be making use of the DCCM memory instead of heap. The I2S library buffering space + is going to the DCCM memory too. The heap space will be increased to maximize the usage of the entire SRAM sapce. + +commit 4c89a183f8e509c3f7c08a589a291ebb91ee171e +Author: lianggao +Date: Mon Dec 19 14:59:55 2016 +0800 + + Add test sketches + +commit ade23f0c2c064b733a1c3d264eb29e1a608f8745 +Author: lianggao +Date: Mon Oct 17 16:11:18 2016 +0800 + + 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 + +commit 431c7812e71c904b0e8212a0234d520ffbbe2b0d +Author: Dino Tinitigan +Date: Tue Dec 13 16:16:17 2016 -0800 + + check mux mode when using digitalWrite (#335) + + -check and make sure that the pin is in GPIO_MUX_MODE when doing a + digitalWrite() + -this has a slight performance impact of digitalWrite + -it also comsumes an extra byte in SRAM per pin + -it does however give us the ability to track the muxing state of each + pin + +commit 16227f53f2591eaa47e551b8000878855294eb35 +Author: Dino Tinitigan +Date: Tue Dec 13 16:15:51 2016 -0800 + + Ensure RTC clock scaling is set to 1Hz (#350) + + -needed to make sure that the RTC scaling is consitent with the factory + firmware and CODK-M based firmware + +commit 0d321db5cc619dc9a4451a6710dabd38fee492bf +Author: Erik Nyquist +Date: Tue Nov 29 11:32:50 2016 -0800 + + CurieIMU.cpp: Don't lock interrupts for SPI transfers + + This is unnecessary- nothing else is contending for access to this + particular SPI controller + +commit e870ba255646afa47debfc0b1cb493cc055520dd +Author: Dino Tinitigan +Date: Tue Nov 29 13:27:38 2016 -0800 + + Ensure RTC clock scaling is set to 1Hz + + -needed to make sure that the RTC scaling is consitent with the factory + firmware and CODK-M based firmware + +commit de208be5c42a305624468d9a1a5208f60d0b5bdb +Author: Erik Nyquist +Date: Tue Nov 29 10:35:10 2016 -0800 + + README.md: add steps for updating intel-arduino-tools + +commit 2bb7d4b58e4152fde9b87153f71618475f54c42a +Author: Erik Nyquist +Date: Fri Oct 21 16:20:37 2016 -0700 + + README.md: remove Serial1 debug information + +commit 205c1e40852f356a4819a16260086e8f3c719592 +Author: Adrien Descamps +Date: Thu Aug 11 23:13:02 2016 +0200 + + Enable interrupt mode of UART tx + + Previous version of write will never use the interrupt mode + transmission, because it fails to check if holding register is + empty. This commit fix that, and several bugs related to + interrupt mode serial transmission. + When possible, it uses the tx FIFO, in order to reduce + the number of interrupts called and the overhead. + +commit 5279509cc2f6d161e1607245f15024e5d578c10e +Author: Erik Nyquist +Date: Mon Nov 14 10:12:39 2016 -0800 + + CurieTime.cpp: check gmtime() return pointer for NULL-ness + +commit 249fb3b7b6a7ebf85a202688c960de9f256a8018 +Author: Erik Nyquist +Date: Mon Nov 14 16:20:09 2016 -0800 + + README.md: add link to ICS for product support + +commit c353f4caa7e5d9a4287410f2826f8e75683ab13c +Author: Calvin Park +Date: Thu Oct 27 15:57:39 2016 -0700 + + Use our own version of copy.exe so that we can copy without cmd.exe + +commit 6ee828b737b4bd08dda78b0769bb9043c64d2c4c +Author: Dino Tinitigan +Date: Thu Oct 20 15:53:53 2016 -0700 + + Move SoftwareSerial buffers into DCCM area + +commit 41f98f9c459a0ed7175b34c9c334c03c352c0706 +Author: Dino Tinitigan +Date: Thu Oct 20 15:14:47 2016 -0700 + + Allocate RingBuffer buffer into DCCM area + + -this buffer is used by the uart. Moving it into the DCCM area would + save us 128 bytes of SRAM + +commit 8619f2b3e088c28c389550a19ecf2e2e3029543e +Author: Dino Tinitigan +Date: Thu Oct 20 11:35:08 2016 -0700 + + Move buffers for Wire library to DCCM area + +commit 4e2232e3e55ab68da0e125f59da2817f45b7e06d +Author: Dino Tinitigan +Date: Wed Oct 19 15:26:19 2016 -0700 + + Expose dccm area + + -expose dccm area giving the user an extra 8k of memory + -simple implementation of a malloc like function for dccm memory area + +commit 1c8de0d7cc0ecf93abeb539bd97b0fee2b09eb97 +Author: Erik Nyquist +Date: Tue Oct 11 10:11:29 2016 -0700 + + Add functions to check if new IMU data is available + + accelDataReady() and gyroDataReady() read the drdy_acc and drdy_gyr + bits, respectively, of the BMI160's sensor status register. This removes + the burden of syncronising sensor reads from the user. + + (adapted from descampsa/corelibs-arduino101@773a800) + +commit 3d3c1f92c2c15724725f71542bc564754ba5d6e3 +Author: Erik Nyquist +Date: Wed Oct 12 15:42:50 2016 -0700 + + I2SDMA: Do not modify g_APinDescription + + The g_APinDesctiption is now 'const', so cannot be modified. + +commit d6b2ecaa47bd543dbb35a33b36dfa2fb9037de86 +Author: Dino Tinitigan +Date: Wed Sep 28 12:54:37 2016 -0700 + + pwm improvements + + -add capability to change pwm signal frequency with analogWriteFrequency + -improve pwm signal frequency accuracy + +commit 27a68decb8e450f52b7c4c9dc254a1b0cb045b17 +Author: Biagio Montaruli +Date: Wed Sep 7 13:15:41 2016 +0200 + + Update sketches that use serial communication of different libraries + + Improve sketches of CurieTime, CurieTimerOne, CurieSoftwareSerial, SPI and + SerialFlash libraries that use Serial communication. + Since Arduino/Genuino 101 uses USB native port, wait for the Serial port + to open to not lose serial data already sent to the Serial monitor. + + Signed-off-by: Biagio Montaruli + +commit 39ef9ef12c50366c4b77f494ae53361bbaf2118c +Author: Calvin Park +Date: Fri Oct 7 11:07:44 2016 -0700 + + Don't save unstripped elf for debugging (#314) + + The current save mechanism breaks when a user has deleted system32 from PATH environment variable. Comment out the copying so that it won't break for such users, but do not delete so that advanced users can use the unstripped elfs to debug. + +commit c396a3ad4f0ffb9163b0a2b270aa4584848764f3 +Author: Erik Nyquist +Date: Tue Oct 4 16:59:36 2016 -0700 + + Shrink oversized objects in SRAM + + 824 bytes is gained for use by sketches. Two new defines are added; + CDCACM_BUFFER_SIZE and UART_BUFFER_SIZE. The CDC-ACM Serial class and + the UART driver had been using the same #define for buffer size-- + SERIAL_BUFFER_SIZE-- and separating them means that the CDC-ACM buffer + can stay the same size, while the UART buffer is shrunk. + +commit 1a5e3f7b2d65cf2b81417f85bd24ca455bdaa1fc +Author: Erik Nyquist +Date: Thu Sep 29 15:17:07 2016 -0700 + + Make g_APinDescription const + + This has the effect of moving g_APinDescription out of .data (SRAM), + and into .rodata (flash). + + Pros: + sketches now have an additional 1160 bytes of SRAM available + + Cons: + state of pins is no longer tracked; e.g. when calling digitalWrite, + the full configuration is done each time. See changes in wiring_digital.c + and wiring_analog.c for details. There is inevitably a performance impact + for digitalRead/Write & analogRead/Write, though it is unlikely to be a + noticable one. + +commit 2e56b81a264c8760f095a12f650381b8ea8de831 +Author: Erik Nyquist +Date: Wed Sep 28 12:02:32 2016 -0700 + + stdlib_noniso.cpp: Handle rounding corner case for dtostrf + + Rounding must be done before separating the integral and decimal portions. + +commit f2a001f4fa557c4374493242c67ecee617a59866 +Merge: 77d9086 d2b7d1d +Author: Calvin Park +Date: Thu Sep 22 11:57:28 2016 -0700 + + Add BLE Central feature + +commit d2b7d1d732ca2ace5eb3f3da2e5cf3371e104f54 +Author: unknown +Date: Mon Sep 12 10:33:51 2016 -0600 + + JIRA-685, Peripheral sketches shall state associated Central sketch. + +commit 9581cf18a0a904e096f1dcab30624871c22714c4 +Author: Brian Baltz +Date: Mon Aug 29 14:01:05 2016 -0700 + + Update expected BLE version for V3 + + Signed-off-by: Brian Baltz + +commit 0304de781d54d0158c070d2d9cc750ab295c0712 +Author: Calvin Sangbin Park +Date: Fri Sep 9 10:55:17 2016 -0700 + + Rebuilt libarc32drv + +commit 84fc3dbbfb900d0d5cf5b8b2a9c7e734accb5771 +Author: lianggao +Date: Fri Sep 9 08:28:50 2016 +0800 + + Create BLE Example sketches + + 1. Fix Jira 664 demonstrates changing the ADV data + 2. Fix Jira 671 Support update connection interval in central/peripheral + +commit e7238f02c945faf6a05eca38ae590d520ec2af62 +Author: lianggao +Date: Fri Sep 9 08:25:31 2016 +0800 + + Jira Tickets and Bug fix + + 1. Jira 541 Peripheral Start fails on X Number of Attributes + -Change the interface and add return value + + 2. Fix BLE Peripheral is not advertising + -Add a delay after register the profile. + + 3. Modify some comments based on code review + + 4. Jira 544 BLE peripheral disconnect and end functions + -The state not aligned and make disconnect failed. Update when connect and disconnect event received + + 5. Delete the duplicated code that base class has implemented + + 6. Fix Jira 665 BLECentral Preview -- compile issue when instantiating BLE descriptor + -Fix the build error. + Note: + i . The current code only support one descriptor on characteristic. + ii . The central discover logic need more twist. + iii. Now is so long and can't process CCCD and another characteristic. + iv . The library will support one other descriptor except CCCD. + + 7. Improve the discover logic and support another descriptor + i. Improve the discover logic + ii. Support another the descriptor and CCCD at same time. + + 8. Modify the comments and delete the unused API + + 9. Fix Jira 670 A compile error occurs with two BLE methods with the same name + i. Add method uuid_cstr in BLEAttribute class. + ii. Remove the duplicate file. + + 10. Fix Jira 672 BLE documentation in code needs to specify units + i. Change the scan and connection interval's unit to ms. + ii. Unify the advertising interval unit as millisecond + iii. Delete some unused code. + + 11. Fix Jria 671 Support update connection interval in central/peripheral + i. Central can update the connection interval. + ii. Unify the interval unit to ms at user API. + + 12. Adjust the example comments and functions + i. Fix Jira 675 - BLE callbacks should use passed arguments instead of global variables + ii. Adjust the code struture to align with Arduino's requirement + + 13. Fix Jira 680 LED and LED Central sketches need some work + -Add a delay to make sure profile register process success. + -The UART send too fast may makes the profile registration failed. + + 14. Fix Jira 673 BLE Api should pass arguments that are typedef + + 15. Fix Jira 671 Support update connection interval in central/peripheral + i. Add peripheral update the connection interval feature. + ii. Update the library. + + 16. Fix Jira 687 Edit the keyword.txt of CurieBLE to reflect Library changes + + 17. Make example function ready. But need to resolve Jira 675 + + 18. Fix Jira 676 Review Edit Update BLE User Manual with China Flex + + 19. Fix Jira 685 BLE Peripheral sketches shall state which associated Central sketch to use + + 20. Fix Jira 588 - BLE Corrupted Long Write + i. Modify the BLECharacteristic to implement the Long write feature. + + 21. Fix Jira 683 Typecasting and Pointers should be avoided in sketch + i. Add write in Characteristic template + ii. Add method in BLE Role class to avoid typecaste + iii. Modify the advertise address parameter and related example sketches + + 22. Fix Jira 704 Type Characteristic should be documented + 23. Fix Jira 684 Documentation about the central/peripheral + +commit ae78b1626ef682331af055075fbddb672cb19552 +Author: lianggao +Date: Fri Jul 29 22:19:36 2016 +0800 + + Add BLE central function, debug interface and refactor peripheral + + -Add debug interface on Serial1 + -Update BLE stack and need update BLE's FW + -Reconstruct the BLE peripheral base on V3 + -Implement the BLE Central Role base on V3 + -Implement some sketches for new BLE library + -Add central read/write example + -Add set advertising parameter interface + -Add API to allow set up advertising after setup + -Add interface to set the device name + + File description + Porting from V3 + system/libarc32_arduino101/common/atomic.h + system/libarc32_arduino101/common/misc/byteorder.h + system/libarc32_arduino101/drivers/atomic_native.c + system/libarc32_arduino101/drivers/bluetooth/att.h + system/libarc32_arduino101/drivers/bluetooth/bluetooth.h + system/libarc32_arduino101/drivers/bluetooth/conn.h + system/libarc32_arduino101/drivers/bluetooth/conn_internal.h + system/libarc32_arduino101/drivers/bluetooth/gatt.h + system/libarc32_arduino101/drivers/bluetooth/hci.h + system/libarc32_arduino101/drivers/bluetooth/uuid.h + system/libarc32_arduino101/drivers/rpc/rpc.h + system/libarc32_arduino101/drivers/rpc/rpc_deserialize.c + system/libarc32_arduino101/drivers/rpc/rpc_functions_to_ble_core.h + system/libarc32_arduino101/drivers/rpc/rpc_functions_to_quark.h + system/libarc32_arduino101/drivers/rpc/rpc_serialize.c + system/libarc32_arduino101/framework/include/util/misc.h + system/libarc32_arduino101/framework/src/os/panic.c + system/libarc32_arduino101/framework/src/services/ble/conn.c + system/libarc32_arduino101/framework/src/services/ble/conn_internal.h + system/libarc32_arduino101/framework/src/services/ble/dtm_tcmd.c + system/libarc32_arduino101/framework/src/services/ble/gap.c + system/libarc32_arduino101/framework/src/services/ble/gatt.c + system/libarc32_arduino101/framework/src/services/ble/hci_core.h + system/libarc32_arduino101/framework/src/services/ble/l2cap.c + system/libarc32_arduino101/framework/src/services/ble/l2cap_internal.h + system/libarc32_arduino101/framework/src/services/ble/smp.h + system/libarc32_arduino101/framework/src/services/ble/smp_null.c + system/libarc32_arduino101/framework/src/services/ble/uuid.c + system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c + system/libarc32_arduino101/framework/src/services/ble_service/ble_service_api.c + system/libarc32_arduino101/framework/src/services/ble_service/ble_service_int.h + system/libarc32_arduino101/framework/src/services/ble_service/ble_service_internal.h + system/libarc32_arduino101/framework/src/services/ble_service/ble_service_utils.c + system/libarc32_arduino101/framework/src/services/ble_service/gap_internal.h + system/libarc32_arduino101/framework/src/services/ble_service/gatt_internal.h + system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.c + +commit 77d908688e799557fa6e582fbd885c825aaa07c6 +Author: Calvin Sangbin Park +Date: Mon Sep 19 16:48:28 2016 -0700 + + Rebuilt libarc + +commit 5340b0c9c1fa02257b7f363339594944f4c6be9e +Author: Martino Facchin +Date: Mon Sep 5 15:41:42 2016 +0200 + + CurieEEPROM refactor + + This PR refactors CurieEEPROM library, syncing it with AVR version. + Since EEPROM on Curie chip is based on OTP flash storage, the minimum write + size is 4 bytes. The current implementation brings some problems like + http://forum.arduino.cc/index.php?topic=421348.0 , and the examples are + quite broken. + + The refactor also changes the name, from CurieEEPROM to EEPROM, so code for + AVR sketches compiles out of the box. The new library resolution engine + (from IDE 1.6.8 onward) handles this perfectly but we can stick to older name + if we need compatibility with older IDEs. + +commit 4c69f724b9e253c014e118ca87d031929deea7ca +Author: Sidney Leung +Date: Thu Sep 1 15:20:32 2016 -0700 + + Added support for Quark I2C interfaces: I2C0 and I2C1. + + Additions: + 1. Ported Quark I2C driver to ARC/CoreLibs, located at system/libarc32_arduino101/drivers. + 2. Created the I2C Adapters, located at cores/arduino. + + Modifications: + 1. Platform.h - added number of I2C interface definition. + +commit 7dcc0742ef43174547efec5497359d6d80cc6387 +Author: Biagio Montaruli +Date: Sat Sep 17 13:45:03 2016 +0200 + + Update bus_scan.ino sketch of Wire library + + Improve documentation, code formatting and use LED_BUILTIN instead of 13 + to control the on-board led connected to digital pin 13 of + Arduino/Genuino 101 + + Signed-off-by: Biagio Montaruli + +commit 23ca972371c3bf9db488e948d35b870a1fa52b2c +Author: Sandeep Mistry +Date: Thu Sep 15 15:22:35 2016 -0400 + + WString: add `toDouble` (#293) + + Port of https://github.com/arduino/Arduino/pull/5362 + +commit f818d7cd360e109c31bc65ed11f0df3b133bc826 +Author: Sandeep Mistry +Date: Tue Sep 6 14:06:34 2016 -0400 + + Make String::move of an invalidated String result in an invalidated String + +commit 2751995ab29738c708b5b7d109d98cbfc34a09e4 +Author: Biagio Montaruli +Date: Wed Sep 7 13:01:32 2016 +0200 + + Fix for USB virtual serial port in sketches of CurieIMU library + + Since Arduino/Genuino 101 uses USB native port, wait for the Serial port + to open before executing the sketch to not lose serial data already sent + to the Serial monitor + + Signed-off-by: Biagio Montaruli + +commit d8d2c495b531fda1ffe98cb59f7e35fd948c5bad +Author: Biagio Montaruli +Date: Wed Sep 7 13:05:01 2016 +0200 + + Fix for USB virtual serial port in sketches of CurieI2S library + + Since Arduino/Genuino 101 uses USB native port, wait for the Serial port + to open before executing the next lines of code to not lose serial data + already sent to the Serial monitor + + Signed-off-by: Biagio Montaruli + +commit e084140957ebe2aa0d891c496ced456538c9a1b9 +Author: Erik Nyquist +Date: Wed Sep 7 16:02:31 2016 -0700 + + stdlib_noniso.cpp: fix dtostrf() handling of integral portion + + Checking only for > 10 here (rather than >= 10) means that numbers with a + leading '10' in the integral portion generate incorrect strings. + +commit b329d080469778b7e9c8e0868ab6edba4a7fdf97 +Author: russmcinnis +Date: Tue Aug 30 15:56:08 2016 -0700 + + add comment to pulseIn() in the header file to warn programmer about trying to detect high frequencies. + +commit 10d1056503099431cda97deacabbb2a68f43121e +Author: Brian Baltz +Date: Thu Aug 25 12:47:00 2016 -0700 + + Verify BLE FW version on every sketch upload + + Signed-off-by: Brian Baltz + +commit 431fb6fd27bdfbd6aaa97e690f1b162a1e02cb62 +Author: sys_maker +Date: Mon Aug 22 12:35:27 2016 -0700 + + Rebuilding libarc32 library + + Signed-off-by: sys_maker + +commit 0d86ead0d4ae870c32c679fca3ef087c83a758fc +Author: Calvin Park +Date: Thu Dec 3 11:34:56 2015 -0800 + + Add flashpack to IDE + +commit 71cf8e44b3d9c28918084c0e5a6c2d1e239b1c98 +Author: Brian Baltz +Date: Wed Aug 10 13:53:03 2016 -0600 + + JIRA-668: Removing Intel version of Adafruit_NeoPixel + + Signed-off-by: Brian Baltz + +commit e4a5b14c51e70933ff3f85603a08c3b6970ec264 +Author: Adrien Descamps +Date: Sat Aug 6 16:27:35 2016 +0200 + + Fix UART receiver corruption + + When the UART receiver is disconnected for a time shorter than a line break + and longer than a start bit, it interpret that as a received packet, and if + data are transmitted immediatly after, they are corrupted. + This commit is a workarround for that bug, that enable the loopback feature + of the UART when UART is disconnected, so the UART module does not notice + it is disconnected. + A previous workarround for this bug (a forced delay after disconnection) is + not needed anymore and is removed. + +commit 7366ed0a4fd26faec64e11b530557c313069b4f7 +Author: Adrien Descamps +Date: Wed Aug 3 01:40:57 2016 +0200 + + Flush wait for transmission to be complete + + In previous version, flush wait for the transmission to be complete + by checking if the transmission holding register (or fifo) is empty. + When this happen, the last byte is still in the shift register, + and has still to be transmitted. + This mean we can't know when the transmission is actually complete, + which is crucial to implement RS485 communication, for example. + + This commit change this behaviour and wait for the shift register to be empty, + which means the transmission is really complete. + +commit 48b0e58306d227b289316fcb4143edb08570042c +Author: Calvin Sangbin Park +Date: Wed Aug 3 14:16:23 2016 -0700 + + Update libarc to the latest + +commit 9ae91d3e9d55d81358681dd1595880de1176d640 +Author: unknown +Date: Tue Aug 2 09:49:30 2016 -0600 + + Jira-556: Add comments to I2S examples + +commit b317284ec9312288dea4e2b5db5696821fa6237f +Author: Calvin Sangbin Park +Date: Thu Jul 28 10:44:44 2016 -0700 + + Update libarc to the latest + +commit 9a871b671ceec5f625c63c9a9e58f044cef2e70c +Author: Erik Nyquist +Date: Wed Jul 27 14:20:29 2016 -0700 + + Add delay to Serial1.end() allow GPIO lines to settle + + Through testing, it was discovered that if Serial1.begin() is called again + immediately after Serial1.end(), then any attempt to write data immediately + following the begin() call could result in corrupted frames. + + After calling Serial1.end(), it will be at least 300us before those pins can + be used as UART again. + +commit e3d90c19d96bbab006f59151b73179ab0c7d9097 +Author: Caihong Ma +Date: Tue Jul 26 01:34:39 2016 +0800 + + CurieI2SDMA: Fix typo and add explanatory notes + CurieI2SDMA.h: Fix typo at line 23 and 24; I2SDMA_RXCallBack.ino: add + explanatory notes to explain how to check the verification of data + +commit fbb7b02a89da37ac9408710ff5ed1a936d00cd8b +Author: Xie,Qi +Date: Tue Jul 19 09:13:39 2016 +0800 + + JIRA-640 I2C operations are sometimes timing out causing a long delay in between i2C operations + + fix this issue by call complete callback if no stop command issue at the end + of transfer. + +commit 160751a8c47f1d4cdd647ad1bf0d8eb6312d5791 +Author: Erik Nyquist +Date: Fri Jul 22 18:21:27 2016 -0700 + + README.md: add "commit message how-to" wiki link + +commit 197434001a973ad7a8df18b823f8365988140b43 +Author: Erik Nyquist +Date: Sun Jul 17 00:33:17 2016 -0700 + + Issue #149: Provide scaled IMU data + + Add implementations of readAccelerometer and readGyro which + return values that are scaled according to the range set with + setAccelerometerRange and setGyroRange + +commit 5671917298c35e6164784da6929cf006daf9d51d +Author: caihongm +Date: Fri May 27 02:17:19 2016 +0800 + + Jira509:add IMU examples:FreeFallDetection, MotionDetection,ZeroMotionDetection; change CurieIMU.cpp line490 from 'setZeroMotionDetectionThreshold' to 'setZeroMtionDetectionDuration' + +commit 12b2376ea58c79ef00ea47af2aea8796bb652d69 +Author: Erik Nyquist +Date: Wed Jul 20 15:23:23 2016 -0700 + + ATLEDGE-643: Add chip ID to SerialFlash example sketch + + RawHardwareTest reports the 101's on-board SPI flash chip as an unknown + device. Fix this by adding the device's JEDEC ID to the example sketch. + +commit b8162e993cccbe4065c1bd55d1f4466669f01f75 +Author: Caihong Ma +Date: Mon Jul 4 15:15:54 2016 +0800 + + Jira566: I2S DMA implement: supplement library and example + +commit c59e2808e1bf27e8adc7c3aff0d0ed1a8344e04f +Author: Erik Nyquist +Date: Fri Jul 15 15:39:16 2016 -0700 + + Issue #241: make Serial1 release GPIO pins on end() + + UARTClass::init() muxes out the UART pins to Arduino header pins 0/1. + Reset the mux on UARTClass::end() so that pins 0/1 can be used as GPIOs + again. + +commit bdaec192070ab196a55282fa57512748c6f1edd9 +Author: Erik Nyquist +Date: Mon Jul 11 11:21:34 2016 -0700 + + Add CurieIMUClass::end() + + Add end() method to IMU class, to disable clocking to the SPI + controller. This is unlikely to have noticeable power-saving effects, + however it is a good habit to get into. + +commit 2ec9b94f941d34ee1419311b194707aad3b0ee67 +Author: Dino Tinitigan +Date: Mon Jul 11 14:47:59 2016 -0700 + + Update Wire examples to use Serial instead of Serial1 + + -change Serial to Serial1 in the master_writer and master_reader + examples + +commit 5f3ee5bb36dc5e69ce26f7d2a64220ebc927ad83 +Author: Martino Facchin +Date: Tue Jul 12 10:22:12 2016 +0200 + + define digitalPinToInterrupt for backwards compatibility + + see http://forum.arduino.cc/index.php?topic=370167.0 + +commit ecee2e9483e39af71df873174df070fdf952c431 +Author: Caihong Ma +Date: Mon Jul 4 15:15:54 2016 +0800 + + I2S DMA - supplement library and examples + +commit 3168ede2cbd3df99121944d46a5b24e9557d2b4b +Author: erik.nyquist +Date: Fri Jun 24 13:08:25 2016 -0700 + + ATLEDGE-619: fix dtostrf + + Use a different method for float->string conversion, and + fix the width calculation so that extra spaces are only + printed if the width parameter exceeds the output string + length (ie. a mininum width, as specified by dtostrf + reference from avr-libc). + +commit 019bb8fcecc1c79d78bd58f18f1b4521e73fad35 +Author: Dino Tinitigan +Date: Fri Jun 24 14:58:48 2016 -0700 + + update libarc32 + + -update libarc32 binary + +commit 9be7604bec5f080683a236cbabfe0d70b174ac13 +Author: Dino Tinitigan +Date: Wed Apr 20 14:52:22 2016 -0700 + + I2C - add functionality to change i2c clock speed + + -adds functionality to change clock speed + -clock speed is set/changed when Wire.begin() is called + -default is standard mode + -use Wire.begin() for standard speed (100k) + -use Wire.begin(I2C_SPEED_FAST) for full speed (400k) + -use Wire.setClock(I2C_SPEED_FAST) for full speed (400k) + +commit 8682149177559757a57ae0b2ad6639dcf97e31b1 +Author: Dino Tinitigan +Date: Fri Jun 24 14:33:45 2016 -0700 + + reduce i2c delays + + -reduce i2c delays to increase performace without changing the timeout + threshold + +commit de0650c15a0d44284440e4c4ba1943639b73da4f +Author: erik.nyquist +Date: Tue Jun 28 15:21:32 2016 -0700 + + Add -std=gnu11 to compiler.c.flags in platform.txt + + Related to issue #221 + +commit 4d9642871ae0bfb28783b1d6fa00b364ea201cf1 +Author: Oren Levy +Date: Mon Jun 20 14:59:09 2016 -0700 + + Added MIDI examples + +commit 2cd33f43a1b84c3f23194b27290114c4cec5d9df +Author: Martino Facchin +Date: Thu Jun 16 11:29:06 2016 +0200 + + Correct core version in platform.txt + +commit 20844ff46b455055f86641c785fb580ae1214222 +Author: Mike Duong +Date: Wed May 25 18:32:05 2016 -0700 + + ATLEDGE-586: String.compareTo() does not compute the same as Uno for POS values + +commit 2f5ae85f235bc1e5a0e5b50d63961f4348f237a0 +Author: erik.nyquist +Date: Mon Jun 13 10:35:53 2016 -0700 + + ATLEDGE-572: Fix getStepDetectionMode() + + This function is reading all 8 bits of RA_STEP_CONF_1 (0x7A-0x7B) + to obtain a value for min_step_buf, though this value is only + represented by the lower 3 bits; bit 4 is used as the 'enable' bit. + As a consequence, this function will read the step detection mode + incorrectly when step counting is enabled. Fix this by only reading + the lower 3 bits of RA_STEP_CONF_1. + +commit de30a61b909802f2dbb3380c4adc9316ee1f64f7 +Author: erik.nyquist +Date: Mon Jun 13 11:57:21 2016 -0700 + + ATLEDGE-553 Modify library examples header info + +commit a694d09698db8d0f77ac19d58d1db34eef3d1b4d +Author: Sidney Leung +Date: Tue Jun 7 16:57:12 2016 -0700 + + Jira-617: String Object constructor hangs up when handling large floating point number + +commit df3b675c1ba4113fa560078ae60f610f4d8b3ae0 +Author: Erik Nyquist +Date: Fri Jun 3 15:02:00 2016 -0700 + + SerialFlash: enlarge chip ID buffer + + SerialFlashChip::readID() writes up to 5 bytes into + the buffer provided, but the caller only allocates + 3 bytes. Increase buffer size to 5 bytes. + +commit c27462cbc168257c2c928d435dcaf3b43a535fe1 +Author: Xie,Qi +Date: Wed May 25 07:30:23 2016 +0800 + + JIRA-560 I2C Bus Scan + using read instead of write to avoid sending the extra byte when scaning I2C bus + +commit e792ba2bd6480a556e50259e0e836ae4cf5ec371 +Author: Dino Tinitigan +Date: Tue May 31 17:03:06 2016 -0700 + + Fix bug when using StringConstructor with doubles + + -Fixes issue of StringContructor instability with doubles + +commit acc4b38ba43d654992b72fa9d4afa68b834a098c +Author: Dino Tinitigan +Date: Tue May 31 16:12:32 2016 -0700 + + Revert "Fix some issues with printing float and double" + + This reverts commit b8ea343bb237762de6ee3a60ba0b1e8d5af12ff0. + +commit 409e4b07f477e6345c972a40376085c89ecdb46e +Author: erik.nyquist +Date: Fri May 27 14:38:05 2016 -0700 + + Remove unsupported SerialFlash example + + CopyFromSD.ino requires SDfatlib (for AVR boards) which + is unsupported. + +commit 4294ca5ad86c2f7f5cfb354ac193ff1ecbfc9fa9 +Author: erik.nyquist +Date: Fri May 27 14:36:36 2016 -0700 + + Remove SPI.set* calls from SerialFlash example + + These functions are not implemented for A101's SPI library. + Remove these calls from examples/CopyFromSerial.ino + +commit f33e362db41f7871a63bc8f0294ce9aa43be3f0d +Author: erik.nyquist +Date: Fri May 27 14:26:46 2016 -0700 + + Add category to Wire/library.properties + +commit 348e36ee6e6eb2c5f5687b642f978aabcace5be2 +Author: Sidney Leung +Date: Thu May 26 15:54:08 2016 -0700 + + Jira-602: Convert float and double to strings. Output string is padded with spaces. + +commit ce7825883b79bd6849cc1fba4bbaa6416e43229d +Author: caihongm +Date: Fri May 27 02:17:19 2016 +0800 + + Jira509:add IMU examples:FreeFallDetection, MotionDetection,ZeroMotionDetection; change CurieIMU.cpp line490 from 'setZeroMotionDetectionThreshold' to 'setZeroMtionDetectionDuration' + +commit b8ea343bb237762de6ee3a60ba0b1e8d5af12ff0 +Author: Dino Tinitigan +Date: Thu May 26 13:51:06 2016 -0700 + + Fix some issues with printing float and double + + -fixes some issues with printing float and double + -limits the number of decimal places printable to 16 + +commit 9472c9aa9427030f3e24968d4915fdf69881ea66 +Author: Dino Tinitigan +Date: Wed May 25 15:43:28 2016 -0700 + + Remove unnecessary #undefs + + -Remove #undefs from CurieSoftwareSerial Library + -Because it was braking some functionality such abs() + +commit 048800b47b8edcbe0cbf9401163ace1f4c15bb3e +Author: Sidney Leung +Date: Thu May 19 11:36:38 2016 -0700 + + Jira-599. Routine, dtostrf, did not work with the latest toolchain. The call to sprintf in the routine crashed the system. Issue corrected by replacing the call. The routine, printfloat, in Print.cpp, is now usint dtostrf to print out double and float values. + +commit 874c3ef08bf7031a06248abbb59274da740444f2 +Author: erik.nyquist +Date: Fri May 20 10:38:55 2016 -0700 + + SPI: add SPI1 to keywords.txt + +commit 92818058e83302e478e7cdddd60d1d5568ce9bc5 +Author: erik.nyquist +Date: Thu May 19 10:34:30 2016 -0700 + + Add Paul Stoffregen's SerialFlash library + +commit 8dea7b4a1430dce33870a97fcf0330e0cda4fef3 +Author: erik.nyquist +Date: Mon May 16 10:41:02 2016 -0700 + + Fix SPI clock divider calculation + + If a clock value greater than the maximum supported + is passed (i.e. > 16MHz), the resulting value written + to the BAUDR register will be 0 which will disable + the serial output clock. Check for this and ensure + the value calculated for BAUDR register is never < 2. + +commit 331b2f0152bb90f1c96ee1726e8017db56e51789 +Author: erik.nyquist +Date: Thu May 12 11:18:35 2016 -0700 + + Clean up the SPI files + + Be consistent about commenting style, and + stay below 80 chars where appropriate. + +commit 2bb742afd2e8cf0a3708b3b50a94089b5b39a3a8 +Author: erik.nyquist +Date: Thu May 12 11:15:04 2016 -0700 + + Add support for SPI1 device to SPIClass + + SPI1 can now be used (SPI1.begin()) to access + the SPI device connected to the Arduino 101's + on-board SPI flash device + +commit 2bea587379b0e78d4006db13e4c9bdf4482637c0 +Author: Brian Baltz +Date: Mon Apr 25 15:36:38 2016 -0600 + + JIRA-550 Retain unstripped ELF file for debugging + + Signed-off-by: Brian Baltz + +commit ea7a804cefdf17a7518ba8c3fec95bb744273d0c +Merge: 2b5a040 9f88024 +Author: Brian Baltz +Date: Wed May 11 14:09:23 2016 -0600 + + Merge pull request #172 from bbaltz505/new_toolchain + + Updating compile command and driver lib for new toolchain + +commit 2b5a0402af9486f527805124a5ef937cbfb89ee1 +Author: Sidney Leung +Date: Fri May 6 16:39:36 2016 -0700 + + Bug fixed: CurieBle library, class BLECharacteristic allocates memory using an incorrect variable which may result in memory corruption if the memory size is bigger than 16 bytes. + +commit cdd2989b4297c10c8759de408ccb021f0874763a +Author: jysholar +Date: Mon May 9 12:48:48 2016 -0600 + + CurieIMU tap/double example, JIRA-508 + +commit 434fcd49ddcae18726b8196153baf0a68882b55e +Author: erik.nyquist +Date: Tue May 10 11:41:19 2016 -0700 + + README.md: clean up and add "support" section + + Break long lines to be <= 80 chars, where possible, and + add a new section to guide users to the appropriate place + for support (Arduino forum for product support, issue + tracker for bugs & features). + +commit eb0cf4f1ceaa0fc4220adcc726063e17934eee3e +Author: Martino Facchin +Date: Wed May 4 10:28:22 2016 +0200 + + Add SERIAL_PORT_USBVIRTUAL and SerialUSB macros + + This is needed to ensure compatibility with YunShield sketches + + asas + +commit 9f880240e6cb1deba4c8bd3f296dff4eb5a54929 +Author: Brian Baltz +Date: Thu Apr 28 15:33:58 2016 -0700 + + Updating compile command and driver lib for new toolchain + + Signed-off-by: Brian Baltz + +commit 2bac6d3432033197ffc23c6da96ddd99054271f9 +Author: Calvin Park +Date: Mon May 2 15:59:10 2016 -0700 + + Fixed the license file to LGPLv2.1 + +commit 443916f2621a808540997f36caa9bcd2c9d7fb5f +Merge: f43789e c1be134 +Author: Calvin Park +Date: Mon May 2 14:54:12 2016 -0700 + + Merge pull request #156 from bigdinotech/i2slib + + CurieI2S library + +commit f43789e71bcc9c7d8181aec61c48eed3a9683cba +Author: jysholar +Date: Tue Apr 26 12:25:01 2016 -0600 + + Jira-560, getInterruptStatus(...) should return type of bool + +commit 212dae55ea664d1bd92ad175615a49bec7d1bc4f +Author: Sandeep Mistry +Date: Tue Apr 5 09:50:36 2016 -0400 + + Sync avr/pgmspace.h entries with SAMD core + +commit 7bd93b3baf802481282e32240fcfc1f941e36561 +Author: Sidney Leung +Date: Thu Apr 14 15:46:58 2016 -0700 + + Jira-584: String constructor create floating point string with incorrect number of decimal places. + +commit 4d1014ff2b89a88f9605e2cb7f75bbe7bd0691e4 +Author: russmcinnis +Date: Wed Apr 13 11:10:56 2016 -0600 + + check pin range first thing in pinMode(), digitalWrite() and digitalRead() + + Signed-off-by: russmcinnis + +commit 285ec1770d831f584ca943f2e4d214ad59e35ffe +Author: Brian Baltz +Date: Tue Apr 12 10:56:38 2016 -0600 + + Removing obsolete TFT library + + Signed-off-by: Brian Baltz + +commit e9aaea9b39eb1496ebccdef422ae2d441f9086f4 +Author: Brian Baltz +Date: Fri Apr 8 15:11:42 2016 -0600 + + Fix CurieTimerOne example filename + + Signed-off-by: Brian Baltz + +commit 4cad8b8e31d7e8f13b394e90ffa2253ffb0d5f9b +Author: russmcinnis +Date: Wed Apr 6 13:30:42 2016 -0600 + + pwmStop() leaves pin HIGH + + pwmStop() was doing a digitalWrite(pwmPin, LOW) after the kill() so unless the pin happened to be 0 the pwmPin might be left in the HIGH state causing motors or other devices to run HIGH after pwmStop(). Just removed the pwmPin = 0 from kill() and it passes all our loopback tests. + +commit 0e4a0d439341781ef6709b73d45a77c79cdf0de7 +Merge: 669fb9b 0ad15b5 +Author: Brian Baltz +Date: Tue Apr 5 09:23:41 2016 -0600 + + Merge pull request #146 from bneedhamia/support-eddystone-url + + Added Eddystone-URL support + +commit 669fb9bcf6e80d774a8a9d836b3354c5ddd04a99 +Author: Brian Baltz +Date: Mon Apr 4 15:55:55 2016 -0600 + + JIRA-558 Remove Intel Ethernet and SD libraries + + Signed-off-by: Brian Baltz + +commit c1be134976c7afa8fd3af95907e0f043efc6bc8d +Author: Dino Tinitigan +Date: Mon Apr 4 10:17:29 2016 -0700 + + CurieI2S - minor fixes/improvements + + -prevent buffer overflow in I2S_RxCallback example + -calculate delay for any sample rate + +commit 0ad15b5a46969ddda239cf764caebb32a81e56ae +Author: Bradford H. Needham +Date: Sun Apr 3 11:07:14 2016 -0700 + + Fixed spaces in keywords.txt; keywords now highlight properly + +commit 89dd2287dfd41a12db0a34eed7d94b8593b2e5da +Author: Dino Tinitigan +Date: Fri Apr 1 12:06:36 2016 -0700 + + CurieI2S add examples + + -add basic examples using callback functions/interrupts + -add callback function to fill tx buffer + +commit 2bca6fc912b51200ad12b833e52321e65ccf553b +Author: Dino Tinitigan +Date: Wed Mar 30 12:18:36 2016 -0700 + + Interrupt based I2S library + + -CurieI2S using interrupts + +commit a54f5132630fb2825e27d84cf4ddf3d8ee22192a +Author: Sidney Leung +Date: Thu Mar 31 11:50:12 2016 -0700 + + Jira-575, Jira-506: Bug fixes to print float and double correctly, in a string or std output. + +commit ccc02128ad2371d3ed35ebb3a01d4146df930e97 +Author: Dino Tinitigan +Date: Wed Mar 23 18:07:38 2016 -0700 + + CurieI2S use interrupts for Tx + + -Use interrupts to fill FIFO buffer from TX buffer + +commit 4fae4ddb4cc9bb14c6c4ccc6147253944c778bb9 +Author: Dino Tinitigan +Date: Tue Mar 8 15:01:49 2016 -0800 + + CurieI2S initial commit + + - Minimal functional version + +commit 2eb99d10a116a092a2ade78a79d96d164847f9b2 +Author: agdl +Date: Fri Mar 11 13:58:24 2016 +0100 + + Removed delay from examples as requested by Tom + +commit 38a508e4552f88649cdd664babd0165673b7caa6 +Author: Sidney Leung +Date: Tue Mar 8 13:31:14 2016 -0800 + + Jira-528. Make CurieTimerOne library to be compatible with the TimerOne library originated from Paul. Created methods that have the same name and functionalities as in the TimerOne library. + +commit 98ee736fa2bb12e99d80bad866a9a298e0bc1c2e +Author: SimonePDA +Date: Mon Mar 14 18:01:40 2016 +0100 + + Update CurieTimer1Interrupt.ino + + Changed the style of the code according to Arduino's Tutorials guidelines. + +commit 40d3fb07d98db264dc1f2b0c2b5a3057359a721d +Author: Bradford H. Needham +Date: Tue Mar 8 20:35:51 2016 -0800 + + Added Eddystone-URL support: added setAdvertisedServiceData() to support BLE Service Data; changed Incomplete to Complete Service UUID list code in Advertising initialization, required by Eddystone-URL protocol; added (for debugging) getAdvertisingLength() and getAdvertising() to enable a Sketch viewing the Advertizing packet. + +commit 17eac79d76b721eb116179aa87f0e3627add4927 +Author: Dino Tinitigan +Date: Tue Mar 8 10:54:11 2016 -0800 + + Fix Klockwork issues in CurieEEPROM #142 + + -Also fixes issue of not being able to rewrite the last 3/4 of the + EEPROM area + +commit e50ef904347f9d4da0a7c4e0e5523b4ff5d040bb +Merge: 1f3899a c7b30f4 +Author: Brian Baltz +Date: Tue Mar 8 11:51:38 2016 -0700 + + Merge pull request #141 from bbaltz505/memory_size + + Fixing sketch size calculation (text + ctors + rodata + datas + +commit c7b30f4c627ad20b5cdbc621803afa76fba61bfd +Author: Brian Baltz +Date: Tue Mar 8 07:07:43 2016 -0800 + + Decrease max sketch size to 155,648 bytes + + Signed-off-by: Brian Baltz + +commit f02410e9bf138f48243b62853b44c9b797ea01e9 +Author: Brian Baltz +Date: Wed Feb 24 20:04:14 2016 -0800 + + Fixing sketch size calculation (text + ctors + rodata) + + Signed-off-by: Brian Baltz + +commit 1f3899a7cabd71ae9fbda7f721a117614587adaa +Author: SimonePDA +Date: Thu Mar 3 22:29:38 2016 +0100 + + Update library.properties + +commit c4a20140569016649b4333ef6c349c8041827164 +Author: SimonePDA +Date: Thu Mar 3 21:55:23 2016 +0100 + + Update library.properties + +commit 72b85b2bdb884a995620e3aeb66d518844b0746a +Author: SimonePDA +Date: Thu Mar 3 21:39:55 2016 +0100 + + Update library.properties + +commit 3d019ed2cebf9034e37cda961d16b08042d8f731 +Author: SimonePDA +Date: Thu Mar 3 21:34:23 2016 +0100 + + Update library.properties + +commit 5bb196cdd961a0fb54129d1fcf2fc99778ab0914 +Author: SimonePDA +Date: Thu Mar 3 19:58:12 2016 +0100 + + Update library.properties + +commit fda58303cb959000bb8d04855f5b11838423d15d +Author: SimonePDA +Date: Thu Mar 3 19:35:08 2016 +0100 + + Update library.properties + +commit 7905133b686bf5dcc0112b828a9d8128ddf6ee8e +Author: SimonePDA +Date: Thu Mar 3 19:24:32 2016 +0100 + + Update library.properties + +commit bc9c387332dc16290f1c1c180eabae0d33eec8fe +Author: SimonePDA +Date: Thu Mar 3 19:12:32 2016 +0100 + + Update library.properties + +commit 953479ae0f37961fb8f4040fbdcd8848f59cae33 +Merge: 3bd268c eacb1bd +Author: Brian Baltz +Date: Wed Mar 2 15:06:03 2016 -0700 + + Merge pull request #129 from 00alis/src_folders + + Moved .h and .cpp file in src folder + +commit 3bd268c3992f095233bb16a18dac8541b86b0bf2 +Author: Brian Baltz +Date: Wed Mar 2 10:40:59 2016 -0800 + + Updating install steps in README + + Signed-off-by: Brian Baltz + +commit eacb1bd114ec57cbecab357dfab8a396b5e7b09d +Author: Alice Pintus +Date: Wed Mar 2 12:49:10 2016 +0100 + + Rename library.properties + +commit 1cbd80b226b237ddb5a2cd5705f63975ed6f9596 +Author: Alice Pintus +Date: Wed Mar 2 11:56:24 2016 +0100 + + Moved .h and .cpp file in src folder + +commit e8b8a5db546696735d5de38e58253b75cb411e7a +Author: Sandeep Mistry +Date: Fri Feb 26 15:05:54 2016 -0500 + + Change set/get accelerometer and gyro offsets to use floats values + + This makes it consistent with other API changes to convert the register + values to floats and vice versa. + +commit ffc51eff1581f38c977798114f94affc5393d0e2 +Author: Sandeep Mistry +Date: Tue Feb 2 10:08:35 2016 -0500 + + Add setConnectionInterval API to CurieBle + +commit f11de80d789e9071a05387cfa96d80e08db563cc +Author: Martino Facchin +Date: Tue Feb 23 15:06:09 2016 +0100 + + use sprintf-based implementation for dtostrf + + altough the current implementation passed all the unit tests, an user reported problems with the actual code (http://forum.arduino.cc/index.php?topic=380422.0). + If libc has float support there is no need to do strange things like on AVR, so we can use the sprintf implementation + +commit ad1586ef179fa0920edb29d4e3a05307a267a3cd +Author: Calvin Park +Date: Mon Feb 29 15:14:32 2016 -0800 + + Updated README.md with instructions to manually install corelibs + +commit 582d03353b5111b8030ec5a6954ec480acaaa6d3 +Author: Dino Tinitigan +Date: Fri Feb 26 11:04:59 2016 -0800 + + i2c bus scan workaround + + -sends a byte of value 0 when doing an i2c bus scan + +commit f6d8aa81847643f1102805e9b1f6ef32c3fbc0a9 +Author: Dino Tinitigan +Date: Mon Feb 22 14:08:34 2016 -0800 + + CurieEEPROM - ensure 32-bit allignment + + -ensure 32-bit address allignment for put(), get(), read(), and write() + +commit 64ee87970db858c1e4caed63e41f21158f6653b7 +Author: Brian Baltz +Date: Wed Feb 24 17:45:23 2016 -0800 + + Remove custom Adafruit_Motor_Shield_V2 library + + Signed-off-by: Brian Baltz + +commit 6f9739fac258c812f74139c16017817aa4045ace +Author: Brian Baltz +Date: Thu Feb 18 17:16:09 2016 -0700 + + Use Go-based upload script + + Signed-off-by: Brian Baltz + +commit 83bb5f5d818a3ed2db05a0b784c6888822d3e2e7 +Author: jysholar +Date: Tue Feb 23 10:33:17 2016 -0700 + + Rename CurieTimer to CurieTimerOne for JIRA-532 + +commit 3448d230240682d1579b26e6f095afe479d68a09 +Author: Sandeep Mistry +Date: Wed Feb 17 08:37:19 2016 -0500 + + Remove enums for ranges and duration, auto calibrate/set offset now enables offset, rename interrupt routines to interrupts/noInterrupts + + keywords.txt updates + +commit 63a01dd918a1fd16d7a914dfd312c763699abdde +Author: Sandeep Mistry +Date: Tue Feb 2 11:40:38 2016 -0500 + + Rename CurieBle to CurieBLE + +commit e82fe904dba101da79bbd7dd63060f223d8716cc +Author: Sidney Leung +Date: Fri Feb 5 11:55:00 2016 -0800 + + Jira-507 and 514: 1. Bug corrected in timer ISR where the clearing of the pending interrupt was at the entrance instead of exit. When interrupts could happen fast enough to put the ISR in a loop. 2. The overhead for the s/w pwm code is 4-5 usec. Thus, limited the shortest pwm pulse to 10 usec (high and low). + +commit 2299da2693c0fe867c7b90d998b43c15d666e763 +Author: Brian Baltz +Date: Wed Feb 10 07:47:38 2016 -0800 + + Stop interrupt when switching to 100% duty cycle + + Signed-off-by: Brian Baltz + +commit da68b6fcd5363a5b771e02ec7fa4ab1774286ebe +Author: Brian Baltz +Date: Wed Feb 3 12:10:41 2016 -0800 + + ATLEDGE-516 Stop callback when setting duty cycle to 0 + + Signed-off-by: Brian Baltz + +commit ef62ce6bff2c6059e1f0750c8fa2ec9af424f712 +Author: Brian Baltz +Date: Wed Feb 3 12:09:33 2016 -0800 + + Fix pwmStop typo in keywords.txt + + Signed-off-by: Brian Baltz + +commit 6059a142b0bed4bd3b1a2e2c45a298bc72ee6754 +Author: agdl +Date: Tue Feb 2 15:13:42 2016 +0100 + + Fixed define + + To be compliant with all the others + +commit 65f3b2887bfc3dfe228a3216a4c8b4a4e51dcfac +Author: Dino Tinitigan +Date: Tue Feb 2 11:32:00 2016 -0800 + + CurieEEPROM eeprom_write example + + -increment eeprom address at the correct location so it matches the read + example + +commit 6885cded61c1dac1e45d4a33436da6c840994cc4 +Author: Dino Tinitigan +Date: Wed Jan 27 14:39:40 2016 -0800 + + CurieEEPROM reimplement put and get to not use STL + + -reimplement put() and get() to not use STL due + to incompatibility with max() and min() macros + +commit 0a382340a0487691627ad3af48bce01572029b4b +Author: Krzysztof Sywula +Date: Wed Jan 27 15:06:53 2016 -0800 + + Green pixel fix + + JIRA: ATLEDGE-470 + +commit a6381cb123d77d3da630eda19c5d2947c1e129c6 +Merge: 07a53ab 555c23b +Author: Brian Baltz +Date: Tue Jan 26 17:05:08 2016 -0700 + + Merge pull request #109 from SidLeung/master + + Jira-501: CurieTimer.resume() did not re-enable interrupt at the controller. + +commit 555c23bc7cabc7217818b18a267a490d01fa63ad +Author: Sidney Leung +Date: Tue Jan 26 15:46:33 2016 -0800 + + Jira-501: CurieTimer.resume() did not re-enable interrupt at the controller. + +commit 07a53ab9f5174153967e1ebdac7527b680ec6232 +Author: Dino Tinitigan +Date: Tue Jan 26 14:13:15 2016 -0800 + + CurieSoftwareSerial remove non-working example + + -Remove non-working TwoPortReceive.ino example + +commit 0eca3b32f09a30c0d146f5730d29f91527836108 +Author: Sidney Leung +Date: Fri Jan 22 15:07:03 2016 -0800 + + Jira-500. Corrected typo error. Routine, pwdStop, is now named as, pwmStop. + +commit 02c960487caaaea5e77ca2370af73b59064f442a +Merge: 8a517b6 1be5b6c +Author: Calvin Park +Date: Thu Jan 21 14:03:13 2016 -0800 + + Merge pull request #103 from pricopb/master + + Remove OneWire and Adafruit_DotStar libraries. + +commit 8a517b656072e22b9394ec58b6ee22e179a39789 +Author: Dino Tinitigan +Date: Wed Jan 20 15:08:42 2016 -0800 + + CurieSoftwareSerial timing issue fixes + + -fix some timing issues with slower baud rates + +commit aaa453d00cf6e06f36dbe5c9012b252ebbcc9170 +Author: Brian Baltz +Date: Tue Jan 19 14:17:18 2016 -0800 + + ATLEDGE-491 - rename sketchUploader tool to arduino101load + + Signed-off-by: Brian Baltz + +commit 4932616c873d7b06af99698214a718f572a404f6 +Author: Dino Tinitigan +Date: Tue Jan 19 15:53:12 2016 -0800 + + CurieEEPROM fixes + + -remove leftover debug prints + -put() and get() should only support 32-bit alligned addresses + +commit effd880b1cae45f923fabcd8582e86544288ed26 +Author: Dino Tinitigan +Date: Thu Jan 7 15:17:40 2016 -0800 + + CurieSoftwareSerial Improvements + + -changed global variables into static + -allow different baud rates for different instances of a SoftwareSerial object + -minor code cleanup of unused code + -moved source code into src directory + +commit 1be5b6cb91fa5f6de5749fa1268f03606f6a83f9 +Author: Bogdan Pricop +Date: Fri Jan 15 08:32:32 2016 +0000 + + Remove OneWire library. + + Remove the OneWire library because the code changes for porting it to + Arduino101 were pushed to Paul Stroffegen's repository: + https://github.com/PaulStoffregen/OneWire/commit/e21914433ec588a23922099fa4435fbe0493e5ab + + Signed-off-by: Bogdan Pricop + +commit 1253ea89e0d81b943a1e22aad79559913ea2e297 +Author: Bogdan Pricop +Date: Fri Jan 15 07:38:11 2016 +0000 + + Remove Adafruit_DotStar library. + + Arduino101 specific changes moved to Adafruit github repository: + https://github.com/adafruit/Adafruit_DotStar/commit/0fce0fe52956a7eaf8f29ecfbaecc4138e958ed5 + + Signed-off-by: Bogdan Pricop + +commit ef9d032bf468a09a6921ce4334aa50c1ac227e9c +Author: sys_maker +Date: Fri Jan 8 15:56:10 2016 -0800 + + Rebuilding libarc32 library and hard-coding tools version + + Signed-off-by: sys_maker + +commit cc7cccc85afe0fe9c558913305c0ffe86b213caf +Author: Kevin Moloney +Date: Fri Jan 8 13:27:59 2016 +0000 + + find() missing overload in Stream.h + + Signed-off-by: Kevin Moloney + +commit b0531afe05cb8e5f301f04e9a207b7fcbb00c93b +Author: Alice Pintus +Date: Fri Jan 8 11:41:36 2016 +0100 + + Update boards.txt + + Change name Arduino 101 into Arduino/Genuino 101 to be consistent with the rest of the ecosystem + +commit 3ca20d94b362264b0e9ddac74c3731c1ab26a467 +Author: Dino Tinitigan +Date: Tue Jan 5 14:06:21 2016 -0800 + + ATLEDGE-487 CurieEEPROM library + + -Initial commit for CurieEEPROM library + -Implements EEPROM functionality using an available area of the ROM + -supports both BYTE and DWORD reading/writing + +commit b19562438ca48b50a4e505d786d522900d9300fd +Merge: 23e6f16 6fc749d +Author: Calvin Park +Date: Wed Jan 6 15:45:42 2016 -0800 + + Merge pull request #90 from sandeepmistry/imu-api + + Add new API's and examples to CurieIMU + +commit 23e6f16a539bfc4b3fef5b76e1787a66d3966a76 +Author: Sidney Leung +Date: Mon Dec 21 12:12:18 2015 -0800 + + Jira-462: Call Available() after calling end() indicated data available. + + Code Modifications: + + 1. Added checking of device open in these routines: available, read, peek. + Will return 0 or -1 if device is not opened to indicate invalid operation. + +commit 46519997e46f5088d627e537086cab14e8e14e6d +Author: Krzysztof Sywula +Date: Tue Dec 22 16:55:54 2015 -0800 + + dtostrf: fix overflow problem + + JIRA: ATLEDGE-315 + +commit 6fc749d08dfe770992fe3f50d3073260960c98d9 +Author: Sandeep Mistry +Date: Mon Dec 21 13:46:04 2015 -0500 + + Remove calibration steps from accelerometer and gyro examples + +commit af28e53e5b3280f4188cc2c379bacf24cd454bb7 +Author: Sandeep Mistry +Date: Mon Dec 21 13:43:50 2015 -0500 + + Use int for accelerometer and gyro API's instead of short + +commit c84af94b39a1efbf3afee132ac49469935b0aa7e +Author: Sandeep Mistry +Date: Mon Dec 21 13:32:14 2015 -0500 + + Remove call to setIntEnabled, attachInterrupt enables interrupts + +commit 44fe3307074cb4f5648dbba5718f893b7a4098f4 +Author: Sandeep Mistry +Date: Mon Dec 21 12:10:45 2015 -0500 + + Rename CurieImu to CurieIMU + +commit 6557caf258f6d4cef6048036a6cdf15c4fa2a882 +Author: Sandeep Mistry +Date: Mon Dec 21 11:37:59 2015 -0500 + + Add accelerometer orientation example + +commit a50c6b649f093f6d5dbb19e9e376f700976b833e +Author: Sandeep Mistry +Date: Mon Dec 21 11:12:32 2015 -0500 + + Update example header + +commit 7e628f15227d24c81c4f42bfe1f3cb351bdb8f78 +Author: Sandeep Mistry +Date: Mon Dec 21 11:12:16 2015 -0500 + + Initial gyroscope example + +commit cfd445144984d9aaaf3995f257cc5db0e14f04af +Author: Sandeep Mistry +Date: Mon Dec 21 10:57:14 2015 -0500 + + Correct typo in example, az was printed instead of ax + +commit e8a13f7c205de09fa50e90946e9634ef0d6e1562 +Author: Sandeep Mistry +Date: Mon Dec 21 10:52:38 2015 -0500 + + Add accelerometer only example + +commit c45ece5a1392b6f0cc7cfdd202b11cc1f87e9d30 +Author: Sandeep Mistry +Date: Mon Dec 21 10:50:17 2015 -0500 + + Rename readAcceleration and readRotation API's to readAccelerometer and readGyro + +commit 162e0cfbbde7b3e7b8c63daa9444507c02917cbe +Author: Sidney Leung +Date: Tue Dec 15 11:12:47 2015 -0800 + + Jira-32 and Jira-386. CurieTimerOne lib + +commit 1e30828bac4a73ef240f1bf8df8aa5e1a2ff1ff3 +Author: Sandeep Mistry +Date: Fri Dec 18 17:46:47 2015 -0500 + + Use if statements instead of switch for a few API's + +commit f6d1516068c33085766dfa042fe843f69511277c +Author: Sandeep Mistry +Date: Fri Dec 18 17:26:19 2015 -0500 + + Sketch auto format + +commit 5e3605e67ef21d1b59fc5d3d7e7f034c674d6477 +Author: Sandeep Mistry +Date: Fri Dec 18 17:15:06 2015 -0500 + + Keyword updates from Helena + +commit ef3ccb5aebee42c2497631b8e7b77ecd5b5c2359 +Author: Sandeep Mistry +Date: Fri Dec 18 17:10:10 2015 -0500 + + Update example to use enums + +commit 0faeb636cda253ad2768af7db12df42b3c689aa0 +Author: Sandeep Mistry +Date: Fri Dec 18 17:09:13 2015 -0500 + + Add set/get gyro/accelerometer rate API's, and map more BMI160 enums + +commit 38f928fff483abfba10927610f889e022779f52a +Author: Sandeep Mistry +Date: Fri Dec 18 16:33:34 2015 -0500 + + Use enums for accelerometer and gyro ranges + +commit e5a1c21005d0f6049008e8856481c4642b5227ef +Author: Sandeep Mistry +Date: Fri Dec 18 15:34:39 2015 -0500 + + Example updates from Helena + +commit 0b57d5eea31457d9bef2bf4df76a8c1dcc0b92da +Author: Sandeep Mistry +Date: Fri Dec 18 15:34:02 2015 -0500 + + Add Arduino style API's for IMU, rename CurieImu to CurieIMU + +commit b5c27aef2d78d264733f1056a070ca49de407f2e +Author: Dino Tinitigan +Date: Tue Dec 15 16:11:48 2015 -0800 + + ATLEDGE-429 CurieSoftwareSerial fixes + + -Creating a new CurieSoftwareSerial object no longer break functionality + of previous ones. + -Minor Cleanup + +commit f9d1d5c5c26d615f08775d9bffd8bd58b5fd75a1 +Author: Martino Facchin +Date: Thu Dec 17 15:58:56 2015 +0100 + + hardcode sketchUploader version + + while we solve the bug IDE-side, it is safe to revert using the hardcoded version number to handle the sketchUploader installation; + This solves a clash with outdeted Galileo/Edison core which tools are picked randomly (depending on json download order and java filesystem library internal ordering). + +commit 520f922f28c53d22be751dc0e8fe60e9466fb548 +Author: Kevin Moloney +Date: Tue Dec 15 14:59:42 2015 +0000 + + ATLEDGE-426/ATLEDGE-445 CurieImu missing functions + + * Removed CurieImu.getZeroShockDetected() & CurieImu.getZeroMotionDetected(). + * Functions replaced with CurieImu.getIntShockStatus() & CurieImu.getIntZeroMotionStatus(). + + * Implemented getStepDetectionMode(). + * There was no means of verifying setStepDetectionMode(). + + Signed-off-by: Kevin Moloney + +commit 4422bce4466f9350baeed33a2b36b97fdde5694b +Author: Dino Tinitigan +Date: Wed Dec 9 15:09:55 2015 -0800 + + AGM-157 Add support for _BV() macro + + Add support for _BV() macro + +commit 0c1ba8baef33e74383c0be797955baf386d5c4aa +Merge: 44bc8db b3273bd +Author: Calvin Park +Date: Mon Dec 14 16:55:28 2015 -0800 + + Merge pull request #84 from sandeepmistry/rtc-api + + Rename CurieRTC to CurieTime and integrate some Processing/Time lib API's + +commit b3273bd9cec3f721e9958a768456965c3a7fe5b2 +Author: Sandeep Mistry +Date: Mon Dec 14 19:26:13 2015 -0500 + + Update name in keywords.txt + +commit d0c2800286ecf575fa900d9210d7473c9e68ba04 +Author: Sandeep Mistry +Date: Mon Dec 14 14:30:26 2015 -0500 + + Simplify SetTime example, use hard coded time + +commit e5072e27a530a68acda9fe84fb96abaf1f31faac +Author: Sandeep Mistry +Date: Mon Dec 14 14:27:15 2015 -0500 + + Add API's to convert epoch time to date/time component + +commit 304c733e07f4306c89533d5a36bc714cb43683ba +Author: Sandeep Mistry +Date: Mon Dec 14 14:03:13 2015 -0500 + + Rename CurieRTC to CurieTime + +commit 4d9c07eb44178b0afb185f247de561da87d8aa3d +Author: Sandeep Mistry +Date: Mon Dec 14 12:06:45 2015 -0500 + + Make current count value and counter load registers volatile + +commit b872431e1df13cebb07cef8b2d85bc0906202462 +Author: Sandeep Mistry +Date: Mon Dec 14 11:48:01 2015 -0500 + + Parse date and time strings using Arduino string class instead of sscanf + +commit ed69e4f8c0f57db4c910b84d65d93d67e716766b +Author: Sandeep Mistry +Date: Mon Dec 14 11:27:42 2015 -0500 + + Auto format + +commit 7bb2c23d0db4503d59cfeab00b7f12be8c8ff32c +Author: Sandeep Mistry +Date: Mon Dec 14 11:06:09 2015 -0500 + + Change CurieRTC lib to have Processing style time API's based on the RTC value + +commit 23a1c3b18c45abf82fd9b13393497d83c4496056 +Author: Sandeep Mistry +Date: Mon Dec 14 10:34:41 2015 -0500 + + Remove bundled time library + +commit 44bc8db8808ede092e85dcdb67c3f03b9f24c59f +Author: Krzysztof Sywula +Date: Fri Dec 11 15:32:34 2015 -0800 + + Fixed comparison between uint32 and int32 + + JIRA: ATLEDGE-457 + + Servo.cpp warning + +commit 51c7f7d9145e11f834a4fae290c6693cb871e3fb +Merge: e093df3 956aac4 +Author: Calvin Park +Date: Mon Dec 7 14:17:16 2015 -0800 + + Merge pull request #81 from sandeepmistry/arduino-ble-peripheral-api + + Initial port of BLEPeripheral style API to CurieBle library + +commit e093df3e60fca0338fe23c46ed3e34bb078ef5df +Merge: 39881ec f6c59c1 +Author: Calvin Park +Date: Mon Dec 7 13:16:24 2015 -0800 + + Merge pull request #80 from sandeepmistry/imu + + CurieImu examples updates and keywords.txt + +commit 956aac4612d5b81da30de7c5621596fa3c8d2be1 +Author: tigoe +Date: Thu Dec 3 23:33:37 2015 -0500 + + Style changes to IMU examples + + Note: IMU Library API still needs to be standardized. + +commit 744e66ebb2de90cfbe48d9d3f3497c18f74efddc +Author: Sandeep Mistry +Date: Fri Dec 4 12:43:53 2015 -0500 + + Shorten local name in examples + +commit 44cd7e505e59644a3fca7303b316293372580f9f +Author: Sandeep Mistry +Date: Fri Dec 4 10:45:47 2015 -0500 + + Update note on local name + +commit b272315dfe91418c79ee6ce445144ca3178a2571 +Author: Sandeep Mistry +Date: Fri Dec 4 10:37:13 2015 -0500 + + Misc example updates + +commit 93ce2bb743a166a88e1cd9e52bfe1d24be005f83 +Author: Sandeep Mistry +Date: Fri Dec 4 10:23:43 2015 -0500 + + Correct case of include + +commit ccfaa837fd5810424474bf4a045ae17837fe4b1f +Author: Sandeep Mistry +Date: Fri Dec 4 10:12:52 2015 -0500 + + Correct setDeviceName + +commit 3c848247e8be8dd1b9c9622ebb4cb09fc2e6379e +Author: Sandeep Mistry +Date: Fri Dec 4 09:27:17 2015 -0500 + + Remove AutomationIO example + +commit 3b8efcc191ecbea77096f116195667fde19d85e4 +Author: tigoe +Date: Thu Dec 3 22:24:01 2015 -0500 + + Style changes to BLE library examples + +commit a5c7f84f29a4f3c14594d50695f372702abec940 +Author: Sandeep Mistry +Date: Thu Dec 3 17:01:21 2015 -0500 + + Correct incorrect read permissions for non-writable characteristics + +commit c6924337ea3cb561a950cf0f08accf8696b6f59c +Author: Sandeep Mistry +Date: Thu Dec 3 16:55:03 2015 -0500 + + Dynamically allocate characteristic and descriptor data buffers + +commit 040b63b1d23533774edf7cb46604740bbe92d2ff +Author: Sandeep Mistry +Date: Thu Dec 3 16:54:28 2015 -0500 + + Set initial value of characteristics + +commit 479e8600a665ad21b3b6181997ce871b100c94f4 +Author: Sandeep Mistry +Date: Thu Dec 3 16:50:16 2015 -0500 + + Sent initial value of switch characteristic + +commit 6d6a0c6e5b681c6be61a110d0fe4309c5a38b2a7 +Author: Sandeep Mistry +Date: Thu Dec 3 16:47:18 2015 -0500 + + Set initial value of switch characateristic + +commit ce32c92177a01fd6be5688bf693050d225570ace +Author: Sandeep Mistry +Date: Thu Dec 3 16:40:57 2015 -0500 + + Remove while(!Serial) + +commit b6eea7463bd84ec516f56f98f50fd0f424460060 +Author: Sandeep Mistry +Date: Thu Dec 3 16:31:11 2015 -0500 + + Use bool for public API's return types instead of BleStatus type + +commit 1596dc12e3ca099d483d52476c3c8378ad8d7c3a +Author: Sandeep Mistry +Date: Thu Dec 3 15:47:57 2015 -0500 + + Change public API types to use non-stdint types + + as per https://www.arduino.cc/en/Reference/HomePage + +commit 026f4c044879ae294e3c41aa0fe5d16336feefdf +Author: Sandeep Mistry +Date: Thu Dec 3 15:34:42 2015 -0500 + + Replace battery monitor example with new version + +commit 7767d3d6c42fb1c555ac68a5c9e687b33e6afbd8 +Author: Sandeep Mistry +Date: Thu Dec 3 15:31:15 2015 -0500 + + Rename Ble prefixes to BLE + +commit ee0ae446682773bbd8b240de2d03a0627096e87c +Author: Sandeep Mistry +Date: Thu Dec 3 14:50:40 2015 -0500 + + Separate device name from advertised local name + +commit c23609225aee221ed7ad83429b186826b55baca7 +Author: Sandeep Mistry +Date: Thu Dec 3 14:34:20 2015 -0500 + + Don't advertise the device appearence + +commit 661fc89a9981932103c2a0cc96d46becd6177018 +Author: Sandeep Mistry +Date: Thu Dec 3 14:29:37 2015 -0500 + + Correct advertised service uuid + +commit fe78397481705f520627c860db257ad62a9c5d72 +Author: Sandeep Mistry +Date: Thu Dec 3 14:08:36 2015 -0500 + + Correct presentation format descriptor handling + +commit c1882ee3119a3754378ed7f085c6ab19f53843ab +Author: Sandeep Mistry +Date: Thu Dec 3 12:54:37 2015 -0500 + + Add special handling for 0x2902 and 0x2904 descriptors + +commit 8ee6a0264c81f9534633234b9ff8e809c37abbe0 +Author: hbisby +Date: Thu Dec 3 17:56:19 2015 +0100 + + variable name changed from switchCharacteristic to ledCharacteristic + +commit e0139b68668f891b4ccd6ef166db67c20c172686 +Author: Sandeep Mistry +Date: Thu Dec 3 11:51:58 2015 -0500 + + Add delay(1) in poll + +commit 1b4f5134b152fc062c2f7d2d2aa29f1dfdcb1218 +Author: Sandeep Mistry +Date: Thu Dec 3 10:41:51 2015 -0500 + + keywords.txt updates + +commit 855737bc879868ea759c4eed46ec9420c886752a +Author: Sandeep Mistry +Date: Thu Dec 3 10:41:33 2015 -0500 + + Change internals to use less private friends, add comments in header files, make var types more consistent + +commit 6c946eda6d1ccd4a610e91ab99babbe7924832ca +Author: Sandeep Mistry +Date: Thu Dec 3 10:33:37 2015 -0500 + + User serial baud rate of 9600 for all examples + +commit df691a9ff790a9cf020878104dc138c9c0aeda66 +Author: Sandeep Mistry +Date: Thu Dec 3 10:28:29 2015 -0500 + + Rename with capital B + +commit f68199943d2c525cb3d6fdcbd0cfaf07427aa18b +Author: Sandeep Mistry +Date: Thu Dec 3 10:21:48 2015 -0500 + + Correct UUID + +commit 90886c696057dfd2fb95edeb60502b82c96f8d48 +Author: Sandeep Mistry +Date: Thu Dec 3 10:06:52 2015 -0500 + + Correct include filename + +commit 03673b47fa1cc950734baffb9bb8313629c366f9 +Author: Sandeep Mistry +Date: Wed Dec 2 16:57:46 2015 -0500 + + Use dashed UUID in example + +commit 940374fa55bae556ac093bad2cf6a200529150f2 +Author: hbisby +Date: Wed Dec 2 15:44:59 2015 +0100 + + changed format of long UUIDs + +commit 3d3f973329d08a4f617da27e30f03abed12c4335 +Author: hbisby +Date: Wed Dec 2 15:26:01 2015 +0100 + + ported examples from BLEPeripheral buttonLED and callbackLED + +commit 2581cffca43521c85d5c0101cf3c57878397930e +Author: Sandeep Mistry +Date: Tue Dec 1 17:29:47 2015 -0500 + + API Cleanup + +commit 03c82d3babe18a2fbae49abd49ec9f72cb9127a2 +Author: Sandeep Mistry +Date: Tue Dec 1 16:03:57 2015 -0500 + + Add new LED example + +commit 6e5a9dee80787f90c47435901792c27c2b2687a1 +Author: Sandeep Mistry +Date: Tue Dec 1 15:20:24 2015 -0500 + + Add written and subscribed API's to BleCharacteristic, pass central into BleCharacteristic even handers, remove BleAck event for now + +commit cd54545dc25a0a47cddbfc7722d24f626db12cb2 +Author: Sandeep Mistry +Date: Tue Dec 1 10:47:07 2015 -0500 + + Update previousMillis in example + +commit 3d3f65f2135bef419ce82dcc06690f251419f258 +Author: Sandeep Mistry +Date: Tue Dec 1 10:31:12 2015 -0500 + + Reverse UUID conversion loop + +commit 4dfc0a4f68b7cf7c57f05d5ae6a6770b8e718509 +Author: Sandeep Mistry +Date: Mon Nov 30 17:52:10 2015 -0500 + + Experimental BatteryMonitor example sketch with new API + +commit 99ab222ec39c31e2c27ba8962f4022c499b341b6 +Author: Sandeep Mistry +Date: Mon Nov 30 17:49:47 2015 -0500 + + BleCharacteristic event handler API's + +commit d4efbb2cf9f86ece8804877b268f8ce00f40b736 +Author: Sandeep Mistry +Date: Mon Nov 30 17:20:28 2015 -0500 + + Make descriptor BleCharacteristic API's private + +commit 06a05c2a86886ee4deab539d76a23e85798b0449 +Author: Sandeep Mistry +Date: Mon Nov 30 15:49:12 2015 -0500 + + More keyword.txt updates + +commit e01b7bf4d56a771c3ff3d9db9b1d26de05160a5e +Author: Sandeep Mistry +Date: Mon Nov 30 15:48:30 2015 -0500 + + BlePeripheral event handler rename, and misc var name consistency + +commit 69697e3670a813cfb6a5912d35e80427012f7645 +Author: Sandeep Mistry +Date: Mon Nov 30 15:21:46 2015 -0500 + + Add typed characteristics + +commit be1fcea44f1b9266a88de7d97b341ca4125585fe +Author: Sandeep Mistry +Date: Mon Nov 30 14:13:03 2015 -0500 + + Change return type of BleCentrall::address a C string, make getLocalAddress private for now + +commit 2fb56144f09631a97e73b86f3ffeae5289497f10 +Author: Sandeep Mistry +Date: Mon Nov 30 13:13:11 2015 -0500 + + Whitespace formatting + +commit a711fa2a7bc1cb0cbeaae4b179010b4c2e5ed326 +Author: Sandeep Mistry +Date: Mon Nov 30 13:12:57 2015 -0500 + + Allow BleCharacteristic::setValue to be called before begin + +commit b06df63df5e01de9a13ceddb74dad9fb15d3b821 +Author: Sandeep Mistry +Date: Mon Nov 30 12:24:52 2015 -0500 + + Introduce BleUuid, UUID's are now strings externally + +commit 67d47f330d4e8de2999a4c82f25eed2c3c4c4e45 +Author: Sandeep Mistry +Date: Mon Nov 30 11:38:22 2015 -0500 + + Change begin to return BleStatus + +commit a1d134b6e8fc2ba15b2df3a28244facb5d7d4bd9 +Author: Sandeep Mistry +Date: Mon Nov 30 11:00:33 2015 -0500 + + Add BleAttibute class and add new public addAttribute API + + Make addPrimaryService and getState API’s private. As well as, + BleService::addCharacteristic, BleService::addSecondaryService, and + BleCharacateristic::addDescriptor. + +commit e136ce82f377896cd8f442fe626682654cb9086b +Author: Sandeep Mistry +Date: Fri Nov 27 17:24:26 2015 -0500 + + Expose characteristic properties instead of access and notify mode + +commit 6d101d6288c9bad8960c73fafa40f54692e55333 +Author: Sandeep Mistry +Date: Fri Nov 27 16:45:50 2015 -0500 + + Split up GAP event callback registration + +commit bf3d0bbe1886be69b0b9fedac2a3c73a0cac59e0 +Author: Sandeep Mistry +Date: Fri Nov 27 15:55:22 2015 -0500 + + More keywords + +commit dbdebdb84f982c1edd8e9088e7b3d71a8534b7aa +Author: Sandeep Mistry +Date: Fri Nov 27 15:55:08 2015 -0500 + + Add BleCentral class, disconnect API, split out BleAddress + +commit 4e2a4d7d64669448ea4edcfbd10f6194041a6a9a +Author: Sandeep Mistry +Date: Fri Nov 27 14:12:03 2015 -0500 + + Add setAdvertisedServiceUuid API, and remove param to advertise from addPrimaryService + +commit 2881aa5175d2e0120a4c25dbf8188a589709405d +Author: Sandeep Mistry +Date: Fri Nov 27 14:11:11 2015 -0500 + + Add uuid method to BleService + +commit e7b71f54697a90f1b27e306817142a502cfd0690 +Author: Sandeep Mistry +Date: Fri Nov 27 13:47:49 2015 -0500 + + Rename set/getName to set/getLocalName + +commit ff11e027d85ae5fc312bb87509b65c0f87e62729 +Author: Sandeep Mistry +Date: Fri Nov 27 13:41:00 2015 -0500 + + Add poll API (no-op for now) + +commit 60f6c475aa6b0cd3fe6e366adebc2d9ae4924c7b +Author: Sandeep Mistry +Date: Fri Nov 27 13:37:21 2015 -0500 + + Add begin and end API's + +commit 6d5aa275c531c4fb180256f86e5ede51158710f5 +Author: Sandeep Mistry +Date: Fri Nov 27 13:31:18 2015 -0500 + + Rename bleDevice variable to blePeripheral + +commit 08fd946c6d1632198f4f3bc40d21ac8bb425c70e +Author: Sandeep Mistry +Date: Fri Nov 27 13:28:13 2015 -0500 + + Initial keywords.txt + +commit e8bdd87b7b0cf9ee3d993b2c5542f7593272c41b +Author: Sandeep Mistry +Date: Fri Nov 27 13:28:01 2015 -0500 + + Rename BleDevice.* files to BlePeripheral.* + +commit 7eea01f3e26eda828208ee64068dde040b253e88 +Author: Sandeep Mistry +Date: Fri Nov 27 13:22:31 2015 -0500 + + Add CurieBle.h for main lib header + +commit f6c59c14f95772fa3d6de4b8dfdc697cf8867b0d +Author: Sandeep Mistry +Date: Fri Dec 4 11:53:02 2015 -0500 + + Change CurieImu and BMI160 to KEYWORD2 + +commit 3642f44a88069e89cd644faabae8c72da78a2bfd +Author: hbisby +Date: Fri Dec 4 17:40:59 2015 +0100 + + added defines and classes to keywords.txt + +commit d8618b23ede8b769ecfe8cafee0695c2eea60956 +Author: hbisby +Date: Fri Dec 4 17:02:10 2015 +0100 + + adding keywords.txt for CurieImu library + +commit 262fd1e2940498c00d9871a455de21be1c73785f +Author: tigoe +Date: Thu Dec 3 23:33:37 2015 -0500 + + Style changes to IMU examples + + Note: IMU Library API still needs to be standardized. + +commit 39881ec83254758186e58c8e291b05ff09f6151e +Author: Dino Tinitigan +Date: Thu Dec 3 13:16:44 2015 -0800 + + ATLEDGE-393 Update SD library + + Rebase library from version 1.05 to 1.06 + Fixes issue with passing String class object to SD.exists() + +commit bca4512908095a1c6f04a1c975849080861ff609 +Author: Dino Tinitigan +Date: Wed Dec 2 11:01:11 2015 -0800 + + ATLEDGE-423 Export compiled binary + + Make changes to platform.txt to allow exporting compiled binary as a + .hex + +commit 3c833b2536b4616b38ef342289bb5efb440b6769 +Author: Dino Tinitigan +Date: Tue Dec 1 11:52:57 2015 -0800 + + ATLEDGE-442 Fix some SoftwareSerial Rx limitations + + Fixed some limitations for Rx on SOC_GPIO pins. + Rx should now be functional on all pins except pin 13. + +commit 0b100b2d006add6692a76d1658ca04b317b640a6 +Author: Dino Tinitigan +Date: Wed Nov 25 21:24:00 2015 -0800 + + Atledge-184 Minor improvements to SoftwareSerial library + + Minor improvements to SoftwareSerial library + +commit 7150436d89ca73fa7af68cd81c312203a7da4d4a +Author: Dino Tinitigan +Date: Wed Nov 25 19:14:24 2015 -0800 + + CurieRTC - SetTime example not setting RTC + + RTC not being set because the wrong call was being made should call + RTC.set(t) to set the hardware RTC instead of setTime(t). + +commit 25acb322cad8505f9564ca435b0e113bf5b5d9df +Author: Calvin Park +Date: Wed Nov 25 16:28:00 2015 -0800 + + Fixed typo in old IDE version error message + +commit fc55eedd7ce7fafbc5b0dd35c61ed12f22c0f26c +Author: Krzysztof Sywula +Date: Tue Nov 24 17:03:18 2015 -0800 + + ATLEDGE-455 verbose/quiet selection for upload + +commit 4984085e655687db615b3a9675ee374fba8d6cad +Author: Martino Facchin +Date: Wed Nov 25 16:37:28 2015 +0100 + + Add error message if using IDE 1.6.6 + +commit 69a88721160920cbd77b26836fc97a0549f87db4 +Author: Martino Facchin +Date: Wed Nov 18 10:59:59 2015 +0100 + + Fix recipe.ar.pattern to avoid warning at startup + +commit a22ea571d1863e3869ec5e9f29936d00822b8dcb +Author: Dino Tinitigan +Date: Tue Nov 24 14:01:14 2015 -0800 + + ATLEDGE-444 Fix Library warnings for Arduino IDE 1.6.6 + + Some libraries were missing the "category" field in library.properties + Minor fixes in some libraries for 1.6.6 compatibility + +commit 5c29db6c5b00b32d7f4d2379c3e89d9fdcc076d6 +Author: Dino Tinitigan +Date: Tue Nov 24 10:30:05 2015 -0800 + + ATLEDGE-396 Fix behavior of analogWrite() on non PWM pins + + For all non PWM pins, the behavior of calling analogWrite() should be + logic 0 if the value is 127 or less. For values of 128 or above, the pin + should go to logic 1. + +commit 7afbd91dd8c0aa2b39fbe696c0dcb506e28fddd6 +Author: Dino Tinitigan +Date: Mon Nov 23 18:09:53 2015 -0800 + + ATLEDGE-416 Fix some compile issues with Time.h + + Fix some issues with compiling Time.h by updating to version 1.50 of + Time library + Also removed some examples that will not compile for Arduino101 + +commit c7b04f7d7915407f5194f42abf3dcfc691cd23d8 +Author: Dino Tinitigan +Date: Mon Nov 23 16:07:16 2015 -0800 + + ATLEDGE-384 Remove Esplora specific examples + + Remove Esplora specific examples + +commit 2beea3e778b05e985ac7daacce86fb94847c054e +Author: Brian Baltz +Date: Fri Nov 6 10:33:12 2015 -0800 + + Resolving KW issue with SoftwareSerial destructor + + Signed-off-by: Brian Baltz + +commit b30a275c7221b614071767772e72b98ce93b7ed2 +Merge: 78afaae 3759bcd +Author: Calvin Park +Date: Thu Nov 5 11:05:04 2015 -0800 + + Merge pull request #62 from kevin306/testing + + Klocwork issues + +commit 3759bcd7fc648ae686de973af3df50f92e2dff9f +Author: Kevin Moloney +Date: Thu Nov 5 17:39:14 2015 +0000 + + Fix Klocwork#2051,2060: Array may be outside index + + Change && to || + + Signed-off-by: Kevin Moloney + +commit 5baa3fc6386781a784148fdf2b3fc65476416f05 +Merge: afcc4ef 344f90d +Author: Kevin Moloney +Date: Thu Nov 5 12:45:34 2015 +0000 + + Merge branch 'testing' of github.com:kevin306/corelibs-arduino101 into testing + + Conflicts: + libraries/CurieBle/src/BleCharacteristic.cpp + +commit afcc4efd16a7c7b9d915913d641a76f0b448eb3d +Author: Kevin Moloney +Date: Tue Nov 3 15:38:37 2015 +0000 + + Fix Klocwork#2051,2060: Array may be outside index + + * Added check to ensure _data_len is within index values. + + * Had to repeat the branch statement rather than move it because of memcpy. + + * Fixed bug introduced(c8ecd5f) in WString.h when "len" became protected. + + Signed-off-by: Kevin Moloney + +commit 344f90d892d33c47f121cc374643c5e28b51bbdd +Author: Kevin Moloney +Date: Tue Nov 3 15:38:37 2015 +0000 + + Fix Klocwork#2051,2060: Array may be outside index + + * Added check to ensure _data_len is within index values. + + * Had to repeat the branch statement rather than move it because of memcpy. + + Signed-off-by: Kevin Moloney + +commit 78afaae4c7f189dc44ec83d19745dc4bb4e7f6ac +Author: Dino Tinitigan +Date: Mon Nov 2 10:48:45 2015 -0800 + + ATLEDGE-229 CurieRTC and Time Library + + Initial implementation of the CurieRTC library for the built-in RTC + Includes the Time library plus 3 new examples for the Arduino101 + +commit cacac37db51b7212b92f3e90ff944006771860c4 +Author: Bogdan Pricop +Date: Thu Oct 29 22:28:12 2015 +0000 + + Fix Klocwork#137: Non-void function does not return value + + Change getTimeStampClks() implementation from assembly to C. + + Signed-off-by: Bogdan Pricop + +commit 8fa6bbaa920005ea42f738f305bcd4852c822a1b +Author: Dino Tinitigan +Date: Wed Oct 28 16:28:50 2015 -0700 + + ATLEDGE-184 SoftwareSerial Library + + Port of SoftwareSerial Library for Arduino101 + Tx works up to 384000 bps + Rx works up to 57600 bps + +commit 2ae262a2a87d0d85032da3d975e7f1385a02ea75 +Author: Kevin Moloney +Date: Thu Oct 29 14:41:28 2015 +0000 + + Fix Klocwork#2051,2060: Array may be outside index + + * Added check to ensure _data_len is within index values. + + * Had to repeat the branch statement rather than move it because of memcpy. + + Signed-off-by: Kevin Moloney + +commit 739c4007ba9c4b7a77d57628eb9ea31ab204d880 +Author: Kevin Moloney +Date: Wed Oct 28 16:47:44 2015 +0000 + + Fix Klocwork#543,544: Buffer overflow/null pointer + + * Added a check to ensure array does not access index value of -1. + + * Added a check for this null pointer. + + Signed-off-by: Kevin Moloney + +commit dd4845e5d4f7dca461587d8bc0cc998598b060bc +Author: Kevin Moloney +Date: Wed Oct 28 16:44:45 2015 +0000 + + Fix Klocwork#2081,2082: Uninitialized variable. + + * using memset to initialize struct to zero. + + Signed-off-by: Kevin Moloney + +commit 79744c4255d4c33d21b79bbc6f99922476ad3adf +Author: Kevin Moloney +Date: Wed Oct 28 16:40:38 2015 +0000 + + Fix Klocwork#79,80,81,82: Uninitialized Variable. + + * using memset to initialize struct to zero. + + Signed-off-by: Kevin Moloney + +commit 79008fa1132cd1c9790c50a3f96232f54774a1dd +Author: Kevin Moloney +Date: Wed Oct 28 16:38:07 2015 +0000 + + Fix Klocwork#671: Infinite loop. + + * Added comment to verify requirement for infinite loop. + + Signed-off-by: Kevin Moloney + +commit cfe0a5df34a7f8b94724a9b99f02b5e62e42854b +Author: Kevin Moloney +Date: Wed Oct 28 16:33:00 2015 +0000 + + Fix Klocwork#105: Pointer returned may be null. + + * Added a check for this null pointer. + + Signed-off-by: Kevin Moloney + +commit 5e71e700d4d1b66f9e68fc725653db9859660255 +Author: Kevin Moloney +Date: Wed Oct 28 16:30:11 2015 +0000 + + Fix Klocwork#100: Infinite loop. + + * Added comment to verify requirement for infinite loop. + + Signed-off-by: Kevin Moloney + +commit db2995a78d772adb072ce743e77eece69a01d0d8 +Author: Kevin Moloney +Date: Wed Oct 28 16:28:23 2015 +0000 + + Fix Klocwork#91: Infinite loop + + * Added comment to verify requirement for infinite loop. + + Signed-off-by: Kevin Moloney + +commit 62395e48e858c433a88c20523481382e90be21a3 +Author: Kevin Moloney +Date: Wed Oct 28 16:25:14 2015 +0000 + + Fix Klocwork#1093: Infinite loop. + + * Added comment to verify requirement for infinite loop. + + Signed-off-by: Kevin Moloney + +commit b3afd60a73d2fa8ead92ffa8e66a264e64bafd12 +Author: Bogdan Pricop +Date: Wed Oct 28 14:19:45 2015 +0000 + + Fix Klocwork#137: Non-void function does not return value + + Move getTimeStampClks() function to an *.S file + It is fully implemented in assembly and we take care of returning value by + filling in the proper value in the proper registers. + + Signed-off-by: Bogdan Pricop + +commit 0e094210f3d645b109adceb393a00b0b41acdf80 +Author: Martino Facchin +Date: Thu Oct 29 09:54:33 2015 +0100 + + fix BLE library String handling + + the correct way to get a String lenght is by calling String.length() + now len field is protected so a compilation error will be triggered + +commit 0ffb9749afb8dc53abd628e00ced6af07d0c97d1 +Merge: b17f321 6f35dfd +Author: Calvin Park +Date: Wed Oct 28 17:03:31 2015 -0700 + + Merge pull request #58 from facchinm/new_delete + + Fixes to pass validation with test suite + +commit 6f35dfd332c30b135c14d1f718f923299d7e6695 +Author: Martino Facchin +Date: Wed Oct 28 12:53:47 2015 +0100 + + stdlib_noniso: expand all functions up to base 36 + + needed to pass a very strict test + Porting of latest Due implementation + +commit f07a20aa02e5421b1fc6e9775fdeb2221c8bd2a7 +Author: Martino Facchin +Date: Wed Oct 28 12:50:35 2015 +0100 + + remove misleading SERIAL_PORT_USBVIRTUAL define + + the Serial via USB port is not a real virtual cdc port (it still emulates an Hardware Serial), remove to avoid sketches getting confused and expect CDC flow control functions + +commit c8ecd5f0af6c87fdd02382f0cab275d83da31f2c +Author: Martino Facchin +Date: Tue Oct 27 18:01:17 2015 +0100 + + Sync IPAddress and Wstring with SAM implementation + + Test suite failed on these functions + +commit d179695b0494b96cc1feb963905ee8fea9a7d2a5 +Author: Martino Facchin +Date: Tue Oct 27 15:41:24 2015 +0100 + + Add new/delete implementations + + This is needed to pass C++ validation in test suite + +commit b17f321e32464222ff433afa69af2ba3fe0149c5 +Author: Martino Facchin +Date: Tue Oct 27 15:40:30 2015 +0100 + + Specify architecture in library properties + + to avoid conflicts with standard libs + +commit cd4f66d4620a8aabb4bfb966fe6564a367a24f32 +Author: Dan O'Donovan +Date: Thu Oct 22 17:50:32 2015 +0100 + + libarc32drv_edu.a: Update of pre-compiled driver library. + + Signed-off-by: Dan O'Donovan + +commit 95174ce3dd682621e1f74ef0e04e818b0ff1dce3 +Author: Dan O'Donovan +Date: Thu Oct 22 17:47:07 2015 +0100 + + build: unused global variables in header file causing BLE build failure + + factory_data.h defines 2 global variables which aren't actually used anywhere. + However, they become global symbols in any C files that include this header + file. For most builds, this header file is included only once but, for CurieBle + library builds, it is included by another C file as well. This causes a linker + error due to duplicate symbols in the final image. + + The simple solution is to just remove these variables, because they aren't used. + + Signed-off-by: Dan O'Donovan + +commit 7052d8fe88034c92bd845b818bd1c028adea0444 +Merge: 6efbac6 c5e9152 +Author: Brian Baltz +Date: Wed Oct 14 16:20:32 2015 -0600 + + Merge pull request #52 from 01org/arduino101-rename + + ATLEDGE-364 Rename EDU to Arduino 101 + +commit c5e915295c995aebc15ecab2a2538b6c3ab20f23 +Author: Brian Baltz +Date: Wed Oct 14 15:14:56 2015 -0700 + + ATLEDGE-364 Rename arduino_101_x to arduino_101 + + Signed-off-by: Brian Baltz + +commit 570a904927ca18737613dbee8f4640382dba0746 +Author: Brian Baltz +Date: Wed Oct 14 14:56:03 2015 -0700 + + Update pre-compiled library + + Signed-off-by: Brian Baltz + +commit d342223adc0676d5c39a0d89f9ab94d0cea5e56e +Author: Brian Baltz +Date: Wed Oct 14 14:55:55 2015 -0700 + + ATLEDGE-364 Remove CONFIG_BOARD_ATLASPEAK_EMU specific code + + Signed-off-by: Brian Baltz + +commit 6efbac63436af6e286aa4b5e09b30bbe4cebeb80 +Author: Manoel Ramon +Date: Tue Oct 13 15:49:41 2015 -0700 + + ATLEDGE-316 - isAlphaNumeric returns incorrect bool for '@' + +commit 31e75615c26a6d19c168fc16e6b44d4e9504c45b +Author: Kevin Moloney +Date: Wed Oct 14 12:11:13 2015 +0100 + + Missing examples folder in Wire library + + - Slave examples not relevant because AtlasPeak does not support + slave-mode I2C. + + - Examples included digital_potentiometer, master_reader, + master_writer, SFRRanger_reader. + + Signed-off-by: Kevin Moloney + +commit 4c04540989a546fb6b668f35cee49a272bed9019 +Author: Brian Baltz +Date: Tue Oct 13 16:01:50 2015 -0700 + + ATLEDGE-364 Rename EDU library folders + + Signed-off-by: Brian Baltz + +commit d7617aba523a269a0d8278ba758bbe706fdd74f3 +Author: Brian Baltz +Date: Tue Oct 13 15:56:24 2015 -0700 + + ATLEDGE-364 Rename EDU to Arduino 101 + + Signed-off-by: Brian Baltz + +commit 7436867a37228a6bdeecf87c80489187a99f01ed +Author: Manoel Ramon +Date: Wed Oct 7 22:10:55 2015 -0700 + + ATLEDGE-358 - BLE Broadcast name must read the product name in the OTP + +commit 94ea92298feee063539ed2ca791cdef826d71333 +Author: Kevin Moloney +Date: Tue Oct 13 14:42:51 2015 +0100 + + Updated libarc32 + + Signed-off-by: Kevin Moloney + +commit 5a1c28cd384e01c50068aca8c7b379b05bb50571 +Author: Kevin Moloney +Date: Tue Oct 13 14:36:32 2015 +0100 + + ATLEDGE 201 Pointer returned may be null + + Added a check for this null pointer. + + Signed-off-by: Kevin Moloney + +commit 45e6046f09e2c465c69456869cee6992792e4f55 +Author: Kevin Moloney +Date: Tue Oct 13 13:04:04 2015 +0100 + + Files no longer in use, can be removed. + +commit 15dbf74d82907eb0af47ad9c5ef682fa298b11f3 +Merge: 685eed6 aa8e01a +Author: Calvin Park +Date: Mon Oct 12 11:43:12 2015 -0700 + + Merge pull request #48 from Dan-Emutex/adafruit_libraries + + Adafruit libraries + +commit 685eed663cedc37c91855048c216872a479605ad +Author: Dan O'Donovan +Date: Fri Oct 9 16:49:22 2015 +0100 + + imu: revert commit b62bbfc89b + + This commit essentially reverts the changes made in b62bbfc89b. + That is, it removes some Accelerometer data sampling rates which + we don't support in the mode that we are using the Accelerometer + + Signed-off-by: Dan O'Donovan + +commit aa8e01a64fe31172758ee0b1c66b4a3ce521730b +Author: Dan O'Donovan +Date: Fri Oct 2 17:59:40 2015 +0100 + + dotstar: initial port to AtlasEdge + + Signed-off-by: Dan O'Donovan + +commit 4fee1cca6bac948cb010cadb6b9fba33d8fbc2c6 +Author: Dan O'Donovan +Date: Fri Oct 2 17:57:02 2015 +0100 + + dotstar: add DotStar library v1.0.1 from Adafruit + + - Adafruit_Dotstar Github repository: https://github.com/adafruit/Adafruit_DotStar + Tag: v1.0.1 + + Signed-off-by: Dan O'Donovan + +commit 6e2de131cbfd585cf027f41353a71707185a8bbd +Author: Dan O'Donovan +Date: Fri Oct 2 17:52:45 2015 +0100 + + motorshield: initial port to AtlasEdge + + Signed-off-by: Dan O'Donovan + +commit e671f3f6c3b134d6b326c671de255c2f47d83969 +Author: Dan O'Donovan +Date: Fri Oct 2 17:07:56 2015 +0100 + + motorshield: add Motor Shield V2 library v1.0.1 from Adafruit + + - Adafruit_Motor_Shield_V2_Library Github repository: https://github.com/adafruit/Adafruit_Motor_Shield_V2_Library + Tag: v1.0.1 + + Signed-off-by: Dan O'Donovan + +commit 146b0bf2912cf09735f2701564261bb06d2aa6b3 +Author: Dan O'Donovan +Date: Mon Sep 28 17:00:30 2015 +0100 + + neopixels: initial port to AtlasEdge + + Signed-off-by: Dan O'Donovan + +commit ca4d7f2530f4077d6d1108d450c32afaa9f7862a +Author: Dan O'Donovan +Date: Mon Sep 28 14:18:41 2015 +0100 + + neopixel: add Neopixel library v1.0.3 from Adafruit + + - Adafruit_Neopixel Github repository: https://github.com/adafruit/Adafruit_NeoPixel + Tag: v1.0.3 + + Signed-off-by: Dan O'Donovan + +commit 7d930a50dbfb0890b4bb0915fe34c5dc2bdd6d79 +Author: Dan O'Donovan +Date: Fri Oct 9 13:21:22 2015 +0100 + + imu: fixed and simplified (set|get)ZeroMotionDetectionDuration methods + + Validation feedback revealed logic errors in + setZeroMotionDetectionDuration and a lack of clarity on how it should + be used. Addressed by providing an explicit set of valid values for + the user to pass to the method, which can in turn be written directly + to the relevant register + + Signed-off-by: Dan O'Donovan + +commit 7134c41e2940db726cfa293f19e9207cc9b643a2 +Author: Dan O'Donovan +Date: Mon Oct 5 10:17:29 2015 +0100 + + imu: improve interrupt-safety of CurieImu API methods + + Ensure that SPI transfers cannot be interrupted, and + avoid use of global buffer to improve interrupt safety + + Signed-off-by: Dan O'Donovan + +commit 252bd5308f34dd442d9190d36bfbe7f7b1379402 +Author: Calvin Sangbin Park +Date: Mon Oct 5 15:26:15 2015 -0700 + + Updated libarc32 + +commit b1cbef53bbd79333c0df25816078a7eab4b441bc +Author: Dan O'Donovan +Date: Fri Oct 2 15:26:25 2015 +0100 + + imu: minor tidy-up of documentation for API enumeration values + + Signed-off-by: Dan O'Donovan + +commit b62bbfc89bc35b3a0a04de74f7e4898371922a83 +Author: Dan O'Donovan +Date: Fri Oct 2 14:31:57 2015 +0100 + + imu: add missing accelerometer sample rate options + + - The following sample rates were previously omitted: + - 25/32 Hz + - 25/16 Hz + - 25/8 Hz + - 25/4 Hz + + Signed-off-by: Dan O'Donovan + +commit 426367514593a075b3dc5d373dca10e82cf24112 +Author: Dan O'Donovan +Date: Fri Oct 2 14:28:22 2015 +0100 + + imu: remove invalid 3200Hz accelerometer sample rate option + + The maximum sample rate for the accelerometer is 1600Hz + + Signed-off-by: Dan O'Donovan + +commit c8c4d98b50644141b77868996e226fba167944c9 +Author: Dan O'Donovan +Date: Fri Oct 2 14:06:00 2015 +0100 + + imu: fixes/improvements needed for TapDetect example + + - Threshold was set incorrectly, leading to over-sensitive detection + - Default accelerometer range of 2g prevented detection of strong taps + - Added comments to explain the code in more detail + + Signed-off-by: Dan O'Donovan + +commit 23bd20fe1081edde07fca0a85718f5a52987abfc +Author: Dan O'Donovan +Date: Fri Oct 2 13:49:24 2015 +0100 + + imu: compile error in RawImuDataSerial.ino when CALIBRATE_ACCELGRYO_OFFSETS defined + + Signed-off-by: Dan O'Donovan + +commit 67629a6de0e9163f1a7631fa2ae28c9f5b9e4765 +Author: Dan O'Donovan +Date: Fri Oct 2 13:34:48 2015 +0100 + + imu: remove delay between bi-dir SPI transfers causing data loss + + A wait for TX to complete, followed then by RX, was causing data + loss due to the fact that it was waiting for the busy status flag + to clear after TX, but this doesn't clear until after RX is also + complete. The result was data loss due to RX FIFO overflow. + Fixed by disabling the wait between TX and RX steps. + + This affected functions requesting more than 8 bytes from the + BMI160, such as getMotion6() and getFIFOBytes() methods. + + Signed-off-by: Dan O'Donovan + +commit 599e33bf4bfd604f68e55252511075e078c673de +Author: Dan O'Donovan +Date: Thu Oct 1 12:55:05 2015 +0100 + + imu: fixed error in reg_write_bits + + - reg_write_bits was erroneously clearing all other bits in + a register outside of the bits targeted for the write + operation. + + Signed-off-by: Dan O'Donovan + +commit 17b8d04c60919d25ccac54d7cf2901c1d0f0e285 +Author: Dan O'Donovan +Date: Mon Sep 28 14:08:28 2015 +0100 + + imu: minor clean-ups + + - removed some dead code + - added/corrected some code comments + + Signed-off-by: Dan O'Donovan + +commit e4904009052717894720323d8b037307b24b9204 +Author: Dan O'Donovan +Date: Fri Sep 25 18:10:25 2015 +0100 + + imu: adding support for tap detection by BMI160 accelerometer + + Signed-off-by: Dan O'Donovan + +commit a5a12fc002430d002d9434f47582577da5c36631 +Author: Dan O'Donovan +Date: Fri Sep 25 15:21:05 2015 +0100 + + imu: added support for Step Detection/Counting in CurieImu library + + Signed-off-by: Dan O'Donovan + +commit b759ec35f46ef38d77fcd90ad1d91a5840607aaa +Author: Dan O'Donovan +Date: Fri Sep 25 01:36:11 2015 +0100 + + imu: fixes for issues found while testing shock detection + + - Created new SPI driver using polling instead of interrupts. + This allows it to be used within an ISR so that + we can check the source of the CurieImu interrupts. + - Fixed bit manipulation logic for partial register read/writes + - Added a new example sketch to demonstrate Shock Detection function + - Fixed IMU power-up logic + + Signed-off-by: Dan O'Donovan + +commit 982a470e6909bd444a399b49ced4749d9da72754 +Author: Dan O'Donovan +Date: Thu Sep 17 15:59:50 2015 +0100 + + Initial commit for new CurieImu Gyro/Accel library + + Signed-off-by: Dan O'Donovan + +commit e25bcde0d0b7f9fc635e376aa649884a369d6c45 +Merge: 6f196e1 2b11283 +Author: Brian Baltz +Date: Fri Oct 2 12:32:31 2015 -0600 + + Merge pull request #46 from 01org/cdc-acm + + CDC-ACM: move allocation of shared buffers to LMT - Has pair commit in Thunderdome + +commit 2b112833285765ef1e9436d5a82f1eeb65f53df8 +Author: Calvin Sangbin Park +Date: Fri Oct 2 10:32:51 2015 -0700 + + Updated libarc + +commit 05d4ecade9f44e2d86a43fde1030b2b0167236ed +Author: Bogdan Pricop +Date: Fri Oct 2 16:02:09 2015 +0100 + + CDC-ACM: avoid race condition. + + * In case bool() is called in a very tight while loop, give LMT space + to set the host_open shared variable. + + Signed-off-by: Bogdan Pricop + +commit b8d9b11d8d1c61d704f967d5d730b244c72d918f +Author: Bogdan Pricop +Date: Fri Oct 2 12:19:29 2015 +0100 + + CDC-ACM: change end and init to avoid race condition. + + Signed-off-by: Bogdan Pricop + +commit 5174c9c7e1634eef20c84b436c58a7b7cbbddbb3 +Author: Bogdan Pricop +Date: Fri Oct 2 10:29:43 2015 +0100 + + libarc32drv_edu.a: Update of pre-compiled driver library. + + Signed-off-by: Bogdan Pricop + +commit a6473d1fc4c8e4207f974eac273b0f5781341bc7 +Author: Dan O'Donovan +Date: Fri Oct 2 00:11:03 2015 +0100 + + CDC-ACM: move allocation of cdc-acm shared buffers to LMT + + LMT shall now be responsible for allocating shared data buffers + used to exchange CDC-ACM data between ARC and LMT. + + Defined a revised set of structures to allow the pointers to + be passed from LMT to ARC during initialisation. + + NOTE THAT THIS DEPENDS ON A CORRESPONDING CHANGE IN THUNDERDOME + FIRMWARE + + Signed-off-by: Dan O'Donovan + +commit 6f196e1fcdb422ca29229af27ff984eb113e3bd0 +Author: Calvin Park +Date: Thu Oct 1 22:11:13 2015 -0700 + + Updated libarc32 + +commit 4f1a71a77685e4b98a2c0345773bb5c92cbc98f4 +Author: Bogdan Pricop +Date: Fri Sep 11 08:59:45 2015 +0100 + + CDC-ACM fix flush() API + + Signed-off-by: Bogdan Pricop + +commit 0f07ea0e5dcf0039761e256a7efe7f327270124e +Author: Dan O'Donovan +Date: Thu Sep 10 13:43:13 2015 +0100 + + CDC-ACM - Fix slow write() performance and availableForWrite() logic + + - Removed delay in write() function which caused slow TX performance + - Removed timeout logic in write() function; moved to LMT side + - Fixed logic in availableForWrite() - ring-buffer capacity is at most + 1 less than actual size to avoid head index passing tail index + + Signed-off-by: Dan O'Donovan + +commit 33f4380d60a5f3fd110965f15565f3b83b57d6c9 +Author: Bogdan Pricop +Date: Tue Sep 8 10:57:00 2015 +0100 + + CDC-ACM - address review comments. + + Signed-off-by: Bogdan Pricop + +commit ddb0003c675d648f1b49ea8956e5b0f3b49d4775 +Author: Bogdan Pricop +Date: Mon Sep 7 15:36:58 2015 +0100 + + CDC-ACM using shared memory buffers + + Signed-off-by: Bogdan Pricop + +commit 6d0fc98091cedcc6c2e947878bdf7f5201592abe +Author: Dan O'Donovan +Date: Sat Sep 19 09:50:03 2015 +0100 + + ble: update libarc32drv_edu.a + + Signed-off-by: Dan O'Donovan + +commit 9314b22ff29e70c400c61d8f39c6486d8faa03ad +Author: Dan O'Donovan +Date: Sat Sep 19 09:41:46 2015 +0100 + + ble: address code review comments from Manoel Ramon + + - Added checks in example sketches for API return values + - Applying Public MAC address if present in factory data + - Applying default device name if user doesn't provide one + - Only issuing notifications/indications if enabled by peer + - Re-sync'd with latest BLE code from Thunderdome + - Added basic DTM support for use in sketch (for internal testing) + - Other minor updates, corrections and clarifications + + Signed-off-by: Dan O'Donovan + +commit a709e252eb60dec7eb7f923797814e72ffda5dcc +Author: Brian Baltz +Date: Fri Sep 18 09:47:10 2015 -0600 + + ATLEDGE-334 Update version header location in linker script + + Need to update linker script to match recent firmware change: + + JIRA: FIRE-2160 + + 1024 bytes were used to store the version header, which is only + 48 bytes big. So we wasted 976 bytes. By moving it after the vector table, + we can free this 1024 bytes space. + + Signed-off-by: Brian Baltz + +commit c33d42adca3d1fd4295e7df8f278c41089da26f6 +Author: Brian Baltz +Date: Thu Sep 17 12:58:44 2015 -0700 + + ATLEDGE-333 CLUploader script path is hard-coded + + Signed-off-by: Brian Baltz + +commit 28304b5b8ba763fe00d23cd074f23b3b8e6b97e0 +Author: Dan O'Donovan +Date: Wed Sep 16 17:51:36 2015 +0100 + + uart: fixing baud divisor calculation to add support for 230400 bps + + Added code to configure the UART fractional divisor to improve the + UART timing at higher speeds such as 230400, 460800 and 921600 bps. + + Signed-off-by: Dan O'Donovan + +commit b96562dbb3c63d5cc894f128d61de4f1bc6de3d5 +Author: Dan O'Donovan +Date: Wed Sep 16 14:45:05 2015 +0100 + + wire: adding support for I2C RESTART and some minor clean-ups + + - Added support for sending repeated-START conditions on the I2C bus + By adding an option to not send a STOP condition at the end of a + transfer, it is possible for the I2C master to hold the bus and + send additional transfers. If switching from writing to reading, + or vice-versa, the I2C master controller will automatically send + a repeated-START (RESTART) in between. For consecutive writes or + consecutive reads to the same slave, a repeated-START is not needed. + + - Cleaned up some redundant code and removed some unsupported methods + in the Wire library implementation for AtlasEdge, particularly those + related to I2C Slave-mode. AtlasEdge supports I2C Master-mode only. + + Signed-off-by: Dan O'Donovan + +commit 026afb2d00b89999b07bd7c5c6bc54cd29bbb310 +Author: Dino Tinitigan +Date: Mon Sep 14 16:11:37 2015 -0700 + + ATLEDGE-231 TFT Library + + Add missing wiring_private.h file + +commit 6b5577e058a46fcfca5ba91ccd3c9824acd941b6 +Author: Dan O'Donovan +Date: Sat Sep 12 11:26:50 2015 +0100 + + ble: adding new CurieBle Arduino library for AtlasEdge + + Added new BLE library and examples, providing access to + basic GAP/GATTS features of Intel Curie module + + Includes a port of the BLE Core Service CFW API and + UART-IPC drivers from Thunderdome code base + + Also includes a shift of CFW master role from LMT to ARC. + Requires matching commit 4d056720 or newer in thunderdome + atlaspeak_atlasedge project source tree to work correctly + + Known issues: + - A fix in Nordic BLE firmware is required to support + adding Presentation Format descriptors to characteristics. + Until that fix is available, the addPresentationMethod() + of the BleCharacteristic class should not be used. + - Due to issues with the current CDC-ACM implementation on + AtlasEdge platforms, use of Serial should be replaced with + Serial1 in the CurieBle example sketches, until CDC-ACM + issues are resolved. + + Not currently supported: + - Direct Test Mode (DTM) + - Bluetooth bonding (pairing) + - BLE Central Device role and GATT client functionality + + Signed-off-by: Dan O'Donovan + +commit 4cf249987b5da85a90025d174021824e5ce09cc6 +Author: Bogdan Pricop +Date: Mon Aug 17 22:49:26 2015 +0100 + + Port OneWire to Atlas Edge board + + * Implement platform specific macros for Intel AtlasPeak SoC. + + Signed-off-by: Bogdan Pricop + +commit 42da7aadcf2439bb4adaa5a1e112381786639486 +Author: Bogdan Pricop +Date: Fri Jul 31 16:23:55 2015 +0100 + + Add Paul Stoffregen's OneWire library version 2.3 + + * 1-Wire Github repository: https://github.com/PaulStoffregen/OneWire + tag: 2.3 + + Signed-off-by: Bogdan Pricop + +commit d439d3be7522a5d4c2adf552039b089dea0d495f +Author: Dino Tinitigan +Date: Thu Sep 10 11:13:58 2015 -0700 + + ATLEDGE-231 TFT Library + + Add AtlasEdge support to TFT library + +commit f34bda4517f1cc199d29e3fe51c220cae5b2cf4c +Author: Dan O'Donovan +Date: Wed Sep 2 12:53:49 2015 +0100 + + Updated license headers in Thunderdome-derived source files + + New license headers and other minor changes have been imported + from the Thunderdome code base. Note that no functionality has + been added/changed/removed in this commit. + + Signed-off-by: Dan O'Donovan + +commit 647e20823be90638e707fbcb66c8be4e1f9ea351 +Author: Dino Tinitigan +Date: Tue Aug 25 10:24:03 2015 -0700 + + ATLEDGE-279 F_CPU not defined + + Added F_CPU to boards.txt and platform.txt. + Fixed format of boards.txt + +commit d3bbf49643a9fdd2a19426f5818cd602e1e99e8d +Author: Dino Tinitigan +Date: Mon Aug 24 16:34:21 2015 -0700 + + ATLEDGE-228 SD Library + + Add AtlasEdge support to SD library + +commit 83a4c0213a24c32e4f2924fbbcad5dd5afca9ec3 +Author: Dino Tinitigan +Date: Fri Aug 21 13:05:14 2015 -0700 + + ATLEDGE-272 SPI broken by new clock gating feature + + Enable clock gate before configuring SPI + +commit c29233c5826e85f49173a77757400678471bd49b +Author: David Hunt +Date: Wed Aug 12 12:40:28 2015 +0100 + + Cleaned up buffer handling in CDCSerial class. + + Signed-off-by: David Hunt + +commit b9d35bdac7c27a1c407fe2e0d4f97236a980f312 +Author: David Hunt +Date: Thu Aug 6 14:28:16 2015 +0100 + + libarc32drv_edu.a: Update of pre-compiled driver library. + + Signed-off-by: David Hunt + +commit 7fa713d2ef47140a82b4b0f50ab31ec43206fada +Author: David Hunt +Date: Tue Aug 11 13:53:48 2015 +0100 + + Address review commments on cdc-acm serial + + Signed-off-by: David Hunt + +commit 511a8d76f20698813cafa1b0f7130e40f37cdaea +Author: David Hunt +Date: Tue Aug 4 12:23:24 2015 +0100 + + Changes out of code reviews. + + Signed-off-by: David Hunt + +commit d85aa9643ac15389004d05217e67d438d689916d +Author: David Hunt +Date: Wed Jul 29 14:45:44 2015 +0100 + + Added CDC-ACM serial functionality as Serial. + + Pin header pins 0 and 1 have moved to Serial1 + + Signed-off-by: David Hunt + +commit 347ded518d9731dd93460b569168f2631ad604c1 +Author: Manoel Ramon +Date: Thu Aug 13 16:28:39 2015 -0700 + + ATLEDGE-44 Creation of platform ID and platform name for Atlas Edge + +commit a9c0430563473d556a766ac7df953969363f3bc8 +Author: Krzysztof Sywula +Date: Tue Aug 11 18:33:04 2015 -0700 + + vid/pid change + + JIRA: ATLEDGE-181 + + Signed-off-by: Krzysztof Sywula + +commit 7d55eb88f2c659ca0dc3bc83ef607fbeb439bbe2 +Author: Dino Tinitigan +Date: Tue Aug 11 10:48:22 2015 -0700 + + ATLEDGE-141 Add ATN pin as a regular gpio + + Added ATN pin as a regular gpio as pin 20 + +commit e487817583d12c6ef1203ef60c14cd4882509b32 +Author: Martino Facchin +Date: Tue Aug 11 13:56:19 2015 +0200 + + import stdlib noniso functions + + try not to reinvent the wheel + solves print(*, BIN) returning "unsupported base" + +commit a03d93d5634335a96549c1c41923e03424f5d488 +Author: Martino Facchin +Date: Tue Aug 11 12:34:10 2015 +0200 + + add support for String operation with uint64_t + + fixes examples StringConstructors, StringAdditionOperator, StringAppendOperator + + only needed because millis() returns a uint64_t + +commit 2dbdd016cc9cf47649c55141b56bc7c2e64315ad +Author: Dino Tinitigan +Date: Thu Aug 6 15:38:23 2015 -0700 + + ATLEDGE-30 Port Ethernet Library + + Port of the Ethernet Library for Atlasedge. + Added missing files from cores directory needed by Ethernet, WiFI, and + other similar libraries. + Works for Arduino 1.6.5 and above + +commit 293b27908134ab79d689020dece0bf6ce8cc02a7 +Author: Bogdan Pricop +Date: Thu Aug 6 15:04:43 2015 +0100 + + Fix the build. + + Signed-off-by: Bogdan Pricop + +commit bc74b7df42ec1a9413dd044b814363c7cc51bdeb +Author: Bogdan Pricop +Date: Thu Aug 6 13:48:37 2015 +0100 + + Address code review comments + + * Correct spelling error evry => every + * Remove magic number 0xFFFFFFFF and define it as FREE_RUN_TIMER + * Remove 2 and 10 magic numbers from Print class; use BIN and DEC instead + * Add base checking inside printNumber() and printLongLong() methods + * Remove some unnecessary white spaces. + + Signed-off-by: Bogdan Pricop + +commit 3cc79807014c1f386e12017c4063ccc62db9e15c +Author: Kevin Moloney +Date: Tue Aug 4 12:21:44 2015 +0100 + + ATLEDGE-185 - String object is missing functions + + Also included Bug #2289 overloaded operators missing + Signed-off-by: Kevin Moloney + +commit e3f227ff6fe7c5530a2f99f2c42206dd08071020 +Author: Martino Facchin +Date: Mon Aug 10 15:05:26 2015 +0200 + + WStrings: add c_str function + + this fixes SD library compilation + + However, in the long term, WString, Print, IPAddress, Stream and WMath (every core file non hardware-related) should be aligned to AVR version to avoid incompatibilities with derived classes + +commit fd8b3e5e6899ed12ada55829c7557be0402dd123 +Author: Martino Facchin +Date: Mon Aug 10 14:53:57 2015 +0200 + + Add digitalPinTo* macros/functions + + these functions ensure compatibility with lots of existing libraries + +commit f3f2b0bc66b053b2cd59cee578906a2f7d83eabc +Author: Martino Facchin +Date: Tue Jul 21 18:09:38 2015 +0200 + + Improve Wire library + + still unreliable though + +commit a33835c16343c18ed75e51418ff02c72684a745b +Author: David Hunt +Date: Fri Jul 31 13:56:57 2015 +0100 + + ATLEDGE-177-char-Signed + + Char is now signed (fix of previous regression in merge) + + Signed-off-by: David Hunt + +commit 0aa51368ea4b1688aecbe2b2b0b8b1bf57058ecf +Author: David Hunt +Date: Fri Jul 31 12:50:14 2015 +0100 + + libarc32drv_edu.a: Update of pre-compiled driver library. + + Signed-off-by: David Hunt + +commit 31513f70d52408093dfbcaeeee4ec77e9c1c3dcb +Author: Bogdan Pricop +Date: Wed Jul 29 17:37:35 2015 +0100 + + Fix bug #2220: delay(150secs) only waits for approx 15secs + + Root cause: + While transforming milliseconds in clocks, the result of an uint32_t + multiplication overflows + Solution: + Cast one of the operands to uint64_t + + Signed-off-by: Bogdan Pricop + +commit b365f143e2b11346ee54a9c48f6c6b3b48e33e56 +Author: Kevin Moloney +Date: Wed Jul 29 11:14:02 2015 +0100 + + Bug#2262 String Object compile error + + Signed-off-by: Kevin Moloney + +commit da0eaa49fe58a431fdf03403355cf71ab671b9bd +Author: Kevin Moloney +Date: Wed Jul 29 14:06:08 2015 +0100 + + ATLEDGE-209 tone() bug when frequency = 0 + + Signed-off-by: Kevin Moloney + +commit bc7fbd8adbf3058f4dc3bae0c9a1568b3e98ba3f +Author: David Hunt +Date: Thu Jul 30 11:32:26 2015 +0100 + + Bug# 2265 - fixed parseInt(skipchar) Compilation error + + Signed-off-by: David Hunt + +commit 67e7f2e9843096b03110cefdc5e702d896bf5be6 +Author: David Hunt +Date: Wed Jul 29 10:07:37 2015 +0100 + + Added support for printing long long values, e.g. millis() + + Signed-off-by: David Hunt + +commit abe68dea6befe32fc4ce2951c5bb2e89c2e4bd45 +Author: Bogdan Pricop +Date: Wed Jul 29 12:14:56 2015 +0100 + + ARC init: enable FIRQ + + * Change interrupts priority from 1 to 0 (FIRQ) + * Modify linker script: add stack area for FIRQ + Define a stack memory section in SRAM and split it between run time + and FIRQ stacks. + * Replace existing generic ISR; it initialises the SP, decodes the IRQ + source and finally jumps and links to the specific ISR. + * Modify _IsrTableEntry structure: get rid of ISR parameter. + + Signed-off-by: Bogdan Pricop + +commit 47fab774f96532134ad74a8c16bcecf6c9fdefbf +Author: Bogdan Pricop +Date: Mon Jul 27 14:37:09 2015 +0100 + + ATLEDGE-169: Change interrupt routing mask + + Issue: The LMT receives an interrupt enabled by SS because the routing + mask is disabled for both cores (the interrupt is routed to both + ARC and LMT) and it masks it because it is a "spurious irq" from + its point of view. + + Fix: When the ARC enables an interrupt source, it configures the interrupt + router to route the interrupt only to ARC core. + + Signed-off-by: Bogdan Pricop + +commit ab97250c0df109b68f71983fe0046b2d16f839fc +Author: Kevin Moloney +Date: Tue Jul 28 11:58:41 2015 +0100 + + Bug #2184 Serial.begin() config not implemented + + Signed-off-by: Kevin Moloney + +commit e542703d8458e5769ab6d8aa38a9a565aaff83a3 +Author: Kevin Moloney +Date: Fri Jul 24 12:43:34 2015 +0100 + + ATLEDGE-177-char-Signed + + Char is now signed + Boolean modified to be a bool not an int + + Signed-off-by: Kevin Moloney + +commit afcd5f7932f62f7692121b9dcce8b67972a2fac6 +Author: Kevin Moloney +Date: Thu Jul 16 15:45:30 2015 +0100 + + Enable/disable_internal-pullup_with-digitalWrite + + Emulate Arduino pull-up control, by allowing a digitalWrite() on an INPUT pin to toggle the internal pull-up + Signed-off-by: Kevin Moloney + +commit 925ddb77900e4bb8ede5f59daf7656073c81bb2b +Author: Dino Tinitigan +Date: Wed Jul 29 15:01:15 2015 -0700 + + ATLEDGE-224 port avr/pgmspace.h + + many libraries fail to compile because they need avr/pgmspace.h. + Galileo/Edison had the same issue and the same code can be ported over. + +commit 6875a7ffed5d9dd038037148efccc0ef1fe2f2f6 +Author: Kevin Moloney +Date: Wed Jul 15 09:26:12 2015 +0100 + + Bug fix to allow three methods for Serial.Write() + + Signed-off-by: Kevin Moloney + +commit a50e097c7c7d217b4d0b9135a9d2e970f435edcf +Author: Bogdan Pricop +Date: Tue Jul 14 23:05:16 2015 +0100 + + Arduino core functions: pulseIn + + Change pulseIn implementation to be inline with the new time functions + which provide 64 bit timestamps. + + Signed-off-by: Bogdan Pricop + +commit 895d7b94b167cafbd6306ce79942c7f2bc811181 +Author: Dan O'Donovan +Date: Wed Jul 15 14:17:35 2015 +0100 + + Temporary fix is to compile with -O0 instead (optimisation disabled). + Further investigation required. + + Signed-off-by: Dan O'Donovan + +commit 9bf75da8684e5917d343795eb9f1156d28eb89b4 +Author: Bogdan Pricop +Date: Wed Jul 15 12:24:01 2015 +0100 + + libarc32drv_edu.a: Update of pre-compiled driver library. + + Signed-off-by: Bogdan Pricop + +commit 33342070baf9a8b5dadc26d9691499d30461be68 +Author: Bogdan Pricop +Date: Tue Jul 14 20:59:11 2015 +0100 + + Time related Arduino core functions: Re-write + + * Change Timer0 configuration according to Alexandre D'Alton suggestion: + use it as a free-run timer which delivers interrupts every 0xFFFFFFFF + clock ticks and its ISR increments a global variable; in this way a + 64-bit virtual RTC is created. The downside is the delay() function + cannot take advantage of ARc's power management features anymore. + + * Change delay() implementation to make use of 64-bit time-stamp. + + * Change delayMicroseconds to use Timer0 counter value and unsigned + arithmetic's features to handle the overflow case. + The credit goes to Dan O'Dononvan. + + * Add function to get an atomic 64-bit time-stamp. + + * Change millis() and micros() implementation to use the 64-bit time-stamp. + + Signed-off-by: Bogdan Pricop + +commit 7405797b110db61e838dc92141f7d8082b159c3a +Author: Dan O'Donovan +Date: Fri Jul 10 09:45:36 2015 +0100 + + Updates and fixes to Component Framework code for Arduino on ARC + + - Sync'd CFW code with Thunderdome code base from 01-July-2015 + - Fixed issue with mailbox interrupts halting CPU, caused by bug in scss_registers.h + - Added CFW test service client to facilitate CFW sanity check (requires LMT build running Test service) + + Signed-off-by: Dan O'Donovan + +commit cdc73f8e532be8ea3c5791fe575bc30381a6e0dc +Author: David Hunt +Date: Thu Jul 9 14:29:46 2015 +0100 + + Fixed redmine 2150 - inByte variable declaration issue + + Signed-off-by: David Hunt + +commit 4489870248153835baa05bcc6956f0f0f81ffb37 +Author: David Hunt +Date: Thu Jul 9 16:03:43 2015 +0100 + + Remove unneeded main.c from driver library. + + Signed-off-by: David Hunt + +commit ecbe76a8febfd31a8b0f1d3dd30fa6545bafafec +Author: David Hunt +Date: Thu Jul 9 14:01:45 2015 +0100 + + Fixed macro for enabling pullups, plus fixed pin# for IO8 + + Signed-off-by: David Hunt + +commit 3193ffc31ab65125c66337aadef85c6da3d22d2b +Author: Bogdan Pricop +Date: Fri Jul 10 15:52:33 2015 +0100 + + Update of pre-compiled driver library. + + Signed-off-by: Bogdan Pricop + +commit c968b172d1b0ca86936ae34793d701ac2fee778f +Author: David Hunt +Date: Wed Jul 8 17:31:48 2015 +0100 + + Reduced footprint of blink sketch from 65K to 25K by removing call to sprintf + + Signed-off-by: David Hunt + +commit 4fc372ef78d1929fc1ff12d1c1a806c1dc236bdd +Author: Kevin Moloney +Date: Wed Jul 8 15:31:45 2015 +0100 + + Remove hardware loop-back flag set in SPI.begin() + + This was added previously for internal testing, but was not fully removed prior to release + + Signed-off-by: Kevin Moloney + +commit f88353e642f8e88361a055fdecdf8fef3f41e337 +Author: David Hunt +Date: Wed Jul 8 13:54:26 2015 +0100 + + Fix for malloc(), round up to 4k Pages. + + Signed-off-by: David Hunt + +commit 13cd850d73127de56c31945a3e24f78d99ef7f2e +Author: David Hunt +Date: Tue Jul 7 16:19:38 2015 +0100 + + ATLEDGE-158 - Serial.end() not working + + Signed-off-by: David Hunt + +commit 3f3c4c207f1342ffd17e65e0025f4ab8a9e998dc +Author: Kevin Moloney +Date: Mon Jul 6 13:02:12 2015 +0100 + + Modify pin 19's pin description for EVT board + + Signed-off-by: Kevin Moloney + +commit d5bf0344ab8762b716e264eec680970b708f7f1b +Author: Kevin Moloney +Date: Mon Jul 6 12:50:20 2015 +0100 + + Modifify pin 19's pin discription for EVT board + + Signed-off-by: Kevin Moloney + +commit a1d94f84bfa3cb7fa164e79b14ba89227d24dea0 +Author: Bogdan Pricop +Date: Mon Jul 6 14:04:56 2015 +0100 + + ATLEDGE-142: noInterrupt() not working: change delay() behaviour. + + * Change delay() implementation NOT to enable interrupts before putting the + ARC core into sleep state. + * delay() still cannot be called from a "no interrupts" context, but it + doesn't overwrite the user's settings anymore. In this way, if delay() + is called in a no interrupts context, the ARC sleeps for ever. + + Signed-off-by: Bogdan Pricop + +commit de8fab5b63ea28e84055a33b5201d0d44bad0dab +Author: Kevin Moloney +Date: Mon Jul 6 11:46:10 2015 +0100 + + Include Knob example in Servo library + + Signed-off-by: Kevin Moloney + +commit ae93ed8d139f36825dcc80bb705d7066af7415c2 +Author: David Hunt +Date: Thu Jul 2 13:56:13 2015 +0100 + + Shifting out wrong bit index. Should be 7-i instead of 8-i + + Signed-off-by: David Hunt + +commit 57d3d3dae78e31623c5d60a770f90938df6c1a8d +Author: Kevin Moloney +Date: Thu Jul 2 10:02:38 2015 +0100 + + Implement Servo Library for Intel EDU + + Signed-off-by: Kevin Moloney + +commit 7ae6ecfc2fec406ea15f8af6d83fb31c3fe45b6e +Author: Dan O'Donovan +Date: Thu Jul 2 13:07:37 2015 +0100 + + SPI library implementation for Intel EDU + + The following functions/features have been implemented: + + * Standard SPI API: + - SPI.begin() + - SPI.setClockDivider() + - SPI.setDataMode() + - SPI.transfer() (single-byte and multi-byte versions) + - SPI.transfer16() + - SPI.end() + * SPI Transaction API + - SPI.beginTransaction() + - SPI.endTransaction() + - SPI.usingInterrupt() + - SPI.notUsingInterrupt() + * New Intel EDU specific API functions + - SPI.transfer24() + - SPI.transfer32() + + Known issues/limitations: + * LSBFIRST bit order not supported in hardware, requires (slower) software emulation + * SPI Transaction API has not been fully tested at time of writing + + Signed-off-by: Dan O'Donovan + +commit 1cdc4f0688be19e32396d50e731ff3cf16b7d8a6 +Author: Bogdan Pricop +Date: Tue Jun 30 15:27:49 2015 +0100 + + Update source code with WRS restrictive license headers + + * Delete arcv2_timer1.[c|h] driver - not used at this stage. + * arcv2_timer0 driver: update license header to WRS permissive license; + arcv2_timer0.h doesn't exist anymore in the latest Tiny Mountain source + code, but we use it, stick in the same WRS permissive license header. + * For linker.cmd and flsh.ld linker scripts: replace license header with + the WRS permissive one fomr the original linker.cmd files from + Tiny Mountain OS + * aux_regs.h - derived work from aux_reg.h from Tiny Mountain OS; + change license header to WRS permissive. + * remove license header as in the original files from thunderdome code + base: compiler.h, portable.h + + Signed-off-by: Bogdan Pricop + +commit c338e4b94124bb619896e8a8c80217e790717ca7 +Author: Kevin Moloney +Date: Wed Jun 17 11:02:39 2015 +0100 + + ADC Optimization (clock ratio) + + Signed-off-by: Kevin Moloney + +commit 98fdd4c83861e084c7819d08c3f90e8600675bc6 +Author: Kevin Moloney +Date: Tue Jun 30 14:22:47 2015 +0100 + + Arduino core functions: tone/noTone implementation.. + + * tone/noTone implemented using ARC's Timer1. + + Signed-off-by: Kevin Moloney + +commit 32ae6276ac86dd24961eb010297e2239b5ee8b80 +Author: Bogdan Pricop +Date: Fri Jun 26 15:43:49 2015 +0100 + + Arduino core functions: pulseIn(pin, value, timeout) + + Implementation of pulseIn() Arduino core function using GPIO interrupts + and micros() for time-stamping. + NOTES: + * The performance is not as good as on Arduino UNO. It should be + re-measured after following optimisation: + * Enable FIRQ (Fast Interrupts) on ARC. + * Optimise the GPIO interrupts + * Optimise the Timer0 interrupt + * Empirical measured accuracy is 10 microseconds. + + Signed-off-by: Bogdan Pricop + +commit cbc40d38c6cd1ef5e433e378c99cc95f19e31fc9 +Author: Dino Tinitigan +Date: Fri Jun 26 16:18:29 2015 -0700 + + Update sketchUploader version + + Updated sketchUploader version in platform.txt + +commit 875ec047d28c3059ca215e261bd28f780d0b48d5 +Author: David Hunt +Date: Fri Jun 26 13:59:13 2015 +0100 + + Change to see new VID/PID for ACM devices for baud change reset + + Must be used with firmware that presents 8087:0A9F as PID/VID + + Signed-off-by: David Hunt + +commit 11dfe699887f47f65276f880b5a4aaa01f282baa +Author: Bogdan Pricop +Date: Tue Jun 23 17:56:55 2015 +0100 + + ATLEDGE-75 - Time: micros() + + * Re-write micros() function in assembly for a 15-20% performance + increase. It should take less than a microsecond now. + + Signed-off-by: Bogdan Pricop + +commit 39eb85ad388a7324a24bb17598e2b6b745f209ab +Author: Kevin Moloney +Date: Tue Jun 23 12:44:16 2015 +0100 + + ATLEDGE-84 - Interrupt framework: make interrupt.h C++ compatible. + + Signed-off-by: Kevin Moloney + +commit 63f6a7698502e7b14798e261be1f2407e06e6a5f +Author: Bogdan Pricop +Date: Wed Jun 17 11:01:10 2015 +0100 + + ATLEDGE-82 - ARC Init: Re-enable I-CACHE + + Enable Instruction cache and invalidate it after interrupt unit device + initialisation + +commit 7f98355166f010b00a64cbf7a949b84d96bec05a +Author: David Hunt +Date: Thu Jun 18 14:13:17 2015 +0100 + + updated pre-compiled driver library. + + Signed-off-by: David Hunt + +commit 653e96c9d9b206297d5c631180c41b4cb13e2860 +Author: David Hunt +Date: Thu Jun 18 13:58:40 2015 +0100 + + ATLEDGE-27 - Wire (i2c) Library - Moving header files. + + Signed-off-by: David Hunt + +commit 31e4e972141524297a096b38d067593a72af5f6e +Author: Bogdan Pricop +Date: Wed Jun 17 10:50:00 2015 +0100 + + ATLEDGE-75 - Time : delayMicroseconds + + Implement delayMicroseconds function in Assembly and using the ARC's + instructions timing as a reference. + The instruction's timing was computed with a scope and using infinite + loops, meaning the code most likely used the advantage of ARC'c cache. + + Function's accuracy is +- 0.15 microseconds in the above stated conditions. + The accuracy can be affected by cache miss annd/or interrupts handling. + It doesn't disable interrupts and it doesn't use any of the SoC power + management features. + + Signed-off-by: Bogdan Pricop + +commit c165e1c12c3918f7dd7e10e05be9550b9fbf1777 +Author: David Hunt +Date: Thu Jun 18 10:20:57 2015 +0100 + + ATLEDGE-27 - Wire (i2c) Library + + Signed-off-by: David Hunt + +commit 648ca2ebcced299f53485e384ea590ff033aafc7 +Author: David Hunt +Date: Tue Jun 16 09:43:33 2015 +0100 + + Updated licence headers according to latest files from Intel. + + Signed-off-by: David Hunt + +commit 4c3f58a7e39ee3da55e83d104897b5abdf60c76f +Merge: c2b4f74 d7c5a80 +Author: David Hunt +Date: Tue Jun 9 16:29:48 2015 +0100 + + ATLEDGE-82 - merge in change of start address for arc code + + Signed-off-by: David Hunt + +commit d7c5a80233d7f574f9060d49f26f102634a50a64 +Author: Bogdan Pricop +Date: Tue Jun 9 15:27:14 2015 +0100 + + ATLEDGE-82 - Init: Change linker scripts + + Modify the start address and the size of the flash area allocated for + ARC core, according to ATLASEDGE/WEEKLY/WW23 tag + +commit c2b4f74efc0cafe3ad4aebd365b2457284a7dafe +Merge: b408ea9 a801dd7 +Author: David Hunt +Date: Tue Jun 9 15:16:08 2015 +0100 + + Merge branch 'fix_1999' - fix for INPUT_PULLUP + + Signed-off-by: David Hunt + +commit b408ea97cbd30aae038381f930995b0f3869702e +Merge: d45290e 539a7c2 +Author: David Hunt +Date: Mon Jun 8 14:20:29 2015 +0100 + + feat_irq_uart: Merged serialEvent() functionality. + + Signed-off-by: David Hunt + +commit 539a7c26495321edae81804e9432a5d0c7f88fbb +Author: David Hunt +Date: Mon Jun 8 13:48:55 2015 +0100 + + feat_irq_uart - Added in serialEvent() functionality + + Signed-off-by: David Hunt + +commit a801dd73b1e5eb41a0bcdb3c50b5a5691ede8f9e +Author: Dan O'Donovan +Date: Mon Jun 8 13:48:20 2015 +0100 + + INPUT_PULLUP doesn't appear to enable an internal pull-up resistor + + Incorrect register base address used by SET_PIN_PULLUP macro in + scss_registers.h to enable/disable internal pull-ups + + Signed-off-by: Dan O'Donovan + +commit d45290e31df7def5c65e1951bd8d3e69347b3843 +Author: David Hunt +Date: Fri Jun 5 13:49:06 2015 +0100 + + Update of precompiled driver library + + Signed-off-by: David Hunt + +commit 88baee7283dc174131c42b7d0256753f6ee9a906 +Merge: ac134d6 79d1ab1 +Author: David Hunt +Date: Fri Jun 5 11:22:49 2015 +0100 + + ATLEDGE-84 - Interrupt framework + + Arduino functions implemented: + * attachInterrupt(Arduino_Pin, handler, mode): + * it works for all Arduino GPIO (IO0 - IO19) + * CHANGE mode is supported only for SoC GPIOs (hardware limitation: + SS doesn't support interrupt generation on both rising and falling + edges.) + * detachInterrupt() + * interrupts() + * noInterrupts() + + Above functions behaviour is according to Arduino website description. + For details please see below references: + http://www.arduino.cc/en/Reference/AttachInterrupt + http://www.arduino.cc/en/Reference/DetachInterrupt + http://www.arduino.cc/en/Reference/Interrupts + http://www.arduino.cc/en/Reference/NoInterrupts + + This implementation uses ss_gpio and soc_gpio drivers from thunderdome. + + Signed-off-by: Bogdan Pricop + +commit 79d1ab16a6687015893a83f8bba079fff0e3be6e +Author: Bogdan Pricop +Date: Fri Jun 5 10:36:30 2015 +0100 + + ATLAEDGE-84: Interrupt framework + + Arduino functions implemented: + * attachInterrupt(Arduino_Pin, handler, mode): + * it works for all Arduino GPIO (IO0 - IO19) + * CHANGE mode is supported only for SoC GPIOs (hardware limitation: + SS doesn't support interrupt generation on both rising and falling + edges.) + * detachInterrupt() + * interrupts() + * noInterrupts() + + Above functions behaviour is according to Arduino website description. + For details please see below references: + http://www.arduino.cc/en/Reference/AttachInterrupt + http://www.arduino.cc/en/Reference/DetachInterrupt + http://www.arduino.cc/en/Reference/Interrupts + http://www.arduino.cc/en/Reference/NoInterrupts + + This implementation uses ss_gpio and soc_gpio drivers from thunderdome. + + Signed-off-by: Bogdan Pricop + +commit ac134d6f742532d9c43783b29c5d145ab8306054 +Merge: 77d7a18 d7cbbf4 +Author: David Hunt +Date: Fri Jun 5 10:09:17 2015 +0100 + + ATLEDGE-14 - Added wiring-shift functionality + + shiftIn() and shiftOut() + + Signed-off-by: David Hunt + +commit d7cbbf4f4bb3abee46f887ee8adbcb9e851594c9 +Author: davids +Date: Fri Jun 5 09:23:44 2015 +0100 + + implement wiring_shift.c() + +commit da2e44848abcb12f4f6c9a891a55425b330fdb25 +Author: davids +Date: Thu Jun 4 15:20:53 2015 +0100 + + implement wiring_shift() + +commit 77d7a180c96bb25b69cc82de14e37104dcc77707 +Merge: de3666c 941f594 +Author: David Hunt +Date: Thu Jun 4 14:44:52 2015 +0100 + + ATLEDGE-83 - Integrating Component Framework IPC with Arduino run-time lib + + Signed-off-by: David Hunt + +commit de3666c598909ffd19079a6e5390559e3435812b +Merge: 59634af 325da1d +Author: David Hunt +Date: Thu Jun 4 14:27:36 2015 +0100 + + ATLEDGE-15 - Analog In functions + +commit 941f594f9fbdc201af1539678e3cd30c9dc8b09d +Author: Dan O'Donovan +Date: Wed Jun 3 10:25:40 2015 +0100 + + cfw: Integrating Component Framework IPC with Arduino run-time lib + + Adding glue layer to initialise CFW and enable interrupt-driven + handling of received messages from LMT core + + Signed-off-by: Dan O'Donovan + +commit cdf2107957c9c8d17ee0667cb754a31d242c75a5 +Author: davids +Date: Thu Jun 4 11:35:13 2015 +0100 + + implement wiring_shift() + +commit 325da1ded53cd4a533c7cd4c2782fa3fbe1dc30c +Author: Kevin Moloney +Date: Thu Jun 4 03:35:04 2015 -0700 + + analogRead fixes + +commit 57310fed13bdf80a77a24b663925ff28b4595bc2 +Author: Kevin Moloney +Date: Thu Jun 4 03:15:40 2015 -0700 + + analogRead fixes: + + Signed-off-by: Kevin Moloney + +commit 59634aff378b89deb36041d94aed7132e5588b37 +Merge: e60cba4 530ba96 +Author: David Hunt +Date: Thu Jun 4 10:17:03 2015 +0100 + + ATLEDGE-76 - Serial functions, addition of interrupt handling + +commit 530ba96203602f7743aac12175c08e4b5ddf0209 +Author: David Hunt +Date: Thu Jun 4 09:52:07 2015 +0100 + + feat_irq_uart: minor changes after code reviews + + Signed-off-by: David Hunt + +commit f0b0954a09cf5808fa87abf8052a088f5ee5e5bd +Author: davids +Date: Wed Jun 3 17:42:21 2015 +0100 + + implement wiring_shift() + +commit 65e2c057260887613e173a274bb439c92bb8141c +Author: Kevin Moloney +Date: Wed Jun 3 07:24:48 2015 -0700 + + adc: Implement analogRead() + + Signed-off-by: Kevin Moloney + +commit acde269f698e087f6e2d23f498576c720dc91c07 +Author: David Hunt +Date: Wed Jun 3 11:19:39 2015 +0100 + + feat_irq_uart: enable UART interrupt handling + + Signed-off-by: David Hunt + +commit e60cba4bcb0e03c4fd8158d9b1226c023e016f19 +Author: David Hunt +Date: Thu May 28 13:56:49 2015 +0100 + + feat_strings: add -fno-exceptions for C++ code to remove linker errors + + Signed-off-by: David Hunt + +commit 81123d478ae1747a9f1148c0270395d95ba62ef8 +Author: David Hunt +Date: Thu May 28 12:03:33 2015 +0100 + + feat_strings: Compiler & linker flags for String builds + + Signed-off-by: David Hunt + +commit d72c26f1c38ef35556dd1faf832b0be8e9c7dd60 +Merge: 07a494f d1289bb +Author: Bogdan Pricop +Date: Wed May 27 18:05:56 2015 +0100 + + ATLEDGE-82: Init - change ARC memory layout. + + * Change start address and size of the Flash area allocated to the ARC core + according to new memory layout received from Toulouse. + + Signed-off-by: Bogdan Pricop + +commit d1289bbdbfdad34ecac16de752237aba65a942ef +Author: Bogdan Pricop +Date: Wed May 27 09:06:25 2015 +0100 + + ATLEDGE-82: Init - change ARC memory layout. + + * Change start address and size of the Flash area allocated to the ARC core + according to new memory layout received from Toulouse. + + Signed-off-by: Bogdan Pricop + +commit 07a494f00158248b1100ad4ba1e240ad0221bfc0 +Author: David Hunt +Date: Thu May 21 15:43:55 2015 +0100 + + Adding updated pre-compiled driver library. + Cleaning up after code reviews and validation of build. + + Signed-off-by: David Hunt + +commit e77ff7a37c2bd3a00199471cbc328d7bf338b87b +Author: David Hunt +Date: Thu May 21 14:08:03 2015 +0100 + + Updated due to code reviews + + * Fix delay() issue + Issue: If value of microsecond is Timer0 limit, arcv2_timer0_count_get() + microseconds condition will always be true, spinning for ever. + The main issue is that the time between 2 consecutive readings of + COUNT0 register is around 90 ticks and the Timer may overflow + before the next reafing of COUNT0 if last values read > LIMIT - 90. + Fix: Take in consideration the number of overflows. If TIMER0 overflows + just leave the loop (even if COUNT0 < microseconds) + * Restructure a bit arcv2_timer0_enable() function in order to reflect the + procedure described in chapter 3.3.76.1 of DesignWare ARCv2 ISA + Programmer's Reference Manual. + Make sure Timer0 initialisation clears the counter register as well. + * code review - added yield via hooks.c + * added back in strip stage when building + * Remove unused code in os.h, os_types.h and pf_init.h + * Uncomment call to yield() from delay() Arduino function; yield() is + added for sake of compatibility. + * Remove dead code from timer0 init function. + * Commented out enable instruction cache + * Added Copyright header to wiring.c + * Setup automatic (hardware) context saving feature for non-fast interrupts + * Serial Communication API Initial checkin of Serial.read() and Serial.write() + using header pins 0 and 1 + * Eliminated some compiler warnings. + + Signed-off-by: David Hunt + +commit c4fad8ecba577bfd28171c31b14aab47a465754a +Merge: 9ec6faf e187e5e +Author: David Hunt +Date: Tue May 19 14:05:37 2015 +0100 + + ATLEDGE82 init code + + small cleanup around interrupt init code. + + Signed-off-by: David Hunt + +commit e187e5ed742bd85ecafa6cde5dfa3ad35ec90bb9 +Author: Dan O'Donovan +Date: Tue May 19 13:42:45 2015 +0100 + + ATLEDGE-82 init code + + Minor tidy-up to interrupt init code + + Currently, timer init functions enable global interrupts. This should + be done in the interrupt setup code during the chip init sequence. + + Signed-off-by: Dan O'Donovan + Signed-off-by: David Hunt + +commit 9ec6fafc1665504fe90fc99623f1348267caeed9 +Author: David Hunt +Date: Tue May 19 13:13:45 2015 +0100 + + ATLEDGE-82 init code + + final cleanup before push. no functional changes. + +commit d37176a3b8483127244c5c72bad861ef4598d756 +Author: David Hunt +Date: Tue May 19 12:56:33 2015 +0100 + + ATLEDGE-82 init code + + Interrupt handling - add hw context saving + + * Disable instruction cache (we don't need it at this stage) + * Setup automatic (hardware) context saving feature for non-fast + interrupts. + * Set optimization to none (-O0) in Makefile. + + build: Eliminating compiler warnings + + Fix interrupt handling bug + + Issue: + A simple blink sketch having a 10 ms delay between blinks crashed + after a few seconds. + It looks like the stack got corrupted. + Root cause: + The generic assembly hardware ISR, "_do_isr", was not properly + written; the "sp" was not properly handled. + Solution: + Replace the assembly hardware ISR with a C function having + "__attribute__((interrupt("ilink")))" + + Note: + We need this generic hardware ISR because the IVT is in flash and + we cannot change it at runtime. + + Allocating more SRAM to ARC (syncing with latest Thunderdome mem allocation) + + build: Removing sdata section declaration from linker scripts + + ARC doesn't support an SDATA section bigger than 1024 bytes, and + we overflow this too easily when we link in code that hasn't been + compiled with the -mno-sdata compiler flag. So we will ensure that + we only use code compiled with -mno-sdata (including standard C lib) + and we remove this section declaration to weed out any exceptions to this. + In other words, if we link any code that tries to use the sdata section, + we will see a linker error. + + build: Adding HEAP section + + Adding heap section with start/end marker symbols to satisfy + malloc() and friends + + Signed-off-by: David Hunt + +commit d4fe5885d8e2bebd071e4e7d378d3fbf8e85a2eb +Author: Bogdan Pricop +Date: Mon May 18 11:12:35 2015 +0100 + + ATLEDGE-82 init code + + Interrupt handling - add hw context saving + + * Disable instruction cache (we don't need it at this stage) + * Setup automatic (hardware) context saving feature for non-fast + interrupts. + * Set optimization to none (-O0) in Makefile. + + Signed-off-by: Bogdan Pricop + Signed-off-by: David Hunt + + Author: Bogdan Pricop + +commit 82c3529091c7c2d1f13d1d55c3d31cdf0aa56840 +Author: Dan O'Donovan +Date: Fri May 15 16:25:07 2015 +0100 + + ATLEDGE-82 init code for arc + + Eliminating compiler warnings and cleanup. + + Signed-off-by: Dan O'Donovan + Signed-off-by: David Hunt + + Conflicts: + system/libarc32_edu/drivers/ns16550.c + system/libarc32_edu/drivers/uart.h + system/libarc32_edu/main.c + +commit 4a29f5c30fb52e433f30c1ab7ba30244953e2d05 +Author: Dan O'Donovan +Date: Fri May 15 14:46:08 2015 +0100 + + cfw: Importing Component Framework library code from Thunderdome + + Framework code extracted from the following commit ID in + Intel's thunderdome git repo: 8b198659675cd820072bdeb00d0c5de87c6f6ee2 + + This brings the necessary library code into the source tree, but not + yet integrated in the Arduino run-time environment (to follow shortly). + + Signed-off-by: Dan O'Donovan + +commit c2f3e0413c6e1c4567faedd5b2e7a8308fc8c42e +Author: Bogdan Pricop +Date: Fri May 15 14:24:10 2015 +0100 + + ATLEDGE-82 init code - Fix interrupt handling bug. + + Issue: + A simple blink sketch having a 10 ms delay between blinks crashed + after a few seconds. + It looks like the stack got corrupted. + Root cause: + The generic assembly hardware ISR, "_do_isr", was not properly + written; the "sp" was not properly handled. + Solution: + Replace the assembly hardware ISR with a C function having + "__attribute__((interrupt("ilink")))" + + Note: + We need this generic hardware ISR because the IVT is in flash and + we cannot change it at runtime. + + Signed-off-by: Bogdan Pricop + Signed-off-by: David Hunt + +commit 723be266ea682a413efca74e5b69190e17bf2a65 +Author: Dan O'Donovan +Date: Fri May 15 14:49:26 2015 +0100 + + Allocating more SRAM to ARC (syncing with latest Thunderdome mem allocation) + + Signed-off-by: Dan O'Donovan + +commit b9bc0fb221574f5f5a52abfa1d9d2b3da85fc1ba +Author: Bogdan Pricop +Date: Tue May 12 21:31:11 2015 +0100 + + .gitignore file added to repo. + + Signed-off-by: Bogdan Pricop + +commit bd205779390d879a5e866941b840e5094204c97c +Author: Dan O'Donovan +Date: Tue May 12 17:47:54 2015 +0100 + + build: Adding HEAP section + + Adding heap section with start/end marker symbols to satisfy + malloc() and friends + + Signed-off-by: Dan O'Donovan + +commit 4139a2efcdee1b210933b7abeee2bea084d1b369 +Author: David Hunt +Date: Tue May 19 12:06:17 2015 +0100 + + ATLEDGE-75 Arduino time related functions. + + A first draft for the following functions: + * delay() + * millis(); + * micros(); + Not yet implemented: + * delayMicroseconds. + + Author: Bogdan Pricop + + Signed-off-by: Bogdan Pricop + Signed-off-by: David Hunt + +commit 3689741b718ce769a50515d661a7ebf13cf733be +Author: David Hunt +Date: Tue May 19 11:58:55 2015 +0100 + + ATLEDGE-24 Serial Communication API + + Intial release of serial functionality, Serial.write, print, println, read + Also added files required, Ringbuffer (not used yet), Print, etc. + Also some small changes to make Intel toolchain work, still more work needed. + + Signed-off-by: David Hunt + +commit 57550ebb85d6f69dc52cefcf38f47a7995f708b3 +Author: David Hunt +Date: Tue May 19 11:52:19 2015 +0100 + + ATLEDGE-83 Component Framework integration into bare metal + + Author: Dan O'Donovan + Date: Fri May 15 14:46:08 2015 +0100 + + Framework code extracted from the following commit ID in + Intel's thunderdome git repo: 8b198659675cd820072bdeb00d0c5de87c6f6ee2 + + This brings the necessary library code into the source tree, but not + yet integrated in the Arduino run-time environment (to follow shortly). + + Signed-off-by: Dan O'Donovan + Signed-off-by: David Hunt + +commit ed4bf0214f53df5b8c255ed6f575fcdc6bf361d3 +Author: David Hunt +Date: Tue May 19 11:48:40 2015 +0100 + + ATLEDGE-15 basic analog functions + + Adding analogWriteResolution and analogReadResolution + + Signed-off-by: Dan O'Donovan + Signed-off-by: David Hunt + +commit 32aff42d8023cafcb6b5b86820e455b1914ecf55 +Author: David Hunt +Date: Wed May 6 17:14:51 2015 +0100 + + ATLEDGE-62 Create Initial Release of Arduino Corelibs + + Adding licence headers to those files that don't have any. + + Signed-off-by: David Hunt + +commit 09a6c2ea91c99a7f8e3aff45cb4bc7f3e501a715 +Author: David Hunt +Date: Wed May 6 14:50:40 2015 +0100 + + ATLEDGE-62 Create Initial Release of Arduino Corelibs + + Allows Arduino IDE to build Blink Sketch + Includes pinMode(), digitalWrite() and delay() + Allows download via JTAG + + Signed-off-by: David Hunt + +commit a5ca45cc6a31e8775363fa6c3f0c32e337945a43 +Author: Dino Tinitigan +Date: Thu Apr 16 14:05:33 2015 -0700 + + Initial commit of core files + + Initial commit of core files + +commit 631d399bf35241b823ae74b3b4dc6b27d303078e +Author: Calvin Park +Date: Thu Apr 16 13:29:04 2015 -0700 + + Initial commit From 8c5268a6a0a1b797321a80aa73dab233e74056c0 Mon Sep 17 00:00:00 2001 From: Yashaswini Hanji Date: Thu, 16 Mar 2017 12:58:35 -0700 Subject: [PATCH 077/125] cleanup unwanted file --- tatus | 4812 --------------------------------------------------------- 1 file changed, 4812 deletions(-) delete mode 100644 tatus diff --git a/tatus b/tatus deleted file mode 100644 index 0bf5a18c..00000000 --- a/tatus +++ /dev/null @@ -1,4812 +0,0 @@ -commit 178bb5f58570f9e71b7423940c11e4d6d97e8f1a -Author: Dino Tinitigan -Date: Wed Feb 22 14:08:30 2017 -0800 - - CDC-ACM CODK-M firmware compatibility changes - - -compatibility changes for CDC-ACM usb serial to work with upcoming - CODK-M based firmware - -use a fixed delay between writes instead of trying to mimick UART - throttling - -commit ee08a03b21d5c141020b328974c2392a8484dfa9 -Author: Erik Nyquist -Date: Fri Jan 27 16:28:38 2017 -0800 - - Use single dataReady() method, instead of accelDataReady/gyroDataReady - - This method is a bit more flexible. There is a version with no - parameters, which will return true if all enabled sensors have new - data. There is one overload, that takes a single 'flags' parameter, - which returns true if all enabled *and* selected sensors have new data. - Sensors are selectable through bit flags. For example; - - CurieIMU.dataReady() : this will return true if both the accel and gyro - are enabled, and both have new data ready. - - CurieIMU.dataReady(ACCEL | GYRO) : this will return true if both the - accel and gyro are enabled, and both have new data ready. - - CurieIMU.dataReady(GYRO) : this will return true if the gyro is - enabled and has new data ready. - - CurieIMU.dataReady(ACCEL) : this will return true if the accel is - enabled and has new data ready. - - This also involved changing the 'begin' method to allow selective - enabling of sensors, e.g. - - CurieIMU.begin() : same as it always has, initializes IMU, enabling - both accel and gyro - - CurieIMU.begin(ACCEL | GYRO) : initializes IMU, enabling both accel - and gyro - - CurieIMU.begin(ACCEL) : initializes IMU, enabling accel only - -commit fb578000afad9bc5cdf74358e8c3acbc7a04f9e8 -Author: Sidney Leung -Date: Wed Feb 15 22:28:08 2017 -0800 - - Jira 792, Support addition UUID types, git PR 386. - - Features added: - - 1. Add the support for two addition UUID types, - UUID16_SOME and UUID128_SOME. These types, - practically, behaves the same as their related - ALL types. Sandeep found that the Apple devices - made use of these additional types and requested - for their support. - - File mods: - - 1. BLEDeviceManager.cpp: - - Add UUID16_SOME and UUID128_SOME at places - checking for UUID types. - -commit f61091473e5888337098a103daccf321e3fe328c -Author: lianggao -Date: Wed Feb 8 10:59:09 2017 +0800 - - Jira 804 BLE Characteristic initialization issue, git 366 - - Issue: - - Created Characteristic failed to reflect the initialized - value. - - Root cause: - - During initialization, the length field was incorrectly - skipped over. - -commit ff32c97b547b42e8afa3e423c7ad9ddf4057b4dd -Author: lianggao -Date: Tue Dec 20 18:21:23 2016 +0800 - - BLE Peripheral cannot discover Central attributes - - 1. Add test sketches discoveratperipheral.ino and profileatcentral.ino - 2. Modify BLEDeviceManager.cpp to add central to profile buffer in peripheral - role. - -commit d6c68f26914bf9508e8b76081d383a5cd432c7b6 -Author: Erik Nyquist -Date: Tue Feb 14 12:40:25 2017 -0800 - - flash.ld: fix mis-aligned .data section - - The end of the .data section in flash must be explicitly word-aligned, - otherwise corruption can occur in a .data section that happens to contain - non-word aligned data. - -commit 0285190f5d94dd2228a33ec5ef91d0e3cecfed56 -Author: Erik Nyquist -Date: Thu Feb 9 10:18:37 2017 -0800 - - EEPROM.cpp: Use double quotes for include file - - works in the Arduino IDE, but it's not actually in a system - include location so it makes non-IDE (i.e. Klocwork) builds fail - -commit 547ecd723e39da4044cb636d6bdbb8956cb2f2f2 -Author: lianggao -Date: Fri Jan 13 09:04:29 2017 +0800 - - Sketch crash using both Firmata and BLE library, gitHub issue #377 - - 1. The BLEStream object adds profile when boot but the HW doesn't be - initialized and makes APP crash. - 2. Change the BLE initial process not depend on HW. Buffer the - attributes if HW not initialized. - 3. Changed files - libraries/CurieBLE/src/BLEDevice.h - expose constructor - libraries/CurieBLE/src/BLEPeripheral.cpp - not call HW init function - libraries/CurieBLE/src/internal/BLEDeviceManager.cpp - change init order - libraries/CurieBLE/src/internal/BLEProfileManager.cpp - change constructor - -commit fe14b8bd2600544330e95ccf22f7f91c114467c0 -Author: lianggao -Date: Mon Feb 6 14:13:37 2017 +0800 - - Fix #380 characteristic written returns incorrect value after first connect - - 1. Not set the flage when local device try to set the value. - 2. Changed file - libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp - -commit 0eac6a00a766e424b87aecbbb734eb76bcb71694 -Author: lianggao -Date: Fri Jan 13 22:07:08 2017 +0800 - - Jira 794 Inconsistent writeInt() result with different Peripherals, git 385 - - Root cause: - - As a Central, the write() operation can produce two different - commands to a Peripheral depending on the Characteristic. - - One command is a simple write without aknowledgement from - Peripheral and the other requires a complete handshake for - each write operation. - - Root cause of the issue was that the function, - BLECharacteristicImp::write(), did not check the - characteristic property to determine if the caller requires - a response. It just treats all calls do not require a - response. - - Feature added: - - For write with respond from Peripheral, it is now a - blocking call (it also satisfied Arduino's request). - - Modifications: - 1. libraries/CurieBLE/src/internal/BLECallbacks.cpp: - - ble_on_write_no_rsp_complete: CB to process Peripheral - Response to complete a write operation. - 2. libraries/CurieBLE/src/internal/BLECallbacks.h: - - Function definition added. - 3. libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp: - - In function write(), check the characteristic property to - determine whether the call requires a response. - - Added a flag to signal the waiting on Peripheral - Response for a write operation. - - Write with response is now a blocking call. - 4. BLECharacteristicImp.h: - - Prototyping. - -commit 40b0c43967187137c9bde6bf88f57732a8a30aa4 -Author: lianggao -Date: Mon Jan 9 10:50:13 2017 +0800 - - Jira 797, Central can scan with MAC address, git 376 - - New feature: - - Add the capability for a Central to scan for a - Peripheral using its MAC address. - - Code Modifications: - - Added the API for MAC address scanning. - - For Device Manager, added provision for holding the - MAC address of a Peripheral. - - For scan discovery handler, added the checking - for MAC address comparison, if MAC address scanning - is requested. - - File changes: - - 1. libraries/CurieBLE/src/BLEDevice.cpp: - - Added API for MAC address scanning. - 2. libraries/CurieBLE/src/BLEDevice.h: - - API definition. - 3. libraries/CurieBLE/src/internal/BLEDeviceManager.cpp: - - Added provision for storing the MAC address. - - Added MAC address comparison in the scan discovery - handle if MAC address scanning is selected. - 4. libraries/CurieBLE/src/internal/BLEDeviceManager.h: - - Prototyping. - -commit 78c3d0ad0a5475f7809d31dfd1c2d75cbcf4a58c -Author: Erik Nyquist -Date: Thu Feb 2 13:49:50 2017 -0800 - - EEPROM library: move function definitions into separate .cpp files - -commit 001cab44592510cf9ea799d7a6f34d237d5b149c -Author: Erik Nyquist -Date: Wed Feb 1 11:36:59 2017 -0800 - - freeMemory.ino: add introductory comment - -commit b60860237540cde903b81615c3324f47f03cd756 -Author: Erik Nyquist -Date: Tue Jan 10 15:18:27 2017 -0800 - - Add new library MemoryFree - - This is the MemoryFree libary from - http://playground.arduino.cc/code/AvailableMemory, rewritten specifically - for Arduino 101 and tinyTILE. It provides some functions for querying - available stack & heap space. - -commit 29daefb64ae643a0cb2bcb15b586e90a206bf7e8 -Author: Dino Tinitigan -Date: Tue Jan 10 10:24:15 2017 -0800 - - add support for Power Management library - -commit cd3215bd5929dd83004cdf3b94e32726ff2eba2e -Author: Dino Tinitigan -Date: Tue Jan 10 10:17:13 2017 -0800 - - expose all AON-GPIOs - - -expose AON-GPIOs since they are available on the TinyTILE - -commit 90e8bc65828a7e65f5a59d9a64c3db7df185e0a3 -Author: Dino Tinitigan -Date: Tue Jan 10 09:55:59 2017 -0800 - - shared memory structure changes for CODK-M - - -added struct for ipm using shared memory - -added cdc-acm buffer space on shared memory which is used by the new firmware - -commit 7c97231c087929a90ae880486b3c0ce539a9b326 -Author: Erik Nyquist -Date: Tue Jan 10 10:31:56 2017 -0800 - - CurieIMU.cpp: initialize saved accel/gyro ranges in begin() - - This ensures that "read[Accelerometer/Gyro]Scaled()" will work as - expected even if a range has not been explicitly set using the API - -commit 17d2e2fb22ba0cd239c46973cca0006704534195 -Author: Erik Nyquist -Date: Mon Jan 9 10:55:34 2017 -0800 - - Rebuild libarc32drv_arduino101.a - -commit 5d91dc66fe0f49ab5cb9d5dc7459a9abba5ea7de -Author: Erik Nyquist -Date: Thu Dec 29 17:57:17 2016 -0800 - - libarc32_arduino101/Makefile: Add 'strip' target - - The file libarc32drv_arduino101.a is built with debugging symbols by - default, however the version we ship needs to be stripped. The new - "strip" target makes it easy for us to strip when committing, and - the default target still behaves the same way, so the library can be - re-compiled for debugging purposes - -commit 4ae5af7f8a63dfaa9d44b35c0c820d93a4a6980e -Author: Noel Paz -Date: Thu Dec 22 13:24:24 2016 -0800 - - Corrected the MIDIBLE example so it advertises correctly - -commit 621949108d0c3def12722b626b57176d10425863 -Author: noelpaz -Date: Wed Dec 21 17:10:09 2016 -0800 - - Modified SensorTag example and added examples derived from 1.0.7 - -commit 2391f48210995aa34fc20e62745811aeaaa0bd7d -Author: Sidney Leung -Date: Wed Dec 21 00:48:55 2016 -0800 - - Moving balloc static memory buffer to DCCM memory - - balloc() is used by interrupt context code for memory allocation. The memory space is statically allocated at - initialization. In order not to take up SRAM memory, this buffer is moved to the DCCM memory space. - -commit 34d0e50135f9e0baff349fe116f92d049e9f97c9 -Author: Sidney Leung -Date: Tue Dec 20 13:11:21 2016 -0800 - - I2S example sketches failed compliation, Jira 786. - - The latest BLE library consumes more heap space that causes these sketches ran out of heap space for their temporary - buffers. For now, the size of the tempporary is reduced. For the near future, there are 3 improvements to address - this issue. The BLE library will be making use of the DCCM memory instead of heap. The I2S library buffering space - is going to the DCCM memory too. The heap space will be increased to maximize the usage of the entire SRAM sapce. - -commit 4c89a183f8e509c3f7c08a589a291ebb91ee171e -Author: lianggao -Date: Mon Dec 19 14:59:55 2016 +0800 - - Add test sketches - -commit ade23f0c2c064b733a1c3d264eb29e1a608f8745 -Author: lianggao -Date: Mon Oct 17 16:11:18 2016 +0800 - - 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 - -commit 431c7812e71c904b0e8212a0234d520ffbbe2b0d -Author: Dino Tinitigan -Date: Tue Dec 13 16:16:17 2016 -0800 - - check mux mode when using digitalWrite (#335) - - -check and make sure that the pin is in GPIO_MUX_MODE when doing a - digitalWrite() - -this has a slight performance impact of digitalWrite - -it also comsumes an extra byte in SRAM per pin - -it does however give us the ability to track the muxing state of each - pin - -commit 16227f53f2591eaa47e551b8000878855294eb35 -Author: Dino Tinitigan -Date: Tue Dec 13 16:15:51 2016 -0800 - - Ensure RTC clock scaling is set to 1Hz (#350) - - -needed to make sure that the RTC scaling is consitent with the factory - firmware and CODK-M based firmware - -commit 0d321db5cc619dc9a4451a6710dabd38fee492bf -Author: Erik Nyquist -Date: Tue Nov 29 11:32:50 2016 -0800 - - CurieIMU.cpp: Don't lock interrupts for SPI transfers - - This is unnecessary- nothing else is contending for access to this - particular SPI controller - -commit e870ba255646afa47debfc0b1cb493cc055520dd -Author: Dino Tinitigan -Date: Tue Nov 29 13:27:38 2016 -0800 - - Ensure RTC clock scaling is set to 1Hz - - -needed to make sure that the RTC scaling is consitent with the factory - firmware and CODK-M based firmware - -commit de208be5c42a305624468d9a1a5208f60d0b5bdb -Author: Erik Nyquist -Date: Tue Nov 29 10:35:10 2016 -0800 - - README.md: add steps for updating intel-arduino-tools - -commit 2bb7d4b58e4152fde9b87153f71618475f54c42a -Author: Erik Nyquist -Date: Fri Oct 21 16:20:37 2016 -0700 - - README.md: remove Serial1 debug information - -commit 205c1e40852f356a4819a16260086e8f3c719592 -Author: Adrien Descamps -Date: Thu Aug 11 23:13:02 2016 +0200 - - Enable interrupt mode of UART tx - - Previous version of write will never use the interrupt mode - transmission, because it fails to check if holding register is - empty. This commit fix that, and several bugs related to - interrupt mode serial transmission. - When possible, it uses the tx FIFO, in order to reduce - the number of interrupts called and the overhead. - -commit 5279509cc2f6d161e1607245f15024e5d578c10e -Author: Erik Nyquist -Date: Mon Nov 14 10:12:39 2016 -0800 - - CurieTime.cpp: check gmtime() return pointer for NULL-ness - -commit 249fb3b7b6a7ebf85a202688c960de9f256a8018 -Author: Erik Nyquist -Date: Mon Nov 14 16:20:09 2016 -0800 - - README.md: add link to ICS for product support - -commit c353f4caa7e5d9a4287410f2826f8e75683ab13c -Author: Calvin Park -Date: Thu Oct 27 15:57:39 2016 -0700 - - Use our own version of copy.exe so that we can copy without cmd.exe - -commit 6ee828b737b4bd08dda78b0769bb9043c64d2c4c -Author: Dino Tinitigan -Date: Thu Oct 20 15:53:53 2016 -0700 - - Move SoftwareSerial buffers into DCCM area - -commit 41f98f9c459a0ed7175b34c9c334c03c352c0706 -Author: Dino Tinitigan -Date: Thu Oct 20 15:14:47 2016 -0700 - - Allocate RingBuffer buffer into DCCM area - - -this buffer is used by the uart. Moving it into the DCCM area would - save us 128 bytes of SRAM - -commit 8619f2b3e088c28c389550a19ecf2e2e3029543e -Author: Dino Tinitigan -Date: Thu Oct 20 11:35:08 2016 -0700 - - Move buffers for Wire library to DCCM area - -commit 4e2232e3e55ab68da0e125f59da2817f45b7e06d -Author: Dino Tinitigan -Date: Wed Oct 19 15:26:19 2016 -0700 - - Expose dccm area - - -expose dccm area giving the user an extra 8k of memory - -simple implementation of a malloc like function for dccm memory area - -commit 1c8de0d7cc0ecf93abeb539bd97b0fee2b09eb97 -Author: Erik Nyquist -Date: Tue Oct 11 10:11:29 2016 -0700 - - Add functions to check if new IMU data is available - - accelDataReady() and gyroDataReady() read the drdy_acc and drdy_gyr - bits, respectively, of the BMI160's sensor status register. This removes - the burden of syncronising sensor reads from the user. - - (adapted from descampsa/corelibs-arduino101@773a800) - -commit 3d3c1f92c2c15724725f71542bc564754ba5d6e3 -Author: Erik Nyquist -Date: Wed Oct 12 15:42:50 2016 -0700 - - I2SDMA: Do not modify g_APinDescription - - The g_APinDesctiption is now 'const', so cannot be modified. - -commit d6b2ecaa47bd543dbb35a33b36dfa2fb9037de86 -Author: Dino Tinitigan -Date: Wed Sep 28 12:54:37 2016 -0700 - - pwm improvements - - -add capability to change pwm signal frequency with analogWriteFrequency - -improve pwm signal frequency accuracy - -commit 27a68decb8e450f52b7c4c9dc254a1b0cb045b17 -Author: Biagio Montaruli -Date: Wed Sep 7 13:15:41 2016 +0200 - - Update sketches that use serial communication of different libraries - - Improve sketches of CurieTime, CurieTimerOne, CurieSoftwareSerial, SPI and - SerialFlash libraries that use Serial communication. - Since Arduino/Genuino 101 uses USB native port, wait for the Serial port - to open to not lose serial data already sent to the Serial monitor. - - Signed-off-by: Biagio Montaruli - -commit 39ef9ef12c50366c4b77f494ae53361bbaf2118c -Author: Calvin Park -Date: Fri Oct 7 11:07:44 2016 -0700 - - Don't save unstripped elf for debugging (#314) - - The current save mechanism breaks when a user has deleted system32 from PATH environment variable. Comment out the copying so that it won't break for such users, but do not delete so that advanced users can use the unstripped elfs to debug. - -commit c396a3ad4f0ffb9163b0a2b270aa4584848764f3 -Author: Erik Nyquist -Date: Tue Oct 4 16:59:36 2016 -0700 - - Shrink oversized objects in SRAM - - 824 bytes is gained for use by sketches. Two new defines are added; - CDCACM_BUFFER_SIZE and UART_BUFFER_SIZE. The CDC-ACM Serial class and - the UART driver had been using the same #define for buffer size-- - SERIAL_BUFFER_SIZE-- and separating them means that the CDC-ACM buffer - can stay the same size, while the UART buffer is shrunk. - -commit 1a5e3f7b2d65cf2b81417f85bd24ca455bdaa1fc -Author: Erik Nyquist -Date: Thu Sep 29 15:17:07 2016 -0700 - - Make g_APinDescription const - - This has the effect of moving g_APinDescription out of .data (SRAM), - and into .rodata (flash). - - Pros: - sketches now have an additional 1160 bytes of SRAM available - - Cons: - state of pins is no longer tracked; e.g. when calling digitalWrite, - the full configuration is done each time. See changes in wiring_digital.c - and wiring_analog.c for details. There is inevitably a performance impact - for digitalRead/Write & analogRead/Write, though it is unlikely to be a - noticable one. - -commit 2e56b81a264c8760f095a12f650381b8ea8de831 -Author: Erik Nyquist -Date: Wed Sep 28 12:02:32 2016 -0700 - - stdlib_noniso.cpp: Handle rounding corner case for dtostrf - - Rounding must be done before separating the integral and decimal portions. - -commit f2a001f4fa557c4374493242c67ecee617a59866 -Merge: 77d9086 d2b7d1d -Author: Calvin Park -Date: Thu Sep 22 11:57:28 2016 -0700 - - Add BLE Central feature - -commit d2b7d1d732ca2ace5eb3f3da2e5cf3371e104f54 -Author: unknown -Date: Mon Sep 12 10:33:51 2016 -0600 - - JIRA-685, Peripheral sketches shall state associated Central sketch. - -commit 9581cf18a0a904e096f1dcab30624871c22714c4 -Author: Brian Baltz -Date: Mon Aug 29 14:01:05 2016 -0700 - - Update expected BLE version for V3 - - Signed-off-by: Brian Baltz - -commit 0304de781d54d0158c070d2d9cc750ab295c0712 -Author: Calvin Sangbin Park -Date: Fri Sep 9 10:55:17 2016 -0700 - - Rebuilt libarc32drv - -commit 84fc3dbbfb900d0d5cf5b8b2a9c7e734accb5771 -Author: lianggao -Date: Fri Sep 9 08:28:50 2016 +0800 - - Create BLE Example sketches - - 1. Fix Jira 664 demonstrates changing the ADV data - 2. Fix Jira 671 Support update connection interval in central/peripheral - -commit e7238f02c945faf6a05eca38ae590d520ec2af62 -Author: lianggao -Date: Fri Sep 9 08:25:31 2016 +0800 - - Jira Tickets and Bug fix - - 1. Jira 541 Peripheral Start fails on X Number of Attributes - -Change the interface and add return value - - 2. Fix BLE Peripheral is not advertising - -Add a delay after register the profile. - - 3. Modify some comments based on code review - - 4. Jira 544 BLE peripheral disconnect and end functions - -The state not aligned and make disconnect failed. Update when connect and disconnect event received - - 5. Delete the duplicated code that base class has implemented - - 6. Fix Jira 665 BLECentral Preview -- compile issue when instantiating BLE descriptor - -Fix the build error. - Note: - i . The current code only support one descriptor on characteristic. - ii . The central discover logic need more twist. - iii. Now is so long and can't process CCCD and another characteristic. - iv . The library will support one other descriptor except CCCD. - - 7. Improve the discover logic and support another descriptor - i. Improve the discover logic - ii. Support another the descriptor and CCCD at same time. - - 8. Modify the comments and delete the unused API - - 9. Fix Jira 670 A compile error occurs with two BLE methods with the same name - i. Add method uuid_cstr in BLEAttribute class. - ii. Remove the duplicate file. - - 10. Fix Jira 672 BLE documentation in code needs to specify units - i. Change the scan and connection interval's unit to ms. - ii. Unify the advertising interval unit as millisecond - iii. Delete some unused code. - - 11. Fix Jria 671 Support update connection interval in central/peripheral - i. Central can update the connection interval. - ii. Unify the interval unit to ms at user API. - - 12. Adjust the example comments and functions - i. Fix Jira 675 - BLE callbacks should use passed arguments instead of global variables - ii. Adjust the code struture to align with Arduino's requirement - - 13. Fix Jira 680 LED and LED Central sketches need some work - -Add a delay to make sure profile register process success. - -The UART send too fast may makes the profile registration failed. - - 14. Fix Jira 673 BLE Api should pass arguments that are typedef - - 15. Fix Jira 671 Support update connection interval in central/peripheral - i. Add peripheral update the connection interval feature. - ii. Update the library. - - 16. Fix Jira 687 Edit the keyword.txt of CurieBLE to reflect Library changes - - 17. Make example function ready. But need to resolve Jira 675 - - 18. Fix Jira 676 Review Edit Update BLE User Manual with China Flex - - 19. Fix Jira 685 BLE Peripheral sketches shall state which associated Central sketch to use - - 20. Fix Jira 588 - BLE Corrupted Long Write - i. Modify the BLECharacteristic to implement the Long write feature. - - 21. Fix Jira 683 Typecasting and Pointers should be avoided in sketch - i. Add write in Characteristic template - ii. Add method in BLE Role class to avoid typecaste - iii. Modify the advertise address parameter and related example sketches - - 22. Fix Jira 704 Type Characteristic should be documented - 23. Fix Jira 684 Documentation about the central/peripheral - -commit ae78b1626ef682331af055075fbddb672cb19552 -Author: lianggao -Date: Fri Jul 29 22:19:36 2016 +0800 - - Add BLE central function, debug interface and refactor peripheral - - -Add debug interface on Serial1 - -Update BLE stack and need update BLE's FW - -Reconstruct the BLE peripheral base on V3 - -Implement the BLE Central Role base on V3 - -Implement some sketches for new BLE library - -Add central read/write example - -Add set advertising parameter interface - -Add API to allow set up advertising after setup - -Add interface to set the device name - - File description - Porting from V3 - system/libarc32_arduino101/common/atomic.h - system/libarc32_arduino101/common/misc/byteorder.h - system/libarc32_arduino101/drivers/atomic_native.c - system/libarc32_arduino101/drivers/bluetooth/att.h - system/libarc32_arduino101/drivers/bluetooth/bluetooth.h - system/libarc32_arduino101/drivers/bluetooth/conn.h - system/libarc32_arduino101/drivers/bluetooth/conn_internal.h - system/libarc32_arduino101/drivers/bluetooth/gatt.h - system/libarc32_arduino101/drivers/bluetooth/hci.h - system/libarc32_arduino101/drivers/bluetooth/uuid.h - system/libarc32_arduino101/drivers/rpc/rpc.h - system/libarc32_arduino101/drivers/rpc/rpc_deserialize.c - system/libarc32_arduino101/drivers/rpc/rpc_functions_to_ble_core.h - system/libarc32_arduino101/drivers/rpc/rpc_functions_to_quark.h - system/libarc32_arduino101/drivers/rpc/rpc_serialize.c - system/libarc32_arduino101/framework/include/util/misc.h - system/libarc32_arduino101/framework/src/os/panic.c - system/libarc32_arduino101/framework/src/services/ble/conn.c - system/libarc32_arduino101/framework/src/services/ble/conn_internal.h - system/libarc32_arduino101/framework/src/services/ble/dtm_tcmd.c - system/libarc32_arduino101/framework/src/services/ble/gap.c - system/libarc32_arduino101/framework/src/services/ble/gatt.c - system/libarc32_arduino101/framework/src/services/ble/hci_core.h - system/libarc32_arduino101/framework/src/services/ble/l2cap.c - system/libarc32_arduino101/framework/src/services/ble/l2cap_internal.h - system/libarc32_arduino101/framework/src/services/ble/smp.h - system/libarc32_arduino101/framework/src/services/ble/smp_null.c - system/libarc32_arduino101/framework/src/services/ble/uuid.c - system/libarc32_arduino101/framework/src/services/ble_service/ble_service.c - system/libarc32_arduino101/framework/src/services/ble_service/ble_service_api.c - system/libarc32_arduino101/framework/src/services/ble_service/ble_service_int.h - system/libarc32_arduino101/framework/src/services/ble_service/ble_service_internal.h - system/libarc32_arduino101/framework/src/services/ble_service/ble_service_utils.c - system/libarc32_arduino101/framework/src/services/ble_service/gap_internal.h - system/libarc32_arduino101/framework/src/services/ble_service/gatt_internal.h - system/libarc32_arduino101/framework/src/services/ble_service/nble_driver.c - -commit 77d908688e799557fa6e582fbd885c825aaa07c6 -Author: Calvin Sangbin Park -Date: Mon Sep 19 16:48:28 2016 -0700 - - Rebuilt libarc - -commit 5340b0c9c1fa02257b7f363339594944f4c6be9e -Author: Martino Facchin -Date: Mon Sep 5 15:41:42 2016 +0200 - - CurieEEPROM refactor - - This PR refactors CurieEEPROM library, syncing it with AVR version. - Since EEPROM on Curie chip is based on OTP flash storage, the minimum write - size is 4 bytes. The current implementation brings some problems like - http://forum.arduino.cc/index.php?topic=421348.0 , and the examples are - quite broken. - - The refactor also changes the name, from CurieEEPROM to EEPROM, so code for - AVR sketches compiles out of the box. The new library resolution engine - (from IDE 1.6.8 onward) handles this perfectly but we can stick to older name - if we need compatibility with older IDEs. - -commit 4c69f724b9e253c014e118ca87d031929deea7ca -Author: Sidney Leung -Date: Thu Sep 1 15:20:32 2016 -0700 - - Added support for Quark I2C interfaces: I2C0 and I2C1. - - Additions: - 1. Ported Quark I2C driver to ARC/CoreLibs, located at system/libarc32_arduino101/drivers. - 2. Created the I2C Adapters, located at cores/arduino. - - Modifications: - 1. Platform.h - added number of I2C interface definition. - -commit 7dcc0742ef43174547efec5497359d6d80cc6387 -Author: Biagio Montaruli -Date: Sat Sep 17 13:45:03 2016 +0200 - - Update bus_scan.ino sketch of Wire library - - Improve documentation, code formatting and use LED_BUILTIN instead of 13 - to control the on-board led connected to digital pin 13 of - Arduino/Genuino 101 - - Signed-off-by: Biagio Montaruli - -commit 23ca972371c3bf9db488e948d35b870a1fa52b2c -Author: Sandeep Mistry -Date: Thu Sep 15 15:22:35 2016 -0400 - - WString: add `toDouble` (#293) - - Port of https://github.com/arduino/Arduino/pull/5362 - -commit f818d7cd360e109c31bc65ed11f0df3b133bc826 -Author: Sandeep Mistry -Date: Tue Sep 6 14:06:34 2016 -0400 - - Make String::move of an invalidated String result in an invalidated String - -commit 2751995ab29738c708b5b7d109d98cbfc34a09e4 -Author: Biagio Montaruli -Date: Wed Sep 7 13:01:32 2016 +0200 - - Fix for USB virtual serial port in sketches of CurieIMU library - - Since Arduino/Genuino 101 uses USB native port, wait for the Serial port - to open before executing the sketch to not lose serial data already sent - to the Serial monitor - - Signed-off-by: Biagio Montaruli - -commit d8d2c495b531fda1ffe98cb59f7e35fd948c5bad -Author: Biagio Montaruli -Date: Wed Sep 7 13:05:01 2016 +0200 - - Fix for USB virtual serial port in sketches of CurieI2S library - - Since Arduino/Genuino 101 uses USB native port, wait for the Serial port - to open before executing the next lines of code to not lose serial data - already sent to the Serial monitor - - Signed-off-by: Biagio Montaruli - -commit e084140957ebe2aa0d891c496ced456538c9a1b9 -Author: Erik Nyquist -Date: Wed Sep 7 16:02:31 2016 -0700 - - stdlib_noniso.cpp: fix dtostrf() handling of integral portion - - Checking only for > 10 here (rather than >= 10) means that numbers with a - leading '10' in the integral portion generate incorrect strings. - -commit b329d080469778b7e9c8e0868ab6edba4a7fdf97 -Author: russmcinnis -Date: Tue Aug 30 15:56:08 2016 -0700 - - add comment to pulseIn() in the header file to warn programmer about trying to detect high frequencies. - -commit 10d1056503099431cda97deacabbb2a68f43121e -Author: Brian Baltz -Date: Thu Aug 25 12:47:00 2016 -0700 - - Verify BLE FW version on every sketch upload - - Signed-off-by: Brian Baltz - -commit 431fb6fd27bdfbd6aaa97e690f1b162a1e02cb62 -Author: sys_maker -Date: Mon Aug 22 12:35:27 2016 -0700 - - Rebuilding libarc32 library - - Signed-off-by: sys_maker - -commit 0d86ead0d4ae870c32c679fca3ef087c83a758fc -Author: Calvin Park -Date: Thu Dec 3 11:34:56 2015 -0800 - - Add flashpack to IDE - -commit 71cf8e44b3d9c28918084c0e5a6c2d1e239b1c98 -Author: Brian Baltz -Date: Wed Aug 10 13:53:03 2016 -0600 - - JIRA-668: Removing Intel version of Adafruit_NeoPixel - - Signed-off-by: Brian Baltz - -commit e4a5b14c51e70933ff3f85603a08c3b6970ec264 -Author: Adrien Descamps -Date: Sat Aug 6 16:27:35 2016 +0200 - - Fix UART receiver corruption - - When the UART receiver is disconnected for a time shorter than a line break - and longer than a start bit, it interpret that as a received packet, and if - data are transmitted immediatly after, they are corrupted. - This commit is a workarround for that bug, that enable the loopback feature - of the UART when UART is disconnected, so the UART module does not notice - it is disconnected. - A previous workarround for this bug (a forced delay after disconnection) is - not needed anymore and is removed. - -commit 7366ed0a4fd26faec64e11b530557c313069b4f7 -Author: Adrien Descamps -Date: Wed Aug 3 01:40:57 2016 +0200 - - Flush wait for transmission to be complete - - In previous version, flush wait for the transmission to be complete - by checking if the transmission holding register (or fifo) is empty. - When this happen, the last byte is still in the shift register, - and has still to be transmitted. - This mean we can't know when the transmission is actually complete, - which is crucial to implement RS485 communication, for example. - - This commit change this behaviour and wait for the shift register to be empty, - which means the transmission is really complete. - -commit 48b0e58306d227b289316fcb4143edb08570042c -Author: Calvin Sangbin Park -Date: Wed Aug 3 14:16:23 2016 -0700 - - Update libarc to the latest - -commit 9ae91d3e9d55d81358681dd1595880de1176d640 -Author: unknown -Date: Tue Aug 2 09:49:30 2016 -0600 - - Jira-556: Add comments to I2S examples - -commit b317284ec9312288dea4e2b5db5696821fa6237f -Author: Calvin Sangbin Park -Date: Thu Jul 28 10:44:44 2016 -0700 - - Update libarc to the latest - -commit 9a871b671ceec5f625c63c9a9e58f044cef2e70c -Author: Erik Nyquist -Date: Wed Jul 27 14:20:29 2016 -0700 - - Add delay to Serial1.end() allow GPIO lines to settle - - Through testing, it was discovered that if Serial1.begin() is called again - immediately after Serial1.end(), then any attempt to write data immediately - following the begin() call could result in corrupted frames. - - After calling Serial1.end(), it will be at least 300us before those pins can - be used as UART again. - -commit e3d90c19d96bbab006f59151b73179ab0c7d9097 -Author: Caihong Ma -Date: Tue Jul 26 01:34:39 2016 +0800 - - CurieI2SDMA: Fix typo and add explanatory notes - CurieI2SDMA.h: Fix typo at line 23 and 24; I2SDMA_RXCallBack.ino: add - explanatory notes to explain how to check the verification of data - -commit fbb7b02a89da37ac9408710ff5ed1a936d00cd8b -Author: Xie,Qi -Date: Tue Jul 19 09:13:39 2016 +0800 - - JIRA-640 I2C operations are sometimes timing out causing a long delay in between i2C operations - - fix this issue by call complete callback if no stop command issue at the end - of transfer. - -commit 160751a8c47f1d4cdd647ad1bf0d8eb6312d5791 -Author: Erik Nyquist -Date: Fri Jul 22 18:21:27 2016 -0700 - - README.md: add "commit message how-to" wiki link - -commit 197434001a973ad7a8df18b823f8365988140b43 -Author: Erik Nyquist -Date: Sun Jul 17 00:33:17 2016 -0700 - - Issue #149: Provide scaled IMU data - - Add implementations of readAccelerometer and readGyro which - return values that are scaled according to the range set with - setAccelerometerRange and setGyroRange - -commit 5671917298c35e6164784da6929cf006daf9d51d -Author: caihongm -Date: Fri May 27 02:17:19 2016 +0800 - - Jira509:add IMU examples:FreeFallDetection, MotionDetection,ZeroMotionDetection; change CurieIMU.cpp line490 from 'setZeroMotionDetectionThreshold' to 'setZeroMtionDetectionDuration' - -commit 12b2376ea58c79ef00ea47af2aea8796bb652d69 -Author: Erik Nyquist -Date: Wed Jul 20 15:23:23 2016 -0700 - - ATLEDGE-643: Add chip ID to SerialFlash example sketch - - RawHardwareTest reports the 101's on-board SPI flash chip as an unknown - device. Fix this by adding the device's JEDEC ID to the example sketch. - -commit b8162e993cccbe4065c1bd55d1f4466669f01f75 -Author: Caihong Ma -Date: Mon Jul 4 15:15:54 2016 +0800 - - Jira566: I2S DMA implement: supplement library and example - -commit c59e2808e1bf27e8adc7c3aff0d0ed1a8344e04f -Author: Erik Nyquist -Date: Fri Jul 15 15:39:16 2016 -0700 - - Issue #241: make Serial1 release GPIO pins on end() - - UARTClass::init() muxes out the UART pins to Arduino header pins 0/1. - Reset the mux on UARTClass::end() so that pins 0/1 can be used as GPIOs - again. - -commit bdaec192070ab196a55282fa57512748c6f1edd9 -Author: Erik Nyquist -Date: Mon Jul 11 11:21:34 2016 -0700 - - Add CurieIMUClass::end() - - Add end() method to IMU class, to disable clocking to the SPI - controller. This is unlikely to have noticeable power-saving effects, - however it is a good habit to get into. - -commit 2ec9b94f941d34ee1419311b194707aad3b0ee67 -Author: Dino Tinitigan -Date: Mon Jul 11 14:47:59 2016 -0700 - - Update Wire examples to use Serial instead of Serial1 - - -change Serial to Serial1 in the master_writer and master_reader - examples - -commit 5f3ee5bb36dc5e69ce26f7d2a64220ebc927ad83 -Author: Martino Facchin -Date: Tue Jul 12 10:22:12 2016 +0200 - - define digitalPinToInterrupt for backwards compatibility - - see http://forum.arduino.cc/index.php?topic=370167.0 - -commit ecee2e9483e39af71df873174df070fdf952c431 -Author: Caihong Ma -Date: Mon Jul 4 15:15:54 2016 +0800 - - I2S DMA - supplement library and examples - -commit 3168ede2cbd3df99121944d46a5b24e9557d2b4b -Author: erik.nyquist -Date: Fri Jun 24 13:08:25 2016 -0700 - - ATLEDGE-619: fix dtostrf - - Use a different method for float->string conversion, and - fix the width calculation so that extra spaces are only - printed if the width parameter exceeds the output string - length (ie. a mininum width, as specified by dtostrf - reference from avr-libc). - -commit 019bb8fcecc1c79d78bd58f18f1b4521e73fad35 -Author: Dino Tinitigan -Date: Fri Jun 24 14:58:48 2016 -0700 - - update libarc32 - - -update libarc32 binary - -commit 9be7604bec5f080683a236cbabfe0d70b174ac13 -Author: Dino Tinitigan -Date: Wed Apr 20 14:52:22 2016 -0700 - - I2C - add functionality to change i2c clock speed - - -adds functionality to change clock speed - -clock speed is set/changed when Wire.begin() is called - -default is standard mode - -use Wire.begin() for standard speed (100k) - -use Wire.begin(I2C_SPEED_FAST) for full speed (400k) - -use Wire.setClock(I2C_SPEED_FAST) for full speed (400k) - -commit 8682149177559757a57ae0b2ad6639dcf97e31b1 -Author: Dino Tinitigan -Date: Fri Jun 24 14:33:45 2016 -0700 - - reduce i2c delays - - -reduce i2c delays to increase performace without changing the timeout - threshold - -commit de0650c15a0d44284440e4c4ba1943639b73da4f -Author: erik.nyquist -Date: Tue Jun 28 15:21:32 2016 -0700 - - Add -std=gnu11 to compiler.c.flags in platform.txt - - Related to issue #221 - -commit 4d9642871ae0bfb28783b1d6fa00b364ea201cf1 -Author: Oren Levy -Date: Mon Jun 20 14:59:09 2016 -0700 - - Added MIDI examples - -commit 2cd33f43a1b84c3f23194b27290114c4cec5d9df -Author: Martino Facchin -Date: Thu Jun 16 11:29:06 2016 +0200 - - Correct core version in platform.txt - -commit 20844ff46b455055f86641c785fb580ae1214222 -Author: Mike Duong -Date: Wed May 25 18:32:05 2016 -0700 - - ATLEDGE-586: String.compareTo() does not compute the same as Uno for POS values - -commit 2f5ae85f235bc1e5a0e5b50d63961f4348f237a0 -Author: erik.nyquist -Date: Mon Jun 13 10:35:53 2016 -0700 - - ATLEDGE-572: Fix getStepDetectionMode() - - This function is reading all 8 bits of RA_STEP_CONF_1 (0x7A-0x7B) - to obtain a value for min_step_buf, though this value is only - represented by the lower 3 bits; bit 4 is used as the 'enable' bit. - As a consequence, this function will read the step detection mode - incorrectly when step counting is enabled. Fix this by only reading - the lower 3 bits of RA_STEP_CONF_1. - -commit de30a61b909802f2dbb3380c4adc9316ee1f64f7 -Author: erik.nyquist -Date: Mon Jun 13 11:57:21 2016 -0700 - - ATLEDGE-553 Modify library examples header info - -commit a694d09698db8d0f77ac19d58d1db34eef3d1b4d -Author: Sidney Leung -Date: Tue Jun 7 16:57:12 2016 -0700 - - Jira-617: String Object constructor hangs up when handling large floating point number - -commit df3b675c1ba4113fa560078ae60f610f4d8b3ae0 -Author: Erik Nyquist -Date: Fri Jun 3 15:02:00 2016 -0700 - - SerialFlash: enlarge chip ID buffer - - SerialFlashChip::readID() writes up to 5 bytes into - the buffer provided, but the caller only allocates - 3 bytes. Increase buffer size to 5 bytes. - -commit c27462cbc168257c2c928d435dcaf3b43a535fe1 -Author: Xie,Qi -Date: Wed May 25 07:30:23 2016 +0800 - - JIRA-560 I2C Bus Scan - using read instead of write to avoid sending the extra byte when scaning I2C bus - -commit e792ba2bd6480a556e50259e0e836ae4cf5ec371 -Author: Dino Tinitigan -Date: Tue May 31 17:03:06 2016 -0700 - - Fix bug when using StringConstructor with doubles - - -Fixes issue of StringContructor instability with doubles - -commit acc4b38ba43d654992b72fa9d4afa68b834a098c -Author: Dino Tinitigan -Date: Tue May 31 16:12:32 2016 -0700 - - Revert "Fix some issues with printing float and double" - - This reverts commit b8ea343bb237762de6ee3a60ba0b1e8d5af12ff0. - -commit 409e4b07f477e6345c972a40376085c89ecdb46e -Author: erik.nyquist -Date: Fri May 27 14:38:05 2016 -0700 - - Remove unsupported SerialFlash example - - CopyFromSD.ino requires SDfatlib (for AVR boards) which - is unsupported. - -commit 4294ca5ad86c2f7f5cfb354ac193ff1ecbfc9fa9 -Author: erik.nyquist -Date: Fri May 27 14:36:36 2016 -0700 - - Remove SPI.set* calls from SerialFlash example - - These functions are not implemented for A101's SPI library. - Remove these calls from examples/CopyFromSerial.ino - -commit f33e362db41f7871a63bc8f0294ce9aa43be3f0d -Author: erik.nyquist -Date: Fri May 27 14:26:46 2016 -0700 - - Add category to Wire/library.properties - -commit 348e36ee6e6eb2c5f5687b642f978aabcace5be2 -Author: Sidney Leung -Date: Thu May 26 15:54:08 2016 -0700 - - Jira-602: Convert float and double to strings. Output string is padded with spaces. - -commit ce7825883b79bd6849cc1fba4bbaa6416e43229d -Author: caihongm -Date: Fri May 27 02:17:19 2016 +0800 - - Jira509:add IMU examples:FreeFallDetection, MotionDetection,ZeroMotionDetection; change CurieIMU.cpp line490 from 'setZeroMotionDetectionThreshold' to 'setZeroMtionDetectionDuration' - -commit b8ea343bb237762de6ee3a60ba0b1e8d5af12ff0 -Author: Dino Tinitigan -Date: Thu May 26 13:51:06 2016 -0700 - - Fix some issues with printing float and double - - -fixes some issues with printing float and double - -limits the number of decimal places printable to 16 - -commit 9472c9aa9427030f3e24968d4915fdf69881ea66 -Author: Dino Tinitigan -Date: Wed May 25 15:43:28 2016 -0700 - - Remove unnecessary #undefs - - -Remove #undefs from CurieSoftwareSerial Library - -Because it was braking some functionality such abs() - -commit 048800b47b8edcbe0cbf9401163ace1f4c15bb3e -Author: Sidney Leung -Date: Thu May 19 11:36:38 2016 -0700 - - Jira-599. Routine, dtostrf, did not work with the latest toolchain. The call to sprintf in the routine crashed the system. Issue corrected by replacing the call. The routine, printfloat, in Print.cpp, is now usint dtostrf to print out double and float values. - -commit 874c3ef08bf7031a06248abbb59274da740444f2 -Author: erik.nyquist -Date: Fri May 20 10:38:55 2016 -0700 - - SPI: add SPI1 to keywords.txt - -commit 92818058e83302e478e7cdddd60d1d5568ce9bc5 -Author: erik.nyquist -Date: Thu May 19 10:34:30 2016 -0700 - - Add Paul Stoffregen's SerialFlash library - -commit 8dea7b4a1430dce33870a97fcf0330e0cda4fef3 -Author: erik.nyquist -Date: Mon May 16 10:41:02 2016 -0700 - - Fix SPI clock divider calculation - - If a clock value greater than the maximum supported - is passed (i.e. > 16MHz), the resulting value written - to the BAUDR register will be 0 which will disable - the serial output clock. Check for this and ensure - the value calculated for BAUDR register is never < 2. - -commit 331b2f0152bb90f1c96ee1726e8017db56e51789 -Author: erik.nyquist -Date: Thu May 12 11:18:35 2016 -0700 - - Clean up the SPI files - - Be consistent about commenting style, and - stay below 80 chars where appropriate. - -commit 2bb742afd2e8cf0a3708b3b50a94089b5b39a3a8 -Author: erik.nyquist -Date: Thu May 12 11:15:04 2016 -0700 - - Add support for SPI1 device to SPIClass - - SPI1 can now be used (SPI1.begin()) to access - the SPI device connected to the Arduino 101's - on-board SPI flash device - -commit 2bea587379b0e78d4006db13e4c9bdf4482637c0 -Author: Brian Baltz -Date: Mon Apr 25 15:36:38 2016 -0600 - - JIRA-550 Retain unstripped ELF file for debugging - - Signed-off-by: Brian Baltz - -commit ea7a804cefdf17a7518ba8c3fec95bb744273d0c -Merge: 2b5a040 9f88024 -Author: Brian Baltz -Date: Wed May 11 14:09:23 2016 -0600 - - Merge pull request #172 from bbaltz505/new_toolchain - - Updating compile command and driver lib for new toolchain - -commit 2b5a0402af9486f527805124a5ef937cbfb89ee1 -Author: Sidney Leung -Date: Fri May 6 16:39:36 2016 -0700 - - Bug fixed: CurieBle library, class BLECharacteristic allocates memory using an incorrect variable which may result in memory corruption if the memory size is bigger than 16 bytes. - -commit cdd2989b4297c10c8759de408ccb021f0874763a -Author: jysholar -Date: Mon May 9 12:48:48 2016 -0600 - - CurieIMU tap/double example, JIRA-508 - -commit 434fcd49ddcae18726b8196153baf0a68882b55e -Author: erik.nyquist -Date: Tue May 10 11:41:19 2016 -0700 - - README.md: clean up and add "support" section - - Break long lines to be <= 80 chars, where possible, and - add a new section to guide users to the appropriate place - for support (Arduino forum for product support, issue - tracker for bugs & features). - -commit eb0cf4f1ceaa0fc4220adcc726063e17934eee3e -Author: Martino Facchin -Date: Wed May 4 10:28:22 2016 +0200 - - Add SERIAL_PORT_USBVIRTUAL and SerialUSB macros - - This is needed to ensure compatibility with YunShield sketches - - asas - -commit 9f880240e6cb1deba4c8bd3f296dff4eb5a54929 -Author: Brian Baltz -Date: Thu Apr 28 15:33:58 2016 -0700 - - Updating compile command and driver lib for new toolchain - - Signed-off-by: Brian Baltz - -commit 2bac6d3432033197ffc23c6da96ddd99054271f9 -Author: Calvin Park -Date: Mon May 2 15:59:10 2016 -0700 - - Fixed the license file to LGPLv2.1 - -commit 443916f2621a808540997f36caa9bcd2c9d7fb5f -Merge: f43789e c1be134 -Author: Calvin Park -Date: Mon May 2 14:54:12 2016 -0700 - - Merge pull request #156 from bigdinotech/i2slib - - CurieI2S library - -commit f43789e71bcc9c7d8181aec61c48eed3a9683cba -Author: jysholar -Date: Tue Apr 26 12:25:01 2016 -0600 - - Jira-560, getInterruptStatus(...) should return type of bool - -commit 212dae55ea664d1bd92ad175615a49bec7d1bc4f -Author: Sandeep Mistry -Date: Tue Apr 5 09:50:36 2016 -0400 - - Sync avr/pgmspace.h entries with SAMD core - -commit 7bd93b3baf802481282e32240fcfc1f941e36561 -Author: Sidney Leung -Date: Thu Apr 14 15:46:58 2016 -0700 - - Jira-584: String constructor create floating point string with incorrect number of decimal places. - -commit 4d1014ff2b89a88f9605e2cb7f75bbe7bd0691e4 -Author: russmcinnis -Date: Wed Apr 13 11:10:56 2016 -0600 - - check pin range first thing in pinMode(), digitalWrite() and digitalRead() - - Signed-off-by: russmcinnis - -commit 285ec1770d831f584ca943f2e4d214ad59e35ffe -Author: Brian Baltz -Date: Tue Apr 12 10:56:38 2016 -0600 - - Removing obsolete TFT library - - Signed-off-by: Brian Baltz - -commit e9aaea9b39eb1496ebccdef422ae2d441f9086f4 -Author: Brian Baltz -Date: Fri Apr 8 15:11:42 2016 -0600 - - Fix CurieTimerOne example filename - - Signed-off-by: Brian Baltz - -commit 4cad8b8e31d7e8f13b394e90ffa2253ffb0d5f9b -Author: russmcinnis -Date: Wed Apr 6 13:30:42 2016 -0600 - - pwmStop() leaves pin HIGH - - pwmStop() was doing a digitalWrite(pwmPin, LOW) after the kill() so unless the pin happened to be 0 the pwmPin might be left in the HIGH state causing motors or other devices to run HIGH after pwmStop(). Just removed the pwmPin = 0 from kill() and it passes all our loopback tests. - -commit 0e4a0d439341781ef6709b73d45a77c79cdf0de7 -Merge: 669fb9b 0ad15b5 -Author: Brian Baltz -Date: Tue Apr 5 09:23:41 2016 -0600 - - Merge pull request #146 from bneedhamia/support-eddystone-url - - Added Eddystone-URL support - -commit 669fb9bcf6e80d774a8a9d836b3354c5ddd04a99 -Author: Brian Baltz -Date: Mon Apr 4 15:55:55 2016 -0600 - - JIRA-558 Remove Intel Ethernet and SD libraries - - Signed-off-by: Brian Baltz - -commit c1be134976c7afa8fd3af95907e0f043efc6bc8d -Author: Dino Tinitigan -Date: Mon Apr 4 10:17:29 2016 -0700 - - CurieI2S - minor fixes/improvements - - -prevent buffer overflow in I2S_RxCallback example - -calculate delay for any sample rate - -commit 0ad15b5a46969ddda239cf764caebb32a81e56ae -Author: Bradford H. Needham -Date: Sun Apr 3 11:07:14 2016 -0700 - - Fixed spaces in keywords.txt; keywords now highlight properly - -commit 89dd2287dfd41a12db0a34eed7d94b8593b2e5da -Author: Dino Tinitigan -Date: Fri Apr 1 12:06:36 2016 -0700 - - CurieI2S add examples - - -add basic examples using callback functions/interrupts - -add callback function to fill tx buffer - -commit 2bca6fc912b51200ad12b833e52321e65ccf553b -Author: Dino Tinitigan -Date: Wed Mar 30 12:18:36 2016 -0700 - - Interrupt based I2S library - - -CurieI2S using interrupts - -commit a54f5132630fb2825e27d84cf4ddf3d8ee22192a -Author: Sidney Leung -Date: Thu Mar 31 11:50:12 2016 -0700 - - Jira-575, Jira-506: Bug fixes to print float and double correctly, in a string or std output. - -commit ccc02128ad2371d3ed35ebb3a01d4146df930e97 -Author: Dino Tinitigan -Date: Wed Mar 23 18:07:38 2016 -0700 - - CurieI2S use interrupts for Tx - - -Use interrupts to fill FIFO buffer from TX buffer - -commit 4fae4ddb4cc9bb14c6c4ccc6147253944c778bb9 -Author: Dino Tinitigan -Date: Tue Mar 8 15:01:49 2016 -0800 - - CurieI2S initial commit - - - Minimal functional version - -commit 2eb99d10a116a092a2ade78a79d96d164847f9b2 -Author: agdl -Date: Fri Mar 11 13:58:24 2016 +0100 - - Removed delay from examples as requested by Tom - -commit 38a508e4552f88649cdd664babd0165673b7caa6 -Author: Sidney Leung -Date: Tue Mar 8 13:31:14 2016 -0800 - - Jira-528. Make CurieTimerOne library to be compatible with the TimerOne library originated from Paul. Created methods that have the same name and functionalities as in the TimerOne library. - -commit 98ee736fa2bb12e99d80bad866a9a298e0bc1c2e -Author: SimonePDA -Date: Mon Mar 14 18:01:40 2016 +0100 - - Update CurieTimer1Interrupt.ino - - Changed the style of the code according to Arduino's Tutorials guidelines. - -commit 40d3fb07d98db264dc1f2b0c2b5a3057359a721d -Author: Bradford H. Needham -Date: Tue Mar 8 20:35:51 2016 -0800 - - Added Eddystone-URL support: added setAdvertisedServiceData() to support BLE Service Data; changed Incomplete to Complete Service UUID list code in Advertising initialization, required by Eddystone-URL protocol; added (for debugging) getAdvertisingLength() and getAdvertising() to enable a Sketch viewing the Advertizing packet. - -commit 17eac79d76b721eb116179aa87f0e3627add4927 -Author: Dino Tinitigan -Date: Tue Mar 8 10:54:11 2016 -0800 - - Fix Klockwork issues in CurieEEPROM #142 - - -Also fixes issue of not being able to rewrite the last 3/4 of the - EEPROM area - -commit e50ef904347f9d4da0a7c4e0e5523b4ff5d040bb -Merge: 1f3899a c7b30f4 -Author: Brian Baltz -Date: Tue Mar 8 11:51:38 2016 -0700 - - Merge pull request #141 from bbaltz505/memory_size - - Fixing sketch size calculation (text + ctors + rodata + datas - -commit c7b30f4c627ad20b5cdbc621803afa76fba61bfd -Author: Brian Baltz -Date: Tue Mar 8 07:07:43 2016 -0800 - - Decrease max sketch size to 155,648 bytes - - Signed-off-by: Brian Baltz - -commit f02410e9bf138f48243b62853b44c9b797ea01e9 -Author: Brian Baltz -Date: Wed Feb 24 20:04:14 2016 -0800 - - Fixing sketch size calculation (text + ctors + rodata) - - Signed-off-by: Brian Baltz - -commit 1f3899a7cabd71ae9fbda7f721a117614587adaa -Author: SimonePDA -Date: Thu Mar 3 22:29:38 2016 +0100 - - Update library.properties - -commit c4a20140569016649b4333ef6c349c8041827164 -Author: SimonePDA -Date: Thu Mar 3 21:55:23 2016 +0100 - - Update library.properties - -commit 72b85b2bdb884a995620e3aeb66d518844b0746a -Author: SimonePDA -Date: Thu Mar 3 21:39:55 2016 +0100 - - Update library.properties - -commit 3d019ed2cebf9034e37cda961d16b08042d8f731 -Author: SimonePDA -Date: Thu Mar 3 21:34:23 2016 +0100 - - Update library.properties - -commit 5bb196cdd961a0fb54129d1fcf2fc99778ab0914 -Author: SimonePDA -Date: Thu Mar 3 19:58:12 2016 +0100 - - Update library.properties - -commit fda58303cb959000bb8d04855f5b11838423d15d -Author: SimonePDA -Date: Thu Mar 3 19:35:08 2016 +0100 - - Update library.properties - -commit 7905133b686bf5dcc0112b828a9d8128ddf6ee8e -Author: SimonePDA -Date: Thu Mar 3 19:24:32 2016 +0100 - - Update library.properties - -commit bc9c387332dc16290f1c1c180eabae0d33eec8fe -Author: SimonePDA -Date: Thu Mar 3 19:12:32 2016 +0100 - - Update library.properties - -commit 953479ae0f37961fb8f4040fbdcd8848f59cae33 -Merge: 3bd268c eacb1bd -Author: Brian Baltz -Date: Wed Mar 2 15:06:03 2016 -0700 - - Merge pull request #129 from 00alis/src_folders - - Moved .h and .cpp file in src folder - -commit 3bd268c3992f095233bb16a18dac8541b86b0bf2 -Author: Brian Baltz -Date: Wed Mar 2 10:40:59 2016 -0800 - - Updating install steps in README - - Signed-off-by: Brian Baltz - -commit eacb1bd114ec57cbecab357dfab8a396b5e7b09d -Author: Alice Pintus -Date: Wed Mar 2 12:49:10 2016 +0100 - - Rename library.properties - -commit 1cbd80b226b237ddb5a2cd5705f63975ed6f9596 -Author: Alice Pintus -Date: Wed Mar 2 11:56:24 2016 +0100 - - Moved .h and .cpp file in src folder - -commit e8b8a5db546696735d5de38e58253b75cb411e7a -Author: Sandeep Mistry -Date: Fri Feb 26 15:05:54 2016 -0500 - - Change set/get accelerometer and gyro offsets to use floats values - - This makes it consistent with other API changes to convert the register - values to floats and vice versa. - -commit ffc51eff1581f38c977798114f94affc5393d0e2 -Author: Sandeep Mistry -Date: Tue Feb 2 10:08:35 2016 -0500 - - Add setConnectionInterval API to CurieBle - -commit f11de80d789e9071a05387cfa96d80e08db563cc -Author: Martino Facchin -Date: Tue Feb 23 15:06:09 2016 +0100 - - use sprintf-based implementation for dtostrf - - altough the current implementation passed all the unit tests, an user reported problems with the actual code (http://forum.arduino.cc/index.php?topic=380422.0). - If libc has float support there is no need to do strange things like on AVR, so we can use the sprintf implementation - -commit ad1586ef179fa0920edb29d4e3a05307a267a3cd -Author: Calvin Park -Date: Mon Feb 29 15:14:32 2016 -0800 - - Updated README.md with instructions to manually install corelibs - -commit 582d03353b5111b8030ec5a6954ec480acaaa6d3 -Author: Dino Tinitigan -Date: Fri Feb 26 11:04:59 2016 -0800 - - i2c bus scan workaround - - -sends a byte of value 0 when doing an i2c bus scan - -commit f6d8aa81847643f1102805e9b1f6ef32c3fbc0a9 -Author: Dino Tinitigan -Date: Mon Feb 22 14:08:34 2016 -0800 - - CurieEEPROM - ensure 32-bit allignment - - -ensure 32-bit address allignment for put(), get(), read(), and write() - -commit 64ee87970db858c1e4caed63e41f21158f6653b7 -Author: Brian Baltz -Date: Wed Feb 24 17:45:23 2016 -0800 - - Remove custom Adafruit_Motor_Shield_V2 library - - Signed-off-by: Brian Baltz - -commit 6f9739fac258c812f74139c16017817aa4045ace -Author: Brian Baltz -Date: Thu Feb 18 17:16:09 2016 -0700 - - Use Go-based upload script - - Signed-off-by: Brian Baltz - -commit 83bb5f5d818a3ed2db05a0b784c6888822d3e2e7 -Author: jysholar -Date: Tue Feb 23 10:33:17 2016 -0700 - - Rename CurieTimer to CurieTimerOne for JIRA-532 - -commit 3448d230240682d1579b26e6f095afe479d68a09 -Author: Sandeep Mistry -Date: Wed Feb 17 08:37:19 2016 -0500 - - Remove enums for ranges and duration, auto calibrate/set offset now enables offset, rename interrupt routines to interrupts/noInterrupts - - keywords.txt updates - -commit 63a01dd918a1fd16d7a914dfd312c763699abdde -Author: Sandeep Mistry -Date: Tue Feb 2 11:40:38 2016 -0500 - - Rename CurieBle to CurieBLE - -commit e82fe904dba101da79bbd7dd63060f223d8716cc -Author: Sidney Leung -Date: Fri Feb 5 11:55:00 2016 -0800 - - Jira-507 and 514: 1. Bug corrected in timer ISR where the clearing of the pending interrupt was at the entrance instead of exit. When interrupts could happen fast enough to put the ISR in a loop. 2. The overhead for the s/w pwm code is 4-5 usec. Thus, limited the shortest pwm pulse to 10 usec (high and low). - -commit 2299da2693c0fe867c7b90d998b43c15d666e763 -Author: Brian Baltz -Date: Wed Feb 10 07:47:38 2016 -0800 - - Stop interrupt when switching to 100% duty cycle - - Signed-off-by: Brian Baltz - -commit da68b6fcd5363a5b771e02ec7fa4ab1774286ebe -Author: Brian Baltz -Date: Wed Feb 3 12:10:41 2016 -0800 - - ATLEDGE-516 Stop callback when setting duty cycle to 0 - - Signed-off-by: Brian Baltz - -commit ef62ce6bff2c6059e1f0750c8fa2ec9af424f712 -Author: Brian Baltz -Date: Wed Feb 3 12:09:33 2016 -0800 - - Fix pwmStop typo in keywords.txt - - Signed-off-by: Brian Baltz - -commit 6059a142b0bed4bd3b1a2e2c45a298bc72ee6754 -Author: agdl -Date: Tue Feb 2 15:13:42 2016 +0100 - - Fixed define - - To be compliant with all the others - -commit 65f3b2887bfc3dfe228a3216a4c8b4a4e51dcfac -Author: Dino Tinitigan -Date: Tue Feb 2 11:32:00 2016 -0800 - - CurieEEPROM eeprom_write example - - -increment eeprom address at the correct location so it matches the read - example - -commit 6885cded61c1dac1e45d4a33436da6c840994cc4 -Author: Dino Tinitigan -Date: Wed Jan 27 14:39:40 2016 -0800 - - CurieEEPROM reimplement put and get to not use STL - - -reimplement put() and get() to not use STL due - to incompatibility with max() and min() macros - -commit 0a382340a0487691627ad3af48bce01572029b4b -Author: Krzysztof Sywula -Date: Wed Jan 27 15:06:53 2016 -0800 - - Green pixel fix - - JIRA: ATLEDGE-470 - -commit a6381cb123d77d3da630eda19c5d2947c1e129c6 -Merge: 07a53ab 555c23b -Author: Brian Baltz -Date: Tue Jan 26 17:05:08 2016 -0700 - - Merge pull request #109 from SidLeung/master - - Jira-501: CurieTimer.resume() did not re-enable interrupt at the controller. - -commit 555c23bc7cabc7217818b18a267a490d01fa63ad -Author: Sidney Leung -Date: Tue Jan 26 15:46:33 2016 -0800 - - Jira-501: CurieTimer.resume() did not re-enable interrupt at the controller. - -commit 07a53ab9f5174153967e1ebdac7527b680ec6232 -Author: Dino Tinitigan -Date: Tue Jan 26 14:13:15 2016 -0800 - - CurieSoftwareSerial remove non-working example - - -Remove non-working TwoPortReceive.ino example - -commit 0eca3b32f09a30c0d146f5730d29f91527836108 -Author: Sidney Leung -Date: Fri Jan 22 15:07:03 2016 -0800 - - Jira-500. Corrected typo error. Routine, pwdStop, is now named as, pwmStop. - -commit 02c960487caaaea5e77ca2370af73b59064f442a -Merge: 8a517b6 1be5b6c -Author: Calvin Park -Date: Thu Jan 21 14:03:13 2016 -0800 - - Merge pull request #103 from pricopb/master - - Remove OneWire and Adafruit_DotStar libraries. - -commit 8a517b656072e22b9394ec58b6ee22e179a39789 -Author: Dino Tinitigan -Date: Wed Jan 20 15:08:42 2016 -0800 - - CurieSoftwareSerial timing issue fixes - - -fix some timing issues with slower baud rates - -commit aaa453d00cf6e06f36dbe5c9012b252ebbcc9170 -Author: Brian Baltz -Date: Tue Jan 19 14:17:18 2016 -0800 - - ATLEDGE-491 - rename sketchUploader tool to arduino101load - - Signed-off-by: Brian Baltz - -commit 4932616c873d7b06af99698214a718f572a404f6 -Author: Dino Tinitigan -Date: Tue Jan 19 15:53:12 2016 -0800 - - CurieEEPROM fixes - - -remove leftover debug prints - -put() and get() should only support 32-bit alligned addresses - -commit effd880b1cae45f923fabcd8582e86544288ed26 -Author: Dino Tinitigan -Date: Thu Jan 7 15:17:40 2016 -0800 - - CurieSoftwareSerial Improvements - - -changed global variables into static - -allow different baud rates for different instances of a SoftwareSerial object - -minor code cleanup of unused code - -moved source code into src directory - -commit 1be5b6cb91fa5f6de5749fa1268f03606f6a83f9 -Author: Bogdan Pricop -Date: Fri Jan 15 08:32:32 2016 +0000 - - Remove OneWire library. - - Remove the OneWire library because the code changes for porting it to - Arduino101 were pushed to Paul Stroffegen's repository: - https://github.com/PaulStoffregen/OneWire/commit/e21914433ec588a23922099fa4435fbe0493e5ab - - Signed-off-by: Bogdan Pricop - -commit 1253ea89e0d81b943a1e22aad79559913ea2e297 -Author: Bogdan Pricop -Date: Fri Jan 15 07:38:11 2016 +0000 - - Remove Adafruit_DotStar library. - - Arduino101 specific changes moved to Adafruit github repository: - https://github.com/adafruit/Adafruit_DotStar/commit/0fce0fe52956a7eaf8f29ecfbaecc4138e958ed5 - - Signed-off-by: Bogdan Pricop - -commit ef9d032bf468a09a6921ce4334aa50c1ac227e9c -Author: sys_maker -Date: Fri Jan 8 15:56:10 2016 -0800 - - Rebuilding libarc32 library and hard-coding tools version - - Signed-off-by: sys_maker - -commit cc7cccc85afe0fe9c558913305c0ffe86b213caf -Author: Kevin Moloney -Date: Fri Jan 8 13:27:59 2016 +0000 - - find() missing overload in Stream.h - - Signed-off-by: Kevin Moloney - -commit b0531afe05cb8e5f301f04e9a207b7fcbb00c93b -Author: Alice Pintus -Date: Fri Jan 8 11:41:36 2016 +0100 - - Update boards.txt - - Change name Arduino 101 into Arduino/Genuino 101 to be consistent with the rest of the ecosystem - -commit 3ca20d94b362264b0e9ddac74c3731c1ab26a467 -Author: Dino Tinitigan -Date: Tue Jan 5 14:06:21 2016 -0800 - - ATLEDGE-487 CurieEEPROM library - - -Initial commit for CurieEEPROM library - -Implements EEPROM functionality using an available area of the ROM - -supports both BYTE and DWORD reading/writing - -commit b19562438ca48b50a4e505d786d522900d9300fd -Merge: 23e6f16 6fc749d -Author: Calvin Park -Date: Wed Jan 6 15:45:42 2016 -0800 - - Merge pull request #90 from sandeepmistry/imu-api - - Add new API's and examples to CurieIMU - -commit 23e6f16a539bfc4b3fef5b76e1787a66d3966a76 -Author: Sidney Leung -Date: Mon Dec 21 12:12:18 2015 -0800 - - Jira-462: Call Available() after calling end() indicated data available. - - Code Modifications: - - 1. Added checking of device open in these routines: available, read, peek. - Will return 0 or -1 if device is not opened to indicate invalid operation. - -commit 46519997e46f5088d627e537086cab14e8e14e6d -Author: Krzysztof Sywula -Date: Tue Dec 22 16:55:54 2015 -0800 - - dtostrf: fix overflow problem - - JIRA: ATLEDGE-315 - -commit 6fc749d08dfe770992fe3f50d3073260960c98d9 -Author: Sandeep Mistry -Date: Mon Dec 21 13:46:04 2015 -0500 - - Remove calibration steps from accelerometer and gyro examples - -commit af28e53e5b3280f4188cc2c379bacf24cd454bb7 -Author: Sandeep Mistry -Date: Mon Dec 21 13:43:50 2015 -0500 - - Use int for accelerometer and gyro API's instead of short - -commit c84af94b39a1efbf3afee132ac49469935b0aa7e -Author: Sandeep Mistry -Date: Mon Dec 21 13:32:14 2015 -0500 - - Remove call to setIntEnabled, attachInterrupt enables interrupts - -commit 44fe3307074cb4f5648dbba5718f893b7a4098f4 -Author: Sandeep Mistry -Date: Mon Dec 21 12:10:45 2015 -0500 - - Rename CurieImu to CurieIMU - -commit 6557caf258f6d4cef6048036a6cdf15c4fa2a882 -Author: Sandeep Mistry -Date: Mon Dec 21 11:37:59 2015 -0500 - - Add accelerometer orientation example - -commit a50c6b649f093f6d5dbb19e9e376f700976b833e -Author: Sandeep Mistry -Date: Mon Dec 21 11:12:32 2015 -0500 - - Update example header - -commit 7e628f15227d24c81c4f42bfe1f3cb351bdb8f78 -Author: Sandeep Mistry -Date: Mon Dec 21 11:12:16 2015 -0500 - - Initial gyroscope example - -commit cfd445144984d9aaaf3995f257cc5db0e14f04af -Author: Sandeep Mistry -Date: Mon Dec 21 10:57:14 2015 -0500 - - Correct typo in example, az was printed instead of ax - -commit e8a13f7c205de09fa50e90946e9634ef0d6e1562 -Author: Sandeep Mistry -Date: Mon Dec 21 10:52:38 2015 -0500 - - Add accelerometer only example - -commit c45ece5a1392b6f0cc7cfdd202b11cc1f87e9d30 -Author: Sandeep Mistry -Date: Mon Dec 21 10:50:17 2015 -0500 - - Rename readAcceleration and readRotation API's to readAccelerometer and readGyro - -commit 162e0cfbbde7b3e7b8c63daa9444507c02917cbe -Author: Sidney Leung -Date: Tue Dec 15 11:12:47 2015 -0800 - - Jira-32 and Jira-386. CurieTimerOne lib - -commit 1e30828bac4a73ef240f1bf8df8aa5e1a2ff1ff3 -Author: Sandeep Mistry -Date: Fri Dec 18 17:46:47 2015 -0500 - - Use if statements instead of switch for a few API's - -commit f6d1516068c33085766dfa042fe843f69511277c -Author: Sandeep Mistry -Date: Fri Dec 18 17:26:19 2015 -0500 - - Sketch auto format - -commit 5e3605e67ef21d1b59fc5d3d7e7f034c674d6477 -Author: Sandeep Mistry -Date: Fri Dec 18 17:15:06 2015 -0500 - - Keyword updates from Helena - -commit ef3ccb5aebee42c2497631b8e7b77ecd5b5c2359 -Author: Sandeep Mistry -Date: Fri Dec 18 17:10:10 2015 -0500 - - Update example to use enums - -commit 0faeb636cda253ad2768af7db12df42b3c689aa0 -Author: Sandeep Mistry -Date: Fri Dec 18 17:09:13 2015 -0500 - - Add set/get gyro/accelerometer rate API's, and map more BMI160 enums - -commit 38f928fff483abfba10927610f889e022779f52a -Author: Sandeep Mistry -Date: Fri Dec 18 16:33:34 2015 -0500 - - Use enums for accelerometer and gyro ranges - -commit e5a1c21005d0f6049008e8856481c4642b5227ef -Author: Sandeep Mistry -Date: Fri Dec 18 15:34:39 2015 -0500 - - Example updates from Helena - -commit 0b57d5eea31457d9bef2bf4df76a8c1dcc0b92da -Author: Sandeep Mistry -Date: Fri Dec 18 15:34:02 2015 -0500 - - Add Arduino style API's for IMU, rename CurieImu to CurieIMU - -commit b5c27aef2d78d264733f1056a070ca49de407f2e -Author: Dino Tinitigan -Date: Tue Dec 15 16:11:48 2015 -0800 - - ATLEDGE-429 CurieSoftwareSerial fixes - - -Creating a new CurieSoftwareSerial object no longer break functionality - of previous ones. - -Minor Cleanup - -commit f9d1d5c5c26d615f08775d9bffd8bd58b5fd75a1 -Author: Martino Facchin -Date: Thu Dec 17 15:58:56 2015 +0100 - - hardcode sketchUploader version - - while we solve the bug IDE-side, it is safe to revert using the hardcoded version number to handle the sketchUploader installation; - This solves a clash with outdeted Galileo/Edison core which tools are picked randomly (depending on json download order and java filesystem library internal ordering). - -commit 520f922f28c53d22be751dc0e8fe60e9466fb548 -Author: Kevin Moloney -Date: Tue Dec 15 14:59:42 2015 +0000 - - ATLEDGE-426/ATLEDGE-445 CurieImu missing functions - - * Removed CurieImu.getZeroShockDetected() & CurieImu.getZeroMotionDetected(). - * Functions replaced with CurieImu.getIntShockStatus() & CurieImu.getIntZeroMotionStatus(). - - * Implemented getStepDetectionMode(). - * There was no means of verifying setStepDetectionMode(). - - Signed-off-by: Kevin Moloney - -commit 4422bce4466f9350baeed33a2b36b97fdde5694b -Author: Dino Tinitigan -Date: Wed Dec 9 15:09:55 2015 -0800 - - AGM-157 Add support for _BV() macro - - Add support for _BV() macro - -commit 0c1ba8baef33e74383c0be797955baf386d5c4aa -Merge: 44bc8db b3273bd -Author: Calvin Park -Date: Mon Dec 14 16:55:28 2015 -0800 - - Merge pull request #84 from sandeepmistry/rtc-api - - Rename CurieRTC to CurieTime and integrate some Processing/Time lib API's - -commit b3273bd9cec3f721e9958a768456965c3a7fe5b2 -Author: Sandeep Mistry -Date: Mon Dec 14 19:26:13 2015 -0500 - - Update name in keywords.txt - -commit d0c2800286ecf575fa900d9210d7473c9e68ba04 -Author: Sandeep Mistry -Date: Mon Dec 14 14:30:26 2015 -0500 - - Simplify SetTime example, use hard coded time - -commit e5072e27a530a68acda9fe84fb96abaf1f31faac -Author: Sandeep Mistry -Date: Mon Dec 14 14:27:15 2015 -0500 - - Add API's to convert epoch time to date/time component - -commit 304c733e07f4306c89533d5a36bc714cb43683ba -Author: Sandeep Mistry -Date: Mon Dec 14 14:03:13 2015 -0500 - - Rename CurieRTC to CurieTime - -commit 4d9c07eb44178b0afb185f247de561da87d8aa3d -Author: Sandeep Mistry -Date: Mon Dec 14 12:06:45 2015 -0500 - - Make current count value and counter load registers volatile - -commit b872431e1df13cebb07cef8b2d85bc0906202462 -Author: Sandeep Mistry -Date: Mon Dec 14 11:48:01 2015 -0500 - - Parse date and time strings using Arduino string class instead of sscanf - -commit ed69e4f8c0f57db4c910b84d65d93d67e716766b -Author: Sandeep Mistry -Date: Mon Dec 14 11:27:42 2015 -0500 - - Auto format - -commit 7bb2c23d0db4503d59cfeab00b7f12be8c8ff32c -Author: Sandeep Mistry -Date: Mon Dec 14 11:06:09 2015 -0500 - - Change CurieRTC lib to have Processing style time API's based on the RTC value - -commit 23a1c3b18c45abf82fd9b13393497d83c4496056 -Author: Sandeep Mistry -Date: Mon Dec 14 10:34:41 2015 -0500 - - Remove bundled time library - -commit 44bc8db8808ede092e85dcdb67c3f03b9f24c59f -Author: Krzysztof Sywula -Date: Fri Dec 11 15:32:34 2015 -0800 - - Fixed comparison between uint32 and int32 - - JIRA: ATLEDGE-457 - - Servo.cpp warning - -commit 51c7f7d9145e11f834a4fae290c6693cb871e3fb -Merge: e093df3 956aac4 -Author: Calvin Park -Date: Mon Dec 7 14:17:16 2015 -0800 - - Merge pull request #81 from sandeepmistry/arduino-ble-peripheral-api - - Initial port of BLEPeripheral style API to CurieBle library - -commit e093df3e60fca0338fe23c46ed3e34bb078ef5df -Merge: 39881ec f6c59c1 -Author: Calvin Park -Date: Mon Dec 7 13:16:24 2015 -0800 - - Merge pull request #80 from sandeepmistry/imu - - CurieImu examples updates and keywords.txt - -commit 956aac4612d5b81da30de7c5621596fa3c8d2be1 -Author: tigoe -Date: Thu Dec 3 23:33:37 2015 -0500 - - Style changes to IMU examples - - Note: IMU Library API still needs to be standardized. - -commit 744e66ebb2de90cfbe48d9d3f3497c18f74efddc -Author: Sandeep Mistry -Date: Fri Dec 4 12:43:53 2015 -0500 - - Shorten local name in examples - -commit 44cd7e505e59644a3fca7303b316293372580f9f -Author: Sandeep Mistry -Date: Fri Dec 4 10:45:47 2015 -0500 - - Update note on local name - -commit b272315dfe91418c79ee6ce445144ca3178a2571 -Author: Sandeep Mistry -Date: Fri Dec 4 10:37:13 2015 -0500 - - Misc example updates - -commit 93ce2bb743a166a88e1cd9e52bfe1d24be005f83 -Author: Sandeep Mistry -Date: Fri Dec 4 10:23:43 2015 -0500 - - Correct case of include - -commit ccfaa837fd5810424474bf4a045ae17837fe4b1f -Author: Sandeep Mistry -Date: Fri Dec 4 10:12:52 2015 -0500 - - Correct setDeviceName - -commit 3c848247e8be8dd1b9c9622ebb4cb09fc2e6379e -Author: Sandeep Mistry -Date: Fri Dec 4 09:27:17 2015 -0500 - - Remove AutomationIO example - -commit 3b8efcc191ecbea77096f116195667fde19d85e4 -Author: tigoe -Date: Thu Dec 3 22:24:01 2015 -0500 - - Style changes to BLE library examples - -commit a5c7f84f29a4f3c14594d50695f372702abec940 -Author: Sandeep Mistry -Date: Thu Dec 3 17:01:21 2015 -0500 - - Correct incorrect read permissions for non-writable characteristics - -commit c6924337ea3cb561a950cf0f08accf8696b6f59c -Author: Sandeep Mistry -Date: Thu Dec 3 16:55:03 2015 -0500 - - Dynamically allocate characteristic and descriptor data buffers - -commit 040b63b1d23533774edf7cb46604740bbe92d2ff -Author: Sandeep Mistry -Date: Thu Dec 3 16:54:28 2015 -0500 - - Set initial value of characteristics - -commit 479e8600a665ad21b3b6181997ce871b100c94f4 -Author: Sandeep Mistry -Date: Thu Dec 3 16:50:16 2015 -0500 - - Sent initial value of switch characteristic - -commit 6d6a0c6e5b681c6be61a110d0fe4309c5a38b2a7 -Author: Sandeep Mistry -Date: Thu Dec 3 16:47:18 2015 -0500 - - Set initial value of switch characateristic - -commit ce32c92177a01fd6be5688bf693050d225570ace -Author: Sandeep Mistry -Date: Thu Dec 3 16:40:57 2015 -0500 - - Remove while(!Serial) - -commit b6eea7463bd84ec516f56f98f50fd0f424460060 -Author: Sandeep Mistry -Date: Thu Dec 3 16:31:11 2015 -0500 - - Use bool for public API's return types instead of BleStatus type - -commit 1596dc12e3ca099d483d52476c3c8378ad8d7c3a -Author: Sandeep Mistry -Date: Thu Dec 3 15:47:57 2015 -0500 - - Change public API types to use non-stdint types - - as per https://www.arduino.cc/en/Reference/HomePage - -commit 026f4c044879ae294e3c41aa0fe5d16336feefdf -Author: Sandeep Mistry -Date: Thu Dec 3 15:34:42 2015 -0500 - - Replace battery monitor example with new version - -commit 7767d3d6c42fb1c555ac68a5c9e687b33e6afbd8 -Author: Sandeep Mistry -Date: Thu Dec 3 15:31:15 2015 -0500 - - Rename Ble prefixes to BLE - -commit ee0ae446682773bbd8b240de2d03a0627096e87c -Author: Sandeep Mistry -Date: Thu Dec 3 14:50:40 2015 -0500 - - Separate device name from advertised local name - -commit c23609225aee221ed7ad83429b186826b55baca7 -Author: Sandeep Mistry -Date: Thu Dec 3 14:34:20 2015 -0500 - - Don't advertise the device appearence - -commit 661fc89a9981932103c2a0cc96d46becd6177018 -Author: Sandeep Mistry -Date: Thu Dec 3 14:29:37 2015 -0500 - - Correct advertised service uuid - -commit fe78397481705f520627c860db257ad62a9c5d72 -Author: Sandeep Mistry -Date: Thu Dec 3 14:08:36 2015 -0500 - - Correct presentation format descriptor handling - -commit c1882ee3119a3754378ed7f085c6ab19f53843ab -Author: Sandeep Mistry -Date: Thu Dec 3 12:54:37 2015 -0500 - - Add special handling for 0x2902 and 0x2904 descriptors - -commit 8ee6a0264c81f9534633234b9ff8e809c37abbe0 -Author: hbisby -Date: Thu Dec 3 17:56:19 2015 +0100 - - variable name changed from switchCharacteristic to ledCharacteristic - -commit e0139b68668f891b4ccd6ef166db67c20c172686 -Author: Sandeep Mistry -Date: Thu Dec 3 11:51:58 2015 -0500 - - Add delay(1) in poll - -commit 1b4f5134b152fc062c2f7d2d2aa29f1dfdcb1218 -Author: Sandeep Mistry -Date: Thu Dec 3 10:41:51 2015 -0500 - - keywords.txt updates - -commit 855737bc879868ea759c4eed46ec9420c886752a -Author: Sandeep Mistry -Date: Thu Dec 3 10:41:33 2015 -0500 - - Change internals to use less private friends, add comments in header files, make var types more consistent - -commit 6c946eda6d1ccd4a610e91ab99babbe7924832ca -Author: Sandeep Mistry -Date: Thu Dec 3 10:33:37 2015 -0500 - - User serial baud rate of 9600 for all examples - -commit df691a9ff790a9cf020878104dc138c9c0aeda66 -Author: Sandeep Mistry -Date: Thu Dec 3 10:28:29 2015 -0500 - - Rename with capital B - -commit f68199943d2c525cb3d6fdcbd0cfaf07427aa18b -Author: Sandeep Mistry -Date: Thu Dec 3 10:21:48 2015 -0500 - - Correct UUID - -commit 90886c696057dfd2fb95edeb60502b82c96f8d48 -Author: Sandeep Mistry -Date: Thu Dec 3 10:06:52 2015 -0500 - - Correct include filename - -commit 03673b47fa1cc950734baffb9bb8313629c366f9 -Author: Sandeep Mistry -Date: Wed Dec 2 16:57:46 2015 -0500 - - Use dashed UUID in example - -commit 940374fa55bae556ac093bad2cf6a200529150f2 -Author: hbisby -Date: Wed Dec 2 15:44:59 2015 +0100 - - changed format of long UUIDs - -commit 3d3f973329d08a4f617da27e30f03abed12c4335 -Author: hbisby -Date: Wed Dec 2 15:26:01 2015 +0100 - - ported examples from BLEPeripheral buttonLED and callbackLED - -commit 2581cffca43521c85d5c0101cf3c57878397930e -Author: Sandeep Mistry -Date: Tue Dec 1 17:29:47 2015 -0500 - - API Cleanup - -commit 03c82d3babe18a2fbae49abd49ec9f72cb9127a2 -Author: Sandeep Mistry -Date: Tue Dec 1 16:03:57 2015 -0500 - - Add new LED example - -commit 6e5a9dee80787f90c47435901792c27c2b2687a1 -Author: Sandeep Mistry -Date: Tue Dec 1 15:20:24 2015 -0500 - - Add written and subscribed API's to BleCharacteristic, pass central into BleCharacteristic even handers, remove BleAck event for now - -commit cd54545dc25a0a47cddbfc7722d24f626db12cb2 -Author: Sandeep Mistry -Date: Tue Dec 1 10:47:07 2015 -0500 - - Update previousMillis in example - -commit 3d3f65f2135bef419ce82dcc06690f251419f258 -Author: Sandeep Mistry -Date: Tue Dec 1 10:31:12 2015 -0500 - - Reverse UUID conversion loop - -commit 4dfc0a4f68b7cf7c57f05d5ae6a6770b8e718509 -Author: Sandeep Mistry -Date: Mon Nov 30 17:52:10 2015 -0500 - - Experimental BatteryMonitor example sketch with new API - -commit 99ab222ec39c31e2c27ba8962f4022c499b341b6 -Author: Sandeep Mistry -Date: Mon Nov 30 17:49:47 2015 -0500 - - BleCharacteristic event handler API's - -commit d4efbb2cf9f86ece8804877b268f8ce00f40b736 -Author: Sandeep Mistry -Date: Mon Nov 30 17:20:28 2015 -0500 - - Make descriptor BleCharacteristic API's private - -commit 06a05c2a86886ee4deab539d76a23e85798b0449 -Author: Sandeep Mistry -Date: Mon Nov 30 15:49:12 2015 -0500 - - More keyword.txt updates - -commit e01b7bf4d56a771c3ff3d9db9b1d26de05160a5e -Author: Sandeep Mistry -Date: Mon Nov 30 15:48:30 2015 -0500 - - BlePeripheral event handler rename, and misc var name consistency - -commit 69697e3670a813cfb6a5912d35e80427012f7645 -Author: Sandeep Mistry -Date: Mon Nov 30 15:21:46 2015 -0500 - - Add typed characteristics - -commit be1fcea44f1b9266a88de7d97b341ca4125585fe -Author: Sandeep Mistry -Date: Mon Nov 30 14:13:03 2015 -0500 - - Change return type of BleCentrall::address a C string, make getLocalAddress private for now - -commit 2fb56144f09631a97e73b86f3ffeae5289497f10 -Author: Sandeep Mistry -Date: Mon Nov 30 13:13:11 2015 -0500 - - Whitespace formatting - -commit a711fa2a7bc1cb0cbeaae4b179010b4c2e5ed326 -Author: Sandeep Mistry -Date: Mon Nov 30 13:12:57 2015 -0500 - - Allow BleCharacteristic::setValue to be called before begin - -commit b06df63df5e01de9a13ceddb74dad9fb15d3b821 -Author: Sandeep Mistry -Date: Mon Nov 30 12:24:52 2015 -0500 - - Introduce BleUuid, UUID's are now strings externally - -commit 67d47f330d4e8de2999a4c82f25eed2c3c4c4e45 -Author: Sandeep Mistry -Date: Mon Nov 30 11:38:22 2015 -0500 - - Change begin to return BleStatus - -commit a1d134b6e8fc2ba15b2df3a28244facb5d7d4bd9 -Author: Sandeep Mistry -Date: Mon Nov 30 11:00:33 2015 -0500 - - Add BleAttibute class and add new public addAttribute API - - Make addPrimaryService and getState API’s private. As well as, - BleService::addCharacteristic, BleService::addSecondaryService, and - BleCharacateristic::addDescriptor. - -commit e136ce82f377896cd8f442fe626682654cb9086b -Author: Sandeep Mistry -Date: Fri Nov 27 17:24:26 2015 -0500 - - Expose characteristic properties instead of access and notify mode - -commit 6d101d6288c9bad8960c73fafa40f54692e55333 -Author: Sandeep Mistry -Date: Fri Nov 27 16:45:50 2015 -0500 - - Split up GAP event callback registration - -commit bf3d0bbe1886be69b0b9fedac2a3c73a0cac59e0 -Author: Sandeep Mistry -Date: Fri Nov 27 15:55:22 2015 -0500 - - More keywords - -commit dbdebdb84f982c1edd8e9088e7b3d71a8534b7aa -Author: Sandeep Mistry -Date: Fri Nov 27 15:55:08 2015 -0500 - - Add BleCentral class, disconnect API, split out BleAddress - -commit 4e2a4d7d64669448ea4edcfbd10f6194041a6a9a -Author: Sandeep Mistry -Date: Fri Nov 27 14:12:03 2015 -0500 - - Add setAdvertisedServiceUuid API, and remove param to advertise from addPrimaryService - -commit 2881aa5175d2e0120a4c25dbf8188a589709405d -Author: Sandeep Mistry -Date: Fri Nov 27 14:11:11 2015 -0500 - - Add uuid method to BleService - -commit e7b71f54697a90f1b27e306817142a502cfd0690 -Author: Sandeep Mistry -Date: Fri Nov 27 13:47:49 2015 -0500 - - Rename set/getName to set/getLocalName - -commit ff11e027d85ae5fc312bb87509b65c0f87e62729 -Author: Sandeep Mistry -Date: Fri Nov 27 13:41:00 2015 -0500 - - Add poll API (no-op for now) - -commit 60f6c475aa6b0cd3fe6e366adebc2d9ae4924c7b -Author: Sandeep Mistry -Date: Fri Nov 27 13:37:21 2015 -0500 - - Add begin and end API's - -commit 6d5aa275c531c4fb180256f86e5ede51158710f5 -Author: Sandeep Mistry -Date: Fri Nov 27 13:31:18 2015 -0500 - - Rename bleDevice variable to blePeripheral - -commit 08fd946c6d1632198f4f3bc40d21ac8bb425c70e -Author: Sandeep Mistry -Date: Fri Nov 27 13:28:13 2015 -0500 - - Initial keywords.txt - -commit e8bdd87b7b0cf9ee3d993b2c5542f7593272c41b -Author: Sandeep Mistry -Date: Fri Nov 27 13:28:01 2015 -0500 - - Rename BleDevice.* files to BlePeripheral.* - -commit 7eea01f3e26eda828208ee64068dde040b253e88 -Author: Sandeep Mistry -Date: Fri Nov 27 13:22:31 2015 -0500 - - Add CurieBle.h for main lib header - -commit f6c59c14f95772fa3d6de4b8dfdc697cf8867b0d -Author: Sandeep Mistry -Date: Fri Dec 4 11:53:02 2015 -0500 - - Change CurieImu and BMI160 to KEYWORD2 - -commit 3642f44a88069e89cd644faabae8c72da78a2bfd -Author: hbisby -Date: Fri Dec 4 17:40:59 2015 +0100 - - added defines and classes to keywords.txt - -commit d8618b23ede8b769ecfe8cafee0695c2eea60956 -Author: hbisby -Date: Fri Dec 4 17:02:10 2015 +0100 - - adding keywords.txt for CurieImu library - -commit 262fd1e2940498c00d9871a455de21be1c73785f -Author: tigoe -Date: Thu Dec 3 23:33:37 2015 -0500 - - Style changes to IMU examples - - Note: IMU Library API still needs to be standardized. - -commit 39881ec83254758186e58c8e291b05ff09f6151e -Author: Dino Tinitigan -Date: Thu Dec 3 13:16:44 2015 -0800 - - ATLEDGE-393 Update SD library - - Rebase library from version 1.05 to 1.06 - Fixes issue with passing String class object to SD.exists() - -commit bca4512908095a1c6f04a1c975849080861ff609 -Author: Dino Tinitigan -Date: Wed Dec 2 11:01:11 2015 -0800 - - ATLEDGE-423 Export compiled binary - - Make changes to platform.txt to allow exporting compiled binary as a - .hex - -commit 3c833b2536b4616b38ef342289bb5efb440b6769 -Author: Dino Tinitigan -Date: Tue Dec 1 11:52:57 2015 -0800 - - ATLEDGE-442 Fix some SoftwareSerial Rx limitations - - Fixed some limitations for Rx on SOC_GPIO pins. - Rx should now be functional on all pins except pin 13. - -commit 0b100b2d006add6692a76d1658ca04b317b640a6 -Author: Dino Tinitigan -Date: Wed Nov 25 21:24:00 2015 -0800 - - Atledge-184 Minor improvements to SoftwareSerial library - - Minor improvements to SoftwareSerial library - -commit 7150436d89ca73fa7af68cd81c312203a7da4d4a -Author: Dino Tinitigan -Date: Wed Nov 25 19:14:24 2015 -0800 - - CurieRTC - SetTime example not setting RTC - - RTC not being set because the wrong call was being made should call - RTC.set(t) to set the hardware RTC instead of setTime(t). - -commit 25acb322cad8505f9564ca435b0e113bf5b5d9df -Author: Calvin Park -Date: Wed Nov 25 16:28:00 2015 -0800 - - Fixed typo in old IDE version error message - -commit fc55eedd7ce7fafbc5b0dd35c61ed12f22c0f26c -Author: Krzysztof Sywula -Date: Tue Nov 24 17:03:18 2015 -0800 - - ATLEDGE-455 verbose/quiet selection for upload - -commit 4984085e655687db615b3a9675ee374fba8d6cad -Author: Martino Facchin -Date: Wed Nov 25 16:37:28 2015 +0100 - - Add error message if using IDE 1.6.6 - -commit 69a88721160920cbd77b26836fc97a0549f87db4 -Author: Martino Facchin -Date: Wed Nov 18 10:59:59 2015 +0100 - - Fix recipe.ar.pattern to avoid warning at startup - -commit a22ea571d1863e3869ec5e9f29936d00822b8dcb -Author: Dino Tinitigan -Date: Tue Nov 24 14:01:14 2015 -0800 - - ATLEDGE-444 Fix Library warnings for Arduino IDE 1.6.6 - - Some libraries were missing the "category" field in library.properties - Minor fixes in some libraries for 1.6.6 compatibility - -commit 5c29db6c5b00b32d7f4d2379c3e89d9fdcc076d6 -Author: Dino Tinitigan -Date: Tue Nov 24 10:30:05 2015 -0800 - - ATLEDGE-396 Fix behavior of analogWrite() on non PWM pins - - For all non PWM pins, the behavior of calling analogWrite() should be - logic 0 if the value is 127 or less. For values of 128 or above, the pin - should go to logic 1. - -commit 7afbd91dd8c0aa2b39fbe696c0dcb506e28fddd6 -Author: Dino Tinitigan -Date: Mon Nov 23 18:09:53 2015 -0800 - - ATLEDGE-416 Fix some compile issues with Time.h - - Fix some issues with compiling Time.h by updating to version 1.50 of - Time library - Also removed some examples that will not compile for Arduino101 - -commit c7b04f7d7915407f5194f42abf3dcfc691cd23d8 -Author: Dino Tinitigan -Date: Mon Nov 23 16:07:16 2015 -0800 - - ATLEDGE-384 Remove Esplora specific examples - - Remove Esplora specific examples - -commit 2beea3e778b05e985ac7daacce86fb94847c054e -Author: Brian Baltz -Date: Fri Nov 6 10:33:12 2015 -0800 - - Resolving KW issue with SoftwareSerial destructor - - Signed-off-by: Brian Baltz - -commit b30a275c7221b614071767772e72b98ce93b7ed2 -Merge: 78afaae 3759bcd -Author: Calvin Park -Date: Thu Nov 5 11:05:04 2015 -0800 - - Merge pull request #62 from kevin306/testing - - Klocwork issues - -commit 3759bcd7fc648ae686de973af3df50f92e2dff9f -Author: Kevin Moloney -Date: Thu Nov 5 17:39:14 2015 +0000 - - Fix Klocwork#2051,2060: Array may be outside index - - Change && to || - - Signed-off-by: Kevin Moloney - -commit 5baa3fc6386781a784148fdf2b3fc65476416f05 -Merge: afcc4ef 344f90d -Author: Kevin Moloney -Date: Thu Nov 5 12:45:34 2015 +0000 - - Merge branch 'testing' of github.com:kevin306/corelibs-arduino101 into testing - - Conflicts: - libraries/CurieBle/src/BleCharacteristic.cpp - -commit afcc4efd16a7c7b9d915913d641a76f0b448eb3d -Author: Kevin Moloney -Date: Tue Nov 3 15:38:37 2015 +0000 - - Fix Klocwork#2051,2060: Array may be outside index - - * Added check to ensure _data_len is within index values. - - * Had to repeat the branch statement rather than move it because of memcpy. - - * Fixed bug introduced(c8ecd5f) in WString.h when "len" became protected. - - Signed-off-by: Kevin Moloney - -commit 344f90d892d33c47f121cc374643c5e28b51bbdd -Author: Kevin Moloney -Date: Tue Nov 3 15:38:37 2015 +0000 - - Fix Klocwork#2051,2060: Array may be outside index - - * Added check to ensure _data_len is within index values. - - * Had to repeat the branch statement rather than move it because of memcpy. - - Signed-off-by: Kevin Moloney - -commit 78afaae4c7f189dc44ec83d19745dc4bb4e7f6ac -Author: Dino Tinitigan -Date: Mon Nov 2 10:48:45 2015 -0800 - - ATLEDGE-229 CurieRTC and Time Library - - Initial implementation of the CurieRTC library for the built-in RTC - Includes the Time library plus 3 new examples for the Arduino101 - -commit cacac37db51b7212b92f3e90ff944006771860c4 -Author: Bogdan Pricop -Date: Thu Oct 29 22:28:12 2015 +0000 - - Fix Klocwork#137: Non-void function does not return value - - Change getTimeStampClks() implementation from assembly to C. - - Signed-off-by: Bogdan Pricop - -commit 8fa6bbaa920005ea42f738f305bcd4852c822a1b -Author: Dino Tinitigan -Date: Wed Oct 28 16:28:50 2015 -0700 - - ATLEDGE-184 SoftwareSerial Library - - Port of SoftwareSerial Library for Arduino101 - Tx works up to 384000 bps - Rx works up to 57600 bps - -commit 2ae262a2a87d0d85032da3d975e7f1385a02ea75 -Author: Kevin Moloney -Date: Thu Oct 29 14:41:28 2015 +0000 - - Fix Klocwork#2051,2060: Array may be outside index - - * Added check to ensure _data_len is within index values. - - * Had to repeat the branch statement rather than move it because of memcpy. - - Signed-off-by: Kevin Moloney - -commit 739c4007ba9c4b7a77d57628eb9ea31ab204d880 -Author: Kevin Moloney -Date: Wed Oct 28 16:47:44 2015 +0000 - - Fix Klocwork#543,544: Buffer overflow/null pointer - - * Added a check to ensure array does not access index value of -1. - - * Added a check for this null pointer. - - Signed-off-by: Kevin Moloney - -commit dd4845e5d4f7dca461587d8bc0cc998598b060bc -Author: Kevin Moloney -Date: Wed Oct 28 16:44:45 2015 +0000 - - Fix Klocwork#2081,2082: Uninitialized variable. - - * using memset to initialize struct to zero. - - Signed-off-by: Kevin Moloney - -commit 79744c4255d4c33d21b79bbc6f99922476ad3adf -Author: Kevin Moloney -Date: Wed Oct 28 16:40:38 2015 +0000 - - Fix Klocwork#79,80,81,82: Uninitialized Variable. - - * using memset to initialize struct to zero. - - Signed-off-by: Kevin Moloney - -commit 79008fa1132cd1c9790c50a3f96232f54774a1dd -Author: Kevin Moloney -Date: Wed Oct 28 16:38:07 2015 +0000 - - Fix Klocwork#671: Infinite loop. - - * Added comment to verify requirement for infinite loop. - - Signed-off-by: Kevin Moloney - -commit cfe0a5df34a7f8b94724a9b99f02b5e62e42854b -Author: Kevin Moloney -Date: Wed Oct 28 16:33:00 2015 +0000 - - Fix Klocwork#105: Pointer returned may be null. - - * Added a check for this null pointer. - - Signed-off-by: Kevin Moloney - -commit 5e71e700d4d1b66f9e68fc725653db9859660255 -Author: Kevin Moloney -Date: Wed Oct 28 16:30:11 2015 +0000 - - Fix Klocwork#100: Infinite loop. - - * Added comment to verify requirement for infinite loop. - - Signed-off-by: Kevin Moloney - -commit db2995a78d772adb072ce743e77eece69a01d0d8 -Author: Kevin Moloney -Date: Wed Oct 28 16:28:23 2015 +0000 - - Fix Klocwork#91: Infinite loop - - * Added comment to verify requirement for infinite loop. - - Signed-off-by: Kevin Moloney - -commit 62395e48e858c433a88c20523481382e90be21a3 -Author: Kevin Moloney -Date: Wed Oct 28 16:25:14 2015 +0000 - - Fix Klocwork#1093: Infinite loop. - - * Added comment to verify requirement for infinite loop. - - Signed-off-by: Kevin Moloney - -commit b3afd60a73d2fa8ead92ffa8e66a264e64bafd12 -Author: Bogdan Pricop -Date: Wed Oct 28 14:19:45 2015 +0000 - - Fix Klocwork#137: Non-void function does not return value - - Move getTimeStampClks() function to an *.S file - It is fully implemented in assembly and we take care of returning value by - filling in the proper value in the proper registers. - - Signed-off-by: Bogdan Pricop - -commit 0e094210f3d645b109adceb393a00b0b41acdf80 -Author: Martino Facchin -Date: Thu Oct 29 09:54:33 2015 +0100 - - fix BLE library String handling - - the correct way to get a String lenght is by calling String.length() - now len field is protected so a compilation error will be triggered - -commit 0ffb9749afb8dc53abd628e00ced6af07d0c97d1 -Merge: b17f321 6f35dfd -Author: Calvin Park -Date: Wed Oct 28 17:03:31 2015 -0700 - - Merge pull request #58 from facchinm/new_delete - - Fixes to pass validation with test suite - -commit 6f35dfd332c30b135c14d1f718f923299d7e6695 -Author: Martino Facchin -Date: Wed Oct 28 12:53:47 2015 +0100 - - stdlib_noniso: expand all functions up to base 36 - - needed to pass a very strict test - Porting of latest Due implementation - -commit f07a20aa02e5421b1fc6e9775fdeb2221c8bd2a7 -Author: Martino Facchin -Date: Wed Oct 28 12:50:35 2015 +0100 - - remove misleading SERIAL_PORT_USBVIRTUAL define - - the Serial via USB port is not a real virtual cdc port (it still emulates an Hardware Serial), remove to avoid sketches getting confused and expect CDC flow control functions - -commit c8ecd5f0af6c87fdd02382f0cab275d83da31f2c -Author: Martino Facchin -Date: Tue Oct 27 18:01:17 2015 +0100 - - Sync IPAddress and Wstring with SAM implementation - - Test suite failed on these functions - -commit d179695b0494b96cc1feb963905ee8fea9a7d2a5 -Author: Martino Facchin -Date: Tue Oct 27 15:41:24 2015 +0100 - - Add new/delete implementations - - This is needed to pass C++ validation in test suite - -commit b17f321e32464222ff433afa69af2ba3fe0149c5 -Author: Martino Facchin -Date: Tue Oct 27 15:40:30 2015 +0100 - - Specify architecture in library properties - - to avoid conflicts with standard libs - -commit cd4f66d4620a8aabb4bfb966fe6564a367a24f32 -Author: Dan O'Donovan -Date: Thu Oct 22 17:50:32 2015 +0100 - - libarc32drv_edu.a: Update of pre-compiled driver library. - - Signed-off-by: Dan O'Donovan - -commit 95174ce3dd682621e1f74ef0e04e818b0ff1dce3 -Author: Dan O'Donovan -Date: Thu Oct 22 17:47:07 2015 +0100 - - build: unused global variables in header file causing BLE build failure - - factory_data.h defines 2 global variables which aren't actually used anywhere. - However, they become global symbols in any C files that include this header - file. For most builds, this header file is included only once but, for CurieBle - library builds, it is included by another C file as well. This causes a linker - error due to duplicate symbols in the final image. - - The simple solution is to just remove these variables, because they aren't used. - - Signed-off-by: Dan O'Donovan - -commit 7052d8fe88034c92bd845b818bd1c028adea0444 -Merge: 6efbac6 c5e9152 -Author: Brian Baltz -Date: Wed Oct 14 16:20:32 2015 -0600 - - Merge pull request #52 from 01org/arduino101-rename - - ATLEDGE-364 Rename EDU to Arduino 101 - -commit c5e915295c995aebc15ecab2a2538b6c3ab20f23 -Author: Brian Baltz -Date: Wed Oct 14 15:14:56 2015 -0700 - - ATLEDGE-364 Rename arduino_101_x to arduino_101 - - Signed-off-by: Brian Baltz - -commit 570a904927ca18737613dbee8f4640382dba0746 -Author: Brian Baltz -Date: Wed Oct 14 14:56:03 2015 -0700 - - Update pre-compiled library - - Signed-off-by: Brian Baltz - -commit d342223adc0676d5c39a0d89f9ab94d0cea5e56e -Author: Brian Baltz -Date: Wed Oct 14 14:55:55 2015 -0700 - - ATLEDGE-364 Remove CONFIG_BOARD_ATLASPEAK_EMU specific code - - Signed-off-by: Brian Baltz - -commit 6efbac63436af6e286aa4b5e09b30bbe4cebeb80 -Author: Manoel Ramon -Date: Tue Oct 13 15:49:41 2015 -0700 - - ATLEDGE-316 - isAlphaNumeric returns incorrect bool for '@' - -commit 31e75615c26a6d19c168fc16e6b44d4e9504c45b -Author: Kevin Moloney -Date: Wed Oct 14 12:11:13 2015 +0100 - - Missing examples folder in Wire library - - - Slave examples not relevant because AtlasPeak does not support - slave-mode I2C. - - - Examples included digital_potentiometer, master_reader, - master_writer, SFRRanger_reader. - - Signed-off-by: Kevin Moloney - -commit 4c04540989a546fb6b668f35cee49a272bed9019 -Author: Brian Baltz -Date: Tue Oct 13 16:01:50 2015 -0700 - - ATLEDGE-364 Rename EDU library folders - - Signed-off-by: Brian Baltz - -commit d7617aba523a269a0d8278ba758bbe706fdd74f3 -Author: Brian Baltz -Date: Tue Oct 13 15:56:24 2015 -0700 - - ATLEDGE-364 Rename EDU to Arduino 101 - - Signed-off-by: Brian Baltz - -commit 7436867a37228a6bdeecf87c80489187a99f01ed -Author: Manoel Ramon -Date: Wed Oct 7 22:10:55 2015 -0700 - - ATLEDGE-358 - BLE Broadcast name must read the product name in the OTP - -commit 94ea92298feee063539ed2ca791cdef826d71333 -Author: Kevin Moloney -Date: Tue Oct 13 14:42:51 2015 +0100 - - Updated libarc32 - - Signed-off-by: Kevin Moloney - -commit 5a1c28cd384e01c50068aca8c7b379b05bb50571 -Author: Kevin Moloney -Date: Tue Oct 13 14:36:32 2015 +0100 - - ATLEDGE 201 Pointer returned may be null - - Added a check for this null pointer. - - Signed-off-by: Kevin Moloney - -commit 45e6046f09e2c465c69456869cee6992792e4f55 -Author: Kevin Moloney -Date: Tue Oct 13 13:04:04 2015 +0100 - - Files no longer in use, can be removed. - -commit 15dbf74d82907eb0af47ad9c5ef682fa298b11f3 -Merge: 685eed6 aa8e01a -Author: Calvin Park -Date: Mon Oct 12 11:43:12 2015 -0700 - - Merge pull request #48 from Dan-Emutex/adafruit_libraries - - Adafruit libraries - -commit 685eed663cedc37c91855048c216872a479605ad -Author: Dan O'Donovan -Date: Fri Oct 9 16:49:22 2015 +0100 - - imu: revert commit b62bbfc89b - - This commit essentially reverts the changes made in b62bbfc89b. - That is, it removes some Accelerometer data sampling rates which - we don't support in the mode that we are using the Accelerometer - - Signed-off-by: Dan O'Donovan - -commit aa8e01a64fe31172758ee0b1c66b4a3ce521730b -Author: Dan O'Donovan -Date: Fri Oct 2 17:59:40 2015 +0100 - - dotstar: initial port to AtlasEdge - - Signed-off-by: Dan O'Donovan - -commit 4fee1cca6bac948cb010cadb6b9fba33d8fbc2c6 -Author: Dan O'Donovan -Date: Fri Oct 2 17:57:02 2015 +0100 - - dotstar: add DotStar library v1.0.1 from Adafruit - - - Adafruit_Dotstar Github repository: https://github.com/adafruit/Adafruit_DotStar - Tag: v1.0.1 - - Signed-off-by: Dan O'Donovan - -commit 6e2de131cbfd585cf027f41353a71707185a8bbd -Author: Dan O'Donovan -Date: Fri Oct 2 17:52:45 2015 +0100 - - motorshield: initial port to AtlasEdge - - Signed-off-by: Dan O'Donovan - -commit e671f3f6c3b134d6b326c671de255c2f47d83969 -Author: Dan O'Donovan -Date: Fri Oct 2 17:07:56 2015 +0100 - - motorshield: add Motor Shield V2 library v1.0.1 from Adafruit - - - Adafruit_Motor_Shield_V2_Library Github repository: https://github.com/adafruit/Adafruit_Motor_Shield_V2_Library - Tag: v1.0.1 - - Signed-off-by: Dan O'Donovan - -commit 146b0bf2912cf09735f2701564261bb06d2aa6b3 -Author: Dan O'Donovan -Date: Mon Sep 28 17:00:30 2015 +0100 - - neopixels: initial port to AtlasEdge - - Signed-off-by: Dan O'Donovan - -commit ca4d7f2530f4077d6d1108d450c32afaa9f7862a -Author: Dan O'Donovan -Date: Mon Sep 28 14:18:41 2015 +0100 - - neopixel: add Neopixel library v1.0.3 from Adafruit - - - Adafruit_Neopixel Github repository: https://github.com/adafruit/Adafruit_NeoPixel - Tag: v1.0.3 - - Signed-off-by: Dan O'Donovan - -commit 7d930a50dbfb0890b4bb0915fe34c5dc2bdd6d79 -Author: Dan O'Donovan -Date: Fri Oct 9 13:21:22 2015 +0100 - - imu: fixed and simplified (set|get)ZeroMotionDetectionDuration methods - - Validation feedback revealed logic errors in - setZeroMotionDetectionDuration and a lack of clarity on how it should - be used. Addressed by providing an explicit set of valid values for - the user to pass to the method, which can in turn be written directly - to the relevant register - - Signed-off-by: Dan O'Donovan - -commit 7134c41e2940db726cfa293f19e9207cc9b643a2 -Author: Dan O'Donovan -Date: Mon Oct 5 10:17:29 2015 +0100 - - imu: improve interrupt-safety of CurieImu API methods - - Ensure that SPI transfers cannot be interrupted, and - avoid use of global buffer to improve interrupt safety - - Signed-off-by: Dan O'Donovan - -commit 252bd5308f34dd442d9190d36bfbe7f7b1379402 -Author: Calvin Sangbin Park -Date: Mon Oct 5 15:26:15 2015 -0700 - - Updated libarc32 - -commit b1cbef53bbd79333c0df25816078a7eab4b441bc -Author: Dan O'Donovan -Date: Fri Oct 2 15:26:25 2015 +0100 - - imu: minor tidy-up of documentation for API enumeration values - - Signed-off-by: Dan O'Donovan - -commit b62bbfc89bc35b3a0a04de74f7e4898371922a83 -Author: Dan O'Donovan -Date: Fri Oct 2 14:31:57 2015 +0100 - - imu: add missing accelerometer sample rate options - - - The following sample rates were previously omitted: - - 25/32 Hz - - 25/16 Hz - - 25/8 Hz - - 25/4 Hz - - Signed-off-by: Dan O'Donovan - -commit 426367514593a075b3dc5d373dca10e82cf24112 -Author: Dan O'Donovan -Date: Fri Oct 2 14:28:22 2015 +0100 - - imu: remove invalid 3200Hz accelerometer sample rate option - - The maximum sample rate for the accelerometer is 1600Hz - - Signed-off-by: Dan O'Donovan - -commit c8c4d98b50644141b77868996e226fba167944c9 -Author: Dan O'Donovan -Date: Fri Oct 2 14:06:00 2015 +0100 - - imu: fixes/improvements needed for TapDetect example - - - Threshold was set incorrectly, leading to over-sensitive detection - - Default accelerometer range of 2g prevented detection of strong taps - - Added comments to explain the code in more detail - - Signed-off-by: Dan O'Donovan - -commit 23bd20fe1081edde07fca0a85718f5a52987abfc -Author: Dan O'Donovan -Date: Fri Oct 2 13:49:24 2015 +0100 - - imu: compile error in RawImuDataSerial.ino when CALIBRATE_ACCELGRYO_OFFSETS defined - - Signed-off-by: Dan O'Donovan - -commit 67629a6de0e9163f1a7631fa2ae28c9f5b9e4765 -Author: Dan O'Donovan -Date: Fri Oct 2 13:34:48 2015 +0100 - - imu: remove delay between bi-dir SPI transfers causing data loss - - A wait for TX to complete, followed then by RX, was causing data - loss due to the fact that it was waiting for the busy status flag - to clear after TX, but this doesn't clear until after RX is also - complete. The result was data loss due to RX FIFO overflow. - Fixed by disabling the wait between TX and RX steps. - - This affected functions requesting more than 8 bytes from the - BMI160, such as getMotion6() and getFIFOBytes() methods. - - Signed-off-by: Dan O'Donovan - -commit 599e33bf4bfd604f68e55252511075e078c673de -Author: Dan O'Donovan -Date: Thu Oct 1 12:55:05 2015 +0100 - - imu: fixed error in reg_write_bits - - - reg_write_bits was erroneously clearing all other bits in - a register outside of the bits targeted for the write - operation. - - Signed-off-by: Dan O'Donovan - -commit 17b8d04c60919d25ccac54d7cf2901c1d0f0e285 -Author: Dan O'Donovan -Date: Mon Sep 28 14:08:28 2015 +0100 - - imu: minor clean-ups - - - removed some dead code - - added/corrected some code comments - - Signed-off-by: Dan O'Donovan - -commit e4904009052717894720323d8b037307b24b9204 -Author: Dan O'Donovan -Date: Fri Sep 25 18:10:25 2015 +0100 - - imu: adding support for tap detection by BMI160 accelerometer - - Signed-off-by: Dan O'Donovan - -commit a5a12fc002430d002d9434f47582577da5c36631 -Author: Dan O'Donovan -Date: Fri Sep 25 15:21:05 2015 +0100 - - imu: added support for Step Detection/Counting in CurieImu library - - Signed-off-by: Dan O'Donovan - -commit b759ec35f46ef38d77fcd90ad1d91a5840607aaa -Author: Dan O'Donovan -Date: Fri Sep 25 01:36:11 2015 +0100 - - imu: fixes for issues found while testing shock detection - - - Created new SPI driver using polling instead of interrupts. - This allows it to be used within an ISR so that - we can check the source of the CurieImu interrupts. - - Fixed bit manipulation logic for partial register read/writes - - Added a new example sketch to demonstrate Shock Detection function - - Fixed IMU power-up logic - - Signed-off-by: Dan O'Donovan - -commit 982a470e6909bd444a399b49ced4749d9da72754 -Author: Dan O'Donovan -Date: Thu Sep 17 15:59:50 2015 +0100 - - Initial commit for new CurieImu Gyro/Accel library - - Signed-off-by: Dan O'Donovan - -commit e25bcde0d0b7f9fc635e376aa649884a369d6c45 -Merge: 6f196e1 2b11283 -Author: Brian Baltz -Date: Fri Oct 2 12:32:31 2015 -0600 - - Merge pull request #46 from 01org/cdc-acm - - CDC-ACM: move allocation of shared buffers to LMT - Has pair commit in Thunderdome - -commit 2b112833285765ef1e9436d5a82f1eeb65f53df8 -Author: Calvin Sangbin Park -Date: Fri Oct 2 10:32:51 2015 -0700 - - Updated libarc - -commit 05d4ecade9f44e2d86a43fde1030b2b0167236ed -Author: Bogdan Pricop -Date: Fri Oct 2 16:02:09 2015 +0100 - - CDC-ACM: avoid race condition. - - * In case bool() is called in a very tight while loop, give LMT space - to set the host_open shared variable. - - Signed-off-by: Bogdan Pricop - -commit b8d9b11d8d1c61d704f967d5d730b244c72d918f -Author: Bogdan Pricop -Date: Fri Oct 2 12:19:29 2015 +0100 - - CDC-ACM: change end and init to avoid race condition. - - Signed-off-by: Bogdan Pricop - -commit 5174c9c7e1634eef20c84b436c58a7b7cbbddbb3 -Author: Bogdan Pricop -Date: Fri Oct 2 10:29:43 2015 +0100 - - libarc32drv_edu.a: Update of pre-compiled driver library. - - Signed-off-by: Bogdan Pricop - -commit a6473d1fc4c8e4207f974eac273b0f5781341bc7 -Author: Dan O'Donovan -Date: Fri Oct 2 00:11:03 2015 +0100 - - CDC-ACM: move allocation of cdc-acm shared buffers to LMT - - LMT shall now be responsible for allocating shared data buffers - used to exchange CDC-ACM data between ARC and LMT. - - Defined a revised set of structures to allow the pointers to - be passed from LMT to ARC during initialisation. - - NOTE THAT THIS DEPENDS ON A CORRESPONDING CHANGE IN THUNDERDOME - FIRMWARE - - Signed-off-by: Dan O'Donovan - -commit 6f196e1fcdb422ca29229af27ff984eb113e3bd0 -Author: Calvin Park -Date: Thu Oct 1 22:11:13 2015 -0700 - - Updated libarc32 - -commit 4f1a71a77685e4b98a2c0345773bb5c92cbc98f4 -Author: Bogdan Pricop -Date: Fri Sep 11 08:59:45 2015 +0100 - - CDC-ACM fix flush() API - - Signed-off-by: Bogdan Pricop - -commit 0f07ea0e5dcf0039761e256a7efe7f327270124e -Author: Dan O'Donovan -Date: Thu Sep 10 13:43:13 2015 +0100 - - CDC-ACM - Fix slow write() performance and availableForWrite() logic - - - Removed delay in write() function which caused slow TX performance - - Removed timeout logic in write() function; moved to LMT side - - Fixed logic in availableForWrite() - ring-buffer capacity is at most - 1 less than actual size to avoid head index passing tail index - - Signed-off-by: Dan O'Donovan - -commit 33f4380d60a5f3fd110965f15565f3b83b57d6c9 -Author: Bogdan Pricop -Date: Tue Sep 8 10:57:00 2015 +0100 - - CDC-ACM - address review comments. - - Signed-off-by: Bogdan Pricop - -commit ddb0003c675d648f1b49ea8956e5b0f3b49d4775 -Author: Bogdan Pricop -Date: Mon Sep 7 15:36:58 2015 +0100 - - CDC-ACM using shared memory buffers - - Signed-off-by: Bogdan Pricop - -commit 6d0fc98091cedcc6c2e947878bdf7f5201592abe -Author: Dan O'Donovan -Date: Sat Sep 19 09:50:03 2015 +0100 - - ble: update libarc32drv_edu.a - - Signed-off-by: Dan O'Donovan - -commit 9314b22ff29e70c400c61d8f39c6486d8faa03ad -Author: Dan O'Donovan -Date: Sat Sep 19 09:41:46 2015 +0100 - - ble: address code review comments from Manoel Ramon - - - Added checks in example sketches for API return values - - Applying Public MAC address if present in factory data - - Applying default device name if user doesn't provide one - - Only issuing notifications/indications if enabled by peer - - Re-sync'd with latest BLE code from Thunderdome - - Added basic DTM support for use in sketch (for internal testing) - - Other minor updates, corrections and clarifications - - Signed-off-by: Dan O'Donovan - -commit a709e252eb60dec7eb7f923797814e72ffda5dcc -Author: Brian Baltz -Date: Fri Sep 18 09:47:10 2015 -0600 - - ATLEDGE-334 Update version header location in linker script - - Need to update linker script to match recent firmware change: - - JIRA: FIRE-2160 - - 1024 bytes were used to store the version header, which is only - 48 bytes big. So we wasted 976 bytes. By moving it after the vector table, - we can free this 1024 bytes space. - - Signed-off-by: Brian Baltz - -commit c33d42adca3d1fd4295e7df8f278c41089da26f6 -Author: Brian Baltz -Date: Thu Sep 17 12:58:44 2015 -0700 - - ATLEDGE-333 CLUploader script path is hard-coded - - Signed-off-by: Brian Baltz - -commit 28304b5b8ba763fe00d23cd074f23b3b8e6b97e0 -Author: Dan O'Donovan -Date: Wed Sep 16 17:51:36 2015 +0100 - - uart: fixing baud divisor calculation to add support for 230400 bps - - Added code to configure the UART fractional divisor to improve the - UART timing at higher speeds such as 230400, 460800 and 921600 bps. - - Signed-off-by: Dan O'Donovan - -commit b96562dbb3c63d5cc894f128d61de4f1bc6de3d5 -Author: Dan O'Donovan -Date: Wed Sep 16 14:45:05 2015 +0100 - - wire: adding support for I2C RESTART and some minor clean-ups - - - Added support for sending repeated-START conditions on the I2C bus - By adding an option to not send a STOP condition at the end of a - transfer, it is possible for the I2C master to hold the bus and - send additional transfers. If switching from writing to reading, - or vice-versa, the I2C master controller will automatically send - a repeated-START (RESTART) in between. For consecutive writes or - consecutive reads to the same slave, a repeated-START is not needed. - - - Cleaned up some redundant code and removed some unsupported methods - in the Wire library implementation for AtlasEdge, particularly those - related to I2C Slave-mode. AtlasEdge supports I2C Master-mode only. - - Signed-off-by: Dan O'Donovan - -commit 026afb2d00b89999b07bd7c5c6bc54cd29bbb310 -Author: Dino Tinitigan -Date: Mon Sep 14 16:11:37 2015 -0700 - - ATLEDGE-231 TFT Library - - Add missing wiring_private.h file - -commit 6b5577e058a46fcfca5ba91ccd3c9824acd941b6 -Author: Dan O'Donovan -Date: Sat Sep 12 11:26:50 2015 +0100 - - ble: adding new CurieBle Arduino library for AtlasEdge - - Added new BLE library and examples, providing access to - basic GAP/GATTS features of Intel Curie module - - Includes a port of the BLE Core Service CFW API and - UART-IPC drivers from Thunderdome code base - - Also includes a shift of CFW master role from LMT to ARC. - Requires matching commit 4d056720 or newer in thunderdome - atlaspeak_atlasedge project source tree to work correctly - - Known issues: - - A fix in Nordic BLE firmware is required to support - adding Presentation Format descriptors to characteristics. - Until that fix is available, the addPresentationMethod() - of the BleCharacteristic class should not be used. - - Due to issues with the current CDC-ACM implementation on - AtlasEdge platforms, use of Serial should be replaced with - Serial1 in the CurieBle example sketches, until CDC-ACM - issues are resolved. - - Not currently supported: - - Direct Test Mode (DTM) - - Bluetooth bonding (pairing) - - BLE Central Device role and GATT client functionality - - Signed-off-by: Dan O'Donovan - -commit 4cf249987b5da85a90025d174021824e5ce09cc6 -Author: Bogdan Pricop -Date: Mon Aug 17 22:49:26 2015 +0100 - - Port OneWire to Atlas Edge board - - * Implement platform specific macros for Intel AtlasPeak SoC. - - Signed-off-by: Bogdan Pricop - -commit 42da7aadcf2439bb4adaa5a1e112381786639486 -Author: Bogdan Pricop -Date: Fri Jul 31 16:23:55 2015 +0100 - - Add Paul Stoffregen's OneWire library version 2.3 - - * 1-Wire Github repository: https://github.com/PaulStoffregen/OneWire - tag: 2.3 - - Signed-off-by: Bogdan Pricop - -commit d439d3be7522a5d4c2adf552039b089dea0d495f -Author: Dino Tinitigan -Date: Thu Sep 10 11:13:58 2015 -0700 - - ATLEDGE-231 TFT Library - - Add AtlasEdge support to TFT library - -commit f34bda4517f1cc199d29e3fe51c220cae5b2cf4c -Author: Dan O'Donovan -Date: Wed Sep 2 12:53:49 2015 +0100 - - Updated license headers in Thunderdome-derived source files - - New license headers and other minor changes have been imported - from the Thunderdome code base. Note that no functionality has - been added/changed/removed in this commit. - - Signed-off-by: Dan O'Donovan - -commit 647e20823be90638e707fbcb66c8be4e1f9ea351 -Author: Dino Tinitigan -Date: Tue Aug 25 10:24:03 2015 -0700 - - ATLEDGE-279 F_CPU not defined - - Added F_CPU to boards.txt and platform.txt. - Fixed format of boards.txt - -commit d3bbf49643a9fdd2a19426f5818cd602e1e99e8d -Author: Dino Tinitigan -Date: Mon Aug 24 16:34:21 2015 -0700 - - ATLEDGE-228 SD Library - - Add AtlasEdge support to SD library - -commit 83a4c0213a24c32e4f2924fbbcad5dd5afca9ec3 -Author: Dino Tinitigan -Date: Fri Aug 21 13:05:14 2015 -0700 - - ATLEDGE-272 SPI broken by new clock gating feature - - Enable clock gate before configuring SPI - -commit c29233c5826e85f49173a77757400678471bd49b -Author: David Hunt -Date: Wed Aug 12 12:40:28 2015 +0100 - - Cleaned up buffer handling in CDCSerial class. - - Signed-off-by: David Hunt - -commit b9d35bdac7c27a1c407fe2e0d4f97236a980f312 -Author: David Hunt -Date: Thu Aug 6 14:28:16 2015 +0100 - - libarc32drv_edu.a: Update of pre-compiled driver library. - - Signed-off-by: David Hunt - -commit 7fa713d2ef47140a82b4b0f50ab31ec43206fada -Author: David Hunt -Date: Tue Aug 11 13:53:48 2015 +0100 - - Address review commments on cdc-acm serial - - Signed-off-by: David Hunt - -commit 511a8d76f20698813cafa1b0f7130e40f37cdaea -Author: David Hunt -Date: Tue Aug 4 12:23:24 2015 +0100 - - Changes out of code reviews. - - Signed-off-by: David Hunt - -commit d85aa9643ac15389004d05217e67d438d689916d -Author: David Hunt -Date: Wed Jul 29 14:45:44 2015 +0100 - - Added CDC-ACM serial functionality as Serial. - - Pin header pins 0 and 1 have moved to Serial1 - - Signed-off-by: David Hunt - -commit 347ded518d9731dd93460b569168f2631ad604c1 -Author: Manoel Ramon -Date: Thu Aug 13 16:28:39 2015 -0700 - - ATLEDGE-44 Creation of platform ID and platform name for Atlas Edge - -commit a9c0430563473d556a766ac7df953969363f3bc8 -Author: Krzysztof Sywula -Date: Tue Aug 11 18:33:04 2015 -0700 - - vid/pid change - - JIRA: ATLEDGE-181 - - Signed-off-by: Krzysztof Sywula - -commit 7d55eb88f2c659ca0dc3bc83ef607fbeb439bbe2 -Author: Dino Tinitigan -Date: Tue Aug 11 10:48:22 2015 -0700 - - ATLEDGE-141 Add ATN pin as a regular gpio - - Added ATN pin as a regular gpio as pin 20 - -commit e487817583d12c6ef1203ef60c14cd4882509b32 -Author: Martino Facchin -Date: Tue Aug 11 13:56:19 2015 +0200 - - import stdlib noniso functions - - try not to reinvent the wheel - solves print(*, BIN) returning "unsupported base" - -commit a03d93d5634335a96549c1c41923e03424f5d488 -Author: Martino Facchin -Date: Tue Aug 11 12:34:10 2015 +0200 - - add support for String operation with uint64_t - - fixes examples StringConstructors, StringAdditionOperator, StringAppendOperator - - only needed because millis() returns a uint64_t - -commit 2dbdd016cc9cf47649c55141b56bc7c2e64315ad -Author: Dino Tinitigan -Date: Thu Aug 6 15:38:23 2015 -0700 - - ATLEDGE-30 Port Ethernet Library - - Port of the Ethernet Library for Atlasedge. - Added missing files from cores directory needed by Ethernet, WiFI, and - other similar libraries. - Works for Arduino 1.6.5 and above - -commit 293b27908134ab79d689020dece0bf6ce8cc02a7 -Author: Bogdan Pricop -Date: Thu Aug 6 15:04:43 2015 +0100 - - Fix the build. - - Signed-off-by: Bogdan Pricop - -commit bc74b7df42ec1a9413dd044b814363c7cc51bdeb -Author: Bogdan Pricop -Date: Thu Aug 6 13:48:37 2015 +0100 - - Address code review comments - - * Correct spelling error evry => every - * Remove magic number 0xFFFFFFFF and define it as FREE_RUN_TIMER - * Remove 2 and 10 magic numbers from Print class; use BIN and DEC instead - * Add base checking inside printNumber() and printLongLong() methods - * Remove some unnecessary white spaces. - - Signed-off-by: Bogdan Pricop - -commit 3cc79807014c1f386e12017c4063ccc62db9e15c -Author: Kevin Moloney -Date: Tue Aug 4 12:21:44 2015 +0100 - - ATLEDGE-185 - String object is missing functions - - Also included Bug #2289 overloaded operators missing - Signed-off-by: Kevin Moloney - -commit e3f227ff6fe7c5530a2f99f2c42206dd08071020 -Author: Martino Facchin -Date: Mon Aug 10 15:05:26 2015 +0200 - - WStrings: add c_str function - - this fixes SD library compilation - - However, in the long term, WString, Print, IPAddress, Stream and WMath (every core file non hardware-related) should be aligned to AVR version to avoid incompatibilities with derived classes - -commit fd8b3e5e6899ed12ada55829c7557be0402dd123 -Author: Martino Facchin -Date: Mon Aug 10 14:53:57 2015 +0200 - - Add digitalPinTo* macros/functions - - these functions ensure compatibility with lots of existing libraries - -commit f3f2b0bc66b053b2cd59cee578906a2f7d83eabc -Author: Martino Facchin -Date: Tue Jul 21 18:09:38 2015 +0200 - - Improve Wire library - - still unreliable though - -commit a33835c16343c18ed75e51418ff02c72684a745b -Author: David Hunt -Date: Fri Jul 31 13:56:57 2015 +0100 - - ATLEDGE-177-char-Signed - - Char is now signed (fix of previous regression in merge) - - Signed-off-by: David Hunt - -commit 0aa51368ea4b1688aecbe2b2b0b8b1bf57058ecf -Author: David Hunt -Date: Fri Jul 31 12:50:14 2015 +0100 - - libarc32drv_edu.a: Update of pre-compiled driver library. - - Signed-off-by: David Hunt - -commit 31513f70d52408093dfbcaeeee4ec77e9c1c3dcb -Author: Bogdan Pricop -Date: Wed Jul 29 17:37:35 2015 +0100 - - Fix bug #2220: delay(150secs) only waits for approx 15secs - - Root cause: - While transforming milliseconds in clocks, the result of an uint32_t - multiplication overflows - Solution: - Cast one of the operands to uint64_t - - Signed-off-by: Bogdan Pricop - -commit b365f143e2b11346ee54a9c48f6c6b3b48e33e56 -Author: Kevin Moloney -Date: Wed Jul 29 11:14:02 2015 +0100 - - Bug#2262 String Object compile error - - Signed-off-by: Kevin Moloney - -commit da0eaa49fe58a431fdf03403355cf71ab671b9bd -Author: Kevin Moloney -Date: Wed Jul 29 14:06:08 2015 +0100 - - ATLEDGE-209 tone() bug when frequency = 0 - - Signed-off-by: Kevin Moloney - -commit bc7fbd8adbf3058f4dc3bae0c9a1568b3e98ba3f -Author: David Hunt -Date: Thu Jul 30 11:32:26 2015 +0100 - - Bug# 2265 - fixed parseInt(skipchar) Compilation error - - Signed-off-by: David Hunt - -commit 67e7f2e9843096b03110cefdc5e702d896bf5be6 -Author: David Hunt -Date: Wed Jul 29 10:07:37 2015 +0100 - - Added support for printing long long values, e.g. millis() - - Signed-off-by: David Hunt - -commit abe68dea6befe32fc4ce2951c5bb2e89c2e4bd45 -Author: Bogdan Pricop -Date: Wed Jul 29 12:14:56 2015 +0100 - - ARC init: enable FIRQ - - * Change interrupts priority from 1 to 0 (FIRQ) - * Modify linker script: add stack area for FIRQ - Define a stack memory section in SRAM and split it between run time - and FIRQ stacks. - * Replace existing generic ISR; it initialises the SP, decodes the IRQ - source and finally jumps and links to the specific ISR. - * Modify _IsrTableEntry structure: get rid of ISR parameter. - - Signed-off-by: Bogdan Pricop - -commit 47fab774f96532134ad74a8c16bcecf6c9fdefbf -Author: Bogdan Pricop -Date: Mon Jul 27 14:37:09 2015 +0100 - - ATLEDGE-169: Change interrupt routing mask - - Issue: The LMT receives an interrupt enabled by SS because the routing - mask is disabled for both cores (the interrupt is routed to both - ARC and LMT) and it masks it because it is a "spurious irq" from - its point of view. - - Fix: When the ARC enables an interrupt source, it configures the interrupt - router to route the interrupt only to ARC core. - - Signed-off-by: Bogdan Pricop - -commit ab97250c0df109b68f71983fe0046b2d16f839fc -Author: Kevin Moloney -Date: Tue Jul 28 11:58:41 2015 +0100 - - Bug #2184 Serial.begin() config not implemented - - Signed-off-by: Kevin Moloney - -commit e542703d8458e5769ab6d8aa38a9a565aaff83a3 -Author: Kevin Moloney -Date: Fri Jul 24 12:43:34 2015 +0100 - - ATLEDGE-177-char-Signed - - Char is now signed - Boolean modified to be a bool not an int - - Signed-off-by: Kevin Moloney - -commit afcd5f7932f62f7692121b9dcce8b67972a2fac6 -Author: Kevin Moloney -Date: Thu Jul 16 15:45:30 2015 +0100 - - Enable/disable_internal-pullup_with-digitalWrite - - Emulate Arduino pull-up control, by allowing a digitalWrite() on an INPUT pin to toggle the internal pull-up - Signed-off-by: Kevin Moloney - -commit 925ddb77900e4bb8ede5f59daf7656073c81bb2b -Author: Dino Tinitigan -Date: Wed Jul 29 15:01:15 2015 -0700 - - ATLEDGE-224 port avr/pgmspace.h - - many libraries fail to compile because they need avr/pgmspace.h. - Galileo/Edison had the same issue and the same code can be ported over. - -commit 6875a7ffed5d9dd038037148efccc0ef1fe2f2f6 -Author: Kevin Moloney -Date: Wed Jul 15 09:26:12 2015 +0100 - - Bug fix to allow three methods for Serial.Write() - - Signed-off-by: Kevin Moloney - -commit a50e097c7c7d217b4d0b9135a9d2e970f435edcf -Author: Bogdan Pricop -Date: Tue Jul 14 23:05:16 2015 +0100 - - Arduino core functions: pulseIn - - Change pulseIn implementation to be inline with the new time functions - which provide 64 bit timestamps. - - Signed-off-by: Bogdan Pricop - -commit 895d7b94b167cafbd6306ce79942c7f2bc811181 -Author: Dan O'Donovan -Date: Wed Jul 15 14:17:35 2015 +0100 - - Temporary fix is to compile with -O0 instead (optimisation disabled). - Further investigation required. - - Signed-off-by: Dan O'Donovan - -commit 9bf75da8684e5917d343795eb9f1156d28eb89b4 -Author: Bogdan Pricop -Date: Wed Jul 15 12:24:01 2015 +0100 - - libarc32drv_edu.a: Update of pre-compiled driver library. - - Signed-off-by: Bogdan Pricop - -commit 33342070baf9a8b5dadc26d9691499d30461be68 -Author: Bogdan Pricop -Date: Tue Jul 14 20:59:11 2015 +0100 - - Time related Arduino core functions: Re-write - - * Change Timer0 configuration according to Alexandre D'Alton suggestion: - use it as a free-run timer which delivers interrupts every 0xFFFFFFFF - clock ticks and its ISR increments a global variable; in this way a - 64-bit virtual RTC is created. The downside is the delay() function - cannot take advantage of ARc's power management features anymore. - - * Change delay() implementation to make use of 64-bit time-stamp. - - * Change delayMicroseconds to use Timer0 counter value and unsigned - arithmetic's features to handle the overflow case. - The credit goes to Dan O'Dononvan. - - * Add function to get an atomic 64-bit time-stamp. - - * Change millis() and micros() implementation to use the 64-bit time-stamp. - - Signed-off-by: Bogdan Pricop - -commit 7405797b110db61e838dc92141f7d8082b159c3a -Author: Dan O'Donovan -Date: Fri Jul 10 09:45:36 2015 +0100 - - Updates and fixes to Component Framework code for Arduino on ARC - - - Sync'd CFW code with Thunderdome code base from 01-July-2015 - - Fixed issue with mailbox interrupts halting CPU, caused by bug in scss_registers.h - - Added CFW test service client to facilitate CFW sanity check (requires LMT build running Test service) - - Signed-off-by: Dan O'Donovan - -commit cdc73f8e532be8ea3c5791fe575bc30381a6e0dc -Author: David Hunt -Date: Thu Jul 9 14:29:46 2015 +0100 - - Fixed redmine 2150 - inByte variable declaration issue - - Signed-off-by: David Hunt - -commit 4489870248153835baa05bcc6956f0f0f81ffb37 -Author: David Hunt -Date: Thu Jul 9 16:03:43 2015 +0100 - - Remove unneeded main.c from driver library. - - Signed-off-by: David Hunt - -commit ecbe76a8febfd31a8b0f1d3dd30fa6545bafafec -Author: David Hunt -Date: Thu Jul 9 14:01:45 2015 +0100 - - Fixed macro for enabling pullups, plus fixed pin# for IO8 - - Signed-off-by: David Hunt - -commit 3193ffc31ab65125c66337aadef85c6da3d22d2b -Author: Bogdan Pricop -Date: Fri Jul 10 15:52:33 2015 +0100 - - Update of pre-compiled driver library. - - Signed-off-by: Bogdan Pricop - -commit c968b172d1b0ca86936ae34793d701ac2fee778f -Author: David Hunt -Date: Wed Jul 8 17:31:48 2015 +0100 - - Reduced footprint of blink sketch from 65K to 25K by removing call to sprintf - - Signed-off-by: David Hunt - -commit 4fc372ef78d1929fc1ff12d1c1a806c1dc236bdd -Author: Kevin Moloney -Date: Wed Jul 8 15:31:45 2015 +0100 - - Remove hardware loop-back flag set in SPI.begin() - - This was added previously for internal testing, but was not fully removed prior to release - - Signed-off-by: Kevin Moloney - -commit f88353e642f8e88361a055fdecdf8fef3f41e337 -Author: David Hunt -Date: Wed Jul 8 13:54:26 2015 +0100 - - Fix for malloc(), round up to 4k Pages. - - Signed-off-by: David Hunt - -commit 13cd850d73127de56c31945a3e24f78d99ef7f2e -Author: David Hunt -Date: Tue Jul 7 16:19:38 2015 +0100 - - ATLEDGE-158 - Serial.end() not working - - Signed-off-by: David Hunt - -commit 3f3c4c207f1342ffd17e65e0025f4ab8a9e998dc -Author: Kevin Moloney -Date: Mon Jul 6 13:02:12 2015 +0100 - - Modify pin 19's pin description for EVT board - - Signed-off-by: Kevin Moloney - -commit d5bf0344ab8762b716e264eec680970b708f7f1b -Author: Kevin Moloney -Date: Mon Jul 6 12:50:20 2015 +0100 - - Modifify pin 19's pin discription for EVT board - - Signed-off-by: Kevin Moloney - -commit a1d94f84bfa3cb7fa164e79b14ba89227d24dea0 -Author: Bogdan Pricop -Date: Mon Jul 6 14:04:56 2015 +0100 - - ATLEDGE-142: noInterrupt() not working: change delay() behaviour. - - * Change delay() implementation NOT to enable interrupts before putting the - ARC core into sleep state. - * delay() still cannot be called from a "no interrupts" context, but it - doesn't overwrite the user's settings anymore. In this way, if delay() - is called in a no interrupts context, the ARC sleeps for ever. - - Signed-off-by: Bogdan Pricop - -commit de8fab5b63ea28e84055a33b5201d0d44bad0dab -Author: Kevin Moloney -Date: Mon Jul 6 11:46:10 2015 +0100 - - Include Knob example in Servo library - - Signed-off-by: Kevin Moloney - -commit ae93ed8d139f36825dcc80bb705d7066af7415c2 -Author: David Hunt -Date: Thu Jul 2 13:56:13 2015 +0100 - - Shifting out wrong bit index. Should be 7-i instead of 8-i - - Signed-off-by: David Hunt - -commit 57d3d3dae78e31623c5d60a770f90938df6c1a8d -Author: Kevin Moloney -Date: Thu Jul 2 10:02:38 2015 +0100 - - Implement Servo Library for Intel EDU - - Signed-off-by: Kevin Moloney - -commit 7ae6ecfc2fec406ea15f8af6d83fb31c3fe45b6e -Author: Dan O'Donovan -Date: Thu Jul 2 13:07:37 2015 +0100 - - SPI library implementation for Intel EDU - - The following functions/features have been implemented: - - * Standard SPI API: - - SPI.begin() - - SPI.setClockDivider() - - SPI.setDataMode() - - SPI.transfer() (single-byte and multi-byte versions) - - SPI.transfer16() - - SPI.end() - * SPI Transaction API - - SPI.beginTransaction() - - SPI.endTransaction() - - SPI.usingInterrupt() - - SPI.notUsingInterrupt() - * New Intel EDU specific API functions - - SPI.transfer24() - - SPI.transfer32() - - Known issues/limitations: - * LSBFIRST bit order not supported in hardware, requires (slower) software emulation - * SPI Transaction API has not been fully tested at time of writing - - Signed-off-by: Dan O'Donovan - -commit 1cdc4f0688be19e32396d50e731ff3cf16b7d8a6 -Author: Bogdan Pricop -Date: Tue Jun 30 15:27:49 2015 +0100 - - Update source code with WRS restrictive license headers - - * Delete arcv2_timer1.[c|h] driver - not used at this stage. - * arcv2_timer0 driver: update license header to WRS permissive license; - arcv2_timer0.h doesn't exist anymore in the latest Tiny Mountain source - code, but we use it, stick in the same WRS permissive license header. - * For linker.cmd and flsh.ld linker scripts: replace license header with - the WRS permissive one fomr the original linker.cmd files from - Tiny Mountain OS - * aux_regs.h - derived work from aux_reg.h from Tiny Mountain OS; - change license header to WRS permissive. - * remove license header as in the original files from thunderdome code - base: compiler.h, portable.h - - Signed-off-by: Bogdan Pricop - -commit c338e4b94124bb619896e8a8c80217e790717ca7 -Author: Kevin Moloney -Date: Wed Jun 17 11:02:39 2015 +0100 - - ADC Optimization (clock ratio) - - Signed-off-by: Kevin Moloney - -commit 98fdd4c83861e084c7819d08c3f90e8600675bc6 -Author: Kevin Moloney -Date: Tue Jun 30 14:22:47 2015 +0100 - - Arduino core functions: tone/noTone implementation.. - - * tone/noTone implemented using ARC's Timer1. - - Signed-off-by: Kevin Moloney - -commit 32ae6276ac86dd24961eb010297e2239b5ee8b80 -Author: Bogdan Pricop -Date: Fri Jun 26 15:43:49 2015 +0100 - - Arduino core functions: pulseIn(pin, value, timeout) - - Implementation of pulseIn() Arduino core function using GPIO interrupts - and micros() for time-stamping. - NOTES: - * The performance is not as good as on Arduino UNO. It should be - re-measured after following optimisation: - * Enable FIRQ (Fast Interrupts) on ARC. - * Optimise the GPIO interrupts - * Optimise the Timer0 interrupt - * Empirical measured accuracy is 10 microseconds. - - Signed-off-by: Bogdan Pricop - -commit cbc40d38c6cd1ef5e433e378c99cc95f19e31fc9 -Author: Dino Tinitigan -Date: Fri Jun 26 16:18:29 2015 -0700 - - Update sketchUploader version - - Updated sketchUploader version in platform.txt - -commit 875ec047d28c3059ca215e261bd28f780d0b48d5 -Author: David Hunt -Date: Fri Jun 26 13:59:13 2015 +0100 - - Change to see new VID/PID for ACM devices for baud change reset - - Must be used with firmware that presents 8087:0A9F as PID/VID - - Signed-off-by: David Hunt - -commit 11dfe699887f47f65276f880b5a4aaa01f282baa -Author: Bogdan Pricop -Date: Tue Jun 23 17:56:55 2015 +0100 - - ATLEDGE-75 - Time: micros() - - * Re-write micros() function in assembly for a 15-20% performance - increase. It should take less than a microsecond now. - - Signed-off-by: Bogdan Pricop - -commit 39eb85ad388a7324a24bb17598e2b6b745f209ab -Author: Kevin Moloney -Date: Tue Jun 23 12:44:16 2015 +0100 - - ATLEDGE-84 - Interrupt framework: make interrupt.h C++ compatible. - - Signed-off-by: Kevin Moloney - -commit 63f6a7698502e7b14798e261be1f2407e06e6a5f -Author: Bogdan Pricop -Date: Wed Jun 17 11:01:10 2015 +0100 - - ATLEDGE-82 - ARC Init: Re-enable I-CACHE - - Enable Instruction cache and invalidate it after interrupt unit device - initialisation - -commit 7f98355166f010b00a64cbf7a949b84d96bec05a -Author: David Hunt -Date: Thu Jun 18 14:13:17 2015 +0100 - - updated pre-compiled driver library. - - Signed-off-by: David Hunt - -commit 653e96c9d9b206297d5c631180c41b4cb13e2860 -Author: David Hunt -Date: Thu Jun 18 13:58:40 2015 +0100 - - ATLEDGE-27 - Wire (i2c) Library - Moving header files. - - Signed-off-by: David Hunt - -commit 31e4e972141524297a096b38d067593a72af5f6e -Author: Bogdan Pricop -Date: Wed Jun 17 10:50:00 2015 +0100 - - ATLEDGE-75 - Time : delayMicroseconds - - Implement delayMicroseconds function in Assembly and using the ARC's - instructions timing as a reference. - The instruction's timing was computed with a scope and using infinite - loops, meaning the code most likely used the advantage of ARC'c cache. - - Function's accuracy is +- 0.15 microseconds in the above stated conditions. - The accuracy can be affected by cache miss annd/or interrupts handling. - It doesn't disable interrupts and it doesn't use any of the SoC power - management features. - - Signed-off-by: Bogdan Pricop - -commit c165e1c12c3918f7dd7e10e05be9550b9fbf1777 -Author: David Hunt -Date: Thu Jun 18 10:20:57 2015 +0100 - - ATLEDGE-27 - Wire (i2c) Library - - Signed-off-by: David Hunt - -commit 648ca2ebcced299f53485e384ea590ff033aafc7 -Author: David Hunt -Date: Tue Jun 16 09:43:33 2015 +0100 - - Updated licence headers according to latest files from Intel. - - Signed-off-by: David Hunt - -commit 4c3f58a7e39ee3da55e83d104897b5abdf60c76f -Merge: c2b4f74 d7c5a80 -Author: David Hunt -Date: Tue Jun 9 16:29:48 2015 +0100 - - ATLEDGE-82 - merge in change of start address for arc code - - Signed-off-by: David Hunt - -commit d7c5a80233d7f574f9060d49f26f102634a50a64 -Author: Bogdan Pricop -Date: Tue Jun 9 15:27:14 2015 +0100 - - ATLEDGE-82 - Init: Change linker scripts - - Modify the start address and the size of the flash area allocated for - ARC core, according to ATLASEDGE/WEEKLY/WW23 tag - -commit c2b4f74efc0cafe3ad4aebd365b2457284a7dafe -Merge: b408ea9 a801dd7 -Author: David Hunt -Date: Tue Jun 9 15:16:08 2015 +0100 - - Merge branch 'fix_1999' - fix for INPUT_PULLUP - - Signed-off-by: David Hunt - -commit b408ea97cbd30aae038381f930995b0f3869702e -Merge: d45290e 539a7c2 -Author: David Hunt -Date: Mon Jun 8 14:20:29 2015 +0100 - - feat_irq_uart: Merged serialEvent() functionality. - - Signed-off-by: David Hunt - -commit 539a7c26495321edae81804e9432a5d0c7f88fbb -Author: David Hunt -Date: Mon Jun 8 13:48:55 2015 +0100 - - feat_irq_uart - Added in serialEvent() functionality - - Signed-off-by: David Hunt - -commit a801dd73b1e5eb41a0bcdb3c50b5a5691ede8f9e -Author: Dan O'Donovan -Date: Mon Jun 8 13:48:20 2015 +0100 - - INPUT_PULLUP doesn't appear to enable an internal pull-up resistor - - Incorrect register base address used by SET_PIN_PULLUP macro in - scss_registers.h to enable/disable internal pull-ups - - Signed-off-by: Dan O'Donovan - -commit d45290e31df7def5c65e1951bd8d3e69347b3843 -Author: David Hunt -Date: Fri Jun 5 13:49:06 2015 +0100 - - Update of precompiled driver library - - Signed-off-by: David Hunt - -commit 88baee7283dc174131c42b7d0256753f6ee9a906 -Merge: ac134d6 79d1ab1 -Author: David Hunt -Date: Fri Jun 5 11:22:49 2015 +0100 - - ATLEDGE-84 - Interrupt framework - - Arduino functions implemented: - * attachInterrupt(Arduino_Pin, handler, mode): - * it works for all Arduino GPIO (IO0 - IO19) - * CHANGE mode is supported only for SoC GPIOs (hardware limitation: - SS doesn't support interrupt generation on both rising and falling - edges.) - * detachInterrupt() - * interrupts() - * noInterrupts() - - Above functions behaviour is according to Arduino website description. - For details please see below references: - http://www.arduino.cc/en/Reference/AttachInterrupt - http://www.arduino.cc/en/Reference/DetachInterrupt - http://www.arduino.cc/en/Reference/Interrupts - http://www.arduino.cc/en/Reference/NoInterrupts - - This implementation uses ss_gpio and soc_gpio drivers from thunderdome. - - Signed-off-by: Bogdan Pricop - -commit 79d1ab16a6687015893a83f8bba079fff0e3be6e -Author: Bogdan Pricop -Date: Fri Jun 5 10:36:30 2015 +0100 - - ATLAEDGE-84: Interrupt framework - - Arduino functions implemented: - * attachInterrupt(Arduino_Pin, handler, mode): - * it works for all Arduino GPIO (IO0 - IO19) - * CHANGE mode is supported only for SoC GPIOs (hardware limitation: - SS doesn't support interrupt generation on both rising and falling - edges.) - * detachInterrupt() - * interrupts() - * noInterrupts() - - Above functions behaviour is according to Arduino website description. - For details please see below references: - http://www.arduino.cc/en/Reference/AttachInterrupt - http://www.arduino.cc/en/Reference/DetachInterrupt - http://www.arduino.cc/en/Reference/Interrupts - http://www.arduino.cc/en/Reference/NoInterrupts - - This implementation uses ss_gpio and soc_gpio drivers from thunderdome. - - Signed-off-by: Bogdan Pricop - -commit ac134d6f742532d9c43783b29c5d145ab8306054 -Merge: 77d7a18 d7cbbf4 -Author: David Hunt -Date: Fri Jun 5 10:09:17 2015 +0100 - - ATLEDGE-14 - Added wiring-shift functionality - - shiftIn() and shiftOut() - - Signed-off-by: David Hunt - -commit d7cbbf4f4bb3abee46f887ee8adbcb9e851594c9 -Author: davids -Date: Fri Jun 5 09:23:44 2015 +0100 - - implement wiring_shift.c() - -commit da2e44848abcb12f4f6c9a891a55425b330fdb25 -Author: davids -Date: Thu Jun 4 15:20:53 2015 +0100 - - implement wiring_shift() - -commit 77d7a180c96bb25b69cc82de14e37104dcc77707 -Merge: de3666c 941f594 -Author: David Hunt -Date: Thu Jun 4 14:44:52 2015 +0100 - - ATLEDGE-83 - Integrating Component Framework IPC with Arduino run-time lib - - Signed-off-by: David Hunt - -commit de3666c598909ffd19079a6e5390559e3435812b -Merge: 59634af 325da1d -Author: David Hunt -Date: Thu Jun 4 14:27:36 2015 +0100 - - ATLEDGE-15 - Analog In functions - -commit 941f594f9fbdc201af1539678e3cd30c9dc8b09d -Author: Dan O'Donovan -Date: Wed Jun 3 10:25:40 2015 +0100 - - cfw: Integrating Component Framework IPC with Arduino run-time lib - - Adding glue layer to initialise CFW and enable interrupt-driven - handling of received messages from LMT core - - Signed-off-by: Dan O'Donovan - -commit cdf2107957c9c8d17ee0667cb754a31d242c75a5 -Author: davids -Date: Thu Jun 4 11:35:13 2015 +0100 - - implement wiring_shift() - -commit 325da1ded53cd4a533c7cd4c2782fa3fbe1dc30c -Author: Kevin Moloney -Date: Thu Jun 4 03:35:04 2015 -0700 - - analogRead fixes - -commit 57310fed13bdf80a77a24b663925ff28b4595bc2 -Author: Kevin Moloney -Date: Thu Jun 4 03:15:40 2015 -0700 - - analogRead fixes: - - Signed-off-by: Kevin Moloney - -commit 59634aff378b89deb36041d94aed7132e5588b37 -Merge: e60cba4 530ba96 -Author: David Hunt -Date: Thu Jun 4 10:17:03 2015 +0100 - - ATLEDGE-76 - Serial functions, addition of interrupt handling - -commit 530ba96203602f7743aac12175c08e4b5ddf0209 -Author: David Hunt -Date: Thu Jun 4 09:52:07 2015 +0100 - - feat_irq_uart: minor changes after code reviews - - Signed-off-by: David Hunt - -commit f0b0954a09cf5808fa87abf8052a088f5ee5e5bd -Author: davids -Date: Wed Jun 3 17:42:21 2015 +0100 - - implement wiring_shift() - -commit 65e2c057260887613e173a274bb439c92bb8141c -Author: Kevin Moloney -Date: Wed Jun 3 07:24:48 2015 -0700 - - adc: Implement analogRead() - - Signed-off-by: Kevin Moloney - -commit acde269f698e087f6e2d23f498576c720dc91c07 -Author: David Hunt -Date: Wed Jun 3 11:19:39 2015 +0100 - - feat_irq_uart: enable UART interrupt handling - - Signed-off-by: David Hunt - -commit e60cba4bcb0e03c4fd8158d9b1226c023e016f19 -Author: David Hunt -Date: Thu May 28 13:56:49 2015 +0100 - - feat_strings: add -fno-exceptions for C++ code to remove linker errors - - Signed-off-by: David Hunt - -commit 81123d478ae1747a9f1148c0270395d95ba62ef8 -Author: David Hunt -Date: Thu May 28 12:03:33 2015 +0100 - - feat_strings: Compiler & linker flags for String builds - - Signed-off-by: David Hunt - -commit d72c26f1c38ef35556dd1faf832b0be8e9c7dd60 -Merge: 07a494f d1289bb -Author: Bogdan Pricop -Date: Wed May 27 18:05:56 2015 +0100 - - ATLEDGE-82: Init - change ARC memory layout. - - * Change start address and size of the Flash area allocated to the ARC core - according to new memory layout received from Toulouse. - - Signed-off-by: Bogdan Pricop - -commit d1289bbdbfdad34ecac16de752237aba65a942ef -Author: Bogdan Pricop -Date: Wed May 27 09:06:25 2015 +0100 - - ATLEDGE-82: Init - change ARC memory layout. - - * Change start address and size of the Flash area allocated to the ARC core - according to new memory layout received from Toulouse. - - Signed-off-by: Bogdan Pricop - -commit 07a494f00158248b1100ad4ba1e240ad0221bfc0 -Author: David Hunt -Date: Thu May 21 15:43:55 2015 +0100 - - Adding updated pre-compiled driver library. - Cleaning up after code reviews and validation of build. - - Signed-off-by: David Hunt - -commit e77ff7a37c2bd3a00199471cbc328d7bf338b87b -Author: David Hunt -Date: Thu May 21 14:08:03 2015 +0100 - - Updated due to code reviews - - * Fix delay() issue - Issue: If value of microsecond is Timer0 limit, arcv2_timer0_count_get() - microseconds condition will always be true, spinning for ever. - The main issue is that the time between 2 consecutive readings of - COUNT0 register is around 90 ticks and the Timer may overflow - before the next reafing of COUNT0 if last values read > LIMIT - 90. - Fix: Take in consideration the number of overflows. If TIMER0 overflows - just leave the loop (even if COUNT0 < microseconds) - * Restructure a bit arcv2_timer0_enable() function in order to reflect the - procedure described in chapter 3.3.76.1 of DesignWare ARCv2 ISA - Programmer's Reference Manual. - Make sure Timer0 initialisation clears the counter register as well. - * code review - added yield via hooks.c - * added back in strip stage when building - * Remove unused code in os.h, os_types.h and pf_init.h - * Uncomment call to yield() from delay() Arduino function; yield() is - added for sake of compatibility. - * Remove dead code from timer0 init function. - * Commented out enable instruction cache - * Added Copyright header to wiring.c - * Setup automatic (hardware) context saving feature for non-fast interrupts - * Serial Communication API Initial checkin of Serial.read() and Serial.write() - using header pins 0 and 1 - * Eliminated some compiler warnings. - - Signed-off-by: David Hunt - -commit c4fad8ecba577bfd28171c31b14aab47a465754a -Merge: 9ec6faf e187e5e -Author: David Hunt -Date: Tue May 19 14:05:37 2015 +0100 - - ATLEDGE82 init code - - small cleanup around interrupt init code. - - Signed-off-by: David Hunt - -commit e187e5ed742bd85ecafa6cde5dfa3ad35ec90bb9 -Author: Dan O'Donovan -Date: Tue May 19 13:42:45 2015 +0100 - - ATLEDGE-82 init code - - Minor tidy-up to interrupt init code - - Currently, timer init functions enable global interrupts. This should - be done in the interrupt setup code during the chip init sequence. - - Signed-off-by: Dan O'Donovan - Signed-off-by: David Hunt - -commit 9ec6fafc1665504fe90fc99623f1348267caeed9 -Author: David Hunt -Date: Tue May 19 13:13:45 2015 +0100 - - ATLEDGE-82 init code - - final cleanup before push. no functional changes. - -commit d37176a3b8483127244c5c72bad861ef4598d756 -Author: David Hunt -Date: Tue May 19 12:56:33 2015 +0100 - - ATLEDGE-82 init code - - Interrupt handling - add hw context saving - - * Disable instruction cache (we don't need it at this stage) - * Setup automatic (hardware) context saving feature for non-fast - interrupts. - * Set optimization to none (-O0) in Makefile. - - build: Eliminating compiler warnings - - Fix interrupt handling bug - - Issue: - A simple blink sketch having a 10 ms delay between blinks crashed - after a few seconds. - It looks like the stack got corrupted. - Root cause: - The generic assembly hardware ISR, "_do_isr", was not properly - written; the "sp" was not properly handled. - Solution: - Replace the assembly hardware ISR with a C function having - "__attribute__((interrupt("ilink")))" - - Note: - We need this generic hardware ISR because the IVT is in flash and - we cannot change it at runtime. - - Allocating more SRAM to ARC (syncing with latest Thunderdome mem allocation) - - build: Removing sdata section declaration from linker scripts - - ARC doesn't support an SDATA section bigger than 1024 bytes, and - we overflow this too easily when we link in code that hasn't been - compiled with the -mno-sdata compiler flag. So we will ensure that - we only use code compiled with -mno-sdata (including standard C lib) - and we remove this section declaration to weed out any exceptions to this. - In other words, if we link any code that tries to use the sdata section, - we will see a linker error. - - build: Adding HEAP section - - Adding heap section with start/end marker symbols to satisfy - malloc() and friends - - Signed-off-by: David Hunt - -commit d4fe5885d8e2bebd071e4e7d378d3fbf8e85a2eb -Author: Bogdan Pricop -Date: Mon May 18 11:12:35 2015 +0100 - - ATLEDGE-82 init code - - Interrupt handling - add hw context saving - - * Disable instruction cache (we don't need it at this stage) - * Setup automatic (hardware) context saving feature for non-fast - interrupts. - * Set optimization to none (-O0) in Makefile. - - Signed-off-by: Bogdan Pricop - Signed-off-by: David Hunt - - Author: Bogdan Pricop - -commit 82c3529091c7c2d1f13d1d55c3d31cdf0aa56840 -Author: Dan O'Donovan -Date: Fri May 15 16:25:07 2015 +0100 - - ATLEDGE-82 init code for arc - - Eliminating compiler warnings and cleanup. - - Signed-off-by: Dan O'Donovan - Signed-off-by: David Hunt - - Conflicts: - system/libarc32_edu/drivers/ns16550.c - system/libarc32_edu/drivers/uart.h - system/libarc32_edu/main.c - -commit 4a29f5c30fb52e433f30c1ab7ba30244953e2d05 -Author: Dan O'Donovan -Date: Fri May 15 14:46:08 2015 +0100 - - cfw: Importing Component Framework library code from Thunderdome - - Framework code extracted from the following commit ID in - Intel's thunderdome git repo: 8b198659675cd820072bdeb00d0c5de87c6f6ee2 - - This brings the necessary library code into the source tree, but not - yet integrated in the Arduino run-time environment (to follow shortly). - - Signed-off-by: Dan O'Donovan - -commit c2f3e0413c6e1c4567faedd5b2e7a8308fc8c42e -Author: Bogdan Pricop -Date: Fri May 15 14:24:10 2015 +0100 - - ATLEDGE-82 init code - Fix interrupt handling bug. - - Issue: - A simple blink sketch having a 10 ms delay between blinks crashed - after a few seconds. - It looks like the stack got corrupted. - Root cause: - The generic assembly hardware ISR, "_do_isr", was not properly - written; the "sp" was not properly handled. - Solution: - Replace the assembly hardware ISR with a C function having - "__attribute__((interrupt("ilink")))" - - Note: - We need this generic hardware ISR because the IVT is in flash and - we cannot change it at runtime. - - Signed-off-by: Bogdan Pricop - Signed-off-by: David Hunt - -commit 723be266ea682a413efca74e5b69190e17bf2a65 -Author: Dan O'Donovan -Date: Fri May 15 14:49:26 2015 +0100 - - Allocating more SRAM to ARC (syncing with latest Thunderdome mem allocation) - - Signed-off-by: Dan O'Donovan - -commit b9bc0fb221574f5f5a52abfa1d9d2b3da85fc1ba -Author: Bogdan Pricop -Date: Tue May 12 21:31:11 2015 +0100 - - .gitignore file added to repo. - - Signed-off-by: Bogdan Pricop - -commit bd205779390d879a5e866941b840e5094204c97c -Author: Dan O'Donovan -Date: Tue May 12 17:47:54 2015 +0100 - - build: Adding HEAP section - - Adding heap section with start/end marker symbols to satisfy - malloc() and friends - - Signed-off-by: Dan O'Donovan - -commit 4139a2efcdee1b210933b7abeee2bea084d1b369 -Author: David Hunt -Date: Tue May 19 12:06:17 2015 +0100 - - ATLEDGE-75 Arduino time related functions. - - A first draft for the following functions: - * delay() - * millis(); - * micros(); - Not yet implemented: - * delayMicroseconds. - - Author: Bogdan Pricop - - Signed-off-by: Bogdan Pricop - Signed-off-by: David Hunt - -commit 3689741b718ce769a50515d661a7ebf13cf733be -Author: David Hunt -Date: Tue May 19 11:58:55 2015 +0100 - - ATLEDGE-24 Serial Communication API - - Intial release of serial functionality, Serial.write, print, println, read - Also added files required, Ringbuffer (not used yet), Print, etc. - Also some small changes to make Intel toolchain work, still more work needed. - - Signed-off-by: David Hunt - -commit 57550ebb85d6f69dc52cefcf38f47a7995f708b3 -Author: David Hunt -Date: Tue May 19 11:52:19 2015 +0100 - - ATLEDGE-83 Component Framework integration into bare metal - - Author: Dan O'Donovan - Date: Fri May 15 14:46:08 2015 +0100 - - Framework code extracted from the following commit ID in - Intel's thunderdome git repo: 8b198659675cd820072bdeb00d0c5de87c6f6ee2 - - This brings the necessary library code into the source tree, but not - yet integrated in the Arduino run-time environment (to follow shortly). - - Signed-off-by: Dan O'Donovan - Signed-off-by: David Hunt - -commit ed4bf0214f53df5b8c255ed6f575fcdc6bf361d3 -Author: David Hunt -Date: Tue May 19 11:48:40 2015 +0100 - - ATLEDGE-15 basic analog functions - - Adding analogWriteResolution and analogReadResolution - - Signed-off-by: Dan O'Donovan - Signed-off-by: David Hunt - -commit 32aff42d8023cafcb6b5b86820e455b1914ecf55 -Author: David Hunt -Date: Wed May 6 17:14:51 2015 +0100 - - ATLEDGE-62 Create Initial Release of Arduino Corelibs - - Adding licence headers to those files that don't have any. - - Signed-off-by: David Hunt - -commit 09a6c2ea91c99a7f8e3aff45cb4bc7f3e501a715 -Author: David Hunt -Date: Wed May 6 14:50:40 2015 +0100 - - ATLEDGE-62 Create Initial Release of Arduino Corelibs - - Allows Arduino IDE to build Blink Sketch - Includes pinMode(), digitalWrite() and delay() - Allows download via JTAG - - Signed-off-by: David Hunt - -commit a5ca45cc6a31e8775363fa6c3f0c32e337945a43 -Author: Dino Tinitigan -Date: Thu Apr 16 14:05:33 2015 -0700 - - Initial commit of core files - - Initial commit of core files - -commit 631d399bf35241b823ae74b3b4dc6b27d303078e -Author: Calvin Park -Date: Thu Apr 16 13:29:04 2015 -0700 - - Initial commit From 6887ba3be67e6db1b0e702fa11edcf7dd7f34f0b Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Thu, 29 Dec 2016 16:28:35 -0500 Subject: [PATCH 078/125] Jira 872, BLE sample sketch update from Arduino, PR 375 --- .../central/led_control/led_control.ino | 17 ++- .../BatteryMonitor.ino} | 64 +++-------- .../peripheral/ButtonLED/ButtonLED.ino | 94 ++++++++++++++++ .../peripheral/CallbackLED/CallbackLED.ino | 95 ++++++++++++++++ .../CurieBLE/examples/peripheral/LED/LED.ino | 99 +++++++++++++++++ .../CurieBLE/examples/peripheral/led/led.ino | 101 ------------------ .../peripheral/led_callback/led_callback.ino | 99 ----------------- .../BatteryMonitor_Central.ino | 0 .../CentralDouble/CentralDouble.ino | 0 .../IMUBleCentral/IMUBleCentral.ino | 0 .../IMUBleNotification/IMUBleNotification.ino | 0 .../PeripheralDouble/PeripheralDouble.ino | 0 libraries/CurieBLE/keywords.txt | 2 + 13 files changed, 313 insertions(+), 258 deletions(-) rename libraries/CurieBLE/examples/peripheral/{BatteryMonitor_Notification/BatteryMonitor_Notification.ino => BatteryMonitor/BatteryMonitor.ino} (67%) create mode 100644 libraries/CurieBLE/examples/peripheral/ButtonLED/ButtonLED.ino create mode 100644 libraries/CurieBLE/examples/peripheral/CallbackLED/CallbackLED.ino create mode 100644 libraries/CurieBLE/examples/peripheral/LED/LED.ino delete mode 100644 libraries/CurieBLE/examples/peripheral/led/led.ino delete mode 100644 libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino rename libraries/CurieBLE/examples/{central => test}/BatteryMonitor_Central/BatteryMonitor_Central.ino (100%) rename libraries/CurieBLE/examples/{central => test}/CentralDouble/CentralDouble.ino (100%) rename libraries/CurieBLE/examples/{central => test}/IMUBleCentral/IMUBleCentral.ino (100%) rename libraries/CurieBLE/examples/{peripheral => test}/IMUBleNotification/IMUBleNotification.ino (100%) rename libraries/CurieBLE/examples/{peripheral => test}/PeripheralDouble/PeripheralDouble.ino (100%) diff --git a/libraries/CurieBLE/examples/central/led_control/led_control.ino b/libraries/CurieBLE/examples/central/led_control/led_control.ino index fd8ce3c2..0ef64ff6 100644 --- a/libraries/CurieBLE/examples/central/led_control/led_control.ino +++ b/libraries/CurieBLE/examples/central/led_control/led_control.ino @@ -39,7 +39,7 @@ void setup() { Serial.println("BLE Central - LED control"); // start scanning for peripherals - BLE.scan(); + BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214"); } void loop() { @@ -56,16 +56,13 @@ void loop() { 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(); + // stop scanning + BLE.stopScan(); - controlLed(peripheral); + controlLed(peripheral); - // peripheral disconnected, start scanning again - BLE.scan(); - } + // peripheral disconnected, start scanning again + BLE.scanForUuid("19b10000-e8f2-537e-4f6c-d104768a1214"); } } @@ -126,6 +123,8 @@ void controlLed(BLEDevice peripheral) { } } } + + Serial.println("Peripheral disconnected"); } /* diff --git a/libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino b/libraries/CurieBLE/examples/peripheral/BatteryMonitor/BatteryMonitor.ino similarity index 67% rename from libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino rename to libraries/CurieBLE/examples/peripheral/BatteryMonitor/BatteryMonitor.ino index a44cd6b6..e898e2aa 100644 --- a/libraries/CurieBLE/examples/peripheral/BatteryMonitor_Notification/BatteryMonitor_Notification.ino +++ b/libraries/CurieBLE/examples/peripheral/BatteryMonitor/BatteryMonitor.ino @@ -4,7 +4,7 @@ */ /* - * Sketch: BatteryMonitor_Notification.ino + * Sketch: BatteryMonitor.ino * * Description: * This sketch example partially implements the standard Bluetooth @@ -13,11 +13,6 @@ * For more information: * https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx * - * Notes: - * - * - Expected to work with BatteryMonitor_Central sketch. - * You can also use an android or IOS app that supports notifications. - * */ #include @@ -25,35 +20,39 @@ BLEService batteryService("180F"); // BLE Battery Service // BLE Battery Level Characteristic" -BLEUnsignedCharCharacteristic batteryLevelChar("2A19", BLERead | BLENotify); // standard 16-bit characteristic UUID defined in the URL above - // remote clients will be able to get notifications if this characteristic changes +BLEUnsignedCharCharacteristic batteryLevelChar("2A19", // standard 16-bit characteristic UUID + 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() { - BLE.begin(); Serial.begin(9600); // initialize serial communication pinMode(13, OUTPUT); // initialize the LED on pin 13 to indicate when a central is connected + // begin initialization + BLE.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 If you want to make this work with the BatteryMonitor_Central sketch, do not modufy the name. */ - BLE.setLocalName("BatteryMonitorSketch"); - BLE.setAdvertisedServiceUuid(batteryService.uuid()); // add the service UUID - BLE.addService(batteryService); // Add the BLE Battery service + BLE.setLocalName("BatteryMonitor"); + BLE.setAdvertisedService(batteryService); // add the service UUID batteryService.addCharacteristic(batteryLevelChar); // add the battery level characteristic + BLE.addService(batteryService); // Add the BLE Battery service batteryLevelChar.setValue(oldBatteryLevel); // initial value for this characteristic - /* Now activate the BLE device. It will start continuously transmitting BLE + /* Start advertising BLE. It will start continuously transmitting BLE advertising packets and will be visible to remote BLE central devices - until it receives a new connection - */ + until it receives a new connection */ + // start advertising BLE.advertise(); + Serial.println("Bluetooth device active, waiting for connections..."); } @@ -77,14 +76,6 @@ void loop() { 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: @@ -104,36 +95,11 @@ void updateBatteryLevel() { if (batteryLevel != oldBatteryLevel) { // if the battery level has changed Serial.print("Battery Level % is now: "); // print it Serial.println(batteryLevel); - batteryLevelChar.writeUnsignedChar(batteryLevel); // and update the battery level characteristic + batteryLevelChar.setValue(batteryLevel); // and update the battery level characteristic oldBatteryLevel = batteryLevel; // save the level for next comparison } } -void updateIntervalParams(BLEDevice central) { - // 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. diff --git a/libraries/CurieBLE/examples/peripheral/ButtonLED/ButtonLED.ino b/libraries/CurieBLE/examples/peripheral/ButtonLED/ButtonLED.ino new file mode 100644 index 00000000..72a88a5e --- /dev/null +++ b/libraries/CurieBLE/examples/peripheral/ButtonLED/ButtonLED.ino @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +#include + +const int ledPin = 13; // set ledPin to on-board LED +const int buttonPin = 4; // set buttonPin to digital pin 4 + +BLEService ledService("19B10010-E8F2-537E-4F6C-D104768A1214"); // create service + + +// 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 + +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 + + // begin initialization + BLE.begin(); + + // set the local name peripheral advertises + BLE.setLocalName("BtnLED"); + // set the UUID for the service this peripheral advertises: + BLE.setAdvertisedService(ledService); + + // add the characteristics to the service + ledService.addCharacteristic(ledCharacteristic); + ledService.addCharacteristic(buttonCharacteristic); + + // add the service + BLE.addService(ledService); + + ledCharacteristic.setValue(0); + buttonCharacteristic.setValue(0); + + // start advertising + BLE.advertise(); + + Serial.println("Bluetooth device active, waiting for connections..."); +} + +void loop() { + // poll for BLE events + BLE.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 (ledCharacteristic.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 +*/ + \ No newline at end of file diff --git a/libraries/CurieBLE/examples/peripheral/CallbackLED/CallbackLED.ino b/libraries/CurieBLE/examples/peripheral/CallbackLED/CallbackLED.ino new file mode 100644 index 00000000..c0d02e3e --- /dev/null +++ b/libraries/CurieBLE/examples/peripheral/CallbackLED/CallbackLED.ino @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +#include + +const int ledPin = 13; // set ledPin to use on-board LED + +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 + + // begin initialization + BLE.begin(); + + // set the local name peripheral advertises + BLE.setLocalName("LEDCB"); + // set the UUID for the service this peripheral advertises + BLE.setAdvertisedService(ledService); + + // add the characteristic to the service + ledService.addCharacteristic(switchChar); + + // add service + BLE.addService(ledService); + + // assign event handlers for connected, disconnected to peripheral + BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler); + BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler); + + // assign event handlers for characteristic + switchChar.setEventHandler(BLEWritten, switchCharacteristicWritten); + // set an initial value for the characteristic + switchChar.setValue(0); + + // start advertising + BLE.advertise(); + + Serial.println(("Bluetooth device active, waiting for connections...")); +} + +void loop() { + // poll for BLE events + BLE.poll(); +} + +void blePeripheralConnectHandler(BLEDevice central) { + // central connected event handler + Serial.print("Connected event, central: "); + Serial.println(central.address()); +} + +void blePeripheralDisconnectHandler(BLEDevice 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/peripheral/LED/LED.ino b/libraries/CurieBLE/examples/peripheral/LED/LED.ino new file mode 100644 index 00000000..258cb455 --- /dev/null +++ b/libraries/CurieBLE/examples/peripheral/LED/LED.ino @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: led.ino + * + * Description: + * This is a Peripheral sketch that works with a connected Central. + * It allows the Central to write a value and set/reset the led + * accordingly. + */ + +#include + +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); + + // begin initialization + BLE.begin(); + + // set advertised local name and service UUID: + BLE.setLocalName("LED"); + BLE.setAdvertisedService(ledService); + + // add the characteristic to the service + ledService.addCharacteristic(switchCharacteristic); + + // add service + BLE.addService(ledService); + + // set the initial value for the characeristic: + switchCharacteristic.setValue(0); + + // start advertising + BLE.advertise(); + + Serial.println("BLE LED Peripheral"); +} + +void loop() { + // listen for BLE peripherals to connect: + BLEDevice central = BLE.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/peripheral/led/led.ino b/libraries/CurieBLE/examples/peripheral/led/led.ino deleted file mode 100644 index 4cbb25dd..00000000 --- a/libraries/CurieBLE/examples/peripheral/led/led.ino +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -/* - * Sketch: led.ino - * - * Description: - * This is a Peripheral sketch that works with a connected Central. - * It allows the Central to write a value and set/reset the led - * accordingly. - */ - -#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()); - } -} - - -/* - 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 -*/ - - diff --git a/libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino b/libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino deleted file mode 100644 index 54a5c659..00000000 --- a/libraries/CurieBLE/examples/peripheral/led_callback/led_callback.ino +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2016 Intel Corporation. All rights reserved. - * See the bottom of this file for the license terms. - */ - -// 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); - } -} - - - -/* - 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 -*/ - - diff --git a/libraries/CurieBLE/examples/central/BatteryMonitor_Central/BatteryMonitor_Central.ino b/libraries/CurieBLE/examples/test/BatteryMonitor_Central/BatteryMonitor_Central.ino similarity index 100% rename from libraries/CurieBLE/examples/central/BatteryMonitor_Central/BatteryMonitor_Central.ino rename to libraries/CurieBLE/examples/test/BatteryMonitor_Central/BatteryMonitor_Central.ino diff --git a/libraries/CurieBLE/examples/central/CentralDouble/CentralDouble.ino b/libraries/CurieBLE/examples/test/CentralDouble/CentralDouble.ino similarity index 100% rename from libraries/CurieBLE/examples/central/CentralDouble/CentralDouble.ino rename to libraries/CurieBLE/examples/test/CentralDouble/CentralDouble.ino diff --git a/libraries/CurieBLE/examples/central/IMUBleCentral/IMUBleCentral.ino b/libraries/CurieBLE/examples/test/IMUBleCentral/IMUBleCentral.ino similarity index 100% rename from libraries/CurieBLE/examples/central/IMUBleCentral/IMUBleCentral.ino rename to libraries/CurieBLE/examples/test/IMUBleCentral/IMUBleCentral.ino diff --git a/libraries/CurieBLE/examples/peripheral/IMUBleNotification/IMUBleNotification.ino b/libraries/CurieBLE/examples/test/IMUBleNotification/IMUBleNotification.ino similarity index 100% rename from libraries/CurieBLE/examples/peripheral/IMUBleNotification/IMUBleNotification.ino rename to libraries/CurieBLE/examples/test/IMUBleNotification/IMUBleNotification.ino diff --git a/libraries/CurieBLE/examples/peripheral/PeripheralDouble/PeripheralDouble.ino b/libraries/CurieBLE/examples/test/PeripheralDouble/PeripheralDouble.ino similarity index 100% rename from libraries/CurieBLE/examples/peripheral/PeripheralDouble/PeripheralDouble.ino rename to libraries/CurieBLE/examples/test/PeripheralDouble/PeripheralDouble.ino diff --git a/libraries/CurieBLE/keywords.txt b/libraries/CurieBLE/keywords.txt index 2db97095..a98db39d 100644 --- a/libraries/CurieBLE/keywords.txt +++ b/libraries/CurieBLE/keywords.txt @@ -104,6 +104,8 @@ setTxPower KEYWORD2 setConnectable KEYWORD2 setDeviceName KEYWORD2 addService KEYWORD2 +addCharacteristic KEYWORD2 +addDescriptor KEYWORD2 advertise KEYWORD2 stopAdvertise KEYWORD2 central KEYWORD2 From 2daed478047ff734551afcaddffb27debc352be3 Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Thu, 16 Mar 2017 15:27:03 -0700 Subject: [PATCH 079/125] Make sure LED is not lit on sketch start -The LED retains the last state it was in after a sketch upload -We need to make sure its is not lit on sketch startup so that behavior is consistent --- variants/arduino_101/variant.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/variants/arduino_101/variant.cpp b/variants/arduino_101/variant.cpp index 9e9b52ce..d352e4c2 100644 --- a/variants/arduino_101/variant.cpp +++ b/variants/arduino_101/variant.cpp @@ -180,6 +180,8 @@ void variantGpioInit(void) SET_PIN_MODE(p->ulSocPin, p->ulPinMode); pinmuxMode[pin] = GPIO_MUX_MODE; } + //make sure the led is not lit on sketch start + digitalWrite(LED_BUILTIN, LOW); } void variantPwmInit(void) From 6b33e736a7ba9c5038c1a14fb7e6fb5bc099be71 Mon Sep 17 00:00:00 2001 From: Dino Tinitigan Date: Tue, 21 Mar 2017 14:17:08 -0700 Subject: [PATCH 080/125] Set all pins to INPUT on sketch startup --- variants/arduino_101/variant.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/variants/arduino_101/variant.cpp b/variants/arduino_101/variant.cpp index d352e4c2..31473536 100644 --- a/variants/arduino_101/variant.cpp +++ b/variants/arduino_101/variant.cpp @@ -179,9 +179,8 @@ void variantGpioInit(void) PinDescription *p = &g_APinDescription[pin]; SET_PIN_MODE(p->ulSocPin, p->ulPinMode); pinmuxMode[pin] = GPIO_MUX_MODE; + pinMode(pin, INPUT); } - //make sure the led is not lit on sketch start - digitalWrite(LED_BUILTIN, LOW); } void variantPwmInit(void) From 07fce54fc33e42db69e626e979a03debf4f3ffab Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Thu, 16 Mar 2017 11:38:50 -0400 Subject: [PATCH 081/125] Set characteristic value internally if it not associated with a service yet --- libraries/CurieBLE/src/BLECharacteristic.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/CurieBLE/src/BLECharacteristic.cpp b/libraries/CurieBLE/src/BLECharacteristic.cpp index 278eb1d4..1416cef3 100644 --- a/libraries/CurieBLE/src/BLECharacteristic.cpp +++ b/libraries/CurieBLE/src/BLECharacteristic.cpp @@ -283,6 +283,10 @@ bool BLECharacteristic::writeValue(const byte value[], int length, int offset) characteristicImp->valueLength()); BLEDeviceManager::instance()->startAdvertising(); } + } else { + // not associated with a service yet + _setValue(value, length); + retVar = true; } return retVar; } From 33df47d4f855250cffe4e0211198121b18dda851 Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 27 Feb 2017 15:18:23 +0800 Subject: [PATCH 082/125] Implement the block call for descriptor read method --- .../CurieBLE/src/internal/BLEDescriptorImp.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp b/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp index 42ca6a24..d773f63c 100644 --- a/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp +++ b/libraries/CurieBLE/src/internal/BLEDescriptorImp.cpp @@ -172,6 +172,7 @@ bool BLEDescriptorImp::read() { int retval = 0; bt_conn_t* conn = NULL; + bool ret_bool = true; if (true == BLEUtils::isLocalBLE(_bledev)) { @@ -209,6 +210,13 @@ bool BLEDescriptorImp::read() { _reading = true; } + + // Block the read + while (_reading == true && ret_bool) + { + delay(5); + ret_bool = _bledev.connected(); + } return _reading; } @@ -219,6 +227,12 @@ bool BLEDescriptorImp::writeValue(const byte value[], bool ret = true; int total_length = length + offset; int write_len = length; + + if (_reading) + { + _reading = false; + } + if (total_length > BLE_MAX_ATTR_DATA_LEN) { return false; From d72f2bd191c35b57d1a4e2f558d3441420fb3d06 Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Wed, 15 Mar 2017 13:40:51 -0400 Subject: [PATCH 083/125] Correct | to &, and swap order of write vs write without response --- .../src/internal/BLECharacteristicImp.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp index 35567130..a7098e96 100644 --- a/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp +++ b/libraries/CurieBLE/src/internal/BLECharacteristicImp.cpp @@ -697,15 +697,7 @@ bool BLECharacteristicImp::write(const unsigned char value[], } // Send write request - if (_gatt_chrc.properties | BT_GATT_CHRC_WRITE_WITHOUT_RESP) - { - retval = bt_gatt_write_without_response(conn, - _value_handle, - value, - length, - false); - } - else if (_gatt_chrc.properties | BT_GATT_CHRC_WRITE) + if (_gatt_chrc.properties & BT_GATT_CHRC_WRITE) { _gattc_writing = true; retval = bt_gatt_write(conn, @@ -718,6 +710,13 @@ bool BLECharacteristicImp::write(const unsigned char value[], { delay(2); } + } else if (_gatt_chrc.properties & BT_GATT_CHRC_WRITE_WITHOUT_RESP) + { + retval = bt_gatt_write_without_response(conn, + _value_handle, + value, + length, + false); } bt_conn_unref(conn); return (0 == retval); From 98c1746d912bbcc7f59c119a92a9a4f008f769a5 Mon Sep 17 00:00:00 2001 From: lianggao Date: Mon, 13 Mar 2017 16:08:25 +0800 Subject: [PATCH 084/125] Fix Jira876 BLE scanForName() device from previous scan is picked up Root cause: 1. The buffer doesn't cleared. The scanned data is not the latest. Changed files: BLEDeviceManager.cpp a. Clear the buffered data when start scan. b. Invalid scan response when buffer timeout or disconnected. --- .../src/internal/BLEDeviceManager.cpp | 41 +++++++++++++++---- .../CurieBLE/src/internal/BLEDeviceManager.h | 1 + 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp index 56e2e960..6ed15e2d 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp @@ -101,8 +101,6 @@ BLEDeviceManager::BLEDeviceManager(): memset(&_available_for_connect_peripheral_adv_data, 0, sizeof(_available_for_connect_peripheral_adv_data)); memset(&_available_for_connect_peripheral_scan_rsp_data, 0, sizeof(_available_for_connect_peripheral_scan_rsp_data)); - - 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)); @@ -576,10 +574,32 @@ BLEDevice BLEDeviceManager::peripheral() return temp; } +void BLEDeviceManager::_clearAdvertiseBuffer() +{ + + // Clear the previous found ADV + memset(_peer_temp_adv_buffer, 0, sizeof(_peer_temp_adv_buffer)); + memset(_peer_temp_adv_data, 0, sizeof(_peer_temp_adv_data)); + memset(_peer_temp_adv_data_len, 0, sizeof(_peer_temp_adv_data_len)); + memset(_peer_temp_adv_connectable, 0, sizeof(_peer_adv_connectable)); + + memset(_peer_adv_buffer, 0, sizeof(_peer_adv_buffer)); + memset(_peer_adv_mill, 0, sizeof(_peer_adv_mill)); + memset(_peer_adv_data, 0, sizeof(_peer_adv_data)); + memset(_peer_adv_data_len, 0, sizeof(_peer_adv_data_len)); + memset(_peer_scan_rsp_data, 0, sizeof(_peer_scan_rsp_data)); + memset(_peer_scan_rsp_data_len, 0, sizeof(_peer_scan_rsp_data_len)); + memset(_peer_adv_rssi, 0, sizeof(_peer_adv_rssi)); + +} + bool BLEDeviceManager::startScanning() { _adv_duplicate_filter_enabled = false; _scan_param.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE; + + _clearAdvertiseBuffer(); + int err = bt_le_scan_start(&_scan_param, ble_central_device_found); if (err) { @@ -594,6 +614,8 @@ bool BLEDeviceManager::startScanningWithDuplicates() _adv_duplicate_filter_enabled = true; memset(_peer_duplicate_address_buffer, 0, sizeof(_peer_duplicate_address_buffer)); _duplicate_filter_header = _duplicate_filter_tail = 0; + + _clearAdvertiseBuffer(); _scan_param.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE; int err = bt_le_scan_start(&_scan_param, ble_central_device_found); @@ -896,11 +918,6 @@ int BLEDeviceManager::advertisedServiceUuidCount(const BLEDevice* device) const 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; - } - /* Sid, 2/15/2017. Sandeep reported that Apple devices may use BT_DATA_UUID16_SOME and BT_DATA_UUID128_SOME in addition to ALL. Practically, these types are same as ALL. */ @@ -1067,6 +1084,11 @@ bool BLEDeviceManager::connect(BLEDevice &device) uint64_t timestamp = millis(); uint64_t timestampcur = timestamp; bool ret = true; + if (_available_for_connect_peripheral_connectable == false) + { + return false; + } + 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); @@ -1247,6 +1269,8 @@ void BLEDeviceManager::handleDisconnectEvent(bt_conn_t *conn, uint8_t reason) memset(_peer_peripheral_adv_data[i], 0, BLE_MAX_ADV_SIZE); _peer_peripheral_adv_data_len[i] = 0; _peer_peripheral_adv_rssi[i] = 0; + memset(_peer_peripheral_scan_rsp_data[i], 0, BLE_MAX_ADV_SIZE); + _peer_peripheral_scan_rsp_data_len[i] = 0; break; } } @@ -1406,7 +1430,10 @@ bool BLEDeviceManager::setAdvertiseBuffer(const bt_addr_le_t* bt_addr, { max_delta = timestamp_delta; if (max_delta > 2000) // expired + { index = i; + _peer_scan_rsp_data_len[index] = 0; // Invalid the scan response + } } if (bt_addr_le_cmp(temp, bt_addr) == 0) diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.h b/libraries/CurieBLE/src/internal/BLEDeviceManager.h index 118c4c11..06accd2f 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.h +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.h @@ -358,6 +358,7 @@ class BLEDeviceManager const uint8_t* data, uint8_t length); BLE_STATUS_T _advDataInit(void); + void _clearAdvertiseBuffer(); bool advertiseDataProc(uint8_t type, const uint8_t *dataPtr, uint8_t data_len); From 82eb1be749a10ef8be97ab847cdc6ef2e11a58ba Mon Sep 17 00:00:00 2001 From: Sidney Leung Date: Tue, 21 Mar 2017 23:26:19 -0700 Subject: [PATCH 085/125] Jira 896, merged to 01org/master Commit error: Committed files contained code mods for another ticket in addition to the those for Jira 896. --- libraries/CurieBLE/src/BLEDevice.cpp | 18 +--- libraries/CurieBLE/src/BLEDevice.h | 101 +++++++++--------- .../src/internal/BLEDeviceManager.cpp | 8 +- .../CurieBLE/src/internal/BLEDeviceManager.h | 2 +- 4 files changed, 54 insertions(+), 75 deletions(-) diff --git a/libraries/CurieBLE/src/BLEDevice.cpp b/libraries/CurieBLE/src/BLEDevice.cpp index 2c5a3189..5306d871 100644 --- a/libraries/CurieBLE/src/BLEDevice.cpp +++ b/libraries/CurieBLE/src/BLEDevice.cpp @@ -247,38 +247,22 @@ bool BLEDevice::startScan(bool withDuplicates) } else { - return BLEDeviceManager::instance()->startScanning(); + return BLEDeviceManager::instance()->startScanningNewPeripherals(); } } - -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()); diff --git a/libraries/CurieBLE/src/BLEDevice.h b/libraries/CurieBLE/src/BLEDevice.h index 76e94f14..6b8ae72d 100644 --- a/libraries/CurieBLE/src/BLEDevice.h +++ b/libraries/CurieBLE/src/BLEDevice.h @@ -361,83 +361,78 @@ class BLEDevice //void scanForAddress(String address); // Not include in baseline. Add here as feature for feature release. /** - * @brief Start scanning for peripherals without filter + * @brief Start scanning for peripherals with the option of accepting all detectable + * Peripherals or just the newly detected. * - * @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. + * @param[in] withDuplicates true - return all detectable Peripherals. + * false- return a detected Peripheral only once. * * @return none * - * @note option to filter out duplicate addresses for Arduino. - * The current only support fileter duplicate mode. + * @note When, withDuplicates = true, accept all detectable Peripherals. + * No Peripheral filtering process applied to the scan result. + * By default, withDuplicates = false, a detected Peripheral is + * reported once. */ - void scanForName(String name); + void scan(bool withDuplicates = false); /** - * @brief Start scanning for peripherals and filter by device name in ADV + * @brief Start scanning for peripherals and filter by device name in ADV and + * the option of accepting all detectable Peripherals or just the + * newly detected. * * @param[in] name The device's local name. * - * @param[in] withDuplicates true - with duplicate filter - * false- without duplicate filter + * @param[in] withDuplicates true - return all detectable Peripherals. + * false- return a detected Peripheral only once. * * @return none * - * @note option to filter out duplicate addresses for Arduino. - * The current only support fileter duplicate mode. + * @note When, withDuplicates = true, accept all detectable Peripherals. + * No Peripheral filtering process applied to the scan result. + * By default, withDuplicates = false, a detected Peripheral is + * reported once. */ - void scanForName(String name, bool withDuplicates); + void scanForName(String name, bool withDuplicates = false); /** - * @brief Start scanning for peripherals and filter by service in ADV + * @brief Start scanning for peripherals and filter by service in ADV and + * the option of accepting all detectable Peripherals or just the + * newly detected. * - * @param service The service + * @param[in] service The service + * + * @param[in] withDuplicates true - return all detectable Peripherals. + * false- return a detected Peripheral only once. * * @return none * - * @note none + * @note When, withDuplicates = true, accept all detectable Peripherals. + * No Peripheral filtering process applied to the scan result. + * By default, withDuplicates = false, a detected Peripheral is + * reported once. */ - void scanForUuid(String uuid); + void scanForUuid(String uuid, bool withDuplicates = false); /** - * @brief Start scanning for peripherals and filter by service in ADV + * @brief Start scanning for peripherals and filter by MAC address and + * the option of accepting all detectable Peripherals or just the + * newly detected. * - * @param[in] service The service + * @param[in] macaddr The Peripheral MAC address * - * @param[in] withDuplicates true - with duplicate filter - * false- without duplicate filter + * @param[in] withDuplicates true - return all detectable Peripherals. + * false- return a detected Peripheral only once. * * @return none * - * @note option to filter out duplicate addresses for Arduino. - * The current only support fileter duplicate mode. + * @note When, withDuplicates = true, accept all detectable Peripherals. + * No Peripheral filtering process applied to the scan result. + * By default, withDuplicates = false, a detected Peripheral is + * reported once. */ - void scanForUuid(String uuid, bool withDuplicates); - - void scanForAddress(String macaddr, bool withDuplicates = true); + void scanForAddress(String macaddr, bool withDuplicates = false); + /** * @brief Stop scanning for peripherals * @@ -679,15 +674,15 @@ class BLEDevice void preCheckProfile(); /** - * @brief Start scanning for peripherals with/without duplicate filter + * @brief Start scanning for peripherals with the option of accepting all + * detectable Peripherals or just the newly detected. * - * @param[in] withDuplicates true - with duplicate filter - * false- without duplicate filter + * @param[in] withDuplicates true - return all detectable Peripherals. + * false- return a detected Peripheral only once. * * @return none * - * @note option to filter out duplicate addresses for Arduino. - * The current only support fileter duplicate mode. + * @note When, withDuplicates = true, accept all detectable Peripherals. */ bool startScan(bool withDuplicates); diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp index 6ed15e2d..2b7f929d 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp @@ -593,7 +593,7 @@ void BLEDeviceManager::_clearAdvertiseBuffer() } -bool BLEDeviceManager::startScanning() +bool BLEDeviceManager::startScanningWithDuplicates() { _adv_duplicate_filter_enabled = false; _scan_param.filter_dup = BT_HCI_LE_SCAN_FILTER_DUP_ENABLE; @@ -609,7 +609,7 @@ bool BLEDeviceManager::startScanning() return true; } -bool BLEDeviceManager::startScanningWithDuplicates() +bool BLEDeviceManager::startScanningNewPeripherals() { _adv_duplicate_filter_enabled = true; memset(_peer_duplicate_address_buffer, 0, sizeof(_peer_duplicate_address_buffer)); @@ -1096,8 +1096,8 @@ bool BLEDeviceManager::connect(BLEDevice &device) _wait_for_connect_peripheral_adv_data_len = _available_for_connect_peripheral_adv_data_len; _wait_for_connect_peripheral_scan_rsp_data_len = _available_for_connect_peripheral_scan_rsp_data_len; _wait_for_connect_peripheral_adv_rssi = _available_for_connect_peripheral_adv_rssi; - - startScanning(); + + startScanningWithDuplicates(); pr_debug(LOG_MODULE_BLE, "%s-%d", __FUNCTION__, __LINE__); // Wait for the connection diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.h b/libraries/CurieBLE/src/internal/BLEDeviceManager.h index 06accd2f..3a7aede2 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.h +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.h @@ -310,7 +310,7 @@ class BLEDeviceManager void setAdvertiseCritical(String name); void setAdvertiseCritical(BLEService& service); void setAdvertiseCritical(const char* macaddress); - bool startScanning(); // start scanning for peripherals + bool startScanningNewPeripherals(); // start scanning for new peripherals, don't report the detected ones bool startScanningWithDuplicates(); // start scanning for peripherals, and report all duplicates bool stopScanning(); // stop scanning for peripherals From bb5b7b4939e3e486d012298b7a7b831c17eb2877 Mon Sep 17 00:00:00 2001 From: lianggao Date: Wed, 8 Feb 2017 20:48:43 +0800 Subject: [PATCH 086/125] Jira 671, Support Connection Interval updating. Description: This feature enables a sketch to change the Connection Interval, Timeout, and Latency. The feature is applicable to both Central and Peripheral mode. A change in the interval in the midst of a connection will terminate the connection. A re-connection is required. File mods: 1. BLEDeviceManager.cpp: - Application facing Connection Interval updating APIs. 2. BLEDevice.cpp: - The execution of the interval changing command. File addition: 1. connupdateperipheral.ino: - A demo sketch to show how a peripheral can change the Connection Interval. --- .../connupdateperipheral.ino | 138 ++++++++++++++++++ libraries/CurieBLE/src/BLEDevice.cpp | 48 +++++- libraries/CurieBLE/src/BLEDevice.h | 14 +- .../src/internal/BLEDeviceManager.cpp | 28 +++- .../CurieBLE/src/internal/BLEDeviceManager.h | 23 +-- 5 files changed, 218 insertions(+), 33 deletions(-) create mode 100644 libraries/CurieBLE/examples/test/connupdateperipheral/connupdateperipheral.ino diff --git a/libraries/CurieBLE/examples/test/connupdateperipheral/connupdateperipheral.ino b/libraries/CurieBLE/examples/test/connupdateperipheral/connupdateperipheral.ino new file mode 100644 index 00000000..f8605f25 --- /dev/null +++ b/libraries/CurieBLE/examples/test/connupdateperipheral/connupdateperipheral.ino @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2017 Intel Corporation. All rights reserved. + * See the bottom of this file for the license terms. + */ + +/* + * Sketch: connupdateperipheral.ino + * + * Description: + * This is a Peripheral sketch that is based on the Arduino LED + * control sketch. The purpose of this sketch is to exercise + * the changing of Connection Interval as a Peripheral. The Central + * writes to the Characteristic to turn the LED on and off similar + * to the original sketch. However, when the LED is turned off, + * this sketch will change the connection interval to a new + * value and, thus, forcing a re-connection. + * + * Notes: + * + * - This sketch is based on the Arduino BLE Peripheral LED example. + * Please refer to licensing info at the bottom of this file. + */ + +#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 bleCentralConnectionParameterUpdateHandler(BLEDevice central) { + Serial1.println("Updated connection parameter"); + Serial1.println("-----------------------"); + + // print address + Serial1.print("Address: "); + Serial1.println(central.address()); + + Serial1.print("Interval: "); + Serial1.println(central.getConnectionInterval()); + Serial1.print("Timeout: "); + Serial1.println(central.getConnectionTimeout()); + Serial1.print("Latency: "); + Serial1.println(central.getConnectionLatency()); + + //Serial1.println(); +} + +void setup() { + Serial.begin(9600); + Serial1.begin(115200); + + // set LED pin to output mode + pinMode(LED_PIN, OUTPUT); + + // begin initialization + BLE.begin(); + Serial1.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); + + //Register callbacks + BLE.setEventHandler(BLEConParamUpdate, bleCentralConnectionParameterUpdateHandler); + + BLE.advertise(); + + Serial1.println(F("BLE LED Peripheral")); +} + +void loop() { + BLEDevice central = BLE.central(); + + if (central) { + // central connected to peripheral + Serial1.print(F("Connected to central: ")); + Serial1.println(central.address()); + static int connection_interval = 15; + + while (central.connected()) { + // central still connected to peripheral + if (switchCharacteristic.written()) { + // central wrote new value to characteristic, update LED + if (switchCharacteristic.value()) { + Serial1.println(F("LED on")); + digitalWrite(LED_PIN, HIGH); + connection_interval++; + } else { + Serial1.println(F("LED off")); + digitalWrite(LED_PIN, LOW); + delay(100); + // The peripheral update the connection interval + // If want central update the connection interval + // comment the below line + central.setConnectionInterval(connection_interval, connection_interval); + } + } + } + + // central disconnected + Serial1.print(F("Disconnected from central: ")); + Serial1.println(central.address()); + } +} + +/* + 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 +*/ + diff --git a/libraries/CurieBLE/src/BLEDevice.cpp b/libraries/CurieBLE/src/BLEDevice.cpp index 5306d871..b894e3c1 100644 --- a/libraries/CurieBLE/src/BLEDevice.cpp +++ b/libraries/CurieBLE/src/BLEDevice.cpp @@ -19,6 +19,8 @@ #include "CurieBLE.h" #include "BLEDevice.h" +#include "./internal/ble_client.h" + #include "./internal/BLEUtils.h" #include "./internal/BLEProfileManager.h" @@ -51,6 +53,7 @@ BLEDevice::BLEDevice(const bt_addr_le_t* bleaddress): BLEDevice() { memcpy(&_bt_addr, bleaddress, sizeof(_bt_addr)); + BLEDeviceManager::instance()->getConnectionInterval(this, &_conn_param); } BLEDevice::BLEDevice(const BLEDevice* bledevice) @@ -154,11 +157,54 @@ void BLEDevice::setAdvertisingInterval(float advertisingInterval) BLEDeviceManager::instance()->setAdvertisingInterval(advertisingInterval); } +void BLEDevice::setConnectionInterval(int minInterval, + int 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_param.interval_min = minVal; + _conn_param.interval_max = maxVal; + _conn_param.timeout = timeoutVal; + _conn_param.latency = latency; + BLEDeviceManager::instance()->setConnectionInterval(this); +} + void BLEDevice::setConnectionInterval(int minimumConnectionInterval, int maximumConnectionInterval) { - // TODO: Update the connection interval need more discussion + uint16_t minVal = (uint16_t)MSEC_TO_UNITS(minimumConnectionInterval, UNIT_1_25_MS); + uint16_t maxVal = (uint16_t)MSEC_TO_UNITS(maximumConnectionInterval, UNIT_1_25_MS); + _conn_param.interval_min = minVal; + _conn_param.interval_max = maxVal; + + BLEDeviceManager::instance()->setConnectionInterval(this); +} + +int BLEDevice::getConnectionInterval() +{ + bt_le_conn_param_t conn_param; + + BLEDeviceManager::instance()->getConnectionInterval(this, &conn_param); + return UNITS_TO_MSEC((int)conn_param.interval_max, UNIT_1_25_MS); +} + +int BLEDevice::getConnectionTimeout() +{ + bt_le_conn_param_t conn_param; + + BLEDeviceManager::instance()->getConnectionInterval(this, &conn_param); + return UNITS_TO_MSEC(conn_param.timeout, UNIT_10_MS);; +} +int BLEDevice::getConnectionLatency() +{ + bt_le_conn_param_t conn_param; + + BLEDeviceManager::instance()->getConnectionInterval(this, &conn_param); + return conn_param.latency; } bool BLEDevice::setTxPower(int txPower) diff --git a/libraries/CurieBLE/src/BLEDevice.h b/libraries/CurieBLE/src/BLEDevice.h index 6b8ae72d..23279446 100644 --- a/libraries/CurieBLE/src/BLEDevice.h +++ b/libraries/CurieBLE/src/BLEDevice.h @@ -232,10 +232,10 @@ class BLEDevice * * @note none */ - //void setConnectionInterval(int minimumConnectionInterval, - // int maximumConnectionInterval, - // uint16_t latency, - // uint16_t timeout); + void setConnectionInterval(int minimumConnectionInterval, + int maximumConnectionInterval, + uint16_t latency, + uint16_t timeout); /** * @brief Set the min and max connection interval and send connection @@ -252,6 +252,10 @@ class BLEDevice void setConnectionInterval(int minimumConnectionInterval, int maximumConnectionInterval); + int getConnectionInterval(); + int getConnectionTimeout(); + int getConnectionLatency(); + /** * @brief Set TX power of the radio in dBM * @@ -689,7 +693,7 @@ class BLEDevice private: bt_addr_le_t _bt_addr; - bt_le_conn_param _conn_param; + bt_le_conn_param_t _conn_param; }; #endif diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp index 2b7f929d..7b3276ce 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.cpp @@ -283,17 +283,31 @@ void BLEDeviceManager::setAdvertisingInterval(float advertisingInterval) _adv_param.interval_max = interval; } -void BLEDeviceManager::setConnectionInterval(float minimumConnectionInterval, - float maximumConnectionInterval, - uint16_t latency, - uint16_t timeout) +void BLEDeviceManager::getConnectionInterval(BLEDevice *device, + bt_le_conn_param* conn_param) { + bt_conn_t* conn = bt_conn_lookup_addr_le(device->bt_le_address()); + if (NULL != conn) + { + conn_param->interval_max = conn->le.interval; + conn_param->interval_min = conn->le.interval; + conn_param->latency = conn->le.latency; + conn_param->timeout = conn->le.timeout; + bt_conn_unref(conn); + } } -void BLEDeviceManager::setConnectionInterval(float minimumConnectionInterval, - float maximumConnectionInterval) +int BLEDeviceManager::setConnectionInterval(BLEDevice *device) { - + bt_conn_t* conn = bt_conn_lookup_addr_le(device->bt_le_address()); + int ret = 0; + if (NULL != conn) + { + ret = bt_conn_le_param_update(conn, device->bt_conn_param()); + pr_debug(LOG_MODULE_BLE, "%s-ret:%d",__FUNCTION__, ret); + bt_conn_unref(conn); + } + return ret; } bool BLEDeviceManager::setTxPower(int txPower) diff --git a/libraries/CurieBLE/src/internal/BLEDeviceManager.h b/libraries/CurieBLE/src/internal/BLEDeviceManager.h index 3a7aede2..38930eb4 100644 --- a/libraries/CurieBLE/src/internal/BLEDeviceManager.h +++ b/libraries/CurieBLE/src/internal/BLEDeviceManager.h @@ -181,26 +181,9 @@ class BLEDeviceManager * * @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); - + int setConnectionInterval (BLEDevice *device); + void getConnectionInterval(BLEDevice *device, + bt_le_conn_param* conn_param); /** * @brief Set TX power of the radio in dBM * From d2cc6721b63280d212033fa87f52a1cdd9d0a65f Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Fri, 24 Mar 2017 15:28:04 -0400 Subject: [PATCH 087/125] Remove comment about BatteryMonitor_Central sketch As it has been moved to the test folder for now. --- .../examples/peripheral/BatteryMonitor/BatteryMonitor.ino | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/CurieBLE/examples/peripheral/BatteryMonitor/BatteryMonitor.ino b/libraries/CurieBLE/examples/peripheral/BatteryMonitor/BatteryMonitor.ino index e898e2aa..8535a697 100644 --- a/libraries/CurieBLE/examples/peripheral/BatteryMonitor/BatteryMonitor.ino +++ b/libraries/CurieBLE/examples/peripheral/BatteryMonitor/BatteryMonitor.ino @@ -38,7 +38,6 @@ void setup() { 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 - If you want to make this work with the BatteryMonitor_Central sketch, do not modufy the name. */ BLE.setLocalName("BatteryMonitor"); BLE.setAdvertisedService(batteryService); // add the service UUID From d6afd8bb3a8701feafbc1dcb39d5c59645126e54 Mon Sep 17 00:00:00 2001 From: Sandeep Mistry Date: Fri, 24 Mar 2017 15:23:26 -0400 Subject: [PATCH 088/125] Remove unnecessary delays from peripheral explorer example --- .../central/peripheral_explorer/peripheral_explorer.ino | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino b/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino index 4c558922..e9511f48 100644 --- a/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino +++ b/libraries/CurieBLE/examples/central/peripheral_explorer/peripheral_explorer.ino @@ -125,7 +125,7 @@ void exploreCharacteristic(BLECharacteristic characteristic) { if (characteristic.canRead()) { // read the characteristic value characteristic.read(); - delay(1000); + if (characteristic.valueLength() > 0) { // print out the value of the characteristic @@ -150,7 +150,6 @@ void exploreDescriptor(BLEDescriptor descriptor) { // read the descriptor value descriptor.read(); - delay(1000); // print out the value of the descriptor Serial.print(", value 0x"); From b28d4c8c37fa0c1c20d9915ce384269ee21426f4 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 1 Feb 2017 12:36:46 +0100 Subject: [PATCH 089/125] Move drivers in core --- drivers/amd64/WdfCoInstaller01009.dll | Bin 0 -> 1721576 bytes drivers/amd64/winusbcoinstaller2.dll | Bin 0 -> 1002728 bytes drivers/dpinst-amd64.exe | Bin 0 -> 1047632 bytes drivers/dpinst-x86.exe | Bin 0 -> 922176 bytes drivers/intc_composite.cat | Bin 0 -> 10637 bytes drivers/intc_composite.inf | 46 +++++++ drivers/intc_libusb.cat | Bin 0 -> 12495 bytes drivers/intc_libusb.inf | 174 ++++++++++++++++++++++++++ drivers/intc_serial.cat | Bin 0 -> 10631 bytes drivers/intc_serial.inf | 73 +++++++++++ drivers/x86/WdfCoInstaller01009.dll | Bin 0 -> 1461992 bytes drivers/x86/winusbcoinstaller2.dll | Bin 0 -> 851176 bytes post_install.bat | 10 ++ 13 files changed, 303 insertions(+) create mode 100644 drivers/amd64/WdfCoInstaller01009.dll create mode 100644 drivers/amd64/winusbcoinstaller2.dll create mode 100644 drivers/dpinst-amd64.exe create mode 100644 drivers/dpinst-x86.exe create mode 100644 drivers/intc_composite.cat create mode 100644 drivers/intc_composite.inf create mode 100644 drivers/intc_libusb.cat create mode 100644 drivers/intc_libusb.inf create mode 100644 drivers/intc_serial.cat create mode 100644 drivers/intc_serial.inf create mode 100644 drivers/x86/WdfCoInstaller01009.dll create mode 100644 drivers/x86/winusbcoinstaller2.dll create mode 100644 post_install.bat diff --git a/drivers/amd64/WdfCoInstaller01009.dll b/drivers/amd64/WdfCoInstaller01009.dll new file mode 100644 index 0000000000000000000000000000000000000000..1731b962d68d52030b32b19d6f0f913cbc47729e GIT binary patch literal 1721576 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