Experimental BLE project showing how IO can be made with an App over BLE. Pointer to matching App will be added when ready, initially this works with: - Android App [nRF-Master Control Panel], supports Write,Read,Notify - Android Project [BluetoothLeGatt]
Dependencies: BLE_API mbed nRF51822
This is an experimental project for BLE (Bluetooth LE == Bluetooth Low Energy == Bluetooth Smart).
- It supports general IO over BLE with Read/Notify/Write support.
- It is compatible with FOTA using Android App "nRF Master Control Panel" (20150126)
- IO supported by:
- Custom Android App is in the WIKI under: Android-App, developed from Android Sample "BluetoothLeGatt"
- Android App: nRF-MCP (Master Control Panel)
- iOS App LightBlue.
- General HRM, HTM, Battery and similar apps should be able to access the matching services.
- It includes combinations of code from other projects, alternative code included can be tried by moving comments (, //)
- 20150126 bleIO r25: It compiles for both "Nordic nRF51822" and "Nordic nRF51822 FOTA" platforms
- 20150126 The matching bleIO App (in wiki) doesn't support FOTA yet, use Android App "nRF Master Control Panel"
Feedback and ideas greatly appreciated!!!
Diff: main.cpp
- Revision:
- 9:2d11beda333f
- Parent:
- 8:f187ba55aed2
- Child:
- 10:ee3a359f7d3f
--- a/main.cpp Tue Dec 16 16:44:56 2014 +0000 +++ b/main.cpp Thu Dec 18 18:53:33 2014 +0000 @@ -1,6 +1,7 @@ //=========Header (PR) // blePRv04, Initial: 20141210 Paul Russell (mbed user: prussell = PR) // This sample includes code from several projects found on http://developer.mbed.org, including but not limited to: +// - http://developer.mbed.org/users/jksoft/code/BLE_RCBController/ (Setting up a custom Service/Characteristic) // - http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/ // - https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LoopbackUART/ // - https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/ @@ -21,7 +22,8 @@ // - Handle All return codes for all functions not void, including BLE not BLE_ERROR_NONE, as a minimum output some debug and log event in non-volatile memory for diagnostics. // - Re-check where voltatile needed // - Evaluate setting: IS_SRVC_CHANGED_CHARACT_PRESENT, see: https://devzone.nordicsemi.com/question/22751/nrftoobox-on-android-not-recognizing-changes-in-application-type-running-on-nordic-pcb/?answer=23097#post-id-23097 -// +// - delete all code with "//x " prefix as that was just for notes and doesn't actually work +// - change all valid code temporarily commended to "//a " prefix to indicate alternnative is valid code //==========End of PR's Header //==========Historic Licencing from original imported sample from mbed website [BLE_HeartRate] ========== @@ -46,7 +48,7 @@ #define UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL 0 //PR: Option to slow the connection intervsal possibly saving power (After Connected) //==========Includes========== -#include "mbed.h" +#include "mbed.h" // mbed #include "BLEDevice.h" // BLE #include "nrf_soc.h" // nRF Internal Temperature Sensor @@ -86,8 +88,14 @@ float f_led2level = 0.0; //Initial Brightness (Typically 0.0~0.5) //==========BLE========== -const static char pcDeviceName[] = "blePRv04"; //PR: Why can App nRF-MCP modify this even though flagged as Const, maybe only temporary mod till App restarts? +const static char pcDeviceName[] = "bleCustomIO"; //PR: Why can App nRF-MCP modify this even though flagged as Const, maybe only temporary mod till App restarts? BLEDevice ble; +//Services&Characteristics + //GattCharacteristic* characteristic = new GattCharacteristic(characteristicUuid, value, size, size, properties); characteristics.push_back(characteristic);//(from: Puck.h) + //characteristic->getHandle() + //ble_error_t readCharacteristicValue (uint16_t handle, uint8_t *const buffer,uint16_t *const lengthP ) + //ble_error_t updateCharacteristicValue(uint16_t handle, const uint8_t *value, uint16_t size, bool localOnly=false) + HealthThermometerService *pServiceHTM; BatteryService *pServiceBattery; HeartRateService *pServiceHRM; @@ -98,6 +106,15 @@ //pServiceUART->getRXCharacteristicHandle(); //ble.updateCharacteristicValue(pServiceUART->getRXCharacteristicHandle(), pData, uLen); // Tx to App +//x /* From: https://developer.mbed.org/users/todotani/code/BLE_Health_Thermometer2-010/wiki/BLE_Health_Thermometer2-010 +//x ble.updateCharacteristicValue(tempChar.getValueAttribute().getHandle(), thermTempPayload, sizeof(thermTempPayload)); +//x ble.updateCharacteristicValue(battLevel.getValueAttribute().getHandle(), (uint8_t *)&batt, sizeof(batt));*/ + +//x /* From: nRF51822_SimpleControls +//x GattCharacteristic txCharacteristic (uart_tx_uuid, txPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); +//x GattCharacteristic rxCharacteristic (uart_rx_uuid, rxPayload, 1, TXRX_BUF_LEN, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); +//x GattCharacteristic *uartChars[] = {&txCharacteristic, &rxCharacteristic}; +//x GattService uartService(uart_base_uuid, uartChars, sizeof(uartChars) / sizeof(GattCharacteristic *));*/ //==========UUID========== //UUID16 List - Advertising these required by App nRF-Toolbox:HRM&HTM @@ -112,30 +129,31 @@ GattService::UUID_BATTERY_SERVICE, //0x180F //BatteryLevel GattService::UUID_LINK_LOSS_SERVICE, //0x1803 //LinkLoss + //TODO: //x GattService::UUID_DFU, //0x1530 - See UARTServiceShortUUID in BLE_API:DFUService.cpp // //x GattService::UARTService, //0x0001~0x0003 - See DFUServiceShortUUID in BLE_API:UARTService.cpp // - //GattService::UUID_ALERT_NOTIFICATION_SERVICE, = 0x1811, - //GattService::UUID_CURRENT_TIME_SERVICE, = 0x1805, - //GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, = 0x1812, - //GattService::UUID_IMMEDIATE_ALERT_SERVICE, = 0x1802, - //GattService::UUID_PHONE_ALERT_STATUS_SERVICE, = 0x180E, - //GattService::UUID_REFERENCE_TIME_UPDATE_SERVICE, = 0x1806, - //GattService::UUID_SCAN_PARAMETERS_SERVICE, = 0x1813, + + //Possibly useful + //GattService::UUID_ALERT_NOTIFICATION_SERVICE, //0x1811 + //GattService::UUID_CURRENT_TIME_SERVICE, //0x1805 + //GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, //0x1812 + //GattService::UUID_IMMEDIATE_ALERT_SERVICE, //0x1802 + //GattService::UUID_PHONE_ALERT_STATUS_SERVICE, //0x180E + //GattService::UUID_REFERENCE_TIME_UPDATE_SERVICE, //0x1806 + //GattService::UUID_SCAN_PARAMETERS_SERVICE, //0x1813 }; //UUID128 List(Only a single UUID128 can fit in advertising) uint8_t puUUID128_Bluetooth_Base_Rev[16] = {0xFB,0x34,0x9B,0x5F,0x80,0, 0,0x80, 0,0x10, 0,0, 0x00,0x00, 0,0};//0000****-0000-1000-8000 00805F9B34FB, Base for UUID16 uint8_t puUUID128_BatteryService[16] = {0xFB,0x34,0x9B,0x5F,0x80,0, 0,0x80, 0,0x10, 0,0, 0x0F,0x18, 0,0};//0000****-0000-1000-8000 00805F9B34FB, ***=0x180F - /* //Adopted from sample code in header of BLE_API:UUID.cpp // // Create a UUID16 (0x180F) // uint8_t shortID[16] = { 0, 0, 0x0F, 0x18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // UUID ble_uuid = UUID(shortID); // Will have: ble_uuid.type = UUID_TYPE_SHORT, ble_uuid.value = 0x180F // // Create a UUID128 // uint8_t longID[16] = { 0x00,0x11,0x22,0x33, 0x44,0x55,0x66,0x77, 0x88,0x99,0xAA,0xBB, 0xCC,0xDD,0xEE,0xFF }; -// UUID custom_uuid = UUID(longID);// Will have: custom_uuid.type=UUID_TYPE_LONG, custom_uuid.value=0x3322, custom_uuid.base=00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF -*/ +// UUID custom_uuid = UUID(longID);// Will have: custom_uuid.type=UUID_TYPE_LONG, custom_uuid.value=0x3322, custom_uuid.base=00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF*/ /* // The Nordic UART Service //static const uint8_t uart_base_uuid[] = {0x71, 0x3D, 0, 0, 0x50, 0x3E, 0x4C, 0x75, 0xBA, 0x94, 0x31, 0x48, 0xF1, 0x8D, 0x94, 0x1E}; @@ -198,6 +216,77 @@ b_Ticker1 = true; //PR: Flag to handle Ticker1 Event in Main loop so interupts not blocked. } +//==========Custom: a service for IO (GPIO,PWM,etc.)========== +// TODO: Change from dummy UUID16 to proper UUID128 +//UUID128 List(Only a single UUID128 can fit in advertising) + +// Custom UUID128 base for this service and its characteristics (from: http://www.itu.int/ITU-T/asn1/uuid.html) + // **This is a temporary UUID for testing by anyone, should be replaced before proceeding to real product.** + // Can check that UUID is correctly entered by viewing with Android App: nRF-Master Control panel + // UUID128 can be generated using: http://www.itu.int/ITU-T/asn1/uuid.html + // 20141218PR: [4d3281c0-86d1-11e4-b084-0002a5d5c51b] == OID[2.25.102612802202735078710424021169255859483] + // {0x1*,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Proper Flipped order + uint8_t puUUID128_CustomServiceIO[16] = {0x10,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Service + uint8_t puUUID128_CustomW8[16] = {0x11,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Value:Write[8byte] to mbed from phone + uint8_t puUUID128_CustomR4[16] = {0x12,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Value:Read[4byte] from mbed to phone + uint8_t puUUID128_CustomN4[16] = {0x13,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Value:Notify[4byte] from mbed to phone + + //uint8_t puUUID128_CustomX_Config32[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Proper Flipped order + //uint8_t puUUID128_CustomX_OutDig1[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Proper Flipped order + //uint8_t puUUID128_CustomX_OutPWM100[16]= {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Proper Flipped order + //uint8_t puUUID128_CustomX_OutPWM256[16]= {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Proper Flipped order + //uint8_t puUUID128_CustomX_InDig1[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Proper Flipped order + //uint8_t puUUID128_CustomX_InADC12[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Proper Flipped order + +// Characteristic Value Storage: + uint8_t pCustomW8[8] = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17}; //Value Storage for Write Characteristic (Doesn't have to be byte array[]) + uint8_t pCustomR4[4] = {0x20,0x21,0x22,0x23}; //Value Storage for Read Characteristic (Doesn't have to be byte array[]) + uint8_t pCustomN4[4] = {0x30,0x31,0x32,0x33}; //Value Storage for Notify Characteristic(Doesn't have to be byte array[]) + + //x GattCharacteristic::GattAttribute *descriptors[]; //How Set Characteristic Descriptors? Maybe in the App is easier? + + //GattCharacteristic xx(*UUID, *Payload=*Value, LenInit, LenMax, Properties, *Descriptors, NumDescriptors) + GattCharacteristic CustomW8(puUUID128_CustomW8, pCustomW8, sizeof(pCustomW8), sizeof(pCustomW8), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); + GattCharacteristic CustomR4(puUUID128_CustomR4, pCustomR4, sizeof(pCustomR4), sizeof(pCustomR4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); + GattCharacteristic CustomN4(puUUID128_CustomN4, pCustomN4, sizeof(pCustomN4), sizeof(pCustomN4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); + GattCharacteristic *pCustomCharacteristics[] = {&CustomW8, &CustomR4, &CustomN4};//Pointers to Characteristics + + //GattService: + //GattService::GattService(*UUID, *GattCharacteristics, numCharacteristics) : + GattService ServiceCustomIO(puUUID128_CustomServiceIO, pCustomCharacteristics, (sizeof(pCustomCharacteristics)/sizeof(pCustomCharacteristics[0]))); + GattService *pServiceCustomIO= &ServiceCustomIO; // Pointer to Service + +void update_CustomW8(void) +{ + static uint8_t u8_w8; //Level: 0..2 + switch(u8_w8++){ + case 0: memcpy(pCustomW8, "w0w0w0wa", sizeof(pCustomW8)); DEBUG(" UpdateW8a"); break; + case 1: memcpy(pCustomW8, "w1w1w1wb", sizeof(pCustomW8)); DEBUG(" UpdateW8b"); break; + default: memcpy(pCustomW8, "w2w2w2wc", sizeof(pCustomW8)); DEBUG(" UpdateW8c"); u8_w8=0; break; + } + //pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE +} + +void update_CustomR4(void) +{ + static uint8_t u8_r4; //Level: 0..2 + switch(u8_r4++){ + case 0: memcpy(pCustomR4, "r0r0", sizeof(pCustomR4)); DEBUG(" UpdateR4a"); break; + case 1: memcpy(pCustomR4, "r1r1", sizeof(pCustomR4)); DEBUG(" UpdateR4b"); break; + default: memcpy(pCustomR4, "r2r2", sizeof(pCustomR4)); DEBUG(" UpdateR4c"); u8_r4=0; break; + } +} + +void update_CustomN4(void) +{ + static uint8_t u8_n4; //Level: 0..2 + switch(u8_n4++){ + case 0: memcpy(pCustomN4, "n0n0", sizeof(pCustomR4)); DEBUG(" UpdateN4a"); break; + case 1: memcpy(pCustomN4, "n1n1", sizeof(pCustomR4)); DEBUG(" UpdateN4b"); break; + default: memcpy(pCustomN4, "n2n2", sizeof(pCustomR4)); DEBUG(" UpdateN4c"); u8_n4=0; break; + } +} + //==========Functions:BLE========== void Callback_BLE_onTimeout(void) { @@ -258,11 +347,43 @@ } void Callback_BLE_onDataWritten(const GattCharacteristicWriteCBParams *pParams) -{ - // Callback_BLE_onDataWritten == This does occur when use nRF-MCP to save New Heart Rate Control Point (Ignored if incorrect length) +{ // This occurs when use nRF-MCP to save a new Characteristic Value (SingleUpASrrowIcon) such as New Heart Rate Control Point (unsent if incorrect length) + GattAttribute::Handle_t tHandle=pParams->charHandle; //Warning: *data may not be NULL terminated - DEBUG("\nBLEi: Callback_BLE_onDataWritten(Handle:%d, eOp:%d, uOffset:%u uLen:%u Data0[0x%02x]=Data[%*s]\n", pParams->charHandle, pParams->op, pParams->offset, pParams->len, (char)(pParams->data[0]), pParams->len, pParams->data); + DEBUG("\nBLEi: Callback_BLE_onDataWritten(Handle:%d, eOp:%d, uOffset:%u uLen:%u Data0[0x%02x]=Data[%*s]\n", tHandle, pParams->op, pParams->offset, pParams->len, (char)(pParams->data[0]), pParams->len, pParams->data); + +//TODO: Add check of op type with thandle check: switch(pParams->op){ case: GATTS_CHAR_OP_WRITE_REQ:}... + + // These are equivalent ways of accessing characteristic handles: + // if (pParams->charHandle == CustomW8.getValueHandle()) { DEBUG("\n\nOK tHandle %d\n\n", pParams->charHandle); } + // if (pParams->charHandle == ServiceCustomIO.getCharacteristic(0)->getValueHandle()) { DEBUG("\n\nOK tHandle %d\n\n", pParams->charHandle); } + // if (CustomW8.getValueHandle() == ServiceCustomIO.getCharacteristic(0)->getValueHandle()) { DEBUG("\n\nOK tHandle equivalent %d\n\n", pParams->charHandle); } + + //Handle Changes to Service Characteristics: + uint16_t uLen; + //x if (tHandle == pServiceLinkLoss->getCharacteristic(0)->getValueHandle()) { + //x LinkLossService::AlertLevel_t tLLnewAlertLevel; + //x ble.readCharacteristicValue(tHandle, tLLnewAlertLevel, &uLen); + //x DEBUG(" LinkLoss[%d]:AlertLevel[%d]\n", uLen, tLLnewAlertLevel); //TODO: Confirm that actually writing to the LinkLoss service is properly handled in the service. + //x //TODO: Update Outputs + //X } else + if (tHandle == CustomW8.getValueHandle()) { + ble.readCharacteristicValue(tHandle, pCustomW8, &uLen); + //x memcpy( pCustomW8b, pCustomW8, sizeof(pCustomW8b)); //TODO: Working copy?? + DEBUG(" CustomW8[%d]:%02X %02X %02X %02X %02X %02X %02X %02X\n", uLen, pCustomW8[0], pCustomW8[1], pCustomW8[2], pCustomW8[3],pCustomW8[4], pCustomW8[5], pCustomW8[6], pCustomW8[7]); + //TODO: Update Outputs + } else if (tHandle == CustomR4.getValueHandle()) { + ble.readCharacteristicValue(tHandle, pCustomR4, &uLen); + DEBUG(" CustomR4[%d]:%02X %02X %02X %02X\n", uLen, pCustomR4[0], pCustomR4[1], pCustomR4[2], pCustomR4[3]); + //TODO: Update Outputs + } else if (tHandle == CustomN4.getValueHandle()) { + ble.readCharacteristicValue(tHandle, pCustomN4, &uLen); + DEBUG(" CustomN4[%d]:%02X %02X %02X %02X\n", uLen, pCustomN4[0], pCustomN4[1], pCustomN4[2], pCustomN4[3]); + //TODO: Update Outputs + } else { + DEBUG("\n Unknown Onwrite(tHandle:%d)\n\n", tHandle); + } //Triggered by BluetoothLEGatt sample changing Linkloss setting: //Alert=1: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x01]=Data[] @@ -293,19 +414,26 @@ } */ - + //pServiceLinkLoss->setAlertLevel( AlertLevel_t newLevel ) } void Callback_BLE_onUpdatesEnabled(Gap::Handle_t tHandle) -{ +{ //Tiggered when click the UpdateContinuousIcon in nRF-MCP(ThreeDownArrows) DEBUG("\nBLEi: Callback_BLE_onUpdates(Handle:%d)\r\n", tHandle); + if (tHandle == CustomW8.getValueHandle()) { DEBUG(" onUpdates Enabled - CustomW8\n"); + } else if (tHandle == CustomR4.getValueHandle()) { DEBUG(" onUpdates Enabled - CustomR4\n"); + } else if (tHandle == CustomN4.getValueHandle()) { DEBUG(" onUpdates Enabled - CustomN4\n"); + } else { DEBUG(" onUpdates Enabled - Characteristic Handle[%d]\n", tHandle); + } } // Adopted 20141213 from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LinkLoss/file/440ee5e8595f/main.cpp void Callback_BLE_onLinkLoss(LinkLossService::AlertLevel_t level) { printf("\nBLEi: Link loss alert[%d]\n", level); + + //pServiceLinkLoss->setAlertLevel( AlertLevel_t newLevel ) } //==========HRM========== //Adopted 2014Dec from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/ @@ -325,8 +453,7 @@ //Adopted 2014Dec from: https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/ // Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.health_thermometer.xml // HTM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml - -//****Although nRF-MCP displays float OK, the HTM Apps don't, its possible IEEE format required like in the original code:BLE_HTM_by_InTempSensr +//****Although nRF-MCP displays float OK, the HTM Apps don't, it is likely IEEE format required like in the original project: BLE_HTM_by_InTempSensr float update_htm(void) { //static float fTemperature = 0;//-123.456; @@ -355,6 +482,7 @@ DEBUG("[BATT:%d%%]", u8_BattPercent); return(u8_BattPercent); } + //==========main========== int main(void) { @@ -381,27 +509,33 @@ ble.onTimeout(Callback_BLE_onTimeout); ble.onUpdatesEnabled(Callback_BLE_onUpdatesEnabled); - DEBUG("BLE: Handles:\n"); - //DEBUG(" Service: BLE: %d\n", ble.getHandle()); - //DEBUG(" Characteristic:BattLevel: %d\n", batteryLevelCharacteristic.getValueAttribute().getHandle()); - //DFUService - - - - -//ble_error_t readCharacteristicValue ( uint16_t handle, uint8_t *const buffer, uint16_t *const lengthP ) -//ble_error_t updateCharacteristicValue (uint16_t handle, const uint8_t *value, uint16_t size, bool localOnly=false) - -//UUID List by Services that are setup //BLE2: Setup Services (with their initial values and options) DEBUG("BLE: Setup Services\n"); // *Order here affects order in nRF-MCP Discovery of Services DeviceInformationService ServiceDeviceInfo(ble, "Maker", pcDeviceName, "sn1234", "hw00", "fw00", "sw00");//(BLEDevice), pcManufacturer, pcModelNumber, pcSerialNumber, pcHWver, pcFWver, pcSWver - pServiceDeviceInfo = &ServiceDeviceInfo; DEBUG(" Handle Service DeviceInfo:%d\n", ServiceDeviceInfo); + pServiceDeviceInfo = &ServiceDeviceInfo; //DEBUG(" Handle Service DeviceInfo:%d\n", ServiceDeviceInfo.XXXgetHandle()); LinkLossService ServiceLinkLoss(ble, Callback_BLE_onLinkLoss, LinkLossService::HIGH_ALERT); //New20141213, TBD pServiceLinkLoss = &ServiceLinkLoss; + //x //How to get characteristics of service, so gan get characteristic's handles used in callbacks? + //x // DEBUG(" DeviceInfo CharacteristicHandle: LinkLoss: AlertLevel:%d\n", ServiceLinkLoss.alertLevelChar.getValueAttribute().getHandle()); + //x //ble.updateCharacteristicValue(tempChar.getValueAttribute().getHandle(), thermTempPayload, sizeof(thermTempPayload)); + //x //ble.updateCharacteristicValue(battLevel.getValueAttribute().getHandle(), (uint8_t *)&batt, sizeof(batt)); + BatteryService ServiceBattery(ble, 10); pServiceBattery = &ServiceBattery; + //DEBUG(" Service:Batt Characteristic:BatteryLevel: %d\n", ServiceBattery->charTable[0].getValueAttribute().getHandle()); //.batteryLevelCharacteristic + //x uint32_t *h = ServiceBattery.charTable[0].getValueAttribute().getHandle(); //.batteryLevelCharacteristic + //x uint32_t *h = ServiceBattery.batteryLevelCharacteristic.getValueAttribute().getHandle(); //.batteryLevelCharacteristic + //x DEBUG(" Service:Batt Characteristic:BatteryLevel: %d\n", ServiceBattery->batteryLevelCharacteristic.getValueAttribute().getHandle()); //.batteryLevelCharacteristic + //x DEBUG(" Service:Batt Characteristic:BatteryLevel: %d\n", ServiceBattery->batteryLevelCharacteristic.getValueAttribute().getHandle()); //.batteryLevelCharacteristic + //x //GattCharacteristic *charTable[] = {&batteryLevelCharacteristic}; + + //x //uartServicePtr->txCharacteristic.getValueAttribute().getHandle() // From: GattCharacteristic txCharacteristic; + //x // DEBUG(" DeviceInfo CharacteristicHandle: BatteryLevel:%d\n", ServiceBattery.batteryLevelCharacteristic.getValueAttribute().getHandle()); + //x // batteryLevelCharacteristic(GattCharacteristic::UUID_BATTERY_LEVEL_CHAR, &batteryLevel, sizeof(batteryLevel), sizeof(batteryLevel), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY) { + //x //ble.updateCharacteristicValue(tempChar.getValueAttribute().getHandle(), thermTempPayload, sizeof(thermTempPayload)); + //x //ble.updateCharacteristicValue(battLevel.getValueAttribute().getHandle(), (uint8_t *)&batt, sizeof(batt)); + HeartRateService ServiceHRM(ble, (uint8_t)111, HeartRateService::LOCATION_FINGER); pServiceHRM = &ServiceHRM; HealthThermometerService ServiceHTM(ble, 33.3, HealthThermometerService::LOCATION_EAR); @@ -409,22 +543,33 @@ //UARTService ServiceUART(ble); // pServiceUART = &ServiceUART; + //Start CustomIO Service: + ble.addService(ServiceCustomIO); //Pointer: pServiceCustomIO + DEBUG(" CustomIO: Field Lengths: [%d] [%d] [%d]\n", sizeof(pCustomW8), sizeof(pCustomR4), sizeof(pCustomN4)); + DEBUG(" CustomIO: Initial Values: [%02x] [%02x] [%02x]\n", pCustomW8[0], pCustomR4[0], pCustomN4[0]); + //BLE3: Setup advertising // Note the Advertising payload is limited to 31bytes, so careful don't overflow this. Multiple UUID16's or a single UUID128 are possible in advertising. // If there isn't enough space, then the accumulatepayload won't add that block to the advertising, it won't add a partial block. - DEBUG("BLE: Setup Advertising\n"); + DEBUG("BLE: Setup Advertising - Apps like HRM/HTM/UART require matching UUID\n"); ble.clearAdvertisingPayload(); //Prep ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(1000)); //PR: Advertise 1sec (1Hz) ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); //PR: TODO: To Study ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); //PR: BLE Only (Option:LE_LIMITED_DISCOVERABLE) // Does "LE_GENERAL_DISCOVERABLE" affect whether UUID needs to be advertised to discover services? - //ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)UUID128_UART_Rev, sizeof(UUID128_UART_Rev)); //Single UUID128 - //ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)puUUID128_BatteryService, sizeof(puUUID128_BatteryService)); //Single UUID128 - //ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, puStrToUUID128( pc_com_none_servA), LENGTH_OF_LONG_UUID); //Single UUID128 - //ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // Multiple UUID16 + //x ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)UUID128_UART_Rev, sizeof(UUID128_UART_Rev)); //Single UUID128 + //a ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)puUUID128_BatteryService, sizeof(puUUID128_BatteryService)); //Single UUID128 + ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)puUUID128_CustomServiceIO, sizeof(puUUID128_CustomServiceIO)); //Single UUID128 + //a ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, puStrToUUID128( pc_com_none_servA), LENGTH_OF_LONG_UUID); //Single UUID128 + //a ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // Multiple UUID16 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // Multiple UUID16 - //ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);//PR: Add Appearance::HeartRate (HRM Connects without this) - //ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER); //PR: Add Appearance::Thermometer (HTM Connects without this, though float format still needs change) + //a ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);//PR: Add Appearance::HeartRate (HRM Connects without this) + //a ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER); //PR: Add Appearance::Thermometer (HTM Connects without this, though float format still needs change) + + +// main() init: +// ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)puUUID128_CustomService1, sizeof(puUUID128_CustomService1)); + // Add LocalName last so if Advertising too long will easily see as Name won't be available for the device. ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)pcDeviceName, sizeof(pcDeviceName)-1);//PR: LocalName (No NULL) ble.startAdvertising(); @@ -461,6 +606,10 @@ update_hrm(); update_batt(); + update_CustomW8(); + update_CustomR4(); + update_CustomN4(); + DEBUG("BLE: Wakes:%u Delta:%d ", u32_wakeevents, u32_wakeevents-u32_wakelast); //For Evaluating Timing u32_wakelast = u32_wakeevents; } else if (b_Ticker1) { @@ -478,3 +627,73 @@ } } //========== end ========== +#ifdef NOT_DEFINED //From puck.h - Adding Characteristic +void Puck::addCharacteristic(const UUID serviceUuid, const UUID characteristicUuid, int bytes, int properties) { + MBED_ASSERT(bytes <= 20); + uint16_t size = sizeof(uint8_t) * bytes; + uint8_t* value = (uint8_t*) malloc(size); + if(value == NULL) { + LOG_ERROR("Unable to malloc value for characteristic. Possibly out of memory!\n"); + } + + GattCharacteristic* characteristic = new GattCharacteristic(characteristicUuid, value, size, size, properties); + characteristics.push_back(characteristic); + + + GattService* service = NULL; + + int removeIndex = -1; + for(int i = 0; i < services.size(); i++) { + if(isEqualUUID(&services[i]->getUUID(), serviceUuid)) { + service = services[i]; + removeIndex = i; + break; + } + } + GattCharacteristic** characteristics = NULL; + int characteristicsLength = 0; + if(service != NULL) { + characteristicsLength = service->getCharacteristicCount() + 1; + characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength); + if(characteristics == NULL) { + LOG_ERROR("Unable to malloc array of characteristics for service creation. Possibly out of memory!\n"); + } + for(int i = 0; i < characteristicsLength; i++) { + characteristics[i] = service->getCharacteristic(i); + } + services.erase(services.begin() + removeIndex); + delete service; + free(previousCharacteristics); + } else { + characteristicsLength = 1; + characteristics = (GattCharacteristic**) malloc(sizeof(GattCharacteristic*) * characteristicsLength); + if(characteristics == NULL) { + LOG_ERROR("Unable to malloc array of characteristics for service creation. Possibly out of memory!\n"); + } + } + + characteristics[characteristicsLength - 1] = characteristic; + previousCharacteristics = characteristics; + service = new GattService(serviceUuid, characteristics, characteristicsLength); + services.push_back(service); + LOG_DEBUG("Added characteristic.\n"); +} + + +void Puck::updateCharacteristicValue(const UUID uuid, uint8_t* value, int length) { + GattCharacteristic* characteristic = NULL; + for( int i = 0; i < characteristics.size(); i++) { + if(isEqualUUID(&characteristics[i]->getUUID(), uuid)) { + characteristic = characteristics[i]; + break; + } + } + if(characteristic != NULL) { + ble.updateCharacteristicValue(characteristic->getHandle(), value, length); + LOG_VERBOSE("Updated characteristic value.\n"); + } else { + LOG_WARN("Tried to update an unkown characteristic!\n"); + } +} +#endif +//================================================= \ No newline at end of file