Naresh Krish
/
itracker-mbed-os-example-blegatt
iTracker mbed os sample to show how to user the BLE API to expose sensor values (float and ints)
main.cpp@0:e4fe77192b73, 2018-02-12 (annotated)
- Committer:
- knaresh89
- Date:
- Mon Feb 12 06:41:24 2018 +0000
- Revision:
- 0:e4fe77192b73
iTracker mbed os sample to show how to user the BLE API to expose sensor values (float and ints)
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
knaresh89 | 0:e4fe77192b73 | 1 | #include "mbed.h" |
knaresh89 | 0:e4fe77192b73 | 2 | #include "ble/BLE.h" |
knaresh89 | 0:e4fe77192b73 | 3 | #include "SEGGER_RTT.h" |
knaresh89 | 0:e4fe77192b73 | 4 | #include "BME280_SPI.h" |
knaresh89 | 0:e4fe77192b73 | 5 | |
knaresh89 | 0:e4fe77192b73 | 6 | //For float values make sure you create a wrapper class to handle the exponent and mantissa properly |
knaresh89 | 0:e4fe77192b73 | 7 | struct ValueBytes { |
knaresh89 | 0:e4fe77192b73 | 8 | static const unsigned OFFSET_OF_FLAGS = 0; |
knaresh89 | 0:e4fe77192b73 | 9 | static const unsigned OFFSET_OF_VALUE = OFFSET_OF_FLAGS + sizeof(uint8_t); |
knaresh89 | 0:e4fe77192b73 | 10 | static const unsigned SIZEOF_VALUE_BYTES = sizeof(uint8_t) + sizeof(float); |
knaresh89 | 0:e4fe77192b73 | 11 | |
knaresh89 | 0:e4fe77192b73 | 12 | static const unsigned UNITS_FLAG_POS = 0; |
knaresh89 | 0:e4fe77192b73 | 13 | static const unsigned TIMESTAMP_FLAG_POS = 1; |
knaresh89 | 0:e4fe77192b73 | 14 | static const unsigned TYPE_FLAG_POS = 2; |
knaresh89 | 0:e4fe77192b73 | 15 | |
knaresh89 | 0:e4fe77192b73 | 16 | static const uint8_t UNITS_UNIT1 = 0; //add as many possible units that the value byte can hold |
knaresh89 | 0:e4fe77192b73 | 17 | static const uint8_t UNITS_UNIT2 = 1; |
knaresh89 | 0:e4fe77192b73 | 18 | |
knaresh89 | 0:e4fe77192b73 | 19 | ValueBytes(float initialValue) : bytes() { |
knaresh89 | 0:e4fe77192b73 | 20 | /* Assumption: values are expressed in some units of mesurement */ |
knaresh89 | 0:e4fe77192b73 | 21 | bytes[OFFSET_OF_FLAGS] = (UNITS_UNIT1 << UNITS_FLAG_POS) | |
knaresh89 | 0:e4fe77192b73 | 22 | (false << TIMESTAMP_FLAG_POS) | |
knaresh89 | 0:e4fe77192b73 | 23 | (false << TYPE_FLAG_POS); |
knaresh89 | 0:e4fe77192b73 | 24 | updateValue(initialValue); |
knaresh89 | 0:e4fe77192b73 | 25 | } |
knaresh89 | 0:e4fe77192b73 | 26 | |
knaresh89 | 0:e4fe77192b73 | 27 | void updateValue(float temp) { |
knaresh89 | 0:e4fe77192b73 | 28 | uint32_t temp_ieee11073 = quick_ieee11073_from_float(temp); |
knaresh89 | 0:e4fe77192b73 | 29 | memcpy(&bytes[OFFSET_OF_VALUE], &temp_ieee11073, sizeof(float)); |
knaresh89 | 0:e4fe77192b73 | 30 | } |
knaresh89 | 0:e4fe77192b73 | 31 | |
knaresh89 | 0:e4fe77192b73 | 32 | uint8_t *getPointer(void) { |
knaresh89 | 0:e4fe77192b73 | 33 | return bytes; |
knaresh89 | 0:e4fe77192b73 | 34 | } |
knaresh89 | 0:e4fe77192b73 | 35 | |
knaresh89 | 0:e4fe77192b73 | 36 | const uint8_t *getPointer(void) const { |
knaresh89 | 0:e4fe77192b73 | 37 | return bytes; |
knaresh89 | 0:e4fe77192b73 | 38 | } |
knaresh89 | 0:e4fe77192b73 | 39 | |
knaresh89 | 0:e4fe77192b73 | 40 | private: |
knaresh89 | 0:e4fe77192b73 | 41 | /** |
knaresh89 | 0:e4fe77192b73 | 42 | * @brief A very quick conversion between a float vale and 11073-20601 FLOAT-Type. |
knaresh89 | 0:e4fe77192b73 | 43 | * @param value The sensor value as a float. |
knaresh89 | 0:e4fe77192b73 | 44 | * @return The value in 11073-20601 FLOAT-Type format. |
knaresh89 | 0:e4fe77192b73 | 45 | */ |
knaresh89 | 0:e4fe77192b73 | 46 | uint32_t quick_ieee11073_from_float(float value) { |
knaresh89 | 0:e4fe77192b73 | 47 | uint8_t exponent = 0xFE; //Exponent is -2 |
knaresh89 | 0:e4fe77192b73 | 48 | uint32_t mantissa = (uint32_t)(value * 100); |
knaresh89 | 0:e4fe77192b73 | 49 | |
knaresh89 | 0:e4fe77192b73 | 50 | return (((uint32_t)exponent) << 24) | mantissa; |
knaresh89 | 0:e4fe77192b73 | 51 | } |
knaresh89 | 0:e4fe77192b73 | 52 | |
knaresh89 | 0:e4fe77192b73 | 53 | private: |
knaresh89 | 0:e4fe77192b73 | 54 | /* First byte: 8-bit flags. Second field is a float holding the value. */ |
knaresh89 | 0:e4fe77192b73 | 55 | /* See https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.temperature_measurement.xml */ |
knaresh89 | 0:e4fe77192b73 | 56 | uint8_t bytes[SIZEOF_VALUE_BYTES]; |
knaresh89 | 0:e4fe77192b73 | 57 | }; |
knaresh89 | 0:e4fe77192b73 | 58 | |
knaresh89 | 0:e4fe77192b73 | 59 | ValueBytes valueBytes(0.0f); |
knaresh89 | 0:e4fe77192b73 | 60 | ReadOnlyGattCharacteristic<ValueBytes> tempMeasurement(GattCharacteristic::UUID_TEMPERATURE_MEASUREMENT_CHAR, (ValueBytes *)valueBytes.getPointer(), GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY); //characteristics that can be used to store a float value |
knaresh89 | 0:e4fe77192b73 | 61 | |
knaresh89 | 0:e4fe77192b73 | 62 | /**Function to update the BLE characteristics value that holds a float value |
knaresh89 | 0:e4fe77192b73 | 63 | * @param value the float value to update |
knaresh89 | 0:e4fe77192b73 | 64 | */ |
knaresh89 | 0:e4fe77192b73 | 65 | void updateCharacteristic(float value) |
knaresh89 | 0:e4fe77192b73 | 66 | { |
knaresh89 | 0:e4fe77192b73 | 67 | if (BLE::Instance(BLE::DEFAULT_INSTANCE).getGapState().connected) { |
knaresh89 | 0:e4fe77192b73 | 68 | valueBytes.updateValue(value); |
knaresh89 | 0:e4fe77192b73 | 69 | BLE::Instance(BLE::DEFAULT_INSTANCE).gattServer().write(tempMeasurement.getValueHandle(), valueBytes.getPointer(), sizeof(ValueBytes)); |
knaresh89 | 0:e4fe77192b73 | 70 | } |
knaresh89 | 0:e4fe77192b73 | 71 | } |
knaresh89 | 0:e4fe77192b73 | 72 | |
knaresh89 | 0:e4fe77192b73 | 73 | |
knaresh89 | 0:e4fe77192b73 | 74 | |
knaresh89 | 0:e4fe77192b73 | 75 | |
knaresh89 | 0:e4fe77192b73 | 76 | DigitalOut led(LED1, 1); |
knaresh89 | 0:e4fe77192b73 | 77 | uint16_t customServiceUUID = 0xA000; |
knaresh89 | 0:e4fe77192b73 | 78 | uint16_t readCharUUID = 0xA001; |
knaresh89 | 0:e4fe77192b73 | 79 | uint16_t writeCharUUID = 0xA002; |
knaresh89 | 0:e4fe77192b73 | 80 | |
knaresh89 | 0:e4fe77192b73 | 81 | const static char DEVICE_NAME[] = "iTracker"; // change this |
knaresh89 | 0:e4fe77192b73 | 82 | static const uint16_t uuid16_list[] = {0xFFFF}; //Custom UUID, FFFF is reserved for development |
knaresh89 | 0:e4fe77192b73 | 83 | |
knaresh89 | 0:e4fe77192b73 | 84 | |
knaresh89 | 0:e4fe77192b73 | 85 | /* Set Up custom Characteristics */ |
knaresh89 | 0:e4fe77192b73 | 86 | static uint8_t readValue[10] = {0}; |
knaresh89 | 0:e4fe77192b73 | 87 | ReadOnlyArrayGattCharacteristic<uint8_t, sizeof(readValue)> readChar(readCharUUID, readValue); |
knaresh89 | 0:e4fe77192b73 | 88 | |
knaresh89 | 0:e4fe77192b73 | 89 | static uint8_t writeValue[10] = {0}; |
knaresh89 | 0:e4fe77192b73 | 90 | WriteOnlyArrayGattCharacteristic<uint8_t, sizeof(writeValue)> writeChar(writeCharUUID, writeValue); |
knaresh89 | 0:e4fe77192b73 | 91 | |
knaresh89 | 0:e4fe77192b73 | 92 | /* Set up custom service */ |
knaresh89 | 0:e4fe77192b73 | 93 | GattCharacteristic *characteristics[] = {&readChar, &writeChar}; |
knaresh89 | 0:e4fe77192b73 | 94 | GattService customService(customServiceUUID, characteristics, sizeof(characteristics) / sizeof(GattCharacteristic *)); |
knaresh89 | 0:e4fe77192b73 | 95 | |
knaresh89 | 0:e4fe77192b73 | 96 | |
knaresh89 | 0:e4fe77192b73 | 97 | /* |
knaresh89 | 0:e4fe77192b73 | 98 | * Restart advertising when phone app disconnects |
knaresh89 | 0:e4fe77192b73 | 99 | */ |
knaresh89 | 0:e4fe77192b73 | 100 | void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *) |
knaresh89 | 0:e4fe77192b73 | 101 | { |
knaresh89 | 0:e4fe77192b73 | 102 | BLE::Instance(BLE::DEFAULT_INSTANCE).gap().startAdvertising(); |
knaresh89 | 0:e4fe77192b73 | 103 | } |
knaresh89 | 0:e4fe77192b73 | 104 | |
knaresh89 | 0:e4fe77192b73 | 105 | /* |
knaresh89 | 0:e4fe77192b73 | 106 | * Handle writes to writeCharacteristic |
knaresh89 | 0:e4fe77192b73 | 107 | */ |
knaresh89 | 0:e4fe77192b73 | 108 | void writeCharCallback(const GattWriteCallbackParams *params) |
knaresh89 | 0:e4fe77192b73 | 109 | { |
knaresh89 | 0:e4fe77192b73 | 110 | /* Check to see what characteristic was written, by handle */ |
knaresh89 | 0:e4fe77192b73 | 111 | if(params->handle == writeChar.getValueHandle()) { |
knaresh89 | 0:e4fe77192b73 | 112 | /* toggle LED if only 1 byte is written */ |
knaresh89 | 0:e4fe77192b73 | 113 | if(params->len == 1) { |
knaresh89 | 0:e4fe77192b73 | 114 | //led = params->data[0]; |
knaresh89 | 0:e4fe77192b73 | 115 | (params->data[0] == 0x00) ? SEGGER_RTT_printf(0, "led on\n\r") : SEGGER_RTT_printf(0, "led off\n\r"); // print led toggle |
knaresh89 | 0:e4fe77192b73 | 116 | } |
knaresh89 | 0:e4fe77192b73 | 117 | /* Print the data if more than 1 byte is written */ |
knaresh89 | 0:e4fe77192b73 | 118 | else { |
knaresh89 | 0:e4fe77192b73 | 119 | SEGGER_RTT_printf(0, "Data received: length = %d, data = 0x",params->len); |
knaresh89 | 0:e4fe77192b73 | 120 | for(int x=0; x < params->len; x++) { |
knaresh89 | 0:e4fe77192b73 | 121 | SEGGER_RTT_printf(0, "%x", params->data[x]); |
knaresh89 | 0:e4fe77192b73 | 122 | } |
knaresh89 | 0:e4fe77192b73 | 123 | SEGGER_RTT_printf(0, "\n\r"); |
knaresh89 | 0:e4fe77192b73 | 124 | } |
knaresh89 | 0:e4fe77192b73 | 125 | /* Update the readChar with the value of writeChar */ |
knaresh89 | 0:e4fe77192b73 | 126 | BLE::Instance(BLE::DEFAULT_INSTANCE).gattServer().write(readChar.getValueHandle(), params->data, params->len); |
knaresh89 | 0:e4fe77192b73 | 127 | } |
knaresh89 | 0:e4fe77192b73 | 128 | } |
knaresh89 | 0:e4fe77192b73 | 129 | /* |
knaresh89 | 0:e4fe77192b73 | 130 | * Initialization callback |
knaresh89 | 0:e4fe77192b73 | 131 | */ |
knaresh89 | 0:e4fe77192b73 | 132 | void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) |
knaresh89 | 0:e4fe77192b73 | 133 | { |
knaresh89 | 0:e4fe77192b73 | 134 | BLE &ble = params->ble; |
knaresh89 | 0:e4fe77192b73 | 135 | ble_error_t error = params->error; |
knaresh89 | 0:e4fe77192b73 | 136 | |
knaresh89 | 0:e4fe77192b73 | 137 | if (error != BLE_ERROR_NONE) { |
knaresh89 | 0:e4fe77192b73 | 138 | return; |
knaresh89 | 0:e4fe77192b73 | 139 | } |
knaresh89 | 0:e4fe77192b73 | 140 | |
knaresh89 | 0:e4fe77192b73 | 141 | ble.gap().onDisconnection(disconnectionCallback); |
knaresh89 | 0:e4fe77192b73 | 142 | ble.gattServer().onDataWritten(writeCharCallback); |
knaresh89 | 0:e4fe77192b73 | 143 | |
knaresh89 | 0:e4fe77192b73 | 144 | /* Setup advertising */ |
knaresh89 | 0:e4fe77192b73 | 145 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); // BLE only, no classic BT |
knaresh89 | 0:e4fe77192b73 | 146 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); // advertising type |
knaresh89 | 0:e4fe77192b73 | 147 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME)); // add name |
knaresh89 | 0:e4fe77192b73 | 148 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); // UUID's broadcast in advertising packet |
knaresh89 | 0:e4fe77192b73 | 149 | ble.gap().setAdvertisingInterval(100); // 100ms. |
knaresh89 | 0:e4fe77192b73 | 150 | |
knaresh89 | 0:e4fe77192b73 | 151 | /* Add our custom service */ |
knaresh89 | 0:e4fe77192b73 | 152 | ble.addService(customService); |
knaresh89 | 0:e4fe77192b73 | 153 | |
knaresh89 | 0:e4fe77192b73 | 154 | /* Start advertising */ |
knaresh89 | 0:e4fe77192b73 | 155 | ble.gap().startAdvertising(); |
knaresh89 | 0:e4fe77192b73 | 156 | } |
knaresh89 | 0:e4fe77192b73 | 157 | |
knaresh89 | 0:e4fe77192b73 | 158 | /* |
knaresh89 | 0:e4fe77192b73 | 159 | * Main loop |
knaresh89 | 0:e4fe77192b73 | 160 | */ |
knaresh89 | 0:e4fe77192b73 | 161 | int main(void) |
knaresh89 | 0:e4fe77192b73 | 162 | { |
knaresh89 | 0:e4fe77192b73 | 163 | /* initialize stuff */ |
knaresh89 | 0:e4fe77192b73 | 164 | SEGGER_RTT_printf(0, "\n\r********* Starting Main Loop *********\n\r"); |
knaresh89 | 0:e4fe77192b73 | 165 | |
knaresh89 | 0:e4fe77192b73 | 166 | BLE& ble = BLE::Instance(BLE::DEFAULT_INSTANCE); |
knaresh89 | 0:e4fe77192b73 | 167 | ble.init(bleInitComplete); |
knaresh89 | 0:e4fe77192b73 | 168 | |
knaresh89 | 0:e4fe77192b73 | 169 | /* SpinWait for initialization to complete. This is necessary because the |
knaresh89 | 0:e4fe77192b73 | 170 | * BLE object is used in the main loop below. */ |
knaresh89 | 0:e4fe77192b73 | 171 | while (ble.hasInitialized() == false) { |
knaresh89 | 0:e4fe77192b73 | 172 | /* spin loop */ |
knaresh89 | 0:e4fe77192b73 | 173 | } |
knaresh89 | 0:e4fe77192b73 | 174 | |
knaresh89 | 0:e4fe77192b73 | 175 | BME280_SPI sensor(p3, p5, p4, p2); // mosi, miso, sclk, cs |
knaresh89 | 0:e4fe77192b73 | 176 | |
knaresh89 | 0:e4fe77192b73 | 177 | /*while (true) { |
knaresh89 | 0:e4fe77192b73 | 178 | SEGGER_RTT_printf(0, "%2.2f degC, %04.2f hPa, %2.2f %%\n", sensor.getTemperature(), sensor.getPressure(), sensor.getHumidity()); |
knaresh89 | 0:e4fe77192b73 | 179 | wait(5.0); |
knaresh89 | 0:e4fe77192b73 | 180 | }*/ |
knaresh89 | 0:e4fe77192b73 | 181 | |
knaresh89 | 0:e4fe77192b73 | 182 | /* Infinite loop waiting for BLE interrupt events */ |
knaresh89 | 0:e4fe77192b73 | 183 | while (true) { |
knaresh89 | 0:e4fe77192b73 | 184 | ble.waitForEvent(); /* Save power */ |
knaresh89 | 0:e4fe77192b73 | 185 | } |
knaresh89 | 0:e4fe77192b73 | 186 | } |
knaresh89 | 0:e4fe77192b73 | 187 | |
knaresh89 | 0:e4fe77192b73 | 188 |