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:
25:1c6c2895f729
Parent:
23:78aad4e53ae2
Child:
26:942faa4201d5
--- a/main.cpp	Mon Jan 26 17:57:11 2015 +0000
+++ b/main.cpp	Mon Jan 26 18:36:11 2015 +0000
@@ -128,20 +128,19 @@
 
 void vShowADC(void)
 {   //TODO: Check what the actual ADC register settings are to know how the nRF51822 is actually configured
-    DEBUG(" ShowADC:\n");
-    DEBUG(" p1=AIN2[%f=0x%04X]", adcA.read(), adcA.read_u16() );
-    DEBUG(" p2=AIN3[%f=0x%04X]", adcB.read(), adcB.read_u16() );
-    //DEBUG(" p3=AIN4[%f=0x%04X]", adcC.read(), adcC.read_u16() );
-    //DEBUG(" p4=AIN5[%f=0x%04X]", adcD.read(), adcD.read_u16() );
-    //DEBUG(" p5=AIN6[%f=0x%04X]", adcE.read(), adcE.read_u16() );
-    //DEBUG(" p6=AIN7[%f=0x%04X]", adcF.read(), adcF.read_u16() );
+    DEBUG(" ShowADC:");
+    DEBUG(" p1=AIN2 0x%04X %f,", adcA.read_u16(), adcA.read() );
+    DEBUG(" p2=AIN3 0x%04X %f,", adcB.read_u16(), adcB.read() );
+    //DEBUG(" p3=AIN4 0x%04X %f,", adcC.read_u16(), adcC.read() );
+    //DEBUG(" p4=AIN5 0x%04X %f,", adcD.read_u16(), adcD.read() );
+    //DEBUG(" p5=AIN6 0x%04X %f,", adcE.read_u16(), adcE.read() );
+    //DEBUG(" p6=AIN7 0x%04X %f,", adcF.read_u16(), adcF.read() );
     DEBUG(" \n");  
 }
 
-
 //==========BLE==========
 //const static char pcDeviceName[] = "bleIOv04_pr"; //Too Long for advertising with a UUID128
-const static char pcDeviceName[] = "bleIOv04"; //Advertised device name (Max length depends on available space with other Advertising data)
+const static char pcDeviceName[] = "bleIO"; //Advertised device name (Max length depends on available space with other Advertising data)
 BLEDevice   ble;
 //Pointers to services for accesses outside main()
     HeartRateService            *pServiceHRM;
@@ -300,28 +299,32 @@
 
     DEBUG(" ShowIO:\n");
 
-    // IOw8 Write
+    // IOw8 Write (Displaying structure and buffer(c) data for comparison)
     uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOw8.getValueHandle(), puBuf8, &uLen);  // Real Characteristic's Actual Value
-    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]);
-    //DEBUG(" pIOw8: Handle:%d Len:%d [%02X %02X %02X %02X  %02X %02X %02X %02X]\n", IOw8.getValueHandle(), sizeof(pIOw8), pIOw8[0], pIOw8[1], pIOw8[2], pIOw8[3],pIOw8[4], pIOw8[5], pIOw8[6], pIOw8[7]);
-    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]);
-    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] );
+    //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]);
+    //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]);
+    //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] );
+    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]);
+    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]);
+    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] );
  
-    // IOr4 Read
+    // IOr4 Read  (Displaying structure and buffer(c) data for comparison)
     uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOr4.getValueHandle(), puBuf8, &uLen);  // Real Characteristic's Actual Value
-    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);
-    //DEBUG(" pIOr4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOr4.getValueHandle(), sizeof(pIOr4), pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]);
-    //DEBUG(" sIOr4: Handle:%d Len:%d [%02X %02X %02X %02X][%.4s]\n", IOr4.getValueHandle(), sizeof(sIOr4), sIOr4.pu[0], sIOr4.pu[1], sIOr4.pu[2], sIOr4.pu[3], sIOr4.pc );
-    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]);
-    // Read Actual IOr4 Characteristic's data back from Service (i.e. last written to service):
-    DEBUG(" cIOr4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOr4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] );
+    //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);
+    //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]);
+    //DEBUG(" cIOr4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOr4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] );
+    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);
+    DEBUG(" sIOr4:[%02X %02X %02X %02X]",   IOr4.getValueHandle(), sizeof(sIOr4), sIOr4.pu[0], sIOr4.pu[1], sIOr4.pu[2], sIOr4.pu[3]);
+    DEBUG(" cIOr4:[%02X %02X %02X %02X]\n", IOr4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] );
 
-    // IOn4 Notify
+    // IOn4 Notify  (Displaying structure and buffer(c) data for comparison)
     uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOn4.getValueHandle(), puBuf8, &uLen);  // Real Characteristic's Actual Value
-    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));
-    //DEBUG("\n pIOn4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOn4.getValueHandle(), sizeof(pIOn4), pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]);
-    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]);
-    DEBUG(" cIOn4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOn4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] );
+    //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));
+    //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]);
+    //DEBUG(" cIOn4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOn4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] );
+    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));
+    DEBUG(" sIOn4:[%02X %02X %02X %02X]",   IOn4.getValueHandle(), sizeof(sIOn4), sIOn4.pu[0], sIOn4.pu[1], sIOn4.pu[2], sIOn4.pu[3]);
+    DEBUG(" cIOn4:[%02X %02X %02X %02X]\n", IOn4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] );
 
     //DEBUG("\n Handles for Standard Services' Characteristics:\n");
     //DEBUG("  HRM: Rate:%d  Location:%d  Control:%d", pServiceHRM->getCharacteristic(0)->getValueHandle(),pServiceHRM->getCharacteristic(1)->getValueHandle(),pServiceHRM->getCharacteristic(2)->getValueHandle());
@@ -467,25 +470,25 @@
     //x #endif // #if UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL
 }
 
-#ifdef NOT_DEFINED
+#ifdef NOT_DEFINED //Not available in BLE_API r277 20150126
 void Callback_BLE_onDataRead(const GattCharacteristicReadAuthCBParams *pParams) //NotAvailableOnlineYet20150114 
 {
     GattAttribute::Handle_t tHandle=pParams->charHandle;
     
     //Handle Changes to Service Characteristics:
     if(!ble.getGapState().connected){ //Ensure BLE still connected
-        DEBUG("BLEi: Callback_BLE_onDataRead() while disconnected!!");
+        DEBUG("\nBLEi: Callback_BLE_onDataRead() while disconnected!!");
     } else if (tHandle == IOr4.getValueHandle()) {     // IOr4 Characteristic?
-        DEBUG("BLEi: Callback_BLE_onDataRead() while disconnected!!");
+        DEBUG("\nBLEi: Callback_BLE_onDataRead() while disconnected!!");
         vFillIO(); // Update the data for this Characteristic
         vShowIO();          
     //} else if (tHandle == IOw8.getValueHandle()) { // Writeonly, Continue with no changes, App can read back whatever was last written
     //} else if (tHandle == IOr4.getValueHandle()) { // Notifyonly, shouldn't occur, is automatically sent when Notify changes, anything needed should be in IOr4
     } else {
-        DEBUG("\n  Unknown onRead(tHandle:%d)\n\n", tHandle);
+        DEBUG("\nBLEi: Callback_BLE_onDataRead(Unknown tHandle:%d)\n\n", tHandle);
     }  
 }
-#endif 
+#endif
 
 static volatile bool bNotifySent = false; //Volatile, don't optimize, changes under interrupt control
 static volatile unsigned uNotifyBLE;
@@ -626,12 +629,14 @@
 }
 
 //==========Functions:Timer==========
+uint32_t u32_wakeevents=0, u32_wakelast=0; // Counter&Register for tracking Wake Events (to see monitor their Frequency)
 static volatile uint8_t uTicker1;//Volatile, don't optimize, changes under interrupt control
 void CallbackTicker1(void)
 {
     static uint32_t u32_Counter; // Counter for checking Timing
-    DEBUG("\nBLEi: Ticker(%04u) ", ++u32_Counter);
-    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
+    DEBUG("\nBLEi: Ticker(%04u) WakeEvents(%d)", ++u32_Counter, u32_wakeevents);
+    //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
+    { fL1level+=0.1; if (fL1level>0.5){fL1level = 0.1;}; fL1pwm=fL1level;}//Allow Ticker to regain control after a host change to LEDs
     uTicker1++; // Flag event, using counter so can detect missed events
 }
 
@@ -680,7 +685,7 @@
     ble.onUpdatesEnabled(Callback_BLE_onUpdatesEnabled);//PR: Occurs when host enables notify on a Characteristic that has Notify property set
     ble.onUpdatesDisabled(Callback_BLE_onUpdatesDisabled);    //TODO:
     ble.onConfirmationReceived(Callback_BLE_onConfirmRx);     //TODO:
-    //NotAvailableOnlineYet20150114 ble.setReadAuthorizationCallback(Callback_BLE_onDataRead);//TODO: Test new callback "setReadAuthorizationCallback()", then can read sensors live instead of polling them continuously.   
+    //NotAvailableOnlineYet20150126 ble.setReadAuthorizationCallback(Callback_BLE_onDataRead);//TODO: Test new callback "setReadAuthorizationCallback()", then can read sensors live instead of polling them continuously.   
     
     //BLE2: Setup Services (with their initial values and options)
     DEBUG("BLE: Setup Services\n");
@@ -742,7 +747,6 @@
     B2int.rise(&onB2release);   // Button2 Release/rise
 
     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 (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
@@ -771,7 +775,11 @@
             //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 
             u32_wakeevents++;   //PR: Count events for frequency monitoring (20141207PR: nRF51822 mbed HRM = 50Hz)
-            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
+            //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
+            //{ fL2level+=0.25; if (fL2level>0.5){fL2level = 0.0;}; fL2pwm=fL2level;} //Allow Ticker to regain control after a host change to LEDs
+
+            //** Possibly PWM is causing extra wakes?
+
          }
     }
 }