Reading SenseAir LP8 CO2 sensor over bluetooth low energy

Dependencies:   BLE_API mbed nRF51822

Files at this revision

API Documentation at this revision

Comitter:
jony1401
Date:
Fri Apr 21 13:26:06 2017 +0000
Child:
1:b512a405b584
Commit message:
alpha v 1.0

Changed in this revision

BLE_API.lib Show annotated file Show diff for this revision Revisions of this file
LP8.h Show annotated file Show diff for this revision Revisions of this file
LP8_Service.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
nRF51822.lib Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/BLE_API.lib	Fri Apr 21 13:26:06 2017 +0000
@@ -0,0 +1,1 @@
+https://mbed.org/teams/Bluetooth-Low-Energy/code/BLE_API/#65474dc93927
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LP8.h	Fri Apr 21 13:26:06 2017 +0000
@@ -0,0 +1,284 @@
+#ifndef LP8_H
+#define LP8_H
+
+class LP8 
+{
+public:
+
+    //constructor
+    LP8(Serial &device, DigitalOut &vbb, DigitalOut &vbb_en, DigitalIn &rdy, Timer &_timer):                  /* initiate class object */
+        Device(device),
+        VBB(vbb),
+        VBB_EN(vbb_en),
+        RDY(rdy),
+        lp8Wait(_timer)
+        {
+            Device.baud(9600);                                                              //set baud rate to 9600 
+            
+            //initialize arrays with modbus commands. 
+            //initial startup command
+            firstWrite[0] = 0xfe;
+            firstWrite[1] = 0x41;
+            firstWrite[2] = 0x00;
+            firstWrite[3] = 0x80;
+            firstWrite[4] = 0x01;
+            firstWrite[5] = 0x10;
+            firstWrite[6] = 0x28;
+            firstWrite[7] = 0x7e;
+            
+            //write sensor state to lp8 command
+            stateWrite[0] = 0xfe;                                                   //device adress
+            stateWrite[1] = 0x41;                                                   //read/write
+            stateWrite[2] = 0x00;                                                   //
+            stateWrite[3] = 0x80;                                                   //starting adress
+            stateWrite[4] = 0x18;                                                   //nr of bytes to send
+            stateWrite[5] = 0x20;                                                   //Calculation Control 
+            for(int k = 6; k < 31; k++) { stateWrite[k] = 0x00; }
+            
+            //read lp8 state command
+            stateRead[0] = 0xfe;        
+            stateRead[1] = 0x44;
+            stateRead[2] = 0x00;
+            stateRead[3] = 0x80;
+            stateRead[4] = 0x2c;
+            stateRead[5] = 0x00;
+            stateRead[5] = 0x00;
+            //response buffer
+            for(int k = 0; k < 60; k++) { response[k] = 0x00; }
+
+
+//            gC      = 0x00;
+            co2     = 400;                                                              
+            counter = 0;                                                                 
+            CRC     = 0x0000;
+            _timeMe = 0.3;
+        };
+
+    
+    //send startup msg                 .
+    bool lp8Init(){
+            VBB.write( 1 );
+            VBB_EN.write( 1 );                                                              //power on
+            timeIt( _timeMe );
+            
+            transmitPacket(firstWrite, 8);                                                  //Send out msg (and nr of elements) over serial line
+            Response( 4 );                                                                  //read 4 bytes response     
+            
+            if( RDY.read() == 0 ) {
+                lp8Wait.start();
+                while( lp8Wait.read() < 0.5 ) { /* spin */ }
+                lp8Wait.stop();
+                lp8Wait.reset(); 
+            }          
+            
+//compute crc
+            CRC = modbusCrc(stateRead, 5);
+//add crc value to the transmit package
+            stateRead[5] = (uint8_t)CRC;                                                    //crc_H
+            stateRead[6] = (uint8_t)(CRC >> 8);                                             //crc_L
+            timeIt( _timeMe );                                                              //wait for message to be sent and processed
+            transmitPacket(stateRead, 7);                                                   //transmit packet  
+            Response( 49 );                                                                 //read sensor state and co2 value(s)
+ 
+            VBB_EN.write( 0 );                                                              //power off
+            VBB.write( 0 );           
+                        
+//was the talk a success?
+            if ( getValue() < 1 ) {
+                return 1; }
+            else {
+                return 0; }
+            
+        };
+    
+    
+    //send subsequent messages to the lp8
+    void lp8Talk(){
+
+            //transfer previous sensor state to the new msg out
+            for (int u = 4; u < 23+4; u++) {
+                stateWrite[u+2] = response[u];
+                }
+            //compute new crc value
+            CRC = modbusCrc(stateWrite, 29);
+            //add new crc value to send list
+            stateWrite[29] = (uint8_t)CRC;   
+            stateWrite[30] = (uint8_t)(CRC >> 8);
+
+    
+            //initialize new transfer sequence    
+            VBB.write( 1 );
+            VBB_EN.write( 1 );                                                              //power on sensor
+            timeIt( _timeMe );
+            
+            transmitPacket(stateWrite, 31);                                                 //Send out msg with previous state (and nr of elements) over serial line
+            Response( 4 );                                                                  //read 4 bytes response     
+
+            //compute crc
+            CRC = modbusCrc(stateRead, 5);
+            //add crc value to the read request transmit package
+            stateRead[5] = (uint8_t)CRC;                                                    //crc_l
+            stateRead[6] = (uint8_t)(CRC >> 8);                                             //crc_h
+            //send read request
+            transmitPacket(stateRead, 7);                                                   //transmit packet  
+            //read sensor response
+            Response( 49 );                                                                 //read sensor state and co2 value(s)
+
+            VBB_EN.write( 0 );                                                              //power off
+            VBB.write( 0 );
+       };
+    
+    
+    
+    //get co2 value from lp8 response    
+    unsigned long getValue()
+    {
+        int high = response[29];
+        int low = response[30]; 
+        unsigned long val = high * 256 + low; 
+    
+        return val;
+    }
+
+        
+        
+    /************************************************* Helper Functions ********************************************/
+    /*************************************************                  ********************************************/
+    void responsePurge(int bytesToPurge){
+        for (int j = 0; j < bytesToPurge; j++) {
+                response[j] = 0x00;
+            }
+        };
+        
+    
+    //read from the lp8    
+    void Response(int bytesToRead ){
+        lp8Wait.start();                    //poll rx line for x seconds
+        do {
+            if(Device.readable()) {
+                response[counter] = Device.getc(); 
+                counter++;
+                }
+            }
+        while( lp8Wait.read() < 0.5 );
+        counter = 0;
+        lp8Wait.stop();
+        lp8Wait.reset();                                                        
+    };
+    
+    void transmitPacket(uint8_t msg[], int le ){
+            //Send out msg over serial line:
+            while(!Device.writeable() ) { /* wait for serial available*/ }
+            for(int pos = 0; pos < le; pos++) { 
+                Device.putc(msg[pos]);
+            }
+            
+        };
+    
+    //timer
+    void timeIt(float &timeMe){
+            //start amd stop timer...
+            lp8Wait.start();
+            while (lp8Wait.read() < timeMe ) { /* W A I T I N G */ }
+            lp8Wait.stop();
+            lp8Wait.reset();
+        };
+    
+    // Computation for the modbus 16-bit crc
+    uint16_t modbusCrc(uint8_t buffer[], int len){
+      uint16_t crc = 0xFFFF;
+      
+      for (int pos = 0; pos < len; pos++) {
+        crc ^= (uint16_t)buffer[pos];                                                   // XOR byte into least sig. byte of crc
+      
+        for (int i = 8; i != 0; i--) {                                                  // Loop over each bit
+          if ((crc & 0x0001) != 0) {                                                    // If the LSB is set
+            crc >>= 1;                                                                  // shift right and XOR 0xA001
+            crc ^= 0xA001;
+          }
+          else                                                                          // Else LSB is not set
+            crc >>= 1;                                                                  // shift right
+        }
+      }
+      // Note, this number has low and high bytes swapped
+      return crc;  
+    };
+
+
+
+
+private:
+
+    Serial      &Device;
+    DigitalOut  &VBB;
+    DigitalOut  &VBB_EN;    
+    DigitalIn   &RDY;
+    Timer       &lp8Wait;
+    
+    //msg containers (needs to be initialized at constructor level)
+    uint8_t  firstWrite[8];     
+    uint8_t  stateWrite[31]; 
+    uint8_t  stateRead[7];
+    uint8_t  response[60];
+    
+    int         co2;                                                                       //CO2 initial value   
+    int         counter;                                                                   //simple counter 
+    uint16_t    CRC;                                                                       //modbus crc value
+    float       _timeMe;                                                                   //timer value
+//    uint8_t     gC;                                                                         //getCharacter...
+
+
+};
+#endif
+
+/* 
+LP8 Communication Protocol (With no external Pressure Sensor):
+--------------------------------------------------------------------------------
+Initial Measurement (first read after powerup):                                     COMMANDS:
+--------------------------------------------------------------------------------
+1) host powers up sensor:                                                           VBB_EN = 1
+2) host waits until rdy signal goes low:                                            RDY = 0;
+3) host writes command, "write 24 bytes starting from adress 0x0080":               { 0xfe, 0x41, 0x00, 0x80, 0x18, 0x10, ... [any 23 bytes] ..., CRC_low, CRC_high };
+4) host reads response:                                                             { 0xfe, 0x41, 0x81, 0xe0 };
+5) host waits until rdy signal goes high:                                           RDY = 1;
+6) host writes command "read 44 bytes starting from adress 0x0080":                 { 0xfe, 0x44, 0x00, 0x80, 0x2c, CRC_low, CRC_high };
+7) host reads response:                                                             { 0xfe, 0x44, 0x2c, 0x00, SS1, SS2, ..., SS23, PP_H, PP_L, D1, D2, ..., D18, CRC_low, CRC_high };
+8) host powers down sensor:                                                         VBB_EN = 0;
+
+[------------------------------------------------------------------------------]
+Optional first reading W/O pressure sensor
+[------------------------------------------------------------------------------]
+1) host powers up sensor:                                                           VBB_EN = 1
+2) host waits until rdy signal goes low:                                            RDY = 0;
+3) host writes command, "write 24 bytes starting from adress 0x0080":               { 0xfe, 0x41, 0x00, 0x80, 0x18, 0x10, 0x28, 0x7e };
+4) host reads response:                                                             { 0xfe, 0x41, 0x81, 0xe0 };
+5) host waits until rdy signal goes high:                                           RDY = 1;
+6) host writes command "read 44 bytes starting from adress 0x0080":                 { 0xfe, 0x44, 0x00, 0x80, 0x2c, CRC_low, CRC_high };
+7) host reads response:                                                             { 0xfe, 0x44, 0x2c, 0x00, SS1, SS2, ..., SS23, PP_H, PP_L, D1, D2, ..., D18, CRC_low, CRC_high };
+8) host powers down sensor:                                                         VBB_EN = 0;
+
+
+--------------------------------------------------------------------------------
+Subsequent readings:
+--------------------------------------------------------------------------------
+1) host powers up sensor:                                                           VBB_EN = 1
+2) host waits until rdy signal goes low:                                            RDY = 0;
+3) host writes command, "write 24 bytes starting from adress 0x0080":               { 0xfe, 0x41, 0x00, 0x80, 0x18, CC, SS1, ..., SS23, CRC_low, CRC_high };
+4) host reads response:                                                             { 0xfe, 0x41, 0x81, 0xe0 };
+5) host waits until rdy signal goes high:                                           RDY = 1;
+6) host writes command "read 44 bytes starting from adress 0x0080":                 { 0xfe, 0x44, 0x00, 0x80, 0x2c, CRC_low, CRC_high };
+7) host reads response:                                                             { 0xfe, 0x44, 0x2c, 0x00, SS1, SS2, ..., SS23, PP_H, PP_L, D1, D2, ..., D18, CRC_low, CRC_high };
+8) host powers down sensor:                                                         VBB_EN = 0;
+
+
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+CC = Calculation Control, 1 byte
+SS = Sensor State, Sensor State, 23 bytes
+D  = Measured data and Sensor Status, 23 bytes
+PP = Host Pressure Value
+CRC= 16 bit CRC error check 
+--------------------------------------------------------------------------------
+--------------------------------------------------------------------------------
+*/ 
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/LP8_Service.h	Fri Apr 21 13:26:06 2017 +0000
@@ -0,0 +1,35 @@
+#ifndef LP8_SERVICE_H
+#define LP8_SERVICE_H
+
+/* LP8 Gatt Service Class */
+
+class LP8_Service 
+{
+public:
+
+    const static uint16_t LP8_SERVICE_UUID                      = 0xA000;           //set custom UUID´s
+    const static uint16_t LP8_STATE_CHARACTERISTIC_UUID         = 0xA001;           //
+    
+    //constructor setup for Gatt Service
+    LP8_Service(BLE &_ble, int co2ValueInitial):                                    /* Passes a refrence to ble stack object and the lp8State copy to the private members */
+        ble(_ble), 
+        lp8State(LP8_STATE_CHARACTERISTIC_UUID, &co2ValueInitial, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY)
+        {
+            GattCharacteristic *charTable[] = {&lp8State };                                                                     //create an 1 element array with lp8 Gatt characteristic
+            GattService        lp8Service(LP8_SERVICE_UUID, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));       //create an LP8 GattService
+            ble.addService(lp8Service);                                                                                         //add gatt service to the ble stack
+        };
+
+    void updateCo2Value(int co2Value)
+    {
+        ble.gattServer().write(lp8State.getValueAttribute().getHandle(),(uint8_t *) &co2Value, sizeof(co2Value));               //update the Gatt Server with new CO2 value
+    };
+
+private:
+
+    BLE                                 &ble;       //reference to ble object
+    ReadOnlyGattCharacteristic<int>     lp8State;   //
+    
+
+};
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Apr 21 13:26:06 2017 +0000
@@ -0,0 +1,123 @@
+#include "mbed.h"
+#include "BLE.h"
+#include "LP8_Service.h"
+#include "LP8.h"
+
+//setup ble stack
+BLE             ble;                                                                    //BLE object
+
+// Pins and timers needed for lp8 communication
+DigitalIn       RDY(P0_8);                                                              //
+DigitalOut      VBB(P0_5);
+DigitalOut      VBB_EN(P0_4);                                                        //
+Serial          Device(P0_9, P0_11);                                                    //tx, rx
+DigitalOut      led1(P0_19, 1);                                                         //board led
+Timer           lp8Wait;                                                                //timer for sensor communication
+
+
+
+//Sensor and ble Configuration parameters
+#define     SENSOR_TIMER                        10.0                                    //lp8 polling interval (seconds)
+#define     BLE_ADV_INTERVAL                    500                                     //advertisment interval (milliseconds)
+
+
+
+//device name and uuid list setup
+const static char       DEVICE_NAME[] = "SenseAir LP8";
+static const uint16_t   uuid16_list[] = {LP8_Service::LP8_SERVICE_UUID};
+
+
+//check for sensor triggering and uppdating Gatt Server
+bool                    triggerSensor = false;                                          //check for sensor polling
+LP8_Service             *lp8ServicePtr;                                                 //pointer to lp8 service object
+
+
+
+//****************************   ble functions      *******************************
+//****************************                      *******************************
+// on Disconnect, Restart BroadCasting
+void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
+{
+    //reset I/O pin, necessary?
+//    VBB_EN.write ( 1 );
+    
+    //restart broadcast
+    ble.gap().startAdvertising();
+}
+//timer function callback function
+void triggerSensorPollingInterupt()
+{
+    triggerSensor = true;
+};
+
+
+
+//main 
+int main(void)
+{
+    led1 = 1;      //turn off led                                                                
+    
+    Ticker lp8Timer;                                                                    //timer object for sensor polling interupts
+    lp8Timer.attach(&triggerSensorPollingInterupt, SENSOR_TIMER);                       //trigger sensor reading every X.Y sec
+    
+    ble.init();
+    ble.gap().onDisconnection(disconnectionCallback);                                   //do callback if disconnection occurs
+
+
+    int                     co2Value = 400;                                             //initial CO2 value
+    bool                    initCheck = true;                                           //check for init sensor state or lost state
+    bool                    successCheck = false;
+    
+//setup LP8 peripheral communication 
+    LP8               lp8(Device, VBB, VBB_EN, RDY, lp8Wait);
+     
+     
+//setup for GattService
+/* Setup Gatt server */
+    LP8_Service      lp8Service(ble, co2Value);                                         //pass in ble object and initial co2 value
+    lp8ServicePtr     = &lp8Service;                                                    //set pointer to "real" lp8 service object
+
+
+/* setup advertising parameters */
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);            //general bluetooth information(only support for ble
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list)); //service list
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
+    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+    ble.gap().setAdvertisingInterval(BLE_ADV_INTERVAL);                 /* advertising interval in ms. */
+    ble.gap().startAdvertising();                                       //start broadcast
+    
+        /* SpinWait for initialization to complete. This is necessary because the
+     * BLE object is used in the main loop below. */
+    while (ble.hasInitialized()  == false) { /* spin loop */ }
+
+
+
+//start the loop    
+    while ( true ) 
+    {
+        if(triggerSensor && ble.gap().getState().connected )                            //trigger when timer interupts ticks and there is an established connection
+        {
+            if ( initCheck ) {
+                successCheck = lp8.lp8Init();
+                
+//                if ( successCheck ) {
+                    initCheck = false;
+//                    led1 = 0;       //turn on led 
+//                }
+            }
+            else {
+                lp8.lp8Talk();
+                led1 = 0;   //on when talking
+            }
+            
+            //reset sensor status
+            triggerSensor = false;
+            //update gattServer with new CO2 value
+            lp8ServicePtr->updateCo2Value( lp8.getValue() );
+        }
+        
+        else { ble.waitForEvent(); }                //ble save energy
+    }
+    
+};
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Fri Apr 21 13:26:06 2017 +0000
@@ -0,0 +1,1 @@
+https://mbed.org/users/mbed_official/code/mbed/builds/856d2700e60b
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nRF51822.lib	Fri Apr 21 13:26:06 2017 +0000
@@ -0,0 +1,1 @@
+https://mbed.org/teams/Nordic-Semiconductor/code/nRF51822/#c90ae1400bf2