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:
- 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