Reading SenseAir LP8 CO2 sensor over bluetooth low energy
Dependencies: BLE_API mbed nRF51822
Revision 0:ee3787c8e209, committed 2017-04-21
- Comitter:
- jony1401
- Date:
- Fri Apr 21 13:26:06 2017 +0000
- Child:
- 1:b512a405b584
- Commit message:
- alpha v 1.0
Changed in this revision
--- /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