golang实现AT命令的发送(Windows版)
问题的提出
问题所依赖的库
serial.go
Goserial is a simple go package to allow you to read and write from
the serial port as a stream of bytes.
It aims to have the same API on all platforms, including windows. As
an added bonus, the windows package does not use cgo, so you can cross
compile for windows from another platform. Unfortunately goinstall
does not currently let you cross compile so you will have to do it
manually:
GOOS=windows make clean install
Currently there is very little in the way of configurability. You can
set the baud rate. Then you can Read(), Write(), or Close() the
connection. Read() will block until at least one byte is returned.
Write is the same. There is currently no exposed way to set the
timeouts, though patches are welcome.
Currently ports are opened with 8 data bits, 1 stop bit, no parity, no hardware
flow control, and no software flow control by default. This works fine for
many real devices and many faux serial devices including usb-to-serial
converters and bluetooth serial ports.
You may Read() and Write() simulantiously on the same connection (from
different goroutines).
Example usage:
package main
import (
"github.com/tarm/goserial"
"log"
)
func main() {
c := &serial.Config{Name: "COM5", Baud: 115200}
s, err := serial.OpenPort(c)
if err != nil {
log.Fatal(err)
}
n, err := s.Write([]byte("test"))
if err != nil {
log.Fatal(err)
}
buf := make([]byte, 128)
n, err = s.Read(buf)
if err != nil {
log.Fatal(err)
}
log.Print("%q", buf[:n])
}
*/
package goserial
import (
"errors"
"io"
)
var (
ErrConfigStopBits = errors.New("goserial config: bad number of stop bits")
ErrConfigByteSize = errors.New("goserial config: bad byte size")
ErrConfigParity = errors.New("goserial config: bad parity")
)
type ParityMode byte
const (
ParityNone = ParityMode(iota)
ParityEven
ParityOdd
)
type ByteSize byte
const (
Byte8 = ByteSize(iota)
Byte5
Byte6
Byte7
)
type StopBits byte
const (
StopBits1 = StopBits(iota)
StopBits2
)
// Config contains the information needed to open a serial port.
//
// Currently few options are implemented, but more may be added in the
// future (patches welcome), so it is recommended that you create a
// new config addressing the fields by name rather than by order.
//
// For example:
//
// c0 := &serial.Config{Name: "COM45", Baud: 115200}
// or
// c1 := new(serial.Config)
// c1.Name = "/dev/tty.usbserial"
// c1.Baud = 115200
//
type Config struct {
Name string
Baud int
Size ByteSize
Parity ParityMode
StopBits StopBits
// RTSFlowControl bool
// DTRFlowControl bool
// XONFlowControl bool
CRLFTranslate bool // Ignored on Windows.
// TimeoutStuff int
}
func (c *Config) check() error {
switch c.Size {
case Byte5, Byte6, Byte7, Byte8:
default:
return ErrConfigByteSize
}
switch c.StopBits {
case StopBits1, StopBits2:
default:
return ErrConfigParity
}
switch c.Parity {
case ParityNone, ParityEven, ParityOdd:
default:
return ErrConfigParity
}
return nil
}
// OpenPort opens a serial port with the specified configuration
func OpenPort(c *Config) (io.ReadWriteCloser, error) {
if err := c.check(); err != nil {
return nil, err
}
return openPort(c.Name, c)
}
// func Flush()
// func SendBreak()
// func RegisterBreakHandler(func())
serial_windows.go
package goserial
import (
"fmt"
"io"
"os"
"sync"
"syscall"
"unsafe"
)
type serialPort struct {
f *os.File
fd syscall.Handle
rl sync.Mutex
wl sync.Mutex
ro *syscall.Overlapped
wo *syscall.Overlapped
}
type structDCB struct {
DCBlength, BaudRate uint32
flags [4]byte
wReserved, XonLim, XoffLim uint16
ByteSize, Parity, StopBits byte
XonChar, XoffChar, ErrorChar, EofChar, EvtChar byte
wReserved1 uint16
}
type structTimeouts struct {
ReadIntervalTimeout uint32
ReadTotalTimeoutMultiplier uint32
ReadTotalTimeoutConstant uint32
WriteTotalTimeoutMultiplier uint32
WriteTotalTimeoutConstant uint32
}
func openPort(name string, c *Config) (rwc io.ReadWriteCloser, err error) {
if len(name) > 0 && name[0]!= '\\' {
name = "\\\\.\\"+ name
}
h, err := syscall.CreateFile(syscall.StringToUTF16Ptr(name),
syscall.GENERIC_READ|syscall.GENERIC_WRITE,
0,
nil,
syscall.OPEN_EXISTING,
syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED,
0)
if err != nil {
return nil, err
}
f := os.NewFile(uintptr(h), name)
defer func() {
if err != nil {
f.Close()
}
}()
if err = setCommState(h, c); err != nil {
return
}
if err = setupComm(h, 64, 64); err != nil {
return
}
if err = setCommTimeouts(h); err != nil {
return
}
if err = setCommMask(h); err != nil {
return
}
ro, err := newOverlapped()
if err != nil {
return
}
wo, err := newOverlapped()
if err != nil {
return
}
port :=new(serialPort)
port.f = f
port.fd = h
port.ro = ro
port.wo = wo
return port, nil
}
func (p *serialPort) Close() error {
return p.f.Close()
}
func (p *serialPort) Write(buf []byte) (int, error) {
p.wl.Lock()
defer p.wl.Unlock()
if err := resetEvent(p.wo.HEvent); err != nil {
return 0, err
}
var n uint32
err := syscall.WriteFile(p.fd, buf,&n, p.wo)
if err != nil && err != syscall.ERROR_IO_PENDING {
return int(n), err
}
return getOverlappedResult(p.fd, p.wo)
}
func (p *serialPort) Read(buf []byte) (int, error) {
if p == nil || p.f == nil {
return 0, fmt.Errorf("Invalid port on read %v %v", p, p.f)
}
p.rl.Lock()
defer p.rl.Unlock()
if err := resetEvent(p.ro.HEvent); err != nil {
return 0, err
}
var done uint32
err := syscall.ReadFile(p.fd, buf,&done, p.ro)
if err != nil && err != syscall.ERROR_IO_PENDING {
return int(done), err
}
return getOverlappedResult(p.fd, p.ro)
}
var (
nSetCommState,
nSetCommTimeouts,
nSetCommMask,
nSetupComm,
nGetOverlappedResult,
nCreateEvent,
nResetEvent uintptr
)
func init() {
k32, err := syscall.LoadLibrary("kernel32.dll")
if err != nil {
panic("LoadLibrary " + err.Error())
}
defer syscall.FreeLibrary(k32)
nSetCommState = getProcAddr(k32, "SetCommState")
nSetCommTimeouts = getProcAddr(k32, "SetCommTimeouts")
nSetCommMask = getProcAddr(k32, "SetCommMask")
nSetupComm = getProcAddr(k32, "SetupComm")
nGetOverlappedResult = getProcAddr(k32, "GetOverlappedResult")
nCreateEvent = getProcAddr(k32, "CreateEventW")
nResetEvent = getProcAddr(k32, "ResetEvent")
}
func getProcAddr(lib syscall.Handle, name string) uintptr {
addr, err := syscall.GetProcAddress(lib, name)
if err != nil {
panic(name + " " + err.Error())
}
return addr
}
func setCommState(h syscall.Handle, c *Config) error {
var params structDCB
params.DCBlength = uint32(unsafe.Sizeof(params))
params.flags[0] = 0x01 // fBinary
params.flags[0] |= 0x10 // Assert DSR
params.BaudRate = uint32(c.Baud)
// Select byte size.
switch c.Size {
case Byte5:
params.ByteSize = 5
case Byte6:
params.ByteSize = 6
case Byte7:
params.ByteSize = 7
case Byte8:
params.ByteSize = 8
default:
panic(c.Size)
}
// Select parity mode.
switch c.Parity {
case ParityNone:
params.Parity = 0
case ParityEven:
params.Parity = 2
case ParityOdd:
params.Parity = 1
default:
panic(c.Parity)
}
// Selet stop bits.
switch c.StopBits {
case StopBits1:
params.StopBits = 0
case StopBits2:
params.StopBits = 2
default:
panic(c.StopBits)
}
r, _, err := syscall.Syscall(nSetCommState,2, uintptr(h), uintptr(unsafe.Pointer(¶ms)),0)
if r == 0 {
return err
}
return nil
}
func setCommTimeouts(h syscall.Handle) error {
var timeouts structTimeouts
const MAXDWORD = 1<<32- 1
timeouts.ReadIntervalTimeout = MAXDWORD
timeouts.ReadTotalTimeoutMultiplier = MAXDWORD
timeouts.ReadTotalTimeoutConstant = MAXDWORD- 1
/* From http://msdn.microsoft.com/en-us/library/aa363190(v=VS.85).aspx
For blocking I/O see below:
Remarks:
If an application sets ReadIntervalTimeout and
ReadTotalTimeoutMultiplier to MAXDWORD and sets
ReadTotalTimeoutConstant to a value greater than zero and
less than MAXDWORD, one of the following occurs when the
ReadFile function is called:
If there are any bytes in the input buffer, ReadFile returns
immediately with the bytes in the buffer.
If there are no bytes in the input buffer, ReadFile waits
until a byte arrives and then returns immediately.
If no bytes arrive within the time specified by
ReadTotalTimeoutConstant, ReadFile times out.
*/
r, _, err := syscall.Syscall(nSetCommTimeouts,2, uintptr(h), uintptr(unsafe.Pointer(&timeouts)),0)
if r == 0 {
return err
}
return nil
}
func setupComm(h syscall.Handle, in, out int) error {
r, _, err := syscall.Syscall(nSetupComm,3, uintptr(h), uintptr(in), uintptr(out))
if r == 0 {
return err
}
return nil
}
func setCommMask(h syscall.Handle) error {
const EV_RXCHAR = 0x0001
r, _, err := syscall.Syscall(nSetCommMask,2, uintptr(h), EV_RXCHAR, 0)
if r == 0 {
return err
}
return nil
}
func resetEvent(h syscall.Handle) error {
r, _, err := syscall.Syscall(nResetEvent,1, uintptr(h), 0,0)
if r == 0 {
return err
}
return nil
}
func newOverlapped() (*syscall.Overlapped, error) {
var overlapped syscall.Overlapped
r, _, err := syscall.Syscall6(nCreateEvent,4, 0, 1, 0, 0,0, 0)
if r == 0 {
return nil, err
}
overlapped.HEvent = syscall.Handle(r)
return &overlapped, nil
}
func getOverlappedResult(h syscall.Handle, overlapped *syscall.Overlapped) (int, error) {
var n int
r, _, err := syscall.Syscall6(nGetOverlappedResult,4,
uintptr(h),
uintptr(unsafe.Pointer(overlapped)),
uintptr(unsafe.Pointer(&n)), 1, 0, 0)
if r == 0 {
return n, err
}
return n, nil
}
问题的解
atsender.go
import (
"./goserial"
"log"
"io"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
comname :="COM51"
atcommand :="at\r\n"
baud :=115200
if len(os.Args) > 1 {
comname = os.Args[1]
}
if len(os.Args) > 2 {
atcommand = checkAtformat(os.Args[2])
baud = 115200
}
if len(os.Args) > 3 {
baud,_ = strconv.Atoi(os.Args[3])
}
fmt.Printf("Com: %s Baud: %d AT: %s ",comname,baud ,atcommand)
s,_ := openCom(comname ,baud)//"COM51",115200
_, err := s.Write([]byte(atcommand))
if err != nil {
log.Fatal(err)
}
receiveCom(s)
s.Close()
}
func openCom(name string,baud int)(io.ReadWriteCloser, error){
c :=&goserial.Config{Name: name, Baud: baud}
s, err := goserial.OpenPort(c)
if err != nil {
log.Fatal(err)
}
return s,nil
}
func checkAtformat(in string)(string){
if strings.Contains(in,"\r\n") {
return in
}else {
return in + "\r\n"
}
return ""
}
func receiveCom(s io.ReadWriteCloser){
buf := make([]byte,128)
for{
n, err := s.Read(buf)
if err != nil {
log.Fatal(err)
}
strResult := string(buf[:n])
fmt.Println(strResult)
if strings.Contains(strResult,"OK\r\n"){
break
}
}
}
辅助工具
sleep.go
package main
import (
"fmt"
"os"
"strconv"
"time"
)
func main() {
if len(os.Args) > 1 {
count, _ := strconv.Atoi(os.Args[1])
// t0 := time.Now()
go promtString("*",count)
time.Sleep(time.Duration(count) * time.Second)
// t1 := time.Now()
// fmt.Printf("\nThe call took %v to run.\n", t1.Sub(t0))
fmt.Println()
} else {
fmt.Println("Sleep count Seconds.\nUsage: sleep [count]")
}
}
func promtString(str string, count int) {
for i := 0; i< count; i++ {
fmt.Print(str)
time.Sleep(time.Second)
}
}
应用脚本
for %%i in (1,2,3,4)do call :Call_Case
:Call_Case
atsender COM51 atd10086;
sleep 10
atsender COM51 at+chup
sleep 5
goto :eof
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。