Reading SenseAir LP8 CO2 sensor over bluetooth low energy

Dependencies:   BLE_API mbed nRF51822

LP8.h

Committer:
jony1401
Date:
2017-08-14
Revision:
2:d02255d8c36f
Parent:
1:b512a405b584
Child:
3:933dd59ad44d

File content as of revision 2:d02255d8c36f:

#ifndef LP8_H
#define LP8_H

/* To initialize the lp8 object, you need to pass a serial (tx, rx), a DigitalOut signal for the en_vbb,
an DigitalIn for the ready signal, DigitalOut for Reset and a timer object. */

class LP8 
{
public:
//constructor
    LP8(Serial &device, DigitalOut &vbb_en, DigitalIn &rdy, DigitalOut &res ,Timer &_timer):  
     Device(device),
     VBB_EN(vbb_en),
     RDY(rdy),
     RES(res),
     lp8Wait(_timer)
    {
        Device.baud(9600);                                                              //set baud rate to 9600 
            
        //initialize arrays with lp8 modbus commands. 
        //initial startup command:
        firstWrite[0] = 0xfe;                                                           //device adress
        firstWrite[1] = 0x41;                                                           //write to ram
        firstWrite[2] = 0x00;                                                           //
        firstWrite[3] = 0x80;                                                           //starting adress
        firstWrite[4] = 0x01;                                                           //nr of bytes to send
        firstWrite[5] = 0x10;                                                           //calculation control byte  
        firstWrite[6] = 0x28;                                                           //crc low
        firstWrite[7] = 0x7e;                                                           //crc high
            
        //write previous sensor state to lp8, command:
        stateWrite[0] = 0xfe;                                                           //device adress
        stateWrite[1] = 0x41;                                                           //write to ram
        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 request from the lp8:
        stateRead[0] = 0xfe;                                                            //adress
        stateRead[1] = 0x44;                                                            //read from ram
        stateRead[2] = 0x00;                                                            //
        stateRead[3] = 0x80;                                                            //starting adress
        stateRead[4] = 0x2c;                                                            //number of bytes to read
        stateRead[5] = 0x00;                                                            //crc_l
        stateRead[5] = 0x00;                                                            //crc_h
            
        //response buffer
        for(int k = 0; k < 60; k++) { response[k] = 0x00; }

        //variable initialization
        co2     = 400;                                                                  //co2 value
        counter = 0;                                                                    //
        CRC     = 0x0000;                                                               //crc value

    };

    
//LP8 Initialization and first message                  
    bool lp8Init(){

//            //Reset LP8
//            RES.write( 0 );                                                                 //reset
//            timeIt( 1.2 );
//            RES.write( 1 );                                                                 //enable
            
            //Enable Sensor      
            VBB_EN.write( 1 );                                                              //power on
            //wait for rdy signal
            timeIt( 0.35 );                                                                 //wait for lp8 rdy signal
            transmitPacket(firstWrite, 8);                                                  //Send out msg (and nr of bytes) over serial line
            Response( 4 );                                                                  //read 4 bytes response     
            //compute crc
            CRC = modbusCrc(stateRead, 5);
            //add crc value to the transmit package
            stateRead[5] = (uint8_t)CRC;                                                    //crc_l
            stateRead[6] = (uint8_t)(CRC >> 8);                                             //crc_h
            //wait for rdy
            timeIt( 0.35 );                                                                 //
            transmitPacket(stateRead, 7);                                                   //transmit packet  
            Response( 49 );                                                                 //read sensor state and co2 value(s)
            VBB_EN.write( 0 );                                                              //power off lp8                 

            //was the talk a success? (simple check...)
            if ( getValue() < 1 ) {
                return 1; }
            else {
                return 0; }
        };
    
//send subsequent messages to the lp8
    void lp8Talk(uint8_t ccByte){

            //transfer previous sensor state to the new msg out
            for (int u = 4; u < 23+4; u++) {
                stateWrite[u+2] = response[u];
                }
            //Set Calibration Control Byte (see end of page for explantion)
            stateWrite[5] = ccByte;
            //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_EN.write( 1 );                                                              //power on sensor
            timeIt( 0.35 );
            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
            timeIt( 0.35 );
            //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        
       };
        
//get value from lp8 response    
    unsigned long getValue()    /* CO2 Value */
    {
        int high    = response[29];
        int low     = response[30]; 
        unsigned long val = high * 256 + low; 
    
        return val;
    }
    
    double getTempValue()
    {
        int h    = response[33];
        int l    = response[34]; 
        unsigned long _temp = h * 256 + l; 
        
        double _tempVal = 0.01 * _temp;
          
        return _tempVal;
    }
    
    int getVcapValue(){
        
        int hB    = response[35];
        int lB    = response[36]; 
        unsigned long temp = hB * 256 + lB; 
        
        return temp;
    }
    
    uint32_t getErrorStatus(){
        
        uint32_t tmp  = 0;  
        tmp += response[39] << (32-8);
        tmp += response[40] << (32-16);
        tmp += response[41] << (32-24);
        tmp += response[42];
        
        return tmp;
    }   
    
    //get calculation Control byte from lp8 response
    uint8_t getCCbyte(){
        uint8_t responseCCbyte = response[0];
        return responseCCbyte;    
    }
    

/************************************************* Helper Functions ********************************************/

//purge response buffer
    void responsePurge(int bytesToPurge){
        for (int j = 0; j < bytesToPurge; j++) {
                response[j] = 0x00;
            }
        };
            
//read response from lp8 (not energy efficient...)   
    void Response(int bytesToRead ){
        lp8Wait.start();                    //poll rx line for 0.5 seconds
        do {
            if(Device.readable()) {
                response[counter] = Device.getc(); 
                counter++;
                }
            }
        while( lp8Wait.read() < 0.2 );
        counter = 0;
        lp8Wait.stop();
        lp8Wait.reset();                                                        
    };
    
//transmit data over serial lines    
    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;  
    };
    
//variables and buffers
private:
    //pins
    Serial      &Device;
    DigitalOut  &VBB_EN;    
    DigitalIn   &RDY;
    DigitalOut  &RES;

    Timer       &lp8Wait;
    
    //msg containers 
    uint8_t  firstWrite[8];     
    uint8_t  stateWrite[31]; 
    uint8_t  stateRead[7];
    uint8_t  response[60];

    //    
    int         co2;                                                                       //CO2 initial value 
    int         tempValue;  
    int         counter;                                                                   //simple counter 
    uint16_t    CRC;                                                                       //modbus crc value


};
#endif

/* 
LP8 Modbus 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;


--------------------------------------------------------------------------------
LP8 Background Calibration, Calculation Control Byte 
--------------------------------------------------------------------------------
To calibrate the lp8, commands needs to be sent with the calculation control byte
Calculation:
CC =
0x10 Initial measurement (filters reset, ABC sample reset and other,
0x20 Subsequent measurement,
0x40 Zero calibration using unfiltered data,
0x41 Zero calibration using filtered data,
0x42 Zero calibration using unfiltered data + reset filters,
0x43 Zero calibration using filtered data + reset filters,
0x50 Background calibration using unfiltered data,
0x51 Background calibration using filtered data,
0x52 Background calibration using unfiltered data + reset filters,
0x53 Background calibration using filtered data + reset filters,
0x70 ABC (based on filtered data),
0x72 ABC (based on filtered data) + reset filters


--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
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 
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
*/