【DigiKey创意大赛】便携生命探测仪04+驱动BME680并设计UI
[复制链接]
上一帖介绍了如何使用DMA提高热成像显示帧率,本帖先介绍驱动BME680并将检测结果显示在屏幕上,然后设计一个操作交互界面,实现用户操作功能。
一、驱动BME680并显示在屏幕上
BME680具有SPI和I2C两种接口,由于我用的这个ESP32-S3开发板引出的SPI口接屏了并且SPI占IO口比较多,我优先选择使用I2C接口连接BME680小板,为了省事我把它和MLX90640挂在同一个I2C总线上了。如下图。
然后在PlatformIO中安装Adafruit BME680 Library,如下图。
直接使用默认例程测试,Adafruit造好的轮子基本不用费心,刷进板子就OK。测试代码如下。
代码1:
/***************************************************************************
This is a library for the BME680 gas, humidity, temperature & pressure sensor
Designed specifically to work with the Adafruit BME680 Breakout
----> http://www.adafruit.com/products/3660
These sensors use I2C or SPI to communicate, 2 or 4 pins are required
to interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing products
from Adafruit!
Written by Limor Fried & Kevin Townsend for Adafruit Industries.
BSD license, all text above must be included in any redistribution
***************************************************************************/
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include "Adafruit_BME680.h"
#define BME_SCK 13
#define BME_MISO 12
#define BME_MOSI 11
#define BME_CS 10
#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME680 bme; // I2C
//Adafruit_BME680 bme(BME_CS); // hardware SPI
//Adafruit_BME680 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK);
void setup() {
Serial.begin(9600);
while (!Serial);
Serial.println(F("BME680 test"));
if (!bme.begin()) {
Serial.println("Could not find a valid BME680 sensor, check wiring!");
while (1);
}
// Set up oversampling and filter initialization
bme.setTemperatureOversampling(BME680_OS_8X);
bme.setHumidityOversampling(BME680_OS_2X);
bme.setPressureOversampling(BME680_OS_4X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme.setGasHeater(320, 150); // 320*C for 150 ms
}
void loop() {
if (! bme.performReading()) {
Serial.println("Failed to perform reading :(");
return;
}
Serial.print("Temperature = ");
Serial.print(bme.temperature);
Serial.println(" *C");
Serial.print("Pressure = ");
Serial.print(bme.pressure / 100.0);
Serial.println(" hPa");
Serial.print("Humidity = ");
Serial.print(bme.humidity);
Serial.println(" %");
Serial.print("Gas = ");
Serial.print(bme.gas_resistance / 1000.0);
Serial.println(" KOhms");
Serial.print("Approx. Altitude = ");
Serial.print(bme.readAltitude(SEALEVELPRESSURE_HPA));
Serial.println(" m");
Serial.println();
delay(2000);
}
然后考虑怎么移植到现有的程序中。我在热成像图像右边留白的区域就是为BME680数据准备的。在右下角放置一个按钮用来切换屏幕功能。留白的显示效果如下图。
程序设计上,专门为BME680建立一个独立任务,设置成每2秒读一次数据。显示任务判断数据变化就更新屏幕,实际显示效果如下图。
虽然整个屏幕显示实现了,但是我发现一个问题,就是每次读取BME680时候,热成像刷新都会卡顿一下,体验不好,原因就是共用总线造成的。好在ESP32-S3有多个I2C,并且还有一个巨牛掰的GPIO交换矩阵,可以让外设使用任意IO口。于是我从引脚分配表上找了2个闲置的IO口A4和A5配置成了I2C总线,如下图。
在程序初始化中也适当修改,选择I2C1,直接调用即可,代码如下。
代码2:
#define SEALEVELPRESSURE_HPA (1019.25)
Adafruit_BME680 bme(&Wire1); // I2C
void Bme680Init(void)
{
Serial.println(F("BME680 async test"));
// I2C init
Wire1.begin(BME_SDA, BME_SCL);
// bme.Adafruit_BME680(Wire1);
if (!bme.begin())
{
Serial.println(F("Could not find a valid BME680 sensor, check wiring!"));
while (1)
;
}
// Set up oversampling and filter initialization
bme.setTemperatureOversampling(BME680_OS_8X);
bme.setHumidityOversampling(BME680_OS_2X);
bme.setPressureOversampling(BME680_OS_4X);
bme.setIIRFilterSize(BME680_FILTER_SIZE_3);
bme.setGasHeater(320, 150); // 320*C for 150 ms
}
以上都弄好后,屏幕显示就流畅起来了,看着虽然简陋些,但是没啥问题。下一步就是设计人机交互。
二、设计操作交互界面
我的预期功能是这样的,本作品具备两个屏幕界面,第一屏界面显示热成像图像和环境信息,用于寻找体温高于环境温度的被施救人员,上面已经做完了。第二屏界面显示心电图,目的是在发现生命体后可以立即给被发现者做一个快速心电检测,帮助救援人员根据心电数据制定下一步的施救方案。
设计的心电图采集界面如下图所示。
整个屏幕上部分分配480x240点阵的区域用于显示心电波形,左下分两行文字,一行显示导联脱落信息,一行显示心率。右下角按钮用来切换两个界面。
心电数据预计从串口接收,这里我新建了一个单独的任务,暂时用于产生随机数模拟心电波形、心率、导联脱落信息。通过模拟数据与显示任务对接,可以仿真心电采集界面的各个功能。仿真数据产生代码如下。
代码3:
#include <Arduino.h>
#include <ecg.h>
#include <lcd.h>
uint8_t ECG_data = 0;
uint16_t ECG_hr = 0;
bool ECG_lead = true;
bool ECG_flag = true;
float ecg_data = 0;
float ecg_hr = 0;
float ecg_lead = 0;
void task_ecg(void *ptr)
{
int i = 0;
while (true)
{
vTaskDelay(4);
ECG_flag = true;
ECG_data = random(0, 239);
ECG_hr = random(60, 120);
if (ECG_hr > 110)
ECG_lead = true;
else
ECG_lead = false;
}
vTaskDelete(NULL);
}
TFT_eSPI库本身支持触摸功能,可以直接调用API接口函数实现触摸输入事件解析,通过按钮锁定逻辑,每点击一次右下角按钮,切换一次界面,按钮文字也随之更改,具体代码如下。
代码4:
void LcdScreenSwitch(void)
{
if (screen_lock == SCREEN_CAM)
{
lock_mxl = true;//禁能热成像采集
screen_lock = SCREEN_ECG;
tft.fillScreen(TFT_BLACK);
LcdEcgBar();
// Serial.println("ECG");
}
else
{
screen_lock = SCREEN_CAM;
tft.fillScreen(TFT_BLACK);
LcdColorBar();
Bme680Display();
lock_mxl = false;//使能热成像采集
// Serial.println("CAM");
}
}
bool Key_Short = false;
void LcdGetTouch(void)
{
uint16_t x = 0, y = 0; // To store the touch coordinates
// Pressed will be set true is there is a valid touch on the screen
bool pressed = tft.getTouch(&x, &y);
// Serial.println(pressed);
// Draw a white spot at the detected coordinates
if (pressed)
{
if ((x > 350) && (y > 260))
{
if (Key_Short == false)
{
Key_Short = true;
LcdScreenSwitch(); // 切换屏幕
}
}
else
{
Key_Short = false;
}
}
else
{
Key_Short = false;
}
}
至此用户交互界面基本设计完成,具体演示效果见开头视频。接下来要搞定心电数据的采集和处理。
|