diff --git a/.github/workflows/trunk-check.yml b/.github/workflows/trunk-check.yml
new file mode 100644
index 000000000..e35b91cb9
--- /dev/null
+++ b/.github/workflows/trunk-check.yml
@@ -0,0 +1,22 @@
+name: Pull Request
+on: [pull_request]
+concurrency:
+ group: ${{ github.head_ref || github.run_id }}
+ cancel-in-progress: true
+
+permissions: read-all
+
+jobs:
+ trunk_check:
+ name: Trunk Check Runner
+ runs-on: ubuntu-latest
+ permissions:
+ checks: write # For trunk to post annotations
+ contents: read # For repo checkout
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Trunk Check
+ uses: trunk-io/trunk-action@v1
diff --git a/.trunk/.gitignore b/.trunk/.gitignore
index 8130ba6d1..695b51906 100644
--- a/.trunk/.gitignore
+++ b/.trunk/.gitignore
@@ -5,4 +5,4 @@
plugins
user_trunk.yaml
user.yaml
-shims
+tools
diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml
index 8ab9166fb..7e81046a7 100644
--- a/.trunk/trunk.yaml
+++ b/.trunk/trunk.yaml
@@ -1,37 +1,42 @@
version: 0.1
cli:
- version: 1.10.0
+ version: 1.13.0
plugins:
sources:
- id: trunk
- ref: v0.0.17
+ ref: v1.1.1
uri: https://github.com/trunk-io/plugins
lint:
enabled:
- - taplo@0.7.0
- - ruff@0.0.265
+ - bandit@1.7.5
+ - checkov@2.4.1
+ - terrascan@1.18.3
+ - trivy@0.44.1
+ - trufflehog@3.48.0
+ - taplo@0.8.1
+ - ruff@0.0.284
- yamllint@1.32.0
- isort@5.12.0
- - markdownlint@0.34.0
+ - markdownlint@0.35.0
- oxipng@8.0.0
- svgo@3.0.2
- - actionlint@1.6.24
- - flake8@6.0.0
+ - actionlint@1.6.25
+ - flake8@6.1.0
- hadolint@2.12.0
- - shfmt@3.5.0
+ - shfmt@3.6.0
- shellcheck@0.9.0
- - black@23.3.0
+ - black@23.7.0
- git-diff-check
- - gitleaks@8.16.3
- - clang-format@14.0.0
- - prettier@2.8.8
+ - gitleaks@8.17.0
+ - clang-format@16.0.3
+ - prettier@3.0.2
disabled:
- - taplo@0.7.0
+ - taplo@0.8.1
- shellcheck@0.9.0
- - shfmt@3.5.0
+ - shfmt@3.6.0
- oxipng@8.0.0
- actionlint@1.6.22
- - markdownlint@0.34.0
+ - markdownlint@0.35.0
- hadolint@2.12.0
- svgo@3.0.2
runtimes:
diff --git a/arch/esp32/esp32.ini b/arch/esp32/esp32.ini
index 5853baf76..aa31db13e 100644
--- a/arch/esp32/esp32.ini
+++ b/arch/esp32/esp32.ini
@@ -1,7 +1,7 @@
; Common settings for ESP targes, mixin with extends = esp32_base
[esp32_base]
extends = arduino_base
-platform = platformio/espressif32@^6.3.2
+platform = platformio/espressif32@6.3.2 # This is a temporary fix to the S3-based devices bluetooth issues until we can determine what within ESP-IDF changed and can develop a suitable patch.
build_src_filter =
${arduino_base.build_src_filter} - - - -
@@ -38,7 +38,7 @@ lib_deps =
${networking_base.lib_deps}
${environmental_base.lib_deps}
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
- h2zero/NimBLE-Arduino@^1.4.0
+ h2zero/NimBLE-Arduino@^1.4.1
jgromes/RadioLib@^6.1.0
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
@@ -57,4 +57,4 @@ lib_ignore =
; customize the partition table
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
-board_build.partitions = partition-table.csv
\ No newline at end of file
+board_build.partitions = partition-table.csv
diff --git a/boards/t-deck.json b/boards/t-deck.json
index 9d74834e9..d62ec48e6 100644
--- a/boards/t-deck.json
+++ b/boards/t-deck.json
@@ -1,7 +1,8 @@
{
"build": {
"arduino": {
- "ldscript": "esp32s3_out.ld"
+ "ldscript": "esp32s3_out.ld",
+ "memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
@@ -13,7 +14,7 @@
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
- "flash_mode": "dio",
+ "flash_mode": "qio",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "t-deck"
diff --git a/boards/t-watch-s3.json b/boards/t-watch-s3.json
index e86917df6..080389f39 100644
--- a/boards/t-watch-s3.json
+++ b/boards/t-watch-s3.json
@@ -1,7 +1,8 @@
{
"build": {
"arduino": {
- "ldscript": "esp32s3_out.ld"
+ "ldscript": "esp32s3_out.ld",
+ "memory_type": "qio_opi"
},
"core": "esp32",
"extra_flags": [
@@ -14,7 +15,7 @@
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
- "flash_mode": "dio",
+ "flash_mode": "qio",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "t-watch-s3"
@@ -31,8 +32,9 @@
"maximum_size": 8388608,
"require_upload_port": true,
"use_1200bps_touch": true,
- "wait_for_upload_port": true
+ "wait_for_upload_port": true,
+ "speed": 921600
},
- "url": "http://www.lilygo.cn/",
+ "url": "https://www.lilygo.cc/en-pl/products/t-watch-s3",
"vendor": "LilyGo"
}
diff --git a/protobufs b/protobufs
index dc28ae3d1..fb28d5935 160000
--- a/protobufs
+++ b/protobufs
@@ -1 +1 @@
-Subproject commit dc28ae3d128b76707c0b87b6f3b2514c7f8514bd
+Subproject commit fb28d593526467977cf353959a66e11373928282
diff --git a/src/AmbientLightingThread.h b/src/AmbientLightingThread.h
new file mode 100644
index 000000000..0dd0fdf4a
--- /dev/null
+++ b/src/AmbientLightingThread.h
@@ -0,0 +1,75 @@
+#include "configuration.h"
+
+#ifdef HAS_NCP5623
+#include
+NCP5623 rgb;
+#endif
+
+namespace concurrency
+{
+class AmbientLightingThread : public concurrency::OSThread
+{
+ public:
+ AmbientLightingThread(ScanI2C::DeviceType type) : OSThread("AmbientLightingThread")
+ {
+ // Uncomment to test module
+ // moduleConfig.ambient_lighting.led_state = true;
+ // moduleConfig.ambient_lighting.current = 10;
+ // // Default to a color based on our node number
+ // moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
+ // moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
+ // moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
+
+#ifdef HAS_NCP5623
+ _type = type;
+ if (_type == ScanI2C::DeviceType::NONE) {
+ LOG_DEBUG("AmbientLightingThread disabling due to no RGB leds found on I2C bus\n");
+ disable();
+ return;
+ }
+ if (!moduleConfig.ambient_lighting.led_state) {
+ LOG_DEBUG("AmbientLightingThread disabling due to moduleConfig.ambient_lighting.led_state OFF\n");
+ disable();
+ return;
+ }
+ LOG_DEBUG("AmbientLightingThread initializing\n");
+ if (_type == ScanI2C::NCP5623) {
+ rgb.begin();
+ setLighting();
+ }
+#endif
+ }
+
+ protected:
+ int32_t runOnce() override
+ {
+#ifdef HAS_NCP5623
+ if (_type == ScanI2C::NCP5623 && moduleConfig.ambient_lighting.led_state) {
+ setLighting();
+ return 30000; // 30 seconds to reset from any animations that may have been running from Ext. Notification
+ } else {
+ return disable();
+ }
+#else
+ return disable();
+#endif
+ }
+
+ private:
+ ScanI2C::DeviceType _type = ScanI2C::DeviceType::NONE;
+
+ void setLighting()
+ {
+#ifdef HAS_NCP5623
+ rgb.setCurrent(moduleConfig.ambient_lighting.current);
+ rgb.setRed(moduleConfig.ambient_lighting.red);
+ rgb.setGreen(moduleConfig.ambient_lighting.green);
+ rgb.setBlue(moduleConfig.ambient_lighting.blue);
+ LOG_DEBUG("Initializing Ambient lighting w/ current=%d, red=%d, green=%d, blue=%d\n",
+ moduleConfig.ambient_lighting.current, moduleConfig.ambient_lighting.red, moduleConfig.ambient_lighting.green,
+ moduleConfig.ambient_lighting.blue);
+#endif
+ }
+};
+
+} // namespace concurrency
\ No newline at end of file
diff --git a/src/ButtonThread.h b/src/ButtonThread.h
index 255ab5162..a8a89e82e 100644
--- a/src/ButtonThread.h
+++ b/src/ButtonThread.h
@@ -164,15 +164,17 @@ class ButtonThread : public concurrency::OSThread
static void userButtonMultiPressed()
{
-#if defined(GPS_POWER_TOGGLE)
- if (config.position.gps_enabled) {
- LOG_DEBUG("Flag set to false for gps power\n");
- } else {
- LOG_DEBUG("Flag set to true to restore power\n");
+ if (!config.device.disable_triple_click && (gps != nullptr)) {
+ config.position.gps_enabled = !(config.position.gps_enabled);
+ if (config.position.gps_enabled) {
+ LOG_DEBUG("Flag set to true to restore power\n");
+ gps->enable();
+
+ } else {
+ LOG_DEBUG("Flag set to false for gps power\n");
+ gps->disable();
+ }
}
- config.position.gps_enabled = !(config.position.gps_enabled);
- doGPSpowersave(config.position.gps_enabled);
-#endif
}
static void userButtonPressedLongStart()
diff --git a/src/Power.cpp b/src/Power.cpp
index aad5b26ae..460f598b3 100644
--- a/src/Power.cpp
+++ b/src/Power.cpp
@@ -16,8 +16,8 @@
#include "buzz/buzz.h"
#include "configuration.h"
#include "main.h"
+#include "meshUtils.h"
#include "sleep.h"
-#include "utils.h"
#ifdef DEBUG_HEAP_MQTT
#include "mqtt/MQTT.h"
@@ -221,10 +221,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
/**
* return true if there is a battery installed in this unit
*/
- virtual bool isBatteryConnect() override
- {
- return getBatteryPercent() != -1;
- }
+ virtual bool isBatteryConnect() override { return getBatteryPercent() != -1; }
/// If we see a battery voltage higher than physics allows - assume charger is pumping
/// in power
@@ -245,10 +242,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
/// Assume charging if we have a battery and external power is connected.
/// we can't be smart enough to say 'full'?
- virtual bool isCharging() override
- {
- return isBatteryConnect() && isVbusIn();
- }
+ virtual bool isCharging() override { return isBatteryConnect() && isVbusIn(); }
private:
/// If we see a battery voltage higher than physics allows - assume charger is pumping
diff --git a/src/PowerFSM.cpp b/src/PowerFSM.cpp
index 827720d1f..eceb545fe 100644
--- a/src/PowerFSM.cpp
+++ b/src/PowerFSM.cpp
@@ -8,7 +8,6 @@
* actions to be taken upon entering or exiting each state.
*/
#include "PowerFSM.h"
-#include "GPS.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "configuration.h"
@@ -137,9 +136,6 @@ static void lsIdle()
static void lsExit()
{
LOG_INFO("Exit state: LS\n");
- // setGPSPower(true); // restore GPS power
- if (gps)
- gps->forceWake(true);
}
static void nbEnter()
@@ -158,6 +154,9 @@ static void darkEnter()
{
setBluetoothEnable(true);
screen->setOn(false);
+#ifdef KB_POWERON
+ digitalWrite(KB_POWERON, LOW);
+#endif
}
static void serialEnter()
@@ -185,6 +184,9 @@ static void powerEnter()
} else {
screen->setOn(true);
setBluetoothEnable(true);
+#ifdef KB_POWERON
+ digitalWrite(KB_POWERON, HIGH);
+#endif
// within enter() the function getState() returns the state we came from
if (strcmp(powerFSM.getState()->name, "BOOT") != 0 && strcmp(powerFSM.getState()->name, "POWER") != 0 &&
strcmp(powerFSM.getState()->name, "DARK") != 0) {
@@ -215,6 +217,9 @@ static void onEnter()
LOG_DEBUG("Enter state: ON\n");
screen->setOn(true);
setBluetoothEnable(true);
+#ifdef KB_POWERON
+ digitalWrite(KB_POWERON, HIGH);
+#endif
}
static void onIdle()
@@ -365,10 +370,6 @@ void PowerFSM_setup()
getConfiguredOrDefaultMs(config.power.wait_bluetooth_secs, default_wait_bluetooth_secs),
NULL, "Bluetooth timeout");
}
-
- if (config.power.sds_secs != UINT32_MAX)
- powerFSM.add_timed_transition(lowPowerState, &stateSDS, getConfiguredOrDefaultMs(config.power.sds_secs), NULL,
- "mesh timeout");
#endif
powerFSM.run_machine(); // run one iteration of the state machine, so we run our on enter tasks for the initial DARK state
diff --git a/src/RedirectablePrint.cpp b/src/RedirectablePrint.cpp
index 0e8e1c798..2d73c7c9b 100644
--- a/src/RedirectablePrint.cpp
+++ b/src/RedirectablePrint.cpp
@@ -18,6 +18,12 @@ NoopPrint noopPrint;
#if HAS_WIFI || HAS_ETHERNET
extern Syslog syslog;
#endif
+void RedirectablePrint::rpInit()
+{
+#ifdef HAS_FREE_RTOS
+ inDebugPrint = xSemaphoreCreateMutexStatic(&this->_MutexStorageSpace);
+#endif
+}
void RedirectablePrint::setDestination(Print *_dest)
{
@@ -66,9 +72,12 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
return 0;
}
size_t r = 0;
-
+#ifdef HAS_FREE_RTOS
+ if (inDebugPrint != nullptr && xSemaphoreTake(inDebugPrint, portMAX_DELAY) == pdTRUE) {
+#else
if (!inDebugPrint) {
inDebugPrint = true;
+#endif
va_list arg;
va_start(arg, format);
@@ -141,7 +150,11 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
va_end(arg);
isContinuationMessage = !hasNewline;
+#ifdef HAS_FREE_RTOS
+ xSemaphoreGive(inDebugPrint);
+#else
inDebugPrint = false;
+#endif
}
return r;
diff --git a/src/RedirectablePrint.h b/src/RedirectablePrint.h
index 560021972..31cc1b6ef 100644
--- a/src/RedirectablePrint.h
+++ b/src/RedirectablePrint.h
@@ -1,5 +1,6 @@
#pragma once
+#include "../freertosinc.h"
#include
#include
#include
@@ -16,14 +17,19 @@ class RedirectablePrint : public Print
/// Used to allow multiple logDebug messages to appear on a single log line
bool isContinuationMessage = false;
+#ifdef HAS_FREE_RTOS
+ SemaphoreHandle_t inDebugPrint = nullptr;
+ StaticSemaphore_t _MutexStorageSpace;
+#else
volatile bool inDebugPrint = false;
-
+#endif
public:
explicit RedirectablePrint(Print *_dest) : dest(_dest) {}
/**
* Set a new destination
*/
+ void rpInit();
void setDestination(Print *dest);
virtual size_t write(uint8_t c);
@@ -54,4 +60,4 @@ class NoopPrint : public Print
/**
* A printer that doesn't go anywhere
*/
-extern NoopPrint noopPrint;
+extern NoopPrint noopPrint;
\ No newline at end of file
diff --git a/src/SerialConsole.cpp b/src/SerialConsole.cpp
index e827dcf3b..ed217c3ed 100644
--- a/src/SerialConsole.cpp
+++ b/src/SerialConsole.cpp
@@ -12,6 +12,7 @@ SerialConsole *console;
void consoleInit()
{
new SerialConsole(); // Must be dynamically allocated because we are now inheriting from thread
+ DEBUG_PORT.rpInit(); // Simply sets up semaphore
}
void consolePrintf(const char *format, ...)
diff --git a/src/concurrency/OSThread.h b/src/concurrency/OSThread.h
index aa8e3e2d8..7957ee952 100644
--- a/src/concurrency/OSThread.h
+++ b/src/concurrency/OSThread.h
@@ -53,7 +53,7 @@ class OSThread : public Thread
static void setup();
- int32_t disable();
+ virtual int32_t disable();
/**
* Wait a specified number msecs starting from the current time (rather than the last time we were run)
@@ -87,4 +87,4 @@ extern bool hasBeenSetup;
void assertIsSetup();
-} // namespace concurrency
+} // namespace concurrency
\ No newline at end of file
diff --git a/src/configuration.h b/src/configuration.h
index fb96430bc..2640b1572 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -101,6 +101,7 @@ along with this program. If not, see .
// I2C Keyboards (M5Stack, RAK14004, T-Deck)
#define CARDKB_ADDR 0x5F
#define TDECK_KB_ADDR 0x55
+#define BBQ10_KB_ADDR 0x1F
// -----------------------------------------------------------------------------
// SENSOR
@@ -144,7 +145,7 @@ along with this program. If not, see .
#define GPS_BAUDRATE 9600
#ifndef GPS_THREAD_INTERVAL
-#define GPS_THREAD_INTERVAL 100
+#define GPS_THREAD_INTERVAL 200
#endif
// convert 24-bit color to 16-bit (56K)
diff --git a/src/detect/ScanI2C.cpp b/src/detect/ScanI2C.cpp
index 996bdf62a..bf206c190 100644
--- a/src/detect/ScanI2C.cpp
+++ b/src/detect/ScanI2C.cpp
@@ -30,8 +30,8 @@ ScanI2C::FoundDevice ScanI2C::firstRTC() const
ScanI2C::FoundDevice ScanI2C::firstKeyboard() const
{
- ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, RAK14004};
- return firstOfOrNONE(3, types);
+ ScanI2C::DeviceType types[] = {CARDKB, TDECKKB, BBQ10KB, RAK14004};
+ return firstOfOrNONE(4, types);
}
ScanI2C::FoundDevice ScanI2C::firstAccelerometer() const
diff --git a/src/detect/ScanI2C.h b/src/detect/ScanI2C.h
index 418a6bf5e..15d9b1342 100644
--- a/src/detect/ScanI2C.h
+++ b/src/detect/ScanI2C.h
@@ -17,6 +17,7 @@ class ScanI2C
RTC_PCF8563,
CARDKB,
TDECKKB,
+ BBQ10KB,
RAK14004,
PMU_AXP192_AXP2101,
BME_680,
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 30f9e7b7c..ced1e34dd 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -213,6 +213,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port)
break;
SCAN_SIMPLE_CASE(TDECK_KB_ADDR, TDECKKB, "T-Deck keyboard found\n");
+ SCAN_SIMPLE_CASE(BBQ10_KB_ADDR, BBQ10KB, "BB Q10 keyboard found\n");
SCAN_SIMPLE_CASE(ST7567_ADDRESS, SCREEN_ST7567, "st7567 display found\n");
#ifdef HAS_NCP5623
SCAN_SIMPLE_CASE(NCP5623_ADDR, NCP5623, "NCP5623 RGB LED found\n");
diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp
index 27fe36fe5..13afaa3e6 100644
--- a/src/gps/GPS.cpp
+++ b/src/gps/GPS.cpp
@@ -2,18 +2,19 @@
#include "NodeDB.h"
#include "RTC.h"
#include "configuration.h"
+#include "main.h" // pmu_found
#include "sleep.h"
+#include "ubx.h"
+
+#ifdef ARCH_PORTDUINO
+#include "meshUtils.h"
+#endif
#ifndef GPS_RESET_MODE
#define GPS_RESET_MODE HIGH
#endif
-// If we have a serial GPS port it will not be null
-#ifdef GPS_SERIAL_NUM
-HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
-HardwareSerial *GPS::_serial_gps = &_serial_gps_real;
-#elif defined(NRF52840_XXAA) || defined(NRF52833_XXAA)
-// Assume NRF52840
+#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32)
HardwareSerial *GPS::_serial_gps = &Serial1;
#else
HardwareSerial *GPS::_serial_gps = NULL;
@@ -27,8 +28,10 @@ static bool didSerialInit;
struct uBloxGnssModelInfo info;
uint8_t uBloxProtocolVersion;
+#define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway
+#define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc)
-void GPS::UBXChecksum(byte *message, size_t length)
+void GPS::UBXChecksum(uint8_t *message, size_t length)
{
uint8_t CK_A = 0, CK_B = 0;
@@ -43,13 +46,71 @@ void GPS::UBXChecksum(byte *message, size_t length)
message[length - 1] = CK_B;
}
-bool GPS::getACK(uint8_t class_id, uint8_t msg_id)
+// Function to create a ublox packet for editing in memory
+uint8_t GPS::makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg)
+{
+ // Construct the UBX packet
+ UBXscratch[0] = 0xB5; // header
+ UBXscratch[1] = 0x62; // header
+ UBXscratch[2] = class_id; // class
+ UBXscratch[3] = msg_id; // id
+ UBXscratch[4] = payload_size; // length
+ UBXscratch[5] = 0x00;
+
+ UBXscratch[6 + payload_size] = 0x00; // CK_A
+ UBXscratch[7 + payload_size] = 0x00; // CK_B
+
+ for (int i = 0; i < payload_size; i++) {
+ UBXscratch[6 + i] = pgm_read_byte(&msg[i]);
+ }
+ UBXChecksum(UBXscratch, (payload_size + 8));
+ return (payload_size + 8);
+}
+
+GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
+{
+ uint8_t buffer[768] = {0};
+ uint8_t b;
+ int bytesRead = 0;
+ uint32_t startTimeout = millis() + waitMillis;
+ while (millis() < startTimeout) {
+ if (_serial_gps->available()) {
+ b = _serial_gps->read();
+ buffer[bytesRead] = b;
+ bytesRead++;
+ if ((bytesRead == 767) || (b == '\r')) {
+ if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
+#ifdef GPS_DEBUG
+ buffer[bytesRead] = '\0';
+ LOG_DEBUG("%s\r", (char *)buffer);
+#endif
+ return GNSS_RESPONSE_OK;
+ } else {
+#ifdef GPS_DEBUG
+ buffer[bytesRead] = '\0';
+ LOG_INFO("Bytes read:%s\n", (char *)buffer);
+#endif
+ bytesRead = 0;
+ }
+ }
+ }
+ }
+#ifdef GPS_DEBUG
+ buffer[bytesRead] = '\0';
+ LOG_INFO("Bytes read:%s\n", (char *)buffer);
+#endif
+ return GNSS_RESPONSE_NONE;
+}
+
+GPS_RESPONSE GPS::getACK(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis)
{
uint8_t b;
uint8_t ack = 0;
const uint8_t ackP[2] = {class_id, msg_id};
uint8_t buf[10] = {0xB5, 0x62, 0x05, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00};
- unsigned long startTime = millis();
+ uint32_t startTime = millis();
+ const char frame_errors[] = "More than 100 frame errors";
+ int sCounter = 0;
for (int j = 2; j < 6; j++) {
buf[8] += buf[j];
@@ -62,28 +123,46 @@ bool GPS::getACK(uint8_t class_id, uint8_t msg_id)
buf[9] += buf[8];
}
- while (1) {
+ while (millis() - startTime < waitMillis) {
if (ack > 9) {
- // LOG_INFO("Got ACK for class %02X message %02X\n", class_id, msg_id);
- return true; // ACK received
- }
- if (millis() - startTime > 3000) {
- LOG_WARN("No response for class %02X message %02X\n", class_id, msg_id);
- return false; // No response received within 3 seconds
+#ifdef GPS_DEBUG
+ LOG_DEBUG("\n");
+ LOG_INFO("Got ACK for class %02X message %02X in %d millis.\n", class_id, msg_id, millis() - startTime);
+#endif
+ return GNSS_RESPONSE_OK; // ACK received
}
if (_serial_gps->available()) {
b = _serial_gps->read();
+ if (b == frame_errors[sCounter]) {
+ sCounter++;
+ if (sCounter == 26) {
+ return GNSS_RESPONSE_FRAME_ERRORS;
+ }
+ } else {
+ sCounter = 0;
+ }
+#ifdef GPS_DEBUG
+ LOG_DEBUG("%02X", b);
+#endif
if (b == buf[ack]) {
ack++;
} else {
- ack = 0; // Reset the acknowledgement counter
- if (buf[3] == 0x00) { // UBX-ACK-NAK message
+ if (ack == 3 && b == 0x00) { // UBX-ACK-NAK message
+#ifdef GPS_DEBUG
+ LOG_DEBUG("\n");
+#endif
LOG_WARN("Got NAK for class %02X message %02X\n", class_id, msg_id);
- return false; // NAK received
+ return GNSS_RESPONSE_NAK; // NAK received
}
+ ack = 0; // Reset the acknowledgement counter
}
}
}
+#ifdef GPS_DEBUG
+ LOG_DEBUG("\n");
+ LOG_WARN("No response for class %02X message %02X\n", class_id, msg_id);
+#endif
+ return GNSS_RESPONSE_NONE; // No response received within timeout
}
/**
@@ -95,14 +174,14 @@ bool GPS::getACK(uint8_t class_id, uint8_t msg_id)
* @param requestedID: request message ID constant
* @retval length of payload message
*/
-int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID)
+int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis)
{
uint16_t ubxFrameCounter = 0;
uint32_t startTime = millis();
uint16_t needRead;
- while (millis() - startTime < 1200) {
- while (_serial_gps->available()) {
+ while (millis() - startTime < waitMillis) {
+ if (_serial_gps->available()) {
int c = _serial_gps->read();
switch (ubxFrameCounter) {
case 0:
@@ -144,8 +223,6 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
// Payload length msb
needRead |= (c << 8);
ubxFrameCounter++;
- break;
- case 6:
// Check for buffer overflow
if (needRead >= size) {
ubxFrameCounter = 0;
@@ -155,6 +232,10 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
ubxFrameCounter = 0;
} else {
// return payload length
+#ifdef GPS_DEBUG
+ LOG_INFO("Got ACK for class %02X message %02X in %d millis.\n", requestedClass, requestedID,
+ millis() - startTime);
+#endif
return needRead;
}
break;
@@ -164,54 +245,41 @@ int GPS::getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
}
}
}
+ // LOG_WARN("No response for class %02X message %02X\n", requestedClass, requestedID);
return 0;
}
-bool GPS::setupGPS()
+bool GPS::setup()
{
- if (_serial_gps && !didSerialInit) {
- didSerialInit = true;
+ int msglen = 0;
-#ifdef ARCH_ESP32
- // In esp32 framework, setRxBufferSize needs to be initialized before Serial
- _serial_gps->setRxBufferSize(SERIAL_BUFFER_SIZE); // the default is 256
-#endif
-
- // if the overrides are not dialled in, set them from the board definitions, if they exist
-
-#if defined(GPS_RX_PIN)
- if (!config.position.rx_gpio)
- config.position.rx_gpio = GPS_RX_PIN;
-#endif
-#if defined(GPS_TX_PIN)
- if (!config.position.tx_gpio)
- config.position.tx_gpio = GPS_TX_PIN;
-#endif
-
-//#define BAUD_RATE 115200
-// ESP32 has a special set of parameters vs other arduino ports
-#if defined(ARCH_ESP32)
- if (config.position.rx_gpio) {
- LOG_DEBUG("Using GPIO%d for GPS RX\n", config.position.rx_gpio);
- LOG_DEBUG("Using GPIO%d for GPS TX\n", config.position.tx_gpio);
- _serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, config.position.rx_gpio, config.position.tx_gpio);
+ if (!didSerialInit) {
+#if !defined(GPS_UC6580)
+ if (tx_gpio) {
+ LOG_DEBUG("Probing for GPS at %d \n", serialSpeeds[speedSelect]);
+ gnssModel = probe(serialSpeeds[speedSelect]);
+ if (gnssModel == GNSS_MODEL_UNKNOWN) {
+ if (++speedSelect == sizeof(serialSpeeds) / sizeof(int)) {
+ speedSelect = 0;
+ if (--probeTries == 0) {
+ LOG_WARN("Giving up on GPS probe and setting to 9600.\n");
+ return true;
+ }
+ }
+ return false;
+ }
+ } else {
+ gnssModel = GNSS_MODEL_UNKNOWN;
}
#else
- _serial_gps->begin(GPS_BAUDRATE);
+ gnssModel = GNSS_MODEL_UC6850;
#endif
- /*
- * T-Beam-S3-Core will be preset to use gps Probe here, and other boards will not be changed first
- */
- gnssModel = probe();
-
if (gnssModel == GNSS_MODEL_MTK) {
/*
* t-beam-s3-core uses the same L76K GNSS module as t-echo.
* Unlike t-echo, L76K uses 9600 baud rate for communication by default.
* */
- // _serial_gps->begin(9600); //The baud rate of 9600 has been initialized at the beginning of setupGPS, this line
- // is the redundant part delay(250);
// Initialize the L76K Chip, use GPS + GLONASS + BEIDOU
_serial_gps->write("$PCAS04,7*1E\r\n");
@@ -228,419 +296,202 @@ bool GPS::setupGPS()
_serial_gps->write("$CFGSYS,h15\r\n");
delay(250);
} else if (gnssModel == GNSS_MODEL_UBLOX) {
-
// Configure GNSS system to GPS+SBAS+GLONASS (Module may restart after this command)
// We need set it because by default it is GPS only, and we want to use GLONASS too
// Also we need SBAS for better accuracy and extra features
// ToDo: Dynamic configure GNSS systems depending of LoRa region
- byte _message_GNSS[36] = {
- 0xb5, 0x62, // Sync message for UBX protocol
- 0x06, 0x3e, // Message class and ID (UBX-CFG-GNSS)
- 0x1c, 0x00, // Length of payload (28 bytes)
- 0x00, // msgVer (0 for this version)
- 0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
- 0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
- 0x03, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
- // GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
- 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, // GPS
- 0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // SBAS
- 0x06, 0x08, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x01, // GLONASS
- 0x00, 0x00 // Checksum (to be calculated below)
- };
- // Calculate the checksum and update the message.
- UBXChecksum(_message_GNSS, sizeof(_message_GNSS));
-
- // Send the message to the module
- _serial_gps->write(_message_GNSS, sizeof(_message_GNSS));
-
- if (!getACK(0x06, 0x3e)) {
- // It's not critical if the module doesn't acknowledge this configuration.
- // The module should operate adequately with its factory or previously saved settings.
- // It appears that there is a firmware bug in some GPS modules: When an attempt is made
- // to overwrite a saved state with identical values, no ACK/NAK is received, contrary to
- // what is specified in the Ublox documentation.
- // There is also a possibility that the module may be GPS-only.
- LOG_INFO("Unable to reconfigure GNSS - defaults maintained. Is this module GPS-only?\n");
- return true;
- } else {
- LOG_INFO("GNSS configured for GPS+SBAS+GLONASS. Pause for 0.75s before sending next command.\n");
- // Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next commands
- delay(750);
- return true;
- }
-
- // Enable interference resistance, because we are using LoRa, WiFi and Bluetooth on same board,
- // and we need to reduce interference from them
- byte _message_JAM[16] = {
- 0xB5, 0x62, // UBX protocol sync characters
- 0x06, 0x39, // Message class and ID (UBX-CFG-ITFM)
- 0x08, 0x00, // Length of payload (8 bytes)
- // bbThreshold (Broadband jamming detection threshold) is set to 0x3F (63 in decimal)
- // cwThreshold (CW jamming detection threshold) is set to 0x10 (16 in decimal)
- // algorithmBits (Reserved algorithm settings) is set to 0x16B156 as recommended
- // enable (Enable interference detection) is set to 1 (enabled)
- 0x3F, 0x10, 0xB1, 0x56, // config: Interference config word
- // generalBits (General settings) is set to 0x31E as recommended
- // antSetting (Antenna setting, 0=unknown, 1=passive, 2=active) is set to 0 (unknown)
- // ToDo: Set to 1 (passive) or 2 (active) if known, for example from UBX-MON-HW, or from board info
- // enable2 (Set to 1 to scan auxiliary bands, u-blox 8 / u-blox M8 only, otherwise ignored) is set to 1
- // (enabled)
- 0x1E, 0x03, 0x00, 0x01, // config2: Extra settings for jamming/interference monitor
- 0x00, 0x00 // Checksum (calculated below)
- };
-
- // Calculate the checksum and update the message.
- UBXChecksum(_message_JAM, sizeof(_message_JAM));
-
- // Send the message to the module
- _serial_gps->write(_message_JAM, sizeof(_message_JAM));
-
- if (!getACK(0x06, 0x39)) {
- LOG_WARN("Unable to enable interference resistance.\n");
- return true;
- }
-
- // Configure navigation engine expert settings:
- byte _message_NAVX5[48] = {
- 0xb5, 0x62, // UBX protocol sync characters
- 0x06, 0x23, // Message class and ID (UBX-CFG-NAVX5)
- 0x28, 0x00, // Length of payload (40 bytes)
- 0x00, 0x00, // msgVer (0 for this version)
- // minMax flag = 1: apply min/max SVs settings
- // minCno flag = 1: apply minimum C/N0 setting
- // initial3dfix flag = 0: apply initial 3D fix settings
- // aop flag = 1: apply aopCfg (useAOP flag) settings (AssistNow Autonomous)
- 0x1B, 0x00, // mask1 (First parameters bitmask)
- // adr flag = 0: apply ADR sensor fusion on/off setting (useAdr flag)
- // If firmware is not ADR/UDR, enabling this flag will fail configuration
- // ToDo: check this with UBX-MON-VER
- 0x00, 0x00, 0x00, 0x00, // mask2 (Second parameters bitmask)
- 0x00, 0x00, // Reserved
- 0x03, // minSVs (Minimum number of satellites for navigation) = 3
- 0x10, // maxSVs (Maximum number of satellites for navigation) = 16
- 0x06, // minCNO (Minimum satellite signal level for navigation) = 6 dBHz
- 0x00, // Reserved
- 0x00, // iniFix3D (Initial fix must be 3D) = 0 (disabled)
- 0x00, 0x00, // Reserved
- 0x00, // ackAiding (Issue acknowledgements for assistance message input) = 0 (disabled)
- 0x00, 0x00, // Reserved
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved
- 0x00, // Reserved
- 0x01, // aopCfg (AssistNow Autonomous configuration) = 1 (enabled)
- 0x00, 0x00, // Reserved
- 0x00, 0x00, // Reserved
- 0x00, 0x00, 0x00, 0x00, // Reserved
- 0x00, 0x00, 0x00, // Reserved
- 0x01, // useAdr (Enable/disable ADR sensor fusion) = 1 (enabled)
- 0x00, 0x00 // Checksum (calculated below)
- };
-
- // Calculate the checksum and update the message.
- UBXChecksum(_message_NAVX5, sizeof(_message_NAVX5));
-
- // Send the message to the module
- _serial_gps->write(_message_NAVX5, sizeof(_message_NAVX5));
-
- if (!getACK(0x06, 0x23)) {
- LOG_WARN("Unable to configure extra settings.\n");
- return true;
- }
-
- /*
- tips: NMEA Only should not be set here, otherwise initializing Ublox gnss module again after
- setting will not output command messages in UART1, resulting in unrecognized module information
-
- // Set the UART port to output NMEA only
- byte _message_nmea[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00,
- 0x80, 0x25, 0x00, 0x00, 0x07, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0xAF};
- _serial_gps->write(_message_nmea, sizeof(_message_nmea));
- if (!getACK(0x06, 0x00)) {
- LOG_WARN("Unable to enable NMEA Mode.\n");
- return true;
+ if (strncmp(info.hwVersion, "00040007", 8) !=
+ 0) { // The original ublox 6 is GPS only and doesn't support the UBX-CFG-GNSS message
+ if (strncmp(info.hwVersion, "00070000", 8) == 0) { // Max7 seems to only support GPS *or* GLONASS
+ LOG_DEBUG("Setting GPS+SBAS\n");
+ msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS_7), _message_GNSS_7);
+ _serial_gps->write(UBXscratch, msglen);
+ } else {
+ msglen = makeUBXPacket(0x06, 0x3e, sizeof(_message_GNSS), _message_GNSS);
+ _serial_gps->write(UBXscratch, msglen);
}
- */
+
+ if (getACK(0x06, 0x3e, 800) == GNSS_RESPONSE_NAK) {
+ // It's not critical if the module doesn't acknowledge this configuration.
+ LOG_INFO("Unable to reconfigure GNSS - defaults maintained. Is this module GPS-only?\n");
+ } else {
+ if (strncmp(info.hwVersion, "00070000", 8) == 0) {
+ LOG_INFO("GNSS configured for GPS+SBAS. Pause for 0.75s before sending next command.\n");
+ } else {
+ LOG_INFO("GNSS configured for GPS+SBAS+GLONASS. Pause for 0.75s before sending next command.\n");
+ }
+ // Documentation say, we need wait atleast 0.5s after reconfiguration of GNSS module, before sending next
+ // commands
+ delay(750);
+ }
+ }
+
+ msglen = makeUBXPacket(0x06, 0x39, sizeof(_message_JAM), _message_JAM);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x39, 300) != GNSS_RESPONSE_OK) {
+ LOG_WARN("Unable to enable interference resistance.\n");
+ }
+
+ msglen = makeUBXPacket(0x06, 0x23, sizeof(_message_NAVX5), _message_NAVX5);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x23, 300) != GNSS_RESPONSE_OK) {
+ LOG_WARN("Unable to configure extra settings.\n");
+ }
// ublox-M10S can be compatible with UBLOX traditional protocol, so the following sentence settings are also valid
- // Set GPS update rate to 1Hz
- // Lowering the update rate helps to save power.
- // Additionally, for some new modules like the M9/M10, an update rate lower than 5Hz
- // is recommended to avoid a known issue with satellites disappearing.
- byte _message_1Hz[] = {
- 0xB5, 0x62, // UBX protocol sync characters
- 0x06, 0x08, // Message class and ID (UBX-CFG-RATE)
- 0x06, 0x00, // Length of payload (6 bytes)
- 0xE8, 0x03, // Measurement Rate (1000ms for 1Hz)
- 0x01, 0x00, // Navigation rate, always 1 in GPS mode
- 0x01, 0x00, // Time reference
- 0x00, 0x00 // Placeholder for checksum, will be calculated next
- };
-
- // Calculate the checksum and update the message.
- UBXChecksum(_message_1Hz, sizeof(_message_1Hz));
-
- // Send the message to the module
- _serial_gps->write(_message_1Hz, sizeof(_message_1Hz));
-
- if (!getACK(0x06, 0x08)) {
+ msglen = makeUBXPacket(0x06, 0x08, sizeof(_message_1HZ), _message_1HZ);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x08, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to set GPS update rate.\n");
- return true;
}
- // Disable GGL. GGL - Geographic position (latitude and longitude), which provides the current geographical
- // coordinates.
- byte _message_GGL[] = {
- 0xB5, 0x62, // UBX sync characters
- 0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
- 0x08, 0x00, // Length of payload (8 bytes)
- 0xF0, 0x01, // NMEA ID for GLL
- 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
- 0x00, // Disable
- 0x01, 0x01, 0x01, 0x01, // Reserved
- 0x00, 0x00 // CK_A and CK_B (Checksum)
- };
-
- // Calculate the checksum and update the message.
- UBXChecksum(_message_GGL, sizeof(_message_GGL));
-
- // Send the message to the module
- _serial_gps->write(_message_GGL, sizeof(_message_GGL));
-
- if (!getACK(0x06, 0x01)) {
+ msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GGL), _message_GGL);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to disable NMEA GGL.\n");
- return true;
}
- // Enable GSA. GSA - GPS DOP and active satellites, used for detailing the satellites used in the positioning and
- // the DOP (Dilution of Precision)
- byte _message_GSA[] = {
- 0xB5, 0x62, // UBX sync characters
- 0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
- 0x08, 0x00, // Length of payload (8 bytes)
- 0xF0, 0x02, // NMEA ID for GSA
- 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
- 0x01, // Enable
- 0x01, 0x01, 0x01, 0x01, // Reserved
- 0x00, 0x00 // CK_A and CK_B (Checksum)
- };
- UBXChecksum(_message_GSA, sizeof(_message_GSA));
- _serial_gps->write(_message_GSA, sizeof(_message_GSA));
- if (!getACK(0x06, 0x01)) {
+ msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GSA), _message_GSA);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to Enable NMEA GSA.\n");
- return true;
}
- // Disable GSV. GSV - Satellites in view, details the number and location of satellites in view.
- byte _message_GSV[] = {
- 0xB5, 0x62, // UBX sync characters
- 0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
- 0x08, 0x00, // Length of payload (8 bytes)
- 0xF0, 0x03, // NMEA ID for GSV
- 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
- 0x00, // Disable
- 0x01, 0x01, 0x01, 0x01, // Reserved
- 0x00, 0x00 // CK_A and CK_B (Checksum)
- };
- UBXChecksum(_message_GSV, sizeof(_message_GSV));
- _serial_gps->write(_message_GSV, sizeof(_message_GSV));
- if (!getACK(0x06, 0x01)) {
+ msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GSV), _message_GSV);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to disable NMEA GSV.\n");
- return true;
}
- // Disable VTG. VTG - Track made good and ground speed, which provides course and speed information relative to
- // the ground.
- byte _message_VTG[] = {
- 0xB5, 0x62, // UBX sync characters
- 0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
- 0x08, 0x00, // Length of payload (8 bytes)
- 0xF0, 0x05, // NMEA ID for VTG
- 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
- 0x00, // Disable
- 0x01, 0x01, 0x01, 0x01, // Reserved
- 0x00, 0x00 // CK_A and CK_B (Checksum)
- };
- UBXChecksum(_message_VTG, sizeof(_message_VTG));
- _serial_gps->write(_message_VTG, sizeof(_message_VTG));
- if (!getACK(0x06, 0x01)) {
+ msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_VTG), _message_VTG);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to disable NMEA VTG.\n");
- return true;
}
- // Enable RMC. RMC - Recommended Minimum data, the essential gps pvt (position, velocity, time) data.
- byte _message_RMC[] = {
- 0xB5, 0x62, // UBX sync characters
- 0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
- 0x08, 0x00, // Length of payload (8 bytes)
- 0xF0, 0x04, // NMEA ID for RMC
- 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
- 0x01, // Enable
- 0x01, 0x01, 0x01, 0x01, // Reserved
- 0x00, 0x00 // CK_A and CK_B (Checksum)
- };
- UBXChecksum(_message_RMC, sizeof(_message_RMC));
- _serial_gps->write(_message_RMC, sizeof(_message_RMC));
- if (!getACK(0x06, 0x01)) {
+ msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_RMC), _message_RMC);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to enable NMEA RMC.\n");
- return true;
}
- // Enable GGA. GGA - Global Positioning System Fix Data, which provides 3D location and accuracy data.
- byte _message_GGA[] = {
- 0xB5, 0x62, // UBX sync characters
- 0x06, 0x01, // Message class and ID (UBX-CFG-MSG)
- 0x08, 0x00, // Length of payload (8 bytes)
- 0xF0, 0x00, // NMEA ID for GGA
- 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
- 0x01, // Enable
- 0x01, 0x01, 0x01, 0x01, // Reserved
- 0x00, 0x00 // CK_A and CK_B (Checksum)
- };
- UBXChecksum(_message_GGA, sizeof(_message_GGA));
- _serial_gps->write(_message_GGA, sizeof(_message_GGA));
- if (!getACK(0x06, 0x01)) {
+ msglen = makeUBXPacket(0x06, 0x01, sizeof(_message_GGA), _message_GGA);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x01, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to enable NMEA GGA.\n");
- return true;
}
- // The Power Management configuration allows the GPS module to operate in different power modes for optimized power
- // consumption.
- // The modes supported are:
- // 0x00 = Full power: The module operates at full power with no power saving.
- // 0x01 = Balanced: The module dynamically adjusts the tracking behavior to balance power consumption.
- // 0x02 = Interval: The module operates in a periodic mode, cycling between tracking and power saving states.
- // 0x03 = Aggressive with 1 Hz: The module operates in a power saving mode with a 1 Hz update rate.
- // 0x04 = Aggressive with 2 Hz: The module operates in a power saving mode with a 2 Hz update rate.
- // 0x05 = Aggressive with 4 Hz: The module operates in a power saving mode with a 4 Hz update rate.
- // The 'period' field specifies the position update and search period. It is only valid when the powerSetupValue is
- // set to Interval; otherwise, it must be set to '0'. The 'onTime' field specifies the duration of the ON phase and
- // must be smaller than the period. It is only valid when the powerSetupValue is set to Interval; otherwise, it must
- // be set to '0'.
- byte UBX_CFG_PMS[14] = {
- 0xB5, 0x62, // UBX sync characters
- 0x06, 0x86, // Message class and ID (UBX-CFG-PMS)
- 0x06, 0x00, // Length of payload (6 bytes)
- 0x00, // Version (0)
- 0x03, // Power setup value
- 0x00, 0x00, // period: not applicable, set to 0
- 0x00, 0x00, // onTime: not applicable, set to 0
- 0x00, 0x00 // Placeholder for checksum, will be calculated next
- };
-
- // Calculate the checksum and update the message
- UBXChecksum(UBX_CFG_PMS, sizeof(UBX_CFG_PMS));
-
- // Send the message to the module
- _serial_gps->write(UBX_CFG_PMS, sizeof(UBX_CFG_PMS));
- if (!getACK(0x06, 0x86)) {
- LOG_WARN("Unable to enable powersaving for GPS.\n");
- return true;
+ if (uBloxProtocolVersion >= 18) {
+ msglen = makeUBXPacket(0x06, 0x86, sizeof(_message_PMS), _message_PMS);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x86, 300) != GNSS_RESPONSE_OK) {
+ LOG_WARN("Unable to enable powersaving for GPS.\n");
+ }
+ } else {
+ if (strncmp(info.hwVersion, "00040007", 8) == 0) { // This PSM mode has only been tested on this hardware
+ msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_PSM);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x11, 300) != GNSS_RESPONSE_OK) {
+ LOG_WARN("Unable to enable powersaving mode for GPS.\n");
+ }
+ msglen = makeUBXPacket(0x06, 0x3B, 44, _message_CFG_PM2);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x3B, 300) != GNSS_RESPONSE_OK) {
+ LOG_WARN("Unable to enable powersaving details for GPS.\n");
+ }
+ } else {
+ msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_ECO);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x11, 300) != GNSS_RESPONSE_OK) {
+ LOG_WARN("Unable to enable powersaving ECO mode for GPS.\n");
+ }
+ }
}
- // We need save configuration to flash to make our config changes persistent
- byte _message_SAVE[21] = {
- 0xB5, 0x62, // UBX protocol header
- 0x06, 0x09, // UBX class ID (Configuration Input Messages), message ID (UBX-CFG-CFG)
- 0x0D, 0x00, // Length of payload (13 bytes)
- 0x00, 0x00, 0x00, 0x00, // clearMask: no sections cleared
- 0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections
- 0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded
- 0x0F, // deviceMask: BBR, Flash, EEPROM, and SPI Flash
- 0x00, 0x00 // Checksum (calculated below)
- };
-
- // Calculate the checksum and update the message.
- UBXChecksum(_message_SAVE, sizeof(_message_SAVE));
-
- // Send the message to the module
- _serial_gps->write(_message_SAVE, sizeof(_message_SAVE));
-
- if (!getACK(0x06, 0x09)) {
+ msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE);
+ _serial_gps->write(UBXscratch, msglen);
+ if (getACK(0x06, 0x09, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to save GNSS module configuration.\n");
- return true;
} else {
LOG_INFO("GNSS module configuration saved!\n");
- return true;
}
}
+ didSerialInit = true;
}
+ notifyDeepSleepObserver.observe(¬ifyDeepSleep);
+ notifyGPSSleepObserver.observe(¬ifyGPSSleep);
return true;
}
-bool GPS::setup()
-{
- // Master power for the GPS
-
-#if defined(HAS_PMU) || defined(PIN_GPS_EN)
- if (config.position.gps_enabled) {
-#ifdef PIN_GPS_EN
- pinMode(PIN_GPS_EN, OUTPUT);
-#endif
- setGPSPower(true);
- }
-#endif
-
-#ifdef PIN_GPS_RESET
- digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
- pinMode(PIN_GPS_RESET, OUTPUT);
- delay(10);
- digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
-#endif
- setAwake(true); // Wake GPS power before doing any init
- bool ok = setupGPS();
-
- if (ok) {
- notifySleepObserver.observe(¬ifySleep);
- notifyDeepSleepObserver.observe(¬ifyDeepSleep);
- notifyGPSSleepObserver.observe(¬ifyGPSSleep);
- }
-
- if (config.position.gps_enabled == false && config.position.fixed_position == false) {
- setAwake(false);
- doGPSpowersave(false);
- }
- return ok;
-}
-
GPS::~GPS()
{
// we really should unregister our sleep observer
- notifySleepObserver.unobserve(¬ifySleep);
notifyDeepSleepObserver.unobserve(¬ifyDeepSleep);
notifyGPSSleepObserver.observe(¬ifyGPSSleep);
}
-bool GPS::hasLock()
+void GPS::setGPSPower(bool on, bool standbyOnly)
{
- return hasValidLocation;
-}
-
-bool GPS::hasFlow()
-{
- return hasGPS;
-}
-
-// Allow defining the polarity of the WAKE output. default is active high
-#ifndef GPS_WAKE_ACTIVE
-#define GPS_WAKE_ACTIVE 1
+ LOG_INFO("Setting GPS power=%d\n", on);
+ if (on) {
+ clearBuffer(); // drop any old data waiting in the buffer before re-enabling
+ if (en_gpio)
+ digitalWrite(en_gpio, on ? GPS_EN_ACTIVE : !GPS_EN_ACTIVE); // turn this on if defined, every time
+ }
+ isInPowersave = !on;
+ if (!standbyOnly && en_gpio != 0 &&
+ !(HW_VENDOR == meshtastic_HardwareModel_RAK4631 && (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1))) {
+ LOG_DEBUG("GPS powerdown using GPS_EN_ACTIVE\n");
+ digitalWrite(en_gpio, on ? GPS_EN_ACTIVE : !GPS_EN_ACTIVE);
+ return;
+ }
+#ifdef HAS_PMU // We only have PMUs on the T-Beam, and that board has a tiny battery to save GPS ephemera, so treat as a standby.
+ if (pmu_found && PMU) {
+ uint8_t model = PMU->getChipModel();
+ if (model == XPOWERS_AXP2101) {
+ if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
+ // t-beam v1.2 GNSS power channel
+ on ? PMU->enablePowerOutput(XPOWERS_ALDO3) : PMU->disablePowerOutput(XPOWERS_ALDO3);
+ } else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) {
+ // t-beam-s3-core GNSS power channel
+ on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4);
+ }
+ } else if (model == XPOWERS_AXP192) {
+ // t-beam v1.1 GNSS power channel
+ on ? PMU->enablePowerOutput(XPOWERS_LDO3) : PMU->disablePowerOutput(XPOWERS_LDO3);
+ }
+ return;
+ }
#endif
-
-void GPS::wake()
-{
-#ifdef PIN_GPS_WAKE
- digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE);
- pinMode(PIN_GPS_WAKE, OUTPUT);
-#endif
-}
-
-void GPS::sleep()
-{
-#ifdef PIN_GPS_WAKE
- digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE ? 0 : 1);
- pinMode(PIN_GPS_WAKE, OUTPUT);
+#ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76K and clones
+ if (on) {
+ LOG_INFO("Waking GPS");
+ digitalWrite(PIN_GPS_STANDBY, 1);
+ pinMode(PIN_GPS_STANDBY, OUTPUT);
+ return;
+ } else {
+ LOG_INFO("GPS entering sleep");
+ // notifyGPSSleep.notifyObservers(NULL);
+ digitalWrite(PIN_GPS_STANDBY, 0);
+ pinMode(PIN_GPS_STANDBY, OUTPUT);
+ return;
+ }
#endif
+ if (!on) {
+ if (gnssModel == GNSS_MODEL_UBLOX) {
+ uint8_t msglen;
+ msglen = gps->makeUBXPacket(0x02, 0x41, 0x08, gps->_message_PMREQ);
+ gps->_serial_gps->write(gps->UBXscratch, msglen);
+ }
+ } else {
+ if (gnssModel == GNSS_MODEL_UBLOX) {
+ gps->_serial_gps->write(0xFF);
+ clearBuffer(); // This often returns old data, so drop it
+ }
+ }
}
/// Record that we have a GPS
@@ -652,14 +503,6 @@ void GPS::setConnected()
}
}
-void GPS::setNumSatellites(uint8_t n)
-{
- if (n != numSatellites) {
- numSatellites = n;
- shouldPublish = true;
- }
-}
-
/**
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
*
@@ -667,22 +510,38 @@ void GPS::setNumSatellites(uint8_t n)
*/
void GPS::setAwake(bool on)
{
- if (!wakeAllowed && on) {
- LOG_WARN("Inhibiting because !wakeAllowed\n");
- on = false;
- }
-
if (isAwake != on) {
LOG_DEBUG("WANT GPS=%d\n", on);
- if (on) {
- lastWakeStartMsec = millis();
- wake();
- } else {
- lastSleepStartMsec = millis();
- sleep();
+ isAwake = on;
+ if (!enabled) { // short circuit if the user has disabled GPS
+ setGPSPower(false, false);
+ return;
}
- isAwake = on;
+ if (on) {
+ lastWakeStartMsec = millis();
+ } else {
+ lastSleepStartMsec = millis();
+ if (GPSCycles == 1) { // Skipping initial lock time, as it will likely be much longer than average
+ averageLockTime = lastSleepStartMsec - lastWakeStartMsec;
+ } else if (GPSCycles > 1) {
+ averageLockTime += ((int32_t)(lastSleepStartMsec - lastWakeStartMsec) - averageLockTime) / (int32_t)GPSCycles;
+ }
+ GPSCycles++;
+ LOG_DEBUG("GPS Lock took %d, average %d\n", (lastSleepStartMsec - lastWakeStartMsec) / 1000, averageLockTime / 1000);
+ }
+ if ((int32_t)getSleepTime() - averageLockTime >
+ 15 * 60 * 1000) { // 15 minutes is probably long enough to make a complete poweroff worth it.
+ setGPSPower(on, false);
+ } else if ((int32_t)getSleepTime() - averageLockTime > 10000) { // 10 seconds is enough for standby
+#ifdef GPS_UC6580
+ setGPSPower(on, false);
+#else
+ setGPSPower(on, true);
+#endif
+ } else if (averageLockTime > 20000) {
+ averageLockTime -= 1000; // eventually want to sleep again.
+ }
}
}
@@ -702,10 +561,9 @@ uint32_t GPS::getWakeTime() const
uint32_t GPS::getSleepTime() const
{
uint32_t t = config.position.gps_update_interval;
- bool gps_enabled = config.position.gps_enabled;
// We'll not need the GPS thread to wake up again after first acq. with fixed position.
- if (!gps_enabled || config.position.fixed_position)
+ if (!config.position.gps_enabled || config.position.fixed_position)
t = UINT32_MAX; // Sleep forever now
if (t == UINT32_MAX)
@@ -720,19 +578,44 @@ void GPS::publishUpdate()
shouldPublish = false;
// In debug logs, identify position by @timestamp:stage (stage 2 = publish)
- LOG_DEBUG("publishing pos@%x:2, hasVal=%d, GPSlock=%d\n", p.timestamp, hasValidLocation, hasLock());
+ LOG_DEBUG("publishing pos@%x:2, hasVal=%d, Sats=%d, GPSlock=%d\n", p.timestamp, hasValidLocation, p.sats_in_view,
+ hasLock());
// Notify any status instances that are observing us
const meshtastic::GPSStatus status = meshtastic::GPSStatus(hasValidLocation, isConnected(), isPowerSaving(), p);
newStatus.notifyObservers(&status);
+ if (config.position.gps_enabled)
+ positionModule->handleNewPosition();
}
}
int32_t GPS::runOnce()
{
+ if (!GPSInitFinished) {
+ if (!_serial_gps)
+ return disable();
+ if (!setup())
+ return 2000; // Setup failed, re-run in two seconds
+
+ // We have now loaded our saved preferences from flash
+ if (config.position.gps_enabled == false && config.position.fixed_position == false) {
+ return disable();
+ }
+ // ONCE we will factory reset the GPS for bug #327
+ if (!devicestate.did_gps_reset) {
+ LOG_WARN("GPS FactoryReset requested\n");
+ if (gps->factoryReset()) { // If we don't succeed try again next time
+ devicestate.did_gps_reset = true;
+ nodeDB.saveToDisk(SEGMENT_DEVICESTATE);
+ }
+ }
+ GPSInitFinished = true;
+ }
+
// Repeaters have no need for GPS
- if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER)
- disable();
+ if (config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
+ return disable();
+ }
if (whileIdle()) {
// if we have received valid NMEA claim we are connected
@@ -740,20 +623,22 @@ int32_t GPS::runOnce()
} else {
if ((config.position.gps_enabled == 1) && (gnssModel == GNSS_MODEL_UBLOX)) {
// reset the GPS on next bootup
- if (devicestate.did_gps_reset && (millis() > 60000) && !hasFlow()) {
+ if (devicestate.did_gps_reset && (millis() - lastWakeStartMsec > 60000) && !hasFlow()) {
LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n");
devicestate.did_gps_reset = false;
nodeDB.saveDeviceStateToDisk();
- disable(); // Stop the GPS thread as it can do nothing useful until next reboot.
+ return disable(); // Stop the GPS thread as it can do nothing useful until next reboot.
}
}
}
// If we are overdue for an update, turn on the GPS and at least publish the current status
uint32_t now = millis();
+ uint32_t timeAsleep = now - lastSleepStartMsec;
auto sleepTime = getSleepTime();
- if (!isAwake && sleepTime != UINT32_MAX && (now - lastSleepStartMsec) > sleepTime) {
+ if (!isAwake && (sleepTime != UINT32_MAX) &&
+ ((timeAsleep > sleepTime) || (isInPowersave && timeAsleep > (sleepTime - averageLockTime)))) {
// We now want to be awake - so wake up the GPS
setAwake(true);
}
@@ -761,11 +646,6 @@ int32_t GPS::runOnce()
// While we are awake
if (isAwake) {
// LOG_DEBUG("looking for location\n");
- if ((now - lastWhileActiveMsec) > 5000) {
- lastWhileActiveMsec = now;
- whileActive();
- }
-
// If we've already set time from the GPS, no need to ask the GPS
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
@@ -780,7 +660,6 @@ int32_t GPS::runOnce()
shouldPublish = true;
}
- // We've been awake too long - force sleep
now = millis();
auto wakeTime = getWakeTime();
bool tooLong = wakeTime != UINT32_MAX && (now - lastWakeStartMsec) > wakeTime;
@@ -805,26 +684,15 @@ int32_t GPS::runOnce()
// If state has changed do a publish
publishUpdate();
+
+ if (config.position.gps_enabled == false) // This should trigger if GPS is disabled but fixed_position is true
+ return disable();
+
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms
// if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake.
return isAwake ? GPS_THREAD_INTERVAL : 5000;
}
-void GPS::forceWake(bool on)
-{
- if (on) {
- LOG_DEBUG("Allowing GPS lock\n");
- // lastSleepStartMsec = 0; // Force an update ASAP
- wakeAllowed = true;
- } else {
- wakeAllowed = false;
-
- // Note: if the gps was already awake, we DO NOT shut it down, because we want to allow it to complete its lock
- // attempt even if we are in light sleep. Once the attempt succeeds (or times out) we'll then shut it down.
- // setAwake(false);
- }
-}
-
// clear the GPS rx buffer as quickly as possible
void GPS::clearBuffer()
{
@@ -833,73 +701,84 @@ void GPS::clearBuffer()
_serial_gps->read();
}
-/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
-int GPS::prepareSleep(void *unused)
-{
- LOG_INFO("GPS prepare sleep!\n");
- forceWake(false);
-
- return 0;
-}
-
/// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
int GPS::prepareDeepSleep(void *unused)
{
LOG_INFO("GPS deep sleep!\n");
- // For deep sleep we also want abandon any lock attempts (because we want minimum power)
- getSleepTime();
setAwake(false);
return 0;
}
-GnssModel_t GPS::probe()
+GnssModel_t GPS::probe(int serialSpeed)
{
- memset(&info, 0, sizeof(struct uBloxGnssModelInfo));
-// return immediately if the model is set by the variant.h file
-//#ifdef GPS_UBLOX (unless it's a ublox, because we might want to know the module info!
-// return GNSS_MODEL_UBLOX; think about removing this macro and return)
-#if defined(GPS_L76K)
- return GNSS_MODEL_MTK;
-#elif defined(GPS_UC6580)
- _serial_gps->updateBaudRate(115200);
- return GNSS_MODEL_UC6850;
+#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040)
+ _serial_gps->end();
+ _serial_gps->begin(serialSpeed);
#else
- uint8_t buffer[384] = {0};
+ if (_serial_gps->baudRate() != serialSpeed) {
+ LOG_DEBUG("Setting Baud to %i\n", serialSpeed);
+ _serial_gps->updateBaudRate(serialSpeed);
+ }
+#endif
+#ifdef GPS_DEBUG
+ for (int i = 0; i < 20; i++) {
+ getACK("$GP", 200);
+ }
+#endif
+ memset(&info, 0, sizeof(struct uBloxGnssModelInfo));
+ uint8_t buffer[768] = {0};
+ delay(100);
// Close all NMEA sentences , Only valid for MTK platform
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20);
// Get version information
+ clearBuffer();
_serial_gps->write("$PCAS06,0*1B\r\n");
- uint32_t startTimeout = millis() + 500;
- while (millis() < startTimeout) {
- if (_serial_gps->available()) {
- String ver = _serial_gps->readStringUntil('\r');
- // Get module info , If the correct header is returned,
- // it can be determined that it is the MTK chip
- int index = ver.indexOf("$");
- if (index != -1) {
- ver = ver.substring(index);
- if (ver.startsWith("$GPTXT,01,01,02,SW=")) {
- LOG_INFO("L76K GNSS init succeeded, using L76K GNSS Module\n");
- return GNSS_MODEL_MTK;
- }
- }
- }
+ if (getACK("$GPTXT,01,01,02,SW=", 500) == GNSS_RESPONSE_OK) {
+ LOG_INFO("L76K GNSS init succeeded, using L76K GNSS Module\n");
+ return GNSS_MODEL_MTK;
}
- uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x0E, 0x30};
+ uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
+ UBXChecksum(cfg_rate, sizeof(cfg_rate));
+ clearBuffer();
_serial_gps->write(cfg_rate, sizeof(cfg_rate));
// Check that the returned response class and message ID are correct
- if (!getAck(buffer, 384, 0x06, 0x08)) {
- LOG_WARN("Failed to find UBlox & MTK GNSS Module\n");
+ GPS_RESPONSE response = getACK(0x06, 0x08, 750);
+ if (response == GNSS_RESPONSE_NONE) {
+ LOG_WARN("Failed to find UBlox & MTK GNSS Module using baudrate %d\n", serialSpeed);
return GNSS_MODEL_UNKNOWN;
+ } else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
+ LOG_INFO("UBlox Frame Errors using baudrate %d\n", serialSpeed);
+ } else if (response == GNSS_RESPONSE_OK) {
+ LOG_INFO("Found a UBlox Module using baudrate %d\n", serialSpeed);
}
+
+ // tips: NMEA Only should not be set here, otherwise initializing Ublox gnss module again after
+ // setting will not output command messages in UART1, resulting in unrecognized module information
+ if (serialSpeed != 9600) {
+ // Set the UART port to 9600
+ uint8_t _message_prt[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00, 0x01, 0x00, 0x00, 0x00, 0xD0, 0x08, 0x00, 0x00,
+ 0x80, 0x25, 0x00, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ UBXChecksum(_message_prt, sizeof(_message_prt));
+ _serial_gps->write(_message_prt, sizeof(_message_prt));
+ delay(500);
+ serialSpeed = 9600;
+#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_RP2040)
+ _serial_gps->end();
+ _serial_gps->begin(serialSpeed);
+#else
+ _serial_gps->updateBaudRate(serialSpeed);
+#endif
+ delay(200);
+ }
+
memset(buffer, 0, sizeof(buffer));
- byte _message_MONVER[8] = {
+ uint8_t _message_MONVER[8] = {
0xB5, 0x62, // Sync message for UBX protocol
0x0A, 0x04, // Message class and ID (UBX-MON-VER)
0x00, 0x00, // Length of payload (we're asking for an answer, so no payload)
@@ -907,9 +786,10 @@ GnssModel_t GPS::probe()
};
// Get Ublox gnss module hardware and software info
UBXChecksum(_message_MONVER, sizeof(_message_MONVER));
+ clearBuffer();
_serial_gps->write(_message_MONVER, sizeof(_message_MONVER));
- uint16_t len = getAck(buffer, 384, 0x0A, 0x04);
+ uint16_t len = getACK(buffer, sizeof(buffer), 0x0A, 0x04, 1200);
if (len) {
// LOG_DEBUG("monver reply size = %d\n", len);
uint16_t position = 0;
@@ -918,13 +798,13 @@ GnssModel_t GPS::probe()
position++;
}
for (int i = 0; i < 10; i++) {
- info.hwVersion[i] = buffer[position - 1];
+ info.hwVersion[i] = buffer[position];
position++;
}
while (len >= position + 30) {
for (int i = 0; i < 30; i++) {
- info.extension[info.extensionNo][i] = buffer[position - 1];
+ info.extension[info.extensionNo][i] = buffer[position];
position++;
}
info.extensionNo++;
@@ -934,7 +814,6 @@ GnssModel_t GPS::probe()
LOG_DEBUG("Module Info : \n");
LOG_DEBUG("Soft version: %s\n", info.swVersion);
- LOG_DEBUG("first char is %c\n", (char)info.swVersion[0]);
LOG_DEBUG("Hard version: %s\n", info.hwVersion);
LOG_DEBUG("Extensions:%d\n", info.extensionNo);
for (int i = 0; i < info.extensionNo; i++) {
@@ -953,7 +832,7 @@ GnssModel_t GPS::probe()
} else {
LOG_INFO("UBlox GNSS init succeeded, using UBlox GNSS Module\n");
}
- } else if (!strncmp(info.extension[i], "PROTVER=", 8)) {
+ } else if (!strncmp(info.extension[i], "PROTVER", 7)) {
char *ptr = nullptr;
memset(buffer, 0, sizeof(buffer));
strncpy((char *)buffer, &(info.extension[i][8]), sizeof(buffer));
@@ -969,37 +848,349 @@ GnssModel_t GPS::probe()
}
return GNSS_MODEL_UBLOX;
-#endif
}
-#if HAS_GPS
-#include "NMEAGPS.h"
-#endif
-
-GPS *createGps()
+GPS *GPS::createGps()
{
+ int8_t _rx_gpio = config.position.rx_gpio;
+ int8_t _tx_gpio = config.position.tx_gpio;
+ int8_t _en_gpio = config.position.gps_en_gpio;
+#if defined(HAS_GPS) && !defined(ARCH_ESP32)
+ _rx_gpio = 1; // We only specify GPS serial ports on ESP32. Otherwise, these are just flags.
+ _tx_gpio = 1;
+#endif
+#if defined(GPS_RX_PIN)
+ if (!_rx_gpio)
+ _rx_gpio = GPS_RX_PIN;
+#endif
+#if defined(GPS_TX_PIN)
+ if (!_tx_gpio)
+ _tx_gpio = GPS_TX_PIN;
+#endif
+#if defined(PIN_GPS_EN)
+ if (!_en_gpio)
+ _en_gpio = PIN_GPS_EN;
+#endif
+ if (!_rx_gpio || !_serial_gps) // Configured to have no GPS at all
+ return nullptr;
-#if !HAS_GPS
- return nullptr;
-#else
- if (config.position.gps_enabled) {
-#ifdef GPS_ALTITUDE_HAE
- LOG_DEBUG("Using HAE altitude model\n");
-#else
- LOG_DEBUG("Using MSL altitude model\n");
-#endif
- if (GPS::_serial_gps) {
- // Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all.
- // Just assume NMEA at 9600 baud.
- GPS *new_gps = new NMEAGPS();
- new_gps->setup();
- return new_gps;
- }
- } else {
- GPS *new_gps = new NMEAGPS();
- new_gps->setup();
- return new_gps;
+ GPS *new_gps = new GPS;
+ new_gps->rx_gpio = _rx_gpio;
+ new_gps->tx_gpio = _tx_gpio;
+ new_gps->en_gpio = _en_gpio;
+
+ if (_en_gpio != 0) {
+ LOG_DEBUG("Setting %d to output.\n", _en_gpio);
+ digitalWrite(_en_gpio, !GPS_EN_ACTIVE);
+ pinMode(_en_gpio, OUTPUT);
}
- return nullptr;
+
+#ifdef PIN_GPS_PPS
+ // pulse per second
+ pinMode(PIN_GPS_PPS, INPUT);
#endif
+
+// Currently disabled per issue #525 (TinyGPS++ crash bug)
+// when fixed upstream, can be un-disabled to enable 3D FixType and PDOP
+#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
+ // see NMEAGPS.h
+ gsafixtype.begin(reader, NMEA_MSG_GXGSA, 2);
+ gsapdop.begin(reader, NMEA_MSG_GXGSA, 15);
+ LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n");
+#endif
+
+ if (config.position.gps_enabled) {
+ new_gps->setGPSPower(true, false);
+ }
+
+#ifdef PIN_GPS_RESET
+ digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
+ pinMode(PIN_GPS_RESET, OUTPUT);
+ delay(10);
+ digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
+#endif
+ new_gps->setAwake(true); // Wake GPS power before doing any init
+
+ if (_serial_gps) {
+#ifdef ARCH_ESP32
+ // In esp32 framework, setRxBufferSize needs to be initialized before Serial
+ _serial_gps->setRxBufferSize(SERIAL_BUFFER_SIZE); // the default is 256
+#endif
+
+// ESP32 has a special set of parameters vs other arduino ports
+#if defined(ARCH_ESP32)
+ LOG_DEBUG("Using GPIO%d for GPS RX\n", new_gps->rx_gpio);
+ LOG_DEBUG("Using GPIO%d for GPS TX\n", new_gps->tx_gpio);
+ _serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, new_gps->rx_gpio, new_gps->tx_gpio);
+
+#else
+ _serial_gps->begin(GPS_BAUDRATE);
+#endif
+
+ /*
+ * T-Beam-S3-Core will be preset to use gps Probe here, and other boards will not be changed first
+ */
+#if defined(GPS_UC6580)
+ _serial_gps->updateBaudRate(115200);
+#endif
+ }
+ return new_gps;
+}
+
+static int32_t toDegInt(RawDegrees d)
+{
+ int32_t degMult = 10000000; // 1e7
+ int32_t r = d.deg * degMult + d.billionths / 100;
+ if (d.negative)
+ r *= -1;
+ return r;
+}
+
+bool GPS::factoryReset()
+{
+#ifdef PIN_GPS_REINIT
+ // The L76K GNSS on the T-Echo requires the RESET pin to be pulled LOW
+ digitalWrite(PIN_GPS_REINIT, 0);
+ pinMode(PIN_GPS_REINIT, OUTPUT);
+ delay(150); // The L76K datasheet calls for at least 100MS delay
+ digitalWrite(PIN_GPS_REINIT, 1);
+#endif
+
+ // send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's UBLOX.
+ // Factory Reset
+ byte _message_reset[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFB, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x17, 0x2B, 0x7E};
+ _serial_gps->write(_message_reset, sizeof(_message_reset));
+ delay(1000);
+ return true;
+}
+
+/**
+ * Perform any processing that should be done only while the GPS is awake and looking for a fix.
+ * Override this method to check for new locations
+ *
+ * @return true if we've acquired a new location
+ */
+bool GPS::lookForTime()
+{
+ auto ti = reader.time;
+ auto d = reader.date;
+ if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
+ /* Convert to unix time
+The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
+(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
+*/
+ struct tm t;
+ t.tm_sec = ti.second();
+ t.tm_min = ti.minute();
+ t.tm_hour = ti.hour();
+ t.tm_mday = d.day();
+ t.tm_mon = d.month() - 1;
+ t.tm_year = d.year() - 1900;
+ t.tm_isdst = false;
+ if (t.tm_mon > -1) {
+ LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
+ t.tm_sec);
+ perhapsSetRTC(RTCQualityGPS, t);
+ return true;
+ } else
+ return false;
+ } else
+ return false;
+}
+
+/**
+ * Perform any processing that should be done only while the GPS is awake and looking for a fix.
+ * Override this method to check for new locations
+ *
+ * @return true if we've acquired a new location
+ */
+bool GPS::lookForLocation()
+{
+ // By default, TinyGPS++ does not parse GPGSA lines, which give us
+ // the 2D/3D fixType (see NMEAGPS.h)
+ // At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
+ fixQual = reader.fixQuality();
+
+#ifndef TINYGPS_OPTION_NO_STATISTICS
+ if (reader.failedChecksum() > lastChecksumFailCount) {
+ LOG_WARN("Warning, %u new GPS checksum failures, for a total of %u.\n", reader.failedChecksum() - lastChecksumFailCount,
+ reader.failedChecksum());
+ lastChecksumFailCount = reader.failedChecksum();
+ }
+#endif
+
+#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
+ fixType = atoi(gsafixtype.value()); // will set to zero if no data
+ // LOG_DEBUG("FIX QUAL=%d, TYPE=%d\n", fixQual, fixType);
+#endif
+
+ // check if GPS has an acceptable lock
+ if (!hasLock())
+ return false;
+
+#ifdef GPS_EXTRAVERBOSE
+ LOG_DEBUG("AGE: LOC=%d FIX=%d DATE=%d TIME=%d\n", reader.location.age(),
+#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
+ gsafixtype.age(),
+#else
+ 0,
+#endif
+ reader.date.age(), reader.time.age());
+#endif // GPS_EXTRAVERBOSE
+
+ // check if a complete GPS solution set is available for reading
+ // tinyGPSDatum::age() also includes isValid() test
+ // FIXME
+ if (!((reader.location.age() < GPS_SOL_EXPIRY_MS) &&
+#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
+ (gsafixtype.age() < GPS_SOL_EXPIRY_MS) &&
+#endif
+ (reader.time.age() < GPS_SOL_EXPIRY_MS) && (reader.date.age() < GPS_SOL_EXPIRY_MS))) {
+ LOG_WARN("SOME data is TOO OLD: LOC %u, TIME %u, DATE %u\n", reader.location.age(), reader.time.age(), reader.date.age());
+ return false;
+ }
+
+ // Is this a new point or are we re-reading the previous one?
+ if (!reader.location.isUpdated())
+ return false;
+
+ // We know the solution is fresh and valid, so just read the data
+ auto loc = reader.location.value();
+
+ // Bail out EARLY to avoid overwriting previous good data (like #857)
+ if (toDegInt(loc.lat) > 900000000) {
+#ifdef GPS_EXTRAVERBOSE
+ LOG_DEBUG("Bail out EARLY on LAT %i\n", toDegInt(loc.lat));
+#endif
+ return false;
+ }
+ if (toDegInt(loc.lng) > 1800000000) {
+#ifdef GPS_EXTRAVERBOSE
+ LOG_DEBUG("Bail out EARLY on LNG %i\n", toDegInt(loc.lng));
+#endif
+ return false;
+ }
+
+ p.location_source = meshtastic_Position_LocSource_LOC_INTERNAL;
+
+ // Dilution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
+#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
+ p.HDOP = reader.hdop.value();
+ p.PDOP = TinyGPSPlus::parseDecimal(gsapdop.value());
+ // LOG_DEBUG("PDOP=%d, HDOP=%d\n", p.PDOP, p.HDOP);
+#else
+ // FIXME! naive PDOP emulation (assumes VDOP==HDOP)
+ // correct formula is PDOP = SQRT(HDOP^2 + VDOP^2)
+ p.HDOP = reader.hdop.value();
+ p.PDOP = 1.41 * reader.hdop.value();
+#endif
+
+ // Discard incomplete or erroneous readings
+ if (reader.hdop.value() == 0) {
+ LOG_WARN("BOGUS hdop.value() REJECTED: %d\n", reader.hdop.value());
+ return false;
+ }
+
+ p.latitude_i = toDegInt(loc.lat);
+ p.longitude_i = toDegInt(loc.lng);
+
+ p.altitude_geoidal_separation = reader.geoidHeight.meters();
+ p.altitude_hae = reader.altitude.meters() + p.altitude_geoidal_separation;
+ p.altitude = reader.altitude.meters();
+
+ p.fix_quality = fixQual;
+#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
+ p.fix_type = fixType;
+#endif
+
+ // positional timestamp
+ struct tm t;
+ t.tm_sec = reader.time.second();
+ t.tm_min = reader.time.minute();
+ t.tm_hour = reader.time.hour();
+ t.tm_mday = reader.date.day();
+ t.tm_mon = reader.date.month() - 1;
+ t.tm_year = reader.date.year() - 1900;
+ t.tm_isdst = false;
+ p.timestamp = mktime(&t);
+
+ // Nice to have, if available
+ if (reader.satellites.isUpdated()) {
+ p.sats_in_view = reader.satellites.value();
+ }
+
+ if (reader.course.isUpdated() && reader.course.isValid()) {
+ if (reader.course.value() < 36000) { // sanity check
+ p.ground_track =
+ reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
+ } else {
+ LOG_WARN("BOGUS course.value() REJECTED: %d\n", reader.course.value());
+ }
+ }
+
+ if (reader.speed.isUpdated() && reader.speed.isValid()) {
+ p.ground_speed = reader.speed.kmph();
+ }
+
+ return true;
+}
+
+bool GPS::hasLock()
+{
+ // Using GPGGA fix quality indicator
+ if (fixQual >= 1 && fixQual <= 5) {
+#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
+ // Use GPGSA fix type 2D/3D (better) if available
+ if (fixType == 3 || fixType == 0) // zero means "no data received"
+#endif
+ return true;
+ }
+
+ return false;
+}
+
+bool GPS::hasFlow()
+{
+ return reader.passedChecksum() > 0;
+}
+
+bool GPS::whileIdle()
+{
+ bool isValid = false;
+ if (!isAwake) {
+ clearBuffer();
+ return isAwake;
+ }
+#ifdef SERIAL_BUFFER_SIZE
+ if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) {
+ LOG_WARN("GPS Buffer full with %u bytes waiting. Flushing to avoid corruption.\n", _serial_gps->available());
+ clearBuffer();
+ }
+#endif
+ // if (_serial_gps->available() > 0)
+ // LOG_DEBUG("GPS Bytes Waiting: %u\n", _serial_gps->available());
+ // First consume any chars that have piled up at the receiver
+ while (_serial_gps->available() > 0) {
+ int c = _serial_gps->read();
+ // LOG_DEBUG("%c", c);
+ isValid |= reader.encode(c);
+ }
+
+ return isValid;
+}
+void GPS::enable()
+{
+ enabled = true;
+ setInterval(GPS_THREAD_INTERVAL);
+ setAwake(true);
+}
+
+int32_t GPS::disable()
+{
+ enabled = false;
+ setInterval(INT32_MAX);
+ setAwake(false);
+
+ return INT32_MAX;
}
\ No newline at end of file
diff --git a/src/gps/GPS.h b/src/gps/GPS.h
index 89f6c491d..4269acf2d 100644
--- a/src/gps/GPS.h
+++ b/src/gps/GPS.h
@@ -2,7 +2,16 @@
#include "GPSStatus.h"
#include "Observer.h"
+#include "TinyGPS++.h"
#include "concurrency/OSThread.h"
+#include "input/RotaryEncoderInterruptImpl1.h"
+#include "input/UpDownInterruptImpl1.h"
+#include "modules/PositionModule.h"
+
+// Allow defining the polarity of the ENABLE output. default is active high
+#ifndef GPS_EN_ACTIVE
+#define GPS_EN_ACTIVE 1
+#endif
struct uBloxGnssModelInfo {
char swVersion[30];
@@ -18,6 +27,13 @@ typedef enum {
GNSS_MODEL_UNKNOWN,
} GnssModel_t;
+typedef enum {
+ GNSS_RESPONSE_NONE,
+ GNSS_RESPONSE_NAK,
+ GNSS_RESPONSE_FRAME_ERRORS,
+ GNSS_RESPONSE_OK,
+} GPS_RESPONSE;
+
// Generate a string representation of DOP
const char *getDOPString(uint32_t dop);
@@ -28,8 +44,29 @@ const char *getDOPString(uint32_t dop);
*/
class GPS : private concurrency::OSThread
{
+ TinyGPSPlus reader;
+ uint8_t fixQual = 0; // fix quality from GPGGA
+ uint32_t lastChecksumFailCount = 0;
+
+#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
+ // (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
+ // via optional feature "custom fields", currently disabled (bug #525)
+ TinyGPSCustom gsafixtype; // custom extract fix type from GPGSA
+ TinyGPSCustom gsapdop; // custom extract PDOP from GPGSA
+ uint8_t fixType = 0; // fix type from GPGSA
+#endif
private:
- uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0, lastWhileActiveMsec = 0;
+ uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0;
+ const int serialSpeeds[6] = {9600, 4800, 38400, 57600, 115200, 9600};
+
+ uint32_t rx_gpio = 0;
+ uint32_t tx_gpio = 0;
+ uint32_t en_gpio = 0;
+ int32_t averageLockTime = 0;
+ uint32_t GPSCycles = 0;
+
+ int speedSelect = 0;
+ int probeTries = 2;
/**
* hasValidLocation - indicates that the position variables contain a complete
@@ -39,15 +76,17 @@ class GPS : private concurrency::OSThread
bool isAwake = false; // true if we want a location right now
- bool wakeAllowed = true; // false if gps must be forced to sleep regardless of what time it is
+ bool isInPowersave = false;
bool shouldPublish = false; // If we've changed GPS state, this will force a publish the next loop()
bool hasGPS = false; // Do we have a GPS we are talking to
+ bool GPSInitFinished = false; // Init thread finished?
+ bool GPSInitStarted = false; // Init thread finished?
+
uint8_t numSatellites = 0;
- CallbackObserver notifySleepObserver = CallbackObserver(this, &GPS::prepareSleep);
CallbackObserver notifyDeepSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep);
CallbackObserver notifyGPSSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep);
@@ -55,6 +94,24 @@ class GPS : private concurrency::OSThread
/** If !NULL we will use this serial port to construct our GPS */
static HardwareSerial *_serial_gps;
+ static const uint8_t _message_PMREQ[];
+ static const uint8_t _message_CFG_RXM_PSM[];
+ static const uint8_t _message_CFG_RXM_ECO[];
+ static const uint8_t _message_CFG_PM2[];
+ static const uint8_t _message_GNSS_7[];
+ static const uint8_t _message_GNSS[];
+ static const uint8_t _message_JAM[];
+ static const uint8_t _message_NAVX5[];
+ static const uint8_t _message_1HZ[];
+ static const uint8_t _message_GGL[];
+ static const uint8_t _message_GSA[];
+ static const uint8_t _message_GSV[];
+ static const uint8_t _message_VTG[];
+ static const uint8_t _message_RMC[];
+ static const uint8_t _message_GGA[];
+ static const uint8_t _message_PMS[];
+ static const uint8_t _message_SAVE[];
+
meshtastic_Position p = meshtastic_Position_init_default;
GPS() : concurrency::OSThread("GPS") {}
@@ -69,6 +126,14 @@ class GPS : private concurrency::OSThread
*/
virtual bool setup();
+ // re-enable the thread
+ void enable();
+
+ // Disable the thread
+ int32_t disable() override;
+
+ void setGPSPower(bool on, bool standbyOnly);
+
/// Returns true if we have acquired GPS lock.
virtual bool hasLock();
@@ -80,71 +145,18 @@ class GPS : private concurrency::OSThread
bool isPowerSaving() const { return !config.position.gps_enabled; }
- /**
- * Restart our lock attempt - try to get and broadcast a GPS reading ASAP
- * called after the CPU wakes from light-sleep state
- *
- * Or set to false, to disallow any sort of waking
- * */
- void forceWake(bool on);
-
- // Some GPS modules (ublock) require factory reset
- virtual bool factoryReset() { return true; }
-
// Empty the input buffer as quickly as possible
void clearBuffer();
- protected:
- /// Do gps chipset specific init, return true for success
- virtual bool setupGPS();
+ // Create a ublox packet for editing in memory
+ uint8_t makeUBXPacket(uint8_t class_id, uint8_t msg_id, uint8_t payload_size, const uint8_t *msg);
- /// If possible force the GPS into sleep/low power mode
- virtual void sleep();
+ // scratch space for creating ublox packets
+ uint8_t UBXscratch[250] = {0};
- /// wake the GPS into normal operation mode
- virtual void wake();
-
- /** Subclasses should look for serial rx characters here and feed it to their GPS parser
- *
- * Return true if we received a valid message from the GPS
- */
- virtual bool whileIdle() = 0;
-
- /** Idle processing while GPS is looking for lock, called once per secondish */
- virtual void whileActive() {}
-
- /**
- * Perform any processing that should be done only while the GPS is awake and looking for a fix.
- * Override this method to check for new locations
- *
- * @return true if we've acquired a time
- */
- virtual bool lookForTime() = 0;
-
- /**
- * Perform any processing that should be done only while the GPS is awake and looking for a fix.
- * Override this method to check for new locations
- *
- * @return true if we've acquired a new location
- */
- virtual bool lookForLocation() = 0;
-
- /// Record that we have a GPS
- void setConnected();
-
- void setNumSatellites(uint8_t n);
-
- private:
- /// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs
- /// always returns 0 to indicate okay to sleep
- int prepareSleep(void *unused);
-
- /// Prepare the GPS for the cpu entering deep sleep, expect to be gone for at least 100s of msecs
- /// always returns 0 to indicate okay to sleep
- int prepareDeepSleep(void *unused);
-
- // Calculate checksum
- void UBXChecksum(byte *message, size_t length);
+ int getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis);
+ GPS_RESPONSE getACK(uint8_t c, uint8_t i, uint32_t waitMillis);
+ GPS_RESPONSE getACK(const char *message, uint32_t waitMillis);
/**
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
@@ -152,6 +164,59 @@ class GPS : private concurrency::OSThread
* calls sleep/wake
*/
void setAwake(bool on);
+ virtual bool factoryReset();
+
+ // Creates an instance of the GPS class.
+ // Returns the new instance or null if the GPS is not present.
+ static GPS *createGps();
+
+ protected:
+ /**
+ * Perform any processing that should be done only while the GPS is awake and looking for a fix.
+ * Override this method to check for new locations
+ *
+ * @return true if we've acquired a time
+ */
+
+ /**
+ * Perform any processing that should be done only while the GPS is awake and looking for a fix.
+ * Override this method to check for new locations
+ *
+ * @return true if we've acquired a new location
+ */
+
+ /// Record that we have a GPS
+ void setConnected();
+
+ /** Subclasses should look for serial rx characters here and feed it to their GPS parser
+ *
+ * Return true if we received a valid message from the GPS
+ */
+ virtual bool whileIdle();
+
+ /**
+ * Perform any processing that should be done only while the GPS is awake and looking for a fix.
+ * Override this method to check for new locations
+ *
+ * @return true if we've acquired a time
+ */
+ virtual bool lookForTime();
+
+ /**
+ * Perform any processing that should be done only while the GPS is awake and looking for a fix.
+ * Override this method to check for new locations
+ *
+ * @return true if we've acquired a new location
+ */
+ virtual bool lookForLocation();
+
+ private:
+ /// Prepare the GPS for the cpu entering deep sleep, expect to be gone for at least 100s of msecs
+ /// always returns 0 to indicate okay to sleep
+ int prepareDeepSleep(void *unused);
+
+ // Calculate checksum
+ void UBXChecksum(uint8_t *message, size_t length);
/** Get how long we should stay looking for each aquisition
*/
@@ -161,8 +226,6 @@ class GPS : private concurrency::OSThread
*/
uint32_t getSleepTime() const;
- bool getACK(uint8_t c, uint8_t i);
-
/**
* Tell users we have new GPS readings
*/
@@ -172,9 +235,7 @@ class GPS : private concurrency::OSThread
// Get GNSS model
String getNMEA();
- GnssModel_t probe();
-
- int getAck(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID);
+ GnssModel_t probe(int serialSpeed);
// delay counter to allow more sats before fixed position stops GPS thread
uint8_t fixeddelayCtr = 0;
@@ -183,8 +244,4 @@ class GPS : private concurrency::OSThread
GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN;
};
-// Creates an instance of the GPS class.
-// Returns the new instance or null if the GPS is not present.
-GPS *createGps();
-
extern GPS *gps;
\ No newline at end of file
diff --git a/src/gps/NMEAGPS.cpp b/src/gps/NMEAGPS.cpp
deleted file mode 100644
index 202185a3b..000000000
--- a/src/gps/NMEAGPS.cpp
+++ /dev/null
@@ -1,272 +0,0 @@
-#include "NMEAGPS.h"
-#include "RTC.h"
-#include "configuration.h"
-
-#include
-
-// GPS solutions older than this will be rejected - see TinyGPSDatum::age()
-#define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway
-#define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc)
-
-static int32_t toDegInt(RawDegrees d)
-{
- int32_t degMult = 10000000; // 1e7
- int32_t r = d.deg * degMult + d.billionths / 100;
- if (d.negative)
- r *= -1;
- return r;
-}
-
-bool NMEAGPS::factoryReset()
-{
-#ifdef PIN_GPS_REINIT
- // The L76K GNSS on the T-Echo requires the RESET pin to be pulled LOW
- digitalWrite(PIN_GPS_REINIT, 0);
- pinMode(PIN_GPS_REINIT, OUTPUT);
- delay(150); // The L76K datasheet calls for at least 100MS delay
- digitalWrite(PIN_GPS_REINIT, 1);
-#endif
-
- // send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's UBLOX.
- // Factory Reset
- byte _message_reset[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFB, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x17, 0x2B, 0x7E};
- _serial_gps->write(_message_reset, sizeof(_message_reset));
- delay(1000);
- return true;
-}
-
-bool NMEAGPS::setupGPS()
-{
- GPS::setupGPS();
-
-#ifdef PIN_GPS_PPS
- // pulse per second
- // FIXME - move into shared GPS code
- pinMode(PIN_GPS_PPS, INPUT);
-#endif
-
-// Currently disabled per issue #525 (TinyGPS++ crash bug)
-// when fixed upstream, can be un-disabled to enable 3D FixType and PDOP
-#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
- // see NMEAGPS.h
- gsafixtype.begin(reader, NMEA_MSG_GXGSA, 2);
- gsapdop.begin(reader, NMEA_MSG_GXGSA, 15);
- LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n");
-#else
- LOG_DEBUG("GxGSA NOT available\n");
-#endif
-
- return true;
-}
-
-/**
- * Perform any processing that should be done only while the GPS is awake and looking for a fix.
- * Override this method to check for new locations
- *
- * @return true if we've acquired a new location
- */
-bool NMEAGPS::lookForTime()
-{
- auto ti = reader.time;
- auto d = reader.date;
- if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
- /* Convert to unix time
-The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
-(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
-*/
- struct tm t;
- t.tm_sec = ti.second();
- t.tm_min = ti.minute();
- t.tm_hour = ti.hour();
- t.tm_mday = d.day();
- t.tm_mon = d.month() - 1;
- t.tm_year = d.year() - 1900;
- t.tm_isdst = false;
- if (t.tm_mon > -1) {
- LOG_DEBUG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min,
- t.tm_sec);
- perhapsSetRTC(RTCQualityGPS, t);
- return true;
- } else
- return false;
- } else
- return false;
-}
-
-/**
- * Perform any processing that should be done only while the GPS is awake and looking for a fix.
- * Override this method to check for new locations
- *
- * @return true if we've acquired a new location
- */
-bool NMEAGPS::lookForLocation()
-{
- // By default, TinyGPS++ does not parse GPGSA lines, which give us
- // the 2D/3D fixType (see NMEAGPS.h)
- // At a minimum, use the fixQuality indicator in GPGGA (FIXME?)
- fixQual = reader.fixQuality();
-
-#ifndef TINYGPS_OPTION_NO_STATISTICS
- if (reader.failedChecksum() > lastChecksumFailCount) {
- LOG_WARN("Warning, %u new GPS checksum failures, for a total of %u.\n", reader.failedChecksum() - lastChecksumFailCount,
- reader.failedChecksum());
- lastChecksumFailCount = reader.failedChecksum();
- }
-#endif
-
-#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
- fixType = atoi(gsafixtype.value()); // will set to zero if no data
- // LOG_DEBUG("FIX QUAL=%d, TYPE=%d\n", fixQual, fixType);
-#endif
-
- // check if GPS has an acceptable lock
- if (!hasLock())
- return false;
-
-#ifdef GPS_EXTRAVERBOSE
- LOG_DEBUG("AGE: LOC=%d FIX=%d DATE=%d TIME=%d\n", reader.location.age(),
-#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
- gsafixtype.age(),
-#else
- 0,
-#endif
- reader.date.age(), reader.time.age());
-#endif // GPS_EXTRAVERBOSE
-
- // check if a complete GPS solution set is available for reading
- // tinyGPSDatum::age() also includes isValid() test
- // FIXME
- if (!((reader.location.age() < GPS_SOL_EXPIRY_MS) &&
-#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
- (gsafixtype.age() < GPS_SOL_EXPIRY_MS) &&
-#endif
- (reader.time.age() < GPS_SOL_EXPIRY_MS) && (reader.date.age() < GPS_SOL_EXPIRY_MS))) {
- LOG_WARN("SOME data is TOO OLD: LOC %u, TIME %u, DATE %u\n", reader.location.age(), reader.time.age(), reader.date.age());
- return false;
- }
-
- // Is this a new point or are we re-reading the previous one?
- if (!reader.location.isUpdated())
- return false;
-
- // We know the solution is fresh and valid, so just read the data
- auto loc = reader.location.value();
-
- // Bail out EARLY to avoid overwriting previous good data (like #857)
- if (toDegInt(loc.lat) > 900000000) {
-#ifdef GPS_EXTRAVERBOSE
- LOG_DEBUG("Bail out EARLY on LAT %i\n", toDegInt(loc.lat));
-#endif
- return false;
- }
- if (toDegInt(loc.lng) > 1800000000) {
-#ifdef GPS_EXTRAVERBOSE
- LOG_DEBUG("Bail out EARLY on LNG %i\n", toDegInt(loc.lng));
-#endif
- return false;
- }
-
- p.location_source = meshtastic_Position_LocSource_LOC_INTERNAL;
-
- // Dilution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
-#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
- p.HDOP = reader.hdop.value();
- p.PDOP = TinyGPSPlus::parseDecimal(gsapdop.value());
- // LOG_DEBUG("PDOP=%d, HDOP=%d\n", p.PDOP, p.HDOP);
-#else
- // FIXME! naive PDOP emulation (assumes VDOP==HDOP)
- // correct formula is PDOP = SQRT(HDOP^2 + VDOP^2)
- p.HDOP = reader.hdop.value();
- p.PDOP = 1.41 * reader.hdop.value();
-#endif
-
- // Discard incomplete or erroneous readings
- if (reader.hdop.value() == 0) {
- LOG_WARN("BOGUS hdop.value() REJECTED: %d\n", reader.hdop.value());
- return false;
- }
-
- p.latitude_i = toDegInt(loc.lat);
- p.longitude_i = toDegInt(loc.lng);
-
- p.altitude_geoidal_separation = reader.geoidHeight.meters();
- p.altitude_hae = reader.altitude.meters() + p.altitude_geoidal_separation;
- p.altitude = reader.altitude.meters();
-
- p.fix_quality = fixQual;
-#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
- p.fix_type = fixType;
-#endif
-
- // positional timestamp
- struct tm t;
- t.tm_sec = reader.time.second();
- t.tm_min = reader.time.minute();
- t.tm_hour = reader.time.hour();
- t.tm_mday = reader.date.day();
- t.tm_mon = reader.date.month() - 1;
- t.tm_year = reader.date.year() - 1900;
- t.tm_isdst = false;
- p.timestamp = mktime(&t);
-
- // Nice to have, if available
- if (reader.satellites.isUpdated()) {
- p.sats_in_view = reader.satellites.value();
- }
-
- if (reader.course.isUpdated() && reader.course.isValid()) {
- if (reader.course.value() < 36000) { // sanity check
- p.ground_track =
- reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
- } else {
- LOG_WARN("BOGUS course.value() REJECTED: %d\n", reader.course.value());
- }
- }
-
- if (reader.speed.isUpdated() && reader.speed.isValid()) {
- p.ground_speed = reader.speed.kmph();
- }
-
- return true;
-}
-
-bool NMEAGPS::hasLock()
-{
- // Using GPGGA fix quality indicator
- if (fixQual >= 1 && fixQual <= 5) {
-#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
- // Use GPGSA fix type 2D/3D (better) if available
- if (fixType == 3 || fixType == 0) // zero means "no data received"
-#endif
- return true;
- }
-
- return false;
-}
-
-bool NMEAGPS::hasFlow()
-{
- return reader.passedChecksum() > 0;
-}
-
-bool NMEAGPS::whileIdle()
-{
- bool isValid = false;
-#ifdef SERIAL_BUFFER_SIZE
- if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) {
- LOG_WARN("GPS Buffer full with %u bytes waiting. Flushing to avoid corruption.\n", _serial_gps->available());
- clearBuffer();
- }
-#endif
- // if (_serial_gps->available() > 0)
- // LOG_DEBUG("GPS Bytes Waiting: %u\n", _serial_gps->available());
- // First consume any chars that have piled up at the receiver
- while (_serial_gps->available() > 0) {
- int c = _serial_gps->read();
- // LOG_DEBUG("%c", c);
- isValid |= reader.encode(c);
- }
-
- return isValid;
-}
\ No newline at end of file
diff --git a/src/gps/NMEAGPS.h b/src/gps/NMEAGPS.h
deleted file mode 100644
index 85521077a..000000000
--- a/src/gps/NMEAGPS.h
+++ /dev/null
@@ -1,57 +0,0 @@
-#pragma once
-
-#include "GPS.h"
-#include "Observer.h"
-#include "TinyGPS++.h"
-
-/**
- * A gps class thatreads from a NMEA GPS stream (and FIXME - eventually keeps the gps powered down except when reading)
- *
- * When new data is available it will notify observers.
- */
-class NMEAGPS : public GPS
-{
- TinyGPSPlus reader;
- uint8_t fixQual = 0; // fix quality from GPGGA
- uint32_t lastChecksumFailCount = 0;
-
-#ifndef TINYGPS_OPTION_NO_CUSTOM_FIELDS
- // (20210908) TinyGps++ can only read the GPGSA "FIX TYPE" field
- // via optional feature "custom fields", currently disabled (bug #525)
- TinyGPSCustom gsafixtype; // custom extract fix type from GPGSA
- TinyGPSCustom gsapdop; // custom extract PDOP from GPGSA
- uint8_t fixType = 0; // fix type from GPGSA
-#endif
-
- public:
- virtual bool setupGPS() override;
-
- virtual bool factoryReset() override;
-
- protected:
- /** Subclasses should look for serial rx characters here and feed it to their GPS parser
- *
- * Return true if we received a valid message from the GPS
- */
- virtual bool whileIdle() override;
-
- /**
- * Perform any processing that should be done only while the GPS is awake and looking for a fix.
- * Override this method to check for new locations
- *
- * @return true if we've acquired a time
- */
- virtual bool lookForTime() override;
-
- /**
- * Perform any processing that should be done only while the GPS is awake and looking for a fix.
- * Override this method to check for new locations
- *
- * @return true if we've acquired a new location
- */
- virtual bool lookForLocation() override;
-
- virtual bool hasLock() override;
-
- virtual bool hasFlow() override;
-};
\ No newline at end of file
diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp
index 2bfb66e12..ef438a7dd 100644
--- a/src/gps/RTC.cpp
+++ b/src/gps/RTC.cpp
@@ -103,9 +103,8 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
bool shouldSet;
if (q > currentQuality) {
- currentQuality = q;
shouldSet = true;
- LOG_DEBUG("Upgrading time to RTC %ld secs (quality %d)\n", tv->tv_sec, q);
+ LOG_DEBUG("Upgrading time to quality %d\n", q);
} else if (q == RTCQualityGPS && (now - lastSetMsec) > (12 * 60 * 60 * 1000UL)) {
// Every 12 hrs we will slam in a new GPS time, to correct for local RTC clock drift
shouldSet = true;
@@ -114,12 +113,12 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
shouldSet = false;
if (shouldSet) {
+ currentQuality = q;
lastSetMsec = now;
// This delta value works on all platforms
timeStartMsec = now;
zeroOffsetSecs = tv->tv_sec;
-
// If this platform has a setable RTC, set it
#ifdef RV3028_RTC
if (rtc_found.address == RV3028_RTC) {
@@ -209,4 +208,4 @@ uint32_t getTime()
uint32_t getValidTime(RTCQuality minQuality)
{
return (currentQuality >= minQuality) ? getTime() : 0;
-}
+}
\ No newline at end of file
diff --git a/src/gps/ubx.h b/src/gps/ubx.h
new file mode 100644
index 000000000..6493b2ce7
--- /dev/null
+++ b/src/gps/ubx.h
@@ -0,0 +1,192 @@
+const uint8_t GPS::_message_PMREQ[] PROGMEM = {
+ 0x00, 0x00, // 4 bytes duration of request task
+ 0x00, 0x00, // (milliseconds)
+ 0x02, 0x00, // Task flag bitfield
+ 0x00, 0x00 // byte index 1 = sleep mode
+};
+
+const uint8_t GPS::_message_CFG_RXM_PSM[] PROGMEM = {
+ 0x08, // Reserved
+ 0x01 // Power save mode
+};
+
+const uint8_t GPS::_message_CFG_RXM_ECO[] PROGMEM = {
+ 0x08, // Reserved
+ 0x04 // eco mode
+};
+
+const uint8_t GPS::_message_CFG_PM2[] PROGMEM = {
+ 0x01, 0x06, 0x00, 0x00, // version, Reserved
+ 0x0e, 0x81, 0x42, 0x01, // flags
+ 0xE8, 0x03, 0x00, 0x00, // update period 1000 ms
+ 0x10, 0x27, 0x00, 0x00, // search period 10s
+ 0x00, 0x00, 0x00, 0x00, // Grod offset 0
+ 0x01, 0x00, // onTime 1 second
+ 0x00, 0x00, // min search time 0
+ 0x2C, 0x01, // reserved
+ 0x00, 0x00, 0x4F, 0xC1, // reserved
+ 0x03, 0x00, 0x87, 0x02, // reserved
+ 0x00, 0x00, 0xFF, 0x00, // reserved
+ 0x01, 0x00, 0xD6, 0x4D // reserved
+};
+
+const uint8_t GPS::_message_GNSS_7[] = {
+ 0x00, // msgVer (0 for this version)
+ 0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
+ 0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
+ 0x02, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
+ // GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
+ 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x00, 0x01, // GPS
+ 0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x01 // SBAS
+};
+
+// It's not critical if the module doesn't acknowledge this configuration.
+// The module should operate adequately with its factory or previously saved settings.
+// It appears that there is a firmware bug in some GPS modules: When an attempt is made
+// to overwrite a saved state with identical values, no ACK/NAK is received, contrary to
+// what is specified in the Ublox documentation.
+// There is also a possibility that the module may be GPS-only.
+const uint8_t GPS::_message_GNSS[] = {
+ 0x00, // msgVer (0 for this version)
+ 0x00, // numTrkChHw (max number of hardware channels, read only, so it's always 0)
+ 0xff, // numTrkChUse (max number of channels to use, 0xff = max available)
+ 0x03, // numConfigBlocks (number of GNSS systems), most modules support maximum 3 GNSS systems
+ // GNSS config format: gnssId, resTrkCh, maxTrkCh, reserved1, flags
+ 0x00, 0x08, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, // GPS
+ 0x01, 0x01, 0x03, 0x00, 0x01, 0x00, 0x01, 0x01, // SBAS
+ 0x06, 0x08, 0x0e, 0x00, 0x01, 0x00, 0x01, 0x01 // GLONASS
+};
+
+// Enable interference resistance, because we are using LoRa, WiFi and Bluetooth on same board,
+// and we need to reduce interference from them
+const uint8_t GPS::_message_JAM[] = {
+ // bbThreshold (Broadband jamming detection threshold) is set to 0x3F (63 in decimal)
+ // cwThreshold (CW jamming detection threshold) is set to 0x10 (16 in decimal)
+ // algorithmBits (Reserved algorithm settings) is set to 0x16B156 as recommended
+ // enable (Enable interference detection) is set to 1 (enabled)
+ 0x3F, 0x10, 0xB1, 0x56, // config: Interference config word
+ // generalBits (General settings) is set to 0x31E as recommended
+ // antSetting (Antenna setting, 0=unknown, 1=passive, 2=active) is set to 0 (unknown)
+ // ToDo: Set to 1 (passive) or 2 (active) if known, for example from UBX-MON-HW, or from board info
+ // enable2 (Set to 1 to scan auxiliary bands, u-blox 8 / u-blox M8 only, otherwise ignored) is set to 1
+ // (enabled)
+ 0x1E, 0x03, 0x00, 0x01 // config2: Extra settings for jamming/interference monitor
+};
+
+// Configure navigation engine expert settings:
+const uint8_t GPS::_message_NAVX5[] = {
+ 0x00, 0x00, // msgVer (0 for this version)
+ // minMax flag = 1: apply min/max SVs settings
+ // minCno flag = 1: apply minimum C/N0 setting
+ // initial3dfix flag = 0: apply initial 3D fix settings
+ // aop flag = 1: apply aopCfg (useAOP flag) settings (AssistNow Autonomous)
+ 0x1B, 0x00, // mask1 (First parameters bitmask)
+ // adr flag = 0: apply ADR sensor fusion on/off setting (useAdr flag)
+ // If firmware is not ADR/UDR, enabling this flag will fail configuration
+ // ToDo: check this with UBX-MON-VER
+ 0x00, 0x00, 0x00, 0x00, // mask2 (Second parameters bitmask)
+ 0x00, 0x00, // Reserved
+ 0x03, // minSVs (Minimum number of satellites for navigation) = 3
+ 0x10, // maxSVs (Maximum number of satellites for navigation) = 16
+ 0x06, // minCNO (Minimum satellite signal level for navigation) = 6 dBHz
+ 0x00, // Reserved
+ 0x00, // iniFix3D (Initial fix must be 3D) = 0 (disabled)
+ 0x00, 0x00, // Reserved
+ 0x00, // ackAiding (Issue acknowledgements for assistance message input) = 0 (disabled)
+ 0x00, 0x00, // Reserved
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved
+ 0x00, // Reserved
+ 0x01, // aopCfg (AssistNow Autonomous configuration) = 1 (enabled)
+ 0x00, 0x00, // Reserved
+ 0x00, 0x00, // Reserved
+ 0x00, 0x00, 0x00, 0x00, // Reserved
+ 0x00, 0x00, 0x00, // Reserved
+ 0x01, // useAdr (Enable/disable ADR sensor fusion) = 1 (enabled)
+};
+
+// Set GPS update rate to 1Hz
+// Lowering the update rate helps to save power.
+// Additionally, for some new modules like the M9/M10, an update rate lower than 5Hz
+// is recommended to avoid a known issue with satellites disappearing.
+const uint8_t GPS::_message_1HZ[] = {
+ 0xE8, 0x03, // Measurement Rate (1000ms for 1Hz)
+ 0x01, 0x00, // Navigation rate, always 1 in GPS mode
+ 0x01, 0x00, // Time reference
+};
+
+// Disable GGL. GGL - Geographic position (latitude and longitude), which provides the current geographical
+// coordinates.
+const uint8_t GPS::_message_GGL[] = {
+ 0xF0, 0x01, // NMEA ID for GLL
+ 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
+ 0x00, // Disable
+ 0x01, 0x01, 0x01, 0x01 // Reserved
+};
+
+// Enable GSA. GSA - GPS DOP and active satellites, used for detailing the satellites used in the positioning and
+// the DOP (Dilution of Precision)
+const uint8_t GPS::_message_GSA[] = {
+ 0xF0, 0x02, // NMEA ID for GSA
+ 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
+ 0x01, // Enable
+ 0x01, 0x01, 0x01, 0x01 // Reserved
+};
+
+// Disable GSV. GSV - Satellites in view, details the number and location of satellites in view.
+const uint8_t GPS::_message_GSV[] = {
+ 0xF0, 0x03, // NMEA ID for GSV
+ 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
+ 0x00, // Disable
+ 0x01, 0x01, 0x01, 0x01 // Reserved
+};
+
+// Disable VTG. VTG - Track made good and ground speed, which provides course and speed information relative to
+// the ground.
+const uint8_t GPS::_message_VTG[] = {
+ 0xF0, 0x05, // NMEA ID for VTG
+ 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
+ 0x00, // Disable
+ 0x01, 0x01, 0x01, 0x01 // Reserved
+};
+
+// Enable RMC. RMC - Recommended Minimum data, the essential gps pvt (position, velocity, time) data.
+const uint8_t GPS::_message_RMC[] = {
+ 0xF0, 0x04, // NMEA ID for RMC
+ 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
+ 0x01, // Enable
+ 0x01, 0x01, 0x01, 0x01 // Reserved
+};
+
+// Enable GGA. GGA - Global Positioning System Fix Data, which provides 3D location and accuracy data.
+const uint8_t GPS::_message_GGA[] = {
+ 0xF0, 0x00, // NMEA ID for GGA
+ 0x01, // I/O Target 0=I/O, 1=UART1, 2=UART2, 3=USB, 4=SPI
+ 0x01, // Enable
+ 0x01, 0x01, 0x01, 0x01 // Reserved
+};
+
+// The Power Management configuration allows the GPS module to operate in different power modes for optimized
+// power consumption. The modes supported are: 0x00 = Full power: The module operates at full power with no power
+// saving. 0x01 = Balanced: The module dynamically adjusts the tracking behavior to balance power consumption.
+// 0x02 = Interval: The module operates in a periodic mode, cycling between tracking and power saving states.
+// 0x03 = Aggressive with 1 Hz: The module operates in a power saving mode with a 1 Hz update rate.
+// 0x04 = Aggressive with 2 Hz: The module operates in a power saving mode with a 2 Hz update rate.
+// 0x05 = Aggressive with 4 Hz: The module operates in a power saving mode with a 4 Hz update rate.
+// The 'period' field specifies the position update and search period. It is only valid when the powerSetupValue
+// is set to Interval; otherwise, it must be set to '0'. The 'onTime' field specifies the duration of the ON phase
+// and must be smaller than the period. It is only valid when the powerSetupValue is set to Interval; otherwise,
+// it must be set to '0'.
+const uint8_t GPS::_message_PMS[] = {
+ 0x00, // Version (0)
+ 0x03, // Power setup value
+ 0x00, 0x00, // period: not applicable, set to 0
+ 0x00, 0x00, // onTime: not applicable, set to 0
+ 0x97, 0x6F // reserved, generated by u-center
+};
+
+const uint8_t GPS::_message_SAVE[] = {
+ 0x00, 0x00, 0x00, 0x00, // clearMask: no sections cleared
+ 0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections
+ 0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded
+ 0x0F // deviceMask: BBR, Flash, EEPROM, and SPI Flash
+};
\ No newline at end of file
diff --git a/src/graphics/RAKled.h b/src/graphics/RAKled.h
index 2e36b874a..659ea9b72 100644
--- a/src/graphics/RAKled.h
+++ b/src/graphics/RAKled.h
@@ -1,5 +1,3 @@
-#include "main.h"
-
#ifdef HAS_NCP5623
#include
extern NCP5623 rgb;
diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp
index d4ed6fdb6..9ccd28aba 100644
--- a/src/graphics/Screen.cpp
+++ b/src/graphics/Screen.cpp
@@ -36,11 +36,11 @@ along with this program. If not, see .
#include "mesh-pb-constants.h"
#include "mesh/Channels.h"
#include "mesh/generated/meshtastic/deviceonly.pb.h"
+#include "meshUtils.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h"
#include "sleep.h"
#include "target_specific.h"
-#include "utils.h"
#ifdef ARCH_ESP32
#include "esp_task_wdt.h"
@@ -365,7 +365,7 @@ static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *sta
// Ignore messages originating from phone (from the current node 0x0) unless range test or store and forward module are enabled
static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
{
- return packet->from != 0 && !moduleConfig.range_test.enabled && !moduleConfig.store_forward.enabled;
+ return packet->from != 0 && !moduleConfig.store_forward.enabled;
}
/// Draw the last text message we received
diff --git a/src/input/BBQ10Keyboard.cpp b/src/input/BBQ10Keyboard.cpp
new file mode 100644
index 000000000..8f8399aef
--- /dev/null
+++ b/src/input/BBQ10Keyboard.cpp
@@ -0,0 +1,181 @@
+// Based on arturo182 arduino_bbq10kbd library https://github.com/arturo182/arduino_bbq10kbd
+
+#include
+
+#include "BBQ10Keyboard.h"
+
+#define _REG_VER 1
+#define _REG_CFG 2
+#define _REG_INT 3
+#define _REG_KEY 4
+#define _REG_BKL 5
+#define _REG_DEB 6
+#define _REG_FRQ 7
+#define _REG_RST 8
+#define _REG_FIF 9
+
+#define _WRITE_MASK (1 << 7)
+
+#define CFG_OVERFLOW_ON (1 << 0)
+#define CFG_OVERFLOW_INT (1 << 1)
+#define CFG_CAPSLOCK_INT (1 << 2)
+#define CFG_NUMLOCK_INT (1 << 3)
+#define CFG_KEY_INT (1 << 4)
+#define CFG_PANIC_INT (1 << 5)
+#define CFG_REPORT_MODS (1 << 6)
+#define CFG_USE_MODS (1 << 7)
+
+#define INT_OVERFLOW (1 << 0)
+#define INT_CAPSLOCK (1 << 1)
+#define INT_NUMLOCK (1 << 2)
+#define INT_KEY (1 << 3)
+#define INT_PANIC (1 << 4)
+
+#define KEY_CAPSLOCK (1 << 5)
+#define KEY_NUMLOCK (1 << 6)
+#define KEY_COUNT_MASK (0x1F)
+
+BBQ10Keyboard::BBQ10Keyboard() : m_wire(nullptr), m_addr(0), readCallback(nullptr), writeCallback(nullptr) {}
+
+void BBQ10Keyboard::begin(uint8_t addr, TwoWire *wire)
+{
+ m_addr = addr;
+ m_wire = wire;
+
+ m_wire->begin();
+
+ reset();
+}
+
+void BBQ10Keyboard::begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr)
+{
+ m_addr = addr;
+ m_wire = nullptr;
+ writeCallback = w;
+ readCallback = r;
+ reset();
+}
+
+void BBQ10Keyboard::reset()
+{
+ if (m_wire) {
+ m_wire->beginTransmission(m_addr);
+ m_wire->write(_REG_RST);
+ m_wire->endTransmission();
+ }
+ if (writeCallback) {
+ uint8_t data = 0;
+ writeCallback(m_addr, _REG_RST, &data, 0);
+ }
+ delay(100);
+ writeRegister(_REG_CFG, readRegister8(_REG_CFG) | CFG_REPORT_MODS);
+ delay(100);
+}
+
+void BBQ10Keyboard::attachInterrupt(uint8_t pin, void (*func)(void)) const
+{
+ pinMode(pin, INPUT_PULLUP);
+ ::attachInterrupt(digitalPinToInterrupt(pin), func, RISING);
+}
+
+void BBQ10Keyboard::detachInterrupt(uint8_t pin) const
+{
+ ::detachInterrupt(pin);
+}
+
+void BBQ10Keyboard::clearInterruptStatus()
+{
+ writeRegister(_REG_INT, 0x00);
+}
+
+uint8_t BBQ10Keyboard::status() const
+{
+ return readRegister8(_REG_KEY);
+}
+
+uint8_t BBQ10Keyboard::keyCount() const
+{
+ return status() & KEY_COUNT_MASK;
+}
+
+BBQ10Keyboard::KeyEvent BBQ10Keyboard::keyEvent() const
+{
+ KeyEvent event = {.key = '\0', .state = StateIdle};
+
+ if (keyCount() == 0)
+ return event;
+
+ const uint16_t buf = readRegister16(_REG_FIF);
+ event.key = buf >> 8;
+ event.state = KeyState(buf & 0xFF);
+
+ return event;
+}
+
+float BBQ10Keyboard::backlight() const
+{
+ return readRegister8(_REG_BKL) / 255.0f;
+}
+
+void BBQ10Keyboard::setBacklight(float value)
+{
+ writeRegister(_REG_BKL, value * 255);
+}
+
+uint8_t BBQ10Keyboard::readRegister8(uint8_t reg) const
+{
+ if (m_wire) {
+ m_wire->beginTransmission(m_addr);
+ m_wire->write(reg);
+ m_wire->endTransmission();
+
+ m_wire->requestFrom(m_addr, (uint8_t)1);
+ if (m_wire->available() < 1)
+ return 0;
+
+ return m_wire->read();
+ }
+ if (readCallback) {
+ uint8_t data;
+ readCallback(m_addr, reg, &data, 1);
+ return data;
+ }
+ return 0;
+}
+
+uint16_t BBQ10Keyboard::readRegister16(uint8_t reg) const
+{
+ uint8_t data[2] = {0};
+ // uint8_t low = 0, high = 0;
+ if (m_wire) {
+ m_wire->beginTransmission(m_addr);
+ m_wire->write(reg);
+ m_wire->endTransmission();
+
+ m_wire->requestFrom(m_addr, (uint8_t)2);
+ if (m_wire->available() < 2)
+ return 0;
+ data[0] = m_wire->read();
+ data[1] = m_wire->read();
+ }
+ if (readCallback) {
+ readCallback(m_addr, reg, data, 2);
+ }
+ return (data[1] << 8) | data[0];
+}
+
+void BBQ10Keyboard::writeRegister(uint8_t reg, uint8_t value)
+{
+ uint8_t data[2];
+ data[0] = reg | _WRITE_MASK;
+ data[1] = value;
+
+ if (m_wire) {
+ m_wire->beginTransmission(m_addr);
+ m_wire->write(data, sizeof(uint8_t) * 2);
+ m_wire->endTransmission();
+ }
+ if (writeCallback) {
+ writeCallback(m_addr, data[0], &(data[1]), 1);
+ }
+}
diff --git a/src/input/BBQ10Keyboard.h b/src/input/BBQ10Keyboard.h
new file mode 100644
index 000000000..07d02308f
--- /dev/null
+++ b/src/input/BBQ10Keyboard.h
@@ -0,0 +1,51 @@
+// Based on arturo182 arduino_bbq10kbd library https://github.com/arturo182/arduino_bbq10kbd
+
+#include "configuration.h"
+#include
+
+#define KEY_MOD_ALT (0x1A)
+#define KEY_MOD_SHL (0x1B)
+#define KEY_MOD_SHR (0x1C)
+#define KEY_MOD_SYM (0x1D)
+
+class BBQ10Keyboard
+{
+ public:
+ typedef uint8_t (*i2c_com_fptr_t)(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint8_t len);
+
+ enum KeyState { StateIdle = 0, StatePress, StateLongPress, StateRelease };
+
+ struct KeyEvent {
+ char key;
+ KeyState state;
+ };
+
+ BBQ10Keyboard();
+
+ void begin(uint8_t addr = BBQ10_KB_ADDR, TwoWire *wire = &Wire);
+
+ void begin(i2c_com_fptr_t r, i2c_com_fptr_t w, uint8_t addr = BBQ10_KB_ADDR);
+
+ void reset(void);
+
+ void attachInterrupt(uint8_t pin, void (*func)(void)) const;
+ void detachInterrupt(uint8_t pin) const;
+ void clearInterruptStatus(void);
+
+ uint8_t status(void) const;
+ uint8_t keyCount(void) const;
+ KeyEvent keyEvent(void) const;
+
+ float backlight() const;
+ void setBacklight(float value);
+
+ uint8_t readRegister8(uint8_t reg) const;
+ uint16_t readRegister16(uint8_t reg) const;
+ void writeRegister(uint8_t reg, uint8_t value);
+
+ private:
+ TwoWire *m_wire;
+ uint8_t m_addr;
+ i2c_com_fptr_t readCallback;
+ i2c_com_fptr_t writeCallback;
+};
diff --git a/src/input/RotaryEncoderInterruptImpl1.cpp b/src/input/RotaryEncoderInterruptImpl1.cpp
index 7bf66ac55..7e79289e5 100644
--- a/src/input/RotaryEncoderInterruptImpl1.cpp
+++ b/src/input/RotaryEncoderInterruptImpl1.cpp
@@ -5,12 +5,12 @@ RotaryEncoderInterruptImpl1 *rotaryEncoderInterruptImpl1;
RotaryEncoderInterruptImpl1::RotaryEncoderInterruptImpl1() : RotaryEncoderInterruptBase("rotEnc1") {}
-void RotaryEncoderInterruptImpl1::init()
+bool RotaryEncoderInterruptImpl1::init()
{
if (!moduleConfig.canned_message.rotary1_enabled) {
// Input device is disabled.
disable();
- return;
+ return false;
}
uint8_t pinA = moduleConfig.canned_message.inputbroker_pin_a;
@@ -25,6 +25,7 @@ void RotaryEncoderInterruptImpl1::init()
RotaryEncoderInterruptImpl1::handleIntA, RotaryEncoderInterruptImpl1::handleIntB,
RotaryEncoderInterruptImpl1::handleIntPressed);
inputBroker->registerSource(this);
+ return true;
}
void RotaryEncoderInterruptImpl1::handleIntA()
@@ -38,4 +39,4 @@ void RotaryEncoderInterruptImpl1::handleIntB()
void RotaryEncoderInterruptImpl1::handleIntPressed()
{
rotaryEncoderInterruptImpl1->intPressHandler();
-}
+}
\ No newline at end of file
diff --git a/src/input/RotaryEncoderInterruptImpl1.h b/src/input/RotaryEncoderInterruptImpl1.h
index 1bdb3a5a5..22ecba37a 100644
--- a/src/input/RotaryEncoderInterruptImpl1.h
+++ b/src/input/RotaryEncoderInterruptImpl1.h
@@ -12,7 +12,7 @@ class RotaryEncoderInterruptImpl1 : public RotaryEncoderInterruptBase
{
public:
RotaryEncoderInterruptImpl1();
- void init();
+ bool init();
static void handleIntA();
static void handleIntB();
static void handleIntPressed();
diff --git a/src/input/UpDownInterruptImpl1.cpp b/src/input/UpDownInterruptImpl1.cpp
index c22152f82..7dd1f76b2 100644
--- a/src/input/UpDownInterruptImpl1.cpp
+++ b/src/input/UpDownInterruptImpl1.cpp
@@ -5,12 +5,12 @@ UpDownInterruptImpl1 *upDownInterruptImpl1;
UpDownInterruptImpl1::UpDownInterruptImpl1() : UpDownInterruptBase("upDown1") {}
-void UpDownInterruptImpl1::init()
+bool UpDownInterruptImpl1::init()
{
if (!moduleConfig.canned_message.updown1_enabled) {
// Input device is disabled.
- return;
+ return false;
}
uint8_t pinUp = moduleConfig.canned_message.inputbroker_pin_a;
@@ -24,6 +24,7 @@ void UpDownInterruptImpl1::init()
UpDownInterruptBase::init(pinDown, pinUp, pinPress, eventDown, eventUp, eventPressed, UpDownInterruptImpl1::handleIntDown,
UpDownInterruptImpl1::handleIntUp, UpDownInterruptImpl1::handleIntPressed);
inputBroker->registerSource(this);
+ return true;
}
void UpDownInterruptImpl1::handleIntDown()
@@ -37,4 +38,4 @@ void UpDownInterruptImpl1::handleIntUp()
void UpDownInterruptImpl1::handleIntPressed()
{
upDownInterruptImpl1->intPressHandler();
-}
+}
\ No newline at end of file
diff --git a/src/input/UpDownInterruptImpl1.h b/src/input/UpDownInterruptImpl1.h
index 17420db95..4739cd2ff 100644
--- a/src/input/UpDownInterruptImpl1.h
+++ b/src/input/UpDownInterruptImpl1.h
@@ -5,10 +5,10 @@ class UpDownInterruptImpl1 : public UpDownInterruptBase
{
public:
UpDownInterruptImpl1();
- void init();
+ bool init();
static void handleIntDown();
static void handleIntUp();
static void handleIntPressed();
};
-extern UpDownInterruptImpl1 *upDownInterruptImpl1;
+extern UpDownInterruptImpl1 *upDownInterruptImpl1;
\ No newline at end of file
diff --git a/src/input/cardKbI2cImpl.cpp b/src/input/cardKbI2cImpl.cpp
index 44db1d952..e000f36eb 100644
--- a/src/input/cardKbI2cImpl.cpp
+++ b/src/input/cardKbI2cImpl.cpp
@@ -7,7 +7,7 @@ CardKbI2cImpl::CardKbI2cImpl() : KbI2cBase("cardKB") {}
void CardKbI2cImpl::init()
{
- if (cardkb_found.address != CARDKB_ADDR && cardkb_found.address != TDECK_KB_ADDR) {
+ if (cardkb_found.address == 0x00) {
disable();
return;
}
diff --git a/src/input/kbI2cBase.cpp b/src/input/kbI2cBase.cpp
index f7fd62645..366e7fbb1 100644
--- a/src/input/kbI2cBase.cpp
+++ b/src/input/kbI2cBase.cpp
@@ -30,7 +30,7 @@ uint8_t read_from_14004(TwoWire *i2cBus, uint8_t reg, uint8_t *data, uint8_t len
int32_t KbI2cBase::runOnce()
{
- if (cardkb_found.address != CARDKB_ADDR && cardkb_found.address != TDECK_KB_ADDR) {
+ if (cardkb_found.address == 0x00) {
// Input device is not detected.
return INT32_MAX;
}
@@ -41,11 +41,19 @@ int32_t KbI2cBase::runOnce()
#ifdef I2C_SDA1
LOG_DEBUG("Using I2C Bus 1 (the second one)\n");
i2cBus = &Wire1;
+ if (cardkb_found.address == BBQ10_KB_ADDR) {
+ Q10keyboard.begin(BBQ10_KB_ADDR, &Wire1);
+ Q10keyboard.setBacklight(0);
+ }
break;
#endif
case ScanI2C::WIRE:
LOG_DEBUG("Using I2C Bus 0 (the first one)\n");
i2cBus = &Wire;
+ if (cardkb_found.address == BBQ10_KB_ADDR) {
+ Q10keyboard.begin(BBQ10_KB_ADDR, &Wire);
+ Q10keyboard.setBacklight(0);
+ }
break;
case ScanI2C::NO_I2C:
default:
@@ -53,7 +61,105 @@ int32_t KbI2cBase::runOnce()
}
}
- if (kb_model == 0x02) {
+ switch (kb_model) {
+ case 0x11: { // BB Q10
+ int keyCount = Q10keyboard.keyCount();
+ while (keyCount--) {
+ const BBQ10Keyboard::KeyEvent key = Q10keyboard.keyEvent();
+ if ((key.key != 0x00) && (key.state == BBQ10Keyboard::StateRelease)) {
+ InputEvent e;
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
+ e.source = this->_originName;
+ switch (key.key) {
+ case 'p': // TAB
+ case 't': // TAB as well
+ if (is_sym) {
+ e.inputEvent = ANYKEY;
+ e.kbchar = 0x09; // TAB Scancode
+ is_sym = false; // reset sym state after second keypress
+ } else {
+ e.inputEvent = ANYKEY;
+ e.kbchar = key.key;
+ }
+ break;
+ case 'q': // ESC
+ if (is_sym) {
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL;
+ e.kbchar = 0x1b;
+ is_sym = false; // reset sym state after second keypress
+ } else {
+ e.inputEvent = ANYKEY;
+ e.kbchar = key.key;
+ }
+ break;
+ case 0x08: // Back
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK;
+ e.kbchar = key.key;
+ break;
+ case 'e': // sym e
+ if (is_sym) {
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
+ e.kbchar = 0xb5;
+ is_sym = false; // reset sym state after second keypress
+ } else {
+ e.inputEvent = ANYKEY;
+ e.kbchar = key.key;
+ }
+ break;
+ case 'x': // sym x
+ if (is_sym) {
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
+ e.kbchar = 0xb6;
+ is_sym = false; // reset sym state after second keypress
+ } else {
+ e.inputEvent = ANYKEY;
+ e.kbchar = key.key;
+ }
+ break;
+ case 's': // sym s
+ if (is_sym) {
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
+ e.kbchar = 0x00; // tweak for destSelect
+ is_sym = false; // reset sym state after second keypress
+ } else {
+ e.inputEvent = ANYKEY;
+ e.kbchar = key.key;
+ }
+ break;
+ case 'f': // sym f
+ if (is_sym) {
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
+ e.kbchar = 0x00; // tweak for destSelect
+ is_sym = false; // reset sym state after second keypress
+ } else {
+ e.inputEvent = ANYKEY;
+ e.kbchar = key.key;
+ }
+ break;
+ case 0x13: // Code scanner says the SYM key is 0x13
+ is_sym = !is_sym;
+ break;
+ case 0x0a: // apparently Enter on Q10 is a line feed instead of carriage return
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
+ break;
+ case 0x00: // nopress
+ e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE;
+ break;
+ default: // all other keys
+ e.inputEvent = ANYKEY;
+ e.kbchar = key.key;
+ is_sym = false; // reset sym state after second keypress
+ break;
+ }
+
+ if (e.inputEvent != meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE) {
+ this->notifyObservers(&e);
+ }
+ }
+ }
+ break;
+ }
+ case 0x02: {
// RAK14004
uint8_t rDataBuf[8] = {0};
uint8_t PrintDataBuf = 0;
@@ -74,9 +180,12 @@ int32_t KbI2cBase::runOnce()
e.kbchar = PrintDataBuf;
this->notifyObservers(&e);
}
- } else if (kb_model == 0x00 || kb_model == 0x10) {
- // m5 cardkb and T-Deck
- i2cBus->requestFrom(kb_model == 0x00 ? CARDKB_ADDR : TDECK_KB_ADDR, 1);
+ break;
+ }
+ case 0x00: // CARDKB
+ case 0x10: { // T-DECK
+
+ i2cBus->requestFrom((int)cardkb_found.address, 1);
while (i2cBus->available()) {
char c = i2cBus->read();
@@ -93,17 +202,19 @@ int32_t KbI2cBase::runOnce()
break;
case 0xb5: // Up
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP;
+ e.kbchar = 0xb5;
break;
case 0xb6: // Down
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN;
+ e.kbchar = 0xb6;
break;
case 0xb4: // Left
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT;
- e.kbchar = c;
+ e.kbchar = 0xb4;
break;
case 0xb7: // Right
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT;
- e.kbchar = c;
+ e.kbchar = 0xb7;
break;
case 0x0d: // Enter
e.inputEvent = meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT;
@@ -121,7 +232,9 @@ int32_t KbI2cBase::runOnce()
this->notifyObservers(&e);
}
}
- } else {
+ break;
+ }
+ default:
LOG_WARN("Unknown kb_model 0x%02x\n", kb_model);
}
return 300;
diff --git a/src/input/kbI2cBase.h b/src/input/kbI2cBase.h
index a0a4dd608..35b9b0901 100644
--- a/src/input/kbI2cBase.h
+++ b/src/input/kbI2cBase.h
@@ -1,5 +1,6 @@
#pragma once
+#include "BBQ10Keyboard.h"
#include "InputBroker.h"
#include "Wire.h"
#include "concurrency/OSThread.h"
@@ -16,4 +17,7 @@ class KbI2cBase : public Observable, public concurrency::OST
const char *_originName;
TwoWire *i2cBus = 0;
+
+ BBQ10Keyboard Q10keyboard;
+ bool is_sym = false;
};
diff --git a/src/main.cpp b/src/main.cpp
index 5f0eaca30..a4caad3fd 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -73,6 +73,7 @@ NRF52Bluetooth *nrf52Bluetooth;
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
#include "AccelerometerThread.h"
+#include "AmbientLightingThread.h"
#endif
using namespace concurrency;
@@ -169,6 +170,7 @@ static OSThread *buttonThread;
uint32_t ButtonThread::longPressTime = 0;
#endif
static OSThread *accelerometerThread;
+static OSThread *ambientLightingThread;
SPISettings spiSettings(4000000, MSBFIRST, SPI_MODE0);
RadioInterface *rIf = NULL;
@@ -219,11 +221,6 @@ void setup()
digitalWrite(VEXT_ENABLE, 0); // turn on the display power
#endif
-#ifdef VGNSS_CTRL
- pinMode(VGNSS_CTRL, OUTPUT);
- digitalWrite(VGNSS_CTRL, LOW);
-#endif
-
#if defined(VTFT_CTRL)
pinMode(VTFT_CTRL, OUTPUT);
digitalWrite(VTFT_CTRL, LOW);
@@ -387,6 +384,10 @@ void setup()
// assign an arbitrary value to distinguish from other models
kb_model = 0x10;
break;
+ case ScanI2C::DeviceType::BBQ10KB:
+ // assign an arbitrary value to distinguish from other models
+ kb_model = 0x11;
+ break;
default:
// use this as default since it's also just zero
LOG_WARN("kb_info.type is unknown(0x%02x), setting kb_model=0x00\n", kb_info.type);
@@ -405,14 +406,6 @@ void setup()
// Only one supported RGB LED currently
#ifdef HAS_NCP5623
rgb_found = i2cScanner->find(ScanI2C::DeviceType::NCP5623);
-
- // Start the RGB LED at 50%
-
- if (rgb_found.type == ScanI2C::NCP5623) {
- rgb.begin();
- rgb.setCurrent(10);
- rgb.setColor(128, 128, 128);
- }
#endif
#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
@@ -517,6 +510,12 @@ void setup()
}
#endif
+#if !defined(ARCH_PORTDUINO) && !defined(ARCH_STM32WL)
+ if (rgb_found.type != ScanI2C::DeviceType::NONE) {
+ ambientLightingThread = new AmbientLightingThread(rgb_found.type);
+ }
+#endif
+
#ifdef T_WATCH_S3
drv.begin();
drv.selectLibrary(1);
@@ -554,14 +553,12 @@ void setup()
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
- gps = createGps();
-
+ gps = GPS::createGps();
if (gps) {
gpsStatus->observe(&gps->newStatus);
} else {
- LOG_WARN("No GPS found - running without GPS\n");
+ LOG_DEBUG("Running without GPS.\n");
}
-
nodeStatus->observe(&nodeDB.newStatus);
service.init();
@@ -586,17 +583,6 @@ void setup()
screen->print("Started...\n");
- // We have now loaded our saved preferences from flash
-
- // ONCE we will factory reset the GPS for bug #327
- if (gps && !devicestate.did_gps_reset) {
- LOG_WARN("GPS FactoryReset requested\n");
- if (gps->factoryReset()) { // If we don't succeed try again next time
- devicestate.did_gps_reset = true;
- nodeDB.saveToDisk(SEGMENT_DEVICESTATE);
- }
- }
-
#ifdef SX126X_ANT_SW
// make analog PA vs not PA switch on SX126x eval board work properly
pinMode(SX126X_ANT_SW, OUTPUT);
@@ -751,7 +737,6 @@ void setup()
PowerFSM_setup(); // we will transition to ON in a couple of seconds, FIXME, only do this for cold boots, not waking from SDS
powerFSMthread = new PowerFSMThread();
- // setBluetoothEnable(false); we now don't start bluetooth until we enter the proper state
setCPUFast(false); // 80MHz is fine for our slow peripherals
}
diff --git a/src/mesh/MeshService.cpp b/src/mesh/MeshService.cpp
index 96ea2f0a1..9d2ecdcee 100644
--- a/src/mesh/MeshService.cpp
+++ b/src/mesh/MeshService.cpp
@@ -327,21 +327,19 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
// load data from GPS object, will add timestamp + battery further down
pos = gps->p;
} else {
- // The GPS has lost lock, if we are fixed position we should just keep using
- // the old position
+ // The GPS has lost lock
#ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("onGPSchanged() - lost validLocation\n");
#endif
- if (config.position.fixed_position) {
- LOG_WARN("Using fixed position\n");
- pos = ConvertToPosition(node->position);
- }
+ }
+ // Used fixed position if configured regalrdless of GPS lock
+ if (config.position.fixed_position) {
+ LOG_WARN("Using fixed position\n");
+ pos = ConvertToPosition(node->position);
}
- // Finally add a fresh timestamp and battery level reading
- // I KNOW this is redundant with refreshLocalMeshNode() above, but these are
- // inexpensive nonblocking calls and can be refactored in due course
- pos.time = getValidTime(RTCQualityGPS);
+ // Add a fresh timestamp
+ pos.time = getValidTime(RTCQualityFromNet);
// In debug logs, identify position by @timestamp:stage (stage 4 = nodeDB)
LOG_DEBUG("onGPSChanged() pos@%x, time=%u, lat=%d, lon=%d, alt=%d\n", pos.timestamp, pos.time, pos.latitude_i,
diff --git a/src/mesh/MeshService.h b/src/mesh/MeshService.h
index fa184b391..eb40b7712 100644
--- a/src/mesh/MeshService.h
+++ b/src/mesh/MeshService.h
@@ -48,6 +48,14 @@ class MeshService
uint32_t oldFromNum = 0;
public:
+ static bool isTextPayload(const meshtastic_MeshPacket *p)
+ {
+ if (moduleConfig.range_test.enabled && p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP) {
+ return true;
+ }
+ return p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP ||
+ p->decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP;
+ }
/// Called when some new packets have arrived from one of the radios
Observable fromNumChanged;
diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index 196874d48..d7fd84fb7 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -169,6 +169,14 @@ void NodeDB::installDefaultConfig()
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
config.lora.modem_preset = meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST;
config.lora.hop_limit = HOP_RELIABLE;
+#ifdef PIN_GPS_EN
+ config.position.gps_en_gpio = PIN_GPS_EN;
+#endif
+#ifdef GPS_POWER_TOGGLE
+ config.device.disable_triple_click = false;
+#else
+ config.device.disable_triple_click = true;
+#endif
config.position.gps_enabled = true;
config.position.position_broadcast_smart_enabled = true;
config.position.broadcast_smart_minimum_distance = 100;
@@ -190,7 +198,9 @@ void NodeDB::installDefaultConfig()
: meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN;
// for backward compat, default position flags are ALT+MSL
config.position.position_flags =
- (meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL);
+ (meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE_MSL |
+ meshtastic_Config_PositionConfig_PositionFlags_SPEED | meshtastic_Config_PositionConfig_PositionFlags_HEADING |
+ meshtastic_Config_PositionConfig_PositionFlags_DOP);
#ifdef T_WATCH_S3
config.display.screen_on_secs = 30;
@@ -253,6 +263,13 @@ void NodeDB::installDefaultModuleConfig()
moduleConfig.detection_sensor.detection_triggered_high = true;
moduleConfig.detection_sensor.minimum_broadcast_secs = 45;
+ moduleConfig.has_ambient_lighting = true;
+ moduleConfig.ambient_lighting.current = 10;
+ // Default to a color based on our node number
+ moduleConfig.ambient_lighting.red = (myNodeInfo.my_node_num & 0xFF0000) >> 16;
+ moduleConfig.ambient_lighting.green = (myNodeInfo.my_node_num & 0x00FF00) >> 8;
+ moduleConfig.ambient_lighting.blue = myNodeInfo.my_node_num & 0x0000FF;
+
initModuleConfigIntervals();
}
@@ -295,6 +312,19 @@ void NodeDB::resetNodes()
neighborInfoModule->resetNeighbors();
}
+void NodeDB::cleanupMeshDB()
+{
+ int newPos = 0, removed = 0;
+ for (int i = 0; i < *numMeshNodes; i++) {
+ if (meshNodes[i].has_user)
+ meshNodes[newPos++] = meshNodes[i];
+ else
+ removed++;
+ }
+ *numMeshNodes -= removed;
+ LOG_DEBUG("cleanupMeshDB purged %d entries\n", removed);
+}
+
void NodeDB::installDefaultDeviceState()
{
LOG_INFO("Installing default DeviceState\n");
@@ -324,6 +354,7 @@ void NodeDB::init()
{
LOG_INFO("Initializing NodeDB\n");
loadFromDisk();
+ cleanupMeshDB();
uint32_t devicestateCRC = crc32Buffer(&devicestate, sizeof(devicestate));
uint32_t configCRC = crc32Buffer(&config, sizeof(config));
@@ -379,25 +410,20 @@ void NodeDB::init()
*/
void NodeDB::pickNewNodeNum()
{
- NodeNum r = myNodeInfo.my_node_num;
-
getMacAddr(ourMacAddr); // Make sure ourMacAddr is set
// Pick an initial nodenum based on the macaddr
- r = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5];
-
- if (r == NODENUM_BROADCAST || r < NUM_RESERVED)
- r = NUM_RESERVED; // don't pick a reserved node number
+ NodeNum nodeNum = (ourMacAddr[2] << 24) | (ourMacAddr[3] << 16) | (ourMacAddr[4] << 8) | ourMacAddr[5];
meshtastic_NodeInfoLite *found;
- while ((found = getMeshNode(r)) && memcmp(found->user.macaddr, owner.macaddr, sizeof(owner.macaddr))) {
- // FIXME: input for random() is int, so NODENUM_BROADCAST becomes -1
- NodeNum n = random(NUM_RESERVED, NODENUM_BROADCAST); // try a new random choice
- LOG_WARN("NOTE! Our desired nodenum 0x%x is in use, so trying for 0x%x\n", r, n);
- r = n;
+ while ((nodeNum == NODENUM_BROADCAST || nodeNum < NUM_RESERVED) ||
+ ((found = getMeshNode(nodeNum)) && memcmp(found->user.macaddr, owner.macaddr, sizeof(owner.macaddr)) != 0)) {
+ NodeNum candidate = random(NUM_RESERVED, LONG_MAX); // try a new random choice
+ LOG_WARN("NOTE! Our desired nodenum 0x%x is invalid or in use, so trying for 0x%x\n", nodeNum, candidate);
+ nodeNum = candidate;
}
- myNodeInfo.my_node_num = r;
+ myNodeInfo.my_node_num = nodeNum;
}
static const char *prefFileName = "/prefs/db.proto";
diff --git a/src/mesh/NodeDB.h b/src/mesh/NodeDB.h
index ab0ec8288..51c1b3043 100644
--- a/src/mesh/NodeDB.h
+++ b/src/mesh/NodeDB.h
@@ -146,6 +146,9 @@ class NodeDB
/// read our db from flash
void loadFromDisk();
+ /// purge db entries without user info
+ void cleanupMeshDB();
+
/// Reinit device state from scratch (not loading from disk)
void installDefaultDeviceState(), installDefaultChannels(), installDefaultConfig(), installDefaultModuleConfig();
};
@@ -209,6 +212,14 @@ inline uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t d
return defaultInterval * 1000;
}
+inline uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue)
+{
+ if (configured > 0)
+ return configured;
+
+ return defaultValue;
+}
+
/// Sometimes we will have Position objects that only have a time, so check for
/// valid lat/lon
static inline bool hasValidPosition(const meshtastic_NodeInfoLite *n)
diff --git a/src/mesh/PhoneAPI.cpp b/src/mesh/PhoneAPI.cpp
index c91f2e815..672c6871e 100644
--- a/src/mesh/PhoneAPI.cpp
+++ b/src/mesh/PhoneAPI.cpp
@@ -155,11 +155,18 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
// app not to send locations on our behalf.
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_my_info_tag;
fromRadioScratch.my_info = myNodeInfo;
- state = STATE_SEND_NODEINFO;
+ state = STATE_SEND_METADATA;
service.refreshLocalMeshNode(); // Update my NodeInfo because the client will be asking for it soon.
break;
+ case STATE_SEND_METADATA:
+ LOG_INFO("getFromRadio=STATE_SEND_METADATA\n");
+ fromRadioScratch.which_payload_variant = meshtastic_FromRadio_metadata_tag;
+ fromRadioScratch.metadata = getDeviceMetadata();
+ state = STATE_SEND_NODEINFO;
+ break;
+
case STATE_SEND_NODEINFO: {
LOG_INFO("getFromRadio=STATE_SEND_NODEINFO\n");
@@ -294,15 +301,11 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
config_state++;
// Advance when we have sent all of our ModuleConfig objects
if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) {
- state = STATE_SEND_METADATA;
+ state = STATE_SEND_COMPLETE_ID;
config_state = 0;
}
break;
- case STATE_SEND_METADATA:
- fromRadioScratch.which_payload_variant = meshtastic_FromRadio_metadata_tag;
- fromRadioScratch.metadata = getDeviceMetadata();
- state = STATE_SEND_COMPLETE_ID;
- break;
+
case STATE_SEND_COMPLETE_ID:
LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n");
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag;
diff --git a/src/mesh/RadioInterface.cpp b/src/mesh/RadioInterface.cpp
index 61b6b85d0..0d2238396 100644
--- a/src/mesh/RadioInterface.cpp
+++ b/src/mesh/RadioInterface.cpp
@@ -534,8 +534,8 @@ size_t RadioInterface::beginSending(meshtastic_MeshPacket *p)
h->id = p->id;
h->channel = p->channel;
if (p->hop_limit > HOP_MAX) {
- LOG_WARN("hop limit %d is too high, setting to %d\n", p->hop_limit, HOP_MAX);
- p->hop_limit = HOP_MAX;
+ LOG_WARN("hop limit %d is too high, setting to %d\n", p->hop_limit, HOP_RELIABLE);
+ p->hop_limit = HOP_RELIABLE;
}
h->flags = p->hop_limit | (p->want_ack ? PACKET_FLAGS_WANT_ACK_MASK : 0);
diff --git a/src/mesh/SX126xInterface.cpp b/src/mesh/SX126xInterface.cpp
index fa158c22b..0e94bff93 100644
--- a/src/mesh/SX126xInterface.cpp
+++ b/src/mesh/SX126xInterface.cpp
@@ -3,7 +3,8 @@
#include "error.h"
#include "mesh/NodeDB.h"
-// Particular boards might define a different max power based on what their hardware can do
+// Particular boards might define a different max power based on what their hardware can do, default to max power output if not
+// specified (may be dangerous if using external PA and SX126x power config forgotten)
#ifndef SX126X_MAX_POWER
#define SX126X_MAX_POWER 22
#endif
@@ -26,20 +27,23 @@ template bool SX126xInterface::init()
pinMode(SX126X_POWER_EN, OUTPUT);
#endif
-#ifndef SX126X_E22
- float tcxoVoltage = 0; // None - we use an XTAL
+// FIXME: correct logic to default to not using TCXO if no voltage is specified for SX126X_DIO3_TCXO_VOLTAGE
+#if !defined(SX126X_DIO3_TCXO_VOLTAGE)
+ float tcxoVoltage =
+ 0; // "TCXO reference voltage to be set on DIO3. Defaults to 1.6 V, set to 0 to skip." per
+ // https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/SX126x/SX126x.h#L471C26-L471C104
+ // (DIO3 is free to be used as an IRQ)
+ LOG_DEBUG("SX126X_DIO3_TCXO_VOLTAGE not defined, not using DIO3 as TCXO reference voltage\n");
#else
- // Use DIO3 to power tcxo per https://github.com/jgromes/RadioLib/issues/12#issuecomment-520695575
- float tcxoVoltage = 1.8;
+ float tcxoVoltage = SX126X_DIO3_TCXO_VOLTAGE;
+ LOG_DEBUG("SX126X_DIO3_TCXO_VOLTAGE defined, using DIO3 as TCXO reference voltage at %f V\n", SX126X_DIO3_TCXO_VOLTAGE);
+ // (DIO3 is not free to be used as an IRQ)
#endif
+ // FIXME: May want to set depending on a definition, currently all SX126x variant files use the DC-DC regulator option
bool useRegulatorLDO = false; // Seems to depend on the connection to pin 9/DCC_SW - if an inductor DCDC?
RadioLibInterface::init();
-
- if (power == 0)
- power = SX126X_MAX_POWER;
-
- if (power > SX126X_MAX_POWER) // This chip has lower power limits than some
+ if (power > SX126X_MAX_POWER) // Clamp power to maximum defined level
power = SX126X_MAX_POWER;
limitPower();
@@ -54,49 +58,50 @@ template bool SX126xInterface::init()
LOG_INFO("Bandwidth set to %f\n", bw);
LOG_INFO("Power output set to %d\n", power);
- // current limit was removed from module' ctor
- // override default value (60 mA)
+ // Overriding current limit
+ // (https://github.com/jgromes/RadioLib/blob/690a050ebb46e6097c5d00c371e961c1caa3b52e/src/modules/SX126x/SX126x.cpp#L85) using
+ // value in SX126xInterface.h (currently 140 mA) It may or may not be neccessary, depending on how RadioLib functions, from
+ // SX1261/2 datasheet: OCP after setting DeviceSel with SetPaConfig(): SX1261 - 60 mA, SX1262 - 140 mA For the SX1268 the IC
+ // defaults to 140mA no matter the set power level, but RadioLib set it lower, this would need further checking Default values
+ // are: SX1262, SX1268: 0x38 (140 mA), SX1261: 0x18 (60 mA)
+ // FIXME: Not ideal to increase SX1261 current limit above 60mA as it can only transmit max 15dBm, should probably only do it
+ // if using SX1262 or SX1268
res = lora.setCurrentLimit(currentLimit);
LOG_DEBUG("Current limit set to %f\n", currentLimit);
LOG_DEBUG("Current limit set result %d\n", res);
-#if defined(SX126X_E22)
- // E22 Emulation explicitly requires DIO2 as RF switch, so set it to TRUE again for good measure. In case somebody defines
- // SX126X_TX for an E22 Module
- if (res == RADIOLIB_ERR_NONE) {
- LOG_DEBUG("SX126X_E22 mode enabled. Setting DIO2 as RF Switch\n");
- res = lora.setDio2AsRfSwitch(true);
- }
+#ifdef SX126X_DIO2_AS_RF_SWITCH
+ LOG_DEBUG("Setting DIO2 as RF switch\n");
+ bool dio2AsRfSwitch = true;
+#else
+ LOG_DEBUG("Setting DIO2 as not RF switch\n");
+ bool dio2AsRfSwitch = false;
#endif
+ if (res == RADIOLIB_ERR_NONE) {
+ res = lora.setDio2AsRfSwitch(dio2AsRfSwitch);
+ }
-#if defined(SX126X_TXEN) && (SX126X_TXEN != RADIOLIB_NC)
- // If SX126X_TXEN is connected to the MCU, we are manually controlling RX and TX.
- // But lora.begin (called above) sets Dio2 as RF switch control, which is not true here, so set it back to false.
- if (res == RADIOLIB_ERR_NONE) {
- LOG_DEBUG("SX126X_TXEN pin defined. Setting RF Switch: RXEN=%i, TXEN=%i\n", SX126X_RXEN, SX126X_TXEN);
- res = lora.setDio2AsRfSwitch(false);
- lora.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
- }
-#elif defined(SX126X_RXEN) && (SX126X_RXEN != RADIOLIB_NC && defined(E22_TXEN_CONNECTED_TO_DIO2))
- // Otherwise, if SX126X_RXEN is connected to the MCU, and E22_TXEN_CONNECTED_TO_DIO2 is defined, we are letting the
- // E22 control RX and TX via DIO2. In this configuration, the E22's TXEN and DIO2 pins are connected to each other,
- // but not to the MCU.
- // However, we must still connect the E22's RXEN pin to the MCU, define SX126X_RXEN accordingly, and then call
- // setRfSwitchPins, otherwise RX sensitivity (observed via RSSI) is greatly diminished.
- LOG_DEBUG("SX126X_RXEN and E22_TXEN_CONNECTED_TO_DIO2 are defined; value of res: %d", res);
- if (res == RADIOLIB_ERR_NONE) {
- LOG_DEBUG("SX126X_TXEN is RADIOLIB_NC, but SX126X_RXEN and E22_TXEN_CONNECTED_TO_DIO2 are both defined; calling "
- "lora.setRfSwitchPins.");
- lora.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
- }
+ // If a pin isn't defined, we set it to RADIOLIB_NC, it is safe to always do external RF switching with RADIOLIB_NC as it has
+ // no effect
+#ifndef SX126X_RXEN
+#define SX126X_RXEN RADIOLIB_NC
+ LOG_DEBUG("SX126X_RXEN not defined, defaulting to RADIOLIB_NC\n");
#endif
+#ifndef SX126X_TXEN
+#define SX126X_TXEN RADIOLIB_NC
+ LOG_DEBUG("SX126X_TXEN not defined, defaulting to RADIOLIB_NC\n");
+#endif
+ if (res == RADIOLIB_ERR_NONE) {
+ LOG_DEBUG("Using MCU pin %i as RXEN and pin %i as TXEN to control RF switching\n", SX126X_RXEN, SX126X_TXEN);
+ lora.setRfSwitchPins(SX126X_RXEN, SX126X_TXEN);
+ }
if (config.lora.sx126x_rx_boosted_gain) {
uint16_t result = lora.setRxBoostedGainMode(true);
- LOG_INFO("Set Rx Boosted Gain mode; result: %d\n", result);
+ LOG_INFO("Set RX gain to boosted mode; result: %d\n", result);
} else {
uint16_t result = lora.setRxBoostedGainMode(false);
- LOG_INFO("Set Rx Power Saving Gain mode; result: %d\n", result);
+ LOG_INFO("Set RX gain to power saving mode (boosted mode off); result: %d\n", result);
}
#if 0
@@ -265,7 +270,7 @@ template bool SX126xInterface::isChannelActive()
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
template bool SX126xInterface::isActivelyReceiving()
{
- // The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
+ // The IRQ status will be cleared when we start our read operation. Check if we've started a header, but haven't yet
// received and handled the interrupt for reading the packet/handling errors.
uint16_t irq = lora.getIrqStatus();
@@ -296,7 +301,7 @@ template bool SX126xInterface::sleep()
{
// Not keeping config is busted - next time nrf52 board boots lora sending fails tcxo related? - see datasheet
// \todo Display actual typename of the adapter, not just `SX126x`
- LOG_DEBUG("sx126x entering sleep mode (FIXME, don't keep config)\n");
+ LOG_DEBUG("SX126x entering sleep mode (FIXME, don't keep config)\n");
setStandby(); // Stop any pending operations
// turn off TCXO if it was powered
@@ -312,4 +317,4 @@ template bool SX126xInterface::sleep()
#endif
return true;
-}
\ No newline at end of file
+}
diff --git a/src/mesh/generated/meshtastic/config.pb.h b/src/mesh/generated/meshtastic/config.pb.h
index ef9d09a19..4b8bef8a1 100644
--- a/src/mesh/generated/meshtastic/config.pb.h
+++ b/src/mesh/generated/meshtastic/config.pb.h
@@ -51,8 +51,9 @@ typedef enum _meshtastic_Config_DeviceConfig_RebroadcastMode {
} meshtastic_Config_DeviceConfig_RebroadcastMode;
/* Bit field of boolean configuration options, indicating which optional
- fields to include when assembling POSITION messages
- Longitude and latitude are always included (also time if GPS-synced)
+ fields to include when assembling POSITION messages.
+ Longitude, latitude, altitude, speed, heading, and DOP
+ are always included (also time if GPS-synced)
NOTE: the more fields are included, the larger the message will be -
leading to longer airtime and a higher risk of packet loss */
typedef enum _meshtastic_Config_PositionConfig_PositionFlags {
@@ -236,6 +237,8 @@ typedef struct _meshtastic_Config_DeviceConfig {
/* If true, device is considered to be "managed" by a mesh administrator
Clients should then limit available configuration and administrative options inside the user interface */
bool is_managed;
+ /* Disables the triple-press of user button to enable or disable GPS */
+ bool disable_triple_click;
} meshtastic_Config_DeviceConfig;
/* Position Config */
@@ -271,6 +274,8 @@ typedef struct _meshtastic_Config_PositionConfig {
uint32_t broadcast_smart_minimum_distance;
/* The minimum number of seconds (since the last send) before we can send a position to the mesh if position_broadcast_smart_enabled */
uint32_t broadcast_smart_minimum_interval_secs;
+ /* (Re)define PIN_GPS_EN for your board. */
+ uint32_t gps_en_gpio;
} meshtastic_Config_PositionConfig;
/* Power Config\
@@ -398,7 +403,8 @@ typedef struct _meshtastic_Config_LoRaConfig {
/* The region code for the radio (US, CN, EU433, etc...) */
meshtastic_Config_LoRaConfig_RegionCode region;
/* Maximum number of hops. This can't be greater than 7.
- Default of 3 */
+ Default of 3
+ Attempting to set a value > 7 results in the default */
uint32_t hop_limit;
/* Disable TX from the LoRa radio. Useful for hot-swapping antennas and other tests.
Defaults to false */
@@ -529,8 +535,8 @@ extern "C" {
/* Initializer values for message structs */
#define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}}
-#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0}
-#define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0}
+#define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
@@ -538,8 +544,8 @@ extern "C" {
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}}
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0}
#define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}}
-#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0}
-#define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0}
+#define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
@@ -557,6 +563,7 @@ extern "C" {
#define meshtastic_Config_DeviceConfig_node_info_broadcast_secs_tag 7
#define meshtastic_Config_DeviceConfig_double_tap_as_button_press_tag 8
#define meshtastic_Config_DeviceConfig_is_managed_tag 9
+#define meshtastic_Config_DeviceConfig_disable_triple_click_tag 10
#define meshtastic_Config_PositionConfig_position_broadcast_secs_tag 1
#define meshtastic_Config_PositionConfig_position_broadcast_smart_enabled_tag 2
#define meshtastic_Config_PositionConfig_fixed_position_tag 3
@@ -568,6 +575,7 @@ extern "C" {
#define meshtastic_Config_PositionConfig_tx_gpio_tag 9
#define meshtastic_Config_PositionConfig_broadcast_smart_minimum_distance_tag 10
#define meshtastic_Config_PositionConfig_broadcast_smart_minimum_interval_secs_tag 11
+#define meshtastic_Config_PositionConfig_gps_en_gpio_tag 12
#define meshtastic_Config_PowerConfig_is_power_saving_tag 1
#define meshtastic_Config_PowerConfig_on_battery_shutdown_after_secs_tag 2
#define meshtastic_Config_PowerConfig_adc_multiplier_override_tag 3
@@ -652,7 +660,8 @@ X(a, STATIC, SINGULAR, UINT32, buzzer_gpio, 5) \
X(a, STATIC, SINGULAR, UENUM, rebroadcast_mode, 6) \
X(a, STATIC, SINGULAR, UINT32, node_info_broadcast_secs, 7) \
X(a, STATIC, SINGULAR, BOOL, double_tap_as_button_press, 8) \
-X(a, STATIC, SINGULAR, BOOL, is_managed, 9)
+X(a, STATIC, SINGULAR, BOOL, is_managed, 9) \
+X(a, STATIC, SINGULAR, BOOL, disable_triple_click, 10)
#define meshtastic_Config_DeviceConfig_CALLBACK NULL
#define meshtastic_Config_DeviceConfig_DEFAULT NULL
@@ -667,7 +676,8 @@ X(a, STATIC, SINGULAR, UINT32, position_flags, 7) \
X(a, STATIC, SINGULAR, UINT32, rx_gpio, 8) \
X(a, STATIC, SINGULAR, UINT32, tx_gpio, 9) \
X(a, STATIC, SINGULAR, UINT32, broadcast_smart_minimum_distance, 10) \
-X(a, STATIC, SINGULAR, UINT32, broadcast_smart_minimum_interval_secs, 11)
+X(a, STATIC, SINGULAR, UINT32, broadcast_smart_minimum_interval_secs, 11) \
+X(a, STATIC, SINGULAR, UINT32, gps_en_gpio, 12)
#define meshtastic_Config_PositionConfig_CALLBACK NULL
#define meshtastic_Config_PositionConfig_DEFAULT NULL
@@ -767,12 +777,12 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg;
/* Maximum encoded size of messages (where known) */
#define meshtastic_Config_BluetoothConfig_size 10
-#define meshtastic_Config_DeviceConfig_size 30
+#define meshtastic_Config_DeviceConfig_size 32
#define meshtastic_Config_DisplayConfig_size 28
#define meshtastic_Config_LoRaConfig_size 77
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
#define meshtastic_Config_NetworkConfig_size 195
-#define meshtastic_Config_PositionConfig_size 54
+#define meshtastic_Config_PositionConfig_size 60
#define meshtastic_Config_PowerConfig_size 40
#define meshtastic_Config_size 198
diff --git a/src/mesh/generated/meshtastic/deviceonly.pb.h b/src/mesh/generated/meshtastic/deviceonly.pb.h
index 143bd4482..c554074a2 100644
--- a/src/mesh/generated/meshtastic/deviceonly.pb.h
+++ b/src/mesh/generated/meshtastic/deviceonly.pb.h
@@ -316,7 +316,7 @@ extern const pb_msgdesc_t meshtastic_NodeRemoteHardwarePin_msg;
#define meshtastic_DeviceState_size 16854
#define meshtastic_NodeInfoLite_size 151
#define meshtastic_NodeRemoteHardwarePin_size 29
-#define meshtastic_OEMStore_size 3210
+#define meshtastic_OEMStore_size 3218
#define meshtastic_PositionLite_size 28
#ifdef __cplusplus
diff --git a/src/mesh/generated/meshtastic/localonly.pb.h b/src/mesh/generated/meshtastic/localonly.pb.h
index 471074948..f672d865c 100644
--- a/src/mesh/generated/meshtastic/localonly.pb.h
+++ b/src/mesh/generated/meshtastic/localonly.pb.h
@@ -174,7 +174,7 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
#define meshtastic_LocalModuleConfig_fields &meshtastic_LocalModuleConfig_msg
/* Maximum encoded size of messages (where known) */
-#define meshtastic_LocalConfig_size 455
+#define meshtastic_LocalConfig_size 463
#define meshtastic_LocalModuleConfig_size 609
#ifdef __cplusplus
diff --git a/src/mesh/generated/meshtastic/mesh.pb.h b/src/mesh/generated/meshtastic/mesh.pb.h
index f319c58b9..c32f55aab 100644
--- a/src/mesh/generated/meshtastic/mesh.pb.h
+++ b/src/mesh/generated/meshtastic/mesh.pb.h
@@ -111,6 +111,8 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_T_WATCH_S3 = 51,
/* Bobricius Picomputer with ESP32-S3 CPU, Keyboard and IPS display */
meshtastic_HardwareModel_PICOMPUTER_S3 = 52,
+ /* Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa */
+ meshtastic_HardwareModel_HELTEC_HT62 = 53,
/* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -297,9 +299,8 @@ typedef struct _meshtastic_Position {
/* In meters above MSL (but see issue #359) */
int32_t altitude;
/* This is usually not sent over the mesh (to save space), but it is sent
- from the phone so that the local device can set its RTC If it is sent over
- the mesh (because there are devices on the mesh without GPS), it will only
- be sent by devices which has a hardware GPS clock.
+ from the phone so that the local device can set its time if it is sent over
+ the mesh (because there are devices on the mesh without GPS or RTC).
seconds since 1970 */
uint32_t time;
/* TODO: REPLACE */
diff --git a/src/mesh/generated/meshtastic/module_config.pb.h b/src/mesh/generated/meshtastic/module_config.pb.h
index 828a44cc7..28a11ffcd 100644
--- a/src/mesh/generated/meshtastic/module_config.pb.h
+++ b/src/mesh/generated/meshtastic/module_config.pb.h
@@ -313,13 +313,14 @@ Initially created for the RAK14001 RGB LED module. */
typedef struct _meshtastic_ModuleConfig_AmbientLightingConfig {
/* Sets LED to on or off. */
bool led_state;
- /* Sets the overall current for the LED, firmware side range for the RAK14001 is 1-31, but users should be given a range of 0-100% */
+ /* Sets the current for the LED output. Default is 10. */
uint8_t current;
- uint8_t red; /* Red level */
- /* Sets the green level of the LED, firmware side values are 0-255, but users should be given a range of 0-100% */
- uint8_t green; /* Green level */
- /* Sets the blue level of the LED, firmware side values are 0-255, but users should be given a range of 0-100% */
- uint8_t blue; /* Blue level */
+ /* Sets the red LED level. Values are 0-255. */
+ uint8_t red;
+ /* Sets the green LED level. Values are 0-255. */
+ uint8_t green;
+ /* Sets the blue LED level. Values are 0-255. */
+ uint8_t blue;
} meshtastic_ModuleConfig_AmbientLightingConfig;
/* A GPIO pin definition for remote hardware module */
diff --git a/src/mesh/generated/meshtastic/portnums.pb.h b/src/mesh/generated/meshtastic/portnums.pb.h
index 44e9df70d..c94349d47 100644
--- a/src/mesh/generated/meshtastic/portnums.pb.h
+++ b/src/mesh/generated/meshtastic/portnums.pb.h
@@ -69,6 +69,9 @@ typedef enum _meshtastic_PortNum {
NOTE: audio frames contain a 3 byte header (0xc0 0xde 0xc2) and a one byte marker for the decompressed bitrate.
This marker comes from the 'moduleConfig.audio.bitrate' enum minus one. */
meshtastic_PortNum_AUDIO_APP = 9,
+ /* Same as Text Message but originating from Detection Sensor Module.
+ NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 */
+ meshtastic_PortNum_DETECTION_SENSOR_APP = 10,
/* Provides a 'ping' service that replies to any packet it receives.
Also serves as a small example module.
ENCODING: ASCII Plaintext */
@@ -88,7 +91,8 @@ typedef enum _meshtastic_PortNum {
ENCODING: Protobuf */
meshtastic_PortNum_STORE_FORWARD_APP = 65,
/* Optional port for messages for the range test module.
- ENCODING: ASCII Plaintext */
+ ENCODING: ASCII Plaintext
+ NOTE: This portnum traffic is not sent to the public MQTT starting at firmware version 2.2.9 */
meshtastic_PortNum_RANGE_TEST_APP = 66,
/* Provides a format to send and receive telemetry data from the Meshtastic network.
Maintained by Charles Crossan (crossan007) : crossan007@gmail.com
diff --git a/src/mesh/generated/meshtastic/telemetry.pb.h b/src/mesh/generated/meshtastic/telemetry.pb.h
index 4a9efc337..760286598 100644
--- a/src/mesh/generated/meshtastic/telemetry.pb.h
+++ b/src/mesh/generated/meshtastic/telemetry.pb.h
@@ -101,11 +101,7 @@ typedef struct _meshtastic_AirQualityMetrics {
/* Types of Measurements the telemetry module is equipped to handle */
typedef struct _meshtastic_Telemetry {
- /* This is usually not sent over the mesh (to save space), but it is sent
- from the phone so that the local device can set its RTC If it is sent over
- the mesh (because there are devices on the mesh without GPS), it will only
- be sent by devices which has a hardware GPS clock (IE Mobile Phone).
- seconds since 1970 */
+ /* Seconds since 1970 - or 0 for unknown/unset */
uint32_t time;
pb_size_t which_variant;
union {
diff --git a/src/mesh/mesh-pb-constants.cpp b/src/mesh/mesh-pb-constants.cpp
index 994fab61f..93dbf0178 100644
--- a/src/mesh/mesh-pb-constants.cpp
+++ b/src/mesh/mesh-pb-constants.cpp
@@ -25,7 +25,7 @@ bool pb_decode_from_bytes(const uint8_t *srcbuf, size_t srcbufsize, const pb_msg
{
pb_istream_t stream = pb_istream_from_buffer(srcbuf, srcbufsize);
if (!pb_decode(&stream, fields, dest_struct)) {
- LOG_ERROR("Can't decode protobuf reason='%s', pb_msgdesc 0x%p\n", PB_GET_ERROR(&stream), fields);
+ LOG_ERROR("Can't decode protobuf reason='%s', pb_msgdesc %p\n", PB_GET_ERROR(&stream), fields);
return false;
} else {
return true;
diff --git a/src/meshUtils.cpp b/src/meshUtils.cpp
new file mode 100644
index 000000000..cab05e54b
--- /dev/null
+++ b/src/meshUtils.cpp
@@ -0,0 +1,58 @@
+#include "meshUtils.h"
+#include
+
+/*
+ * Find the first occurrence of find in s, where the search is limited to the
+ * first slen characters of s.
+ * -
+ * Copyright (c) 2001 Mike Barcroft
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Torek.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+char *strnstr(const char *s, const char *find, size_t slen)
+{
+ char c, sc;
+ size_t len;
+
+ if ((c = *find++) != '\0') {
+ len = strlen(find);
+ do {
+ do {
+ if (slen-- < 1 || (sc = *s++) == '\0')
+ return (NULL);
+ } while (sc != c);
+ if (len > slen)
+ return (NULL);
+ } while (strncmp(s, find, len) != 0);
+ s--;
+ }
+ return ((char *)s);
+}
\ No newline at end of file
diff --git a/src/utils.h b/src/meshUtils.h
similarity index 62%
rename from src/utils.h
rename to src/meshUtils.h
index a725bf13c..a6436a8d5 100644
--- a/src/utils.h
+++ b/src/meshUtils.h
@@ -4,4 +4,9 @@
template constexpr const T &clamp(const T &v, const T &lo, const T &hi)
{
return (v < lo) ? lo : (hi < v) ? hi : v;
-}
\ No newline at end of file
+}
+
+#if (defined(ARCH_PORTDUINO) && !defined(STRNSTR))
+#define STRNSTR
+char *strnstr(const char *s, const char *find, size_t slen);
+#endif
\ No newline at end of file
diff --git a/src/modules/CannedMessageModule.cpp b/src/modules/CannedMessageModule.cpp
index 1e605656f..ade9d0e5a 100644
--- a/src/modules/CannedMessageModule.cpp
+++ b/src/modules/CannedMessageModule.cpp
@@ -65,8 +65,8 @@ CannedMessageModule::CannedMessageModule()
{
if (moduleConfig.canned_message.enabled || CANNED_MESSAGE_MODULE_ENABLE) {
this->loadProtoForModule();
- if ((this->splitConfiguredMessages() <= 0) && (cardkb_found.address != CARDKB_ADDR) &&
- (cardkb_found.address != TDECK_KB_ADDR) && !INPUTBROKER_MATRIX_TYPE && !CANNED_MESSAGE_MODULE_ENABLE) {
+ if ((this->splitConfiguredMessages() <= 0) && (cardkb_found.address == 0x00) && !INPUTBROKER_MATRIX_TYPE &&
+ !CANNED_MESSAGE_MODULE_ENABLE) {
LOG_INFO("CannedMessageModule: No messages are configured. Module is disabled\n");
this->runState = CANNED_MESSAGE_RUN_STATE_DISABLED;
disable();
@@ -171,7 +171,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
(event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) ||
(event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_RIGHT))) {
// LOG_DEBUG("Canned message event (%x)\n", event->kbchar);
- // tweak for left/right events generated via trackball/touch with empty kbchar
+ // tweak for left/right events generated via trackball/touch with empty kbchar
if (!event->kbchar) {
if (event->inputEvent == static_cast(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_LEFT)) {
this->payload = 0xb4;
@@ -195,6 +195,7 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
}
// pass the pressed key
+ // LOG_DEBUG("Canned message ANYKEY (%x)\n", event->kbchar);
this->payload = event->kbchar;
this->lastTouchMillis = millis();
validEvent = true;
@@ -649,4 +650,4 @@ String CannedMessageModule::drawWithCursor(String text, int cursor)
return result;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/modules/DetectionSensorModule.cpp b/src/modules/DetectionSensorModule.cpp
index c8eb7793b..f1fc26244 100644
--- a/src/modules/DetectionSensorModule.cpp
+++ b/src/modules/DetectionSensorModule.cpp
@@ -46,10 +46,7 @@ int32_t DetectionSensorModule::runOnce()
if ((millis() - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs) &&
hasDetectionEvent()) {
sendDetectionMessage();
- return getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs <
- moduleConfig.detection_sensor.state_broadcast_secs
- ? moduleConfig.detection_sensor.minimum_broadcast_secs
- : moduleConfig.detection_sensor.state_broadcast_secs);
+ return DELAYED_INTERVAL;
}
// Even if we haven't detected an event, broadcast our current state to the mesh on the scheduled interval as a sort
// of heartbeat. We only do this if the minimum broadcast interval is greater than zero, otherwise we'll only broadcast state
@@ -57,10 +54,7 @@ int32_t DetectionSensorModule::runOnce()
else if (moduleConfig.detection_sensor.state_broadcast_secs > 0 &&
(millis() - lastSentToMesh) >= getConfiguredOrDefaultMs(moduleConfig.detection_sensor.state_broadcast_secs)) {
sendCurrentStateMessage();
- return getConfiguredOrDefaultMs(moduleConfig.detection_sensor.minimum_broadcast_secs <
- moduleConfig.detection_sensor.state_broadcast_secs
- ? moduleConfig.detection_sensor.minimum_broadcast_secs
- : moduleConfig.detection_sensor.state_broadcast_secs);
+ return DELAYED_INTERVAL;
}
return GPIO_POLLING_INTERVAL;
}
diff --git a/src/modules/DetectionSensorModule.h b/src/modules/DetectionSensorModule.h
index bcc0b9419..ed6cddda5 100644
--- a/src/modules/DetectionSensorModule.h
+++ b/src/modules/DetectionSensorModule.h
@@ -5,7 +5,7 @@ class DetectionSensorModule : public SinglePortModule, private concurrency::OSTh
{
public:
DetectionSensorModule()
- : SinglePortModule("detection", meshtastic_PortNum_TEXT_MESSAGE_APP), OSThread("DetectionSensorModule")
+ : SinglePortModule("detection", meshtastic_PortNum_DETECTION_SENSOR_APP), OSThread("DetectionSensorModule")
{
}
diff --git a/src/modules/ExternalNotificationModule.cpp b/src/modules/ExternalNotificationModule.cpp
index cbcf4e452..1e0e8250d 100644
--- a/src/modules/ExternalNotificationModule.cpp
+++ b/src/modules/ExternalNotificationModule.cpp
@@ -27,7 +27,6 @@
#ifdef HAS_NCP5623
#include
-NCP5623 rgb;
uint8_t red = 0;
uint8_t green = 0;
@@ -128,6 +127,11 @@ int32_t ExternalNotificationModule::runOnce()
}
}
+bool ExternalNotificationModule::wantPacket(const meshtastic_MeshPacket *p)
+{
+ return MeshService::isTextPayload(p);
+}
+
/**
* Sets the external notification on for the specified index.
*
@@ -212,8 +216,8 @@ void ExternalNotificationModule::stopNow()
}
ExternalNotificationModule::ExternalNotificationModule()
- : SinglePortModule("ExternalNotificationModule", meshtastic_PortNum_TEXT_MESSAGE_APP), concurrency::OSThread(
- "ExternalNotificationModule")
+ : SinglePortModule("ExternalNotificationModule", meshtastic_PortNum_TEXT_MESSAGE_APP),
+ concurrency::OSThread("ExternalNotificationModule")
{
/*
Uncomment the preferences below if you want to use the module
diff --git a/src/modules/ExternalNotificationModule.h b/src/modules/ExternalNotificationModule.h
index f8ec053dd..3331ec428 100644
--- a/src/modules/ExternalNotificationModule.h
+++ b/src/modules/ExternalNotificationModule.h
@@ -52,6 +52,8 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
virtual int32_t runOnce() override;
+ virtual bool wantPacket(const meshtastic_MeshPacket *p) override;
+
bool isNagging = false;
virtual AdminMessageHandleResult handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
@@ -59,4 +61,4 @@ class ExternalNotificationModule : public SinglePortModule, private concurrency:
meshtastic_AdminMessage *response) override;
};
-extern ExternalNotificationModule *externalNotificationModule;
+extern ExternalNotificationModule *externalNotificationModule;
\ No newline at end of file
diff --git a/src/modules/Modules.cpp b/src/modules/Modules.cpp
index bcb33c640..1780a8528 100644
--- a/src/modules/Modules.cpp
+++ b/src/modules/Modules.cpp
@@ -31,7 +31,7 @@
#if defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)
#include "modules/ExternalNotificationModule.h"
#include "modules/RangeTestModule.h"
-#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(CONFIG_IDF_TARGET_ESP32S2)
+#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2)
#include "modules/SerialModule.h"
#endif
#endif
@@ -60,9 +60,15 @@ void setupModules()
new ReplyModule();
#if HAS_BUTTON
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
- rotaryEncoderInterruptImpl1->init();
+ if (!rotaryEncoderInterruptImpl1->init()) {
+ delete rotaryEncoderInterruptImpl1;
+ rotaryEncoderInterruptImpl1 = nullptr;
+ }
upDownInterruptImpl1 = new UpDownInterruptImpl1();
- upDownInterruptImpl1->init();
+ if (!upDownInterruptImpl1->init()) {
+ delete upDownInterruptImpl1;
+ upDownInterruptImpl1 = nullptr;
+ }
cardKbI2cImpl = new CardKbI2cImpl();
cardKbI2cImpl->init();
#ifdef INPUTBROKER_MATRIX_TYPE
@@ -86,7 +92,8 @@ void setupModules()
new AirQualityTelemetryModule();
}
#endif
-#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
+#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \
+ !defined(CONFIG_IDF_TARGET_ESP32C3)
new SerialModule();
#endif
#ifdef ARCH_ESP32
diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp
index 706b1db58..cf2276f0e 100644
--- a/src/modules/NeighborInfoModule.cpp
+++ b/src/modules/NeighborInfoModule.cpp
@@ -15,12 +15,12 @@ NOTE: For debugging only
*/
void NeighborInfoModule::printNeighborInfo(const char *header, const meshtastic_NeighborInfo *np)
{
- LOG_DEBUG("%s NEIGHBORINFO PACKET from Node %d to Node %d (last sent by %d)\n", header, np->node_id, nodeDB.getNodeNum(),
- np->last_sent_by_id);
+ LOG_DEBUG("%s NEIGHBORINFO PACKET from Node 0x%x to Node 0x%x (last sent by 0x%x)\n", header, np->node_id,
+ nodeDB.getNodeNum(), np->last_sent_by_id);
LOG_DEBUG("----------------\n");
LOG_DEBUG("Packet contains %d neighbors\n", np->neighbors_count);
for (int i = 0; i < np->neighbors_count; i++) {
- LOG_DEBUG("Neighbor %d: node_id=%d, snr=%.2f\n", i, np->neighbors[i].node_id, np->neighbors[i].snr);
+ LOG_DEBUG("Neighbor %d: node_id=0x%x, snr=%.2f\n", i, np->neighbors[i].node_id, np->neighbors[i].snr);
}
LOG_DEBUG("----------------\n");
}
@@ -31,12 +31,12 @@ NOTE: for debugging only
void NeighborInfoModule::printNodeDBNodes(const char *header)
{
int num_nodes = nodeDB.getNumMeshNodes();
- LOG_DEBUG("%s NODEDB SELECTION from Node %d:\n", header, nodeDB.getNodeNum());
+ LOG_DEBUG("%s NODEDB SELECTION from Node 0x%x:\n", header, nodeDB.getNodeNum());
LOG_DEBUG("----------------\n");
LOG_DEBUG("DB contains %d nodes\n", num_nodes);
for (int i = 0; i < num_nodes; i++) {
const meshtastic_NodeInfoLite *dbEntry = nodeDB.getMeshNodeByIndex(i);
- LOG_DEBUG(" Node %d: node_id=%d, snr=%.2f\n", i, dbEntry->num, dbEntry->snr);
+ LOG_DEBUG(" Node %d: node_id=0x%x, snr=%.2f\n", i, dbEntry->num, dbEntry->snr);
}
LOG_DEBUG("----------------\n");
}
@@ -48,12 +48,12 @@ NOTE: for debugging only
void NeighborInfoModule::printNodeDBNeighbors(const char *header)
{
int num_neighbors = getNumNeighbors();
- LOG_DEBUG("%s NODEDB SELECTION from Node %d:\n", header, nodeDB.getNodeNum());
+ LOG_DEBUG("%s NODEDB SELECTION from Node 0x%x:\n", header, nodeDB.getNodeNum());
LOG_DEBUG("----------------\n");
LOG_DEBUG("DB contains %d neighbors\n", num_neighbors);
for (int i = 0; i < num_neighbors; i++) {
const meshtastic_Neighbor *dbEntry = getNeighborByIndex(i);
- LOG_DEBUG(" Node %d: node_id=%d, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
+ LOG_DEBUG(" Node %d: node_id=0x%x, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
}
LOG_DEBUG("----------------\n");
}
@@ -66,7 +66,7 @@ NOTE: For debugging only
void NeighborInfoModule::printNodeDBSelection(const char *header, const meshtastic_NeighborInfo *np)
{
int num_neighbors = getNumNeighbors();
- LOG_DEBUG("%s NODEDB SELECTION from Node %d:\n", header, nodeDB.getNodeNum());
+ LOG_DEBUG("%s NODEDB SELECTION from Node 0x%x:\n", header, nodeDB.getNodeNum());
LOG_DEBUG("----------------\n");
LOG_DEBUG("Selected %d neighbors of %d DB neighbors\n", np->neighbors_count, num_neighbors);
for (int i = 0; i < num_neighbors; i++) {
@@ -78,9 +78,9 @@ void NeighborInfoModule::printNodeDBSelection(const char *header, const meshtast
}
}
if (!chosen) {
- LOG_DEBUG(" Node %d: neighbor=%d, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
+ LOG_DEBUG(" Node %d: neighbor=0x%x, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
} else {
- LOG_DEBUG("---> Node %d: neighbor=%d, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
+ LOG_DEBUG("---> Node %d: neighbor=0x%x, snr=%.2f\n", i, dbEntry->node_id, dbEntry->snr);
}
}
LOG_DEBUG("----------------\n");
@@ -103,16 +103,6 @@ NeighborInfoModule::NeighborInfoModule()
}
}
-/*
-Allocate a zeroed neighbor info packet
-*/
-meshtastic_NeighborInfo *NeighborInfoModule::allocateNeighborInfoPacket()
-{
- meshtastic_NeighborInfo *neighborInfo = (meshtastic_NeighborInfo *)malloc(sizeof(meshtastic_NeighborInfo));
- memset(neighborInfo, 0, sizeof(meshtastic_NeighborInfo));
- return neighborInfo;
-}
-
/*
Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
Assumes that the neighborInfo packet has been allocated
@@ -120,7 +110,7 @@ Assumes that the neighborInfo packet has been allocated
*/
uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo)
{
- int my_node_id = nodeDB.getNodeNum();
+ uint my_node_id = nodeDB.getNodeNum();
neighborInfo->node_id = my_node_id;
neighborInfo->last_sent_by_id = my_node_id;
neighborInfo->node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
@@ -184,14 +174,14 @@ size_t NeighborInfoModule::cleanUpNeighbors()
/* Send neighbor info to the mesh */
void NeighborInfoModule::sendNeighborInfo(NodeNum dest, bool wantReplies)
{
- meshtastic_NeighborInfo *neighborInfo = allocateNeighborInfoPacket();
- collectNeighborInfo(neighborInfo);
- meshtastic_MeshPacket *p = allocDataProtobuf(*neighborInfo);
+ meshtastic_NeighborInfo neighborInfo = meshtastic_NeighborInfo_init_zero;
+ collectNeighborInfo(&neighborInfo);
+ meshtastic_MeshPacket *p = allocDataProtobuf(neighborInfo);
// send regardless of whether or not we have neighbors in our DB,
// because we want to get neighbors for the next cycle
p->to = dest;
p->decoded.want_response = wantReplies;
- printNeighborInfo("SENDING", neighborInfo);
+ printNeighborInfo("SENDING", &neighborInfo);
service.sendToMesh(p, RX_SRC_LOCAL, true);
}
@@ -212,8 +202,10 @@ Pass it to an upper client; do not persist this data on the mesh
*/
bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *np)
{
- printNeighborInfo("RECEIVED", np);
- updateNeighbors(mp, np);
+ if (enabled) {
+ printNeighborInfo("RECEIVED", np);
+ updateNeighbors(mp, np);
+ }
// Allow others to handle this packet
return false;
}
@@ -255,7 +247,7 @@ void NeighborInfoModule::updateNeighbors(const meshtastic_MeshPacket &mp, const
}
meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSender, NodeNum n,
- uint32_t node_broadcast_interval_secs, int snr)
+ uint32_t node_broadcast_interval_secs, float snr)
{
// our node and the phone are the same node (not neighbors)
if (n == 0) {
@@ -277,7 +269,7 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen
}
// otherwise, allocate one and assign data to it
// TODO: max memory for the database should take neighbors into account, but currently doesn't
- if (*numNeighbors < MAX_NUM_NODES) {
+ if (*numNeighbors < MAX_NUM_NEIGHBORS) {
(*numNeighbors)++;
}
meshtastic_Neighbor *new_nbr = &neighbors[((*numNeighbors) - 1)];
diff --git a/src/modules/NeighborInfoModule.h b/src/modules/NeighborInfoModule.h
index 20f68ca87..0e3ec09ca 100644
--- a/src/modules/NeighborInfoModule.h
+++ b/src/modules/NeighborInfoModule.h
@@ -49,7 +49,7 @@ class NeighborInfoModule : public ProtobufModule, priva
meshtastic_NeighborInfo *allocateNeighborInfoPacket();
// Find a neighbor in our DB, create an empty neighbor if missing
- meshtastic_Neighbor *getOrCreateNeighbor(NodeNum originalSender, NodeNum n, uint32_t node_broadcast_interval_secs, int snr);
+ meshtastic_Neighbor *getOrCreateNeighbor(NodeNum originalSender, NodeNum n, uint32_t node_broadcast_interval_secs, float snr);
/*
* Send info on our node's neighbors into the mesh
diff --git a/src/modules/PositionModule.cpp b/src/modules/PositionModule.cpp
index cb49f0a8f..39e467cda 100644
--- a/src/modules/PositionModule.cpp
+++ b/src/modules/PositionModule.cpp
@@ -11,8 +11,8 @@
PositionModule *positionModule;
PositionModule::PositionModule()
- : ProtobufModule("position", meshtastic_PortNum_POSITION_APP, &meshtastic_Position_msg), concurrency::OSThread(
- "PositionModule")
+ : ProtobufModule("position", meshtastic_PortNum_POSITION_APP, &meshtastic_Position_msg),
+ concurrency::OSThread("PositionModule")
{
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
setIntervalFromNow(60 * 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)
@@ -33,12 +33,12 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
// return false;
}
- // Log packet size and list of fields
- LOG_INFO("POSITION node=%08x l=%d %s%s%s%s%s%s%s%s%s%s%s%s%s\n", getFrom(&mp), mp.decoded.payload.size,
- p.latitude_i ? "LAT " : "", p.longitude_i ? "LON " : "", p.altitude ? "MSL " : "", p.altitude_hae ? "HAE " : "",
- p.altitude_geoidal_separation ? "GEO " : "", p.PDOP ? "PDOP " : "", p.HDOP ? "HDOP " : "", p.VDOP ? "VDOP " : "",
- p.sats_in_view ? "SIV " : "", p.fix_quality ? "FXQ " : "", p.fix_type ? "FXT " : "", p.timestamp ? "PTS " : "",
- p.time ? "TIME " : "");
+ // Log packet size and data fields
+ LOG_INFO("POSITION node=%08x l=%d latI=%d lonI=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d "
+ "time=%d\n",
+ getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae,
+ p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp,
+ p.time);
if (p.time) {
struct timeval tv;
@@ -65,7 +65,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
meshtastic_MeshPacket *PositionModule::allocReply()
{
if (ignoreRequest) {
- return NULL;
+ return nullptr;
}
meshtastic_NodeInfoLite *node = service.refreshLocalMeshNode(); // should guarantee there is now a position
@@ -142,6 +142,11 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha
service.cancelSending(prevPacketId);
meshtastic_MeshPacket *p = allocReply();
+ if (p == nullptr) {
+ LOG_WARN("allocReply returned a nullptr");
+ return;
+ }
+
p->to = dest;
p->decoded.want_response = wantReplies;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER)
@@ -188,26 +193,19 @@ int32_t PositionModule::runOnce()
const meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position
if (hasValidPosition(node2)) {
- // The minimum distance to travel before we are able to send a new position packet.
- const uint32_t distanceTravelThreshold =
- config.position.broadcast_smart_minimum_distance > 0 ? config.position.broadcast_smart_minimum_distance : 100;
-
// The minimum time (in seconds) that would pass before we are able to send a new position packet.
const uint32_t minimumTimeThreshold =
getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30);
- // Determine the distance in meters between two points on the globe
- float distanceTraveledSinceLastSend =
- GeoCoord::latLongToMeter(lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7, node->position.latitude_i * 1e-7,
- node->position.longitude_i * 1e-7);
+ auto smartPosition = getDistanceTraveledSinceLastSend(node->position);
- if ((abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold) && msSinceLastSend >= minimumTimeThreshold) {
+ if (smartPosition.hasTraveledOverThreshold && msSinceLastSend >= minimumTimeThreshold) {
bool requestReplies = currentGeneration != radioGeneration;
currentGeneration = radioGeneration;
LOG_INFO("Sending smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, "
"minTimeInterval=%ims)\n",
- localPosition.timestamp, abs(distanceTraveledSinceLastSend), distanceTravelThreshold,
+ localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold,
msSinceLastSend, minimumTimeThreshold);
sendOurPosition(NODENUM_BROADCAST, requestReplies);
@@ -225,4 +223,48 @@ int32_t PositionModule::runOnce()
}
return 5000; // to save power only wake for our callback occasionally
+}
+
+struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition)
+{
+ // The minimum distance to travel before we are able to send a new position packet.
+ const uint32_t distanceTravelThreshold = getConfiguredOrDefault(config.position.broadcast_smart_minimum_distance, 100);
+
+ // Determine the distance in meters between two points on the globe
+ float distanceTraveledSinceLastSend = GeoCoord::latLongToMeter(
+ lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7, currentPosition.latitude_i * 1e-7, currentPosition.longitude_i * 1e-7);
+
+ return SmartPosition{.distanceTraveled = abs(distanceTraveledSinceLastSend),
+ .distanceThreshold = distanceTravelThreshold,
+ .hasTraveledOverThreshold = abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold};
+}
+
+void PositionModule::handleNewPosition()
+{
+ meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
+ const meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position
+ // We limit our GPS broadcasts to a max rate
+ uint32_t now = millis();
+ uint32_t msSinceLastSend = now - lastGpsSend;
+
+ if (hasValidPosition(node2)) {
+ auto smartPosition = getDistanceTraveledSinceLastSend(node->position);
+ if (smartPosition.hasTraveledOverThreshold) {
+ bool requestReplies = currentGeneration != radioGeneration;
+ currentGeneration = radioGeneration;
+
+ LOG_INFO("Sending smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims)\n",
+ localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold, msSinceLastSend);
+ sendOurPosition(NODENUM_BROADCAST, requestReplies);
+
+ // Set the current coords as our last ones, after we've compared distance with current and decided to send
+ lastGpsLatitude = node->position.latitude_i;
+ lastGpsLongitude = node->position.longitude_i;
+
+ /* Update lastGpsSend to now. This means if the device is stationary, then
+ getPref_position_broadcast_secs will still apply.
+ */
+ lastGpsSend = now;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/modules/PositionModule.h b/src/modules/PositionModule.h
index aaa5c76c5..3114f31e1 100644
--- a/src/modules/PositionModule.h
+++ b/src/modules/PositionModule.h
@@ -31,6 +31,8 @@ class PositionModule : public ProtobufModule, private concu
*/
void sendOurPosition(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false, uint8_t channel = 0);
+ void handleNewPosition();
+
protected:
/** Called to handle a particular incoming message
@@ -44,6 +46,15 @@ class PositionModule : public ProtobufModule, private concu
/** Does our periodic broadcast */
virtual int32_t runOnce() override;
+
+ private:
+ struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition);
+};
+
+struct SmartPosition {
+ float distanceTraveled;
+ uint32_t distanceThreshold;
+ bool hasTraveledOverThreshold;
};
extern PositionModule *positionModule;
diff --git a/src/modules/RangeTestModule.h b/src/modules/RangeTestModule.h
index ae2a8f182..b632d343e 100644
--- a/src/modules/RangeTestModule.h
+++ b/src/modules/RangeTestModule.h
@@ -29,7 +29,7 @@ class RangeTestModuleRadio : public SinglePortModule
uint32_t lastRxID = 0;
public:
- RangeTestModuleRadio() : SinglePortModule("RangeTestModuleRadio", meshtastic_PortNum_TEXT_MESSAGE_APP)
+ RangeTestModuleRadio() : SinglePortModule("RangeTestModuleRadio", meshtastic_PortNum_RANGE_TEST_APP)
{
loopbackOk = true; // Allow locally generated messages to loop back to the client
}
diff --git a/src/modules/SerialModule.cpp b/src/modules/SerialModule.cpp
index 16cd3a263..42b109050 100644
--- a/src/modules/SerialModule.cpp
+++ b/src/modules/SerialModule.cpp
@@ -44,9 +44,10 @@
*/
-#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
+#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \
+ !defined(CONFIG_IDF_TARGET_ESP32C3)
-#define RX_BUFFER 128
+#define RX_BUFFER 256
#define TIMEOUT 250
#define BAUD 38400
#define ACK 1
@@ -141,7 +142,12 @@ int32_t SerialModule::runOnce()
}
#elif !defined(TTGO_T_ECHO)
if (moduleConfig.serial.rxd && moduleConfig.serial.txd) {
+#ifdef ARCH_RP2040
+ Serial2.setFIFOSize(RX_BUFFER);
+ Serial2.setPinout(moduleConfig.serial.txd, moduleConfig.serial.rxd);
+#else
Serial2.setPins(moduleConfig.serial.rxd, moduleConfig.serial.txd);
+#endif
Serial2.begin(baud, SERIAL_8N1);
Serial2.setTimeout(moduleConfig.serial.timeout > 0 ? moduleConfig.serial.timeout : TIMEOUT);
} else {
@@ -182,7 +188,7 @@ int32_t SerialModule::runOnce()
}
}
}
-#ifndef TTGO_T_ECHO
+#if !defined(TTGO_T_ECHO)
else {
while (Serial2.available()) {
serialPayloadSize = Serial2.readBytes(serialBytes, meshtastic_Constants_DATA_PAYLOAD_LEN);
diff --git a/src/modules/SerialModule.h b/src/modules/SerialModule.h
index 562ccd42b..18ad8a1ba 100644
--- a/src/modules/SerialModule.h
+++ b/src/modules/SerialModule.h
@@ -8,7 +8,8 @@
#include
#include
-#if (defined(ARCH_ESP32) || defined(ARCH_NRF52)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && !defined(CONFIG_IDF_TARGET_ESP32C3)
+#if (defined(ARCH_ESP32) || defined(ARCH_NRF52) || defined(ARCH_RP2040)) && !defined(CONFIG_IDF_TARGET_ESP32S2) && \
+ !defined(CONFIG_IDF_TARGET_ESP32C3)
class SerialModule : public StreamAPI, private concurrency::OSThread
{
@@ -74,4 +75,4 @@ class SerialModuleRadio : public MeshModule
extern SerialModuleRadio *serialModuleRadio;
-#endif
+#endif
\ No newline at end of file
diff --git a/src/modules/TextMessageModule.cpp b/src/modules/TextMessageModule.cpp
index 8ff034fa9..0f86a6470 100644
--- a/src/modules/TextMessageModule.cpp
+++ b/src/modules/TextMessageModule.cpp
@@ -1,4 +1,5 @@
#include "TextMessageModule.h"
+#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
#include "configuration.h"
@@ -22,3 +23,8 @@ ProcessMessage TextMessageModule::handleReceived(const meshtastic_MeshPacket &mp
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
+
+bool TextMessageModule::wantPacket(const meshtastic_MeshPacket *p)
+{
+ return MeshService::isTextPayload(p);
+}
\ No newline at end of file
diff --git a/src/modules/TextMessageModule.h b/src/modules/TextMessageModule.h
index 93b1bfaa2..cc0b0f9d5 100644
--- a/src/modules/TextMessageModule.h
+++ b/src/modules/TextMessageModule.h
@@ -20,6 +20,7 @@ class TextMessageModule : public SinglePortModule, public Observablenode_id);
+ msgPayload["node_broadcast_interval_secs"] = new JSONValue((uint)decoded->node_broadcast_interval_secs);
+ msgPayload["last_sent_by_id"] = new JSONValue((uint)decoded->last_sent_by_id);
msgPayload["neighbors_count"] = new JSONValue(decoded->neighbors_count);
- msgPayload["neighbors"] = new JSONValue(decoded->neighbors);
+ JSONArray neighbors;
+ for (uint8_t i = 0; i < decoded->neighbors_count; i++) {
+ JSONObject neighborObj;
+ neighborObj["node_id"] = new JSONValue((uint)decoded->neighbors[i].node_id);
+ neighborObj["snr"] = new JSONValue((int)decoded->neighbors[i].snr);
+ neighbors.push_back(new JSONValue(neighborObj));
+ }
+ msgPayload["neighbors"] = new JSONValue(neighbors);
+ jsonObj["payload"] = new JSONValue(msgPayload);
} else {
LOG_ERROR("Error decoding protobuf for neighborinfo message!\n");
}
diff --git a/src/platform/esp32/architecture.h b/src/platform/esp32/architecture.h
index 8f71bace6..163cc8b84 100644
--- a/src/platform/esp32/architecture.h
+++ b/src/platform/esp32/architecture.h
@@ -119,22 +119,8 @@
#define HW_VENDOR meshtastic_HardwareModel_BETAFPV_900_NANO_TX
#elif defined(PICOMPUTER_S3)
#define HW_VENDOR meshtastic_HardwareModel_PICOMPUTER_S3
-#endif
-
-//
-// Standard definitions for ESP32 targets
-//
-
-#define GPS_SERIAL_NUM 1
-#ifndef GPS_RX_PIN
-#define GPS_RX_PIN 34
-#endif
-#ifndef GPS_TX_PIN
-#ifdef USE_JTAG
-#define GPS_TX_PIN -1
-#else
-#define GPS_TX_PIN 12
-#endif
+#elif defined(HELTEC_HT62)
+#define HW_VENDOR meshtastic_HardwareModel_HELTEC_HT62
#endif
// -----------------------------------------------------------------------------
diff --git a/src/platform/esp32/main-esp32.cpp b/src/platform/esp32/main-esp32.cpp
index 8abe6d56d..17a312664 100644
--- a/src/platform/esp32/main-esp32.cpp
+++ b/src/platform/esp32/main-esp32.cpp
@@ -9,10 +9,10 @@
#include "BleOta.h"
#include "mesh/http/WiFiAPClient.h"
+#include "meshUtils.h"
#include "sleep.h"
#include "soc/rtc.h"
#include "target_specific.h"
-#include "utils.h"
#include
#include
#include
@@ -220,4 +220,4 @@ void cpuDeepSleep(uint32_t msecToWake)
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery)
-}
+}
\ No newline at end of file
diff --git a/src/sleep.cpp b/src/sleep.cpp
index 3d6da7feb..62ce0064f 100644
--- a/src/sleep.cpp
+++ b/src/sleep.cpp
@@ -90,33 +90,6 @@ void setLed(bool ledOn)
#endif
}
-void setGPSPower(bool on)
-{
- LOG_INFO("Setting GPS power=%d\n", on);
-
-#ifdef PIN_GPS_EN
- digitalWrite(PIN_GPS_EN, on ? 1 : 0);
-#endif
-
-#ifdef HAS_PMU
- if (pmu_found && PMU) {
- uint8_t model = PMU->getChipModel();
- if (model == XPOWERS_AXP2101) {
- if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
- // t-beam v1.2 GNSS power channel
- on ? PMU->enablePowerOutput(XPOWERS_ALDO3) : PMU->disablePowerOutput(XPOWERS_ALDO3);
- } else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) {
- // t-beam-s3-core GNSS power channel
- on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4);
- }
- } else if (model == XPOWERS_AXP192) {
- // t-beam v1.1 GNSS power channel
- on ? PMU->enablePowerOutput(XPOWERS_LDO3) : PMU->disablePowerOutput(XPOWERS_LDO3);
- }
- }
-#endif
-}
-
// Perform power on init that we do on each wake from deep sleep
void initDeepSleep()
{
@@ -182,30 +155,6 @@ static void waitEnterSleep()
notifySleep.notifyObservers(NULL);
}
-void doGPSpowersave(bool on)
-{
-#if defined(HAS_PMU) || defined(PIN_GPS_EN)
- if (on) {
- LOG_INFO("Turning GPS back on\n");
- gps->forceWake(1);
- setGPSPower(1);
- } else {
- LOG_INFO("Turning off GPS chip\n");
- notifyGPSSleep.notifyObservers(NULL);
- setGPSPower(0);
- }
-#endif
-#ifdef PIN_GPS_WAKE
- if (on) {
- LOG_INFO("Waking GPS");
- gps->forceWake(1);
- } else {
- LOG_INFO("GPS entering sleep");
- notifyGPSSleep.notifyObservers(NULL);
- }
-#endif
-}
-
void doDeepSleep(uint32_t msecToWake)
{
if (INCLUDE_vTaskSuspend && (msecToWake == portMAX_DELAY)) {
@@ -224,7 +173,7 @@ void doDeepSleep(uint32_t msecToWake)
nodeDB.saveToDisk();
// Kill GPS power completely (even if previously we just had it in sleep mode)
- setGPSPower(false);
+ gps->setGPSPower(false, false);
setLed(false);
diff --git a/src/sleep.h b/src/sleep.h
index 856d8d6b1..8ff81035c 100644
--- a/src/sleep.h
+++ b/src/sleep.h
@@ -18,8 +18,6 @@ extern esp_sleep_source_t wakeCause;
extern XPowersLibInterface *PMU;
#endif
-void setGPSPower(bool on);
-void doGPSpowersave(bool on);
// Perform power on init that we do on each wake from deep sleep
void initDeepSleep();
diff --git a/variants/Dongle_nRF52840-pca10059-v1/variant.h b/variants/Dongle_nRF52840-pca10059-v1/variant.h
index d165d6bdc..81e2ad995 100644
--- a/variants/Dongle_nRF52840-pca10059-v1/variant.h
+++ b/variants/Dongle_nRF52840-pca10059-v1/variant.h
@@ -47,12 +47,12 @@ extern "C" {
#define PIN_LED2 (0 + 6) // Built in Green P0.06
// Green Built in LED1
-//#define PIN_LED1 (0 + 6) // LED1 P1.15
+// #define PIN_LED1 (0 + 6) // LED1 P1.15
// RGB NeoPixel LED2
-//#define PIN_LED1 (0 + 8) Red
-//#define PIN_LED1 (32 + 9) Green
-//#define PIN_LED1 (0 + 12) Blue
+// #define PIN_LED1 (0 + 8) Red
+// #define PIN_LED1 (32 + 9) Green
+// #define PIN_LED1 (0 + 12) Blue
#define LED_BUILTIN PIN_LED1
#define LED_CONN PIN_LED2
@@ -113,7 +113,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
* eink display pins
*/
-//#define PIN_EINK_EN (-1)
+// #define PIN_EINK_EN (-1)
#define PIN_EINK_EN (0 + 6) // Turn on the Green built in LED
#define PIN_EINK_CS (32) // EPD_CS
#define PIN_EINK_BUSY (20) // EPD_BUSY
@@ -140,7 +140,8 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define SX126X_RESET (32 + 15) // LORA_RESET P1.15
#define SX126X_TXEN (32 + 13) // TXEN P1.13 NiceRF 868 dont use
#define SX126X_RXEN (32 + 10) // RXEN P1.10 NiceRF 868 dont use
-#define SX126X_E22
+
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define PIN_GPS_EN (-1)
#define PIN_GPS_PPS (-1) // Pulse per second input from the GPS
diff --git a/variants/MakePython_nRF52840_eink/variant.h b/variants/MakePython_nRF52840_eink/variant.h
index 8cf1f7dd9..2ff9c76fd 100644
--- a/variants/MakePython_nRF52840_eink/variant.h
+++ b/variants/MakePython_nRF52840_eink/variant.h
@@ -73,7 +73,7 @@ static const uint8_t AREF = PIN_AREF;
*/
#define SPI_INTERFACES_COUNT 2
// here
-//#define SPI_INTERFACES_COUNT 1
+// #define SPI_INTERFACES_COUNT 1
#define PIN_SPI_MISO (0 + 31) // MISO P0.31
#define PIN_SPI_MOSI (0 + 30) // MOSI P0.30
@@ -94,7 +94,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
* eink display pins
*/
-//#define PIN_EINK_EN (-1)
+// #define PIN_EINK_EN (-1)
#define PIN_EINK_CS (0 + 3) // EPD_CS
#define PIN_EINK_BUSY (32 + 11) // EPD_BUSY
#define PIN_EINK_DC (32 + 13) // EPD_D/C
@@ -118,8 +118,8 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define SX128X_CS (0 + 23)
#define SX128X_DIO1 (0 + 4)
#define SX128X_BUSY (0 + 7)
-//#define SX128X_TXEN (32 + 9)
-//#define SX128X_RXEN (0 + 12)
+// #define SX128X_TXEN (32 + 9)
+// #define SX128X_RXEN (0 + 12)
#define SX128X_RESET LORA_RESET
#define PIN_GPS_EN (-1)
diff --git a/variants/MakePython_nRF52840_oled/variant.h b/variants/MakePython_nRF52840_oled/variant.h
index 2f4323dd8..e7375a610 100644
--- a/variants/MakePython_nRF52840_oled/variant.h
+++ b/variants/MakePython_nRF52840_oled/variant.h
@@ -96,8 +96,8 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define SX128X_CS (0 + 23)
#define SX128X_DIO1 (0 + 4)
#define SX128X_BUSY (0 + 7)
-//#define SX128X_TXEN (32 + 9)
-//#define SX128X_RXEN (0 + 12)
+// #define SX128X_TXEN (32 + 9)
+// #define SX128X_RXEN (0 + 12)
#define SX128X_RESET LORA_RESET
#define PIN_GPS_EN (-1)
diff --git a/variants/ai-c3/variant.h b/variants/ai-c3/variant.h
index 352d189f1..254f5fd36 100644
--- a/variants/ai-c3/variant.h
+++ b/variants/ai-c3/variant.h
@@ -23,7 +23,9 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY 10
#define SX126X_RESET LORA_RESET
-#define SX126X_E22 // use DIO2 as RF switch
+
+#define SX126X_DIO2_AS_RF_SWITCH // use DIO2 as RF switch
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define HAS_GPS 0
#undef GPS_RX_PIN
diff --git a/variants/bpi_picow_esp32_s3/pins_arduino.h b/variants/bpi_picow_esp32_s3/pins_arduino.h
index ee0e34ebf..af03bf28a 100644
--- a/variants/bpi_picow_esp32_s3/pins_arduino.h
+++ b/variants/bpi_picow_esp32_s3/pins_arduino.h
@@ -27,11 +27,11 @@ static const uint8_t SCK = 21;
static const uint8_t MOSI = 38;
static const uint8_t SS = 17;
-//#define SPI_MOSI (11)
-//#define SPI_SCK (14)
-//#define SPI_MISO (2)
-//#define SPI_CS (13)
+// #define SPI_MOSI (11)
+// #define SPI_SCK (14)
+// #define SPI_MISO (2)
+// #define SPI_CS (13)
-//#define SDCARD_CS SPI_CS
+// #define SDCARD_CS SPI_CS
-#endif /* Pins_Arduino_h */
\ No newline at end of file
+#endif /* Pins_Arduino_h */
diff --git a/variants/bpi_picow_esp32_s3/variant.h b/variants/bpi_picow_esp32_s3/variant.h
index 78eae1dad..8114b9ea3 100644
--- a/variants/bpi_picow_esp32_s3/variant.h
+++ b/variants/bpi_picow_esp32_s3/variant.h
@@ -2,10 +2,10 @@
#undef GPS_RX_PIN
#undef GPS_TX_PIN
-//#define HAS_SCREEN 0
+// #define HAS_SCREEN 0
-//#define HAS_SDCARD
-//#define SDCARD_USE_SPI1
+// #define HAS_SDCARD
+// #define SDCARD_USE_SPI1
#define USE_SSD1306
#define I2C_SDA 12
@@ -14,13 +14,13 @@
#define LED_PIN 46
#define LED_STATE_ON 0 // State when LED is litted
-//#define BUTTON_PIN 15 // Pico OLED 1.3 User key 0 - removed User key 1 (17)
+// #define BUTTON_PIN 15 // Pico OLED 1.3 User key 0 - removed User key 1 (17)
#define BUTTON_PIN 40
-//#define BUTTON_PIN 0 // This is the BOOT button pad at the moment
-//#define BUTTON_NEED_PULLUP
+// #define BUTTON_PIN 0 // This is the BOOT button pad at the moment
+// #define BUTTON_NEED_PULLUP
-//#define USE_RF95 // RFM95/SX127x
+// #define USE_RF95 // RFM95/SX127x
#undef RF95_SCK
#undef RF95_MISO
@@ -43,10 +43,11 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_BUSY
#define SX126X_RESET LORA_RESET
-#define SX126X_E22
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif
-//#define USE_SX1280
+// #define USE_SX1280
#ifdef USE_SX1280
#define RF95_MISO 1
#define RF95_SCK 3
@@ -61,13 +62,13 @@
#define SX128X_RESET LORA_RESET
#endif
-//#define USE_EINK
+// #define USE_EINK
/*
* eink display pins
*/
-//#define PIN_EINK_CS
-//#define PIN_EINK_BUSY
-//#define PIN_EINK_DC
-//#define PIN_EINK_RES (-1)
-//#define PIN_EINK_SCLK 3
-//#define PIN_EINK_MOSI 4
\ No newline at end of file
+// #define PIN_EINK_CS
+// #define PIN_EINK_BUSY
+// #define PIN_EINK_DC
+// #define PIN_EINK_RES (-1)
+// #define PIN_EINK_SCLK 3
+// #define PIN_EINK_MOSI 4
diff --git a/variants/diy/dr-dev/variant.h b/variants/diy/dr-dev/variant.h
index b9c82a9c8..08d57eec9 100644
--- a/variants/diy/dr-dev/variant.h
+++ b/variants/diy/dr-dev/variant.h
@@ -31,10 +31,13 @@
// PINS FOR THE 900M22S
-#define LORA_DIO1 26 // IRQ for SX1262/SX1268
-#define LORA_DIO2 22 // BUSY for SX1262/SX1268
-#define LORA_TXEN NOT_A_PIN // Input - RF switch TX control, connecting external MCU IO or DIO2, valid in high level
-#define LORA_RXEN 17 // Input - RF switch RX control, connecting external MCU IO, valid in high level
+#define LORA_DIO1 26 // IRQ for SX1262/SX1268
+#define LORA_DIO2 22 // BUSY for SX1262/SX1268
+// NOT_A_PIN is treated as RADIOLIB_NC due to how they are defined, best to use RADIOLIB_NC directly
+#define LORA_TXEN RADIOLIB_NC // Input - RF switch TX control, connecting external MCU IO or DIO2, valid in high level
+// E22_TXEN_CONNECTED_TO_DIO2 wasn't defined, so RXEN wasn't controlled. Commented it out to maintain behavior, but shouldn't be.
+// Need to comment out defining SX126X_RXEN as LORA_RXEN too
+// #define LORA_RXEN 17 // Input - RF switch RX control, connecting external MCU IO, valid in high level
#undef RF95_NSS
#define RF95_NSS 16
#define SX126X_BUSY 22
@@ -53,7 +56,7 @@
*/
// RX/TX for RFM95/SX127x
-#define RF95_RXEN LORA_RXEN
+// #define RF95_RXEN LORA_RXEN
#define RF95_TXEN LORA_TXEN
// #define RF95_TCXO
@@ -61,12 +64,13 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_RESET LORA_RESET
-#define SX126X_RXEN LORA_RXEN
+// #define SX126X_RXEN LORA_RXEN
#define SX126X_TXEN LORA_TXEN
// supported modules list
-//#define USE_RF95 // RFM95/SX127x
+// #define USE_RF95 // RFM95/SX127x
#define USE_SX1262
-//#define USE_SX1268
-//#define USE_LLCC68
-#define SX126X_E22
\ No newline at end of file
+// #define USE_SX1268
+// #define USE_LLCC68
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
diff --git a/variants/diy/hydra/variant.h b/variants/diy/hydra/variant.h
index 65bf839fd..98c1c2ae1 100644
--- a/variants/diy/hydra/variant.h
+++ b/variants/diy/hydra/variant.h
@@ -37,8 +37,9 @@
#define SX126X_RESET LORA_RESET
#define SX126X_RXEN 14
#define SX126X_TXEN RADIOLIB_NC
-#define E22_TXEN_CONNECTED_TO_DIO2 1
+#define SX126X_DIO2_AS_RF_SWITCH
// Set lora.tx_power to 13 for Hydra or other E22 900M30S target due to PA
#define SX126X_MAX_POWER 13
-#define SX126X_E22
+
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
diff --git a/variants/diy/v1/variant.h b/variants/diy/v1/variant.h
index a1083f9bb..48906515b 100644
--- a/variants/diy/v1/variant.h
+++ b/variants/diy/v1/variant.h
@@ -52,5 +52,5 @@
#ifdef EBYTE_E22
// Internally the TTGO module hooks the SX126x-DIO2 in to control the TX/RX switch
// (which is the default for the sx1262interface code)
-#define SX126X_E22
-#endif
\ No newline at end of file
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+#endif
diff --git a/variants/diy/v1_1/variant.h b/variants/diy/v1_1/variant.h
index a550b9d1c..fd5276ced 100644
--- a/variants/diy/v1_1/variant.h
+++ b/variants/diy/v1_1/variant.h
@@ -53,5 +53,5 @@
#ifdef EBYTE_E22
// Internally the TTGO module hooks the SX126x-DIO2 in to control the TX/RX switch
// (which is the default for the sx1262interface code)
-#define SX126X_E22
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif
diff --git a/variants/feather_diy/variant.h b/variants/feather_diy/variant.h
index 85614d9b5..5e889b04e 100644
--- a/variants/feather_diy/variant.h
+++ b/variants/feather_diy/variant.h
@@ -105,7 +105,7 @@ extern "C" {
#ifdef EBYTE_E22
// Internally the TTGO module hooks the SX126x-DIO2 in to control the TX/RX switch
// (which is the default for the sx1262interface code)
-#define SX126X_E22
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif
#ifdef __cplusplus
diff --git a/variants/heltec_esp32c3/pins_arduino.h b/variants/heltec_esp32c3/pins_arduino.h
new file mode 100644
index 000000000..db30a2f30
--- /dev/null
+++ b/variants/heltec_esp32c3/pins_arduino.h
@@ -0,0 +1,32 @@
+#ifndef Pins_Arduino_h
+#define Pins_Arduino_h
+
+#include
+
+#define EXTERNAL_NUM_INTERRUPTS 22
+#define NUM_DIGITAL_PINS 22
+#define NUM_ANALOG_INPUTS 6
+
+#define analogInputToDigitalPin(p) (((p) < NUM_ANALOG_INPUTS) ? (esp32_adc2gpio[(p)]) : -1)
+#define digitalPinToInterrupt(p) (((p) < NUM_DIGITAL_PINS) ? (p) : -1)
+#define digitalPinHasPWM(p) (p < EXTERNAL_NUM_INTERRUPTS)
+
+static const uint8_t TX = 21;
+static const uint8_t RX = 20;
+
+static const uint8_t SDA = 1;
+static const uint8_t SCL = 0;
+
+static const uint8_t SS = 8;
+static const uint8_t MOSI = 7;
+static const uint8_t MISO = 6;
+static const uint8_t SCK = 10;
+
+static const uint8_t A0 = 0;
+static const uint8_t A1 = 1;
+static const uint8_t A2 = 2;
+static const uint8_t A3 = 3;
+static const uint8_t A4 = 4;
+static const uint8_t A5 = 5;
+
+#endif /* Pins_Arduino_h */
\ No newline at end of file
diff --git a/variants/heltec_esp32c3/platformio.ini b/variants/heltec_esp32c3/platformio.ini
new file mode 100644
index 000000000..a9843ef85
--- /dev/null
+++ b/variants/heltec_esp32c3/platformio.ini
@@ -0,0 +1,12 @@
+[env:heltec-ht62-esp32c3-sx1262]
+extends = esp32c3_base
+board = esp32-c3-devkitm-1
+board_level = extra
+build_flags =
+ ${esp32_base.build_flags}
+ -D HELTEC_HT62
+ -I variants/heltec_esp32c3
+monitor_speed = 115200
+upload_protocol = esptool
+upload_port = /dev/ttyUSB0
+upload_speed = 921600
diff --git a/variants/heltec_esp32c3/variant.h b/variants/heltec_esp32c3/variant.h
new file mode 100644
index 000000000..7d113720d
--- /dev/null
+++ b/variants/heltec_esp32c3/variant.h
@@ -0,0 +1,37 @@
+#define I2C_SDA 1
+#define I2C_SCL 0
+
+#define BUTTON_PIN 9
+#define BUTTON_NEED_PULLUP
+
+// LED flashes brighter
+// https://resource.heltec.cn/download/HT-CT62/HT-CT62_Reference_Design.pdf
+#define LED_PIN 18 // LED
+#define LED_INVERTED 1
+
+#define HAS_SCREEN 0
+#define HAS_GPS 0
+#undef GPS_RX_PIN
+#undef GPS_TX_PIN
+
+#undef RF95_SCK
+#undef RF95_MISO
+#undef RF95_MOSI
+#undef RF95_NSS
+
+#define USE_SX1262
+#define RF95_SCK 10
+#define RF95_MISO 6
+#define RF95_MOSI 7
+#define RF95_NSS 8
+#define LORA_DIO0 RADIOLIB_NC
+#define LORA_RESET 5
+#define LORA_DIO1 3
+#define LORA_DIO2 RADIOLIB_NC
+#define LORA_BUSY 4
+#define SX126X_CS RF95_NSS
+#define SX126X_DIO1 LORA_DIO1
+#define SX126X_BUSY LORA_BUSY
+#define SX126X_RESET LORA_RESET
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
diff --git a/variants/heltec_v3/variant.h b/variants/heltec_v3/variant.h
index cb26ba0a3..4ce47996b 100644
--- a/variants/heltec_v3/variant.h
+++ b/variants/heltec_v3/variant.h
@@ -11,8 +11,6 @@
#define VEXT_ENABLE Vext // active low, powers the oled display and the lora antenna boost
#define BUTTON_PIN 0
-#define PIN_GPS_EN 46 // GPS power enable pin
-
#define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider
@@ -35,4 +33,6 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22
+
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
diff --git a/variants/heltec_wireless_paper/variant.h b/variants/heltec_wireless_paper/variant.h
index eb67ea816..b73596f50 100644
--- a/variants/heltec_wireless_paper/variant.h
+++ b/variants/heltec_wireless_paper/variant.h
@@ -40,4 +40,6 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22
\ No newline at end of file
+
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
diff --git a/variants/heltec_wireless_tracker/platformio.ini b/variants/heltec_wireless_tracker/platformio.ini
index 45be3bf98..fa79eeb6a 100644
--- a/variants/heltec_wireless_tracker/platformio.ini
+++ b/variants/heltec_wireless_tracker/platformio.ini
@@ -5,6 +5,7 @@ upload_protocol = esp-builtin
build_flags =
${esp32s3_base.build_flags} -I variants/heltec_wireless_tracker
+ -DGPS_POWER_TOGGLE
lib_deps =
${esp32s3_base.lib_deps}
diff --git a/variants/heltec_wireless_tracker/variant.h b/variants/heltec_wireless_tracker/variant.h
index 83f41ecdf..cfb752d03 100644
--- a/variants/heltec_wireless_tracker/variant.h
+++ b/variants/heltec_wireless_tracker/variant.h
@@ -38,6 +38,8 @@
#define PIN_GPS_RESET 35
#define PIN_GPS_PPS 36
#define VGNSS_CTRL 37 // Heltec Tracker needs this pulled low for GPS
+#define PIN_GPS_EN VGNSS_CTRL
+#define GPS_EN_ACTIVE LOW
#define GPS_RESET_MODE LOW
#define GPS_UC6580
@@ -57,4 +59,6 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22
\ No newline at end of file
+
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
diff --git a/variants/heltec_wsl_v3/variant.h b/variants/heltec_wsl_v3/variant.h
index 40671e4fb..240a482f7 100644
--- a/variants/heltec_wsl_v3/variant.h
+++ b/variants/heltec_wsl_v3/variant.h
@@ -32,4 +32,6 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22
+
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
diff --git a/variants/lora_isp4520/variant.h b/variants/lora_isp4520/variant.h
index ce0c48297..30b8fc169 100644
--- a/variants/lora_isp4520/variant.h
+++ b/variants/lora_isp4520/variant.h
@@ -28,7 +28,7 @@
#define USE_LFXO
-//#define USE_SEGGER
+// #define USE_SEGGER
// Number of pins defined in PinDescription array
#define PINS_COUNT (16)
@@ -88,6 +88,7 @@
#define BATTERY_PIN 3
#define ADC_MULTIPLIER 1.436
-#define SX126X_E22 // Not really an E22 but this board clones using DIO3 for tcxo control
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8 // Not really an E22 but this board clones using DIO3 for tcxo control
#endif
diff --git a/variants/lora_relay_v1/variant.h b/variants/lora_relay_v1/variant.h
index 3ee0406ae..b310223d7 100644
--- a/variants/lora_relay_v1/variant.h
+++ b/variants/lora_relay_v1/variant.h
@@ -134,7 +134,8 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define SX126X_TXEN (31)
#define SX126X_POWER_EN \
(15) // FIXME, see warning hre https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay/blob/master/LORA_RELAY_NRF52840.ino
-#define SX126X_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that
+// Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define ST7735_RESET (11) // Output
#define ST7735_CS (12)
diff --git a/variants/lora_relay_v2/variant.h b/variants/lora_relay_v2/variant.h
index bc1f0714a..172da17f7 100644
--- a/variants/lora_relay_v2/variant.h
+++ b/variants/lora_relay_v2/variant.h
@@ -154,7 +154,8 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define SX126X_TXEN (31)
#define SX126X_POWER_EN \
(15) // FIXME, see warning hre https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay/blob/master/LORA_RELAY_NRF52840.ino
-#define SX126X_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that
+// Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// ST7565 SPI
#define ST7735_RESET (11) // Output
@@ -164,8 +165,8 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define ST7735_SDA (39) // actually spi MOSI
#define ST7735_SCK (37) // actually spi clk
-#define PIN_GPS_WAKE 36 // Just kill GPS power when we want it to sleep? FIXME
-#define GPS_WAKE_ACTIVE 0 // GPS Power output is active low
+#define PIN_GPS_EN 36 // Just kill GPS power when we want it to sleep? FIXME
+#define GPS_EN_ACTIVE 0 // GPS Power output is active low
// #define LORA_DISABLE_SENDING // The board can brownout during lora TX if you don't have a battery connected. Disable sending
// to allow USB power only based debugging
diff --git a/variants/m5stack-stamp-c3/variant.h b/variants/m5stack-stamp-c3/variant.h
index b1a192af5..87adbc226 100644
--- a/variants/m5stack-stamp-c3/variant.h
+++ b/variants/m5stack-stamp-c3/variant.h
@@ -4,7 +4,7 @@
#define BUTTON_PIN 3 // M5Stack STAMP C3 built in button
#define BUTTON_NEED_PULLUP
-//#define HAS_SCREEN 0
+// #define HAS_SCREEN 0
#define HAS_GPS 0
#undef GPS_RX_PIN
#undef GPS_TX_PIN
@@ -28,45 +28,46 @@
// WaveShare Core1262-868M OK
// https://www.waveshare.com/wiki/Core1262-868M
-//#define USE_SX1262
-//#define RF95_SCK 4
-//#define RF95_MISO 5
-//#define RF95_MOSI 6
-//#define RF95_NSS 7
-//#define LORA_DIO0 RADIOLIB_NC
-//#define LORA_RESET 8
-//#define LORA_DIO1 10
-//#define LORA_DIO2 RADIOLIB_NC
-//#define LORA_BUSY 18
-//#define SX126X_CS RF95_NSS
-//#define SX126X_DIO1 LORA_DIO1
-//#define SX126X_BUSY LORA_BUSY
-//#define SX126X_RESET LORA_RESET
-//#define SX126X_E22
+// #define USE_SX1262
+// #define RF95_SCK 4
+// #define RF95_MISO 5
+// #define RF95_MOSI 6
+// #define RF95_NSS 7
+// #define LORA_DIO0 RADIOLIB_NC
+// #define LORA_RESET 8
+// #define LORA_DIO1 10
+// #define LORA_DIO2 RADIOLIB_NC
+// #define LORA_BUSY 18
+// #define SX126X_CS RF95_NSS
+// #define SX126X_DIO1 LORA_DIO1
+// #define SX126X_BUSY LORA_BUSY
+// #define SX126X_RESET LORA_RESET
+// #define SX126X_DIO2_AS_RF_SWITCH
+// #define SX126X_DIO3_TCXO_VOLTAGE 1.8
// SX128X 2.4 Ghz LoRa module Not OK - RadioLib issue ? still to confirm
-//#define USE_SX1280
-//#define RF95_SCK 4
-//#define RF95_MISO 5
-//#define RF95_MOSI 6
-//#define RF95_NSS 7
-//#define LORA_DIO0 -1
-//#define LORA_DIO1 10
-//#define LORA_DIO2 21
-//#define LORA_RESET 8
-//#define LORA_BUSY 1
-//#define SX128X_CS RF95_NSS
-//#define SX128X_DIO1 LORA_DIO1
-//#define SX128X_BUSY LORA_BUSY
-//#define SX128X_RESET LORA_RESET
-//#define SX128X_MAX_POWER 10
+// #define USE_SX1280
+// #define RF95_SCK 4
+// #define RF95_MISO 5
+// #define RF95_MOSI 6
+// #define RF95_NSS 7
+// #define LORA_DIO0 -1
+// #define LORA_DIO1 10
+// #define LORA_DIO2 21
+// #define LORA_RESET 8
+// #define LORA_BUSY 1
+// #define SX128X_CS RF95_NSS
+// #define SX128X_DIO1 LORA_DIO1
+// #define SX128X_BUSY LORA_BUSY
+// #define SX128X_RESET LORA_RESET
+// #define SX128X_MAX_POWER 10
// Not yet tested
-//#define USE_EINK
-//#define PIN_EINK_EN -1 // N/C
-//#define PIN_EINK_CS 9 // EPD_CS
-//#define PIN_EINK_BUSY 18 // EPD_BUSY
-//#define PIN_EINK_DC 19 // EPD_D/C
-//#define PIN_EINK_RES -1 // Connected but not needed
-//#define PIN_EINK_SCLK 4 // EPD_SCLK
-//#define PIN_EINK_MOSI 6 // EPD_MOSI
\ No newline at end of file
+// #define USE_EINK
+// #define PIN_EINK_EN -1 // N/C
+// #define PIN_EINK_CS 9 // EPD_CS
+// #define PIN_EINK_BUSY 18 // EPD_BUSY
+// #define PIN_EINK_DC 19 // EPD_D/C
+// #define PIN_EINK_RES -1 // Connected but not needed
+// #define PIN_EINK_SCLK 4 // EPD_SCLK
+// #define PIN_EINK_MOSI 6 // EPD_MOSI
diff --git a/variants/m5stack_core/variant.h b/variants/m5stack_core/variant.h
index 22fa323d7..c671d77fa 100644
--- a/variants/m5stack_core/variant.h
+++ b/variants/m5stack_core/variant.h
@@ -4,7 +4,7 @@
#define I2C_SCL 22
// #define BUTTON_PIN 39 // 38, 37
-//#define BUTTON_PIN 0
+// #define BUTTON_PIN 0
#define BUTTON_NEED_PULLUP
// #define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Plugin.
diff --git a/variants/m5stack_coreink/variant.h b/variants/m5stack_coreink/variant.h
index 5ec1a81a3..90ce41334 100644
--- a/variants/m5stack_coreink/variant.h
+++ b/variants/m5stack_coreink/variant.h
@@ -3,8 +3,8 @@
#define I2C_SCL 22
// 7-07-2023 Or enable Secondary I2C Bus
-//#define I2C_SDA1 32
-//#define I2C_SCL1 33
+// #define I2C_SDA1 32
+// #define I2C_SCL1 33
#define HAS_GPS 1
#undef GPS_RX_PIN
@@ -39,7 +39,7 @@
#undef RF95_MOSI
#undef RF95_NSS
#define USE_RF95
-//#define USE_SX1280
+// #define USE_SX1280
#ifdef USE_RF95
#define RF95_SCK 18
diff --git a/variants/monteops_hw1/variant.h b/variants/monteops_hw1/variant.h
index 866ddf471..f7df0688b 100644
--- a/variants/monteops_hw1/variant.h
+++ b/variants/monteops_hw1/variant.h
@@ -64,7 +64,7 @@ extern "C" {
* Buttons
*/
-//#define PIN_BUTTON1 9 // Pin for button on E-ink button module or IO expansion
+// #define PIN_BUTTON1 9 // Pin for button on E-ink button module or IO expansion
#define BUTTON_NEED_PULLUP
#define PIN_BUTTON2 12
#define PIN_BUTTON3 24
@@ -191,7 +191,9 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
// #define SX126X_TXEN (39)
// #define SX126X_RXEN (37)
#define SX126X_POWER_EN (37)
-#define SX126X_E22 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+
+#define SX126X_DIO2_AS_RF_SWITCH // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#define PIN_GPS_RESET (34) // Must be P1.02
// #define PIN_GPS_EN
@@ -220,7 +222,7 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
#define ADC_MULTIPLIER VBAT_DIVIDER_COMP // REAL_VBAT_MV_PER_LSB
#define VBAT_RAW_TO_SCALED(x) (REAL_VBAT_MV_PER_LSB * x)
-//#define HAS_RTC 1
+// #define HAS_RTC 1
#define HAS_ETHERNET 1
@@ -237,4 +239,4 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/my_esp32s3_diy_eink/pins_arduino.h b/variants/my_esp32s3_diy_eink/pins_arduino.h
index 1f276899c..39e316624 100644
--- a/variants/my_esp32s3_diy_eink/pins_arduino.h
+++ b/variants/my_esp32s3_diy_eink/pins_arduino.h
@@ -24,11 +24,11 @@ static const uint8_t SCK = 5;
static const uint8_t MOSI = 6;
static const uint8_t SS = 7;
-//#define SPI_MOSI (11)
-//#define SPI_SCK (14)
-//#define SPI_MISO (2)
-//#define SPI_CS (13)
+// #define SPI_MOSI (11)
+// #define SPI_SCK (14)
+// #define SPI_MISO (2)
+// #define SPI_CS (13)
-//#define SDCARD_CS SPI_CS
+// #define SDCARD_CS SPI_CS
#endif /* Pins_Arduino_h */
diff --git a/variants/my_esp32s3_diy_eink/variant.h b/variants/my_esp32s3_diy_eink/variant.h
index 288daf9ff..7e4fe2756 100644
--- a/variants/my_esp32s3_diy_eink/variant.h
+++ b/variants/my_esp32s3_diy_eink/variant.h
@@ -2,22 +2,22 @@
#undef GPS_RX_PIN
#undef GPS_TX_PIN
-//#define HAS_SCREEN 0
-//#define HAS_SDCARD
-//#define SDCARD_USE_SPI1
+// #define HAS_SCREEN 0
+// #define HAS_SDCARD
+// #define SDCARD_USE_SPI1
-//#define USE_SSD1306
+// #define USE_SSD1306
#define I2C_SDA 18 // 1 // I2C pins for this board
#define I2C_SCL 17 // 2
-//#define LED_PIN 38 // This is a RGB LED not a standard LED
+// #define LED_PIN 38 // This is a RGB LED not a standard LED
#define BUTTON_PIN 0 // This is the BOOT button
#define BUTTON_NEED_PULLUP
-//#define USE_RF95 // RFM95/SX127x
-//#define USE_SX1262
+// #define USE_RF95 // RFM95/SX127x
+// #define USE_SX1262
#define USE_SX1280
#define RF95_MISO 3
diff --git a/variants/my_esp32s3_diy_oled/pins_arduino.h b/variants/my_esp32s3_diy_oled/pins_arduino.h
index 1f276899c..39e316624 100644
--- a/variants/my_esp32s3_diy_oled/pins_arduino.h
+++ b/variants/my_esp32s3_diy_oled/pins_arduino.h
@@ -24,11 +24,11 @@ static const uint8_t SCK = 5;
static const uint8_t MOSI = 6;
static const uint8_t SS = 7;
-//#define SPI_MOSI (11)
-//#define SPI_SCK (14)
-//#define SPI_MISO (2)
-//#define SPI_CS (13)
+// #define SPI_MOSI (11)
+// #define SPI_SCK (14)
+// #define SPI_MISO (2)
+// #define SPI_CS (13)
-//#define SDCARD_CS SPI_CS
+// #define SDCARD_CS SPI_CS
#endif /* Pins_Arduino_h */
diff --git a/variants/my_esp32s3_diy_oled/variant.h b/variants/my_esp32s3_diy_oled/variant.h
index 9eda00791..bb9657c5b 100644
--- a/variants/my_esp32s3_diy_oled/variant.h
+++ b/variants/my_esp32s3_diy_oled/variant.h
@@ -2,22 +2,22 @@
#undef GPS_RX_PIN
#undef GPS_TX_PIN
-//#define HAS_SCREEN 0
-//#define HAS_SDCARD
-//#define SDCARD_USE_SPI1
+// #define HAS_SCREEN 0
+// #define HAS_SDCARD
+// #define SDCARD_USE_SPI1
#define USE_SSD1306
#define I2C_SDA 18 // 1 // I2C pins for this board
#define I2C_SCL 17 // 2
-//#define LED_PIN 38 // This is a RGB LED not a standard LED
+// #define LED_PIN 38 // This is a RGB LED not a standard LED
#define BUTTON_PIN 0 // This is the BOOT button
#define BUTTON_NEED_PULLUP
-//#define USE_RF95 // RFM95/SX127x
-//#define USE_SX1262
+// #define USE_RF95 // RFM95/SX127x
+// #define USE_SX1262
#define USE_SX1280
#define RF95_MISO 3
@@ -44,13 +44,13 @@
#define SX128X_RESET LORA_RESET
#endif
-//#define USE_EINK
+// #define USE_EINK
/*
* eink display pins
*/
-//#define PIN_EINK_CS 13
-//#define PIN_EINK_BUSY 2
-//#define PIN_EINK_DC 1
-//#define PIN_EINK_RES (-1)
-//#define PIN_EINK_SCLK 5
-//#define PIN_EINK_MOSI 6
+// #define PIN_EINK_CS 13
+// #define PIN_EINK_BUSY 2
+// #define PIN_EINK_DC 1
+// #define PIN_EINK_RES (-1)
+// #define PIN_EINK_SCLK 5
+// #define PIN_EINK_MOSI 6
diff --git a/variants/nano-g1-explorer/variant.h b/variants/nano-g1-explorer/variant.h
index fb3fc72d1..71dd49f05 100644
--- a/variants/nano-g1-explorer/variant.h
+++ b/variants/nano-g1-explorer/variant.h
@@ -4,8 +4,8 @@
#define I2C_SCL 22
#define BUTTON_PIN 36 // The user button (information button) GPIO on the Nano G1 explorer
-//#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented
-// anywhere.
+// #define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented
+// anywhere.
#define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.
// common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if
@@ -13,6 +13,9 @@
#define USE_RF95
#define USE_SX1262
+#define GPS_RX_PIN 34
+#define GPS_TX_PIN 12
+
#define LORA_DIO0 26 // a No connect on the SX1262 module
#define LORA_RESET 23
#define LORA_DIO1 33 // SX1262 IRQ
@@ -24,7 +27,9 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22 // Not really an E22
+// Not really an E22
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Internally the module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
// code)
#endif
@@ -34,4 +39,4 @@
#define BATTERY_SENSE_SAMPLES 15 // Set the number of samples, It has an effect of increasing sensitivity.
#define ADC_MULTIPLIER 2
-#define USE_SH1107_128_64
\ No newline at end of file
+#define USE_SH1107_128_64
diff --git a/variants/nano-g1/variant.h b/variants/nano-g1/variant.h
index eec6e8a07..5ceb96f0c 100644
--- a/variants/nano-g1/variant.h
+++ b/variants/nano-g1/variant.h
@@ -4,8 +4,8 @@
#define I2C_SCL 22
#define BUTTON_PIN 36 // The middle button GPIO on the Nano G1
-//#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented
-// anywhere.
+// #define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented
+// anywhere.
#define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.
// common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if
@@ -13,6 +13,9 @@
#define USE_RF95
#define USE_SX1262
+#define GPS_RX_PIN 34
+#define GPS_TX_PIN 12
+
#define LORA_DIO0 26 // a No connect on the SX1262 module
#define LORA_RESET 23
#define LORA_DIO1 33 // SX1262 IRQ
@@ -24,10 +27,12 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22 // Not really an E22
+// Not really an E22
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Internally the module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
// code)
#endif
// different screen
-#define USE_SH1106
+#define USE_SH1106
\ No newline at end of file
diff --git a/variants/nano-g2-ultra/variant.h b/variants/nano-g2-ultra/variant.h
index 4c1720af8..d69235fd4 100644
--- a/variants/nano-g2-ultra/variant.h
+++ b/variants/nano-g2-ultra/variant.h
@@ -23,7 +23,7 @@
#define VARIANT_MCK (64000000ul)
#define USE_LFXO // Board uses 32khz crystal for LF
-//#define USE_LFRC // Board uses 32khz RC for LF
+// #define USE_LFRC // Board uses 32khz RC for LF
/*----------------------------------------------------------------------------
* Headers
@@ -54,7 +54,7 @@ extern "C" {
#define LED_CONN PIN_GREEN
#define LED_STATE_ON 0 // State when LED is lit
-//#define LED_INVERTED 1
+// #define LED_INVERTED 1
/*
* Buttons
@@ -114,11 +114,13 @@ External serial flash W25Q16JV_IQ
#define SX126X_CS (32 + 13) // FIXME - we really should define LORA_CS instead
#define SX126X_DIO1 (32 + 10)
// Note DIO2 is attached internally to the module to an analog switch for TX/RX switching
-//#define SX1262_DIO3 (0 + 21)
+// #define SX1262_DIO3 (0 + 21)
// This is used as an *output* from the sx1262 and connected internally to power the tcxo, do not drive from the main CPU?
#define SX126X_BUSY (32 + 11)
#define SX126X_RESET (32 + 15)
-#define SX126X_E22 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// #define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...)
@@ -130,11 +132,11 @@ External serial flash W25Q16JV_IQ
#define GPS_L76K
-#define PIN_GPS_WAKE (0 + 13) // An output to wake GPS, low means allow sleep, high means force wake
-#define PIN_GPS_TX (0 + 9) // This is for bits going TOWARDS the CPU
-#define PIN_GPS_RX (0 + 10) // This is for bits going TOWARDS the GPS
+#define PIN_GPS_STANDBY (0 + 13) // An output to wake GPS, low means allow sleep, high means force wake STANDBY
+#define PIN_GPS_TX (0 + 9) // This is for bits going TOWARDS the CPU
+#define PIN_GPS_RX (0 + 10) // This is for bits going TOWARDS the GPS
-//#define GPS_THREAD_INTERVAL 50
+// #define GPS_THREAD_INTERVAL 50
#define PIN_SERIAL1_RX PIN_GPS_TX
#define PIN_SERIAL1_TX PIN_GPS_RX
@@ -152,7 +154,7 @@ External serial flash W25Q16JV_IQ
#define PIN_SPI_MOSI (0 + 11)
#define PIN_SPI_SCK (0 + 12)
-//#define PIN_PWR_EN (0 + 6)
+// #define PIN_PWR_EN (0 + 6)
// To debug via the segger JLINK console rather than the CDC-ACM serial device
// #define USE_SEGGER
@@ -193,4 +195,4 @@ External serial flash W25Q16JV_IQ
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/pca10056-rc-clock/variant.h b/variants/pca10056-rc-clock/variant.h
index 20aed8cb6..032e1de2b 100644
--- a/variants/pca10056-rc-clock/variant.h
+++ b/variants/pca10056-rc-clock/variant.h
@@ -146,6 +146,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define SX126X_BUSY (32 + 4) // P1.04
#define SX126X_RESET (0 + 3) // P0.03
#define SX126X_ANT_SW (32 + 10) // P1.10
+#define SX126X_DIO2_AS_RF_SWITCH
// To debug via the segger JLINK console rather than the CDC-ACM serial device
// #define USE_SEGGER
diff --git a/variants/picomputer-s3/platformio.ini b/variants/picomputer-s3/platformio.ini
index e7093697a..202cd05e7 100644
--- a/variants/picomputer-s3/platformio.ini
+++ b/variants/picomputer-s3/platformio.ini
@@ -1,7 +1,7 @@
[env:picomputer-s3]
extends = esp32s3_base
board = bpi_picow_esp32_s3
-board_level = extra
+
;OpenOCD flash method
;upload_protocol = esp-builtin
;Normal method
diff --git a/variants/picomputer-s3/variant.h b/variants/picomputer-s3/variant.h
index 716f2779d..732be4bcf 100644
--- a/variants/picomputer-s3/variant.h
+++ b/variants/picomputer-s3/variant.h
@@ -5,9 +5,14 @@
#define PIN_BUZZER 43
-#define HAS_GPS 0
#define HAS_WIRE 0
+#define BATTERY_PIN ADC1_CHANNEL_1_GPIO_NUM // 2
+// A battery voltage measurement pin, voltage divider connected here to measure battery voltage
+// ratio of voltage divider = 3.0 (R11=200k, R7=100k)
+#define ADC_MULTIPLIER 3.1 // 3.0 with correction of display undervoltage.
+#define ADC_CHANNEL ADC1_GPIO2_CHANNEL
+
#define USE_RF95 // RFM95/SX127x
#define RF95_SCK SCK // 21
diff --git a/variants/portduino/variant.h b/variants/portduino/variant.h
index 32f2bf1ef..33437de9e 100644
--- a/variants/portduino/variant.h
+++ b/variants/portduino/variant.h
@@ -53,6 +53,6 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#endif
+#define SX126X_DIO2_AS_RF_SWITCH
#endif
diff --git a/variants/ppr/variant.h b/variants/ppr/variant.h
index 4b37970a4..4c6cc015c 100644
--- a/variants/ppr/variant.h
+++ b/variants/ppr/variant.h
@@ -89,8 +89,8 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (0xff)
-//#define PIN_NFC1 (9)
-//#define PIN_NFC2 (10)
+// #define PIN_NFC1 (9)
+// #define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@@ -103,8 +103,8 @@ static const uint8_t AREF = PIN_AREF;
#define PIN_SERIAL1_TX (9)
// Connected to Jlink CDC
-//#define PIN_SERIAL2_RX (8)
-//#define PIN_SERIAL2_TX (6)
+// #define PIN_SERIAL2_RX (8)
+// #define PIN_SERIAL2_TX (6)
/*
* SPI Interfaces
@@ -138,7 +138,8 @@ static const uint8_t SCK = PIN_SPI_SCK;
// #define SX126X_ANT_SW (32 + 10)
#define SX126X_RXEN (22)
#define SX126X_TXEN (24)
-#define SX126X_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that
+// Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// ERC12864-10 LCD
#define ERC12864_CS (32 + 4)
diff --git a/variants/ppr1/variant.h b/variants/ppr1/variant.h
index 635614da0..ba3a25c2a 100644
--- a/variants/ppr1/variant.h
+++ b/variants/ppr1/variant.h
@@ -89,8 +89,8 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (0xff)
-//#define PIN_NFC1 (9)
-//#define PIN_NFC2 (10)
+// #define PIN_NFC1 (9)
+// #define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@@ -158,7 +158,8 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define SX126X_RESET (0 + 17)
#define SX126X_TXEN (0 + 24)
#define SX126X_RXEN (0 + 22)
-#define SX126X_E22 // Not really an E22 but this board clones using DIO3 for tcxo control
+// Not really an E22 but this board clones using DIO3 for tcxo control
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// FIXME, to prevent burning out parts I've set the power level super low, because I don't have
// an antenna wired up
diff --git a/variants/rak11200/variant.h b/variants/rak11200/variant.h
index 7f2c24082..b6d9c4229 100644
--- a/variants/rak11200/variant.h
+++ b/variants/rak11200/variant.h
@@ -81,4 +81,6 @@ static const uint8_t SCK = 33;
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
#define SX126X_POWER_EN WB_IO3
-#define SX126X_E22 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
diff --git a/variants/rak11310/variant.h b/variants/rak11310/variant.h
index 1d1577cfd..c2ab3628d 100644
--- a/variants/rak11310/variant.h
+++ b/variants/rak11310/variant.h
@@ -11,8 +11,6 @@
#undef ECB
#define ECB 0
-#undef GPS_SERIAL_NUM
-
#define LED_CONN PIN_LED2
#define LED_PIN LED_BUILTIN
@@ -50,5 +48,7 @@
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
#define SX126X_POWER_EN 25
-#define SX126X_E22 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
-#endif
\ No newline at end of file
+// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+#endif
diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini
index 34eed88dd..b9789166f 100644
--- a/variants/rak4631/platformio.ini
+++ b/variants/rak4631/platformio.ini
@@ -4,6 +4,7 @@ extends = nrf52840_base
board = wiscore_rak4631
build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631 -D RAK_4631
-L "${platformio.libdeps_dir}/${this.__env__}/BSEC2 Software Library/src/cortex-m4/fpv4-sp-d16-hard"
+ -DGPS_POWER_TOGGLE ; comment this line to disable triple press function on the user button to turn off gps entirely.
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/rak4631> + + +
lib_deps =
${nrf52840_base.lib_deps}
@@ -13,4 +14,4 @@ lib_deps =
rakwireless/RAKwireless NCP5623 RGB LED library@^1.0.2
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
-;upload_protocol = jlink
\ No newline at end of file
+;upload_protocol = jlink
diff --git a/variants/rak4631/variant.h b/variants/rak4631/variant.h
index 258e4eb3c..7b5f6b14d 100644
--- a/variants/rak4631/variant.h
+++ b/variants/rak4631/variant.h
@@ -209,7 +209,9 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
// #define SX126X_TXEN (39)
// #define SX126X_RXEN (37)
#define SX126X_POWER_EN (37)
-#define SX126X_E22 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// enables 3.3V periphery like GPS or IO Module
#define PIN_3V3_EN (34)
@@ -227,6 +229,8 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
#define GPS_RX_PIN PIN_SERIAL1_RX
#define GPS_TX_PIN PIN_SERIAL1_TX
+// Define pin to enable GPS toggle (set GPIO to LOW) via user button triple press
+
// RAK12002 RTC Module
#define RV3028_RTC (uint8_t)0b1010010
@@ -273,4 +277,4 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/rak4631_epaper/variant.h b/variants/rak4631_epaper/variant.h
index ad3e4b87f..0253ec14d 100644
--- a/variants/rak4631_epaper/variant.h
+++ b/variants/rak4631_epaper/variant.h
@@ -186,7 +186,9 @@ static const uint8_t SCK = PIN_SPI_SCK;
// #define SX126X_TXEN (39)
// #define SX126X_RXEN (37)
#define SX126X_POWER_EN (37)
-#define SX126X_E22 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// enables 3.3V periphery like GPS or IO Module
#define PIN_3V3_EN (34)
@@ -239,4 +241,4 @@ static const uint8_t SCK = PIN_SPI_SCK;
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/rak4631_epaper_onrxtx/variant.h b/variants/rak4631_epaper_onrxtx/variant.h
index e4d7c7d45..6fc6da373 100644
--- a/variants/rak4631_epaper_onrxtx/variant.h
+++ b/variants/rak4631_epaper_onrxtx/variant.h
@@ -43,7 +43,7 @@ extern "C" {
#define PIN_BUTTON1 9 // Pin for button on E-ink button module or IO expansion
#define BUTTON_NEED_PULLUP
-//#define PIN_BUTTON2 12
+// #define PIN_BUTTON2 12
/*
* Analog pins
@@ -69,8 +69,8 @@ static const uint8_t A7 = PIN_A7;
// Other pins
#define PIN_AREF (2)
-//#define PIN_NFC1 (9)
-//#define PIN_NFC2 (10)
+// #define PIN_NFC1 (9)
+// #define PIN_NFC2 (10)
static const uint8_t AREF = PIN_AREF;
@@ -111,7 +111,7 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define PIN_EINK_CS (0 + 16) // TX1
#define PIN_EINK_BUSY (0 + 15) // RX1
#define PIN_EINK_DC (0 + 17) // IO1
-//#define PIN_EINK_RES (-1) //first try without RESET then connect it to AIN (AIN0 5 )
+// #define PIN_EINK_RES (-1) //first try without RESET then connect it to AIN (AIN0 5 )
#define PIN_EINK_RES (0 + 5) // 2.13 BN Display needs RESET
#define PIN_EINK_SCLK (0 + 14) // SCL
#define PIN_EINK_MOSI (0 + 13) // SDA
@@ -155,7 +155,9 @@ static const uint8_t SCK = PIN_SPI_SCK;
// #define SX126X_TXEN (39)
// #define SX126X_RXEN (37)
#define SX126X_POWER_EN (37)
-#define SX126X_E22 // DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+// DIO2 controlls an antenna switch and the TCXO voltage is controlled by DIO3
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// enables 3.3V periphery like GPS or IO Module
#define PIN_3V3_EN (34)
@@ -171,36 +173,36 @@ static const uint8_t SCK = PIN_SPI_SCK;
// Therefore must be 1 to keep peripherals powered
// Power is on the controllable 3V3_S rail
// #define PIN_GPS_RESET (34)
-//#define PIN_GPS_EN PIN_3V3_EN
-//#define PIN_GPS_PPS (17) // Pulse per second input from the GPS
+// #define PIN_GPS_EN PIN_3V3_EN
+// #define PIN_GPS_PPS (17) // Pulse per second input from the GPS
-//#define GPS_RX_PIN PIN_SERIAL1_RX
-//#define GPS_TX_PIN PIN_SERIAL1_TX
+// #define GPS_RX_PIN PIN_SERIAL1_RX
+// #define GPS_TX_PIN PIN_SERIAL1_TX
// RAK12002 RTC Module
#define RV3028_RTC (uint8_t)0b1010010
// Battery
// The battery sense is hooked to pin A0 (5)
-//#define BATTERY_PIN PIN_A0
+// #define BATTERY_PIN PIN_A0
// and has 12 bit resolution
-//#define BATTERY_SENSE_RESOLUTION_BITS 12
-//#define BATTERY_SENSE_RESOLUTION 4096.0
+// #define BATTERY_SENSE_RESOLUTION_BITS 12
+// #define BATTERY_SENSE_RESOLUTION 4096.0
// Definition of milliVolt per LSB => 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096
-//#define VBAT_MV_PER_LSB (0.73242188F)
+// #define VBAT_MV_PER_LSB (0.73242188F)
// Voltage divider value => 1.5M + 1M voltage divider on VBAT = (1.5M / (1M + 1.5M))
-//#define VBAT_DIVIDER (0.4F)
+// #define VBAT_DIVIDER (0.4F)
// Compensation factor for the VBAT divider
-//#define VBAT_DIVIDER_COMP (1.73)
+// #define VBAT_DIVIDER_COMP (1.73)
// Fixed calculation of milliVolt from compensation value
-//#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)
-//#undef AREF_VOLTAGE
-//#define AREF_VOLTAGE 3.0
-//#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
-//#define ADC_MULTIPLIER VBAT_DIVIDER_COMP // REAL_VBAT_MV_PER_LSB
-//#define VBAT_RAW_TO_SCALED(x) (REAL_VBAT_MV_PER_LSB * x)
+// #define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)
+// #undef AREF_VOLTAGE
+// #define AREF_VOLTAGE 3.0
+// #define VBAT_AR_INTERNAL AR_INTERNAL_3_0
+// #define ADC_MULTIPLIER VBAT_DIVIDER_COMP // REAL_VBAT_MV_PER_LSB
+// #define VBAT_RAW_TO_SCALED(x) (REAL_VBAT_MV_PER_LSB * x)
-//#define HAS_RTC 1
+// #define HAS_RTC 1
#ifdef __cplusplus
}
@@ -210,4 +212,4 @@ static const uint8_t SCK = PIN_SPI_SCK;
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/rpipico/variant.h b/variants/rpipico/variant.h
index 71d1bd159..5c92dec59 100644
--- a/variants/rpipico/variant.h
+++ b/variants/rpipico/variant.h
@@ -11,14 +11,16 @@
#undef ECB
#define ECB 0
-#define NO_GPS 1
#define USE_SH1106 1
-#undef GPS_SERIAL_NUM
// default I2C pins:
// SDA = 4
// SCL = 5
+// Recommended pins for SerialModule:
+// txd = 8
+// rxd = 9
+
#define EXT_NOTIFY_OUT 22
#define BUTTON_PIN 17
@@ -51,5 +53,6 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22
-#endif
\ No newline at end of file
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+#endif
diff --git a/variants/rpipicow/variant.h b/variants/rpipicow/variant.h
index ac393d4d3..6de2f7bd4 100644
--- a/variants/rpipicow/variant.h
+++ b/variants/rpipicow/variant.h
@@ -11,14 +11,16 @@
#undef ECB
#define ECB 0
-#define NO_GPS 1
#define USE_SH1106 1
-#undef GPS_SERIAL_NUM
// default I2C pins:
// SDA = 4
// SCL = 5
+// Recommended pins for SerialModule:
+// txd = 8
+// rxd = 9
+
#define EXT_NOTIFY_OUT 22
#define BUTTON_PIN 17
@@ -49,5 +51,6 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22
-#endif
\ No newline at end of file
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+#endif
diff --git a/variants/station-g1/variant.h b/variants/station-g1/variant.h
index 3439cb125..79a49fc96 100644
--- a/variants/station-g1/variant.h
+++ b/variants/station-g1/variant.h
@@ -3,9 +3,12 @@
#define I2C_SDA 21
#define I2C_SCL 22
+#define I2C_SDA1 14 // Second i2c channel on external IO connector
+#define I2C_SCL1 15 // Second i2c channel on external IO connector
+
#define BUTTON_PIN 36 // The middle button GPIO on the Nano G1
-//#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented
-// anywhere.
+// #define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented
+// anywhere.
#define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.
// common pinout for their SX1262 vs RF95 modules - both can be enabled and we will probe at runtime for RF95 and if
@@ -24,9 +27,7 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-//#define SX126X_E22 // Not really an E22
-// Internally the module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
-// code)
+#define SX126X_DIO2_AS_RF_SWITCH // Internally the module hooks the SX1262-DIO2 in to control the TX/RX switch
#define SX126X_MAX_POWER \
16 // Ensure the PA does not exceed the saturation output power. More
// Info:https://uniteng.com/wiki/doku.php?id=meshtastic:station#rf_design_-_lora_station_edition_g1
@@ -42,4 +43,8 @@
#define BAT_NOBATVOLT 6690
// different screen
-#define USE_SH1106
\ No newline at end of file
+#define USE_SH1106
+
+// Station may not have GPS installed, but it has a labeled GPS pinout
+#define GPS_RX_PIN 34
+#define GPS_TX_PIN 12
diff --git a/variants/t-deck/platformio.ini b/variants/t-deck/platformio.ini
index c3c508a0f..38e334a30 100644
--- a/variants/t-deck/platformio.ini
+++ b/variants/t-deck/platformio.ini
@@ -8,7 +8,8 @@ debug_tool = esp-builtin
build_flags = ${esp32_base.build_flags}
-DT_DECK
-DBOARD_HAS_PSRAM
+ -DGPS_POWER_TOGGLE
-Ivariants/t-deck
lib_deps = ${esp32s3_base.lib_deps}
- lovyan03/LovyanGFX@^1.1.8
\ No newline at end of file
+ lovyan03/LovyanGFX@^1.1.9
\ No newline at end of file
diff --git a/variants/t-deck/variant.h b/variants/t-deck/variant.h
index 2f4643fde..b1673d338 100644
--- a/variants/t-deck/variant.h
+++ b/variants/t-deck/variant.h
@@ -27,8 +27,8 @@
#define BUTTON_PIN 0
// #define BUTTON_NEED_PULLUP
-#undef GPS_RX_PIN
-#undef GPS_TX_PIN
+#define GPS_RX_PIN 44
+#define GPS_TX_PIN 43
// Have SPI interface SD card slot
#define HAS_SDCARD 1
@@ -41,7 +41,7 @@
#define BATTERY_PIN 4 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
// ratio of voltage divider = 2.0 (RD2=100k, RD3=100k)
#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage.
-#define ADC_CHANNEL ADC1_GPIO1_CHANNEL
+#define ADC_CHANNEL ADC1_GPIO4_CHANNEL
// keyboard
#define I2C_SDA 18 // I2C pins for this board
@@ -84,6 +84,8 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22 // Not really an E22 but TTGO seems to be trying to clone that
+// Not really an E22 but TTGO seems to be trying to clone that
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
-// code)
\ No newline at end of file
+// code)
diff --git a/variants/t-echo/variant.h b/variants/t-echo/variant.h
index bd8dd838f..6bd5091dd 100644
--- a/variants/t-echo/variant.h
+++ b/variants/t-echo/variant.h
@@ -85,9 +85,9 @@ static const uint8_t A0 = PIN_A0;
/*
No longer populated on PCB
*/
-//#define PIN_SERIAL2_RX (0 + 6)
-//#define PIN_SERIAL2_TX (0 + 8)
-// #define PIN_SERIAL2_EN (0 + 17)
+// #define PIN_SERIAL2_RX (0 + 6)
+// #define PIN_SERIAL2_TX (0 + 8)
+// #define PIN_SERIAL2_EN (0 + 17)
/**
Wire Interfaces
@@ -133,7 +133,9 @@ External serial flash WP25R1635FZUIL0
// CPU?
#define SX126X_BUSY (0 + 17)
#define SX126X_RESET (0 + 25)
-#define SX126X_E22 // Not really an E22 but TTGO seems to be trying to clone that
+// Not really an E22 but TTGO seems to be trying to clone that
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
// code)
@@ -171,7 +173,7 @@ External serial flash WP25R1635FZUIL0
#define GPS_L76K
#define PIN_GPS_REINIT (32 + 5) // An output to reset L76K GPS. As per datasheet, low for > 100ms will reset the L76K
-#define PIN_GPS_WAKE (32 + 2) // An output to wake GPS, low means allow sleep, high means force wake
+#define PIN_GPS_STANDBY (32 + 2) // An output to wake GPS, low means allow sleep, high means force wake
// Seems to be missing on this new board
// #define PIN_GPS_PPS (32 + 4) // Pulse per second input from the GPS
#define PIN_GPS_TX (32 + 9) // This is for bits going TOWARDS the CPU
diff --git a/variants/t-watch-s3/platformio.ini b/variants/t-watch-s3/platformio.ini
index 08751d463..162384bfd 100644
--- a/variants/t-watch-s3/platformio.ini
+++ b/variants/t-watch-s3/platformio.ini
@@ -3,8 +3,6 @@
extends = esp32s3_base
board = t-watch-s3
upload_protocol = esptool
-upload_speed = 115200
-#upload_port = /dev/tty.usbmodem3485188D636C1
build_flags = ${esp32_base.build_flags}
-DT_WATCH_S3
@@ -12,6 +10,6 @@ build_flags = ${esp32_base.build_flags}
-DPCF8563_RTC=0x51
lib_deps = ${esp32s3_base.lib_deps}
- lovyan03/LovyanGFX@^1.1.8
+ lovyan03/LovyanGFX@^1.1.9
lewisxhe/PCF8563_Library@1.0.1
adafruit/Adafruit DRV2605 Library@^1.2.2
\ No newline at end of file
diff --git a/variants/t-watch-s3/variant.h b/variants/t-watch-s3/variant.h
index dc363603b..c87845afa 100644
--- a/variants/t-watch-s3/variant.h
+++ b/variants/t-watch-s3/variant.h
@@ -63,6 +63,8 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22 // Not really an E22 but TTGO seems to be trying to clone that
- // Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for
- // the sx1262interface code)
\ No newline at end of file
+// Not really an E22 but TTGO seems to be trying to clone that
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
+// Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for
+// the sx1262interface code)
diff --git a/variants/tbeam-s3-core/variant.h b/variants/tbeam-s3-core/variant.h
index 532b5b85c..5e9894cc6 100644
--- a/variants/tbeam-s3-core/variant.h
+++ b/variants/tbeam-s3-core/variant.h
@@ -7,9 +7,9 @@
#define I2C_SCL 18 // For QMC6310 sensors and screens
#define BUTTON_PIN 0 // The middle button GPIO on the T-Beam S3
-//#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented
-// anywhere.
-// #define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.
+// #define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented
+// anywhere.
+// #define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.
#define LED_INVERTED 1
@@ -29,7 +29,9 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22 // Not really an E22 but TTGO seems to be trying to clone that
+// Not really an E22 but TTGO seems to be trying to clone that
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
// code)
#endif
@@ -64,4 +66,4 @@
// has 32768 Hz crystal
#define HAS_32768HZ
-#define USE_SH1106
\ No newline at end of file
+#define USE_SH1106
diff --git a/variants/tbeam/variant.h b/variants/tbeam/variant.h
index 642e3721c..84a3477d4 100644
--- a/variants/tbeam/variant.h
+++ b/variants/tbeam/variant.h
@@ -4,8 +4,8 @@
#define I2C_SCL 22
#define BUTTON_PIN 38 // The middle button GPIO on the T-Beam
-//#define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented
-// anywhere.
+// #define BUTTON_PIN_ALT 13 // Alternate GPIO for an external button if needed. Does anyone use this? It is not documented
+// anywhere.
#define EXT_NOTIFY_OUT 13 // Default pin to use for Ext Notify Module.
#define LED_INVERTED 1
@@ -28,7 +28,9 @@
#define SX126X_DIO1 LORA_DIO1
#define SX126X_BUSY LORA_DIO2
#define SX126X_RESET LORA_RESET
-#define SX126X_E22 // Not really an E22 but TTGO seems to be trying to clone that
+// Not really an E22 but TTGO seems to be trying to clone that
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
// Internally the TTGO module hooks the SX1262-DIO2 in to control the TX/RX switch (which is the default for the sx1262interface
// code)
#endif
@@ -37,4 +39,6 @@
// and waking from light sleep
// #define PMU_IRQ 35
#define HAS_AXP192
-#define GPS_UBLOX
\ No newline at end of file
+#define GPS_UBLOX
+#define GPS_RX_PIN 34
+#define GPS_TX_PIN 12
diff --git a/variants/tlora_t3s3_v1/variant.h b/variants/tlora_t3s3_v1/variant.h
index 7f914c055..6e1d1d0eb 100644
--- a/variants/tlora_t3s3_v1/variant.h
+++ b/variants/tlora_t3s3_v1/variant.h
@@ -1,8 +1,3 @@
-#undef GPS_RX_PIN
-#undef GPS_TX_PIN
-
-#define PIN_GPS_EN 42 // GPS power enable pin
-
#define HAS_SDCARD
#define SDCARD_USE_SPI1
@@ -49,7 +44,8 @@
#define SX126X_DIO1 33
#define SX126X_BUSY 34
#define SX126X_RESET LORA_RESET
-#define SX126X_E22
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif
// per SX128x_Receive_Interrupt/utilities.h
@@ -63,4 +59,4 @@
#define SX128X_RXEN 21
#define SX128X_TXEN 10
#define SX128X_MAX_POWER 3
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/tlora_v1/variant.h b/variants/tlora_v1/variant.h
index b4365a443..08fefa809 100644
--- a/variants/tlora_v1/variant.h
+++ b/variants/tlora_v1/variant.h
@@ -1,6 +1,3 @@
-#undef GPS_RX_PIN
-#undef GPS_TX_PIN
-
#define I2C_SDA 4 // I2C pins for this board
#define I2C_SCL 15
@@ -16,4 +13,4 @@
#define LORA_DIO0 26 // a No connect on the SX1262 module
#define LORA_RESET 14
#define LORA_DIO1 33 // Must be manually wired: https://www.thethingsnetwork.org/forum/t/big-esp32-sx127x-topic-part-3/18436
-#define LORA_DIO2 32 // Not really used
+#define LORA_DIO2 32 // Not really used
\ No newline at end of file
diff --git a/variants/tlora_v1_3/variant.h b/variants/tlora_v1_3/variant.h
index 50041f296..73cb31f27 100644
--- a/variants/tlora_v1_3/variant.h
+++ b/variants/tlora_v1_3/variant.h
@@ -1,8 +1,3 @@
-#undef GPS_RX_PIN
-#undef GPS_TX_PIN
-#define GPS_RX_PIN 36
-#define GPS_TX_PIN 13 // per @eugene
-
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
diff --git a/variants/tlora_v2/variant.h b/variants/tlora_v2/variant.h
index b9b521771..8a7cf89ec 100644
--- a/variants/tlora_v2/variant.h
+++ b/variants/tlora_v2/variant.h
@@ -1,8 +1,3 @@
-#undef GPS_RX_PIN
-#undef GPS_TX_PIN
-#define GPS_RX_PIN 36
-#define GPS_TX_PIN 13 // per @eugene
-
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
diff --git a/variants/tlora_v2_1_16/variant.h b/variants/tlora_v2_1_16/variant.h
index 30a176e4c..b8c43e557 100644
--- a/variants/tlora_v2_1_16/variant.h
+++ b/variants/tlora_v2_1_16/variant.h
@@ -1,10 +1,3 @@
-#undef GPS_RX_PIN
-#undef GPS_TX_PIN
-#define GPS_RX_PIN 15 // per @der_bear on the forum, 36 is incorrect for this board type and 15 is a better pick
-#define GPS_TX_PIN 13
-
-#define PIN_GPS_EN 19 // GPS power enable pin
-
#define BATTERY_PIN 35
#define ADC_CHANNEL ADC1_GPIO35_CHANNEL
#define BATTERY_SENSE_SAMPLES 30
diff --git a/variants/tlora_v2_1_18/variant.h b/variants/tlora_v2_1_18/variant.h
index e8f0a9659..efc676992 100644
--- a/variants/tlora_v2_1_18/variant.h
+++ b/variants/tlora_v2_1_18/variant.h
@@ -1,6 +1,3 @@
-#undef GPS_RX_PIN
-#undef GPS_TX_PIN
-
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
// ratio of voltage divider = 2.0 (R42=100k, R43=100k)
#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage.
diff --git a/variants/xiao_ble/variant.h b/variants/xiao_ble/variant.h
index ae7d37458..e2b8eb613 100644
--- a/variants/xiao_ble/variant.h
+++ b/variants/xiao_ble/variant.h
@@ -5,7 +5,7 @@
#define VARIANT_MCK (64000000ul)
#define USE_LFXO // Board uses 32khz crystal for LF
-//#define USE_LFRC // Board uses RC for LF
+// #define USE_LFRC // Board uses RC for LF
/*----------------------------------------------------------------------------
* Headers
@@ -125,7 +125,6 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define SX126X_TXEN RADIOLIB_NC
#define SX126X_RXEN D7
-#define E22_TXEN_CONNECTED_TO_DIO2
// ------------------------------ OR ------------------------------
@@ -141,7 +140,8 @@ static const uint8_t SCK = PIN_SPI_SCK;
#ifdef EBYTE_E22
// Internally the TTGO module hooks the SX126x-DIO2 in to control the TX/RX switch
// (which is the default for the sx1262interface code)
-#define SX126X_E22
+#define SX126X_DIO2_AS_RF_SWITCH
+#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif
/*
@@ -198,4 +198,4 @@ static const uint8_t SCL = PIN_WIRE_SCL;
* Arduino objects - C++ only
*----------------------------------------------------------------------------*/
-#endif
\ No newline at end of file
+#endif
diff --git a/version.properties b/version.properties
index f26bc2b58..fc48509e1 100644
--- a/version.properties
+++ b/version.properties
@@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 2
-build = 2
+build = 10