20 コミット dac4b82ba8 ... 251f99df60

作者 SHA1 メッセージ 日付
  pacom 251f99df60 Add hid read COS temprature meter functions 3 年 前
  Cliff Brake 04a96460e7 add support for architectures other than x86 6 年 前
  Cliff Brake 2ca646c958 add USB bus and device fields to info struct 6 年 前
  Dmitry Yu Okunev 4d4ab70939 Fixed "go vet" problem: unreachable code 7 年 前
  mz 3282805c0c fix wrong log init func 7 年 前
  zhuo.meng 71302e0bf8 add default logger for lib silence 7 年 前
  zhuo.meng 89c412f53a make ctrl exportable 7 年 前
  Matthew Di Ferrante 4951867aa2 fix walker such that it returns 1 device per interface 8 年 前
  Matthew Di Ferrante 861032f28e reenable hid.claim 8 年 前
  Matthew Di Ferrante 638497e269 add interface support 8 年 前
  Matthew Di Ferrante 3a6504b709 add the ability to explicitly set endpoints 8 年 前
  Serge A. Zaitsev bef0a2cefc taking optional read size to be compatible with broken hardware 9 年 前
  Serge A. Zaitsev 7e8aadc6e7 using packet size from the endpoint descriptor 9 年 前
  Serge A. Zaitsev ebd9de6bb1 using readline instead of liner for line editing 9 年 前
  Serge A. Zaitsev 2b42443369 added interactive shell 9 年 前
  Serge A. Zaitsev 8eccce7691 checking errors in fs walk 10 年 前
  Serge A. Zaitsev 5aa45637d9 fixed output and feature reports 10 年 前
  Serge A. Zaitsev ae58655ae2 added note about linux 10 年 前
  zserge 809bf8301e Initial commit 10 年 前
  Serge A. Zaitsev adfcbafd00 initial commit 10 年 前
11 ファイル変更1014 行追加1 行削除
  1. 70 1
      README.md
  2. 61 0
      example/crc.go
  3. 10 0
      example/go.mod
  4. 16 0
      example/go.sum
  5. 228 0
      example/main.go
  6. 121 0
      example/mqtt.go
  7. 41 0
      hid.go
  8. 310 0
      usb_linux.go
  9. 40 0
      usbdef32_linux.go
  10. 42 0
      usbdef64_linux.go
  11. 75 0
      usbdef_linux.go

+ 70 - 1
README.md

@@ -1,3 +1,72 @@
 # COSMeter
 
-COS company temperature/humididy meter read by HID
+COS company temperature/humididy meter read by HID
+
+# hid
+ sudo cat /sys/kernel/debug/usb/devices
+T:  Bus=01 Lev=02 Prnt=21 Port=03 Cnt=01 Dev#= 29 Spd=12   MxCh= 0
+D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
+P:  Vendor=0483 ProdID=0003 Rev= 2.00
+S:  Manufacturer=STMicroelectronics
+S:  Product=COS-03 - Device
+S:  SerialNumber=2468ACF08765
+C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr=100mA
+I:* If#= 0 Alt= 0 #EPs= 2 Cls=03(HID  ) Sub=00 Prot=00 Driver=usbhid
+E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=2ms
+E:  Ad=01(O) Atr=03(Int.) MxPS=  64 Ivl=2ms
+
+num : number of data group
+timestamp : MSB, 61509EE4=1632673508="2021-09-27 00:25:08"-8Hour = "2021-09-26 16:25:08" global timestamp + 8hour
+temp : temperature MSB, 00F9 = 248= 24.8 deg.
+humid: Humididy MSB, 02AA=682 = 68.2%
+..........................|num|.timestamp..|.temp|.humid|.timestamp..|.temp| humid|.timestamp..|.temp| humid|                                             
+ 161    IN     00 aa 3a 03  07 61 50 9e  e4 00 f9 02  aa 61 50 9f  20 00 fa 02  aa 61 50 9f  5c 00 fa 02  aa 61 50 9f  ..:..aP......aP. ....aP.\....aP.        11.1.0        
+               98 00 fa 02  ab 61 50 9f  d4 00 f9 02  ab 61 50 a0  10 00 f9 02  aa 61 50 a0  4c 00 f8 02  ac 00 00 2e  .....aP......aP......aP.L.......        11.1.32       
+               e6   
+
+
+161    OUT     00 55 03 01  (2d 2d)(RANDOM INDEX) 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  .U..--..........................         1.1.0        
+               00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 d6  ................................         1.1.32       
+               b7                                                                                                      .                                        1.1.64       
+ 161    IN     00 aa 26 01  ad 9f 00 00  00 00 02 03  01 00 61 50  a6 d0 01 61  50 9d 3e 61  50 9e e4 62  3e a8 58 00  ..&...........aP...aP.>aP..b>.X.         2.1.0        
+               00 00 00 00  00 f6 02 b2  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 3a  ...............................:         2.1.32       
+               c5                                                                                                      .                                        2.1.64       
+ 161    OUT    00 55 01 07  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  .U..............................         3.1.0        
+               00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 c8  ................................         3.1.32       
+               8a                                                                                                      .                                        3.1.64       
+ 161    IN     00 aa 3b 07  55 53 42 2d  54 48 00 00  00 55 53 42  e6 b8 a9 e6  b9 bf e5 ba  a6 e8 ae b0  e5 bd 95 e4  ..;.USB-TH...USB................         4.1.0        
+               bb aa 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 34  ...............................4         4.1.32       
+               dc                                                                                                      .                                        4.1.64       
+ 161    OUT    00 55 01 09  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  .U..............................         5.1.0        
+               00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 c5  ................................         5.1.32       
+               a2                                                                                                      .                                        5.1.64       
+ 161    IN     00 aa 23 09  00 3c 00 3c  03 e8 fe 70  03 e8 00 00  00 00 00 00  00 00 0a 00  ef 7f 00 00  00 00 00 00  ..#..<.<...p....................         6.1.0        
+               00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 ce  ................................         6.1.32       
+               c9                                                                                                      .                                        6.1.64       
+ 161    OUT    00 55 01 02  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  .U..............................         7.1.0        
+               00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 cd  ................................         7.1.32       
+               46                                                                                                      F                                        7.1.64       
+ 161    IN     00 aa 3b 02  00 00 00 22(TOTAL)  55 53 42 2d  54 48 00 00  00 55 53 42  e6 b8 a9 e6  b9 bf e5 ba  a6 e8 ae b0  ..;...."USB-TH...USB............         8.1.0        
+               e5 bd 95 e4  bb aa 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 15  ................................         8.1.32       
+               89                                                                                                      .                                        8.1.64       
+ 161    OUT    00 55 01 0b  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  .U..............................         9.1.0        
+               00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 c7  ................................         9.1.32       
+               da                                                                                                      .                                        9.1.64       
+ 161    IN     00 aa 3b 02  00 00 00 22(TOTAL)  55 53 42 2d  54 48 00 00  00 55 53 42  e6 b8 a9 e6  b9 bf e5 ba  a6 e8 ae b0  ..;...."USB-TH...USB............        10.1.0        
+               e5 bd 95 e4  bb aa 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 15  ................................        10.1.32       
+               89                                                                                                      .                                       10.1.64       
+ 161    IN     00 aa 3a 03  07 61 50 9e  e4 00 f9 02  aa 61 50 9f  20 00 fa 02  aa 61 50 9f  5c 00 fa 02  aa 61 50 9f  ..:..aP......aP. ....aP.\....aP.        11.1.0        
+               98 00 fa 02  ab 61 50 9f  d4 00 f9 02  ab 61 50 a0  10 00 f9 02  aa 61 50 a0  4c 00 f8 02  ac 00 00 2e  .....aP......aP......aP.L.......        11.1.32       
+               e6                                                                                                      .                                       11.1.64       
+ 161    IN     00 aa 3a 03  07 61 50 a0  88 00 fa 02  ab 61 50 a0  c4 00 fa 02  aa 61 50 a1  00 00 fa 02  aa 61 50 a1  ..:..aP......aP......aP......aP.        12.1.0        
+               3c 00 fa 02  aa 61 50 a1  78 00 fa 02  aa 61 50 a1  b4 00 fa 02  a9 61 50 a1  f0 00 fa 02  a8 00 00 f7  <....aP.x....aP......aP.........        12.1.32       
+               6c                                                                                                      l                                       12.1.64       
+ 161    IN     00 aa 3a 03  07 61 50 a2  2c 00 fa 02  aa 61 50 a2  68 00 f9 02  aa 61 50 a2  a4 00 fa 02  aa 61 50 a2  ..:..aP.,....aP.h....aP......aP.        13.1.0        
+               e0 00 f9 02  aa 61 50 a3  1c 00 fa 02  aa 61 50 a3  58 00 fa 02  aa 61 50 a3  94 00 fa 02  aa 00 00 fc  .....aP......aP.X....aP.........        13.1.32       
+               7d                                                                                                      .                                       13.1.64       
+ 161    IN     00 aa 3a 03  07 61 50 a3  d0 00 f9 02  aa 61 50 a4  0c 00 fa 02  ab 61 50 a4  48 00 f9 02  ab 61 50 a4  ..:..aP......aP......aP.H....aP.        14.1.0        
+               84 00 f8 02  aa 61 50 a4  c0 00 f8 02  aa 61 50 a4  fc 00 f8 02  ac 61 50 a5  38 00 f8 02  ac 00 00 24  .....aP......aP......aP.8......$        14.1.32       
+               5c                                                                                                      \                                       14.1.64       
+ 161    IN     00 aa 3a 03  06 61 50 a5  74 00 f7 02  ad 61 50 a5  b0 00 f7 02  af 61 50 a5  ec 00 f7 02  af 61 50 a6  ..:..aP.t....aP......aP......aP.        15.1.0        
+               28 00 f7 02  ae 61 50 a6  64 00 f7 02  af 61 50 a6  a0 00 f6 02  af 00 00 00  00 00 00 00  00 00 00 98  (....aP.d....aP.................        15.1.32       
+               f1     

+ 61 - 0
example/crc.go

@@ -0,0 +1,61 @@
+package main
+
+import "fmt"
+
+var MbTable = []uint16{
+	0X0000, 0XC0C1, 0XC181, 0X0140, 0XC301, 0X03C0, 0X0280, 0XC241,
+	0XC601, 0X06C0, 0X0780, 0XC741, 0X0500, 0XC5C1, 0XC481, 0X0440,
+	0XCC01, 0X0CC0, 0X0D80, 0XCD41, 0X0F00, 0XCFC1, 0XCE81, 0X0E40,
+	0X0A00, 0XCAC1, 0XCB81, 0X0B40, 0XC901, 0X09C0, 0X0880, 0XC841,
+	0XD801, 0X18C0, 0X1980, 0XD941, 0X1B00, 0XDBC1, 0XDA81, 0X1A40,
+	0X1E00, 0XDEC1, 0XDF81, 0X1F40, 0XDD01, 0X1DC0, 0X1C80, 0XDC41,
+	0X1400, 0XD4C1, 0XD581, 0X1540, 0XD701, 0X17C0, 0X1680, 0XD641,
+	0XD201, 0X12C0, 0X1380, 0XD341, 0X1100, 0XD1C1, 0XD081, 0X1040,
+	0XF001, 0X30C0, 0X3180, 0XF141, 0X3300, 0XF3C1, 0XF281, 0X3240,
+	0X3600, 0XF6C1, 0XF781, 0X3740, 0XF501, 0X35C0, 0X3480, 0XF441,
+	0X3C00, 0XFCC1, 0XFD81, 0X3D40, 0XFF01, 0X3FC0, 0X3E80, 0XFE41,
+	0XFA01, 0X3AC0, 0X3B80, 0XFB41, 0X3900, 0XF9C1, 0XF881, 0X3840,
+	0X2800, 0XE8C1, 0XE981, 0X2940, 0XEB01, 0X2BC0, 0X2A80, 0XEA41,
+	0XEE01, 0X2EC0, 0X2F80, 0XEF41, 0X2D00, 0XEDC1, 0XEC81, 0X2C40,
+	0XE401, 0X24C0, 0X2580, 0XE541, 0X2700, 0XE7C1, 0XE681, 0X2640,
+	0X2200, 0XE2C1, 0XE381, 0X2340, 0XE101, 0X21C0, 0X2080, 0XE041,
+	0XA001, 0X60C0, 0X6180, 0XA141, 0X6300, 0XA3C1, 0XA281, 0X6240,
+	0X6600, 0XA6C1, 0XA781, 0X6740, 0XA501, 0X65C0, 0X6480, 0XA441,
+	0X6C00, 0XACC1, 0XAD81, 0X6D40, 0XAF01, 0X6FC0, 0X6E80, 0XAE41,
+	0XAA01, 0X6AC0, 0X6B80, 0XAB41, 0X6900, 0XA9C1, 0XA881, 0X6840,
+	0X7800, 0XB8C1, 0XB981, 0X7940, 0XBB01, 0X7BC0, 0X7A80, 0XBA41,
+	0XBE01, 0X7EC0, 0X7F80, 0XBF41, 0X7D00, 0XBDC1, 0XBC81, 0X7C40,
+	0XB401, 0X74C0, 0X7580, 0XB541, 0X7700, 0XB7C1, 0XB681, 0X7640,
+	0X7200, 0XB2C1, 0XB381, 0X7340, 0XB101, 0X71C0, 0X7080, 0XB041,
+	0X5000, 0X90C1, 0X9181, 0X5140, 0X9301, 0X53C0, 0X5280, 0X9241,
+	0X9601, 0X56C0, 0X5780, 0X9741, 0X5500, 0X95C1, 0X9481, 0X5440,
+	0X9C01, 0X5CC0, 0X5D80, 0X9D41, 0X5F00, 0X9FC1, 0X9E81, 0X5E40,
+	0X5A00, 0X9AC1, 0X9B81, 0X5B40, 0X9901, 0X59C0, 0X5880, 0X9841,
+	0X8801, 0X48C0, 0X4980, 0X8941, 0X4B00, 0X8BC1, 0X8A81, 0X4A40,
+	0X4E00, 0X8EC1, 0X8F81, 0X4F40, 0X8D01, 0X4DC0, 0X4C80, 0X8C41,
+	0X4400, 0X84C1, 0X8581, 0X4540, 0X8701, 0X47C0, 0X4680, 0X8641,
+	0X8201, 0X42C0, 0X4380, 0X8341, 0X4100, 0X81C1, 0X8081, 0X4040}
+
+func CheckSum(data []byte) uint16 {
+	var crc16 uint16
+	crc16 = 0xffff
+	for _, v := range data {
+		n := uint8(uint16(v) ^ crc16)
+		crc16 >>= 8
+		crc16 ^= MbTable[n]
+	}
+	return crc16
+}
+
+func FmtPrintBinary(buf []byte) {
+	if buf != nil && len(buf) != 0 {
+		sv := ""
+		for index, v := range buf {
+			sv += fmt.Sprintf("0x%02X,", v)
+			if index%16 == 15 {
+				sv += "\n"
+			}
+		}
+		fmt.Println(sv)
+	}
+}

+ 10 - 0
example/go.mod

@@ -0,0 +1,10 @@
+module github.com/hid/example
+
+go 1.14
+
+require (
+	github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
+	github.com/eclipse/paho.mqtt.golang v1.3.5
+	github.com/yuguorong/go v0.0.0-20180604090527-bdc77568d726
+	github.com/zserge/hid v0.0.0-20190124175232-e1626f1782f3
+)

+ 16 - 0
example/go.sum

@@ -0,0 +1,16 @@
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
+github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
+github.com/eclipse/paho.mqtt.golang v1.3.5 h1:sWtmgNxYM9P2sP+xEItMozsR3w0cqZFlqnNN1bdl41Y=
+github.com/eclipse/paho.mqtt.golang v1.3.5/go.mod h1:eTzb4gxwwyWpqBUHGQZ4ABAV7+Jgm1PklsYT/eo8Hcc=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/yuguorong/go v0.0.0-20180604090527-bdc77568d726 h1:3BSFfDVmj5yqpvj3H9m1EpZWhEKgJXW2C4MbTRv6rIM=
+github.com/yuguorong/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:i+SDEIi6IWgErb4Yq6H542yr5F8vxSoNbe9wFC+N5jc=
+github.com/zserge/hid v0.0.0-20190124175232-e1626f1782f3 h1:DAdExZJtAXR150WHt4wzTCzHH7lXkD4vKN9xLtF05VU=
+github.com/zserge/hid v0.0.0-20190124175232-e1626f1782f3/go.mod h1:OpyudhSlA/GSwcydk4+0Ex9DBI+9mOs/Pk06vmIjLSA=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0 h1:Jcxah/M+oLZ/R4/z5RzfPzGbPXnVDPkEDtf2JnuxN+U=
+golang.org/x/net v0.0.0-20200425230154-ff2c4b7c35a0/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

+ 228 - 0
example/main.go

@@ -0,0 +1,228 @@
+package main
+
+import (
+	"encoding/binary"
+	"encoding/hex"
+	"fmt"
+	"log"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/chzyer/readline"
+	"github.com/zserge/hid"
+)
+
+//output: 550301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037c3
+//input : 550301000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000037c3
+var cur_cmd string
+var cmdList map[string][]byte
+var tmrCmdTimeout *time.Timer = nil
+var chnMain chan string
+
+func init() {
+	cmdList = map[string][]byte{
+		"read":    []byte{0x55, 0x03, 0x01, 0x00},
+		"config":  []byte{0x55, 0x01, 0x07, 0x00},
+		"config2": []byte{0x55, 0x01, 0x09, 0x00},
+		"query":   []byte{0x55, 0x01, 0x02, 0x00}, //QUERY NAME
+		"dump":    []byte{0x55, 0x01, 0x0b, 0x00},
+		"clean":   []byte{0x55, 0x01, 0x05, 0x00},
+	}
+}
+
+func sendcmd(device hid.Device, strcmd string) error {
+	log.Println("send cmd : ", strcmd)
+	cmd := cmdList[strcmd]
+	cmdbuf := make([]byte, 64)
+	copy(cmdbuf[0:4], cmd[0:4])
+	crc := CheckSum(cmdbuf[0:62])
+	cmdbuf[63] = byte(crc >> 8)
+	cmdbuf[62] = byte(crc)
+	log.Println("output:", hex.EncodeToString(cmdbuf))
+	cur_cmd = strcmd
+	if _, err := device.Write(cmdbuf, 1*time.Second); err != nil {
+		log.Println("Output report write failed:", err)
+		return err
+	}
+
+	return nil
+}
+
+func resetCmdTimer() {
+	if tmrCmdTimeout != nil {
+		tmrCmdTimeout.Stop()
+		tmrCmdTimeout.Reset(3 * time.Second)
+	}
+}
+
+func shell(device hid.Device) {
+
+	if err := device.Open(); err != nil {
+		log.Println("Open error: ", err)
+		return
+	}
+	defer device.Close()
+
+	if report, err := device.HIDReport(); err != nil {
+		log.Println("HID report error:", err)
+		return
+	} else {
+		log.Println("HID report", hex.EncodeToString(report))
+	}
+	chnMain = make(chan string, 20)
+	tmrCmdTimeout = time.NewTimer(time.Second)
+	defer tmrCmdTimeout.Stop()
+
+	num_total := 0
+	num_read := 0
+
+	go func() {
+
+		for {
+			if buf, err := device.Read(-1, 1*time.Second); err == nil {
+				log.Println("Input report:  ", hex.EncodeToString(buf))
+				resetCmdTimer()
+				switch cur_cmd {
+				case "read":
+					FmtPrintBinary(buf)
+					temp := binary.BigEndian.Uint16(buf[23:25])
+					humidity := binary.BigEndian.Uint16(buf[25:27])
+					log.Printf("%d, %d\n", temp, humidity)
+					chnMain <- cur_cmd
+				case "query": // aa3b020000861c5553422d5448000000555342e6b8a9e6b9bfe5baa6e8aeb0e5bd95e4bbaa000000000000000000000000000000000000000000000000006223
+					num_total = int(buf[6])
+					log.Println(num_total)
+					chnMain <- cur_cmd
+				case "dump":
+					if buf[2] == 2 {
+						num_total = int(buf[6])
+						log.Println(num_total)
+						//                   aa 3a 03 07  6150d959 00f3 02c4;  6150d97700f302c16150d99500f302be6150d9b300f302be6150d9d100f202bd6150d9ef00f302c46150da0d00f202c200001f77
+					} else if buf[2] == 3 { //aa 3a 03 07; 61509ee4 00f9 02aa;  61509f2000fa02aa61509f5c00fa02aa61509f9800fa02ab61509fd400f902ab6150a01000f902aa6150a04c00f802ac00002ee6
+						items := int(buf[3])
+						for i := 0; i < items; i++ {
+							pos := i*8 + 4
+							ts := binary.BigEndian.Uint32(buf[pos:pos+4]) - 8*3600
+							temp := binary.BigEndian.Uint16(buf[pos+4 : pos+6])
+							humidity := binary.BigEndian.Uint16(buf[pos+6 : pos+8])
+							if ts > 1632760878 {
+								ReportTelemty(ts, temp, humidity)
+							}
+							num_read++
+						}
+						if items < 7 {
+							log.Printf("Total recived :%d\n", num_read)
+							chnMain <- cur_cmd
+						}
+					}
+				default:
+					log.Println(cur_cmd + " skipped")
+					chnMain <- cur_cmd
+				}
+			}
+		}
+	}()
+
+	var completer = readline.NewPrefixCompleter(
+		readline.PcItem("output"),
+		readline.PcItem("set-feature"),
+		readline.PcItem("get-feature"),
+	)
+	rl, err := readline.NewEx(&readline.Config{
+		Prompt:       "> ",
+		AutoComplete: completer,
+	})
+	if err != nil {
+		panic(err)
+	}
+
+	defer rl.Close()
+	log.SetOutput(rl.Stderr())
+
+	//sendcmd(device, "read")
+	cmds := []string{
+		"read",
+		"config",
+		"config2",
+		"query",
+		"dump",
+	}
+
+	timeout := 0
+
+	for _, k := range cmds {
+		sendcmd(device, k)
+		tmrCmdTimeout.Stop()
+		tmrCmdTimeout.Reset(3 * time.Second)
+		select {
+		case msg := <-chnMain:
+			log.Println("Send command [", k, "<>", msg, "] done!")
+		case <-tmrCmdTimeout.C:
+			log.Println("Send command [", k, "] timeout!")
+			timeout = 1
+		}
+		if timeout != 0 {
+			break
+		}
+	}
+	time.Sleep(1 * time.Second)
+	log.Printf("\nConfirm to clean all record data in device? [Yes/no]:")
+	if timeout == 0 && num_read > 0 && num_read == num_total {
+		line, err := rl.Readline()
+		if err == nil {
+			line = strings.ToLower(line)
+			switch line {
+			case "yes", "y":
+				sendcmd(device, "clean")
+				tmrCmdTimeout.Stop()
+				tmrCmdTimeout.Reset(3 * time.Second)
+				select {
+				case <-chnMain:
+				case <-tmrCmdTimeout.C:
+					log.Println("Send command [clean] timeout!")
+				}
+			default:
+				log.Println("Skip clean device")
+			}
+		}
+	}
+
+}
+
+func main() {
+	if len(os.Args) == 2 && (os.Args[1] == "-h" || os.Args[1] == "--help") {
+		fmt.Println("USAGE:")
+		fmt.Printf("  %s              list USB HID devices\n", os.Args[0])
+		fmt.Printf("  %s <id>         open USB HID device shell for the given input report size\n", os.Args[0])
+		fmt.Printf("  %s -h|--help    show this help\n", os.Args[0])
+		fmt.Println()
+		return
+	}
+
+	// Without arguments - enumerate all HID devices
+	if len(os.Args) == 1 {
+		found := false
+		hid.UsbWalk(func(device hid.Device) {
+			info := device.Info()
+			fmt.Printf("%04x:%04x:%04x:%02x\n", info.Vendor, info.Product, info.Revision, info.Interface)
+			found = true
+		})
+		if !found {
+			fmt.Println("No USB HID devices found\n")
+		}
+		return
+	}
+
+	hid.UsbWalk(func(device hid.Device) {
+		info := device.Info()
+		id := fmt.Sprintf("%04x:%04x:%04x:%02x", info.Vendor, info.Product, info.Revision, info.Interface)
+		if id != os.Args[1] {
+			return
+		}
+
+		StartMqttServer()
+		shell(device)
+		StopMqttServer()
+	})
+}

+ 121 - 0
example/mqtt.go

@@ -0,0 +1,121 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+
+	MQTT "github.com/eclipse/paho.mqtt.golang"
+	"github.com/yuguorong/go/log"
+)
+
+type QdMqtt struct {
+	User     string
+	Pswd     string
+	MqttUrl  string
+	MqttPort string
+	cnn      MQTT.Client
+}
+
+const (
+	mqttBrokerUser = ""
+	mqttBrokerPswd = ""
+)
+
+type CbRegistMqttSubs func(cnn *QdMqtt) int
+
+var cbMqttEdgeConfig MQTT.MessageHandler = func(client MQTT.Client, msg MQTT.Message) {
+	log.Infof("R_TOPIC: %s\n", msg.Topic())
+	log.Infof("R_MSG: %s\n", msg.Payload())
+}
+
+func Mqttsubs(mqtt *QdMqtt, subtopic string, Qos byte, hdlr MQTT.MessageHandler) {
+	fmt.Println("MQTT sub :[" + subtopic + "]")
+	if token := mqtt.cnn.Subscribe(subtopic, Qos, hdlr); token.Wait() && token.Error() != nil {
+		log.Info(token.Error())
+	}
+}
+
+func (mqtt *QdMqtt) PublishObj(topic string, qos byte, in interface{}) {
+	jsonBytes, _ := json.Marshal(in)
+	sjson := string(jsonBytes)
+	log.Info(topic)
+	log.Info(sjson)
+	mqtt.cnn.Publish(topic, qos, true, sjson)
+}
+
+//MqttDisocnnect mqtt disconnect
+func (mqtt *QdMqtt) Disocnnect() {
+	mqtt.cnn.Disconnect(0)
+}
+
+//MqttConnServer mqtt init and connect server
+func (mqtt *QdMqtt) ConnServer(retry int, cbSubs CbRegistMqttSubs) {
+	//create a ClientOptions struct setting the broker address, clientid, turn
+	//off trace output and set the default message handler
+	suid := "8d18db45-2fa6-410a-a46d-acf30f011dac"
+	brokerurl := "tcp://" + strings.Trim(mqtt.MqttUrl, " ") + ":" + strings.Trim(mqtt.MqttPort, "")
+	log.Info("Mqtt broker:", brokerurl)
+	opts := MQTT.NewClientOptions().AddBroker(brokerurl)
+	opts.SetClientID(suid)
+	opts.SetUsername(mqtt.User)
+	opts.SetPassword(mqtt.Pswd)
+	opts.SetDefaultPublishHandler(cbMqttEdgeConfig)
+
+	//create and start a client using the above ClientOptions
+	mqtt.cnn = MQTT.NewClient(opts)
+	for ; retry != 0; retry-- {
+		if token := mqtt.cnn.Connect(); token.Wait() && token.Error() == nil {
+			if cbSubs != nil {
+				cbSubs(mqtt)
+			}
+			log.Info("MQTT connect OK!")
+			return
+		} else {
+			log.Error("Retry to connect the MQTT server!! ", token.Error())
+		}
+		time.Sleep(time.Duration(3) * time.Second)
+	}
+	log.Info("Fault Error! can not connect MQTT server!!!")
+}
+
+func AppMqttSubs(mqtt *QdMqtt) int {
+	Mqttsubs(mqtt, "/sub/default", 1, cbMqttEdgeConfig)
+	return 0
+}
+
+var mqServer *QdMqtt = nil
+
+func MqttConnServer(cbSubs CbRegistMqttSubs) *QdMqtt {
+	mq := &QdMqtt{
+		User:     "5V1K3yCy5I5TRlVoCeeY", //"6XiAxAxtm0uTPnzwjtWd",
+		Pswd:     "",
+		MqttUrl:  "test-sbuilding.pacom.cn",
+		MqttPort: "1885",
+	}
+	mq.ConnServer(-1, cbSubs)
+	return mq
+}
+
+//{"ts":1632289574000, "values":{"temperature":"26.6", "pressure":"1000"}}
+func ReportTelemty(ts uint32, temp uint16, humidity uint16) {
+	devVal := make(map[string]interface{})
+	devVal["ts"] = float64(ts) * 1000
+	devItems := make(map[string]string)
+	devItems["temperature"] = strconv.Itoa(int(temp)/10) + "." + strconv.Itoa(int(temp)%10)
+	devItems["humidity"] = strconv.Itoa(int(humidity)/10) + "." + strconv.Itoa(int(humidity)%10)
+	devVal["values"] = devItems
+	mqServer.PublishObj("v1/devices/me/telemetry", 1, devVal)
+}
+
+func StartMqttServer() {
+	log.Info("Start MQTT server")
+	mqServer = MqttConnServer(AppMqttSubs)
+}
+
+func StopMqttServer() {
+	mqServer.Disocnnect()
+	log.Info("Stop MQTT server")
+}

+ 41 - 0
hid.go

@@ -0,0 +1,41 @@
+package hid
+
+import (
+	"io/ioutil"
+	"log"
+	"time"
+)
+
+//
+// General information about the HID device
+//
+type Info struct {
+	Vendor   uint16
+	Product  uint16
+	Revision uint16
+
+	SubClass uint8
+	Protocol uint8
+
+	Interface uint8
+	Bus       int
+	Device    int
+}
+
+//
+// A common HID device interace
+//
+type Device interface {
+	Open() error
+	Close()
+	Info() Info
+	HIDReport() ([]byte, error)
+	SetReport(int, []byte) error
+	GetReport(int) ([]byte, error)
+	Read(size int, ms time.Duration) ([]byte, error)
+	Write(data []byte, ms time.Duration) (int, error)
+	Ctrl(rtype, req, val, index int, data []byte, t int) (int, error)
+}
+
+// Default Logger setting
+var Logger = log.New(ioutil.Discard, "hid", log.LstdFlags)

+ 310 - 0
usb_linux.go

@@ -0,0 +1,310 @@
+package hid
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"syscall"
+	"time"
+	"unsafe"
+)
+
+type usbDevice struct {
+	info Info
+
+	f *os.File
+
+	epIn  int
+	epOut int
+
+	inputPacketSize  uint16
+	outputPacketSize uint16
+
+	path string
+}
+
+func (hid *usbDevice) Open() (err error) {
+	if hid.f != nil {
+		return errors.New("device is already opened")
+	}
+	if hid.f, err = os.OpenFile(hid.path, os.O_RDWR, 0644); err != nil {
+		return
+	} else {
+		return hid.claim()
+	}
+}
+
+func (hid *usbDevice) Close() {
+	if hid.f != nil {
+		hid.release()
+		hid.f.Close()
+		hid.f = nil
+	}
+}
+
+func (hid *usbDevice) Info() Info {
+	return hid.info
+}
+
+func (hid *usbDevice) ioctl(n uint32, arg interface{}) (int, error) {
+	b := new(bytes.Buffer)
+	if err := binary.Write(b, binary.LittleEndian, arg); err != nil {
+		return -1, err
+	}
+	r, _, err := syscall.Syscall6(syscall.SYS_IOCTL,
+		uintptr(hid.f.Fd()), uintptr(n),
+		uintptr(unsafe.Pointer(&(b.Bytes()[0]))), 0, 0, 0)
+	return int(r), err
+}
+
+func (hid *usbDevice) claim() error {
+	ifno := uint32(hid.info.Interface)
+	if r, errno := hid.ioctl(USBDEVFS_IOCTL, &usbfsIoctl{
+		Interface: ifno,
+		IoctlCode: USBDEVFS_DISCONNECT,
+		Data:      0,
+	}); r == -1 {
+		Logger.Println("driver disconnect failed:", r, errno)
+	}
+
+	if r, errno := hid.ioctl(USBDEVFS_CLAIM, &ifno); r == -1 {
+		return errno
+	}
+	return nil
+}
+
+func (hid *usbDevice) release() error {
+	ifno := uint32(hid.info.Interface)
+	if r, errno := hid.ioctl(USBDEVFS_RELEASE, &ifno); r == -1 {
+		return errno
+	}
+
+	if r, errno := hid.ioctl(USBDEVFS_IOCTL, &usbfsIoctl{
+		Interface: ifno,
+		IoctlCode: USBDEVFS_CONNECT,
+		Data:      0,
+	}); r == -1 {
+		Logger.Println("driver connect failed:", r, errno)
+	}
+	return nil
+}
+
+func (hid *usbDevice) Ctrl(rtype, req, val, index int, data []byte, t int) (int, error) {
+	return hid.ctrl(rtype, req, val, index, data, t)
+}
+
+func (hid *usbDevice) ctrl(rtype, req, val, index int, data []byte, t int) (int, error) {
+	s := usbfsCtrl{
+		ReqType: uint8(rtype),
+		Req:     uint8(req),
+		Value:   uint16(val),
+		Index:   uint16(index),
+		Len:     uint16(len(data)),
+		Timeout: uint32(t),
+		Data:    slicePtr(data),
+	}
+	if r, err := hid.ioctl(USBDEVFS_CONTROL, &s); r == -1 {
+		return -1, err
+	} else {
+		return r, nil
+	}
+}
+
+func (hid *usbDevice) intr(ep int, data []byte, t int) (int, error) {
+	if r, err := hid.ioctl(USBDEVFS_BULK, &usbfsBulk{
+		Endpoint: uint32(ep),
+		Len:      uint32(len(data)),
+		Timeout:  uint32(t),
+		Data:     slicePtr(data),
+	}); r == -1 {
+		return -1, err
+	} else {
+		return r, nil
+	}
+}
+
+func (hid *usbDevice) Read(size int, timeout time.Duration) ([]byte, error) {
+	if size < 0 {
+		size = int(hid.inputPacketSize)
+	}
+	data := make([]byte, size, size)
+	ms := timeout / (1 * time.Millisecond)
+	n, err := hid.intr(hid.epIn, data, int(ms))
+	if err == nil {
+		return data[:n], nil
+	} else {
+		return nil, err
+	}
+}
+
+func (hid *usbDevice) Write(data []byte, timeout time.Duration) (int, error) {
+	if hid.epOut > 0 {
+		ms := timeout / (1 * time.Millisecond)
+		return hid.intr(hid.epOut, data, int(ms))
+	} else {
+		return hid.ctrl(0x21, 0x09, 2<<8+0, int(hid.info.Interface), data, len(data))
+	}
+}
+
+func (hid *usbDevice) HIDReport() ([]byte, error) {
+	buf := make([]byte, 256, 256)
+	// In transfer, recepient interface, GetDescriptor, HidReport type
+	n, err := hid.ctrl(0x81, 0x06, 0x22<<8+int(hid.info.Interface), 0, buf, 1000)
+	if err != nil {
+		return nil, err
+	} else {
+		return buf[:n], nil
+	}
+}
+
+func (hid *usbDevice) GetReport(report int) ([]byte, error) {
+	buf := make([]byte, 256, 256)
+	// 10100001, GET_REPORT, type*256+id, intf, len, data
+	n, err := hid.ctrl(0xa1, 0x01, 3<<8+report, int(hid.info.Interface), buf, 1000)
+	if err != nil {
+		return nil, err
+	} else {
+		return buf[:n], nil
+	}
+}
+
+func (hid *usbDevice) SetReport(report int, data []byte) error {
+	// 00100001, SET_REPORT, type*256+id, intf, len, data
+	_, err := hid.ctrl(0x21, 0x09, 3<<8+report, int(hid.info.Interface), data, 1000)
+	return err
+}
+
+//
+// Enumeration
+//
+
+func cast(b []byte, to interface{}) error {
+	r := bytes.NewBuffer(b)
+	return binary.Read(r, binary.LittleEndian, to)
+}
+
+// typical /dev bus entry: /dev/bus/usb/006/003
+var reDevBusDevice = regexp.MustCompile(`/dev/bus/usb/(\d+)/(\d+)`)
+
+func walker(path string, cb func(Device)) error {
+	if desc, err := ioutil.ReadFile(path); err != nil {
+		return err
+	} else {
+		r := bytes.NewBuffer(desc)
+		expected := map[byte]bool{
+			UsbDescTypeDevice: true,
+		}
+		devDesc := deviceDesc{}
+		var device *usbDevice
+		for r.Len() > 0 {
+			if length, err := r.ReadByte(); err != nil {
+				return err
+			} else if err := r.UnreadByte(); err != nil {
+				return err
+			} else {
+				body := make([]byte, length, length)
+				if n, err := r.Read(body); err != nil {
+					return err
+				} else if n != int(length) || length < 2 {
+					return errors.New("short read")
+				} else {
+					if !expected[body[1]] {
+						continue
+					}
+					switch body[1] {
+					case UsbDescTypeDevice:
+						expected[UsbDescTypeDevice] = false
+						expected[UsbDescTypeConfig] = true
+						if err := cast(body, &devDesc); err != nil {
+							return err
+						}
+						//info := Info{
+						//}
+					case UsbDescTypeConfig:
+						expected[UsbDescTypeInterface] = true
+						expected[UsbDescTypeReport] = false
+						expected[UsbDescTypeEndpoint] = false
+						// Device left from the previous config
+						if device != nil {
+							cb(device)
+							device = nil
+						}
+					case UsbDescTypeInterface:
+						if device != nil {
+							cb(device)
+							device = nil
+						}
+						expected[UsbDescTypeEndpoint] = true
+						expected[UsbDescTypeReport] = true
+						i := &interfaceDesc{}
+						if err := cast(body, i); err != nil {
+							return err
+						}
+						if i.InterfaceClass == UsbHidClass {
+							matches := reDevBusDevice.FindStringSubmatch(path)
+							bus := 0
+							dev := 0
+							if len(matches) >= 3 {
+								bus, _ = strconv.Atoi(matches[1])
+								dev, _ = strconv.Atoi(matches[2])
+							}
+							device = &usbDevice{
+								info: Info{
+									Vendor:    devDesc.Vendor,
+									Product:   devDesc.Product,
+									Revision:  devDesc.Revision,
+									SubClass:  i.InterfaceSubClass,
+									Protocol:  i.InterfaceProtocol,
+									Interface: i.Number,
+									Bus:       bus,
+									Device:    dev,
+								},
+								path: path,
+							}
+						}
+					case UsbDescTypeEndpoint:
+						if device != nil {
+							if device.epIn != 0 && device.epOut != 0 {
+								cb(device)
+								device.epIn = 0
+								device.epOut = 0
+							}
+							e := &endpointDesc{}
+							if err := cast(body, e); err != nil {
+								return err
+							}
+							if e.Address > 0x80 && device.epIn == 0 {
+								device.epIn = int(e.Address)
+								device.inputPacketSize = e.MaxPacketSize
+							} else if e.Address < 0x80 && device.epOut == 0 {
+								device.epOut = int(e.Address)
+								device.outputPacketSize = e.MaxPacketSize
+							}
+						}
+					}
+				}
+			}
+		}
+		if device != nil {
+			cb(device)
+		}
+	}
+	return nil
+}
+
+func UsbWalk(cb func(Device)) {
+	filepath.Walk(DevBusUsb, func(f string, fi os.FileInfo, err error) error {
+		if err != nil || fi.IsDir() {
+			return nil
+		}
+		if err := walker(f, cb); err != nil {
+			Logger.Println("UsbWalk: ", err)
+		}
+		return nil
+	})
+}

+ 40 - 0
usbdef32_linux.go

@@ -0,0 +1,40 @@
+// +build 386 amd64p32 arm armbe mips mipsle mips64p32 mips64p32le ppc s390 sparc
+
+package hid
+
+import (
+	"unsafe"
+)
+
+const (
+	USBDEVFS_IOCTL   = 0xc00c5512
+	USBDEVFS_BULK    = 0xc0105502
+	USBDEVFS_CONTROL = 0xc0105500
+)
+
+type usbfsIoctl struct {
+	Interface uint32
+	IoctlCode uint32
+	Data      uint32
+}
+
+type usbfsCtrl struct {
+	ReqType uint8
+	Req     uint8
+	Value   uint16
+	Index   uint16
+	Len     uint16
+	Timeout uint32
+	Data    uint32
+}
+
+type usbfsBulk struct {
+	Endpoint uint32
+	Len      uint32
+	Timeout  uint32
+	Data     uint32
+}
+
+func slicePtr(b []byte) uint32 {
+	return uint32(uintptr(unsafe.Pointer(&b[0])))
+}

+ 42 - 0
usbdef64_linux.go

@@ -0,0 +1,42 @@
+// +build amd64 arm64 arm64be ppc64 ppc64le mips64 mips64le s390x sparc64
+
+package hid
+
+import (
+	"unsafe"
+)
+
+const (
+	USBDEVFS_IOCTL   = 0xc0105512
+	USBDEVFS_BULK    = 0xc0185502
+	USBDEVFS_CONTROL = 0xc0185500
+)
+
+type usbfsIoctl struct {
+	Interface uint32
+	IoctlCode uint32
+	Data      uint64
+}
+
+type usbfsCtrl struct {
+	ReqType uint8
+	Req     uint8
+	Value   uint16
+	Index   uint16
+	Len     uint16
+	Timeout uint32
+	_       uint32
+	Data    uint64 // FIXME
+}
+
+type usbfsBulk struct {
+	Endpoint uint32
+	Len      uint32
+	Timeout  uint32
+	_        uint32
+	Data     uint64
+}
+
+func slicePtr(b []byte) uint64 {
+	return uint64(uintptr(unsafe.Pointer(&b[0])))
+}

+ 75 - 0
usbdef_linux.go

@@ -0,0 +1,75 @@
+package hid
+
+const UsbHidClass = 3
+
+type deviceDesc struct {
+	Length            uint8
+	DescriptorType    uint8
+	USB               uint16
+	DeviceClass       uint8
+	DeviceSubClass    uint8
+	DeviceProtocol    uint8
+	MaxPacketSize     uint8
+	Vendor            uint16
+	Product           uint16
+	Revision          uint16
+	ManufacturerIndex uint8
+	ProductIndex      uint8
+	SerialIndex       uint8
+	NumConfigurations uint8
+}
+
+type configDesc struct {
+	Length             uint8
+	DescriptorType     uint8
+	TotalLength        uint16
+	NumInterfaces      uint8
+	ConfigurationValue uint8
+	Configuration      uint8
+	Attributes         uint8
+	MaxPower           uint8
+}
+
+type interfaceDesc struct {
+	Length            uint8
+	DescriptorType    uint8
+	Number            uint8
+	AltSetting        uint8
+	NumEndpoints      uint8
+	InterfaceClass    uint8
+	InterfaceSubClass uint8
+	InterfaceProtocol uint8
+	InterfaceIndex    uint8
+}
+
+type endpointDesc struct {
+	Length         uint8
+	DescriptorType uint8
+	Address        uint8
+	Attributes     uint8
+	MaxPacketSize  uint16
+	Interval       uint8
+}
+
+type hidReportDesc struct {
+	Length         uint8
+	DescriptorType uint8
+}
+
+const (
+	USBDEVFS_CONNECT    = 0x5517
+	USBDEVFS_DISCONNECT = 0x5516
+	USBDEVFS_CLAIM      = 0x8004550f
+	USBDEVFS_RELEASE    = 0x80045510
+)
+
+const DevBusUsb = "/dev/bus/usb"
+
+const (
+	UsbDescTypeDevice    = 1
+	UsbDescTypeConfig    = 2
+	UsbDescTypeString    = 3
+	UsbDescTypeInterface = 4
+	UsbDescTypeEndpoint  = 5
+	UsbDescTypeReport    = 33
+)