Experimental BLE project showing how IO can be made with an App over BLE. Pointer to matching App will be added when ready, initially this works with: - Android App [nRF-Master Control Panel], supports Write,Read,Notify - Android Project [BluetoothLeGatt]

Dependencies:   BLE_API mbed nRF51822

This is an experimental project for BLE (Bluetooth LE == Bluetooth Low Energy == Bluetooth Smart).

  • It supports general IO over BLE with Read/Notify/Write support.
  • It is compatible with FOTA using Android App "nRF Master Control Panel" (20150126)
  • IO supported by:
    • Custom Android App is in the WIKI under: Android-App, developed from Android Sample "BluetoothLeGatt"
    • Android App: nRF-MCP (Master Control Panel)
    • iOS App LightBlue.
    • General HRM, HTM, Battery and similar apps should be able to access the matching services.
  • It includes combinations of code from other projects, alternative code included can be tried by moving comments (, //)
  • 20150126 bleIO r25: It compiles for both "Nordic nRF51822" and "Nordic nRF51822 FOTA" platforms
  • 20150126 The matching bleIO App (in wiki) doesn't support FOTA yet, use Android App "nRF Master Control Panel"

Feedback and ideas greatly appreciated!!!

Revision:
11:7d02fe5ebea5
Parent:
10:ee3a359f7d3f
Child:
12:8bac5f5d3a3e
--- a/main.cpp	Fri Dec 19 22:37:29 2014 +0000
+++ b/main.cpp	Sun Dec 21 16:26:46 2014 +0000
@@ -25,8 +25,8 @@
 //    - Handle All return codes for all functions not void, including BLE not BLE_ERROR_NONE, as a minimum output some debug and log event in non-volatile memory for diagnostics.
 //    - Re-check where voltatile needed
 //    - Evaluate setting: IS_SRVC_CHANGED_CHARACT_PRESENT, see: https://devzone.nordicsemi.com/question/22751/nrftoobox-on-android-not-recognizing-changes-in-application-type-running-on-nordic-pcb/?answer=23097#post-id-23097
-//    - delete all code with "//x " prefix as that was just for notes and doesn't actually work
-//    - change all valid code temporarily commended to "//a " prefix to indicate alternnative is valid code
+//    - invalid or untested reference code commented with "//x ", feel free to delete or experiment
+//    - valid alternate code commented with "//a " prefix to indicate alternnative is valid code
 //==========End of PR's Header
 
 //==========Historic Licencing from original imported sample from mbed website [BLE_HeartRate] ==========
@@ -47,8 +47,8 @@
 
 
 //==========Compile Options==========
-#define ENABLE_SerialUSB_DEBUG_CONSOLE                  1  //PR: Enable Debug on mbed's USB Serial Debug, Setup: Serial 9600,8,N,1,NoFlowControl (TeraTerm: http://en.sourceforge.jp/projects/ttssh2/releases/)
-#define UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL    0  //PR: Option to slow the connection intervsal possibly saving power (After Connected)
+#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/)
+//x #define ENABLE_BLE_SLOW 0   //PR: Option to slow the connection interval possibly saving power (After Connected) -- Imported feature never tested
 
 //==========Includes==========
 #include "mbed.h"                       // mbed
@@ -65,11 +65,12 @@
 //#include "UARTService.h"              //TODO: Add a UART Channel for streaming data like logs?
 
 //==========Debug Console==========
-#if ENABLE_SerialUSB_DEBUG_CONSOLE
-    //Restart TeraTerm just before Pressing Reset on mbed, Default:9600-8N1(No Flow Control)
+#if ENABLE_uSerial
+    //Restart TeraTerm just before Pressing Reset on mbed, Default Serual=9600-8N1(No Flow Control)
     //Using default baud rate to avoid issues with DEBUG in a constructor being at default baud rate before main()
-    Serial  debug_serial(USBTX, USBRX); //PR: DebugSerialOverMbedUSB
-    #define DEBUG(...) { debug_serial.printf(__VA_ARGS__); }
+    //TeraTerm: http://en.sourceforge.jp/projects/ttssh2/releases/
+    Serial  debug_userial(USBTX, USBRX); //PR: DebugSerialOverMbedUSB
+    #define DEBUG(...) { debug_userial.printf(__VA_ARGS__); }
 #else
     #define DEBUG(...) //Do Nothing, DEBUG() lines are ignored
 #endif 
@@ -81,16 +82,29 @@
 }
 const bool bPrep = u8_prep_dummy();
 
-//==========LEDs==========
-//DigitalOut  out_led1(LED1);             //PR: Firmware heartbeat
-//DigitalOut  out_led2(LED2);             //PR: Firmware heartbeat
-PwmOut      pwm_led1(LED1);             //PR: Firmware Life Indicator 
-PwmOut      pwm_led2(LED2);             //TODO: Controlled by App 
-float       f_led1level = 0.1;          //Initial Brightness (Typically 0.0~0.5)
-float       f_led2level = 0.1;          //Initial Brightness (Typically 0.0~0.5)
+//========== IO Hardware: Buttons, LEDs, PWM ==========
+// Inputs:
+DigitalIn       bB1in(BUTTON1); //if(bB1in){}
+DigitalIn       bB2in(BUTTON2); //if(bB2in){}
+InterruptIn     B1int(BUTTON1); //B1int.rise(&onB1rise);  
+InterruptIn     B2int(BUTTON2); //B1int.fall(&onB1fall);
+
+// Outputs:
+//DigitalOut      bL1out(LED1);   // Direct LED1 drive
+//DigitalOut    bL2out(LED2);   // Direct LED2 drive
+PwmOut fL1pwm(LED1); float fL1level = 0.1; // PWM LED1, brightness=float(0.0~1.0)
+PwmOut fL2pwm(LED2); float fL2level = 0.1; // PWM LED2, brightness=float(0.0~1.0)
+
+// onButton Callbacks for InterruptIn
+// *When direct driving hardware consider adjusting drive within the onCallback to reduce response time
+//TODO: Check need of volatile for changes in interrupts affecting variables in non-interrupt code?
+volatile uint8_t uB1rise; void onB1rise(void){ uB1rise++; };// Flag Event, Counter helps detect missed events since last cleared
+volatile uint8_t uB1fall; void onB1fall(void){ uB1fall++; };// Flag Event, Counter helps detect missed events since last cleared
+volatile uint8_t uB2rise; void onB2rise(void){ uB2rise++; };// Flag Event, Counter helps detect missed events since last cleared
+volatile uint8_t uB2fall; void onB2fall(void){ uB2fall++; };// Flag Event, Counter helps detect missed events since last cleared
 
 //==========BLE==========
-const static char     pcDeviceName[]    = "bleIO"; //PR: Why can App nRF-MCP modify this even though flagged as Const, maybe only temporary mod till App restarts?
+const static char pcDeviceName[] = "bleIOv04_pr"; //PR: Why can App nRF-MCP modify this even though flagged as Const, maybe only temporary mod till App restarts?
 BLEDevice   ble;
 //Pointers to services for accesses outside main()
     HealthThermometerService    *pServiceHTM;       //tempChar.getValueAttribute().getHandle() 
@@ -123,7 +137,7 @@
 //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
 //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
 
-//a //UUID16 List - Advertising these required by App nRF-Toolbox:HRM&HTM // Keep list short so advertizing not too long.
+//a //UUID16 List - Advertising these required by App nRF-Toolbox:HRM&HTM // Keep list short so advertizing not over 31bytes.
 /*static const uint16_t uuid16_list[]     = { //Service List (Pre-defined standard 16bit services)
     // *Order here doesn't affect order in nRF-MCP Discovery of Services
     //BLE_UUID_GAP  UUID_GENERIC_ACCESS                 //0x1800    //Included by Default, DeviceName, Appearance, PreferredConnectionParam
@@ -155,7 +169,7 @@
     // 20141218PR: [4d3281c0-86d1-11e4-b084-0002a5d5c51b] == OID[2.25.102612802202735078710424021169255859483]
     // Base UUID128 (1 digit modified):{0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x1*};// NormalOrder
     uint8_t puUUID128_IOService[16]  = {0x4d,0x32,0x81,0xc0,0x86,0xd1,0x11,0xe4,0xb0,0x84,0x00,0x02,0xa5,0xd5,0xc5,0x10};// Service UUID
-    uint8_t puUUID128_IOAdvertise[16]= {0x10,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Advertising Service UUID (=FlippedOrder)
+    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)
     // Characteristic UUID: Initially using generic blocks of IO data that may be manually dividied up into Buttons, LEDs, PWM, ADC, etc.
     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
     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
@@ -168,7 +182,6 @@
     //a uint8_t puUUID128_bleIn1[16]      = {0x1X,0xc5,0xd5,0xa5,0x02,0x00,0x84,0xb0,0xe4,0x11,0xd1,0x86,0xc0,0x81,0x32,0x4d};// Input 1bit
     //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
 
-
 /* Android:
         //bleIO Service and Characteristics
         attributes.put("4d3281c0-86d1-11e4-b084-0002a5d5c51b", "bleIO base");
@@ -203,7 +216,7 @@
     GattService ServiceIO(puUUID128_IOService, pCustomCharacteristics, (sizeof(pCustomCharacteristics)/sizeof(pCustomCharacteristics[0])));
     GattService *pServiceIO= &ServiceIO;  // Pointer to Service
 
-//a Option: Allow device to modify its settings and inform host (Typically the device wouldn't change a host only setting)
+//a Option: Allow device to modify its settings and inform host (Some devices may be able to self-modify host written settings)
 /*void vUpdate_IOw8(void)  // Handle in device changes to w8 characteristic 
 {
     static uint8_t uw8; //Level: 0..2 
@@ -217,8 +230,8 @@
     //pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE
 }*/
 
-void vUpdate_IOr4(void) // Handle in device changes to r4 characteristic
-{
+void vUpdate_IOr4(void) // Handle device updates r4 characteristics (Without Notify, Host must poll for these)
+{   // For out project this would be: Temperature, Version Info, etc.
     static uint8_t ur4; //Level: 0..2 
     switch(++ur4){
         case 2:         memcpy(pIOr4, "r2r2", sizeof(pIOr4)); DEBUG(" r4c"); break;
@@ -228,8 +241,8 @@
     ble.updateCharacteristicValue(IOr4.getValueHandle(), pIOr4, sizeof(pIOr4));
 }
 
-void vUpdate_IOn4(void) // Handle in device changes to n4 characteristic
-{
+void vUpdate_IOn4(void) // Handle device updates n4 characteristics (With notify)
+{   // For our Device this would be: Dispenser count changes, Button Activity, Empty warning, Timeouts, etc.
     static uint8_t un4; //Level: 0..2 
     switch(++un4){
         case 2:         memcpy(pIOn4, "n2n2", sizeof(pIOr4)); DEBUG(" n4c"); break;
@@ -264,24 +277,23 @@
 {
     DEBUG("\nBLEi: Callback_BLE_Connect(Handle:%d, eType:%d, Add:%u ...)\n", tHandle, ePeerAddrType, c6PeerAddr);
 
-    #if UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL //PR: Adopted optional code never tested
-        /* Updating connection parameters can be attempted only after a connection has been
-         * established. Please note that the ble-Central is still the final arbiter for
-         * the effective parameters; the peripheral can only hope that the request is
-         * honored. Please also be mindful of the constraints that might be enforced by
-         * the BLE stack on the underlying controller.*/
-        #define MIN_CONN_INTERVAL 250  /**< Minimum connection interval (250 ms) */
-        #define MAX_CONN_INTERVAL 350  /**< Maximum connection interval (350 ms). */
-        #define CONN_SUP_TIMEOUT  6000 /**< Connection supervisory timeout (6 seconds). */
-        #define SLAVE_LATENCY     4
-
-        Gap::ConnectionParams_t tGap_conn_params;
-        tGap_conn_params.minConnectionInterval        = Gap::MSEC_TO_GAP_DURATION_UNITS(MIN_CONN_INTERVAL);
-        tGap_conn_params.maxConnectionInterval        = Gap::MSEC_TO_GAP_DURATION_UNITS(MAX_CONN_INTERVAL);
-        tGap_conn_params.connectionSupervisionTimeout = Gap::MSEC_TO_GAP_DURATION_UNITS(CONN_SUP_TIMEOUT);
-        tGap_conn_params.slaveLatency                 = SLAVE_LATENCY;
-        ble.updateConnectionParams(tHandle, &tGap_conn_params);
-    #endif // #if UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL
+    //x #if ENABLE_BLE_SLOW //UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL //PR: Adopted optional code never tested
+    //x    // Updating connection parameters can be attempted only after a connection has been
+    //x    // established. Please note that the ble-Central is still the final arbiter for
+    //x    // the effective parameters; the peripheral can only hope that the request is
+    //x    // honored. Please also be mindful of the constraints that might be enforced by
+    //x    // the BLE stack on the underlying controller.
+    //x    #define MIN_CONN_INTERVAL 250  // Minimum connection interval (250 ms)
+    //x    #define MAX_CONN_INTERVAL 350  // Maximum connection interval (350 ms)
+    //x    #define CONN_SUP_TIMEOUT  6000 // Connection supervisory timeout (6 seconds)
+    //x    #define SLAVE_LATENCY     4
+    //x    Gap::ConnectionParams_t tGap_conn_params;
+    //x    tGap_conn_params.minConnectionInterval        = Gap::MSEC_TO_GAP_DURATION_UNITS(MIN_CONN_INTERVAL);
+    //x    tGap_conn_params.maxConnectionInterval        = Gap::MSEC_TO_GAP_DURATION_UNITS(MAX_CONN_INTERVAL);
+    //x    tGap_conn_params.connectionSupervisionTimeout = Gap::MSEC_TO_GAP_DURATION_UNITS(CONN_SUP_TIMEOUT);
+    //x    tGap_conn_params.slaveLatency                 = SLAVE_LATENCY;
+    //x    ble.updateConnectionParams(tHandle, &tGap_conn_params);
+    //x #endif // #if UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL
 }
 
 static volatile bool bSent = false; //Volatile, don't optimize, changes under interrupt control
@@ -326,12 +338,12 @@
         if (tHandle == IOw8.getValueHandle()) {     // This occurs from Write by App nRF-MCP since has Write Property set
             ble.readCharacteristicValue(tHandle, pIOw8, &uLen);
             DEBUG("  IOw8[%d]:%02X %02X %02X %02X %02X %02X %02X %02X\n", uLen, pIOw8[0], pIOw8[1], pIOw8[2], pIOw8[3],pIOw8[4], pIOw8[5], pIOw8[6], pIOw8[7]);
-            //TODO: Update Outputs
-        //} else if (tHandle == IOr4.getValueHandle()) {
+            //TODO: Update Outputs and settings: Direct LED Control or Output Control, Or Configuration (Includes flags for: Factory Reset, Enter Test Mode, Enter Demo Mode)
+        //} else if (tHandle == IOr4.getValueHandle()) { // Readonly, shouldn't occur
         //    ble.readCharacteristicValue(tHandle, pIOr4, &uLen);
         //    DEBUG("  IOr4[%d]:%02X %02X %02X %02X\n", uLen, pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]);
         //    //TODO: Update Outputs
-        //} else if (tHandle == IOn4.getValueHandle()) {
+        //} else if (tHandle == IOn4.getValueHandle()) { // Readonly, shouldn't occur
         //    ble.readCharacteristicValue(tHandle, pIOn4, &uLen);
         //    DEBUG("  IOn4[%d]:%02X %02X %02X %02X\n", uLen, pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]);
         //    //TODO: Update Outputs
@@ -355,8 +367,8 @@
 void Callback_BLE_onUpdatesEnabled(Gap::Handle_t tHandle)
 {   //Triggered when click the UpdateContinuousIcon in nRF-MCP(ThreeDownArrows)
     if        (tHandle == IOn4.getValueHandle())    { DEBUG("  onUpdates(Handle:%d) Enabled - IOn4\n", tHandle); //This Occurs when selected by App nRF-MCP as has Notify property set
-  //} else if (tHandle == IOr4.getValueHandle())    { DEBUG("  onUpdates(Handle:%d) Enabled - IOr4\n", tHandle);
-  //} else if (tHandle == IOw8.getValueHandle())    { DEBUG("  onUpdates(Handle:%d) Enabled - IOw8\n", tHandle);
+  //} else if (tHandle == IOr4.getValueHandle())    { DEBUG("  onUpdates(Handle:%d) Enabled - IOr4\n", tHandle); // Notify not enabled on this Characteristic
+  //} else if (tHandle == IOw8.getValueHandle())    { DEBUG("  onUpdates(Handle:%d) Enabled - IOw8\n", tHandle); // Notify not enabled on this Characteristic
     } else                                          { DEBUG("  onUpdates(Handle:%d) Enabled - Characteristic?\n", tHandle);
     }
 }
@@ -417,22 +429,24 @@
 }
 
 //==========Functions:Timer==========
-static volatile bool  b_Ticker1 = false;//Volatile, don't optimize, changes under interrupt control
+static volatile uint8_t uTicker1;//Volatile, don't optimize, changes under interrupt control
 void CallbackTicker1(void)
 {
-    static uint32_t    u32_Counter; // Counter for Debug Output
-    f_led1level+=0.1; if (f_led1level>0.5){f_led1level = 0.1;}; pwm_led1=f_led1level;//PR: Ramp Blink
-    DEBUG("\nBLEi: Ticker1(%u) ", ++u32_Counter);
-    b_Ticker1 = true;   //PR: Flag to handle Ticker1 Event in Main loop so interupts not blocked.
+    static uint32_t u32_Counter; // Counter for checking Timing
+    DEBUG("\nBLEi: Ticker(%04u) ", ++u32_Counter);
+    fL1level+=0.1; if (fL1level>0.5){fL1level = 0.1;}; fL1pwm=fL1level;//PR: Ramp Blink
+    uTicker1++; // Flag event, using counter so can detect missed events
 }
+
 //==========main==========
 int main(void)
 {
-    f_led1level = 1; pwm_led1 = f_led1level;//Start LED1=OnMax
-    f_led2level = 1; pwm_led2 = f_led2level;//Start LED2=OnMax
+    fL1level = 1; fL1pwm = fL1level;//Start LED1=OnMax
+    fL2level = 1; fL2pwm = fL2level;//Start LED2=OnMax
 
     //Restart TeraTerm just before Pressing Reset on mbed, 9600-8N1(No Flow Control)
     DEBUG("\nBLE: ___%s___\n", pcDeviceName); 
+    DEBUG(" Built:[ %s %s ]  Compiler:[ %s ]\n", __DATE__, __TIME__, __VERSION__);
     DEBUG("BLE: Connect App for Data: nRF-MCP, nRF-Toolbox:HRM/HTM, Android Sample BluetoothLeGatt, etc.\n");
     DEBUG("BLE: BluetoothLeGatt: App->mbed: LinkLoss->AlertLevel(UpArrow)\n"); 
     DEBUG("BLE: BluetoothLeGatt: App->mbed: BatteryService->BatteryLevel(DownArrow)\n"); 
@@ -484,8 +498,8 @@
     ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); //PR: BLE Only (Option:LE_LIMITED_DISCOVERABLE)
     // Does "LE_GENERAL_DISCOVERABLE" affect whether UUID needs to be advertised to discover services?
     //a ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // Multiple UUID16 for Standard Services
-    ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)puUUID128_IOService, sizeof(puUUID128_IOService)); //Single UUID128 for primary Service
-    
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::INCOMPLETE_LIST_128BIT_SERVICE_IDS, (const uint8_t *)puUUID128_IOAdvertise, sizeof(puUUID128_IOAdvertise)); //Single UUID128 for primary Service
+ 
     //? PR: I'm not sure what these lines do, they were inherited from an example:
     //? ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
     //? ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER);         
@@ -505,11 +519,18 @@
     // = Len07 Type02 Value 0918 0A18 0D18                      (UUID16: 1809 180A 180D )
     // = LocalName field wasn't appended as insufficient space, so Device won't be named when scanning.
 
+    // Setup InterruptIn for Buttons
+    DEBUG("BLE: Enable Button Interrupts\n");
+    B1int.fall(&onB1fall);// Button1 Press
+    B1int.rise(&onB1rise);// Button1 Release
+    B2int.fall(&onB2fall);// Button2 Press
+    B2int.rise(&onB2rise);// Button2 Release
+
     DEBUG("BLE: Main Loop\n");
     uint32_t u32_wakeevents=0, u32_wakelast=0; // Counter&Register for tracking Wake Events (to see monitor their Frequency)
     while (true) {
-        if (b_Ticker1 && ble.getGapState().connected) { //If Ticker1 and Connected Update Data
-            b_Ticker1 = false; // Clear flag for next Ticker1, see CallbackTicker1()
+        if (uTicker1 && ble.getGapState().connected) { //If Ticker1 and Connected then Update Appropriate Data
+            uTicker1 = 0; // Clear flag for next Ticker1, see CallbackTicker1(), TODO: log if missed any ticks
   
             // Read Sensors, and update matching Service Characteristics (only if connected)
             // *Order here doesn't affect order in nRF-MCP Discovery of Services
@@ -525,16 +546,16 @@
 
             DEBUG("  BLE:Wakes:%u,Delta:%u ", u32_wakeevents, u32_wakeevents-u32_wakelast); //For Evaluating Timing
             u32_wakelast = u32_wakeevents;           
-        } else if (b_Ticker1) {
-            b_Ticker1 = false; // Clear flag for next Ticker1, see CallbackTicker1()
+        } else if (uTicker1) {
+            uTicker1 = 0; // Clear flag for next Ticker1, see CallbackTicker1(), TODO: log if missed any ticks
             DEBUG("BLE: Tick while unconnected ");
         } else if (bSent){
             bSent=false; //clear flag
             //DEBUG("BLE: Sent %ubytes ", uSentBLE);
         } else {
-            //DEBUG("BLE: Wait for Event\n\r"); //x Debug output here causes unnecessary wakes resulting in endless awakes.    
-            ble.waitForEvent(); //PR: Wait for event - Yield control to BLE Stack and other events (Process ALL pending events before waiting again)
-            f_led2level+=0.25; if (f_led2level>0.5){f_led2level = 0.0;}; pwm_led2=f_led2level;//PR: Ramp Blink
+            //DEBUG("BLE: Wait for Event\n\r"); //x Debug output here causes endless wakes from sleep()
+            ble.waitForEvent(); //PR: Like sleep() with ble handling. TODO: Handle Error return
+            fL2level+=0.25; if (fL2level>0.5){fL2level = 0.0;}; fL2pwm=fL2level;//PR: Ramp Blink
             u32_wakeevents++;   //PR: Count events for frequency monitoring (20141207PR: nRF51822 mbed HRM = 50Hz)
         }
     }