zoologist 发表于 2022-7-25 14:47:32

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/

zoologist 发表于 2022-7-25 14:49:53

本文提到的电路图和PCB

小企鹅 发表于 2022-9-1 17:36:25

厉害厉害
页: [1]
查看完整版本: Fire Beetle 实现蓝牙功率计