本文为使用golang实现串口通讯以及串口管理的示例。
Serial包使用:https://pkg.go.dev/github.com/tarm/serial
Golang版本:1.19
测试环境:Windows10 22H2、CentOS9 Stream、Ubuntu Server 22.10、OpenEuler 22.09
- 环境创建
新建项目文件夹。
mkdir serial_prj
cd serial_prj
初始化go mod。
go mod init serial_prj
初始化go main。
touch main.go
下载依赖包。
go mod download github.com/tarm/serial
- 基本通讯
package main import ( "fmt" "github.com/tarm/serial" "log" "time" ) func main() { config := &serial.Config{ //串口配置 Name: "COM7", //主机串口 win为COM* linux多为 /dev/ttyS* Baud: 9600, //比特率 ReadTimeout: 300 * time.Millisecond, //一次读操作的最大用时 Size: 8, //数据位 Parity: 0, //奇偶校验配置 0为N 不进行校验 StopBits: 1, //停止位 默认为1 } //打开串口 port, err := serial.OpenPort(config) if err != nil { //如主机无此串口或者串口已被其他应用打开时,会报错。 log.Println(fmt.Sprintf("Open serial port error:%s", err.Error())) return } //传输数据 可以是任意的uint8数组,即[]byte 可以是json、bytes字符串等等。 var writeData = []byte{'h', 'e', 'l', 'l', 'o'} i, err := port.Write(writeData) //i为成功写入的字节长度 if err != nil { log.Println(fmt.Sprintf( "Send data(%v) to %s failed with error:%s", writeData, config.Name, err.Error(), )) return } log.Println(fmt.Sprintf( "Send %dbytes data(%v) to %s", i, writeData, config.Name, )) i = 1 //进入读循环 var readData = make([]byte, 0) for i != 0 && err == nil { //循环读取到数据传完或者出现错误 var buf = make([]byte, 256) //使用buf暂存读到的数据 一次读的最大尺寸为数组容量 i, err = port.Read(buf) readData = append(readData, buf[:i]...) //添加暂存的数据到readData } log.Println(fmt.Sprintf( "Read %dbytes data(%v) from %s", len(readData), readData, config.Name, )) }
控制台输出:
2022/11/25 17:02:20 Send 5bytes data([104 101 108 108 111]) to COM7 2022/11/25 17:02:21 Read 0bytes data([]) from COM7
- 串口间通讯
虽然串口基本是只用于与Modbus等通讯协议的设备进行通讯,但是用于测试可行性,以下为两端串口通讯的示例。
使用虚拟串口软件创建并连接虚拟串口。
基本的通讯以及串口对象管理示例
*注:仅仅为示例,实际运用应使用携程配合通道监听串口信息为最佳实例。
package main import ( "fmt" "github.com/tarm/serial" "log" "sync" "time" ) // UartSerialObjs 存放串口对象的串口池 var UartSerialObjs sync.Map // UartSerial 串口对象类型 type UartSerial struct { Mutex sync.RWMutex //锁 Waiters sync.WaitGroup //等待 Status bool //串口存活状态 Port *serial.Port //串口 Config *serial.Config //串口配置 } // Create 方法 创建串口并存储对象 func (u *UartSerial) Create() error { port, err := serial.OpenPort(u.Config) if err != nil { return err } u.Port = port u.Status = true //存储对象到池中 UartSerialObjs.Store(u.Config.Name, u) return err } func main() { var sender = new(UartSerial) var receiver = new(UartSerial) sender.Waiters = sync.WaitGroup{} receiver.Waiters = sync.WaitGroup{} sender.Config = &serial.Config{ //串口配置 Name: "COM5", //主机串口 win为COM* linux多为 /dev/ttyS* Baud: 115200, //比特率 ReadTimeout: 300 * time.Millisecond, //一次读操作的最大用时 Size: 8, //数据位 Parity: 0, //奇偶校验配置 0为N 不进行校验 StopBits: 1, //停止位 默认为1 } receiver.Config = &serial.Config{ //串口配置 Name: "COM6", //主机串口 win为COM* linux多为 /dev/ttyS* Baud: 115200, //比特率 ReadTimeout: 300 * time.Millisecond, //一次读操作的最大用时 Size: 8, //数据位 Parity: 0, //奇偶校验配置 0为N 不进行校验 StopBits: 1, //停止位 默认为1 } //打开串口 err := sender.Create() if err != nil { //如主机无此串口或者串口已被其他应用打开时,会报错。 log.Println(fmt.Sprintf("Open serial port error:%s", err.Error())) return } //打开串口 err = receiver.Create() if err != nil { //如主机无此串口或者串口已被其他应用打开时,会报错。 log.Println(fmt.Sprintf("Open serial port error:%s", err.Error())) return } receiver.Waiters.Add(1) go func() { defer receiver.Waiters.Done() for { receiver.Mutex.Lock() var i int i = 1 //进入读循环 var readData = make([]byte, 0) for i != 0 && err == nil { //循环读取到数据传完或者出现错误 var buf = make([]byte, 256) //使用buf暂存读到的数据 一次读的最大尺寸为数组容量 i, err = receiver.Port.Read(buf) readData = append(readData, buf[:i]...) //添加暂存的数据到readData } if len(readData) == 0 { continue } log.Println(fmt.Sprintf( "Read %dbytes data(%v) from %s", len(readData), readData, receiver.Config.Name, )) var writeData = []byte{'h', 'i', ',', 't', 'h', 'e', 'r', 'e'} _, err = receiver.Port.Write(writeData) //i为成功写入的字节长度 if err != nil { log.Println(fmt.Sprintf( "Send data(%v) to %s failed with error:%s", writeData, receiver.Config.Name, err.Error(), )) return } receiver.Mutex.Unlock() return } }() //发送和监听返回 sender.Waiters.Add(1) go func() { defer sender.Waiters.Done() for { sender.Mutex.Lock() //传输数据 可以是任意的uint8数组 即[]byte 可以是json、bytes字符串等等。 var i int var writeData = []byte{'h', 'e', 'l', 'l', 'o'} _ = sender.Port.Flush() i, err = sender.Port.Write(writeData) //i为成功写入的字节长度 if err != nil { log.Println(fmt.Sprintf( "Send data(%v) to %s failed with error:%s", writeData, sender.Config.Name, err.Error(), )) return } log.Println(fmt.Sprintf( "Send %dbytes data(%v) to %s", i, writeData, sender.Config.Name, )) i = 1 //进入读循环 var readData = make([]byte, 0) for i != 0 && err == nil { //循环读取到数据传完或者出现错误 var buf = make([]byte, 256) //使用buf暂存读到的数据 一次读的最大尺寸为数组容量 i, err = sender.Port.Read(buf) readData = append(readData, buf[:i]...) //添加暂存的数据到readData } log.Println(fmt.Sprintf( "Read %dbytes data(%v) from %s", len(readData), readData, sender.Config.Name, )) sender.Mutex.Unlock() if readData != nil { return } time.Sleep(time.Second * 3) } }() sender.Waiters.Wait() receiver.Waiters.Wait() }
控制台输出:
2022/11/25 18:10:13 Send 5bytes data([104 101 108 108 111]) to COM5 2022/11/25 18:10:13 Read 5bytes data([104 101 108 108 111]) from COM6 2022/11/25 18:10:13 Read 8bytes data([104 105 44 116 104 101 114 101]) from COM5