diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 4368bc3a7..468f8e9f4 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -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(¬ifySleep); notifyDeepSleepObserver.observe(¬ifyDeepSleep); notifyGPSSleepObserver.observe(¬ifyGPSSleep); } - 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; diff --git a/src/mesh/RadioLibInterface.h b/src/mesh/RadioLibInterface.h index c5cc85df7..6694393c7 100644 --- a/src/mesh/RadioLibInterface.h +++ b/src/mesh/RadioLibInterface.h @@ -18,162 +18,168 @@ */ class LockingModule : public Module { - public: - /*! - \brief Extended SPI-based module constructor. +public: + /*! + \brief Extended SPI-based module constructor. - \param cs Arduino pin to be used as chip select. + \param cs Arduino pin to be used as chip select. - \param irq Arduino pin to be used as interrupt/GPIO. + \param irq Arduino pin to be used as interrupt/GPIO. - \param rst Arduino pin to be used as hardware reset for the module. + \param rst Arduino pin to be used as hardware reset for the module. - \param gpio Arduino pin to be used as additional interrupt/GPIO. + \param gpio Arduino pin to be used as additional interrupt/GPIO. - \param spi SPI interface to be used, can also use software SPI implementations. + \param spi SPI interface to be used, can also use software SPI implementations. - \param spiSettings SPI interface settings. - */ - LockingModule(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE gpio, SPIClass &spi, - SPISettings spiSettings) - : Module(cs, irq, rst, gpio, spi, spiSettings) - { - } + \param spiSettings SPI interface settings. + */ + LockingModule(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE gpio, SPIClass &spi, + SPISettings spiSettings) + : Module(cs, irq, rst, gpio, spi, spiSettings) + { + } - void SPIbeginTransaction() override; - void SPIendTransaction() override; + void SPIbeginTransaction() override; + void SPIendTransaction() override; }; 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 }; + /// Used as our notification from the ISR + enum PendingISR + { + ISR_NONE = 0, + ISR_RX, + ISR_TX, + TRANSMIT_DELAY_COMPLETED + }; - /** - * Raw ISR handler that just calls our polymorphic method - */ - static void isrTxLevel0(), isrLevel0Common(PendingISR code); + /** + * Raw ISR handler that just calls our polymorphic method + */ + static void isrTxLevel0(), isrLevel0Common(PendingISR code); - /** - * Debugging counts - */ - uint32_t rxBad = 0, rxGood = 0, txGood = 0; + /** + * Debugging counts + */ + uint32_t rxBad = 0, rxGood = 0, txGood = 0; - MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE); + MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE); - 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 - * - * We now use 0x2b (so that someday we can possibly use NOT 2b - because that would be funny pun). We will be staying with - * this code for a long time. - */ - const uint8_t syncWord = 0x2b; +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 + * + * We now use 0x2b (so that someday we can possibly use NOT 2b - because that would be funny pun). We will be staying with + * this code for a long time. + */ + const uint8_t syncWord = 0x2b; - float currentLimit = 100; // 100mA OCP - Should be acceptable for RFM95/SX127x chipset. + float currentLimit = 100; // 100mA OCP - Should be acceptable for RFM95/SX127x chipset. - LockingModule module; // The HW interface to the radio + LockingModule module; // The HW interface to the radio - /** - * provides lowest common denominator RadioLib API - */ - PhysicalLayer *iface; + /** + * provides lowest common denominator RadioLib API + */ + PhysicalLayer *iface; - /// are _trying_ to receive a packet currently (note - we might just be waiting for one) - bool isReceiving = false; + /// are _trying_ to receive a packet currently (note - we might just be waiting for one) + bool isReceiving = false; - public: - /** Our ISR code currently needs this to find our active instance - */ - static RadioLibInterface *instance; +public: + /** Our ISR code currently needs this to find our active instance + */ + static RadioLibInterface *instance; - /** - * Glue functions called from ISR land - */ - virtual void disableInterrupt() = 0; + /** + * Glue functions called from ISR land + */ + virtual void disableInterrupt() = 0; - /** - * Enable a particular ISR callback glue function - */ - virtual void enableInterrupt(void (*)()) = 0; + /** + * Enable a particular ISR callback glue function + */ + virtual void enableInterrupt(void (*)()) = 0; - public: - RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi, - PhysicalLayer *iface = NULL); +public: + RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi, + PhysicalLayer *iface = NULL); - virtual ErrorCode send(meshtastic_MeshPacket *p) override; + virtual ErrorCode send(meshtastic_MeshPacket *p) override; - /** - * Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving) - * - * This method must be used before putting the CPU into deep or light sleep. - */ - virtual bool canSleep() override; + /** + * Return true if we think the board can go to sleep (i.e. our tx queue is empty, we are not sending or receiving) + * + * This method must be used before putting the CPU into deep or light sleep. + */ + virtual bool canSleep() override; - /** - * Start waiting to receive a message - * - * External functions can call this method to wake the device from sleep. - */ - virtual void startReceive() = 0; + /** + * Start waiting to receive a message + * + * External functions can call this method to wake the device from sleep. + */ + virtual void startReceive() = 0; - /** can we detect a LoRa preamble on the current channel? */ - virtual bool isChannelActive() = 0; + /** can we detect a LoRa preamble on the current channel? */ + virtual bool isChannelActive() = 0; - /** are we actively receiving a packet (only called during receiving state) - * This method is only public to facilitate debugging. Do not call. - */ - virtual bool isActivelyReceiving() = 0; + /** are we actively receiving a packet (only called during receiving state) + * This method is only public to facilitate debugging. Do not call. + */ + virtual bool isActivelyReceiving() = 0; - /** 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; + /** 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: - /** 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(); +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(); - /** random timer with certain min. and max. settings */ - void startTransmitTimer(bool withDelay = true); + /** random timer with certain min. and max. settings */ + void startTransmitTimer(bool withDelay = true); - /** timer scaled to SNR of to be flooded packet */ - void startTransmitTimerSNR(float snr); + /** timer scaled to SNR of to be flooded packet */ + void startTransmitTimerSNR(float snr); - void handleTransmitInterrupt(); - void handleReceiveInterrupt(); + void handleTransmitInterrupt(); + void handleReceiveInterrupt(); - static void timerCallback(void *p1, uint32_t p2); + static void timerCallback(void *p1, uint32_t p2); - virtual void onNotify(uint32_t notification) override; + virtual void onNotify(uint32_t notification) override; - /** start an immediate transmit - * This method is virtual so subclasses can hook as needed, subclasses should not call directly - */ - virtual void startSend(meshtastic_MeshPacket *txp); + /** start an immediate transmit + * This method is virtual so subclasses can hook as needed, subclasses should not call directly + */ + virtual void startSend(meshtastic_MeshPacket *txp); - meshtastic_QueueStatus getQueueStatus(); + meshtastic_QueueStatus getQueueStatus(); - protected: - /** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */ - virtual void configHardwareForSend() {} +protected: + /** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */ + virtual void configHardwareForSend() {} - /** Could we send right now (i.e. either not actively receiving or transmitting)? */ - virtual bool canSendImmediately(); + /** Could we send right now (i.e. either not actively receiving or transmitting)? */ + virtual bool canSendImmediately(); - /** - * Raw ISR handler that just calls our polymorphic method - */ - static void isrRxLevel0(); + /** + * Raw ISR handler that just calls our polymorphic method + */ + static void isrRxLevel0(); - /** - * If a send was in progress finish it and return the buffer to the pool */ - void completeSending(); + /** + * If a send was in progress finish it and return the buffer to the pool */ + void completeSending(); - /** - * Add SNR data to received messages - */ - virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) = 0; + /** + * Add SNR data to received messages + */ + virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) = 0; - virtual void setStandby() = 0; + virtual void setStandby() = 0; }; diff --git a/src/mesh/ReliableRouter.cpp b/src/mesh/ReliableRouter.cpp index 8000926f3..0e8481a8b 100644 --- a/src/mesh/ReliableRouter.cpp +++ b/src/mesh/ReliableRouter.cpp @@ -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; diff --git a/src/mesh/Router.cpp b/src/mesh/Router.cpp index 0cb7ee8be..6540d2652 100644 --- a/src/mesh/Router.cpp +++ b/src/mesh/Router.cpp @@ -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" } @@ -25,13 +26,13 @@ extern "C" { * **/ -#define MAX_RX_FROMRADIO \ +#define MAX_RX_FROMRADIO \ 4 // max number of packets destined to our queue, we dispatch packets quickly so it doesn't need to be big // I think this is right, one packet for each of the three fifos + one packet being currently assembled for TX or RX // And every TX packet might have a retransmission packet or an ack alive at any moment -#define MAX_PACKETS \ - (MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \ +#define MAX_PACKETS \ + (MAX_RX_TOPHONE + MAX_RX_FROMRADIO + 2 * MAX_TX_QUEUE + \ 2) // max number of packets which can be in flight (either queued from reception or queued for sending) // static MemoryPool staticPool(MAX_PACKETS); @@ -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); } diff --git a/src/mesh/Router.h b/src/mesh/Router.h index 8e116dca0..8ad67bc0e 100644 --- a/src/mesh/Router.h +++ b/src/mesh/Router.h @@ -13,122 +13,122 @@ */ class Router : protected concurrency::OSThread { - private: - /// Packets which have just arrived from the radio, ready to be processed by this service and possibly - /// forwarded to the phone. - PointerQueue fromRadioQueue; +private: + /// Packets which have just arrived from the radio, ready to be processed by this service and possibly + /// forwarded to the phone. + PointerQueue fromRadioQueue; - protected: - RadioInterface *iface = NULL; +protected: + RadioInterface *iface = NULL; - public: - /** - * Constructor - * - */ - Router(); +public: + /** + * Constructor + * + */ + Router(); - /** - * Currently we only allow one interface, that may change in the future - */ - void addInterface(RadioInterface *_iface) { iface = _iface; } + /** + * Currently we only allow one interface, that may change in the future + */ + void addInterface(RadioInterface *_iface) { iface = _iface; } - /** - * do idle processing - * Mostly looking in our incoming rxPacket queue and calling handleReceived. - */ - virtual int32_t runOnce() override; + /** + * do idle processing + * Mostly looking in our incoming rxPacket queue and calling handleReceived. + */ + virtual int32_t runOnce() override; - /** - * Works like send, but if we are sending to the local node, we directly put the message in the receive queue. - * This is the primary method used for sending packets, because it handles both the remote and local cases. - * - * NOTE: This method will free the provided packet (even if we return an error code) - */ - ErrorCode sendLocal(meshtastic_MeshPacket *p, RxSource src = RX_SRC_RADIO); + /** + * Works like send, but if we are sending to the local node, we directly put the message in the receive queue. + * This is the primary method used for sending packets, because it handles both the remote and local cases. + * + * NOTE: This method will free the provided packet (even if we return an error code) + */ + ErrorCode sendLocal(meshtastic_MeshPacket *p, RxSource src = RX_SRC_RADIO); - /** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */ - bool cancelSending(NodeNum from, PacketId id); + /** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */ + bool cancelSending(NodeNum from, PacketId id); - /** Allocate and return a meshpacket which defaults as send to broadcast from the current node. - * The returned packet is guaranteed to have a unique packet ID already assigned - */ - meshtastic_MeshPacket *allocForSending(); + /** Allocate and return a meshpacket which defaults as send to broadcast from the current node. + * The returned packet is guaranteed to have a unique packet ID already assigned + */ + meshtastic_MeshPacket *allocForSending(); - /** Return Underlying interface's TX queue status */ - meshtastic_QueueStatus getQueueStatus(); + /** Return Underlying interface's TX queue status */ + meshtastic_QueueStatus getQueueStatus(); - /** - * @return our local nodenum */ - NodeNum getNodeNum(); + /** + * @return our local nodenum */ + NodeNum getNodeNum(); - /** Wake up the router thread ASAP, because we just queued a message for it. - * FIXME, this is kinda a hack because we don't have a nice way yet to say 'wake us because we are 'blocked on this queue' - */ - void setReceivedMessage(); + /** Wake up the router thread ASAP, because we just queued a message for it. + * FIXME, this is kinda a hack because we don't have a nice way yet to say 'wake us because we are 'blocked on this queue' + */ + void setReceivedMessage(); - /** - * RadioInterface calls this to queue up packets that have been received from the radio. The router is now responsible for - * freeing the packet - */ - void enqueueReceivedMessage(meshtastic_MeshPacket *p); + /** + * RadioInterface calls this to queue up packets that have been received from the radio. The router is now responsible for + * freeing the packet + */ + 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. + * If the txmit queue is full it might return an error + * + * NOTE: This method will free the provided packet (even if we return an error code) + */ + virtual ErrorCode send(meshtastic_MeshPacket *p); - /** - * Send a packet on a suitable interface. This routine will - * later free() the packet to pool. This routine is not allowed to stall. - * If the txmit queue is full it might return an error - * - * NOTE: This method will free the provided packet (even if we return an error code) - */ - virtual ErrorCode send(meshtastic_MeshPacket *p); +protected: + friend class RoutingModule; - /** - * Should this incoming filter be dropped? - * - * FIXME, move this into the new RoutingModule and do the filtering there using the regular module logic - * - * Called immedately on receiption, before any further processing. - * @return true to abandon the packet - */ - virtual bool shouldFilterReceived(const meshtastic_MeshPacket *p) { return false; } + /** + * Should this incoming filter be dropped? + * + * FIXME, move this into the new RoutingModule and do the filtering there using the regular module logic + * + * Called immedately on receiption, before any further processing. + * @return true to abandon the packet + */ + virtual bool shouldFilterReceived(const meshtastic_MeshPacket *p) { return false; } - /** - * Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to - * update routing tables etc... based on what we overhear (even for messages not destined to our node) - */ - virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c); + /** + * Every (non duplicate) packet this node receives will be passed through this method. This allows subclasses to + * update routing tables etc... based on what we overhear (even for messages not destined to our node) + */ + virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c); - /** - * Send an ack or a nak packet back towards whoever sent idFrom - */ - void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); + /** + * Send an ack or a nak packet back towards whoever sent idFrom + */ + void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); - private: - /** - * Called from loop() - * Handle any packet that is received by an interface on this node. - * Note: some packets may merely being passed through this node and will be forwarded elsewhere. - * - * Note: this packet will never be called for messages sent/generated by this node. - * Note: this method will free the provided packet. - */ - void perhapsHandleReceived(meshtastic_MeshPacket *p); +private: + /** + * Called from loop() + * Handle any packet that is received by an interface on this node. + * Note: some packets may merely being passed through this node and will be forwarded elsewhere. + * + * Note: this packet will never be called for messages sent/generated by this node. + * Note: this method will free the provided packet. + */ + void perhapsHandleReceived(meshtastic_MeshPacket *p); - /** - * Called from perhapsHandleReceived() - allows subclass message delivery behavior. - * Handle any packet that is received by an interface on this node. - * Note: some packets may merely being passed through this node and will be forwarded elsewhere. - * - * Note: this packet will never be called for messages sent/generated by this node. - * Note: this method will free the provided packet. - */ - void handleReceived(meshtastic_MeshPacket *p, RxSource src = RX_SRC_RADIO); + /** + * Called from perhapsHandleReceived() - allows subclass message delivery behavior. + * Handle any packet that is received by an interface on this node. + * Note: some packets may merely being passed through this node and will be forwarded elsewhere. + * + * Note: this packet will never be called for messages sent/generated by this node. + * Note: this method will free the provided packet. + */ + void handleReceived(meshtastic_MeshPacket *p, RxSource src = RX_SRC_RADIO); - /** Frees the provided packet, and generates a NAK indicating the speicifed error while sending */ - void abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket *p); + /** Frees the provided packet, and generates a NAK indicating the speicifed error while sending */ + void abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket *p); }; /** FIXME - move this into a mesh packet class diff --git a/src/modules/RepeaterModule.cpp b/src/modules/RepeaterModule.cpp new file mode 100644 index 000000000..06147f9f2 --- /dev/null +++ b/src/modules/RepeaterModule.cpp @@ -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(&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; +} diff --git a/src/modules/RepeaterModule.h b/src/modules/RepeaterModule.h new file mode 100644 index 000000000..d24bbb296 --- /dev/null +++ b/src/modules/RepeaterModule.h @@ -0,0 +1,31 @@ +#pragma once +#include "Channels.h" +#include "ProtobufModule.h" + +/** + * Routing module for router control messages + */ +class RepeaterModule : public ProtobufModule +{ +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;