aboutsummaryrefslogtreecommitdiffstats
path: root/arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.cpp')
-rwxr-xr-xarduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.cpp391
1 files changed, 391 insertions, 0 deletions
diff --git a/arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.cpp b/arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.cpp
new file mode 100755
index 0000000..e62c116
--- /dev/null
+++ b/arduino/libraries/Bluefruit52Lib/src/clients/BLEAncs.cpp
@@ -0,0 +1,391 @@
+/**************************************************************************/
+/*!
+ @file BLEAncs.cpp
+ @author hathach (tinyusb.org)
+
+ @section LICENSE
+
+ Software License Agreement (BSD License)
+
+ Copyright (c) 2018, Adafruit Industries (adafruit.com)
+ 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 holders 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 ''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 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 "bluefruit.h"
+
+#define BLE_ANCS_TIMEOUT (5*BLE_GENERIC_TIMEOUT)
+
+#define DEBUG_ANCS 0
+
+void bleancs_notification_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);
+void bleancs_data_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);
+
+/* ANCS Service : 7905F431-B5CE-4E99-A40F-4B1E122D00D0
+ * Control Point : 69D1D8F3-45E1-49A8-9821-9BBDFDAAD9D9
+ * Notification Source : 9FBF120D-6301-42D9-8C58-25E699A21DBD
+ * Data Source : 22EAC6E9-24D6-4BB5-BE44-B36ACE7C7BFB
+ */
+
+const uint8_t BLEANCS_UUID_SERVICE[] =
+{
+ 0xD0, 0x00, 0x2D, 0x12, 0x1E, 0x4B, 0x0F, 0xA4,
+ 0x99, 0x4E, 0xCE, 0xB5, 0x31, 0xF4, 0x05, 0x79
+};
+
+const uint8_t BLEANCS_UUID_CHR_CONTROL[] =
+{
+ 0xD9, 0xD9, 0xAA, 0xFD, 0xBD, 0x9B, 0x21, 0x98,
+ 0xA8, 0x49, 0xE1, 0x45, 0xF3, 0xD8, 0xD1, 0x69
+};
+
+const uint8_t BLEANCS_UUID_CHR_NOTIFICATION[]
+{
+ 0xBD, 0x1D, 0xA2, 0x99, 0xE6, 0x25, 0x58, 0x8C,
+ 0xD9, 0x42, 0x01, 0x63, 0x0D, 0x12, 0xBF, 0x9F
+};
+
+const uint8_t BLEANCS_UUID_CHR_DATA[] =
+{
+ 0xFB, 0x7B, 0x7C, 0xCE, 0x6A, 0xB3, 0x44, 0xBE,
+ 0xB5, 0x4B, 0xD6, 0x24, 0xE9, 0xC6, 0xEA, 0x22
+};
+
+BLEAncs::BLEAncs(void)
+ : BLEClientService(BLEANCS_UUID_SERVICE), _control(BLEANCS_UUID_CHR_CONTROL),
+ _notification(BLEANCS_UUID_CHR_NOTIFICATION), _data(BLEANCS_UUID_CHR_DATA),
+ _adamsg()
+{
+ _notif_cb = NULL;
+}
+
+bool BLEAncs::begin(void)
+{
+ // Invoke base class begin()
+ BLEClientService::begin();
+
+ _adamsg.begin(false);
+
+ _control.begin();
+ _notification.begin();
+ _data.begin();
+
+ _notification.setNotifyCallback(bleancs_notification_cb);
+
+ // Data Attribute is most likely requested in notification callback
+ // let's call data's callback in the ble task
+ _data.setNotifyCallback(bleancs_data_cb, false);
+
+ return true;
+}
+
+bool BLEAncs::discover(uint16_t conn_handle)
+{
+ // Call BLECentralService discover
+ VERIFY( BLEClientService::discover(conn_handle) );
+ _conn_hdl = BLE_CONN_HANDLE_INVALID; // make as invalid until we found all chars
+
+ // Discover characteristics
+ BLEClientCharacteristic* chr_arr[] = { &_control, &_notification, &_data };
+
+ VERIFY( 3 == Bluefruit.Discovery.discoverCharacteristic(conn_handle, chr_arr, 3) );
+
+ _conn_hdl = conn_handle;
+ return true;
+}
+
+void BLEAncs::setNotificationCallback(notification_callback_t fp)
+{
+ _notif_cb = fp;
+}
+
+bool BLEAncs::enableNotification(void)
+{
+ // enable both Notification & Data Source
+ VERIFY ( _data.enableNotify() );
+ VERIFY ( _notification.enableNotify() );
+
+ return true;
+}
+
+bool BLEAncs::disableNotification(void)
+{
+ _notification.disableNotify();
+ _data.disableNotify();
+
+ return true;
+}
+
+/*------------------------------------------------------------------*/
+/* NOTIFICATION
+ *------------------------------------------------------------------*/
+typedef struct ATTR_PACKED
+{
+ // Cortex M4 can access unaligned memory
+ uint8_t cmd;
+ uint32_t uid;
+ uint8_t attr;
+ uint16_t len; // optional when sending command
+}get_notif_attr_t;
+
+VERIFY_STATIC( sizeof(get_notif_attr_t) == 8);
+
+typedef struct ATTR_PACKED
+{
+ // Cortex M4 can access unaligned memory
+ uint8_t cmd;
+ uint32_t uid;
+ uint8_t actionid;
+}perform_action_t;
+
+VERIFY_STATIC( sizeof(perform_action_t) == 6);
+
+/*------------------------------------------------------------------*/
+/*
+ *------------------------------------------------------------------*/
+uint16_t BLEAncs::getAttribute(uint32_t uid, uint8_t attr, void* buffer, uint16_t bufsize)
+{
+ VERIFY ( attr < ANCS_ATTR_INVALID, 0);
+
+ // command ID | uid | attr (+ len)
+ get_notif_attr_t command =
+ {
+ .cmd = ANCS_CMD_GET_NOTIFICATION_ATTR,
+ .uid = uid,
+ .attr = attr,
+ .len = bufsize
+ };
+ uint8_t cmdlen = 6;
+
+ // Title, Subtitle, Message must require 2-byte length
+ if (attr == ANCS_ATTR_TITLE || attr == ANCS_ATTR_SUBTITLE || attr == ANCS_ATTR_MESSAGE)
+ {
+ cmdlen = 8;
+ }
+
+ // Write command using write response
+#if DEBUG_ANCS
+ PRINT_BUFFER(&command, cmdlen);
+#endif
+
+ _adamsg.prepare(buffer, bufsize);
+ VERIFY( cmdlen == _control.write_resp(&command, cmdlen), 0);
+ VERIFY( _adamsg.waitUntilComplete(BLE_ANCS_TIMEOUT) >= 0, 0);
+
+ // At least 1 packet arrived, enough to know attribute length
+ uint16_t attr_len = ((get_notif_attr_t*) buffer)->len;
+
+ // wait until all data received Or we run out of memory
+ while ( ( (attr_len + sizeof(get_notif_attr_t)) > _adamsg.xferlen ) &&
+ ( _adamsg.remaining > 0 ) )
+ {
+ VERIFY( _adamsg.waitUntilComplete(BLE_ANCS_TIMEOUT) >= 0, 0);
+ }
+
+ // Received length could be less if we run out of buffer
+ attr_len = _adamsg.xferlen - sizeof(get_notif_attr_t);
+
+ // Shift out the Command data, left only Attribute data
+ memmove(buffer, ((uint8_t*)buffer) +sizeof(get_notif_attr_t), attr_len);
+
+ // Include null-terminator for some string application
+ if ( attr_len < bufsize )
+ {
+ ((char*) buffer)[attr_len] = 0;
+ }
+
+ return attr_len;
+
+}
+
+uint16_t BLEAncs::getAppAttribute(const char* appid, uint8_t attr, void* buffer, uint16_t bufsize)
+{
+ VERIFY ( attr < ANCS_APP_ATTR_INVALID, 0);
+
+ // command ID | App ID (including Null terminator) | Attr
+ uint8_t cmdlen = 1 + strlen(appid)+1 + 1;
+ uint8_t* command = (uint8_t*) rtos_malloc( cmdlen );
+
+ command[0] = ANCS_CMD_GET_APP_ATTR;
+ strcpy( (char*) command+1, appid);
+ command[cmdlen-1] = attr;
+
+#if DEBUG_ANCS
+ PRINT_BUFFER(command, cmdlen);
+#endif
+ _adamsg.prepare(buffer, bufsize);
+
+ // Write command using write response
+ if ( cmdlen != _control.write_resp(command, cmdlen) )
+ {
+ rtos_free(command);
+ return 0;
+ }
+ rtos_free(command);
+
+ // Phase 1: Get data until Attribute Length is known
+ while ( (cmdlen+2) > _adamsg.xferlen &&
+ (_adamsg.remaining > 0) )
+ {
+ VERIFY( _adamsg.waitUntilComplete(BLE_ANCS_TIMEOUT) >= 0, 0);
+ }
+
+ uint16_t attr_len;
+ memcpy(&attr_len, ((uint8_t*)buffer)+cmdlen, 2);
+
+ // Phase 2: Get data until all attribute data received
+ // Or we run out of memory
+ while ( (attr_len + cmdlen+2) > _adamsg.xferlen &&
+ (_adamsg.remaining > 0) )
+ {
+ VERIFY( _adamsg.waitUntilComplete(BLE_ANCS_TIMEOUT) >= 0, 0);
+ }
+
+ // Received length could be less if we run out of buffer
+ attr_len = _adamsg.xferlen - (cmdlen+2);
+
+ // Shift out the Command data, left only Attribute data
+ memmove(buffer, ((uint8_t*)buffer) +cmdlen+2, attr_len);
+
+ // including null-terminator for some string application
+ if ( attr_len < bufsize )
+ {
+ ((char*) buffer)[attr_len] = 0;
+ }
+
+ return attr_len;
+}
+
+bool BLEAncs::performAction(uint32_t uid, uint8_t actionid)
+{
+ perform_action_t action =
+ {
+ .cmd = ANCS_CMD_PERFORM_NOTIFICATION_ACTION,
+ .uid = uid,
+ .actionid = actionid
+ };
+
+ return sizeof(perform_action_t) == _control.write_resp(&action, sizeof(perform_action_t));
+}
+
+void BLEAncs::_handleNotification(uint8_t* data, uint16_t len)
+{
+ if ( len != 8 ) return;
+ if ( _notif_cb ) _notif_cb((AncsNotification_t*) data);
+}
+
+void BLEAncs::_handleData(uint8_t* data, uint16_t len)
+{
+#if DEBUG_ANCS
+ PRINT_BUFFER(data, len);
+#endif
+
+ _adamsg.feed(data, len);
+ _adamsg.complete(); // mark as complete each time we received data
+}
+
+/*------------------------------------------------------------------*/
+/* High Level API
+ *------------------------------------------------------------------*/
+uint16_t BLEAncs::getAppID(uint32_t uid, void* buffer, uint16_t bufsize)
+{
+ return getAttribute(uid, ANCS_ATTR_APP_IDENTIFIER, buffer, bufsize);
+}
+
+uint16_t BLEAncs::getTitle(uint32_t uid, void* buffer, uint16_t bufsize)
+{
+ return getAttribute(uid, ANCS_ATTR_TITLE, buffer, bufsize);
+}
+
+uint16_t BLEAncs::getSubtitle(uint32_t uid, void* buffer, uint16_t bufsize)
+{
+ return getAttribute(uid, ANCS_ATTR_SUBTITLE, buffer, bufsize);
+}
+
+uint16_t BLEAncs::getMessage(uint32_t uid, void* buffer, uint16_t bufsize)
+{
+ return getAttribute(uid, ANCS_ATTR_MESSAGE, buffer, bufsize);
+}
+
+uint16_t BLEAncs::getMessageSize(uint32_t uid)
+{
+ char buf[20] = { 0 };
+
+ VERIFY( getAttribute(uid, ANCS_ATTR_MESSAGE_SIZE, buf, sizeof(buf)), 0);
+ uint16_t result = strtoul(buf, NULL, 10);
+
+ return result;
+}
+
+uint16_t BLEAncs::getDate(uint32_t uid, void* buffer, uint16_t bufsize)
+{
+ return getAttribute(uid, ANCS_ATTR_DATE, buffer, bufsize);
+}
+
+uint16_t BLEAncs::getPosActionLabel(uint32_t uid, void* buffer, uint16_t bufsize)
+{
+ return getAttribute(uid, ANCS_ATTR_POSITIVE_ACTION_LABEL, buffer, bufsize);
+}
+
+uint16_t BLEAncs::getNegActionLabel(uint32_t uid, void* buffer, uint16_t bufsize)
+{
+ return getAttribute(uid, ANCS_ATTR_NEGATIVE_ACTION_LABEL, buffer, bufsize);
+}
+
+uint16_t BLEAncs::getAppName(uint32_t uid, void* buffer, uint16_t bufsize)
+{
+ // First get AppID
+ char appID[64] = { 0 };
+ VERIFY( getAppID(uid, appID, sizeof(appID)), 0 );
+
+ // Then get App Display Name
+ return getAppAttribute(appID, ANCS_APP_ATTR_DISPLAY_NAME, buffer, bufsize);
+}
+
+bool BLEAncs::actPositive(uint32_t uid)
+{
+ return performAction(uid, ANCS_ACTION_POSITIVE);
+}
+
+bool BLEAncs::actNegative(uint32_t uid)
+{
+ return performAction(uid, ANCS_ACTION_NEGATIVE);
+}
+
+
+/*------------------------------------------------------------------*/
+/* Callback
+ *------------------------------------------------------------------*/
+void bleancs_notification_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
+{
+ BLEAncs& svc = (BLEAncs&) chr->parentService();
+ svc._handleNotification(data, len);
+}
+
+void bleancs_data_cb(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
+{
+ BLEAncs& svc = (BLEAncs&) chr->parentService();
+ svc._handleData(data, len);
+}