Skip to content

Instantly share code, notes, and snippets.

@gabonator
Last active June 22, 2016 13:20
Show Gist options
  • Save gabonator/7150910 to your computer and use it in GitHub Desktop.
Save gabonator/7150910 to your computer and use it in GitHub Desktop.
Garmin HUD protocol
CString Hex(int n)
{
CString str;
str.Format( _T("%02x"), n );
return str;
}
void SendHud(unsigned char* pBuf, int nLen)
{
CArray<int> arrPacket;
arrPacket.Add( 0x10 );
arrPacket.Add( 0x7b );
arrPacket.Add( nLen+6 );
arrPacket.Add( nLen );
arrPacket.Add( 0x00 );
arrPacket.Add( 0x00 );
arrPacket.Add( 0x00 );
arrPacket.Add( 0x55 );
arrPacket.Add( 0x15 );
for ( int i=0; i<nLen; i++)
arrPacket.Add( pBuf[i] );
//crc
unsigned int nCrc = 0;
for ( int i=1; i<arrPacket.GetSize(); i++)
nCrc += arrPacket[i];
nCrc = (-(int)nCrc) & 0xff;
arrPacket.Add( nCrc );
arrPacket.Add( 0x10 );
arrPacket.Add( 0x03 );
CString strPacket;
for ( int i=0; i<arrPacket.GetSize(); i++)
{
strPacket += Hex( arrPacket[i] );
// stuffing
if ( i > 0 && i < arrPacket.GetSize()-2 && arrPacket[i] == 0x10 )
strPacket += Hex( arrPacket[i] );
}
bt.Send( strPacket );
}
int Digit(int n)
{
n = n % 10;
if ( n == 0 )
return 10;
return n;
}
void SetTime(int nH, int nM)
{
bool bColon = true;
bool bFlag = false;
bool bTraffic = true;
unsigned char arr[] = {0x05,
bTraffic ? 0xff : 0x00,
Digit(nH/10), Digit(nH), bColon ? 0xff : 0x00,
Digit(nM/10), Digit(nM), bFlag ? 0x00 : 0xff, 0x00};
SendHud(arr, sizeof(arr));
}
void SetDistance(int nDist)
{
enum {
None = 0,
Metres = 1,
Kilometres = 3,
Miles = 5
} units = Metres;
bool bDecimal = false;
unsigned char arr[] = {0x03,
Digit(nDist/1000), Digit(nDist/100), Digit(nDist/10), bDecimal ? 0xff : 0x00, Digit(nDist), (int)units};
SendHud(arr, sizeof(arr));
}
void SetDirection(int nDir)
{
enum {
SharpRight = 0x02,
Right = 0x04,
EasyRight = 0x08,
Straight = 0x10,
EasyLeft = 0x20,
Left = 0x40,
SharpLeft = 0x80
} eOutAngle = EasyRight;
unsigned char arr[] = {0x01, 0x01, 0x00, (int)eOutAngle};
SendHud(arr, sizeof(arr));
}
void SetLanes( CString strLanes )
{
for (int i=strLanes.GetLength(); i < 6; i++ )
if (i&1)
strLanes = _T(" ") + strLanes;
else
strLanes = strLanes + _T(" ");
int nOutline = 0, nArrow = 0;
for ( int i=0; i<6; i++)
{
char ch = (char)strLanes[i];
if (ch == '0' || ch == '1')
nOutline |= 1<<(6-i);
if (ch == '1')
nArrow |= 1<<(6-i);
}
unsigned char arr[] = {0x02, nOutline, nArrow};
SendHud(arr, sizeof(arr));
}
void SetSpeedWarning(int nSpeed, int nLimit)
{
bool bSpeeding = true;
bool bIcon = true;
unsigned char arr[] = {0x06,
(nSpeed/100)%10, Digit(nSpeed/10), Digit(nSpeed), 0xff,
(nLimit/100)%10, Digit(nLimit/10), Digit(nLimit), bSpeeding ? 0xff : 0x00, bIcon ? 0xff : 0x00};
SendHud(arr, sizeof(arr));
}
void ShowCameraIcon()
{
unsigned char arr[] = {0x04, 0x01};
SendHud( arr, sizeof(arr) );
}
void ShowGpsLabel()
{
unsigned char arr[] = {0x07, 0x01};
SendHud( arr, sizeof(arr) );
}
int ZD(int n)
{
if (n==0) return 10;
return n;
}
void _SetTime(int nHr, int nMin)
{
CArray<int>arrPacket;
// preamble
static unsigned char a =0;
arrPacket.Add(0x10); arrPacket.Add(0x7b); arrPacket.Add(0x0f); arrPacket.Add(0x09); arrPacket.Add(0x00); arrPacket.Add(0x00); arrPacket.Add(0x00);
arrPacket.Add(0x55); arrPacket.Add(0x15); arrPacket.Add(0x05); arrPacket.Add(0x00);
arrPacket.Add((nHr/10)%10); arrPacket.Add(ZD(nHr%10)); arrPacket.Add(0xff); arrPacket.Add(ZD((nMin/10)%10)); arrPacket.Add(ZD(nMin%10)); arrPacket.Add(0x00); arrPacket.Add(0xff);
//\10\7b\0f\09\00\00\00\55\15\05\00\00\08\ff\01\0a\00\ff\ed\10\03
// crc
int nCrc = 0;
for (int i=0; i<arrPacket.GetSize(); i++) nCrc += arrPacket[i];
nCrc = (0x10+(-nCrc))&0xff;
arrPacket.Add(nCrc);
// terminator
arrPacket.Add(0x10); arrPacket.Add(0x03);
CString strBuf;
for (int i=0; i<arrPacket.GetSize(); i++) strBuf += CString::FormatInline( _T("%02x"), arrPacket[i] );
CString strRequest = CString::FormatInline( _T("$JS: Driver.HudRequest(\"%s\");\n"), strBuf);
CLowDevice::DeviceTerminalSend( strRequest );
}
void _SetDistance(int nDistance )
{
CArray<int>arrPacket;
// preamble
arrPacket.Add(0x10); arrPacket.Add(0x7b); arrPacket.Add(0x0d); arrPacket.Add(0x07); arrPacket.Add(0x00); arrPacket.Add(0x00); arrPacket.Add(0x00); arrPacket.Add(0x55); arrPacket.Add(0x15); arrPacket.Add(0x03); arrPacket.Add(0x00);
// numeric
if ( nDistance < 100 && nDistance >= 0 )
{
arrPacket.Add(0x00);arrPacket.Add(nDistance/10);arrPacket.Add(0x00);arrPacket.Add(0x0a);arrPacket.Add(0x01);
}
if ( nDistance < 1000 && nDistance >= 100 )
{
arrPacket.Add(nDistance/100);arrPacket.Add(_ZD((nDistance/10)%10));arrPacket.Add(/*(nDistance/10)%10*/ 0x00);arrPacket.Add(/*0x0a*/ _ZD(nDistance%10));arrPacket.Add(0x01);
}
if ( nDistance < 10000 && nDistance >= 1000 )
{
int nZ = (nDistance / 10000)%10;
int nA = (nDistance / 1000)%10;
int nB = (nDistance / 100)% 10;
if (nB == 0) nB = 10; // desatinna bodka // jednotky (3=km, 5=mi)
arrPacket.Add(nZ);arrPacket.Add(nA);arrPacket.Add(0xff);arrPacket.Add(nB);arrPacket.Add(0x03);
}
// crc
int nCrc = 0;
for (int i=1; i<arrPacket.GetSize(); i++) nCrc += arrPacket[i];
nCrc = (-nCrc)&0xff;
arrPacket.Add(nCrc);
// terminator
arrPacket.Add(0x10); arrPacket.Add(0x03);
CString strBuf;
for (int i=0; i<arrPacket.GetSize(); i++) strBuf += CString::FormatInline( _T("%02x"), arrPacket[i] );
CString strRequest = CString::FormatInline( _T("$JS: Driver.HudRequest(\"%s\");\n"), strBuf);
CLowDevice::DeviceTerminalSend( strRequest );
}
void _SetDirection(int nDir)
{
CArray<int>arrPacket;
// nDistance = 900;
// preable
static unsigned char a =0;
arrPacket.Add(0x10); arrPacket.Add(0x7b); arrPacket.Add(0x0a); arrPacket.Add(0x04); arrPacket.Add(0x00); arrPacket.Add(0x00); arrPacket.Add(0x00);
arrPacket.Add(0x55); arrPacket.Add(0x15); arrPacket.Add(0x01); arrPacket.Add(0x01);
//arrPacket.Add(0x41); arrPacket.Add(0x40); // dolava
//arrPacket.Add(0x21); arrPacket.Add(0x20); // trochu dolava
//arrPacket.Add(0x05); arrPacket.Add(0x80); // ostro dolava
//arrPacket.Add(0x05); arrPacket.Add(0x04); // doprava
//arrPacket.Add(0x11); arrPacket.Add(0x10); arrPacket.Add(0x10); // rovno
switch (nDir)
{
case CJunctionEntry::DirectionLeft:
case CJunctionEntry::DirectionExitLeft:arrPacket.Add(0x41); arrPacket.Add(0x40); break;
//case CJunctionEntry::DirectionSharpLeft:
case CJunctionEntry::DirectionEasyLeft: arrPacket.Add(0x21); arrPacket.Add(0x20); break;
//case CJunctionEntry::DirectionEnd:
//case CJunctionEntry::DirectionOutOfRoute:
case CJunctionEntry::DirectionExitRight:
case CJunctionEntry::DirectionEasyRight:
case CJunctionEntry::DirectionSharpRight:
case CJunctionEntry::DirectionRight: arrPacket.Add(0x05); arrPacket.Add(0x04); break;
case CJunctionEntry::DirectionSharpLeft: arrPacket.Add(0x05); arrPacket.Add(0x80); break;
case CJunctionEntry::DirectionKeepLeft:
case CJunctionEntry::DirectionKeepRight:
case CJunctionEntry::DirectionFollow:
case CJunctionEntry::DirectionStraight: arrPacket.Add(0x11); arrPacket.Add(0x10); arrPacket.Add(0x10); break;
default:
return;
// arrPacket.Add(0x00); arrPacket.Add(0x00);
}
/*
camera:
arrPacket.Add(0x10); arrPacket.Add(0x7b); arrPacket.Add(0x0a); arrPacket.Add(0x04); arrPacket.Add(0x00); arrPacket.Add(0x00); arrPacket.Add(0x00);
arrPacket.Add(0x55); arrPacket.Add(0x15);
arrPacket.Add(0x04); arrPacket.Add(0x01); arrPacket.Add(0x11); arrPacket.Add(0x10); arrPacket.Add(0x10);
01 ** 11 10 10: priamo, horna sipka, rb 180, pravy finish
** 01 11 10 10: ikony, GPS
*/
// crc
int nCrc = 0;
for (int i=0; i<arrPacket.GetSize(); i++) nCrc += arrPacket[i];
if ( arrPacket.GetSize() == 14 )
nCrc = (0x20+(-nCrc))&0xff;
else
nCrc = (0x10+(-nCrc))&0xff;
arrPacket.Add(nCrc);
// terminator
arrPacket.Add(0x10); arrPacket.Add(0x03);
CString strBuf;
for (int i=0; i<arrPacket.GetSize(); i++) strBuf += CString::FormatInline( _T("%02x"), arrPacket[i] );
CString strRequest = CString::FormatInline( _T("$JS: Driver.HudRequest(\"%s\");\n"), strBuf);
CLowDevice::DeviceTerminalSend( strRequest );
}
void _SetLanes( CString strMask )
{ BYTE bA = 0x15, bB = 0x02;
bA = 0;
bB = 0;
for ( int i=0; i<6; i++)
{
char ch = strMask[i];
if (ch == '0' || ch == '1')
bA |= 1<<(6-i);
if (ch == '1')
bB |= 1<<(6-i);
}
//\10\7b\09\03\00\00\00\55\15\02\1c\04\ed\10\03
// 10 7b 09 03 00 00 00 55 1c 04 7e 0e 78 10 03
CArray<int>arrPacket;
// preable
arrPacket.Add(0x10); arrPacket.Add(0x7b); arrPacket.Add(0x09); arrPacket.Add(0x03); arrPacket.Add(0x00); arrPacket.Add(0x00); arrPacket.Add(0x00);
arrPacket.Add(0x55); arrPacket.Add(0x15); arrPacket.Add(0x02);
arrPacket.Add(bA); arrPacket.Add(bB);
// crc
int nCrc = 0;
for (int i=0; i<arrPacket.GetSize(); i++) nCrc += arrPacket[i];
nCrc = (0x20+0xf0+(-nCrc))&0xff;
arrPacket.Add(nCrc);
// terminator
arrPacket.Add(0x10); arrPacket.Add(0x03);
CString strBuf;
for (int i=0; i<arrPacket.GetSize(); i++) strBuf += CString::FormatInline( _T("%02x"), arrPacket[i] );
CString strRequest = CString::FormatInline( _T("$JS: Driver.HudRequest(\"%s\");\n"), strBuf);
CLowDevice::DeviceTerminalSend( strRequest );
}
void _SetSpeedWarn(int nSpd, int nLim, BOOL32 bWarn)
{
if ( nSpd == 0 && nLim == 0 )
return;
//\10\7b\10\10\0a\00\00\00\55\15\06\00\05\0a\ff\00\05\0a\00\ff\df\10\03
//107b10100a00000055150600050aff00050a00ffdf1003
CArray<int>arrPacket;
arrPacket.Add(0x10); arrPacket.Add(0x7b); arrPacket.Add(0x10); arrPacket.Add(0x10); arrPacket.Add(0x0a); arrPacket.Add(0x00); arrPacket.Add(0x00); arrPacket.Add(0x00);
arrPacket.Add(0x55); arrPacket.Add(0x15);
arrPacket.Add(0x06); arrPacket.Add((nSpd/100)%10); arrPacket.Add((nSpd/10)%10); arrPacket.Add(0x0a);
arrPacket.Add(0xff); arrPacket.Add((nLim/100)%10); arrPacket.Add((nLim/10)%10); arrPacket.Add(0x0a);
arrPacket.Add(bWarn); arrPacket.Add(0xff);
// crc
int nCrc = 0;
for (int i=0; i<arrPacket.GetSize(); i++) nCrc += arrPacket[i];
nCrc = (0x20+(-nCrc))&0xff;
arrPacket.Add(nCrc);
// terminator
arrPacket.Add(0x10); arrPacket.Add(0x03);
CString strBuf;
for (int i=0; i<arrPacket.GetSize(); i++) strBuf += CString::FormatInline( _T("%02x"), arrPacket[i] );
CString strRequest = CString::FormatInline( _T("$JS: Driver.HudRequest(\"%s\");\n"), strBuf);
CLowDevice::DeviceTerminalSend( strRequest );
}
@gabonator
Copy link
Author

packet structure: { 0x10 0x47 (packet_len: 1 byte) (data_len: 4 bytes) data: { 55 15 ... } (crc: 1 byte) 0x10 0x03 }
packet_len = data_len+6

set arrival time "Hh:Mm": 05 00 (HH) (hh) ff (MM) (mm) 00 ff
set distance "X0 m": 03 00 00 (XX) 00 0a 01
set distance "XYZ m": 03 00 (XX) (YY) 00 (ZZ) 01
set distance "XY.Z km": 03 00 (XX) (YY) ff (ZZ) 03
set distance "XY.Z mi": 03 00 (XX) (YY) ff (ZZ) 05
set direction XY: 01 01 (XX) (YY)
left 41 40
right 05 04
sharp right 05 80
straight 11 10

set lanes: XY: 02 (XX) (YY)
bits of XX: "dots/outline/outline/outline/outline/outline/outline/dots"
bits of YY: "none/arrow/arrow/arrow/arrow/arrow/arrow/none"

set speed warning XYZ/PQR kmh: 06 (XX) (YY) (ZZ) ff (PP) (QQ) (RR) 00 ff
set speed warning with alert: 06 (XX) (YY) (ZZ) ff (PP) (QQ) (RR) 01 ff

two consecutive 10 10 are caused by stuffing, do not include both in CRC calculation
for displaying zero digit use 0x0A instead of 0x00 which shows an empty digit

@gabonator
Copy link
Author

sample sniffed frames:
10 7b 0d 07 00 00 00 55 15 03 00 00 09 00 0a 01 f0 10 03
10 7b 0d 07 00 00 00 55 15 03 00 00 08 00 0a 01 f1 10 03
10 7b 0d 07 00 00 00 55 15 03 00 00 07 00 0a 01 f2 10 03
10 7b 08 02 00 00 00 55 15 07 00 0a 10 03
10 7b 08 02 00 00 00 55 15 04 00 0d 10 03
10 7b 0a 04 00 00 00 55 15 01 01 41 40 8a 10 03
10 7b 10 10 0a 00 00 00 55 15 06 00 05 0a ff 00 05 0a 00 ff df 10 03

@gabonator
Copy link
Author

description of garmin protocol, stuffing and crc:
http://www8.garmin.com/support/commProtocol.html

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment