BigW Consortium Gitlab

Commit 5b78beb2 by David Frey

bme680: use userspace library

Use the bme680 driver library provided by Bosch and package it as a Legato component. This provides a more complete driver implementation than the kernel module that was being used previously. Also integrate the proprietary BSEC library from Bosch to get more intelligence out of the sensor.
parent 94cf4bf4
......@@ -53,3 +53,6 @@
[submodule "apps/yellow/DataHub-Buzzer"]
path = apps/yellow/DataHub-Buzzer
url = https://github.com/mangOH/DataHub-Buzzer.git
[submodule "components/boschBme680Driver/Bosch_BME680_Driver"]
path = components/boschBme680Driver/Bosch_BME680_Driver
url = https://github.com/mangOH/Bosch_BME680_Driver.git
# BME680 Environmental Sensor
The environment variable `$BME680_I2C_BUS` needs to be defined to an interger (e.g. `6` for wp76)
which is the I2C bus that the BME680 environmental sensor is on.
sandboxed: true
version: 1.0.0
start: auto
executables:
{
bme680EnvironmentalSensor = ( bsecIntegrationComponent )
}
processes:
{
envVars:
{
LE_LOG_LEVEL = DEBUG
}
run:
{
( bme680EnvironmentalSensor )
}
faultAction: restart
}
extern:
{
bme680EnvironmentalSensor.bsecIntegrationComponent.mangOH_bme680
}
requires:
{
device:
{
[rw] /dev/i2c-${BME680_I2C_BUS} /dev/
}
}
bindings:
{
bme680EnvironmentalSensor.bsecIntegrationComponent.ambient -> mcp9700aTemperatureSensor.mangOH_ambientTemperature
}
sources:
{
bsecIntegration.c
bme680ApiImpl.c
}
cflags:
{
-std=c99
-DBME680_I2C_BUS=${BME680_I2C_BUS}
}
provides:
{
api:
{
mangOH_bme680.api
}
}
requires:
{
api:
{
ambient = mangOH_ambientTemperature.api
}
component:
{
boschBme680Driver
boschBsec
}
}
#include "legato.h"
#include "interfaces.h"
#include "bsec_interface.h"
#include "bsecIntegration.h"
extern struct Bme680State _s;
le_result_t mangOH_bme680_Configure(
mangOH_bme680_SamplingRate_t samplingRate,
bool enableIaq,
bool enableCo2Equivalent,
bool enableBreathVoc,
bool enablePressure,
bool enableTemperature,
bool enableHumidity)
{
float sr;
switch (samplingRate)
{
case MANGOH_BME680_SAMPLING_RATE_DISABLED:
sr = BSEC_SAMPLE_RATE_DISABLED;
break;
case MANGOH_BME680_SAMPLING_RATE_LP:
sr = BSEC_SAMPLE_RATE_LP;
break;
case MANGOH_BME680_SAMPLING_RATE_ULP:
sr = BSEC_SAMPLE_RATE_ULP;
break;
default:
return LE_BAD_PARAMETER;
}
_s.enableIaq = enableIaq;
_s.enableCo2Equivalent = enableCo2Equivalent;
_s.enableBreathVoc = enableBreathVoc;
_s.enablePressure = enablePressure;
_s.enableTemperature = enableTemperature;
_s.enableHumidity = enableHumidity;
bsec_sensor_configuration_t virtualOutputs[] = {
{
.sensor_id = BSEC_OUTPUT_IAQ,
.sample_rate = enableIaq ? sr : BSEC_SAMPLE_RATE_DISABLED,
},
{
.sensor_id = BSEC_OUTPUT_CO2_EQUIVALENT,
.sample_rate = enableCo2Equivalent ? sr : BSEC_SAMPLE_RATE_DISABLED,
},
{
.sensor_id = BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
.sample_rate = enableBreathVoc ? sr : BSEC_SAMPLE_RATE_DISABLED,
},
{
.sensor_id = BSEC_OUTPUT_RAW_PRESSURE,
.sample_rate = enablePressure ? sr : BSEC_SAMPLE_RATE_DISABLED,
},
{
.sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
.sample_rate = enableTemperature ? sr : BSEC_SAMPLE_RATE_DISABLED,
},
{
.sensor_id = BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
.sample_rate = enableHumidity ? sr : BSEC_SAMPLE_RATE_DISABLED,
},
};
bsec_sensor_configuration_t requiredSensorSettings[BSEC_MAX_PHYSICAL_SENSOR];
uint8_t numRequiredSensorSettings = BSEC_MAX_PHYSICAL_SENSOR;
bsec_library_return_t status = bsec_update_subscription(
virtualOutputs,
NUM_ARRAY_MEMBERS(virtualOutputs),
requiredSensorSettings,
&numRequiredSensorSettings);
if (samplingRate != _s.samplingRate)
{
_s.samplingRate = samplingRate;
LE_ASSERT_OK(le_timer_SetMsInterval(_s.timer, (uint32_t)(1000.0 / sr)));
if (!le_timer_IsRunning(_s.timer))
{
LE_ASSERT_OK(le_timer_Start(_s.timer));
}
}
return status == BSEC_OK ? LE_OK : LE_FAULT;
}
mangOH_bme680_SensorReadingHandlerRef_t mangOH_bme680_AddSensorReadingHandler
(
mangOH_bme680_SensorReadingHandlerFunc_t handlerFunc,
void* contextPtr
)
{
struct Bme680HandlerMapping *hm = le_mem_ForceAlloc(_s.HandlerPool);
hm->owningSession = mangOH_bme680_GetClientSessionRef();
hm->handlerFunc = handlerFunc;
hm->context = contextPtr;
return le_ref_CreateRef(_s.HandlerRefMap, hm);
}
void mangOH_bme680_RemoveSensorReadingHandler
(
mangOH_bme680_SensorReadingHandlerRef_t handlerRef
)
{
struct Bme680HandlerMapping *hm = le_ref_Lookup(_s.HandlerRefMap, handlerRef);
if (!hm)
{
LE_WARN("No handler found for the given reference");
return;
}
if (mangOH_bme680_GetClientSessionRef() != hm->owningSession)
{
LE_WARN("Cannot delete handler that isn't owned");
return;
}
le_ref_DeleteRef(_s.HandlerRefMap, handlerRef);
le_mem_Release(hm);
}
#include "legato.h"
#include "interfaces.h"
#include "bsec_interface.h"
#include "bme680_linux_i2c.h"
#include "bme680.h"
#include "bsecIntegration.h"
#define BSEC_STATE_FILE "bsec_state.bin"
#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)
{
struct timespec monotime;
if (clock_gettime(CLOCK_MONOTONIC, &monotime)) {
LE_ERROR("couldn't get timestamp - %s\n", strerror(errno));
}
return (monotime.tv_sec * 1000LL * 1000LL * 1000LL) + (monotime.tv_nsec);
}
static void SleepNs(uint64_t ns)
{
const uint64_t ns_per_s = (1000LL * 1000LL * 1000LL);
const struct timespec delay = {
.tv_sec = ns / ns_per_s,
.tv_nsec = ns % ns_per_s,
};
int ret = nanosleep(&delay, NULL);
if (ret) {
LE_ERROR("nanosleep failed with error: %s\n", strerror(errno));
}
}
static le_result_t LoadState(void)
{
le_result_t res = LE_OK;
uint8_t bsecState[BSEC_MAX_PROPERTY_BLOB_SIZE] = {0};
int fd = open(BSEC_STATE_FILE, O_RDONLY);
if (fd == -1)
{
LE_ERROR("Couldn't open \"%s\" for reading - %s\n", BSEC_STATE_FILE, strerror(errno));
res = LE_FAULT;
goto done;
}
size_t numRead = 0;
while (true) {
ssize_t spaceRemaining = NUM_ARRAY_MEMBERS(bsecState) - numRead;
if (spaceRemaining == 0) {
LE_ERROR("File content in \"%s\" too large to fit in buffer\n", BSEC_STATE_FILE);
res = LE_FAULT;
goto fail_read;
}
int readRes = read(fd, &bsecState[numRead], spaceRemaining);
if (readRes < 0) {
LE_ERROR("Failed to read \"%s\" - %s\n", BSEC_STATE_FILE, strerror(errno));
res = LE_FAULT;
goto fail_read;
}
if (readRes == 0) {
// Reached EOF
break;
}
numRead += readRes;
}
close(fd);
uint8_t workBuffer[BSEC_MAX_PROPERTY_BLOB_SIZE] = {0};
bsec_library_return_t bsecRes = bsec_set_state(bsecState, numRead, workBuffer, NUM_ARRAY_MEMBERS(workBuffer));
if (bsecRes != BSEC_OK)
{
LE_ERROR("Failed to set BSEC state");
res = LE_FAULT;
}
return res;
fail_read:
close(fd);
done:
return res;
}
static le_result_t SaveState(void)
{
le_result_t ret = LE_OK;
uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE];
uint8_t workBuffer[BSEC_MAX_STATE_BLOB_SIZE];
uint32_t bsecStateLen = 0;
bsec_library_return_t bsecStatus = bsec_get_state(
0, bsecState, sizeof(bsecState), workBuffer, sizeof(workBuffer), &bsecStateLen);
if (bsecStatus != BSEC_OK)
{
LE_ERROR("Couldn't get state out of BSEC");
ret = LE_FAULT;
goto done;
}
int fd = open(BSEC_STATE_FILE, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if (fd == -1) {
LE_ERROR("Couldn't open \"%s\" for writing - %s\n", BSEC_STATE_FILE, strerror(errno));
ret = LE_FAULT;
goto done;
}
unsigned int numWritten = 0;
do {
const size_t numToWrite = bsecStateLen - numWritten;
int res = write(fd, &bsecState[numWritten], numToWrite);
if (res < 0) {
LE_ERROR("Failed to write \"%s\" - %s\n", BSEC_STATE_FILE, strerror(errno));
ret = LE_FAULT;
goto done;
}
numWritten += res;
} while (numWritten < bsecStateLen);
int res = fsync(fd);
if (res) {
LE_ERROR("Failed to write \"%s\" - %s\n", BSEC_STATE_FILE, strerror(errno));
ret = LE_FAULT;
goto done;
}
done:
close(fd);
return ret;
}
static void TriggerMeasurement(struct bme680_dev *bme680, const bsec_bme_settings_t *sensorSettings)
{
if (sensorSettings->trigger_measurement)
{
bme680->tph_sett.os_hum = sensorSettings->humidity_oversampling;
bme680->tph_sett.os_pres = sensorSettings->pressure_oversampling;
bme680->tph_sett.os_temp = sensorSettings->temperature_oversampling;
bme680->gas_sett.run_gas = sensorSettings->run_gas;
bme680->gas_sett.heatr_temp = sensorSettings->heater_temperature;
bme680->gas_sett.heatr_dur = sensorSettings->heating_duration;
bme680->power_mode = BME680_FORCED_MODE;
int8_t bme680Status = bme680_set_sensor_settings(
BME680_OST_SEL | BME680_OSP_SEL | BME680_OSH_SEL | BME680_GAS_SENSOR_SEL, bme680);
if (bme680Status != BME680_OK)
{
LE_ERROR("Failed to set sensor settings");
return;
}
bme680Status = bme680_set_sensor_mode(bme680);
if (bme680Status != BME680_OK)
{
LE_ERROR("Failed to set sensor mode");
return;
}
uint16_t measPeriodMs;
bme680_get_profile_dur(&measPeriodMs, bme680);
SleepNs(measPeriodMs * 1000 * 1000);
}
do
{
int8_t bme680Status = bme680_get_sensor_mode(bme680);
if (bme680Status == BME680_OK && bme680->power_mode != BME680_FORCED_MODE)
{
break;
}
SleepNs(5 * 1000 * 1000);
} while(true);
}
static void ReadData(
struct bme680_dev *bme680,
int64_t timestampTrigger,
bsec_input_t *inputs,
size_t *numBsecInputs,
const bsec_bme_settings_t *sensorSettings)
{
if (sensorSettings->process_data)
{
struct bme680_field_data data;
int8_t bme680Status = bme680_get_sensor_data(&data, bme680);
if (bme680Status != BME680_OK)
{
LE_ERROR("Failed to read data from the bme680");
return;
}
if (data.status & BME680_NEW_DATA_MSK)
{
/* Pressure to be processed by BSEC */
if (sensorSettings->process_data & BSEC_PROCESS_PRESSURE)
{
/* Place presssure sample into input struct */
inputs[*numBsecInputs].sensor_id = BSEC_INPUT_PRESSURE;
inputs[*numBsecInputs].signal = data.pressure;
inputs[*numBsecInputs].time_stamp = timestampTrigger;
(*numBsecInputs)++;
}
/* Temperature to be processed by BSEC */
if (sensorSettings->process_data & BSEC_PROCESS_TEMPERATURE)
{
/* Place temperature sample into input struct */
inputs[*numBsecInputs].sensor_id = BSEC_INPUT_TEMPERATURE;
#ifdef BME680_FLOAT_POINT_COMPENSATION
inputs[*numBsecInputs].signal = data.temperature;
#else
inputs[*numBsecInputs].signal = data.temperature / 100.0f;
#endif
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)*/
inputs[*numBsecInputs].sensor_id = BSEC_INPUT_HEATSOURCE;
inputs[*numBsecInputs].signal = Bme680TemperatureOffset;
inputs[*numBsecInputs].time_stamp = timestampTrigger;
(*numBsecInputs)++;
}
/* Humidity to be processed by BSEC */
if (sensorSettings->process_data & BSEC_PROCESS_HUMIDITY)
{
/* Place humidity sample into input struct */
inputs[*numBsecInputs].sensor_id = BSEC_INPUT_HUMIDITY;
#ifdef BME680_FLOAT_POINT_COMPENSATION
inputs[*numBsecInputs].signal = data.humidity;
#else
inputs[*numBsecInputs].signal = data.humidity / 1000.0f;
#endif
inputs[*numBsecInputs].time_stamp = timestampTrigger;
(*numBsecInputs)++;
}
/* Gas to be processed by BSEC */
if (sensorSettings->process_data & BSEC_PROCESS_GAS)
{
/* Check whether gas_valid flag is set */
if(data.status & BME680_GASM_VALID_MSK)
{
/* Place sample into input struct */
inputs[*numBsecInputs].sensor_id = BSEC_INPUT_GASRESISTOR;
inputs[*numBsecInputs].signal = data.gas_resistance;
inputs[*numBsecInputs].time_stamp = timestampTrigger;
(*numBsecInputs)++;
}
}
}
}
}
static void ProcessData(bsec_input_t *bsecInputs, size_t numBsecInputs, int64_t timestamp)
{
if (numBsecInputs > 0)
{
mangOH_bme680_Reading_t reading = {.timestamp = timestamp};
bsec_output_t bsecOutputs[BSEC_NUMBER_OUTPUTS];
uint8_t numBsecOutputs = NUM_ARRAY_MEMBERS(bsecOutputs);
bsec_library_return_t bsecStatus = bsec_do_steps(bsecInputs, numBsecInputs, bsecOutputs, &numBsecOutputs);
LE_ASSERT(bsecStatus == BSEC_OK);
for (size_t i = 0; i < numBsecOutputs; i++)
{
switch (bsecOutputs[i].sensor_id)
{
case BSEC_OUTPUT_IAQ:
reading.iaq.valid = true;
reading.iaq.value = bsecOutputs[i].signal;
reading.iaq.accuracy = bsecOutputs[i].accuracy;
break;
case BSEC_OUTPUT_CO2_EQUIVALENT:
reading.co2Equivalent.valid = true;
reading.co2Equivalent.value = bsecOutputs[i].signal;
reading.co2Equivalent.accuracy = bsecOutputs[i].accuracy;
break;
case BSEC_OUTPUT_BREATH_VOC_EQUIVALENT:
reading.breathVoc.valid = true;
reading.breathVoc.value = bsecOutputs[i].signal;
reading.breathVoc.accuracy = bsecOutputs[i].accuracy;
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
reading.temperature.valid = true;
reading.temperature.value = bsecOutputs[i].signal;
break;
case BSEC_OUTPUT_RAW_PRESSURE:
reading.pressure.valid = true;
reading.pressure.value = bsecOutputs[i].signal;
break;
case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
reading.humidity.valid = true;
reading.humidity.value = bsecOutputs[i].signal;
break;
default:
LE_ERROR("unhandled data output type (%d)", bsecOutputs[i].sensor_id);
break;
}
}
le_ref_IterRef_t it = le_ref_GetIterator(_s.HandlerRefMap);
while (le_ref_NextNode(it) == LE_OK)
{
struct Bme680HandlerMapping *hm = le_ref_GetValue(it);
hm->handlerFunc(&reading, hm->context);
}
}
}
static void TimerHandler(le_timer_Ref_t t)
{
static size_t samplesSinceSave = 0;
bsec_bme_settings_t sensorSettings;
int64_t timestamp = GetTimestampNs();
bsec_sensor_control(timestamp, &sensorSettings);
LE_INFO("timestamp at start of TimerHandler is %lld and sensorSettings.next_call is %lld", timestamp, sensorSettings.next_call);
TriggerMeasurement(&_s.bme680->dev, &sensorSettings);
/* Allocate enough memory for up to BSEC_MAX_PHYSICAL_SENSOR physical inputs*/
bsec_input_t bsecInputs[BSEC_MAX_PHYSICAL_SENSOR];
/* Number of inputs to BSEC */
size_t numBsecInputs = 0;
ReadData(&_s.bme680->dev, timestamp, bsecInputs, &numBsecInputs, &sensorSettings);
ProcessData(bsecInputs, numBsecInputs, timestamp);
samplesSinceSave++;
if (samplesSinceSave >= SAMPLES_BETWEEN_SAVES)
{
if (SaveState() == LE_OK)
{
samplesSinceSave = 0;
}
}
int64_t nextTimerMs = (sensorSettings.next_call - GetTimestampNs()) / (1000LL * 1000LL);
LE_FATAL_IF(nextTimerMs <= 0, "Processing too slowly - next timeout scheduled for %lld ms", nextTimerMs);
LE_ASSERT_OK(le_timer_SetMsInterval(t, nextTimerMs));
LE_ASSERT_OK(le_timer_Start(t));
}
static void ClientSessionClosedHandler
(
le_msg_SessionRef_t clientSession,
void *context
)
{
le_ref_IterRef_t it = le_ref_GetIterator(_s.HandlerRefMap);
bool finished = le_ref_NextNode(it) != LE_OK;
while (!finished)
{
struct Bme680HandlerMapping *hm = le_ref_GetValue(it);
LE_ASSERT(hm != NULL);
// In order to prevent invalidating the iterator, we store the reference of the device we
// want to close and advance the iterator before calling le_spi_Close which will remove the
// reference from the hashmap.
void* refToRemove =
(hm->owningSession == clientSession) ? ((void*)le_ref_GetSafeRef(it)) : NULL;
finished = le_ref_NextNode(it) != LE_OK;
if (refToRemove != NULL)
{
le_ref_DeleteRef(_s.HandlerRefMap, refToRemove);
le_mem_Release(hm);
}
}
}
COMPONENT_INIT
{
bsec_library_return_t bsecRes;
bsec_version_t bsecVersion;
bsecRes = bsec_init();
LE_FATAL_IF(bsecRes != BSEC_OK, "Failed to initialize the BSEC library. Error: %d", bsecRes);
bsecRes = bsec_get_version(&bsecVersion);
LE_FATAL_IF(bsecRes != BSEC_OK, "Failed to lookup BSEC library version. Error: %d", bsecRes);
LE_INFO(
"Using BSEC library v%d.%d.%d.%d",
bsecVersion.major,
bsecVersion.minor,
bsecVersion.major_bugfix,
bsecVersion.minor_bugfix);
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);
LE_FATAL_IF(!_s.bme680, "Couldn't create bme680 device");
if (!_s.bme680) {
fprintf(stderr, "Failed to create device\n");
exit(1);
}
int8_t initRes = bme680_init(&_s.bme680->dev);
LE_FATAL_IF(initRes != BME680_OK, "Failed to initialize bme680 library");
_s.timer = le_timer_Create("bme680 sample");
LE_ASSERT_OK(le_timer_SetHandler(_s.timer, TimerHandler));
_s.samplingRate = MANGOH_BME680_SAMPLING_RATE_DISABLED;
_s.HandlerPool = le_mem_CreatePool("bme680 handlers", sizeof(struct Bme680HandlerMapping));
_s.HandlerRefMap = le_ref_CreateMap("bme680 refmap", 2);
le_msg_AddServiceCloseHandler(mangOH_bme680_GetServiceRef(), ClientSessionClosedHandler, NULL);
}
#ifndef _BSEC_INTEGRATION_H_
#define _BSEC_INTEGRATION_H_
struct Bme680HandlerMapping
{
le_msg_SessionRef_t owningSession;
mangOH_bme680_SensorReadingHandlerFunc_t handlerFunc;
void *context;
};
struct Bme680State
{
struct bme680_linux *bme680;
le_timer_Ref_t timer;
mangOH_bme680_SamplingRate_t samplingRate;
bool enableIaq;
bool enableCo2Equivalent;
bool enableBreathVoc;
bool enablePressure;
bool enableTemperature;
bool enableHumidity;
le_mem_PoolRef_t HandlerPool;
le_ref_MapRef_t HandlerRefMap;
};
#endif // _BSEC_INTEGRATION_H_
ENUM SamplingRate
{
SAMPLING_RATE_DISABLED,
SAMPLING_RATE_LP, // every 3 seconds
SAMPLING_RATE_ULP, // every 300 seconds (5 minutes)
};
FUNCTION le_result_t Configure
(
SamplingRate samplingRate IN,
bool enableIaq IN,
bool enableCo2Equivalent IN,
bool enableBreathVoc IN,
bool enablePressure IN,
bool enableTemperature IN,
bool enableHumidity IN
);
STRUCT ReadingIaq
{
bool valid;
double value;
uint8 accuracy;
};
STRUCT ReadingCo2Equivalent
{
bool valid;
double value;
uint8 accuracy;
};
STRUCT ReadingBreathVoc
{
bool valid;
double value;
uint8 accuracy;
};
STRUCT ReadingPressure
{
bool valid;
double value;
};
STRUCT ReadingTemperature
{
bool valid;
double value;
};
STRUCT ReadingHumidity
{
bool valid;
double value;
};
STRUCT Reading
{
int64 timestamp;
ReadingIaq iaq;
ReadingCo2Equivalent co2Equivalent;
ReadingBreathVoc breathVoc;
ReadingPressure pressure;
ReadingTemperature temperature;
ReadingHumidity humidity;
};
HANDLER SensorReadingHandler
(
Reading reading
);
EVENT SensorReading
(
SensorReadingHandler handler
);
sources:
{
mcp9700aOnWp.c
}
cflags:
{
-std=c99
}
requires:
{
api:
{
modemServices/le_adc.api
}
component:
{
${CURDIR}/../Mcp970xComponent
}
}
provides:
{
api:
{
mangOH_ambientTemperature.api
}
}
\ No newline at end of file
#include "legato.h"
#include "interfaces.h"
#include "mcp970x.h"
static int WpAdcFunction(int32_t *valueUv)
{
return le_adc_ReadValue("EXT_ADC0", valueUv);
}
le_result_t mangOH_ambientTemperature_Read(double *temperature)
{
int32_t tempInt;
int res = mcp970x_read_temperature(MCP970X_CHIP_9700A, WpAdcFunction, &tempInt);
if (res != 0)
{
return LE_FAULT;
}
*temperature = tempInt / 1000.0;
return LE_OK;
}
COMPONENT_INIT
{
}
sources:
{
component.c
mcp970x/mcp970x.c
}
cflags:
{
-std=c99
-fvisibility=default
}
provides:
{
headerDir:
{
${CURDIR}/mcp970x
}
}
\ No newline at end of file
#include "legato.h"
// Requird to make this code a Legato component
COMPONENT_INIT
{
}
#include "mcp970x.h"
#include <errno.h>
struct mcp970x_chip_spec
{
uint32_t uv_at_zero_celcius;
// uV/degree celcius
uint32_t temperature_coefficient;
};
static const struct mcp970x_chip_spec chip_specs[] =
{
[MCP970X_CHIP_9700] = {
.uv_at_zero_celcius = 500000,
.temperature_coefficient = 10000,
},
[MCP970X_CHIP_9701] = {
.uv_at_zero_celcius = 400000,
.temperature_coefficient = 19500,
},
};
/*
* Calculate the temperature in milli-degrees celcius. Divide by 1000 to get degrees celcius.
*
* Datasheet equation 4-1:
* Vout = (Tc * Ta) + V0c
* So:
* Ta = (Vout - V0c) / Tc
*/
static int32_t calculate_temperature(const struct mcp970x_chip_spec *chip_spec, uint32_t voltage_uv)
{
return (1000L * (voltage_uv - chip_spec->uv_at_zero_celcius)) / chip_spec->temperature_coefficient;
}
int mcp970x_read_temperature(enum mcp970x_chip chip, mcp970x_adc_function adc, int32_t *temperature)
{
if (chip < 0 || chip >= MCP970X_CHIP_NUM_OF)
{
return -EINVAL;
}
const struct mcp970x_chip_spec *spec = &chip_specs[chip];
int32_t adc_uv;
int res = adc(&adc_uv);
if (res)
{
return res;
}
*temperature = calculate_temperature(spec, adc_uv);
return 0;
}
#ifndef _MCP970X_H_
#define _MCP970X_H_
#include <stdint.h>
/*
* "A" versions are the same except with better accuracy
*/
enum mcp970x_chip
{
MCP970X_CHIP_9700,
MCP970X_CHIP_9700A = MCP970X_CHIP_9700,
MCP970X_CHIP_9701,
MCP970X_CHIP_9701A = MCP970X_CHIP_9701,
MCP970X_CHIP_NUM_OF,
};
typedef int (*mcp970x_adc_function)(int32_t *val_uv);
int mcp970x_read_temperature(
enum mcp970x_chip chip, mcp970x_adc_function adc, int32_t *temperature);
#endif // _MCP970X_H_
FUNCTION le_result_t Read
(
double temperature OUT
);
sandboxed: true
version: 1.0.0
start: auto
executables:
{
mcp9700aTemperatureSensor = ( Mcp9700aAppComponent )
}
processes:
{
envVars:
{
LE_LOG_LEVEL = DEBUG
}
run:
{
( mcp9700aTemperatureSensor )
}
faultAction: restart
}
bindings:
{
mcp9700aTemperatureSensor.Mcp9700aAppComponent.le_adc -> modemService.le_adc
}
extern:
{
mcp9700aTemperatureSensor.Mcp9700aAppComponent.mangOH_ambientTemperature
}
\ No newline at end of file
Subproject commit b79dcfd1968a127400e9d6bf4b422cff4df90f01
sources:
{
bme680_linux_i2c.c
Bosch_BME680_Driver/bme680.c
}
cflags:
{
-I${CURDIR}/Bosch_BME680_Driver
-fvisibility=default
}
provides:
{
headerDir:
{
${CURDIR}
${CURDIR}/Bosch_BME680_Driver
}
}
\ No newline at end of file
#include "legato.h"
#include "interfaces.h"
#include "bme680_linux_i2c.h"
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
/*
* Private data that is opaque to clients.
*/
struct bme680_linux_priv
{
int i2c_bus_fd;
uint16_t i2c_bus_num;
uint16_t i2c_addr;
};
static void bme680_linux_delay_ms(uint32_t period_ms, void *context)
{
const struct timespec delay = {
.tv_sec = period_ms / 1000,
.tv_nsec = (period_ms % 1000) * 1000 * 1000,
};
int ret = nanosleep(&delay, NULL);
if (ret) {
LE_ERROR("nanosleep failed with error: %s\n", strerror(errno));
}
}
static int8_t bme680_linux_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint16_t len, void *context)
{
int8_t res = 0; /* Return 0 for Success, non-zero for failure */
struct bme680_linux *bme680 = context;
struct i2c_msg i2c_msgs[] = {
{
.addr = bme680->priv->i2c_addr,
.flags = 0,
.buf = &reg_addr,
.len = 1,
},
{
.addr = bme680->priv->i2c_addr,
.flags = I2C_M_RD,
.buf = reg_data,
.len = len,
},
};
struct i2c_rdwr_ioctl_data i2c_xfer = {
.msgs = i2c_msgs,
.nmsgs = NUM_ARRAY_MEMBERS(i2c_msgs),
};
if (ioctl(bme680->priv->i2c_bus_fd, I2C_RDWR, &i2c_xfer) < 0) {
LE_WARN(
"I2C read on bus %u, address %u to register %u of length %u failed - %s\n",
bme680->priv->i2c_bus_num, bme680->priv->i2c_addr, reg_addr, len, strerror(errno));
res = BME680_E_COM_FAIL;
}
return res;
}
static int8_t bme680_linux_i2c_write(uint8_t reg_addr, const uint8_t *reg_data, uint16_t len, void *context)
{
uint8_t buffer[64];
int8_t res = 0; /* Return 0 for Success, non-zero for failure */
struct bme680_linux *bme680 = context;
if (len >= sizeof(buffer)) {
LE_ERROR("I2C write buffer is too small (%u)\n", sizeof(buffer));
return BME680_E_INVALID_LENGTH;
}
buffer[0] = reg_addr;
memcpy(&buffer[1], reg_data, len);
struct i2c_msg i2c_msgs[] = {
{
.addr = bme680->priv->i2c_addr,
.flags = 0,
.buf = buffer,
.len = len + 1,
},
};
struct i2c_rdwr_ioctl_data i2c_xfer = {
.msgs = i2c_msgs,
.nmsgs = NUM_ARRAY_MEMBERS(i2c_msgs),
};
if (ioctl(bme680->priv->i2c_bus_fd, I2C_RDWR, &i2c_xfer) < 0) {
LE_WARN(
"I2C write on bus %u, address %u to register %u of length %u failed - %s\n",
bme680->priv->i2c_bus_num, bme680->priv->i2c_addr, reg_addr, len, strerror(errno));
res = BME680_E_COM_FAIL;
}
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 = calloc(sizeof(*bme680), 1);
if (!bme680)
return NULL;
bme680->priv = calloc(sizeof(*(bme680->priv)), 1);
if (!bme680->priv)
goto fail;
// Initialize Bosch driver struct
bme680->dev.context = bme680;
bme680->dev.intf = BME680_I2C_INTF;
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;
// Initialize private data required for read/write/delay_ms
bme680->priv->i2c_bus_num = i2c_bus_num;
bme680->priv->i2c_addr = i2c_addr;
char bus_filename[100];
int needed_len = snprintf(bus_filename, sizeof(bus_filename) - 1, "/dev/i2c-%u", i2c_bus_num);
LE_ASSERT(needed_len < sizeof(bus_filename));
bme680->priv->i2c_bus_fd = open(bus_filename, O_RDWR);
if (bme680->priv->i2c_bus_fd < 0) {
LE_ERROR("Couldn't open I2C bus %u - %s\n", i2c_bus_num, strerror(errno));
goto fail;
}
return bme680;
fail:
if (bme680->priv)
free(bme680->priv);
free(bme680);
return NULL;
}
void bme680_linux_i2c_destroy(struct bme680_linux* bme680)
{
int close_res = close(bme680->priv->i2c_bus_fd);
if (close_res != 0) {
LE_ERROR("Couldn't close I2C bus file descriptor - %s\n", strerror(errno));
}
free(bme680->priv);
free(bme680);
}
COMPONENT_INIT
{
}
#ifndef _BME680_LINUX_I2C_H_
#define _BME680_LINUX_I2C_H_
#include "legato.h"
#include "bme680_defs.h"
#include <stdint.h>
typedef struct bme680_linux_priv bme680_linux_priv;
struct bme680_linux
{
struct bme680_dev dev;
bme680_linux_priv* priv;
};
struct bme680_linux* bme680_linux_i2c_create(unsigned i2c_bus_num, uint8_t i2c_addr, int8_t ambient_temperature);
void bme680_linux_i2c_destroy(struct bme680_linux* bme680);
#endif // _BME680_LINUX_I2C_H_
sources:
{
component.c
}
cflags:
{
}
ldflags:
{
-lm
-L ${BSEC_DIR}
}
requires:
{
lib:
{
libalgobsec.a
}
}
provides:
{
headerDir:
{
${BSEC_DIR}
}
}
\ No newline at end of file
# Bosch BSEC Component
[BSEC](https://www.bosch-sensortec.com/bst/products/all_products/bsec) (Bosch Sensortec
Environmental Cluster) is a proprietary library which is used to process the data provided by a
BME680 environmental sensor to derive more useful results.
The library published at the URL above cannot be used in a Legato component because the code is not
relocatable and Legato components are built as shared libraries. This issue was raised with Bosch
in a [forum post](https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BSEC-integration-problem-on-gcc-Cortex-A7/m-p/5995)
in which Bosch provides an alternative download compiled with the options necessary to make the
library relocatable.
To build this component, download and extract the zip file from the forum post and then create an
enironment variable `$BSEC_DIR` which points to the directory containing the `.a` file. For
example: `export BSEC_DIR=~/BSEC_1.4.7.2_GCC_CortexA7_20190225/algo/bin/Normal_version/Cortex_A7`
#include "legato.h"
COMPONENT_INIT
{
// Required to implement a component
}
/*
* Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
* Copyright (C) 2004 - 2009 Ivo van Doorn <IvDoorn@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef _LINUX_BITFIELD_H
#define _LINUX_BITFIELD_H
#include <asm/byteorder.h>
/*
* Bitfield access macros
*
* FIELD_{GET,PREP} macros take as first parameter shifted mask
* from which they extract the base mask and shift amount.
* Mask must be a compilation time constant.
*
* Example:
*
* #define REG_FIELD_A GENMASK(6, 0)
* #define REG_FIELD_B BIT(7)
* #define REG_FIELD_C GENMASK(15, 8)
* #define REG_FIELD_D GENMASK(31, 16)
*
* Get:
* a = FIELD_GET(REG_FIELD_A, reg);
* b = FIELD_GET(REG_FIELD_B, reg);
*
* Set:
* reg = FIELD_PREP(REG_FIELD_A, 1) |
* FIELD_PREP(REG_FIELD_B, 0) |
* FIELD_PREP(REG_FIELD_C, c) |
* FIELD_PREP(REG_FIELD_D, 0x40);
*
* Modify:
* reg &= ~REG_FIELD_C;
* reg |= FIELD_PREP(REG_FIELD_C, c);
*/
#define __bf_shf(x) (__builtin_ffsll(x) - 1)
/**
* FIELD_PREP() - prepare a bitfield element
* @_mask: shifted mask defining the field's length and position
* @_val: value to put in the field
*
* FIELD_PREP() masks and shifts up the value. The result should
* be combined with other fields of the bitfield using logical OR.
*/
#define FIELD_PREP(_mask, _val) \
({ \
((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
})
/**
* FIELD_GET() - extract a bitfield element
* @_mask: shifted mask defining the field's length and position
* @_reg: value of entire bitfield
*
* FIELD_GET() extracts the field specified by @_mask from the
* bitfield passed in as @_reg by masking and shifting it down.
*/
#define FIELD_GET(_mask, _reg) \
({ \
(typeof(_mask))(((_reg) & (_mask)) >> __bf_shf(_mask)); \
})
#endif
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef BME680_H_
#define BME680_H_
#define BME680_REG_CHIP_I2C_ID 0xD0
#define BME680_REG_CHIP_SPI_ID 0x50
#define BME680_CHIP_ID_VAL 0x61
#define BME680_REG_SOFT_RESET_I2C 0xE0
#define BME680_REG_SOFT_RESET_SPI 0x60
#define BME680_CMD_SOFTRESET 0xB6
#define BME680_REG_STATUS 0x73
#define BME680_SPI_MEM_PAGE_BIT BIT(4)
#define BME680_SPI_MEM_PAGE_1_VAL 1
#define BME680_REG_TEMP_MSB 0x22
#define BME680_REG_PRESS_MSB 0x1F
#define BM6880_REG_HUMIDITY_MSB 0x25
#define BME680_REG_GAS_MSB 0x2A
#define BME680_REG_GAS_R_LSB 0x2B
#define BME680_GAS_STAB_BIT BIT(4)
#define BME680_GAS_RANGE_MASK GENMASK(3, 0)
#define BME680_REG_CTRL_HUMIDITY 0x72
#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
#define BME680_REG_CTRL_MEAS 0x74
#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
#define BME680_MODE_MASK GENMASK(1, 0)
#define BME680_MODE_FORCED 1
#define BME680_MODE_SLEEP 0
#define BME680_REG_CONFIG 0x75
#define BME680_FILTER_MASK GENMASK(4, 2)
#define BME680_FILTER_COEFF_VAL BIT(1)
/* TEMP/PRESS/HUMID reading skipped */
#define BME680_MEAS_SKIPPED 0x8000
#define BME680_MAX_OVERFLOW_VAL 0x40000000
#define BME680_HUM_REG_SHIFT_VAL 4
#define BME680_BIT_H1_DATA_MASK GENMASK(3, 0)
#define BME680_REG_RES_HEAT_RANGE 0x02
#define BME680_RHRANGE_MASK GENMASK(5, 4)
#define BME680_REG_RES_HEAT_VAL 0x00
#define BME680_REG_RANGE_SW_ERR 0x04
#define BME680_RSERROR_MASK GENMASK(7, 4)
#define BME680_REG_RES_HEAT_0 0x5A
#define BME680_REG_GAS_WAIT_0 0x64
#define BME680_ADC_GAS_RES_SHIFT 6
#define BME680_AMB_TEMP 25
#define BME680_REG_CTRL_GAS_1 0x71
#define BME680_RUN_GAS_MASK BIT(4)
#define BME680_NB_CONV_MASK GENMASK(3, 0)
#define BME680_REG_MEAS_STAT_0 0x1D
#define BME680_GAS_MEAS_BIT BIT(6)
/* Calibration Parameters */
#define BME680_T2_LSB_REG 0x8A
#define BME680_T3_REG 0x8C
#define BME680_P1_LSB_REG 0x8E
#define BME680_P2_LSB_REG 0x90
#define BME680_P3_REG 0x92
#define BME680_P4_LSB_REG 0x94
#define BME680_P5_LSB_REG 0x96
#define BME680_P7_REG 0x98
#define BME680_P6_REG 0x99
#define BME680_P8_LSB_REG 0x9C
#define BME680_P9_LSB_REG 0x9E
#define BME680_P10_REG 0xA0
#define BME680_H2_LSB_REG 0xE2
#define BME680_H2_MSB_REG 0xE1
#define BME680_H1_MSB_REG 0xE3
#define BME680_H1_LSB_REG 0xE2
#define BME680_H3_REG 0xE4
#define BME680_H4_REG 0xE5
#define BME680_H5_REG 0xE6
#define BME680_H6_REG 0xE7
#define BME680_H7_REG 0xE8
#define BME680_T1_LSB_REG 0xE9
#define BME680_GH2_LSB_REG 0xEB
#define BME680_GH1_REG 0xED
#define BME680_GH3_REG 0xEE
extern const struct regmap_config bme680_regmap_config;
int bme680_core_probe(struct device *dev, struct regmap *regmap,
const char *name);
#endif /* BME680_H_ */
// SPDX-License-Identifier: GPL-2.0
/*
* Bosch BME680 - Temperature, Pressure, Humidity & Gas Sensor
*
* Copyright (C) 2017 - 2018 Bosch Sensortec GmbH
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
*
* Datasheet:
* https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME680-DS001-00.pdf
*/
#include <linux/acpi.h>
#include "bitfield.h"
#include <linux/device.h>
#include <linux/module.h>
#include <linux/log2.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/version.h>
#include "bme680.h"
#define regmap_write_bits regmap_update_bits
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
#define IIO_SUPPORTS_OVERSAMPLING_RATIO
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
#define REGMAP_SUPPORTS_GET_DEVICE
#endif
struct bme680_calib {
u16 par_t1;
s16 par_t2;
s8 par_t3;
u16 par_p1;
s16 par_p2;
s8 par_p3;
s16 par_p4;
s16 par_p5;
s8 par_p6;
s8 par_p7;
s16 par_p8;
s16 par_p9;
u8 par_p10;
u16 par_h1;
u16 par_h2;
s8 par_h3;
s8 par_h4;
s8 par_h5;
s8 par_h6;
s8 par_h7;
s8 par_gh1;
s16 par_gh2;
s8 par_gh3;
u8 res_heat_range;
s8 res_heat_val;
s8 range_sw_err;
};
struct bme680_data {
struct regmap *regmap;
struct bme680_calib bme680;
u8 oversampling_temp;
u8 oversampling_press;
u8 oversampling_humid;
u16 heater_dur;
u16 heater_temp;
/*
* Carryover value from temperature conversion, used in pressure
* and humidity compensation calculations.
*/
s32 t_fine;
#ifndef REGMAP_SUPPORTS_GET_DEVICE
struct device *dev;
#endif /* REGMAP_SUPPORTS_GET_DEVICE */
};
const struct regmap_config bme680_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
};
EXPORT_SYMBOL(bme680_regmap_config);
static const struct iio_chan_spec bme680_channels[] = {
{
.type = IIO_TEMP,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED)
#ifdef IIO_SUPPORTS_OVERSAMPLING_RATIO
| BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
#endif
},
{
.type = IIO_PRESSURE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED)
#ifdef IIO_SUPPORTS_OVERSAMPLING_RATIO
| BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
#endif
},
{
.type = IIO_HUMIDITYRELATIVE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED)
#ifdef IIO_SUPPORTS_OVERSAMPLING_RATIO
| BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
#endif
},
{
.type = IIO_RESISTANCE,
.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
},
};
static int bme680_read_calib(struct bme680_data *data,
struct bme680_calib *calib)
{
#ifdef REGMAP_SUPPORTS_GET_DEVICE
struct device *dev = regmap_get_device(data->regmap);
#else
struct device *dev = data->dev;
#endif
unsigned int tmp, tmp_msb, tmp_lsb;
int ret;
__le16 buf;
/* Temperature related coefficients */
ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_T1_LSB_REG\n");
return ret;
}
calib->par_t1 = le16_to_cpu(buf);
ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_T2_LSB_REG\n");
return ret;
}
calib->par_t2 = le16_to_cpu(buf);
ret = regmap_read(data->regmap, BME680_T3_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_T3_REG\n");
return ret;
}
calib->par_t3 = tmp;
/* Pressure related coefficients */
ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P1_LSB_REG\n");
return ret;
}
calib->par_p1 = le16_to_cpu(buf);
ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P2_LSB_REG\n");
return ret;
}
calib->par_p2 = le16_to_cpu(buf);
ret = regmap_read(data->regmap, BME680_P3_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P3_REG\n");
return ret;
}
calib->par_p3 = tmp;
ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P4_LSB_REG\n");
return ret;
}
calib->par_p4 = le16_to_cpu(buf);
ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P5_LSB_REG\n");
return ret;
}
calib->par_p5 = le16_to_cpu(buf);
ret = regmap_read(data->regmap, BME680_P6_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P6_REG\n");
return ret;
}
calib->par_p6 = tmp;
ret = regmap_read(data->regmap, BME680_P7_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P7_REG\n");
return ret;
}
calib->par_p7 = tmp;
ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P8_LSB_REG\n");
return ret;
}
calib->par_p8 = le16_to_cpu(buf);
ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P9_LSB_REG\n");
return ret;
}
calib->par_p9 = le16_to_cpu(buf);
ret = regmap_read(data->regmap, BME680_P10_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P10_REG\n");
return ret;
}
calib->par_p10 = tmp;
/* Humidity related coefficients */
ret = regmap_read(data->regmap, BME680_H1_MSB_REG, &tmp_msb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H1_MSB_REG\n");
return ret;
}
ret = regmap_read(data->regmap, BME680_H1_LSB_REG, &tmp_lsb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H1_LSB_REG\n");
return ret;
}
calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
(tmp_lsb & BME680_BIT_H1_DATA_MASK);
ret = regmap_read(data->regmap, BME680_H2_MSB_REG, &tmp_msb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H2_MSB_REG\n");
return ret;
}
ret = regmap_read(data->regmap, BME680_H2_LSB_REG, &tmp_lsb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H2_LSB_REG\n");
return ret;
}
calib->par_h2 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
(tmp_lsb >> BME680_HUM_REG_SHIFT_VAL);
ret = regmap_read(data->regmap, BME680_H3_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H3_REG\n");
return ret;
}
calib->par_h3 = tmp;
ret = regmap_read(data->regmap, BME680_H4_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H4_REG\n");
return ret;
}
calib->par_h4 = tmp;
ret = regmap_read(data->regmap, BME680_H5_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H5_REG\n");
return ret;
}
calib->par_h5 = tmp;
ret = regmap_read(data->regmap, BME680_H6_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H6_REG\n");
return ret;
}
calib->par_h6 = tmp;
ret = regmap_read(data->regmap, BME680_H7_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H7_REG\n");
return ret;
}
calib->par_h7 = tmp;
/* Gas heater related coefficients */
ret = regmap_read(data->regmap, BME680_GH1_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_GH1_REG\n");
return ret;
}
calib->par_gh1 = tmp;
ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG, (u8 *) &buf,
2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_GH2_LSB_REG\n");
return ret;
}
calib->par_gh2 = le16_to_cpu(buf);
ret = regmap_read(data->regmap, BME680_GH3_REG, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read BME680_GH3_REG\n");
return ret;
}
calib->par_gh3 = tmp;
/* Other coefficients */
ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_RANGE, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read resistance heat range\n");
return ret;
}
calib->res_heat_range = FIELD_GET(BME680_RHRANGE_MASK, tmp);
ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_VAL, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read resistance heat value\n");
return ret;
}
calib->res_heat_val = tmp;
ret = regmap_read(data->regmap, BME680_REG_RANGE_SW_ERR, &tmp);
if (ret < 0) {
dev_err(dev, "failed to read range software error\n");
return ret;
}
calib->range_sw_err = FIELD_GET(BME680_RSERROR_MASK, tmp);
return 0;
}
/*
* Taken from Bosch BME680 API:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L876
*
* Returns temperature measurement in DegC, resolutions is 0.01 DegC. Therefore,
* output value of "3233" represents 32.33 DegC.
*/
static s16 bme680_compensate_temp(struct bme680_data *data,
s32 adc_temp)
{
struct bme680_calib *calib = &data->bme680;
s64 var1, var2, var3;
s16 calc_temp;
var1 = (adc_temp >> 3) - (calib->par_t1 << 1);
var2 = (var1 * calib->par_t2) >> 11;
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
var3 = (var3 * (calib->par_t3 << 4)) >> 14;
data->t_fine = var2 + var3;
calc_temp = (data->t_fine * 5 + 128) >> 8;
return calc_temp;
}
/*
* Taken from Bosch BME680 API:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L896
*
* Returns pressure measurement in Pa. Output value of "97356" represents
* 97356 Pa = 973.56 hPa.
*/
static u32 bme680_compensate_press(struct bme680_data *data,
u32 adc_press)
{
struct bme680_calib *calib = &data->bme680;
s32 var1, var2, var3, press_comp;
var1 = (data->t_fine >> 1) - 64000;
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * calib->par_p6) >> 2;
var2 = var2 + (var1 * calib->par_p5 << 1);
var2 = (var2 >> 2) + (calib->par_p4 << 16);
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
(calib->par_p3 << 5)) >> 3) +
((calib->par_p2 * var1) >> 1);
var1 = var1 >> 18;
var1 = ((32768 + var1) * calib->par_p1) >> 15;
press_comp = 1048576 - adc_press;
press_comp = ((press_comp - (var2 >> 12)) * 3125);
if (press_comp >= BME680_MAX_OVERFLOW_VAL)
press_comp = ((press_comp / (u32)var1) << 1);
else
press_comp = ((press_comp << 1) / (u32)var1);
var1 = (calib->par_p9 * (((press_comp >> 3) *
(press_comp >> 3)) >> 13)) >> 12;
var2 = ((press_comp >> 2) * calib->par_p8) >> 13;
var3 = ((press_comp >> 8) * (press_comp >> 8) *
(press_comp >> 8) * calib->par_p10) >> 17;
press_comp += (var1 + var2 + var3 + (calib->par_p7 << 7)) >> 4;
return press_comp;
}
/*
* Taken from Bosch BME680 API:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L937
*
* Returns humidity measurement in percent, resolution is 0.001 percent. Output
* value of "43215" represents 43.215 %rH.
*/
static u32 bme680_compensate_humid(struct bme680_data *data,
u16 adc_humid)
{
struct bme680_calib *calib = &data->bme680;
s32 var1, var2, var3, var4, var5, var6, temp_scaled, calc_hum;
temp_scaled = (data->t_fine * 5 + 128) >> 8;
var1 = (adc_humid - ((s32) ((s32) calib->par_h1 * 16))) -
(((temp_scaled * (s32) calib->par_h3) / 100) >> 1);
var2 = ((s32) calib->par_h2 *
(((temp_scaled * calib->par_h4) / 100) +
(((temp_scaled * ((temp_scaled * calib->par_h5) / 100))
>> 6) / 100) + (1 << 14))) >> 10;
var3 = var1 * var2;
var4 = calib->par_h6 << 7;
var4 = (var4 + ((temp_scaled * calib->par_h7) / 100)) >> 4;
var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
var6 = (var4 * var5) >> 1;
calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
calc_hum = clamp(calc_hum, 0, 100000); /* clamp between 0-100 %rH */
return calc_hum;
}
/*
* Taken from Bosch BME680 API:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L973
*
* Returns gas measurement in Ohm. Output value of "82986" represent 82986 ohms.
*/
static u32 bme680_compensate_gas(struct bme680_data *data, u16 gas_res_adc,
u8 gas_range)
{
struct bme680_calib *calib = &data->bme680;
s64 var1;
u64 var2;
s64 var3;
u32 calc_gas_res;
/* Look up table for the possible gas range values */
const u32 lookupTable[16] = {2147483647u, 2147483647u,
2147483647u, 2147483647u, 2147483647u,
2126008810u, 2147483647u, 2130303777u,
2147483647u, 2147483647u, 2143188679u,
2136746228u, 2147483647u, 2126008810u,
2147483647u, 2147483647u};
var1 = ((1340 + (5 * (s64) calib->range_sw_err)) *
((s64) lookupTable[gas_range])) >> 16;
var2 = ((gas_res_adc << 15) - 16777216) + var1;
var3 = ((125000 << (15 - gas_range)) * var1) >> 9;
var3 += (var2 >> 1);
calc_gas_res = div64_s64(var3, (s64) var2);
return calc_gas_res;
}
/*
* Taken from Bosch BME680 API:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L1002
*/
static u8 bme680_calc_heater_res(struct bme680_data *data, u16 temp)
{
struct bme680_calib *calib = &data->bme680;
s32 var1, var2, var3, var4, var5, heatr_res_x100;
u8 heatr_res;
if (temp > 400) /* Cap temperature */
temp = 400;
var1 = (((s32) BME680_AMB_TEMP * calib->par_gh3) / 1000) * 256;
var2 = (calib->par_gh1 + 784) * (((((calib->par_gh2 + 154009) *
temp * 5) / 100)
+ 3276800) / 10);
var3 = var1 + (var2 / 2);
var4 = (var3 / (calib->res_heat_range + 4));
var5 = 131 * calib->res_heat_val + 65536;
heatr_res_x100 = ((var4 / var5) - 250) * 34;
heatr_res = (heatr_res_x100 + 50) / 100;
return heatr_res;
}
/*
* Taken from Bosch BME680 API:
* https://github.com/BoschSensortec/BME680_driver/blob/63bb5336/bme680.c#L1188
*/
static u8 bme680_calc_heater_dur(u16 dur)
{
u8 durval, factor = 0;
if (dur >= 0xfc0) {
durval = 0xff; /* Max duration */
} else {
while (dur > 0x3F) {
dur = dur / 4;
factor += 1;
}
durval = dur + (factor * 64);
}
return durval;
}
static int bme680_set_mode(struct bme680_data *data, bool mode)
{
#ifdef REGMAP_SUPPORTS_GET_DEVICE
struct device *dev = regmap_get_device(data->regmap);
#else
struct device *dev = data->dev;
#endif
int ret;
if (mode) {
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
BME680_MODE_MASK, BME680_MODE_FORCED);
if (ret < 0)
dev_err(dev, "failed to set forced mode\n");
} else {
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
BME680_MODE_MASK, BME680_MODE_SLEEP);
if (ret < 0)
dev_err(dev, "failed to set sleep mode\n");
}
return ret;
}
static u8 bme680_oversampling_to_reg(u8 val)
{
return ilog2(val) + 1;
}
static int bme680_chip_config(struct bme680_data *data)
{
#ifdef REGMAP_SUPPORTS_GET_DEVICE
struct device *dev = regmap_get_device(data->regmap);
#else
struct device *dev = data->dev;
#endif
int ret;
u8 osrs;
osrs = FIELD_PREP(
BME680_OSRS_HUMIDITY_MASK,
bme680_oversampling_to_reg(data->oversampling_humid));
/*
* Highly recommended to set oversampling of humidity before
* temperature/pressure oversampling.
*/
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_HUMIDITY,
BME680_OSRS_HUMIDITY_MASK, osrs);
if (ret < 0) {
dev_err(dev, "failed to write ctrl_hum register\n");
return ret;
}
/* IIR filter settings */
ret = regmap_update_bits(data->regmap, BME680_REG_CONFIG,
BME680_FILTER_MASK,
BME680_FILTER_COEFF_VAL);
if (ret < 0) {
dev_err(dev, "failed to write config register\n");
return ret;
}
osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK,
bme680_oversampling_to_reg(data->oversampling_temp)) |
FIELD_PREP(BME680_OSRS_PRESS_MASK,
bme680_oversampling_to_reg(data->oversampling_press));
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
BME680_OSRS_TEMP_MASK | BME680_OSRS_PRESS_MASK,
osrs);
if (ret < 0)
dev_err(dev, "failed to write ctrl_meas register\n");
return ret;
}
static int bme680_gas_config(struct bme680_data *data)
{
#ifdef REGMAP_SUPPORTS_GET_DEVICE
struct device *dev = regmap_get_device(data->regmap);
#else
struct device *dev = data->dev;
#endif
int ret;
u8 heatr_res, heatr_dur;
heatr_res = bme680_calc_heater_res(data, data->heater_temp);
/* set target heater temperature */
ret = regmap_write(data->regmap, BME680_REG_RES_HEAT_0, heatr_res);
if (ret < 0) {
dev_err(dev, "failed to write res_heat_0 register\n");
return ret;
}
heatr_dur = bme680_calc_heater_dur(data->heater_dur);
/* set target heating duration */
ret = regmap_write(data->regmap, BME680_REG_GAS_WAIT_0, heatr_dur);
if (ret < 0) {
dev_err(dev, "failed to write gas_wait_0 register\n");
return ret;
}
/* Enable the gas sensor and select heater profile set-point 0 */
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1,
BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK,
FIELD_PREP(BME680_RUN_GAS_MASK, 1) |
FIELD_PREP(BME680_NB_CONV_MASK, 0));
if (ret < 0)
dev_err(dev, "failed to write ctrl_gas_1 register\n");
return ret;
}
static int bme680_read_temp(struct bme680_data *data,
int *val, int *val2)
{
#ifdef REGMAP_SUPPORTS_GET_DEVICE
struct device *dev = regmap_get_device(data->regmap);
#else
struct device *dev = data->dev;
#endif
int ret;
__be32 tmp = 0;
s32 adc_temp;
s16 comp_temp;
/* set forced mode to trigger measurement */
ret = bme680_set_mode(data, true);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, BME680_REG_TEMP_MSB,
(u8 *) &tmp, 3);
if (ret < 0) {
dev_err(dev, "failed to read temperature\n");
return ret;
}
adc_temp = be32_to_cpu(tmp) >> 12;
if (adc_temp == BME680_MEAS_SKIPPED) {
/* reading was skipped */
dev_err(dev, "reading temperature skipped\n");
return -EINVAL;
}
comp_temp = bme680_compensate_temp(data, adc_temp);
/*
* val might be NULL if we're called by the read_press/read_humid
* routine which is callled to get t_fine value used in
* compensate_press/compensate_humid to get compensated
* pressure/humidity readings.
*/
if (val && val2) {
*val = comp_temp;
*val2 = 100;
return IIO_VAL_FRACTIONAL;
}
return ret;
}
static int bme680_read_press(struct bme680_data *data,
int *val, int *val2)
{
#ifdef REGMAP_SUPPORTS_GET_DEVICE
struct device *dev = regmap_get_device(data->regmap);
#else
struct device *dev = data->dev;
#endif
int ret;
__be32 tmp = 0;
s32 adc_press;
/* Read and compensate temperature to get a reading of t_fine */
ret = bme680_read_temp(data, NULL, NULL);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, BME680_REG_PRESS_MSB,
(u8 *) &tmp, 3);
if (ret < 0) {
dev_err(dev, "failed to read pressure\n");
return ret;
}
adc_press = be32_to_cpu(tmp) >> 12;
if (adc_press == BME680_MEAS_SKIPPED) {
/* reading was skipped */
dev_err(dev, "reading pressure skipped\n");
return -EINVAL;
}
*val = bme680_compensate_press(data, adc_press);
*val2 = 100;
return IIO_VAL_FRACTIONAL;
}
static int bme680_read_humid(struct bme680_data *data,
int *val, int *val2)
{
#ifdef REGMAP_SUPPORTS_GET_DEVICE
struct device *dev = regmap_get_device(data->regmap);
#else
struct device *dev = data->dev;
#endif
int ret;
__be16 tmp = 0;
s32 adc_humidity;
u32 comp_humidity;
/* Read and compensate temperature to get a reading of t_fine */
ret = bme680_read_temp(data, NULL, NULL);
if (ret < 0)
return ret;
ret = regmap_bulk_read(data->regmap, BM6880_REG_HUMIDITY_MSB,
(u8 *) &tmp, 2);
if (ret < 0) {
dev_err(dev, "failed to read humidity\n");
return ret;
}
adc_humidity = be16_to_cpu(tmp);
if (adc_humidity == BME680_MEAS_SKIPPED) {
/* reading was skipped */
dev_err(dev, "reading humidity skipped\n");
return -EINVAL;
}
comp_humidity = bme680_compensate_humid(data, adc_humidity);
*val = comp_humidity;
*val2 = 1000;
return IIO_VAL_FRACTIONAL;
}
static int bme680_read_gas(struct bme680_data *data,
int *val)
{
#ifdef REGMAP_SUPPORTS_GET_DEVICE
struct device *dev = regmap_get_device(data->regmap);
#else
struct device *dev = data->dev;
#endif
int ret;
__be16 tmp = 0;
unsigned int check;
u16 adc_gas_res;
u8 gas_range;
/* Set heater settings */
ret = bme680_gas_config(data);
if (ret < 0) {
dev_err(dev, "failed to set gas config\n");
return ret;
}
/* set forced mode to trigger measurement */
ret = bme680_set_mode(data, true);
if (ret < 0)
return ret;
ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &check);
if (check & BME680_GAS_MEAS_BIT) {
dev_err(dev, "gas measurement incomplete\n");
return -EBUSY;
}
ret = regmap_read(data->regmap, BME680_REG_GAS_R_LSB, &check);
if (ret < 0) {
dev_err(dev, "failed to read gas_r_lsb register\n");
return ret;
}
/*
* occurs if either the gas heating duration was insuffient
* to reach the target heater temperature or the target
* heater temperature was too high for the heater sink to
* reach.
*/
if ((check & BME680_GAS_STAB_BIT) == 0) {
dev_err(dev, "heater failed to reach the target temperature\n");
return -EINVAL;
}
ret = regmap_bulk_read(data->regmap, BME680_REG_GAS_MSB,
(u8 *) &tmp, 2);
if (ret < 0) {
dev_err(dev, "failed to read gas resistance\n");
return ret;
}
gas_range = check & BME680_GAS_RANGE_MASK;
adc_gas_res = be16_to_cpu(tmp) >> BME680_ADC_GAS_RES_SHIFT;
*val = bme680_compensate_gas(data, adc_gas_res, gas_range);
return IIO_VAL_INT;
}
static int bme680_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct bme680_data *data = iio_priv(indio_dev);
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_TEMP:
return bme680_read_temp(data, val, val2);
case IIO_PRESSURE:
return bme680_read_press(data, val, val2);
case IIO_HUMIDITYRELATIVE:
return bme680_read_humid(data, val, val2);
case IIO_RESISTANCE:
return bme680_read_gas(data, val);
default:
return -EINVAL;
}
#ifdef IIO_SUPPORTS_OVERSAMPLING_RATIO
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (chan->type) {
case IIO_TEMP:
*val = data->oversampling_temp;
return IIO_VAL_INT;
case IIO_PRESSURE:
*val = data->oversampling_press;
return IIO_VAL_INT;
case IIO_HUMIDITYRELATIVE:
*val = data->oversampling_humid;
return IIO_VAL_INT;
default:
return -EINVAL;
}
#endif
default:
return -EINVAL;
}
}
#ifdef IIO_SUPPORTS_OVERSAMPLING_RATIO
static bool bme680_is_valid_oversampling(int rate)
{
return (rate > 0 && rate <= 16 && is_power_of_2(rate));
}
#endif
static int bme680_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
#ifdef IIO_SUPPORTS_OVERSAMPLING_RATIO
struct bme680_data *data = iio_priv(indio_dev);
#endif
if (val2 != 0)
return -EINVAL;
switch (mask) {
#ifdef IIO_SUPPORTS_OVERSAMPLING_RATIO
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
{
if (!bme680_is_valid_oversampling(val))
return -EINVAL;
switch (chan->type) {
case IIO_TEMP:
data->oversampling_temp = val;
break;
case IIO_PRESSURE:
data->oversampling_press = val;
break;
case IIO_HUMIDITYRELATIVE:
data->oversampling_humid = val;
break;
default:
return -EINVAL;
}
return bme680_chip_config(data);
}
#endif
default:
return -EINVAL;
}
}
#ifdef IIO_SUPPORTS_OVERSAMPLING_RATIO
static const char bme680_oversampling_ratio_show[] = "1 2 4 8 16";
static IIO_CONST_ATTR(oversampling_ratio_available,
bme680_oversampling_ratio_show);
#endif
static struct attribute *bme680_attributes[] = {
#ifdef IIO_SUPPORTS_OVERSAMPLING_RATIO
&iio_const_attr_oversampling_ratio_available.dev_attr.attr,
#endif
NULL,
};
static const struct attribute_group bme680_attribute_group = {
.attrs = bme680_attributes,
};
static const struct iio_info bme680_info = {
.read_raw = &bme680_read_raw,
.write_raw = &bme680_write_raw,
.attrs = &bme680_attribute_group,
};
static const char *bme680_match_acpi_device(struct device *dev)
{
const struct acpi_device_id *id;
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return NULL;
return dev_name(dev);
}
int bme680_core_probe(struct device *dev, struct regmap *regmap,
const char *name)
{
struct iio_dev *indio_dev;
struct bme680_data *data;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
if (!name && ACPI_HANDLE(dev))
name = bme680_match_acpi_device(dev);
data = iio_priv(indio_dev);
dev_set_drvdata(dev, indio_dev);
data->regmap = regmap;
#ifndef REGMAP_SUPPORTS_GET_DEVICE
data->dev = dev;
#endif
indio_dev->dev.parent = dev;
indio_dev->name = name;
indio_dev->channels = bme680_channels;
indio_dev->num_channels = ARRAY_SIZE(bme680_channels);
indio_dev->info = &bme680_info;
indio_dev->modes = INDIO_DIRECT_MODE;
/* default values for the sensor */
data->oversampling_humid = 2; /* 2X oversampling rate */
data->oversampling_press = 4; /* 4X oversampling rate */
data->oversampling_temp = 8; /* 8X oversampling rate */
data->heater_temp = 320; /* degree Celsius */
data->heater_dur = 150; /* milliseconds */
ret = bme680_chip_config(data);
if (ret < 0) {
dev_err(dev, "failed to set chip_config data\n");
return ret;
}
ret = bme680_gas_config(data);
if (ret < 0) {
dev_err(dev, "failed to set gas config data\n");
return ret;
}
ret = bme680_read_calib(data, &data->bme680);
if (ret < 0) {
dev_err(dev,
"failed to read calibration coefficients at probe\n");
return ret;
}
return devm_iio_device_register(dev, indio_dev);
}
EXPORT_SYMBOL_GPL(bme680_core_probe);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("Bosch BME680 Driver");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0
/*
* BME680 - I2C Driver
*
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
*
* 7-Bit I2C slave address is:
* - 0x76 if SDO is pulled to GND
* - 0x77 if SDO is pulled to VDDIO
*
* Note: SDO pin cannot be left floating otherwise I2C address
* will be undefined.
*/
#include <linux/acpi.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "bme680.h"
static int bme680_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct regmap *regmap;
const char *name = NULL;
unsigned int val;
int ret;
regmap = devm_regmap_init_i2c(client, &bme680_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Failed to register i2c regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
ret = regmap_write(regmap, BME680_REG_SOFT_RESET_I2C,
BME680_CMD_SOFTRESET);
if (ret < 0) {
dev_err(&client->dev, "Failed to reset chip\n");
return ret;
}
ret = regmap_read(regmap, BME680_REG_CHIP_I2C_ID, &val);
if (ret < 0) {
dev_err(&client->dev, "Error reading I2C chip ID\n");
return ret;
}
if (val != BME680_CHIP_ID_VAL) {
dev_err(&client->dev, "Wrong chip ID, got %x expected %x\n",
val, BME680_CHIP_ID_VAL);
return -ENODEV;
}
if (id)
name = id->name;
return bme680_core_probe(&client->dev, regmap, name);
}
static const struct i2c_device_id bme680_i2c_id[] = {
{"bme680", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, bme680_i2c_id);
static const struct acpi_device_id bme680_acpi_match[] = {
{"BME0680", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
static struct i2c_driver bme680_i2c_driver = {
.driver = {
.name = "bme680_i2c",
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
},
.probe = bme680_i2c_probe,
.id_table = bme680_i2c_id,
};
module_i2c_driver(bme680_i2c_driver);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("BME680 I2C driver");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0
/*
* BME680 - SPI Driver
*
* Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com>
*/
#include <linux/acpi.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "bme680.h"
static int bme680_regmap_spi_write(void *context, const void *data,
size_t count)
{
struct spi_device *spi = context;
u8 buf[2];
memcpy(buf, data, 2);
/*
* The SPI register address (= full register address without bit 7)
* and the write command (bit7 = RW = '0')
*/
buf[0] &= ~0x80;
return spi_write_then_read(spi, buf, 2, NULL, 0);
}
static int bme680_regmap_spi_read(void *context, const void *reg,
size_t reg_size, void *val, size_t val_size)
{
struct spi_device *spi = context;
return spi_write_then_read(spi, reg, reg_size, val, val_size);
}
static struct regmap_bus bme680_regmap_bus = {
.write = bme680_regmap_spi_write,
.read = bme680_regmap_spi_read,
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
static int bme680_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
struct regmap *regmap;
unsigned int val;
int ret;
spi->bits_per_word = 8;
ret = spi_setup(spi);
if (ret < 0) {
dev_err(&spi->dev, "spi_setup failed!\n");
return ret;
}
regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus,
&spi->dev, &bme680_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
ret = regmap_write(regmap, BME680_REG_SOFT_RESET_SPI,
BME680_CMD_SOFTRESET);
if (ret < 0) {
dev_err(&spi->dev, "Failed to reset chip\n");
return ret;
}
/* after power-on reset, Page 0(0x80-0xFF) of spi_mem_page is active */
ret = regmap_read(regmap, BME680_REG_CHIP_SPI_ID, &val);
if (ret < 0) {
dev_err(&spi->dev, "Error reading SPI chip ID\n");
return ret;
}
if (val != BME680_CHIP_ID_VAL) {
dev_err(&spi->dev, "Wrong chip ID, got %x expected %x\n",
val, BME680_CHIP_ID_VAL);
return -ENODEV;
}
/*
* select Page 1 of spi_mem_page to enable access to
* to registers from address 0x00 to 0x7F.
*/
ret = regmap_write_bits(regmap, BME680_REG_STATUS,
BME680_SPI_MEM_PAGE_BIT,
BME680_SPI_MEM_PAGE_1_VAL);
if (ret < 0) {
dev_err(&spi->dev, "failed to set page 1 of spi_mem_page\n");
return ret;
}
return bme680_core_probe(&spi->dev, regmap, id->name);
}
static const struct spi_device_id bme680_spi_id[] = {
{"bme680", 0},
{},
};
MODULE_DEVICE_TABLE(spi, bme680_spi_id);
static const struct acpi_device_id bme680_acpi_match[] = {
{"BME0680", 0},
{},
};
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
static struct spi_driver bme680_spi_driver = {
.driver = {
.name = "bme680_spi",
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
},
.probe = bme680_spi_probe,
.id_table = bme680_spi_id,
};
module_spi_driver(bme680_spi_driver);
MODULE_AUTHOR("Himanshu Jha <himanshujha199640@gmail.com>");
MODULE_DESCRIPTION("Bosch BME680 SPI driver");
MODULE_LICENSE("GPL v2");
......@@ -25,7 +25,6 @@ requires:
{
$CURDIR/../bmi160/bmi160-i2c
$CURDIR/../bme680/bme680-i2c
$CURDIR/../rtc-pcf85063/rtc-pcf85063
$CURDIR/../bq25601/bq25601
$CURDIR/../bq27xxx/bq27xxx_battery
......
......@@ -57,6 +57,10 @@ apps:
// $MANGOH_ROOT/apps/YellowSensorToCloud/yellowSensor
$LEGATO_ROOT/apps/sample/dataHub/dataHub
$MANGOH_ROOT/apps/Mcp9700aTemperatureSensor/mcp9700aTemperatureSensor
$MANGOH_ROOT/apps/Bme680EnvironmentalSensor/bme680EnvironmentalSensor
$MANGOH_ROOT/samples/BsecTest/bsecTest
#endif // MANGOH_BOARD
$MANGOH_ROOT/apps/MqttClient/mqttClient
......@@ -65,7 +69,6 @@ apps:
$MANGOH_ROOT/apps/SocialService/socialService
$LEGATO_ROOT/apps/tools/devMode
}
commands:
......@@ -92,6 +95,8 @@ interfaceSearch:
#elif ${MANGOH_BOARD} = yellow
$LEGATO_ROOT/apps/sample/dataHub
$MANGOH_ROOT/apps/Mcp9700aTemperatureSensor
$MANGOH_ROOT/apps/Bme680EnvironmentalSensor
#endif // MANGOH_BOARD
$MANGOH_ROOT/apps/DataRouter
......@@ -121,6 +126,7 @@ componentSearch:
kernelModules:
{
#if ${MANGOH_BOARD} = red
$CURDIR/linux_kernel_modules/mangoh/mangoh_red
$CURDIR/linux_kernel_modules/mt7697wifi/mt7697wifi_core
......@@ -164,7 +170,6 @@ kernelModules:
$CURDIR/linux_kernel_modules/iio/iio-kfifo-buf
$CURDIR/linux_kernel_modules/iio/iio
#endif // MANGOH_KERNEL_LACKS_IIO
$CURDIR/linux_kernel_modules/bme680/bme680-i2c
$CURDIR/linux_kernel_modules/bmi160/bmi160-i2c
$CURDIR/linux_kernel_modules/bmi160/bmi160
$CURDIR/linux_kernel_modules/bmm150/bmc150_magn_i2c
......
sandboxed: true
version: 1.0.0
start: auto
executables:
{
bsecTest = ( bsecTestComponent )
}
processes:
{
run:
{
( bsecTest )
}
faultAction: restart
}
bindings:
{
bsecTest.bsecTestComponent.mangOH_bme680 -> bme680EnvironmentalSensor.mangOH_bme680
}
\ No newline at end of file
cflags:
sources:
{
// This driver depends on IIO
-DCONFIG_IIO
-DCONFIG_IIO_BUFFER
-DCONFIG_IIO_TRIGGERRED_BUFFER
-DDEBUG
-DREGMAP
-DREGMAP_I2C
component.c
}
sources:
cflags:
{
bme680_i2c.c
bme680_core.c
-std=c99
}
requires:
{
kernelModules:
api:
{
#if ${MANGOH_KERNEL_LACKS_IIO} = 1
$CURDIR/../iio/iio
#endif // MANGOH_KERNEL_LACKS_IIO
mangOH_bme680.api
}
}
#include "legato.h"
#include "interfaces.h"
static void SensorReadingHandler(
const mangOH_bme680_Reading_t *reading,
void* context
)
{
LE_INFO("bme680 reading received with ts=%llu", reading->timestamp);
LE_INFO(" iaq_valid=%d, iaq=%f, iaq_accuracy=%d",
reading->iaq.valid, reading->iaq.value, reading->iaq.accuracy);
LE_INFO(" co2_equivalent_valid=%d, co2_equivalent=%f, co2_equivalent_accuracy=%d",
reading->co2Equivalent.valid, reading->co2Equivalent.value, reading->co2Equivalent.accuracy);
LE_INFO(" breath_voc_valid=%d, breath_voc=%f, breath_voc_accuracy=%d",
reading->breathVoc.valid, reading->breathVoc.value, reading->breathVoc.accuracy);
LE_INFO(" pressure_valid=%d, pressure=%f", reading->pressure.valid, reading->pressure.value);
LE_INFO(" temperature_valid=%d, temperature=%f", reading->temperature.valid, reading->temperature.value);
LE_INFO(" humidity_valid=%d, humidity=%f", reading->humidity.valid, reading->humidity.value);
}
COMPONENT_INIT
{
mangOH_bme680_AddSensorReadingHandler(SensorReadingHandler, NULL);
const bool enableIaq = true;
const bool enableCo2Equivalent = true;
const bool enableBreathVoc = true;
const bool enablePressure = true;
const bool enableTemperature = true;
const bool enableHumidity = true;
le_result_t configRes = mangOH_bme680_Configure(
MANGOH_BME680_SAMPLING_RATE_LP,
enableIaq,
enableCo2Equivalent,
enableBreathVoc,
enablePressure,
enableTemperature,
enableHumidity);
LE_ASSERT_OK(configRes);
}
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