Bluetooth LE with Nordic nRF51 & nRF52 series
The easy way!
Part 2
The easy way!
Part 2
Advertising environmental sensor data
(Temperature, Humidity, Pressure)
with Bosch BME280 & BME680
Advertising environmental sensor data
(Temperature, Humidity, Pressure)
with Bosch BME280 & BME680
In previous post, we advertise over Bluetooth only an incremental counter. Lets do something more interesting this time by advertising environmental sensor data, temperature, pressure and humidity. Most digital sensors can be communicated using either I2C or SPI interface. EHAL provides a simple way to us those interface. Similar to the BLE interface, the I2C & SPI also make use to configuration data structure and an Init function.
The hardware
The environmental sensors used in this example are the BME280 and BME680 from Bosch.
Bluetooth 5 Sensor Board avail at https://www.crowdsupply.com/i-syst/blyst-nano |
The BME680 breakout board available on Tindie this board support both I2C & SPI interface. |
Configuring I2C for the BME680 breakout board
// Configure I2C interface
static const I2CCFG s_I2cCfg = {
0, // I2C device number
{
// I2C pins in which the device is connected to.
// I2C pins in which the device is connected to.
{I2C0_SDA_PORT, I2C0_SDA_PIN, I2C0_SDA_PINOP, IOPINDIR_BI, IOPINRES_NONE, IOPINTYPE_NORMAL},
{I2C0_SCL_PORT, I2C0_SCL_PIN, I2C0_SCL_PINOP, IOPINDIR_OUTPUT, IOPINRES_NONE, IOPINTYPE_NORMAL},
},
100000, // Rate in Hz
I2CMODE_MASTER,
0, // Slave address
5, // Retry
7, // Interrupt prio
NULL // Event callback
};
// I2C interface instance
I2C g_I2c;
g_I2c.Init(s_I2cCfg);
uint8_t regaddr = 0xD0; // BME680 device ID register address
uint8_t d;
g_I2c.Read(0x76, // BME680 I2C Device address
®addr, 1, // register address to read
&d, 1 // buffer to return data
);
If the device is connected correctly, the variable d should contains the value 0x61 (97 decimal).
Configuring SPI
// Motsai Neblina V2 module uses SPI interface
static const IOPINCFG gsSpiBoschPin[] = {
{SPI_SCK_PORT, SPI_SCK_PIN, SPI_SCK_PINOP,
IOPINDIR_OUTPUT, IOPINRES_NONE, IOPINTYPE_NORMAL},
{SPI_MISO_PORT, SPI_MISO_PIN, SPI_MISO_PINOP,
IOPINDIR_INPUT, IOPINRES_NONE, IOPINTYPE_NORMAL},
{SPI_MOSI_PORT, SPI_MOSI_PIN, SPI_MOSI_PINOP,
IOPINDIR_OUTPUT, IOPINRES_NONE, IOPINTYPE_NORMAL},
{SPI_BME280_CS_PORT, SPI_BME280_CS_PIN, SPI_BME280_CS_PINOP,
IOPINDIR_OUTPUT, IOPINRES_PULLUP, IOPINTYPE_NORMAL},
};
static const SPICFG s_SpiCfg = {
SPI_DEVNO,
SPIMODE_MASTER,
gsSpiBoschPin,
sizeof( gsSpiBoschPin ) / sizeof( IOPINCFG ),
8000000, // Speed in Hz
8, // Data Size
5, // Max retries
SPIDATABIT_MSB,
SPIDATAPHASE_SECOND_CLK, // Data phase
SPICLKPOL_LOW, // clock polarity
SPICSEL_AUTO,
6, //APP_IRQ_PRIORITY_LOW, // Interrupt priority
nullptr
};
SPI g_Spi;
// Initialize I2C
g_Spi.Init(s_SpiCfg);
uint8_t regaddr = 0xD0; // BME280 device ID register address
uint8_t d;
g_Spi.Read(0x76, // BME280 I2C Device address
®addr, 1, // register address to read
&d, 1 // buffer to return data
);
printf("BME280 device id = 0x%02x\r\n", d);
If the device is connected correctly, the variable d should contains the value 0x60 (96 decimal).
Luckily we don't need to manually reading data from the sensor that way. The EHAL already have driver written for both the BME280 & the BM680. Here is how to initialize the driver and read sensor data.
Configuring sensor drivers
// Configure environmental sensor
// Configure BME680 for I2C
// Configure BME680 for I2C
static TPHSENSOR_CFG s_Bme680SensorCfg = {
BME680_I2C_DEV_ADDR0, // I2C device address
SENSOR_OPMODE_SINGLE,
100, // Sampling frequency in Hz
1,
1,
1,
1
};
// BME680 Environmental sensor instance
TphgBme680 g_Bme680Sensor;
// Configure BME280 for SPI
static TPHSENSOR_CFG s_Bme280SensorCfg = {
0, // SPI chip select index
SENSOR_OPMODE_SINGLE,
100, // Sampling frequency in Hz
1,
1,
1,
1
};
// BME280 Environmental sensor instance
TphBme280 g_Bme280Sensor;
Initialization and reading sensor data
// Intitialize BM680 using I2C interface
g_Bme680Sensor.Init(s_Bme680SensorCfg, &g_I2c, NULL);
// or
// intitialize BM680 using SPI interface
// intitialize BM680 using SPI interface
g_Bme680Sensor.Init(s_Bme680SensorCfg, &g_Spi, NULL);
// Intitialize BM280 using SPI interface
// Update sensor data
TPHSENSOR_DATA tphdata;
g_Bme680Sensor.StartSampling();
g_Bme680Sensor.Read(tphdata);
g_Bme680Sensor.Read(tphdata);
g_Bme280Sensor.StartSampling();
g_Bme280Sensor.Read(tphdata);
Integrating into BLE advertising data
In the previous article, we advertising only a 32bits count. Let replace the configuration now to advertise the sensor data instead
BLEADV_MANDATA g_AdvData
const BLEAPP_CFG s_BleAppCfg = {
{ // Clock config nrf_clock_lf_cfg_t
#ifdef IMM_NRF51822
NRF_CLOCK_LF_SRC_RC, // Source RC
1, 1, 0
#else
0, 0, NRF_CLOCK_LF_ACCURACY_20_PPM
#endif
},
0, // Number of central link
0, // Number of peripheral link
BLEAPP_MODE_NOCONNECT, // Connectionless beacon type
DEVICE_NAME, // Device name
ISYST_BLUETOOTH_ID, // PnP Bluetooth/USB vendor id
1, // PnP Product ID
0, // Pnp prod version
false, // Enable device information service (DIS)
NULL, // Pointer device info descriptor
(uint8_t*)& g_AdvData, // Manufacture specific data to advertise
sizeof(g_AdvData), // Length of manufacture specific data
BLEAPP_SECTYPE_NONE, // Secure connection type
BLEAPP_SECEXCHG_NONE, // Security key exchange
NULL, // Service uuids to advertise
0, // Total number of uuids
APP_ADV_INTERVAL, // Advertising interval in msec
APP_ADV_TIMEOUT_IN_SECONDS, // Advertising timeout in sec
100, // Slow advertising interval, if > 0, fallback to
// slow interval on adv timeout and advertise until connected
0, // Min. connection interval
0, // Max. connection interval
-1, // Led port nuber
-1, // Led pin number
0, // Tx power
NULL // RTOS Softdevice handler
};
void ReadPTHData()
{
TPHSENSOR_DATA data;
g_TphSensor.Read(data);
g_TphSensor.StartSampling();
g_AdvData.Type = BLEADV_MANDATA_TYPE_TPH;
// NOTE : M0 does not access unaligned data
// use local 4 bytes align stack variable then mem copy
// skip timestamp as advertising pack is limited in size
memcpy(g_AdvData.Data, ((uint8_t*)&data) + 4, sizeof(BLEADV_MANDATA_TPHSENSOR));
// Update advertisement data
BleAppAdvManDataSet(g_AdvDataBuff, sizeof(g_AdvDataBuff));
}
void BlePeriphEvtUserHandler(ble_evt_t * p_ble_evt)
{
if (p_ble_evt->header.evt_id == BLE_GAP_EVT_TIMEOUT)
{
// Update environmental sensor data every time advertisement timeout
// for re-advertisement
ReadPTHData();
}
}
Complete Eclipse based project is on github: nRF52 Project, nRF51 Project. Same code works for both nRF51 & nRF52 series including nRF52840.