BigW Consortium Gitlab

Commit 18378c94 by David Frey

improve ambient temperature data fed to BSEC

The BSEC temperature offset and ambient temperature required by the bme680 driver library are now supplied dynamically. Additionally, an openweathermap.org based ambient temperature API was added. This is of course only useful if the device will be outdoors.
parent 38f16aa7
......@@ -35,5 +35,6 @@ requires:
bindings:
{
bme680EnvironmentalSensor.bsecIntegrationComponent.ambient -> mcp9700aTemperatureSensor.mangOH_ambientTemperature
// bme680EnvironmentalSensor.bsecIntegrationComponent.ambient -> mcp9700aTemperatureSensor.mangOH_ambientTemperature
bme680EnvironmentalSensor.bsecIntegrationComponent.ambient -> openWeatherMapAmbientTemperature.mangOH_ambientTemperature
}
......@@ -56,6 +56,10 @@ le_result_t mangOH_bme680_Configure(
.sample_rate = enablePressure ? sr : BSEC_SAMPLE_RATE_DISABLED,
},
{
.sensor_id = BSEC_OUTPUT_RAW_TEMPERATURE,
.sample_rate = sr,
},
{
.sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
.sample_rate = enableTemperature ? sr : BSEC_SAMPLE_RATE_DISABLED,
},
......
......@@ -9,9 +9,6 @@
#define SAMPLES_BETWEEN_SAVES 200
#define BME680_I2C_ADDR 0x76
// TODO: Find a better way to manage this
static float Bme680TemperatureOffset = 9.0;
struct Bme680State _s;
int64_t GetTimestampNs(void)
......@@ -218,10 +215,13 @@ static void ReadData(
inputs[*numBsecInputs].time_stamp = timestampTrigger;
(*numBsecInputs)++;
/* Also add optional heatsource input which will be subtracted from the temperature reading to
* compensate for device-specific self-heating (supported in BSEC IAQ solution)*/
/*
* Also add optional heatsource input which will be subtracted from the temperature
* reading to compensate for device-specific self-heating (supported in BSEC IAQ
* solution)
*/
inputs[*numBsecInputs].sensor_id = BSEC_INPUT_HEATSOURCE;
inputs[*numBsecInputs].signal = Bme680TemperatureOffset;
inputs[*numBsecInputs].signal = _s.temperatureOffset;
inputs[*numBsecInputs].time_stamp = timestampTrigger;
(*numBsecInputs)++;
}
......@@ -283,6 +283,23 @@ static void ProcessData(bsec_input_t *bsecInputs, size_t numBsecInputs, int64_t
reading.breathVoc.value = bsecOutputs[i].signal;
reading.breathVoc.accuracy = bsecOutputs[i].accuracy;
break;
case BSEC_OUTPUT_RAW_TEMPERATURE:
/*
* Update the temperature offset based upon the raw temperature from the BME680 and
* the ambient temperature given by the ambient temperature API. It is assumed that
* the value from the ambient temperature is the true ambient temperature.
*/
{
double ambientTemperature;
le_result_t ambientRes = ambient_Read(&ambientTemperature);
if (ambientRes == LE_OK)
{
double delta = bsecOutputs[i].signal - ambientTemperature;
_s.temperatureOffset = (delta * 0.25) + (_s.temperatureOffset * 0.75);
LE_INFO("Temperature offset is now %f", _s.temperatureOffset);
}
}
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
reading.temperature.valid = true;
reading.temperature.value = bsecOutputs[i].signal;
......@@ -369,6 +386,22 @@ static void ClientSessionClosedHandler
}
}
int8_t ReadAmbientTemperature(void)
{
int8_t ambientInt = 20;
double ambientTemperature;
le_result_t ambientRes = ambient_Read(&ambientTemperature);
if (ambientRes == LE_OK)
{
ambientInt = (int8_t)round(ambientTemperature);
}
else
{
LE_ERROR("Retrieval of ambient temperture failed");
}
return ambientInt;
}
COMPONENT_INIT
{
......@@ -389,10 +422,7 @@ COMPONENT_INIT
LoadState();
double ambientTemperature;
le_result_t ambientRes = ambient_Read(&ambientTemperature);
LE_FATAL_IF(ambientRes != LE_OK, "Failed to read ambient temperature: %s", LE_RESULT_TXT(ambientRes));
_s.bme680 = bme680_linux_i2c_create(BME680_I2C_BUS, BME680_I2C_ADDR, (int8_t)ambientTemperature);
_s.bme680 = bme680_linux_i2c_create(BME680_I2C_BUS, BME680_I2C_ADDR, ReadAmbientTemperature);
LE_FATAL_IF(!_s.bme680, "Couldn't create bme680 device");
if (!_s.bme680) {
fprintf(stderr, "Failed to create device\n");
......
......@@ -21,6 +21,7 @@ struct Bme680State
bool enableHumidity;
le_mem_PoolRef_t HandlerPool;
le_ref_MapRef_t HandlerRefMap;
double temperatureOffset;
};
#endif // _BSEC_INTEGRATION_H_
sandboxed: true
start: auto
version: 1.0
executables:
{
owmAmbientTemperature = ( openWeatherMapAmbientTemperatureComponent )
}
processes:
{
run:
{
( owmAmbientTemperature )
}
faultAction: stopApp
envVars:
{
LE_LOG_LEVEL = DEBUG
}
}
extern:
{
owmAmbientTemperature.openWeatherMapAmbientTemperatureComponent.mangOH_ambientTemperature
}
requires:
{
configTree:
{
[r] .
}
}
bindings:
{
owmAmbientTemperature.openWeatherMapAmbientTemperatureComponent.le_data -> dataConnectionService.le_data
}
\ No newline at end of file
sources:
{
owmAmbientTemperature.c
}
provides:
{
api:
{
mangOH_ambientTemperature.api [manual-start]
}
}
requires:
{
api:
{
le_cfg.api
le_data.api
}
component:
{
openWeatherMap
}
}
\ No newline at end of file
#include "legato.h"
#include "interfaces.h"
#include "openWeatherMap.h"
/*
* TODO:
* We should be bound to a positioning service or provide a way for clients to set the position.
*/
static struct
{
double latitude;
double longitude;
} Location = {49.172781, -123.071147};
static char ApiKey[32 + 1];
static struct
{
int64_t timestampNs;
double temperature;
} LastResult;
static le_data_RequestObjRef_t DataRequest = NULL;
static int64_t GetTimestampNs(void)
{
struct timespec monotime;
LE_ASSERT(clock_gettime(CLOCK_MONOTONIC, &monotime) == 0);
return (monotime.tv_sec * 1000LL * 1000LL * 1000LL) + (monotime.tv_nsec);
}
le_result_t mangOH_ambientTemperature_Read(double *temperature)
{
// Use a cached value if the temperature was requested recently
const int64_t now = GetTimestampNs();
const int64_t minDelay = 60LL * 1000LL * 1000LL * 1000LL;
if (LastResult.timestampNs && ((now - LastResult.timestampNs) < minDelay))
{
*temperature = LastResult.temperature;
return LE_OK;
}
json_t *response = OpenWeatherMapGet(Location.latitude, Location.longitude, ApiKey);
if (!response)
{
return LE_FAULT;
}
const int unpackRes = json_unpack(response, "{s:{s:f}}", "main", "temp", temperature);
json_decref(response);
if (unpackRes != 0)
{
return LE_FAULT;
}
LE_DEBUG("Received ambient temperature reading: %f", *temperature);
LastResult.timestampNs = now;
LastResult.temperature = *temperature;
return LE_OK;
}
static void DcsStateHandler
(
const char *intfName,
bool isConnected,
void *context
)
{
if (isConnected)
{
LE_INFO("Data connection established using interface %s", intfName);
// Now that we have a connection, advertise the ambient temperature service
mangOH_ambientTemperature_AdvertiseService();
}
else
{
LE_WARN("Data connection lost on interface %s", intfName);
}
}
COMPONENT_INIT
{
const le_result_t cfgRes = le_cfg_QuickGetString(
"/ApiKey", ApiKey, sizeof(ApiKey), "");
LE_FATAL_IF(
cfgRes != LE_OK || ApiKey[0] == '\0',
"Failed to read OpenWeatherMap API Key from config tree");
le_data_AddConnectionStateHandler(DcsStateHandler, NULL);
DataRequest = le_data_Request();
}
Subproject commit b79dcfd1968a127400e9d6bf4b422cff4df90f01
Subproject commit 57dd5365a23a61412f6966088ba4a1b6f3bdad95
......@@ -107,7 +107,8 @@ static int8_t bme680_linux_i2c_write(uint8_t reg_addr, const uint8_t *reg_data,
return res;
}
struct bme680_linux* bme680_linux_i2c_create(unsigned i2c_bus_num, uint8_t i2c_addr, int8_t ambient_temperature)
struct bme680_linux* bme680_linux_i2c_create(
unsigned i2c_bus_num, uint8_t i2c_addr, bme680_ambient_temperature_fptr_t read_ambient_temperature)
{
struct bme680_linux *bme680 = calloc(sizeof(*bme680), 1);
if (!bme680)
......@@ -122,7 +123,7 @@ struct bme680_linux* bme680_linux_i2c_create(unsigned i2c_bus_num, uint8_t i2c_a
bme680->dev.read = bme680_linux_i2c_read;
bme680->dev.write = bme680_linux_i2c_write;
bme680->dev.delay_ms = bme680_linux_delay_ms;
bme680->dev.amb_temp = ambient_temperature;
bme680->dev.read_ambient_temperature = read_ambient_temperature;
// Initialize private data required for read/write/delay_ms
bme680->priv->i2c_bus_num = i2c_bus_num;
......
......@@ -13,7 +13,8 @@ struct bme680_linux
bme680_linux_priv* priv;
};
struct bme680_linux* bme680_linux_i2c_create(unsigned i2c_bus_num, uint8_t i2c_addr, int8_t ambient_temperature);
struct bme680_linux* bme680_linux_i2c_create(
unsigned i2c_bus_num, uint8_t i2c_addr, bme680_ambient_temperature_fptr_t read_ambient_temperature);
void bme680_linux_i2c_destroy(struct bme680_linux* bme680);
#endif // _BME680_LINUX_I2C_H_
sources:
{
openWeatherMap.c
}
cflags:
{
-I$LEGATO_BUILD/framework/libjansson/include
}
ldflags:
{
-ljansson
}
provides:
{
headerDir:
{
$LEGATO_BUILD/framework/libjansson/include
${CURDIR}
}
}
requires:
{
lib:
{
curl
}
component:
{
${LEGATO_ROOT}/components/3rdParty/curl
}
file:
{
/legato/systems/current/lib/libjansson.so /usr/lib/
/*
* The below list of required files was copied from the Combain app. Are all of these really
* necessary? Definitely some of them are, but perhaps not all.
*/
/lib/libz.so.1 /lib/
/lib/libnss_dns.so.2 /lib/
/lib/libnss_dns-2.24.so /lib/
/lib/libresolv.so.2 /lib/
/lib/libresolv-2.24.so /lib/
/etc/resolv.conf /etc/
/etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
/usr/bin/curl /usr/bin/
}
}
cflags:
{
-std=c99
-I${LEGATO_BUILD}/framework/libjansson/include
}
#include "legato.h"
#include <curl/curl.h>
#include "openWeatherMap.h"
struct MemoryStruct {
size_t size;
char *memory;
};
static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
const size_t realSize = size * nmemb;
struct MemoryStruct *mem = (struct MemoryStruct *)userp;
char *ptr = realloc(mem->memory, mem->size + realSize + 1);
LE_ASSERT(ptr);
mem->memory = ptr;
memcpy(&(mem->memory[mem->size]), contents, realSize);
mem->size += realSize;
mem->memory[mem->size] = 0;
return realSize;
}
json_t *OpenWeatherMapGet(double latitude, double longitude, const char *apiKey)
{
json_t *ret = NULL;
char url[256];
int res = snprintf(
url,
sizeof(url),
"https://api.openweathermap.org/data/2.5/weather?appid=%s&mode=json&units=metric&lat=%f&lon=%f",
apiKey,
latitude,
longitude);
LE_ASSERT(res >= 0);
LE_ASSERT(res < sizeof(url));
CURL *curl = curl_easy_init();
LE_ASSERT(curl);
LE_ASSERT(curl_easy_setopt(curl, CURLOPT_URL, url) == CURLE_OK);
/* send all data to this function */
LE_ASSERT(curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback) == CURLE_OK);
/* we pass our 'chunk' struct to the callback function */
struct MemoryStruct chunk;
chunk.memory = malloc(1); /* will be grown as needed by the realloc above */
chunk.size = 0;
LE_ASSERT(curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk) == CURLE_OK);
CURLcode curlRes = curl_easy_perform(curl);
if(curlRes != CURLE_OK)
{
LE_ERROR("curl_easy_perform() failed: %s", curl_easy_strerror(res));
goto cleanup;
}
json_error_t error;
size_t flags = 0;
ret = json_loads(chunk.memory, flags, &error);
if (!ret)
{
LE_ERROR("Failed to parse response as json: %s", (const char *)chunk.memory);
goto cleanup;
}
cleanup:
free(chunk.memory);
curl_easy_cleanup(curl);
return ret;
}
COMPONENT_INIT
{
LE_FATAL_IF(curl_global_init(CURL_GLOBAL_DEFAULT) != CURLE_OK, "Failed to initialize libcurl");
}
#ifndef _OPEN_WEATHER_MAP_H_
#define _OPEN_WEATHER_MAP_H_
#include <jansson.h>
LE_SHARED json_t *OpenWeatherMapGet(double latitude, double longitude, const char *apiKey);
#endif // _OPEN_WEATHER_MAP_H_
......@@ -60,6 +60,7 @@ apps:
$MANGOH_ROOT/apps/Mcp9700aTemperatureSensor/mcp9700aTemperatureSensor
$MANGOH_ROOT/apps/Bme680EnvironmentalSensor/bme680EnvironmentalSensor
$MANGOH_ROOT/samples/BsecTest/bsecTest
$MANGOH_ROOT/apps/OpenWeatherMapAmbientTemperature/openWeatherMapAmbientTemperature
#endif // MANGOH_BOARD
......@@ -84,6 +85,7 @@ commands:
interfaceSearch:
{
$MANGOH_ROOT/interfaces
#if ${MANGOH_BOARD} = red
$MANGOH_ROOT/apps/BatteryService
......@@ -95,7 +97,6 @@ interfaceSearch:
#elif ${MANGOH_BOARD} = yellow
$LEGATO_ROOT/apps/sample/dataHub
$MANGOH_ROOT/apps/Mcp9700aTemperatureSensor
$MANGOH_ROOT/apps/Bme680EnvironmentalSensor
#endif // MANGOH_BOARD
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment