Skip to content

Commit f9bc3a1

Browse files
committed
Merge branch 'dev' into t114-display
2 parents cf3d552 + fbfa8bb commit f9bc3a1

File tree

19 files changed

+738
-30
lines changed

19 files changed

+738
-30
lines changed
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"build": {
3+
"arduino": {
4+
"ldscript": "nrf52840_s140_v7.ld"
5+
},
6+
"core": "nRF5",
7+
"cpu": "cortex-m4",
8+
"extra_flags": "-DARDUINO_Seeed_XIAO_nRF52840 -DNRF52840_XXAA -DSEEED_XIAO_NRF52840 ",
9+
"f_cpu": "64000000L",
10+
"hwids": [
11+
[ "0x2886", "0x8044" ],
12+
[ "0x2886", "0x0044" ]
13+
],
14+
"mcu": "nrf52840",
15+
"variant": "Seeed_XIAO_nRF52840",
16+
"softdevice": {
17+
"sd_flags": "-DS140",
18+
"sd_name": "s140",
19+
"sd_version": "7.3.0",
20+
"sd_fwid": "0x0123"
21+
},
22+
"bsp": {
23+
"name": "adafruit"
24+
},
25+
"bootloader": {
26+
"settings_addr": "0xFF000"
27+
},
28+
"usb_product": "XIAO nRF52840"
29+
},
30+
"connectivity": [
31+
"bluetooth"
32+
],
33+
"debug": {
34+
"jlink_device": "nRF52840_xxAA",
35+
"openocd_target": "nrf52.cfg",
36+
"svd_path": "nrf52840.svd"
37+
},
38+
"frameworks": [
39+
"arduino"
40+
],
41+
"name": "Seeed Studio XIAO nRF52840",
42+
"upload": {
43+
"maximum_ram_size": 237568,
44+
"maximum_size": 811008,
45+
"protocol": "nrfutil",
46+
"speed": 115200,
47+
"protocols": [
48+
"jlink",
49+
"nrfjprog",
50+
"nrfutil",
51+
"cmsis-dap",
52+
"sam-ba",
53+
"blackmagic"
54+
],
55+
"use_1200bps_touch": true,
56+
"require_upload_port": true,
57+
"wait_for_upload_port": true
58+
},
59+
"url": "https://wiki.seeedstudio.com/XIAO_BLE",
60+
"vendor": "Seeed Studio"
61+
}

examples/companion_radio/main.cpp

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ static uint32_t _atoi(const char* sp) {
8686

8787
/*------------ Frame Protocol --------------*/
8888

89-
#define FIRMWARE_VER_CODE 3
89+
#define FIRMWARE_VER_CODE 4
9090

9191
#ifndef FIRMWARE_BUILD_DATE
9292
#define FIRMWARE_BUILD_DATE "7 Apr 2025"
@@ -133,6 +133,7 @@ static uint32_t _atoi(const char* sp) {
133133
#define CMD_SIGN_FINISH 35
134134
#define CMD_SEND_TRACE_PATH 36
135135
#define CMD_SET_DEVICE_PIN 37
136+
#define CMD_SET_OTHER_PARAMS 38
136137

137138
#define RESP_CODE_OK 0
138139
#define RESP_CODE_ERR 1
@@ -167,6 +168,7 @@ static uint32_t _atoi(const char* sp) {
167168
#define PUSH_CODE_STATUS_RESPONSE 0x87
168169
#define PUSH_CODE_LOG_RX_DATA 0x88
169170
#define PUSH_CODE_TRACE_DATA 0x89
171+
#define PUSH_CODE_NEW_ADVERT 0x8A
170172

171173
#define ERR_CODE_UNSUPPORTED_CMD 1
172174
#define ERR_CODE_NOT_FOUND 2
@@ -187,7 +189,7 @@ struct NodePrefs { // persisted to file
187189
uint8_t sf;
188190
uint8_t cr;
189191
uint8_t reserved1;
190-
uint8_t reserved2;
192+
uint8_t manual_add_contacts;
191193
float bw;
192194
uint8_t tx_power_dbm;
193195
uint8_t unused[3];
@@ -502,11 +504,19 @@ class MyMesh : public BaseChatMesh {
502504
}
503505
}
504506

507+
bool isAutoAddEnabled() const override {
508+
return (_prefs.manual_add_contacts & 1) == 0;
509+
}
510+
505511
void onDiscoveredContact(ContactInfo& contact, bool is_new) override {
506512
if (_serial->isConnected()) {
507-
out_frame[0] = PUSH_CODE_ADVERT;
508-
memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE);
509-
_serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE);
513+
if (!isAutoAddEnabled() && is_new) {
514+
writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact);
515+
} else {
516+
out_frame[0] = PUSH_CODE_ADVERT;
517+
memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE);
518+
_serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE);
519+
}
510520
} else {
511521
soundBuzzer();
512522
}
@@ -666,6 +676,10 @@ class MyMesh : public BaseChatMesh {
666676
}
667677

668678
void onRawDataRecv(mesh::Packet* packet) override {
679+
if (packet->payload_len + 4 > sizeof(out_frame)) {
680+
MESH_DEBUG_PRINTLN("onRawDataRecv(), payload_len too long: %d", packet->payload_len);
681+
return;
682+
}
669683
int i = 0;
670684
out_frame[i++] = PUSH_CODE_RAW_DATA;
671685
out_frame[i++] = (int8_t)(_radio->getLastSNR() * 4);
@@ -749,7 +763,7 @@ class MyMesh : public BaseChatMesh {
749763
file.read((uint8_t *) &_prefs.sf, sizeof(_prefs.sf)); // 60
750764
file.read((uint8_t *) &_prefs.cr, sizeof(_prefs.cr)); // 61
751765
file.read((uint8_t *) &_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
752-
file.read((uint8_t *) &_prefs.reserved2, sizeof(_prefs.reserved2)); // 63
766+
file.read((uint8_t *) &_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63
753767
file.read((uint8_t *) &_prefs.bw, sizeof(_prefs.bw)); // 64
754768
file.read((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
755769
file.read((uint8_t *) _prefs.unused, sizeof(_prefs.unused)); // 69
@@ -850,7 +864,7 @@ class MyMesh : public BaseChatMesh {
850864
file.write((uint8_t *) &_prefs.sf, sizeof(_prefs.sf)); // 60
851865
file.write((uint8_t *) &_prefs.cr, sizeof(_prefs.cr)); // 61
852866
file.write((uint8_t *) &_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
853-
file.write((uint8_t *) &_prefs.reserved2, sizeof(_prefs.reserved2)); // 63
867+
file.write((uint8_t *) &_prefs.manual_add_contacts, sizeof(_prefs.manual_add_contacts)); // 63
854868
file.write((uint8_t *) &_prefs.bw, sizeof(_prefs.bw)); // 64
855869
file.write((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
856870
file.write((uint8_t *) _prefs.unused, sizeof(_prefs.unused)); // 69
@@ -891,12 +905,15 @@ class MyMesh : public BaseChatMesh {
891905
out_frame[i++] = MAX_LORA_TX_POWER;
892906
memcpy(&out_frame[i], self_id.pub_key, PUB_KEY_SIZE); i += PUB_KEY_SIZE;
893907

894-
int32_t lat, lon, alt = 0;
908+
int32_t lat, lon;
895909
lat = (_prefs.node_lat * 1000000.0);
896910
lon = (_prefs.node_lon * 1000000.0);
897911
memcpy(&out_frame[i], &lat, 4); i += 4;
898912
memcpy(&out_frame[i], &lon, 4); i += 4;
899-
memcpy(&out_frame[i], &alt, 4); i += 4;
913+
out_frame[i++] = 0; // reserved
914+
out_frame[i++] = 0; // reserved
915+
out_frame[i++] = 0; // reserved
916+
out_frame[i++] = _prefs.manual_add_contacts;
900917

901918
uint32_t freq = _prefs.freq * 1000;
902919
memcpy(&out_frame[i], &freq, 4); i += 4;
@@ -1180,6 +1197,10 @@ class MyMesh : public BaseChatMesh {
11801197
_prefs.airtime_factor = ((float)af) / 1000.0f;
11811198
savePrefs();
11821199
writeOKFrame();
1200+
} else if (cmd_frame[0] == CMD_SET_OTHER_PARAMS) {
1201+
_prefs.manual_add_contacts = cmd_frame[1];
1202+
savePrefs();
1203+
writeOKFrame();
11831204
} else if (cmd_frame[0] == CMD_REBOOT && memcmp(&cmd_frame[1], "reboot", 6) == 0) {
11841205
board.reboot();
11851206
} else if (cmd_frame[0] == CMD_GET_BATTERY_VOLTAGE) {

src/Dispatcher.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ void Dispatcher::checkRecv() {
101101
#endif
102102

103103
pkt->header = raw[i++];
104+
if (pkt->hasTransportCodes()) {
105+
memcpy(&pkt->transport_codes[0], &raw[i], 2); i += 2;
106+
memcpy(&pkt->transport_codes[1], &raw[i], 2); i += 2;
107+
} else {
108+
pkt->transport_codes[0] = pkt->transport_codes[1] = 0;
109+
}
104110
pkt->path_len = raw[i++];
105111

106112
if (pkt->path_len > MAX_PATH_SIZE || i + pkt->path_len > len) {
@@ -132,7 +138,7 @@ void Dispatcher::checkRecv() {
132138
#if MESH_PACKET_LOGGING
133139
Serial.print(getLogDateTime());
134140
Serial.printf(": RX, len=%d (type=%d, route=%s, payload_len=%d) SNR=%d RSSI=%d score=%d",
135-
2 + pkt->path_len + pkt->payload_len, pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
141+
pkt->getRawLength(), pkt->getPayloadType(), pkt->isRouteDirect() ? "D" : "F", pkt->payload_len,
136142
(int)pkt->getSNR(), (int)_radio->getLastRSSI(), (int)(score*1000));
137143

138144
static uint8_t packet_hash[MAX_HASH_SIZE];
@@ -147,7 +153,7 @@ void Dispatcher::checkRecv() {
147153
Serial.printf("\n");
148154
}
149155
#endif
150-
logRx(pkt, 2 + pkt->path_len + pkt->payload_len, score); // hook for custom logging
156+
logRx(pkt, pkt->getRawLength(), score); // hook for custom logging
151157

152158
if (pkt->isRouteFlood()) {
153159
n_recv_flood++;
@@ -212,6 +218,10 @@ void Dispatcher::checkSend() {
212218
raw[len++] = NODE_ID;
213219
#endif
214220
raw[len++] = outbound->header;
221+
if (outbound->hasTransportCodes()) {
222+
memcpy(&raw[len], &outbound->transport_codes[0], 2); len += 2;
223+
memcpy(&raw[len], &outbound->transport_codes[1], 2); len += 2;
224+
}
215225
raw[len++] = outbound->path_len;
216226
memcpy(&raw[len], outbound->path, outbound->path_len); len += outbound->path_len;
217227

src/Mesh.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ bool Mesh::allowPacketForward(const mesh::Packet* packet) {
1515
return false; // by default, Transport NOT enabled
1616
}
1717
uint32_t Mesh::getRetransmitDelay(const mesh::Packet* packet) {
18-
uint32_t t = (_radio->getEstAirtimeFor(packet->path_len + packet->payload_len + 2) * 52 / 50) / 2;
18+
uint32_t t = (_radio->getEstAirtimeFor(packet->getRawLength()) * 52 / 50) / 2;
1919

2020
return _rng->nextInt(0, 5)*t;
2121
}

src/Packet.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Packet::Packet() {
1010
payload_len = 0;
1111
}
1212

13+
int Packet::getRawLength() const {
14+
return 2 + path_len + payload_len + (hasTransportCodes() ? 4 : 0);
15+
}
16+
1317
void Packet::calculatePacketHash(uint8_t* hash) const {
1418
SHA256 sha;
1519
uint8_t t = getPayloadType();
@@ -24,6 +28,10 @@ void Packet::calculatePacketHash(uint8_t* hash) const {
2428
uint8_t Packet::writeTo(uint8_t dest[]) const {
2529
uint8_t i = 0;
2630
dest[i++] = header;
31+
if (hasTransportCodes()) {
32+
memcpy(&dest[i], &transport_codes[0], 2); i += 2;
33+
memcpy(&dest[i], &transport_codes[1], 2); i += 2;
34+
}
2735
dest[i++] = path_len;
2836
memcpy(&dest[i], path, path_len); i += path_len;
2937
memcpy(&dest[i], payload, payload_len); i += payload_len;
@@ -33,6 +41,12 @@ uint8_t Packet::writeTo(uint8_t dest[]) const {
3341
bool Packet::readFrom(const uint8_t src[], uint8_t len) {
3442
uint8_t i = 0;
3543
header = src[i++];
44+
if (hasTransportCodes()) {
45+
memcpy(&transport_codes[0], &src[i], 2); i += 2;
46+
memcpy(&transport_codes[1], &src[i], 2); i += 2;
47+
} else {
48+
transport_codes[0] = transport_codes[1] = 0;
49+
}
3650
path_len = src[i++];
3751
if (path_len > sizeof(path)) return false; // bad encoding
3852
memcpy(path, &src[i], path_len); i += path_len;

src/Packet.h

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ namespace mesh {
1111
#define PH_VER_SHIFT 6
1212
#define PH_VER_MASK 0x03 // 2-bits
1313

14-
#define ROUTE_TYPE_RESERVED1 0x00 // FUTURE
15-
#define ROUTE_TYPE_FLOOD 0x01 // flood mode, needs 'path' to be built up (max 64 bytes)
16-
#define ROUTE_TYPE_DIRECT 0x02 // direct route, 'path' is supplied
17-
#define ROUTE_TYPE_RESERVED2 0x03 // FUTURE
14+
#define ROUTE_TYPE_TRANSPORT_FLOOD 0x00 // flood mode + transport codes
15+
#define ROUTE_TYPE_FLOOD 0x01 // flood mode, needs 'path' to be built up (max 64 bytes)
16+
#define ROUTE_TYPE_DIRECT 0x02 // direct route, 'path' is supplied
17+
#define ROUTE_TYPE_TRANSPORT_DIRECT 0x03 // direct route + transport codes
1818

1919
#define PAYLOAD_TYPE_REQ 0x00 // request (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
2020
#define PAYLOAD_TYPE_RESPONSE 0x01 // response to REQ or ANON_REQ (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
@@ -43,6 +43,7 @@ class Packet {
4343

4444
uint8_t header;
4545
uint16_t payload_len, path_len;
46+
uint16_t transport_codes[2];
4647
uint8_t path[MAX_PATH_SIZE];
4748
uint8_t payload[MAX_PACKET_PAYLOAD];
4849
int8_t _snr;
@@ -58,8 +59,10 @@ class Packet {
5859
*/
5960
uint8_t getRouteType() const { return header & PH_ROUTE_MASK; }
6061

61-
bool isRouteFlood() const { return getRouteType() == ROUTE_TYPE_FLOOD; }
62-
bool isRouteDirect() const { return getRouteType() == ROUTE_TYPE_DIRECT; }
62+
bool isRouteFlood() const { return getRouteType() == ROUTE_TYPE_FLOOD || getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD; }
63+
bool isRouteDirect() const { return getRouteType() == ROUTE_TYPE_DIRECT || getRouteType() == ROUTE_TYPE_TRANSPORT_DIRECT; }
64+
65+
bool hasTransportCodes() const { return getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD || getRouteType() == ROUTE_TYPE_TRANSPORT_DIRECT; }
6366

6467
/**
6568
* \returns one of PAYLOAD_TYPE_ values
@@ -76,6 +79,11 @@ class Packet {
7679

7780
float getSNR() const { return ((float)_snr) / 4.0f; }
7881

82+
/**
83+
* \returns the encoded/wire format length of this packet
84+
*/
85+
int getRawLength() const;
86+
7987
/**
8088
* \brief save entire packet as a blob
8189
* \param dest (OUT) destination buffer (assumed to be MAX_MTU_SIZE)

src/helpers/BaseChatMesh.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,29 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id,
3131
}
3232
}
3333

34+
// save a copy of raw advert packet (to support "Share..." function)
35+
int plen = packet->writeTo(temp_buf);
36+
putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen);
37+
3438
bool is_new = false;
3539
if (from == NULL) {
40+
if (!isAutoAddEnabled()) {
41+
ContactInfo ci;
42+
memset(&ci, 0, sizeof(ci));
43+
ci.id = id;
44+
ci.out_path_len = -1; // initially out_path is unknown
45+
StrHelper::strncpy(ci.name, parser.getName(), sizeof(ci.name));
46+
ci.type = parser.getType();
47+
if (parser.hasLatLon()) {
48+
ci.gps_lat = parser.getIntLat();
49+
ci.gps_lon = parser.getIntLon();
50+
}
51+
ci.last_advert_timestamp = timestamp;
52+
ci.lastmod = getRTCClock()->getCurrentTime();
53+
onDiscoveredContact(ci, true); // let UI know
54+
return;
55+
}
56+
3657
is_new = true;
3758
if (num_contacts < MAX_CONTACTS) {
3859
from = &contacts[num_contacts++];
@@ -50,10 +71,6 @@ void BaseChatMesh::onAdvertRecv(mesh::Packet* packet, const mesh::Identity& id,
5071
}
5172
}
5273

53-
// save a copy of raw advert packet (to support "Share..." function)
54-
int plen = packet->writeTo(temp_buf);
55-
putBlobByKey(id.pub_key, PUB_KEY_SIZE, temp_buf, plen);
56-
5774
// update
5875
StrHelper::strncpy(from->name, parser.getName(), sizeof(from->name));
5976
from->type = parser.getType();
@@ -252,7 +269,7 @@ int BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp,
252269
mesh::Packet* pkt = composeMsgPacket(recipient, timestamp, attempt, text, expected_ack);
253270
if (pkt == NULL) return MSG_SEND_FAILED;
254271

255-
uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
272+
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
256273

257274
int rc;
258275
if (recipient.out_path_len < 0) {
@@ -279,7 +296,7 @@ int BaseChatMesh::sendCommandData(const ContactInfo& recipient, uint32_t timest
279296
auto pkt = createDatagram(PAYLOAD_TYPE_TXT_MSG, recipient.id, recipient.shared_secret, temp, 5 + text_len);
280297
if (pkt == NULL) return MSG_SEND_FAILED;
281298

282-
uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
299+
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
283300
int rc;
284301
if (recipient.out_path_len < 0) {
285302
sendFlood(pkt);
@@ -362,7 +379,7 @@ int BaseChatMesh::sendLogin(const ContactInfo& recipient, const char* password,
362379

363380
auto pkt = createAnonDatagram(PAYLOAD_TYPE_ANON_REQ, self_id, recipient.id, recipient.shared_secret, temp, tlen);
364381
if (pkt) {
365-
uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
382+
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
366383
if (recipient.out_path_len < 0) {
367384
sendFlood(pkt);
368385
est_timeout = calcFloodTimeoutMillisFor(t);
@@ -386,7 +403,7 @@ int BaseChatMesh::sendStatusRequest(const ContactInfo& recipient, uint32_t& est
386403

387404
auto pkt = createDatagram(PAYLOAD_TYPE_REQ, recipient.id, recipient.shared_secret, temp, sizeof(temp));
388405
if (pkt) {
389-
uint32_t t = _radio->getEstAirtimeFor(pkt->payload_len + pkt->path_len + 2);
406+
uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength());
390407
if (recipient.out_path_len < 0) {
391408
sendFlood(pkt);
392409
est_timeout = calcFloodTimeoutMillisFor(t);

src/helpers/BaseChatMesh.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class BaseChatMesh : public mesh::Mesh {
103103
}
104104

105105
// 'UI' concepts, for sub-classes to implement
106+
virtual bool isAutoAddEnabled() const { return true; }
106107
virtual void onDiscoveredContact(ContactInfo& contact, bool is_new) = 0;
107108
virtual bool processAck(const uint8_t *data) = 0;
108109
virtual void onContactPathUpdated(const ContactInfo& contact) = 0;

0 commit comments

Comments
 (0)