mirror of
https://github.com/meshtastic/firmware.git
synced 2025-09-06 03:29:17 +00:00
Compare commits
8 Commits
33d96cc218
...
d9bb74ddbb
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d9bb74ddbb | ||
![]() |
bd3cbfc1ad | ||
![]() |
fddc4e00ca | ||
![]() |
5f7eec5504 | ||
![]() |
6b94c297b9 | ||
![]() |
edeb25cab5 | ||
![]() |
44688e8363 | ||
![]() |
7d3af3aa8e |
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -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
7
debian/changelog
vendored
@ -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
|
||||
|
@ -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
|
||||
@ -681,7 +682,7 @@ bool Power::setup()
|
||||
found = true;
|
||||
} else if (lipoChargerInit()) {
|
||||
found = true;
|
||||
} else if (meshSolarInit()) {
|
||||
} else if (meshSolarInit()) {
|
||||
found = true;
|
||||
} else if (analogInit()) {
|
||||
found = true;
|
||||
@ -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"
|
||||
|
||||
@ -1492,7 +1494,7 @@ class meshSolarBatteryLevel : public HasBatteryLevel
|
||||
/**
|
||||
* return true if there is an external power source detected
|
||||
*/
|
||||
virtual bool isVbusIn() override { return meshSolarIsVbusIn();}
|
||||
virtual bool isVbusIn() override { return meshSolarIsVbusIn(); }
|
||||
|
||||
/**
|
||||
* return true if the battery is currently charging
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
if (currentQuality == RTCQualityNone) {
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
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);
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
if (currentQuality == RTCQualityNone) {
|
||||
timeStartMsec = now;
|
||||
zeroOffsetSecs = tv.tv_sec;
|
||||
currentQuality = RTCQualityDevice;
|
||||
}
|
||||
return RTCSetResultSuccess;
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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
|
||||
startReceive(); // try receiving this packet, afterwards we'll be trying to transmit again
|
||||
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
|
||||
|
242
src/modules/MeshTipsModule.cpp
Normal file
242
src/modules/MeshTipsModule.cpp
Normal 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;
|
||||
}
|
80
src/modules/MeshTipsModule.h
Normal file
80
src/modules/MeshTipsModule.h
Normal 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;
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -4,4 +4,7 @@
|
||||
#define CANNED_MESSAGE_MODULE_ENABLE 1
|
||||
#define HAS_GPS 1
|
||||
#define MAX_RX_TOPHONE settingsMap[maxtophone]
|
||||
#define MAX_NUM_NODES settingsMap[maxnodes]
|
||||
#define MAX_NUM_NODES settingsMap[maxnodes]
|
||||
|
||||
// RAK12002 RTC Module
|
||||
#define RV3028_RTC (uint8_t)0b1010010
|
@ -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
|
||||
|
@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 2
|
||||
minor = 7
|
||||
build = 7
|
||||
build = 8
|
||||
|
Loading…
Reference in New Issue
Block a user