简介
在上一篇文章中我们已经使用NRF52840实现蓝牙和手机App的通讯,并且成功的接受到了对应的Notify和发送命令控制灯的闪烁,正好前两天我参加了咱们论坛举办的这个低功耗BLE温度计的活动. 正好可以使用这个开发板作为主控结合DHT11 做一个NRF版本的低功耗温度计。说干就干,首先来连接DHT11和开发板如下所示。

将DHT11 的VCC 接到开发板的3.3V gnd 接 GND 然后Dout接任意的一个GPIO即可。
驱动DHT11
DHT11的驱动是使用的Adafruit的 DHT KXN库进行驱动的。可以在Arduino的库管理器中进行下载。然后将其整合在上一章的代码中。SparkfunNRF52840-mini蓝牙控制. 需要注意的一点是,上文中的代码我们在main loop中的循环时间设置的是20ms,但是DHT11的通讯的时长是需要远远的高于20ms的。但是对于蓝牙的协议栈的话又需要较低的延时,所以我们需要对代码进行一点小小的修改使其适配两者。
1- 首先根据库函数要求来定于对应的Pin和类
#include "DHT.h" #define DHTPIN 2 #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE);
2- 读取DHT11
// Reading temperature or humidity takes about 250 milliseconds!
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float h = dht.readHumidity();
// Read temperature as Celsius (the default)
float t = dht.readTemperature();
// Read temperature as Fahrenheit (isFahrenheit = true)
float f = dht.readTemperature(true);
// Check if any reads failed and exit early (to try again).
if (isnan(h) || isnan(t) || isnan(f)) {
Serial.println(F("Failed to read from DHT sensor!"));
return;
}
// Compute heat index in Fahrenheit (the default)
float hif = dht.computeHeatIndex(f, h);
// Compute heat index in Celsius (isFahreheit = false)
float hic = dht.computeHeatIndex(t, h, false);
Serial.print(F("Humidity: "));
Serial.print(h);
Serial.print(F("% Temperature: "));
Serial.print(t);
Serial.print(F("°C "));
Serial.print(f);
Serial.print(F("°F Heat index: "));
Serial.print(hic);
Serial.print(F("°C "));
Serial.print(hif);
Serial.println(F("°F"));3- 很显然我们不只是想读取到DHT11的数据而且还需要将其发送出去,因此我们需要额外定义Temperature/Humidity的特性。
BLECharacteristic tempCharacteristic( "19B10013-E8F2-537E-4F6C-D104768A1214");
然后在初始化蓝牙之前初始化这个特征服务
// Temperature / Humidity tempCharacteristic.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ); tempCharacteristic.setPermission(SECMODE_OPEN, SECMODE_OPEN); tempCharacteristic.setFixedLen(8); // 2 floats tempCharacteristic.begin();
4 - 在主循环中将DHT11的数据发送出去
void loop() {
// ---------------- BUTTON ----------------
uint8_t buttonState = (digitalRead(BUTTON_PIN) == LOW) ? 1 : 0;
if (buttonState != lastButtonState) {
lastButtonState = buttonState;
buttonCharacteristic.write(&buttonState, 1);
buttonCharacteristic.notify(&buttonState, 1);
Serial.print("Button: ");
Serial.println(buttonState);
}
// ---------------- DHT (2秒一次) ----------------
if (millis() - lastDHTRead > 2000) {
lastDHTRead = millis();
float h = dht.readHumidity();
float t = dht.readTemperature();
if (!isnan(h) && !isnan(t)) {
float data[2] = { t, h };
tempCharacteristic.write((uint8_t*)data, sizeof(data));
tempCharacteristic.notify((uint8_t*)data, sizeof(data));
Serial.print("Temp: ");
Serial.print(t);
Serial.print(" C Hum: ");
Serial.println(h);
} else {
Serial.println("DHT read failed");
}
}
delay(20);
}上述代码中我们使用了一个计时器来判断上次进入loop的时间是否是在2000ms。这样的话也不会和蓝牙的20ms的delay冲突从而导致服务的不可用。
完整程序代码如下所示:
#include <bluefruit.h>
#include "DHT.h"
#define DHTPIN 2
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE);
// Pins
#define BUTTON_PIN 13
#define LED_PIN LED_BUILTIN
// BLE Service
BLEService envService("19B10010-E8F2-537E-4F6C-D104768A1214");
// LED Control (phone -> board)
BLECharacteristic ledCharacteristic(
"19B10011-E8F2-537E-4F6C-D104768A1214");
// Button Notify (board -> phone)
BLECharacteristic buttonCharacteristic(
"19B10012-E8F2-537E-4F6C-D104768A1214");
// Temperature/Humidity Notify
BLECharacteristic tempCharacteristic(
"19B10013-E8F2-537E-4F6C-D104768A1214");
uint8_t lastButtonState = 0;
unsigned long lastDHTRead = 0;
// LED write callback
void led_write_callback(uint16_t conn_hdl,
BLECharacteristic* chr,
uint8_t* data,
uint16_t len) {
if (len < 1) return;
digitalWrite(LED_PIN, data[0] ? HIGH : LOW);
Serial.print("LED: ");
Serial.println(data[0] ? "ON" : "OFF");
}
// Advertising
void startAdvertising() {
Bluefruit.Advertising.stop();
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
Bluefruit.Advertising.addService(envService);
Bluefruit.ScanResponse.addName();
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244);
Bluefruit.Advertising.setFastTimeout(30);
Bluefruit.Advertising.start(0);
}
void setup() {
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
Serial.begin(115200);
while (!Serial) delay(10);
Serial.println("BLE starting...");
Bluefruit.begin();
Bluefruit.setTxPower(4);
Bluefruit.setName("EnvSensor");
envService.begin();
// LED
ledCharacteristic.setProperties(CHR_PROPS_READ | CHR_PROPS_WRITE);
ledCharacteristic.setPermission(SECMODE_OPEN, SECMODE_OPEN);
ledCharacteristic.setFixedLen(1);
ledCharacteristic.setWriteCallback(led_write_callback);
ledCharacteristic.begin();
uint8_t v = 0;
ledCharacteristic.write(&v, 1);
// Button
buttonCharacteristic.setProperties(CHR_PROPS_READ | CHR_PROPS_NOTIFY);
buttonCharacteristic.setPermission(SECMODE_OPEN, SECMODE_OPEN);
buttonCharacteristic.setFixedLen(1);
buttonCharacteristic.begin();
buttonCharacteristic.write(&v, 1);
// Temperature / Humidity
tempCharacteristic.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
tempCharacteristic.setPermission(SECMODE_OPEN, SECMODE_OPEN);
tempCharacteristic.setFixedLen(8); // 2 floats
tempCharacteristic.begin();
dht.begin();
startAdvertising();
Serial.println("BLE ready");
}
void loop() {
// ---------------- BUTTON ----------------
uint8_t buttonState = (digitalRead(BUTTON_PIN) == LOW) ? 1 : 0;
if (buttonState != lastButtonState) {
lastButtonState = buttonState;
buttonCharacteristic.write(&buttonState, 1);
buttonCharacteristic.notify(&buttonState, 1);
Serial.print("Button: ");
Serial.println(buttonState);
}
// ---------------- DHT (2秒一次) ----------------
if (millis() - lastDHTRead > 2000) {
lastDHTRead = millis();
float h = dht.readHumidity();
float t = dht.readTemperature();
if (!isnan(h) && !isnan(t)) {
float data[2] = { t, h };
tempCharacteristic.write((uint8_t*)data, sizeof(data));
tempCharacteristic.notify((uint8_t*)data, sizeof(data));
Serial.print("Temp: ");
Serial.print(t);
Serial.print(" C Hum: ");
Serial.println(h);
} else {
Serial.println("DHT read failed");
}
}
delay(20);
}实验现象
现在将程序编译和烧录到单片机中重新打开我们的蓝牙工具然后进行蓝牙设备的连接,我们便会看到原本的两个的特征服务现在变成了三个,额外多了一个湿度和温度的服务。

当点击了上述温度服务的notify的按钮之后,那么开发板便会两秒将程序的数据进行蓝牙发送。我们可以在上图中看到目前接受到的数据是0000-F441-0000-8042, 对应的发送结构是 float data[2] = { t, h }; 所以 00 00 F4 41 是温度数据,而00 00 80 42 是湿度数据。
然后将上述接受到的数据使用little-endian float 排序的方式就变成了0x41F40000 和 0x42800000, 所以温度和湿度就变成了30.5度和64.0% 也和下图串口中的输出数据一致。

总结
到此为止本次的开发就已经完毕了,可以看到通过Arduino IDE的库管理器提供的驱动库我们可以非常方便的快速来实现对应的功能。虽然是BLE,但是由于没有对应的测量设备(高精度的电流表)实际上很难对比实际的功耗有多少。
我要赚赏金
