@@ -288,20 +288,20 @@ Bytes 7+: Message Text (UTF-8, variable length)
|
|||||||
**Command Format**:
|
**Command Format**:
|
||||||
```
|
```
|
||||||
Byte 0: 0x3E
|
Byte 0: 0x3E
|
||||||
Byte 1: Data Type (`data_type`)
|
Bytes 1-2: Data Type (`data_type`, 16-bit little-endian)
|
||||||
Byte 2: Channel Index (0-7)
|
Byte 3: Channel Index (0-7)
|
||||||
Bytes 3+: Binary payload bytes (variable length)
|
Bytes 4+: Binary payload bytes (variable length)
|
||||||
```
|
```
|
||||||
|
|
||||||
**Data Type / Transport Mapping**:
|
**Data Type / Transport Mapping**:
|
||||||
- `0xFF` (`DATA_TYPE_CUSTOM`) must be used for custom-protocol binary datagrams.
|
- `0x0000` is invalid for this command.
|
||||||
- `0x00` is invalid for this command.
|
- `0xFFFF` (`DATA_TYPE_CUSTOM`) is the generic custom-app namespace.
|
||||||
- Values other than `0xFF` are reserved for official protocol extensions.
|
- Other non-zero values can be used as assigned application/community namespaces.
|
||||||
|
|
||||||
**Note**: Applications that need a timestamp should encode it inside the binary payload.
|
**Note**: Applications that need a timestamp should encode it inside the binary payload.
|
||||||
|
|
||||||
**Limits**:
|
**Limits**:
|
||||||
- Maximum payload length is `166` bytes.
|
- Maximum payload length is `163` bytes.
|
||||||
- Larger payloads are rejected with `PACKET_ERROR`.
|
- Larger payloads are rejected with `PACKET_ERROR`.
|
||||||
|
|
||||||
**Response**: `PACKET_OK` (0x00) on success
|
**Response**: `PACKET_OK` (0x00) on success
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ https://github.com/meshcore-dev/MeshCore/blob/main/src/Packet.h#L19
|
|||||||
#define PAYLOAD_TYPE_TXT_MSG 0x02 // a plain text message (prefixed with dest/src hashes, MAC) (enc data: timestamp, text)
|
#define PAYLOAD_TYPE_TXT_MSG 0x02 // a plain text message (prefixed with dest/src hashes, MAC) (enc data: timestamp, text)
|
||||||
#define PAYLOAD_TYPE_ACK 0x03 // a simple ack #define PAYLOAD_TYPE_ADVERT 0x04 // a node advertising its Identity
|
#define PAYLOAD_TYPE_ACK 0x03 // a simple ack #define PAYLOAD_TYPE_ADVERT 0x04 // a node advertising its Identity
|
||||||
#define PAYLOAD_TYPE_GRP_TXT 0x05 // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg")
|
#define PAYLOAD_TYPE_GRP_TXT 0x05 // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg")
|
||||||
#define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, blob)
|
#define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: data_type, data_len, blob)
|
||||||
#define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...)
|
#define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...)
|
||||||
#define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra)
|
#define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra)
|
||||||
|
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
#define RESP_ALLOWED_REPEAT_FREQ 26
|
#define RESP_ALLOWED_REPEAT_FREQ 26
|
||||||
#define RESP_CODE_CHANNEL_DATA_RECV 27
|
#define RESP_CODE_CHANNEL_DATA_RECV 27
|
||||||
|
|
||||||
#define MAX_CHANNEL_DATA_LENGTH (MAX_FRAME_SIZE - 8)
|
#define MAX_CHANNEL_DATA_LENGTH (MAX_FRAME_SIZE - 9)
|
||||||
|
|
||||||
#define SEND_TIMEOUT_BASE_MILLIS 500
|
#define SEND_TIMEOUT_BASE_MILLIS 500
|
||||||
#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f
|
#define FLOOD_SEND_TIMEOUT_FACTOR 16.0f
|
||||||
@@ -569,7 +569,7 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyMesh::onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint8_t data_type,
|
void MyMesh::onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint16_t data_type,
|
||||||
const uint8_t *data, size_t data_len) {
|
const uint8_t *data, size_t data_len) {
|
||||||
if (data_len > MAX_CHANNEL_DATA_LENGTH) {
|
if (data_len > MAX_CHANNEL_DATA_LENGTH) {
|
||||||
MESH_DEBUG_PRINTLN("onChannelDataRecv: dropping payload_len=%d exceeds frame limit=%d",
|
MESH_DEBUG_PRINTLN("onChannelDataRecv: dropping payload_len=%d exceeds frame limit=%d",
|
||||||
@@ -586,7 +586,8 @@ void MyMesh::onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *
|
|||||||
uint8_t channel_idx = findChannelIdx(channel);
|
uint8_t channel_idx = findChannelIdx(channel);
|
||||||
out_frame[i++] = channel_idx;
|
out_frame[i++] = channel_idx;
|
||||||
out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF;
|
out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF;
|
||||||
out_frame[i++] = data_type;
|
out_frame[i++] = (uint8_t)(data_type & 0xFF);
|
||||||
|
out_frame[i++] = (uint8_t)(data_type >> 8);
|
||||||
out_frame[i++] = (uint8_t)data_len;
|
out_frame[i++] = (uint8_t)data_len;
|
||||||
|
|
||||||
int copy_len = (int)data_len;
|
int copy_len = (int)data_len;
|
||||||
@@ -1091,8 +1092,13 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (cmd_frame[0] == CMD_SEND_CHANNEL_DATA) { // send GroupChannel datagram
|
} else if (cmd_frame[0] == CMD_SEND_CHANNEL_DATA) { // send GroupChannel datagram
|
||||||
|
if (len < 4) {
|
||||||
|
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
int i = 1;
|
int i = 1;
|
||||||
uint8_t data_type = cmd_frame[i++];
|
uint16_t data_type = ((uint16_t)cmd_frame[i]) | (((uint16_t)cmd_frame[i + 1]) << 8);
|
||||||
|
i += 2;
|
||||||
uint8_t channel_idx = cmd_frame[i++];
|
uint8_t channel_idx = cmd_frame[i++];
|
||||||
const uint8_t *payload = &cmd_frame[i];
|
const uint8_t *payload = &cmd_frame[i];
|
||||||
int payload_len = (len > (size_t)i) ? (int)(len - i) : 0;
|
int payload_len = (len > (size_t)i) ? (int)(len - i) : 0;
|
||||||
@@ -1100,8 +1106,8 @@ void MyMesh::handleCmdFrame(size_t len) {
|
|||||||
ChannelDetails channel;
|
ChannelDetails channel;
|
||||||
if (!getChannel(channel_idx, channel)) {
|
if (!getChannel(channel_idx, channel)) {
|
||||||
writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx
|
writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx
|
||||||
} else if (data_type != DATA_TYPE_CUSTOM) {
|
} else if (data_type == 0) {
|
||||||
writeErrFrame(ERR_CODE_UNSUPPORTED_CMD);
|
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
|
||||||
} else if (payload_len > MAX_CHANNEL_DATA_LENGTH) {
|
} else if (payload_len > MAX_CHANNEL_DATA_LENGTH) {
|
||||||
MESH_DEBUG_PRINTLN("CMD_SEND_CHANNEL_DATA payload too long: %d > %d", payload_len, MAX_CHANNEL_DATA_LENGTH);
|
MESH_DEBUG_PRINTLN("CMD_SEND_CHANNEL_DATA payload too long: %d > %d", payload_len, MAX_CHANNEL_DATA_LENGTH);
|
||||||
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
|
writeErrFrame(ERR_CODE_ILLEGAL_ARG);
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ protected:
|
|||||||
const uint8_t *sender_prefix, const char *text) override;
|
const uint8_t *sender_prefix, const char *text) override;
|
||||||
void onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp,
|
void onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint32_t timestamp,
|
||||||
const char *text) override;
|
const char *text) override;
|
||||||
void onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint8_t data_type,
|
void onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint16_t data_type,
|
||||||
const uint8_t *data, size_t data_len) override;
|
const uint8_t *data, size_t data_len) override;
|
||||||
|
|
||||||
uint8_t onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data,
|
uint8_t onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
#define PATH_HASH_SIZE 1
|
#define PATH_HASH_SIZE 1
|
||||||
|
|
||||||
#define MAX_PACKET_PAYLOAD 184
|
#define MAX_PACKET_PAYLOAD 184
|
||||||
#define MAX_GROUP_DATA_LENGTH (MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE - 2)
|
#define MAX_GROUP_DATA_LENGTH (MAX_PACKET_PAYLOAD - CIPHER_BLOCK_SIZE - 3)
|
||||||
#define MAX_PATH_SIZE 64
|
#define MAX_PATH_SIZE 64
|
||||||
#define MAX_TRANS_UNIT 255
|
#define MAX_TRANS_UNIT 255
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ namespace mesh {
|
|||||||
#define PAYLOAD_TYPE_ACK 0x03 // a simple ack
|
#define PAYLOAD_TYPE_ACK 0x03 // a simple ack
|
||||||
#define PAYLOAD_TYPE_ADVERT 0x04 // a node advertising its Identity
|
#define PAYLOAD_TYPE_ADVERT 0x04 // a node advertising its Identity
|
||||||
#define PAYLOAD_TYPE_GRP_TXT 0x05 // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg")
|
#define PAYLOAD_TYPE_GRP_TXT 0x05 // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg")
|
||||||
#define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: data_type, data_len, blob)
|
#define PAYLOAD_TYPE_GRP_DATA 0x06 // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: data_type(uint16), data_len, blob)
|
||||||
#define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...)
|
#define PAYLOAD_TYPE_ANON_REQ 0x07 // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...)
|
||||||
#define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra)
|
#define PAYLOAD_TYPE_PATH 0x08 // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra)
|
||||||
#define PAYLOAD_TYPE_TRACE 0x09 // trace a path, collecting SNI for each hop
|
#define PAYLOAD_TYPE_TRACE 0x09 // trace a path, collecting SNI for each hop
|
||||||
|
|||||||
@@ -374,14 +374,14 @@ void BaseChatMesh::onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mes
|
|||||||
// notify UI of this new message
|
// notify UI of this new message
|
||||||
onChannelMessageRecv(channel, packet, timestamp, (const char *) &data[5]); // let UI know
|
onChannelMessageRecv(channel, packet, timestamp, (const char *) &data[5]); // let UI know
|
||||||
} else if (type == PAYLOAD_TYPE_GRP_DATA) {
|
} else if (type == PAYLOAD_TYPE_GRP_DATA) {
|
||||||
if (len < 2) {
|
if (len < 3) {
|
||||||
MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping short group data payload len=%d", (uint32_t)len);
|
MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping short group data payload len=%d", (uint32_t)len);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t data_type = data[0];
|
uint16_t data_type = ((uint16_t)data[0]) | (((uint16_t)data[1]) << 8);
|
||||||
uint8_t data_len = data[1];
|
uint8_t data_len = data[2];
|
||||||
size_t available_len = len - 2;
|
size_t available_len = len - 3;
|
||||||
|
|
||||||
if (data_len > available_len) {
|
if (data_len > available_len) {
|
||||||
MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping malformed group data type=%d len=%d available=%d",
|
MESH_DEBUG_PRINTLN("onGroupDataRecv: dropping malformed group data type=%d len=%d available=%d",
|
||||||
@@ -389,7 +389,7 @@ void BaseChatMesh::onGroupDataRecv(mesh::Packet* packet, uint8_t type, const mes
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onChannelDataRecv(channel, packet, data_type, &data[2], data_len);
|
onChannelDataRecv(channel, packet, data_type, &data[3], data_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,7 +481,7 @@ bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& chan
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BaseChatMesh::sendGroupData(mesh::GroupChannel& channel, uint8_t data_type, const uint8_t* data, int data_len) {
|
bool BaseChatMesh::sendGroupData(mesh::GroupChannel& channel, uint16_t data_type, const uint8_t* data, int data_len) {
|
||||||
if (data_len < 0) {
|
if (data_len < 0) {
|
||||||
MESH_DEBUG_PRINTLN("sendGroupData: invalid negative data_len=%d", data_len);
|
MESH_DEBUG_PRINTLN("sendGroupData: invalid negative data_len=%d", data_len);
|
||||||
return false;
|
return false;
|
||||||
@@ -491,12 +491,13 @@ bool BaseChatMesh::sendGroupData(mesh::GroupChannel& channel, uint8_t data_type,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t temp[2 + MAX_GROUP_DATA_LENGTH];
|
uint8_t temp[3 + MAX_GROUP_DATA_LENGTH];
|
||||||
temp[0] = data_type;
|
temp[0] = (uint8_t)(data_type & 0xFF);
|
||||||
temp[1] = (uint8_t)data_len;
|
temp[1] = (uint8_t)(data_type >> 8);
|
||||||
if (data_len > 0) memcpy(&temp[2], data, data_len);
|
temp[2] = (uint8_t)data_len;
|
||||||
|
if (data_len > 0) memcpy(&temp[3], data, data_len);
|
||||||
|
|
||||||
auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_DATA, channel, temp, 2 + data_len);
|
auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_DATA, channel, temp, 3 + data_len);
|
||||||
if (pkt == NULL) {
|
if (pkt == NULL) {
|
||||||
MESH_DEBUG_PRINTLN("sendGroupData: unable to create group datagram, data_len=%d", data_len);
|
MESH_DEBUG_PRINTLN("sendGroupData: unable to create group datagram, data_len=%d", data_len);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ protected:
|
|||||||
virtual uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const = 0;
|
virtual uint32_t calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t path_len) const = 0;
|
||||||
virtual void onSendTimeout() = 0;
|
virtual void onSendTimeout() = 0;
|
||||||
virtual void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) = 0;
|
virtual void onChannelMessageRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t timestamp, const char *text) = 0;
|
||||||
virtual void onChannelDataRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint8_t data_type,
|
virtual void onChannelDataRecv(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint16_t data_type,
|
||||||
const uint8_t* data, size_t data_len) {}
|
const uint8_t* data, size_t data_len) {}
|
||||||
virtual uint8_t onContactRequest(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t* data, uint8_t len, uint8_t* reply) = 0;
|
virtual uint8_t onContactRequest(const ContactInfo& contact, uint32_t sender_timestamp, const uint8_t* data, uint8_t len, uint8_t* reply) = 0;
|
||||||
virtual void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) = 0;
|
virtual void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) = 0;
|
||||||
@@ -150,7 +150,7 @@ public:
|
|||||||
int sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout);
|
int sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout);
|
||||||
int sendCommandData(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& est_timeout);
|
int sendCommandData(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& est_timeout);
|
||||||
bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len);
|
bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len);
|
||||||
bool sendGroupData(mesh::GroupChannel& channel, uint8_t data_type, const uint8_t* data, int data_len);
|
bool sendGroupData(mesh::GroupChannel& channel, uint16_t data_type, const uint8_t* data, int data_len);
|
||||||
int sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout);
|
int sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout);
|
||||||
int sendAnonReq(const ContactInfo& recipient, const uint8_t* data, uint8_t len, uint32_t& tag, uint32_t& est_timeout);
|
int sendAnonReq(const ContactInfo& recipient, const uint8_t* data, uint8_t len, uint32_t& tag, uint32_t& est_timeout);
|
||||||
int sendRequest(const ContactInfo& recipient, uint8_t req_type, uint32_t& tag, uint32_t& est_timeout);
|
int sendRequest(const ContactInfo& recipient, uint8_t req_type, uint32_t& tag, uint32_t& est_timeout);
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#define TXT_TYPE_PLAIN 0 // a plain text message
|
#define TXT_TYPE_PLAIN 0 // a plain text message
|
||||||
#define TXT_TYPE_CLI_DATA 1 // a CLI command
|
#define TXT_TYPE_CLI_DATA 1 // a CLI command
|
||||||
#define TXT_TYPE_SIGNED_PLAIN 2 // plain text, signed by sender
|
#define TXT_TYPE_SIGNED_PLAIN 2 // plain text, signed by sender
|
||||||
#define DATA_TYPE_CUSTOM 0xFF // custom app binary payload (group/channel datagrams)
|
#define DATA_TYPE_CUSTOM 0xFFFF // generic custom app namespace for group/channel datagrams
|
||||||
|
|
||||||
class StrHelper {
|
class StrHelper {
|
||||||
public:
|
public:
|
||||||
|
|||||||
Reference in New Issue
Block a user