Finally BME680 driver is now upstream….

The final version v5 was sent and applied whilst one minor cleanup which Jonathan did himself without bothering me for v6 patch.

Final Link: https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?id=1b3bd8592780c87c5eddabbe98666b086bbaee36

And yes, it will be next coming release of Linux kernel 🙂

It was an amazing journey and got to learn a hell lot of concepts from mentors, reviewers, and community.

Special thanks to Daniel Baluta, Jonathan Cameron, David Frey, Andy Shevchenko, Matt Ranostay, Greg Kroah-Hartman and the kernel community.

Again thanks Greg for funding the hardware for my project!

I have successfully completed Google Summer of Code’18 project and this is the last submission week. But this is not the end 😉

I opened a new discussion thread for further support of the driver. Much fun ahead 🙂

P.S: I sincerely apologise my blog readers for not being consistent with regular blogging and specially I saw one single person from Croatia (perhaps a future GSoC/Outreachy aspirant) regularly visiting my blog. So, if you need ANY help please poke in comments or write me personally, and I would be glad to be of any help 🙂
Really sorry!

[PATCH v4] iio: chemical: Add support for Bosch BME680 sensor

So, again I sent the next version hoping that it would be merged this time xP
But I was wrong and again received few more comments especially from Andy Shevchenko (Senior Linux Kernel Developer @ Intel) about the 64 bit heavy multiplications in the bme680_compensate_gas().

What I really love about kernel community is that the reviewers/hobbyist can come from any subsystem and review your code irrespective of their primary work. AFAIK Andy’s primary job at Intel is not involved with IIO and I have often seen him reviewing code ranging to several subsystems.

Anyways, let’s first take a look at code snippet of discussion:


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 1 for the possible gas range values */
u32 lookupTable1[16] = {2147483647u, 2147483647u, 2147483647u,
2147483647u, 2147483647u, 2126008810u,
2147483647u, 2130303777u, 2147483647u,
2147483647u, 2143188679u, 2136746228u,
2147483647u, 2126008810u, 2147483647u,
2147483647u};
/* Look up table 2 for the possible gas range values */
u32 lookupTable2[16] = {4096000000u, 2048000000u, 1024000000u,
512000000u, 255744255u, 127110228u, 64000000u,
32258064u, 16016016u, 8000000u, 4000000u,
2000000u, 1000000u, 500000u, 250000u, 125000u};

var1 = ((1340 + (5 * (s64) calib->range_sw_err)) *
((s64) lookupTable1[gas_range])) >> 16;
var2 = (((s64) ((s64) gas_res_adc < 15) - 16777216) + var1);
var3 = (((s64) lookupTable2[gas_range] * (s64) var1) >> 9);
calc_gas_res = (u32) ((var3 + ((s64) var2 >> 1)) / (s64) var2);

return calc_gas_res;
}

So, Andy writes:

>> > + /* Look up table 2 for the possible gas range values */
>> > + u32 lookupTable2[16] = {4096000000u, 2048000000u, 1024000000u,
>> > + 512000000u, 255744255u, 127110228u, 64000000u,
>> > + 32258064u, 16016016u, 8000000u, 4000000u,
>> > + 2000000u, 1000000u, 500000u, 250000u, 125000u};

…this one obviously just a not needed one. You may replace it with a one constant and simple calculation to get either value (index from value, or value from index).

The solution was to use near approximation of array members as clearly we can figure out that the members are indeed power of 2 * 125000. Conclusion was to use bitshifting technique and avoid 64 bit expensive multiplications as evident:

var3 = (((s64) lookupTable2[gas_range] * (s64) var1) >> 9);

This was replaced by:

var3 = ((125000 << (15 – gas_range)) * var1) >> 9;

Amazing! Isn’t it ?
Saves a lot CPU cycles and speeds up calculations 🙂

I tested this new shifting technique and there was no such odd behavior that was visible. But had to confirm since, it is a near approximation, and might be possible that we could get disasterous results! For instance,

125000 << 8 = 32000000

and not 32258064 which is near enough.

But in my opinion it doesn’t matter for gas sensor because the gas sensor simply outputs in Ωs, which is often used in calculating IAQ index. So, there will be an adjustment in scale only. For eg. if you previously got 90kΩ maybe now we get 100kΩ or maybe 80kΩ but doesn’t hurt anything other than adjusting IAQ reference scale.

What I observed while testing was that bad air quality is inversely proportional to the gas readings. Infact, when I sprayed my deodorant right above the sensor, the reading showed a major downfall instantaneously from 88kΩ to 1300Ω to further 530Ω for few seconds. Then again it slowly began to rise further while calibrating with the environment.

If it were the case for temperaure/pressure/humidity channel then certainly it was BIG NO! for approximating the members.

Anothering review comment was what kbuild test robot (Intel Open Source Technology Center) hit while building through the numerous configurations! This bot tests in about 300 different kernel configuration and very likely catches huge number of bugs. I have this testing service since November’17 and I test all patches through it before sending to the maintainers.

Special thanks to Fengguang Wu 🙂

make ARCH=i386

All errors (new ones prefixed by >>):

drivers/iio/chemical/bme680_core.o: In function `bme680_compensate_gas’:
>> drivers/iio/chemical/bme680_core.c:450: undefined reference to `__divdi3′

vim +450 drivers/iio/chemical/bme680_core.c

> 450 calc_gas_res = (u32) ((var3 + ((s64) var2 >> 1)) / (s64) var2);

So, the build broke due 64 bit division on 32 bit arch(i386) if you want to exlore more, then read here. The solution was simply to use div64_s64() and there would be no build errors even on i386.

Last review comment from Jonathan since v3 was due to redundant casting in the compensate functions. Therefore, I removed the all those unnecessary casts carefully as the implicit conversion rules of C language are one of the scariest mindfuck if you don’t handle them with care!

[PATCH v3] iio: chemical: Add support for Bosch BME680 sensor

So, after cleaning the read_calib() mess, I sent the next version for review with few minor cleanups.

This time a new reviewer David Frey (Seniro Staff Engineer @ Sierra Wireless) pointed some vital review comments. He is one of those who will be using the sensor in their product mangOH board.

Comments:

  1. Consistency with indenting macros and its subfileds: At IIO drivers we somehow try to add a stray space to the macro to differnentiate between register address, its subfield bit and val to be written to that subfiled/register.

#define  BME680_REG_X                                             0x00
#define       BME680_X_FOO_EN_MASK                    BIT(0)
#define       BME680_X_BAR_MASK                           GENMASK(3, 1)
#define                BME680_BAR_VAL1                        3
#define                BME680_BAR_VAL2                        7

Not sure if you see the indenting pattern !? WordPress wraps the spaces I guess.

2. The device has some peculiar characteristics that soft reset register address differs for  I2C and SPI. For I2C it is 0xE0 and for SPI it is 0x60 when page 0 is selected. Therefore, I added the SPI soft reset address.

Now again, you must be wondering about page 0 ?

Yes, the SPI has two pages:
In SPI mode complete memory map is accessed using page 0 and page 1. Register spi_mem_page is used for page selection.
After power-on, spi_mem_page is in its reset state and page 0 (0x80 to 0xFF) will be active. Page 1 (0x00 to 0x7F) will be active on setting spi_mem_page to 1. Refer Global Memory Map for more details in the datasheet.

3. Use of FIELD_PREP macro from <linux/bitfield.h> to eliminate few open coding techniques I used and thereby replacing them with:

ctrl_meas_reg = FIELD_PREP(BME680_OSRS_TEMP_MASK, temp_val) |
FIELD_PREP(BME680_OSRS_PRESS_MASK, press_val) |
FIELD_PREP(BME880_MODE_MASK, mode_val);

4. Add links for each compensation function used from the Bosch API and also document what values are returned by the functions. Something like:

/*
* 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.
*/

5. Matt Ranostay’s nit-picky comments 😉 –>

> +config BME680_I2C
> + tristate
> + depends on BME680
> + depends on I2C

Wouldn’t “depends on I2C && BME680” be cleaner? Maybe someone else
here can tell me if I’m too nit-picky 🙂

 

[PATCH v2] iio: chemical: bme680: Add Bosch BME680 sensor support

Meanwhile I sent the v2 with the changes discussed in the RFC patch and got some interesting review from Jonathan:

  1. I used the wrapper to do the general endian conversions:

/* Macro to combine two 8 bit data’s to form a 16 bit data */

#define BME680_CONCAT_BYTES(MSB, LSB) (((uint16_t)MSB << 8) | (uint16_t)LSB)

Now, this macro was used in the read_calib(), where I read and store the calibration parameters only once during the probe since these constants don’t change. These constants are fed into the NVM of the sensor at the time of production and can’t be altered by the user. Anyway, one of thing that bugs every maintainer/reviewer is that
DON’T ROLL OUT YOUR OWN WRAPPER FUNCTIONS AND USE STANDARD FUNCTIONS AVAILABLE IN THE KERNEL!!

So, what he meant was to use standard conversion functions like be16_to_cpu() instead of rolling my own concat wrapper.

2. Use direct returns save a local variable, but use error handling if there is possibility of error.

3. In bme680_read_calib(), I read all the compensation parameters registers in one go and then did the endian conversion while storing the result.

Now this had few problems:

  • What if some calibration parameter register read fails ? And we are left with garbage value perhaps ?
  • The reworking of calibration data and placing them into appropriate structures wasted a lot of time.

Therefore, the solution was to read the calibration data serially and do the standard conversions while placing them at their appropriate place.

But wait! I didn’t knew the individual addresses of each calibration registers ? Remember I discussed in an earlier post that these data are missing from the BME680 datasheet 😦

I thought for a while and had a meeting with Daniel(mentor) and we agreed upon idea of relative addressing of register address.

Let me explain you how actually regmap_bulk_read() works:

We need to supply following values to the function:

  • pointer to regmap struct.
  • starting address of register to read.
  • buffer to store the data.
  • number of registers to read serially.

Therefore, I reverse engineered all the calibration registers address 😉

To clearly understand first take a look at Bosch implementation and now how I reverse engineered the addresses is below:

*********************************
Array — Relative — Register
address
*********************************
tbuf[0] — 0x89 — starting address(25 bytes read, nothing here)
tbuf[1] — +1 0x8A — BME680_T2_LSB_REG
tbuf[2] — +2 0x8B — BME680_T2_MSB_REG
tbuf[3] — +3 0x8C — BME680_T3_REG
tbuf[4] — +4 0x8D — nothing here
tbuf[5] — +5 0x8E — BME680_P1_LSB_REG
tbuf[6] — +6 0x8F — BME680_P1_MSB_REG
tbuf[7] — +7 0x90 — BME680_P2_LSB_REG
tbuf[8] — +8 0x91 — BME680_P2_MSB_REG
tbuf[9] — +9 0x92 — BME680_P3_REG
tbuf[10] — +10 0x93 — nothing here
tbuf[11] — +11 0x94 — BME680_P4_LSB_REG
tbuf[12] — +12 0x95 — BME680_P4_MSB_REG
tbuf[13] — +13 0x96 — BME680_P5_LSB_REG
tbuf[14] — +14 0x97 — BME680_P5_MSB_REG
tbuf[15] — +15 0x98 — BME680_P7_REG
tbuf[16] — +16 0x99 — BME680_P6_REG
tbuf[17] — +17 0x9A — nothing here
tbuf[18] — +18 0x9B — nothing here
tbuf[19] — +19 0x9C — BME680_P8_LSB_REG
tbuf[20] — +20 0x9D — BME680_P8_MSB_REG
tbuf[21] — +21 0x9E — BME680_P9_LSB_REG
tbuf[22] — +22 0x9F — BME680_P9_MSB_REG
tbuf[23} — +23 0xA0 — BME680_P10_REG
tbuf[24] — +24 0xA1 — nothing here

Now, we stop here because we did bulk read of 25 bytes
into the buffer. So, lets start now with the other address
0xE1 where we did bulk read of 16 bytes.

tbuf[25] — 0xE1 — BME680_H2_MSB_REG
tbuf[26] — +1 0xE2 — BME680_H2_LSB_REG & BME680_H1_LSB_REG(H2 is BigEndian here!!)
tbuf[27] — +2 0xE3 — BME680_H1_MSB_REG
tbuf[28] — +3 0xE4 — BME680_H3_REG
tbuf[29] — +4 0xE5 — BME680_H4_REG
tbuf[30] — +5 0xE6 — BME680_H5_REG
tbuf[31] — +6 0xE7 — BME680_H6_REG
tbuf[32] — +7 0xE8 — BME680_H7_REG
tbuf[33] — +8 0xE9 — BME680_T1_LSB_REG
tbuf[34] — +9 0xEA — BME680_T1_MSB_REG
tbuf[35] — +10 0xEB — BME680_GH2_LSB_REG
tbuf[36] — +11 0xEC — BME680_GH2_MSB_REG
tbuf[37] — +12 0xED — BME680_GH1_REG
tbuf[38] — +13 0xEE — BME680_GH3_REG
tbuf[39] — +14 0xEF — nothing here
tbuf[40] — +15 0xF0 — nothing here

So, I read the calib parameters serially while performing the necessary error handling.
Click here to see the update code!

0x03[BME680]: Gas channel

In the post I will be discussing about the gas channel of the sensor.

The gas channel is the most complex part of this device with some peculiar characteristics unlike the other channels discussed so far.

There are two things that needs to be supplied for the gas config setup:

  1. Heater temperature: It is the target temperature at which heater operation occurs.
  2. Heater duration: The amount of duration for which the hot plate is heaterd.

Usually, heater temperature should lie around 200 ºC – 400 ºC and duration should be less than 4032ms. But if the combination of these values are not suitable then the heater will not be stable and we can’t get the readings.

Therefore, it is imperative to check for stability of heater before actually taking the readings via regmap_bulk_read(). So, as a solution I wrote the code:

5.3.5.6 Heater Stability Status
Heater temperature stability for target heater resistance is indicated heat_stab_x status bits.(heat_stab_r<4>)

/*
* 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;
}

After supplying the configuration, to start gas conversion we need to set run_gas<4> bit at 0x71 address.

5.3.3.6 Run Gas – run_gas

The gas conversions are started only in appropriate mode if run_gas = ‘1’.

The gas sensor neeeds some time to calibrate according to the environment. The configuration ideally depends upon the environment and on temperature channel. So, the perfect comination of heater temperaure & duration is hard to decide/guess because temperature channel is slightly resposible for the heating of the sensor and pehaps the heating duration might be wasteful in order of few milliseconds.

On the other hand, if you solely use the gas sensor then maybe the duration be justified. I didn’t think much about it as we have much more complex stuff to care about. And yes, Jonathan Cameron(IIO Maintainer) also suggested the same:

Jonathan writes:

Hmm. I guess this issue here is that the temperature rise time is
dependent on the environment (moisture / temperature etc) so we can’t
do the obvious and just put in reasonable defaults (assuming the temperature
is the only controlled channel).

So, the reasonable defaults as suggested by Bosch are:

  1. data->heater_temp = 320; /* degree Celsius */
  2. data->heater_dur = 150; /* milliseconds */

I supplied the above values to the sensor when the device gets probed.
By the way, these two values are not directly written to the register but they are converted to some special register code with the help of Bosch API, and thus the value obtained is written to the respective registers.

0x02[BME680]: Pressure & Humidity channel

Last post discussed about the temperature channel and now its time to discuss Pressure and humidity channel.

The pressure channel is similar to that of temperature channel in terms of resolution i.e., 20 bits, but humidity is fixed at 16 bit resolution.

Now, coming to the compensation functions discussed in an earlier post:
I got a postive reply from Bosch Sensortec GmbH allowing to use their compensation algorithms(BME680 API) by adding their copyright tag on top of source.

* Copyright (C) 2017 – 2018 Bosch Sensortec GmbH

They also confirmed that I was correct about the resolution of the output data.
Remember 3254 * 0.01 = 32.54 °C ?

Yes, the temperature resolution is 0.01 degC!
And similarly others are deduced by:

https://github.com/BoschSensortec/BME680_driver#example-for-reading-all-sensor-data

printf("T: %.2f degC, P: %.2f hPa, H %.2f %%rH ", data.temperature / 100.0f,
            data.pressure / 100.0f, data.humidity / 1000.0f );
printf(", G: %d ohms", data.gas_resistance);

So, you can too guess the resolutions now 😉

  1. Temperature: 0.01 DegC
  2. Pressure:  0.01 hPa
  3. Humidity: 0.001 percent %r.H
  4. Gas: linear (ohms)

Meanwhile I also sent an RFC — Request For Comment patch with these three channels: https://marc.info/?t=152960352900055&r=1&w=2

Reviews :

  1. Remove the abstracted design pattern i.e., the driver was too generic desgin and since we don’t need to support any other sensor along with it, so making it abstracted is redundant.
  2. Identations/Spaces/Blank lines.
  3. Not an Inertial Measurement Unit(IMU) sensor, therefore move it suitably to “chemical” directory since Gas sensor seemed to be the intergeral part of the sensor. Suggested by –> Matt Ranostay (Chemical Sensors expert)
  4. SPDX License instead of redundant lines of code at top of source file. Slowly, all drivers are changing into SPDX indentifier.
  5. ACPI naming ? Refered Advanced Configuration and Power
    Interface Specification manual.

6.1.5 _HID (Hardware ID)

A valid PNP ID must be of the form “AAA####” where A is an uppercase letter and # is a hex digit. A valid ACPI ID must be of the form “NNNN####” where N is an uppercase letter or a digit (‘0’-‘9’) and # is a hex digit. This specification reserves the string “ACPI” for use only with devices defined herein. It further reserves all strings representing 4 HEX digits for exclusive use with PCI-assigned Vendor IDs.

Name (_HID, “BME0680”)

19.5.55 I2CSerialBus (I2C Serial Bus Connection Resource Descriptor
Macro)

Syntax
I2CSerialBus ( SlaveAddress , SlaveMode , ConnectionSpeed , AddressingMode , ResourceSource ,
ResourceSourceIndex , ResourceUsage , DescriptorName , VendorData )

I2cSerialBus (0x0076, ControllerInitiated, 0x00061A80, AddressingMode7Bit, “\\I2C0”,
0x00, ResourceConsumer, ,)

Want to dig more ?
Go ahead and download the manual: http://www.acpi.info/DOWNLOADS/ACPI_5_Errata%20A.pdf

I didn’t write the Differentiated System Description Table (DSDT) by myself, instead I had setup already configured (just with small tweak by chaning _HID and I2C bus address) ready to go.

An lwn article which I found will surely help you understand about ACPI.

0x01[BME680]: Temperature channel

This post discusses about obtaining calibrated readings from the temperature channel.

So, according to the datasheet the temperature channel is a 20 BIT resolution register divided into:

  1. temp_msb (0x22) <7:0> : Contains the MSB part of raw output data.
  2. temp_lsb (0x23) <7:0> : Contains the LSB part of raw output data.
  3. temp_xlsb (0x24) <7:4> : Contains the XLSB part of raw output data depending on the oversampling ratio setting.

The various oversampling ratio available are {1, 2, 4, 8, 16} and can be set by setting osrs_t<2:0> field of ctrl_meas register.

IIR Filter :

The sensor also has an optional IIR filter to remove short term fluctuations from the temperature and pressure readings. This filtered output reading is responsible for the XLSB(4 Bits) part of measurement.

  • When the IIR filter is enabled, the temperature resolution is 20 bit.
  • When the IIR filter is disabled, the temperature resolution is 16 + (osrs_t – 1) bit, e.g. 18 bit when osrs_t is set to ‘3’.

The sensor has two modes:

  1. Sleep mode.
  2. Forced mode.

These modes can be selected using the mode<1:0> control register and for reading the data it should be in Forced mode only. Also, the sensor automatically returns to sleep mode after a single T P H G cycle is performed.

For reading the temp data, I wrote a function bme680_read_temp() which basically does a regmap_bulk_read() from the 3 registers specified above. Since, we can clearly see from above register addressing the MSB resides at low address and LSB Byte at High address which means it follows Big Endian and I used __be32 type so that if any address space violation takes place Sparse will warn about it.

This raw data need to be compensated using the BME6xy API provided by Bosch Sensortec. This compensation function manipulates the raw data to give out the compensated readings. Without the use of the function we simply cannot get calibrated readings therefore I contacted Bosch Sensortec GmbH about the usage of the mentioned function in my code and several other register addressing and calibration parameters.

We could easily use their function by adding:

  • Copyright (C) 2017 – 2018 Bosch Sensortec GmbH

to the top of our source code but Linux kernel uses GPL License and Bosch has uploaded
a different License file. I’m not sure kernel community would entertain such License file or adding those 39 lines of text above my source code. So, we are still waiting for their approval and legally use their code.

Anyway, for these compensation function we need calibration parameters which are programmed into the devices’ non-volatile memory (NVM) during
production and cannot be altered by the customer. These calibration parameters along with the raw_data supplied from the bme680_read_temp() combined produce the compensated output.

But sadly these details are missing from the datasheet! So, how did I know come to know about these missing details ?

Well, firstly we already have such sensors (not exactly like bme680) in the mainline kernel such as bmp280, bme280, bmi180 consisting of pressure sensors, IMUs and few others. So, I referred especially to the bmp280 in iio/pressure/ directory which is also supplied by Bosch Sensortec. And it has all the data mentioned in the datasheet such as:

  1. calibration parameters: “3.11.2 Trimming parameter readout” Pg21 BMP280 Digital Pressure Sensor datasheet
  2. resolution of the readings output: “3.11.3 Compensation formula
    1. “// Returns temperature in DegC, resolution is 0.01 DegC. Output value of “5123” equals 51.23 DegC.”
    2. /”/ Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits).
      // Output value of “24674867” represents 24674867/256 = 96386.2 Pa = 963.862 hPa”

So, we can see all these details supplied in the BMP280 datasheet but not in my case!

Therefore, I asked Bosch about these specific missing details and waiting for their reply. For now I have used these functions and calibration details from BME6xy API just to see what readings I get. And I get the correct readings if BME680 has the same/similar resolution as in the case of BMP280. But we can’t be sure until we get a confirmation from Bosch.

The various device attributes:

Screenshot from 2018-06-14 13-34-38

And the readings that I get is below:

Screenshot from 2018-06-14 13-35-16

Don’t mind those reports before the readings in the above image. That was only for debugging purposes 😉

Alright! We get a reading of 3254 which is perfect if it has a similar 0.01 °C resolution as we saw in BMP280 sensor.

Therefore,

3254*0.01 = 32.54 °C

which is my current room temperature 🙂

So, that’s it for this post and I will be discuss about Pressure and subsequent channels in the next post and also, about ACPI matching which I got to learn from my mentor.

Gist Source Code: temperature channel

0x00 [BME680]: Implementing a simple probe function

This is the first part of writing driver for the sensor module and stay tuned for incremental blog series.

In this post, I wrote a skeletal driver doing a minimal probe and registering to the kernel. The probe does a simple power on reset and then checks for chip_id. The driver code uses the Regmap API to avoid unnecessary redundancy.

regmap_api

Before the regmap API was developed, there were redundant codes for the device drivers dealing with SPI core, I2C core, or both. The principle was the same; accessing the register for read/write operations. The following figure shows how either SPI or I2C API were standalone before Regmap was introduced to kernel.

The regmap API was introduced in version 3.1 of the kernel, to factorize and unify the way kernel developers access SPI/I2C devices.

Now, while testing the code I got the following error:

Screenshot from 2018-05-09 16-35-15

bme680_i2c: probe of 7-0077 failed with error -71

include/uapi/asm-generic/errno.h: #define EPROTO 71 /* Protocol error */

So, it was due to protocol error!
But there was no problem with the code and I reconfirmed with Daniel and he told me check my connections. The connections were perfectly fine since it was detected by i2cdetect utility. So, I began to dump registers using i2cdump utility and checked the chip_id register located at 0xd0 with I2C and 0x50 with SPI. The dump of register had a value 0x61 at address 0xd0 which is the reset state of the sensor according to the datasheet.

After 2-3 hours of debugging, I came to know a about very important information regarding the I2C interface of the device from the datasheet:

Pg 35/50

6.2 I2C Interface

The 7-bit device address is 111011x. The 6 MSB bits are fixed. The last bit is changeable by SDO value and can be changed
during operation. Connecting SDO to GND results in slave address 1110110 (0x76); connection it to V DDIO results in slave
address 1110111 (0x77), which is the same as BMP280’s I2C address. The SDO pin cannot be left floating; if left floating, the
I2C address will be undefined.

So, yes, I had left the SDO pin floating and that is why the I2C interface was not working!
Therefore, I added a jumper from SDO pin to GND pin and ….

img_20180509_205834.jpg

voilà!

Screenshot from 2018-05-09 20-54-45

Note that after you connect SDO –> GND, the I2C default address(0x77) changes to 0x76. So, I got a reset state value of 0x61 when read the I2C chip_id address 0xd0.

Trivial Note:

While instantiating the i2c device from userspace, I posed the following error which seems interesting (Take a look at the first image for details) :

$ echo bme680 0x76 > new_device

bash: new_device: Permission denied

The problem is how bash interprets the command: Normally prefixing a command with sudo will give you root privileges, but because of the way that the command is interpreted, the echo bme680 0x76 will be run as root, but not the > part which writes to the /sys/class… file. To resolve this issue use:

himanshu@himanshu-Vostro-3559:$sudo bash 
root@himanshu-Vostro-3559:#echo bme680 0x76 > /sys/class/i2c-adapter/i2c-7/new_device

Source Code: Gist Link

Hardware Setup

There are two devices required in the project:

  1. DLN-2 USB-I2C/SPI/GPIO ADAPTER
  2. I2C CABLE WITH CLIPS AND CROCODILE
  3. Adafruit BME680

The first two products arrived from Israel through Diolan and the sensor from U.S bought from Mouser Electronics.

Greg Kroah-Hartman from Linux Foundation has funded all the payment to buy these stuff and tinker.

IMG_20180502_121417

The actual sensor is in the extremely compact metal-lid LGA package with a
footprint of only 3.0 × 3.0 mm2 with a maximum height of 1.00 mm (0.93 ± 0.07 mm).

Since the sensor supports I2C and SPI both so we need a bridge circuit between my machine and the sensor. The Diolan DLN2 adapter already has support in the mainline kernel and it is easy to test compiled kernel with QEMU.

IMG_20180502_121623

IMG_20180509_210724

For now I have planned to test the sensor with Arduino UNO using the Adafruit BME680 library which is readily available online.

IMG_20180510_105249

IMG_20180510_105223

As you can see that the sensor communicates through serial monitor and we get calibrated readings with the help Adafruit BME680 library.

For easy installation and instructions follow here

I will be testing/burning for at least 2 days since it is recommended:

Please note, this sensor, like all VOC/gas sensors, has variability and to get precise measurements you will want to calibrate it against known sources! That said, for general environmental sensors, it will give you a good idea of trends and comparisons. We recommend that you run this sensor for 48 hours when you first receive it to “burn it in”, and then 30 minutes in the desired mode every time the sensor is in use. This is because the sensitivity levels of the sensor will change during early use and the resistance will slowly rise over time as the MOX warms up to its baseline reading.

QEMU Setup:

We need to test the kernel image and check for build/boot errors, regressions, loading/unloading sensor modules. Therefore, QEMU is used for this purpose along with kvm extension, which is extremely fast way boot up and test kernel image. You may follow kernel-labs setup to begin testing.

For testing the kernel I have included all the Diolan DLN2 modules to be built-in(*) since we don’t need to change/test those and bme680 modules are added as modules[M] with the make menuconfig command.

Compiling the kernel for the first time would take 1-2 hours depending on your CPU and too speed up use make ‘nproc’ but that would eat up all the cores and you won’t be able to perform any other tasks parallel to it.

Introduction

Hello everyone! My name is Himanshu Jha, an undergraduate student, currently in 3rd year pursuing Electronics & Communication Engineering from Guru Tegh Bahadur Institute of Technology, New Delhi, India.

I have been selected for Google Summer of Code(GSoC) 2018 under Linux Foundation.
My project

For the next three months I will be working on Bosch BME680 sensor under the mentor ship of Daniel Baluta.

The project aims at developing a device driver for the sensor using the Industrial Input/Output(IIO) interface developed by Jonathan Cameron(IIO subsytem Maintainer).

The driver will be responsible to collect data from each of the four channels that are:

  1. Temperature.
  2. Pressure.
  3. Humidity.
  4. Gas.

The sensor communicates via I2C and SPI communication protocol and the data collected from the various channels are exported from kernelspace to userspace using the sysfs bindings with the help of IIO core. The goal is also to add triggered buffer readings and lastly, the power management support to allow suspend and resume functions.

Documentation: https://www.kernel.org/doc/html/latest/driver-api/iio/index.html
YouTube:
1. Industial I/O And You by Matt Ranostay
2. libiio – Access to Sensor Devices Made Easy by Lars-Peter Clausen