This commit is contained in:
Ben Meadors 2023-01-28 06:39:14 -06:00
parent de82119415
commit e8908784f9
6 changed files with 357 additions and 512 deletions

View File

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

View File

@ -18,168 +18,162 @@
*/ */
class LockingModule : public Module class LockingModule : public Module
{ {
public: public:
/*! /*!
\brief Extended SPI-based module constructor. \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. \param spiSettings SPI interface settings.
*/ */
LockingModule(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE gpio, SPIClass &spi, LockingModule(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE gpio, SPIClass &spi,
SPISettings spiSettings) SPISettings spiSettings)
: Module(cs, irq, rst, gpio, spi, spiSettings) : Module(cs, irq, rst, gpio, spi, spiSettings)
{ {
} }
void SPIbeginTransaction() override; void SPIbeginTransaction() override;
void SPIendTransaction() override; void SPIendTransaction() override;
}; };
class RadioLibInterface : public RadioInterface, protected concurrency::NotifiedWorkerThread class RadioLibInterface : public RadioInterface, protected concurrency::NotifiedWorkerThread
{ {
/// Used as our notification from the ISR /// Used as our notification from the ISR
enum PendingISR enum PendingISR { ISR_NONE = 0, ISR_RX, ISR_TX, TRANSMIT_DELAY_COMPLETED };
{
ISR_NONE = 0,
ISR_RX,
ISR_TX,
TRANSMIT_DELAY_COMPLETED
};
/** /**
* Raw ISR handler that just calls our polymorphic method * Raw ISR handler that just calls our polymorphic method
*/ */
static void isrTxLevel0(), isrLevel0Common(PendingISR code); static void isrTxLevel0(), isrLevel0Common(PendingISR code);
/** /**
* Debugging counts * Debugging counts
*/ */
uint32_t rxBad = 0, rxGood = 0, txGood = 0; uint32_t rxBad = 0, rxGood = 0, txGood = 0;
MeshPacketQueue txQueue = MeshPacketQueue(MAX_TX_QUEUE); 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 * 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 * 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 * 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. * this code for a long time.
*/ */
const uint8_t syncWord = 0x2b; 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 * provides lowest common denominator RadioLib API
*/ */
PhysicalLayer *iface; PhysicalLayer *iface;
/// are _trying_ to receive a packet currently (note - we might just be waiting for one) /// are _trying_ to receive a packet currently (note - we might just be waiting for one)
bool isReceiving = false; bool isReceiving = false;
public: public:
/** Our ISR code currently needs this to find our active instance /** Our ISR code currently needs this to find our active instance
*/ */
static RadioLibInterface *instance; static RadioLibInterface *instance;
/** /**
* Glue functions called from ISR land * Glue functions called from ISR land
*/ */
virtual void disableInterrupt() = 0; virtual void disableInterrupt() = 0;
/** /**
* Enable a particular ISR callback glue function * Enable a particular ISR callback glue function
*/ */
virtual void enableInterrupt(void (*)()) = 0; 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, RadioLibInterface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy, SPIClass &spi,
PhysicalLayer *iface = NULL); 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) * 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. * This method must be used before putting the CPU into deep or light sleep.
*/ */
virtual bool canSleep() override; virtual bool canSleep() override;
/** /**
* Start waiting to receive a message * Start waiting to receive a message
* *
* External functions can call this method to wake the device from sleep. * External functions can call this method to wake the device from sleep.
*/ */
virtual void startReceive() = 0; virtual void startReceive() = 0;
/** can we detect a LoRa preamble on the current channel? */ /** can we detect a LoRa preamble on the current channel? */
virtual bool isChannelActive() = 0; virtual bool isChannelActive() = 0;
/** are we actively receiving a packet (only called during receiving state) /** are we actively receiving a packet (only called during receiving state)
* This method is only public to facilitate debugging. Do not call. * This method is only public to facilitate debugging. Do not call.
*/ */
virtual bool isActivelyReceiving() = 0; virtual bool isActivelyReceiving() = 0;
/** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */ /** 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; 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 /** if we have something waiting to send, start a short (random) timer so we can come check for collision before actually
* doing the transmit */ * doing the transmit */
void setTransmitDelay(); void setTransmitDelay();
/** random timer with certain min. and max. settings */ /** random timer with certain min. and max. settings */
void startTransmitTimer(bool withDelay = true); void startTransmitTimer(bool withDelay = true);
/** timer scaled to SNR of to be flooded packet */ /** timer scaled to SNR of to be flooded packet */
void startTransmitTimerSNR(float snr); void startTransmitTimerSNR(float snr);
void handleTransmitInterrupt(); void handleTransmitInterrupt();
void handleReceiveInterrupt(); 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 /** start an immediate transmit
* This method is virtual so subclasses can hook as needed, subclasses should not call directly * This method is virtual so subclasses can hook as needed, subclasses should not call directly
*/ */
virtual void startSend(meshtastic_MeshPacket *txp); virtual void startSend(meshtastic_MeshPacket *txp);
meshtastic_QueueStatus getQueueStatus(); meshtastic_QueueStatus getQueueStatus();
protected: protected:
/** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */ /** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */
virtual void configHardwareForSend() {} virtual void configHardwareForSend() {}
/** Could we send right now (i.e. either not actively receiving or transmitting)? */ /** Could we send right now (i.e. either not actively receiving or transmitting)? */
virtual bool canSendImmediately(); virtual bool canSendImmediately();
/** /**
* Raw ISR handler that just calls our polymorphic method * Raw ISR handler that just calls our polymorphic method
*/ */
static void isrRxLevel0(); static void isrRxLevel0();
/** /**
* If a send was in progress finish it and return the buffer to the pool */ * If a send was in progress finish it and return the buffer to the pool */
void completeSending(); void completeSending();
/** /**
* Add SNR data to received messages * Add SNR data to received messages
*/ */
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) = 0; virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) = 0;
virtual void setStandby() = 0; virtual void setStandby() = 0;
}; };

View File

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

View File

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

View File

@ -13,122 +13,122 @@
*/ */
class Router : protected concurrency::OSThread class Router : protected concurrency::OSThread
{ {
private: private:
/// Packets which have just arrived from the radio, ready to be processed by this service and possibly /// Packets which have just arrived from the radio, ready to be processed by this service and possibly
/// forwarded to the phone. /// forwarded to the phone.
PointerQueue<meshtastic_MeshPacket> fromRadioQueue; PointerQueue<meshtastic_MeshPacket> fromRadioQueue;
protected: protected:
RadioInterface *iface = NULL; RadioInterface *iface = NULL;
public: public:
/** /**
* Constructor * Constructor
* *
*/ */
Router(); Router();
/** /**
* Currently we only allow one interface, that may change in the future * Currently we only allow one interface, that may change in the future
*/ */
void addInterface(RadioInterface *_iface) { iface = _iface; } void addInterface(RadioInterface *_iface) { iface = _iface; }
/** /**
* do idle processing * do idle processing
* Mostly looking in our incoming rxPacket queue and calling handleReceived. * Mostly looking in our incoming rxPacket queue and calling handleReceived.
*/ */
virtual int32_t runOnce() override; 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. * 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. * 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) * 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); 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 */ /** Attempt to cancel a previously sent packet. Returns true if a packet was found we could cancel */
bool cancelSending(NodeNum from, PacketId id); bool cancelSending(NodeNum from, PacketId id);
/** Allocate and return a meshpacket which defaults as send to broadcast from the current node. /** 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 * The returned packet is guaranteed to have a unique packet ID already assigned
*/ */
meshtastic_MeshPacket *allocForSending(); meshtastic_MeshPacket *allocForSending();
/** Return Underlying interface's TX queue status */ /** Return Underlying interface's TX queue status */
meshtastic_QueueStatus getQueueStatus(); meshtastic_QueueStatus getQueueStatus();
/** /**
* @return our local nodenum */ * @return our local nodenum */
NodeNum getNodeNum(); NodeNum getNodeNum();
/** Wake up the router thread ASAP, because we just queued a message for it. /** 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' * 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(); void setReceivedMessage();
/** /**
* RadioInterface calls this to queue up packets that have been received from the radio. The router is now responsible for * RadioInterface calls this to queue up packets that have been received from the radio. The router is now responsible for
* freeing the packet * freeing the packet
*/ */
void enqueueReceivedMessage(meshtastic_MeshPacket *p); void enqueueReceivedMessage(meshtastic_MeshPacket *p);
/** /**
* Send a packet on a suitable interface. This routine will * Send a packet on a suitable interface. This routine will
* later free() the packet to pool. This routine is not allowed to stall. * later free() the packet to pool. This routine is not allowed to stall.
* If the txmit queue is full it might return an error * 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) * NOTE: This method will free the provided packet (even if we return an error code)
*/ */
virtual ErrorCode send(meshtastic_MeshPacket *p); virtual ErrorCode send(meshtastic_MeshPacket *p);
protected: protected:
friend class RoutingModule; friend class RoutingModule;
/** /**
* Should this incoming filter be dropped? * Should this incoming filter be dropped?
* *
* FIXME, move this into the new RoutingModule and do the filtering there using the regular module logic * 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. * Called immedately on receiption, before any further processing.
* @return true to abandon the packet * @return true to abandon the packet
*/ */
virtual bool shouldFilterReceived(const meshtastic_MeshPacket *p) { return false; } 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 * 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) * 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); virtual void sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Routing *c);
/** /**
* Send an ack or a nak packet back towards whoever sent idFrom * Send an ack or a nak packet back towards whoever sent idFrom
*/ */
void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex); void sendAckNak(meshtastic_Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
private: private:
/** /**
* Called from loop() * Called from loop()
* Handle any packet that is received by an interface on this node. * 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: 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 packet will never be called for messages sent/generated by this node.
* Note: this method will free the provided packet. * Note: this method will free the provided packet.
*/ */
void perhapsHandleReceived(meshtastic_MeshPacket *p); void perhapsHandleReceived(meshtastic_MeshPacket *p);
/** /**
* Called from perhapsHandleReceived() - allows subclass message delivery behavior. * Called from perhapsHandleReceived() - allows subclass message delivery behavior.
* Handle any packet that is received by an interface on this node. * 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: 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 packet will never be called for messages sent/generated by this node.
* Note: this method will free the provided packet. * Note: this method will free the provided packet.
*/ */
void handleReceived(meshtastic_MeshPacket *p, RxSource src = RX_SRC_RADIO); void handleReceived(meshtastic_MeshPacket *p, RxSource src = RX_SRC_RADIO);
/** Frees the provided packet, and generates a NAK indicating the speicifed error while sending */ /** Frees the provided packet, and generates a NAK indicating the speicifed error while sending */
void abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket *p); void abortSendAndNak(meshtastic_Routing_Error err, meshtastic_MeshPacket *p);
}; };
/** FIXME - move this into a mesh packet class /** FIXME - move this into a mesh packet class

View File

@ -7,25 +7,25 @@
*/ */
class RepeaterModule : public ProtobufModule<meshtastic_Routing> class RepeaterModule : public ProtobufModule<meshtastic_Routing>
{ {
public: public:
/** Constructor /** Constructor
* name is for debugging output * name is for debugging output
*/ */
RepeaterModule(); RepeaterModule();
protected: protected:
/** Called to handle a particular incoming message /** 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 @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; 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 /** 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. */ * so that subclasses can (optionally) send a response back to the original sender. */
virtual meshtastic_MeshPacket *allocReply() override; virtual meshtastic_MeshPacket *allocReply() override;
/// Override wantPacket to say we want to see all packets, not just those for our port number /// 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; } virtual bool wantPacket(const meshtastic_MeshPacket *p) override { return true; }
}; };
extern RepeaterModule *repeaterModule; extern RepeaterModule *repeaterModule;