mirror of
https://github.com/meshtastic/firmware.git
synced 2025-09-06 19:49:19 +00:00
Compare commits
4 Commits
b9dd902ee2
...
8cd2a2ae27
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8cd2a2ae27 | ||
![]() |
c5fad6cca1 | ||
![]() |
b8d7222423 | ||
![]() |
5b52fd06b8 |
@ -1,7 +1,14 @@
|
|||||||
#include "DisplayFormatters.h"
|
#include "DisplayFormatters.h"
|
||||||
|
|
||||||
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
|
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
||||||
|
bool usePreset)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// If use_preset is false, always return "Custom"
|
||||||
|
if (!usePreset) {
|
||||||
|
return "Custom";
|
||||||
|
}
|
||||||
|
|
||||||
switch (preset) {
|
switch (preset) {
|
||||||
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_TURBO:
|
||||||
return useShortName ? "ShortT" : "ShortTurbo";
|
return useShortName ? "ShortT" : "ShortTurbo";
|
||||||
|
@ -4,5 +4,6 @@
|
|||||||
class DisplayFormatters
|
class DisplayFormatters
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName);
|
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName,
|
||||||
|
bool usePreset);
|
||||||
};
|
};
|
||||||
|
@ -429,6 +429,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||||||
#define MESHTASTIC_EXCLUDE_WAYPOINT 1
|
#define MESHTASTIC_EXCLUDE_WAYPOINT 1
|
||||||
#define MESHTASTIC_EXCLUDE_INPUTBROKER 1
|
#define MESHTASTIC_EXCLUDE_INPUTBROKER 1
|
||||||
#define MESHTASTIC_EXCLUDE_SERIAL 1
|
#define MESHTASTIC_EXCLUDE_SERIAL 1
|
||||||
|
#define MESHTASTIC_EXCLUDE_SERIALPACKET 1
|
||||||
#define MESHTASTIC_EXCLUDE_POWERSTRESS 1
|
#define MESHTASTIC_EXCLUDE_POWERSTRESS 1
|
||||||
#define MESHTASTIC_EXCLUDE_ADMIN 1
|
#define MESHTASTIC_EXCLUDE_ADMIN 1
|
||||||
#endif
|
#endif
|
||||||
|
@ -843,9 +843,6 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
|||||||
setPowerPMU(true); // Power (PMU): on
|
setPowerPMU(true); // Power (PMU): on
|
||||||
writePinStandby(false); // Standby (pin): awake (not standby)
|
writePinStandby(false); // Standby (pin): awake (not standby)
|
||||||
setPowerUBLOX(true); // Standby (UBLOX): awake
|
setPowerUBLOX(true); // Standby (UBLOX): awake
|
||||||
#ifdef GNSS_AIROHA
|
|
||||||
lastFixStartMsec = 0;
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GPS_SOFTSLEEP:
|
case GPS_SOFTSLEEP:
|
||||||
@ -863,9 +860,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
|||||||
writePinStandby(true); // Standby (pin): asleep (not awake)
|
writePinStandby(true); // Standby (pin): asleep (not awake)
|
||||||
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
|
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
|
||||||
#ifdef GNSS_AIROHA
|
#ifdef GNSS_AIROHA
|
||||||
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
|
digitalWrite(PIN_GPS_EN, LOW);
|
||||||
digitalWrite(PIN_GPS_EN, LOW);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -877,9 +872,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
|||||||
writePinStandby(true); // Standby (pin): asleep
|
writePinStandby(true); // Standby (pin): asleep
|
||||||
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
|
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
|
||||||
#ifdef GNSS_AIROHA
|
#ifdef GNSS_AIROHA
|
||||||
if (config.position.gps_update_interval * 1000 >= GPS_FIX_HOLD_TIME * 2) {
|
digitalWrite(PIN_GPS_EN, LOW);
|
||||||
digitalWrite(PIN_GPS_EN, LOW);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1062,6 +1055,8 @@ void GPS::down()
|
|||||||
}
|
}
|
||||||
// If update interval long enough (or softsleep unsupported): hardsleep instead
|
// If update interval long enough (or softsleep unsupported): hardsleep instead
|
||||||
setPowerState(GPS_HARDSLEEP, sleepTime);
|
setPowerState(GPS_HARDSLEEP, sleepTime);
|
||||||
|
// Reset the fix quality to 0, since we're off.
|
||||||
|
fixQual = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1121,11 +1116,19 @@ int32_t GPS::runOnce()
|
|||||||
shouldPublish = true;
|
shouldPublish = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t prev_fixQual = fixQual;
|
||||||
bool gotLoc = lookForLocation();
|
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");
|
LOG_DEBUG("hasValidLocation RISING EDGE");
|
||||||
hasValidLocation = true;
|
hasValidLocation = true;
|
||||||
shouldPublish = true;
|
shouldPublish = true;
|
||||||
|
// Hold for 20secs after getting a lock to download ephemeris etc
|
||||||
|
fixHoldEnds = millis() + 20000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gotLoc && prev_fixQual == 0) { // just got a lock after turning back on.
|
||||||
|
fixHoldEnds = millis() + 20000;
|
||||||
|
shouldPublish = true; // Publish immediately, since next publish is at end of hold
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tooLong = scheduling.searchedTooLong();
|
bool tooLong = scheduling.searchedTooLong();
|
||||||
@ -1134,8 +1137,7 @@ 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
|
||||||
if ((gotLoc && gotTime) || tooLong) {
|
if ((gotLoc && gotTime) || tooLong) {
|
||||||
|
if (tooLong && !gotLoc) {
|
||||||
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");
|
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
||||||
@ -1143,9 +1145,15 @@ int32_t GPS::runOnce()
|
|||||||
p = meshtastic_Position_init_default;
|
p = meshtastic_Position_init_default;
|
||||||
hasValidLocation = false;
|
hasValidLocation = false;
|
||||||
}
|
}
|
||||||
|
if (millis() > fixHoldEnds) {
|
||||||
down();
|
shouldPublish = true; // publish our update at the end of the lock hold
|
||||||
shouldPublish = true; // publish our update for this just finished acquisition window
|
publishUpdate();
|
||||||
|
down();
|
||||||
|
#ifdef GPS_DEBUG
|
||||||
|
} else {
|
||||||
|
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If state has changed do a publish
|
// If state has changed do a publish
|
||||||
@ -1508,24 +1516,6 @@ static int32_t toDegInt(RawDegrees d)
|
|||||||
*/
|
*/
|
||||||
bool GPS::lookForTime()
|
bool GPS::lookForTime()
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef GNSS_AIROHA
|
|
||||||
uint8_t fix = reader.fixQuality();
|
|
||||||
if (fix >= 1 && fix <= 5) {
|
|
||||||
if (lastFixStartMsec > 0) {
|
|
||||||
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
clearBuffer();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lastFixStartMsec = millis();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
auto ti = reader.time;
|
auto ti = reader.time;
|
||||||
auto d = reader.date;
|
auto d = reader.date;
|
||||||
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
|
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
|
||||||
@ -1564,25 +1554,6 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
|
|||||||
*/
|
*/
|
||||||
bool GPS::lookForLocation()
|
bool GPS::lookForLocation()
|
||||||
{
|
{
|
||||||
#ifdef GNSS_AIROHA
|
|
||||||
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
|
|
||||||
uint8_t fix = reader.fixQuality();
|
|
||||||
if (fix >= 1 && fix <= 5) {
|
|
||||||
if (lastFixStartMsec > 0) {
|
|
||||||
if (Throttle::isWithinTimespanMs(lastFixStartMsec, GPS_FIX_HOLD_TIME)) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
clearBuffer();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
lastFixStartMsec = millis();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
// By default, TinyGPS++ does not parse GPGSA lines, which give us
|
// By default, TinyGPS++ does not parse GPGSA lines, which give us
|
||||||
// the 2D/3D fixType (see NMEAGPS.h)
|
// the 2D/3D fixType (see NMEAGPS.h)
|
||||||
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
|
// At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
|
||||||
|
@ -159,7 +159,7 @@ class GPS : private concurrency::OSThread
|
|||||||
uint8_t fixType = 0; // fix type from GPGSA
|
uint8_t fixType = 0; // fix type from GPGSA
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastFixStartMsec = 0;
|
uint32_t fixHoldEnds = 0;
|
||||||
uint32_t rx_gpio = 0;
|
uint32_t rx_gpio = 0;
|
||||||
uint32_t tx_gpio = 0;
|
uint32_t tx_gpio = 0;
|
||||||
|
|
||||||
|
@ -263,12 +263,6 @@ void drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
|
|||||||
display->drawString(x + 1, y, "USB");
|
display->drawString(x + 1, y, "USB");
|
||||||
}
|
}
|
||||||
|
|
||||||
// auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true);
|
|
||||||
|
|
||||||
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
|
|
||||||
// if (config.display.heading_bold)
|
|
||||||
// display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode) - 1, y, mode);
|
|
||||||
|
|
||||||
uint32_t currentMillis = millis();
|
uint32_t currentMillis = millis();
|
||||||
uint32_t seconds = currentMillis / 1000;
|
uint32_t seconds = currentMillis / 1000;
|
||||||
uint32_t minutes = seconds / 60;
|
uint32_t minutes = seconds / 60;
|
||||||
@ -398,7 +392,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x,
|
|||||||
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
display->drawString(nameX, getTextPositions(display)[line++], shortnameble);
|
||||||
|
|
||||||
// === Second Row: Radio Preset ===
|
// === Second Row: Radio Preset ===
|
||||||
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
|
auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
|
||||||
char regionradiopreset[25];
|
char regionradiopreset[25];
|
||||||
const char *region = myRegion ? myRegion->name : NULL;
|
const char *region = myRegion ? myRegion->name : NULL;
|
||||||
if (region != nullptr) {
|
if (region != nullptr) {
|
||||||
|
@ -368,7 +368,7 @@ const char *Channels::getName(size_t chIndex)
|
|||||||
// Per mesh.proto spec, if bandwidth is specified we must ignore modemPreset enum, we assume that in that case
|
// Per mesh.proto spec, if bandwidth is specified we must ignore modemPreset enum, we assume that in that case
|
||||||
// the app effed up and forgot to set channelSettings.name
|
// the app effed up and forgot to set channelSettings.name
|
||||||
if (config.lora.use_preset) {
|
if (config.lora.use_preset) {
|
||||||
channelName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
|
channelName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
|
||||||
} else {
|
} else {
|
||||||
channelName = "Custom";
|
channelName = "Custom";
|
||||||
}
|
}
|
||||||
@ -382,7 +382,8 @@ bool Channels::isDefaultChannel(ChannelIndex chIndex)
|
|||||||
const auto &ch = getByIndex(chIndex);
|
const auto &ch = getByIndex(chIndex);
|
||||||
if (ch.settings.psk.size == 1 && ch.settings.psk.bytes[0] == 1) {
|
if (ch.settings.psk.size == 1 && ch.settings.psk.bytes[0] == 1) {
|
||||||
const char *name = getName(chIndex);
|
const char *name = getName(chIndex);
|
||||||
const char *presetName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
|
const char *presetName =
|
||||||
|
DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset);
|
||||||
// Check if the name is the default derived from the modem preset
|
// Check if the name is the default derived from the modem preset
|
||||||
if (strcmp(name, presetName) == 0)
|
if (strcmp(name, presetName) == 0)
|
||||||
return true;
|
return true;
|
||||||
|
@ -589,7 +589,8 @@ void RadioInterface::applyModemConfig()
|
|||||||
|
|
||||||
// Check if we use the default frequency slot
|
// Check if we use the default frequency slot
|
||||||
RadioInterface::uses_default_frequency_slot =
|
RadioInterface::uses_default_frequency_slot =
|
||||||
channel_num == hash(DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false)) % numChannels;
|
channel_num ==
|
||||||
|
hash(DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false, config.lora.use_preset)) % numChannels;
|
||||||
|
|
||||||
// Old frequency selection formula
|
// Old frequency selection formula
|
||||||
// float freq = myRegion->freqStart + ((((myRegion->freqEnd - myRegion->freqStart) / numChannels) / 2) * channel_num);
|
// float freq = myRegion->freqStart + ((((myRegion->freqEnd - myRegion->freqStart) / numChannels) / 2) * channel_num);
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
#if !MESHTASTIC_EXCLUDE_MQTT
|
#if !MESHTASTIC_EXCLUDE_MQTT
|
||||||
#include "mqtt/MQTT.h"
|
#include "mqtt/MQTT.h"
|
||||||
#endif
|
#endif
|
||||||
|
#if !MESHTASTIC_EXCLUDE_SERIALPACKET
|
||||||
|
#include "modules/SerialPacketModule.h"
|
||||||
|
#endif
|
||||||
#include "Default.h"
|
#include "Default.h"
|
||||||
#if ARCH_PORTDUINO
|
#if ARCH_PORTDUINO
|
||||||
#include "platform/portduino/PortduinoGlue.h"
|
#include "platform/portduino/PortduinoGlue.h"
|
||||||
@ -292,6 +295,11 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
|||||||
#endif
|
#endif
|
||||||
packetPool.release(p_decoded);
|
packetPool.release(p_decoded);
|
||||||
}
|
}
|
||||||
|
#if !MESHTASTIC_EXCLUDE_SERIALPACKET
|
||||||
|
if (serialPacketEnabled){
|
||||||
|
serialPacketModule->onSend(*p);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if HAS_UDP_MULTICAST
|
#if HAS_UDP_MULTICAST
|
||||||
if (udpHandler && config.network.enabled_protocols & meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST) {
|
if (udpHandler && config.network.enabled_protocols & meshtastic_Config_NetworkConfig_ProtocolFlags_UDP_BROADCAST) {
|
||||||
|
@ -868,6 +868,8 @@ typedef struct _meshtastic_MeshPacket {
|
|||||||
meshtastic_MeshPacket_Delayed delayed;
|
meshtastic_MeshPacket_Delayed delayed;
|
||||||
/* Describes whether this packet passed via MQTT somewhere along the path it currently took. */
|
/* Describes whether this packet passed via MQTT somewhere along the path it currently took. */
|
||||||
bool via_mqtt;
|
bool via_mqtt;
|
||||||
|
/* Describes whether this packet entered the radio via the serial packet link. Not sent over links */
|
||||||
|
bool via_slink;
|
||||||
/* Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header.
|
/* Hop limit with which the original packet started. Sent via LoRa using three bits in the unencrypted header.
|
||||||
When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. */
|
When receiving a packet, the difference between hop_start and hop_limit gives how many hops it traveled. */
|
||||||
uint8_t hop_start;
|
uint8_t hop_start;
|
||||||
|
@ -98,6 +98,10 @@
|
|||||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_SERIAL
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_SERIAL
|
||||||
#include "modules/SerialModule.h"
|
#include "modules/SerialModule.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#if defined(ARCH_NRF52) && !MESHTASTIC_EXCLUDE_SERIALPACKET
|
||||||
|
#include "modules/SerialPacketModule.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
#if !MESHTASTIC_EXCLUDE_DROPZONE
|
||||||
@ -254,6 +258,9 @@ void setupModules()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
#if defined(ARCH_NRF52) && !MESHTASTIC_EXCLUDE_SERIALPACKET
|
||||||
|
new SerialPacketModule();
|
||||||
|
#endif
|
||||||
#ifdef ARCH_ESP32
|
#ifdef ARCH_ESP32
|
||||||
// Only run on an esp32 based device.
|
// Only run on an esp32 based device.
|
||||||
#if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO
|
#if defined(USE_SX1280) && !MESHTASTIC_EXCLUDE_AUDIO
|
||||||
|
312
src/modules/SerialPacketModule.cpp
Normal file
312
src/modules/SerialPacketModule.cpp
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
#include "SerialPacketModule.h"
|
||||||
|
#include "GeoCoord.h"
|
||||||
|
#include "MeshService.h"
|
||||||
|
#include "NMEAWPL.h"
|
||||||
|
#include "NodeDB.h"
|
||||||
|
#include "RTC.h"
|
||||||
|
#include "Router.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Throttle.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
This module implements a serial link for Meshtastic packets.
|
||||||
|
|
||||||
|
This has been tested with the WisMesh starter kit (19007 board+ RAK4630) + RS485 (RAK5802), CPU is the NRF52840
|
||||||
|
This combination uses UART2_RX(P0.15)/UART2_TX(P0.16) on the RAK4630.
|
||||||
|
Arduino Serial1 is used in this module for the StreamAPI serial driver
|
||||||
|
|
||||||
|
The RS485 serial link is used as an alternative path for packets (similar to mqtt)
|
||||||
|
1. Any packet that comes in via wireless is sent out via RS485 (if the packet is rebroadcast)
|
||||||
|
2. Any packet that comes in via RS485 serial link is sent out wireless
|
||||||
|
3. Any packet that came in via RS485 serial link is never rebroadcast back to the serial link
|
||||||
|
|
||||||
|
A Meshtastic packet sent over the serial link is wrapped in a header with magic numbers and a CRC.
|
||||||
|
Incoming packets that fail magic number match or CRC check are discarded.
|
||||||
|
This has been tested with RS485 links in excess of 1 km @4800 baud.
|
||||||
|
Complete testing results is at: https://github.com/rbreesems/flamingo
|
||||||
|
|
||||||
|
This module does NOT have any module config data yet, so the serialPacketEnabled byte
|
||||||
|
below is used for enable/disable, this module is DISABLED by default.
|
||||||
|
Also, the module currently uses the Baud rate setting from the serial module.
|
||||||
|
|
||||||
|
You need to be careful of conflicts between this module and the GPS, Serial modules.
|
||||||
|
The GPS module for the NRF52840 by default uses Serial1. So, if there is a GPS module, but
|
||||||
|
it does not use the serial uart pins required by this RS485 interface, you could change this code
|
||||||
|
to use Serial2.
|
||||||
|
|
||||||
|
However, the Serial module uses the Serial2 StreamAPI serial driver, so, if you change this code to use Serial2, you
|
||||||
|
would need to disable the serial module (or change the serial module to use Serial1).
|
||||||
|
|
||||||
|
If you use this module and the Serial module, be careful that the Serial module UART pin configuration does
|
||||||
|
not clash with the pin configuration assumed in this module.
|
||||||
|
|
||||||
|
Assuming that you have two setups of (19007 board+ 4630) + RS485 (RAK5802) connected together
|
||||||
|
(RS485 A wire to A wire, B wire to B wire), the easiest way to test is by doing the following:
|
||||||
|
|
||||||
|
a. Have both Radios configured with Lora Transmit enabled on both.
|
||||||
|
b. Connect your phone to Radio1 , verify that a DM to Radio2 is received/acked.
|
||||||
|
c. Disable Lora transmit on Radio1.
|
||||||
|
d. Send a DM to Radio2 - this should be received/acked and as the packet will go via the RS485 link. If this fails, then
|
||||||
|
something is wrong with your serial link -either a wire connection, or a failure in the RS485 module(s)
|
||||||
|
e. Assuming success from step (d), disconnect one of the wires
|
||||||
|
f. Send a DM to Radio2 - this should time out with max transmissions reached as there is no transmit path for the packet.
|
||||||
|
g. Reconnect the wire, and verify that sending another DM to Radio2 works.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(ARCH_NRF52)
|
||||||
|
|
||||||
|
#define TIMEOUT 250
|
||||||
|
#define BAUD 19200
|
||||||
|
|
||||||
|
// defined as UART2 TX/RX on 4630
|
||||||
|
// This is what is connected on WisMesh starter kit (19007 board+ 4630) + RS485 (RAK5802)
|
||||||
|
#define RS485_TXPIN 16
|
||||||
|
#define RS485_RXPIN 15
|
||||||
|
|
||||||
|
#define PACKET_FLAGS_ENCRYPTED_MASK 0x20
|
||||||
|
|
||||||
|
SerialPacketModule *serialPacketModule;
|
||||||
|
|
||||||
|
meshtastic_serialPacket outPacket;
|
||||||
|
meshtastic_serialPacket inPacket;
|
||||||
|
#ifdef SLINK_DEBUG
|
||||||
|
char tmpbuf[250]; // for debug only
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// since we do not have module config data for this yet, need to put enable byte here
|
||||||
|
// DISABLED BY DEFAULT
|
||||||
|
#define SERIAL_PACKET_ENABLED 0
|
||||||
|
bool serialPacketEnabled = SERIAL_PACKET_ENABLED;
|
||||||
|
|
||||||
|
SerialPacketModule::SerialPacketModule() : StreamAPI(&Serial1), concurrency::OSThread("SerialPacket") {}
|
||||||
|
|
||||||
|
#define headerByte1 0xaa
|
||||||
|
#define headerByte2 0x55
|
||||||
|
|
||||||
|
|
||||||
|
size_t serialPacketPayloadSize;
|
||||||
|
|
||||||
|
uint32_t computeCrc32(const uint8_t* buf, uint16_t len) {
|
||||||
|
uint32_t crc = 0xFFFFFFFF; // Initial value
|
||||||
|
const uint32_t poly = 0xEDB88320; // CRC-32 polynomial
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < len; i++) {
|
||||||
|
crc ^= (uint8_t)buf[i]; // XOR with the current byte
|
||||||
|
for (int j = 7; j >= 0; j--) { // Perform 8 bitwise operations
|
||||||
|
if (crc & 0x80000000) { // Check if the MSB is set
|
||||||
|
crc = (crc << 1) ^ poly; // Shift and XOR with polynomial
|
||||||
|
} else {
|
||||||
|
crc <<= 1; // Shift if MSB is not set
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ~crc; // Return the final CRC value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void meshPacketToSerialPacket (const meshtastic_MeshPacket &mp, meshtastic_serialPacket *sp) {
|
||||||
|
sp->header.hbyte1 = headerByte1;
|
||||||
|
sp->header.hbyte2 = headerByte2;
|
||||||
|
sp->header.crc = 0;
|
||||||
|
|
||||||
|
if (mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag ){
|
||||||
|
sp->header.size = sizeof(SerialPacketHeader) + mp.encrypted.size;
|
||||||
|
memcpy(sp->payload, mp.encrypted.bytes, mp.encrypted.size);
|
||||||
|
} else {
|
||||||
|
sp->header.size = sizeof(SerialPacketHeader) + mp.decoded.payload.size;
|
||||||
|
memcpy(sp->payload, mp.decoded.payload.bytes, mp.decoded.payload.size);
|
||||||
|
}
|
||||||
|
sp->header.from = mp.from;
|
||||||
|
sp->header.to = mp.to;
|
||||||
|
sp->header.id = mp.id;
|
||||||
|
sp->header.channel = mp.channel;
|
||||||
|
|
||||||
|
sp->header.hop_limit = mp.hop_limit & PACKET_FLAGS_HOP_LIMIT_MASK;
|
||||||
|
sp->header.hop_start = mp.hop_start & PACKET_FLAGS_HOP_START_MASK;
|
||||||
|
sp->header.flags =
|
||||||
|
0x00 |
|
||||||
|
(mp.want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0) |
|
||||||
|
(mp.via_mqtt ? PACKET_FLAGS_VIA_MQTT_MASK : 0) |
|
||||||
|
((mp.which_payload_variant == meshtastic_MeshPacket_encrypted_tag) ? PACKET_FLAGS_ENCRYPTED_MASK : 0);
|
||||||
|
|
||||||
|
sp->header.crc = computeCrc32((const uint8_t *)sp, sp->header.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertSerialPacketToMesh(meshtastic_serialPacket *sp) {
|
||||||
|
|
||||||
|
UniquePacketPoolPacket p = packetPool.allocUniqueZeroed();
|
||||||
|
|
||||||
|
p->from = sp->header.from;
|
||||||
|
p->to = sp->header.to;
|
||||||
|
p->id = sp->header.id;
|
||||||
|
p->channel = sp->header.channel;
|
||||||
|
p->hop_limit = sp->header.hop_limit;
|
||||||
|
p->hop_start = sp->header.hop_start;
|
||||||
|
p->want_ack = !!(sp->header.flags & PACKET_FLAGS_WANT_ACK_MASK);
|
||||||
|
p->via_slink = true;
|
||||||
|
p->via_mqtt = !!(sp->header.flags & PACKET_FLAGS_VIA_MQTT_MASK);
|
||||||
|
uint16_t payloadLen = sp->header.size - sizeof(SerialPacketHeader);
|
||||||
|
if (!!(sp->header.flags & PACKET_FLAGS_ENCRYPTED_MASK)) {
|
||||||
|
p->which_payload_variant = meshtastic_MeshPacket_encrypted_tag;
|
||||||
|
memcpy(p->encrypted.bytes, sp->payload, payloadLen);
|
||||||
|
p->encrypted.size = payloadLen;
|
||||||
|
} else {
|
||||||
|
p->which_payload_variant = meshtastic_MeshPacket_decoded_tag;
|
||||||
|
memcpy(p->decoded.payload.bytes, sp->payload, payloadLen);
|
||||||
|
p->decoded.payload.size = payloadLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG ("SerialPacketModule:: RX from=0x%0x, to=0x%0x, packet_id=0x%0x",
|
||||||
|
p->from, p->to, p->id);
|
||||||
|
|
||||||
|
#ifdef SLINK_DEBUG
|
||||||
|
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||||
|
memcpy(tmpbuf, p->decoded.payload.bytes, p->decoded.payload.size);
|
||||||
|
tmpbuf[p->decoded.payload.size+1]=0;
|
||||||
|
LOG_DEBUG("SerialPacketModule:: RX packet of %d bytes, msg: %s", sp->header.size, tmpbuf);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
router->enqueueReceivedMessage(p.release());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// check if this recieved serial packet is valid
|
||||||
|
bool checkIfValidSerialPacket(meshtastic_serialPacket *sp) {
|
||||||
|
|
||||||
|
if (sp->header.hbyte1 != headerByte1 || sp->header.hbyte2 != headerByte2 ) {
|
||||||
|
LOG_DEBUG("SerialPacketModule:: valid packet check fail, header bytes");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sp->header.size == 0 || sp->header.size > sizeof(meshtastic_serialPacket)) {
|
||||||
|
LOG_DEBUG("SerialPacketModule:: valid packet check fail, invalid size");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t received_crc = sp->header.crc;
|
||||||
|
sp->header.crc = 0; // need to set to zero for computing CRC
|
||||||
|
if (computeCrc32((const uint8_t *)sp, sp->header.size) != received_crc) {
|
||||||
|
LOG_DEBUG("SerialPacketModule:: valid packet check fail, invalid crc");
|
||||||
|
sp->header.crc = received_crc; // restore
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
sp->header.crc = received_crc; // restore
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int32_t SerialPacketModule::runOnce()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!serialPacketEnabled)
|
||||||
|
return disable();
|
||||||
|
|
||||||
|
if (firstTime) {
|
||||||
|
// Interface with the serial peripheral from in here.
|
||||||
|
LOG_INFO("SerialPacketModule:: Init serial interface");
|
||||||
|
|
||||||
|
uint32_t baud = getBaudRate();
|
||||||
|
Serial1.setPins(RS485_RXPIN, RS485_TXPIN);
|
||||||
|
Serial1.begin(baud, SERIAL_8N1);
|
||||||
|
Serial1.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT);
|
||||||
|
firstTime = 0;
|
||||||
|
} else {
|
||||||
|
//stream.cpp/readBytes arduinofruit library
|
||||||
|
while (Serial1.available()) {
|
||||||
|
serialPacketPayloadSize = Serial1.readBytes((uint8_t *) &inPacket, sizeof(meshtastic_serialPacket));
|
||||||
|
if (!checkIfValidSerialPacket(&inPacket)) {
|
||||||
|
LOG_DEBUG("SerialPacketModule:: failed CRC on RX");
|
||||||
|
} else {
|
||||||
|
// checks passed, pass this packet on
|
||||||
|
LOG_DEBUG("SerialPacketModule:: RX Insert packet to mesh");
|
||||||
|
insertSerialPacketToMesh(&inPacket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (50);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the serial connection is established.
|
||||||
|
*
|
||||||
|
* @return true if the serial connection is established, false otherwise.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
bool SerialPacketModule::checkIsConnected()
|
||||||
|
{
|
||||||
|
// we are not going to be able to determine if connected to another radio or not
|
||||||
|
// at the end of the serial, just always return true
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Called from Router.cpp/Router::send
|
||||||
|
Send this over the serial link
|
||||||
|
*/
|
||||||
|
void SerialPacketModule::onSend(const meshtastic_MeshPacket &mp) {
|
||||||
|
|
||||||
|
if (mp.via_slink) {
|
||||||
|
LOG_DEBUG("SerialPacketModule:: Onsend TX - ignoring packet that came from slink");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("SerialPacketModule:: Onsend TX from=0x%0x, to=0x%0x, packet_id=0x%0x",
|
||||||
|
mp.from, mp.to, mp.id);
|
||||||
|
meshPacketToSerialPacket(mp, &outPacket);
|
||||||
|
// debug check
|
||||||
|
if (!checkIfValidSerialPacket(&outPacket)) {
|
||||||
|
LOG_DEBUG("SerialPacketModule:: failed CRC on TX");
|
||||||
|
} else {
|
||||||
|
if (Serial1.availableForWrite()) {
|
||||||
|
LOG_DEBUG("SerialPacketModule:: onSend TX packet of %d bytes", outPacket.header.size);
|
||||||
|
Serial1.write((uint8_t *) &outPacket, outPacket.header.size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the baud rate of the serial module from the module configuration.
|
||||||
|
*
|
||||||
|
* @return uint32_t The baud rate of the serial module.
|
||||||
|
*/
|
||||||
|
uint32_t SerialPacketModule::getBaudRate()
|
||||||
|
{
|
||||||
|
if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_110) {
|
||||||
|
return 110;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_300) {
|
||||||
|
return 300;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_600) {
|
||||||
|
return 600;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_1200) {
|
||||||
|
return 1200;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_2400) {
|
||||||
|
return 2400;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_4800) {
|
||||||
|
return 4800;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_9600) {
|
||||||
|
return 9600;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_19200) {
|
||||||
|
return 19200;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_38400) {
|
||||||
|
return 38400;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_57600) {
|
||||||
|
return 57600;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_115200) {
|
||||||
|
return 115200;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_230400) {
|
||||||
|
return 230400;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_460800) {
|
||||||
|
return 460800;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_576000) {
|
||||||
|
return 576000;
|
||||||
|
} else if (moduleConfig.serial.baud == meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600) {
|
||||||
|
return 921600;
|
||||||
|
}
|
||||||
|
return BAUD;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
69
src/modules/SerialPacketModule.h
Normal file
69
src/modules/SerialPacketModule.h
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "MeshModule.h"
|
||||||
|
#include "Router.h"
|
||||||
|
#include "SinglePortModule.h"
|
||||||
|
#include "concurrency/OSThread.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#if defined(ARCH_NRF52)
|
||||||
|
|
||||||
|
extern bool serialPacketEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Header for wrapper around Meshtastic packet data sent over the serial link
|
||||||
|
**/
|
||||||
|
typedef struct _SerialPacketHeader{
|
||||||
|
uint8_t hbyte1; //magic number for early rejection
|
||||||
|
uint8_t hbyte2; //magic number for early rejection
|
||||||
|
uint16_t size; //this is size of header + payload length
|
||||||
|
uint32_t crc;
|
||||||
|
NodeNum to, from; // can be 1 byte or four bytes
|
||||||
|
PacketId id; // can be 1 byte or 4 bytes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This flag bytes holds 3 flags from original Meshtastic flags - want_ack, via_mqtt, is_encrypted
|
||||||
|
**/
|
||||||
|
uint8_t flags;
|
||||||
|
|
||||||
|
/** The channel hash - used as a hint for the decoder to limit which channels we consider */
|
||||||
|
uint8_t channel;
|
||||||
|
uint8_t hop_limit;
|
||||||
|
uint8_t hop_start;
|
||||||
|
|
||||||
|
|
||||||
|
} SerialPacketHeader;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct _meshtastic_serialPacket{
|
||||||
|
SerialPacketHeader header;
|
||||||
|
uint8_t payload[256]; // 256 is max payload size
|
||||||
|
} meshtastic_serialPacket;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SerialPacketModule : public StreamAPI, private concurrency::OSThread
|
||||||
|
{
|
||||||
|
bool firstTime = 1;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SerialPacketModule();
|
||||||
|
void onSend(const meshtastic_MeshPacket &mp);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual int32_t runOnce() override;
|
||||||
|
|
||||||
|
/// Check the current underlying physical link to see if the client is currently connected
|
||||||
|
virtual bool checkIsConnected() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t getBaudRate();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SerialPacketModule *serialPacketModule;
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
@ -124,8 +124,7 @@ extern "C" {
|
|||||||
#define GPS_RTC_INT (0 + 15) // P0.15, normal is LOW, wake by HIGH
|
#define GPS_RTC_INT (0 + 15) // P0.15, normal is LOW, wake by HIGH
|
||||||
#define GPS_RESETB_OUT (32 + 14) // P1.14, always input pull_up
|
#define GPS_RESETB_OUT (32 + 14) // P1.14, always input pull_up
|
||||||
|
|
||||||
#define GPS_FIX_HOLD_TIME 15000 // ms
|
#define BATTERY_PIN 2 // P0.02/AIN0, BAT_ADC
|
||||||
#define BATTERY_PIN 2 // P0.02/AIN0, BAT_ADC
|
|
||||||
#define BATTERY_IMMUTABLE
|
#define BATTERY_IMMUTABLE
|
||||||
#define ADC_MULTIPLIER (2.0F)
|
#define ADC_MULTIPLIER (2.0F)
|
||||||
// P0.04/AIN2 is VCC_ADC, P0.05/AIN3 is CHARGER_DET, P1.03 is CHARGE_STA, P1.04 is CHARGE_DONE
|
// P0.04/AIN2 is VCC_ADC, P0.05/AIN3 is CHARGER_DET, P1.03 is CHARGE_STA, P1.04 is CHARGE_DONE
|
||||||
|
@ -123,7 +123,6 @@ extern "C" {
|
|||||||
#define GPS_RESETB_OUT (32 + 14) // P1.14, awlays input pull_up
|
#define GPS_RESETB_OUT (32 + 14) // P1.14, awlays input pull_up
|
||||||
|
|
||||||
// #define GPS_THREAD_INTERVAL 50
|
// #define GPS_THREAD_INTERVAL 50
|
||||||
#define GPS_FIX_HOLD_TIME 15000 // ms
|
|
||||||
|
|
||||||
#define BATTERY_PIN 2
|
#define BATTERY_PIN 2
|
||||||
// #define ADC_CHANNEL ADC1_GPIO2_CHANNEL
|
// #define ADC_CHANNEL ADC1_GPIO2_CHANNEL
|
||||||
|
Loading…
Reference in New Issue
Block a user