Skip to content
Snippets Groups Projects
bsec.c 11 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* Adapted from  bsec_iot_example.c and bsec_iot_ulp_plus_example.c */
    
    #include "card10.h"
    #include "bosch.h"
    
    schneider's avatar
    schneider committed
    #include "bsec_integration.h"
    
    
    #include "epicardium.h"
    #include "modules.h"
    #include "modules/log.h"
    
    #include "FreeRTOS.h"
    #include "task.h"
    
    #include "max32665.h"
    #include "gcr_regs.h"
    
    #include <stdio.h>
    
    
    TaskHandle_t bsec_task_id;
    
    static struct bme680_sensor_data last_bme680_data;
    static int64_t last_bme680_timestamp;
    
    schneider's avatar
    schneider committed
    #define ULP 0
    
    // From generic_18v_3s_4d/bsec_serialized_configurations_iaq.c
    
    static const uint8_t bsec_config_generic_18v_3s_4d[454] = {
    	4,   7,   4,   1,   61,  0,   0,   0,   0,   0,   0,   0,   174, 1,
    	0,   0,   48,  0,   1,   0,   0,   192, 168, 71,  64,  49,  119, 76,
    	0,   0,   225, 68,  137, 65,  0,   63,  205, 204, 204, 62,  0,   0,
    	64,  63,  205, 204, 204, 62,  0,   0,   0,   0,   216, 85,  0,   100,
    	0,   0,   0,   0,   0,   0,   0,   0,   28,  0,   2,   0,   0,   244,
    	1,   225, 0,   25,  0,   0,   128, 64,  0,   0,   32,  65,  144, 1,
    	0,   0,   112, 65,  0,   0,   0,   63,  16,  0,   3,   0,   10,  215,
    	163, 60,  10,  215, 35,  59,  10,  215, 35,  59,  9,   0,   5,   0,
    	0,   0,   0,   0,   1,   88,  0,   9,   0,   7,   240, 150, 61,  0,
    	0,   0,   0,   0,   0,   0,   0,   28,  124, 225, 61,  52,  128, 215,
    	63,  0,   0,   160, 64,  0,   0,   0,   0,   0,   0,   0,   0,   205,
    	204, 12,  62,  103, 213, 39,  62,  230, 63,  76,  192, 0,   0,   0,
    	0,   0,   0,   0,   0,   145, 237, 60,  191, 251, 58,  64,  63,  177,
    	80,  131, 64,  0,   0,   0,   0,   0,   0,   0,   0,   93,  254, 227,
    	62,  54,  60,  133, 191, 0,   0,   64,  64,  12,  0,   10,  0,   0,
    	0,   0,   0,   0,   0,   0,   0,   229, 0,   254, 0,   2,   1,   5,
    	48,  117, 100, 0,   44,  1,   112, 23,  151, 7,   132, 3,   197, 0,
    	92,  4,   144, 1,   64,  1,   64,  1,   144, 1,   48,  117, 48,  117,
    	48,  117, 48,  117, 100, 0,   100, 0,   100, 0,   48,  117, 48,  117,
    	48,  117, 100, 0,   100, 0,   48,  117, 48,  117, 100, 0,   100, 0,
    	100, 0,   100, 0,   48,  117, 48,  117, 48,  117, 100, 0,   100, 0,
    	100, 0,   48,  117, 48,  117, 100, 0,   100, 0,   44,  1,   44,  1,
    	44,  1,   44,  1,   44,  1,   44,  1,   44,  1,   44,  1,   44,  1,
    	44,  1,   44,  1,   44,  1,   44,  1,   44,  1,   8,   7,   8,   7,
    	8,   7,   8,   7,   8,   7,   8,   7,   8,   7,   8,   7,   8,   7,
    	8,   7,   8,   7,   8,   7,   8,   7,   8,   7,   112, 23,  112, 23,
    	112, 23,  112, 23,  112, 23,  112, 23,  112, 23,  112, 23,  112, 23,
    	112, 23,  112, 23,  112, 23,  112, 23,  112, 23,  255, 255, 255, 255,
    	255, 255, 255, 255, 220, 5,   220, 5,   220, 5,   255, 255, 255, 255,
    	255, 255, 220, 5,   220, 5,   255, 255, 255, 255, 255, 255, 255, 255,
    	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
    	255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 44,  1,   0,   0,
    	0,   0,   52,  233, 0,   0
    };
    
    /*!
     * @brief           Capture the system time in microseconds
     *
     * @return          system_current_time    current system timestamp in microseconds
     */
    int64_t get_timestamp_us()
    {
    
    	int64_t tick = xTaskGetTickCount();
    
    schneider's avatar
    schneider committed
    	return tick * 1000;
    
    }
    
    /*!
     * @brief           Handling of the ready outputs
     *
     * @param[in]       timestamp       time in nanoseconds
     * @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
     *
     * @return          none
     */
    
    schneider's avatar
    schneider committed
    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
    ) {
    
    	last_bme680_data.temperature    = temperature;
    	last_bme680_data.humidity       = humidity;
    	last_bme680_data.pressure       = pressure / 100.;
    	last_bme680_data.gas_resistance = gas;
    	last_bme680_timestamp           = timestamp;
    
    schneider's avatar
    schneider committed
    	return;
    	printf("bosch data time: %u, iaq: %u, iaq_a: %u, temp10: %u, hum10: %u, pres: %u, raw_temp10: %u, raw_hum10: %u, gas: %u, static_iaq: %u, co21e6: %u, breath_voc1e6: %u\n",
    
    	       (unsigned int)(timestamp / 1e6),
    
    schneider's avatar
    schneider committed
    	       (unsigned int)(iaq),
    	       (unsigned int)(iaq_accuracy),
    	       (unsigned int)(temperature * 10),
    	       (unsigned int)(humidity * 10),
    	       (unsigned int)(pressure),
    	       (unsigned int)(raw_temperature * 10),
    	       (unsigned int)(raw_humidity * 10),
    	       (unsigned int)(gas),
    	       (unsigned int)(static_iaq),
    	       (unsigned int)(co2_equivalent * 1e6),
    	       (unsigned int)(breath_voc_equivalent * 1e6));
    
    static uint32_t bsec_load(char *path, uint8_t *buffer, uint32_t n_buffer)
    
    schneider's avatar
    schneider committed
    	uint32_t len = 0;
    	int fd, res;
    
    	printf("BSEC load %s %lu\n", path, n_buffer);
    
    
    	if ((fd = epic_file_open(path, "r")) < 0) {
    
    schneider's avatar
    schneider committed
    		printf("Open failed\n");
    		return 0;
    	}
    
    schneider's avatar
    schneider committed
    	uint32_t header;
    	if ((res = epic_file_read(fd, &header, sizeof(header))) !=
    	    sizeof(header)) {
    		printf("Header failed\n");
    		goto done;
    	}
    
    schneider's avatar
    schneider committed
    	if (header > n_buffer) {
    		printf("Too large\n");
    		goto done;
    	}
    
    	if (epic_file_read(fd, buffer, header) != (int)header) {
    
    schneider's avatar
    schneider committed
    		printf("Read failed\n");
    		goto done;
    	}
    
    schneider's avatar
    schneider committed
    	len = header;
    
    schneider's avatar
    schneider committed
    	printf("Success\n");
    done:
    
    	epic_file_close(fd);
    
    schneider's avatar
    schneider committed
    	return len;
    
    }
    /*!
     * @brief           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
     */
    uint32_t state_load(uint8_t *state_buffer, uint32_t n_buffer)
    {
    
    schneider's avatar
    schneider committed
    	return bsec_load("bsec_iaq.state", state_buffer, n_buffer);
    
    }
    
    /*!
     * @brief           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)
    {
    
    schneider's avatar
    schneider committed
    	int fd, res;
    
    schneider's avatar
    schneider committed
    	printf("BSEC state_save %d\n", (int)length);
    
    
    	if ((fd = epic_file_open("bsec_iaq.state", "w")) < 0) {
    
    schneider's avatar
    schneider committed
    		printf("Open failed\n");
    		return;
    	}
    
    schneider's avatar
    schneider committed
    	uint32_t header = length;
    	if ((res = epic_file_write(fd, &header, sizeof(header))) !=
    	    sizeof(header)) {
    		printf("Header failed\n");
    		goto done;
    	}
    
    	if (epic_file_write(fd, state_buffer, header) != (int)header) {
    
    schneider's avatar
    schneider committed
    		printf("Write failed\n");
    		goto done;
    	}
    
    schneider's avatar
    schneider committed
    	printf("Success\n");
    done:
    
    	epic_file_close(fd);
    }
    
    
    static int8_t
    i2c_write(uint8_t addr, uint8_t reg, uint8_t *p_buf, uint16_t size)
    {
    	int8_t ret;
    	hwlock_acquire(HWLOCK_I2C);
    	ret = card10_bosch_i2c_write(addr, reg, p_buf, size);
    	hwlock_release(HWLOCK_I2C);
    	return ret;
    }
    
    
    static int8_t i2c_read(uint8_t addr, uint8_t reg, uint8_t *p_buf, uint16_t size)
    
    {
    	int8_t ret;
    	hwlock_acquire(HWLOCK_I2C);
    	ret = card10_bosch_i2c_read(addr, reg, p_buf, size);
    	hwlock_release(HWLOCK_I2C);
    	return ret;
    }
    
    
    static void delay(uint32_t msec)
    {
    	if (xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED) {
    		/* We need to fall back to hardware waits if not running
    		 * in a task context */
    		card10_bosch_delay(msec);
    	} else {
    		vTaskDelay(pdMS_TO_TICKS(msec));
    	}
    }
    
    
    /*!
     * @brief           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
     */
    uint32_t config_load(uint8_t *config_buffer, uint32_t n_buffer)
    {
    
    	uint32_t len = bsec_load("bsec_iaq.config", config_buffer, n_buffer);
    
    	if (len == 0) {
    		printf("Using default bsec_config_generic_18v_3s_4d\n");
    		len = sizeof(bsec_config_generic_18v_3s_4d);
    		memcpy(config_buffer, bsec_config_generic_18v_3s_4d, len);
    	}
    
    	return len;
    
    schneider's avatar
    schneider committed
    #if ULP
    void ulp_plus_trigger_iaq()
    
    schneider's avatar
    schneider committed
    	/* We call bsec_update_subscription() in order to instruct BSEC to perform an extra measurement at the next
    
         * possible time slot
         */
    
    
    schneider's avatar
    schneider committed
    	bsec_sensor_configuration_t requested_virtual_sensors[1];
    	uint8_t n_requested_virtual_sensors = 1;
    	bsec_sensor_configuration_t
    		required_sensor_settings[BSEC_MAX_PHYSICAL_SENSOR];
    	uint8_t n_required_sensor_settings = BSEC_MAX_PHYSICAL_SENSOR;
    	bsec_library_return_t status       = BSEC_OK;
    
    	/* To trigger a ULP plus, we request the IAQ virtual sensor with a specific sample rate code */
    	requested_virtual_sensors[0].sensor_id = BSEC_OUTPUT_IAQ;
    	requested_virtual_sensors[0].sample_rate =
    		BSEC_SAMPLE_RATE_ULP_MEASUREMENT_ON_DEMAND;
    
    	/* Call bsec_update_subscription() to enable/disable the requested virtual sensors */
    	status = bsec_update_subscription(
    		requested_virtual_sensors,
    		n_requested_virtual_sensors,
    		required_sensor_settings,
    		&n_required_sensor_settings
    	);
    
    schneider's avatar
    schneider committed
    	/* The status code would tell is if the request was accepted. It will be rejected if the sensor is not already in
    
         * ULP mode, or if the time difference between requests is too short, for example. */
    }
    #endif
    
    
    bool bsec_active(void)
    {
    
    }
    
    int bsec_read_bme680(struct bme680_sensor_data *data)
    {
    
    		return BME680_E_COM_FAIL;
    	}
    	while (last_bme680_timestamp == 0)
    		vTaskDelay(pdMS_TO_TICKS(10));
    	*data = last_bme680_data;
    	return BME680_OK;
    }
    
    
    /**
     * Checks config and activates the BSEC libary if requested.
     *
     * Initializes the BSEC library before starting the task to
     * reduce the stack size needed for the task by at least 512 bytes
     */
    int bsec_activate(void)
    
    schneider's avatar
    schneider committed
    	return_values_init ret;
    
    schneider's avatar
    schneider committed
    #if ULP
    	float sample_rate = BSEC_SAMPLE_RATE_ULP;
    
    #else
    
    schneider's avatar
    schneider committed
    	float sample_rate = BSEC_SAMPLE_RATE_LP;
    
    #endif
    
    schneider's avatar
    schneider committed
    
    	float temperature_offset = 0.0;
    
    
    	/* Puts AT LEAST 2 * #BSEC_MAX_PROPERTY_BLOB_SIZE = 2 * 454 = 908 bytes onto the stack */
    	ret = bsec_iot_init(
    		sample_rate,
    		temperature_offset,
    		i2c_write,
    		i2c_read,
    		delay,
    		state_load,
    		config_load
    
    schneider's avatar
    schneider committed
    	if (ret.bme680_status) {
    
    		printf("bme680 init failed\n");
    
    		/* Could not intialize BME680 */
    
    schneider's avatar
    schneider committed
    	} else if (ret.bsec_status) {
    
    		printf("bsec init failed\n");
    
    schneider's avatar
    schneider committed
    		/* Could not intialize BSEC library */
    
    schneider's avatar
    schneider committed
    	}
    
    	return 0;
    }
    
    void vBSECTask(void *pvParameters)
    {
    	bsec_task_active = true;
    	bsec_task_id     = xTaskGetCurrentTaskHandle();
    
    #if ULP
    	/* State is saved every 100 samples, which means every 100 * 300 secs = 500 minutes  */
    	const int stat_save_interval = 100;
    #else
    	/* State is saved every 10.000 samples, which means every 10.000 * 3 secs = 500 minutes  */
    	const int stat_save_interval = 10000;
    #endif
    
    schneider's avatar
    schneider committed
    	/* Call to endless loop function which reads and processes data based on sensor settings */
    
    	/* Puts AT LEAST 2 * BSEC_MAX_STATE_BLOB_SIZE + 8 * sizeof(bsec_input_t) =
    	 * 2 * 139 + 8 * 20 = 438 bytes onto the stack */
    
    schneider's avatar
    schneider committed
    	bsec_iot_loop(
    		delay,
    		get_timestamp_us,
    		output_ready,
    		state_save,
    		stat_save_interval
    	);