BLE Lightning sensor for Nordic NRF51822 based module

Dependencies:   AS3935 AS3935_ext BLE_API mbed nRF51822 nrf51_rtc

Revision:
1:a4119049dd99
Parent:
0:371bcac81ea2
Child:
2:e1e638cbf972
diff -r 371bcac81ea2 -r a4119049dd99 main.cpp
--- a/main.cpp	Thu Aug 27 10:14:50 2015 +0000
+++ b/main.cpp	Thu Aug 27 16:18:20 2015 +0000
@@ -1,34 +1,82 @@
 /*
-This example code shows one way of creating a real time clock for the nRF51 without interfering with the mbed library functions
-
-Two methods are created to replace the similarly named C routines:  rtc.time and rtc.set_time.
-If the rtc.time method isn't called frequently (< 512 seconds between calls), a ticker (from the mbed library) is required 
-to keep the time updated.
-
-In this example, LED2 toggles and the time is printed when button1 is pushed.  LED1 will toggle when the time is periodically updated.
-
-references:
-  -- see NRF_RTC_Type in nrf51.h 
-  (e.g.:  http://developer.mbed.org/users/mbed_official/code/mbed-src/file/12a9a5f8fea0/targets/cmsis/TARGET_NORDIC/TARGET_MCU_NRF51822/nrf51.h)
-  -- see "nRF51 Series Reference Manual" chapter on Real Time Counter (chapter 19 in V3.0)
-Note:  RTC2 does not exist, despite what the nrf51 reference manual (V3.0) claims.  The other RTCs (0/1) are used by various other libraries,
-so you can read, but don't write! 
 
 */
 
 #include "mbed.h"
+#include "BLE.h"
 #include "nrf51_rtc.h"
 #include "AS3935_ext.h"
 
+#include "nrf_soc.h" // for internal Thermo sensoer
+
+#define NEED_CONSOLE_OUTPUT 1 /* Set this if you need debug messages on the console;
+                               * it will have an impact on code-size and power consumption. */
+
+#if NEED_CONSOLE_OUTPUT
+Serial  pc(USBTX, USBRX);
+#define DEBUG(...) { pc.printf(__VA_ARGS__); }
+#else
+#define DEBUG(...) /* nothing */
+#endif /* #if NEED_CONSOLE_OUTPUT */
+
+// Prepare BLE device
+BLEDevice  ble;
+const static char  DEVICE_NAME[] = "BLE-LITNING-S";
+
+/* Health Thermometer Service */ 
+/* 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 *));
+
+/* Original Thermometer Service */
+/* with nRF51822 internal thermal sensor */
+uint8_t             internalTempPayload[5] = { 0, 0, 0, 0, 0 };
+
+GattCharacteristic  internalTempChar (GattCharacteristic::UUID_TEMPERATURE_MEASUREMENT_CHAR + 0x2000,
+                                internalTempPayload, sizeof(internalTempPayload), sizeof(internalTempPayload),
+                                GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE);
+GattCharacteristic *itmChars[] = {&internalTempChar, };
+GattService        itmService(GattService::UUID_HEALTH_THERMOMETER_SERVICE + 0x2000, itmChars, 
+                                sizeof(itmChars) / sizeof(GattCharacteristic *));
+
+/* Battery Level Service */
+uint8_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 *));
+
+static const uint16_t uuid16_list[] = {GattService::UUID_HEALTH_THERMOMETER_SERVICE, GattService::UUID_BATTERY_SERVICE, GattService::UUID_HEALTH_THERMOMETER_SERVICE + 0x2000 };
+
+static volatile bool triggerSensorPolling = false; /* set to high periodically to indicate to the main thread that
+                                                    * polling is necessary. */
+static Gap::ConnectionParams_t connectionParams;
+
+uint32_t quick_ieee11073_from_float(float temperature);
+
+void Update_Values();
+
+
+// Prepare LED device
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+
 AS3935_ext Lightning(I2C_SDA0,I2C_SCL0,0x00,P0_23);
 InterruptIn IntLightning(P0_23); //IRQ AS3935
 
 // used for the example only, not required for rtc use
-DigitalOut rtc_update_led(LED1); // toggle when update_rtc is called
-DigitalOut button_push_led(LED2); // toggle when button1 is pushed
 DigitalIn  button1(BUTTON1);  // used to trigger the time report
 InterruptIn button1Press(BUTTON1);
-Serial pc(USBTX,USBRX);
 
 
 time_t example_time() {
@@ -50,15 +98,15 @@
 
     char date[24];
     strftime(date,sizeof(date),"%H:%M:%S on %m/%d/%G",init_timeinfo);
-    pc.printf("Initial time set is %s.\r\n",date);
+    DEBUG("Initial time set is %s.\r\n",date);
     
     // compute the proper value for time in time_t type
     rawtime = mktime(init_timeinfo);
     return rawtime;
 }
+
 void print_time() {
     // called when a button is pushed, this prints the current time to the USB-connected console
-    button_push_led = !button_push_led; 
     
     time_t rawtime=rtc.time();
     
@@ -67,16 +115,18 @@
     timeinfo = localtime(&rawtime);
     char date[24];
     strftime(date,sizeof(date),"%H:%M:%S on %m/%d/%G",timeinfo);
-    pc.printf("The current time is %s.(%d)\r\n",date,rawtime);
+    DEBUG("The current time is %s.(%d)\r\n",date,rawtime);
 }
 
-// (probably) required for rtc use
-void update_rtc() {
+void periodic_update() {
     // for use as interrupt routine, to insure that RTC is updated periodically
     //  ...if rtc is not read before the underlying counter rolls over (typically 512 seconds), the RTC value will be wrong
     //  ...ideally this would be done as part of the nrf51_rtc method, but I couldn't get it to behave (see nrf51_rtc.cpp for details)
     rtc.time();
-    rtc_update_led = !rtc_update_led;
+    Lightning.lightningDistanceKm();
+    led1 = !led1;
+
+    triggerSensorPolling = true;
     // print_time();
 }
 
@@ -95,43 +145,64 @@
     OriginInt = Lightning.interruptSource();
     distance = Lightning.lightningDistanceKm();
 
-    if (OriginInt == 1) { 
-        pc.printf("%24s : Noise level too high. %d km\r\n",date,distance);
+    if (OriginInt == 1) {
+        led2 = !led2; 
+        DEBUG("%24s : Noise level too high. %d km\r\n",date,distance);
         }
     if (OriginInt == 4) {
-        pc.printf("%24s : Disturber detected. %d km\r\n",date,distance);
+        led2 = !led2;
+        DEBUG("%24s : Disturber detected. %d km\r\n",date,distance);
         }
     if (OriginInt == 8) {
-        distance = Lightning.lightningDistanceKm();
-        pc.printf("%24s : Lightning interrupt %d km\r\n",date,distance);
+        led2 = !led2; 
+        DEBUG("%24s : Lightning interrupt %d km\r\n",date,distance);
         }
 }
 
+void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)    // Mod
+{
+    DEBUG("Disconnected handle %u!\n\r", handle);
+    DEBUG("Restarting the advertising process\n\r");
+    led2 = 0;
+    ble.gap().startAdvertising();
+}
+
+void onConnectionCallback(const Gap::ConnectionCallbackParams_t *params)
+{
+    DEBUG("connected. Got handle %u\r\n", params->handle);
+
+    connectionParams.slaveLatency = 1;
+    led2 = 1;
+    if (ble.gap().updateConnectionParams(params->handle, &connectionParams) != BLE_ERROR_NONE) {
+        DEBUG("failed to update connection paramter\r\n");
+    }
+    
+}
 
 int main(void)
 {
-    rtc_update_led=0;
-    button_push_led=0;
+    led1=0;
+    led2=0;
     int hz=0;
 
     //initialisations
-    pc.printf("reset\r\n");
+    DEBUG("reset\r\n");
     Lightning.reset();
-    pc.printf("setTuneCap as 5\r\n");
+    DEBUG("setTuneCap as 5\r\n");
     Lightning.setTuneCap(5); // Tuning Parameter
-    pc.printf("powerup\r\n");
+    DEBUG("powerup\r\n");
     Lightning.powerUp();
-    pc.printf("setIndoors\r\n");
-    Lightning.setIndoors(); // Set Device into Indoor mode
+    DEBUG("setOutdoors\r\n");
+    Lightning.setOutdoors(); // Set Device into Outdoor mode
  
-    pc.printf("Auto Calibration Start\r\n");
+    DEBUG("Auto Calibration Start\r\n");
     float minerr = 100;
     int fincap = 7;
     for(int i=0;i<16;i++) {
         Lightning.setTuneCap(i); // Tuning Parameter
         hz = Lightning.MeasureLCOFreq();
         float err = (hz-500000.)/500000.*100.;
-        pc.printf("%d : hz=%10d Hz (%5.2f%%)\r\n",i,hz,err);
+        DEBUG("%d : hz=%10d Hz (%5.2f%%)\r\n",i,hz,err);
         if ( abs(err) < minerr ) {
             minerr = abs(err);
             fincap = i;
@@ -141,25 +212,90 @@
     wait_ms(100);
     hz = Lightning.MeasureLCOFreq();
     float err = (hz-500000.)/500000.*100.;
-    pc.printf("Final %d : hz=%10d Hz (%5.2f%%)\r\n",fincap,hz,err);
+    DEBUG("Final %d : hz=%10d Hz (%5.2f%%)\r\n",fincap,hz,err);
     
-    pc.printf("Auto Calibration finished\r\n");
+    DEBUG("Auto Calibration finished\r\n");
     
     // user selectable, any time < 512 seconds is OK
     #define PERIODIC_UPDATE 1
     Ticker rtc_ticker;
-    rtc_ticker.attach(&update_rtc, PERIODIC_UPDATE); // update the time regularly
+    rtc_ticker.attach(&periodic_update, PERIODIC_UPDATE);
     
     time_t initial_time = example_time();
     rtc.set_time(initial_time);
     
     button1Press.fall(&print_time);  // when button1 is pressed, this calls rtc.time() and prints it
     
-    // Lightning.setIndoors(); // Set Device into Indoor mode
-    Lightning.setOutdoors(); // Set Device into Outdoor mode
     IntLightning.rise(&DetectLightning);
 
+    ble.init();
+    ble.gap().onDisconnection(disconnectionCallback);
+    ble.gap().onConnection(onConnectionCallback);
+
+    ble.gap().getPreferredConnectionParams(&connectionParams);
+
+    /* setup advertising */
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t*)uuid16_list, sizeof(uuid16_list));
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_THERMOMETER);
+
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
+
+    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+    ble.gap().setAdvertisingInterval(160); /* 100ms; in multiples of 0.625ms. */
+    ble.gap().startAdvertising();
+
+    ble.gattServer().addService(htmService);
+    ble.gattServer().addService(battService);
+
+
     while (true) {
-        sleep();
+        if (triggerSensorPolling) {
+            triggerSensorPolling = false;
+
+            Update_Values();
+            
+        } else {
+            ble.waitForEvent();
+        }
     }
 }
+
+void Update_Values()
+{
+    /* Update the temperature. Note that we need to convert to an ieee11073 format float. */
+
+    int32_t p_temp;
+    sd_temp_get(&p_temp);
+    float temperature = float(p_temp)/4.;
+    temperature -= 14.; // It should be changed device by device. 
+           
+    // DEBUG("temp:%f\n\r", temperature);
+    uint32_t temp_ieee11073 = quick_ieee11073_from_float(temperature);
+    memcpy(thermTempPayload+1, &temp_ieee11073, 4);
+
+    /* Battery Service Update */
+    /* Update battery level */
+    //ble.getGattServer().updateValue(battLevel.handle, (uint8_t*)&batt, sizeof(batt));
+    /* Decrement the battery level. */
+    batt <=50 ? batt=100 : batt--;;
+    bpm2[0] = batt;
+
+    if (ble.gap().getState().connected ) {
+        ble.gattServer().write(tempChar.getValueAttribute().getHandle(), thermTempPayload, sizeof(thermTempPayload));
+        ble.gattServer().write(battLevel.getValueAttribute().getHandle(), (uint8_t *)&batt, sizeof(batt));
+    }
+}
+
+/**
+ * @brief A very quick conversion between a float temperature and 11073-20601 FLOAT-Type.
+ * @param temperature The temperature as a float.
+ * @return The temperature in 11073-20601 FLOAT-Type format.
+ */
+uint32_t quick_ieee11073_from_float(float temperature)
+{
+    uint8_t  exponent = 0xFE; //exponent is -2
+    uint32_t mantissa = (uint32_t)(temperature*100);
+    
+    return ( ((uint32_t)exponent) << 24) | mantissa;
+}