add protocol version2

This commit is contained in:
Dennis Gunia
2025-10-08 23:42:32 +02:00
parent 2fe870a68d
commit 0e03de54b4
18 changed files with 214 additions and 199 deletions

View File

@@ -14,26 +14,12 @@ After the transmission of the payload length, the receiver *node* counts the rem
In protocol version 1.0, the receiving *node* expects the last byte to be `0x24`. If this is not the case, the transmission is marked as invalid and will not be processed further. The receiving *node* will not send any response.
In protocol version 2.0, the checksum is verified. If it does not match the payload, the transmission is marked as invalid and will not be processed further. The receiving *node* will not send any response.
In protocol version 2.0, the checksum of the payload is verified. If it does not match the payload, the transmission is marked as invalid and will not be processed further. The receiving *node* will not send any response.
No stop-byte is expected. Version 2.0 also requires a timeout-check in the receiving function. This allows the bus to support hot-plugging, to cancel incomplete or invalid packages and reliably detect the start of a new transmission.
The communication is typically unidirectional. Lost transmissions are not detectable. The *flap controller* NEVER initiates a communication to the *main controller interface*. *node* to *master* communication only occures in response to specific commands. This is specified in the command / payload documentation.
## Packet format (1.0)
```
+---------------------------------+----------------------------------------+
| Header | Frame |
+------------+-----------+--------+----------+-----------------+-----------+
| Start-Byte | Protocol- | Frame- | Receiver | Payload | Stop-Byte |
| | Version | Length | Address | | |
| 0x2B | 0x00 | 1 byte | 2 bytes | framelemgth - 3 | 0x24 |
+------------+-----------+--------+----------+-----------------+-----------+
The frame length includes the address and stop-byte
Therefor, the payload is framelegth - 3.
```
## Packet format (2.0)
*This is not yet implemented!!*
```
+---------------------------------+----------------------------------------+
| Header | Frame |
@@ -46,6 +32,7 @@ The communication is typically unidirectional. Lost transmissions are not detect
Therefor, the payload is framelegth - 4.
Checksum is based on MODBUS CRC algorithm. Check implementation in sfbus.c
Checksum is only created for the payload, not the entire frame or packet.
```
More infromation regarding the crc algorithm: https://ctlsys.com/support/how_to_compute_the_modbus_rtu_message_crc/

View File

@@ -23,8 +23,8 @@ JSON_C_DIR=/path/to/json_c/install
CFLAGS += -I$(JSON_C_DIR)/include/json-c
# Or to use lines like: #include <json-c/json_object.h>
#CFLAGS += -I$(JSON_C_DIR)/include
LDFLAGS+= -L$(JSON_C_DIR)/lib -ljson-c
LDFLAGS+= -L$(JSON_C_DIR)/lib -lws
LDFLAGS+= -L$(JSON_C_DIR)/lib -ljson-c # json-c lib
LDFLAGS+= -L$(JSON_C_DIR)/lib -lws # websockets lib
CPPFLAGS ?= $(INC_FLAGS) -MMD -MP
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJS)

View File

@@ -9,7 +9,7 @@
#include "console.h"
const char *device_config_file = "./flapconfig.json";
char *device_config_file = "./flapconfig.json";
int fd;
// command handlers
@@ -133,7 +133,7 @@ void cmd_dm_print(json_object *req, json_object *res)
{
int x = json_object_get_int(jx);
int y = json_object_get_int(jy);
char *str = json_object_get_string(jstr);
const char *str = json_object_get_string(jstr);
devicemgr_printText(str, x, y);
json_object_object_add(res, "ack", json_object_new_boolean(true));
}
@@ -332,7 +332,7 @@ json_object *parse_command(json_object *req)
json_object *commandObj;
json_object *res = json_object_new_object();
json_object_object_get_ex(req, "command", &commandObj);
char *command = json_object_get_string(commandObj);
const char *command = json_object_get_string(commandObj);
free(commandObj);
// command 'table'
if (strcmp(command, "dm_dump") == 0)

View File

@@ -12,6 +12,7 @@
#include "devicemgr.h"
#include <json-c/json_object.h>
#include <string.h>
#include <sys/types.h>
enum SFDEVICE_STATE
{
@@ -29,6 +30,7 @@ enum SFDEVICE_POWER
UNKNOWN
};
struct SFDEVICE
{
int pos_x;
@@ -262,7 +264,7 @@ void setSingleRaw(int id, int flap)
devices[nextFreeSlot].current_flap = flap;
}
void devicemgr_printText(char *text, int x, int y)
void devicemgr_printText(const char *text, int x, int y)
{
for (int i = 0; i < strlen(text); i++)
{
@@ -340,7 +342,7 @@ int devicemgr_remove(int id)
{
devices[nextFreeSlot].deviceState = REMOVED;
devices[nextFreeSlot].address = 0;
devices[nextFreeSlot].rs485_descriptor = NULL;
devices[nextFreeSlot].rs485_descriptor = -1;
return 0;
}
@@ -361,7 +363,7 @@ int devicemgr_save(char *file)
json_object_object_add(root, "devices", device_array);
char *data = json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY);
const char *data = json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY);
printf("[INFO][console] store data to %s\n", file);
FILE *fptr;
@@ -373,7 +375,7 @@ int devicemgr_save(char *file)
int devicemgr_load(char *file)
{
FILE *fptr;
const char *line_in_file = malloc(JSON_MAX_LINE_LEN); // maximum of 256 bytes per line;
char *line_in_file = malloc(JSON_MAX_LINE_LEN); // maximum of 256 bytes per line;
fptr = fopen(file, "r");
json_tokener *tok = json_tokener_new();
json_object *jobj = NULL;
@@ -382,7 +384,7 @@ int devicemgr_load(char *file)
do
{
int read_ret = fgets(line_in_file, JSON_MAX_LINE_LEN, fptr); // read line from file
char* read_ret = fgets(line_in_file, JSON_MAX_LINE_LEN, fptr); // read line from file
stringlen = strlen(line_in_file);
// printf("Read line with chars: %i : %s", stringlen, line_in_file); // only for testing
jobj = json_tokener_parse_ex(tok, line_in_file, stringlen);

View File

@@ -31,5 +31,8 @@ void devicemgr_init();
int devicemgr_print(char *text);
int devicemgr_refresh();
int devicemgr_save(char *file);
void devicemgr_printText(char *text, int x, int y);
void devicemgr_printFlap(int flap, int x, int y);
void devicemgr_printText(const char *text, int x, int y);
void devicemgr_printFlap(int flap, int x, int y);
int devicemgr_load(char *file);
int devicemgr_load_single(json_object *device_obj);
int devicemgr_remove(int id);

View File

@@ -3,14 +3,7 @@
* Copyright (c) 2024-2025 GuniaLabs (www.dennisgunia.de)
* Authors: Dennis Gunia
*
* This program is free software (...)
* You should have received a copy of the GNU Affero General Public License
* along with this program (...)
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the split-flap software without
* disclosing the source code of your own applications (...)
* This program is licenced under AGPL-3.0 license.
*
*/
@@ -28,6 +21,7 @@
#include "ftdi485.h"
#include "sfbus.h"
extern char *optarg;
void printUsage(char *argv[])
{
@@ -86,7 +80,7 @@ int main(int argc, char *argv[])
setvbuf(stdout, NULL, _IONBF, 0); // do not buffer stdout!!!!
printf("Open device at %s\n", port);
int fd = rs485_init(port, B19200); // setup rs485
int fd = rs485_init(port, B57600); // setup rs485
if (strcmp(command, "ping") == 0)
{

View File

@@ -27,14 +27,14 @@ void print_bufferHexRx(char *buffer, int length, u_int16_t address)
{
printf("Rx (0x%04X): ", address);
print_charHex(buffer, length);
printf(" | %i bytes received\n", length);
printf("| %i bytes received\n", length);
}
void print_bufferHexTx(char *buffer, int length, u_int16_t address)
{
printf("Tx (0x%04X): ", address);
print_charHex(buffer, length);
printf(" | %i bytes sent\n", length);
printf("| %i bytes sent\n", length);
}
ssize_t sfbus_recv_frame_wait(int fd, u_int16_t address, char *buffer)
@@ -43,7 +43,7 @@ ssize_t sfbus_recv_frame_wait(int fd, u_int16_t address, char *buffer)
int retryCount = 2;
do
{
len = sfbus_recv_frame(fd, address, buffer);
len = sfbus_recv_frame_v2(fd, address, buffer);
retryCount--;
if (retryCount == 0)
{
@@ -51,12 +51,14 @@ ssize_t sfbus_recv_frame_wait(int fd, u_int16_t address, char *buffer)
return -1;
}
} while (len <= 0);
print_bufferHexRx(buffer, len - 3, address);
print_bufferHexRx(buffer, len - 4, address);
return len;
}
ssize_t sfbus_recv_frame(int fd, u_int16_t address, char *buffer)
ssize_t sfbus_recv_frame_v2(int fd, u_int16_t address, char *buffer)
{
memset(buffer, 0, sizeof(buffer));
// wait for start byte
char byte = 0x00;
int retryCount = 3;
@@ -71,10 +73,9 @@ ssize_t sfbus_recv_frame(int fd, u_int16_t address, char *buffer)
}
}
u_int8_t frm_version;
u_int8_t frm_addr_l;
u_int8_t frm_addr_h;
u_int8_t frm_addr_l, frm_addr_h;
u_int8_t frm_length;
u_int8_t frm_eof;
u_int8_t frm_crc_l, frm_crc_h;
read(fd, &frm_version, 1);
read(fd, &frm_length, 1);
@@ -84,60 +85,32 @@ ssize_t sfbus_recv_frame(int fd, u_int16_t address, char *buffer)
u_int16_t dst_addr = frm_addr_l | (frm_addr_h << 8);
if (dst_addr != address)
{
return 0;
//return 0;
}
u_int8_t frm_length_counter = frm_length - 3;
// read all bytes:
while (frm_length_counter > 0)
{
read(fd, buffer, 1);
buffer++;
frm_length_counter--;
}
read(fd, &frm_eof, 1);
read(fd, buffer, frm_length - 4); // read n bytes from buffer where n = payload length
// read crc
u_int16_t frm_crc = 0xFFFF; // read crc value
read(fd, &frm_crc, 2);
if (frm_eof == '$')
u_int16_t calc_crc = calc_CRC16(buffer, frm_length - 4); // calculate CRC
if (frm_crc == calc_crc)
{
printf("crc check okay! expected: %04X received: %04X\n", calc_crc, frm_crc);
return frm_length;
}
else
{
print_bufferHexRx(buffer, frm_length - 4, address);
printf("crc check failed! expected: %04X received: %04X\n", calc_crc, frm_crc);
return -1;
}
}
void sfbus_send_frame(int fd, u_int16_t address, u_int8_t length, char *buffer)
{
int frame_size_complete = length + 6;
char *frame = malloc(frame_size_complete);
char *frame_ptr = frame;
*frame = '+'; // startbyte
frame++;
*frame = 0; // protocol version
frame++;
*frame = length + 3; // length
frame++;
*frame = (address);
frame++;
*frame = ((address >> 8));
frame++;
while (length > 0)
{
*frame = *buffer;
length--;
buffer++;
frame++;
}
*frame = '$'; // startbyte
int result = write(fd, frame_ptr, frame_size_complete);
print_bufferHexTx(frame_ptr + 5, frame_size_complete - 6, address);
free(frame_ptr);
}
/*
* Send SFBus frame with protocol version 2.0 and calculated CRC
*/
@@ -157,9 +130,8 @@ void sfbus_send_frame_v2(int fd, u_int16_t address, u_int8_t length, char *buffe
// add crc to end of frame
u_int16_t crc = calc_CRC16(buffer, length); // calculate CRC
*(frame + (frame_size_complete - 1)) = (crc); // address high byte
*(frame + (frame_size_complete - 0)) = ((crc >> 8)); // address low byte
*(frame + (frame_size_complete - 2)) = (crc); // address high byte
*(frame + (frame_size_complete - 1)) = ((crc >> 8)); // address low byte
// send data
int result = write(fd, frame, frame_size_complete);
print_bufferHexTx(frame, frame_size_complete, address);
@@ -194,9 +166,9 @@ int sfbus_read_eeprom(int fd, u_int16_t address, char *buffer)
{
char *cmd = "\xF0";
char *_buffer = malloc(256);
sfbus_send_frame(fd, address, strlen(cmd), cmd);
sfbus_send_frame_v2(fd, address, strlen(cmd), cmd);
int len = sfbus_recv_frame_wait(fd, 0xFFFF, _buffer);
if (len != 9)
if (len != 10)
{
printf("Invalid data!\n");
return -1;
@@ -217,12 +189,12 @@ int sfbus_write_eeprom(int fd, u_int16_t address, char *wbuffer, char *rbuffer)
char *cmd = malloc(5);
*cmd = (char)0xF1; // write eeprom command
memcpy(cmd + 1, wbuffer, 4);
sfbus_send_frame(fd, address, 5, cmd);
sfbus_send_frame_v2(fd, address, 5, cmd);
free(cmd);
// wait for readback
char *_buffer = malloc(256);
int len = sfbus_recv_frame_wait(fd, 0xFFFF, _buffer);
if (len != 9)
if (len != 10)
{
printf("Invalid data!\n");
return -1;
@@ -243,7 +215,7 @@ int sfbus_display(int fd, u_int16_t address, u_int8_t flap)
char *cmd = malloc(5);
*cmd = (char)0x10; // write eeprom command
*(cmd + 1) = flap;
sfbus_send_frame(fd, address, 2, cmd);
sfbus_send_frame_v2(fd, address, 2, cmd);
free(cmd);
return 0;
}
@@ -253,7 +225,7 @@ int sfbus_display_full(int fd, u_int16_t address, u_int8_t flap)
char *cmd = malloc(5);
*cmd = (char)0x11; // write eeprom command
*(cmd + 1) = flap;
sfbus_send_frame(fd, address, 2, cmd);
sfbus_send_frame_v2(fd, address, 2, cmd);
free(cmd);
return 0;
}
@@ -262,7 +234,7 @@ u_int8_t sfbus_read_status(int fd, u_int16_t address, double *voltage, u_int32_t
{
char *cmd = "\xF8";
char *_buffer = malloc(256);
sfbus_send_frame(fd, address, strlen(cmd), cmd);
sfbus_send_frame_v2(fd, address, strlen(cmd), cmd);
int res = sfbus_recv_frame_wait(fd, 0xFFFF, _buffer);
if (res < 0)
{
@@ -282,7 +254,7 @@ u_int8_t sfbus_read_status(int fd, u_int16_t address, double *voltage, u_int32_t
void sfbus_reset_device(int fd, u_int16_t address)
{
char *cmd = "\x30";
sfbus_send_frame(fd, address, strlen(cmd), cmd);
sfbus_send_frame_v2(fd, address, strlen(cmd), cmd);
}
void sfbus_motor_power(int fd, u_int16_t address, u_int8_t state)
@@ -292,7 +264,7 @@ void sfbus_motor_power(int fd, u_int16_t address, u_int8_t state)
{
cmd = "\x21";
}
sfbus_send_frame(fd, address, 1, cmd);
sfbus_send_frame_v2(fd, address, 1, cmd);
}
u_int16_t calc_CRC16(char *buffer, u_int8_t len)

View File

@@ -9,9 +9,9 @@
#include "ftdi485.h"
ssize_t sfbus_recv_frame(int fd, u_int16_t address, char *buffer);
ssize_t sfbus_recv_frame_v2(int fd, u_int16_t address, char *buffer);
ssize_t sfbus_recv_frame_wait(int fd, u_int16_t address, char *buffer);
void sfbus_send_frame(int fd, u_int16_t address, u_int8_t length, char *buffer);
void sfbus_send_frame_v2(int fd, u_int16_t address, u_int8_t length, char *buffer);
void print_charHex(char *buffer, int length);
int sfbus_ping(int fd, u_int16_t address);
int sfbus_read_eeprom(int fd, u_int16_t address, char* buffer);

View File

@@ -18,7 +18,7 @@ json_object *(*commandparser_func)(json_object *);
// this sections handles ws connections and communications
// called on opening websocket client
void onopen(ws_cli_conn_t client)
void ws_opencon(ws_cli_conn_t client)
{
char *cli;
cli = ws_getaddress(client);
@@ -26,7 +26,7 @@ void onopen(ws_cli_conn_t client)
}
// called on closing websocket client
void onclose(ws_cli_conn_t client)
void ws_closecon(ws_cli_conn_t client)
{
char *cli;
cli = ws_getaddress(client);
@@ -34,7 +34,7 @@ void onclose(ws_cli_conn_t client)
}
// called on receiving websocket message
void onmessage(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, int type)
void ws_messagehandler(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, int type)
{
char *cli = ws_getaddress(client);
printf("received message: %s (%zu), from: %s\n", msg, size, cli);
@@ -55,7 +55,7 @@ void onmessage(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, in
printf("test");
if (commandObj != NULL)
{
char *command = json_object_to_json_string(commandObj);
const char *command = json_object_to_json_string(commandObj);
// get key
json_object *res = commandparser_func(req);
if (res == NULL)
@@ -79,11 +79,11 @@ void onmessage(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, in
void send_json_response(ws_cli_conn_t client, json_object *res)
{
char *message = json_object_to_json_string_ext(res, JSON_C_TO_STRING_PRETTY);
const char *message = json_object_to_json_string_ext(res, JSON_C_TO_STRING_PRETTY);
ws_sendframe_txt(client, message);
}
void send_json_error(ws_cli_conn_t client, char *error, char *detail)
void send_json_error(ws_cli_conn_t client, char *error, const char *detail)
{
json_object *root = json_object_new_object();
json_object_object_add(root, "error", json_object_new_string(error));
@@ -105,9 +105,9 @@ int start_webserver(json_object *(*commandparser_func_ptr)(json_object *))
.port = WS_SERVER_PORT,
.thread_loop = 0,
.timeout_ms = 1000,
.evs.onopen = &onopen,
.evs.onclose = &onclose,
.evs.onmessage = &onmessage});
.evs.onopen = &ws_opencon,
.evs.onclose = &ws_closecon,
.evs.onmessage = &ws_messagehandler});
return (0);
}

View File

@@ -18,3 +18,10 @@
#define WS_SERVER_ADDR "localhost"
int start_webserver();
void send_json_error(ws_cli_conn_t client, char *error, const char *detail);
void send_json_response(ws_cli_conn_t client, json_object *res);
void ws_messagehandler(ws_cli_conn_t client, const unsigned char *msg, uint64_t size, int type);
void ws_opencon(ws_cli_conn_t client);
void ws_closecon(ws_cli_conn_t client);