Fire Beetle 实现蓝牙功率计
这次为了测试微软Surface平板电脑的功耗,我设计了一个功率计,主要硬件是 DFRobot 的 ESP32 FireBeetle(DFR0478), DC-DC降压电源模块5.5~28V转3.3V3A(DFR0570),FireBeetle萤火虫OLED12864显示屏(DFR0507)以及INA226电压电流监控模块(CJMCU-226)。其中 FireBeetle 是主控,提供蓝牙连接功能;INA226是德州仪器出品的INA226高侧/低侧测量、 双向电流/功率监视器芯片,测的总线电压范围:0V至 36V,能够报告电流、电压和功率,精度0.1%增益误差(最大值),10μV 偏移(最大值),10引脚 DGS 超薄小外形尺寸(VSSOP) 封装。美中不足的是这款芯片价格太高,因此我选择淘宝直接购买模块。从硬件角度来说,基本设计如下:电源经过INA226后提供给设备,主控通过 I2C 和 INA226进行通讯;此外为了方便起见我们直接从电源上取电,经过降压模块之后共给INA226、主控和屏幕来使用。INA226 的基本原理是:通过在被测试电源上串接一个电阻(通常是电阻很小的精密电阻),后再通过读取两端电压再经过 ADC 即可推算出当前电流,配合测量出来的电压即可得知当前输出端的功耗。
最终电路图设计如下,特别注意降压模块旁边有一个名为3.3VPOWER的条线,在使用USBMicro接口连接 FireBeetle 下载和调试时,板子会提供3.3V电压,这种情况下需要断开这个条线;而当独立工作时,需要将此位置短路。
PCB 设计如下:
上方是电源的输入和输出接口。焊接之后工作的照片,这里使用MicrosoftSurface 平板电脑适配器(磁吸接口)进行测试,这个适配器内部有三根线:VCCGND 和一个Signal。从测试来看,Signal的作用是标志当前磁吸接口是否连接到电脑上,如果连接上Signal会拉低,适配器VCC会按照15V 输出。这次实验中,我将 Signal 接地让适配器始终按照15V输出。
代码如下:#include "DFRobot_OLED12864.h"
#include "INA226.h"
#include "BluetoothSerial.h"
#define DISPLAYTIMEOUT 30000
INA226 INA(0x40);
// OLED 地址
const uint8_t OLED_I2C_addr = 0x3c;
const uint8_t OLED_pin_SPI_cs = D2;
// OLED 对象
DFRobot_OLED12864 OLED(OLED_I2C_addr, OLED_pin_SPI_cs);
// 声明蓝牙串口
BluetoothSerial SerialBT;
// 蓝牙连接标记
boolean Connected = false;
unsigned long DisplayTimeOut;
boolean displayOff = false;
// 蓝牙连接事件
void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
if (event == ESP_SPP_SRV_OPEN_EVT) {
// 蓝牙已连接
Serial.println("Client Connected");
// 标记蓝牙已连接
Connected = true;
}
if (event == ESP_SPP_CLOSE_EVT ) {
// 蓝牙已断开
Serial.println("Client disconnected");
// 标记蓝牙已断开
Connected = false;
}
}
void IRAM_ATTR detectButton() {
Serial.println("Button");
// 如果当前屏幕是关闭的状态,触发之后要打开屏幕
if (displayOff) {
displayOff = false;
DisplayTimeOut = millis();
}
}
void setup()
{
Serial.begin(115200);
// 注册回调函数
SerialBT.register_callback(callback);
if (!SerialBT.begin("OxiSensor")) {
Serial.println("An error occurred initializing Bluetooth");
} else {
Serial.println("Bluetooth initialized");
}
Wire.begin();
if (!INA.begin() )
{
Serial.println("could not connect. Fix and Reboot");
}
INA.setMaxCurrentShunt(1, 0.002);
// 初始化屏幕
OLED.init();
OLED.flipScreenVertically();
OLED.clear();
DisplayTimeOut = millis();
pinMode(D8, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(D8), detectButton, RISING);
}
// 要显示内容的缓冲
char Buffer;
// 存放浮点转字符串的缓冲
char FloatToStr;
float Voltage; //当前电压,单位是 V
float Ampere;//当前电流,单位是 mA
float Watt; //当前功率,单位是 mW
unsigned long Elsp;
void fillchar(char *p, int len) {
char *blank=" ";
strncat(p, blank, len - strlen(p));
}
void loop()
{
Elsp = millis();
Voltage = INA.getBusVoltage();
Ampere = INA.getCurrent_mA();
Watt = INA.getPower_mW();
Serial.print(Elsp);
Serial.print(",");
Serial.print(Voltage, 3);
Serial.print(",");
Serial.print(Ampere, 3);
Serial.print(",");
Serial.print(Watt, 3);
Serial.println();
// 如果当前OLED 处于关闭状态,就不对屏幕发送任何显示数据
if (displayOff == false) {
// 打开屏幕
OLED.displayOn();
dtostrf(Voltage, 1, 3, FloatToStr);
sprintf(Buffer, "V:%sV", FloatToStr);
fillchar(&Buffer,16);
OLED.disStr(0, 0, Buffer);
memset(Buffer, ' ', sizeof(Buffer));
dtostrf(Ampere, 1, 3, FloatToStr);
sprintf(Buffer, "A:%smA", FloatToStr);
fillchar(&Buffer,16);
OLED.disStr(0, 16, Buffer);
memset(Buffer, ' ', sizeof(Buffer));
dtostrf(Watt, 1, 3, FloatToStr);
sprintf(Buffer, "P:%smW", FloatToStr);
fillchar(&Buffer,16);
OLED.disStr(0, 32, Buffer);
OLED.display();
// 如果屏幕亮超过DISPLAYTIMEOUT指定时间,就关闭屏幕
if (millis() - DisplayTimeOut > DISPLAYTIMEOUT) {
Serial.println("Turn off display");
displayOff = true;
OLED.displayOff();
}
}
if (Connected) {
SerialBT.print(Elsp);
memset(Buffer, 0, sizeof(Buffer));
dtostrf(Voltage, 1, 3, FloatToStr);
sprintf(Buffer, ",%s,", FloatToStr);
SerialBT.print(Buffer);
dtostrf(Ampere, 1, 3, FloatToStr);
sprintf(Buffer, "%s,", FloatToStr);
SerialBT.println(Buffer);
}
delay(1000);
}
为了读取功率数据,使用了 INA226 库,我手上的 INA226 地址是0x40;代码中创建了一个 BLE 的UART,用户可以搜索发现设备,配对之后就可以获得数据。除了蓝牙之外,用户还可以通过USB的 UART来获得当前的电压电流信息。为了保护 OLED, 程序还设计了经过DISPLAYTIMEOUT设定的时间后自动关闭屏幕的功能,按下OLED按钮后会再次唤醒屏幕显示。
https://www.bilibili.com/video/BV1ve4y197Gc/
本文提到的电路图和PCB 厉害厉害
页:
[1]