[ESP8266/ESP32]Fire Beetle 实现蓝牙功率计 精华

2879浏览
查看: 2879|回复: 2

[ESP8266/ESP32] Fire Beetle 实现蓝牙功率计

[复制链接]
这次为了测试微软Surface平板电脑的功耗,我设计了一个功率计,主要硬件是 DFRobot ESP32 FireBeetleDFR0478), DC-DC降压电源模块5.5~28V3.3V3ADFR0570),FireBeetle萤火虫OLED12864显示屏(DFR0507)以及INA226电压电流监控模块(CJMCU-226)。其中 FireBeetle 是主控,提供蓝牙连接功能;INA226是德州仪器出品的INA226高侧/低侧测量、 双向电流/功率监视器芯片,测的总线电压范围:0V 36V,能够报告电流、电压和功率,精度0.1%增益误差(最大值),10μV 偏移(最大值),10引脚 DGS 超薄小外形尺寸(VSSOP) 封装。美中不足的是这款芯片价格太高,因此我选择淘宝直接购买模块。
从硬件角度来说,基本设计如下:电源经过INA226后提供给设备,主控通过 I2C INA226进行通讯;此外为了方便起见我们直接从电源上取电,经过降压模块之后共给INA226、主控和屏幕来使用。
Fire Beetle 实现蓝牙功率计图5
INA226 的基本原理是:通过在被测试电源上串接一个电阻(通常是电阻很小的精密电阻),后再通过读取两端电压再经过 ADC 即可推算出当前电流,配合测量出来的电压即可得知当前输出端的功耗。
Fire Beetle 实现蓝牙功率计图4
最终电路图设计如下,特别注意降压模块旁边有一个名为3.3VPOWER的条线,在使用USBMicro接口连接 FireBeetle 下载和调试时,板子会提供3.3V电压,这种情况下需要断开这个条线;而当独立工作时,需要将此位置短路。
Fire Beetle 实现蓝牙功率计图3
PCB 设计如下:
Fire Beetle 实现蓝牙功率计图2
上方是电源的输入和输出接口。
焊接之后工作的照片,这里使用MicrosoftSurface 平板电脑适配器(磁吸接口)进行测试,这个适配器内部有三根线:VCCGND 和一个Signal。从测试来看,Signal的作用是标志当前磁吸接口是否连接到电脑上,如果连接上Signal会拉低,适配器VCC会按照15V 输出。这次实验中,我将 Signal 接地让适配器始终按照15V输出。
Fire Beetle 实现蓝牙功率计图1
代码如下:
  1. #include "DFRobot_OLED12864.h"
  2. #include "INA226.h"
  3. #include "BluetoothSerial.h"
  4. #define DISPLAYTIMEOUT 30000
  5. INA226 INA(0x40);
  6. // OLED 地址
  7. const uint8_t OLED_I2C_addr = 0x3c;
  8. const uint8_t OLED_pin_SPI_cs = D2;
  9. // OLED 对象
  10. DFRobot_OLED12864 OLED(OLED_I2C_addr, OLED_pin_SPI_cs);
  11. // 声明蓝牙串口
  12. BluetoothSerial SerialBT;
  13. // 蓝牙连接标记
  14. boolean Connected = false;
  15. unsigned long DisplayTimeOut;
  16. boolean displayOff = false;
  17. // 蓝牙连接事件
  18. void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param) {
  19.   if (event == ESP_SPP_SRV_OPEN_EVT) {
  20.     // 蓝牙已连接
  21.     Serial.println("Client Connected");
  22.     // 标记蓝牙已连接
  23.     Connected = true;
  24.   }
  25.   if (event == ESP_SPP_CLOSE_EVT ) {
  26.     // 蓝牙已断开
  27.     Serial.println("Client disconnected");
  28.     // 标记蓝牙已断开
  29.     Connected = false;
  30.   }
  31. }
  32. void IRAM_ATTR detectButton() {
  33.   Serial.println("Button");
  34.   // 如果当前屏幕是关闭的状态,触发之后要打开屏幕
  35.   if (displayOff) {
  36.     displayOff = false;
  37.     DisplayTimeOut = millis();
  38.   }
  39. }
  40. void setup()
  41. {
  42.   Serial.begin(115200);
  43.   // 注册回调函数
  44.   SerialBT.register_callback(callback);
  45.   if (!SerialBT.begin("OxiSensor")) {
  46.     Serial.println("An error occurred initializing Bluetooth");
  47.   } else {
  48.     Serial.println("Bluetooth initialized");
  49.   }
  50.   Wire.begin();
  51.   if (!INA.begin() )
  52.   {
  53.     Serial.println("could not connect. Fix and Reboot");
  54.   }
  55.   INA.setMaxCurrentShunt(1, 0.002);
  56.   // 初始化屏幕
  57.   OLED.init();
  58.   OLED.flipScreenVertically();
  59.   OLED.clear();
  60.   DisplayTimeOut = millis();
  61.   pinMode(D8, INPUT_PULLUP);
  62.   attachInterrupt(digitalPinToInterrupt(D8), detectButton, RISING);
  63. }
  64. // 要显示内容的缓冲
  65. char Buffer[40];
  66. // 存放浮点转字符串的缓冲
  67. char FloatToStr[20];
  68. float Voltage; //当前电压,单位是 V
  69. float Ampere;  //当前电流,单位是 mA
  70. float Watt;   //当前功率,单位是 mW
  71. unsigned long Elsp;
  72. void fillchar(char *p, int len) {
  73.   char *blank="                 ";
  74.   strncat(p, blank, len - strlen(p));
  75. }
  76. void loop()
  77. {
  78.   Elsp = millis();
  79.   Voltage = INA.getBusVoltage();
  80.   Ampere = INA.getCurrent_mA();
  81.   Watt = INA.getPower_mW();
  82.   Serial.print(Elsp);
  83.   Serial.print(",");
  84.   Serial.print(Voltage, 3);
  85.   Serial.print(",");
  86.   Serial.print(Ampere, 3);
  87.   Serial.print(",");
  88.   Serial.print(Watt, 3);
  89.   Serial.println();
  90.   // 如果当前OLED 处于关闭状态,就不对屏幕发送任何显示数据
  91.   if (displayOff == false) {
  92.     // 打开屏幕
  93.     OLED.displayOn();
  94.     dtostrf(Voltage, 1, 3, FloatToStr);
  95.     sprintf(Buffer, "V:%sV", FloatToStr);
  96.     fillchar(&Buffer[0],16);
  97.     OLED.disStr(0, 0, Buffer);
  98.     memset(Buffer, ' ', sizeof(Buffer));
  99.     dtostrf(Ampere, 1, 3, FloatToStr);
  100.     sprintf(Buffer, "A:%smA", FloatToStr);
  101.     fillchar(&Buffer[0],16);
  102.     OLED.disStr(0, 16, Buffer);
  103.     memset(Buffer, ' ', sizeof(Buffer));
  104.     dtostrf(Watt, 1, 3, FloatToStr);
  105.     sprintf(Buffer, "P:%smW", FloatToStr);
  106.     fillchar(&Buffer[0],16);
  107.     OLED.disStr(0, 32, Buffer);
  108.     OLED.display();
  109.     // 如果屏幕亮超过DISPLAYTIMEOUT指定时间,就关闭屏幕
  110.     if (millis() - DisplayTimeOut > DISPLAYTIMEOUT) {
  111.       Serial.println("Turn off display");
  112.       displayOff = true;
  113.       OLED.displayOff();
  114.     }
  115.   }
  116.   if (Connected) {
  117.     SerialBT.print(Elsp);
  118.     memset(Buffer, 0, sizeof(Buffer));
  119.     dtostrf(Voltage, 1, 3, FloatToStr);
  120.     sprintf(Buffer, ",%s,", FloatToStr);
  121.     SerialBT.print(Buffer);
  122.     dtostrf(Ampere, 1, 3, FloatToStr);
  123.     sprintf(Buffer, "%s,", FloatToStr);
  124.     SerialBT.println(Buffer);
  125.   }
  126.   delay(1000);
  127. }
复制代码

为了读取功率数据,使用了 INA226 库,我手上的 INA226 地址是0x40;代码中创建了一个 BLE UART,用户可以搜索发现设备,配对之后就可以获得数据。除了蓝牙之外,用户还可以通过USB UART来获得当前的电压电流信息。为了保护 OLED, 程序还设计了经过DISPLAYTIMEOUT设定的时间后自动关闭屏幕的功能,按下OLED按钮后会再次唤醒屏幕显示。



zoologist  高级技匠
 楼主|

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

本文提到的电路图和PCB下载附件FireBeetle Power.zip
回复

使用道具 举报

小企鹅  初级技匠

发表于 2022-9-1 17:36:25

厉害厉害
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail