Feetech.ControlTable behaviour (feetech v0.2.4)

Copy Markdown View Source

Behaviour for servo-specific control table definitions.

Each servo model has a different memory layout (control table) that defines the addresses, sizes, and conversions for readable/writable parameters.

Implementing a Control Table

defmodule MyServo do
  @behaviour Feetech.ControlTable

  @impl true
  def model_name, do: "MyServo"

  @impl true
  def registers do
    %{
      id: {5, 1, nil},
      goal_position: {42, 2, :position},
      present_position: {56, 2, :position}
    }
  end
end

Conversion Types

The third element of each register tuple specifies how to convert between raw register values and user-friendly values:

  • nil - No conversion, raw integer value
  • :bool - 0/1 to false/true
  • float - Scale factor (e.g., 0.1 for voltage in 0.1V units)
  • :position - Steps to radians (unsigned, servo-specific)
  • :position_signed - Steps to radians with sign-magnitude encoding (bit 15 = sign)
  • :speed - Speed units to rad/s
  • :speed_signed - Signed speed to rad/s (sign-magnitude, bit 15 = sign)
  • :load_signed - Signed load percentage (sign-magnitude, bit 10 = sign)
  • :mode - Operating mode enum
  • :baud_rate - Baud rate enum
  • {:sign_magnitude, sign_bit} - Raw sign-magnitude with specified sign bit
  • {module, decode_fun, encode_fun} - Custom conversion functions

Summary

Types

Memory address in control table

Number of bytes for register

Conversion specification for translating between raw and user values.

Register definition tuple

Register name atom

Map of register names to their definitions

Callbacks

Returns the human-readable model name

Returns the register definitions map

Functions

Decodes raw bytes from a register to a user value.

Decodes raw bytes to an integer (no conversion).

Encodes a user value to raw bytes for writing to a register.

Encodes a raw integer value to bytes (no conversion).

Looks up a register definition by name.

Types

address()

@type address() :: non_neg_integer()

Memory address in control table

byte_length()

@type byte_length() :: 1 | 2 | 4

Number of bytes for register

conversion()

@type conversion() ::
  nil
  | :bool
  | :position
  | :position_signed
  | :speed
  | :speed_signed
  | :load_signed
  | :mode
  | :baud_rate
  | float()
  | {:sign_magnitude, non_neg_integer()}
  | {module(), atom(), atom()}

Conversion specification for translating between raw and user values.

register_def()

@type register_def() :: {address(), byte_length(), conversion()}

Register definition tuple

register_name()

@type register_name() :: atom()

Register name atom

registers()

@type registers() :: %{required(register_name()) => register_def()}

Map of register names to their definitions

Callbacks

model_name()

@callback model_name() :: String.t()

Returns the human-readable model name

registers()

@callback registers() :: registers()

Returns the register definitions map

Functions

decode(control_table, name, data)

@spec decode(module(), register_name(), binary()) :: {:ok, term()} | {:error, atom()}

Decodes raw bytes from a register to a user value.

decode_raw(data)

@spec decode_raw(binary()) :: integer()

Decodes raw bytes to an integer (no conversion).

encode(control_table, name, value)

@spec encode(module(), register_name(), term()) :: {:ok, binary()} | {:error, atom()}

Encodes a user value to raw bytes for writing to a register.

encode_raw(control_table, name, value)

@spec encode_raw(module(), register_name(), integer()) ::
  {:ok, binary()} | {:error, atom()}

Encodes a raw integer value to bytes (no conversion).

get_register(control_table, name)

@spec get_register(module(), register_name()) ::
  {:ok, register_def()} | {:error, :unknown_register}

Looks up a register definition by name.