Skip to content

Commit

Permalink
Added vsec_bme680.c with modifications on output and checkpoint interval
Browse files Browse the repository at this point in the history
  • Loading branch information
lokijota committed Jan 22, 2020
1 parent 5408782 commit 9a97a8d
Showing 1 changed file with 368 additions and 0 deletions.
368 changes: 368 additions & 0 deletions bsec_bme680.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,368 @@
/* Copyright (C) 2017 alexh.name */
/* I2C code by twartzek 2017 */

/*
* Read the BME680 sensor with the BSEC library by running an endless loop in
* the bsec_iot_loop() function under Linux.
*
*/

/*#define _POSIX_C_SOURCE 200809L*/
#define _XOPEN_SOURCE 700

/* header files */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/i2c-dev.h>
#include "bsec_integration.h"

/* definitions */

#define DESTZONE "TZ=Europe/Berlin"
#define temp_offset (5.0f)
#define sample_rate_mode (BSEC_SAMPLE_RATE_LP)

int g_i2cFid; // I2C Linux device handle

int i2#_address = BME600_I2C_ADDR_SECONDARY; // was BME680_I2C_ADDR_PRIMARY - 76,secundary is 77
int i2c_address = BME680_I2C_ADDR_PRIMARY;
char *filename_state = "bsec_iaq.state";
char *filename_config = "bsec_iaq.config";

/* functions */

// open the Linux device
void i2cOpen()
{
g_i2cFid = open("/dev/i2c-1", O_RDWR);
if (g_i2cFid < 0) {
perror("i2cOpen");
exit(1);
}
}

// close the Linux device
void i2cClose()
{
close(g_i2cFid);
}

// set the I2C slave address for all subsequent I2C device transfers
void i2cSetAddress(int address)
{
if (ioctl(g_i2cFid, I2C_SLAVE, address) < 0) {
perror("i2cSetAddress");
exit(1);
}
}

/*
* Write operation in either I2C or SPI
*
* param[in] dev_addr I2C or SPI device address
* param[in] reg_addr register address
* param[in] reg_data_ptr pointer to the data to be written
* param[in] data_len number of bytes to be written
*
* return result of the bus communication function
*/
int8_t bus_write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data_ptr,
uint16_t data_len)
{
int8_t rslt = 0; /* Return 0 for Success, non-zero for failure */

uint8_t reg[16];
reg[0]=reg_addr;
int i;

for (i=1; i<data_len+1; i++)
reg[i] = reg_data_ptr[i-1];

if (write(g_i2cFid, reg, data_len+1) != data_len+1) {
perror("user_i2c_write");
rslt = 1;
exit(1);
}

return rslt;
}

/*
* Read operation in either I2C or SPI
*
* param[in] dev_addr I2C or SPI device address
* param[in] reg_addr register address
* param[out] reg_data_ptr pointer to the memory to be used to store
* the read data
* param[in] data_len number of bytes to be read
*
* return result of the bus communication function
*/
int8_t bus_read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *reg_data_ptr,
uint16_t data_len)
{
int8_t rslt = 0; /* Return 0 for Success, non-zero for failure */

uint8_t reg[1];
reg[0]=reg_addr;

if (write(g_i2cFid, reg, 1) != 1) {
perror("user_i2c_read_reg");
rslt = 1;
}

if (read(g_i2cFid, reg_data_ptr, data_len) != data_len) {
perror("user_i2c_read_data");
rslt = 1;
}

return rslt;
}

/*
* System specific implementation of sleep function
*
* param[in] t_ms time in milliseconds
*
* return none
*/
void _sleep(uint32_t t_ms)
{
struct timespec ts;
ts.tv_sec = 0;
/* mod because nsec must be in the range 0 to 999999999 */
ts.tv_nsec = (t_ms % 1000) * 1000000L;
nanosleep(&ts, NULL);
}

/*
* Capture the system time in microseconds
*
* return system_current_time system timestamp in microseconds
*/
int64_t get_timestamp_us()
{
struct timespec spec;
//clock_gettime(CLOCK_REALTIME, &spec);
/* MONOTONIC in favor of REALTIME to avoid interference by time sync. */
clock_gettime(CLOCK_MONOTONIC, &spec);

int64_t system_current_time_ns = (int64_t)(spec.tv_sec) * (int64_t)1000000000
+ (int64_t)(spec.tv_nsec);
int64_t system_current_time_us = system_current_time_ns / 1000;

return system_current_time_us;
}

/*
* Handling of the ready outputs
*
* param[in] timestamp time in microseconds
* param[in] iaq IAQ signal
* param[in] iaq_accuracy accuracy of IAQ signal
* param[in] temperature temperature signal
* param[in] humidity humidity signal
* param[in] pressure pressure signal
* param[in] raw_temperature raw temperature signal
* param[in] raw_humidity raw humidity signal
* param[in] gas raw gas sensor signal
* param[in] bsec_status value returned by the bsec_do_steps() call
* param[in] static_iaq unscaled indoor-air-quality estimate
* param[in] co2_equivalent CO2 equivalent estimate [ppm]
* param[in] breath_voc_equivalent breath VOC concentration estimate [ppm]
*
* return none
*/
void output_ready(int64_t timestamp, float iaq, uint8_t iaq_accuracy,
float temperature, float humidity, float pressure,
float raw_temperature, float raw_humidity, float gas,
bsec_library_return_t bsec_status,
float static_iaq, float co2_equivalent,
float breath_voc_equivalent)
{
//int64_t timestamp_s = timestamp / 1000000000;
////int64_t timestamp_ms = timestamp / 1000;

//time_t t = timestamp_s;
/*
* timestamp for localtime only makes sense if get_timestamp_us() uses
* CLOCK_REALTIME
*/
time_t t = time(NULL);
struct tm tm = *localtime(&t);

FILE *fp;
char filename[50];
sprintf(filename, "./data/%d%02d%02d-%02d%02d%02d.csv", tm.tm_year + 1900,tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
fp = fopen (filename,"w");

fprintf(fp,"readingtime,deviceid,iaqaccuracy,iaqstate,temperature,humidity,pressure,gas,staticiaq,eco2ppm,bvocppm,notes,status\n"); /* print csv header */

fprintf(fp,"%d-%02d-%02d %02d:%02d:%02d", tm.tm_year + 1900,tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); /* localtime */
fprintf(fp,",'bme680'");
fprintf(fp,",%d, %.2f", iaq_accuracy, iaq);
fprintf(fp,",%.2f, %.2f, %.2f", temperature, humidity, pressure / 100);
fprintf(fp,",%.0f", gas);
fprintf(fp,",%.2f", static_iaq);
fprintf(fp,",%.15f", co2_equivalent);
fprintf(fp,",%.25f", breath_voc_equivalent);

//printf(",%" PRId64, timestamp);
//printf(",%" PRId64, timestamp_ms);

fprintf(fp,",'generated by bsec bme680' ");
fprintf(fp,",%d", bsec_status);
fprintf(fp, "\r\n");

fflush(fp);
fclose(fp);

fflush(stdout);
}

/*
* Load binary file from non-volatile memory into buffer
*
* param[in,out] state_buffer buffer to hold the loaded data
* param[in] n_buffer size of the allocated buffer
* param[in] filename name of the file on the NVM
* param[in] offset offset in bytes from where to start copying
* to buffer
* return number of bytes copied to buffer or zero on failure
*/
uint32_t binary_load(uint8_t *b_buffer, uint32_t n_buffer, char *filename,
uint32_t offset)
{
int32_t copied_bytes = 0;
int8_t rslt = 0;

struct stat fileinfo;
rslt = stat(filename, &fileinfo);
if (rslt != 0) {
fprintf(stderr,"stat'ing binary file %s: ",filename);
perror("");
return 0;
}

uint32_t filesize = fileinfo.st_size - offset;

if (filesize > n_buffer) {
fprintf(stderr,"%s: %d > %d\n", "binary data bigger than buffer", filesize,
n_buffer);
return 0;
} else {
FILE *file_ptr;
file_ptr = fopen(filename,"rb");
if (!file_ptr) {
perror("fopen");
return 0;
}
fseek(file_ptr,offset,SEEK_SET);
copied_bytes = fread(b_buffer,sizeof(char),filesize,file_ptr);
if (copied_bytes == 0) {
fprintf(stderr,"%s empty\n",filename);
}
fclose(file_ptr);
return copied_bytes;
}
}

/*
* Load previous library state from non-volatile memory
*
* param[in,out] state_buffer buffer to hold the loaded state string
* param[in] n_buffer size of the allocated state buffer
*
* return number of bytes copied to state_buffer or zero on failure
*/
uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer)
{
int32_t rslt = 0;
rslt = binary_load(state_buffer, n_buffer, filename_state, 0);
return rslt;
}

/*
* Save library state to non-volatile memory
*
* param[in] state_buffer buffer holding the state to be stored
* param[in] length length of the state string to be stored
*
* return none
*/
void state_save(const uint8_t *state_buffer, uint32_t length)
{
FILE *state_w_ptr;
state_w_ptr = fopen(filename_state,"wb");
fwrite(state_buffer,length,1,state_w_ptr);
fclose(state_w_ptr);
}

/*
* Load library config from non-volatile memory
*
* param[in,out] config_buffer buffer to hold the loaded state string
* param[in] n_buffer size of the allocated state buffer
*
* return number of bytes copied to config_buffer or zero on failure
*/
uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer)
{
int32_t rslt = 0;
/*
* Provided config file is 4 bytes larger than buffer.
* Apparently skipping the first 4 bytes works fine.
*
*/
rslt = binary_load(config_buffer, n_buffer, filename_config, 4);
return rslt;
}

/* main */

/*
* Main function which configures BSEC library and then reads and processes
* the data from sensor based on timer ticks
*
* return result of the processing
*/
int main()
{
putenv(DESTZONE); // Switch to destination time zone

i2cOpen();
i2cSetAddress(i2c_address);

return_values_init ret;

ret = bsec_iot_init(sample_rate_mode, temp_offset, bus_write, bus_read, _sleep, state_load, config_load);

if (ret.bme680_status) {
/* Could not intialize BME680 */
return (int)ret.bme680_status;
} else if (ret.bsec_status) {
/* Could not intialize BSEC library */
return (int)ret.bsec_status;
}

/* Call to endless loop function which reads and processes data based on
* sensor settings.
* State is saved every 10.000 samples, which means every 10.000 * 3 secs
* = 500 minutes (depending on the config).
* NOTAJOTA: CHANGED TO 4 hours = 4800
*
*/
bsec_iot_loop(_sleep, get_timestamp_us, output_ready, state_save, 4800);

i2cClose();
return 0;
}

0 comments on commit 9a97a8d

Please sign in to comment.