数码之家

 找回密码
 立即注册

QQ登录

只需一步,快速开始

微信登录

微信扫一扫,快速登录

搜索
查看: 441|回复: 17

初学单片机写了个低成本铁塔电池电量表

[复制链接]
发表于 2026-1-8 11:35:31 | 显示全部楼层 |阅读模式


虽然我已经把铁塔的电池连接APP已经写的基本等于完美了,但是白天的时候看电池电量悬浮窗还是费劲的,于是拿着大厦龙雀的DX-WF25(ESP32-C2)做了个电量表解析到数码管



材料:
  • 大厦龙雀DX-WF25 成本约 1元 ( 可能涨价了 我用的开发板会贵点 没有开发板需要把io9拉低进入烧录模式)
  • [可选] 大厦龙雀2.4G主从透传模块*2  (没花钱 用券薅的) (用来无线连接BMS的,主要是我有多个电池多台电动车需要热切换)
  • [可选] (用来当自动关闭开关的(天邦达可能会失效))电平信号MOS管模块(单MOS也行 上面的mos型号 D4184(信号放大MOS管) )
  • 中盛Modbus主从显示数码管(TTL版本 约9.9  (我用的)  如果不在乎成本可以选中盛Ascii数码管(不需要手动调浮点 可以花式切换电量和百分比模式)
  • [直连串口则可选] 485转TTL模块 2-10元
  • [可选] 5V稳压电源 , 3.3V稳压电源 (BMS上面也能取电 我取的3.3和12V)
  • [可选] 环氧树脂 2块 我用来灌胶的 防水用的 不管目前还没灌胶因为我感觉还有需要优化的地方


设备电压备注:
  • DX-WF25  3.3V
  • 2.4G主从透传模块  3.3V
  • 中盛Modbus主从显示数码管 3.3V - 36V (官方说的是5V 我实测3.3V 50ma以上也能正常工作(但是可能很容易坏))
  • 485转TTL模块 3.3-5V (我的是2.8-33V的)
  • 电平信号MOS管 过电上限好像是36V 至于电流忽略不计




备注:我对硬件不太了解,可能会有说错的地方多担待


步骤
1. 下载Arduino(自开发的话) 可以参考这篇帖子 :arduino烧录成功DX-WF25  可以避开很多坑 我用的 3.13 的


2.直接烧录的话 下载我编译好的bin (自行找 ---flash_download_tool_3.9.3--烧录工具 大厦龙雀DX-WF25资料包里面有 )
下载链接:https://x-1300389275.cos.ap-shan ... %9B%BA%E4%BB%B6.zip
参考这个配置方式:


单片机源代码:

  1. // 定义第二个串口的引脚
  2. #define UART1_RX_PIN 4  // 从设备1读取数据
  3. #define UART1_TX_PIN 5  // 向设备1发送数据
  4. #define COM_SWITCH GPIO_NUM_2  // 向设备1发送数据

  5. int LOOP_COUNT = 0;
  6. bool IS_RS_ONCE = false;

  7. void loopSendRSCode() {
  8.   uint8_t data[8] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x1D, 0x85, 0xC3 };
  9.   Serial.write(data, 8);
  10. }

  11. // CRC校验函数 (Modbus CRC-16)
  12. bool isCrc16Valid(uint8_t *data, int len) {
  13.   if (len < 3) return false;

  14.   uint16_t crc = 0xFFFF;
  15.   for (int i = 0; i < len - 2; i++) {
  16.     crc ^= (uint16_t)data[i];
  17.     for (int j = 0; j < 8; j++) {
  18.       crc = (crc & 0x0001) ? ((crc >> 1) ^ 0xA001) : (crc >> 1);
  19.     }
  20.   }

  21.   uint16_t receivedCrc = (data[len - 1] << 8) | data[len - 2];
  22.   return (crc == receivedCrc);
  23. }

  24. // 获取SOC值 (索引8-9)
  25. uint16_t getSOC(uint8_t *buffer) {
  26.   return (buffer[7] << 8) | buffer[8];
  27. }

  28. // 获取电流值 (索引13-14)
  29. // 01 03 3C 19 FF 00 14 00 5A 06 5E 00 5A 00 00 00 1D 00 1C 00 1D 0C FD 0C FD 0C FA 0C FA 0C FA 0C FB 0C FE 0C FE 0C FE 0C FD 0C FB 0C FD 0C FD 0C FB 0C FB 0D 01 0D 03 0D 04 0D 03 0D 03 00 1D 8A 50
  30. float getCurrent(uint8_t *buffer) {
  31.   int16_t raw = (buffer[13] << 8) | buffer[14];

  32.   if (raw == 0) {
  33.     return 0.0f;
  34.   } else if (raw > 0 && raw < 25536) {
  35.     return raw / 100.0f;
  36.   } else {
  37.     return (raw - 65536) / 100.0f;
  38.   }
  39. }

  40. bool isCurrent(uint8_t *buffer) {
  41.   return (buffer[13] + buffer[14]) != 0;
  42. }

  43. // Modbus数据包生成函数
  44. void sendSocModbusInDigitalTube(int num) {
  45.   uint8_t data[6] = { 0x01, 0x06, 0x00, 0x00 };
  46.   data[4] = (num >> 8) & 0xFF;
  47.   data[5] = num & 0xFF;

  48.   uint16_t crc = 0xFFFF;
  49.   for (int i = 0; i < 6; i++) {
  50.     crc ^= (uint16_t)data[i];
  51.     for (int j = 0; j < 8; j++) {
  52.       crc = (crc & 0x0001) ? ((crc >> 1) ^ 0xA001) : (crc >> 1);
  53.     }
  54.   }

  55.   uint8_t packet[8];
  56.   memcpy(packet, data, 6);
  57.   packet[6] = crc & 0xFF;
  58.   packet[7] = (crc >> 8) & 0xFF;

  59.   Serial1.write(packet, 8);
  60. }

  61. // 从串口缓冲区解析Modbus响应
  62. void parseModbusResponse() {

  63.   int len = Serial.available();

  64.   //清空不符合条件的包 避免内存溢出
  65.   if (len < 63) {
  66.     while (Serial.available() > 0) {
  67.       Serial.read();
  68.     }
  69.     return;
  70.   }


  71.   static uint8_t buffer[64];  // 静态缓冲区,保存完整帧
  72.   int bufferIndex = 0;
  73.   while (Serial.available() > 0 && bufferIndex < sizeof(buffer)) {
  74.     buffer[bufferIndex++] = Serial.read();
  75.   }

  76.   bool foundHeader = false;
  77.   // 01 03 3C   (2.0版本)
  78.   // 01 03 3A   (1.0版本)
  79.   // 两个版本 位置是一样的
  80.   if (buffer[0] == 0x01 && buffer[1] == 0x03 && (buffer[2] == 0x3A || buffer[2] == 0x3C)) {
  81.     foundHeader = true;
  82.   }
  83.   
  84.   // 清空其他数据
  85.   while (Serial.available() > 0) {
  86.     Serial.read();
  87.   }

  88.   if (!foundHeader || !isCrc16Valid(buffer, 63)) {
  89.     return;
  90.   }

  91.   uint16_t soc = getSOC(buffer);

  92.   if (isCurrent(buffer)) {
  93.     LOOP_COUNT = 0;
  94.     gpio_set_level(COM_SWITCH, 1);
  95.     delay(500);
  96.   }

  97.   sendSocModbusInDigitalTube(soc);
  98. }

  99. void gpio_init(void) {

  100.   // 配置GPIO为输出模式
  101.   gpio_config_t io_conf = {
  102.     .pin_bit_mask = (1ULL << COM_SWITCH),
  103.     .mode = GPIO_MODE_OUTPUT,               // 输出模式
  104.     .pull_up_en = GPIO_PULLUP_DISABLE,      // 不上拉
  105.     .pull_down_en = GPIO_PULLDOWN_DISABLE,  // 不下拉
  106.     .intr_type = GPIO_INTR_DISABLE          // 禁用中断
  107.   };

  108.   // 应用配置
  109.   gpio_config(&io_conf);

  110.   // 初始状态设为低电平
  111.   gpio_set_level(COM_SWITCH, 0);
  112. }

  113. void setup() {

  114.   //波特率
  115.   Serial.begin(9600);
  116.   gpio_init();
  117.   delay(1000);

  118.   // 數碼管串口
  119.   Serial1.begin(9600, SERIAL_8N1, UART1_RX_PIN, UART1_TX_PIN);

  120.   gpio_set_level(COM_SWITCH, 1);

  121. }

  122. void loop() {

  123.   // 发送查询
  124.   loopSendRSCode();
  125.   parseModbusResponse();

  126.   delay(5000);
  127.   LOOP_COUNT++;

  128.   // 约1分钟没有电流时候自动关闭数码管
  129.   if ((LOOP_COUNT % 15) == 0) {
  130.     gpio_set_level(COM_SWITCH, 0);
  131.   }

  132. }
复制代码



我对WF25的引脚定义:
  • IO2 数码管供电MOS 电平信号 (高电平开启)
  • IO4 数码管TX(A标识)
  • IO5 数码管 RX(B标识)
  • TX 默认 接485
  • RX 默认 接485
  • 3.3 默认 接电源
  • GND 接电源(如果是那种两块一片的可能需要接电池GND)










本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x

打赏

参与人数 2家元 +60 收起 理由
200birds + 30
xw801 + 30 专业!!

查看全部打赏

 楼主| 发表于 2026-1-8 11:36:41 | 显示全部楼层
游客请登录后查看回复内容

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册 微信登录

x
回复 支持 反对

使用道具 举报

发表于 2026-1-8 11:56:05 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2026-1-8 13:03:38 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2026-1-8 13:24:19 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

 楼主| 发表于 2026-1-8 16:34:58 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2026-1-8 22:47:18 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2026-1-9 01:28:27 来自手机浏览器 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

 楼主| 发表于 2026-1-9 06:32:13 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2026-1-9 08:49:57 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2026-1-9 09:40:34 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2026-1-9 09:50:03 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

 楼主| 发表于 2026-1-12 12:14:03 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2026-1-12 20:28:32 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2026-1-14 10:25:56 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 2026-1-14 13:01:53 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 6 天前 来自手机浏览器 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

发表于 3 天前 | 显示全部楼层
游客请登录后查看回复内容
回复 支持 反对

使用道具 举报

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

本版积分规则

APP|手机版|小黑屋|关于我们|联系我们|法律条款|技术知识分享平台

闽公网安备35020502000485号

闽ICP备2021002735号-2

GMT+8, 2026-2-7 10:22 , Processed in 0.109200 second(s), 11 queries , Gzip On, Redis On.

Powered by Discuz!

© 2006-2026 MyDigit.Net

快速回复 返回顶部 返回列表