commit 058bc70b84fa40b0318a43d1e660e3ea616c6067 Author: Thies Lennart Alff Date: Thu Feb 20 17:13:51 2025 +0100 initial commit diff --git a/include/hydrolink/hydrolink.h b/include/hydrolink/hydrolink.h new file mode 100644 index 0000000..645f402 --- /dev/null +++ b/include/hydrolink/hydrolink.h @@ -0,0 +1,7 @@ +#pragma once +#include "protocol.h" +#include "hydrolink_msg_ack.h" +#include "hydrolink_msg_set_cmd.h" +#include "hydrolink_msg_set_response.h" +#include "hydrolink_msg_get_cmd.h" +#include "hydrolink_msg_get_response.h" diff --git a/include/hydrolink/hydrolink_enums.h b/include/hydrolink/hydrolink_enums.h new file mode 100644 index 0000000..ba5258f --- /dev/null +++ b/include/hydrolink/hydrolink_enums.h @@ -0,0 +1,20 @@ +#pragma once +typedef enum CMD_ID { +CMD_ID_SWITCH = 1, /// Command for getting/setting the state of a switch +CMD_ID_CMD_ID_PWM = 2, /// Command for getting/setting the value of a PWM channel +CMD_ID_ENUM_END = 3 +}CMD_ID; + #pragma once +typedef enum CMD_RESULT { +CMD_RESULT_SUCCESS = 1, /// +CMD_RESULT_FAILED = 2, /// +CMD_RESULT_CMD_UKNOWN = 3, /// The received command was unknown or unhandled. +CMD_RESULT_ENUM_END = 4 +}CMD_RESULT; + #pragma once +typedef enum ACK { +ACK_ACK = 1, /// +ACK_NACK = 2, /// +ACK_ENUM_END = 3 +}ACK; + \ No newline at end of file diff --git a/include/hydrolink/hydrolink_msg_ack.h b/include/hydrolink/hydrolink_msg_ack.h new file mode 100644 index 0000000..087d806 --- /dev/null +++ b/include/hydrolink/hydrolink_msg_ack.h @@ -0,0 +1,25 @@ +#pragma once +#include "protocol.h" +#define HYDROLINK_MSG_ID_ACK 1 +#define HYDROLINK_MSG_ID_ACK_LEN 1 +#define HYDROLINK_MSG_ID_1_LEN 1 +#define HYDROLINK_MSG_ID_ACK_CRC_EXTRA 73 + +typedef struct hydrolink_msg_ack_t_s { +uint8_t ack; /// +} hydrolink_msg_ack_t; + +void hydrolink_ack_msg_encode(uint8_t src_id, uint8_t dst_id, const hydrolink_msg_ack_t *msg, hydrolink_msg_t *packet) { + packet->id = HYDROLINK_MSG_ID_ACK; + packet->src_id = src_id; + packet->dst_id = dst_id; + packet->length = HYDROLINK_MSG_ID_1_LEN + HYDROLINK_NON_PAYLOAD_LEN; + hydrolink_put_uint8_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 0, msg->ack); +} + +static inline uint8_t hydrolink_msg_ack_get_ack(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_uint8_t(packet, 0); +} +static inline void hydrolink_msgack_decode(const hydrolink_msg_t *packet, hydrolink_msg_ack_t *ack) { + ack->ack = hydrolink_msg_ack_get_ack(packet); +} \ No newline at end of file diff --git a/include/hydrolink/hydrolink_msg_get_cmd.h b/include/hydrolink/hydrolink_msg_get_cmd.h new file mode 100644 index 0000000..84334f6 --- /dev/null +++ b/include/hydrolink/hydrolink_msg_get_cmd.h @@ -0,0 +1,31 @@ +#pragma once +#include "protocol.h" +#define HYDROLINK_MSG_ID_GET_CMD 4 +#define HYDROLINK_MSG_ID_GET_CMD_LEN 2 +#define HYDROLINK_MSG_ID_4_LEN 2 +#define HYDROLINK_MSG_ID_GET_CMD_CRC_EXTRA 38 + +typedef struct hydrolink_msg_get_cmd_t_s { +uint8_t index; /// +uint8_t cmd_id; /// +} hydrolink_msg_get_cmd_t; + +void hydrolink_get_cmd_msg_encode(uint8_t src_id, uint8_t dst_id, const hydrolink_msg_get_cmd_t *msg, hydrolink_msg_t *packet) { + packet->id = HYDROLINK_MSG_ID_GET_CMD; + packet->src_id = src_id; + packet->dst_id = dst_id; + packet->length = HYDROLINK_MSG_ID_4_LEN + HYDROLINK_NON_PAYLOAD_LEN; + hydrolink_put_uint8_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 0, msg->index); + hydrolink_put_uint8_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 1, msg->cmd_id); +} + +static inline uint8_t hydrolink_msg_get_cmd_get_index(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_uint8_t(packet, 0); +} +static inline uint8_t hydrolink_msg_get_cmd_get_cmd_id(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_uint8_t(packet, 1); +} +static inline void hydrolink_msgget_cmd_decode(const hydrolink_msg_t *packet, hydrolink_msg_get_cmd_t *get_cmd) { + get_cmd->index = hydrolink_msg_get_cmd_get_index(packet); + get_cmd->cmd_id = hydrolink_msg_get_cmd_get_cmd_id(packet); +} \ No newline at end of file diff --git a/include/hydrolink/hydrolink_msg_get_response.h b/include/hydrolink/hydrolink_msg_get_response.h new file mode 100644 index 0000000..5ee68fe --- /dev/null +++ b/include/hydrolink/hydrolink_msg_get_response.h @@ -0,0 +1,37 @@ +#pragma once +#include "protocol.h" +#define HYDROLINK_MSG_ID_GET_RESPONSE 3 +#define HYDROLINK_MSG_ID_GET_RESPONSE_LEN 6 +#define HYDROLINK_MSG_ID_3_LEN 6 +#define HYDROLINK_MSG_ID_GET_RESPONSE_CRC_EXTRA 28 + +typedef struct hydrolink_msg_get_response_t_s { +int32_t value; /// +uint8_t result; /// +uint8_t cmd_id; /// +} hydrolink_msg_get_response_t; + +void hydrolink_get_response_msg_encode(uint8_t src_id, uint8_t dst_id, const hydrolink_msg_get_response_t *msg, hydrolink_msg_t *packet) { + packet->id = HYDROLINK_MSG_ID_GET_RESPONSE; + packet->src_id = src_id; + packet->dst_id = dst_id; + packet->length = HYDROLINK_MSG_ID_3_LEN + HYDROLINK_NON_PAYLOAD_LEN; + hydrolink_put_int32_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 0, msg->value); + hydrolink_put_uint8_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 4, msg->result); + hydrolink_put_uint8_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 5, msg->cmd_id); +} + +static inline int32_t hydrolink_msg_get_response_get_value(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_int32_t(packet, 0); +} +static inline uint8_t hydrolink_msg_get_response_get_result(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_uint8_t(packet, 4); +} +static inline uint8_t hydrolink_msg_get_response_get_cmd_id(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_uint8_t(packet, 5); +} +static inline void hydrolink_msgget_response_decode(const hydrolink_msg_t *packet, hydrolink_msg_get_response_t *get_response) { + get_response->value = hydrolink_msg_get_response_get_value(packet); + get_response->result = hydrolink_msg_get_response_get_result(packet); + get_response->cmd_id = hydrolink_msg_get_response_get_cmd_id(packet); +} \ No newline at end of file diff --git a/include/hydrolink/hydrolink_msg_set_cmd.h b/include/hydrolink/hydrolink_msg_set_cmd.h new file mode 100644 index 0000000..753e912 --- /dev/null +++ b/include/hydrolink/hydrolink_msg_set_cmd.h @@ -0,0 +1,37 @@ +#pragma once +#include "protocol.h" +#define HYDROLINK_MSG_ID_SET_CMD 2 +#define HYDROLINK_MSG_ID_SET_CMD_LEN 6 +#define HYDROLINK_MSG_ID_2_LEN 6 +#define HYDROLINK_MSG_ID_SET_CMD_CRC_EXTRA 58 + +typedef struct hydrolink_msg_set_cmd_t_s { +int32_t value; /// +uint8_t index; /// +uint8_t cmd_id; /// Command defined as in CMD_ID enum. +} hydrolink_msg_set_cmd_t; + +void hydrolink_set_cmd_msg_encode(uint8_t src_id, uint8_t dst_id, const hydrolink_msg_set_cmd_t *msg, hydrolink_msg_t *packet) { + packet->id = HYDROLINK_MSG_ID_SET_CMD; + packet->src_id = src_id; + packet->dst_id = dst_id; + packet->length = HYDROLINK_MSG_ID_2_LEN + HYDROLINK_NON_PAYLOAD_LEN; + hydrolink_put_int32_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 0, msg->value); + hydrolink_put_uint8_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 4, msg->index); + hydrolink_put_uint8_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 5, msg->cmd_id); +} + +static inline int32_t hydrolink_msg_set_cmd_get_value(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_int32_t(packet, 0); +} +static inline uint8_t hydrolink_msg_set_cmd_get_index(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_uint8_t(packet, 4); +} +static inline uint8_t hydrolink_msg_set_cmd_get_cmd_id(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_uint8_t(packet, 5); +} +static inline void hydrolink_msgset_cmd_decode(const hydrolink_msg_t *packet, hydrolink_msg_set_cmd_t *set_cmd) { + set_cmd->value = hydrolink_msg_set_cmd_get_value(packet); + set_cmd->index = hydrolink_msg_set_cmd_get_index(packet); + set_cmd->cmd_id = hydrolink_msg_set_cmd_get_cmd_id(packet); +} \ No newline at end of file diff --git a/include/hydrolink/hydrolink_msg_set_response.h b/include/hydrolink/hydrolink_msg_set_response.h new file mode 100644 index 0000000..935edaf --- /dev/null +++ b/include/hydrolink/hydrolink_msg_set_response.h @@ -0,0 +1,31 @@ +#pragma once +#include "protocol.h" +#define HYDROLINK_MSG_ID_SET_RESPONSE 3 +#define HYDROLINK_MSG_ID_SET_RESPONSE_LEN 2 +#define HYDROLINK_MSG_ID_3_LEN 2 +#define HYDROLINK_MSG_ID_SET_RESPONSE_CRC_EXTRA 250 + +typedef struct hydrolink_msg_set_response_t_s { +uint8_t result; /// +uint8_t cmd_id; /// +} hydrolink_msg_set_response_t; + +void hydrolink_set_response_msg_encode(uint8_t src_id, uint8_t dst_id, const hydrolink_msg_set_response_t *msg, hydrolink_msg_t *packet) { + packet->id = HYDROLINK_MSG_ID_SET_RESPONSE; + packet->src_id = src_id; + packet->dst_id = dst_id; + packet->length = HYDROLINK_MSG_ID_3_LEN + HYDROLINK_NON_PAYLOAD_LEN; + hydrolink_put_uint8_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 0, msg->result); + hydrolink_put_uint8_t(HYDROLINK_PAYLOAD_NON_CONST(packet), 1, msg->cmd_id); +} + +static inline uint8_t hydrolink_msg_set_response_get_result(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_uint8_t(packet, 0); +} +static inline uint8_t hydrolink_msg_set_response_get_cmd_id(const hydrolink_msg_t *packet) { + return HYDROLINK_RETURN_uint8_t(packet, 1); +} +static inline void hydrolink_msgset_response_decode(const hydrolink_msg_t *packet, hydrolink_msg_set_response_t *set_response) { + set_response->result = hydrolink_msg_set_response_get_result(packet); + set_response->cmd_id = hydrolink_msg_set_response_get_cmd_id(packet); +} \ No newline at end of file diff --git a/include/hydrolink/protocol.h b/include/hydrolink/protocol.h new file mode 100644 index 0000000..3d54cf0 --- /dev/null +++ b/include/hydrolink/protocol.h @@ -0,0 +1,155 @@ + +#pragma once + +#include +#define HYDROLINK_HEADER_LEN 4 +#define HYDROLINK_CRC_LEN 2 +#define HYDROLINK_NON_PAYLOAD_LEN (HYDROLINK_HEADER_LEN + HYDROLINK_CRC_LEN) +#define HYDROLINK_MAX_PAYLOAD_LEN 6 +#define HYDROLINK_MAX_MSG_LEN (HYDROLINK_MAX_PAYLOAD_LEN + HYDROLINK_NON_PAYLOAD_LEN) + +struct hydrolink_msg_s; + +typedef struct hydrolink_msg_s hydrolink_msg_t; + +struct hydrolink_msg_s { + uint8_t id; + uint8_t payload_length; + uint8_t dst_id; + uint8_t src_id; + uint8_t payload[HYDROLINK_MAX_PAYLOAD_LEN]; + uint16_t crc; +}; + +#define HYDROLINK_PAYLOAD(msg) ((const char *)(&((msg)->payload[0]))) +#define HYDROLINK_PAYLOAD_NON_CONST(msg) ((char *)(&((msg)->payload[0]))) + +#define hydrolink_put_float(buf, wire_offset, b) byte_swap_4(&buf[wire_offset], (const char *)&b) +#define hydrolink_put_double(buf, wire_offset, b) byte_swap_8(&buf[wire_offset], (const char *)&b) +#define hydrolink_put_char(buf, wire_offset, b) buf[wire_offset] = (char)b +#define hydrolink_put_int8_t(buf, wire_offset, b) buf[wire_offset] = (int8_t)b +#define hydrolink_put_uint8_t(buf, wire_offset, b) buf[wire_offset] = (uint8_t)b +#define hydrolink_put_int16_t(buf, wire_offset, b) byte_swap_2(&buf[wire_offset], (const char *)&b) +#define hydrolink_put_uint16_t(buf, wire_offset, b) byte_swap_2(&buf[wire_offset], (const char *)&b) +#define hydrolink_put_int32_t(buf, wire_offset, b) byte_swap_4(&buf[wire_offset], (const char *)&b) +#define hydrolink_put_uint32_t(buf, wire_offset, b) byte_swap_4(&buf[wire_offset], (const char *)&b) +#define hydrolink_put_int64_t(buf, wire_offset, b) byte_swap_8(&buf[wire_offset], (const char *)&b) +#define hydrolink_put_uint64_t(buf, wire_offset, b) byte_swap_8(&buf[wire_offset], (const char *)&b) + +inline void byte_swap_2(char *dst, const char *src) { + dst[0] = src[1]; + dst[1] = src[0]; +} + +inline void byte_swap_4(char *dst, const char *src) { + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +} + +inline void byte_swap_8(char *dst, const char *src) { + dst[0] = src[7]; + dst[1] = src[6]; + dst[2] = src[5]; + dst[3] = src[4]; + dst[4] = src[3]; + dst[5] = src[2]; + dst[6] = src[1]; + dst[7] = src[0]; +} + +#define HYDROLINK_MSG_RETURN_TYPE(TYPE, SIZE) \ +static inline TYPE HYDROLINK_RETURN_## TYPE(const hydrolink_msg_t *msg, uint8_t offset) {\ + TYPE ret; \ + byte_swap_## SIZE((char*)&ret, &HYDROLINK_PAYLOAD(msg)[offset]); \ + return ret; \ +} + +HYDROLINK_MSG_RETURN_TYPE(float, 4) + +HYDROLINK_MSG_RETURN_TYPE(double, 8) + +#define HYDROLINK_RETURN_char(msg, wire_offset) (char)HYDROLINK_PAYLOAD(msg)[wire_offset] +#define HYDROLINK_RETURN_int8_t(msg, wire_offset) (int8_t)HYDROLINK_PAYLOAD(msg)[wire_offset] +#define HYDROLINK_RETURN_uint8_t(msg, wire_offset) (uint8_t)HYDROLINK_PAYLOAD(msg)[wire_offset] +HYDROLINK_MSG_RETURN_TYPE(int16_t, 2) + +HYDROLINK_MSG_RETURN_TYPE(uint16_t, 2) + +HYDROLINK_MSG_RETURN_TYPE(int32_t, 4) + +HYDROLINK_MSG_RETURN_TYPE(uint32_t, 4) + +HYDROLINK_MSG_RETURN_TYPE(int64_t, 8) + +HYDROLINK_MSG_RETURN_TYPE(uint64_t, 8) + +inline void crc_xmodem_init(uint16_t *crc) { *crc = 0; } + +inline void crc_xmodem_accumulate(uint8_t data, uint16_t *crc) { + *crc = *crc ^ ((uint16_t)data << 8); + for (uint8_t j = 0; j < 8; j++) { + if (*crc & 0x8000) { + *crc = (*crc << 1) ^ 0x1021; + } else { + *crc <<= 1; + } + } +} + +inline uint16_t crc_xmodem_calculate(const uint8_t *data, uint8_t length) { + uint16_t crc; + crc_xmodem_init(&crc); + for (uint8_t i = 0; i < length; i++) { + crc_xmodem_accumulate(*data, &crc); + } + return crc; +} + +void hydrolink_fill_header_and_crc(hydrolink_msg_t *msg, uint8_t msg_id, uint8_t payload_length, uint8_t dst_id, uint8_t src_id, uint8_t crc_extra) { + msg->id = msg_id; + msg->payload_length = payload_length; + msg->dst_id = dst_id; + msg->src_id = src_id; + uint16_t crc; + crc_xmodem_init(&crc); + for(int i = 0; i < msg->payload_length; ++i) { + crc_xmodem_accumulate(msg->payload[i], &crc); + } + crc_xmodem_accumulate(crc_extra, &crc); + msg->crc = crc; +} + +uint8_t hydrolink_serialize_message(hydrolink_msg_t *msg, uint8_t *buffer) { + buffer[0] = msg->id; + buffer[1] = msg->payload_length; + buffer[2] = msg->dst_id; + buffer[3] = msg->src_id; + uint8_t *p = &buffer[4]; + for(int i =0; ipayload_length;++i) { + *p++ = msg->payload[i]; + } + *p++ = (uint8_t)(msg->crc >> 8); + *p++ = (uint8_t)(msg->crc & 0xff); + return HYDROLINK_NON_PAYLOAD_LEN + msg->payload_length; +} + +uint8_t hydrolink_deserialize_header(hydrolink_msg_t *msg, uint8_t *buffer, uint8_t buffer_length) { + if (buffer_length < HYDROLINK_NON_PAYLOAD_LEN) { + return 0; + } + msg->id = buffer[0]; + msg->payload_length = buffer[1]; + msg->dst_id = buffer[2]; + msg->src_id = buffer[3]; + msg->crc = buffer[buffer_length-1] | (((uint16_t)buffer[buffer_length-2]) << 8); + if (msg->payload_length + HYDROLINK_NON_PAYLOAD_LEN != buffer_length) { + return 0; + } + uint8_t *p = &buffer[HYDROLINK_HEADER_LEN]; + for (int i = 0; i < msg->payload_length; ++i) { + msg->payload[i] = *p++; + } + return msg->payload_length + HYDROLINK_NON_PAYLOAD_LEN; +}