TeltonikaCodec (TeltonikaCodec v0.1.0)

Copy Markdown View Source

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:

  1. Device connects and sends IMEI as <<length::16, imei::binary>>
  2. Server responds with 0x01 (accept) or 0x00 (reject)
  3. Device sends data packets containing AVL records
  4. Server ACKs each packet with <<record_count::32>>
  5. Repeat from step 3

Supported Codecs

CodecIDIO ID SizeIO Count SizeVariable IOGeneration Type
80x081 byte1 byteNoNo
8 Ext0x8E2 bytes2 bytesYes (NX)No
160x102 bytes1 byteNoYes

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

avl_record()

@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()
}

parse_result()

@type parse_result() :: {:ok, [avl_record()]} | {:error, atom()}

priority()

@type priority() :: :low | :high | :panic

Functions

ack(record_count)

@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>>

check_packet(buffer)

@spec check_packet(binary()) ::
  {:complete, binary(), binary()} | :incomplete | {:error, atom()}

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

imei_accept()

@spec imei_accept() :: binary()

Builds the IMEI accept response (0x01).

imei_reject()

@spec imei_reject() :: binary()

Builds the IMEI reject response (0x00).

parse_imei(data)

@spec parse_imei(binary()) :: {:ok, String.t()} | {:error, atom()}

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}

parse_packet(arg1)

@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