码丁实验室,一站式儿童编程学习产品,寻地方代理合作共赢,微信联系:leon121393608。
去年买了一个带有 USB 输出噪音计型号是
WS1361,我特地去查了一下,2017年2月6日买的,然后一直拖到了最近才动手编写 Arduino 的代码。也多亏找到其他人的研究资料【参考1】,所以才比较顺利的完成解码。这部分的工作就像在解密一样,在不知道答案的情况下是一头雾水,当完成之后如释重负。
同样的,USB逻辑分析在这次编写中也发挥了重要。此外,最近接触到的Windows USB分析软件对于反向工程也是很多有效果【参考2】。
第一步还是抓取描述符

之前的键盘鼠标的描述符对于分析非常有用,但是这次描述符在分析过程中几乎可以称作毫无用处。当然,如果非要说有什么作用的话,只是让我得知他使用了自定义的协议。
第二步,使用逻辑分析仪查看抓包。这款逻辑分析仪带有USB接口,插入系统后,安装对应的驱动和应用程序可以在电脑上实时看到获得的当前音量(美中不足的是他们的软件没有数字签名,使用Windows 8/8.1/10 64位的朋友,必须禁用签名才能安装和运行起来)。
抓到的关键数据如下: Get Device Descriptor(Transfer 12) -> 获取自定义数据(Transfer 13) –> Get Device Descriptor(Transfer 14) 这样循环下去。这样的循环是他的应用程序驱动完成的。在我看来Get Device Descriptor 是毫无用处的。可能是应用程序用来确定设备是否被 remove才做的。

详细分析获取数据的过程 Transfer 13, 由 Transaction 283/286/287 三笔来组成。Transaction 283用户自定义的 Setup 过程,我们代码只需要做出和他内容相同的发出去即可。然后Transaction 286 是噪音计回复的数据包,其中有我们需要的当前音量数据。具体格式从【参考1】可以看到。
硬件方面使用的是 Arduino USB Host板+ Arduino Uno,因为是 Shield板,直接插上即可使用。为了更清楚的展示接收数据的过程,我使用了巨大的LED数码管(1.8寸),关于这个数码管的介绍可以在之前的文章中找到。

根据上述内容,编写程序如下:
#include
“Usb.h”
USB Usb;
uint16_t LastDB=0;
void digitalshow(int
value)
{
//这是数码管要求的数据头信息
Serial.write(0xff);
Serial.write(0×00);
Serial.write(0×04); //显示四位数值
//下面是四位当前值
Serial.write(0×10); //第一位黑
Serial.write((value – value /1000 * 1000) /
100);
//第三位后面有小数点,最高位为 1 表示显示小数点
Serial.write((value – value /100 * 100) /
10+ 0×80);
Serial.write(value % 10);
//最后一位是亮度
Serial.write(1);
}
void
DataParser(UsbDevice *pdev)
{
int nbytes=4;
uint8_t value[2];
Usb.ctrlReq(
pdev->address.devAddress,
0, //EndPoint
0xC0, //bmRequestType
0×04, //bRequest
0×01, //wValeLow
0×00, //wValueHigh
0×0000, //wIndex
nbytes,
nbytes,
&value[0],
NULL);
if ((value[0]+(value[1]<<8))==LastDB) {
return;
}
else
LastDB=(value[0]+(value[1]<<8));
//Serial.print(“RAW:”);
//Serial.print(value[0],HEX);
//Serial.print(” “);
//Serial.println(value[1],HEX);
//Serial.print(” DB:”);
//Serial.println((value[0] + ((value[1] &
3) * 256)) * 0.1 + 30);
digitalshow(((value[0] + ((value[1] & 3)
* 256)) * 0.1 + 30)*10);
}
void setup()
{
Serial.begin( 115200 );
Serial.println(“Start”);
if (Usb.Init() == -1)
Serial.println(“OSC did not
start.”);
delay( 200 );
}
void loop()
{
Usb.Task();
if ( Usb.getUsbTaskState() ==
USB_STATE_RUNNING )
{
Usb.ForEachUsbDevice(&DataParser);
delay(200);
}
}
工作的视频:
https://www.zhihu.com/video/948948344039550976
参考:
1. https://www.ebswift.com/reverse-engineering-spl-usb.html
Reverse Engineering the USB Protocol on the WENSN WS1361 Sound Pressure Level
Meter
2. http://www.lab-z.com/usblyzer/介绍一个 USB
分析软件
Usblyzer
始发于知乎专栏:王朝