BLE PLX profile with customized services (raw data).
Dependencies: MtSense06
https://blog.mtmtech.com.tw/2016/06/28/pulse-oximeter-with-mtconnect04/
main.cpp@2:7a08b9410b96, 2017-03-31 (annotated)
- Committer:
- johnathanlyu
- Date:
- Fri Mar 31 08:05:22 2017 +0000
- Revision:
- 2:7a08b9410b96
- Parent:
- 1:0ec0e5694c4a
add feature UART show raw data.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
bcc6 | 0:528034040a12 | 1 | /* Copyright (c) 2016 MtM Technology Corporation, MIT License |
bcc6 | 0:528034040a12 | 2 | * |
bcc6 | 0:528034040a12 | 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this software |
bcc6 | 0:528034040a12 | 4 | * and associated documentation files (the "Software"), to deal in the Software without restriction, |
bcc6 | 0:528034040a12 | 5 | * including without limitation the rights to use, copy, modify, merge, publish, distribute, |
bcc6 | 0:528034040a12 | 6 | * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is |
bcc6 | 0:528034040a12 | 7 | * furnished to do so, subject to the following conditions: |
bcc6 | 0:528034040a12 | 8 | * |
bcc6 | 0:528034040a12 | 9 | * The above copyright notice and this permission notice shall be included in all copies or |
bcc6 | 0:528034040a12 | 10 | * substantial portions of the Software. |
bcc6 | 0:528034040a12 | 11 | * |
bcc6 | 0:528034040a12 | 12 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING |
bcc6 | 0:528034040a12 | 13 | * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
bcc6 | 0:528034040a12 | 14 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
bcc6 | 0:528034040a12 | 15 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
bcc6 | 0:528034040a12 | 16 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
bcc6 | 0:528034040a12 | 17 | */ |
bcc6 | 0:528034040a12 | 18 | #include <events/mbed_events.h> |
bcc6 | 0:528034040a12 | 19 | #include <mbed.h> |
bcc6 | 0:528034040a12 | 20 | #include "ble/BLE.h" |
bcc6 | 0:528034040a12 | 21 | #include "ble/Gap.h" |
bcc6 | 0:528034040a12 | 22 | #include "ble/services/BatteryService.h" |
bcc6 | 0:528034040a12 | 23 | #include "ble/services/DeviceInformationService.h" |
bcc6 | 0:528034040a12 | 24 | #include "PulseOximeterService.h" |
bcc6 | 0:528034040a12 | 25 | #include "M1.h" |
bcc6 | 0:528034040a12 | 26 | |
johnathanlyu | 2:7a08b9410b96 | 27 | /* |
johnathanlyu | 2:7a08b9410b96 | 28 | * set 0 disable M1 data send to uart |
johnathanlyu | 2:7a08b9410b96 | 29 | * 1 enable M1 data send to urat |
johnathanlyu | 2:7a08b9410b96 | 30 | */ |
johnathanlyu | 2:7a08b9410b96 | 31 | #define M1_UART_RAW_DATA_ENABLE 0 |
johnathanlyu | 2:7a08b9410b96 | 32 | |
bcc6 | 0:528034040a12 | 33 | DigitalOut ledRed(p16, 1); |
bcc6 | 0:528034040a12 | 34 | Serial pc(p5, p4); |
bcc6 | 0:528034040a12 | 35 | I2C i2c(p3, p2); |
bcc6 | 0:528034040a12 | 36 | M1 m1(i2c); |
bcc6 | 0:528034040a12 | 37 | |
bcc6 | 0:528034040a12 | 38 | const static char DEVICE_NAME[] = "MtM_PLX"; |
bcc6 | 0:528034040a12 | 39 | static const uint16_t uuid16_list[] = { 0x1822, /* UUID_PULSE_OXIMETER_SERVICE */ |
bcc6 | 0:528034040a12 | 40 | GattService::UUID_BATTERY_SERVICE, |
bcc6 | 0:528034040a12 | 41 | GattService::UUID_DEVICE_INFORMATION_SERVICE}; |
bcc6 | 0:528034040a12 | 42 | |
bcc6 | 0:528034040a12 | 43 | static PulseOximeterService* plxServicePtr; |
bcc6 | 0:528034040a12 | 44 | static BatteryService* batteryServicePtr; |
bcc6 | 0:528034040a12 | 45 | |
bcc6 | 0:528034040a12 | 46 | static EventQueue eventQueue( |
bcc6 | 0:528034040a12 | 47 | /* event count */ 16 * /* event size */ 32 |
bcc6 | 0:528034040a12 | 48 | ); |
bcc6 | 0:528034040a12 | 49 | |
bcc6 | 0:528034040a12 | 50 | |
bcc6 | 0:528034040a12 | 51 | void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params) |
bcc6 | 0:528034040a12 | 52 | { |
bcc6 | 0:528034040a12 | 53 | BLE::Instance().gap().startAdvertising(); |
bcc6 | 0:528034040a12 | 54 | } |
bcc6 | 0:528034040a12 | 55 | |
bcc6 | 0:528034040a12 | 56 | void blinkCallback(void) |
bcc6 | 0:528034040a12 | 57 | { |
bcc6 | 0:528034040a12 | 58 | ledRed = !ledRed; |
bcc6 | 0:528034040a12 | 59 | } |
bcc6 | 0:528034040a12 | 60 | |
bcc6 | 0:528034040a12 | 61 | void plxCallback(void) |
bcc6 | 0:528034040a12 | 62 | { |
bcc6 | 0:528034040a12 | 63 | M1::Plx plx; |
bcc6 | 0:528034040a12 | 64 | |
bcc6 | 0:528034040a12 | 65 | BLE &ble = BLE::Instance(); |
bcc6 | 0:528034040a12 | 66 | if (ble.gap().getState().connected && m1.IsSkinIn()) { |
bcc6 | 0:528034040a12 | 67 | m1.GetPlx(&plx); |
bcc6 | 0:528034040a12 | 68 | plxServicePtr->updatePlxMeas((float)plx.spo2, (float)plx.pulseRate); |
bcc6 | 1:0ec0e5694c4a | 69 | } |
bcc6 | 0:528034040a12 | 70 | } |
bcc6 | 0:528034040a12 | 71 | |
bcc6 | 0:528034040a12 | 72 | void rawCallback(void) |
bcc6 | 0:528034040a12 | 73 | { |
bcc6 | 0:528034040a12 | 74 | M1::Raw raw; |
bcc6 | 0:528034040a12 | 75 | static uint8_t cnt = 0; |
bcc6 | 0:528034040a12 | 76 | static uint8_t raw_x3[6*3]; |
bcc6 | 0:528034040a12 | 77 | |
bcc6 | 0:528034040a12 | 78 | BLE &ble = BLE::Instance(); |
bcc6 | 0:528034040a12 | 79 | if (ble.gap().getState().connected && m1.IsSkinIn()) { |
bcc6 | 0:528034040a12 | 80 | m1.GetRaw(&raw); |
bcc6 | 0:528034040a12 | 81 | |
bcc6 | 1:0ec0e5694c4a | 82 | raw_x3[6*cnt+0] = (uint8_t)(raw.red >> 8); |
bcc6 | 1:0ec0e5694c4a | 83 | raw_x3[6*cnt+1] = (uint8_t)(raw.red >> 0); |
bcc6 | 1:0ec0e5694c4a | 84 | raw_x3[6*cnt+2] = (uint8_t)(raw.ir >> 8); |
bcc6 | 1:0ec0e5694c4a | 85 | raw_x3[6*cnt+3] = (uint8_t)(raw.ir >> 0); |
bcc6 | 1:0ec0e5694c4a | 86 | raw_x3[6*cnt+4] = (uint8_t)(raw.green >> 8); |
bcc6 | 1:0ec0e5694c4a | 87 | raw_x3[6*cnt+5] = (uint8_t)(raw.green >> 0); |
bcc6 | 0:528034040a12 | 88 | |
bcc6 | 0:528034040a12 | 89 | if (++cnt >= 3) { |
bcc6 | 0:528034040a12 | 90 | plxServicePtr->updateRaw(raw_x3); |
bcc6 | 0:528034040a12 | 91 | cnt = 0; |
bcc6 | 0:528034040a12 | 92 | } |
bcc6 | 0:528034040a12 | 93 | } else { |
bcc6 | 0:528034040a12 | 94 | cnt = 0; |
bcc6 | 0:528034040a12 | 95 | } |
bcc6 | 0:528034040a12 | 96 | } |
bcc6 | 0:528034040a12 | 97 | |
johnathanlyu | 2:7a08b9410b96 | 98 | void rawCallbackToUART() { |
johnathanlyu | 2:7a08b9410b96 | 99 | M1::Raw raw; |
johnathanlyu | 2:7a08b9410b96 | 100 | if(m1.IsSkinIn()) { |
johnathanlyu | 2:7a08b9410b96 | 101 | m1.GetRaw(&raw); |
johnathanlyu | 2:7a08b9410b96 | 102 | pc.printf("%d,%d,%d\r\n",raw.red, raw.ir, raw.green); |
johnathanlyu | 2:7a08b9410b96 | 103 | } |
johnathanlyu | 2:7a08b9410b96 | 104 | } |
johnathanlyu | 2:7a08b9410b96 | 105 | |
bcc6 | 0:528034040a12 | 106 | void StatusCallback(void) |
bcc6 | 0:528034040a12 | 107 | { |
bcc6 | 0:528034040a12 | 108 | M1::Status sta; |
bcc6 | 0:528034040a12 | 109 | |
bcc6 | 0:528034040a12 | 110 | BLE &ble = BLE::Instance(); |
bcc6 | 0:528034040a12 | 111 | if (ble.gap().getState().connected) { |
bcc6 | 0:528034040a12 | 112 | m1.GetStatus(&sta); |
bcc6 | 0:528034040a12 | 113 | plxServicePtr->updateStatusFlagAndSignalQuality(sta.flags, sta.signalQuality); |
bcc6 | 0:528034040a12 | 114 | } |
bcc6 | 0:528034040a12 | 115 | } |
bcc6 | 0:528034040a12 | 116 | |
bcc6 | 0:528034040a12 | 117 | void batteryCallback(void ) |
bcc6 | 0:528034040a12 | 118 | { |
bcc6 | 0:528034040a12 | 119 | uint8_t batteryLevel = 99; |
bcc6 | 0:528034040a12 | 120 | |
bcc6 | 0:528034040a12 | 121 | BLE &ble = BLE::Instance(); |
bcc6 | 0:528034040a12 | 122 | if (ble.gap().getState().connected) { |
bcc6 | 0:528034040a12 | 123 | batteryServicePtr->updateBatteryLevel(batteryLevel); |
bcc6 | 0:528034040a12 | 124 | } |
bcc6 | 0:528034040a12 | 125 | } |
bcc6 | 0:528034040a12 | 126 | |
bcc6 | 0:528034040a12 | 127 | /** |
bcc6 | 0:528034040a12 | 128 | * This function is called when the ble initialization process has failled |
bcc6 | 0:528034040a12 | 129 | */ |
bcc6 | 0:528034040a12 | 130 | void onBleInitError(BLE &ble, ble_error_t error) |
bcc6 | 0:528034040a12 | 131 | { |
bcc6 | 0:528034040a12 | 132 | /* Initialization error handling should go here */ |
bcc6 | 0:528034040a12 | 133 | } |
bcc6 | 0:528034040a12 | 134 | |
bcc6 | 0:528034040a12 | 135 | /** |
bcc6 | 0:528034040a12 | 136 | * Callback triggered when the ble initialization process has finished |
bcc6 | 0:528034040a12 | 137 | */ |
bcc6 | 0:528034040a12 | 138 | void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) |
bcc6 | 0:528034040a12 | 139 | { |
bcc6 | 0:528034040a12 | 140 | BLE& ble = params->ble; |
bcc6 | 0:528034040a12 | 141 | ble_error_t error = params->error; |
bcc6 | 0:528034040a12 | 142 | |
bcc6 | 0:528034040a12 | 143 | if (error != BLE_ERROR_NONE) { |
bcc6 | 0:528034040a12 | 144 | /* In case of error, forward the error handling to onBleInitError */ |
bcc6 | 0:528034040a12 | 145 | onBleInitError(ble, error); |
bcc6 | 0:528034040a12 | 146 | return; |
bcc6 | 0:528034040a12 | 147 | } |
bcc6 | 0:528034040a12 | 148 | |
bcc6 | 0:528034040a12 | 149 | /* Ensure that it is the default instance of BLE */ |
bcc6 | 0:528034040a12 | 150 | if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { |
bcc6 | 0:528034040a12 | 151 | return; |
bcc6 | 0:528034040a12 | 152 | } |
bcc6 | 0:528034040a12 | 153 | |
bcc6 | 0:528034040a12 | 154 | ble.gap().onDisconnection(disconnectionCallback); |
bcc6 | 0:528034040a12 | 155 | |
bcc6 | 0:528034040a12 | 156 | /* Setup primary service */ |
bcc6 | 0:528034040a12 | 157 | plxServicePtr = new PulseOximeterService(ble, 0, 0); |
bcc6 | 0:528034040a12 | 158 | batteryServicePtr = new BatteryService(ble, 100); |
bcc6 | 0:528034040a12 | 159 | DeviceInformationService deviceInfo(ble, "MtM"); |
bcc6 | 0:528034040a12 | 160 | |
bcc6 | 0:528034040a12 | 161 | /* Setup advertising */ |
bcc6 | 0:528034040a12 | 162 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
bcc6 | 0:528034040a12 | 163 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *) uuid16_list, sizeof(uuid16_list)); |
bcc6 | 0:528034040a12 | 164 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *) DEVICE_NAME, sizeof(DEVICE_NAME)); |
bcc6 | 0:528034040a12 | 165 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::PULSE_OXIMETER_GENERIC); |
bcc6 | 0:528034040a12 | 166 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
bcc6 | 0:528034040a12 | 167 | ble.gap().setAdvertisingInterval(1000); /* 1000ms */ |
bcc6 | 0:528034040a12 | 168 | ble.gap().startAdvertising(); |
bcc6 | 0:528034040a12 | 169 | } |
bcc6 | 0:528034040a12 | 170 | |
bcc6 | 0:528034040a12 | 171 | void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) { |
bcc6 | 0:528034040a12 | 172 | BLE &ble = BLE::Instance(); |
bcc6 | 0:528034040a12 | 173 | eventQueue.call(Callback<void()>(&ble, &BLE::processEvents)); |
bcc6 | 0:528034040a12 | 174 | } |
bcc6 | 0:528034040a12 | 175 | |
bcc6 | 0:528034040a12 | 176 | int main() |
bcc6 | 0:528034040a12 | 177 | { |
bcc6 | 0:528034040a12 | 178 | /* Disable the hardware flow control of Serial, then show the mbed version */ |
bcc6 | 0:528034040a12 | 179 | pc.set_flow_control(SerialBase::Disabled); |
bcc6 | 0:528034040a12 | 180 | pc.baud(115200); |
johnathanlyu | 2:7a08b9410b96 | 181 | pc.printf("\r\n"); |
johnathanlyu | 2:7a08b9410b96 | 182 | pc.printf("mbed version(%d.%d.%d)\r\n", MBED_MAJOR_VERSION, MBED_MINOR_VERSION, MBED_PATCH_VERSION); |
johnathanlyu | 2:7a08b9410b96 | 183 | pc.printf("\r\n"); |
bcc6 | 0:528034040a12 | 184 | |
bcc6 | 0:528034040a12 | 185 | /* Config device */ |
bcc6 | 0:528034040a12 | 186 | m1.ConfigDevice(); |
bcc6 | 0:528034040a12 | 187 | |
johnathanlyu | 2:7a08b9410b96 | 188 | #if M1_UART_RAW_DATA_ENABLE |
johnathanlyu | 2:7a08b9410b96 | 189 | /* 35ms getter raw data to uart */ |
johnathanlyu | 2:7a08b9410b96 | 190 | eventQueue.call_every(35, rawCallbackToUART); |
johnathanlyu | 2:7a08b9410b96 | 191 | eventQueue.call_every(500, blinkCallback); |
johnathanlyu | 2:7a08b9410b96 | 192 | #else |
bcc6 | 0:528034040a12 | 193 | /* Update each data every N ms */ |
bcc6 | 0:528034040a12 | 194 | eventQueue.call_every(1000, plxCallback); |
bcc6 | 0:528034040a12 | 195 | eventQueue.call_every(35, rawCallback); |
bcc6 | 0:528034040a12 | 196 | eventQueue.call_every(1000, StatusCallback); |
bcc6 | 0:528034040a12 | 197 | eventQueue.call_every(500, batteryCallback); |
bcc6 | 0:528034040a12 | 198 | eventQueue.call_every(500, blinkCallback); |
bcc6 | 0:528034040a12 | 199 | |
bcc6 | 0:528034040a12 | 200 | BLE &ble = BLE::Instance(); |
bcc6 | 0:528034040a12 | 201 | ble.onEventsToProcess(scheduleBleEventsProcessing); |
bcc6 | 0:528034040a12 | 202 | ble.init(bleInitComplete); |
johnathanlyu | 2:7a08b9410b96 | 203 | #endif |
bcc6 | 0:528034040a12 | 204 | |
bcc6 | 0:528034040a12 | 205 | eventQueue.dispatch_forever(); |
bcc6 | 0:528034040a12 | 206 | |
bcc6 | 0:528034040a12 | 207 | return 0; |
bcc6 | 0:528034040a12 | 208 | } |