diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index b629e0b8..9f6bacb7 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -66,7 +66,7 @@ Tinymovr consists of **two separate projects**: | **adc** | Sensing | Current measurement, ADC trigger | | **gatedriver** | Actuation | PWM generation, Space Vector Modulation | | **can** | Comm | CAN bus interface, Avlos endpoint dispatch | -| **uart** | Comm | UART interface (alternative to CAN) | +| **uart** | Comm | UART interface (same Avlos endpoints as CAN, ASCII by endpoint ID) | | **ssp** | I/O | SPI for external encoders | | **nvm** | Storage | Configuration persistence to flash | | **scheduler** | Timing | Interrupt sync, CPU load monitoring | diff --git a/avlos_config.yaml b/avlos_config.yaml index 7ac383bd..fe612b8b 100644 --- a/avlos_config.yaml +++ b/avlos_config.yaml @@ -8,6 +8,8 @@ generators: output_enums: ./firmware/src/tm_enums.h output_header: ./firmware/src/can/can_endpoints.h output_impl: ./firmware/src/can/can_endpoints.c + output_metadata_header: ./firmware/src/can/avlos_endpoint_metadata.h + output_metadata_impl: ./firmware/src/can/avlos_endpoint_metadata.c header_includes: - src/common.h - src/tm_enums.h diff --git a/docs/interfaces/interfaces.rst b/docs/interfaces/interfaces.rst index d2ba9424..6a3d2862 100644 --- a/docs/interfaces/interfaces.rst +++ b/docs/interfaces/interfaces.rst @@ -27,7 +27,7 @@ For a detailed description, please see :ref:`integrating` and :ref:`api-referenc UART ---- -As an alternative to CAN Bus, Tinymovr offers UART-based (serial) communication. The protocol is much simpler than CAN and mainly designed for troubleshooting or testing in the absence of CAN hardware. +As an alternative to CAN Bus, Tinymovr offers UART-based (serial) communication. The protocol is human-readable ASCII and exposes the same endpoints as CAN, so you can use any serial terminal without Tinymovr Studio. .. warning:: The UART port on Tinymovr is NOT 5V tolerant. Applying 5V voltage will immediately damage the onboard PAC5527 controller. Please use only 3.3V for UART communication. @@ -40,297 +40,76 @@ Protocol Description The UART port is TTL at 115200 baud. A regular FTDI-style USB to UART adapter should be sufficient. -UART communication is based on a simple human-readable protocole dubbed the "dot protocol", because the dot is the command starting character. The protocol is response-only, meaning that Tinymovr will only respond to commands initiated by the client, it will never initiate a transmission on it's own. +UART uses the same Avlos endpoint set as CAN. Each command is one line: it starts with a dot (``.``), followed by the **endpoint ID** (0–99, decimal), then optionally one or more space-separated **values** for writes or calls. The line is terminated by a newline (``\n``). Tinymovr only responds to commands; it never sends on its own. -The command template is as follows: +**Command format** -.. code-block:: shell - - .Cxxxx - -The command begins with a dot. The next single character identifies the command. The characters following the second one are optional values to pass to write commands. Read commands only include the dot and command character. The command is finalized with a newline character (\n, not shown above). - -For instance, to get the current position estimate: - -.. code-block:: shell - - command: .p - response: 1000 - -To set the velocity estimate in encoder ticks: - -.. code-block:: shell - - command: .V10000 - (no response) - -The values passed or returned are always integers scaled by the mentioned factor (see command reference below). - -Note that command characters are case-sensitive, i.e. capitals and small represent different commands. As a convention, capital letters are setters and small are getters, where applicable. - -Command Reference -################# - -.Z -== - -Transition to idle state. - -Example - -.. code-block:: shell - - .Z - 0 - -.Q -== - -Transition to calibration state. - -Example - -.. code-block:: shell - - .Q - 0 - -.A -== - -Transition to closed loop control state. - -Example - -.. code-block:: shell +- **Read**: ``.`` → response is one line with the value (number, ``true``/``false``, or string). +- **Write** (single value): ``. `` → response is ``ok``. +- **Call** (no args, e.g. save config): ``.`` → response is ``ok``. +- **Call** (with args, e.g. set position + velocity): ``. ...`` → response is ``ok`` or one value if the call returns data. - .A - 0 +Values are human-readable: integers, decimals (e.g. ``1000.5``), and ``true``/``false`` or ``1``/``0`` for booleans. No scaling is required; the firmware parses and formats values according to the endpoint type. -.e -== +**Response format** -Get the error code. +- One line per command: the value (ASCII), or ``ok`` for writes and void calls, or ``error: invalid endpoint`` / ``error: bad value`` on failure. -Example - -.. code-block:: shell - - .e - 0 - -.p -== - -Get position estimate (ticks). - -Example - -.. code-block:: shell - - .p - 1000 - -.v -== - -Get velocity estimate (ticks/s). - - -Example - -.. code-block:: shell - - .v - -200 - -.i -== - -Get current (Iq) estimate (mA). - -Example - -.. code-block:: shell - - .i - 2000 - -.P -== - -Get/set position setpoint (ticks). - -Example - -.. code-block:: shell - - .P - 1000 - -.. code-block:: shell +**Endpoint IDs** - .P1000 +Endpoint IDs match the same API as CAN. For the full list and types, see :ref:`api-reference`. Endpoints are numbered in the order they appear in the spec (e.g. 0 = protocol hash, 4 = Vbus, 11 = save_config, 25 = controller position setpoint). -.V -== - -Get/set velocity setpoint (ticks/s). - -Example - -.. code-block:: shell - - .V - -10000 - -.. code-block:: shell - - .V-10000 - -.I -== - -Get/set current (Iq) setpoint (mA). - -Example - -.. code-block:: shell - - .I - 1000 - -.. code-block:: shell - - .I1000 - -.W -== - -Get/set current (Iq) limit (mA). - -Example - -.. code-block:: shell - - .W - 10000 - -.. code-block:: shell - - .W15000 - -.Y -== - -Get/set position gain. - -Example - -.. code-block:: shell - - .Y - 25 - -.. code-block:: shell - - .Y25 - -.F -== - -Get/set velocity gain (x0.000001). - -Example - -.. code-block:: shell +Examples +######## - .F - 20 +Read protocol hash: .. code-block:: shell - .F20 - -.G -== - -Get/set velocity integrator gain (x0.001). - -Note that high values (e.g. above 10) may cause instability. - -Example - -.. code-block:: shell + .0 + 3999954334 - .G - 2 +Read bus voltage (float): .. code-block:: shell - .G2 + .4 + 24.500000 -.H -== - -Get/set motor phase resistance (mOhm). - -Example - -.. code-block:: shell - - .H - 200 +Read firmware version (string): .. code-block:: shell - .H 200 - -.L -== + .2 + 2.3.4-17-g0768c21-dirty -Get/set motor phase inductance (μH). - -Example +Set position setpoint then read it back (endpoint 25, float): .. code-block:: shell - .L - 2000 + .25 1000.5 + ok -.. code-block:: shell + .25 + 1000.500000 - .L 2000 - -.R -== - -Reset the MCU. - -Example +Save configuration (endpoint 11, void call): .. code-block:: shell - .R - -.S -== + .11 + ok -Save board configuration. - -Example +Call an endpoint that takes multiple arguments (e.g. two floats): .. code-block:: shell - .S - -.X -== - -Erase board configuration and reset. + .47 1000.0 50.0 + ok -Example +Invalid endpoint: .. code-block:: shell - .X + .200 + error: invalid endpoint diff --git a/docs/protocol/reference.rst b/docs/protocol/reference.rst index 150ee25e..571e64c7 100644 --- a/docs/protocol/reference.rst +++ b/docs/protocol/reference.rst @@ -1,9 +1,10 @@ - .. _api-reference: API REFERENCE ============= +The :ref:`api-reference` section below lists all endpoints. + protocol_hash diff --git a/firmware/src/can/avlos_endpoint_metadata.c b/firmware/src/can/avlos_endpoint_metadata.c new file mode 100644 index 00000000..da905a83 --- /dev/null +++ b/firmware/src/can/avlos_endpoint_metadata.c @@ -0,0 +1,1416 @@ +/* +* This file was automatically generated using Avlos. +* https://github.com/tinymovr/avlos +* +* Any changes to this file will be overwritten when +* content is regenerated. +*/ + +#include "avlos_endpoint_metadata.h" + + +const Avlos_EndpointMeta avlos_endpoint_meta[] = { + + /* avlos_protocol_hash */ + [0] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT32, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_uid */ + [1] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT32, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_fw_version */ + [2] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_STRING, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_hw_revision */ + [3] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT32, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_Vbus */ + [4] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_Ibus */ + [5] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_power */ + [6] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_temp */ + [7] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_calibrated */ + [8] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_BOOL, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_errors */ + [9] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_warnings */ + [10] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_save_config */ + [11] = { + .kind = AVLOS_EP_KIND_CALL_NO_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_erase_config */ + [12] = { + .kind = AVLOS_EP_KIND_CALL_NO_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_nvm_num_slots */ + [13] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_nvm_current_slot */ + [14] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_nvm_write_count */ + [15] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT32, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_reset */ + [16] = { + .kind = AVLOS_EP_KIND_CALL_NO_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_enter_dfu */ + [17] = { + .kind = AVLOS_EP_KIND_CALL_NO_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_config_size */ + [18] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT32, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_scheduler_load */ + [19] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT32, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_scheduler_warnings */ + [20] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_state */ + [21] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_mode */ + [22] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_warnings */ + [23] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_errors */ + [24] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_position_setpoint */ + [25] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_position_p_gain */ + [26] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_velocity_setpoint */ + [27] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_velocity_limit */ + [28] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_velocity_p_gain */ + [29] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_velocity_i_gain */ + [30] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_velocity_deadband */ + [31] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_velocity_increment */ + [32] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_current_Iq_setpoint */ + [33] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_current_Id_setpoint */ + [34] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_current_Iq_limit */ + [35] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_current_Iq_estimate */ + [36] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_current_bandwidth */ + [37] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_current_Iq_p_gain */ + [38] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_current_max_Ibus_regen */ + [39] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_current_max_Ibrake */ + [40] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_voltage_Vq_setpoint */ + [41] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_calibrate */ + [42] = { + .kind = AVLOS_EP_KIND_CALL_NO_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_idle */ + [43] = { + .kind = AVLOS_EP_KIND_CALL_NO_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_position_mode */ + [44] = { + .kind = AVLOS_EP_KIND_CALL_NO_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_velocity_mode */ + [45] = { + .kind = AVLOS_EP_KIND_CALL_NO_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_current_mode */ + [46] = { + .kind = AVLOS_EP_KIND_CALL_NO_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_controller_set_pos_vel_setpoints */ + [47] = { + .kind = AVLOS_EP_KIND_CALL_WITH_ARGS, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 2, + .arg_dtypes = { + AVLOS_DTYPE_FLOAT, + AVLOS_DTYPE_FLOAT, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_comms_can_rate */ + [48] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_UINT32, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_comms_can_id */ + [49] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_UINT32, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_comms_can_heartbeat */ + [50] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_BOOL, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_motor_R */ + [51] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_motor_L */ + [52] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_motor_pole_pairs */ + [53] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_motor_type */ + [54] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_motor_calibrated */ + [55] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_BOOL, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_motor_I_cal */ + [56] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_motor_errors */ + [57] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_user_frame_position_estimate */ + [58] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_user_frame_velocity_estimate */ + [59] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_user_frame_offset */ + [60] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_user_frame_multiplier */ + [61] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_setup_onboard_calibrated */ + [62] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_BOOL, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_setup_onboard_errors */ + [63] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_setup_external_spi_type */ + [64] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_setup_external_spi_rate */ + [65] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_setup_external_spi_calibrated */ + [66] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_BOOL, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_setup_external_spi_errors */ + [67] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_setup_hall_calibrated */ + [68] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_BOOL, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_setup_hall_errors */ + [69] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_select_position_sensor_connection */ + [70] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_select_position_sensor_bandwidth */ + [71] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_select_position_sensor_raw_angle */ + [72] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_INT32, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_select_position_sensor_position_estimate */ + [73] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_select_position_sensor_velocity_estimate */ + [74] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_select_commutation_sensor_connection */ + [75] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_select_commutation_sensor_bandwidth */ + [76] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_select_commutation_sensor_raw_angle */ + [77] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_INT32, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_select_commutation_sensor_position_estimate */ + [78] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_sensors_select_commutation_sensor_velocity_estimate */ + [79] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_traj_planner_max_accel */ + [80] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_traj_planner_max_decel */ + [81] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_traj_planner_max_vel */ + [82] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_traj_planner_t_accel */ + [83] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_traj_planner_t_decel */ + [84] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_traj_planner_t_total */ + [85] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_traj_planner_move_to */ + [86] = { + .kind = AVLOS_EP_KIND_CALL_WITH_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 1, + .arg_dtypes = { + AVLOS_DTYPE_FLOAT, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_traj_planner_move_to_tlimit */ + [87] = { + .kind = AVLOS_EP_KIND_CALL_WITH_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 1, + .arg_dtypes = { + AVLOS_DTYPE_FLOAT, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_traj_planner_errors */ + [88] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_homing_velocity */ + [89] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_homing_max_homing_t */ + [90] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_homing_retract_dist */ + [91] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_homing_warnings */ + [92] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_UINT8, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_homing_stall_detect_velocity */ + [93] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_homing_stall_detect_delta_pos */ + [94] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_homing_stall_detect_t */ + [95] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_homing_home */ + [96] = { + .kind = AVLOS_EP_KIND_CALL_NO_ARGS, + .value_dtype = AVLOS_DTYPE_VOID, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_watchdog_enabled */ + [97] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_BOOL, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_watchdog_triggered */ + [98] = { + .kind = AVLOS_EP_KIND_READ_ONLY, + .value_dtype = AVLOS_DTYPE_BOOL, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + }, + + + /* avlos_watchdog_timeout */ + [99] = { + .kind = AVLOS_EP_KIND_READ_WRITE, + .value_dtype = AVLOS_DTYPE_FLOAT, + .num_args = 0, + .arg_dtypes = { + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID, + AVLOS_DTYPE_VOID + } + } + + +}; + +const uint8_t avlos_endpoint_meta_count = sizeof(avlos_endpoint_meta) / sizeof(avlos_endpoint_meta[0]); diff --git a/firmware/src/can/avlos_endpoint_metadata.h b/firmware/src/can/avlos_endpoint_metadata.h new file mode 100644 index 00000000..45dcc63a --- /dev/null +++ b/firmware/src/can/avlos_endpoint_metadata.h @@ -0,0 +1,43 @@ +/* +* This file was automatically generated using Avlos. +* https://github.com/tinymovr/avlos +* +* Any changes to this file will be overwritten when +* content is regenerated. +* +* Endpoint type metadata for type-aware UART/ASCII parsing and formatting. +* No dependency on UART; consumed by the firmware layer. +*/ + +#pragma once +#include + +typedef enum { + AVLOS_DTYPE_UINT8, + AVLOS_DTYPE_UINT32, + AVLOS_DTYPE_INT32, + AVLOS_DTYPE_FLOAT, + AVLOS_DTYPE_BOOL, + AVLOS_DTYPE_STRING, + AVLOS_DTYPE_VOID +} Avlos_Dtype; + +typedef enum { + AVLOS_EP_KIND_READ_ONLY, + AVLOS_EP_KIND_WRITE_ONLY, + AVLOS_EP_KIND_READ_WRITE, + AVLOS_EP_KIND_CALL_NO_ARGS, + AVLOS_EP_KIND_CALL_WITH_ARGS +} Avlos_EndpointKind; + +#define AVLOS_MAX_CALL_ARGS 4 + +typedef struct { + Avlos_EndpointKind kind; + Avlos_Dtype value_dtype; + uint8_t num_args; + Avlos_Dtype arg_dtypes[AVLOS_MAX_CALL_ARGS]; +} Avlos_EndpointMeta; + +extern const Avlos_EndpointMeta avlos_endpoint_meta[]; +extern const uint8_t avlos_endpoint_meta_count; diff --git a/firmware/src/uart/uart_interface.c b/firmware/src/uart/uart_interface.c index 5166208b..b372ff00 100644 --- a/firmware/src/uart/uart_interface.c +++ b/firmware/src/uart/uart_interface.c @@ -1,253 +1,502 @@ - -// * This file is part of the Tinymovr-Firmware distribution -// * (https://github.com/yconst/tinymovr-firmware). -// * Copyright (c) 2020-2023 Ioannis Chatzikonstantinou. -// * -// * This program is free software: you can redistribute it and/or modify -// * it under the terms of the GNU General Public License as published by -// * the Free Software Foundation, version 3. -// * -// * This program is distributed in the hope that it will be useful, but -// * WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// * General Public License for more details. -// * -// * You should have received a copy of the GNU General Public License -// * along with this program. If not, see . - -#include "string.h" -#include "src/system/system.h" -#include "src/motor/motor.h" -#include "src/observer/observer.h" -#include "src/controller/controller.h" -#include "src/controller/trajectory_planner.h" -#include "src/adc/adc.h" -#include "src/nvm/nvm.h" -#include "src/can/can.h" -#include "src/utils/utils.h" +/* + * This file is part of the Tinymovr-Firmware distribution + * (https://github.com/yconst/tinymovr-firmware). + * Copyright (c) 2020-2023 Ioannis Chatzikonstantinou. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "src/can/can_endpoints.h" +#include "src/can/avlos_endpoint_metadata.h" +#include "src/tm_enums.h" #include "src/uart/uart_lowlevel.h" #include "src/uart/uart_interface.h" -void UART_WriteAddr(uint8_t addr, int32_t data) +static void uart_send_line(const char *str) { - switch (addr) + size_t i = 0; + while (i < (size_t)(UART_BYTE_LIMIT - 1) && str[i] != '\0') { - case 'P': // pos setpoint - controller_set_Iq_setpoint_user_frame(0); - controller_set_vel_setpoint_user_frame(0); - controller_set_pos_setpoint_user_frame(data); - controller_set_mode(CONTROLLER_MODE_POSITION); - break; - - case 'V': // vel setpoint - controller_set_Iq_setpoint_user_frame(0); - controller_set_vel_setpoint_user_frame(data); - controller_set_mode(CONTROLLER_MODE_VELOCITY); - controller_set_vel_setpoint_user_frame((float)data); - break; - - case 'I': // current setpoint - controller_set_mode(CONTROLLER_MODE_CURRENT); - controller_set_Iq_setpoint_user_frame((float)data * ONE_OVER_UART_I_SCALING_FACTOR); - break; - - case 'G': // velocity integrator gain - controller_set_vel_integral_gain((float)data * ONE_OVER_UART_VEL_INT_SCALING_FACTOR); - break; - - case 'Y': // Position gain - controller_set_pos_gain(data); - break; - - case 'F': // Velocity gain - controller_set_vel_gain(data * ONE_OVER_UART_VEL_GAIN_SCALING_FACTOR); - break; - - case 'H': // phase resistance - motor_set_phase_resistance((float)data * ONE_OVER_UART_R_SCALING_FACTOR); - break; - - case 'L': // phase inductance - motor_set_phase_inductance((float)data * ONE_OVER_UART_L_SCALING_FACTOR); - break; - - case 'M': // Set is motor gimbal? - motor_set_is_gimbal((bool)data); - break; + uart_tx_msg[i] = (char)str[i]; + i++; + } + uart_tx_msg[i] = (char)UART_LINEFEED; + if (i + 1 < (size_t)UART_BYTE_LIMIT) + { + uart_tx_msg[i + 1] = '\0'; + } + pac5xxx_uart_int_enable_THREI2(UART_REF, 1); +} - case 'W': // Set Iq Limit - controller_set_Iq_limit((float)data * ONE_OVER_UART_IQ_LIMIT_SCALING_FACTOR); - break; - - case 'U': // CAN Baud Rate - CAN_set_kbit_rate((uint16_t)data); - break; +/* Parse one token from *p (space or \0 terminated); advance *p past token. Return 0 on parse error. */ +static int parse_token_float(const char **p, float *out) +{ + const char *s = *p; + const char *start = s; + float sign = 1.0f; + if (*s == '-') + { + sign = -1.0f; + s++; + } + else if (*s == '+') + { + s++; + } + unsigned long int_part = 0; + while (*s >= '0' && *s <= '9') + { + int_part = int_part * 10u + (unsigned long)(*s - '0'); + s++; + } + float v = (float)int_part; + if (*s == '.' || *s == ',') + { + s++; + float frac = 0.1f; + while (*s >= '0' && *s <= '9') + { + v += frac * (float)(*s - '0'); + frac *= 0.1f; + s++; + } + } + *p = s; + *out = v * sign; + return (s > start); +} - case 'C': // CAN ID - CAN_set_ID((uint8_t)data); - break; +static int parse_token_uint32(const char **p, uint32_t *out) +{ + char *end; + unsigned long v = strtoul(*p, &end, 10); + if (end == *p || v > 0xFFFFFFFFUL) + { + return 0; + } + *p = end; + *out = (uint32_t)v; + return 1; +} - case '<': // Max Decel - planner_set_max_decel((float)data); - break; +static int parse_token_int32(const char **p, int32_t *out) +{ + char *end; + long v = strtol(*p, &end, 10); + if (end == *p) + { + return 0; + } + *p = end; + *out = (int32_t)v; + return 1; +} - case '>': // Max Accel - planner_set_max_accel((float)data); - break; +static int parse_token_uint8(const char **p, uint8_t *out) +{ + uint32_t v; + if (!parse_token_uint32(p, &v) || v > 0xFF) + { + return 0; + } + *out = (uint8_t)v; + return 1; +} - case '^': // Max Vel - planner_set_max_vel((float)data); - break; +static int parse_token_bool(const char **p, uint8_t *out) +{ + while (**p == ' ' || **p == '\t') + { + (*p)++; + } + if (**p == '1' || (**p == 't' && (*p)[1] == 'r' && (*p)[2] == 'u' && (*p)[3] == 'e')) + { + *out = 1; + if ((*p)[0] == 't') + { + *p += 4; + } + else + { + (*p)++; + } + return 1; + } + if (**p == '0' || (**p == 'f' && (*p)[1] == 'a' && (*p)[2] == 'l' && (*p)[3] == 's' && (*p)[4] == 'e')) + { + *out = 0; + if ((*p)[0] == 'f') + { + *p += 5; + } + else + { + (*p)++; + } + return 1; + } + return 0; +} - case 'T': // Plan trajectory - planner_move_to_vlimit((float)data); - break; +/* Fill buf from tokens according to meta; return 0 on error. */ +static int uart_parse_args_into_buf(const char **p, uint8_t *buf, const Avlos_EndpointMeta *meta) +{ + uint8_t offset = 0; + for (uint8_t i = 0; i < meta->num_args && offset < 8; i++) + { + switch (meta->arg_dtypes[i]) + { + case AVLOS_DTYPE_FLOAT: + { + float v; + if (!parse_token_float(p, &v)) + { + return 0; + } + memcpy(buf + offset, &v, sizeof(float)); + offset += sizeof(float); + break; + } + case AVLOS_DTYPE_UINT8: + { + uint8_t v; + if (!parse_token_uint8(p, &v)) + { + return 0; + } + buf[offset++] = v; + break; + } + case AVLOS_DTYPE_UINT32: + { + uint32_t v; + if (!parse_token_uint32(p, &v)) + { + return 0; + } + memcpy(buf + offset, &v, sizeof(uint32_t)); + offset += sizeof(uint32_t); + break; + } + case AVLOS_DTYPE_INT32: + { + int32_t v; + if (!parse_token_int32(p, &v)) + { + return 0; + } + memcpy(buf + offset, &v, sizeof(int32_t)); + offset += sizeof(int32_t); + break; + } + case AVLOS_DTYPE_BOOL: + { + uint8_t v; + if (!parse_token_bool(p, &v)) + { + return 0; + } + buf[offset++] = v; + break; + } + default: + return 0; + } + } + return 1; +} +/* For single-value SET: parse one token into buf by value_dtype. */ +static int uart_parse_value_into_buf(const char **p, uint8_t *buf, Avlos_Dtype dtype) +{ + switch (dtype) + { + case AVLOS_DTYPE_FLOAT: + { + float v; + return parse_token_float(p, &v) ? (memcpy(buf, &v, sizeof(float)), 1) : 0; + } + case AVLOS_DTYPE_UINT8: + { + uint8_t v; + return parse_token_uint8(p, &v) ? (buf[0] = v, 1) : 0; + } + case AVLOS_DTYPE_UINT32: + { + uint32_t v; + return parse_token_uint32(p, &v) ? (memcpy(buf, &v, sizeof(uint32_t)), 1) : 0; + } + case AVLOS_DTYPE_INT32: + { + int32_t v; + return parse_token_int32(p, &v) ? (memcpy(buf, &v, sizeof(int32_t)), 1) : 0; + } + case AVLOS_DTYPE_BOOL: + { + uint8_t v; + return parse_token_bool(p, &v) ? (buf[0] = v, 1) : 0; + } default: - // No action - break; + return 0; } } -int32_t UART_ReadAddr(uint8_t addr) +/* Append unsigned decimal to line at *i; ensure room in max. */ +static void append_uint32(char *line, size_t max, size_t *i, uint32_t v) { - int32_t ret_val = 0; - switch (addr) + char tmp[12]; + size_t n = 0; + if (v == 0) { - case 'b': // vbus value - ret_val = (int32_t)(system_get_Vbus() * UART_V_SCALING_FACTOR); - break; - - case 'e': // controller error + tmp[n++] = '0'; + } + else { - // pass + while (v > 0 && n < sizeof(tmp) - 1) + { + tmp[n++] = (char)('0' + (v % 10)); + v /= 10; + } } - break; - - case 'p': // pos estimate - ret_val = user_frame_get_pos_estimate(); - break; - - case 'P': // pos setpoint - ret_val = controller_get_pos_setpoint_user_frame(); - break; - - case 'v': // vel estimate - ret_val = (int32_t)user_frame_get_vel_estimate(); - break; - - case 'V': // vel setpoint - ret_val = (int32_t)controller_get_vel_setpoint_user_frame(); - break; - - case 'i': // current estimate - ret_val = (int32_t)(controller_get_Iq_estimate_user_frame() * UART_I_SCALING_FACTOR); - break; - - case 'I': // current setpoint - ret_val = (int32_t)(controller_get_Iq_setpoint_user_frame() * UART_I_SCALING_FACTOR); - break; - - case 'G': // velocity integrator setpoint - ret_val = (int32_t)(controller_get_vel_integral_gain() * UART_VEL_INT_SCALING_FACTOR); - break; - - case 'H': // phase resistance - ret_val = (int32_t)(motor_get_phase_resistance() * UART_R_SCALING_FACTOR); - break; - - case 'L': // phase inductance - ret_val = (int32_t)(motor_get_phase_inductance() * UART_L_SCALING_FACTOR); - break; - - case 'W': // Get Iq Limit - ret_val = (int32_t)(controller_get_Iq_limit() * UART_IQ_LIMIT_SCALING_FACTOR); - break; - - case 'C': // CAN ID - ret_val = CAN_get_ID(); - break; - - case 'M': // Is motor gimbal? - ret_val = motor_get_is_gimbal(); - break; + while (n > 0 && *i < max - 1) + { + line[(*i)++] = tmp[--n]; + } +} - case 'Y': // - ret_val = controller_get_pos_gain(); - break; +/* Append int32 to line. */ +static void append_int32(char *line, size_t max, size_t *i, int32_t v) +{ + if (v < 0) + { + if (*i < max - 1) + { + line[(*i)++] = '-'; + } + append_uint32(line, max, i, (uint32_t)(-(int64_t)v)); + } + else + { + append_uint32(line, max, i, (uint32_t)v); + } +} - case 'F': // - ret_val = controller_get_vel_gain() * UART_VEL_GAIN_SCALING_FACTOR; - break; +/* Append float to line (simple form: integer part and 6 decimal places). */ +static void append_float(char *line, size_t max, size_t *i, float v) +{ + if (v < 0.0f) + { + if (*i < max - 1) + { + line[(*i)++] = '-'; + } + v = -v; + } + int32_t ip = (int32_t)v; + append_int32(line, max, i, ip); + if (*i < max - 1) + { + line[(*i)++] = '.'; + } + float frac = v - (float)ip; + for (int d = 0; d < 6 && *i < max - 1; d++) + { + frac *= 10.0f; + int digit = (int)frac; + if (digit > 9) + { + digit = 9; + } + line[(*i)++] = (char)('0' + digit); + frac -= (float)digit; + } +} - case 'Q': // calibrate - controller_set_state(CONTROLLER_STATE_CALIBRATE); +/* Format buffer to ASCII by dtype/len and send. */ +static void uart_format_and_send(uint8_t *buf, uint8_t len, Avlos_Dtype dtype) +{ + char line[UART_BYTE_LIMIT]; + size_t idx = 0; + switch (dtype) + { + case AVLOS_DTYPE_UINT8: + append_uint32(line, sizeof(line), &idx, len >= 1 ? buf[0] : 0); break; - - case 'A': // closed loop - controller_set_state(CONTROLLER_STATE_CL_CONTROL); + case AVLOS_DTYPE_UINT32: + { + uint32_t v = 0; + if (len >= sizeof(uint32_t)) + { + memcpy(&v, buf, sizeof(uint32_t)); + } + append_uint32(line, sizeof(line), &idx, v); break; - - case 'Z': // idle - controller_set_state(CONTROLLER_STATE_IDLE); + } + case AVLOS_DTYPE_INT32: + { + int32_t v = 0; + if (len >= sizeof(int32_t)) + { + memcpy(&v, buf, sizeof(int32_t)); + } + append_int32(line, sizeof(line), &idx, v); break; - - case 'R': // reset mcu - system_reset(); + } + case AVLOS_DTYPE_FLOAT: + { + float v = 0.0f; + if (len >= sizeof(float)) + { + memcpy(&v, buf, sizeof(float)); + } + append_float(line, sizeof(line), &idx, v); break; - - case 'S': // save config - nvm_save_config(); + } + case AVLOS_DTYPE_BOOL: + if (len >= 1 && buf[0]) + { + const char *t = "true"; + while (*t && idx < sizeof(line) - 1) + { + line[idx++] = *t++; + } + } + else + { + const char *f = "false"; + while (*f && idx < sizeof(line) - 1) + { + line[idx++] = *f++; + } + } break; - - case 'X': // erase config - nvm_erase(); + case AVLOS_DTYPE_STRING: + if (len > 0 && len < UART_BYTE_LIMIT - 2) + { + memcpy(line, buf, len); + idx = len; + } break; - default: - // No action + append_uint32(line, sizeof(line), &idx, 0); break; } - return ret_val; + line[idx] = '\0'; + uart_send_line(line); } void UART_process_message(void) { - int8_t addr = uart_rx_msg[1]; - int8_t len = ((int8_t)uart_rx_msg_len) - 3; + uint8_t buf[8]; + uint8_t buf_len = 0; + const char *p; + unsigned long ep_id_u; + char *end; - // Ensure buffer is null-terminated + if (uart_rx_msg_len < 2) + { + uart_send_line("error: bad format"); + return; + } uart_rx_msg[uart_rx_msg_len] = '\0'; - if (len > 0) + if (uart_rx_msg[0] != (char)UART_ASCII_PROT_START_BYTE) + { + return; + } + + p = &uart_rx_msg[1]; + while (*p == ' ' || *p == '\t') { - // Write operation - int32_t n = atol(&(uart_rx_msg)[2]); - UART_WriteAddr(addr, n); + p++; } - else if (len == 0) + ep_id_u = strtoul(p, &end, 10); + if (end == p || ep_id_u >= avlos_endpoint_meta_count) { - // Read operation - int32_t val = UART_ReadAddr(uart_rx_msg[1]); - UART_SendInt32(val); + uart_send_line("error: invalid endpoint"); + return; } - else + p = end; + + uint32_t ep_id = (uint32_t)ep_id_u; + const Avlos_EndpointMeta *meta = &avlos_endpoint_meta[ep_id]; + + Avlos_Command cmd = AVLOS_CMD_READ; + memset(buf, 0, sizeof(buf)); + + while (*p == ' ' || *p == '\t') { - // Error + p++; } -} -void UART_SendInt32(int32_t val) -{ - (void)itoa(val, uart_tx_msg, 10); - for (uint8_t i = 0; i < UART_BYTE_LIMIT; i++) + if (*p != '\0' && *p != '\r' && *p != '\n') { - if (uart_tx_msg[i] == '\0') + cmd = AVLOS_CMD_WRITE; + if (meta->kind == AVLOS_EP_KIND_CALL_WITH_ARGS) { - uart_tx_msg[i] = UART_LINEFEED; - break; + if (!uart_parse_args_into_buf(&p, buf, meta)) + { + uart_send_line("error: bad value"); + return; + } + buf_len = 8; + } + else if (meta->kind == AVLOS_EP_KIND_READ_WRITE || meta->kind == AVLOS_EP_KIND_WRITE_ONLY) + { + if (!uart_parse_value_into_buf(&p, buf, meta->value_dtype)) + { + uart_send_line("error: bad value"); + return; + } + if (meta->value_dtype == AVLOS_DTYPE_FLOAT || meta->value_dtype == AVLOS_DTYPE_UINT32 || + meta->value_dtype == AVLOS_DTYPE_INT32) + { + buf_len = 4; + } + else + { + buf_len = 1; + } + } + else + { + uart_send_line("error: endpoint not writable"); + return; } } - // Enable transmit interrupt to send reponse to host - pac5xxx_uart_int_enable_THREI2(UART_REF, 1); + + uint8_t (*callback)(uint8_t *, uint8_t *, Avlos_Command) = avlos_endpoints[ep_id]; + uint8_t ret = callback(buf, &buf_len, cmd); + + if (ret == AVLOS_RET_READ || ret == AVLOS_RET_CALL) + { + if (buf_len > 0) + { + uart_format_and_send(buf, buf_len, meta->value_dtype); + } + else + { + uart_send_line("ok"); + } + } + else if (ret == AVLOS_RET_WRITE || ret == AVLOS_RET_NOACTION) + { + uart_send_line("ok"); + } + else + { + uart_send_line("ok"); + } } diff --git a/firmware/src/uart/uart_interface.h b/firmware/src/uart/uart_interface.h index b1b3d920..8b0ed0b0 100644 --- a/firmware/src/uart/uart_interface.h +++ b/firmware/src/uart/uart_interface.h @@ -22,6 +22,5 @@ #include "src/common.h" void UART_process_message(void); -void UART_SendInt32(int32_t val); #endif /* UART_UART_INTERFACE_H_ */