From 6e3f7ade3d54907f0446bf2bacec530205ea496c Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Mon, 11 May 2026 15:27:25 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9C=E7=A8=8B=E9=80=9A=E4=BF=A1=E5=8D=8F?= =?UTF-8?q?=E8=AE=AE=E6=9B=B4=E6=94=B9=E4=B8=BA=20=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E6=9C=BA=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 1 + src/remoteDisplay.c | 266 ++++++++++++++++++--------------- src/remoteDisplay.h | 34 +++-- tests/CMakeLists.txt | 2 + tests/test_p0_remote_display.c | 91 ++++++----- 5 files changed, 223 insertions(+), 171 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 65007ea..4a1647a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ add_executable(DTU-HMI src/common/utf8.c src/thread_utils.c src/remoteDisplay.c + src/remoteDisplayProtocol.c src/Drv/menu/app/menu.c src/Drv/pages/page_manager.c src/Drv/pages/global/global_state.c diff --git a/src/remoteDisplay.c b/src/remoteDisplay.c index 5d3fafe..0efcbf9 100644 --- a/src/remoteDisplay.c +++ b/src/remoteDisplay.c @@ -1,8 +1,11 @@ /* - * remoteDisplay.c - TCP 服务器线程实现 - * 实现 RemoDispBus 协议:解析 remo_disp_server.py 的请求,返回 lcd 显存数据等。 - * 帧格式: [TAG][cmd][len_hi][len_lo][data][crc],CRC = data 区逐字节异或低 8 位。 - * 客户端 TAG=0xAA,设备回复 TAG=0xBB。 + * 模块名称:Remote Display + * 模块功能:实现 RemoDispBus 协议,提供远程 LCD 显示与按键转发功能 + * 适用平台:通用嵌入式平台 + * 作者:王建锋 + * 创建日期:2026-05-11 + * 修改记录: + * 2026-05-11 王建锋 创建初始版本 */ #include @@ -12,53 +15,49 @@ #include "TCP/tcp.h" #include "Drv/key.h" #include "remoteDisplay.h" - -/* RemoDispBus 协议常量(与 remo_disp_server.py 一致) */ -#define TAG_CLIENT 0xAAu -#define TAG_DEVICE 0xBBu -#define CMD_KEEPLIVE 0 -#define CMD_INIT 1 -#define CMD_KEY 2 -#define CMD_LCDMEM 3 - -/* 计算 data 区 CRC:逐字节异或取低 8 位 */ -static uint8_t calc_crc(const uint8_t* data, unsigned int len) -{ - uint8_t crc = 0; - while (len--) - crc ^= *data++; - return crc; -} - -/* 从 recv 缓冲区解析一帧: [TAG][cmd][len_hi][len_lo][data...][crc] - * 成功返回 1 并设置 *p_cmd, *p_data, *p_data_len;失败返回 0。 - * *p_consume 返回本帧总字节数(含头尾),调用方从缓冲区移除。 */ -static int parse_frame(const uint8_t* buf, unsigned int buf_len, - uint8_t* p_cmd, const uint8_t** p_data, unsigned int* p_data_len, - unsigned int* p_consume) -{ - if (buf_len < 5 || buf[0] != TAG_CLIENT) - return 0; - unsigned int data_len = ((unsigned int)buf[2] << 8) | buf[3]; - unsigned int frame_len = 4 + data_len + 1; /* 头4 + data + crc */ - if (buf_len < frame_len) - return 0; - uint8_t crc = calc_crc(buf + 4, data_len); - if (crc != buf[4 + data_len]) - return 0; - *p_cmd = buf[1]; - *p_data = buf + 4; - *p_data_len = data_len; - *p_consume = frame_len; - return 1; -} +#include "remoteDisplayProtocol.h" /* 回复帧最大长度:头4 + 最大 payload(4+LCD_DISPLAYMEMORYSIZE) + crc1 */ #define REMO_REPLY_BUF_SIZE (4 + 4 + LCD_DISPLAYMEMORYSIZE + 1) -/* 构造设备回复帧并发送: [TAG_DEVICE][cmd][len_hi][len_lo][data][crc],一次性发送 */ -static int send_reply(int client, uint8_t cmd, const uint8_t* data, unsigned int data_len) -{ +/* + * 函数功能:计算数据的XOR校验和 + * 入口参数:p_data - 数据缓冲区指针 const uint8_t* 非NULL + * len - 数据长度 unsigned int 0 - PROTOCOL_MAX_DATA_LEN + * 返回值:校验和值 uint8_t 0 - 255 + * 限定条件:p_data指针必须指向有效的内存区域,长度至少为len字节 + * 函数说明:1. 采用XOR算法计算校验和 + * 2. 将所有数据字节进行异或运算 + * 3. 当len为0时,返回0 + */ +static uint8_t calc_crc(const uint8_t *p_data, unsigned int len) { + uint8_t crc = 0; + unsigned int i; + + if (p_data == NULL) { + return 0; + } + + for (i = 0; i < len; i++) { + crc ^= p_data[i]; + } + + return crc; +} + +/* + * 函数功能:构造设备回复帧并发送 + * 入口参数:client - 客户端 socket int 有效的 socket 描述符 + * cmd - 命令字 uint8_t 参考 RemoDispBus 命令定义 + * data - 回复数据指针 const uint8_t* 可为 NULL + * data_len - 回复数据长度 unsigned int 0 - PROTOCOL_MAX_DATA_LEN + * 出口参数:无 + * 返回值:0 - 发送成功 + * -1 - 发送失败或参数非法 + * 限定条件:client 已连接且有效 + * 函数说明:构造 [TAG_DEVICE][cmd][len_hi][len_lo][data][crc] 帧并一次性发送 + */ +static int send_reply(int client, uint8_t cmd, const uint8_t* data, unsigned int data_len) { uint8_t buf[REMO_REPLY_BUF_SIZE]; unsigned int total = 4 + data_len + 1; @@ -75,9 +74,17 @@ static int send_reply(int client, uint8_t cmd, const uint8_t* data, unsigned int return TcpClient_Send(client, (const char*)buf, total) == (int)total ? 0 : -1; } -/* 处理 CMD_LCDMEM:请求 data 为 4 字节大端起始地址;回复 [4B 地址][显存数据] */ -static void handle_cmd_lcdmem(int client, const uint8_t* req_data, unsigned int req_len) -{ +/* + * 函数功能:处理 CMD_LCDMEM 请求并回复显存数据 + * 入口参数:client - 客户端 socket int 有效的 socket 描述符 + * req_data - 请求数据指针 const uint8_t* 期望至少 4 个字节 + * req_len - 请求数据长度 unsigned int 0 - PROTOCOL_MAX_DATA_LEN + * 出口参数:无 + * 返回值:无 + * 限定条件:req_data 可为 NULL,但 req_len 必须有效 + * 函数说明:解析大端起始地址,读取显存并通过 send_reply 发送结果 + */ +static void handle_cmd_lcdmem(int client, const uint8_t* req_data, unsigned int req_len) { const uint8_t *framebuffer = Lcd_GetFrameBuffer(); unsigned int start_addr = 0; @@ -106,9 +113,15 @@ static void handle_cmd_lcdmem(int client, const uint8_t* req_data, unsigned int send_reply(client, CMD_LCDMEM, payload, 4 + copy_len); } -/* 处理 CMD_INIT:回复宽、高、显存大小(与 Python 端约定一致时可扩展) */ -static void handle_cmd_init(int client) -{ +/* + * 函数功能:处理 CMD_INIT 请求并回复 LCD 参数 + * 入口参数:client - 客户端 socket int 有效的 socket 描述符 + * 出口参数:无 + * 返回值:无 + * 限定条件:client 已连接且有效 + * 函数说明:回复 LCD 宽度、高度和显存大小的 8 字节信息 + */ +static void handle_cmd_init(int client) { uint8_t data[8]; data[0] = (LCD_SIZE_X >> 8) & 0xFF; data[1] = LCD_SIZE_X & 0xFF; @@ -121,9 +134,17 @@ static void handle_cmd_init(int client) send_reply(client, CMD_INIT, data, 8); } -/* 处理 CMD_KEY:可选转交菜单,此处仅回 ACK */ -static void handle_cmd_key(int client, const uint8_t* data, unsigned int len) -{ +/* + * 函数功能:处理 CMD_KEY 请求并更新远程按键信号 + * 入口参数:client - 客户端 socket int 有效的 socket 描述符 + * data - 键值数据指针 const uint8_t* 期望至少 1 字节 + * len - 数据长度 unsigned int 0 - PROTOCOL_MAX_DATA_LEN + * 出口参数:无 + * 返回值:无 + * 限定条件:data 必须包含至少 1 个字节键值 + * 函数说明:将按键值写入全局远程按键结构,供主循环处理 + */ +static void handle_cmd_key(int client, const uint8_t* data, unsigned int len) { #ifdef DEBUG printf("handle_cmd_key: 0x%02X\n", data[0]); #endif @@ -131,14 +152,26 @@ static void handle_cmd_key(int client, const uint8_t* data, unsigned int len) g_tRemoteKey.byKeyValue = data[0]; } -/* 处理 CMD_KEEPLIVE:保活,回空 */ -static void handle_cmd_keeplive(int client) -{ +/* + * 函数功能:处理 CMD_KEEPLIVE 请求并回复空帧 + * 入口参数:client - 客户端 socket int 有效的 socket 描述符 + * 出口参数:无 + * 返回值:无 + * 限定条件:client 已连接且有效 + * 函数说明:发送保活响应帧,无数据部分 + */ +static void handle_cmd_keeplive(int client) { send_reply(client, CMD_KEEPLIVE, (const uint8_t*)NULL, 0); } - -void tcp_server_thread_fn(void* arg) -{ +/* + * 函数功能:TCP 服务器线程入口函数 + * 入口参数:arg - 线程参数指针 void* 实际类型为 server_thread_arg_t* + * 出口参数:无 + * 返回值:无 + * 限定条件:arg 必须指向有效的 server_thread_arg_t 实例 + * 函数说明:创建 TCP 监听 socket,接受客户端连接并按 RemoDispBus 协议处理请求 + */ +void tcp_server_thread_fn(void* arg) { server_thread_arg_t* ctx = (server_thread_arg_t*)arg; int server_sock = TcpServer_Listen((uint16_t)ctx->port); if (server_sock == TCP_INVALID_SOCKET) { @@ -148,69 +181,55 @@ void tcp_server_thread_fn(void* arg) *ctx->p_server_sock = server_sock; printf("[TCP Server] RemoDispBus 监听端口 %d,等待客户端连接...\n", ctx->port); -#define REMO_BUF_SIZE 4096 - uint8_t recv_buf[4096]; - unsigned int recv_len = 0; - /* ========== 外层循环:主线程未请求退出时,持续等待并接受新客户端 ========== */ while (!*ctx->p_quit) { /* 阻塞等待一个客户端连接;主线程关闭 server_sock 时 Accept 会失败并返回 INVALID */ int client = TcpServer_Accept(server_sock); - if (client == TCP_INVALID_SOCKET) - { - if (*ctx->p_quit) + if (client == TCP_INVALID_SOCKET) { + if (*ctx->p_quit) { break; + } continue; } printf("[TCP Server] 客户端连接成功\n"); - recv_len = 0; /* 新连接对应新的接收缓冲区,避免混用上一连接的残留数据 */ + + /* 为当前客户端初始化独立的协议解析实例 */ + protocol_instance_t proto_ins; + protocol_init(&proto_ins); /* ========== 内层循环:处理当前连接上的 RemoDispBus 请求,直到断开或退出 ========== */ - while (!*ctx->p_quit) - { - /* ----- 1. 若缓冲区中已有一条完整且校验通过的帧,则解析并分发处理 ----- */ - uint8_t cmd; - const uint8_t* pdata; - unsigned int data_len, consume; - if (parse_frame(recv_buf, recv_len, &cmd, &pdata, &data_len, &consume)) - { - switch (cmd) - { - case CMD_KEEPLIVE: - handle_cmd_keeplive(client); - break; - case CMD_INIT: - handle_cmd_init(client); - break; - case CMD_KEY: - handle_cmd_key(client, pdata, data_len); - break; - case CMD_LCDMEM: - handle_cmd_lcdmem(client, pdata, data_len); - break; - default: - send_reply(client, cmd, (const uint8_t*)NULL, 0); - break; + while (!*ctx->p_quit) { + uint8_t recv_byte; /* 逐字节接收 */ + int n = TcpClient_Recv(client, (uint8_t*)&recv_byte, 1); + if (n > 0) { /* 成功收到1字节 */ + /* 喂给状态机逐字节处理 */ + protocol_byte_handler(&proto_ins, recv_byte); + /* 检查是否有完整帧解析完成 */ + if (proto_ins.frame_done_flag == 1) { + /* 根据解析到的CMD分发处理 */ + switch (proto_ins.recv_cmd) + { + case CMD_KEEPLIVE: + handle_cmd_keeplive(client); + break; + case CMD_INIT: + handle_cmd_init(client); + break; + case CMD_KEY: + handle_cmd_key(client, proto_ins.recv_buf, proto_ins.expect_data_len); + break; + case CMD_LCDMEM: + handle_cmd_lcdmem(client, proto_ins.recv_buf, proto_ins.expect_data_len); + break; + default: + send_reply(client, proto_ins.recv_cmd, (const uint8_t*)NULL, 0); + break; + } + /* 处理完成后,复位协议实例,准备接收下一帧 */ + protocol_init(&proto_ins); } - /* 从缓冲区头部移除已消费的 consume 字节,避免重复解析同一帧 */ - memmove(recv_buf, recv_buf + consume, recv_len - consume); - recv_len -= consume; - continue; /* 继续检查是否还有完整帧,避免不必要的 recv */ } - - /* ----- 2. 缓冲区中尚无完整帧:先防止溢出,再收一批数据 ----- */ - if (recv_len >= REMO_BUF_SIZE - 256) - { - recv_len = 0; /* 异常情况:数据过多且始终不成帧,清空缓冲区防止越界 */ - } - int n = TcpClient_Recv(client, (char*)(recv_buf + recv_len), REMO_BUF_SIZE - recv_len - 1); - if (n > 0) - { - recv_len += (unsigned int)n; - } - else - { - /* n==0 表示对方正常关闭连接,n<0 表示读取出错;均退出本连接循环 */ + else { /* n==0(客户端关闭)或 n<0(读取错误)*/ printf("[TCP Server] 客户端关闭连接\n"); break; } @@ -223,16 +242,17 @@ void tcp_server_thread_fn(void* arg) *ctx->p_server_sock = TCP_INVALID_SOCKET; printf("[TCP Server] 已退出\n"); } -/* ---------------------------------------------------------------------------- - * 启动 TCP 服务与服务器线程(Tcp_Init + 创建线程 + 短暂等待就绪) - * port: 监听端口(如 7070) - * out_server_sock: 输出监听 socket,供主线程退出时 TcpServer_Close - * out_server_quit: 输出退出标志,主线程置 1 通知服务器线程退出 - * out_server_th: 输出线程句柄,供主线程 Thread_Join - * 返回:0 成功,1 失败(已调用 Tcp_Cleanup) - * ---------------------------------------------------------------------------- */ -int StartTcpServerThread(thread_handle_t *out_server_th, server_thread_arg_t *io_server_arg) - { +/* + * 函数功能:启动 TCP 服务并创建服务器线程 + * 入口参数:out_server_th - 输出线程句柄 thread_handle_t* 非空 + * io_server_arg - 输入输出服务器线程参数 server_thread_arg_t* 非空 + * 出口参数:out_server_th - 输出创建的线程句柄 + * 返回值:0 - 成功 + * 1 - 失败(已调用 Tcp_Cleanup) + * 限定条件:io_server_arg 必须初始化并提供有效端口、socket 和退出标志指针 + * 函数说明:初始化 TCP 子系统并创建服务器线程,若失败则清理资源 + */ +int StartTcpServerThread(thread_handle_t *out_server_th, server_thread_arg_t *io_server_arg) { if (Tcp_Init() != 0) { fprintf(stderr, "Tcp_Init failed\n"); return 1; diff --git a/src/remoteDisplay.h b/src/remoteDisplay.h index 6103177..7dc9954 100644 --- a/src/remoteDisplay.h +++ b/src/remoteDisplay.h @@ -1,24 +1,38 @@ -#ifndef __REMOTEDISPLAY_H +#ifndef __REMOTEDISPLAY_H #define __REMOTEDISPLAY_H +/* + * 模块名称:Remote Display + * 模块功能:提供远程显示 TCP 服务器线程接口 + * 适用平台:通用嵌入式平台 + * 作者:王建锋 + * 创建日期:2026-05-11 + * 修改记录: + * 2026-05-11 王建锋 创建初始版本 + */ +#ifdef __cplusplus +extern "C" { +#endif +#include #include "thread_utils.h" /* - * 远程显示 / TCP 服务器相关接口 - * - server_thread_arg_t: TCP 服务器线程参数 - * - tcp_server_thread_fn: 服务器线程入口函数(用于 Thread_Create) + * TCP 服务器线程参数 */ - typedef struct { - int port; /* 监听端口号(如 7070) */ - int* p_server_sock; /* 线程内赋值为监听 socket,主线程用其关闭监听 */ - volatile int* p_quit; /* 主线程置 1 通知线程退出 */ + uint16_t port; /* 监听端口号(如 7070) */ + int *p_server_sock; /* 线程内赋值为监听 socket,主线程用其关闭监听 */ + volatile int *p_quit; /* 主线程置 1 通知线程退出 */ } server_thread_arg_t; -int StartTcpServerThread(thread_handle_t *out_server_th, server_thread_arg_t *io_server_arg); +int StartTcpServerThread(thread_handle_t *out_server_th, + server_thread_arg_t *io_server_arg); +#ifdef __cplusplus +} +#endif -#endif +#endif /* __REMOTEDISPLAY_H */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ccc6698..2aeb068 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -52,6 +52,7 @@ add_dtu_test(test_smoke tests_smoke.c) add_dtu_test( test_p0_remote_display test_p0_remote_display.c + "${CMAKE_SOURCE_DIR}/src/remoteDisplayProtocol.c" "${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd.c" "${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd_draw.c" "${CMAKE_SOURCE_DIR}/src/Drv/lcd/lcd_text.c" @@ -60,6 +61,7 @@ add_dtu_test( "${CMAKE_SOURCE_DIR}/src/TCP/tcp.c" "${CMAKE_SOURCE_DIR}/src/thread_utils.c" ) +target_compile_definitions(test_p0_remote_display PRIVATE UNIT_TEST) add_dtu_test( test_p0_utf8_hz12_get test_p0_utf8_hz12_get.c diff --git a/tests/test_p0_remote_display.c b/tests/test_p0_remote_display.c index 138ba4d..41890d0 100644 --- a/tests/test_p0_remote_display.c +++ b/tests/test_p0_remote_display.c @@ -1,13 +1,13 @@ /* P0 测试:remoteDisplay 协议核心 * 目标: * - 校验 CRC 纯函数行为(calc_crc) - * - 校验 RemoDispBus 帧解析边界(parse_frame) + * - 校验 Protocol_ByteHandler 状态机解析行为 */ #include +#include #include "test_common.h" -/* 直接包含 .c 以访问 static 函数(calc_crc / parse_frame) */ -#include "../src/remoteDisplay.c" +#include "../src/remoteDisplayProtocol.h" /* 用例1:CRC 计算 * - 空数据长度应返回 0 @@ -21,49 +21,64 @@ static int test_calc_crc(void) return 0; } -/* 用例2:合法帧解析 - * 验证: - * - parse_frame 返回成功 - * - cmd/data/data_len/consume 与期望一致 +/* 用例2:Protocol_ByteHandler 基本帧解析 + * 验证:完整 RemoDispBus 帧按字节输入后,状态机完成帧解析并保持正确 cmd/data */ -static int test_parse_frame_ok(void) +static int test_protocol_byte_handler_ok(void) { - uint8_t cmd = 0; - const uint8_t* data = NULL; - unsigned int data_len = 0; - unsigned int consume = 0; + protocol_instance_t proto_ins; const uint8_t payload[] = {0x10, 0x20}; - const uint8_t frame[] = {0xAA, 0x03, 0x00, 0x02, 0x10, 0x20, (uint8_t)(0x10 ^ 0x20)}; + const uint8_t frame[] = { + TAG_CLIENT, + CMD_KEY, + 0x00, + (uint8_t)sizeof(payload), + payload[0], + payload[1], + (uint8_t)(payload[0] ^ payload[1]) + }; - ASSERT_EQ_INT(1, parse_frame(frame, sizeof(frame), &cmd, &data, &data_len, &consume)); - ASSERT_EQ_INT(0x03, cmd); - ASSERT_EQ_INT(2, (int)data_len); - ASSERT_EQ_INT((int)sizeof(frame), (int)consume); - ASSERT_EQ_INT(payload[0], data[0]); - ASSERT_EQ_INT(payload[1], data[1]); - + protocol_init(&proto_ins); + ASSERT_EQ_INT(PROTO_STATE_WAIT_TAG, proto_ins.state); + ASSERT_EQ_INT(0, proto_ins.frame_done_flag); + + for (unsigned int i = 0; i < sizeof(frame); ++i) { + protocol_byte_handler(&proto_ins, frame[i]); + } + + ASSERT_EQ_INT(1, proto_ins.frame_done_flag); + ASSERT_EQ_INT(CMD_KEY, proto_ins.recv_cmd); + ASSERT_EQ_INT((int)sizeof(payload), (int)proto_ins.expect_data_len); + ASSERT_EQ_INT((int)sizeof(payload), (int)proto_ins.recv_len); + ASSERT_EQ_INT(payload[0], proto_ins.recv_buf[0]); + ASSERT_EQ_INT(payload[1], proto_ins.recv_buf[1]); return 0; } -/* 用例3:非法输入集合 - * - TAG 非客户端 TAG - * - 帧长度不足(截断) - * - CRC 错误 - * 期望均解析失败(返回 0) +/* 用例3:Protocol_ByteHandler CRC 错误处理 + * 验证:CRC 校验失败后状态机重置为 WAIT_TAG,不置完成标志 */ -static int test_parse_frame_invalid_cases(void) +static int test_protocol_byte_handler_bad_crc(void) { - uint8_t cmd = 0; - const uint8_t* data = NULL; - unsigned int data_len = 0; - unsigned int consume = 0; - const uint8_t bad_tag[] = {0xBB, 0x03, 0x00, 0x00, 0x00}; - const uint8_t short_buf[] = {0xAA, 0x03, 0x00, 0x01}; - const uint8_t bad_crc[] = {0xAA, 0x02, 0x00, 0x01, 0x55, 0x00}; + protocol_instance_t proto_ins; + const uint8_t payload[] = {0x10, 0x20}; + const uint8_t frame[] = { + TAG_CLIENT, + CMD_KEY, + 0x00, + (uint8_t)sizeof(payload), + payload[0], + payload[1], + 0xFF + }; - ASSERT_EQ_INT(0, parse_frame(bad_tag, sizeof(bad_tag), &cmd, &data, &data_len, &consume)); - ASSERT_EQ_INT(0, parse_frame(short_buf, sizeof(short_buf), &cmd, &data, &data_len, &consume)); - ASSERT_EQ_INT(0, parse_frame(bad_crc, sizeof(bad_crc), &cmd, &data, &data_len, &consume)); + protocol_init(&proto_ins); + for (unsigned int i = 0; i < sizeof(frame); ++i) { + protocol_byte_handler(&proto_ins, frame[i]); + } + + ASSERT_EQ_INT(PROTO_STATE_WAIT_TAG, proto_ins.state); + ASSERT_EQ_INT(0, proto_ins.frame_done_flag); return 0; } @@ -71,7 +86,7 @@ static int test_parse_frame_invalid_cases(void) int main(void) { if (test_calc_crc() != 0) return 1; - if (test_parse_frame_ok() != 0) return 1; - if (test_parse_frame_invalid_cases() != 0) return 1; + if (test_protocol_byte_handler_ok() != 0) return 1; + if (test_protocol_byte_handler_bad_crc() != 0) return 1; return 0; }