Packet encoding and decoding for the Feetech servo protocol.
Packet Format
Instruction packet (controller to servo):
Header | ID | Length | Instruction | Params | Checksum
0xFF 0xFF | 1 byte | 1 byte | 1 byte | N bytes | 1 byteResponse packet (servo to controller):
Header | ID | Length | Status | Params | Checksum
0xFF 0xFF | 1 byte | 1 byte | 1 byte | N bytes | 1 byteLength = number of parameters + 2 (for instruction/status + checksum) Checksum = ~(ID + Length + Instruction + Params) & 0xFF
Summary
Types
Memory address in control table
Parsed response from servo
Servo ID (0-253, or 254 for broadcast)
Functions
Builds an ACTION instruction packet.
Builds a PING instruction packet.
Builds a READ instruction packet.
Builds a RECOVERY instruction packet (factory reset).
Builds a REG_WRITE instruction packet.
Builds a RESET instruction packet (reset rotation count).
Builds a SYNC_READ instruction packet.
Builds a SYNC_WRITE instruction packet.
Builds a WRITE instruction packet.
Calculates the checksum for packet data.
Decodes a little-endian binary to an unsigned integer.
Decodes a little-endian binary to a signed integer (two's complement).
Decodes a sign-magnitude encoded integer.
Encodes an integer as little-endian binary.
Encodes a signed integer using sign-magnitude encoding.
Attempts to extract a complete packet from a binary buffer.
Parses a response packet from a servo.
Validates a packet's checksum.
Types
@type address() :: non_neg_integer()
Memory address in control table
@type response() :: %{id: servo_id(), status: non_neg_integer(), params: binary()}
Parsed response from servo
@type servo_id() :: 0..254
Servo ID (0-253, or 254 for broadcast)
Functions
Builds an ACTION instruction packet.
Triggers all buffered REG_WRITE commands. Typically sent to broadcast ID.
Builds a PING instruction packet.
PING queries the servo's status and returns a response even with broadcast ID.
Examples
iex> Feetech.Protocol.build_ping(1)
<<0xFF, 0xFF, 0x01, 0x02, 0x01, 0xFB>>
@spec build_read(servo_id(), address(), pos_integer()) :: binary()
Builds a READ instruction packet.
Examples
iex> Feetech.Protocol.build_read(1, 56, 2)
<<0xFF, 0xFF, 0x01, 0x04, 0x02, 0x38, 0x02, 0xBE>>
Builds a RECOVERY instruction packet (factory reset).
Builds a REG_WRITE instruction packet.
Like WRITE, but the servo buffers the command until an ACTION is received.
Builds a RESET instruction packet (reset rotation count).
@spec build_sync_read(address(), pos_integer(), [servo_id()]) :: binary()
Builds a SYNC_READ instruction packet.
Reads the same register(s) from multiple servos. Responses are returned in the order of the ID list.
Parameters
address- Starting address in control tablelength- Number of bytes to read from each servoids- List of servo IDs to read from
@spec build_sync_write(address(), pos_integer(), [{servo_id(), binary()}]) :: binary()
Builds a SYNC_WRITE instruction packet.
Writes the same register(s) to multiple servos in a single packet.
Parameters
address- Starting address in control tabledata_length- Number of bytes per servoservo_data- List of{servo_id, data_binary}tuples
Examples
iex> Feetech.Protocol.build_sync_write(42, 2, [{1, <<0x00, 0x08>>}, {2, <<0x00, 0x04>>}])
# Writes position 2048 to servo 1, position 1024 to servo 2
Builds a WRITE instruction packet.
Examples
iex> Feetech.Protocol.build_write(1, 42, <<0x00, 0x08>>)
<<0xFF, 0xFF, 0x01, 0x05, 0x03, 0x2A, 0x00, 0x08, 0xC4>>
Calculates the checksum for packet data.
The checksum is the bitwise NOT of the sum of all bytes, masked to 8 bits.
Examples
iex> Feetech.Protocol.checksum(<<1, 2, 1>>)
0xFB
@spec decode_int(binary()) :: non_neg_integer()
Decodes a little-endian binary to an unsigned integer.
Examples
iex> Feetech.Protocol.decode_int(<<0x00, 0x08>>)
2048
iex> Feetech.Protocol.decode_int(<<0xE8, 0x03>>)
1000
Decodes a little-endian binary to a signed integer (two's complement).
Examples
iex> Feetech.Protocol.decode_int_signed(<<0xFF, 0xFF>>)
-1
iex> Feetech.Protocol.decode_int_signed(<<0x00, 0x08>>)
2048
@spec decode_sign_magnitude(binary(), non_neg_integer()) :: integer()
Decodes a sign-magnitude encoded integer.
The sign bit position determines where the sign is stored:
- Bit 11 for homing_offset
- Bit 15 for position values
Examples
iex> Feetech.Protocol.decode_sign_magnitude(<<0xE8, 0x0B>>, 11)
-1000
iex> Feetech.Protocol.decode_sign_magnitude(<<0xE8, 0x03>>, 11)
1000
Encodes an integer as little-endian binary.
Examples
iex> Feetech.Protocol.encode_int(2048, 2)
<<0x00, 0x08>>
iex> Feetech.Protocol.encode_int(1000, 2)
<<0xE8, 0x03>>
@spec encode_sign_magnitude(integer(), non_neg_integer(), 1 | 2) :: binary()
Encodes a signed integer using sign-magnitude encoding.
The sign bit position determines where the sign is stored:
- Bit 11 for homing_offset (range: -2047 to +2047)
- Bit 15 for position values (range: -32767 to +32767)
Examples
iex> Feetech.Protocol.encode_sign_magnitude(-1000, 11, 2)
<<0xE8, 0x0B>>
iex> Feetech.Protocol.encode_sign_magnitude(1000, 11, 2)
<<0xE8, 0x03>>
Attempts to extract a complete packet from a binary buffer.
Returns {:ok, packet, remaining} if a complete packet is found,
or {:incomplete, buffer} if more data is needed.
Parses a response packet from a servo.
Returns {:ok, response} with the parsed ID, status, and parameters,
or {:error, reason} if the packet is invalid.
Validates a packet's checksum.