Skip to content

Commit 65be15e

Browse files
committed
Merge remote-tracking branch 'upstream/dev' into dev
2 parents 1b0999f + d4856a5 commit 65be15e

File tree

30 files changed

+724
-70
lines changed

30 files changed

+724
-70
lines changed
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"build": {
3+
"arduino": {
4+
"ldscript": "esp32s3_out.ld",
5+
"partitions": "default_16MB.csv",
6+
"memory_type": "qio_opi"
7+
},
8+
"core": "esp32",
9+
"extra_flags": [
10+
"-DBOARD_HAS_PSRAM",
11+
"-DARDUINO_USB_MODE=0",
12+
"-DARDUINO_USB_CDC_ON_BOOT=1",
13+
"-DARDUINO_RUNNING_CORE=1",
14+
"-DARDUINO_EVENT_RUNNING_CORE=1"
15+
],
16+
"f_cpu": "240000000L",
17+
"f_flash": "80000000L",
18+
"flash_mode": "qio",
19+
"psram_type": "opi",
20+
"hwids": [
21+
["0x303A", "0x1001"],
22+
["0x303A", "0x0002"]
23+
],
24+
"mcu": "esp32s3",
25+
"variant": "heltec_vision_master_t190"
26+
},
27+
"connectivity": ["wifi", "bluetooth", "lora"],
28+
"debug": {
29+
"openocd_target": "esp32s3.cfg"
30+
},
31+
"frameworks": ["arduino", "espidf"],
32+
"name": "Heltec Vision Master T190",
33+
"upload": {
34+
"flash_size": "16MB",
35+
"maximum_ram_size": 8388608,
36+
"maximum_size": 16777216,
37+
"use_1200bps_touch": true,
38+
"wait_for_upload_port": true,
39+
"require_upload_port": true,
40+
"speed": 921600
41+
},
42+
"url": "https://heltec.org/project/vision-master-t190/",
43+
"vendor": "Heltec"
44+
}

examples/simple_repeater/main.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -719,6 +719,17 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
719719
*dp = 0; // null terminator
720720
}
721721

722+
void removeNeighbor(const uint8_t* pubkey, int key_len) override {
723+
#if MAX_NEIGHBOURS
724+
for (int i = 0; i < MAX_NEIGHBOURS; i++) {
725+
NeighbourInfo* neighbour = &neighbours[i];
726+
if(memcmp(neighbour->id.pub_key, pubkey, key_len) == 0){
727+
neighbours[i] = NeighbourInfo(); // clear neighbour entry
728+
}
729+
}
730+
#endif
731+
}
732+
722733
mesh::LocalIdentity& getSelfId() override { return self_id; }
723734

724735
void clearStats() override {

examples/simple_sensor/SensorMesh.cpp

Lines changed: 72 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,23 @@ void SensorMesh::getPeerSharedSecret(uint8_t* dest_secret, int peer_idx) {
613613
}
614614
}
615615

616+
void SensorMesh::sendAckTo(const ContactInfo& dest, uint32_t ack_hash) {
617+
if (dest.out_path_len < 0) {
618+
mesh::Packet* ack = createAck(ack_hash);
619+
if (ack) sendFlood(ack, TXT_ACK_DELAY);
620+
} else {
621+
uint32_t d = TXT_ACK_DELAY;
622+
if (getExtraAckTransmitCount() > 0) {
623+
mesh::Packet* a1 = createMultiAck(ack_hash, 1);
624+
if (a1) sendDirect(a1, dest.out_path, dest.out_path_len, d);
625+
d += 300;
626+
}
627+
628+
mesh::Packet* a2 = createAck(ack_hash);
629+
if (a2) sendDirect(a2, dest.out_path, dest.out_path_len, d);
630+
}
631+
}
632+
616633
void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) {
617634
int i = matching_peer_indexes[sender_idx];
618635
if (i < 0 || i >= num_contacts) {
@@ -656,45 +673,71 @@ void SensorMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_i
656673
memcpy(&sender_timestamp, data, 4); // timestamp (by sender's RTC clock - which could be wrong)
657674
uint flags = (data[4] >> 2); // message attempt number, and other flags
658675

659-
if (!(flags == TXT_TYPE_CLI_DATA)) {
660-
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported text type received: flags=%02x", (uint32_t)flags);
661-
} else if (sender_timestamp > from.last_timestamp) { // prevent replay attacks
662-
from.last_timestamp = sender_timestamp;
663-
from.last_activity = getRTCClock()->getCurrentTime();
664-
665-
// len can be > original length, but 'text' will be padded with zeroes
666-
data[len] = 0; // need to make a C string again, with null terminator
667-
668-
uint8_t temp[166];
669-
char *command = (char *) &data[5];
670-
char *reply = (char *) &temp[5];
671-
handleCommand(sender_timestamp, command, reply);
672-
673-
int text_len = strlen(reply);
674-
if (text_len > 0) {
675-
uint32_t timestamp = getRTCClock()->getCurrentTimeUnique();
676-
if (timestamp == sender_timestamp) {
677-
// WORKAROUND: the two timestamps need to be different, in the CLI view
678-
timestamp++;
679-
}
680-
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique
681-
temp[4] = (TXT_TYPE_CLI_DATA << 2);
682-
683-
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, from.id, secret, temp, 5 + text_len);
684-
if (reply) {
685-
if (from.out_path_len < 0) {
686-
sendFlood(reply, CLI_REPLY_DELAY_MILLIS);
676+
if (sender_timestamp > from.last_timestamp) { // prevent replay attacks
677+
if (flags == TXT_TYPE_PLAIN) {
678+
bool handled = handleIncomingMsg(from, sender_timestamp, &data[5], flags, len - 5);
679+
if (handled) { // if msg was handled then send an ack
680+
uint32_t ack_hash; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it
681+
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 5 + strlen((char *)&data[5]), from.id.pub_key, PUB_KEY_SIZE);
682+
683+
if (packet->isRouteFlood()) {
684+
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK
685+
mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len,
686+
PAYLOAD_TYPE_ACK, (uint8_t *) &ack_hash, 4);
687+
if (path) sendFlood(path, TXT_ACK_DELAY);
687688
} else {
688-
sendDirect(reply, from.out_path, from.out_path_len, CLI_REPLY_DELAY_MILLIS);
689+
sendAckTo(from, ack_hash);
690+
}
691+
}
692+
} else if (flags == TXT_TYPE_CLI_DATA) {
693+
from.last_timestamp = sender_timestamp;
694+
from.last_activity = getRTCClock()->getCurrentTime();
695+
696+
// len can be > original length, but 'text' will be padded with zeroes
697+
data[len] = 0; // need to make a C string again, with null terminator
698+
699+
uint8_t temp[166];
700+
char *command = (char *) &data[5];
701+
char *reply = (char *) &temp[5];
702+
handleCommand(sender_timestamp, command, reply);
703+
704+
int text_len = strlen(reply);
705+
if (text_len > 0) {
706+
uint32_t timestamp = getRTCClock()->getCurrentTimeUnique();
707+
if (timestamp == sender_timestamp) {
708+
// WORKAROUND: the two timestamps need to be different, in the CLI view
709+
timestamp++;
710+
}
711+
memcpy(temp, &timestamp, 4); // mostly an extra blob to help make packet_hash unique
712+
temp[4] = (TXT_TYPE_CLI_DATA << 2);
713+
714+
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, from.id, secret, temp, 5 + text_len);
715+
if (reply) {
716+
if (from.out_path_len < 0) {
717+
sendFlood(reply, CLI_REPLY_DELAY_MILLIS);
718+
} else {
719+
sendDirect(reply, from.out_path, from.out_path_len, CLI_REPLY_DELAY_MILLIS);
720+
}
689721
}
690722
}
723+
} else {
724+
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported text type received: flags=%02x", (uint32_t)flags);
691725
}
692726
} else {
693727
MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected");
694728
}
695729
}
696730
}
697731

732+
bool SensorMesh::handleIncomingMsg(ContactInfo& from, uint32_t timestamp, uint8_t* data, uint flags, size_t len) {
733+
MESH_DEBUG_PRINT("handleIncomingMsg: unhandled msg from ");
734+
#ifdef MESH_DEBUG
735+
mesh::Utils::printHex(Serial, from.id.pub_key, PUB_KEY_SIZE);
736+
Serial.printf(": %s\n", data);
737+
#endif
738+
return false;
739+
}
740+
698741
bool SensorMesh::onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) {
699742
int i = matching_peer_indexes[sender_idx];
700743
if (i < 0 || i >= num_contacts) {

examples/simple_sensor/SensorMesh.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ class SensorMesh : public mesh::Mesh, public CommonCLICallbacks {
140140
void onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender_idx, const uint8_t* secret, uint8_t* data, size_t len) override;
141141
bool onPeerPathRecv(mesh::Packet* packet, int sender_idx, const uint8_t* secret, uint8_t* path, uint8_t path_len, uint8_t extra_type, uint8_t* extra, uint8_t extra_len) override;
142142
void onAckRecv(mesh::Packet* packet, uint32_t ack_crc) override;
143-
143+
virtual bool handleIncomingMsg(ContactInfo& from, uint32_t timestamp, uint8_t* data, uint flags, size_t len);
144+
void sendAckTo(const ContactInfo& dest, uint32_t ack_hash);
144145
private:
145146
FILESYSTEM* _fs;
146147
unsigned long next_local_advert, next_flood_advert;

src/helpers/CommonCLI.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,17 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
165165
}
166166
} else if (memcmp(command, "neighbors", 9) == 0) {
167167
_callbacks->formatNeighborsReply(reply);
168+
} else if (memcmp(command, "neighbor.remove ", 16) == 0) {
169+
const char* hex = &command[16];
170+
uint8_t pubkey[PUB_KEY_SIZE];
171+
int hex_len = min((int)strlen(hex), PUB_KEY_SIZE*2);
172+
int pubkey_len = hex_len / 2;
173+
if (mesh::Utils::fromHex(pubkey, pubkey_len, hex)) {
174+
_callbacks->removeNeighbor(pubkey, pubkey_len);
175+
strcpy(reply, "OK");
176+
} else {
177+
strcpy(reply, "ERR: bad pubkey");
178+
}
168179
} else if (memcmp(command, "tempradio ", 10) == 0) {
169180
strcpy(tmp, &command[10]);
170181
const char *parts[5];

src/helpers/CommonCLI.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class CommonCLICallbacks {
4343
virtual void dumpLogFile() = 0;
4444
virtual void setTxPower(uint8_t power_dbm) = 0;
4545
virtual void formatNeighborsReply(char *reply) = 0;
46+
virtual void removeNeighbor(const uint8_t* pubkey, int key_len) {
47+
// no op by default
48+
};
4649
virtual mesh::LocalIdentity& getSelfId() = 0;
4750
virtual void clearStats() = 0;
4851
virtual void applyTempRadioParams(float freq, float bw, uint8_t sf, uint8_t cr, int timeout_mins) = 0;

src/helpers/esp32/SerialWifiInterface.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,18 @@ bool SerialWifiInterface::isWriteBusy() const {
4444
}
4545

4646
size_t SerialWifiInterface::checkRecvFrame(uint8_t dest[]) {
47-
if (!client) client = server.available();
47+
// check if new client connected
48+
auto newClient = server.available();
49+
if (newClient) {
50+
51+
// disconnect existing client
52+
deviceConnected = false;
53+
client.stop();
54+
55+
// switch active connection to new client
56+
client = newClient;
57+
58+
}
4859

4960
if (client.connected()) {
5061
if (!deviceConnected) {

src/helpers/nrf52/T114Board.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,45 @@ void T114Board::begin() {
2626

2727
pinMode(PIN_VBAT_READ, INPUT);
2828

29+
// Enable SoftDevice low-power mode
30+
sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
31+
32+
// Enable DC/DC converter for better efficiency (REG1 stage)
33+
NRF_POWER->DCDCEN = 1;
34+
35+
// Power down unused communication peripherals
36+
// UART1 - Not used on T114
37+
NRF_UARTE1->ENABLE = 0;
38+
39+
// SPIM2/SPIS2 - Not used (SPI is on SPIM0)
40+
NRF_SPIM2->ENABLE = 0;
41+
NRF_SPIS2->ENABLE = 0;
42+
43+
// TWI1 (I2C1) - Not used (I2C is on TWI0)
44+
NRF_TWIM1->ENABLE = 0;
45+
NRF_TWIS1->ENABLE = 0;
46+
47+
// PWM modules - Not used for standard T114 functions
48+
NRF_PWM1->ENABLE = 0;
49+
NRF_PWM2->ENABLE = 0;
50+
NRF_PWM3->ENABLE = 0;
51+
52+
// PDM (Digital Microphone Interface) - Not used
53+
NRF_PDM->ENABLE = 0;
54+
55+
// I2S - Not used
56+
NRF_I2S->ENABLE = 0;
57+
58+
// QSPI - Not used (no external flash)
59+
NRF_QSPI->ENABLE = 0;
60+
61+
// Disable unused analog peripherals
62+
// SAADC channels - only keep what's needed for battery monitoring
63+
NRF_SAADC->ENABLE = 0; // Re-enable only when needed for measurements
64+
65+
// COMP - Comparator not used
66+
NRF_COMP->ENABLE = 0;
67+
2968
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
3069
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
3170
#endif

src/helpers/nrf52/T114Board.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ class T114Board : public mesh::MainBoard {
4040

4141
uint16_t getBattMilliVolts() override {
4242
int adcvalue = 0;
43+
44+
NRF_SAADC->ENABLE = 1;
45+
4346
analogReadResolution(12);
4447
analogReference(AR_INTERNAL_3_0);
4548
pinMode(PIN_BAT_CTL, OUTPUT); // battery adc can be read only ctrl pin 6 set to high
@@ -49,6 +52,8 @@ class T114Board : public mesh::MainBoard {
4952
adcvalue = analogRead(PIN_VBAT_READ);
5053
digitalWrite(6, 0);
5154

55+
NRF_SAADC->ENABLE = 0;
56+
5257
return (uint16_t)((float)adcvalue * MV_LSB * 4.9);
5358
}
5459

src/helpers/ui/ST7789Display.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ bool ST7789Display::begin() {
1818
pinMode(PIN_TFT_VDD_CTL, OUTPUT);
1919
pinMode(PIN_TFT_LEDA_CTL, OUTPUT);
2020
digitalWrite(PIN_TFT_VDD_CTL, LOW);
21+
#ifdef PIN_TFT_LEDA_CTL_ACTIVE
22+
digitalWrite(PIN_TFT_LEDA_CTL, PIN_TFT_LEDA_CTL_ACTIVE);
23+
#else
2124
digitalWrite(PIN_TFT_LEDA_CTL, LOW);
25+
#endif
2226
digitalWrite(PIN_TFT_RST, HIGH);
2327

2428
display.init();
@@ -43,15 +47,22 @@ void ST7789Display::turnOn() {
4347
delay(20);
4448

4549
// Now turn on the backlight
50+
#ifdef PIN_TFT_LEDA_CTL_ACTIVE
51+
digitalWrite(PIN_TFT_LEDA_CTL, PIN_TFT_LEDA_CTL_ACTIVE);
52+
#else
4653
digitalWrite(PIN_TFT_LEDA_CTL, LOW);
47-
54+
#endif
4855
_isOn = true;
4956
}
5057
}
5158

5259
void ST7789Display::turnOff() {
5360
digitalWrite(PIN_TFT_VDD_CTL, HIGH);
61+
#ifdef PIN_TFT_LEDA_CTL_ACTIVE
62+
digitalWrite(PIN_TFT_LEDA_CTL, !PIN_TFT_LEDA_CTL_ACTIVE);
63+
#else
5464
digitalWrite(PIN_TFT_LEDA_CTL, HIGH);
65+
#endif
5566
digitalWrite(PIN_TFT_RST, LOW);
5667
_isOn = false;
5768
}

0 commit comments

Comments
 (0)