Merge branch 'ripplebiz:dev' into dev

This commit is contained in:
Rastislav Vysoky
2025-06-13 09:59:06 +02:00
committed by GitHub
17 changed files with 151 additions and 26 deletions

View File

@@ -46,7 +46,8 @@
],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd"
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52.cfg"
},
"frameworks": [
"arduino"
@@ -69,4 +70,4 @@
},
"url": "https://wiki.uniteng.com/en/meshtastic/nano-g2-ultra",
"vendor": "BQ Consulting"
}
}

View File

@@ -5,7 +5,7 @@
#include "MyMesh.h"
#define AUTO_OFF_MILLIS 15000 // 15 seconds
#define BOOT_SCREEN_MILLIS 4000 // 4 seconds
#define BOOT_SCREEN_MILLIS 3000 // 3 seconds
#ifdef PIN_STATUS_LED
#define LED_ON_MILLIS 20
@@ -163,9 +163,9 @@ void UITask::renderCurrScreen() {
char tmp[80];
if (_alert[0]) {
_display->setTextSize(1.4);
uint16_t textWidth = _display->getTextWidth(_alert);
_display->setCursor((_display->width() - textWidth) / 2, 22);
_display->setTextSize(1.4);
_display->setColor(DisplayDriver::GREEN);
_display->print(_alert);
_alert[0] = 0;
@@ -191,7 +191,7 @@ void UITask::renderCurrScreen() {
sprintf(tmp, "%d", _msgcount);
_display->print(tmp);
_display->setColor(DisplayDriver::YELLOW); // last color will be kept on T114
} else if (millis() < BOOT_SCREEN_MILLIS) { // boot screen
} else if ((millis() - ui_started_at) < BOOT_SCREEN_MILLIS) { // boot screen
// meshcore logo
_display->setColor(DisplayDriver::BLUE);
int logoWidth = 128;
@@ -302,7 +302,7 @@ void UITask::loop() {
if (_display != NULL && _display->isOn()) {
static bool _firstBoot = true;
if(_firstBoot && millis() >= BOOT_SCREEN_MILLIS) {
if(_firstBoot && (millis() - ui_started_at) >= BOOT_SCREEN_MILLIS) {
_need_refresh = true;
_firstBoot = false;
}
@@ -344,6 +344,8 @@ void UITask::handleButtonShortPress() {
// Otherwise, refresh the display
_need_refresh = true;
}
} else {
_need_refresh = true; // display just turned on, so we need to refresh
}
// Note: Display turn-on and auto-off timer extension are handled by handleButtonAnyPress
}
@@ -372,10 +374,12 @@ void UITask::handleButtonTriplePress() {
if (buzzer.isQuiet()) {
buzzer.quiet(false);
soundBuzzer(UIEventType::ack);
sprintf(_alert, "Buzzer: ON");
} else {
soundBuzzer(UIEventType::ack);
buzzer.quiet(true);
sprintf(_alert, "Buzzer: OFF");
}
_need_refresh = true;
#endif
}

View File

@@ -337,6 +337,9 @@ protected:
int getInterferenceThreshold() const override {
return _prefs.interference_threshold;
}
int getAGCResetInterval() const override {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
}
void onAnonDataRecv(mesh::Packet* packet, uint8_t type, const mesh::Identity& sender, uint8_t* data, size_t len) override {
if (type == PAYLOAD_TYPE_ANON_REQ) { // received an initial request by a possible admin client (unknown at this stage)
@@ -575,7 +578,7 @@ public:
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_advert_interval = 3; // 3 hours
_prefs.flood_max = 64;
_prefs.interference_threshold = 14; // DB
_prefs.interference_threshold = 0; // disabled
}
CommonCLI* getCLI() { return &_cli; }

View File

@@ -420,6 +420,9 @@ protected:
int getInterferenceThreshold() const override {
return _prefs.interference_threshold;
}
int getAGCResetInterval() const override {
return ((int)_prefs.agc_reset_interval) * 4000; // milliseconds
}
bool allowPacketForward(const mesh::Packet* packet) override {
if (_prefs.disable_fwd) return false;
@@ -725,7 +728,7 @@ public:
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_advert_interval = 3; // 3 hours
_prefs.flood_max = 64;
_prefs.interference_threshold = 14; // DB
_prefs.interference_threshold = 0; // disabled
#ifdef ROOM_PASSWORD
StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
#endif

View File

@@ -87,6 +87,14 @@ void Dispatcher::loop() {
} else {
return; // can't do any more radio activity until send is complete or timed out
}
// going back into receive mode now...
next_agc_reset_time = futureMillis(getAGCResetInterval());
}
if (getAGCResetInterval() > 0 && millisHasNowPassed(next_agc_reset_time)) {
_radio->resetAGC();
next_agc_reset_time = futureMillis(getAGCResetInterval());
}
// check inbound (delayed) queue

View File

@@ -65,6 +65,8 @@ public:
virtual void triggerNoiseFloorCalibrate(int threshold) { }
virtual void resetAGC() { }
virtual bool isInRecvMode() const = 0;
/**
@@ -116,7 +118,7 @@ class Dispatcher {
unsigned long next_tx_time;
unsigned long cad_busy_start;
unsigned long radio_nonrx_start;
unsigned long next_floor_calib_time;
unsigned long next_floor_calib_time, next_agc_reset_time;
bool prev_isrecv_mode;
uint32_t n_sent_flood, n_sent_direct;
uint32_t n_recv_flood, n_recv_direct;
@@ -134,7 +136,7 @@ protected:
{
outbound = NULL; total_air_time = 0; next_tx_time = 0;
cad_busy_start = 0;
next_floor_calib_time = 0;
next_floor_calib_time = next_agc_reset_time = 0;
_err_flags = 0;
radio_nonrx_start = 0;
prev_isrecv_mode = true;
@@ -154,6 +156,7 @@ protected:
virtual uint32_t getCADFailRetryDelay() const;
virtual uint32_t getCADFailMaxDuration() const;
virtual int getInterferenceThreshold() const { return 0; } // disabled by default
virtual int getAGCResetInterval() const { return 0; } // disabled by default
public:
void begin();

View File

@@ -53,7 +53,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114
file.read((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115
file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116
file.read(pad, 4); // 120
file.read((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120
file.read(pad, 3); // 121
file.read((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124
file.read((uint8_t *) &_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125
file.read((uint8_t *) &_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126
@@ -107,7 +108,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *) &_prefs->allow_read_only, sizeof(_prefs->allow_read_only)); // 114
file.write((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115
file.write((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116
file.write(pad, 4); // 120
file.write((uint8_t *) &_prefs->agc_reset_interval, sizeof(_prefs->agc_reset_interval)); // 120
file.write(pad, 3); // 121
file.write((uint8_t *) &_prefs->flood_max, sizeof(_prefs->flood_max)); // 124
file.write((uint8_t *) &_prefs->flood_advert_interval, sizeof(_prefs->flood_advert_interval)); // 125
file.write((uint8_t *) &_prefs->interference_threshold, sizeof(_prefs->interference_threshold)); // 126
@@ -180,6 +182,8 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
sprintf(reply, "> %s", StrHelper::ftoa(_prefs->airtime_factor));
} else if (memcmp(config, "int.thresh", 10) == 0) {
sprintf(reply, "> %d", (uint32_t) _prefs->interference_threshold);
} else if (memcmp(config, "agc.reset.interval", 18) == 0) {
sprintf(reply, "> %d", ((uint32_t) _prefs->agc_reset_interval) * 4);
} else if (memcmp(config, "allow.read.only", 15) == 0) {
sprintf(reply, "> %s", _prefs->allow_read_only ? "on" : "off");
} else if (memcmp(config, "flood.advert.interval", 21) == 0) {
@@ -231,6 +235,10 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
_prefs->interference_threshold = atoi(&config[11]);
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "agc.reset.interval ", 19) == 0) {
_prefs->agc_reset_interval = atoi(&config[19]) / 4;
savePrefs();
strcpy(reply, "OK");
} else if (memcmp(config, "allow.read.only ", 16) == 0) {
_prefs->allow_read_only = memcmp(&config[16], "on", 2) == 0;
savePrefs();

View File

@@ -25,6 +25,7 @@ struct NodePrefs { // persisted to file
float bw;
uint8_t flood_max;
uint8_t interference_threshold;
uint8_t agc_reset_interval; // secs / 4
};
class CommonCLICallbacks {

View File

@@ -52,6 +52,15 @@ void RadioLibWrapper::triggerNoiseFloorCalibrate(int threshold) {
}
}
void RadioLibWrapper::resetAGC() {
// make sure we're not mid-receive of packet!
if ((state & STATE_INT_READY) != 0 || isReceivingPacket()) return;
// NOTE: according to higher powers, just issuing RadioLib's startReceive() will reset the AGC.
// revisit this if a better impl is discovered.
state = STATE_IDLE; // trigger a startReceive()
}
void RadioLibWrapper::loop() {
if (state == STATE_RX && _num_floor_samples < NUM_NOISE_FLOOR_SAMPLES) {
if (!isReceivingPacket()) {

View File

@@ -39,6 +39,7 @@ public:
int getNoiseFloor() const override { return _noise_floor; }
void triggerNoiseFloorCalibrate(int threshold) override;
void resetAGC() override;
void loop() override;

View File

@@ -26,6 +26,11 @@ void ThinkNodeM1Board::begin() {
Wire.begin();
#ifdef P_LORA_TX_LED
pinMode(P_LORA_TX_LED, OUTPUT);
digitalWrite(P_LORA_TX_LED, LOW);
#endif
pinMode(SX126X_POWER_EN, OUTPUT);
digitalWrite(SX126X_POWER_EN, HIGH);
delay(10); // give sx1262 some time to power up

View File

@@ -39,6 +39,15 @@ public:
return startup_reason;
}
#if defined(P_LORA_TX_LED)
void onBeforeTransmit() override {
digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on
}
void onAfterTransmit() override {
digitalWrite(P_LORA_TX_LED, LOW); // turn TX LED off
}
#endif
const char* getManufacturerName() const override {
return "Elecrow ThinkNode-M1";
}

View File

@@ -0,0 +1,33 @@
#pragma once
#include <MeshCore.h>
#include <Arduino.h>
#if defined(ESP_PLATFORM)
#include <helpers/ESP32Board.h>
class Heltec_CT62_Board : public ESP32Board {
public:
uint16_t getBattMilliVolts() override {
#ifdef PIN_VBAT_READ
analogReadResolution(12); // ESP32-C3 ADC is 12-bit - 3.3/4096 (ref voltage/max counts)
uint32_t raw = 0;
for (int i = 0; i < 8; i++) {
raw += analogRead(PIN_VBAT_READ);
}
raw = raw / 8;
return ((6.52 * raw) / 1024.0) * 1000;
#else
return 0; // not supported
#endif
}
const char* getManufacturerName() const override {
return "Heltec CT62";
}
};
#endif

View File

@@ -12,12 +12,13 @@ build_flags =
-D P_LORA_TX_LED=18
-D PIN_BOARD_SDA=0
-D PIN_BOARD_SCL=1
-D PIN_USER_BTN=9
;-D PIN_USER_BTN=9
-D PIN_VBAT_READ=2
-D P_LORA_DIO_1=3
-D P_LORA_NSS=8
-D P_LORA_RESET=5
-D P_LORA_RESET=RADIOLIB_NC
-D P_LORA_DIO_0=RADIOLIB_NC
-D P_LORA_DIO_2=RADIOLIB_NC
-D P_LORA_BUSY=4
-D P_LORA_SCLK=10
-D P_LORA_MISO=6
@@ -33,6 +34,8 @@ build_src_filter = ${esp32_base.build_src_filter}
extends = Heltec_ct62
build_flags =
${Heltec_ct62.build_flags}
;-D ARDUINO_USB_MODE=1
;-D ARDUINO_USB_CDC_ON_BOOT=1
-D ADVERT_NAME='"HT-CT62 Repeater"'
-D ADVERT_LAT=0.0
-D ADVERT_LON=0.0
@@ -45,3 +48,41 @@ build_src_filter = ${Heltec_ct62.build_src_filter}
lib_deps =
${Heltec_ct62.lib_deps}
${esp32_ota.lib_deps}
[env:Heltec_ct62_companion_radio_usb]
extends = Heltec_ct62
build_flags =
${Heltec_ct62.build_flags}
; -D ARDUINO_USB_MODE=1
; -D ARDUINO_USB_CDC_ON_BOOT=1
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D OFFLINE_QUEUE_SIZE=256
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_ct62.build_src_filter}
+<../examples/companion_radio>
lib_deps =
${Heltec_ct62.lib_deps}
${esp32_ota.lib_deps}
densaugeo/base64 @ ~1.4.0
[env:Heltec_ct62_companion_radio_ble]
extends = Heltec_ct62
build_flags =
${Heltec_ct62.build_flags}
; -D ARDUINO_USB_MODE=1
; -D ARDUINO_USB_CDC_ON_BOOT=1
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=8
-D OFFLINE_QUEUE_SIZE=256
-D BLE_PIN_CODE=123456
; -D MESH_PACKET_LOGGING=1
; -D MESH_DEBUG=1
build_src_filter = ${Heltec_ct62.build_src_filter}
+<../examples/companion_radio>
+<helpers/esp32/SerialBLEInterface.cpp>
lib_deps =
${Heltec_ct62.lib_deps}
${esp32_ota.lib_deps}
densaugeo/base64 @ ~1.4.0

View File

@@ -1,14 +1,9 @@
#include <Arduino.h>
#include "target.h"
ESP32Board board;
#if defined(P_LORA_SCLK)
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
#else
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
#endif
Heltec_CT62_Board board;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
WRAPPER_CLASS radio_driver(radio, board);
ESP32RTCClock fallback_clock;
@@ -72,4 +67,4 @@ void radio_set_tx_power(uint8_t dbm) {
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}
}

View File

@@ -3,12 +3,12 @@
#define RADIOLIB_STATIC_ONLY 1
#include <RadioLib.h>
#include <helpers/RadioLibWrappers.h>
#include <helpers/ESP32Board.h>
#include "HT-CT62Board.h"
#include <helpers/CustomSX1262Wrapper.h>
#include <helpers/AutoDiscoverRTCClock.h>
#include <helpers/SensorManager.h>
extern ESP32Board board;
extern Heltec_CT62_Board board;
extern WRAPPER_CLASS radio_driver;
extern AutoDiscoverRTCClock rtc_clock;
extern SensorManager sensors;
@@ -17,4 +17,4 @@ bool radio_init();
uint32_t radio_get_rng_seed();
void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);
void radio_set_tx_power(uint8_t dbm);
mesh::LocalIdentity radio_new_identity();
mesh::LocalIdentity radio_new_identity();

View File

@@ -19,6 +19,7 @@ build_flags = ${nrf52840_thinknode_m1.build_flags}
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D P_LORA_TX_LED=13
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
build_src_filter = ${nrf52840_thinknode_m1.build_src_filter}