Start of repeater role with in firmware

This commit is contained in:
Ben Meadors 2023-01-28 06:38:13 -06:00
parent cda7e8b6a5
commit de82119415
7 changed files with 564 additions and 343 deletions

View File

@ -29,29 +29,38 @@ bool GPS::getACK(uint8_t c, uint8_t i)
uint8_t buf[10] = {0xB5, 0x62, 0x05, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00};
unsigned long startTime = millis();
for (int j = 2; j < 6; j++) {
for (int j = 2; j < 6; j++)
{
buf[8] += buf[j];
buf[9] += buf[8];
}
for (int j = 0; j < 2; j++) {
for (int j = 0; j < 2; j++)
{
buf[6 + j] = ackP[j];
buf[8] += buf[6 + j];
buf[9] += buf[8];
}
while (1) {
if (ack > 9) {
while (1)
{
if (ack > 9)
{
return true;
}
if (millis() - startTime > 1000) {
if (millis() - startTime > 1000)
{
return false;
}
if (_serial_gps->available()) {
if (_serial_gps->available())
{
b = _serial_gps->read();
if (b == buf[ack]) {
if (b == buf[ack])
{
ack++;
} else {
}
else
{
ack = 0;
}
}
@ -73,37 +82,50 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
uint32_t startTime = millis();
uint16_t needRead;
while (millis() - startTime < 800) {
while (_serial_gps->available()) {
while (millis() - startTime < 800)
{
while (_serial_gps->available())
{
int c = _serial_gps->read();
switch (ubxFrameCounter) {
switch (ubxFrameCounter)
{
case 0:
// ubxFrame 'μ'
if (c == 0xB5) {
if (c == 0xB5)
{
ubxFrameCounter++;
}
break;
case 1:
// ubxFrame 'b'
if (c == 0x62) {
if (c == 0x62)
{
ubxFrameCounter++;
} else {
}
else
{
ubxFrameCounter = 0;
}
break;
case 2:
// Class
if (c == requestedClass) {
if (c == requestedClass)
{
ubxFrameCounter++;
} else {
}
else
{
ubxFrameCounter = 0;
}
break;
case 3:
// Message ID
if (c == requestedID) {
if (c == requestedID)
{
ubxFrameCounter++;
} else {
}
else
{
ubxFrameCounter = 0;
}
break;
@ -119,13 +141,17 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
break;
case 6:
// Check for buffer overflow
if (needRead >= size) {
if (needRead >= size)
{
ubxFrameCounter = 0;
break;
}
if (_serial_gps->readBytes(buffer, needRead) != needRead) {
if (_serial_gps->readBytes(buffer, needRead) != needRead)
{
ubxFrameCounter = 0;
} else {
}
else
{
// return payload lenght
return needRead;
}
@ -141,7 +167,8 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
bool GPS::setupGPS()
{
if (_serial_gps && !didSerialInit) {
if (_serial_gps && !didSerialInit)
{
didSerialInit = true;
#ifdef ARCH_ESP32
@ -173,7 +200,8 @@ bool GPS::setupGPS()
*/
gnssModel = probe();
if (gnssModel == GNSS_MODEL_MTK) {
if (gnssModel == GNSS_MODEL_MTK)
{
/*
* t-beam-s3-core uses the same L76K GNSS module as t-echo.
* Unlike t-echo, L76K uses 9600 baud rate for communication by default.
@ -190,8 +218,9 @@ bool GPS::setupGPS()
// Switch to Vehicle Mode, since SoftRF enables Aviation < 2g
_serial_gps->write("$PCAS11,3*1E\r\n");
delay(250);
} else if (gnssModel == GNSS_MODEL_UBLOX) {
}
else if (gnssModel == GNSS_MODEL_UBLOX)
{
/*
tips: NMEA Only should not be set here, otherwise initializing Ublox gnss module again after
@ -213,7 +242,8 @@ bool GPS::setupGPS()
byte _message_GGL[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x01,
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x05, 0x3A};
_serial_gps->write(_message_GGL, sizeof(_message_GGL));
if (!getACK(0x06, 0x01)) {
if (!getACK(0x06, 0x01))
{
LOG_WARN("Unable to disable NMEA GGL.\n");
return true;
}
@ -222,7 +252,8 @@ bool GPS::setupGPS()
byte _message_GSA[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x02,
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x06, 0x41};
_serial_gps->write(_message_GSA, sizeof(_message_GSA));
if (!getACK(0x06, 0x01)) {
if (!getACK(0x06, 0x01))
{
LOG_WARN("Unable to disable NMEA GSA.\n");
return true;
}
@ -231,7 +262,8 @@ bool GPS::setupGPS()
byte _message_GSV[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x03,
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x07, 0x48};
_serial_gps->write(_message_GSV, sizeof(_message_GSV));
if (!getACK(0x06, 0x01)) {
if (!getACK(0x06, 0x01))
{
LOG_WARN("Unable to disable NMEA GSV.\n");
return true;
}
@ -240,7 +272,8 @@ bool GPS::setupGPS()
byte _message_VTG[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x05,
0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x09, 0x56};
_serial_gps->write(_message_VTG, sizeof(_message_VTG));
if (!getACK(0x06, 0x01)) {
if (!getACK(0x06, 0x01))
{
LOG_WARN("Unable to disable NMEA VTG.\n");
return true;
}
@ -249,7 +282,8 @@ bool GPS::setupGPS()
byte _message_RMC[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x04,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x09, 0x54};
_serial_gps->write(_message_RMC, sizeof(_message_RMC));
if (!getACK(0x06, 0x01)) {
if (!getACK(0x06, 0x01))
{
LOG_WARN("Unable to enable NMEA RMC.\n");
return true;
}
@ -258,7 +292,8 @@ bool GPS::setupGPS()
byte _message_GGA[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00, 0xF0, 0x00,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x05, 0x38};
_serial_gps->write(_message_GGA, sizeof(_message_GGA));
if (!getACK(0x06, 0x01)) {
if (!getACK(0x06, 0x01))
{
LOG_WARN("Unable to enable NMEA GGA.\n");
}
}
@ -276,7 +311,8 @@ bool GPS::setup()
#endif
#ifdef HAS_PMU
if (config.position.gps_enabled) {
if (config.position.gps_enabled)
{
setGPSPower(true);
}
#endif
@ -290,13 +326,15 @@ bool GPS::setup()
setAwake(true); // Wake GPS power before doing any init
bool ok = setupGPS();
if (ok) {
if (ok)
{
notifySleepObserver.observe(&notifySleep);
notifyDeepSleepObserver.observe(&notifyDeepSleep);
notifyGPSSleepObserver.observe(&notifyGPSSleep);
}
if (config.position.gps_enabled == false && config.position.fixed_position == false) {
if (config.position.gps_enabled == false && config.position.fixed_position == false)
{
setAwake(false);
doGPSpowersave(false);
}
@ -345,7 +383,8 @@ void GPS::sleep()
/// Record that we have a GPS
void GPS::setConnected()
{
if (!hasGPS) {
if (!hasGPS)
{
hasGPS = true;
shouldPublish = true;
}
@ -353,7 +392,8 @@ void GPS::setConnected()
void GPS::setNumSatellites(uint8_t n)
{
if (n != numSatellites) {
if (n != numSatellites)
{
numSatellites = n;
shouldPublish = true;
}
@ -366,17 +406,22 @@ void GPS::setNumSatellites(uint8_t n)
*/
void GPS::setAwake(bool on)
{
if (!wakeAllowed && on) {
if (!wakeAllowed && on)
{
LOG_WARN("Inhibiting because !wakeAllowed\n");
on = false;
}
if (isAwake != on) {
if (isAwake != on)
{
LOG_DEBUG("WANT GPS=%d\n", on);
if (on) {
if (on)
{
lastWakeStartMsec = millis();
wake();
} else {
}
else
{
lastSleepStartMsec = millis();
sleep();
}
@ -415,7 +460,8 @@ uint32_t GPS::getSleepTime() const
void GPS::publishUpdate()
{
if (shouldPublish) {
if (shouldPublish)
{
shouldPublish = false;
// In debug logs, identify position by @timestamp:stage (stage 2 = publish)
@ -429,13 +475,22 @@ void GPS::publishUpdate()
int32_t GPS::runOnce()
{
if (whileIdle()) {
// Repeaters have no need for GPS
if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER)
disable();
if (whileIdle())
{
// if we have received valid NMEA claim we are connected
setConnected();
} else {
if ((config.position.gps_enabled == 1) && (gnssModel == GNSS_MODEL_UBLOX)) {
}
else
{
if ((config.position.gps_enabled == 1) && (gnssModel == GNSS_MODEL_UBLOX))
{
// reset the GPS on next bootup
if (devicestate.did_gps_reset && (millis() > 60000) && !hasFlow()) {
if (devicestate.did_gps_reset && (millis() > 60000) && !hasFlow())
{
LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n");
devicestate.did_gps_reset = false;
nodeDB.saveDeviceStateToDisk();
@ -448,28 +503,33 @@ int32_t GPS::runOnce()
uint32_t now = millis();
auto sleepTime = getSleepTime();
if (!isAwake && sleepTime != UINT32_MAX && (now - lastSleepStartMsec) > sleepTime) {
if (!isAwake && sleepTime != UINT32_MAX && (now - lastSleepStartMsec) > sleepTime)
{
// We now want to be awake - so wake up the GPS
setAwake(true);
}
// While we are awake
if (isAwake) {
if (isAwake)
{
// LOG_DEBUG("looking for location\n");
if ((now - lastWhileActiveMsec) > 5000) {
if ((now - lastWhileActiveMsec) > 5000)
{
lastWhileActiveMsec = now;
whileActive();
}
// If we've already set time from the GPS, no need to ask the GPS
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
if (!gotTime && lookForTime())
{ // Note: we count on this && short-circuiting and not resetting the RTC time
gotTime = true;
shouldPublish = true;
}
bool gotLoc = lookForLocation();
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
if (gotLoc && !hasValidLocation)
{ // declare that we have location ASAP
LOG_DEBUG("hasValidLocation RISING EDGE\n");
hasValidLocation = true;
shouldPublish = true;
@ -482,11 +542,14 @@ int32_t GPS::runOnce()
// Once we get a location we no longer desperately want an update
// LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime);
if ((gotLoc && gotTime) || tooLong) {
if ((gotLoc && gotTime) || tooLong)
{
if (tooLong) {
if (tooLong)
{
// we didn't get a location during this ack window, therefore declare loss of lock
if (hasValidLocation) {
if (hasValidLocation)
{
LOG_DEBUG("hasValidLocation FALLING EDGE (last read: %d)\n", gotLoc);
}
p = meshtastic_Position_init_default;
@ -501,10 +564,12 @@ int32_t GPS::runOnce()
// If state has changed do a publish
publishUpdate();
if (!(fixeddelayCtr >= 20) && config.position.fixed_position && hasValidLocation) {
if (!(fixeddelayCtr >= 20) && config.position.fixed_position && hasValidLocation)
{
fixeddelayCtr++;
// LOG_DEBUG("Our delay counter is %d\n", fixeddelayCtr);
if (fixeddelayCtr >= 20) {
if (fixeddelayCtr >= 20)
{
doGPSpowersave(false);
forceWake(false);
}
@ -516,11 +581,14 @@ int32_t GPS::runOnce()
void GPS::forceWake(bool on)
{
if (on) {
if (on)
{
LOG_DEBUG("Allowing GPS lock\n");
// lastSleepStartMsec = 0; // Force an update ASAP
wakeAllowed = true;
} else {
}
else
{
wakeAllowed = false;
// Note: if the gps was already awake, we DO NOT shut it down, because we want to allow it to complete its lock
@ -575,15 +643,19 @@ GnssModel_t GPS::probe()
// Get version information
_serial_gps->write("$PCAS06,0*1B\r\n");
uint32_t startTimeout = millis() + 500;
while (millis() < startTimeout) {
if (_serial_gps->available()) {
while (millis() < startTimeout)
{
if (_serial_gps->available())
{
String ver = _serial_gps->readStringUntil('\r');
// Get module info , If the correct header is returned,
// it can be determined that it is the MTK chip
int index = ver.indexOf("$");
if (index != -1) {
if (index != -1)
{
ver = ver.substring(index);
if (ver.startsWith("$GPTXT,01,01,02")) {
if (ver.startsWith("$GPTXT,01,01,02"))
{
LOG_INFO("L76K GNSS init succeeded, using L76K GNSS Module\n");
return GNSS_MODEL_MTK;
}
@ -594,7 +666,8 @@ GnssModel_t GPS::probe()
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x0E, 0x30};
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
// Check that the returned response class and message ID are correct
if (!getAck(buffer, 256, 0x06, 0x08)) {
if (!getAck(buffer, 256, 0x06, 0x08))
{
LOG_WARN("Failed to find UBlox & MTK GNSS Module\n");
return GNSS_MODEL_UNKONW;
}
@ -604,20 +677,25 @@ GnssModel_t GPS::probe()
_serial_gps->write(cfg_get_hw, sizeof(cfg_get_hw));
uint16_t len = getAck(buffer, 256, 0x0A, 0x04);
if (len) {
if (len)
{
uint16_t position = 0;
for (int i = 0; i < 30; i++) {
for (int i = 0; i < 30; i++)
{
info.swVersion[i] = buffer[position];
position++;
}
for (int i = 0; i < 10; i++) {
for (int i = 0; i < 10; i++)
{
info.hwVersion[i] = buffer[position];
position++;
}
while (len >= position + 30) {
for (int i = 0; i < 30; i++) {
while (len >= position + 30)
{
for (int i = 0; i < 30; i++)
{
info.extension[info.extensionNo][i] = buffer[position];
position++;
}
@ -630,24 +708,30 @@ GnssModel_t GPS::probe()
LOG_DEBUG("Soft version: %s\n", info.swVersion);
LOG_DEBUG("Hard version: %s\n", info.hwVersion);
LOG_DEBUG("Extensions:%d\n", info.extensionNo);
for (int i = 0; i < info.extensionNo; i++) {
for (int i = 0; i < info.extensionNo; i++)
{
LOG_DEBUG(" %s\n", info.extension[i]);
}
memset(buffer, 0, sizeof(buffer));
// tips: extensionNo field is 0 on some 6M GNSS modules
for (int i = 0; i < info.extensionNo; ++i) {
if (!strncmp(info.extension[i], "OD=", 3)) {
for (int i = 0; i < info.extensionNo; ++i)
{
if (!strncmp(info.extension[i], "OD=", 3))
{
strncpy((char *)buffer, &(info.extension[i][3]), sizeof(buffer));
LOG_DEBUG("GetModel:%s\n", (char *)buffer);
}
}
}
if (strlen((char *)buffer)) {
if (strlen((char *)buffer))
{
LOG_INFO("UBlox GNSS init succeeded, using UBlox %s GNSS Module\n", buffer);
} else {
}
else
{
LOG_INFO("UBlox GNSS init succeeded, using UBlox GNSS Module\n");
}
@ -665,20 +749,24 @@ GPS *createGps()
#if !HAS_GPS
return nullptr;
#else
if (config.position.gps_enabled) {
if (config.position.gps_enabled)
{
#ifdef GPS_ALTITUDE_HAE
LOG_DEBUG("Using HAE altitude model\n");
#else
LOG_DEBUG("Using MSL altitude model\n");
#endif
if (GPS::_serial_gps) {
if (GPS::_serial_gps)
{
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
// assume NMEA at 9600 baud.
GPS *new_gps = new NMEAGPS();
new_gps->setup();
return new_gps;
}
} else {
}
else
{
GPS *new_gps = new NMEAGPS();
new_gps->setup();
return new_gps;

View File

@ -18,7 +18,7 @@
*/
class LockingModule : public Module
{
public:
public:
/*!
\brief Extended SPI-based module constructor.
@ -47,7 +47,13 @@ class LockingModule : public Module
class RadioLibInterface : public RadioInterface, protected concurrency::NotifiedWorkerThread
{
/// Used as our notification from the ISR
enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };
enum PendingISR
{
ISR_NONE = 0,
ISR_RX,
ISR_TX,
TRANSMIT_DELAY_COMPLETED
};
/**
* Raw ISR handler that just calls our polymorphic method
@ -61,7 +67,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE);
protected:
protected:
/**
* We use a meshtastic sync word, but hashed with the Channel name. For releases before 1.2 we used 0x12 (or for very old
* loads 0x14) Note: do not use 0x34 - that is reserved for lorawan
@ -83,7 +89,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
/// are _trying_ to receive a packet currently (note - we might just be waiting for one)
bool isReceiving = false;
public:
public:
/** Our ISR code currently needs this to find our active instance
*/
static RadioLibInterface *instance;
@ -98,7 +104,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/
virtual void enableInterrupt(void (*)()) = 0;
public:
public:
RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi,
PhysicalLayer *iface = NULL);
@ -129,7 +135,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
virtual bool cancelSending(NodeNum from, PacketId id) override;
private:
private:
/** if we have something waiting to send, start a short (random) timer so we can come check for collision before actually
* doing the transmit */
void setTransmitDelay();
@ -154,7 +160,7 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
meshtastic_QueueStatus getQueueStatus();
protected:
protected:
/** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */
virtual void configHardwareForSend() {}

View File

@ -12,11 +12,13 @@
*/
ErrorCode ReliableRouter::send(meshtastic_MeshPacket *p)
{
if (p->want_ack) {
if (p->want_ack)
{
// If someone asks for acks on broadcast, we need the hop limit to be at least one, so that first node that receives our
// message will rebroadcast. But asking for hop_limit 0 in that context means the client app has no preference on hop
// counts and we want this message to get through the whole mesh, so use the default.
if (p->hop_limit == 0) {
if (p->hop_limit == 0)
{
p->hop_limit = (config.lora.hop_limit >= HOP_MAX) ? HOP_MAX : config.lora.hop_limit;
}
@ -30,7 +32,8 @@ ErrorCode ReliableRouter::send(meshtastic_MeshPacket *p)
bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
{
// Note: do not use getFrom() here, because we want to ignore messages sent from phone
if (p->from == getNodeNum()) {
if (p->from == getNodeNum())
{
printPacket("Rx someone rebroadcasting for us", p);
// We are seeing someone rebroadcast one of our broadcast attempts.
@ -41,14 +44,17 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
// from the intended recipient.
auto key = GlobalPacketId(getFrom(p), p->id);
auto old = findPendingPacket(key);
if (old) {
if (old)
{
LOG_DEBUG("generating implicit ack\n");
// NOTE: we do NOT check p->wantAck here because p is the INCOMING rebroadcast and that packet is not expected to be
// marked as wantAck
sendAckNak(meshtastic_Routing_Error_NONE, getFrom(p), p->id, old->packet->channel);
stopRetransmission(key);
} else {
}
else
{
LOG_DEBUG("didn't find pending packet\n");
}
}
@ -57,7 +63,8 @@ bool ReliableRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
* this way if an implicit ACK is dropped and a packet is resent we'll rebroadcast again.
* Resending real ACKs is omitted, as you might receive a packet multiple times due to flooding and
* flooding this ACK back to the original sender already adds redundancy. */
if (wasSeenRecently(p, false) && p->hop_limit == HOP_RELIABLE && !MeshModule::currentReply && p->to != nodeDB.getNodeNum()) {
if (wasSeenRecently(p, false) && p->hop_limit == HOP_RELIABLE && !MeshModule::currentReply && p->to != nodeDB.getNodeNum())
{
// retransmission on broadcast has hop_limit still equal to HOP_RELIABLE
LOG_DEBUG("Resending implicit ack for a repeated floodmsg\n");
meshtastic_MeshPacket *tosend = packetPool.allocCopy(*p);
@ -84,8 +91,10 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
{
NodeNum ourNode = getNodeNum();
if (p->to == ourNode) { // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability)
if (p->want_ack) {
if (p->to == ourNode)
{ // ignore ack/nak/want_ack packets that are not address to us (we only handle 0 hop reliability)
if (p->want_ack)
{
if (MeshModule::currentReply)
LOG_DEBUG("Some other module has replied to this message, no need for a 2nd ack\n");
else if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)
@ -102,11 +111,15 @@ void ReliableRouter::sniffReceived(const meshtastic_MeshPacket *p, const meshtas
PacketId nakId = (c && c->error_reason != meshtastic_Routing_Error_NONE) ? p->decoded.request_id : 0;
// We intentionally don't check wasSeenRecently, because it is harmless to delete non existent retransmission records
if (ackId || nakId) {
if (ackId) {
if (ackId || nakId)
{
if (ackId)
{
LOG_DEBUG("Received an ack for 0x%x, stopping retransmissions\n", ackId);
stopRetransmission(p->to, ackId);
} else {
}
else
{
LOG_DEBUG("Received a nak for 0x%x, stopping retransmissions\n", nakId);
stopRetransmission(p->to, nakId);
}
@ -128,9 +141,11 @@ PendingPacket::PendingPacket(meshtastic_MeshPacket *p)
PendingPacket *ReliableRouter::findPendingPacket(GlobalPacketId key)
{
auto old = pending.find(key); // If we have an old record, someone messed up because id got reused
if (old != pending.end()) {
if (old != pending.end())
{
return &old->second;
} else
}
else
return NULL;
}
/**
@ -145,12 +160,14 @@ bool ReliableRouter::stopRetransmission(NodeNum from, PacketId id)
bool ReliableRouter::stopRetransmission(GlobalPacketId key)
{
auto old = findPendingPacket(key);
if (old) {
if (old)
{
auto numErased = pending.erase(key);
assert(numErased == 1);
cancelSending(getFrom(old->packet), old->packet->id);
return true;
} else
}
else
return false;
}
@ -180,22 +197,27 @@ int32_t ReliableRouter::doRetransmissions()
// FIXME, we should use a better datastructure rather than walking through this map.
// for(auto el: pending) {
for (auto it = pending.begin(), nextIt = it; it != pending.end(); it = nextIt) {
for (auto it = pending.begin(), nextIt = it; it != pending.end(); it = nextIt)
{
++nextIt; // we use this odd pattern because we might be deleting it...
auto &p = it->second;
bool stillValid = true; // assume we'll keep this record around
// FIXME, handle 51 day rolloever here!!!
if (p.nextTxMsec <= now) {
if (p.numRetransmissions == 0) {
if (p.nextTxMsec <= now)
{
if (p.numRetransmissions == 0)
{
LOG_DEBUG("Reliable send failed, returning a nak for fr=0x%x,to=0x%x,id=0x%x\n", p.packet->from, p.packet->to,
p.packet->id);
sendAckNak(meshtastic_Routing_Error_MAX_RETRANSMIT, getFrom(p.packet), p.packet->id, p.packet->channel);
// Note: we don't stop retransmission here, instead the Nak packet gets processed in sniffReceived
stopRetransmission(it->first);
stillValid = false; // just deleted it
} else {
}
else
{
LOG_DEBUG("Sending reliable retransmission fr=0x%x,to=0x%x,id=0x%x, tries left=%d\n", p.packet->from,
p.packet->to, p.packet->id, p.numRetransmissions);
@ -209,7 +231,8 @@ int32_t ReliableRouter::doRetransmissions()
}
}
if (stillValid) {
if (stillValid)
{
// Update our desired sleep delay
int32_t t = p.nextTxMsec - now;

View File

@ -8,7 +8,8 @@
#include "main.h"
#include "mesh-pb-constants.h"
#include "modules/RoutingModule.h"
extern "C" {
extern "C"
{
#include "mesh/compression/unishox2.h"
}
@ -64,7 +65,8 @@ Router::Router() : concurrency::OSThread("Router"), fromRadioQueue(MAX_RX_FROMRA
int32_t Router::runOnce()
{
meshtastic_MeshPacket *mp;
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL) {
while ((mp = fromRadioQueue.dequeuePtr(0)) != NULL)
{
// printPacket("handle fromRadioQ", mp);
perhapsHandleReceived(mp);
}
@ -79,11 +81,14 @@ int32_t Router::runOnce()
*/
void Router::enqueueReceivedMessage(meshtastic_MeshPacket *p)
{
if (fromRadioQueue.enqueue(p, 0)) { // NOWAIT - fixme, if queue is full, delete older messages
if (fromRadioQueue.enqueue(p, 0))
{ // NOWAIT - fixme, if queue is full, delete older messages
// Nasty hack because our threading is primitive. interfaces shouldn't need to know about routers FIXME
setReceivedMessage();
} else {
}
else
{
printPacket("BUG! fromRadioQueue is full! Discarding!", p);
packetPool.release(p);
}
@ -98,7 +103,8 @@ PacketId generatePacketId()
uint32_t numPacketId = UINT32_MAX;
if (!didInit) {
if (!didInit)
{
didInit = true;
// pick a random initial sequence number at boot (to prevent repeated reboots always starting at 0)
@ -157,19 +163,25 @@ meshtastic_QueueStatus Router::getQueueStatus()
ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
{
// No need to deliver externally if the destination is the local node
if (p->to == nodeDB.getNodeNum()) {
if (p->to == nodeDB.getNodeNum())
{
printPacket("Enqueued local", p);
enqueueReceivedMessage(p);
return ERRNO_OK;
} else if (!iface) {
}
else if (!iface)
{
// We must be sending to remote nodes also, fail if no interface found
abortSendAndNak(meshtastic_Routing_Error_NO_INTERFACE, p);
return ERRNO_NO_INTERFACES;
} else {
}
else
{
// If we are sending a broadcast, we also treat it as if we just received it ourself
// this allows local apps (and PCs) to see broadcasts sourced locally
if (p->to == NODENUM_BROADCAST) {
if (p->to == NODENUM_BROADCAST)
{
handleReceived(p, src);
}
@ -192,22 +204,35 @@ void printBytes(const char *label, const uint8_t *p, size_t numbytes)
*/
ErrorCode Router::send(meshtastic_MeshPacket *p)
{
if (p->to == nodeDB.getNodeNum()) {
// Skip the normal ceremony for repeaters
if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER)
{
assert(iface);
return iface->send(p);
}
if (p->to == nodeDB.getNodeNum())
{
LOG_ERROR("BUG! send() called with packet destined for local node!\n");
packetPool.release(p);
return meshtastic_Routing_Error_BAD_REQUEST;
} // should have already been handled by sendLocal
// Abort sending if we are violating the duty cycle
if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100) {
if (!config.lora.override_duty_cycle && myRegion->dutyCycle < 100)
{
float hourlyTxPercent = airTime->utilizationTXPercent();
if (hourlyTxPercent > myRegion->dutyCycle) {
if (hourlyTxPercent > myRegion->dutyCycle)
{
uint8_t silentMinutes = airTime->getSilentMinutes(hourlyTxPercent, myRegion->dutyCycle);
LOG_WARN("Duty cycle limit exceeded. Aborting send for now, you can send again in %d minutes.\n", silentMinutes);
meshtastic_Routing_Error err = meshtastic_Routing_Error_DUTY_CYCLE_LIMIT;
if (getFrom(p) == nodeDB.getNodeNum()) { // only send NAK to API, not to the mesh
if (getFrom(p) == nodeDB.getNodeNum())
{ // only send NAK to API, not to the mesh
abortSendAndNak(err, p);
} else {
}
else
{
packetPool.release(p);
}
return err;
@ -232,13 +257,15 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now
// If the packet is not yet encrypted, do so now
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)
{
ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it
bool shouldActuallyEncrypt = true;
#if HAS_WIFI || HAS_ETHERNET
if (moduleConfig.mqtt.enabled) {
if (moduleConfig.mqtt.enabled)
{
// check if we should send decrypted packets to mqtt
// truth table:
@ -251,7 +278,8 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
* => so we only decrypt mqtt if they have a custom mqtt server AND mqtt_encryption_enabled is FALSE
*/
if (*moduleConfig.mqtt.address && !moduleConfig.mqtt.encryption_enabled) {
if (*moduleConfig.mqtt.address && !moduleConfig.mqtt.encryption_enabled)
{
shouldActuallyEncrypt = false;
}
@ -264,13 +292,15 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
#endif
auto encodeResult = perhapsEncode(p);
if (encodeResult != meshtastic_Routing_Error_NONE) {
if (encodeResult != meshtastic_Routing_Error_NONE)
{
abortSendAndNak(encodeResult, p);
return encodeResult; // FIXME - this isn't a valid ErrorCode
}
#if HAS_WIFI || HAS_ETHERNET
if (moduleConfig.mqtt.enabled) {
if (moduleConfig.mqtt.enabled)
{
// the packet is now encrypted.
// check if we should send encrypted packets to mqtt
if (mqtt && shouldActuallyEncrypt)
@ -309,9 +339,11 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
// assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
// Try to find a channel that works with this hash
for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++)
{
// Try to use this hash/channel pair
if (channels.decryptForHash(chIndex, p->channel)) {
if (channels.decryptForHash(chIndex, p->channel))
{
// Try to decrypt the packet if we can
size_t rawSize = p->encrypted.size;
assert(rawSize <= sizeof(bytes));
@ -323,17 +355,23 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
// Take those raw bytes and convert them back into a well structured protobuf we can understand
memset(&p->decoded, 0, sizeof(p->decoded));
if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded)) {
if (!pb_decode_from_bytes(bytes, rawSize, &meshtastic_Data_msg, &p->decoded))
{
LOG_ERROR("Invalid protobufs in received mesh packet (bad psk?)!\n");
} else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP) {
}
else if (p->decoded.portnum == meshtastic_PortNum_UNKNOWN_APP)
{
LOG_ERROR("Invalid portnum (bad psk?)!\n");
} else {
}
else
{
// parsing was successful
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag; // change type to decoded
p->channel = chIndex; // change to store the index instead of the hash
// Decompress if needed. jm
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP) {
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_COMPRESSED_APP)
{
// Decompress the payload
char compressed_in[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
char decompressed_out[meshtastic_Constants_DATA_PAYLOAD_LEN] = {};
@ -366,13 +404,14 @@ bool perhapsDecode(meshtastic_MeshPacket *p)
meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
{
// If the packet is not yet encrypted, do so now
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)
{
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), &meshtastic_Data_msg, &p->decoded);
// Only allow encryption on the text message app.
// TODO: Allow modules to opt into compression.
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) {
if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP)
{
char original_payload[meshtastic_Constants_DATA_PAYLOAD_LEN];
memcpy(original_payload, p->decoded.payload.bytes, p->decoded.payload.size);
@ -387,14 +426,17 @@ meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p)
LOG_DEBUG("Original message - %s \n", p->decoded.payload.bytes);
// If the compressed length is greater than or equal to the original size, don't use the compressed form
if (compressed_len >= p->decoded.payload.size) {
if (compressed_len >= p->decoded.payload.size)
{
LOG_DEBUG("Not using compressing message.\n");
// Set the uncompressed payload varient anyway. Shouldn't hurt?
// p->decoded.which_payloadVariant = Data_payload_tag;
// Otherwise we use the compressor
} else {
}
else
{
LOG_DEBUG("Using compressed message.\n");
// Copy the compressed data into the meshpacket
@ -445,7 +487,8 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
// Take those raw bytes and convert them back into a well structured protobuf we can understand
bool decoded = perhapsDecode(p);
if (decoded) {
if (decoded)
{
// parsing was successful, queue for our recipient
if (src == RX_SRC_LOCAL)
printPacket("handleReceived(LOCAL)", p);
@ -453,7 +496,9 @@ void Router::handleReceived(meshtastic_MeshPacket *p, RxSource src)
printPacket("handleReceived(USER)", p);
else
printPacket("handleReceived(REMOTE)", p);
} else {
}
else
{
printPacket("packet decoding failed (no PSK?)", p);
}
@ -468,7 +513,8 @@ void Router::perhapsHandleReceived(meshtastic_MeshPacket *p)
if (ignore)
LOG_DEBUG("Ignoring incoming message, 0x%x is in our ignore list\n", p->from);
else if (ignore |= shouldFilterReceived(p)) {
else if (ignore |= shouldFilterReceived(p))
{
LOG_DEBUG("Incoming message was filtered 0x%x\n", p->from);
}

View File

@ -13,15 +13,15 @@
*/
class Router : protected concurrency::OSThread
{
private:
private:
/// Packets which have just arrived from the radio, ready to be processed by this service and possibly
/// forwarded to the phone.
PointerQueue<meshtastic_MeshPacket> fromRadioQueue;
protected:
protected:
RadioInterface *iface = NULL;
public:
public:
/**
* Constructor
*
@ -73,9 +73,6 @@ class Router : protected concurrency::OSThread
*/
void enqueueReceivedMessage(meshtastic_MeshPacket *p);
protected:
friend class RoutingModule;
/**
* Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall.
@ -85,6 +82,9 @@ class Router : protected concurrency::OSThread
*/
virtual ErrorCode send(meshtastic_MeshPacket *p);
protected:
friend class RoutingModule;
/**
* Should this incoming filter be dropped?
*
@ -106,7 +106,7 @@ class Router : protected concurrency::OSThread
*/
void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
private:
private:
/**
* Called from loop()
* Handle any packet that is received by an interface on this node.

View File

@ -0,0 +1,27 @@
#include "RepeaterModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
RepeaterModule *repeaterModule;
bool RepeaterModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Routing *r)
{
printPacket("Repeater rebroadcasting", &mp);
meshtastic_MeshPacket *p = const_cast<meshtastic_MeshPacket *>(&mp);
router->send(p);
return true;
}
meshtastic_MeshPacket *RepeaterModule::allocReply()
{
return NULL;
}
RepeaterModule::RepeaterModule() : ProtobufModule("repeater", meshtastic_PortNum_ROUTING_APP, &meshtastic_Routing_msg)
{
isPromiscuous = true;
encryptedOk = true;
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "Channels.h"
#include "ProtobufModule.h"
/**
* Routing module for router control messages
*/
class RepeaterModule : public ProtobufModule<meshtastic_Routing>
{
public:
/** Constructor
* name is for debugging output
*/
RepeaterModule();
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Routing *p) override;
/** Messages can be received that have the want_response bit set. If set, this callback will be invoked
* so that subclasses can (optionally) send a response back to the original sender. */
virtual meshtastic_MeshPacket *allocReply() override;
/// Override wantPacket to say we want to see all packets, not just those for our port number
virtual bool wantPacket(const meshtastic_MeshPacket *p) override { return true; }
};
extern RepeaterModule *repeaterModule;