BLE PLX profile with customized services (raw data).

Dependencies:   MtSense06

https://mtmtechblog.files.wordpress.com/2016/04/mtsense06-post-title.jpeg?w=772 https://blog.mtmtech.com.tw/2016/06/28/pulse-oximeter-with-mtconnect04/

Revision:
0:528034040a12
Child:
1:0ec0e5694c4a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PulseOximeterService.h	Wed Feb 22 09:45:51 2017 +0000
@@ -0,0 +1,199 @@
+/* Copyright (c) 2016 MtM Technology Corporation, MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
+ * and associated documentation files (the "Software"), to deal in the Software without restriction, 
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or 
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING 
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __BLE_PULSE_OXIMETER_SERVICE_H__
+#define __BLE_PULSE_OXIMETER_SERVICE_H__
+
+#include "ble/BLE.h"
+
+class PulseOximeterService {
+public:
+    PulseOximeterService(BLE &_ble, float spo2, float pr) :
+        ble(_ble),
+        plxMeasValueBytes(spo2, pr),
+        plxFeatValueBytes(),
+        rawValueBytes(),
+        flagSigValueBytes(),
+        plxSpotCheckMeasurementCharacteristic(0x2A5E/* UUID:PLX Spot-Check Measurement */, 
+            plxMeasValueBytes.getPointer(),
+            PlxSpotCheckMeasurementValueBytes::SIZEOF_VALUE_BYTES,
+            PlxSpotCheckMeasurementValueBytes::SIZEOF_VALUE_BYTES,
+            GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_INDICATE,
+            NULL, 0, false
+        ),
+        plxFeaturesCharacteristic(0x2A60/* UUID:PLX Features */,
+            plxFeatValueBytes.getPointer(),
+            PlxFeaturesValueBytes::SIZEOF_VALUE_BYTES,
+            PlxFeaturesValueBytes::SIZEOF_VALUE_BYTES,
+            GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ,
+            NULL, 0, false
+        ),
+        rawDataCharacteristic(0x2B00/* UUID:Self defined for raw data */,
+            rawValueBytes.getPointer(),
+            RawDataValueBytes::SIZEOF_VALUE_BYTES,
+            RawDataValueBytes::SIZEOF_VALUE_BYTES,
+            GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY,
+            NULL, 0, false
+        ),
+        signalCharacteristic(0x2B01/* UUID:Self defined for flag & signal */,
+            flagSigValueBytes.getPointer(),
+            FlagSignalValueBytes::SIZEOF_VALUE_BYTES,
+            FlagSignalValueBytes::SIZEOF_VALUE_BYTES,
+            GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ,
+            NULL, 0, false
+        )
+    {
+        GattCharacteristic *charTable[] = {
+            &plxSpotCheckMeasurementCharacteristic,
+            &plxFeaturesCharacteristic,
+            &rawDataCharacteristic,
+            &signalCharacteristic
+        };
+        GattService     PulseOximeterService(0x1822/* UUID:Pulse Oximeter Service */, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
+        ble.gattServer().addService(PulseOximeterService);
+    }
+
+    void updatePlxMeas(float spo2, float pr) {
+        plxMeasValueBytes.updatePlxSpotCheckMeasurement(spo2, pr);
+        ble.gattServer().write(plxSpotCheckMeasurementCharacteristic.getValueHandle(),
+            plxMeasValueBytes.getPointer(),
+            PlxSpotCheckMeasurementValueBytes::SIZEOF_VALUE_BYTES
+        );
+    }
+    
+    void updateRaw(uint8_t *raw) {
+        rawValueBytes.updateRawData(raw);
+        ble.gattServer().write(rawDataCharacteristic.getValueHandle(),
+            rawValueBytes.getPointer(),
+            RawDataValueBytes::SIZEOF_VALUE_BYTES
+        );
+    }
+    
+    void updateStatusFlagAndSignalQuality(uint8_t statusFlag, uint8_t signalQuality) {
+        flagSigValueBytes.updateFlagSignal(statusFlag, signalQuality);
+        ble.gattServer().write(signalCharacteristic.getValueHandle(),
+            flagSigValueBytes.getPointer(),
+            FlagSignalValueBytes::SIZEOF_VALUE_BYTES
+        );
+    }
+//==================================================================================
+private:
+    struct PlxSpotCheckMeasurementValueBytes {
+        static const unsigned OFFSET_OF_FLAGS   = 0;
+        static const unsigned OFFSET_OF_SPO2    = 1;
+        static const unsigned OFFSET_OF_PR      = 3;
+        static const unsigned SIZEOF_VALUE_BYTES    = 5;
+
+        PlxSpotCheckMeasurementValueBytes(float spo2, float pr) : bytes() {
+            bytes[OFFSET_OF_FLAGS] = 0x00;  /* All fields aren't present */
+            updatePlxSpotCheckMeasurement(spo2, pr);
+        }
+      
+        void updatePlxSpotCheckMeasurement(float spo2, float pr) {
+            uint16_t sfloat_spo2 = ieee11073_SFLOAT(spo2);
+            uint16_t sfloat_pr   = ieee11073_SFLOAT(pr);
+
+            memcpy(&bytes[OFFSET_OF_SPO2], &sfloat_spo2, sizeof(uint16_t));
+            memcpy(&bytes[OFFSET_OF_PR],   &sfloat_pr,   sizeof(uint16_t));
+        }
+
+        uint8_t       *getPointer(void)       { return bytes; }
+        const uint8_t *getPointer(void) const { return bytes; }
+
+        uint16_t ieee11073_SFLOAT(float v) {
+            /* Bitmap: eeee_mmmm_mmmm_mmmm */
+            /* Exponent: 4bits, Base10, 2'sComplement */
+            /* Mantissa: 12bits, 2's Complement */
+            uint8_t  exponent = 0; 
+            uint16_t mantissa = (uint16_t)v;
+            return (((uint16_t)exponent) << 12) | mantissa;
+        }
+ 
+        uint8_t bytes[SIZEOF_VALUE_BYTES];
+    };
+    //-------------------------------------------------------------------------------
+    struct PlxFeaturesValueBytes {
+        static const unsigned OFFSET_OF_SUPPORTED_FEATURES  = 0;
+        static const unsigned SIZEOF_VALUE_BYTES    = 2;
+
+        PlxFeaturesValueBytes() : bytes() {
+            memset(bytes, 0x00, SIZEOF_VALUE_BYTES);    /* All features aren't support */
+        }
+
+        void updatePlxFeatures(uint16_t feat) {
+            memcpy(&bytes[OFFSET_OF_SUPPORTED_FEATURES], &feat, SIZEOF_VALUE_BYTES);
+        }
+
+        uint8_t       *getPointer(void)       { return bytes; }
+        const uint8_t *getPointer(void) const { return bytes; }
+        
+        uint8_t bytes[SIZEOF_VALUE_BYTES];
+    };
+    //-------------------------------------------------------------------------------
+    struct RawDataValueBytes {
+        static const unsigned OFFSET_OF_RAW  = 0;
+        static const unsigned SIZEOF_VALUE_BYTES    = 18;
+
+        RawDataValueBytes() : bytes() {
+            memset(bytes, 0x00, SIZEOF_VALUE_BYTES);
+        }
+
+        void updateRawData(uint8_t *raw) {
+            memcpy(&bytes[OFFSET_OF_RAW], raw, SIZEOF_VALUE_BYTES);
+        }
+
+        uint8_t       *getPointer(void)       { return bytes; }
+        const uint8_t *getPointer(void) const { return bytes; }
+ 
+        uint8_t bytes[SIZEOF_VALUE_BYTES];
+    };
+    //-------------------------------------------------------------------------------
+    struct FlagSignalValueBytes {
+        static const unsigned OFFSET_OF_FLAG            = 0;
+        static const unsigned OFFSET_OF_SIGNAL_QUALITY  = 1;
+        static const unsigned SIZEOF_VALUE_BYTES    = 2;
+
+        FlagSignalValueBytes() : bytes() {
+            memset(bytes, 0x00, SIZEOF_VALUE_BYTES);
+        }
+
+        void updateFlagSignal(uint8_t statusFlag, uint8_t signalQuality) {
+            bytes[OFFSET_OF_FLAG]           = statusFlag;
+            bytes[OFFSET_OF_SIGNAL_QUALITY] = signalQuality;
+        }
+
+        uint8_t       *getPointer(void)       { return bytes; }
+        const uint8_t *getPointer(void) const { return bytes; }
+ 
+        uint8_t bytes[SIZEOF_VALUE_BYTES];
+    };
+//==================================================================================
+protected:
+    BLE &ble;
+
+    PlxSpotCheckMeasurementValueBytes   plxMeasValueBytes;
+    PlxFeaturesValueBytes               plxFeatValueBytes;
+    RawDataValueBytes                   rawValueBytes;
+    FlagSignalValueBytes                flagSigValueBytes;
+
+    GattCharacteristic plxSpotCheckMeasurementCharacteristic;
+    GattCharacteristic plxFeaturesCharacteristic;
+    GattCharacteristic rawDataCharacteristic;
+    GattCharacteristic signalCharacteristic;
+};
+
+#endif /* #ifndef __BLE_PULSE_OXIMETER_SERVICE_H__*/
\ No newline at end of file