如何在Flutter中基于GT06协议向Traccar GT06设备发送GPS数据?
解决Flutter向Traccar GT06设备(服务器)发送GPS坐标的问题
你当前的代码直接发送字符串"Data"是无效的,因为GT06设备使用二进制协议与Traccar服务器通信,必须严格按照协议格式构造二进制数据包才能被服务器识别。
一、GT06协议核心结构
GT06的通信帧是固定格式的二进制数据,结构如下:
- 帧头:
0x78 0x78 - 帧长度:2字节,代表后续「命令字+数据体+校验位」的总字节数
- 命令字:1字节,位置上报使用
0x02 - 数据体:对应命令的具体参数(IMEI、坐标、时间等)
- 校验位:1字节,从「帧长度」到「数据体」所有字节的异或和
- 帧尾:
0x0D 0x0A
二、位置上报(0x02命令)的数据体格式
针对位置上报,数据体需要包含以下字段(按顺序):
- 设备IMEI:10字节(BCD码,每个字节对应IMEI的一位数字)
- 报警状态:2字节,
0x0000表示无报警 - 状态位:2字节,比如
0x0008表示GPS定位有效(参考GT06协议定义的比特位) - 纬度:4字节,将纬度值乘以
10^6转为整数,大端字节序;最高位为1表示南纬,0表示北纬 - 经度:4字节,将经度值乘以
10^6转为整数,大端字节序;最高位为1表示西经,0表示东经 - 海拔:2字节,单位米(可设为0)
- 速度:2字节,单位km/h(需将Geolocator返回的m/s转为km/h)
- 方向:2字节,0-359度(来自Geolocator的heading值)
- 时间:6字节(BCD码,格式为「后两位年、月、日、时、分、秒」)
三、Flutter实现代码
1. 导入依赖包
import 'dart:io'; import 'dart:typed_data'; import 'package:geolocator/geolocator.dart';
2. 构造GT06位置数据包
Uint8List buildGT06LocationPacket(String imei, Position position) { // 处理纬度:转度*1e6,添加南北纬标识 int latInt = (position.latitude.abs() * 1000000).toInt(); if (position.latitude < 0) latInt |= 0x80000000; // 南纬设最高位为1 Uint8List latBytes = Uint8List(4); latBytes[0] = (latInt >> 24) & 0xFF; latBytes[1] = (latInt >> 16) & 0xFF; latBytes[2] = (latInt >> 8) & 0xFF; latBytes[3] = latInt & 0xFF; // 处理经度:转度*1e6,添加东西经标识 int lngInt = (position.longitude.abs() * 1000000).toInt(); if (position.longitude < 0) lngInt |= 0x80000000; // 西经设最高位为1 Uint8List lngBytes = Uint8List(4); lngBytes[0] = (lngInt >> 24) & 0xFF; lngBytes[1] = (lngInt >> 16) & 0xFF; lngBytes[2] = (lngInt >> 8) & 0xFF; lngBytes[3] = lngInt & 0xFF; // 处理IMEI为BCD码 Uint8List imeiBytes = Uint8List(10); for (int i = 0; i < 10; i++) { imeiBytes[i] = int.parse(imei[i]); } // 固定字段:报警状态、状态位、海拔 Uint8List alarmStatus = Uint8List.fromList([0x00, 0x00]); Uint8List status = Uint8List.fromList([0x00, 0x08]); // GPS定位有效 Uint8List altitude = Uint8List.fromList([0x00, 0x00]); // 处理速度:m/s转km/h int speed = (position.speed * 3.6).toInt(); Uint8List speedBytes = Uint8List(2); speedBytes[0] = (speed >> 8) & 0xFF; speedBytes[1] = speed & 0xFF; // 处理方向 int heading = position.heading.toInt(); Uint8List headingBytes = Uint8List(2); headingBytes[0] = (heading >> 8) & 0xFF; headingBytes[1] = heading & 0xFF; // 处理时间为BCD码 DateTime now = DateTime.now(); Uint8List timeBytes = Uint8List(6); timeBytes[0] = int.parse(now.year.toString().substring(2)); timeBytes[1] = now.month; timeBytes[2] = now.day; timeBytes[3] = now.hour; timeBytes[4] = now.minute; timeBytes[5] = now.second; // 组装数据体 Uint8List body = Uint8List.fromList([ ...imeiBytes, ...alarmStatus, ...status, ...latBytes, ...lngBytes, ...altitude, ...speedBytes, ...headingBytes, ...timeBytes ]); // 命令字:位置上报 Uint8List cmd = Uint8List.fromList([0x02]); // 计算帧长度 int length = cmd.length + body.length + 1; // +1是校验位 Uint8List lengthBytes = Uint8List(2); lengthBytes[0] = (length >> 8) & 0xFF; lengthBytes[1] = length & 0xFF; // 计算校验位 int checksum = 0; for (byte in lengthBytes) checksum ^= byte; for (byte in cmd) checksum ^= byte; for (byte in body) checksum ^= byte; Uint8List checksumByte = Uint8List.fromList([checksum & 0xFF]); // 组装完整帧 return Uint8List.fromList([ 0x78, 0x78, ...lengthBytes, ...cmd, ...body, ...checksumByte, 0x0D, 0x0A ]); }
3. 发送数据包到Traccar服务器
void sendDataToGT06Device() async { try { final socket = await Socket.connect("3.108.199.67", 5023); print("已连接到服务器 ${socket.remoteAddress}:${socket.remotePort}"); // 获取当前GPS位置 Position position = await Geolocator.getCurrentPosition( desiredAccuracy: LocationAccuracy.best, ); // 替换为你的GT06设备真实IMEI(必须10位,且已在Traccar后台添加) String deviceImei = "1234567890"; // 构造并发送数据包 Uint8List packet = buildGT06LocationPacket(deviceImei, position); socket.add(packet); print("位置数据包已发送"); // 监听服务器响应 socket.listen( (data) => print("收到服务器响应: ${data.toList()}"), onDone: () { print("连接已关闭"); socket.destroy(); }, onError: (error) { print("连接错误: $error"); socket.destroy(); }, ); } catch (e) { print("执行错误: $e"); } }
四、关键注意事项
- 必须使用
socket.add()发送二进制数据,不能用socket.write()发送字符串 - IMEI必须是设备的真实10位号码,且已在Traccar服务器后台添加该设备,否则服务器会忽略数据包
- 坐标的符号位(南北纬/东西经)必须正确设置,否则服务器解析的位置会偏差
- 校验位计算必须准确,否则服务器会丢弃无效数据包
- 状态位的取值需参考GT06官方协议文档,根据实际情况调整
内容的提问来源于stack exchange,提问作者Vedant Kakade
相关产品推荐
相关产品推荐

