UT325D 接触式温度计分析-Arduino中文社区 - Powered by Discuz! Archiver

Zoologist 发表于 2018-7-12 12:51

UT325D 接触式温度计分析

最近公司有要求进行温度的记录,经过研究入手“UT325数字测温仪”,这是是优利德(UNI-T) 生产的双路测温仪。                              但是对于需要用上位机采集数据的用户来说,它只是单路的。它可以通过USB口上报 T1 温度,T2温度或者 T1-T2温度,但是没有办法同时报告 T1 和 T2温度,并且只能通过正面按键才能切换报告的通道,因此,对于我们来说,这只是单通道的测温仪。 官方没有提供支持,为了获得通讯协议,需要使用 USBlyzer 抓取数据逐步分析。第一步就是抓取 Descriptor。USB Input Device
Connection StatusDevice connected
Current Configuration1
SpeedFull (12 Mbit/s)
Device Address8
Number Of Open Pipes2
Device Descriptor USBto Serial
OffsetFieldSizeValueDescription
0bLength112h
1bDescriptorType101hDevice
2bcdUSB20100hUSB Spec 1.0
4bDeviceClass100hClass info in Ifc Descriptors
5bDeviceSubClass100h
6bDeviceProtocol100h
7bMaxPacketSize0108h8 bytes
8idVendor21A86h
10idProduct2E008h
12bcdDevice21400h14.00
14iManufacturer101h"WCH.CN ."
15iProduct102h"USB to Serial"
16iSerialNumber100h
17bNumConfigurations101h
Configuration Descriptor1 Bus Powered, 100 mA
OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType102hConfiguration
2wTotalLength20029h
4bNumInterfaces101h
5bConfigurationValue101h
6iConfiguration104h
7bmAttributes180hBus Powered
4..0: Reserved...00000
5: Remote Wakeup..0..... No
6: Self Powered.0...... No, Bus Powered
7: Reserved (set to one)
(bus-powered for 1.0)1.......
8bMaxPower132h100 mA
Interface Descriptor 0/0 HID,2 Endpoints
OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType104hInterface
2bInterfaceNumber100h
3bAlternateSetting100h
4bNumEndpoints102h
5bInterfaceClass103hHID
6bInterfaceSubClass100h
7bInterfaceProtocol100h
8iInterface100h
HID Descriptor
OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType121hHID
2bcdHID20100h1.00
4bCountryCode100h
5bNumDescriptors101h
6bDescriptorType122hReport
7wDescriptorLength20025h37 bytes
Endpoint Descriptor 82 2In, Interrupt, 5 ms
OffsetFieldSizeValueDescription
0bLength107h
1bDescriptorType105hEndpoint
2bEndpointAddress182h2 In
3bmAttributes103hInterrupt
1..0: Transfer Type......11 Interrupt
7..2: Reserved000000..
4wMaxPacketSize20008h8 bytes
6bInterval105h5 ms
Endpoint Descriptor 02 2Out, Interrupt, 5 ms
OffsetFieldSizeValueDescription
0bLength107h
1bDescriptorType105hEndpoint
2bEndpointAddress102h2 Out
3bmAttributes103hInterrupt
1..0: Transfer Type......11 Interrupt
7..2: Reserved000000..
4wMaxPacketSize20008h8 bytes
6bInterval105h5 ms
Interface 0 HID ReportDescriptor Vendor-Defined 1
Item Tag (Value)Raw Data
Usage Page (Vendor-Defined 161)06 A0 FF
Usage (Vendor-Defined 1)09 01
Collection (Application)A1 01
    Usage(Vendor-Defined 1)09 01
    LogicalMinimum (0)15 00
    LogicalMaximum (255)26 FF 00
    ReportSize (8)75 08
    ReportCount (8)95 08
    Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit)81 02
    Usage(Vendor-Defined 2)09 02
    ReportSize (8)75 08
    ReportCount (8)95 08
    Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit)91 02
    Usage(Vendor-Defined 3)09 03
    ReportSize (8)75 08
    ReportCount (5)95 05
    Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit)B1 02
End CollectionC0
This report was generated by USBlyzer从Descriptor上可以看到,它是通过 HID来进行通讯的(根据【参考1】,使用WCH CH9325 Uart转 HID 芯片),这种通讯的好处是无需使用驱动,缺点是速度不快,这里对于温度这种物理量来说已经足够使用。第二步,分析具体通讯协议了,好在网上找到了一些资料【参考2】,虽然对不上(国产设备经常发生型号相同,但是细节完全不同的情况),但是根据提示重新捋一下还是能够得到大概的结果。 最终的结论是:每次读取HID数据中第二个字节是有意义的,我们需要的温度信息就在上面数值的第二个字节中。
编写一个 Application,输出读取到的数据,可以看到存在一个以0x0D,0x0A作为结尾的“循环节”。下面的数据中,第一个是HID的Report ID, 所以会比前面USB Analyzer 抓到的多一个。 针对有意义的数值(就是前面红框中的第三个数值:00 00 00 00 00 00 32 3A…….0D 0A)进行分析。

偏移(Byte)
0保留,0x00
1保留,0x00
2保留,0x00
3保留,0x00
4保留,0x00
5保留,0x00
6保留,0x32
7ASCII,符号位,正=0x3A,负=0x3B
8ASCII
9ASCII
10ASCII
11当前温度单位,摄氏度=1,华氏度=2,开尔文温度=3
12保留,0x30
13保留,0x30
14保留,0x30
15ASCII格式的时间
16ASCII格式的时间
17ASCII格式的时间
18ASCII格式的时间
19数据来源:T1=0T2=1T1-T2=2
20保留,0x00
21保留,0x00
22保留,0x31
23保留,0x0D
24保留,0x0A
接下来,发现遇到一个奇怪的问题:第一次插入之后运行数据什么数值都没有,就是说:设备上电之后不会主动发送数据出来,而我抓取的数据都是先运行 UT325D的软件查看设备能否正常工作之后才有的。猜测是运行配套软件之后,上位机的软件发送了什么命令给设备,设备才会打开输出输出的功能。为此,用 USBlyzer录制启动软件后的通讯。可以看到,按下上位机软件的 USB Connection 之后,有三条“可疑”的指令(确定肯定是上位机触发的,因此在 OUT 的通讯中查找即可),一个是 Set Report 60 09 0000 03,另外两个是 HID02 5A 00 00 00 00 00 00 和 01 01 00 00 00 00 0000 。 为了便于实验,使用SimpleHIDWrite3 这个软件,它是专门针对 HID设备的测试软件,能够在无需编程的情况下直接发送命令。打开工具后选择 UT325D 这个 USB 设备,在 SimpleHIDWrite3 这个软件中,该设备名称是 USB to Serial (这个软件使用 Device Descriptor 中的iProduct字符串作为设备名) 再进一步,我们使用 Info 按钮查看设备信息,可以看到如下内容: 特别注意的是,对于这个设备 HID Report的Size 是9字节(1个 Report ID + 8 个自定义内容),Feature Report 的字节是6个。经过测试,首先使用这个工具SetFeature6009 00 00 03给设备数据即可开始发送全0的数据,然后需要再使用工具发送 HID 01 01 00 00 00 0000 00,这个设备即可像自带工具一样发送正常数据了。整个代码是基于我们之前编写的 HIDSend,不同点在于我们引入了HidD_SetFeature API 针对设备发送 SetFeature命令                                                   //发送报告的缓冲区,1字节报告ID+8字节报告数据。                                                   UCHAR WriteReportBuffer;                                                    WriteReportBuffer= 0x00;                                                      WriteReportBuffer = 0x60;                                                   WriteReportBuffer= 0x09;                                                   WriteReportBuffer= 0x00;                                                   WriteReportBuffer= 0x00;                                                   WriteReportBuffer= 0x03;                                                    //调用HidD_SetFeature函数发送数据                                                   Result= HidD_SetFeature(                                                            hUsb,                                                            WriteReportBuffer,                                                            6);这步完成之后,再使用WriteFile发送我们的 HID 数据:                                                   WriteReportBuffer= 0x00;                                                   WriteReportBuffer= 0x01;                                                   WriteReportBuffer= 0x01;                                                   WriteReportBuffer= 0x00;                                                   WriteReportBuffer= 0x00;                                                   WriteReportBuffer= 0x00;                                                   WriteReportBuffer= 0x00;                                                   WriteReportBuffer= 0x00;                                                    DWORD lpNumberOfBytesWritten;                                                    //调用WriteFile函数发送数据                                                   Result= WriteFile(hUsb,                                                            WriteReportBuffer,                                                            9,                                                            &lpNumberOfBytesWritten,                                                            NULL);上述完成之后就可以正常接收数据了,使用ReadFile反复读取                                                   //调用ReadFile 接收数据                                                   Result= ReadFile(                                                            hUsb,                                                            ReadReportBuffer,                                                            9,                                                            &lpNumberOfBytesRead,                                                            NULL); 之后再对收到的数据进行解析,需要注意的是,每次收到的 HID报文中,只有第三个数值是有效的,因此我还引入counter变量进行解析。也正是因为这样解析的缘故,所以输出的时间格式是:SS“ MM’                                 //if (counter == 6) { printf("%c", ReadReportBuffer);}                                 if (counter == 7) { printf("%c", ReadReportBuffer); }                                 if (counter == 8) { printf("%c", ReadReportBuffer); }                                 if (counter == 9) { printf("%c", ReadReportBuffer); }                                 if (counter == 10) { printf("%c", ReadReportBuffer); }                                 if ((counter ==11)&&(ReadReportBuffer==0x31)) { printf("C"); }                                 if ((counter == 11) &&(ReadReportBuffer == 0x32)) { printf("F"); }                                 if ((counter == 11) &&(ReadReportBuffer == 0x33)) { printf("K"); }                                  if (counter == 15) { printf(" %c", ReadReportBuffer); }                                 if (counter == 16) { printf("%c\"",ReadReportBuffer); }                                 if (counter == 17) { printf("%c", ReadReportBuffer); }                                 if (counter == 18) { printf("%c'", ReadReportBuffer); }                                 if ((counter == 19) &&(ReadReportBuffer == 0x30)) { printf("T1"); }                                 if ((counter == 19) &&(ReadReportBuffer == 0x31)) { printf("T2"); }                                 if ((counter == 19) &&(ReadReportBuffer == 0x32)) { printf("T1-T2"); }                                                                                    if (ReadReportBuffer == 0xA) {                                                   printf("\n"); counter = 0;                                                   }运行结果如下: 参考:1. https://sigrok.org/wiki/WCH_CH93252. https://sigrok.org/wiki/UNI-T_UT3253. http://www.uni-trend.com.cn/productsdetail_1302_460_460.html

Zoologist 发表于 2018-7-12 12:53

完整代码下载

ykczj 发表于 2019-7-2 09:38

非常好的技术贴!
页: [1]
查看完整版本: UT325D 接触式温度计分析