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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

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