Objective
This post will walk you through steps on how to integrate the BME688 gas sensor driver with the PX4 Autopilot. Bosch has well written documentations on how to write your own driver for your specific platform with the sensor interface. My driver is tailored to work with the PX4 I2C/SPI HAL using the BME68x-Sensor-API, you could find details on my Github page.
To demonstrate sensor usage plus telemetry, we will create a sensor publishing library that you could launch from the Nuttx shell command line, either from a text terminal or qGroundControl console (software v1.13.2
has been tested and ran on the NXP FMUK-66 but could be easily extended for any PX4 flight controllers)
Code Walkthrough
The Bosch API handles low level register interfacing and memory mapping on both SPI/I2C communication buses as it would only expect user applications to provide on primarily 3 interface methods, a) read, b) write, c) delay (per hardware instance, either I2C or SPI, total of 6 if supporting both communication buses) Underlying implementation is agnostic to the device bus protocol, you could reference bme68x_defs.h
header for what the exact bme68x_dev
structure expects.
void *intf_ptr
must reference to either of interface pointers and device address. The assigned enum bme68x_intf intf
interface will call the associates methods given by the user at runtime. Refer to BME688::initPX4-BME688-Driver/bme688/BME688.cpp
for code implementation - see snippet below:
#if defined(CONFIG_I2C)
i2c_intf.i2c_slv_addr = slv_addr;
i2c_intf.i2c_cinst = reinterpret_cast<device::I2C*>(_interface);
dev.intf_ptr = &i2c_intf;
#endif
#if defined(CONFIG_SPI)
spi_intf.spi_slv_addr = slv_addr;
spi_intf.spi_cinst = reinterpret_cast<device::SPI*>(_interface);
dev.intf_ptr = &spi_intf;
#endif
dev.read = dev_read;
dev.write = dev_write;
dev.delay_us = dev_delay_us;
dev.amb_temp = 25;
HardwareConfiguration
The default I2C addresses: 0x76, 0x77 (Adafruit breakout has default address of 0x77. For other vendor design, minor trace cut is usually required to swap to another address)
The FMUK-66 shares its external I2C bus with NFC port. The external SPI has its own dedicated port.
Firmware Setup
Follow these instructions on setting up PX4 before proceeding to the setup steps. You will want to properly compile the PX4 stack before uploading and flashing a pixhawk hardware target. Clone driver as submodule from Github. The code will reside under <PX4-Autopilot/src/driver>
. You could default Kconfig to "on" so the driver is compiled and included in Nuttx console.
menu "gas"
menuconfig COMMON_GAS
bool "Common gas"
default y
select DRIVERS_GAS_BOSCH_BME688
select DRIVERS_GAS_BOSCH_BME688
---help---
Enable default set of gas drivers
rsource "*/Kconfig"
endmenu #gas
Once completed the PX4 upload and boot up process, goto NuttX shell console (either through serial or qGroundControl). Type bme688
and you should see the prompt helper as shown below (this driver is designed in similar fashion as the bmp280/bmp388 on PX4)
Usage: bme688 <command> [arguments...]
Commands:
start
[-I] Internal I2C bus(es)
[-X] External I2C bus(es)
[-s] Internal SPI bus(es)
[-S] External SPI bus
[-b <val>] board-specific bus (default=all) (external SPI: n-th bus (default=1))
[-c <val>] chip-select pin (for internal SPI) or index (for external SPI)
[-m <val>] SPI mode
[-f <val>] bus frequency in kHz
[-q] quiet startup (no message if no device found)
[-a <val>] I2C address
default: 119
stop
status print status info
Common Issues
nsh> bme688 status
INFO [SPI_I2C] Not running
- In order to enable auto start at device bootup, add command under
PX4 src/boards/nxp/fmuk66-v3/init/rc.board_sensors
nsh> bme688 start -X
INFO [SPI_I2C] device bus ==> 1
INFO [SPI_I2C] assigned board bus type: 1
INFO [SPI_I2C] device_id devid: 9
INFO [SPI_I2C] wq_config: wq:I2C1
INFO [SPI_I2C] runtime instance count: 0
INFO [SPI_I2C] instantiate failed (no device on bus 1 (devid 0)?)
ERROR [SPI_I2C] bme688: no instance started (no device on bus?)
- If encounter "no instance started" error, you could build and run (by default) the i2cdetect in terminal to ensure address showed up properly. Also ensure the detection of right bus. For example: on NXP FMUK66: internal_i2c=2, external_i2c=1, external_spi=3
nsh> i2cdetect -b 1
Scanning I2C bus: 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- 77 -- -- -- -- -- -- -- --
You
could slow down the bus speed as there might be wiring or noise issues on your hardware set up.
nsh> bme688 start -X -f 20000
...
nsh> bme688 start -S -f 500000
For
a properly instantiated instance, you should see the follow followed by start
INFO [SPI_I2C] adding a running instance -set started to true
bme688 #0 on I2C bus 1 (external) address 0x77
- Confirm there is no communication error by issuing status
nsh> bme688 status
INFO [SPI_I2C] Running on I2C Bus 1, Address 0x77
bme688: sample: 108 events, 115304us elapsed, 1067.63us avg, min 884us max 1411us 93.582us rms
bme688: measure: 109 events, 16119470us elapsed, 147885.05us avg, min 147140us max 148614us 266.258us rms
bme688: comms errors: 0 events
uORB
Topic Publishing
The driver also introduce a uORB topic called <sensor_gas> which you could inspect with the listener command line tool or a custom pixhawk application.
nsh> uorb status
TOPIC NAME INST #SUB #Q SIZE PATH
sensor_gas 0 0 4 48 /obj/sensor_gas0
nsh> listener sensor_gas
TOPIC: sensor_gas
sensor_gas
timestamp: 185075367 (0.630325 seconds ago)
timestamp_sample: 185074305 (1062 us before timestamp)
device_id: 12744457 (Type: 0xC2, I2C:1 (0x77))
sample_count: 1
temperature: 23.7633
pressure: 101306.1875
humidity: 59.5401
gas_resistance: 35814.2148
error_count: 0
device_status: 176
Comments