通过I2C与u-blox GPS通信的Golang实现方法
通过I2C与u-blox GPS通信的Golang实现方法 我一直在寻找通过I2C总线与u-blox M8系列GPS接收器进行二进制通信的Golang实现方案,但至今没有找到合适的。
我已经编写了一个使用USB端口和NMEA消息的实现,但担心这对我的应用来说会有些慢。
在阅读了u-blox的规格说明后(我使用的GPS是RY836AI板卡的一部分),我的印象是这种实现不会像普通的I2C总线通信那样简单,特别是如果使用二进制通信而不是冗长的NMEA消息。
在我将快速流逝的青春耗费在这个项目之前,我向各位求助。有人愿意接手吗?
致以最诚挚的问候
我正在使用 periph.io 项目进行 I2C 通信,既用于树莓派也在某种程度上用于 Rock64,因为我认为其背后的开发者非常致力于高质量和良好的架构。
iNav 项目确实很有趣,stratux 的实现也同样出色。
感谢!
更多关于通过I2C与u-blox GPS通信的Golang实现方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
看起来你可以在树莓派上使用这个库通过I2C进行通信。
我之前没有使用过这个库,但它是搜索"golang i2c"时的第一个结果 🙂
iNav(和Betaflight)是用于遥控飞行器(如四轴飞行器和飞翼)的飞行控制软件项目。你应该能够了解它们如何与u-blox GPS设备通信,并在Go中实现相应功能。参考iNav源代码:https://github.com/iNavFlight/inav/blob/912d131556302cf6ea67d59308e43dfe65ca8026/src/main/drivers/gps_i2cnav.c
你的平台是什么?如果你使用的是类似树莓派、Beaglebone或其他类似的"小型"开发板,可能会比较容易,因为你拥有硬件I2C接口,并且已经存在一些现成的代码。如果你使用的是普通PC,就需要一个硬件接口。最终FTDI应该有类似的产品,可以通过USB端口连接。然后他们提供驱动程序和示例,但你可能需要制作Golang绑定… (如果你在谷歌搜索"golang ftdi",会有几个结果,所以可能已经有现成的解决方案…)
[编辑:] 如果你使用的是Linux(我使用的是Windows),似乎会更容易:https://dave.cheney.net/2014/08/03/tinyterm-a-silly-terminal-emulator-written-in-go
以下是一个基于Go语言通过I2C与u-blox M8系列GPS接收器进行二进制通信的示例实现。该实现使用periph.io库来处理I2C通信,并遵循u-blox二进制协议(UBX协议)的结构。假设GPS设备已正确连接到I2C总线,且地址为0x42(u-blox设备的常见I2C地址)。
首先,确保安装必要的依赖:
go get periph.io/x/conn/v3/i2c
go get periph.io/x/host/v3
然后,创建Go文件(例如main.go)并实现以下代码:
package main
import (
"encoding/binary"
"fmt"
"log"
"time"
"periph.io/x/conn/v3/i2c"
"periph.io/x/conn/v3/i2c/i2creg"
"periph.io/x/host/v3"
)
const (
i2cBus = "1" // I2C总线编号,根据系统调整(如树莓派上常用1)
gpsAddress = 0x42 // u-blox GPS的I2C地址
ubxSync1 = 0xB5 // UBX协议同步字符1
ubxSync2 = 0x62 // UBX协议同步字符2
)
// UBXMessage 定义UBX消息结构
type UBXMessage struct {
Class uint8
ID uint8
Length uint16
Payload []byte
Checksum [2]byte
}
// calcChecksum 计算UBX消息的校验和
func calcChecksum(class, id uint8, length uint16, payload []byte) [2]byte {
var ckA, ckB uint8
ckA += class
ckB += ckA
ckA += id
ckB += ckA
ckA += uint8(length & 0xFF)
ckB += ckA
ckA += uint8(length >> 8)
ckB += ckA
for _, b := range payload {
ckA += b
ckB += ckA
}
return [2]byte{ckA, ckB}
}
// sendUBXMessage 通过I2C发送UBX消息
func sendUBXMessage(dev *i2c.Dev, msg UBXMessage) error {
// 构建消息字节序列
buf := make([]byte, 6+len(msg.Payload)+2) // 同步头(2) + 类(1) + ID(1) + 长度(2) + 载荷 + 校验和(2)
buf[0] = ubxSync1
buf[1] = ubxSync2
buf[2] = msg.Class
buf[3] = msg.ID
binary.LittleEndian.PutUint16(buf[4:6], msg.Length)
copy(buf[6:6+len(msg.Payload)], msg.Payload)
checksum := calcChecksum(msg.Class, msg.ID, msg.Length, msg.Payload)
buf[6+len(msg.Payload)] = checksum[0]
buf[6+len(msg.Payload)+1] = checksum[1]
// 通过I2C写入消息
_, err := dev.Write(buf)
return err
}
// readUBXMessage 从I2C读取UBX消息
func readUBXMessage(dev *i2c.Dev) (*UBXMessage, error) {
// 先读取头部以确定消息长度
header := make([]byte, 6)
if _, err := dev.Read(header); err != nil {
return nil, err
}
// 检查同步字符
if header[0] != ubxSync1 || header[1] != ubxSync2 {
return nil, fmt.Errorf("invalid sync characters")
}
class := header[2]
id := header[3]
length := binary.LittleEndian.Uint16(header[4:6])
// 读取载荷和校验和
payloadAndChecksum := make([]byte, length+2)
if _, err := dev.Read(payloadAndChecksum); err != nil {
return nil, err
}
payload := payloadAndChecksum[:length]
checksum := [2]byte{payloadAndChecksum[length], payloadAndChecksum[length+1]}
// 验证校验和
expectedChecksum := calcChecksum(class, id, length, payload)
if checksum != expectedChecksum {
return nil, fmt.Errorf("checksum mismatch")
}
return &UBXMessage{
Class: class,
ID: id,
Length: length,
Payload: payload,
}, nil
}
func main() {
// 初始化主机驱动
if _, err := host.Init(); err != nil {
log.Fatal(err)
}
// 打开I2C总线
bus, err := i2creg.Open(i2cBus)
if err != nil {
log.Fatal(err)
}
defer bus.Close()
// 创建设备实例
dev := &i2c.Dev{Bus: bus, Addr: gpsAddress}
// 示例:发送UBX-CFG-PRT命令配置I2C端口(具体载荷根据u-blox文档构建)
// 这里使用一个示例消息,实际使用时参考u-blox协议文档
msg := UBXMessage{
Class: 0x06, // CFG类
ID: 0x00, // PRT消息ID
Length: 20, // 假设载荷长度为20字节
Payload: make([]byte, 20), // 实际载荷需根据配置填充
}
if err := sendUBXMessage(dev, msg); err != nil {
log.Fatal("发送消息失败:", err)
}
fmt.Println("UBX消息发送成功")
// 等待并读取响应
time.Sleep(100 * time.Millisecond)
response, err := readUBXMessage(dev)
if err != nil {
log.Fatal("读取消息失败:", err)
}
fmt.Printf("收到响应: Class=0x%02X, ID=0x%02X, Length=%d\n", response.Class, response.ID, response.Length)
}
说明:
- I2C配置:代码使用
periph.io库处理I2C通信,需根据实际硬件调整总线编号(如树莓派上常用"1")。 - UBX协议:实现了UBX二进制消息的构建、发送和接收,包括同步字符、校验和计算。校验和计算遵循u-blox规范。
- 消息示例:示例中发送了一个UBX-CFG-PRT消息(端口配置),实际使用时需参考u-blox M8协议文档填充正确的载荷数据。
- 错误处理:包含基本的错误检查,如校验和验证。
此代码提供了一个基础框架,您需要根据具体需求调整消息类、ID和载荷。u-blox协议文档是必读的,以正确配置GPS模块。

