mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-01 10:21:59 +00:00
feat(companion): add device-originated message frames
Adds RESP_CODE_CONTACT_MSG_SENT_V3 (0x1E) and RESP_CODE_CHANNEL_MSG_SENT_V3 (0x1F) so firmware that sends messages on its own (bot auto-replies, on-device keyboard input) can mirror them into the companion app's history, which today only ever sees inbound traffic and messages the app itself sent. See #3225. Both ride the existing offline-queue / MSG_WAITING / SYNC_NEXT_MESSAGE pipe and reuse the RECV_V3 byte layout, with the pubkey field holding the recipient instead of the sender and the SNR byte reserved. Requires app_target_ver >= 3; there is no pre-V3 shape, so older apps are skipped. Mainline never calls these -- nothing in companion_radio self-originates messages -- so this is API surface for custom builds, and the linker drops both functions when unused (identical flash usage before/after). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -428,6 +428,7 @@ Byte 0: 0x0A
|
||||
- `PACKET_CHANNEL_MSG_RECV` (0x08) or `PACKET_CHANNEL_MSG_RECV_V3` (0x11) for channel messages
|
||||
- `PACKET_CONTACT_MSG_RECV` (0x07) or `PACKET_CONTACT_MSG_RECV_V3` (0x10) for contact messages
|
||||
- `PACKET_CHANNEL_DATA_RECV` (0x1B) for channel data datagrams
|
||||
- `PACKET_CONTACT_MSG_SENT_V3` (0x1E) or `PACKET_CHANNEL_MSG_SENT_V3` (0x1F) for messages the device itself sent
|
||||
- `PACKET_NO_MORE_MSGS` (0x0A) if no messages available
|
||||
|
||||
**Note**: Poll this command periodically to retrieve queued messages. The device may also send `PACKET_MESSAGES_WAITING` (0x83) as a notification when messages are available.
|
||||
@@ -619,6 +620,45 @@ def parse_channel_message(data):
|
||||
}
|
||||
```
|
||||
|
||||
### Device-Sent Messages
|
||||
|
||||
A message the device *sent* on its own (bot auto-reply, on-device keyboard
|
||||
input, etc.) rather than via a host-issued `SEND_TXT_MESSAGE` /
|
||||
`SEND_CHANNEL_MESSAGE`. Mainline firmware never emits these — they're for a
|
||||
custom firmware that self-originates messages, delivered through the same
|
||||
offline-queue / `PACKET_MESSAGES_WAITING` / `SYNC_NEXT_MESSAGE` pipe as any
|
||||
other message.
|
||||
|
||||
Same byte layout as the RECV_V3 counterpart, except the pubkey field is the
|
||||
*recipient* (not sender), and the RECV_V3 SNR byte is reserved (no SNR for a
|
||||
local send). Requires `app_target_ver >= 3` — no pre-V3 shape exists.
|
||||
|
||||
Text Type is limited to `TXT_TYPE_PLAIN` (0) or `TXT_TYPE_CLI_DATA` (1).
|
||||
`TXT_TYPE_SIGNED_PLAIN` is not emitted: it would need the 4-byte sender prefix
|
||||
that RECV_V3 carries, which is meaningless when the device is the sender.
|
||||
|
||||
**Contact Message Sent** (`PACKET_CONTACT_MSG_SENT_V3`, 0x1E):
|
||||
```
|
||||
Byte 0: 0x1E (packet type)
|
||||
Bytes 1-3: Reserved
|
||||
Bytes 4-9: Recipient Public Key Prefix (6 bytes, hex)
|
||||
Byte 10: Path Length (always 0xFF — meaningless for a local send)
|
||||
Byte 11: Text Type
|
||||
Bytes 12-15: Timestamp (32-bit little-endian)
|
||||
Bytes 16+: Message Text (UTF-8)
|
||||
```
|
||||
|
||||
**Channel Message Sent** (`PACKET_CHANNEL_MSG_SENT_V3`, 0x1F):
|
||||
```
|
||||
Byte 0: 0x1F (packet type)
|
||||
Bytes 1-3: Reserved
|
||||
Byte 4: Channel Index (0-7)
|
||||
Byte 5: Path Length (always 0xFF — meaningless for a local send)
|
||||
Byte 6: Text Type (always 0 / TXT_TYPE_PLAIN — a channel carries no CLI-data/signed-plain concept)
|
||||
Bytes 7-10: Timestamp (32-bit little-endian)
|
||||
Bytes 11+: Message Text (UTF-8)
|
||||
```
|
||||
|
||||
### Sending Messages
|
||||
|
||||
Use the `SEND_CHANNEL_MESSAGE` command (see [Commands](#commands)).
|
||||
@@ -662,6 +702,8 @@ Byte values are authoritative; names are aliases. When reading firmware source,
|
||||
| 0x11 | PACKET_CHANNEL_MSG_RECV_V3 | Channel message (V3 with SNR) |
|
||||
| 0x12 | PACKET_CHANNEL_INFO | Channel information |
|
||||
| 0x1B | PACKET_CHANNEL_DATA_RECV | Channel data datagram |
|
||||
| 0x1E | PACKET_CONTACT_MSG_SENT_V3 | Contact message the device itself sent (see [Device-Sent Messages](#device-sent-messages)) |
|
||||
| 0x1F | PACKET_CHANNEL_MSG_SENT_V3 | Channel message the device itself sent (see [Device-Sent Messages](#device-sent-messages)) |
|
||||
| 0x80 | PACKET_ADVERTISEMENT | Advertisement packet |
|
||||
| 0x82 | PACKET_ACK | Acknowledgment |
|
||||
| 0x83 | PACKET_MESSAGES_WAITING | Messages waiting notification |
|
||||
|
||||
@@ -102,6 +102,8 @@
|
||||
#define RESP_CODE_CHANNEL_DATA_RECV 27
|
||||
#define RESP_CODE_DEFAULT_FLOOD_SCOPE 28
|
||||
#define RESP_CODE_CLI_REPLY 29 // v14+, a reply to CMD_RUN_CLI_COMMAND
|
||||
#define RESP_CODE_CONTACT_MSG_SENT_V3 30 // a reply to CMD_SYNC_NEXT_MESSAGE -- device-originated msg, see queueSentMessage()
|
||||
#define RESP_CODE_CHANNEL_MSG_SENT_V3 31 // a reply to CMD_SYNC_NEXT_MESSAGE -- device-originated msg, see queueSentChannelMessage()
|
||||
|
||||
#define MAX_CHANNEL_DATA_LENGTH (MAX_FRAME_SIZE - 9)
|
||||
|
||||
@@ -222,7 +224,7 @@ void MyMesh::updateContactFromFrame(ContactInfo &contact, uint32_t& last_mod, co
|
||||
|
||||
bool MyMesh::Frame::isChannelMsg() const {
|
||||
return buf[0] == RESP_CODE_CHANNEL_MSG_RECV || buf[0] == RESP_CODE_CHANNEL_MSG_RECV_V3 ||
|
||||
buf[0] == RESP_CODE_CHANNEL_DATA_RECV;
|
||||
buf[0] == RESP_CODE_CHANNEL_DATA_RECV || buf[0] == RESP_CODE_CHANNEL_MSG_SENT_V3;
|
||||
}
|
||||
|
||||
void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) {
|
||||
@@ -532,6 +534,68 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe
|
||||
#endif
|
||||
}
|
||||
|
||||
// Tells the connected app this device sent `text` to `to` on its own (bot
|
||||
// reply, on-device keyboard, etc.) -- mainline never calls this. No pre-V3
|
||||
// fallback, so a non-V3 app just doesn't get it.
|
||||
void MyMesh::queueSentMessage(const ContactInfo &to, uint8_t txt_type, uint32_t timestamp, const char *text) {
|
||||
if (app_target_ver < 3) return;
|
||||
if (txt_type == TXT_TYPE_SIGNED_PLAIN) return; // has no sender prefix field, and we ARE the sender
|
||||
|
||||
int i = 0;
|
||||
out_frame[i++] = RESP_CODE_CONTACT_MSG_SENT_V3;
|
||||
out_frame[i++] = 0; // reserved1 (RECV_V3's SNR byte)
|
||||
out_frame[i++] = 0; // reserved2
|
||||
out_frame[i++] = 0; // reserved3
|
||||
memcpy(&out_frame[i], to.id.pub_key, 6); // recipient, not sender
|
||||
i += 6;
|
||||
out_frame[i++] = 0xFF; // path_len n/a
|
||||
out_frame[i++] = txt_type;
|
||||
memcpy(&out_frame[i], ×tamp, 4);
|
||||
i += 4;
|
||||
int tlen = strlen(text); // TODO: UTF-8 ??
|
||||
if (i + tlen > MAX_FRAME_SIZE) {
|
||||
tlen = MAX_FRAME_SIZE - i;
|
||||
}
|
||||
memcpy(&out_frame[i], text, tlen);
|
||||
i += tlen;
|
||||
addToOfflineQueue(out_frame, i);
|
||||
|
||||
if (_serial->isConnected()) {
|
||||
uint8_t frame[1];
|
||||
frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle'
|
||||
_serial->writeFrame(frame, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Channel counterpart to queueSentMessage().
|
||||
void MyMesh::queueSentChannelMessage(uint8_t channel_idx, uint32_t timestamp, const char *text) {
|
||||
if (app_target_ver < 3) return;
|
||||
|
||||
int i = 0;
|
||||
out_frame[i++] = RESP_CODE_CHANNEL_MSG_SENT_V3;
|
||||
out_frame[i++] = 0; // reserved1
|
||||
out_frame[i++] = 0; // reserved2
|
||||
out_frame[i++] = 0; // reserved3
|
||||
out_frame[i++] = channel_idx;
|
||||
out_frame[i++] = 0xFF; // path_len n/a
|
||||
out_frame[i++] = TXT_TYPE_PLAIN;
|
||||
memcpy(&out_frame[i], ×tamp, 4);
|
||||
i += 4;
|
||||
int tlen = strlen(text); // TODO: UTF-8 ??
|
||||
if (i + tlen > MAX_FRAME_SIZE) {
|
||||
tlen = MAX_FRAME_SIZE - i;
|
||||
}
|
||||
memcpy(&out_frame[i], text, tlen);
|
||||
i += tlen;
|
||||
addToOfflineQueue(out_frame, i);
|
||||
|
||||
if (_serial->isConnected()) {
|
||||
uint8_t frame[1];
|
||||
frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle'
|
||||
_serial->writeFrame(frame, 1);
|
||||
}
|
||||
}
|
||||
|
||||
bool MyMesh::filterRecvFloodPacket(mesh::Packet* packet) {
|
||||
// REVISIT: try to determine which Region (from transport_codes[1]) that Sender is indicating for replies/responses
|
||||
// if unknown, fallback to finding Region from transport_codes[0], the 'scope' used by Sender
|
||||
|
||||
@@ -192,6 +192,11 @@ public:
|
||||
_prefs.clearDirty();
|
||||
}
|
||||
|
||||
// For a custom firmware's own self-originated sends (bot replies, on-device
|
||||
// keyboard, etc.) -- mainline doesn't call these. See MyMesh.cpp.
|
||||
void queueSentMessage(const ContactInfo &to, uint8_t txt_type, uint32_t timestamp, const char *text);
|
||||
void queueSentChannelMessage(uint8_t channel_idx, uint32_t timestamp, const char *text);
|
||||
|
||||
#if ENV_INCLUDE_GPS == 1
|
||||
void applyGpsPrefs() {
|
||||
sensors.setSettingValue("gps", _prefs.gps_enabled ? "1" : "0");
|
||||
|
||||
Reference in New Issue
Block a user