Skip to content
forked from aeo123/upacker

RT THREAD 支持; 用于端对端通讯数据封包、解包,解决各种粘包、分包问题。极简内存占用。

Notifications You must be signed in to change notification settings

luzefeng123/upacker

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Upacker

用于段对端通讯数据封包、解包,解决各种粘包、分包问题。极简内存占用。

软件包位置: /packages/misc/upacker

数据帧格式


--每包数据负载长度最长位14位16384字节-- 每帧数据含4字节Header和N字节负载,包含14位数据长度,4位Header校验,6位负载校验

D0 D1 D2 D3
D0[7:0] D1[7:0] D2[5:0] D2[7:6] D3[1:0] D3[7:2]
包头 包长(低8) 包长(高6) Header校验[3:2] Header校验[5:4] check[7:2]
0x55 0XFF 0X3F 0X0C 0X30 0XFC
因为头校验只有四位, 在一定概率下容易出错, 所以调整了一下协议内容 
 1 包长         14位 -> 16位
 2 header校验    4位 -> 8位 
 3 数据校验      6位  -> 16位 
 
相比之前的协议, 总计增加了 16位 , 两个字节
调整如下 

D0 D1 D2 D3 D4 D5
D0[7:0] D1[7:0] D2[7:0] D2[7:0] D4[7:0] D5[7:0]
包头 包长(低8) 包长(高8) Header校验 check[7:0] check[16:8]
0x55 0XFF 0X3F 0X0C 0X30 0XFC

使用

配置

packer内部需要一段内存用于保存解析完成的包,可以配置为静态内存或者动态内存。 内存分配的长度为MAX_PACK_SIZE,根据应用需要自行调节

#define USE_DYNAMIC_MEM 0
#define MAX_PACK_SIZE 1024 //最长消息长度,最大可用14位即16384

完整的packer结构体

//使用动态内存
#define USE_DYNAMIC_MEM 0

#if USE_DYNAMIC_MEM
#define UP_MALLOC
#define UP_FREE
#endif

#define MAX_PACK_SIZE 1024 //最长消息长度,最大可用14位即16384
#define STX_L 0X55         //数据包头

typedef void (*PACKER_CB)(uint8_t *d, uint16_t s);
    
typedef struct
{
#if !USE_DYNAMIC_MEM
        uint8_t data[MAX_PACK_SIZE]; //payload的内存
#else
        uint8_t *data; //用来做payload序列化的内存
#endif
        uint16_t flen; //frame长度
        uint8_t calc;  //frame校验计算值
        uint8_t check; //frame校验值
        uint8_t state; //frame解析状态
        uint16_t cnt;  //frame数据接收cnt
    
        PACKER_CB cb;   //数据包处理回调
        PACKER_CB send; //数据发送回调
} upacker_inst;

实例一个packer

#include "upacker.h"
upacker_inst msg_packer;

初始化,需要用户自行实现两个函数

void data_send(uint8_t *d, uint16_t size);       //发送数据
void handle_callback(uint8_t *d, uint16_t size); //解包成功后的处理回调
/**
  * @brief  串口dma发送接口
  * @note   
  * @param  *d: 
  * @param  size: 
  * @retval None
  */
static void uart_send(uint8_t *d, uint16_t size)
{
    dbuff_push(&uart3_dbuff, d, size);
}


/**
  * @brief  消息解析回调
  * @note   
  * @param  *d: 
  * @param  size: 
  * @retval None
  */
static void handle_cb(uint8_t *d, uint16_t size)
{
    //接收到payload
    rt_kprintf("pack len%d", size);
}


//init packer
upacker_init(&msg_packer, handle_cb, uart_send);

解析数据

//使用串口dma接收数据的示例
static void thread_entry(void *parameter)
{
    struct rx_msg msg;
    rt_err_t result;
    rt_uint32_t rx_length;
    static char rx_buffer[UART_QUE_LEN + 1];

    while (1)
    {
        rt_memset(&msg, 0, sizeof(msg));
        /* 从消息队列中读取消息*/
        result = rt_mq_recv(&rx_mq, &msg, sizeof(msg), 10);
        if (result == RT_EOK)
        {
            /* 从串口读取数据*/
            rx_length = rt_device_read(msg.dev, 0, rx_buffer, msg.size);
            //丢到packer解析,成功了调用callback
            upacker_unpack(&msg_packer, (uint8_t *)rx_buffer, msg.size);
        }
    }
}

封包数据

        buff[0] = 0x40;
        buff[1] = 0x40;
        upacker_pack(&msg_packer, (uint8_t *)buff, 2);

应用建议


最简单的协议示例,一个字节用来设置指令类型,后面接数据。

#define CMD1_CODE 0X40
#define CMD2_CODE 0X41

static void handle_cb(uint8_t *d, uint16_t size)
{
    //接收到payload
    rt_kprintf("pack len%d", size);

    if(d[0] == CMD1_CODE){
        //处理功能1的数据
        rt_kprintf("cmd1 data: %d", d[1]);
    }else if(d[0] == CMD2_CODE){
        //处理功能2的数据    
        rt_kprintf("cmd2 data: %d", d[1]);
    }
}

//发送cmd1数据
void send_cmd1(){
    buff[0] = 0x40;
    buff[1] = 55;
    upacker_pack(&msg_packer, (uint8_t *)buff, 2);
}     

使用json序列化数据,把json用来pack传输,收到一帧直接反序列化


使用msgpack序列化数据,和json类似


使用protobuf序列化数据,类似

代码从项目工程复制出来的,有运行问题请提issue

About

RT THREAD 支持; 用于端对端通讯数据封包、解包,解决各种粘包、分包问题。极简内存占用。

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C 43.0%
  • C++ 41.5%
  • Java 14.5%
  • Python 1.0%