23653浏览
查看: 23653|回复: 1

[用户分享] TinkNode 制作功率记录器

[复制链接]
今天,我们使用“焦耳”作为能量或功的单位。1焦耳能量相等于1牛顿力的作用点在力的方向上移动1米距离所做的功, 1焦=1瓦·秒。这是为了纪念詹姆斯·普雷斯科特·焦耳(James Prescott Joule)。他英国著名的物理学家,为了表彰他在热学、热力学和电方面的贡献,皇家学会授予他最高荣誉的科普利奖章(CopleyMedal)。后人为了纪念他,把能量或功的单位命名为“焦耳”,简称“焦”;并用焦耳姓氏的第一个字母“J”来标记热量以及“功”的物理量。
1847年,焦耳做了迄今认为是设计思想最巧妙的实验:他在量热器里装了水,中间安上带有叶片的转轴,然后让下降重物带动叶片旋转,由于叶片和水的磨擦,水和量热器都变热了。根据重物下落的高度,可以算出转化的机械功;根据量热器内水的升高的温度,就可以计算水的内能的升高值。把两数进行比较就可以求出热功当量的准确值来。焦耳还用鲸鱼油代替水来作实验,测得了热功当量的平均值为423.9千克米/千卡。接着又用水银来代替水,不断改进实验方法,直到1878年。这时距他开始进行这一工作将近四十年了,他已前后用各种方法进行了四百多次的实验。
焦耳准确地测定了热功当量,进一步证明了能的转化和守恒定律是客观真理。这一定律的确定,宣告了制造“永动机”的幻想彻底破灭。【参考1】

随着时代的发展,越来越多家用电器走入日常生活,常见的有电冰箱洗衣机,还有扫地机来承担清洁地板的工作。而这一切都是通过电力驱动的。家用电器都有标注消耗功率的铭牌,但是工作时并不会一直处于满负荷的状态。因此如果想知道一个设备工作时需要的能耗情况就需要功率计来进行测试。这次制作的就是使用TinkerNode 配合HP-9800 来实现记录功率消耗情况。

HP-9800是宏品电子科技有限公司推出的一款用电检测仪,能够在面板上实时显示当前设备的用电情况,同时提供给了一个USB接口将测量结果传输到外部。
TinkNode 制作功率记录器图1


这次使用TinkerNode配合USBHost MINI,通过 HP-9800 的USB接口来实现读取和记录耗电情况。同时TinkerNode开发板提供了一个特别的功能:板载一个4MB 的存储空间,这意味可以通过这个板子方便的实现数据的记录功能。记录完毕之后直接插入电脑即可被识别为一个U盘可以方便读取。HP-9800 USB输出方式是串口,从Windows设备管理器中根据设备 PID 和 VID可以看到是通过CH340 USB串口芯片完成转接的。这是我们这次试验的基本原理。

接下来介绍实现的硬件部分:
首先,为了让USB Host Mini 能在 TinkerNode 上使用,需要实现一个转接板的功能,这个转接板无需任何元件,只是将对应的引脚连接起来:
  
TinkerNode
  
USB Host Mini
功能
D1
INT
中断请求
D3/IO13
SS
SPI 的 SS
5V
5V
给USB设备供电
3.3V
3.3V
给USB HOST MINI供电(特别注意:这个板子的工作电压是3.3V)
IO10
MISO

IO23
MOSI

IO18
SCK
SPI CLOCK
GND
GND

3.3V
RST
USB Host 的 RESET, 必须拉高


TinkNode 制作功率记录器图4
TinkNode 制作功率记录器图2

上述工程可以在【参考2】下载到。
制作PCB之后焊接排插,即可使用。特别需要注意的是USBHost Mini可能需要Rework【参考3】

TinkNode 制作功率记录器图5TinkNode 制作功率记录器图7


软件方面,比较特别的地方有下面4个:
1.      1.为了实现对 CH340 的串口通讯,使用了【参考4】给出的库,配合 USB Host 很容易读取到串口数据;
2.      2.我们使用板载的 SET 按钮触发记录的事件,这个按钮对应 D3 Pin
TinkNode 制作功率记录器图3

3.      3.通过板载的彩灯提示当前正在进行记录, 对应是一个WS2812的彩灯。上电之后我们使用红色表示正在记录中,绿色表示没有记录。
TinkNode 制作功率记录器图6

4.HP-9800 的采用问答形式,例如,USBHOST 端发送 0x01,0x03,0x00,0x00,0x00,0x14,0x45,0xC5 命令,设备回应数据如下 01 03 28 41 41 D3 42 44 96 EF 3E 71 2D 61 43 0000 48 42 00 00 80 3F 51 37 9A 43 E1 AB C6 3B 64 F2 7A 37 75 C3 5D 40 00 01 0001 A4 F5 解析如下:

  
偏移
  
含义
00
0x01
地址码
01
0x03
读数指令
02
0x28
数据长度
03
0x41,0x41,0xD3,0x42
为有功功率: 0x42D34141= 105.627瓦特
07
0x44,0x96,0xEF,0x3E
电流: 0x3EEFD9644=0.468安培
11
0x71,0x2D,0x61,0x43
电压0x43612D71=225.178伏特
15
0x00,0x00,0x48,0x42
频率: 0x42480000=50.00赫兹
19
0x00,0x00,0x80,0x3F
功率因数:0x3F800000=1
23
0x51,0x37,0x9A,0x43
年用电量:0x439A3751=308.432度
27
0xE1,0xAB,0xC6,0x3B   
有功电能: 0x3BC6ABE1=0.006
31
0x64,0xF2,0x7A,0x37
无功电能: 0x377AF264=0.000015
35
0x75,0xC3,0x5D,0x40
带负载工作时间:3.46分钟
39
0x00,0x01
每日工作时间:0x0001=1小时
41
0x00,0x01
仪表地址:0x0001
43
0xA4,0xF5
CRC16

上面的数据是 4 Byte 的Float ,可以在 http://lostphp.com/hexconvert/ 进行转换。

有了读取的硬件和通讯格式,我们就可以取得测量的结果并且保存在文件中。

完整代码:
  1. #include "FS.h"
  2. #include "FFat.h"
  3. // USB Host CH341 库
  4. #include "cdc_ch34x.h"
  5. #include <DFRobot_NeoPixel.h>
  6. class CH34XAsyncOper : public CDCAsyncOper
  7. {
  8.   public:
  9.     uint8_t OnInit(CH34X *pch34x);
  10. };
  11. // 初始化 CH341 USB 转 UART芯片
  12. uint8_t CH34XAsyncOper::OnInit(CH34X *pch34x)
  13. {
  14.   uint8_t rcode;
  15.   LINE_CODING lc;
  16.   // 通讯波特率 9600
  17.   lc.dwDTERate = 9600;
  18.   lc.bCharFormat = 0;
  19.   lc.bParityType = 0;
  20.   lc.bDataBits = 8;
  21.   lc.bFlowControl = 0;
  22.   rcode = pch34x->SetLineCoding(&lc);
  23.   if (rcode)
  24.     ErrorMessage<uint8_t>(PSTR("SetLineCoding"), rcode);
  25.   return rcode;
  26. }
  27. USB Usb;
  28. CH34XAsyncOper AsyncOper;
  29. CH34X Ch34x(&Usb, &AsyncOper);
  30. // 定义发给设备的读取命令
  31. uint8_t readCMD[]={0x01,0x03,0x00,0x00,0x00,0x14,0x45,0xC5};
  32. // 定时取得数据的计时器
  33. unsigned long DataElsp=0;
  34. // 按键计时器,只有超过特定时间才可以再次按下这样能够避免抖动
  35. unsigned long ButtonElsp=0;
  36. // 记录当前灯色,RED 表示正在记录,绿色表示未记录
  37. byte CurrentColor;
  38. // 文件名变量
  39. String filePath = "/log.csv";
  40. // 检查给定的文件名是否存在
  41. bool checkFile(fs::FS &fs, const char * path) {
  42.   Serial.printf("Check file: %s\r\n", path);
  43.   File file = fs.open(path);
  44.   if (!file || file.isDirectory()) {
  45.     return false;
  46.   }
  47.   return true;
  48. }
  49. // 写入 message 到 path 给定的文件中, mode 是写入模式,有新建和追加两种模式
  50. void writeFile(fs::FS &fs, const char * path, const char * message,char* mode) {
  51.   // 新建的情况下显示文件名
  52.   if (mode==FILE_WRITE) {
  53.     Serial.printf("Writing file: %s\r\n", path); }
  54.   
  55.   //尝试打开文件,这样判断文件是否存在
  56.   File file = fs.open(path, mode);
  57.   if (!file) {
  58.     Serial.println("- failed to open file for writing");
  59.     return;
  60.   }
  61.   
  62.   // 串口输出写入的数据
  63.   file.print(message);
  64. }
  65. // 将 Data 给出的缓冲区中的第 num 个字符串转化为浮点数
  66. float BufferToFloat(uint8_t num,uint8_t *Data){
  67.   float   value;
  68.   uint32_t *pValue;
  69.   
  70.    pValue=(uint32_t *)&value;
  71.   *pValue=((uint32_t) Data[3+num*4]) |
  72.           ((uint32_t) Data[4+num*4]<<8)|
  73.           ((uint32_t) Data[5+num*4]<<16)|
  74.           ((uint32_t) Data[6+num*4]<<24);
  75.          
  76.   return value;            
  77. }
  78. void setup() {
  79.   Serial.begin(115200);
  80.   // 打开 U盘
  81.   if (!FFat.begin()) {
  82.     Serial.println("FFat Mount Failed");
  83.         FFat.format();
  84.             Serial.println("Format");
  85.     return;
  86.   }
  87.   RGB_LED.begin();
  88.   // 低亮度
  89.   RGB_LED.setBrightness(DARK);
  90.   // 初始为绿色
  91.   CurrentColor=GREEN;
  92.   RGB_LED.setColor(GREEN);
  93.   RGB_LED.show();
  94.   RGB_LED.show();
  95.   
  96.   // 设置 SET 按钮对应的 GPIO 为输入
  97.   pinMode(D3,INPUT);
  98.   if (Usb.Init() == -1)
  99.     Serial.println("OSC did not start.");
  100.   delay( 200 );
  101.   Serial.println("Start....");
  102. }
  103. void loop() {
  104.   // 距离上一次按键时间大于2秒
  105.   if (millis()-ButtonElsp>2000UL) {
  106.       if (digitalRead(D3)==LOW) {
  107.           // 如果当前是绿灯,那么切换为红色,并且创建一个文件
  108.        if (CurrentColor==GREEN) {
  109.           Serial.println("Switch to Red");
  110.           CurrentColor=RED;
  111.           RGB_LED.setColor(CurrentColor);
  112.           RGB_LED.show();
  113.           RGB_LED.show();
  114.          
  115.           // 尝试创建文件名
  116.           for(int i = 1; i < 20; i++){
  117.                   char fileNameNum[4];
  118.                   sprintf(fileNameNum,"%04d",i);
  119.                   String nowFileName  = (char *)fileNameNum;
  120.                   filePath = "/log"+nowFileName+".csv";
  121.                   // 如果这个文件名不存在,那么就创建之
  122.                   if (!checkFile(FFat, filePath.c_str() )) {
  123.                           Serial.println("Create a log.csv file");
  124.                           // 写入第一行
  125.                           writeFile(FFat, filePath.c_str(), "Time(MS),Current(A),Voltage(V),Power(W)\r\n",FILE_WRITE);         
  126.                           Serial.print(filePath.c_str());
  127.                           Serial.println(" cteated!");
  128.                           break;
  129.                   }
  130.           }
  131.           Serial.println("Log begin...");
  132.        } else
  133.             //如果当前是红色,那么切换为绿灯
  134.        {
  135.         Serial.println("Switch to Green");
  136.         CurrentColor=GREEN;
  137.         RGB_LED.setColor(CurrentColor);
  138.         RGB_LED.show();
  139.         RGB_LED.show();        
  140.         Serial.println("It is paused now.");
  141.         }
  142.      }
  143.      // 按键重新计时
  144.      ButtonElsp=millis();
  145.   }
  146.   char s[20];
  147.   uint8_t Buffer[64];
  148.   
  149.   Usb.Task();
  150.   if (( Usb.getUsbTaskState() == USB_STATE_RUNNING ) &&
  151.      (CurrentColor==RED) && (millis()-DataElsp>300UL)) {
  152.       if (Ch34x.isReady()) {
  153.           Serial.println("CH34X:Ready!");
  154.           // 将命令拷贝到 Buffer 中
  155.           for (uint8_t i=0;i<sizeof(readCMD);i++) {
  156.               Buffer<i>=readCMD<i>;
  157.             }
  158.           // 发送读取命令  
  159.           Ch34x.SndData(sizeof(readCMD), Buffer);
  160.          
  161.           Serial.println("RDCMD");
  162.           delay(2000);
  163.          
  164.           // 接收
  165.           uint16_t size = sizeof(Buffer);
  166.           Ch34x.RcvData(&size, Buffer);
  167.           if (size > 0) {
  168.             // 将接收到的数据写入文件
  169.                 // 写入时间信息,单位是 100ms
  170.             itoa(millis() /100,s,10);      
  171.             writeFile(FFat, filePath.c_str(), s,FILE_APPEND);
  172.             writeFile(FFat, filePath.c_str(), "," ,FILE_APPEND);
  173.             Serial.print("Time(s):");Serial.println(s);
  174.                 // 写入电流信息        
  175.             dtostrf(BufferToFloat(1,Buffer),1,3,s);
  176.             writeFile(FFat, filePath.c_str(), s ,FILE_APPEND);
  177.             writeFile(FFat, filePath.c_str(), "," ,FILE_APPEND);
  178.             Serial.print("Current(A):");Serial.println(s);
  179.                 // 写入电压信息  
  180.             dtostrf(BufferToFloat(2,Buffer),1,3,s);
  181.             writeFile(FFat, filePath.c_str(), s ,FILE_APPEND);
  182.             writeFile(FFat, filePath.c_str(), "," ,FILE_APPEND);
  183.             Serial.print("Voltage(V):");Serial.println(s);
  184.                 // 写入功率
  185.             dtostrf(BufferToFloat(0,Buffer),1,3,s);
  186.             writeFile(FFat, filePath.c_str(), s ,FILE_APPEND);
  187.             writeFile(FFat, filePath.c_str(), "\r\n" ,FILE_APPEND);
  188.             Serial.print("Power(W):");Serial.println(s);   
  189.             
  190.             // 重置取得数据的定时器
  191.             DataElsp=millis();   
  192.             
  193.             } //if (size > 0)
  194.             else {
  195.               Serial.println("received 0");
  196.               }
  197.           } //if (Ch34x.isReady())
  198.           else {
  199.               Serial.println("CH34X:not ready.");
  200.           }
  201.       }; //stop
  202. }</i></i>
复制代码

使用时,上电后LED为绿灯,按下 SET按钮后开始记录数据,LED 为红色。记录结束后插入电脑显示为一个U盘,其中可以看到记录数据的文件。工作的视频,使用 USB 供电,此外还可以直接通过外部供电输入,使用2节 18650串联。
HP-9800 是家用级别的功率计,如果有需要特别精确的测量设备功率还需要选择更高级的设备。
参考:

2.  立创EDA 设计的,按道理可以直接开源分享,但是他们平台限制必须有10个以上元件才可以直接分享,所以这里只能提供直接下载。


3. http://www.lab-z.com/cuhm/祖国版 USB HOST MINI 使用
4. https://www.suke-blog.com/arduino_usb_host_shield_with_ch340/  [Arduino] USB Host ShieldとUSBシリアル通信を行う(CH340編)

zoologist  高级技匠
 楼主|

发表于 2023-7-17 19:47:06

下载附件HopiPower.zip

完整的代码,带有 CH340 USB Host 的代码
回复

使用道具 举报

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

本版积分规则

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

硬件清单

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

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

mail