Mio Blog

do while

使用 Golang 进行串口通讯

本文为使用golang实现串口通讯以及串口管理的示例。

Serial包使用:https://pkg.go.dev/github.com/tarm/serial

Golang版本:1.19

测试环境:Windows10 22H2、CentOS9 Stream、Ubuntu Server 22.10、OpenEuler 22.09


  1. 环境创建

新建项目文件夹。

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

  1. 基本通讯
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
  1. 串口间通讯

虽然串口基本是只用于与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

发表评论

电子邮件地址不会被公开。 必填项已用*标注