mirror of
https://github.com/sipeed/MaixCDK.git
synced 2026-11-09 17:04:38 -06:00
fix(protocol): include error code in response length
This commit is contained in:
@@ -13,6 +13,27 @@
|
||||
namespace maix::protocol
|
||||
{
|
||||
uint32_t HEADER = 0xBBACCAAA;
|
||||
|
||||
namespace
|
||||
{
|
||||
// Frame layout:
|
||||
// [header][data_len][flags][cmd][body][crc16]
|
||||
// RESP_ERR stores the error code as the first byte of body.
|
||||
constexpr int HEADER_LEN = sizeof(uint32_t);
|
||||
constexpr int DATA_LEN_FIELD_LEN = sizeof(uint32_t);
|
||||
constexpr int FLAGS_LEN = sizeof(uint8_t);
|
||||
constexpr int CMD_LEN = sizeof(uint8_t);
|
||||
constexpr int CRC_LEN = sizeof(uint16_t);
|
||||
constexpr int ERROR_CODE_LEN = sizeof(uint8_t);
|
||||
constexpr int DATA_LEN_OFFSET = HEADER_LEN;
|
||||
constexpr int FLAGS_OFFSET = HEADER_LEN + DATA_LEN_FIELD_LEN;
|
||||
constexpr int CMD_OFFSET = FLAGS_OFFSET + FLAGS_LEN;
|
||||
constexpr int BODY_OFFSET = CMD_OFFSET + CMD_LEN;
|
||||
constexpr int FRAME_DATA_OFFSET = HEADER_LEN + DATA_LEN_FIELD_LEN;
|
||||
constexpr int FRAME_DATA_OVERHEAD_LEN = FLAGS_LEN + CMD_LEN + CRC_LEN;
|
||||
constexpr int FRAME_OVERHEAD_LEN = HEADER_LEN + DATA_LEN_FIELD_LEN + FRAME_DATA_OVERHEAD_LEN;
|
||||
}
|
||||
|
||||
uint16_t crc16_IBM(uint8_t *ptr, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
@@ -47,32 +68,40 @@ namespace maix::protocol
|
||||
|
||||
if (version != VERSION)
|
||||
return -err::ERR_ARGS;
|
||||
if (out_buff_len < body_len + 12)
|
||||
if (body_len < 0 || (body_len > 0 && !body))
|
||||
return -err::ERR_ARGS;
|
||||
const int code_len = code == 0xFF ? 0 : ERROR_CODE_LEN;
|
||||
const int frame_len = body_len + FRAME_OVERHEAD_LEN + code_len;
|
||||
const int data_len = body_len + FRAME_DATA_OVERHEAD_LEN + code_len;
|
||||
if (out_buff_len < frame_len)
|
||||
return -err::ERR_ARGS;
|
||||
((uint32_t *)out_buff)[0] = HEADER;
|
||||
((uint32_t *)out_buff)[1] = body_len + 4;
|
||||
out_buff[8] = flags | version;
|
||||
out_buff[9] = cmd;
|
||||
((uint32_t *)out_buff)[1] = data_len;
|
||||
out_buff[FLAGS_OFFSET] = flags | version;
|
||||
out_buff[CMD_OFFSET] = cmd;
|
||||
const int crc_offset = BODY_OFFSET + code_len + body_len;
|
||||
if (code != 0xFF)
|
||||
{
|
||||
out_buff[10] = code;
|
||||
memcpy(out_buff + 11, body, body_len);
|
||||
uint16_t crc16 = crc16_IBM(out_buff, body_len + 11);
|
||||
out_buff[11 + body_len] = crc16 & 0xFF;
|
||||
out_buff[12 + body_len] = crc16 >> 8 & 0xFF;
|
||||
return body_len + 13;
|
||||
out_buff[BODY_OFFSET] = code;
|
||||
if (body_len > 0)
|
||||
memcpy(out_buff + BODY_OFFSET + ERROR_CODE_LEN, body, body_len);
|
||||
uint16_t crc16 = crc16_IBM(out_buff, crc_offset);
|
||||
out_buff[crc_offset] = crc16 & 0xFF;
|
||||
out_buff[crc_offset + 1] = crc16 >> 8 & 0xFF;
|
||||
return frame_len;
|
||||
}
|
||||
memcpy(out_buff + 10, body, body_len);
|
||||
uint16_t crc16 = crc16_IBM(out_buff, body_len + 10);
|
||||
out_buff[10 + body_len] = crc16 & 0xFF;
|
||||
out_buff[11 + body_len] = crc16 >> 8 & 0xFF;
|
||||
return body_len + 12;
|
||||
if (body_len > 0)
|
||||
memcpy(out_buff + BODY_OFFSET, body, body_len);
|
||||
uint16_t crc16 = crc16_IBM(out_buff, crc_offset);
|
||||
out_buff[crc_offset] = crc16 & 0xFF;
|
||||
out_buff[crc_offset + 1] = crc16 >> 8 & 0xFF;
|
||||
return frame_len;
|
||||
}
|
||||
|
||||
Bytes *encode_resp_ok(uint8_t cmd, uint8_t *body, int body_len)
|
||||
{
|
||||
uint8_t *buff = new uint8_t[12 + body_len];
|
||||
int len = encode(buff, 12 + body_len, cmd, FLAG_RESP | FLAG_RESP_OK, body, body_len);
|
||||
uint8_t *buff = new uint8_t[FRAME_OVERHEAD_LEN + body_len];
|
||||
int len = encode(buff, FRAME_OVERHEAD_LEN + body_len, cmd, FLAG_RESP | FLAG_RESP_OK, body, body_len);
|
||||
if (len < 0)
|
||||
{
|
||||
delete[] buff;
|
||||
@@ -84,14 +113,16 @@ namespace maix::protocol
|
||||
|
||||
Bytes *encode_resp_ok(uint8_t cmd, Bytes *body)
|
||||
{
|
||||
if (!body)
|
||||
return protocol::encode_resp_ok(cmd, nullptr, 0);
|
||||
int body_len = body->size();
|
||||
return protocol::encode_resp_ok(cmd, body->data, body_len);
|
||||
}
|
||||
|
||||
Bytes *encode_resp_err(uint8_t cmd, err::Err code, const std::string &msg)
|
||||
{
|
||||
uint8_t *buff = new uint8_t[13 + msg.length()];
|
||||
int len = encode(buff, 13 + msg.length(), cmd, FLAG_RESP | FLAG_RESP_ERR, (uint8_t *)msg.c_str(), msg.length(), code);
|
||||
uint8_t *buff = new uint8_t[FRAME_OVERHEAD_LEN + ERROR_CODE_LEN + msg.length()];
|
||||
int len = encode(buff, FRAME_OVERHEAD_LEN + ERROR_CODE_LEN + msg.length(), cmd, FLAG_RESP | FLAG_RESP_ERR, (uint8_t *)msg.c_str(), msg.length(), code);
|
||||
if (len < 0)
|
||||
{
|
||||
delete[] buff;
|
||||
@@ -116,12 +147,12 @@ namespace maix::protocol
|
||||
size_t data_len = 0;
|
||||
|
||||
*idx = 0;
|
||||
if (len < 12)
|
||||
if (len < FRAME_OVERHEAD_LEN)
|
||||
return false;
|
||||
// find header
|
||||
uint32_t i = 0;
|
||||
bool found = false;
|
||||
for (; i < (uint32_t)len - 4; i++)
|
||||
for (; i < (uint32_t)len - HEADER_LEN; i++)
|
||||
{
|
||||
if (data[i] == (header & 0xFF) &&
|
||||
data[i + 1] == ((header >> 8) & 0xFF) &&
|
||||
@@ -132,35 +163,39 @@ namespace maix::protocol
|
||||
break;
|
||||
}
|
||||
}
|
||||
// len >= 12
|
||||
// Have at least one full minimal frame after the header position.
|
||||
if (!found)
|
||||
{
|
||||
*idx = i;
|
||||
return false;
|
||||
}
|
||||
if (len - i < 12)
|
||||
if (len - i < FRAME_OVERHEAD_LEN)
|
||||
return false;
|
||||
|
||||
// get data_len, and check data length
|
||||
data_len = data[i + 4] | (data[i + 5] << 8) | (data[i + 6] << 16) | (data[i + 7] << 24);
|
||||
if (data_len > len - i - 8)
|
||||
data_len = data[i + DATA_LEN_OFFSET] |
|
||||
(data[i + DATA_LEN_OFFSET + 1] << 8) |
|
||||
(data[i + DATA_LEN_OFFSET + 2] << 16) |
|
||||
(data[i + DATA_LEN_OFFSET + 3] << 24);
|
||||
if (data_len < FRAME_DATA_OVERHEAD_LEN || data_len > len - i - FRAME_DATA_OFFSET)
|
||||
return false;
|
||||
*idx = i + 8 + data_len;
|
||||
*idx = i + FRAME_DATA_OFFSET + data_len;
|
||||
// check crc
|
||||
uint16_t crc16 = crc16_IBM(data + i, data_len + 6);
|
||||
if (data[i + 6 + data_len] != (crc16 & 0xFF) || data[i + 7 + data_len] != (crc16 >> 8 & 0xFF))
|
||||
const int crc_offset = i + FRAME_DATA_OFFSET + data_len - CRC_LEN;
|
||||
uint16_t crc16 = crc16_IBM(data + i, FRAME_DATA_OFFSET + data_len - CRC_LEN);
|
||||
if (data[crc_offset] != (crc16 & 0xFF) || data[crc_offset + 1] != (crc16 >> 8 & 0xFF))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// parse data
|
||||
frame->version = data[i + 8] & FLAG_VERSION_MASK;
|
||||
frame->is_resp = data[i + 8] & FLAG_IS_RESP_MASK;
|
||||
frame->version = data[i + FLAGS_OFFSET] & FLAG_VERSION_MASK;
|
||||
frame->is_resp = data[i + FLAGS_OFFSET] & FLAG_IS_RESP_MASK;
|
||||
frame->is_req = !frame->is_resp;
|
||||
frame->is_report = data[i + 8] & FLAG_REPORT_MASK;
|
||||
frame->resp_ok = data[i + 8] & FLAG_RESP_OK_MASK;
|
||||
frame->cmd = data[i + 9];
|
||||
frame->set_body(data + i + 10, data_len - 4);
|
||||
frame->body_len = data_len - 4;
|
||||
frame->is_report = data[i + FLAGS_OFFSET] & FLAG_REPORT_MASK;
|
||||
frame->resp_ok = data[i + FLAGS_OFFSET] & FLAG_RESP_OK_MASK;
|
||||
frame->cmd = data[i + CMD_OFFSET];
|
||||
frame->set_body(data + i + BODY_OFFSET, data_len - FRAME_DATA_OVERHEAD_LEN);
|
||||
frame->body_len = data_len - FRAME_DATA_OVERHEAD_LEN;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -210,6 +245,8 @@ namespace maix::protocol
|
||||
|
||||
Bytes *MSG::encode_resp_ok(Bytes *body)
|
||||
{
|
||||
if (!body)
|
||||
return protocol::encode_resp_ok(this->cmd, nullptr, 0);
|
||||
return protocol::encode_resp_ok(this->cmd, body);
|
||||
}
|
||||
|
||||
@@ -220,8 +257,8 @@ namespace maix::protocol
|
||||
|
||||
Bytes *MSG::encode_report(uint8_t *body, int body_len)
|
||||
{
|
||||
uint8_t *buff = new uint8_t[12 + body_len];
|
||||
int len = protocol::encode(buff, 12 + body_len, this->cmd, FLAG_RESP | FLAG_RESP_OK | FLAG_REPORT, body, body_len);
|
||||
uint8_t *buff = new uint8_t[FRAME_OVERHEAD_LEN + body_len];
|
||||
int len = protocol::encode(buff, FRAME_OVERHEAD_LEN + body_len, this->cmd, FLAG_RESP | FLAG_RESP_OK | FLAG_REPORT, body, body_len);
|
||||
if (len < 0)
|
||||
{
|
||||
delete[] buff;
|
||||
@@ -233,8 +270,10 @@ namespace maix::protocol
|
||||
|
||||
Bytes *MSG::encode_report(Bytes *body)
|
||||
{
|
||||
uint8_t *buff = new uint8_t[12 + body_len];
|
||||
int len = protocol::encode(buff, 12 + body_len, this->cmd, FLAG_RESP | FLAG_RESP_OK | FLAG_REPORT, body->data, (int)body->size());
|
||||
int body_len = body ? (int)body->size() : 0;
|
||||
uint8_t *body_data = body ? body->data : nullptr;
|
||||
uint8_t *buff = new uint8_t[FRAME_OVERHEAD_LEN + body_len];
|
||||
int len = protocol::encode(buff, FRAME_OVERHEAD_LEN + body_len, this->cmd, FLAG_RESP | FLAG_RESP_OK | FLAG_REPORT, body_data, body_len);
|
||||
if (len < 0)
|
||||
{
|
||||
delete[] buff;
|
||||
@@ -283,6 +322,8 @@ namespace maix::protocol
|
||||
|
||||
Bytes *Protocol::encode_resp_ok(uint8_t cmd, Bytes *body)
|
||||
{
|
||||
if (!body)
|
||||
return protocol::encode_resp_ok(cmd, nullptr, 0);
|
||||
return protocol::encode_resp_ok(cmd, body);
|
||||
}
|
||||
|
||||
@@ -293,8 +334,8 @@ namespace maix::protocol
|
||||
|
||||
Bytes *Protocol::encode_report(uint8_t cmd, uint8_t *body, int body_len)
|
||||
{
|
||||
uint8_t *buff = new uint8_t[12 + body_len];
|
||||
int len = protocol::encode(buff, 12 + body_len, cmd, FLAG_RESP | FLAG_RESP_OK | FLAG_REPORT, body, body_len);
|
||||
uint8_t *buff = new uint8_t[FRAME_OVERHEAD_LEN + body_len];
|
||||
int len = protocol::encode(buff, FRAME_OVERHEAD_LEN + body_len, cmd, FLAG_RESP | FLAG_RESP_OK | FLAG_REPORT, body, body_len);
|
||||
if (len < 0)
|
||||
{
|
||||
delete[] buff;
|
||||
@@ -306,6 +347,8 @@ namespace maix::protocol
|
||||
|
||||
Bytes *Protocol::encode_report(uint8_t cmd, Bytes *body)
|
||||
{
|
||||
if (!body)
|
||||
return Protocol::encode_report(cmd, nullptr, 0);
|
||||
return Protocol::encode_report(cmd, body->data, body->size());
|
||||
}
|
||||
|
||||
@@ -344,7 +387,7 @@ namespace maix::protocol
|
||||
|
||||
err::Err Protocol::push_data(const Bytes *new_data)
|
||||
{
|
||||
return push_data((uint8_t *)&new_data[0], new_data->size());
|
||||
return push_data(new_data->data, new_data->size());
|
||||
}
|
||||
|
||||
MSG *Protocol::decode(uint8_t *new_data, size_t len)
|
||||
@@ -372,7 +415,9 @@ namespace maix::protocol
|
||||
|
||||
MSG *Protocol::decode(const Bytes *new_data)
|
||||
{
|
||||
return decode((uint8_t *)&new_data[0], new_data->size());
|
||||
if (!new_data)
|
||||
return decode(nullptr, 0);
|
||||
return decode(new_data->data, new_data->size());
|
||||
}
|
||||
|
||||
} // namespace maix::protocol
|
||||
|
||||
@@ -48,12 +48,12 @@ The master device can implement the protocol based on the chip and programming l
|
||||
| Hex Example | 0xBBACCAAA | 0x00000009 | 0x00 | 0x01 | hello | 0x442B |
|
||||
| Byte Stream (Hex) | AA CA AC BB | 09 00 00 00 | 00 | 01 | 68 65 6C 6C 6F | 2B 44 |
|
||||
|
||||
> **Note**: Multi-byte data uses little-endian (LE) encoding. For example, `data_len` is `0x00000006`, with `06` in the lowest byte, so it is sent first as `0x06`, followed by `0x00 0x00 0x00`.
|
||||
> **Note**: Multi-byte data uses little-endian (LE) encoding. For example, `data_len` is `0x00000009`, with `09` in the lowest byte, so it is sent first as `0x09`, followed by `0x00 0x00 0x00`.
|
||||
> Strings are sent in order, e.g., `hello` is sent as `h`, then `e`, `l`, `l`, `o`.
|
||||
> In this example, the final data sent is: `AA CA AC BB 09 00 00 00 00 01 68 65 6C 6C 6F 2B 44`.
|
||||
|
||||
- `header`: 4-byte header marking the start of a frame, fixed as `0xAA 0xCA 0xAC 0xBB`, sent starting with `0xAA`.
|
||||
- `data len`: 4-byte data length, including the length of `flags`, `cmd`, `body`, and `CRC`.
|
||||
- `data len`: 4-byte data length, including the length of `flags`, `cmd`, `body`, and `CRC`, excluding `header` and `data len` itself.
|
||||
- `flags`: 1-byte flag where each bit indicates:
|
||||
- MSB `is_resp`: Indicates if it is a response. `0` for request, `1` for response or active report (requires the third highest bit set to `1`).
|
||||
- Second highest bit `resp_ok`:
|
||||
@@ -89,7 +89,7 @@ Set the highest bit of `flags` (`is_resp`) to `1`.
|
||||
- For a successful response: Set `resp_ok` to `1`, and the `body` content is determined by `cmd`. An empty `body` is the simplest successful response.
|
||||
- For a failed response:
|
||||
- Set `resp_ok` to `0`.
|
||||
- The first byte of `body` is the error code. Refer to [MaixCDK maix.err.Err](../../../components/basic/include/maix_err.hpp) for error codes.
|
||||
- The first byte of `body` is the error code. Refer to [MaixCDK maix.err.Err](../../../components/basic/include/maix_err.hpp) for error codes. This error-code byte is included in both the `body` length and the frame `data_len`.
|
||||
- The following bytes in `body` contain the error message in `UTF-8` encoding, preferably in plain English for better compatibility.
|
||||
|
||||
Each request must have a corresponding response, indicating either success or failure. Hereafter, `RESP_OK` and `RESP_ERR` are used to represent successful and failed responses, respectively.
|
||||
@@ -120,7 +120,7 @@ The MCU requests the list of applications from the Maix device. The steps are as
|
||||
`AA CA AC BB 04 00 00 00 01 F9 C9 77`.
|
||||
|
||||
2. The Maix device responds with `cmd` set to `0xF9` and `flags` set to `0xC1` (with `|0x80` for response and `|0x40` for success). The `body` contains the application list. If there are two applications, the actual byte stream is:
|
||||
`AA CA AC BB 0A 00 00 00 C1 F9 02 66 61 63 65 00 66 61 63 65 00 F6 06`.
|
||||
`AA CA AC BB 0F 00 00 00 C1 F9 02 66 61 63 65 00 66 61 63 65 00 E5 57`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -8,5 +8,7 @@ Maix Comm Protocol 内部根据系统设置的通信方式(比如 uart),
|
||||
用户需要使用串口时构建UART对象构造函数会先检查是否已经注册过(比如判断已经注册过的obj使用的UART编号和即将初始化的UART编号是否相同),
|
||||
如果没有注册过就普通构造,已经注册过那就先回调设置的callback函数,callback 负责释放obj(注意 uart 里面 callback后UART不要再使用register_comm_callback传进来的obj了),然后再构造。
|
||||
|
||||
协议帧的 `data_len` 包含 `flags` + `cmd` + `body` + `CRC`,不包含 `header` 和 `data_len` 自身。
|
||||
错误响应的错误码是 `body` 的第一个字节,后面才是错误字符串,所以错误码必须计入 `body` 长度、`data_len`、CRC 输入长度以及发送总长度。
|
||||
|
||||
|
||||
|
||||
@@ -43,12 +43,12 @@ update:
|
||||
| 十六进制示例 | 0xBBACCAAA | 0x00000009 | 0x00 | 0x01 | hello | 0x442B |
|
||||
| 字节流(十六进制)| AA CA AC BB | 09 00 00 00 | 00 | 01 | 68 65 6c 6c 6F | 2B 44 |
|
||||
|
||||
> **注意**这里超过一个字节的数据均采用小端(LE)编码,比如这里 `data_len` 为 `0x00000006`,`06`在最低位,发送时需要先发送`0x06`再发送`0x00` `0x00` `0x00`。
|
||||
> **注意**这里超过一个字节的数据均采用小端(LE)编码,比如这里 `data_len` 为 `0x00000009`,`09`在最低位,发送时需要先发送`0x09`再发送`0x00` `0x00` `0x00`。
|
||||
> 字符串就按照顺序发即可,比如`hello`,先发`h`再发`e` `l` `l` `o`。
|
||||
> 这里的例子,最终发送数据: `AA CA AC BB 09 00 00 00 00 01 68 65 6c 6c 6F 2B 44`。
|
||||
|
||||
* `header`: 头,4 字节,用来标识一帧的开始,固定为 4 字节 `0xAA 0xCA 0xAC 0xBB`, 先发`0xAA`
|
||||
* `data len`: 数据长度, 4 字节, 包含了 `flags` `cmd` + `body` + `CRC` 的长度。
|
||||
* `data len`: 数据长度, 4 字节, 包含了 `flags` + `cmd` + `body` + `CRC` 的长度,不包含 `header` 和 `data len` 自身。
|
||||
* `flags`: 一个字节,各个位分别表示:
|
||||
* 最高位 `is_resp`: 是否是响应,`0`代表发送请求,`1`代表 响应 或者 主动上报(第三高位需要置`1`)。
|
||||
* 第二高位 `resp_ok`:
|
||||
@@ -82,7 +82,7 @@ update:
|
||||
* 对于成功响应:`resp_ok` 设置为 `1`, `body` 内容由`cmd`决定, 其中最简单的成功响应即 `body` 为空。
|
||||
* 对是失败响应:
|
||||
* `resp_ok`设置为 `0`。
|
||||
* `body` 第一个字节为错误码, 具体的错误码见[MaixCDK maix.err.Err](../../../components/basic/include/maix_err.hpp)。
|
||||
* `body` 第一个字节为错误码, 具体的错误码见[MaixCDK maix.err.Err](../../../components/basic/include/maix_err.hpp),这个错误码字节也计入 `body` 长度和帧头中的 `data_len`。
|
||||
* `body` 后面的字节为错误字符串信息,`UTF-8` 编码,一般情况下建议用纯英文,提高兼容性。
|
||||
|
||||
每个请求均应有对应的响应,即执行成功或者失败,后文均用`RESP_OK`和`RESP_ERR`来代替执行成功和失败两种响应。
|
||||
@@ -110,7 +110,7 @@ update:
|
||||
`AA CA AC BB 04 00 00 00 01 F9 C9 77`。
|
||||
|
||||
2. Maix 设备收到请求后,返回`cmd`为`0xF9`, `flags`为`0x01 | 0x80 | 0x40 => 0xC1`的响应(响应`flags`最高为`1`所以有`|0x80`, 成功响应第二高位为`1`所以有`|0x40`),`body`内容为应用列表,具体内容见下方协议说明。假如有两个 APP,实际字节流:
|
||||
`AA CA AC BB 0A 00 00 00 C1 F9 02 66 61 63 65 00 66 61 63 65 00 F6 06`。
|
||||
`AA CA AC BB 0F 00 00 00 C1 F9 02 66 61 63 65 00 66 61 63 65 00 E5 57`。
|
||||
|
||||
|
||||
## 命令定义和系统命令
|
||||
@@ -548,5 +548,3 @@ unsigned short crc16_IBM(const unsigned char *ptr,int len)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -8,5 +8,7 @@ Maix Comm Protocol 内部根据系统设置的通信方式(比如 uart),
|
||||
用户需要使用串口时构建UART对象构造函数会先检查是否已经注册过(比如判断已经注册过的obj使用的UART编号和即将初始化的UART编号是否相同),
|
||||
如果没有注册过就普通构造,已经注册过那就先回调设置的callback函数,callback 负责释放obj(注意 uart 里面 callback后UART不要再使用register_comm_callback传进来的obj了),然后再构造。
|
||||
|
||||
协议帧的 `data_len` 包含 `flags` + `cmd` + `body` + `CRC`,不包含 `header` 和 `data_len` 自身。
|
||||
错误响应的错误码是 `body` 的第一个字节,后面才是错误字符串,所以错误码必须计入 `body` 长度、`data_len`、CRC 输入长度以及发送总长度。
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user