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

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 3752|回复: 2

UT325D 接触式温度计分析

[复制链接]
发表于 2018-7-12 12:51 | 显示全部楼层 |阅读模式
最近公司有要求进行温度的记录,经过研究入手“UT325数字测温仪,这是是优利德(UNI-T 生产的双路测温仪。
image001.png
                              
但是对于需要用上位机采集数据的用户来说,它只是单路的。它可以通过USB口上报 T1 温度,T2温度或者 T1-T2温度,但是没有办法同时报告 T1 T2温度,并且只能通过正面按键才能切换报告的通道,因此,对于我们来说,这只是单通道的测温仪。
image002.png
官方没有提供支持,为了获得通讯协议,需要使用 USBlyzer 抓取数据逐步分析。第一步就是抓取 Descriptor
USB Input Device
  
Connection Status
  
  
Device connected
  
  
Current Configuration
  
  
1
  
  
Speed
  
  
Full (12 Mbit/s)
  
  
Device Address
  
  
8
  
  
Number Of Open Pipes
  
  
2
  
Device Descriptor USBto Serial
  
Offset
  
  
Field
  
  
Size
  
  
Value
  
  
Description
  
  
0
  
  
bLength
  
  
1
  
  
12h
  
  
1
  
  
bDescriptorType
  
  
1
  
  
01h
  
  
Device
  
  
2
  
  
bcdUSB
  
  
2
  
  
0100h
  
  
USB Spec 1.0
  
  
4
  
  
bDeviceClass
  
  
1
  
  
00h
  
  
Class info in Ifc Descriptors
  
  
5
  
  
bDeviceSubClass
  
  
1
  
  
00h
  
  
6
  
  
bDeviceProtocol
  
  
1
  
  
00h
  
  
7
  
  
bMaxPacketSize0
  
  
1
  
  
08h
  
  
8 bytes
  
  
8
  
  
idVendor
  
  
2
  
  
1A86h
  
  
10
  
  
idProduct
  
  
2
  
  
E008h
  
  
12
  
  
bcdDevice
  
  
2
  
  
1400h
  
  
14.00
  
  
14
  
  
iManufacturer
  
  
1
  
  
01h
  
  
"WCH.CN ."
  
  
15
  
  
iProduct
  
  
1
  
  
02h
  
  
"USB to Serial"
  
  
16
  
  
iSerialNumber
  
  
1
  
  
00h
  
  
17
  
  
bNumConfigurations
  
  
1
  
  
01h
  
Configuration Descriptor1 Bus Powered, 100 mA
  
Offset
  
  
Field
  
  
Size
  
  
Value
  
  
Description
  
  
0
  
  
bLength
  
  
1
  
  
09h
  
  
1
  
  
bDescriptorType
  
  
1
  
  
02h
  
  
Configuration
  
  
2
  
  
wTotalLength
  
  
2
  
  
0029h
  
  
4
  
  
bNumInterfaces
  
  
1
  
  
01h
  
  
5
  
  
bConfigurationValue
  
  
1
  
  
01h
  
  
6
  
  
iConfiguration
  
  
1
  
  
04h
  
  
7
  
  
bmAttributes
  
  
1
  
  
80h
  
  
Bus 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.......
  
  
8
  
  
bMaxPower
  
  
1
  
  
32h
  
  
100 mA
  
Interface Descriptor 0/0 HID,2 Endpoints
  
Offset
  
  
Field
  
  
Size
  
  
Value
  
  
Description
  
  
0
  
  
bLength
  
  
1
  
  
09h
  
  
1
  
  
bDescriptorType
  
  
1
  
  
04h
  
  
Interface
  
  
2
  
  
bInterfaceNumber
  
  
1
  
  
00h
  
  
3
  
  
bAlternateSetting
  
  
1
  
  
00h
  
  
4
  
  
bNumEndpoints
  
  
1
  
  
02h
  
  
5
  
  
bInterfaceClass
  
  
1
  
  
03h
  
  
HID
  
  
6
  
  
bInterfaceSubClass
  
  
1
  
  
00h
  
  
7
  
  
bInterfaceProtocol
  
  
1
  
  
00h
  
  
8
  
  
iInterface
  
  
1
  
  
00h
  
HID Descriptor
  
Offset
  
  
Field
  
  
Size
  
  
Value
  
  
Description
  
  
0
  
  
bLength
  
  
1
  
  
09h
  
  
1
  
  
bDescriptorType
  
  
1
  
  
21h
  
  
HID
  
  
2
  
  
bcdHID
  
  
2
  
  
0100h
  
  
1.00
  
  
4
  
  
bCountryCode
  
  
1
  
  
00h
  
  
5
  
  
bNumDescriptors
  
  
1
  
  
01h
  
  
6
  
  
bDescriptorType
  
  
1
  
  
22h
  
  
Report
  
  
7
  
  
wDescriptorLength
  
  
2
  
  
0025h
  
  
37 bytes
  
Endpoint Descriptor 82 2In, Interrupt, 5 ms
  
Offset
  
  
Field
  
  
Size
  
  
Value
  
  
Description
  
  
0
  
  
bLength
  
  
1
  
  
07h
  
  
1
  
  
bDescriptorType
  
  
1
  
  
05h
  
  
Endpoint
  
  
2
  
  
bEndpointAddress
  
  
1
  
  
82h
  
  
2 In
  
  
3
  
  
bmAttributes
  
  
1
  
  
03h
  
  
Interrupt
  
  
1..0: Transfer Type
  
  
......11
  
  
Interrupt
  
  
7..2: Reserved
  
  
000000..
  
  
4
  
  
wMaxPacketSize
  
  
2
  
  
0008h
  
  
8 bytes
  
  
6
  
  
bInterval
  
  
1
  
  
05h
  
  
5 ms
  
Endpoint Descriptor 02 2Out, Interrupt, 5 ms
  
Offset
  
  
Field
  
  
Size
  
  
Value
  
  
Description
  
  
0
  
  
bLength
  
  
1
  
  
07h
  
  
1
  
  
bDescriptorType
  
  
1
  
  
05h
  
  
Endpoint
  
  
2
  
  
bEndpointAddress
  
  
1
  
  
02h
  
  
2 Out
  
  
3
  
  
bmAttributes
  
  
1
  
  
03h
  
  
Interrupt
  
  
1..0: Transfer Type
  
  
......11
  
  
Interrupt
  
  
7..2: Reserved
  
  
000000..
  
  
4
  
  
wMaxPacketSize
  
  
2
  
  
0008h
  
  
8 bytes
  
  
6
  
  
bInterval
  
  
1
  
  
05h
  
  
5 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
  
  
    Logical  Minimum (0)
  
  
15 00
  
  
    Logical  Maximum (255)
  
  
26 FF 00
  
  
    Report  Size (8)
  
  
75 08
  
  
    Report  Count (8)
  
  
95 08
  
  
    Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit)
  
  
81 02
  
  
    Usage  (Vendor-Defined 2)
  
  
09 02
  
  
    Report  Size (8)
  
  
75 08
  
  
    Report  Count (8)
  
  
95 08
  
  
    Output (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit)
  
  
91 02
  
  
    Usage  (Vendor-Defined 3)
  
  
09 03
  
  
    Report  Size (8)
  
  
75 08
  
  
    Report  Count (5)
  
  
95 05
  
  
    Feature (Data,Var,Abs,NWrp,Lin,Pref,NNul,NVol,Bit)
  
  
B1 02
  
  
End Collection
  
  
C0
  
This report was generated by USBlyzer
Descriptor上可以看到,它是通过 HID来进行通讯的(根据【参考1】,使用WCH CH9325 Uart HID 芯片),这种通讯的好处是无需使用驱动,缺点是速度不快,这里对于温度这种物理量来说已经足够使用。
第二步,分析具体通讯协议了,好在网上找到了一些资料【参考2】,虽然对不上(国产设备经常发生型号相同,但是细节完全不同的情况),但是根据提示重新捋一下还是能够得到大概的结果。
image003.png image004.png
image005.png
  
最终的结论是:每次读取HID数据中第二个字节是有意义的,我们需要的温度信息就在上面数值的第二个字节中。

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

 楼主| 发表于 2018-7-12 12:53 | 显示全部楼层
完整代码下载
UT325D.zip (65.79 KB, 下载次数: 20)
发表于 2019-7-2 09:38 | 显示全部楼层
非常好的技术贴!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-11-28 11:44 , Processed in 0.109856 second(s), 19 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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