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!!!
Diff: main.cpp
- Revision:
- 25:1c6c2895f729
- Parent:
- 23:78aad4e53ae2
- Child:
- 26:942faa4201d5
diff -r 3fd510b912f6 -r 1c6c2895f729 main.cpp --- 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? + } } }