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!!!
main.cpp@10:ee3a359f7d3f, 2014-12-19 (annotated)
- Committer:
- prussell
- Date:
- Fri Dec 19 22:37:29 2014 +0000
- Revision:
- 10:ee3a359f7d3f
- Parent:
- 9:2d11beda333f
- Child:
- 11:7d02fe5ebea5
Cleanup, Characteristic Updates to Host Enabled
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
prussell | 0:0217a862b047 | 1 | //=========Header (PR) |
prussell | 0:0217a862b047 | 2 | // blePRv04, Initial: 20141210 Paul Russell (mbed user: prussell = PR) |
prussell | 0:0217a862b047 | 3 | // This sample includes code from several projects found on http://developer.mbed.org, including but not limited to: |
prussell | 9:2d11beda333f | 4 | // - http://developer.mbed.org/users/jksoft/code/BLE_RCBController/ (Setting up a custom Service/Characteristic) |
prussell | 0:0217a862b047 | 5 | // - http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/ |
prussell | 0:0217a862b047 | 6 | // - https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LoopbackUART/ |
prussell | 1:4a25d917fb6a | 7 | // - https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/ |
prussell | 6:5b6fb35b4450 | 8 | // - https://developer.mbed.org/users/garimagupta002/notebook/ble-uart-lcd-demo/ (Advertise UUID16+UUID128) |
prussell | 10:ee3a359f7d3f | 9 | // - http://developer.mbed.org/teams/UCL-IoT/code/BLE_LED_Controller/ (With matching Android App, uses BLE-UART) |
prussell | 10:ee3a359f7d3f | 10 | // - https://developer.mbed.org/users/todotani/code/BLE_Health_Thermometer2-010/wiki/BLE_Health_Thermometer2-010 //Temperature handles |
prussell | 10:ee3a359f7d3f | 11 | // - |
prussell | 0:0217a862b047 | 12 | // - miscellaneous adopted from more samples... |
prussell | 0:0217a862b047 | 13 | // Reference: |
prussell | 0:0217a862b047 | 14 | // - http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/ |
prussell | 0:0217a862b047 | 15 | // - Reference: http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/docs/tip/ |
prussell | 0:0217a862b047 | 16 | // - Reference: http://developer.mbed.org/teams/Bluetooth-Low-Energy/ |
prussell | 0:0217a862b047 | 17 | // Warnings: |
prussell | 0:0217a862b047 | 18 | // - As of 20141210 it is necessary to use Android App [nRF-Master Control Panel] to ensure any previous connected |
prussell | 0:0217a862b047 | 19 | // code on mkit is properly Disconnected before trying to connect other Android nRF Apps (nRFToolbox, nRF UART 2.0, etc.). |
prussell | 0:0217a862b047 | 20 | // As UART device doesn't offer disconnect you may need to load a 3rf sample, then connect, then discoonect, to clear the link. |
prussell | 0:0217a862b047 | 21 | // Notes: |
prussell | 10:ee3a359f7d3f | 22 | // - onDataSent() maybe only occuring when confirmed receive by phone, as onDataSent() didn't happen when phone moved out of range, and no onDataSent() with App nRF-MCP. |
prussell | 0:0217a862b047 | 23 | // - onDisconnect(Reason:8) occured after ~20Tx with no onDataSent() after phone moved out of range, OnTimeout didn't occur at all. |
prussell | 0:0217a862b047 | 24 | // ToDo: and ToCheck: |
prussell | 8:f187ba55aed2 | 25 | // - 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. |
prussell | 0:0217a862b047 | 26 | // - Re-check where voltatile needed |
prussell | 8:f187ba55aed2 | 27 | // - 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 |
prussell | 9:2d11beda333f | 28 | // - delete all code with "//x " prefix as that was just for notes and doesn't actually work |
prussell | 9:2d11beda333f | 29 | // - change all valid code temporarily commended to "//a " prefix to indicate alternnative is valid code |
prussell | 0:0217a862b047 | 30 | //==========End of PR's Header |
prussell | 0:0217a862b047 | 31 | |
prussell | 0:0217a862b047 | 32 | //==========Historic Licencing from original imported sample from mbed website [BLE_HeartRate] ========== |
prussell | 0:0217a862b047 | 33 | //From: http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/ |
prussell | 0:0217a862b047 | 34 | /* mbed Microcontroller Library |
prussell | 0:0217a862b047 | 35 | * Copyright (c) 2006-2013 ARM Limited |
prussell | 0:0217a862b047 | 36 | * |
prussell | 0:0217a862b047 | 37 | * Licensed under the Apache License, Version 2.0 (the "License"); |
prussell | 0:0217a862b047 | 38 | * you may not use this file except in compliance with the License. |
prussell | 0:0217a862b047 | 39 | * You may obtain a copy of the License at |
prussell | 0:0217a862b047 | 40 | * http://www.apache.org/licenses/LICENSE-2.0 |
prussell | 0:0217a862b047 | 41 | * Unless required by applicable law or agreed to in writing, software |
prussell | 0:0217a862b047 | 42 | * distributed under the License is distributed on an "AS IS" BASIS, |
prussell | 0:0217a862b047 | 43 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
prussell | 0:0217a862b047 | 44 | * See the License for the specific language governing permissions and |
prussell | 0:0217a862b047 | 45 | * limitations under the License. */ |
prussell | 0:0217a862b047 | 46 | //==========end of Historic Licencing ========== |
prussell | 0:0217a862b047 | 47 | |
prussell | 0:0217a862b047 | 48 | |
prussell | 0:0217a862b047 | 49 | //==========Compile Options========== |
prussell | 0:0217a862b047 | 50 | #define ENABLE_SerialUSB_DEBUG_CONSOLE 1 //PR: Enable Debug on mbed's USB Serial Debug, Setup: Serial 9600,8,N,1,NoFlowControl (TeraTerm: http://en.sourceforge.jp/projects/ttssh2/releases/) |
prussell | 0:0217a862b047 | 51 | #define UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL 0 //PR: Option to slow the connection intervsal possibly saving power (After Connected) |
prussell | 0:0217a862b047 | 52 | |
prussell | 0:0217a862b047 | 53 | //==========Includes========== |
prussell | 9:2d11beda333f | 54 | #include "mbed.h" // mbed |
prussell | 1:4a25d917fb6a | 55 | #include "BLEDevice.h" // BLE |
prussell | 1:4a25d917fb6a | 56 | #include "nrf_soc.h" // nRF Internal Temperature Sensor |
prussell | 0:0217a862b047 | 57 | |
prussell | 0:0217a862b047 | 58 | //Services |
prussell | 0:0217a862b047 | 59 | #include "BatteryService.h" |
prussell | 0:0217a862b047 | 60 | #include "DeviceInformationService.h" |
prussell | 5:d36bbb315e31 | 61 | #include "HealthThermometerService.h" |
prussell | 0:0217a862b047 | 62 | #include "HeartRateService.h" |
prussell | 5:d36bbb315e31 | 63 | #include "LinkLossService.h" //TODO: How support this? |
prussell | 8:f187ba55aed2 | 64 | //#include "DFUService" //TODO: DFU and FOTA Support |
prussell | 0:0217a862b047 | 65 | //#include "UARTService.h" //TODO: Add a UART Channel for streaming data like logs? |
prussell | 0:0217a862b047 | 66 | |
prussell | 0:0217a862b047 | 67 | //==========Debug Console========== |
prussell | 0:0217a862b047 | 68 | #if ENABLE_SerialUSB_DEBUG_CONSOLE |
prussell | 6:5b6fb35b4450 | 69 | //Restart TeraTerm just before Pressing Reset on mbed, Default:9600-8N1(No Flow Control) |
prussell | 10:ee3a359f7d3f | 70 | //Using default baud rate to avoid issues with DEBUG in a constructor being at default baud rate before main() |
prussell | 6:5b6fb35b4450 | 71 | Serial debug_serial(USBTX, USBRX); //PR: DebugSerialOverMbedUSB |
prussell | 0:0217a862b047 | 72 | #define DEBUG(...) { debug_serial.printf(__VA_ARGS__); } |
prussell | 0:0217a862b047 | 73 | #else |
prussell | 6:5b6fb35b4450 | 74 | #define DEBUG(...) //Do Nothing, DEBUG() lines are ignored |
prussell | 0:0217a862b047 | 75 | #endif |
prussell | 0:0217a862b047 | 76 | |
prussell | 2:c77c2b06d604 | 77 | //========== This Section is to Create Debug output showing variable prep before main() ========== |
prussell | 2:c77c2b06d604 | 78 | bool u8_prep_dummy(void) { |
prussell | 6:5b6fb35b4450 | 79 | DEBUG("\n\nBLE: ____Prep Memory____\n"); //May comment this out if none of the following Objects/Classes call initiator functions with debug |
prussell | 2:c77c2b06d604 | 80 | return true; |
prussell | 2:c77c2b06d604 | 81 | } |
prussell | 2:c77c2b06d604 | 82 | const bool bPrep = u8_prep_dummy(); |
prussell | 2:c77c2b06d604 | 83 | |
prussell | 0:0217a862b047 | 84 | //==========LEDs========== |
prussell | 0:0217a862b047 | 85 | //DigitalOut out_led1(LED1); //PR: Firmware heartbeat |
prussell | 0:0217a862b047 | 86 | //DigitalOut out_led2(LED2); //PR: Firmware heartbeat |
prussell | 0:0217a862b047 | 87 | PwmOut pwm_led1(LED1); //PR: Firmware Life Indicator |
prussell | 0:0217a862b047 | 88 | PwmOut pwm_led2(LED2); //TODO: Controlled by App |
prussell | 10:ee3a359f7d3f | 89 | float f_led1level = 0.1; //Initial Brightness (Typically 0.0~0.5) |
prussell | 10:ee3a359f7d3f | 90 | float f_led2level = 0.1; //Initial Brightness (Typically 0.0~0.5) |
prussell | 0:0217a862b047 | 91 | |
prussell | 0:0217a862b047 | 92 | //==========BLE========== |
prussell | 10:ee3a359f7d3f | 93 | const static char pcDeviceName[] = "bleIO"; //PR: Why can App nRF-MCP modify this even though flagged as Const, maybe only temporary mod till App restarts? |
prussell | 0:0217a862b047 | 94 | BLEDevice ble; |
prussell | 10:ee3a359f7d3f | 95 | //Pointers to services for accesses outside main() |
prussell | 10:ee3a359f7d3f | 96 | HealthThermometerService *pServiceHTM; //tempChar.getValueAttribute().getHandle() |
prussell | 10:ee3a359f7d3f | 97 | BatteryService *pServiceBattery; //battLevel.getValueAttribute().getHandle() |
prussell | 8:f187ba55aed2 | 98 | HeartRateService *pServiceHRM; |
prussell | 8:f187ba55aed2 | 99 | DeviceInformationService *pServiceDeviceInfo; |
prussell | 8:f187ba55aed2 | 100 | LinkLossService *pServiceLinkLoss; |
prussell | 10:ee3a359f7d3f | 101 | //x DFUService *pServiceDFU; |
prussell | 10:ee3a359f7d3f | 102 | //x UARTService *pServiceUART; //FromApp: pServiceUART->getTXCharacteristicHandle(); //ToApp: ble.updateCharacteristicValue(pServiceUART->getRXCharacteristicHandle(), pData, uLen); |
prussell | 10:ee3a359f7d3f | 103 | //x bleIOService *pServiceIO; |
prussell | 3:a98203f84063 | 104 | |
prussell | 6:5b6fb35b4450 | 105 | //==========UUID========== |
prussell | 6:5b6fb35b4450 | 106 | //UUID Info: |
prussell | 5:d36bbb315e31 | 107 | // 20141213 From https://developer.bluetooth.org/community/lists/community%20discussion/flat.aspx?rootfolder=/community/lists/community+discussion/16+bit+uuid+vs.+128+uuid&folderctid=0x01200200e2f0e56e3d53004dba96bdf0c357551f |
prussell | 5:d36bbb315e31 | 108 | /* All the custom GATT based services and characteristics must use a 128 bit UUID. |
prussell | 5:d36bbb315e31 | 109 | The Bluetooth_Base_UUID is: 00000000-0000-1000-8000 00805F9B34FB. |
prussell | 5:d36bbb315e31 | 110 | All the 16-bit Attribute UUIDs defined in the adopted specifications use the above base. |
prussell | 5:d36bbb315e31 | 111 | Generating a 128 bit UUID for custom profiles: For the 128 bit UUID, please refer to The ITU-T Rec. X.667. |
prussell | 5:d36bbb315e31 | 112 | You can download a free copy of ITU-T Rec. X.667 from http://www.itu.int/ITU-T/studygroups/com17/oid/X.667-E.pdf. |
prussell | 5:d36bbb315e31 | 113 | In addition if you go to http://www.itu.int/ITU-T/asn1/uuid.html, you can generate a unique 128-bit UUID. |
prussell | 5:d36bbb315e31 | 114 | Latency: Refer to Core Spec V4.0, Vol 3, Part F - 3.3.1, which is "Ready by Type Request". |
prussell | 5:d36bbb315e31 | 115 | If you look at the PDU, you have to send 2 bytes UUID for adopted profiles and 16 bytes UUID for custom profiles. |
prussell | 5:d36bbb315e31 | 116 | There is additional 14 extra bytes over head for the custom profiles for "Ready By Type Request Method" |
prussell | 5:d36bbb315e31 | 117 | Note: All attribute types are effectively compared as 128-bit UUIDs, even if a 16-bit UUID is provided in this request or defined for an attribute. (See Section 3.2.1.) |
prussell | 10:ee3a359f7d3f | 118 | A suggestive alternative will be to use a notification method, (see 3.4.7.1), where you don't need the 128 bit UUID or the indication method (see 3.4.7.2) */ |
prussell | 10:ee3a359f7d3f | 119 | // 16bit UUID uses: 128bit base(32 hex digits): 0000****-0000-1000-8000 00805F9B34FB (Careful to use unsigned to avoid negatives and overflows) |
prussell | 10:ee3a359f7d3f | 120 | // 32bit UUID uses: 128bit base(32 hex digits): ********-0000-1000-8000 00805F9B34FB (Careful to use unsigned to avoid negatives and overflows) |
prussell | 10:ee3a359f7d3f | 121 | |
prussell | 10:ee3a359f7d3f | 122 | //UUID128 List(Only a single UUID128 can fit in advertising) |
prussell | 10:ee3a359f7d3f | 123 | //a 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, where ****==UUID16 |
prussell | 10:ee3a359f7d3f | 124 | //a 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, where ****==0x180F |
prussell | 4:976394791d7a | 125 | |
prussell | 10:ee3a359f7d3f | 126 | //a //UUID16 List - Advertising these required by App nRF-Toolbox:HRM&HTM // Keep list short so advertizing not too long. |
prussell | 10:ee3a359f7d3f | 127 | /*static const uint16_t uuid16_list[] = { //Service List (Pre-defined standard 16bit services) |
prussell | 10:ee3a359f7d3f | 128 | // *Order here doesn't affect order in nRF-MCP Discovery of Services |
prussell | 10:ee3a359f7d3f | 129 | //BLE_UUID_GAP UUID_GENERIC_ACCESS //0x1800 //Included by Default, DeviceName, Appearance, PreferredConnectionParam |
prussell | 10:ee3a359f7d3f | 130 | //BLE_UUID_GATT UUID_GENERIC ATTRIBUTE //0x1801 //Included by Default, ServiceChanged, |
prussell | 10:ee3a359f7d3f | 131 | GattService::UUID_HEALTH_THERMOMETER_SERVICE, //0x1809 //HTM (Might need to be first for nRF App) |
prussell | 10:ee3a359f7d3f | 132 | GattService::UUID_HEART_RATE_SERVICE, //0x180D //HRM, BodyLocation, ControlPoint |
prussell | 10:ee3a359f7d3f | 133 | GattService::UUID_DEVICE_INFORMATION_SERVICE, //0x180A //sManufacturer, sModelNumber, sSerialNumber, sHWver, sFWver, sSWver |
prussell | 10:ee3a359f7d3f | 134 | GattService::UUID_BATTERY_SERVICE, //0x180F //BatteryLevel |
prussell | 10:ee3a359f7d3f | 135 | GattService::UUID_LINK_LOSS_SERVICE, //0x1803 //LinkLoss |
prussell | 10:ee3a359f7d3f | 136 | //x GattService::UUID_DFU, //0x1530 - See UARTServiceShortUUID in BLE_API:DFUService.cpp // |
prussell | 10:ee3a359f7d3f | 137 | //x GattService::UARTService, //0x0001~0x0003 - See DFUServiceShortUUID in BLE_API:UARTService.cpp // |
prussell | 10:ee3a359f7d3f | 138 | //Possibly useful: |
prussell | 10:ee3a359f7d3f | 139 | //x GattService::UUID_ALERT_NOTIFICATION_SERVICE, //0x1811 |
prussell | 10:ee3a359f7d3f | 140 | //x GattService::UUID_CURRENT_TIME_SERVICE, //0x1805 |
prussell | 10:ee3a359f7d3f | 141 | //x GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, //0x1812 |
prussell | 10:ee3a359f7d3f | 142 | //x GattService::UUID_IMMEDIATE_ALERT_SERVICE, //0x1802 |
prussell | 10:ee3a359f7d3f | 143 | //x GattService::UUID_PHONE_ALERT_STATUS_SERVICE, //0x180E |
prussell | 10:ee3a359f7d3f | 144 | //x GattService::UUID_REFERENCE_TIME_UPDATE_SERVICE, //0x1806 |
prussell | 10:ee3a359f7d3f | 145 | //x GattService::UUID_SCAN_PARAMETERS_SERVICE, //0x1813 |
prussell | 10:ee3a359f7d3f | 146 | };*/ |
prussell | 2:c77c2b06d604 | 147 | |
prussell | 10:ee3a359f7d3f | 148 | //========== bleIO - A Custom Service for Handling IO with an App (GPIO,PWM,etc.)========== |
prussell | 10:ee3a359f7d3f | 149 | //UUID128 List - Note only the single Service UUID128 can fit in the advertising |
prussell | 9:2d11beda333f | 150 | |
prussell | 9:2d11beda333f | 151 | // Custom UUID128 base for this service and its characteristics (from: http://www.itu.int/ITU-T/asn1/uuid.html) |
prussell | 9:2d11beda333f | 152 | // **This is a temporary UUID for testing by anyone, should be replaced before proceeding to real product.** |
prussell | 9:2d11beda333f | 153 | // Can check that UUID is correctly entered by viewing with Android App: nRF-Master Control panel |
prussell | 9:2d11beda333f | 154 | // UUID128 can be generated using: http://www.itu.int/ITU-T/asn1/uuid.html |
prussell | 9:2d11beda333f | 155 | // 20141218PR: [4d3281c0-86d1-11e4-b084-0002a5d5c51b] == OID[2.25.102612802202735078710424021169255859483] |
prussell | 10:ee3a359f7d3f | 156 | // Base UUID128 (1 digit modified):{0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x1*};// NormalOrder |
prussell | 10:ee3a359f7d3f | 157 | uint8_t puUUID128_IOService[16] = {0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x10};// Service UUID |
prussell | 10:ee3a359f7d3f | 158 | uint8_t puUUID128_IOAdvertise[16]= {0x10,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Advertising Service UUID (=FlippedOrder) |
prussell | 10:ee3a359f7d3f | 159 | // Characteristic UUID: Initially using generic blocks of IO data that may be manually dividied up into Buttons, LEDs, PWM, ADC, etc. |
prussell | 10:ee3a359f7d3f | 160 | uint8_t puUUID128_IOw8[16] = {0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x11};// Write[8byte] to mbed from phone |
prussell | 10:ee3a359f7d3f | 161 | uint8_t puUUID128_IOr4[16] = {0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x12};// Read[4byte] from mbed to phone |
prussell | 10:ee3a359f7d3f | 162 | uint8_t puUUID128_IOn4[16] = {0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x13};// Notify[4byte] from mbed to phone |
prussell | 10:ee3a359f7d3f | 163 | // Alternately can have a characterostic for each discrete item, such as: |
prussell | 10:ee3a359f7d3f | 164 | //a uint8_t puUUID128_bleConfig32[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Configuration 32bits |
prussell | 10:ee3a359f7d3f | 165 | //a uint8_t puUUID128_bleOut1[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Output 1bit |
prussell | 10:ee3a359f7d3f | 166 | //a uint8_t puUUID128_bleOutPWM100[16]= {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Output PWM (Range 0~100%) |
prussell | 10:ee3a359f7d3f | 167 | //a uint8_t puUUID128_bleOutPWM256[16]= {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Output PWM (256 levels, 0~255) |
prussell | 10:ee3a359f7d3f | 168 | //a uint8_t puUUID128_bleIn1[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Input 1bit |
prussell | 10:ee3a359f7d3f | 169 | //a uint8_t puUUID128_bleInADC12[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Input ADC 12bit |
prussell | 10:ee3a359f7d3f | 170 | |
prussell | 9:2d11beda333f | 171 | |
prussell | 10:ee3a359f7d3f | 172 | /* Android: |
prussell | 10:ee3a359f7d3f | 173 | //bleIO Service and Characteristics |
prussell | 10:ee3a359f7d3f | 174 | attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c51b", "bleIO base"); |
prussell | 10:ee3a359f7d3f | 175 | attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c510", "bleIO Service"); |
prussell | 10:ee3a359f7d3f | 176 | attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c511", "bleIO w8"); |
prussell | 10:ee3a359f7d3f | 177 | attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c512", "bleIO r4"); |
prussell | 10:ee3a359f7d3f | 178 | attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c513", "bleIO n4"); |
prussell | 9:2d11beda333f | 179 | |
prussell | 10:ee3a359f7d3f | 180 | attributes.put("1bc5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev base"); |
prussell | 10:ee3a359f7d3f | 181 | attributes.put("10c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev Service"); |
prussell | 10:ee3a359f7d3f | 182 | attributes.put("11c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev w8"); |
prussell | 10:ee3a359f7d3f | 183 | attributes.put("12c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev r4"); |
prussell | 10:ee3a359f7d3f | 184 | attributes.put("13c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev n4"); |
prussell | 10:ee3a359f7d3f | 185 | */ |
prussell | 10:ee3a359f7d3f | 186 | |
prussell | 10:ee3a359f7d3f | 187 | // Characteristic Value Storage (with Initial values, should match actual data type transferred, doesn't have to be byte array): |
prussell | 10:ee3a359f7d3f | 188 | uint8_t pIOw8[8] = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17}; //Value Storage for Characteristic Write8 |
prussell | 10:ee3a359f7d3f | 189 | uint8_t pIOr4[4] = {0x20,0x21,0x22,0x23}; //Value Storage for Characteristic Read4 |
prussell | 10:ee3a359f7d3f | 190 | uint8_t pIOn4[4] = {0x30,0x31,0x32,0x33}; //Value Storage for Characteristic Notify4 |
prussell | 9:2d11beda333f | 191 | |
prussell | 9:2d11beda333f | 192 | //x GattCharacteristic::GattAttribute *descriptors[]; //How Set Characteristic Descriptors? Maybe in the App is easier? |
prussell | 9:2d11beda333f | 193 | |
prussell | 10:ee3a359f7d3f | 194 | //IO Characteristics: //GattCharacteristics xx(*UUID, *Payload=*Value, LenInit, LenMax, Properties) |
prussell | 10:ee3a359f7d3f | 195 | //GattCharacteristic IOw8(puUUID128_IOw8, pIOw8, sizeof(pIOw8), sizeof(pIOw8), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); |
prussell | 10:ee3a359f7d3f | 196 | GattCharacteristic IOw8(puUUID128_IOw8, pIOw8, sizeof(pIOw8), sizeof(pIOw8), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE);// Allow Host to read back last setting |
prussell | 10:ee3a359f7d3f | 197 | GattCharacteristic IOr4(puUUID128_IOr4, pIOr4, sizeof(pIOr4), sizeof(pIOr4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); // Host can manually request |
prussell | 10:ee3a359f7d3f | 198 | //GattCharacteristic IOn4(puUUID128_IOn4, pIOn4, sizeof(pIOn4), sizeof(pIOn4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); |
prussell | 10:ee3a359f7d3f | 199 | GattCharacteristic IOn4(puUUID128_IOn4, pIOn4, sizeof(pIOn4), sizeof(pIOn4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); // Host is notified of changes |
prussell | 10:ee3a359f7d3f | 200 | GattCharacteristic *pCustomCharacteristics[] = {&IOw8, &IOr4, &IOn4};//Table of Pointers to Characteristics |
prussell | 10:ee3a359f7d3f | 201 | |
prussell | 10:ee3a359f7d3f | 202 | //IO Service: //GattService::GattService(*UUID, *GattCharacteristics, (numCharacteristics) ) : |
prussell | 10:ee3a359f7d3f | 203 | GattService ServiceIO(puUUID128_IOService, pCustomCharacteristics, (sizeof(pCustomCharacteristics)/sizeof(pCustomCharacteristics[0]))); |
prussell | 10:ee3a359f7d3f | 204 | GattService *pServiceIO= &ServiceIO; // Pointer to Service |
prussell | 9:2d11beda333f | 205 | |
prussell | 10:ee3a359f7d3f | 206 | //a Option: Allow device to modify its settings and inform host (Typically the device wouldn't change a host only setting) |
prussell | 10:ee3a359f7d3f | 207 | /*void vUpdate_IOw8(void) // Handle in device changes to w8 characteristic |
prussell | 9:2d11beda333f | 208 | { |
prussell | 10:ee3a359f7d3f | 209 | static uint8_t uw8; //Level: 0..2 |
prussell | 10:ee3a359f7d3f | 210 | switch(++uw8){ |
prussell | 10:ee3a359f7d3f | 211 | case 2: memcpy(pIOw8, "w2w2w2wc", sizeof(pIOw8)); DEBUG(" w8c"); break; |
prussell | 10:ee3a359f7d3f | 212 | case 1: memcpy(pIOw8, "w1w1w1wb", sizeof(pIOw8)); DEBUG(" w8b"); break; |
prussell | 10:ee3a359f7d3f | 213 | default: uw8=0; memcpy(pIOw8, "w0w0w0wa", sizeof(pIOw8)); DEBUG(" w8a"); break; |
prussell | 9:2d11beda333f | 214 | } |
prussell | 10:ee3a359f7d3f | 215 | //ble.updateCharacteristicValue(uint16_t handle, const uint8_t *value, uint16_t size, bool localOnly) |
prussell | 10:ee3a359f7d3f | 216 | ble.updateCharacteristicValue(IOw8.getValueHandle(), pIOw8, sizeof(pIOw8)); |
prussell | 9:2d11beda333f | 217 | //pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE |
prussell | 10:ee3a359f7d3f | 218 | }*/ |
prussell | 10:ee3a359f7d3f | 219 | |
prussell | 10:ee3a359f7d3f | 220 | void vUpdate_IOr4(void) // Handle in device changes to r4 characteristic |
prussell | 10:ee3a359f7d3f | 221 | { |
prussell | 10:ee3a359f7d3f | 222 | static uint8_t ur4; //Level: 0..2 |
prussell | 10:ee3a359f7d3f | 223 | switch(++ur4){ |
prussell | 10:ee3a359f7d3f | 224 | case 2: memcpy(pIOr4, "r2r2", sizeof(pIOr4)); DEBUG(" r4c"); break; |
prussell | 10:ee3a359f7d3f | 225 | case 1: memcpy(pIOr4, "r1r1", sizeof(pIOr4)); DEBUG(" r4b"); break; |
prussell | 10:ee3a359f7d3f | 226 | default: ur4=0; memcpy(pIOr4, "r0r0", sizeof(pIOr4)); DEBUG(" r4a"); break; |
prussell | 10:ee3a359f7d3f | 227 | } |
prussell | 10:ee3a359f7d3f | 228 | ble.updateCharacteristicValue(IOr4.getValueHandle(), pIOr4, sizeof(pIOr4)); |
prussell | 9:2d11beda333f | 229 | } |
prussell | 9:2d11beda333f | 230 | |
prussell | 10:ee3a359f7d3f | 231 | void vUpdate_IOn4(void) // Handle in device changes to n4 characteristic |
prussell | 9:2d11beda333f | 232 | { |
prussell | 10:ee3a359f7d3f | 233 | static uint8_t un4; //Level: 0..2 |
prussell | 10:ee3a359f7d3f | 234 | switch(++un4){ |
prussell | 10:ee3a359f7d3f | 235 | case 2: memcpy(pIOn4, "n2n2", sizeof(pIOr4)); DEBUG(" n4c"); break; |
prussell | 10:ee3a359f7d3f | 236 | case 1: memcpy(pIOn4, "n1n1", sizeof(pIOr4)); DEBUG(" n4b"); break; |
prussell | 10:ee3a359f7d3f | 237 | default: un4=0; memcpy(pIOn4, "n0n0", sizeof(pIOr4)); DEBUG(" n4a"); break; |
prussell | 9:2d11beda333f | 238 | } |
prussell | 10:ee3a359f7d3f | 239 | ble.updateCharacteristicValue(IOn4.getValueHandle(), pIOn4, sizeof(pIOn4)); |
prussell | 9:2d11beda333f | 240 | } |
prussell | 9:2d11beda333f | 241 | |
prussell | 0:0217a862b047 | 242 | //==========Functions:BLE========== |
prussell | 0:0217a862b047 | 243 | void Callback_BLE_onTimeout(void) |
prussell | 10:ee3a359f7d3f | 244 | { //PR: Haven't seen this, even when phone moved out of range and events occur like OnDisconnect(Reason0x08) or LinkLoss |
prussell | 10:ee3a359f7d3f | 245 | DEBUG("\nBLEi: Callback_BLE_onTimeout()\n" ); |
prussell | 0:0217a862b047 | 246 | |
prussell | 0:0217a862b047 | 247 | //DEBUG("\nBLE:Callback_BLE_onTimeout(), Restarting Advertising\n" ); |
prussell | 0:0217a862b047 | 248 | //ble.startAdvertising(); |
prussell | 0:0217a862b047 | 249 | } |
prussell | 0:0217a862b047 | 250 | |
prussell | 0:0217a862b047 | 251 | void Callback_BLE_onDisconnect(Gap::Handle_t tHandle, Gap::DisconnectionReason_t eReason) |
prussell | 10:ee3a359f7d3f | 252 | { //PR: onDisconnect(Reason:8) occured after ~20Tx with no onDataSent() after phone moved out of range |
prussell | 0:0217a862b047 | 253 | |
prussell | 0:0217a862b047 | 254 | // REMOTE_USER_TERMINATED_CONNECTION = 0x13 = 19, |
prussell | 0:0217a862b047 | 255 | // LOCAL_HOST_TERMINATED_CONNECTION = 0x16 = 22, |
prussell | 0:0217a862b047 | 256 | // CONN_INTERVAL_UNACCEPTABLE = 0x3B = 59, |
prussell | 0:0217a862b047 | 257 | DEBUG("\nBLEi: Callback_BLE_Disconnect(Handle:%d, eReason:0x%02x), Restarting Advertising\n",tHandle,eReason );//PR: Occurs properly when click disconnect in App nRFToolbox:HRM |
prussell | 0:0217a862b047 | 258 | |
prussell | 0:0217a862b047 | 259 | //DEBUG("Wait10sec...\n");wait(10.0); //PR: Optional to test effect on advertising |
prussell | 0:0217a862b047 | 260 | ble.startAdvertising(); // restart advertising |
prussell | 0:0217a862b047 | 261 | } |
prussell | 0:0217a862b047 | 262 | |
prussell | 0:0217a862b047 | 263 | void Callback_BLE_onConnect(Gap::Handle_t tHandle, Gap::addr_type_t ePeerAddrType, const Gap::address_t c6PeerAddr, const Gap::ConnectionParams_t *params) |
prussell | 0:0217a862b047 | 264 | { |
prussell | 0:0217a862b047 | 265 | DEBUG("\nBLEi: Callback_BLE_Connect(Handle:%d, eType:%d, Add:%u ...)\n", tHandle, ePeerAddrType, c6PeerAddr); |
prussell | 0:0217a862b047 | 266 | |
prussell | 10:ee3a359f7d3f | 267 | #if UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL //PR: Adopted optional code never tested |
prussell | 0:0217a862b047 | 268 | /* Updating connection parameters can be attempted only after a connection has been |
prussell | 0:0217a862b047 | 269 | * established. Please note that the ble-Central is still the final arbiter for |
prussell | 0:0217a862b047 | 270 | * the effective parameters; the peripheral can only hope that the request is |
prussell | 0:0217a862b047 | 271 | * honored. Please also be mindful of the constraints that might be enforced by |
prussell | 0:0217a862b047 | 272 | * the BLE stack on the underlying controller.*/ |
prussell | 0:0217a862b047 | 273 | #define MIN_CONN_INTERVAL 250 /**< Minimum connection interval (250 ms) */ |
prussell | 0:0217a862b047 | 274 | #define MAX_CONN_INTERVAL 350 /**< Maximum connection interval (350 ms). */ |
prussell | 0:0217a862b047 | 275 | #define CONN_SUP_TIMEOUT 6000 /**< Connection supervisory timeout (6 seconds). */ |
prussell | 0:0217a862b047 | 276 | #define SLAVE_LATENCY 4 |
prussell | 0:0217a862b047 | 277 | |
prussell | 0:0217a862b047 | 278 | Gap::ConnectionParams_t tGap_conn_params; |
prussell | 0:0217a862b047 | 279 | tGap_conn_params.minConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(MIN_CONN_INTERVAL); |
prussell | 0:0217a862b047 | 280 | tGap_conn_params.maxConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(MAX_CONN_INTERVAL); |
prussell | 0:0217a862b047 | 281 | tGap_conn_params.connectionSupervisionTimeout = Gap::MSEC_TO_GAP_DURATION_UNITS(CONN_SUP_TIMEOUT); |
prussell | 0:0217a862b047 | 282 | tGap_conn_params.slaveLatency = SLAVE_LATENCY; |
prussell | 0:0217a862b047 | 283 | ble.updateConnectionParams(tHandle, &tGap_conn_params); |
prussell | 10:ee3a359f7d3f | 284 | #endif // #if UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL |
prussell | 0:0217a862b047 | 285 | } |
prussell | 0:0217a862b047 | 286 | |
prussell | 0:0217a862b047 | 287 | static volatile bool bSent = false; //Volatile, don't optimize, changes under interrupt control |
prussell | 0:0217a862b047 | 288 | static volatile unsigned uSentBLE; |
prussell | 0:0217a862b047 | 289 | void Callback_BLE_onDataSent(unsigned uSent){ |
prussell | 0:0217a862b047 | 290 | uSentBLE=uSent; |
prussell | 10:ee3a359f7d3f | 291 | DEBUG("BLEi: SentI(%u)", uSentBLE); //TODO: PR: Why uSent always "1", expected it to match sent bytes length |
prussell | 0:0217a862b047 | 292 | bSent = true; |
prussell | 10:ee3a359f7d3f | 293 | //FYI: App nRF-MCP doesn't cause onDataSent(), while App nRF-ToolBox:HRM does cause onDataSent() |
prussell | 10:ee3a359f7d3f | 294 | //TBD: onDataSent() maybe only occuring when confirmed receive by phone, as onDataSent() didn't happen when phone moved out of range. |
prussell | 0:0217a862b047 | 295 | } |
prussell | 0:0217a862b047 | 296 | |
prussell | 0:0217a862b047 | 297 | void Callback_BLE_onDataWritten(const GattCharacteristicWriteCBParams *pParams) |
prussell | 9:2d11beda333f | 298 | { // This occurs when use nRF-MCP to save a new Characteristic Value (SingleUpASrrowIcon) such as New Heart Rate Control Point (unsent if incorrect length) |
prussell | 9:2d11beda333f | 299 | GattAttribute::Handle_t tHandle=pParams->charHandle; |
prussell | 0:0217a862b047 | 300 | |
prussell | 0:0217a862b047 | 301 | //Warning: *data may not be NULL terminated |
prussell | 9:2d11beda333f | 302 | 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); |
prussell | 9:2d11beda333f | 303 | |
prussell | 10:ee3a359f7d3f | 304 | /* //TODO: Add check of op type with tHandle check: switch(pParams->op){ case: GATTS_CHAR_OP_WRITE_REQ:}... |
prussell | 10:ee3a359f7d3f | 305 | switch(pParams->op){ |
prussell | 10:ee3a359f7d3f | 306 | case GATTS_CHAR_OP_INVALID: |
prussell | 10:ee3a359f7d3f | 307 | case GATTS_CHAR_OP_WRITE_REQ |
prussell | 10:ee3a359f7d3f | 308 | case GATTS_CHAR_OP_WRITE_CMD |
prussell | 10:ee3a359f7d3f | 309 | case GATTS_CHAR_OP_SIGN_WRITE_CMD //< Signed Write Command |
prussell | 10:ee3a359f7d3f | 310 | case GATTS_CHAR_OP_PREP_WRITE_REQ //< Prepare Write Request |
prussell | 10:ee3a359f7d3f | 311 | case GATTS_CHAR_OP_EXEC_WRITE_REQ_CANCEL //< Execute Write Request: Cancel all prepared writes |
prussell | 10:ee3a359f7d3f | 312 | case GATTS_CHAR_OP_EXEC_WRITE_REQ_NOW //< Execute Write Request: Immediately execute all prepared writes |
prussell | 10:ee3a359f7d3f | 313 | default: Error? |
prussell | 10:ee3a359f7d3f | 314 | }*/ |
prussell | 9:2d11beda333f | 315 | |
prussell | 10:ee3a359f7d3f | 316 | //a These are equivalent ways of accessing characteristic handles: |
prussell | 10:ee3a359f7d3f | 317 | //a if (pParams->charHandle == IOw8.getValueHandle()) { DEBUG("\n\nOK tHandle %d\n\n", pParams->charHandle); } |
prussell | 10:ee3a359f7d3f | 318 | //a if (pParams->charHandle == ServiceIO.getCharacteristic(0)->getValueHandle()) { DEBUG("\n\nOK tHandle %d\n\n", pParams->charHandle); } |
prussell | 10:ee3a359f7d3f | 319 | //a if (IOw8.getValueHandle() == ServiceIO.getCharacteristic(0)->getValueHandle()) { DEBUG("\n\nOK tHandle equivalent %d\n\n", pParams->charHandle); } |
prussell | 9:2d11beda333f | 320 | |
prussell | 9:2d11beda333f | 321 | //Handle Changes to Service Characteristics: |
prussell | 10:ee3a359f7d3f | 322 | if(!ble.getGapState().connected){ //Ensure BLE still connected |
prussell | 10:ee3a359f7d3f | 323 | DEBUG("BLEi: Callback_BLE_onDataWritten() while disconnected!!"); |
prussell | 9:2d11beda333f | 324 | } else { |
prussell | 10:ee3a359f7d3f | 325 | uint16_t uLen; |
prussell | 10:ee3a359f7d3f | 326 | if (tHandle == IOw8.getValueHandle()) { // This occurs from Write by App nRF-MCP since has Write Property set |
prussell | 10:ee3a359f7d3f | 327 | ble.readCharacteristicValue(tHandle, pIOw8, &uLen); |
prussell | 10:ee3a359f7d3f | 328 | DEBUG(" IOw8[%d]:%02X %02X %02X %02X %02X %02X %02X %02X\n", uLen, pIOw8[0], pIOw8[1], pIOw8[2], pIOw8[3],pIOw8[4], pIOw8[5], pIOw8[6], pIOw8[7]); |
prussell | 10:ee3a359f7d3f | 329 | //TODO: Update Outputs |
prussell | 10:ee3a359f7d3f | 330 | //} else if (tHandle == IOr4.getValueHandle()) { |
prussell | 10:ee3a359f7d3f | 331 | // ble.readCharacteristicValue(tHandle, pIOr4, &uLen); |
prussell | 10:ee3a359f7d3f | 332 | // DEBUG(" IOr4[%d]:%02X %02X %02X %02X\n", uLen, pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]); |
prussell | 10:ee3a359f7d3f | 333 | // //TODO: Update Outputs |
prussell | 10:ee3a359f7d3f | 334 | //} else if (tHandle == IOn4.getValueHandle()) { |
prussell | 10:ee3a359f7d3f | 335 | // ble.readCharacteristicValue(tHandle, pIOn4, &uLen); |
prussell | 10:ee3a359f7d3f | 336 | // DEBUG(" IOn4[%d]:%02X %02X %02X %02X\n", uLen, pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]); |
prussell | 10:ee3a359f7d3f | 337 | // //TODO: Update Outputs |
prussell | 10:ee3a359f7d3f | 338 | } else { |
prussell | 10:ee3a359f7d3f | 339 | DEBUG("\n Unknown onWrite(tHandle:%d)\n\n", tHandle); |
prussell | 10:ee3a359f7d3f | 340 | } |
prussell | 9:2d11beda333f | 341 | } |
prussell | 8:f187ba55aed2 | 342 | |
prussell | 10:ee3a359f7d3f | 343 | //LinkLoss: |
prussell | 10:ee3a359f7d3f | 344 | //x pServiceLinkLoss->setAlertLevel( AlertLevel_t newLevel ) |
prussell | 10:ee3a359f7d3f | 345 | //x Triggered by BluetoothLEGatt sample changing Linkloss setting: |
prussell | 10:ee3a359f7d3f | 346 | //x Alert=1: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x01]=Data[]) |
prussell | 10:ee3a359f7d3f | 347 | //x Alert=2: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x02]=Data[]) |
prussell | 8:f187ba55aed2 | 348 | |
prussell | 10:ee3a359f7d3f | 349 | //Characteristic Status: |
prussell | 10:ee3a359f7d3f | 350 | DEBUG(" IOw8: Handle:%d Len:%d Initial:[%02X %02X %02X %02X %02X %02X %02X %02X]\n", IOw8.getValueHandle(), sizeof(pIOw8), pIOw8[0], pIOw8[1], pIOw8[2], pIOw8[3],pIOw8[4], pIOw8[5], pIOw8[6], pIOw8[7]); |
prussell | 10:ee3a359f7d3f | 351 | DEBUG(" IOr4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOr4.getValueHandle(), sizeof(pIOr4), pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]); |
prussell | 10:ee3a359f7d3f | 352 | DEBUG(" IOn4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOn4.getValueHandle(), sizeof(pIOn4), pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]); |
prussell | 0:0217a862b047 | 353 | } |
prussell | 0:0217a862b047 | 354 | |
prussell | 0:0217a862b047 | 355 | void Callback_BLE_onUpdatesEnabled(Gap::Handle_t tHandle) |
prussell | 10:ee3a359f7d3f | 356 | { //Triggered when click the UpdateContinuousIcon in nRF-MCP(ThreeDownArrows) |
prussell | 10:ee3a359f7d3f | 357 | if (tHandle == IOn4.getValueHandle()) { DEBUG(" onUpdates(Handle:%d) Enabled - IOn4\n", tHandle); //This Occurs when selected by App nRF-MCP as has Notify property set |
prussell | 10:ee3a359f7d3f | 358 | //} else if (tHandle == IOr4.getValueHandle()) { DEBUG(" onUpdates(Handle:%d) Enabled - IOr4\n", tHandle); |
prussell | 10:ee3a359f7d3f | 359 | //} else if (tHandle == IOw8.getValueHandle()) { DEBUG(" onUpdates(Handle:%d) Enabled - IOw8\n", tHandle); |
prussell | 10:ee3a359f7d3f | 360 | } else { DEBUG(" onUpdates(Handle:%d) Enabled - Characteristic?\n", tHandle); |
prussell | 9:2d11beda333f | 361 | } |
prussell | 0:0217a862b047 | 362 | } |
prussell | 0:0217a862b047 | 363 | |
prussell | 5:d36bbb315e31 | 364 | // Adopted 20141213 from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LinkLoss/file/440ee5e8595f/main.cpp |
prussell | 5:d36bbb315e31 | 365 | void Callback_BLE_onLinkLoss(LinkLossService::AlertLevel_t level) |
prussell | 5:d36bbb315e31 | 366 | { |
prussell | 10:ee3a359f7d3f | 367 | printf("\nBLEi: Link Loss Alert, Level:%d\n", level); |
prussell | 10:ee3a359f7d3f | 368 | //TODO: Handle Link Loss, maybe lower power mode and/or advertising mode |
prussell | 5:d36bbb315e31 | 369 | } |
prussell | 10:ee3a359f7d3f | 370 | //==========BLE:HRM========== |
prussell | 8:f187ba55aed2 | 371 | //Adopted 2014Dec from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/ |
prussell | 8:f187ba55aed2 | 372 | uint8_t update_hrm(void)//(bool bInit) |
prussell | 8:f187ba55aed2 | 373 | { |
prussell | 8:f187ba55aed2 | 374 | static uint8_t u8_hrm = 100; |
prussell | 10:ee3a359f7d3f | 375 | if (++u8_hrm >= 175) { |
prussell | 8:f187ba55aed2 | 376 | u8_hrm = 100; |
prussell | 10:ee3a359f7d3f | 377 | DEBUG(" HRM>100"); |
prussell | 10:ee3a359f7d3f | 378 | } else { |
prussell | 10:ee3a359f7d3f | 379 | DEBUG(" HRM:%d", u8_hrm); |
prussell | 10:ee3a359f7d3f | 380 | } |
prussell | 8:f187ba55aed2 | 381 | pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE |
prussell | 8:f187ba55aed2 | 382 | return(u8_hrm); |
prussell | 8:f187ba55aed2 | 383 | } |
prussell | 10:ee3a359f7d3f | 384 | //==========BLE:HTM(Using nRF Internal Temperature)========== |
prussell | 10:ee3a359f7d3f | 385 | // *If not using nRF51822 IC then change this to a simple counter like BLE:Battery section |
prussell | 10:ee3a359f7d3f | 386 | // Adopted 2014Dec from: https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/ |
prussell | 8:f187ba55aed2 | 387 | // Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.health_thermometer.xml |
prussell | 8:f187ba55aed2 | 388 | // HTM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml |
prussell | 9:2d11beda333f | 389 | //****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 |
prussell | 8:f187ba55aed2 | 390 | float update_htm(void) |
prussell | 8:f187ba55aed2 | 391 | { |
prussell | 8:f187ba55aed2 | 392 | //static float fTemperature = 0;//-123.456; |
prussell | 8:f187ba55aed2 | 393 | int32_t i32_temp; |
prussell | 8:f187ba55aed2 | 394 | sd_temp_get(&i32_temp); //Read the nRF Internal Temperature (Die in 0.25'C steps, Counting From TBD), TODO:Check Scaling |
prussell | 8:f187ba55aed2 | 395 | float fTemperature = (float(i32_temp)/4.0) - 16.0; // Scale&Shift (0.25'C from -16'C?) |
prussell | 8:f187ba55aed2 | 396 | |
prussell | 8:f187ba55aed2 | 397 | //{//Force to IEEE format to match needs of Apps like nRF-HTM and nRF-Toolbox:HTM |
prussell | 8:f187ba55aed2 | 398 | // PR: Didn't work 20141213, might need 5byte style from BLE_HTM_by_InTempSensr if this is really necessary. OK in nRF-MCP for now. |
prussell | 8:f187ba55aed2 | 399 | // uint8_t exponent = 0xFE; //exponent is -2 |
prussell | 8:f187ba55aed2 | 400 | // uint32_t mantissa = (uint32_t)(fTemperature*100); |
prussell | 8:f187ba55aed2 | 401 | // uint32_t temp_ieee11073 = ((((uint32_t)exponent) << 24) | (mantissa)); //Note: Assumes Mantissa within 24bits |
prussell | 8:f187ba55aed2 | 402 | // memcpy(((uint8_t*)&fTemperature)+1, (uint8_t*)&temp_ieee11073, 4); //Overwrite with IEEE format float |
prussell | 8:f187ba55aed2 | 403 | //} |
prussell | 8:f187ba55aed2 | 404 | |
prussell | 8:f187ba55aed2 | 405 | pServiceHTM->updateTemperature( fTemperature );// Update Characteristic so sent by BLE |
prussell | 10:ee3a359f7d3f | 406 | DEBUG(" HTM%03d==%2.2f", i32_temp, fTemperature); |
prussell | 8:f187ba55aed2 | 407 | return(fTemperature); |
prussell | 8:f187ba55aed2 | 408 | } |
prussell | 10:ee3a359f7d3f | 409 | //==========BLE:Battery========== |
prussell | 8:f187ba55aed2 | 410 | uint8_t update_batt(void) |
prussell | 8:f187ba55aed2 | 411 | { |
prussell | 8:f187ba55aed2 | 412 | static uint8_t u8_BattPercent=33; //Level: 0..100% |
prussell | 8:f187ba55aed2 | 413 | u8_BattPercent <= 50 ? u8_BattPercent=100 : u8_BattPercent--; // Simulate Battery Decay |
prussell | 8:f187ba55aed2 | 414 | pServiceBattery->updateBatteryLevel( u8_BattPercent ); // Update Characteristic so sent by BLE |
prussell | 10:ee3a359f7d3f | 415 | DEBUG(" Batt%03d%%", u8_BattPercent); |
prussell | 8:f187ba55aed2 | 416 | return(u8_BattPercent); |
prussell | 8:f187ba55aed2 | 417 | } |
prussell | 9:2d11beda333f | 418 | |
prussell | 10:ee3a359f7d3f | 419 | //==========Functions:Timer========== |
prussell | 10:ee3a359f7d3f | 420 | static volatile bool b_Ticker1 = false;//Volatile, don't optimize, changes under interrupt control |
prussell | 10:ee3a359f7d3f | 421 | void CallbackTicker1(void) |
prussell | 10:ee3a359f7d3f | 422 | { |
prussell | 10:ee3a359f7d3f | 423 | static uint32_t u32_Counter; // Counter for Debug Output |
prussell | 10:ee3a359f7d3f | 424 | f_led1level+=0.1; if (f_led1level>0.5){f_led1level = 0.1;}; pwm_led1=f_led1level;//PR: Ramp Blink |
prussell | 10:ee3a359f7d3f | 425 | DEBUG("\nBLEi: Ticker1(%u) ", ++u32_Counter); |
prussell | 10:ee3a359f7d3f | 426 | b_Ticker1 = true; //PR: Flag to handle Ticker1 Event in Main loop so interupts not blocked. |
prussell | 10:ee3a359f7d3f | 427 | } |
prussell | 0:0217a862b047 | 428 | //==========main========== |
prussell | 0:0217a862b047 | 429 | int main(void) |
prussell | 0:0217a862b047 | 430 | { |
prussell | 0:0217a862b047 | 431 | f_led1level = 1; pwm_led1 = f_led1level;//Start LED1=OnMax |
prussell | 0:0217a862b047 | 432 | f_led2level = 1; pwm_led2 = f_led2level;//Start LED2=OnMax |
prussell | 6:5b6fb35b4450 | 433 | |
prussell | 6:5b6fb35b4450 | 434 | //Restart TeraTerm just before Pressing Reset on mbed, 9600-8N1(No Flow Control) |
prussell | 6:5b6fb35b4450 | 435 | DEBUG("\nBLE: ___%s___\n", pcDeviceName); |
prussell | 8:f187ba55aed2 | 436 | DEBUG("BLE: Connect App for Data: nRF-MCP, nRF-Toolbox:HRM/HTM, Android Sample BluetoothLeGatt, etc.\n"); |
prussell | 8:f187ba55aed2 | 437 | DEBUG("BLE: BluetoothLeGatt: App->mbed: LinkLoss->AlertLevel(UpArrow)\n"); |
prussell | 8:f187ba55aed2 | 438 | DEBUG("BLE: BluetoothLeGatt: App->mbed: BatteryService->BatteryLevel(DownArrow)\n"); |
prussell | 8:f187ba55aed2 | 439 | DEBUG("BLE: BluetoothLeGatt: mbed->App: BatteryService->BatteryLevel->EnableNotify(ThreeDownArrows), Also App Acks the send=DEBUG('SentI')\n"); |
prussell | 0:0217a862b047 | 440 | |
prussell | 0:0217a862b047 | 441 | Ticker ticker1; //PR: Timer Object(Structure) |
prussell | 0:0217a862b047 | 442 | ticker1.attach(CallbackTicker1, 2.0); //PR: Timer Handler, Float=PeriodSeconds |
prussell | 0:0217a862b047 | 443 | |
prussell | 3:a98203f84063 | 444 | //BLE1: Setup BLE Service (and event actions) //TODO: Check for services declared before main() - Is that OK? |
prussell | 3:a98203f84063 | 445 | DEBUG("BLE: Setup BLE\n"); |
prussell | 0:0217a862b047 | 446 | ble.init(); |
prussell | 0:0217a862b047 | 447 | ble.onDisconnection(Callback_BLE_onDisconnect); |
prussell | 0:0217a862b047 | 448 | ble.onConnection(Callback_BLE_onConnect); //PR: Not required if no actions enabled, enabled now just for debug printf() |
prussell | 0:0217a862b047 | 449 | ble.onDataSent(Callback_BLE_onDataSent); |
prussell | 0:0217a862b047 | 450 | ble.onDataWritten(Callback_BLE_onDataWritten); |
prussell | 0:0217a862b047 | 451 | ble.onTimeout(Callback_BLE_onTimeout); |
prussell | 0:0217a862b047 | 452 | ble.onUpdatesEnabled(Callback_BLE_onUpdatesEnabled); |
prussell | 8:f187ba55aed2 | 453 | |
prussell | 6:5b6fb35b4450 | 454 | //BLE2: Setup Services (with their initial values and options) |
prussell | 3:a98203f84063 | 455 | DEBUG("BLE: Setup Services\n"); |
prussell | 4:976394791d7a | 456 | // *Order here affects order in nRF-MCP Discovery of Services |
prussell | 8:f187ba55aed2 | 457 | DeviceInformationService ServiceDeviceInfo(ble, "Maker", pcDeviceName, "sn1234", "hw00", "fw00", "sw00");//(BLEDevice), pcManufacturer, pcModelNumber, pcSerialNumber, pcHWver, pcFWver, pcSWver |
prussell | 9:2d11beda333f | 458 | pServiceDeviceInfo = &ServiceDeviceInfo; //DEBUG(" Handle Service DeviceInfo:%d\n", ServiceDeviceInfo.XXXgetHandle()); |
prussell | 8:f187ba55aed2 | 459 | LinkLossService ServiceLinkLoss(ble, Callback_BLE_onLinkLoss, LinkLossService::HIGH_ALERT); //New20141213, TBD |
prussell | 8:f187ba55aed2 | 460 | pServiceLinkLoss = &ServiceLinkLoss; |
prussell | 8:f187ba55aed2 | 461 | BatteryService ServiceBattery(ble, 10); |
prussell | 8:f187ba55aed2 | 462 | pServiceBattery = &ServiceBattery; |
prussell | 8:f187ba55aed2 | 463 | HeartRateService ServiceHRM(ble, (uint8_t)111, HeartRateService::LOCATION_FINGER); |
prussell | 8:f187ba55aed2 | 464 | pServiceHRM = &ServiceHRM; |
prussell | 8:f187ba55aed2 | 465 | HealthThermometerService ServiceHTM(ble, 33.3, HealthThermometerService::LOCATION_EAR); |
prussell | 8:f187ba55aed2 | 466 | pServiceHTM = &ServiceHTM; |
prussell | 8:f187ba55aed2 | 467 | //UARTService ServiceUART(ble); |
prussell | 8:f187ba55aed2 | 468 | // pServiceUART = &ServiceUART; |
prussell | 5:d36bbb315e31 | 469 | |
prussell | 9:2d11beda333f | 470 | //Start CustomIO Service: |
prussell | 10:ee3a359f7d3f | 471 | ble.addService(ServiceIO); //Pointer: pServiceIO |
prussell | 10:ee3a359f7d3f | 472 | DEBUG(" IOw8: Handle:%d Len:%d Initial:[%02X %02X %02X %02X %02X %02X %02X %02X]\n", IOw8.getValueHandle(), sizeof(pIOw8), pIOw8[0], pIOw8[1], pIOw8[2], pIOw8[3],pIOw8[4], pIOw8[5], pIOw8[6], pIOw8[7]); |
prussell | 10:ee3a359f7d3f | 473 | DEBUG(" IOr4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOr4.getValueHandle(), sizeof(pIOr4), pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]); |
prussell | 10:ee3a359f7d3f | 474 | DEBUG(" IOn4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOn4.getValueHandle(), sizeof(pIOn4), pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]); |
prussell | 10:ee3a359f7d3f | 475 | //TODO: Ensure outputs in correct Initial state |
prussell | 9:2d11beda333f | 476 | |
prussell | 3:a98203f84063 | 477 | //BLE3: Setup advertising |
prussell | 10:ee3a359f7d3f | 478 | // 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. |
prussell | 10:ee3a359f7d3f | 479 | // If there isn't enough space, then the accumulate payload won't add that block to the advertising, it won't add a partial block, and if name last then device shows as unnamed in App. |
prussell | 9:2d11beda333f | 480 | DEBUG("BLE: Setup Advertising - Apps like HRM/HTM/UART require matching UUID\n"); |
prussell | 6:5b6fb35b4450 | 481 | ble.clearAdvertisingPayload(); //Prep |
prussell | 6:5b6fb35b4450 | 482 | ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(1000)); //PR: Advertise 1sec (1Hz) |
prussell | 6:5b6fb35b4450 | 483 | ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); //PR: TODO: To Study |
prussell | 6:5b6fb35b4450 | 484 | ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); //PR: BLE Only (Option:LE_LIMITED_DISCOVERABLE) |
prussell | 6:5b6fb35b4450 | 485 | // Does "LE_GENERAL_DISCOVERABLE" affect whether UUID needs to be advertised to discover services? |
prussell | 10:ee3a359f7d3f | 486 | //a ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // Multiple UUID16 for Standard Services |
prussell | 10:ee3a359f7d3f | 487 | ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)puUUID128_IOService, sizeof(puUUID128_IOService)); //Single UUID128 for primary Service |
prussell | 9:2d11beda333f | 488 | |
prussell | 10:ee3a359f7d3f | 489 | //? PR: I'm not sure what these lines do, they were inherited from an example: |
prussell | 10:ee3a359f7d3f | 490 | //? ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR); |
prussell | 10:ee3a359f7d3f | 491 | //? ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER); |
prussell | 9:2d11beda333f | 492 | |
prussell | 6:5b6fb35b4450 | 493 | // Add LocalName last so if Advertising too long will easily see as Name won't be available for the device. |
prussell | 6:5b6fb35b4450 | 494 | ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)pcDeviceName, sizeof(pcDeviceName)-1);//PR: LocalName (No NULL) |
prussell | 6:5b6fb35b4450 | 495 | ble.startAdvertising(); |
prussell | 3:a98203f84063 | 496 | |
prussell | 10:ee3a359f7d3f | 497 | //Example Advertising (Max 31 bytes): 0x01020304050607080910111213141516171819202122232425262728293031 |
prussell | 6:5b6fb35b4450 | 498 | // Example Raw Advertising caught by App nRF-MCP: 0x020106070309180A180D180909626C655052763034 |
prussell | 6:5b6fb35b4450 | 499 | // = Len02 Type01 Value06 (Flags: BREDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE) |
prussell | 6:5b6fb35b4450 | 500 | // = Len07 Type03 Values: 0918 0A18 0D18 (ListUUID16: 1809 180A 180D ) |
prussell | 6:5b6fb35b4450 | 501 | // = Len09 Type09 Values: 62 6C 65 50 52 76 30 34 (LocalName = "blePRv04") |
prussell | 6:5b6fb35b4450 | 502 | // Example Raw Advertising caught by App nRF-MCP: 0x0201061106FB349B5F80000080001000000F180000070209180A180D18 |
prussell | 6:5b6fb35b4450 | 503 | // = Len02 Type01 Value06 (Flags: BREDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE) |
prussell | 6:5b6fb35b4450 | 504 | // = Len11 Type06 ValueFB349B5F8000008000100000_0F18_0000 (UUID128: BluetoothBattery=0x180F) |
prussell | 6:5b6fb35b4450 | 505 | // = Len07 Type02 Value 0918 0A18 0D18 (UUID16: 1809 180A 180D ) |
prussell | 6:5b6fb35b4450 | 506 | // = LocalName field wasn't appended as insufficient space, so Device won't be named when scanning. |
prussell | 0:0217a862b047 | 507 | |
prussell | 0:0217a862b047 | 508 | DEBUG("BLE: Main Loop\n"); |
prussell | 0:0217a862b047 | 509 | uint32_t u32_wakeevents=0, u32_wakelast=0; // Counter&Register for tracking Wake Events (to see monitor their Frequency) |
prussell | 0:0217a862b047 | 510 | while (true) { |
prussell | 0:0217a862b047 | 511 | if (b_Ticker1 && ble.getGapState().connected) { //If Ticker1 and Connected Update Data |
prussell | 0:0217a862b047 | 512 | b_Ticker1 = false; // Clear flag for next Ticker1, see CallbackTicker1() |
prussell | 3:a98203f84063 | 513 | |
prussell | 3:a98203f84063 | 514 | // Read Sensors, and update matching Service Characteristics (only if connected) |
prussell | 4:976394791d7a | 515 | // *Order here doesn't affect order in nRF-MCP Discovery of Services |
prussell | 6:5b6fb35b4450 | 516 | //TBD: Maybe save power by not Tx unless enabled by App? |
prussell | 8:f187ba55aed2 | 517 | // The Services are discovered if they were setup/started (They don't need update to be discovered) |
prussell | 8:f187ba55aed2 | 518 | update_htm(); |
prussell | 8:f187ba55aed2 | 519 | update_hrm(); |
prussell | 8:f187ba55aed2 | 520 | update_batt(); |
prussell | 8:f187ba55aed2 | 521 | |
prussell | 10:ee3a359f7d3f | 522 | //a vUpdate_IOw8(); |
prussell | 10:ee3a359f7d3f | 523 | vUpdate_IOr4(); |
prussell | 10:ee3a359f7d3f | 524 | vUpdate_IOn4(); |
prussell | 9:2d11beda333f | 525 | |
prussell | 10:ee3a359f7d3f | 526 | DEBUG(" BLE:Wakes:%u,Delta:%u ", u32_wakeevents, u32_wakeevents-u32_wakelast); //For Evaluating Timing |
prussell | 3:a98203f84063 | 527 | u32_wakelast = u32_wakeevents; |
prussell | 0:0217a862b047 | 528 | } else if (b_Ticker1) { |
prussell | 0:0217a862b047 | 529 | b_Ticker1 = false; // Clear flag for next Ticker1, see CallbackTicker1() |
prussell | 3:a98203f84063 | 530 | DEBUG("BLE: Tick while unconnected "); |
prussell | 0:0217a862b047 | 531 | } else if (bSent){ |
prussell | 0:0217a862b047 | 532 | bSent=false; //clear flag |
prussell | 3:a98203f84063 | 533 | //DEBUG("BLE: Sent %ubytes ", uSentBLE); |
prussell | 0:0217a862b047 | 534 | } else { |
prussell | 3:a98203f84063 | 535 | //DEBUG("BLE: Wait for Event\n\r"); //x Debug output here causes unnecessary wakes resulting in endless awakes. |
prussell | 0:0217a862b047 | 536 | ble.waitForEvent(); //PR: Wait for event - Yield control to BLE Stack and other events (Process ALL pending events before waiting again) |
prussell | 0:0217a862b047 | 537 | f_led2level+=0.25; if (f_led2level>0.5){f_led2level = 0.0;}; pwm_led2=f_led2level;//PR: Ramp Blink |
prussell | 0:0217a862b047 | 538 | u32_wakeevents++; //PR: Count events for frequency monitoring (20141207PR: nRF51822 mbed HRM = 50Hz) |
prussell | 0:0217a862b047 | 539 | } |
prussell | 0:0217a862b047 | 540 | } |
prussell | 0:0217a862b047 | 541 | } |
prussell | 10:ee3a359f7d3f | 542 | //========== end of main.cpp ========== |