【BW21-CBV-Kit 开发套件测评】OLED蓝牙温湿度计与Home Assistant连接
本文介绍了安信可 BW21-CBV-Kit 开发套件实现 OLED 蓝牙温湿度计 并与 Home Assistant 智能家居平台连接的项目设计。
OLED蓝牙温湿度计
设计思路
基于前面关于 蓝牙温湿度计 和 OLED 显示 的测试,进一步扩展,将两种功能融合,实现 OLED 实时显示的蓝牙温湿度计,使之更贴近产品级的应用。
硬件连接
OLED_SCL -> 12
OLED_SDA -> 13
DHT11_DATA -> 8
示意图

此外,需要手机安装 Bluefruit Connect
应用和 Home Assistant
应用,便于后续测试的实现。
代码
结合例程 Examples - AmebaWire - OLED_SSD1306
和 Examples - AmebaBLE - DHT_over_BLEUart
,实现OLED蓝牙温湿度计。
#include "BLEDevice.h"
#include "DHT.h"
/* OLED */
#include <Wire.h>
#include <Adafruit_OLED_libraries/Adafruit_GFX.h>
#include <Adafruit_OLED_libraries/Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define UART_SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
#define STRING_BUF_SIZE 100
// The digital pin we're connected to.
#define DHTPIN 8
// Uncomment whatever type you're using!
#define DHTTYPE DHT11 // DHT 11
// #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
// #define DHTTYPE DHT21 // DHT 21 (AM2301)
DHT dht(DHTPIN, DHTTYPE);
BLEService UartService(UART_SERVICE_UUID);
BLECharacteristic Rx(CHARACTERISTIC_UUID_RX);
BLECharacteristic Tx(CHARACTERISTIC_UUID_TX);
BLEAdvertData advdata;
BLEAdvertData scndata;
bool notify = false;
void writeCB(BLECharacteristic* chr, uint8_t connID)
{
printf("Characteristic %s write by connection %d :\n", chr->getUUID().str(), connID);
if (chr->getDataLen() > 0) {
Serial.print("Received string: ");
Serial.print(chr->readString());
Serial.println();
}
}
void notifCB(BLECharacteristic* chr, uint8_t connID, uint16_t cccd)
{
if (cccd & GATT_CLIENT_CHAR_CONFIG_NOTIFY) {
printf("Notifications enabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
notify = true;
} else {
printf("Notifications disabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
notify = false;
}
}
void setup()
{
Serial.begin(115200);
advdata.addFlags();
advdata.addCompleteName("AMEBA_BLE_DEV");
scndata.addCompleteServices(BLEUUID(UART_SERVICE_UUID));
Rx.setWriteProperty(true);
Rx.setWritePermissions(GATT_PERM_WRITE);
Rx.setWriteCallback(writeCB);
Rx.setBufferLen(STRING_BUF_SIZE);
Tx.setReadProperty(true);
Tx.setReadPermissions(GATT_PERM_READ);
Tx.setNotifyProperty(true);
Tx.setCCCDCallback(notifCB);
Tx.setBufferLen(STRING_BUF_SIZE);
UartService.addCharacteristic(Rx);
UartService.addCharacteristic(Tx);
BLE.init();
BLE.configAdvert()->setAdvData(advdata);
BLE.configAdvert()->setScanRspData(scndata);
BLE.configServer(1);
BLE.addService(UartService);
BLE.beginPeripheral();
dht.begin();
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;)
; // Don't proceed, loop forever
}
delay(1000);
display.clearDisplay();
display.setTextColor(WHITE);
}
void loop()
{
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
// display error information
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.print("Failed to read from DHT sensor!");
display.display();
}
String msg = ("Humidity: " + String((int)h) + "%\t" + "Temperature: " + String((int)t) + "°C\n");
Tx.writeString(msg);
if (BLE.connected(0) && notify) {
Tx.notify(0);
}
// clear display
display.clearDisplay();
// display temperature
display.setTextSize(1);
display.setCursor(0,0);
display.print("Temperature: ");
display.setTextSize(2);
display.setCursor(0,17);
display.print(String(t,2));
display.print(" ");
display.setTextSize(1);
display.cp437(true);
display.write(167);
display.setTextSize(2);
display.print("C");
// display humidity
display.setTextSize(1);
display.setCursor(0, 35);
display.print("Humidity: ");
display.setTextSize(2);
display.setCursor(0, 45);
display.print(String(h,2));
display.print(" %");
display.display();
delay(2000);
}
编译并上传固件,复位运行程序;
手机打开 Bluefruit Connect
应用,连接 AMEBA_BLE_DEV
蓝牙设备,选择 UART,即可接收蓝牙串口发送的数据。
效果
蓝牙串口发送温湿度数据,同时 OLED 显示温湿度

动态演示

Home Assistant 连接
在例程 Examples - AmebaWire - OLED_SSD1306
和 Examples - AmebaBLE - DHT_over_BLEUart
实现 OLED 蓝牙温湿度计的基础上,配合 Bluefruit Connect
应用的蓝牙串口 MQTT 传输功能,进一步实现 Home Assistant (HA)智能家居平台温湿度监测。
HA 环境搭建详见之前的帖子:蓝牙温湿度计 DIY 和 Home Assistant 连接 .
设计思路
引入 <ArduinoJson.h>
库,实现温度和湿度数据的标准 JSON 格式转换。
Json
- JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
- JSON 是轻量级的文本数据交换格式
举例:
{"age":30}
最终蓝牙串口输出效果如下
{"temperature": "28.80","humidity": "44.00"}
代码
相较之前的代码,引入 <ArduinoJson.h>
库,并在 loop 中创建 JSON 装载即可
#include "BLEDevice.h"
#include "DHT.h"
/* OLED */
#include <Wire.h>
#include <Adafruit_OLED_libraries/Adafruit_GFX.h>
#include <Adafruit_OLED_libraries/Adafruit_SSD1306.h>
#include <ArduinoJson.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define UART_SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
#define STRING_BUF_SIZE 100
// The digital pin we're connected to.
#define DHTPIN 8
// Uncomment whatever type you're using!
#define DHTTYPE DHT11 // DHT 11
// #define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
// #define DHTTYPE DHT21 // DHT 21 (AM2301)
DHT dht(DHTPIN, DHTTYPE);
BLEService UartService(UART_SERVICE_UUID);
BLECharacteristic Rx(CHARACTERISTIC_UUID_RX);
BLECharacteristic Tx(CHARACTERISTIC_UUID_TX);
BLEAdvertData advdata;
BLEAdvertData scndata;
bool notify = false;
void writeCB(BLECharacteristic* chr, uint8_t connID)
{
printf("Characteristic %s write by connection %d :\n", chr->getUUID().str(), connID);
if (chr->getDataLen() > 0) {
Serial.print("Received string: ");
Serial.print(chr->readString());
Serial.println();
}
}
void notifCB(BLECharacteristic* chr, uint8_t connID, uint16_t cccd)
{
if (cccd & GATT_CLIENT_CHAR_CONFIG_NOTIFY) {
printf("Notifications enabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
notify = true;
} else {
printf("Notifications disabled on Characteristic %s for connection %d \n", chr->getUUID().str(), connID);
notify = false;
}
}
void setup()
{
Serial.begin(115200);
advdata.addFlags();
advdata.addCompleteName("AMEBA_BLE_DEV");
scndata.addCompleteServices(BLEUUID(UART_SERVICE_UUID));
Rx.setWriteProperty(true);
Rx.setWritePermissions(GATT_PERM_WRITE);
Rx.setWriteCallback(writeCB);
Rx.setBufferLen(STRING_BUF_SIZE);
Tx.setReadProperty(true);
Tx.setReadPermissions(GATT_PERM_READ);
Tx.setNotifyProperty(true);
Tx.setCCCDCallback(notifCB);
Tx.setBufferLen(STRING_BUF_SIZE);
UartService.addCharacteristic(Rx);
UartService.addCharacteristic(Tx);
BLE.init();
BLE.configAdvert()->setAdvData(advdata);
BLE.configAdvert()->setScanRspData(scndata);
BLE.configServer(1);
BLE.addService(UartService);
BLE.beginPeripheral();
dht.begin();
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;)
; // Don't proceed, loop forever
}
delay(1000);
display.clearDisplay();
display.setTextColor(WHITE);
}
void loop()
{
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
// Create JSON payload
DynamicJsonDocument doc(256);
doc["temperature"] = String(t,2);
doc["humidity"] = String(h,2);
char json_string[256];
serializeJson(doc, json_string);
Serial.print("Publishing: ");
Serial.println(json_string);
// define message: {"temperature":t,"humidity":h}
String msg = (json_string);
Tx.writeString(msg);
if (BLE.connected(0) && notify) {
Tx.notify(0);
}
// clear display
display.clearDisplay();
// display temperature
display.setTextSize(1);
display.setCursor(0,0);
display.print("Temperature: ");
display.setTextSize(2);
display.setCursor(0,17);
display.print(String(t,2));
display.print(" ");
display.setTextSize(1);
display.cp437(true);
display.write(167);
display.setTextSize(2);
display.print("C");
// display humidity
display.setTextSize(1);
display.setCursor(0, 35);
display.print("Humidity: ");
display.setTextSize(2);
display.setCursor(0, 45);
display.print(String(h,2));
display.print(" %");
display.display();
delay(2000);
}
此时连接 MQTTX 输出文本为标准 JSON 格式

YAML 代码
进入 HomeAssistant 配置文件夹,编辑 configuration.yaml
文件,添加如下代码
mqtt:
sensor:
- name: "DHT/Temperature"
state_topic: "dht11"
suggested_display_precision: 1
unit_of_measurement: "C"
value_template: "{{ value_json.temperature }}"
- name: "DHT/Humidity"
state_topic: "dht11"
suggested_display_precision: 1
unit_of_measurement: "%"
value_template: "{{ value_json.humidity }}"
打开 HA 主页面,选择左侧标签栏中的 开发者工具
- 点击 所有 YAML 配置
加载修改后的配置文件,刷新网页,即可在 概览
界面看到新增的传感器。

同时可在 HA 手机应用上获取传感器数据、历史变化曲线,以及 OLED 实时显示。

分析
该方案相较于 开发板 WiFi 直连实现 MQTT 数据传输
的方案的优点如下
- 通过手机应用可以随时切换使用场景,不会受制于 WiFi 网络覆盖范围的限制;
- MQTT 网络上传仅依赖于手机应用转发所需的网络;
- 便于切换蓝牙从机设备的连接和查看(其他手机也可扫描蓝牙并连接)
总结
本文介绍了安信可 BW21-CBV-Kit 开发套件实现 OLED 蓝牙温湿度计 并与 Home Assistant 智能家居平台连接的项目设计,为小安派系列相关产品的物联网应用提供了参考。