You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 08:30:03