13namespace roboctrl::device::motor_protocol {
15using frame = std::array<std::byte, 8>;
17inline uint8_t octet(std::span<const std::byte> data,
size_t index) {
18 return std::to_integer<uint8_t>(data[index]);
21inline uint16_t read_u16_le(std::span<const std::byte> data,
size_t index) {
22 return static_cast<uint16_t
>(octet(data, index) | uint16_t{octet(data, index + 1)} << 8);
25inline int16_t read_i16_le(std::span<const std::byte> data,
size_t index) {
26 return std::bit_cast<int16_t>(read_u16_le(data, index));
29inline int16_t gated_current(
float command,
float scale,
float limit,
bool enabled,
bool online) {
30 if (!enabled || !online || !std::isfinite(command) || !std::isfinite(scale)
31 || std::isnan(limit) || limit < 0.f)
return 0;
32 const float bound = std::min(limit, 32767.f);
33 return static_cast<int16_t
>(std::clamp(command * std::clamp(scale, 0.f, 1.f), -bound, bound));
43inline std::optional<m9025_feedback> decode_m9025(std::span<const std::byte> data) {
44 if (data.size() != 8 || octet(data, 0) != 0xa0)
return std::nullopt;
45 return m9025_feedback{octet(data, 1), read_i16_le(data, 2), read_i16_le(data, 4), read_u16_le(data, 6)};
48inline frame encode_m9025_current(int16_t current) {
50 const auto raw = std::bit_cast<uint16_t>(current);
51 data[0] = std::byte{0xa0};
52 data[4] =
static_cast<std::byte
>(raw & 0xff);
53 data[5] =
static_cast<std::byte
>(raw >> 8);
58 float position_max {12.5f};
59 float velocity_max {45.f};
60 float torque_max {20.f};
64 uint8_t controller_id;
66 uint16_t position_raw;
70 uint8_t mos_temperature;
71 uint8_t rotor_temperature;
75 return std::isfinite(ranges.position_max) && ranges.position_max > 0.f
76 && std::isfinite(ranges.velocity_max) && ranges.velocity_max > 0.f
77 && std::isfinite(ranges.torque_max) && ranges.torque_max > 0.f;
80inline std::optional<j6006_feedback> decode_j6006(
81 std::span<const std::byte> data, uint16_t
id,
const j6006_ranges& ranges) {
82 if (data.size() != 8 || (octet(data, 0) & 0x0f) != (
id & 0x0f) || !valid_ranges(ranges))
84 const auto status =
static_cast<uint8_t
>(octet(data, 0) >> 4);
85 if (status > 1 && (status < 8 || status > 14))
return std::nullopt;
86 const auto position =
static_cast<uint16_t
>(uint16_t{octet(data, 1)} << 8 | octet(data, 2));
87 const auto velocity =
static_cast<uint16_t
>(uint16_t{octet(data, 3)} << 4 | octet(data, 4) >> 4);
88 const auto torque =
static_cast<uint16_t
>((octet(data, 4) & 0x0f) << 8 | octet(data, 5));
89 return j6006_feedback{
90 static_cast<uint8_t
>(octet(data, 0) & 0x0f),
static_cast<uint8_t
>(octet(data, 0) >> 4), position,
91 (2.f *
static_cast<float>(position) / 65535.f - 1.f) * ranges.position_max,
92 (2.f *
static_cast<float>(velocity) / 4095.f - 1.f) * ranges.velocity_max,
93 (2.f *
static_cast<float>(torque) / 4095.f - 1.f) * ranges.torque_max,
94 octet(data, 6), octet(data, 7)};
97inline std::array<std::byte, 4> encode_j6006_velocity(
float velocity) {
98 static_assert(
sizeof(float) == 4 && std::numeric_limits<float>::is_iec559);
99 std::array<std::byte, 4> data{};
100 const auto raw = std::bit_cast<uint32_t>(std::isfinite(velocity) ? velocity : 0.f);
101 for (
size_t i = 0; i < 4; ++i) data[i] = static_cast<std::byte>((raw >> (8 * i)) & 0xff);
105inline frame encode_j6006_enabled(
bool enabled) {
107 data.fill(std::byte{0xff});
108 data[7] = enabled ? std::byte{0xfc} : std::byte{0xfd};
112inline float gated_j6006_velocity(
float velocity,
bool enabled,
bool online, uint8_t status) {
113 return enabled && online && status == 1 && std::isfinite(velocity) ? velocity : 0.f;