Feetech.Protocol (feetech v0.2.3)

Copy Markdown View Source

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 byte

Response packet (servo to controller):

Header | ID | Length | Status | Params | Checksum
0xFF 0xFF | 1 byte | 1 byte | 1 byte | N bytes | 1 byte

Length = 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

address()

@type address() :: non_neg_integer()

Memory address in control table

response()

@type response() :: %{id: servo_id(), status: non_neg_integer(), params: binary()}

Parsed response from servo

servo_id()

@type servo_id() :: 0..254

Servo ID (0-253, or 254 for broadcast)

Functions

build_action(id \\ Instruction.broadcast_id())

@spec build_action(servo_id()) :: binary()

Builds an ACTION instruction packet.

Triggers all buffered REG_WRITE commands. Typically sent to broadcast ID.

build_ping(id)

@spec build_ping(servo_id()) :: binary()

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

build_read(id, address, length)

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

build_recovery(id)

@spec build_recovery(servo_id()) :: binary()

Builds a RECOVERY instruction packet (factory reset).

build_reg_write(id, address, data)

@spec build_reg_write(servo_id(), address(), binary()) :: binary()

Builds a REG_WRITE instruction packet.

Like WRITE, but the servo buffers the command until an ACTION is received.

build_reset(id)

@spec build_reset(servo_id()) :: binary()

Builds a RESET instruction packet (reset rotation count).

build_sync_read(address, length, ids)

@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 table
  • length - Number of bytes to read from each servo
  • ids - List of servo IDs to read from

build_sync_write(address, data_length, servo_data)

@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 table
  • data_length - Number of bytes per servo
  • servo_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

build_write(id, address, data)

@spec build_write(servo_id(), address(), binary()) :: binary()

Builds a WRITE instruction packet.

Examples

iex> Feetech.Protocol.build_write(1, 42, <<0x00, 0x08>>)
<<0xFF, 0xFF, 0x01, 0x05, 0x03, 0x2A, 0x00, 0x08, 0xC4>>

checksum(data)

@spec checksum(binary()) :: byte()

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

decode_int(arg)

@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

decode_int_signed(data)

@spec decode_int_signed(binary()) :: integer()

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

decode_sign_magnitude(data, sign_bit)

@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

encode_int(value, int)

@spec encode_int(integer(), 1 | 2 | 4) :: binary()

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

encode_sign_magnitude(value, sign_bit, length)

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

extract_packet(buffer)

@spec extract_packet(binary()) :: {:ok, binary(), binary()} | {:incomplete, binary()}

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.

parse_response(arg1)

@spec parse_response(binary()) :: {:ok, response()} | {:error, atom()}

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.

valid_checksum?(data, expected_checksum)

@spec valid_checksum?(binary(), byte()) :: boolean()

Validates a packet's checksum.