|
|
@ -96,8 +96,15 @@ static uint32_t _atoi(const char* sp) { |
|
|
|
|
|
|
|
|
/*------------ Frame Protocol --------------*/ |
|
|
/*------------ Frame Protocol --------------*/ |
|
|
|
|
|
|
|
|
#define FIRMWARE_VER_CODE 1 |
|
|
#define FIRMWARE_VER_CODE 2 |
|
|
#define FIRMWARE_BUILD_DATE "19 Feb 2025" |
|
|
|
|
|
|
|
|
#ifndef FIRMWARE_BUILD_DATE |
|
|
|
|
|
#define FIRMWARE_BUILD_DATE "3 Mar 2025" |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
#ifndef FIRMWARE_VERSION |
|
|
|
|
|
#define FIRMWARE_VERSION "v1.0.0" |
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
#define CMD_APP_START 1 |
|
|
#define CMD_APP_START 1 |
|
|
#define CMD_SEND_TXT_MSG 2 |
|
|
#define CMD_SEND_TXT_MSG 2 |
|
|
@ -126,6 +133,8 @@ static uint32_t _atoi(const char* sp) { |
|
|
#define CMD_SEND_RAW_DATA 25 |
|
|
#define CMD_SEND_RAW_DATA 25 |
|
|
#define CMD_SEND_LOGIN 26 |
|
|
#define CMD_SEND_LOGIN 26 |
|
|
#define CMD_SEND_STATUS_REQ 27 |
|
|
#define CMD_SEND_STATUS_REQ 27 |
|
|
|
|
|
#define CMD_HAS_CONNECTION 28 |
|
|
|
|
|
#define CMD_LOGOUT 29 // 'Disconnect'
|
|
|
|
|
|
|
|
|
#define RESP_CODE_OK 0 |
|
|
#define RESP_CODE_OK 0 |
|
|
#define RESP_CODE_ERR 1 |
|
|
#define RESP_CODE_ERR 1 |
|
|
@ -435,16 +444,19 @@ protected: |
|
|
expected_ack_crc = 0; // reset our expected hash, now that we have received ACK
|
|
|
expected_ack_crc = 0; // reset our expected hash, now that we have received ACK
|
|
|
return true; |
|
|
return true; |
|
|
} |
|
|
} |
|
|
return false; |
|
|
return checkConnectionsAck(data); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void queueMessage(const ContactInfo& from, uint8_t txt_type, uint8_t path_len, uint32_t sender_timestamp, const char *text) { |
|
|
void queueMessage(const ContactInfo& from, uint8_t txt_type, uint8_t path_len, uint32_t sender_timestamp, const uint8_t* extra, int extra_len, const char *text) { |
|
|
int i = 0; |
|
|
int i = 0; |
|
|
out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV; |
|
|
out_frame[i++] = RESP_CODE_CONTACT_MSG_RECV; |
|
|
memcpy(&out_frame[i], from.id.pub_key, 6); i += 6; // just 6-byte prefix
|
|
|
memcpy(&out_frame[i], from.id.pub_key, 6); i += 6; // just 6-byte prefix
|
|
|
out_frame[i++] = path_len; |
|
|
out_frame[i++] = path_len; |
|
|
out_frame[i++] = txt_type; |
|
|
out_frame[i++] = txt_type; |
|
|
memcpy(&out_frame[i], &sender_timestamp, 4); i += 4; |
|
|
memcpy(&out_frame[i], &sender_timestamp, 4); i += 4; |
|
|
|
|
|
if (extra_len > 0) { |
|
|
|
|
|
memcpy(&out_frame[i], extra, extra_len); i += extra_len; |
|
|
|
|
|
} |
|
|
int tlen = strlen(text); // TODO: UTF-8 ??
|
|
|
int tlen = strlen(text); // TODO: UTF-8 ??
|
|
|
if (i + tlen > MAX_FRAME_SIZE) { |
|
|
if (i + tlen > MAX_FRAME_SIZE) { |
|
|
tlen = MAX_FRAME_SIZE - i; |
|
|
tlen = MAX_FRAME_SIZE - i; |
|
|
@ -462,11 +474,19 @@ protected: |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void onMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override { |
|
|
void onMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override { |
|
|
queueMessage(from, TXT_TYPE_PLAIN, path_len, sender_timestamp, text); |
|
|
markConnectionActive(from); // in case this is from a server, and we have a connection
|
|
|
|
|
|
queueMessage(from, TXT_TYPE_PLAIN, path_len, sender_timestamp, NULL, 0, text); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void onCommandDataRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override { |
|
|
void onCommandDataRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override { |
|
|
queueMessage(from, TXT_TYPE_CLI_DATA, path_len, sender_timestamp, text); |
|
|
markConnectionActive(from); // in case this is from a server, and we have a connection
|
|
|
|
|
|
queueMessage(from, TXT_TYPE_CLI_DATA, path_len, sender_timestamp, NULL, 0, text); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void onSignedMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const uint8_t *sender_prefix, const char *text) override { |
|
|
|
|
|
markConnectionActive(from); |
|
|
|
|
|
saveContacts(); // from.sync_since change needs to be persisted
|
|
|
|
|
|
queueMessage(from, TXT_TYPE_SIGNED_PLAIN, path_len, sender_timestamp, sender_prefix, 4, text); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void onChannelMessageRecv(const mesh::GroupChannel& channel, int in_path_len, uint32_t timestamp, const char *text) override { |
|
|
void onChannelMessageRecv(const mesh::GroupChannel& channel, int in_path_len, uint32_t timestamp, const char *text) override { |
|
|
@ -505,7 +525,10 @@ protected: |
|
|
out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; |
|
|
out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; |
|
|
out_frame[i++] = 0; // legacy: is_admin = false
|
|
|
out_frame[i++] = 0; // legacy: is_admin = false
|
|
|
} else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response
|
|
|
} else if (data[4] == RESP_SERVER_LOGIN_OK) { // new login response
|
|
|
// keep_alive_interval = data[5] * 16
|
|
|
uint16_t keep_alive_secs = ((uint16_t)data[5]) * 16; |
|
|
|
|
|
if (keep_alive_secs > 0) { |
|
|
|
|
|
startConnection(contact, keep_alive_secs); |
|
|
|
|
|
} |
|
|
out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; |
|
|
out_frame[i++] = PUSH_CODE_LOGIN_SUCCESS; |
|
|
out_frame[i++] = data[6]; // permissions (eg. is_admin)
|
|
|
out_frame[i++] = data[6]; // permissions (eg. is_admin)
|
|
|
} else { |
|
|
} else { |
|
|
@ -640,11 +663,9 @@ public: |
|
|
out_frame[i++] = FIRMWARE_VER_CODE; |
|
|
out_frame[i++] = FIRMWARE_VER_CODE; |
|
|
memset(&out_frame[i], 0, 6); i += 6; // reserved
|
|
|
memset(&out_frame[i], 0, 6); i += 6; // reserved
|
|
|
memset(&out_frame[i], 0, 12); |
|
|
memset(&out_frame[i], 0, 12); |
|
|
strcpy((char *) &out_frame[i], FIRMWARE_BUILD_DATE); |
|
|
strcpy((char *) &out_frame[i], FIRMWARE_BUILD_DATE); i += 12; |
|
|
i += 12; |
|
|
StrHelper::strzcpy((char *) &out_frame[i], board.getManufacturerName(), 40); i += 40; |
|
|
const char* name = board.getManufacturerName(); |
|
|
StrHelper::strzcpy((char *) &out_frame[i], FIRMWARE_VERSION, 20); i += 20; |
|
|
int tlen = strlen(name); |
|
|
|
|
|
memcpy(&out_frame[i], name, tlen); i += tlen; |
|
|
|
|
|
_serial->writeFrame(out_frame, i); |
|
|
_serial->writeFrame(out_frame, i); |
|
|
} else if (cmd_frame[0] == CMD_APP_START && len >= 8) { // sent when app establishes connection, respond with node ID
|
|
|
} else if (cmd_frame[0] == CMD_APP_START && len >= 8) { // sent when app establishes connection, respond with node ID
|
|
|
// cmd_frame[1..7] reserved future
|
|
|
// cmd_frame[1..7] reserved future
|
|
|
@ -1013,6 +1034,17 @@ public: |
|
|
} else { |
|
|
} else { |
|
|
writeErrFrame(); // contact not found
|
|
|
writeErrFrame(); // contact not found
|
|
|
} |
|
|
} |
|
|
|
|
|
} else if (cmd_frame[0] == CMD_HAS_CONNECTION && len >= 1+PUB_KEY_SIZE) { |
|
|
|
|
|
uint8_t* pub_key = &cmd_frame[1]; |
|
|
|
|
|
if (hasConnectionTo(pub_key)) { |
|
|
|
|
|
writeOKFrame(); |
|
|
|
|
|
} else { |
|
|
|
|
|
writeErrFrame(); |
|
|
|
|
|
} |
|
|
|
|
|
} else if (cmd_frame[0] == CMD_LOGOUT && len >= 1+PUB_KEY_SIZE) { |
|
|
|
|
|
uint8_t* pub_key = &cmd_frame[1]; |
|
|
|
|
|
stopConnection(pub_key); |
|
|
|
|
|
writeOKFrame(); |
|
|
} else { |
|
|
} else { |
|
|
writeErrFrame(); |
|
|
writeErrFrame(); |
|
|
MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); |
|
|
MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]); |
|
|
@ -1042,6 +1074,8 @@ public: |
|
|
_serial->writeFrame(out_frame, 5); |
|
|
_serial->writeFrame(out_frame, 5); |
|
|
_iter_started = false; |
|
|
_iter_started = false; |
|
|
} |
|
|
} |
|
|
|
|
|
} else if (!_serial->isWriteBusy()) { |
|
|
|
|
|
checkConnections(); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|