BLE Lightning sensor for Nordic NRF51822 based module
Dependencies: AS3935 AS3935_ext BLE_API mbed nRF51822 nrf51_rtc
Diff: main.cpp
- Revision:
- 1:a4119049dd99
- Parent:
- 0:371bcac81ea2
- Child:
- 2:e1e638cbf972
--- 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; +}