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 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() }) }