Compare commits
44 Commits
main
...
meshcore-v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b7a3fb122c | ||
|
|
c0a107cb23 | ||
|
|
e9e98f1c87 | ||
|
|
c41309601d | ||
|
|
30d4059a8c | ||
|
|
08b25cf724 | ||
|
|
f7f6dbc22d | ||
|
|
9a1be5386d | ||
|
|
2d2c0475ac | ||
|
|
d81626ece5 | ||
|
|
fe7c388fdb | ||
|
|
3955a96df7 | ||
|
|
1cb6c7a900 | ||
|
|
e902b8f121 | ||
|
|
8217a67f0c | ||
|
|
46b5076082 | ||
|
|
b4f690bfeb | ||
|
|
c33639ee3c | ||
|
|
1f066ad9c4 | ||
|
|
321d5a7e98 | ||
|
|
aa796e8623 | ||
|
|
06e0273e1f | ||
|
|
5372388e6e | ||
|
|
dc548578ff | ||
|
|
d3b457681a | ||
|
|
f1f1361fd5 | ||
|
|
bdae60498c | ||
|
|
66fe9ac2db | ||
|
|
cc6139bc23 | ||
|
|
d516cd8a9e | ||
|
|
07671cf1fe | ||
|
|
d532481f34 | ||
|
|
28b1586fd7 | ||
|
|
3d982711a6 | ||
|
|
db7baa7bd7 | ||
|
|
e8907a3108 | ||
|
|
992dddeee5 | ||
|
|
dc9e6ae893 | ||
|
|
de97baba4f | ||
|
|
8744213945 | ||
|
|
952d12c8b9 | ||
|
|
a5ae89aac0 | ||
|
|
c0252ab82c | ||
|
|
f864c5f547 |
4
.github/workflows/pr-build-check.yml
vendored
4
.github/workflows/pr-build-check.yml
vendored
@@ -39,6 +39,10 @@ jobs:
|
||||
- wio-e5-mini_repeater
|
||||
# ESP32-C6
|
||||
- LilyGo_Tlora_C6_repeater_
|
||||
# LR1110 (nRF52)
|
||||
- wio_wm1110_repeater
|
||||
# SX1276 (ESP32)
|
||||
- Tbeam_SX1276_repeater
|
||||
|
||||
steps:
|
||||
- name: Clone Repo
|
||||
|
||||
@@ -53,10 +53,13 @@
|
||||
"protocols": [
|
||||
"jlink",
|
||||
"nrfjprog",
|
||||
"nrfutil",
|
||||
"stlink",
|
||||
"cmsis-dap",
|
||||
"blackmagic"
|
||||
]
|
||||
],
|
||||
"use_1200bps_touch": true,
|
||||
"wait_for_upload_port": true
|
||||
},
|
||||
"url": "https://os.mbed.com/platforms/Nordic-nRF52840-DK/",
|
||||
"vendor": "Nordic"
|
||||
|
||||
@@ -258,7 +258,7 @@ float MyMesh::getAirtimeBudgetFactor() const {
|
||||
}
|
||||
|
||||
int MyMesh::getInterferenceThreshold() const {
|
||||
return 0; // disabled for now, until currentRSSI() problem is resolved
|
||||
return 1; // non-zero enables hardware CAD (Channel Activity Detection) before TX
|
||||
}
|
||||
|
||||
int MyMesh::calcRxDelay(float score, uint32_t air_time) const {
|
||||
|
||||
@@ -447,6 +447,15 @@ bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Limit flood advert paket forwarding using a probabilistic reduction defined by P(h) = 0.308^(hops-1)
|
||||
// https://github.com/meshcore-dev/MeshCore/issues/1223
|
||||
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood()) {
|
||||
double roll_dice = (double)rand() / RAND_MAX;
|
||||
double forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1);
|
||||
if (roll_dice > forw_prob)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -550,7 +559,9 @@ bool MyMesh::filterRecvFloodPacket(mesh::Packet* pkt) {
|
||||
if (pkt->getRouteType() == ROUTE_TYPE_TRANSPORT_FLOOD) {
|
||||
recv_pkt_region = region_map.findMatch(pkt, REGION_DENY_FLOOD);
|
||||
} else if (pkt->getRouteType() == ROUTE_TYPE_FLOOD) {
|
||||
if (region_map.getWildcard().flags & REGION_DENY_FLOOD) {
|
||||
if ((pkt->getPayloadType() == PAYLOAD_TYPE_GRP_TXT ||
|
||||
pkt->getPayloadType() == PAYLOAD_TYPE_GRP_DATA) &&
|
||||
region_map.getWildcard().flags & REGION_DENY_FLOOD) {
|
||||
recv_pkt_region = NULL;
|
||||
} else {
|
||||
recv_pkt_region = ®ion_map.getWildcard();
|
||||
@@ -884,9 +895,10 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
||||
_prefs.cr = LORA_CR;
|
||||
_prefs.tx_power_dbm = LORA_TX_POWER;
|
||||
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
|
||||
_prefs.flood_advert_interval = 12; // 12 hours
|
||||
_prefs.flood_advert_interval = 0; // disabled
|
||||
_prefs.flood_advert_base = 0.308f;
|
||||
_prefs.flood_max = 64;
|
||||
_prefs.interference_threshold = 0; // disabled
|
||||
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX
|
||||
|
||||
// bridge defaults
|
||||
_prefs.bridge_enabled = 1; // enabled
|
||||
|
||||
@@ -282,7 +282,17 @@ uint32_t MyMesh::getDirectRetransmitDelay(const mesh::Packet *packet) {
|
||||
|
||||
bool MyMesh::allowPacketForward(const mesh::Packet *packet) {
|
||||
if (_prefs.disable_fwd) return false;
|
||||
if (packet->isRouteFlood() && packet->getPathHashCount() >= _prefs.flood_max) return false;
|
||||
if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false;
|
||||
|
||||
// Limit flood advert packet forwarding using a probabilistic reduction defined by P(h) = base^(hops-1)
|
||||
// https://github.com/meshcore-dev/MeshCore/issues/1223
|
||||
if (packet->getPayloadType() == PAYLOAD_TYPE_ADVERT && packet->isRouteFlood()) {
|
||||
double roll_dice = (double)rand() / RAND_MAX;
|
||||
double forw_prob = pow(_prefs.flood_advert_base, packet->path_len - 1);
|
||||
if (roll_dice > forw_prob)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -642,8 +652,9 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc
|
||||
_prefs.disable_fwd = 1;
|
||||
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
|
||||
_prefs.flood_advert_interval = 12; // 12 hours
|
||||
_prefs.flood_advert_base = 0.308f;
|
||||
_prefs.flood_max = 64;
|
||||
_prefs.interference_threshold = 0; // disabled
|
||||
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX
|
||||
#ifdef ROOM_PASSWORD
|
||||
StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
|
||||
#endif
|
||||
|
||||
@@ -725,7 +725,7 @@ SensorMesh::SensorMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh::Millise
|
||||
_prefs.flood_advert_interval = 0; // disabled
|
||||
_prefs.disable_fwd = true;
|
||||
_prefs.flood_max = 64;
|
||||
_prefs.interference_threshold = 0; // disabled
|
||||
_prefs.interference_threshold = 1; // non-zero enables hardware CAD before TX
|
||||
|
||||
// GPS defaults
|
||||
_prefs.gps_enabled = 0;
|
||||
|
||||
@@ -28,6 +28,7 @@ build_flags = -w -DNDEBUG -DRADIOLIB_STATIC_ONLY=1 -DRADIOLIB_GODMODE=1
|
||||
-D LORA_FREQ=869.618
|
||||
-D LORA_BW=62.5
|
||||
-D LORA_SF=8
|
||||
-D LORA_CR=8
|
||||
-D ENABLE_ADVERT_ON_BOOT=1
|
||||
-D ENABLE_PRIVATE_KEY_IMPORT=1 ; NOTE: comment these out for more secure firmware
|
||||
-D ENABLE_PRIVATE_KEY_EXPORT=1
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "CommonCLI.h"
|
||||
#include "TxtDataHelpers.h"
|
||||
#include "AdvertDataHelpers.h"
|
||||
#include "TxtDataHelpers.h"
|
||||
#include <RTClib.h>
|
||||
|
||||
#ifndef BRIDGE_MAX_BAUD
|
||||
@@ -87,8 +88,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
||||
file.read((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
||||
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
||||
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
||||
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
|
||||
// next: 291
|
||||
file.read((uint8_t *)&_prefs->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290
|
||||
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 294
|
||||
|
||||
// sanitise bad pref values
|
||||
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
|
||||
@@ -118,6 +119,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
|
||||
|
||||
// sanitise settings
|
||||
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
|
||||
_prefs->flood_advert_base = constrain(_prefs->flood_advert_base, 0, 1);
|
||||
|
||||
file.close();
|
||||
}
|
||||
@@ -178,8 +180,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
|
||||
file.write((uint8_t *)&_prefs->discovery_mod_timestamp, sizeof(_prefs->discovery_mod_timestamp)); // 162
|
||||
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
|
||||
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
|
||||
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
|
||||
// next: 291
|
||||
file.write((uint8_t *)&_prefs->flood_advert_base, sizeof(_prefs->flood_advert_base)); // 290
|
||||
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 294
|
||||
|
||||
file.close();
|
||||
}
|
||||
@@ -285,7 +287,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re
|
||||
// change admin password
|
||||
StrHelper::strncpy(_prefs->password, &command[9], sizeof(_prefs->password));
|
||||
savePrefs();
|
||||
sprintf(reply, "password now: %s", _prefs->password); // echo back just to let admin know for sure!!
|
||||
sprintf(reply, "password now: ");
|
||||
StrHelper::strncpy(&reply[14], _prefs->password, 160-15); // echo back just to let admin know for sure!!
|
||||
} else if (memcmp(command, "clear stats", 11) == 0) {
|
||||
_callbacks->clearStats();
|
||||
strcpy(reply, "(OK - stats reset)");
|
||||
@@ -605,6 +608,15 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
} else {
|
||||
strcpy(reply, "Error, max 64");
|
||||
}
|
||||
} else if (memcmp(config, "flood.advert.base ", 18) == 0) {
|
||||
float f = atof(&config[18]);
|
||||
if (f >= 0.0f && f <= 1.0f) {
|
||||
_prefs->flood_advert_base = f;
|
||||
savePrefs();
|
||||
strcpy(reply, "OK");
|
||||
} else {
|
||||
strcpy(reply, "Error: base must be between 0 and 1");
|
||||
}
|
||||
} else if (memcmp(config, "direct.txdelay ", 15) == 0) {
|
||||
float f = atof(&config[15]);
|
||||
if (f >= 0) {
|
||||
@@ -726,7 +738,8 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
strcpy(reply, "Error: unsupported by this board");
|
||||
};
|
||||
} else {
|
||||
sprintf(reply, "unknown config: %s", config);
|
||||
strcpy(reply, "unknown config: ");
|
||||
StrHelper::strncpy(&reply[16], config, 160-17);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -781,13 +794,16 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
|
||||
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->tx_delay_factor));
|
||||
} else if (memcmp(config, "flood.max", 9) == 0) {
|
||||
sprintf(reply, "> %d", (uint32_t)_prefs->flood_max);
|
||||
} else if (memcmp(config, "flood.advert.base", 17) == 0) {
|
||||
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->flood_advert_base));
|
||||
} else if (memcmp(config, "direct.txdelay", 14) == 0) {
|
||||
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->direct_tx_delay_factor));
|
||||
} else if (memcmp(config, "owner.info", 10) == 0) {
|
||||
auto start = reply;
|
||||
*reply++ = '>';
|
||||
*reply++ = ' ';
|
||||
const char* sp = _prefs->owner_info;
|
||||
while (*sp) {
|
||||
while (*sp && reply - start < 159) {
|
||||
*reply++ = (*sp == '\n') ? '|' : *sp; // translate newline back to orig '|'
|
||||
sp++;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ struct NodePrefs { // persisted to file
|
||||
uint8_t flood_max;
|
||||
uint8_t interference_threshold;
|
||||
uint8_t agc_reset_interval; // secs / 4
|
||||
float flood_advert_base;
|
||||
// Bridge settings
|
||||
uint8_t bridge_enabled; // boolean
|
||||
uint16_t bridge_delay; // milliseconds (default 500 ms)
|
||||
|
||||
@@ -168,10 +168,20 @@ void RadioLibWrapper::onSendFinished() {
|
||||
state = STATE_IDLE;
|
||||
}
|
||||
|
||||
int16_t RadioLibWrapper::performChannelScan() {
|
||||
return _radio->scanChannel();
|
||||
}
|
||||
|
||||
bool RadioLibWrapper::isChannelActive() {
|
||||
return _threshold == 0
|
||||
? false // interference check is disabled
|
||||
: getCurrentRSSI() > _noise_floor + _threshold;
|
||||
if (_threshold == 0) return false; // interference check is disabled
|
||||
|
||||
int16_t result = performChannelScan();
|
||||
// scanChannel() triggers DIO interrupt (CAD done) which sets STATE_INT_READY
|
||||
// via setFlag() ISR. Clear it before restarting RX so recvRaw() doesn't
|
||||
// try to read a non-existent packet and count a spurious recv error.
|
||||
state = STATE_IDLE;
|
||||
startRecv();
|
||||
return result != RADIOLIB_CHANNEL_FREE;
|
||||
}
|
||||
|
||||
float RadioLibWrapper::getLastRSSI() const {
|
||||
|
||||
@@ -38,6 +38,7 @@ public:
|
||||
}
|
||||
|
||||
virtual float getCurrentRSSI() =0;
|
||||
virtual int16_t performChannelScan();
|
||||
|
||||
int getNoiseFloor() const override { return _noise_floor; }
|
||||
void triggerNoiseFloorCalibrate(int threshold) override;
|
||||
|
||||
@@ -9,7 +9,7 @@ void LoRaFEMControl::init(void)
|
||||
pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
|
||||
pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH);
|
||||
setLnaCanControl(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@ class LoRaFEMControl
|
||||
void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
|
||||
|
||||
private:
|
||||
bool lna_enabled = false;
|
||||
bool lna_enabled = true;
|
||||
bool lna_can_control = false;
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
// Power management boot protection threshold (millivolts)
|
||||
// Set to 0 to disable boot protection
|
||||
#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV)
|
||||
#define PWRMGT_VOLTAGE_BOOTLOCK 0 // Won't boot below this voltage (mV)
|
||||
// LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
|
||||
// AIN2 = P0.04 = BATTERY_PIN / PIN_VBAT_READ
|
||||
#define PWRMGT_LPCOMP_AIN 2
|
||||
|
||||
@@ -14,7 +14,7 @@ void LoRaFEMControl::init(void)
|
||||
pinMode(P_LORA_KCT8103L_PA_CSD, OUTPUT);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CSD, HIGH);
|
||||
pinMode(P_LORA_KCT8103L_PA_CTX, OUTPUT);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CTX, HIGH);
|
||||
digitalWrite(P_LORA_KCT8103L_PA_CTX, lna_enabled ? LOW : HIGH);
|
||||
setLnaCanControl(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,6 @@ class LoRaFEMControl
|
||||
void setLnaCanControl(bool can_control) { lna_can_control = can_control; }
|
||||
|
||||
private:
|
||||
bool lna_enabled = false;
|
||||
bool lna_enabled = true;
|
||||
bool lna_can_control = false;
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@ void HeltecV4Board::begin() {
|
||||
|
||||
digitalWrite(PIN_ADC_CTRL, LOW);
|
||||
|
||||
return (5.42 * (3.3 / 1024.0) * raw) * 1000;
|
||||
return (adc_mult * (3.3 / 1024.0) * raw) * 1000;
|
||||
}
|
||||
|
||||
const char* HeltecV4Board::getManufacturerName() const {
|
||||
|
||||
@@ -5,8 +5,16 @@
|
||||
#include <helpers/ESP32Board.h>
|
||||
#include <driver/rtc_io.h>
|
||||
#include "LoRaFEMControl.h"
|
||||
|
||||
#ifndef ADC_MULTIPLIER
|
||||
#define ADC_MULTIPLIER 5.42
|
||||
#endif
|
||||
|
||||
class HeltecV4Board : public ESP32Board {
|
||||
|
||||
protected:
|
||||
float adc_mult = ADC_MULTIPLIER;
|
||||
|
||||
public:
|
||||
RefCountedDigitalPin periph_power;
|
||||
LoRaFEMControl loRaFEMControl;
|
||||
@@ -18,6 +26,14 @@ public:
|
||||
void enterDeepSleep(uint32_t secs, int pin_wake_btn = -1);
|
||||
void powerOff() override;
|
||||
uint16_t getBattMilliVolts() override;
|
||||
const char* getManufacturerName() const override ;
|
||||
|
||||
bool setAdcMultiplier(float multiplier) override {
|
||||
if (multiplier == 0.0f) {
|
||||
adc_mult = ADC_MULTIPLIER;
|
||||
} else {
|
||||
adc_mult = multiplier;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
float getAdcMultiplier() const override { return adc_mult; }
|
||||
const char* getManufacturerName() const override;
|
||||
};
|
||||
|
||||
@@ -102,6 +102,28 @@ lib_deps =
|
||||
${esp32_ota.lib_deps}
|
||||
bakercp/CRC32 @ ^2.0.0
|
||||
|
||||
[env:heltec_v4_expansionkit_repeater]
|
||||
extends = heltec_v4_oled
|
||||
build_flags =
|
||||
${heltec_v4_oled.build_flags}
|
||||
-D DISPLAY_CLASS=SSD1306Display
|
||||
-D ADVERT_NAME='"Heltec Repeater"'
|
||||
-D ADVERT_LAT=0.0
|
||||
-D ADVERT_LON=0.0
|
||||
-D ADMIN_PASSWORD='"password"'
|
||||
-D MAX_NEIGHBOURS=50
|
||||
; -D MESH_PACKET_LOGGING=1
|
||||
; -D MESH_DEBUG=1
|
||||
-D ENV_PIN_SDA=4
|
||||
-D ENV_PIN_SCL=3
|
||||
build_src_filter = ${heltec_v4_oled.build_src_filter}
|
||||
+<helpers/ui/SSD1306Display.cpp>
|
||||
+<../examples/simple_repeater>
|
||||
lib_deps =
|
||||
${heltec_v4_oled.lib_deps}
|
||||
${esp32_ota.lib_deps}
|
||||
bakercp/CRC32 @ ^2.0.0
|
||||
|
||||
[env:heltec_v4_repeater_bridge_espnow]
|
||||
extends = heltec_v4_oled
|
||||
build_flags =
|
||||
|
||||
@@ -8,24 +8,47 @@
|
||||
void TechoBoard::begin() {
|
||||
NRF52Board::begin();
|
||||
|
||||
// Configure battery measurement control BEFORE Wire.begin()
|
||||
// to ensure P0.02 is not claimed by another peripheral
|
||||
pinMode(PIN_VBAT_MEAS_EN, OUTPUT);
|
||||
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
|
||||
pinMode(PIN_VBAT_READ, INPUT);
|
||||
|
||||
Wire.begin();
|
||||
|
||||
pinMode(SX126X_POWER_EN, OUTPUT);
|
||||
digitalWrite(SX126X_POWER_EN, HIGH);
|
||||
delay(10); // give sx1262 some time to power up
|
||||
delay(10);
|
||||
}
|
||||
|
||||
uint16_t TechoBoard::getBattMilliVolts() {
|
||||
int adcvalue = 0;
|
||||
|
||||
// Use LilyGo's exact ADC configuration
|
||||
analogReference(AR_INTERNAL_3_0);
|
||||
analogReadResolution(12);
|
||||
delay(10);
|
||||
|
||||
// ADC range is 0..3000mV and resolution is 12-bit (0..4095)
|
||||
adcvalue = analogRead(PIN_VBAT_READ);
|
||||
// Convert the raw value to compensated mv, taking the resistor-
|
||||
// divider into account (providing the actual LIPO voltage)
|
||||
return (uint16_t)((float)adcvalue * REAL_VBAT_MV_PER_LSB);
|
||||
// Enable battery voltage divider (MOSFET gate on P0.31)
|
||||
pinMode(PIN_VBAT_MEAS_EN, OUTPUT);
|
||||
digitalWrite(PIN_VBAT_MEAS_EN, HIGH);
|
||||
|
||||
// Reclaim P0.02 for analog input (in case another peripheral touched it)
|
||||
pinMode(PIN_VBAT_READ, INPUT);
|
||||
delay(10); // let divider + ADC settle
|
||||
|
||||
// Read and average (matching LilyGo's approach)
|
||||
uint32_t sum = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
sum += analogRead(PIN_VBAT_READ);
|
||||
delayMicroseconds(100);
|
||||
}
|
||||
uint16_t adc = sum / 8;
|
||||
|
||||
// Disable divider to save power
|
||||
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
|
||||
|
||||
// LilyGo's exact formula: adc * (3000.0 / 4096.0) * 2.0
|
||||
// = adc * 0.73242188 * 2.0 = adc * 1.46484375
|
||||
uint16_t millivolts = (uint16_t)((float)adc * (3000.0f / 4096.0f) * 2.0f);
|
||||
|
||||
return millivolts;
|
||||
}
|
||||
#endif
|
||||
@@ -4,14 +4,12 @@
|
||||
#include <Arduino.h>
|
||||
#include <helpers/NRF52Board.h>
|
||||
|
||||
// built-ins
|
||||
#define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096
|
||||
|
||||
#define VBAT_DIVIDER (0.5F) // 150K + 150K voltage divider on VBAT
|
||||
#define VBAT_DIVIDER_COMP (2.0F) // Compensation factor for the VBAT divider
|
||||
|
||||
#define PIN_VBAT_READ (4)
|
||||
#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)
|
||||
// ============================================================
|
||||
// T-Echo Lite battery pins — hardcoded from LilyGo t_echo_lite_config.h
|
||||
// NOT using any defines from variant.h for battery measurement
|
||||
// ============================================================
|
||||
#define PIN_VBAT_READ _PINNUM(0, 2) // BATTERY_ADC_DATA
|
||||
#define PIN_VBAT_MEAS_EN _PINNUM(0, 31) // BATTERY_MEASUREMENT_CONTROL
|
||||
|
||||
class TechoBoard : public NRF52BoardDCDC {
|
||||
public:
|
||||
@@ -20,10 +18,11 @@ public:
|
||||
uint16_t getBattMilliVolts() override;
|
||||
|
||||
const char* getManufacturerName() const override {
|
||||
return "LilyGo T-Echo";
|
||||
return "LilyGo T-Echo Lite";
|
||||
}
|
||||
|
||||
void powerOff() override {
|
||||
digitalWrite(PIN_VBAT_MEAS_EN, LOW);
|
||||
#ifdef LED_RED
|
||||
digitalWrite(LED_RED, LOW);
|
||||
#endif
|
||||
|
||||
@@ -96,3 +96,47 @@ build_src_filter = ${LilyGo_T-Echo-Lite.build_src_filter}
|
||||
lib_deps =
|
||||
${LilyGo_T-Echo-Lite.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
[env:LilyGo_T-Echo-Lite_non_shell_companion_radio_ble]
|
||||
extends = LilyGo_T-Echo-Lite
|
||||
upload_protocol = nrfutil
|
||||
board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld
|
||||
board_upload.maximum_size = 712704
|
||||
build_flags =
|
||||
${nrf52_base.build_flags}
|
||||
-I variants/lilygo_techo_lite
|
||||
-I src/helpers/nrf52
|
||||
-I lib/nrf52/s140_nrf52_6.1.1_API/include
|
||||
-I lib/nrf52/s140_nrf52_6.1.1_API/include/nrf52
|
||||
-I src/helpers/ui
|
||||
-I examples/companion_radio/ui-new
|
||||
-D LILYGO_TECHO
|
||||
-D RADIO_CLASS=CustomSX1262
|
||||
-D WRAPPER_CLASS=CustomSX1262Wrapper
|
||||
-D LORA_TX_POWER=22
|
||||
-D SX126X_POWER_EN=30
|
||||
-D SX126X_CURRENT_LIMIT=140
|
||||
-D SX126X_RX_BOOSTED_GAIN=1
|
||||
-D P_LORA_TX_LED=LED_GREEN
|
||||
-D DISABLE_DIAGNOSTIC_OUTPUT
|
||||
-D ENV_INCLUDE_GPS=1
|
||||
-D GPS_BAUD_RATE=9600
|
||||
-D PIN_GPS_EN=GPS_EN
|
||||
-D AUTO_OFF_MILLIS=0
|
||||
-D MAX_CONTACTS=350
|
||||
-D MAX_GROUP_CHANNELS=40
|
||||
-D BLE_PIN_CODE=123456
|
||||
-D OFFLINE_QUEUE_SIZE=256
|
||||
-D UI_RECENT_LIST_SIZE=9
|
||||
-D AUTO_SHUTDOWN_MILLIVOLTS=3300
|
||||
build_src_filter = ${nrf52_base.build_src_filter}
|
||||
+<helpers/*.cpp>
|
||||
+<TechoBoard.cpp>
|
||||
+<helpers/sensors/EnvironmentSensorManager.cpp>
|
||||
+<helpers/ui/MomentaryButton.cpp>
|
||||
+<../variants/lilygo_techo_lite>
|
||||
+<helpers/nrf52/SerialBLEInterface.cpp>
|
||||
+<../examples/companion_radio/*.cpp>
|
||||
lib_deps =
|
||||
${LilyGo_T-Echo-Lite.lib_deps}
|
||||
densaugeo/base64 @ ~1.4.0
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#define PIN_PWR_EN _PINNUM(0, 30) // RT9080_EN
|
||||
|
||||
#define BATTERY_PIN _PINNUM(0, 2)
|
||||
#define ADC_MULTIPLIER (4.90F)
|
||||
#define ADC_MULTIPLIER (2.0F)
|
||||
|
||||
#define ADC_RESOLUTION (14)
|
||||
#define BATTERY_SENSE_RES (12)
|
||||
@@ -47,13 +47,13 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// I2C pin definition
|
||||
|
||||
#define PIN_WIRE_SDA _PINNUM(0, 4) // (SDA)
|
||||
#define PIN_WIRE_SCL _PINNUM(0, 2) // (SCL)
|
||||
#define PIN_WIRE_SDA _PINNUM(1, 4) // (SDA) - per LilyGo IIC_1_SDA
|
||||
#define PIN_WIRE_SCL _PINNUM(1, 2) // (SCL) - per LilyGo IIC_1_SCL
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SPI pin definition
|
||||
|
||||
#define SPI_INTERFACES_COUNT _PINNUM(0, 2)
|
||||
#define SPI_INTERFACES_COUNT (2)
|
||||
|
||||
#define PIN_SPI_MISO _PINNUM(0, 17) // (MISO)
|
||||
#define PIN_SPI_MOSI _PINNUM(0, 15) // (MOSI)
|
||||
@@ -149,10 +149,11 @@ extern const int SCK;
|
||||
#define PIN_DISPLAY_BUSY DISP_BUSY
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// GPS
|
||||
// GPS — per LilyGo t_echo_lite_config.h
|
||||
// PIN_GPS_TX/RX named from GPS module's perspective
|
||||
|
||||
#define PIN_GPS_RX _PINNUM(1, 13) // RXD
|
||||
#define PIN_GPS_TX _PINNUM(1, 15) // TXD
|
||||
#define GPS_EN _PINNUM(1, 11) // POWER_RT9080_EN
|
||||
#define PIN_GPS_STANDBY _PINNUM(1, 10)
|
||||
#define PIN_GPS_PPS _PINNUM(0, 29) // 1PPS
|
||||
#define PIN_GPS_TX _PINNUM(0, 29) // GPS UART TX → MCU RX
|
||||
#define PIN_GPS_RX _PINNUM(1, 10) // GPS UART RX ← MCU TX
|
||||
#define GPS_EN _PINNUM(1, 11) // GPS RT9080 power enable
|
||||
#define PIN_GPS_STANDBY _PINNUM(1, 13) // GPS wake-up
|
||||
#define PIN_GPS_PPS _PINNUM(1, 15) // GPS 1PPS
|
||||
@@ -14,9 +14,12 @@ const PowerMgtConfig power_config = {
|
||||
|
||||
void R1NeoBoard::initiateShutdown(uint8_t reason) {
|
||||
// Disable LoRa module power before shutdown
|
||||
MESH_DEBUG_PRINTLN("R1Neo: shutting down");
|
||||
digitalWrite(SX126X_POWER_EN, LOW);
|
||||
|
||||
// Signal IO controller that MCU is off, then release DCDC latch
|
||||
digitalWrite(PIN_SOFT_SHUTDOWN, LOW);
|
||||
digitalWrite(PIN_DCDC_EN_MCU_HOLD, LOW);
|
||||
|
||||
if (reason == SHUTDOWN_REASON_LOW_VOLTAGE ||
|
||||
reason == SHUTDOWN_REASON_BOOT_PROTECT) {
|
||||
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);
|
||||
|
||||
@@ -18,6 +18,12 @@ public:
|
||||
R1NeoBoard() : NRF52Board("R1NEO_OTA") {}
|
||||
void begin();
|
||||
|
||||
#ifdef NRF52_POWER_MANAGEMENT
|
||||
void powerOff() override {
|
||||
initiateShutdown(SHUTDOWN_REASON_USER);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(P_LORA_TX_LED)
|
||||
void onBeforeTransmit() override {
|
||||
digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on
|
||||
|
||||
@@ -106,7 +106,7 @@ extern "C"
|
||||
|
||||
// Power management boot protection threshold (millivolts)
|
||||
// Set to 0 to disable boot protection
|
||||
#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage (mV)
|
||||
#define PWRMGT_VOLTAGE_BOOTLOCK 0 // Won't boot below this voltage (mV)
|
||||
// LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
|
||||
// AIN3 = P0.05 = PIN_A0 / PIN_VBAT_READ
|
||||
#define PWRMGT_LPCOMP_AIN 3
|
||||
@@ -147,7 +147,7 @@ extern "C"
|
||||
// LoRa radio module pins for RAK4631
|
||||
#define P_LORA_DIO_1 (47)
|
||||
#define P_LORA_NSS (42)
|
||||
#define P_LORA_RESET (-1)
|
||||
#define P_LORA_RESET (38)
|
||||
#define P_LORA_BUSY (46)
|
||||
#define P_LORA_SCLK (43)
|
||||
#define P_LORA_MISO (45)
|
||||
|
||||
@@ -77,7 +77,7 @@ static const uint8_t D10 = 10;
|
||||
|
||||
// Power management boot protection threshold (millivolts)
|
||||
// Set to 0 to disable boot protection
|
||||
#define PWRMGT_VOLTAGE_BOOTLOCK 3300 // Won't boot below this voltage
|
||||
#define PWRMGT_VOLTAGE_BOOTLOCK 0 // Won't boot below this voltage
|
||||
|
||||
// LPCOMP wake configuration (voltage recovery from SYSTEMOFF)
|
||||
#define PWRMGT_LPCOMP_AIN 7 // AIN7 = P0.31 = PIN_VBAT
|
||||
|
||||
Reference in New Issue
Block a user