Compare commits

...

8 Commits

Author SHA1 Message Date
Erayd
d9bb74ddbb
Merge 7d3af3aa8e into bd3cbfc1ad 2025-09-01 14:30:59 -04:00
Jonathan Bennett
bd3cbfc1ad Add support for the RV-3028 on native Linux (#7802)
Some checks failed
CI / docker-deb-amd64-tft (push) Waiting to run
CI / docker-alp-amd64 (push) Waiting to run
CI / docker-alp-amd64-tft (push) Waiting to run
CI / docker-deb-arm64 (push) Waiting to run
CI / docker-deb-armv7 (push) Waiting to run
CI / gather-artifacts (esp32) (push) Blocked by required conditions
CI / gather-artifacts (esp32c3) (push) Blocked by required conditions
CI / gather-artifacts (esp32c6) (push) Blocked by required conditions
CI / gather-artifacts (esp32s3) (push) Blocked by required conditions
CI / gather-artifacts (nrf52840) (push) Blocked by required conditions
CI / gather-artifacts (rp2040) (push) Blocked by required conditions
CI / gather-artifacts (rp2350) (push) Blocked by required conditions
CI / gather-artifacts (stm32) (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
CI / release-firmware (esp32) (push) Blocked by required conditions
CI / release-firmware (esp32c3) (push) Blocked by required conditions
CI / release-firmware (esp32c6) (push) Blocked by required conditions
CI / release-firmware (esp32s3) (push) Blocked by required conditions
CI / release-firmware (nrf52840) (push) Blocked by required conditions
CI / release-firmware (rp2040) (push) Blocked by required conditions
CI / release-firmware (rp2350) (push) Blocked by required conditions
CI / release-firmware (stm32) (push) Blocked by required conditions
CI / publish-firmware (push) Blocked by required conditions
Daily Packaging / docker-multiarch (push) Has been cancelled
Daily Packaging / package-ppa (jammy) (push) Has been cancelled
Daily Packaging / package-ppa (noble) (push) Has been cancelled
Daily Packaging / package-ppa (plucky) (push) Has been cancelled
Daily Packaging / package-ppa (questing) (push) Has been cancelled
Daily Packaging / package-obs (push) Has been cancelled
Daily Packaging / hook-copr (push) Has been cancelled
2025-09-01 08:04:04 -05:00
github-actions[bot]
fddc4e00ca Automated version bumps (#7790)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2025-09-01 08:03:03 -05:00
github-actions[bot]
5f7eec5504
Upgrade trunk (#7804)
Co-authored-by: vidplace7 <1779290+vidplace7@users.noreply.github.com>
2025-09-01 07:58:01 -05:00
Manuel
6b94c297b9
fix: T-LoRa Pager / T-Deck Pro shutdown (#7792)
* power down during LS and shutdown

* fix T-Deck Pro shutdown

* use device specific define

* slightly rephrase the power off display message
2025-09-01 07:57:49 -05:00
Onyx Clawe
edeb25cab5
Update variant.h (#7520)
Updated ADC, Full charge now results in 100% charge being reported instead of 95% charge

Co-authored-by: OnyxtheDragon <58921814+OnyxtheDragon@users.noreply.github.com>
2025-09-01 07:57:15 -05:00
Tom Fifield
44688e8363
Fix device-install.bat baud rate (#7486)
Some checks are pending
CI / build-rp2040 (push) Blocked by required conditions
CI / build-rp2350 (push) Blocked by required conditions
CI / build-stm32 (push) Blocked by required conditions
CI / build-debian-src (push) Waiting to run
CI / package-pio-deps-native-tft (push) Waiting to run
CI / test-native (push) Waiting to run
CI / docker-deb-amd64 (push) Waiting to run
CI / docker-deb-amd64-tft (push) Waiting to run
CI / docker-alp-amd64 (push) Waiting to run
CI / docker-alp-amd64-tft (push) Waiting to run
CI / docker-deb-arm64 (push) Waiting to run
CI / docker-deb-armv7 (push) Waiting to run
CI / gather-artifacts (esp32) (push) Blocked by required conditions
CI / gather-artifacts (esp32c3) (push) Blocked by required conditions
CI / gather-artifacts (esp32c6) (push) Blocked by required conditions
CI / gather-artifacts (esp32s3) (push) Blocked by required conditions
CI / gather-artifacts (nrf52840) (push) Blocked by required conditions
CI / gather-artifacts (rp2040) (push) Blocked by required conditions
CI / gather-artifacts (rp2350) (push) Blocked by required conditions
CI / gather-artifacts (stm32) (push) Blocked by required conditions
CI / release-artifacts (push) Blocked by required conditions
CI / release-firmware (esp32) (push) Blocked by required conditions
CI / release-firmware (esp32c3) (push) Blocked by required conditions
CI / release-firmware (esp32c6) (push) Blocked by required conditions
CI / release-firmware (esp32s3) (push) Blocked by required conditions
CI / release-firmware (nrf52840) (push) Blocked by required conditions
CI / release-firmware (rp2040) (push) Blocked by required conditions
CI / release-firmware (rp2350) (push) Blocked by required conditions
CI / release-firmware (stm32) (push) Blocked by required conditions
CI / publish-firmware (push) Blocked by required conditions
As reported by @gruberaaron , work to improve the 1200bps reset for
esptool caused all runs of device-install.bat to use 1200bps as
the baud rate.

This change removes the general SET "ESPTOOL_BAUD=1200" that was causing
the issues and places the baud settings for reset mode inside the conditional.

Fixes https://github.com/meshtastic/firmware/issues/7172
2025-09-01 14:16:24 +10:00
Steve Gilberd
7d3af3aa8e
Tips robot virtual node / relayer to different LoRa modes & channels
Note that this commit has details hardcoded for the Wellington (NZ)
mesh, and also requires the following patch to the protobufs:

-----
diff --git a/meshtastic/mesh.proto b/meshtastic/mesh.proto
index 03162d8..ec54c99 100644
--- a/meshtastic/mesh.proto
+++ b/meshtastic/mesh.proto
@@ -1393,6 +1393,21 @@ message MeshPacket {
    * Set by the firmware internally, clients are not supposed to set this.
    */
   uint32 tx_after = 20;
+
+  /*
+   * The modem preset to use fo rthis packet
+   */
+  uint32 modem_preset = 21;
+
+  /*
+   * The frequency slot to use for this packet
+   */
+  uint32 frequency_slot = 22;
+
+  /*
+   * Whether the packet has a nonstandard radio config
+   */
+  bool nonstandard_radio_config = 23;
 }

 /*
-----
2025-07-01 11:42:27 +12:00
18 changed files with 439 additions and 48 deletions

View File

@ -9,14 +9,14 @@ plugins:
lint:
enabled:
- checkov@3.2.467
- renovate@41.88.0
- renovate@41.90.1
- prettier@3.6.2
- trufflehog@3.90.5
- yamllint@1.37.1
- bandit@1.8.6
- trivy@0.65.0
- taplo@0.10.0
- ruff@0.12.10
- ruff@0.12.11
- isort@6.0.1
- markdownlint@0.45.0
- oxipng@9.1.5

View File

@ -100,7 +100,6 @@ IF NOT "!FILENAME:update=!"=="!FILENAME!" (
)
:skip-filename
SET "ESPTOOL_BAUD=1200"
CALL :LOG_MESSAGE DEBUG "Determine the correct esptool command to use..."
IF NOT "__%PYTHON%__"=="____" (
@ -142,7 +141,7 @@ CALL :LOG_MESSAGE INFO "Using esptool baud: !ESPTOOL_BAUD!."
IF %BPS_RESET% EQU 1 (
@REM Attempt to change mode via 1200bps Reset.
CALL :RUN_ESPTOOL !ESPTOOL_BAUD! --after no_reset read_flash_status
CALL :RUN_ESPTOOL 1200 --after no_reset read_flash_status
GOTO eof
)

View File

@ -87,6 +87,9 @@
</screenshots>
<releases>
<release version="2.7.8" date="2025-08-30">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.8</url>
</release>
<release version="2.7.7" date="2025-08-28">
<url type="details">https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.7</url>
</release>

7
debian/changelog vendored
View File

@ -1,4 +1,4 @@
meshtasticd (2.7.7.0) UNRELEASED; urgency=medium
meshtasticd (2.7.8.0) UNRELEASED; urgency=medium
[ Austin Lane ]
* Initial packaging
@ -41,4 +41,7 @@ meshtasticd (2.7.7.0) UNRELEASED; urgency=medium
* GitHub Actions Automatic version bump
* GitHub Actions Automatic version bump
-- Ubuntu <github-actions[bot]@users.noreply.github.com> Thu, 28 Aug 2025 10:33:25 +0000
[ ]
* GitHub Actions Automatic version bump
-- <github-actions[bot]@users.noreply.github.com> Sat, 30 Aug 2025 00:26:04 +0000

View File

@ -128,6 +128,7 @@ RAK9154Sensor rak9154Sensor;
#ifdef HAS_PPM
// note: XPOWERS_CHIP_XXX must be defined in variant.h
#include <XPowersLib.h>
XPowersPPM *PPM = NULL;
#endif
#ifdef HAS_BQ27220
@ -745,7 +746,11 @@ void Power::shutdown()
#if HAS_SCREEN
if (screen) {
#ifdef T_DECK_PRO
screen->showSimpleBanner("Device is powered off.\nConnect USB to start!", 0); // T-Deck Pro has no power button
#else
screen->showSimpleBanner("Shutting Down...", 0); // stays on screen
#endif
}
#endif
#if !defined(ARCH_STM32WL)
@ -763,7 +768,7 @@ void Power::shutdown()
#ifdef PIN_LED3
ledOff(PIN_LED3);
#endif
doDeepSleep(DELAY_FOREVER, false, true);
doDeepSleep(DELAY_FOREVER, true, true);
#elif defined(ARCH_PORTDUINO)
exit(EXIT_SUCCESS);
#else
@ -1320,7 +1325,6 @@ bool Power::lipoInit()
class LipoCharger : public HasBatteryLevel
{
private:
XPowersPPM *ppm = nullptr;
BQ27220 *bq = nullptr;
public:
@ -1329,41 +1333,41 @@ class LipoCharger : public HasBatteryLevel
*/
bool runOnce()
{
if (ppm == nullptr) {
ppm = new XPowersPPM;
bool result = ppm->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR);
if (PPM == nullptr) {
PPM = new XPowersPPM;
bool result = PPM->init(Wire, I2C_SDA, I2C_SCL, BQ25896_ADDR);
if (result) {
LOG_INFO("PPM BQ25896 init succeeded");
// Set the minimum operating voltage. Below this voltage, the PPM will protect
// ppm->setSysPowerDownVoltage(3100);
// PPM->setSysPowerDownVoltage(3100);
// Set input current limit, default is 500mA
// ppm->setInputCurrentLimit(800);
// PPM->setInputCurrentLimit(800);
// Disable current limit pin
// ppm->disableCurrentLimitPin();
// PPM->disableCurrentLimitPin();
// Set the charging target voltage, Range:3840 ~ 4608mV ,step:16 mV
ppm->setChargeTargetVoltage(4288);
PPM->setChargeTargetVoltage(4288);
// Set the precharge current , Range: 64mA ~ 1024mA ,step:64mA
// ppm->setPrechargeCurr(64);
// PPM->setPrechargeCurr(64);
// The premise is that limit pin is disabled, or it will
// only follow the maximum charging current set by limit pin.
// Set the charging current , Range:0~5056mA ,step:64mA
ppm->setChargerConstantCurr(1024);
PPM->setChargerConstantCurr(1024);
// To obtain voltage data, the ADC must be enabled first
ppm->enableMeasure();
PPM->enableMeasure();
// Turn on charging function
// If there is no battery connected, do not turn on the charging function
ppm->enableCharge();
PPM->enableCharge();
} else {
LOG_WARN("PPM BQ25896 init failed");
delete ppm;
ppm = nullptr;
delete PPM;
PPM = nullptr;
return false;
}
}
@ -1404,23 +1408,23 @@ class LipoCharger : public HasBatteryLevel
/**
* return true if there is a battery installed in this unit
*/
virtual bool isBatteryConnect() override { return ppm->getBattVoltage() > 0; }
virtual bool isBatteryConnect() override { return PPM->getBattVoltage() > 0; }
/**
* return true if there is an external power source detected
*/
virtual bool isVbusIn() override { return ppm->getVbusVoltage() > 0; }
virtual bool isVbusIn() override { return PPM->getVbusVoltage() > 0; }
/**
* return true if the battery is currently charging
*/
virtual bool isCharging() override
{
bool isCharging = ppm->isCharging();
bool isCharging = PPM->isCharging();
if (isCharging) {
LOG_DEBUG("BQ27220 time to full charge: %d min", bq->getTimeToFull());
} else {
if (!ppm->isVbusIn()) {
if (!PPM->isVbusIn()) {
LOG_DEBUG("BQ27220 time to empty: %d min (%d mAh)", bq->getTimeToEmpty(), bq->getRemainingCapacity());
}
}
@ -1453,8 +1457,6 @@ bool Power::lipoChargerInit()
}
#endif
#ifdef HELTEC_MESH_SOLAR
#include "meshSolarApp.h"

View File

@ -26,10 +26,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <Arduino.h>
#ifdef RV3028_RTC
#if __has_include("Melopero_RV3028.h")
#include "Melopero_RV3028.h"
#endif
#ifdef PCF8563_RTC
#if __has_include("pcf8563.h")
#include "pcf8563.h"
#endif

View File

@ -55,9 +55,9 @@ RTCSetResult readFromRTC()
LOG_DEBUG("Read RTC time from RV3028 getTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
if (currentQuality == RTCQualityNone) {
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
if (currentQuality == RTCQualityNone) {
currentQuality = RTCQualityDevice;
}
return RTCSetResultSuccess;
@ -94,9 +94,9 @@ RTCSetResult readFromRTC()
LOG_DEBUG("Read RTC time from PCF8563 getDateTime as %02d-%02d-%02d %02d:%02d:%02d (%ld)", t.tm_year + 1900, t.tm_mon + 1,
t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, printableEpoch);
if (currentQuality == RTCQualityNone) {
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
if (currentQuality == RTCQualityNone) {
currentQuality = RTCQualityDevice;
}
return RTCSetResultSuccess;

View File

@ -419,7 +419,7 @@ void setup()
struct timeval tv;
tv.tv_sec = time(NULL);
tv.tv_usec = 0;
perhapsSetRTC(RTCQualityNTP, &tv);
perhapsSetRTC(RTCQualityDevice, &tv);
#endif
powerMonInit();

View File

@ -94,6 +94,13 @@ class Channels
bool ensureLicensedOperation();
/**
* Validate a channel, fixing any errors as needed
*/
meshtastic_Channel &fixupChannel(ChannelIndex chIndex);
int16_t getHash(ChannelIndex i) { return hashes[i]; }
private:
/** Given a channel index, change to use the crypto key specified by that index
*
@ -111,13 +118,6 @@ class Channels
*/
int16_t generateHash(ChannelIndex channelNum);
int16_t getHash(ChannelIndex i) { return hashes[i]; }
/**
* Validate a channel, fixing any errors as needed
*/
meshtastic_Channel &fixupChannel(ChannelIndex chIndex);
/**
* Writes the default lora config
*/

View File

@ -8,6 +8,9 @@
#include "error.h"
#include "main.h"
#include "mesh-pb-constants.h"
#if !MESHTASTIC_EXCLUDE_TIPS
#include "modules/MeshTipsModule.h"
#endif
#include <pb_decode.h>
#include <pb_encode.h>
@ -248,6 +251,9 @@ void RadioLibInterface::onNotify(uint32_t notification)
switch (notification) {
case ISR_TX:
handleTransmitInterrupt();
#if !MESHTASTIC_EXCLUDE_TIPS
MeshTipsModule::configureRadioForPacket(this, txQueue.getFront());
#endif
startReceive();
setTransmitDelay();
break;
@ -270,9 +276,16 @@ void RadioLibInterface::onNotify(uint32_t notification)
if (delay_remaining > 0) {
// There's still some delay pending on this packet, so resume waiting for it to elapse
notifyLater(delay_remaining, TRANSMIT_DELAY_COMPLETED, false);
#if !MESHTASTIC_EXCLUDE_TIPS
} else if (MeshTipsModule::configureRadioForPacket(this, txp)) {
// We just switched radio config, so wait to ensure the new channel is available
setTransmitDelay();
#endif
} else {
if (isChannelActive()) { // check if there is currently a LoRa packet on the channel
if (!txp->nonstandard_radio_config) {
startReceive(); // try receiving this packet, afterwards we'll be trying to transmit again
}
setTransmitDelay();
} else {
// Send any outgoing packets we have ready as fast as possible to keep the time between channel scan and

View File

@ -0,0 +1,242 @@
#include "MeshTipsModule.h"
#include "Default.h"
#include "MeshService.h"
#include "RTC.h"
#include "RadioInterface.h"
#include "configuration.h"
#include "main.h"
#include <Throttle.h>
#define NODENUM_TIPS 0x00000004
static meshtastic_Config_LoRaConfig_ModemPreset originalModemPreset; // original modem preset
static uint16_t originalLoraChannel; // original frequency slot
char originalChannelName[sizeof(MeshTipsModule_TXSettings)]; // original channel name
MeshTipsModule::MeshTipsModule()
{
originalModemPreset = config.lora.modem_preset;
originalLoraChannel = config.lora.channel_num;
strncpy(originalChannelName, channels.getPrimary().name, sizeof(originalChannelName));
}
bool MeshTipsModule::configureRadioForPacket(RadioInterface *iface, meshtastic_MeshPacket *p)
{
meshtastic_ChannelSettings *c = (meshtastic_ChannelSettings *)&channels.getPrimary();
if (p && p->from == NODENUM_TIPS && p->nonstandard_radio_config &&
(p->modem_preset != config.lora.modem_preset || p->frequency_slot != config.lora.channel_num)) {
LOG_INFO("Reconfiguring for TX of packet %#08lx (from=%#08lx size=%lu)", p->id, p->from, p->decoded.payload.size);
config.lora.modem_preset = (meshtastic_Config_LoRaConfig_ModemPreset)p->modem_preset;
config.lora.channel_num = p->frequency_slot;
memset(c->name, 0, sizeof(c->name));
switch (p->modem_preset) {
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
strncpy(c->name, "ShortTurbo", sizeof(c->name));
break;
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
strncpy(c->name, "ShortFast", sizeof(c->name));
break;
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
strncpy(c->name, "ShortSlow", sizeof(c->name));
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
strncpy(c->name, "MediumFast", sizeof(c->name));
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
strncpy(c->name, "MediumSlow", sizeof(c->name));
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
strncpy(c->name, "LongFast", sizeof(c->name));
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
strncpy(c->name, "LongMod", sizeof(c->name));
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
strncpy(c->name, "LongSlow", sizeof(c->name));
break;
}
channels.fixupChannel(channels.getPrimaryIndex());
p->channel = channels.getHash(channels.getPrimaryIndex());
iface->reconfigure();
return true;
} else if ((!p || !p->nonstandard_radio_config) &&
(config.lora.modem_preset != originalModemPreset || config.lora.channel_num != originalLoraChannel)) {
LOG_INFO("Reconfiguring for TX of packet %#08lx (from=%#08lx size=%lu)", p->id, p->from, p->decoded.payload.size);
config.lora.modem_preset = originalModemPreset;
config.lora.channel_num = originalLoraChannel;
memset(c->name, 0, sizeof(c->name));
strncpy(c->name, originalChannelName, sizeof(c->name));
channels.fixupChannel(channels.getPrimaryIndex());
iface->reconfigure();
return true;
}
return false;
}
MeshTipsModule_TXSettings MeshTipsModule::stripTargetRadioSettings(meshtastic_MeshPacket *p)
{
MeshTipsModule_TXSettings s = {
.preset = originalModemPreset,
.slot = originalLoraChannel,
};
// clamp final byte of payload, just in case, because I don't know if this is taken care of elsewhere
p->decoded.payload.bytes[p->decoded.payload.size] = 0;
if (!p || strlen((char *)p->decoded.payload.bytes) < 4 || p->decoded.payload.bytes[0] != '#')
return s;
char *msg = strchr((char *)p->decoded.payload.bytes, ' ');
if (!*msg)
return s;
*msg++ = 0;
const char presetString[3] = {p->decoded.payload.bytes[1], p->decoded.payload.bytes[2], 0};
char *slotString = (char *)&p->decoded.payload.bytes[3];
if (!*slotString)
return s;
for (char *c = slotString; *c; c++) {
if (!strchr("1234567890", *c))
return s;
}
uint8_t preset = 0;
if (!strncmp(presetString, "ST", 2))
s.preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO;
else if (!strncmp(presetString, "SF", 2))
s.preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST;
else if (!strncmp(presetString, "SS", 2))
s.preset = meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW;
else if (!strncmp(presetString, "MF", 2))
s.preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST;
else if (!strncmp(presetString, "MS", 2))
s.preset = meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW;
else if (!strncmp(presetString, "LF", 2))
s.preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
else if (!strncmp(presetString, "LM", 2))
s.preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE;
else if (!strncmp(presetString, "LS", 2))
s.preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW;
else
return s;
s.slot = std::stoi(slotString);
p->decoded.payload.size = 1;
// don't use strcpy, because strcpy has undefined behaviour if src & dst overlap
for (char *a = msg, *b = (char *)p->decoded.payload.bytes; (*b++ = *a++);)
p->decoded.payload.size++;
return s;
}
MeshTipsNodeInfoModule *meshTipsNodeInfoModule;
bool MeshTipsNodeInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_User *pptr)
{
// do nothing if we receive nodeinfo, because we only care about sending our own
return true;
}
void MeshTipsNodeInfoModule::sendTipsNodeInfo()
{
LOG_INFO("Send NodeInfo for mesh tips");
static meshtastic_User u = {
.hw_model = meshtastic_HardwareModel_PRIVATE_HW,
.is_licensed = false,
.role = meshtastic_Config_DeviceConfig_Role_CLIENT_MUTE,
.public_key =
{
.size = 32,
.bytes = {0x39, 0x37, 0x58, 0xe4, 0x05, 0x34, 0x7d, 0xe0, 0x49, 0x73, 0xec, 0xaf, 0xbc, 0x8e, 0x07, 0xe8,
0x66, 0x57, 0xe4, 0xa1, 0x2d, 0x53, 0x0e, 0x26, 0x51, 0x1f, 0x1a, 0x6c, 0xbf, 0xe8, 0x5e, 0x04},
},
.has_is_unmessagable = true,
.is_unmessagable = true,
};
strncpy(u.id, "!mesh_tips", sizeof(u.id));
strncpy(u.long_name, "WLG Mesh Tips Robot", sizeof(u.long_name));
strncpy(u.short_name, "TIPS", sizeof(u.short_name));
meshtastic_MeshPacket *p = allocDataProtobuf(u);
p->to = NODENUM_BROADCAST;
p->from = NODENUM_TIPS;
p->hop_limit = 0;
p->decoded.want_response = false;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
p->modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
meshtastic_MeshPacket *p_LF20 = packetPool.allocCopy(*p);
service->sendToMesh(p, RX_SRC_LOCAL, false);
p_LF20->frequency_slot = 20;
p_LF20->nonstandard_radio_config = true;
service->sendToMesh(p_LF20, RX_SRC_LOCAL, false);
}
MeshTipsNodeInfoModule::MeshTipsNodeInfoModule()
: ProtobufModule("nodeinfo_tips", meshtastic_PortNum_NODEINFO_APP, &meshtastic_User_msg),
concurrency::OSThread("MeshTipsNodeInfo")
{
MeshTipsModule();
setIntervalFromNow(setStartDelay()); // Send our initial owner announcement 30 seconds
// after we start (to give network time to setup)
}
int32_t MeshTipsNodeInfoModule::runOnce()
{
if (airTime->isTxAllowedAirUtil() && config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) {
sendTipsNodeInfo();
}
return Default::getConfiguredOrDefaultMs(config.device.node_info_broadcast_secs, default_node_info_broadcast_secs);
}
MeshTipsMessageModule *meshTipsMessageModule;
ProcessMessage MeshTipsMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
{
#ifdef DEBUG_PORT
auto &d = mp.decoded;
#endif
meshtastic_MeshPacket *p = packetPool.allocCopy(mp);
MeshTipsModule_TXSettings s = stripTargetRadioSettings(p);
if (!p->decoded.payload.size || p->decoded.payload.bytes[0] == '#')
return ProcessMessage::STOP;
p->to = NODENUM_BROADCAST;
p->decoded.source = p->from;
p->from = NODENUM_TIPS;
p->channel = channels.getPrimaryIndex();
p->hop_limit = 0;
p->hop_start = 0;
p->rx_rssi = 0;
p->rx_snr = 0;
p->priority = meshtastic_MeshPacket_Priority_HIGH;
p->want_ack = false;
p->modem_preset = s.preset;
p->frequency_slot = s.slot;
if (s.preset != originalModemPreset || s.slot != originalLoraChannel) {
p->nonstandard_radio_config = true;
}
p->rx_time = getValidTime(RTCQualityFromNet);
service->sendToMesh(p, RX_SRC_LOCAL, false);
powerFSM.trigger(EVENT_RECEIVED_MSG);
notifyObservers(&mp);
return ProcessMessage::CONTINUE; // No other module should be caring about this message
}
bool MeshTipsMessageModule::wantPacket(const meshtastic_MeshPacket *p)
{
meshtastic_Channel *c = &channels.getByIndex(p->channel);
return c->role == meshtastic_Channel_Role_SECONDARY && strlen(c->settings.name) == strlen("Tips") &&
!strcmp(c->settings.name, "Tips") && p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP;
}

View File

@ -0,0 +1,80 @@
#pragma once
#include "Observer.h"
#include "ProtobufModule.h"
#include "RadioInterface.h"
#include "SinglePortModule.h"
typedef struct {
meshtastic_Config_LoRaConfig_ModemPreset preset;
uint16_t slot;
} MeshTipsModule_TXSettings;
/**
* Base class for the tips robot
*/
class MeshTipsModule
{
public:
/**
* Constructor
*/
MeshTipsModule();
/**
* Configure the radio to send the target packet, or return to default config if p is NULL
*/
static bool configureRadioForPacket(RadioInterface *iface, meshtastic_MeshPacket *p);
/**
* Get target modem settings
*/
MeshTipsModule_TXSettings stripTargetRadioSettings(meshtastic_MeshPacket *p);
};
/**
* Tips module for sending tips into the mesh
*/
class MeshTipsNodeInfoModule : private MeshTipsModule, public ProtobufModule<meshtastic_User>, private concurrency::OSThread
{
public:
/**
* Constructor
*/
MeshTipsNodeInfoModule();
protected:
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_User *p) override;
/**
* Send NodeInfo to the mesh
*/
void sendTipsNodeInfo();
/** Does our periodic broadcast */
virtual int32_t runOnce() override;
};
extern MeshTipsNodeInfoModule *meshTipsNodeInfoModule;
/**
* Text message handling for the tips robot
*/
class MeshTipsMessageModule : private MeshTipsModule, public SinglePortModule, public Observable<const meshtastic_MeshPacket *>
{
public:
/**
* Constructor
*/
MeshTipsMessageModule() : MeshTipsModule(), SinglePortModule("tips", meshtastic_PortNum_TEXT_MESSAGE_APP) {}
protected:
/**
* Called to handle a particular incoming message
*/
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
/**
* Indicate whether this module wants to process the packet
*/
virtual bool wantPacket(const meshtastic_MeshPacket *p) override;
};
extern MeshTipsMessageModule *meshTipsMessageModule;

View File

@ -35,6 +35,9 @@
#if !MESHTASTIC_EXCLUDE_NODEINFO
#include "modules/NodeInfoModule.h"
#endif
#if !MESHTASTIC_EXCLUDE_TIPS
#include "modules/MeshTipsModule.h"
#endif
#if !MESHTASTIC_EXCLUDE_GPS
#include "modules/PositionModule.h"
#endif
@ -123,6 +126,10 @@ void setupModules()
#if !MESHTASTIC_EXCLUDE_NODEINFO
nodeInfoModule = new NodeInfoModule();
#endif
#if !MESHTASTIC_EXCLUDE_TIPS
meshTipsNodeInfoModule = new MeshTipsNodeInfoModule();
meshTipsMessageModule = new MeshTipsMessageModule();
#endif
#if !MESHTASTIC_EXCLUDE_GPS
positionModule = new PositionModule();
#endif

View File

@ -32,6 +32,16 @@ esp_sleep_source_t wakeCause; // the reason we booted this time
#endif
#include "Throttle.h"
#ifdef USE_XL9555
#include "ExtensionIOXL9555.hpp"
extern ExtensionIOXL9555 io;
#endif
#ifdef HAS_PPM
#include <XPowersLib.h>
extern XPowersPPM *PPM;
#endif
#ifndef INCLUDE_vTaskSuspend
#define INCLUDE_vTaskSuspend 0
#endif
@ -297,6 +307,14 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false, bool skipSaveN
#endif
#endif
#ifdef HAS_PPM
if (PPM) {
LOG_INFO("PMM shutdown");
console->flush();
PPM->shutdown();
}
#endif
#ifdef HAS_PMU
if (pmu_found && PMU) {
// Obsolete comment: from back when we we used to receive lora packets while CPU was in deep sleep.
@ -412,6 +430,16 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
if (pmu_found)
gpio_wakeup_enable((gpio_num_t)PMU_IRQ, GPIO_INTR_LOW_LEVEL); // pmu irq
#endif
#ifdef T_LORA_PAGER
LOG_DEBUG("power down XL9555 io");
io.digitalWrite(EXPANDS_DRV_EN, LOW);
io.digitalWrite(EXPANDS_AMP_EN, LOW);
io.digitalWrite(EXPANDS_KB_EN, LOW);
io.digitalWrite(EXPANDS_SD_EN, LOW);
io.digitalWrite(EXPANDS_GPIO_EN, LOW);
#endif
auto res = esp_sleep_enable_gpio_wakeup();
if (res != ESP_OK) {
LOG_ERROR("esp_sleep_enable_gpio_wakeup result %d", res);
@ -452,6 +480,14 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
gpio_wakeup_disable((gpio_num_t)RF95_IRQ);
}
#endif
#ifdef T_LORA_PAGER
LOG_DEBUG("power up XL9555 io");
io.digitalWrite(EXPANDS_DRV_EN, HIGH);
io.digitalWrite(EXPANDS_AMP_EN, HIGH);
io.digitalWrite(EXPANDS_KB_EN, HIGH);
io.digitalWrite(EXPANDS_SD_EN, HIGH);
io.digitalWrite(EXPANDS_GPIO_EN, HIGH);
#endif
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
notifyLightSleepEnd.notifyObservers(cause); // Button interrupts are reattached here

View File

@ -3,7 +3,10 @@ extends = portduino_base
build_flags = ${portduino_base.build_flags} -I variants/native/portduino
-I /usr/include
board = cross_platform
lib_deps = ${portduino_base.lib_deps}
lib_deps =
${portduino_base.lib_deps}
melopero/Melopero RV3028@^1.1.0
build_src_filter = ${portduino_base.build_src_filter}
[env:native]

View File

@ -5,3 +5,6 @@
#define HAS_GPS 1
#define MAX_RX_TOPHONE settingsMap[maxtophone]
#define MAX_NUM_NODES settingsMap[maxnodes]
// RAK12002 RTC Module
#define RV3028_RTC (uint8_t)0b1010010

View File

@ -208,7 +208,7 @@ No longer populated on PCB
#undef AREF_VOLTAGE
#define AREF_VOLTAGE 3.0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER (4.90F)
#define ADC_MULTIPLIER (4.99F)
#define HAS_RTC 0
#ifdef __cplusplus

View File

@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 7
build = 7
build = 8