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
main.cpp
00001 //=========Header (PR) 00002 // blePRv04, Initial: 20141210 Paul Russell (mbed user: prussell = PR) 00003 // This sample includes code from several projects found on http://developer.mbed.org, including but not limited to: 00004 // - http://developer.mbed.org/users/jksoft/code/BLE_RCBController/ (Setting up a custom Service/Characteristic) 00005 // - http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/ 00006 // - https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LoopbackUART/ 00007 // - https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/ 00008 // - https://developer.mbed.org/users/garimagupta002/notebook/ble-uart-lcd-demo/ (Advertise UUID16+UUID128) 00009 // - http://developer.mbed.org/teams/UCL-IoT/code/BLE_LED_Controller/ (With matching Android App, uses BLE-UART) 00010 // - https://developer.mbed.org/users/todotani/code/BLE_Health_Thermometer2-010/wiki/BLE_Health_Thermometer2-010 //Temperature handles 00011 // - https://developer.mbed.org/users/rosterloh84/code/Buddi_Blueband (Disconnect Reasons) 00012 // - miscellaneous adopted from more samples... 00013 // Reference: 00014 // - nRF51822: 00015 // - MKit Pins: http://developer.mbed.org/users/mbed_official/code/mbed-src/file/c80ac197fa6a/targets/hal/TARGET_NORDIC/TARGET_MCU_NRF51822/TARGET_NRF51822_MKIT/PinNames.h 00016 // - Platform: http://developer.mbed.org/platforms/Nordic-nRF51822/ 00017 // - MCU: http://developer.mbed.org/users/mbed_official/code/mbed-src/file/d2c15dda23c1/targets/cmsis/TARGET_NORDIC/TARGET_MCU_NRF51822 00018 // - http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/ 00019 // - Reference: http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/docs/tip/ 00020 // - Reference: http://developer.mbed.org/teams/Bluetooth-Low-Energy/ 00021 // Warnings: 00022 // - As of 20141210 it is necessary to use Android App [nRF-Master Control Panel] to ensure any previous connected 00023 // code on mkit is properly Disconnected before trying to connect other Android nRF Apps (nRFToolbox, nRF UART 2.0, etc.). 00024 // As UART device doesn't offer disconnect you may need to load a non-uart sample, then use MCP to connect and discoonect, to clear the link. 00025 // Notes: 00026 // - 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. 00027 // - onDisconnect(Reason:8) occured after ~20Tx with no onDataSent() after phone moved out of range, OnTimeout didn't occur at all. 00028 // ToDo: and ToCheck: 00029 // - 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. 00030 // - Re-check where voltatile needed 00031 // - ToCheck(Maybe in App): device.CreateBond(), device.SetPairingConfirmation(), gatt.refresh(to clear ble cache when changing services in devlopment) 00032 // - 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 00033 // - invalid or untested reference code commented with "//x ", feel free to delete or experiment 00034 // - valid alternate code commented with "//a " prefix to indicate alternnative is valid code 00035 //==========End of PR's Header 00036 00037 //==========Historic Licencing from original imported sample from mbed website [BLE_HeartRate] ========== 00038 //From: http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/ 00039 /* mbed Microcontroller Library 00040 * Copyright (c) 2006-2013 ARM Limited 00041 * 00042 * Licensed under the Apache License, Version 2.0 (the "License"); 00043 * you may not use this file except in compliance with the License. 00044 * You may obtain a copy of the License at 00045 * http://www.apache.org/licenses/LICENSE-2.0 00046 * Unless required by applicable law or agreed to in writing, software 00047 * distributed under the License is distributed on an "AS IS" BASIS, 00048 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00049 * See the License for the specific language governing permissions and 00050 * limitations under the License. */ 00051 //==========end of Historic Licencing ========== 00052 00053 00054 //==========Compile Options========== 00055 #define ENABLE_uSerial 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/) 00056 //x #define ENABLE_BLE_SLOW 0 //PR: Option to slow the connection interval possibly saving power (After Connected) -- Imported feature never tested 00057 00058 //==========Includes========== 00059 #include "mbed.h" // mbed 00060 #include "BLEDevice.h" // BLE 00061 #include "nrf_soc.h" // nRF Internal Temperature Sensor 00062 00063 //Services 00064 #include "BatteryService.h" 00065 #include "DeviceInformationService.h" 00066 #include "HealthThermometerService.h" 00067 #include "HeartRateService.h" 00068 #include "LinkLossService.h" //TODO: How support this? 00069 //#include "DFUService" //TODO: DFU and FOTA Support 00070 //#include "UARTService.h" //TODO: Add a UART Channel for streaming data like logs? 00071 00072 //==========Debug Console========== 00073 #if ENABLE_uSerial 00074 //Restart TeraTerm just before Pressing Reset on mbed, Default Serual=9600-8N1(No Flow Control) 00075 //Using default baud rate to avoid issues with DEBUG in a constructor being at default baud rate before main() 00076 //TeraTerm: http://en.sourceforge.jp/projects/ttssh2/releases/ 00077 Serial debug_userial(USBTX, USBRX); //PR: DebugSerialOverMbedUSB 00078 #define DEBUG(...) { debug_userial.printf(__VA_ARGS__); } 00079 #else 00080 #define DEBUG(...) //Do Nothing, DEBUG() lines are ignored 00081 #endif 00082 00083 //========== This Section is to Create Debug output showing variable prep before main() ========== 00084 bool u8_prep_dummy(void) { 00085 DEBUG("\n\nBLE: ____Prep Memory____\n"); //May comment this out if none of the following Objects/Classes call initiator functions with debug 00086 return true; 00087 } 00088 const bool bPrep = u8_prep_dummy(); 00089 00090 //========== IO Hardware: Buttons, LEDs, PWM ========== 00091 // Inputs: (Simultaneous DigitalIn and InteruptIn is OK) 00092 DigitalIn bB1in(BUTTON1); //nRF51822p0.16==pin22 //if(bB1in){} 00093 DigitalIn bB2in(BUTTON2); //nRF51822p0.17==pin25 //if(bB2in){} 00094 InterruptIn B1int(BUTTON1); //nRF51822p0.16==pin22 //B1int.rise(&onB1rise); 00095 InterruptIn B2int(BUTTON2); //nRF51822p0.17==pin25 //B1int.fall(&onB1fall); 00096 00097 // Outputs: 00098 //a DigitalOut bL1out(LED1); // Direct LED1 drive 00099 //a DigitalOut bL2out(LED2); // Direct LED2 drive 00100 PwmOut fL1pwm(LED1); float fL1level = 0.1; // PWM LED1, brightness=float(0.0~1.0) 00101 PwmOut fL2pwm(LED2); float fL2level = 0.1; // PWM LED2, brightness=float(0.0~1.0) 00102 bool bAppControl = false; //Flag: Host has control of LEDs through BLE 00103 00104 //a volatile uint8_t uB1rise; void onB1rise(void){ uB1rise++; };// Flag Event, Counter helps detect missed events since last cleared 00105 //a volatile uint8_t uB1fall; void onB1fall(void){ uB1fall++; };// Flag Event, Counter helps detect missed events since last cleared 00106 //a volatile uint8_t uB2rise; void onB2rise(void){ uB2rise++; };// Flag Event, Counter helps detect missed events since last cleared 00107 //a volatile uint8_t uB2fall; void onB2fall(void){ uB2fall++; };// Flag Event, Counter helps detect missed events since last cleared 00108 00109 //========== ADC ========== 00110 // ADC/Analog: 20150106PR 00111 // - Options: 00112 // - AnalogIn, Default: http://developer.mbed.org/handbook/AnalogIn 00113 // - ADC Lib, http://developer.mbed.org/users/simonb/code/ADC_test/ 00114 // - Fast, but burns power: http://developer.mbed.org/users/Sissors/code/FastAnalogIn/ 00115 // Initially using the mbed standard: AnalogIn (Later may change when power or timing require it) 00116 // - adcX.read() returns a float, scaled so 0.0~1.0 == ADC range 0~Vcc (0.3 == 30% of Vcc) 00117 // - adcX.read_u16() returns uint16, raw (0x0000~0xFFFF) 00118 //nRF51822 mkit Analog Pins are only p1~p6 (P0_1~P0_6): 00119 //AnalogIn adc0(p26);// nRF51822p0.26==pin45==AIN0==XL2 (Reserve for low power 32KHz Crystal) 00120 //AnalogIn adc1(p27);// nRF51822p0.27==pin46==AIN1==XL1 (Reserve for low power 32KHz Crystal) 00121 //AnalogXX AREF0 // nRF51822p0.00==pin04 ==AREF0 (Reserve for Analog Reference) 00122 AnalogIn adcA(p1); // nRF51822p0.01==pin05==AIN2 00123 AnalogIn adcB(p2); // nRF51822p0.02==pin06==AIN3 00124 //AnalogIn adcC(p3); // nRF51822p0.03==pin07==AIN4 00125 //AnalogIn adcD(p4); // nRF51822p0.04==pin08==AIN5 00126 //AnalogIn adcE(p5); // nRF51822p0.05==pin09==AIN6 00127 //AnalogIn adcF(p6); // nRF51822p0.06==pin10==AIN7==AREF1 (Option: Reserve for Analog Reference) 00128 00129 void vShowADC(void) 00130 { //TODO: Check what the actual ADC register settings are to know how the nRF51822 is actually configured 00131 DEBUG(" ShowADC:"); 00132 DEBUG(" p1=AIN2 0x%04X %f,", adcA.read_u16(), adcA.read() ); 00133 DEBUG(" p2=AIN3 0x%04X %f,", adcB.read_u16(), adcB.read() ); 00134 //DEBUG(" p3=AIN4 0x%04X %f,", adcC.read_u16(), adcC.read() ); 00135 //DEBUG(" p4=AIN5 0x%04X %f,", adcD.read_u16(), adcD.read() ); 00136 //DEBUG(" p5=AIN6 0x%04X %f,", adcE.read_u16(), adcE.read() ); 00137 //DEBUG(" p6=AIN7 0x%04X %f,", adcF.read_u16(), adcF.read() ); 00138 DEBUG(" \n"); 00139 } 00140 00141 //==========BLE========== 00142 //const static char pcDeviceName[] = "bleIOv04_pr"; //Too Long for advertising with a UUID128 00143 const static char pcDeviceName[] = "bleIOr25"; //Advertised device name (Max length depends on available space with other Advertising data) 00144 BLEDevice ble; 00145 //Pointers to services for accesses outside main() 00146 HeartRateService *pServiceHRM; 00147 HealthThermometerService *pServiceHTM; 00148 BatteryService *pServiceBattery; 00149 DeviceInformationService *pServiceDeviceInfo; 00150 LinkLossService *pServiceLinkLoss; 00151 //x DFUService *pServiceDFU; 00152 //x UARTService *pServiceUART; //FromApp: pServiceUART->getTXCharacteristicHandle(); //ToApp: ble.updateCharacteristicValue(pServiceUART->getRXCharacteristicHandle(), pData, uLen); 00153 //x bleIOService *pServiceIO; 00154 00155 //==========UUID========== 00156 //UUID Info: 00157 // 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 00158 /* All the custom GATT based services and characteristics must use a 128 bit UUID. 00159 The Bluetooth_Base_UUID is: 00000000-0000-1000-8000 00805F9B34FB. 00160 All the 16-bit Attribute UUIDs defined in the adopted specifications use the above base. 00161 Generating a 128 bit UUID for custom profiles: For the 128 bit UUID, please refer to The ITU-T Rec. X.667. 00162 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. 00163 In addition if you go to http://www.itu.int/ITU-T/asn1/uuid.html, you can generate a unique 128-bit UUID. 00164 Latency: Refer to Core Spec V4.0, Vol 3, Part F - 3.3.1, which is "Ready by Type Request". 00165 If you look at the PDU, you have to send 2 bytes UUID for adopted profiles and 16 bytes UUID for custom profiles. 00166 There is additional 14 extra bytes over head for the custom profiles for "Ready By Type Request Method" 00167 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.) 00168 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) */ 00169 // 16bit UUID uses: 128bit base(32 hex digits): 0000****-0000-1000-8000 00805F9B34FB (Careful to use unsigned to avoid negatives and overflows) 00170 // 32bit UUID uses: 128bit base(32 hex digits): ********-0000-1000-8000 00805F9B34FB (Careful to use unsigned to avoid negatives and overflows) 00171 00172 //UUID128 List(Only a single UUID128 can fit in advertising) 00173 //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 00174 //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 00175 00176 //a //UUID16 List - Advertising these required by App nRF-Toolbox:HRM&HTM // Keep list short so advertizing not over 31bytes. 00177 /*static const uint16_t uuid16_list[] = { //Service List (Pre-defined standard 16bit services) 00178 // *Order here doesn't affect order in nRF-MCP Discovery of Services 00179 //BLE_UUID_GAP UUID_GENERIC_ACCESS //0x1800 //Included by Default, DeviceName, Appearance, PreferredConnectionParam 00180 //BLE_UUID_GATT UUID_GENERIC ATTRIBUTE //0x1801 //Included by Default, ServiceChanged, 00181 GattService::UUID_HEALTH_THERMOMETER_SERVICE, //0x1809 //HTM (Might need to be first for nRF App) 00182 GattService::UUID_HEART_RATE_SERVICE, //0x180D //HRM, BodyLocation, ControlPoint 00183 GattService::UUID_DEVICE_INFORMATION_SERVICE, //0x180A //sManufacturer, sModelNumber, sSerialNumber, sHWver, sFWver, sSWver 00184 GattService::UUID_BATTERY_SERVICE, //0x180F //BatteryLevel 00185 GattService::UUID_LINK_LOSS_SERVICE, //0x1803 //LinkLoss 00186 //x GattService::UUID_DFU, //0x1530 - See UARTServiceShortUUID in BLE_API:DFUService.cpp // 00187 //x GattService::UARTService, //0x0001~0x0003 - See DFUServiceShortUUID in BLE_API:UARTService.cpp // 00188 //Possibly useful: 00189 //x GattService::UUID_ALERT_NOTIFICATION_SERVICE, //0x1811 00190 //x GattService::UUID_CURRENT_TIME_SERVICE, //0x1805 00191 //x GattService::UUID_HUMAN_INTERFACE_DEVICE_SERVICE, //0x1812 00192 //x GattService::UUID_IMMEDIATE_ALERT_SERVICE, //0x1802 00193 //x GattService::UUID_PHONE_ALERT_STATUS_SERVICE, //0x180E 00194 //x GattService::UUID_REFERENCE_TIME_UPDATE_SERVICE, //0x1806 00195 //x GattService::UUID_SCAN_PARAMETERS_SERVICE, //0x1813 00196 };*/ 00197 00198 //========== bleIO - A Custom Service for Handling IO with an App (GPIO,PWM,etc.)========== 00199 //UUID128 List - Note only the single Service UUID128 can fit in the advertising 00200 00201 // Custom UUID128 base for this service and its characteristics (from: http://www.itu.int/ITU-T/asn1/uuid.html) 00202 // **This is a temporary UUID for testing by anyone, should be replaced before proceeding to real product.** 00203 // Can check that UUID is correctly entered by viewing with Android App: nRF-Master Control panel 00204 // UUID128 can be generated using: http://www.itu.int/ITU-T/asn1/uuid.html 00205 // 20141218PR: [4d3281c0-86d1-11e4-b084-0002a5d5c51b] == OID[2.25.102612802202735078710424021169255859483] 00206 // Base UUID128 (1 digit modified):{0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x1*};// NormalOrder 00207 uint8_t puUUID128_IOService[16] = {0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x10};// Service UUID 00208 uint8_t puUUID128_IOAdvertise[16]= {0x10,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Advertising Service UUID (=ReversedOrder) 00209 // Characteristic UUID: Initially using generic blocks of IO data that may be manually dividied up into Buttons, LEDs, PWM, ADC, etc. 00210 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 00211 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 00212 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 00213 // Alternately can have a characterostic for each discrete item, such as: 00214 //a uint8_t puUUID128_bleConfig32[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Configuration 32bits 00215 //a uint8_t puUUID128_bleOut1[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Output 1bit 00216 //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%) 00217 //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) 00218 //a uint8_t puUUID128_bleIn1[16] = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Input 1bit 00219 //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 00220 00221 /* Android: 00222 //bleIO Service and Characteristics 00223 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c51b", "bleIO base"); 00224 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c510", "bleIO Service"); 00225 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c511", "bleIO w8"); 00226 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c512", "bleIO r4"); 00227 attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c513", "bleIO n4"); 00228 00229 attributes.put("1bc5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev base"); 00230 attributes.put("10c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev Service"); 00231 attributes.put("11c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev w8"); 00232 attributes.put("12c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev r4"); 00233 attributes.put("13c5d5a5-0200-84b0-e411-d186c081324d", "bleIO rev n4"); 00234 */ 00235 00236 // Characteristic Value Storage (with Initial values, should match actual data type transferred, doesn't have to be byte array): 00237 //a uint8_t pIOw8[8] = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17}; //Value Storage for Characteristic Write8 00238 //a uint8_t pIOr4[4] = {0x20,0x21,0x22,0x23}; //Value Storage for Characteristic Read4 00239 //a uint8_t pIOn4[4] = {0x30,0x31,0x32,0x33}; //Value Storage for Characteristic Notify4 00240 00241 union { // Option: split long setup from short updates to minimize BLE power and traffic 00242 struct {// First item in union defines the initializers 00243 uint8_t L1pwm100; // LED1 Brightness (0%~100%) 00244 uint8_t L2pwm255; // LED2 Brightness (0~255) 00245 //float xxxx; //Option: If both smartphone and device use same type of float, else put converter in smartphone code (phone has larger battery and memory) 00246 uint16_t px[3]; // Spare 3*16bit (Test: incremented by Ticker1 00247 } s; 00248 uint8_t pu[]; //Direct Byte access 00249 char pc[]; //Character Access (No Terminating Null) 00250 } sIOw8 = {100,255,0xA00A, 0xB000, 0xC000}; // Structure/union for Characteristic IOw8 00251 // sIOw8.s.L1pwm100, sIOw8.s.L2pwm255, sIOw8.s.px, sIOw8.pu 00252 00253 union { // Longer maybe OK since only read upon command 00254 struct {// First item in union defines the initializers 00255 uint8_t bB1p:1,bB2p:1; // Button status, 1/true=Pressed, bit1==Button1, bit2==Button2 00256 uint8_t uB1p:6; // Count Button1 Presses (6bit) 00257 uint8_t uB1r; // Count Button1 Releases 00258 uint8_t uB2p; // Count Button2 Presses 00259 uint8_t uB2r; // Count Button2 Releases 00260 // // Option: ADC or other inputs or device status 00261 } s; 00262 uint8_t pu[]; //Direct Byte access 00263 char pc[]; //Character Access (No Terminating Null) 00264 } sIOr4 = { true, false, 0x3F, 255, 255, 255}; // Structure/union for Characteristic IOr4 00265 // sIOr4.s.bB1p, sIOr4.s.bB1p, sIOr4.s.uB1p, sIOr4.s.bB1r, sIOr4.s.uB2p, sIOr4.s.uB2r, sIOr4.pu 00266 00267 union { // Option: Shorten content to reduce BLE power and traffic 00268 struct {// First item in union defines the initializers 00269 uint8_t bB1p:1,bB2p:1; // Button status, 1/true=Pressed, bit1==Button1, bit2==Button2 00270 uint8_t uB1p:6; // Count Button2 Presses (6bit) *Notify* 00271 uint8_t uB2r; // Count Button2 Releases *Notify* 00272 int16_t iTempC100; // Temperature in hundreths of 'C (i.e. float = iTempC10/100) 00273 // // Option: ADC or other inputs or device status 00274 } s; 00275 uint8_t pu[]; //Direct Byte access 00276 char pc[]; //Character Access (No Terminating Null) 00277 } sIOn4 = { false, true, 0x3F, 255, -2345/*=-23.45'C*/}; // Structure/union for Characteristic IOn4 00278 // sIOn4.s.bB1p, sIOn4.s.bB2p, sIOn4.s.uB1p, sIOn4.s.uB2r, sIOn4.s.iTempC100, sIOn4.pu 00279 00280 //x GattCharacteristic::GattAttribute *descriptors[]; //How Set Characteristic Descriptors? Maybe in the App is easier? 00281 00282 //IO Characteristics: //GattCharacteristics xx(*UUID, *Payload=*Value, LenInit, LenMax, Properties) 00283 //GattCharacteristic IOw8(puUUID128_IOw8, sIOw8.pu, sizeof(sIOw8), sizeof(sIOw8), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); 00284 GattCharacteristic IOw8(puUUID128_IOw8, sIOw8.pu, sizeof(sIOw8), sizeof(sIOw8), 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 00285 GattCharacteristic IOr4(puUUID128_IOr4, sIOr4.pu, sizeof(sIOr4), sizeof(sIOr4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); // Host can manually request 00286 GattCharacteristic IOn4(puUUID128_IOn4, sIOn4.pu, sizeof(sIOn4), sizeof(sIOn4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY);// Host is Notified of changes (Same as HRM), *Works but first read not until a channge 00287 //GattCharacteristic IOn4(puUUID128_IOn4, sIOn4.pu, sizeof(sIOn4), sizeof(sIOn4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); // Host is notified of changes (Same as HTM & BattLevel), Buggy until can queue BLE Operations 00288 GattCharacteristic *pCustomCharacteristics[] = {&IOw8, &IOr4, &IOn4};//Table of Pointers to GattCharacteristics 00289 00290 //IO Service: //GattService::GattService(*UUID, *GattCharacteristics, (numCharacteristics) ) : 00291 GattService ServiceIO(puUUID128_IOService, pCustomCharacteristics, (sizeof(pCustomCharacteristics)/sizeof(pCustomCharacteristics[0]))); 00292 GattService *pServiceIO= &ServiceIO; // Pointer to Service 00293 00294 //========== Functions for IO Characteristics ======== 00295 void vShowIO(void) 00296 { //TODO: For Read Characteristics get data actually written to Characteristic, versus the memory expected to be in characteristic. 00297 uint8_t puBuf8[8]; 00298 uint16_t uLen; 00299 00300 DEBUG(" ShowIO:\n"); 00301 00302 // IOw8 Write (Displaying structure and buffer(c) data for comparison) 00303 uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOw8.getValueHandle(), puBuf8, &uLen); // Real Characteristic's Actual Value 00304 //DEBUG(" sIOw8: Handle:%d Len:%d L1:%d L2:%d %04X %04X %04X\n", IOw8.getValueHandle(), uLen, sIOw8.s.L1pwm100, sIOw8.s.L2pwm255, sIOw8.s.px[0], sIOw8.s.px[1], sIOw8.s.px[2]); 00305 //DEBUG(" sIOw8: Handle:%d Len:%d [%02X %02X %02X %02X %02X %02X %02X %02X]\n", IOw8.getValueHandle(), sizeof(sIOw8), sIOw8.pu[0],sIOw8.pu[1],sIOw8.pu[2],sIOw8.pu[3],sIOw8.pu[4],sIOw8.pu[5],sIOw8.pu[6],sIOw8.pu[7]); 00306 //DEBUG(" cIOw8: Handle:%d Len:%d [%02X %02X %02X %02X %02X %02X %02X %02X]\n", IOw8.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3], puBuf8[4], puBuf8[5], puBuf8[6], puBuf8[7] ); 00307 DEBUG(" sIOw8: Handle:%d Len:%d L1:%d L2:%d %04X %04X %04X", IOw8.getValueHandle(), uLen, sIOw8.s.L1pwm100, sIOw8.s.L2pwm255, sIOw8.s.px[0], sIOw8.s.px[1], sIOw8.s.px[2]); 00308 DEBUG(" sIOw8:[%02X %02X %02X %02X %02X %02X %02X %02X]", IOw8.getValueHandle(), sizeof(sIOw8), sIOw8.pu[0],sIOw8.pu[1],sIOw8.pu[2],sIOw8.pu[3],sIOw8.pu[4],sIOw8.pu[5],sIOw8.pu[6],sIOw8.pu[7]); 00309 DEBUG(" cIOw8:[%02X %02X %02X %02X %02X %02X %02X %02X]\n", IOw8.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3], puBuf8[4], puBuf8[5], puBuf8[6], puBuf8[7] ); 00310 00311 // IOr4 Read (Displaying structure and buffer(c) data for comparison) 00312 uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOr4.getValueHandle(), puBuf8, &uLen); // Real Characteristic's Actual Value 00313 //DEBUG("\n sIOr4: Handle:%d Len:%d B1:%d B2:%d B1p:%d B1r:%d B2p:%d B2r:%d\n", IOr4.getValueHandle(), uLen, sIOr4.s.bB1p, sIOr4.s.bB2p, sIOr4.s.uB1p, sIOr4.s.uB1r, sIOr4.s.uB2p, sIOr4.s.uB2r); 00314 //DEBUG(" sIOr4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOr4.getValueHandle(), sizeof(sIOr4), sIOr4.pu[0], sIOr4.pu[1], sIOr4.pu[2], sIOr4.pu[3]); 00315 //DEBUG(" cIOr4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOr4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] ); 00316 DEBUG(" sIOr4: Handle:%d Len:%d B1:%d B2:%d B1p:%d B1r:%d B2p:%d B2r:%d", IOr4.getValueHandle(), uLen, sIOr4.s.bB1p, sIOr4.s.bB2p, sIOr4.s.uB1p, sIOr4.s.uB1r, sIOr4.s.uB2p, sIOr4.s.uB2r); 00317 DEBUG(" sIOr4:[%02X %02X %02X %02X]", IOr4.getValueHandle(), sizeof(sIOr4), sIOr4.pu[0], sIOr4.pu[1], sIOr4.pu[2], sIOr4.pu[3]); 00318 DEBUG(" cIOr4:[%02X %02X %02X %02X]\n", IOr4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] ); 00319 00320 // IOn4 Notify (Displaying structure and buffer(c) data for comparison) 00321 uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOn4.getValueHandle(), puBuf8, &uLen); // Real Characteristic's Actual Value 00322 //DEBUG("\n sIOn4: Handle:%d Len:%d B1:%d B2:%d B1p:%d B2r:%d %.2f\n", IOn4.getValueHandle(), uLen, sIOn4.s.bB1p, sIOn4.s.bB2p, sIOn4.s.uB1p, sIOn4.s.uB2r, ((float)sIOn4.s.iTempC100/100.0)); 00323 //DEBUG(" sIOn4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOn4.getValueHandle(), sizeof(sIOn4), sIOn4.pu[0], sIOn4.pu[1], sIOn4.pu[2], sIOn4.pu[3]); 00324 //DEBUG(" cIOn4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOn4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] ); 00325 DEBUG(" sIOn4: Handle:%d Len:%d B1:%d B2:%d B1p:%d B2r:%d %.2f", IOn4.getValueHandle(), uLen, sIOn4.s.bB1p, sIOn4.s.bB2p, sIOn4.s.uB1p, sIOn4.s.uB2r, ((float)sIOn4.s.iTempC100/100.0)); 00326 DEBUG(" sIOn4:[%02X %02X %02X %02X]", IOn4.getValueHandle(), sizeof(sIOn4), sIOn4.pu[0], sIOn4.pu[1], sIOn4.pu[2], sIOn4.pu[3]); 00327 DEBUG(" cIOn4:[%02X %02X %02X %02X]\n", IOn4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] ); 00328 00329 //DEBUG("\n Handles for Standard Services' Characteristics:\n"); 00330 //DEBUG(" HRM: Rate:%d Location:%d Control:%d", pServiceHRM->getCharacteristic(0)->getValueHandle(),pServiceHRM->getCharacteristic(1)->getValueHandle(),pServiceHRM->getCharacteristic(2)->getValueHandle()); 00331 //DEBUG(" HTM: Temp:%d Type:%d", pServiceHTM->getCharacteristic(0)->getValueHandle(),pServiceHTM->getCharacteristic(1)->getValueHandle()); 00332 //DEBUG(" Batt: Level:%d", pServiceBattery->getCharacteristic(0)->getHandle()); 00333 //DEBUG(" DeviceInfo: %d~%d", pServiceDeviceInfo->getCharacteristic(0)->getHandle(), pServiceDeviceInfo->getCharacteristic(5)->getHandle()); 00334 //DEBUG(" LinkLoss: AlertLevel:%d", pServiceLinkLoss->getCharacteristic(0)->getHandle()); 00335 //DEBUG(" DFU:%d", pServiceDFU.getValueAttribute().getHandle()); 00336 //DEBUG(" UART:%d\n", pServiceUART.getValueAttribute().getHandle()); 00337 00338 vShowADC(); 00339 } 00340 00341 //==== Soft Updates to Characteristics for initial tests: (Most replaced by real buttons/sensors/timers ) 00342 /*void vUpdate_IOw8(void) // Handle in device changes to w8 characteristic 00343 {//a Option: Allow device to modify its settings and inform host (Some devices may be able to self-modify host written settings) 00344 static uint8_t uw8; //Level: 0..2 00345 switch(++uw8){ 00346 case 2: memcpy(pIOw8, "w2w2w2wc", sizeof(pIOw8)); DEBUG(" w8c"); break; 00347 case 1: memcpy(pIOw8, "w1w1w1wb", sizeof(pIOw8)); DEBUG(" w8b"); break; 00348 default: uw8=0; memcpy(pIOw8, "w0w0w0wa", sizeof(pIOw8)); DEBUG(" w8a"); break; 00349 } 00350 //ble.updateCharacteristicValue(uint16_t handle, const uint8_t *value, uint16_t size, bool localOnly) 00351 ble.updateCharacteristicValue(IOw8.getValueHandle(), pIOw8, sizeof(pIOw8)); 00352 //pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE 00353 vShowIO(); 00354 }*/ 00355 00356 //==== Hard Updates to Characteristics: [onButton()] Notify: B1press or B2release 00357 // *When direct driving hardware consider adjusting drive within the onCallback to reduce response time 00358 //TODO: Check need of volatile for changes in interrupts affecting variables in non-interrupt code? 00359 00360 volatile uint8_t uB1press; // Events since last handled in main() 00361 volatile uint8_t uB1release;// Events since last handled in main() 00362 volatile uint8_t uB2press; // Events since last handled in main() 00363 volatile uint8_t uB2release;// Events since last handled in main() 00364 00365 void vFillIO(void) // Fill the structures for the Characteristics, but leave any Notify to calling function 00366 { //Structures are filled completely so available for BLE read at any time 00367 // sIOr4.s.bB1p, sIOr4.s.bB1p, sIOr4.s.uB1p, sIOr4.s.bB1r, sIOr4.s.uB2p, sIOr4.s.uB2r, sIOr4.pu 00368 sIOr4.s.bB1p = !bB1in; //Button1 Pressed (nRF51822 mkit: Pressed=Lo) 00369 sIOr4.s.bB2p = !bB2in; //Button2 Pressed (nRF51822 mkit: Pressed=Lo) 00370 sIOr4.s.uB1p = uB1press;// Button1 Presses (Truncates to size) 00371 sIOr4.s.uB1r = uB1release; 00372 sIOr4.s.uB2p = uB2press; 00373 sIOr4.s.uB2r = uB2release; 00374 00375 // sIOn4.s.bB1p, sIOn4.s.bB2p, sIOn4.s.uB1p, sIOn4.s.uB2r, sIOn4.s.iTempC100, sIOn4.pu 00376 sIOn4.s.bB1p = !bB1in; //Button1 Pressed (nRF51822 mkit: Pressed=Lo) 00377 sIOn4.s.bB2p = !bB2in; //Button2 Pressed (nRF51822 mkit: Pressed=Lo) 00378 sIOn4.s.uB1p = uB1press;//Button1 Presses (Truncates to size) 00379 sIOn4.s.uB2r = uB2release; 00380 00381 int32_t i32_C_nRF; sd_temp_get(&i32_C_nRF); //Read the nRF Internal Temperature (Die in 0.25'C steps, offset +16'C) 00382 sIOn4.s.iTempC100 = (i32_C_nRF*25)-(1600); //Save temperature in hundreths (0.01'C steps relative to 0'C)(.25'C * 25=.01'C steps) 00383 //float fTemperature = (float(i32_temp)/4.0) - 16.0; // Scale&Shift (0.25'C from -16'C?) 00384 00385 ble.updateCharacteristicValue(IOr4.getValueHandle(), sIOr4.pu, sizeof(sIOr4)); // Update r4 data so ready for read (Doesn't Trigger Notify) 00386 } 00387 00388 //PR: Notify: Doing Notify on both Press and Release since n4 contains status of multiple inputs so need both bits to be correct. If Notify Characteristic is single button then notify could be on just press or just release. 00389 void onB1press(void) // B1 Press == *Notify* 00390 { // Update Characteristics that include this button 00391 uB1press++; // Flag/Count Events (allows detect any missed processing) 00392 vFillIO(); // Prepare IO Characteristic data 00393 ble.updateCharacteristicValue(IOn4.getValueHandle(), sIOn4.pu, sizeof(sIOn4));// Triggers Notify (Reading n4 at other times may have old data) 00394 DEBUG("\n B1p%d", uB1press); vShowIO(); 00395 }; 00396 void onB1release(void) // B1 Release 00397 { // Update Characteristics that include this button 00398 uB1release++; // Flag/Count Events (allows detect any missed processing) 00399 vFillIO(); // Prepare IO Characteristic data 00400 ble.updateCharacteristicValue(IOn4.getValueHandle(), sIOn4.pu, sizeof(sIOn4));// Triggers Notify (Reading n4 at other times may have old data) 00401 DEBUG("\n B1r%d", uB1release); vShowIO(); 00402 }; 00403 void onB2press(void) // B2 Press 00404 { // Update Characteristics that include this button 00405 uB2press++; // Flag/Count Events (allows detect any missed processing) 00406 vFillIO(); // Prepare IO Characteristic data 00407 ble.updateCharacteristicValue(IOn4.getValueHandle(), sIOn4.pu, sizeof(sIOn4));// Triggers Notify (Reading n4 at other times may have old data) 00408 DEBUG("\n B2p%d", uB2press); vShowIO(); 00409 }; 00410 void onB2release(void) // B2 Release 00411 { // Update Characteristics that include this button 00412 uB2release++; // Flag/Count Events (allows detect any missed processing) 00413 vFillIO(); // Prepare IO Characteristic data 00414 ble.updateCharacteristicValue(IOn4.getValueHandle(), sIOn4.pu, sizeof(sIOn4));// Triggers Notify (Reading n4 at other times may have old data) 00415 DEBUG("\n B2r%d", uB2release); vShowIO(); 00416 }; 00417 00418 //==========Functions:BLE========== 00419 void Callback_BLE_onTimeout(void) 00420 { //PR: Haven't seen this, even when phone moved out of range and events occur like OnDisconnect(Reason0x08) or LinkLoss 00421 DEBUG("\n\n\n\n**** BLEi: Callback_BLE_onTimeout() ****\n\n\n\n" ); 00422 00423 //DEBUG("\nBLE:Callback_BLE_onTimeout(), Restarting Advertising\n" ); 00424 //ble.startAdvertising(); 00425 } 00426 00427 void Callback_BLE_onDisconnect(Gap::Handle_t tHandle, Gap::DisconnectionReason_t eReason) 00428 { //PR: onDisconnect(Reason:8) occured after ~20Tx with no onDataSent() after phone moved out of range 00429 00430 00431 // REMOTE_USER_TERMINATED_CONNECTION = 0x13 = 19, 00432 // LOCAL_HOST_TERMINATED_CONNECTION = 0x16 = 22, 00433 // CONN_INTERVAL_UNACCEPTABLE = 0x3B = 59, 00434 DEBUG("\nBLEi: Callback_BLE_Disconnect(Handle:%d, eReason:0x%02x=", tHandle, eReason );//PR: Occurs properly when click disconnect in App nRFToolbox:HRM 00435 switch(eReason) { 00436 case Gap::REMOTE_USER_TERMINATED_CONNECTION: DEBUG("REMOTE_USER_TERMINATED_CONNECTION"); break; 00437 case Gap::CONN_INTERVAL_UNACCEPTABLE: DEBUG("CONN_INTERVAL_UNACCEPTABLE"); break; 00438 case Gap::LOCAL_HOST_TERMINATED_CONNECTION: DEBUG("LOCAL_HOST_TERMINATED_CONNECTION"); break; 00439 default: DEBUG("UNKNOWN"); break; 00440 } 00441 DEBUG("), Restarting Advertising\n", tHandle, eReason );//PR: Occurs properly when click disconnect in App nRFToolbox:HRM 00442 00443 //DEBUG("Wait10sec...\n");wait(10.0); //PR: Optional to test effect on advertising 00444 ble.startAdvertising(); // restart advertising 00445 00446 //TODO: LED Mode to Indicate Advertising until something else changes LED Mode? 00447 } 00448 00449 00450 void Callback_BLE_onConnect(Gap::Handle_t tHandle, Gap::addr_type_t ePeerAddrType, const Gap::address_t c6PeerAddr, const Gap::ConnectionParams_t *params) 00451 { 00452 DEBUG("\nBLEi: Callback_BLE_Connect(Handle:%d, eType:%d, Add:%u ...)\n", tHandle, ePeerAddrType, c6PeerAddr); 00453 00454 //x #if ENABLE_BLE_SLOW //UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL //PR: Adopted optional code never tested 00455 //x // Updating connection parameters can be attempted only after a connection has been 00456 //x // established. Please note that the ble-Central is still the final arbiter for 00457 //x // the effective parameters; the peripheral can only hope that the request is 00458 //x // honored. Please also be mindful of the constraints that might be enforced by 00459 //x // the BLE stack on the underlying controller. 00460 //x #define MIN_CONN_INTERVAL 250 // Minimum connection interval (250 ms) 00461 //x #define MAX_CONN_INTERVAL 350 // Maximum connection interval (350 ms) 00462 //x #define CONN_SUP_TIMEOUT 6000 // Connection supervisory timeout (6 seconds) 00463 //x #define SLAVE_LATENCY 4 00464 //x Gap::ConnectionParams_t tGap_conn_params; 00465 //x tGap_conn_params.minConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(MIN_CONN_INTERVAL); 00466 //x tGap_conn_params.maxConnectionInterval = Gap::MSEC_TO_GAP_DURATION_UNITS(MAX_CONN_INTERVAL); 00467 //x tGap_conn_params.connectionSupervisionTimeout = Gap::MSEC_TO_GAP_DURATION_UNITS(CONN_SUP_TIMEOUT); 00468 //x tGap_conn_params.slaveLatency = SLAVE_LATENCY; 00469 //x ble.updateConnectionParams(tHandle, &tGap_conn_params); 00470 //x #endif // #if UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL 00471 } 00472 00473 #ifdef NOT_DEFINED //Not available in BLE_API r277 20150126 00474 void Callback_BLE_onDataRead(const GattCharacteristicReadAuthCBParams *pParams) //NotAvailableOnlineYet20150114 00475 { 00476 GattAttribute::Handle_t tHandle=pParams->charHandle; 00477 00478 //Handle Changes to Service Characteristics: 00479 if(!ble.getGapState().connected){ //Ensure BLE still connected 00480 DEBUG("\nBLEi: Callback_BLE_onDataRead() while disconnected!!"); 00481 } else if (tHandle == IOr4.getValueHandle()) { // IOr4 Characteristic? 00482 DEBUG("\nBLEi: Callback_BLE_onDataRead() while disconnected!!"); 00483 vFillIO(); // Update the data for this Characteristic 00484 vShowIO(); 00485 //} else if (tHandle == IOw8.getValueHandle()) { // Writeonly, Continue with no changes, App can read back whatever was last written 00486 //} else if (tHandle == IOr4.getValueHandle()) { // Notifyonly, shouldn't occur, is automatically sent when Notify changes, anything needed should be in IOr4 00487 } else { 00488 DEBUG("\nBLEi: Callback_BLE_onDataRead(Unknown tHandle:%d)\n\n", tHandle); 00489 } 00490 } 00491 #endif 00492 00493 static volatile bool bNotifySent = false; //Volatile, don't optimize, changes under interrupt control 00494 static volatile unsigned uNotifyBLE; 00495 void Callback_BLE_onNotifySent(unsigned uNotify) //TODO: Consider renaming to onNotifySent(uNotified) 00496 { // Appears to get called when a Characteristic flagged for Notify is Updated/Sent (not for a characteristic being Read by the app). 00497 uNotifyBLE=uNotify; 00498 DEBUG(" Senti(%u)", uNotifyBLE); //TODO: PR: Why uSent always "1", expected it to match sent bytes length 00499 bNotifySent = true; 00500 } 00501 00502 void Callback_BLE_onDataWritten(const GattCharacteristicWriteCBParams *pParams) 00503 { // This occurs when use nRF-MCP to save a new Characteristic Value (SingleUpASrrowIcon) such as New Heart Rate Control Point (unsent if incorrect length) 00504 GattAttribute::Handle_t tHandle=pParams->charHandle; 00505 uint16_t uLen; 00506 00507 //Warning: *data may not be NULL terminated 00508 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); 00509 00510 /* //TODO: Add check of op type with tHandle check: switch(pParams->op){ case: GATTS_CHAR_OP_WRITE_REQ:}... 00511 switch(pParams->op){ 00512 case GATTS_CHAR_OP_INVALID: 00513 case GATTS_CHAR_OP_WRITE_REQ 00514 case GATTS_CHAR_OP_WRITE_CMD 00515 case GATTS_CHAR_OP_SIGN_WRITE_CMD //< Signed Write Command 00516 case GATTS_CHAR_OP_PREP_WRITE_REQ //< Prepare Write Request 00517 case GATTS_CHAR_OP_EXEC_WRITE_REQ_CANCEL //< Execute Write Request: Cancel all prepared writes 00518 case GATTS_CHAR_OP_EXEC_WRITE_REQ_NOW //< Execute Write Request: Immediately execute all prepared writes 00519 default: Error? 00520 }*/ 00521 00522 //a These are equivalent ways of accessing characteristic handles: 00523 //a if (pParams->charHandle == IOw8.getValueHandle()) { DEBUG("\n\nOK tHandle %d\n\n", pParams->charHandle); } 00524 //a if (pParams->charHandle == ServiceIO.getCharacteristic(0)->getValueHandle()) { DEBUG("\n\nOK tHandle %d\n\n", pParams->charHandle); } 00525 //a if (IOw8.getValueHandle() == ServiceIO.getCharacteristic(0)->getValueHandle()) { DEBUG("\n\nOK tHandle equivalent %d\n\n", pParams->charHandle); } 00526 00527 //Handle Changes to Service Characteristics: 00528 if(!ble.getGapState().connected){ //Ensure BLE still connected 00529 DEBUG("BLEi: Callback_BLE_onDataWritten() while disconnected!!"); 00530 } else if (tHandle == IOw8.getValueHandle()) { // This occurs from Write by App nRF-MCP since has Write Property set 00531 ble.readCharacteristicValue(tHandle, sIOw8.pu, &uLen); //Update Characteristic with new information (Option: Verify length or a checksum first) 00532 vShowIO(); 00533 bAppControl = true; //Host has taken control of LEDs 00534 fL1level = (((float)sIOw8.s.L1pwm100)/100.0); if(fL1level>1.0){fL1level=1.0;}; fL1pwm=fL1level; //Set LED1 Level 00535 fL2level = (((float)sIOw8.s.L2pwm255)/255.0); if(fL2level>1.0){fL2level=1.0;}; fL2pwm=fL2level; //Set LED1 Level 00536 DEBUG(" L1:%d==%f L2:%d==%f\n", sIOw8.s.L1pwm100, fL1level, sIOw8.s.L2pwm255, fL2level); 00537 //TODO: Update Outputs and settings (Includes flags for: Factory Reset, Enter Test Mode, Enter Demo Mode) 00538 00539 //} else if (tHandle == LinkLossHandle) { // TODO: Handle LinkLoss Changes 00540 //x pServiceLinkLoss->setAlertLevel( AlertLevel_t newLevel ) 00541 //x Can be Triggered by MCP or BluetoothLEGatt sample changing Linkloss setting: 00542 //x Alert=1: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x01]=Data[]) 00543 //x Alert=2: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x02]=Data[]) 00544 //} else if (tHandle == IOr4.getValueHandle()) { // Readonly, shouldn't occur 00545 //} else if (tHandle == IOn4.getValueHandle()) { // Notifyonly, shouldn't occur 00546 } else { 00547 DEBUG("\n Unknown onWrite(tHandle:%d)\n\n", tHandle); 00548 //This appears to happen at the beginning of a FOTA DFU Update, 20150126PR 00549 } 00550 } 00551 00552 void Callback_BLE_onUpdatesEnabled(Gap::Handle_t tHandle) // Notifications 00553 { //Triggered when click the UpdateContinuousIcon in nRF-MCP(ThreeDownArrows) 00554 if (tHandle == IOn4.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Enabled - IOn4 == Use Buttons to Trigger Update\n", tHandle); //This Occurs when selected by App nRF-MCP as has Notify property set 00555 } else if (tHandle == IOr4.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Enabled - IOr4\n", tHandle); // Notify not enabled on this Characteristic 00556 } else if (tHandle == IOw8.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Enabled - IOw8\n", tHandle); // Notify not enabled on this Characteristic 00557 } else { DEBUG("\nBLEi: onUpdates(Handle:%d) Enabled - Characteristic?\n", tHandle); 00558 } 00559 //TODO: process Enable for Each Notify Characteriistic of each Service 00560 } 00561 00562 void Callback_BLE_onUpdatesDisabled(Gap::Handle_t tHandle) // Notifications 00563 { //Triggered when click the UpdateContinuousIcon in nRF-MCP(ThreeDownArrows) 00564 if (tHandle == IOn4.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Disabled - IOn4\n", tHandle); //This Occurs when selected by App nRF-MCP as has Notify property set 00565 } else if (tHandle == IOr4.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Disabled - IOr4\n", tHandle); // Notify not enabled on this Characteristic 00566 } else if (tHandle == IOw8.getValueHandle()) { DEBUG("\nBLEi: onUpdates(Handle:%d) Disabled - IOw8\n", tHandle); // Notify not enabled on this Characteristic 00567 } else { DEBUG("\nBLEi: onUpdates(Handle:%d) Disabled - Characteristic?\n", tHandle); 00568 } 00569 //TODO: process Disable for Each Notify Characteriistic of each Service 00570 } 00571 void Callback_BLE_onConfirmRx(Gap::Handle_t tHandle) // Confirmations?? 00572 { 00573 DEBUG("\nBLEi: ConfirmationRx(Handle:%d)\n", tHandle); 00574 } 00575 00576 00577 // Adopted 20141213 from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LinkLoss/file/440ee5e8595f/main.cpp 00578 void Callback_BLE_onLinkLoss(LinkLossService::AlertLevel_t level) 00579 { 00580 printf("\nBLEi: Link Loss Alert, Level:%d\n", level); 00581 //TODO: Handle Link Loss, maybe lower power mode and/or advertising mode 00582 } 00583 //==========BLE:HRM========== 00584 //Adopted 2014Dec from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/ 00585 uint8_t update_hrm(void)//(bool bInit) 00586 { 00587 static uint8_t u8_hrm = 100; 00588 if (++u8_hrm >= 175) { 00589 u8_hrm = 100; 00590 DEBUG(" HRM>100"); 00591 } else { 00592 DEBUG(" HRM:%d", u8_hrm); 00593 } 00594 pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE 00595 return(u8_hrm); 00596 } 00597 //==========BLE:HTM(Using nRF Internal Temperature)========== 00598 // *If not using nRF51822 IC then change this to a simple counter like BLE:Battery section 00599 // Adopted 2014Dec from: https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/ 00600 // Service: https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.health_thermometer.xml 00601 // HTM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml 00602 //****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 00603 float update_htm(void) 00604 { 00605 //static float fTemperature = 0;//-123.456; 00606 int32_t i32_temp; 00607 sd_temp_get(&i32_temp); //Read the nRF Internal Temperature (Die in 0.25'C steps, Offset:TBD), TODO:Check Scaling 00608 float fTemperature = (float(i32_temp)/4.0) - 16.0; // Scale&Shift (0.25'C from -16'C?) 00609 00610 //{//Force to IEEE format to match needs of Apps like nRF-HTM and nRF-Toolbox:HTM 00611 // 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. 00612 // uint8_t exponent = 0xFE; //exponent is -2 00613 // uint32_t mantissa = (uint32_t)(fTemperature*100); 00614 // uint32_t temp_ieee11073 = ((((uint32_t)exponent) << 24) | (mantissa)); //Note: Assumes Mantissa within 24bits 00615 // memcpy(((uint8_t*)&fTemperature)+1, (uint8_t*)&temp_ieee11073, 4); //Overwrite with IEEE format float 00616 //} 00617 00618 pServiceHTM->updateTemperature( fTemperature );// Update Characteristic so sent by BLE 00619 DEBUG(" HTM%03d==%2.2f", i32_temp, fTemperature); 00620 return(fTemperature); 00621 } 00622 //==========BLE:Battery========== 00623 uint8_t update_batt(void) 00624 { 00625 static uint8_t u8_BattPercent=33; //Level: 0..100% 00626 u8_BattPercent <= 50 ? u8_BattPercent=100 : u8_BattPercent--; // Simulate Battery Decay 00627 pServiceBattery->updateBatteryLevel( u8_BattPercent ); // Update Characteristic so sent by BLE 00628 DEBUG(" Batt%03d%%", u8_BattPercent); 00629 return(u8_BattPercent); 00630 } 00631 00632 //==========Functions:Timer========== 00633 uint32_t u32_wakeevents=0, u32_wakelast=0; // Counter&Register for tracking Wake Events (to see monitor their Frequency) 00634 static volatile uint8_t uTicker1;//Volatile, don't optimize, changes under interrupt control 00635 void CallbackTicker1(void) 00636 { 00637 static uint32_t u32_Counter; // Counter for checking Timing 00638 DEBUG("\nBLEi: Ticker(%04u) WakeEvents(%d)", ++u32_Counter, u32_wakeevents); 00639 //if (!bAppControl) { fL1level+=0.1; if (fL1level>0.5){fL1level = 0.1;}; fL1pwm=fL1level;}//If host not controlling LED then ramp blink to show life 00640 { fL1level+=0.1; if (fL1level>0.5){fL1level = 0.1;}; fL1pwm=fL1level;}//Allow Ticker to regain control after a host change to LEDs 00641 uTicker1++; // Flag event, using counter so can detect missed events 00642 } 00643 00644 //==========main========== 00645 int main(void) 00646 { 00647 fL1level = 1.0; fL1pwm = fL1level;//Start LED1=OnMax 00648 fL2level = 0.2; fL2pwm = fL2level;//Start LED2=Dim 00649 00650 //Restart TeraTerm just before Pressing Reset on mbed, 9600-8N1(No Flow Control) 00651 DEBUG("\nBLE: ___%s___\n", pcDeviceName); 00652 00653 //ARM Predefines: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0491c/BABJFEFG.html 00654 // *Some are numbers not strings so printf(%s) not always appropriate 00655 DEBUG(" ARM Compiler Predefines:\n"); 00656 DEBUG(" Built UTC:[ %s %s ]\n", __DATE__, __TIME__); 00657 DEBUG(" __arm__[%d]", __arm__); 00658 DEBUG(" __ARMCC_VERSION [%d]=[P:major V:minor bbbb:build]\n", __ARMCC_VERSION); 00659 DEBUG(" __VERSION__(gnu Compiler)[" __VERSION__ "]\n"); 00660 //DEBUG(" __STDC_VERSION__ [%d]\n", __STDC_VERSION__); 00661 //DEBUG(" __BIG_ENDIAN[%d] ", __BIG_ENDIAN); 00662 DEBUG(" __OPTIMISE_LEVEL[%d]", __OPTIMISE_LEVEL); 00663 //DEBUG(" __OPTIMISE_SPACE[%d]", __OPTIMISE_SPACE); 00664 DEBUG(" __OPTIMISE_TIME[%d]", __OPTIMISE_TIME); 00665 DEBUG("\n __MODULE__[" __MODULE__ "] __FILE__[" __FILE__ "] __BASE_FILE__[" __BASE_FILE__ "]\n"); 00666 DEBUG(" __FUNCTION__[%s] __PRETTY_FUNCTION__[%s]\n", __FUNCTION__, __PRETTY_FUNCTION__) 00667 00668 //vShowIO(); Show Raw initialized values before any BLE 00669 00670 Ticker ticker1; //PR: Timer Object(Structure) 00671 ticker1.attach(CallbackTicker1, 5.0); //PR: Timer Handler, Float=PeriodSeconds (Note BLE default wakes about 50Hz/20msec) 00672 00673 //BLE1: Setup BLE Service (and event actions) //TODO: Check for services declared before main() - Is that OK? 00674 DEBUG("\nBLE: Connect App for Data: nRF-MCP, nRF-Toolbox:HRM/HTM, Android Sample BluetoothLeGatt, etc.\n"); 00675 DEBUG(" nRF-MCP: App->mbed: LinkLoss->AlertLevel(UpArrow)\n"); 00676 DEBUG(" nRF-MCP: App->mbed: BatteryService->BatteryLevel(DownArrow)\n"); 00677 DEBUG(" nRF-MCP: mbed->App: BatteryService->BatteryLevel->EnableNotify(ThreeDownArrows), Also App Acks the send=DEBUG('SentI')\n"); 00678 00679 DEBUG("BLE: Setup BLE\n"); 00680 ble.init(); 00681 ble.onDisconnection(Callback_BLE_onDisconnect); //PR: Host disconnects, restart advertising, clear any unnecessary functions to save power 00682 ble.onConnection(Callback_BLE_onConnect); //PR: Host connects (Not required if no actions enabled, enabled now just for debug) 00683 ble.onDataSent(Callback_BLE_onNotifySent); //PR: Occurs when a Notify Characteristic has been updated and sent over BLE 00684 ble.onDataWritten(Callback_BLE_onDataWritten); //PR: Occurs when an update to a Characteristic has been received over BLE 00685 ble.onTimeout(Callback_BLE_onTimeout); //PR: ??? Hasn't occured, TODO: Monitor and find out what causes this 00686 ble.onUpdatesEnabled(Callback_BLE_onUpdatesEnabled);//PR: Occurs when host enables notify on a Characteristic that has Notify property set 00687 ble.onUpdatesDisabled(Callback_BLE_onUpdatesDisabled); //TODO: 00688 ble.onConfirmationReceived(Callback_BLE_onConfirmRx); //TODO: 00689 //NotAvailableOnlineYet20150126 ble.setReadAuthorizationCallback(Callback_BLE_onDataRead);//TODO: Test new callback "setReadAuthorizationCallback()", then can read sensors live instead of polling them continuously. 00690 00691 //BLE2: Setup Services (with their initial values and options) 00692 DEBUG("BLE: Setup Services\n"); 00693 // *Order here affects order in nRF-MCP Discovery of Services 00694 DeviceInformationService ServiceDeviceInfo(ble, "Maker", pcDeviceName, "sn1234", "hw00", "fw00", "sw00");//(BLEDevice), pcManufacturer, pcModelNumber, pcSerialNumber, pcHWver, pcFWver, pcSWver 00695 pServiceDeviceInfo = &ServiceDeviceInfo; //DEBUG(" Handle Service DeviceInfo:%d\n", ServiceDeviceInfo.XXXgetHandle()); 00696 LinkLossService ServiceLinkLoss(ble, Callback_BLE_onLinkLoss, LinkLossService::HIGH_ALERT); //New20141213, TBD 00697 pServiceLinkLoss = &ServiceLinkLoss; 00698 BatteryService ServiceBattery(ble, 10); 00699 pServiceBattery = &ServiceBattery; 00700 HeartRateService ServiceHRM(ble, (uint8_t)111, HeartRateService::LOCATION_FINGER); 00701 pServiceHRM = &ServiceHRM; 00702 HealthThermometerService ServiceHTM(ble, 33.3, HealthThermometerService::LOCATION_EAR); 00703 pServiceHTM = &ServiceHTM; 00704 //UARTService ServiceUART(ble); 00705 // pServiceUART = &ServiceUART; 00706 00707 DEBUG("BLE: Setup Custom IO Service\n"); 00708 ble.addService(ServiceIO); //Pointer: pServiceIO 00709 vShowIO(); // Show Initialized values with bleIO initialized (includes tHandle) 00710 //TODO: Ensure outputs in correct Initial state 00711 00712 //BLE3: Setup advertising 00713 // 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. 00714 // 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. 00715 DEBUG("BLE: Setup Advertising - Apps like HRM/HTM/UART require matching UUID\n"); 00716 ble.clearAdvertisingPayload(); //Prep 00717 ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(1000)); //PR: Advertise 1sec (1Hz) 00718 ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); //PR: TODO: To Study 00719 ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); //PR: BLE Only (Option:LE_LIMITED_DISCOVERABLE) 00720 // Does "LE_GENERAL_DISCOVERABLE" affect whether UUID needs to be advertised to discover services? 00721 //a ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // Multiple UUID16 for Standard Services 00722 ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)puUUID128_IOAdvertise, sizeof(puUUID128_IOAdvertise)); //Single UUID128 for primary Service 00723 00724 //? PR: I'm not sure what these lines do, they were inherited from an example: 00725 //? ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR); 00726 //? ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER); 00727 00728 // Add LocalName last so if Advertising too long will easily see as Name won't be available for the device. 00729 ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)pcDeviceName, sizeof(pcDeviceName)-1);//PR: LocalName (No NULL) 00730 ble.startAdvertising(); 00731 00732 //Example Advertising (Max 31 bytes): 0x01020304050607080910111213141516171819202122232425262728293031 00733 // Example Raw Advertising caught by App nRF-MCP: 0x020106070309180A180D180909626C655052763034 00734 // = Len02 Type01 Value06 (Flags: BREDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE) 00735 // = Len07 Type03 Values: 0918 0A18 0D18 (ListUUID16: 1809 180A 180D ) 00736 // = Len09 Type09 Values: 62 6C 65 50 52 76 30 34 (LocalName = "blePRv04") 00737 // Example Raw Advertising caught by App nRF-MCP: 0x0201061106FB349B5F80000080001000000F180000070209180A180D18 00738 // = Len02 Type01 Value06 (Flags: BREDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE) 00739 // = Len11 Type06 ValueFB349B5F8000008000100000_0F18_0000 (UUID128: BluetoothBattery=0x180F) 00740 // = Len07 Type02 Value 0918 0A18 0D18 (UUID16: 1809 180A 180D ) 00741 // = LocalName field wasn't appended as insufficient space, so Device won't be named when scanning. 00742 00743 // Setup InterruptIn for Buttons (coded for nRF51822-mkit polarities on these buttons) 00744 DEBUG("BLE: Enable Button Interrupts\n"); 00745 B1int.fall(&onB1press); // Button1 Press/fall 00746 B1int.rise(&onB1release); // Button1 Release/rise 00747 B2int.fall(&onB2press); // Button2 Press/fall 00748 B2int.rise(&onB2release); // Button2 Release/rise 00749 00750 DEBUG("BLE: Main Loop\n"); 00751 while (true) { 00752 if (uTicker1 && ble.getGapState().connected) { //If Ticker1 and Connected then Update Appropriate Data 00753 uTicker1 = 0; // Clear flag for next Ticker1, see CallbackTicker1(), TODO: log if missed any ticks 00754 00755 // Read Sensors, and update matching Service Characteristics (only if connected) 00756 // *Order here doesn't affect order in nRF-MCP Discovery of Services 00757 //TBD: Maybe save power by not Tx unless enabled by App? 00758 // The Services are discovered if they were setup/started (They don't need update to be discovered) 00759 update_htm(); 00760 update_hrm(); 00761 update_batt(); 00762 00763 //a vUpdate_IOw8(); 00764 //a vUpdate_IOr4(); 00765 //a vUpdate_IOn4(); 00766 00767 DEBUG(" BLE:Wakes:%04u,Delta:%03u ", u32_wakeevents, u32_wakeevents-u32_wakelast); //For Evaluating Timing 00768 u32_wakelast = u32_wakeevents; 00769 } else if (uTicker1) { 00770 uTicker1 = 0; // Clear flag for next Ticker1, see CallbackTicker1(), TODO: log if missed any ticks 00771 DEBUG(" BLE: Tick while unconnected "); 00772 } else if (bNotifySent){ 00773 bNotifySent=false; //clear flag Notify Characteristic Transmitted 00774 DEBUG(" BLE: Notify(%u) ", uNotifyBLE); 00775 } else { 00776 //DEBUG("BLE: Wait for Event\n\r"); //x Debug output here causes endless wakes from sleep() 00777 ble.waitForEvent(); //PR: Like sleep() with ble handling. TODO: Handle Error return 00778 u32_wakeevents++; //PR: Count events for frequency monitoring (20141207PR: nRF51822 mbed HRM = 50Hz) 00779 //if (!bAppControl) { fL2level+=0.25; if (fL2level>0.5){fL2level = 0.0;}; fL2pwm=fL2level;} //If host not controling LED then Ramp Blink to show life 00780 //{ fL2level+=0.25; if (fL2level>0.5){fL2level = 0.0;}; fL2pwm=fL2level;} //Allow Ticker to regain control after a host change to LEDs 00781 00782 //** Possibly PWM is causing extra wakes? 00783 00784 } 00785 } 00786 } 00787 //========== end of main.cpp ========== 00788 00789
Generated on Fri Jul 15 2022 13:14:29 by 1.7.2