Teltonika GPS tracker protocol parser for Elixir.
Parses the binary protocols used by Teltonika GPS tracking devices (TAT141, FMB920, FMC130, TMT250, etc.) over TCP connections. Supports Codec 8, Codec 8 Extended, and Codec 16.
Quick Start
# Parse an IMEI handshake
{:ok, "352093085698206"} = TeltonikaCodec.parse_imei(data)
# Parse a data packet (auto-detects codec)
{:ok, records} = TeltonikaCodec.parse_packet(data)Protocol Overview
Teltonika devices communicate over TCP using a simple protocol:
- Device connects and sends IMEI as
<<length::16, imei::binary>> - Server responds with
0x01(accept) or0x00(reject) - Device sends data packets containing AVL records
- Server ACKs each packet with
<<record_count::32>> - Repeat from step 3
Supported Codecs
| Codec | ID | IO ID Size | IO Count Size | Variable IO | Generation Type |
|---|---|---|---|---|---|
| 8 | 0x08 | 1 byte | 1 byte | No | No |
| 8 Ext | 0x8E | 2 bytes | 2 bytes | Yes (NX) | No |
| 16 | 0x10 | 2 bytes | 1 byte | No | Yes |
Packet Structure
All codecs share the same outer frame:
<<0::32, data_length::32, data::binary, crc::32>>The data field starts with a codec ID byte, then N AVL records, then
a repeat of the record count for validation.
Summary
Functions
Builds an ACK response for the device.
Checks if a complete packet is available in the buffer.
Builds the IMEI accept response (0x01).
Builds the IMEI reject response (0x00).
Parses an IMEI handshake packet.
Parses a complete Codec 8/8E/16 data packet, auto-detecting the codec.
Types
@type avl_record() :: %{ altitude: non_neg_integer(), heading: non_neg_integer(), io_elements: map(), latitude: float(), longitude: float(), priority: priority(), satellites: non_neg_integer(), speed: non_neg_integer(), timestamp: DateTime.t() }
@type parse_result() :: {:ok, [avl_record()]} | {:error, atom()}
@type priority() :: :low | :high | :panic
Functions
@spec ack(non_neg_integer()) :: binary()
Builds an ACK response for the device.
The server must respond with the number of accepted data records as a 4-byte big-endian integer. Failure to ACK causes the device to retransmit, draining battery.
Examples
iex> TeltonikaCodec.ack(2)
<<0, 0, 0, 2>>
Checks if a complete packet is available in the buffer.
Returns {:complete, packet_binary, remaining_buffer} if a full packet
is available, :incomplete if more data is needed, or {:error, reason}.
Useful for accumulating TCP data until a full frame arrives.
Examples
iex> packet = Base.decode16!("000000000000002808010000016B40D9AD80010000000000000000000000000000000103021503010101425E100000010000F22A")
iex> {:complete, ^packet, <<>>} = TeltonikaCodec.check_packet(packet)
iex> TeltonikaCodec.check_packet(<<0, 0, 0>>)
:incomplete
@spec imei_accept() :: binary()
Builds the IMEI accept response (0x01).
@spec imei_reject() :: binary()
Builds the IMEI reject response (0x00).
Parses an IMEI handshake packet.
The device sends <<length::16, imei::binary-size(length)>> on connect.
Examples
iex> TeltonikaCodec.parse_imei(<<0, 15, "352093085698206">>)
{:ok, "352093085698206"}
iex> TeltonikaCodec.parse_imei(<<0, 5, "12345">>)
{:error, :invalid_imei_format}
@spec parse_packet(binary()) :: parse_result()
Parses a complete Codec 8/8E/16 data packet, auto-detecting the codec.
Returns {:ok, records} where records is a list of AVL record maps.
Examples
iex> packet = Base.decode16!("000000000000002808010000016B40D9AD80010000000000000000000000000000000103021503010101425E100000010000F22A")
iex> {:ok, [record]} = TeltonikaCodec.parse_packet(packet)
iex> record.priority
:high