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:
3:a98203f84063
Parent:
2:c77c2b06d604
Child:
4:976394791d7a
--- a/main.cpp	Sat Dec 13 14:14:39 2014 +0000
+++ b/main.cpp	Sat Dec 13 23:45:32 2014 +0000
@@ -81,13 +81,17 @@
 //==========BLE==========
 BLEDevice   ble;
 const static char     pcDeviceName[]    = "blePRv04"; //PR: Why can App nRF-MCP modify this even though flagged as Const, maybe only temporary mod till App restarts?
+
+//UUID List by Advertised 16bit UUID list (PartA)
 static const uint16_t uuid16_list[]     = { //Service List (Pre-defined standard 16bit services)
+    //*This list seems different from that shown in App:nRF-MCP, 
     //BLE_UUID_GAP  UUID_GENERIC_ACCESS                 //0x1800    //Included by Default, DeviceName, Appearance, PreferredConnectionParam
     //BLE_UUID_GATT UUID_GENERIC ATTRIBUTE              //0x1801    //Included by Default, ServiceChanged, 
+    GattService::UUID_HEALTH_THERMOMETER_SERVICE,       //0x1809    //HTM (Might need to be first for nRF App)
     GattService::UUID_BATTERY_SERVICE,                  //0x180F    //BatteryLevel
     GattService::UUID_DEVICE_INFORMATION_SERVICE,       //0x180A    //sManufacturer, sModelNumber, sSerialNumber, sHWver, sFWver, sSWver
     GattService::UUID_HEART_RATE_SERVICE,               //0x180D    //HRM, BodyLocation, ControlPoint
-    //GattService::UUID_HEALTH_THERMOMETER_SERVICE,     //0x1809    //HTM
+//    GattService::UUID_HEALTH_THERMOMETER_SERVICE,       //0x1809    //HTM
     //x GattService::UUID_DFU,                          //0x1530 - See UARTServiceShortUUID in BLE_API:DFUService.cpp  //
     //x GattService::UARTService,                       //0x0001 - See DFUServiceShortUUID  in BLE_API:UARTService.cpp //
     //GattService::UUID_ALERT_NOTIFICATION_SERVICE,     = 0x1811,
@@ -101,6 +105,7 @@
  };
  
 //========== Prep UUID list (before main()) ==========
+//UUID List by Advertised 128bit UUID list (PartA, ToDo:)
 // Gatt characteristic and service UUIDs - Readable to UUID
 const UUID stringToUUID(const char* str) {
     uint8_t array[16] = {0x55,0x55,0x55,0x55, 0x55,0x55,0x55,0x55, 0x55,0x55,0x55,0x55, 0x55,0x55,0x55,0x55};//Prefill 'U' so fully specified even short string
@@ -124,30 +129,6 @@
 const UUID UUID_GATT_CHARACTERISTIC_A2          = stringToUUID("com.test.charA2");
 const UUID UUID_GATT_CHARACTERISTIC_SHORT       = stringToUUID("com.short");
 
-//==========Internal Temperature==========
-//Adopted From: https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/
-// Service:  https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.health_thermometer.xml
-// HTM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml
-uint8_t             thermTempPayload[5] = { 0, 0, 0, 0, 0 };
-GattCharacteristic  tempChar (GattCharacteristic::UUID_TEMPERATURE_MEASUREMENT_CHAR,
-                                thermTempPayload, sizeof(thermTempPayload), sizeof(thermTempPayload),
-                                GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE);
-GattCharacteristic *htmChars[] = {&tempChar, };
-GattService        htmService(GattService::UUID_HEALTH_THERMOMETER_SERVICE, htmChars, 
-                                sizeof(htmChars) / sizeof(GattCharacteristic *));
-
-//==========Battery==========
-//Adopted From: https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/
-///int8_t            batt = 98;     /* Battery level */
-//uint8_t            read_batt = 0; /* Variable to hold battery level reads */
-//static uint8_t bpm2[1] = {batt};
-//GattCharacteristic battLevel   ( GattCharacteristic::UUID_BATTERY_LEVEL_CHAR, bpm2, sizeof(bpm2), sizeof(bpm2),
-//                                 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY |
-//                                 GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ);
-//GattCharacteristic *battChars[] = {&battLevel, };
-//GattService        battService(GattService::UUID_BATTERY_SERVICE, battChars, sizeof(battChars) / sizeof(GattCharacteristic *));
-
-
 //==========Functions:Timer==========
 static volatile bool  b_Ticker1 = false;//Volatile, don't optimize, changes under interrupt control
 void CallbackTicker1(void)
@@ -240,11 +221,10 @@
     DEBUG("BLE: Connect App for Data: nRF-MCP, nRF-Toolbox:HRM, etc.\n");
 
     Ticker ticker1;                             //PR: Timer Object(Structure)
-    //ticker1.attach(CallbackTicker1, 1.0);       //PR: Timer Handler, Float=PeriodSeconds
     ticker1.attach(CallbackTicker1, 2.0);       //PR: Timer Handler, Float=PeriodSeconds
-    //ticker1.attach(CallbackTicker1, 5.0);       //PR: Timer Handler, Float=PeriodSeconds
 
-    //Initialize BLE Service (and event actions)
+    //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()
@@ -256,50 +236,95 @@
 //ble_error_t readCharacteristicValue  ( uint16_t  handle,    uint8_t *const  buffer,    uint16_t *const  lengthP  ) 
 //ble_error_t  updateCharacteristicValue (uint16_t handle, const uint8_t *value, uint16_t size, bool localOnly=false) 
 
-    /* Setup primary service. */
-    uint8_t u8LoopCounter = 100;
-    HeartRateService hrService(ble, u8LoopCounter, HeartRateService::LOCATION_FINGER); //Start the service for BLE:HRM
-
-    /* Setup auxiliary services. */
-    BatteryService   battery(ble); //Start the service for BLE:Battery
-    DeviceInformationService deviceInfo(ble, "Maker", pcDeviceName, "sn1234", "hw00", "fw00", "sw00");//Start the service for BLE:DeviceInfo
-    //                              (BLEDevice), pcManufacturer, pcModelNumber, pcSerialNumber, pcHWver, pcFWver, pcSWver
-
-    /* Setup advertising. */
+//UUID List by Services that are setup (Maybe this is what nRF-MCP discovers?)
+    //BLE2: Setup Services
+    DEBUG("BLE: Setup Services\n");
+    HeartRateService            hrmService(ble, (uint8_t)111, HeartRateService::LOCATION_FINGER);
+    HealthThermometerService    htmService(ble, 30.0, HealthThermometerService::LOCATION_EAR); 
+    BatteryService              battService(ble, 11);//Declare the service for BLE:BATTERY
+    DeviceInformationService    deviceInfo(ble, "Maker", pcDeviceName, "sn1234", "hw00", "fw00", "sw00");
+                                //(BLEDevice), pcManufacturer, pcModelNumber, pcSerialNumber, pcHWver, pcFWver, pcSWver
+    //BLE3: Setup advertising
+    DEBUG("BLE: Setup Advertising\n");
     ble.setAdvertisingInterval(Gap::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(1000)); //PR: Advertise 1sec (1Hz)
     ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); //PR: TODO
     ble.accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); //PR: BLE Only, Options(LE_GENERAL_DISCOVERABLE/LE_LIMITED_DISCOVERABLE)
+
+//UUID List by Advertised 16bit UUID list (PartB)
     ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); //PR: Might need to change for Custom Services/Characteristics
-    ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);//PR: TODO: Change to: UNKNOWN or custom
+
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);//PR: Add Heart Rate
+    ble.accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER);  //PR: Add Thermometer
     ble.accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)pcDeviceName, sizeof(pcDeviceName));//PR: Product?
+    //Thermometer: ble.setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+    //Thermometer: ble.setAdvertisingInterval(160); /* 100ms; in multiples of 0.625ms. */
     ble.startAdvertising();
 
     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()
-
-            /* Handle sensor polling == Simulate Data */
-            u8LoopCounter++;
-            if (u8LoopCounter >= 175) {
-                u8LoopCounter = 100;
-                DEBUG("BLE: HRM Rollover175->100 ", u8LoopCounter);
-            }
-            DEBUG("BLE: HRM:%u, Wakes:%u Delta:%d ", u8LoopCounter, u32_wakeevents, u32_wakeevents-u32_wakelast); u32_wakelast=u32_wakeevents; 
-            hrService.updateHeartRate(u8LoopCounter);
+  
+            // Read Sensors, and update matching Service Characteristics (only if connected)
+            uint8_t update_hrm(void); //prototype
+            float   update_htm(void); //prototype
+            uint8_t update_batt(void);//prototype
+            hrmService.updateHeartRate( update_hrm() );
+            htmService.updateTemperature( update_htm() );
+            battService.updateBatteryLevel( update_batt() );
+            
+            DEBUG("BLE: Wakes:%u Delta:%d ", 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()
-            DEBUG("BLE: Tick while unconnected ", u8LoopCounter);
+            DEBUG("BLE: Tick while unconnected ");
         } else if (bSent){
             bSent=false; //clear flag
-            //DEBUG("BLE:Sent %ubytes ", uSentBLE);
+            //DEBUG("BLE: Sent %ubytes ", uSentBLE);
         } else {
-            //DEBUG("BLE:Wait for Event\n\r"); //x Debug output here causes unnecessary wakes resulting in endless awakes.    
+            //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
             u32_wakeevents++;   //PR: Count events for frequency monitoring (20141207PR: nRF51822 mbed HRM = 50Hz)
         }
     }
 }
+
+//==========HRM==========
+//Adopted 2014Dec from http://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/
+uint8_t update_hrm(void)//(bool bInit)
+{
+    static uint8_t u8_hrm = 100;
+    u8_hrm++;
+    if (u8_hrm >= 175) {
+        u8_hrm = 100;
+        DEBUG("BLE: HRM Rollover175->100 ");
+    }
+    DEBUG("[HRM:%d]", u8_hrm);   
+    return(u8_hrm);
+}
+//==========HTM:Internal Temperature==========
+//Adopted 2014Dec from: https://developer.mbed.org/users/takafuminaka/code/BLE_HTM_by_InTempSensr/
+// Service:  https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.health_thermometer.xml
+// HTM Char: https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml
+
+//****Although nRF-MCP displays float OK, the HTM Apps don't, its possible IEEE format required like in the original code:BLE_HTM_by_InTempSensr 
+float update_htm(void)
+{
+    //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
+    float fTemperature = (float(i32_temp)/4.0) - 16.0;   // Scale&Shift (0.25'C from -16'C?)
+    DEBUG("[HTMi:%d HTMf:%f]", i32_temp, fTemperature);
+    return(fTemperature);
+}
+//==========Battery==========
+uint8_t update_batt(void)
+{
+    static uint8_t u8_BattPercent=33; //Level: 0..100% 
+    u8_BattPercent <= 50 ? u8_BattPercent=100 : u8_BattPercent--; // Simulate Battery Decay
+    DEBUG("[BATT:%d%%]", u8_BattPercent);
+    return(u8_BattPercent);
+}
+//========== end ==========
\ No newline at end of file