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:
- 12:8bac5f5d3a3e
- Parent:
- 11:7d02fe5ebea5
- Child:
- 13:1c67c03bbf53
--- a/main.cpp Sun Dec 21 16:26:46 2014 +0000 +++ b/main.cpp Sun Dec 21 20:44:37 2014 +0000 @@ -83,25 +83,23 @@ const bool bPrep = u8_prep_dummy(); //========== IO Hardware: Buttons, LEDs, PWM ========== -// Inputs: +// Inputs: (Simultaneous DigitalIn and InteruptIn is OK) 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) +//a DigitalOut bL1out(LED1); // Direct LED1 drive +//a 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) +bool bAppControl = false; //Flag: Host has control of LEDs through BLE -// 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 +//a volatile uint8_t uB1rise; void onB1rise(void){ uB1rise++; };// Flag Event, Counter helps detect missed events since last cleared +//a volatile uint8_t uB1fall; void onB1fall(void){ uB1fall++; };// Flag Event, Counter helps detect missed events since last cleared +//a volatile uint8_t uB2rise; void onB2rise(void){ uB2rise++; };// Flag Event, Counter helps detect missed events since last cleared +//a volatile uint8_t uB2fall; void onB2fall(void){ uB2fall++; };// Flag Event, Counter helps detect missed events since last cleared //==========BLE========== 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? @@ -198,27 +196,98 @@ */ // Characteristic Value Storage (with Initial values, should match actual data type transferred, doesn't have to be byte array): - uint8_t pIOw8[8] = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17}; //Value Storage for Characteristic Write8 - uint8_t pIOr4[4] = {0x20,0x21,0x22,0x23}; //Value Storage for Characteristic Read4 - uint8_t pIOn4[4] = {0x30,0x31,0x32,0x33}; //Value Storage for Characteristic Notify4 + //a uint8_t pIOw8[8] = {0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17}; //Value Storage for Characteristic Write8 + //a uint8_t pIOr4[4] = {0x20,0x21,0x22,0x23}; //Value Storage for Characteristic Read4 + //a uint8_t pIOn4[4] = {0x30,0x31,0x32,0x33}; //Value Storage for Characteristic Notify4 + + union { // Option: split long setup from short updates to minimize BLE power and traffic + struct {// First item in union defines the initializers + uint8_t L1pwm100; // LED1 Brightness (0%~100%) + uint8_t L2pwm255; // LED2 Brightness (0~255) + //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) + uint16_t px[3]; // Spare 3*16bit (Test: incremented by Ticker1 + } s; + uint8_t pu[]; //Direct Byte access + char pc[]; //Character Access (No Terminating Null) + } sIOw8 = {100,255,0xA00A, 0xB000, 0xC000}; // Structure/union for Characteristic IOw8 + // sIOw8.s.L1pwm100, sIOw8.s.L2pwm255, sIOw8.s.px, sIOw8.pu + + union { // Longer maybe OK since only read upon command + struct {// First item in union defines the initializers + uint8_t bB1p:1,bB2p:1; // Button status, 1/true=Pressed, bit1==Button1, bit2==Button2 + uint8_t uB1p:6; // Count Button1 Presses (6bit) + uint8_t uB1r; // Count Button1 Releases + uint8_t uB2p; // Count Button2 Presses + uint8_t uB2r; // Count Button2 Releases + // // Option: ADC or other inputs or device status + } s; + uint8_t pu[]; //Direct Byte access + char pc[]; //Character Access (No Terminating Null) + } sIOr4 = { true, false, 0x3F, 255, 255, 255}; // Structure/union for Characteristic IOr4 + // sIOr4.s.bB1p, sIOr4.s.bB1p, sIOr4.s.uB1p, sIOr4.s.bB1r, sIOr4.s.uB2p, sIOr4.s.uB2r, sIOr4.pu + + union { // Option: Shorten content to reduce BLE power and traffic + struct {// First item in union defines the initializers + uint8_t bB1p:1,bB2p:1; // Button status, 1/true=Pressed, bit1==Button1, bit2==Button2 + uint8_t uB1p:6; // Count Button2 Presses (6bit) *Notify* + uint8_t uB2r; // Count Button2 Releases *Notify* + int16_t iTempC100; // Temperature in hundreths of 'C (i.e. float = iTempC10/100) + // // Option: ADC or other inputs or device status + } s; + uint8_t pu[]; //Direct Byte access + char pc[]; //Character Access (No Terminating Null) + } sIOn4 = { false, true, 0x3F, 255, -2345/*=-23.45'C*/}; // Structure/union for Characteristic IOn4 + // sIOn4.s.bB1p, sIOn4.s.bB2p, sIOn4.s.uB1p, sIOn4.s.uB2r, sIOn4.s.iTempC100, sIOn4.pu //x GattCharacteristic::GattAttribute *descriptors[]; //How Set Characteristic Descriptors? Maybe in the App is easier? //IO Characteristics: //GattCharacteristics xx(*UUID, *Payload=*Value, LenInit, LenMax, Properties) //GattCharacteristic IOw8(puUUID128_IOw8, pIOw8, sizeof(pIOw8), sizeof(pIOw8), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE_WITHOUT_RESPONSE); - GattCharacteristic IOw8(puUUID128_IOw8, pIOw8, sizeof(pIOw8), sizeof(pIOw8), 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 - GattCharacteristic IOr4(puUUID128_IOr4, pIOr4, sizeof(pIOr4), sizeof(pIOr4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); // Host can manually request + 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 + GattCharacteristic IOr4(puUUID128_IOr4, sIOr4.pu, sizeof(sIOr4), sizeof(sIOr4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ); // Host can manually request //GattCharacteristic IOn4(puUUID128_IOn4, pIOn4, sizeof(pIOn4), sizeof(pIOn4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); - GattCharacteristic IOn4(puUUID128_IOn4, pIOn4, sizeof(pIOn4), sizeof(pIOn4), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); // Host is notified of changes - GattCharacteristic *pCustomCharacteristics[] = {&IOw8, &IOr4, &IOn4};//Table of Pointers to Characteristics + 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 + GattCharacteristic *pCustomCharacteristics[] = {&IOw8, &IOr4, &IOn4};//Table of Pointers to GattCharacteristics //IO Service: //GattService::GattService(*UUID, *GattCharacteristics, (numCharacteristics) ) : 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 (Some devices may be able to self-modify host written settings) +//========== Functions for IO Characteristics ======== +void vShowIO(void) +{ //TODO: For Read Characteristics get data actually written to Characteristic, versus the memory expected to be in characteristic. + uint8_t puBuf8[8]; + uint16_t uLen; + + DEBUG(" ShowIO:\n"); + + // IOw8 Write + 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[0], sIOw8.s.px[0]); + //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]); + uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOw8.getValueHandle(), puBuf8, &uLen); // Real Characteristic's Actual Value + 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] ); + + // IOr4 Read + 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): + uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOr4.getValueHandle(), puBuf8, &uLen); // Real Characteristic's Actual Value + DEBUG(" cIOr4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOr4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] ); + + // IOn4 Notify + 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]); + uLen=sizeof(puBuf8); ble.readCharacteristicValue(IOn4.getValueHandle(), puBuf8, &uLen); // Real Characteristic's Actual Value + DEBUG(" cIOn4: Handle:%d Len:%d [%02X %02X %02X %02X]\n", IOn4.getValueHandle(), uLen, puBuf8[0], puBuf8[1], puBuf8[2], puBuf8[3] ); +} + +//==== Soft Updates to Characteristics for initial tests: (Most replaced by real buttons/sensors/timers ) /*void vUpdate_IOw8(void) // Handle in device changes to w8 characteristic -{ +{//a Option: Allow device to modify its settings and inform host (Some devices may be able to self-modify host written settings) static uint8_t uw8; //Level: 0..2 switch(++uw8){ case 2: memcpy(pIOw8, "w2w2w2wc", sizeof(pIOw8)); DEBUG(" w8c"); break; @@ -228,34 +297,72 @@ //ble.updateCharacteristicValue(uint16_t handle, const uint8_t *value, uint16_t size, bool localOnly) ble.updateCharacteristicValue(IOw8.getValueHandle(), pIOw8, sizeof(pIOw8)); //pServiceHRM->updateHeartRate( u8_hrm );// Update Characteristic so sent by BLE + vShowIO(); }*/ -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; - case 1: memcpy(pIOr4, "r1r1", sizeof(pIOr4)); DEBUG(" r4b"); break; - default: ur4=0; memcpy(pIOr4, "r0r0", sizeof(pIOr4)); DEBUG(" r4a"); break; - } - ble.updateCharacteristicValue(IOr4.getValueHandle(), pIOr4, sizeof(pIOr4)); +//==== Hard Updates to Characteristics: [onButton()] Notify: B1press or B2release +// *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 uB1press; // Events since last handled in main() +volatile uint8_t uB1release;// Events since last handled in main() +volatile uint8_t uB2press; // Events since last handled in main() +volatile uint8_t uB2release;// Events since last handled in main() + +void vFillIO(void) // Fill the structures for the Characteristics, but leave any Notify to calling function +{ //Structures are filled completely so available for BLE read at any time + // sIOr4.s.bB1p, sIOr4.s.bB1p, sIOr4.s.uB1p, sIOr4.s.bB1r, sIOr4.s.uB2p, sIOr4.s.uB2r, sIOr4.pu + sIOr4.s.bB1p = !bB1in; //Button1 Pressed (nRF51822 mkit: Pressed=Lo) + sIOr4.s.bB2p = !bB2in; //Button2 Pressed (nRF51822 mkit: Pressed=Lo) + sIOr4.s.uB1p = uB1press;// Button1 Presses (Truncates to size) + sIOr4.s.uB1r = uB1release; + sIOr4.s.uB2p = uB2press; + sIOr4.s.uB2r = uB2release; + + // sIOn4.s.bB1p, sIOn4.s.bB2p, sIOn4.s.uB1p, sIOn4.s.uB2r, sIOn4.s.iTempC100, sIOn4.pu + sIOn4.s.bB1p = !bB1in; //Button1 Pressed (nRF51822 mkit: Pressed=Lo) + sIOn4.s.bB2p = !bB2in; //Button2 Pressed (nRF51822 mkit: Pressed=Lo) + sIOn4.s.uB1p = uB1press;//Button1 Presses (Truncates to size) + sIOn4.s.uB2r = uB2release; + + 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) + 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) + //float fTemperature = (float(i32_temp)/4.0) - 16.0; // Scale&Shift (0.25'C from -16'C?) } -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; - case 1: memcpy(pIOn4, "n1n1", sizeof(pIOr4)); DEBUG(" n4b"); break; - default: un4=0; memcpy(pIOn4, "n0n0", sizeof(pIOr4)); DEBUG(" n4a"); break; - } - ble.updateCharacteristicValue(IOn4.getValueHandle(), pIOn4, sizeof(pIOn4)); -} +void onB1press(void) // B1 Press == *Notify* +{ // Update Characteristics that include this button + uB1press++; // Flag/Count Events (allows detect any missed processing) + vFillIO(); // Prepare IO Characteristic data + ble.updateCharacteristicValue(IOn4.getValueHandle(), sIOn4.pu, sizeof(sIOn4));// Notify + DEBUG("\n B1p%d", uB1press); vShowIO(); +}; +void onB1release(void) // B1 Release +{ // Update Characteristics that include this button + uB1release++; // Flag/Count Events (allows detect any missed processing) + vFillIO(); // Prepare IO Characteristic data + ble.updateCharacteristicValue(IOr4.getValueHandle(), sIOr4.pu, sizeof(sIOr4)); + DEBUG("\n B1r%d", uB1release); vShowIO(); +}; +void onB2press(void) // B2 Press +{ // Update Characteristics that include this button + uB2press++; // Flag/Count Events (allows detect any missed processing) + vFillIO(); // Prepare IO Characteristic data + ble.updateCharacteristicValue(IOr4.getValueHandle(), sIOr4.pu, sizeof(sIOr4)); + DEBUG("\n B2p%d", uB2press); vShowIO(); +}; +void onB2release(void) // B2 Release +{ // Update Characteristics that include this button + uB2release++; // Flag/Count Events (allows detect any missed processing) + vFillIO(); // Prepare IO Characteristic data + ble.updateCharacteristicValue(IOn4.getValueHandle(), sIOn4.pu, sizeof(sIOn4));// Notify + DEBUG("\n B2r%d", uB2release); vShowIO(); +}; //==========Functions:BLE========== void Callback_BLE_onTimeout(void) { //PR: Haven't seen this, even when phone moved out of range and events occur like OnDisconnect(Reason0x08) or LinkLoss - DEBUG("\nBLEi: Callback_BLE_onTimeout()\n" ); + DEBUG("\n\n\n\n**** BLEi: Callback_BLE_onTimeout() ****\n\n\n\n" ); //DEBUG("\nBLE:Callback_BLE_onTimeout(), Restarting Advertising\n" ); //ble.startAdvertising(); @@ -296,14 +403,13 @@ //x #endif // #if UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL } -static volatile bool bSent = false; //Volatile, don't optimize, changes under interrupt control -static volatile unsigned uSentBLE; -void Callback_BLE_onDataSent(unsigned uSent){ - uSentBLE=uSent; - DEBUG("BLEi: SentI(%u)", uSentBLE); //TODO: PR: Why uSent always "1", expected it to match sent bytes length - bSent = true; - //FYI: App nRF-MCP doesn't cause onDataSent(), while App nRF-ToolBox:HRM does cause onDataSent() - //TBD: onDataSent() maybe only occuring when confirmed receive by phone, as onDataSent() didn't happen when phone moved out of range. +static volatile bool bNotifySent = false; //Volatile, don't optimize, changes under interrupt control +static volatile unsigned uNotifyBLE; +void Callback_BLE_onNotifySent(unsigned uNotify) //TODO: Consider renaming to onNotifySent(uNotified) +{ // Appears to get called when a Characteristic flagged for Notify is Updated/Sent (not for a characteristic being Read by the app). + uNotifyBLE=uNotify; + DEBUG(" Senti(%u)", uNotifyBLE); //TODO: PR: Why uSent always "1", expected it to match sent bytes length + bNotifySent = true; } void Callback_BLE_onDataWritten(const GattCharacteristicWriteCBParams *pParams) @@ -336,9 +442,16 @@ } else { uint16_t uLen; 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]); + ble.readCharacteristicValue(tHandle, sIOw8.pu, &uLen); //Update Characteristic with new information (Option: Verify length or a checksum first) + vShowIO(); + + bAppControl = true; //Host has taken control of LEDs + fL1level = (((float)sIOw8.s.L1pwm100)/100.0); if(fL1level>1.0){fL1level=1.0;}; fL1pwm=fL1level; //Set LED1 Level + fL2level = (((float)sIOw8.s.L2pwm255)/255.0); if(fL2level>1.0){fL2level=1.0;}; fL2pwm=fL2level; //Set LED1 Level + DEBUG(" L1:%d==%f L2:%d==%f\n", sIOw8.s.L1pwm100, fL1level, sIOw8.s.L2pwm255, fL2level); + //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]); @@ -358,19 +471,19 @@ //x Alert=1: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x01]=Data[]) //x Alert=2: Debug=BLEi: Callback_BLE_onDataWritten(Handle:12, eOp:1, uOffset:0 uLen:1 Data0[0x02]=Data[]) - //Characteristic Status: - DEBUG(" IOw8: Handle:%d Len:%d Initial:[%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(" IOr4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOr4.getValueHandle(), sizeof(pIOr4), pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]); - DEBUG(" IOn4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOn4.getValueHandle(), sizeof(pIOn4), pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]); + //vShowIO(); //Show IO Characteristic Status } 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 + if (tHandle == IOn4.getValueHandle()) { DEBUG("\nBLEi: 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); // 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); + } else { DEBUG("\nBLEi: onUpdates(Handle:%d) Enabled - Characteristic?\n", tHandle); } + + + } // Adopted 20141213 from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_LinkLoss/file/440ee5e8595f/main.cpp @@ -403,7 +516,7 @@ { //static float fTemperature = 0;//-123.456; int32_t i32_temp; - sd_temp_get(&i32_temp); //Read the nRF Internal Temperature (Die in 0.25'C steps, Counting From TBD), TODO:Check Scaling + sd_temp_get(&i32_temp); //Read the nRF Internal Temperature (Die in 0.25'C steps, Offset:TBD), TODO:Check Scaling float fTemperature = (float(i32_temp)/4.0) - 16.0; // Scale&Shift (0.25'C from -16'C?) //{//Force to IEEE format to match needs of Apps like nRF-HTM and nRF-Toolbox:HTM @@ -434,36 +547,39 @@ { 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 + 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 uTicker1++; // Flag event, using counter so can detect missed events } //==========main========== int main(void) -{ +{ + vShowIO(); + 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(" Built UTC:[ %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"); DEBUG("BLE: BluetoothLeGatt: mbed->App: BatteryService->BatteryLevel->EnableNotify(ThreeDownArrows), Also App Acks the send=DEBUG('SentI')\n"); Ticker ticker1; //PR: Timer Object(Structure) - ticker1.attach(CallbackTicker1, 2.0); //PR: Timer Handler, Float=PeriodSeconds + ticker1.attach(CallbackTicker1, 5.0); //PR: Timer Handler, Float=PeriodSeconds (Note BLE default wakes about 50Hz/20msec) //BLE1: Setup BLE Service (and event actions) //TODO: Check for services declared before main() - Is that OK? DEBUG("BLE: Setup BLE\n"); ble.init(); - ble.onDisconnection(Callback_BLE_onDisconnect); - ble.onConnection(Callback_BLE_onConnect); //PR: Not required if no actions enabled, enabled now just for debug printf() - ble.onDataSent(Callback_BLE_onDataSent); - ble.onDataWritten(Callback_BLE_onDataWritten); - ble.onTimeout(Callback_BLE_onTimeout); - ble.onUpdatesEnabled(Callback_BLE_onUpdatesEnabled); + ble.onDisconnection(Callback_BLE_onDisconnect); //PR: Host disconnects, restart advertising, clear any unnecessary functions to save power + ble.onConnection(Callback_BLE_onConnect); //PR: Host connects (Not required if no actions enabled, enabled now just for debug) + ble.onDataSent(Callback_BLE_onNotifySent); //PR: Occurs when a Notify Characteristic has been updated and sent over BLE + ble.onDataWritten(Callback_BLE_onDataWritten); //PR: Occurs when an update to a Characteristic has been received over BLE + ble.onTimeout(Callback_BLE_onTimeout); //PR: ??? Hasn't occured, TODO: Monitor and find out what causes this + ble.onUpdatesEnabled(Callback_BLE_onUpdatesEnabled);//PR: Occurs when host enables notify on a Characteristic that has Notify property set + //ble.onDataRead(Callback_BLE_onDataRead) //TODO: Need a callback for when reading data so can ensure characteristic is fully up to data before it is passed back (For now update periodically) //BLE2: Setup Services (with their initial values and options) DEBUG("BLE: Setup Services\n"); @@ -481,12 +597,10 @@ //UARTService ServiceUART(ble); // pServiceUART = &ServiceUART; - //Start CustomIO Service: + DEBUG("BLE: Setup Custom IO Service\n"); ble.addService(ServiceIO); //Pointer: pServiceIO - DEBUG(" IOw8: Handle:%d Len:%d Initial:[%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(" IOr4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOr4.getValueHandle(), sizeof(pIOr4), pIOr4[0], pIOr4[1], pIOr4[2], pIOr4[3]); - DEBUG(" IOn4: Handle:%d Len:%d Initial:[%02X %02X %02X %02X]\n", IOn4.getValueHandle(), sizeof(pIOn4), pIOn4[0], pIOn4[1], pIOn4[2], pIOn4[3]); - //TODO: Ensure outputs in correct Initial state + vShowIO(); + //TODO: Ensure outputs in correct Initial state //BLE3: Setup advertising // 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. @@ -519,12 +633,12 @@ // = 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 + // Setup InterruptIn for Buttons (coded for nRF51822-mkit polarities on these 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 + B1int.fall(&onB1press); // Button1 Press/fall + B1int.rise(&onB1release); // Button1 Release/rise + B2int.fall(&onB2press); // Button2 Press/fall + 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) @@ -541,23 +655,23 @@ update_batt(); //a vUpdate_IOw8(); - vUpdate_IOr4(); - vUpdate_IOn4(); + //a vUpdate_IOr4(); + //a vUpdate_IOn4(); - DEBUG(" BLE:Wakes:%u,Delta:%u ", u32_wakeevents, u32_wakeevents-u32_wakelast); //For Evaluating Timing + DEBUG(" BLE:Wakes:%04u,Delta:%03u ", u32_wakeevents, u32_wakeevents-u32_wakelast); //For Evaluating Timing u32_wakelast = u32_wakeevents; } 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); + DEBUG(" BLE: Tick while unconnected "); + } else if (bNotifySent){ + bNotifySent=false; //clear flag Notify Characteristic Transmitted + DEBUG(" BLE: Notify(%u) ", uNotifyBLE); } else { //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 + 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 + } } } //========== end of main.cpp ==========