Merge branch 'master' into apollo

This commit is contained in:
Thomas Göttgens
2023-10-31 14:10:07 +01:00
committed by GitHub
78 changed files with 906 additions and 615 deletions

View File

@@ -7,7 +7,7 @@
is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc... is appreciated." This will allow other devs to potentially save you time by not accidentially duplicating work etc...
- Please do not check in files that don't have real changes - Please do not check in files that don't have real changes
- Please do not reformat lines that you didn't have to change the code on - Please do not reformat lines that you didn't have to change the code on
- We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor along with the ['Trunk Check' extension](https://marketplace.visualstudio.com/items?itemName=trunk.io) (WSL2 is required on windows), - We recommend using the [Visual Studio Code](https://platformio.org/install/ide?install=vscode) editor along with the ['Trunk Check' extension](https://marketplace.visualstudio.com/items?itemName=trunk.io) (In beta for windows, WSL2 for the linux version),
because it automatically follows our indentation rules and its auto reformatting will not cause spurious changes to lines. because it automatically follows our indentation rules and its auto reformatting will not cause spurious changes to lines.
- If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description. - If your PR fixes a bug, mention "fixes #bugnum" somewhere in your pull request description.
- If your other co-developers have comments on your PR please tweak as needed. - If your other co-developers have comments on your PR please tweak as needed.

2
.trunk/.gitignore vendored
View File

@@ -2,7 +2,7 @@
*logs *logs
*actions *actions
*notifications *notifications
*tools
plugins plugins
user_trunk.yaml user_trunk.yaml
user.yaml user.yaml
tools

View File

@@ -1,53 +1,53 @@
version: 0.1 version: 0.1
cli: cli:
version: 1.13.0 version: 1.17.0
plugins: plugins:
sources: sources:
- id: trunk - id: trunk
ref: v1.1.1 ref: v1.2.5
uri: https://github.com/trunk-io/plugins uri: https://github.com/trunk-io/plugins
lint: lint:
enabled: enabled:
- bandit@1.7.5 - bandit@1.7.5
- checkov@2.4.1 - checkov@2.5.0
- terrascan@1.18.3 - terrascan@1.18.3
- trivy@0.44.1 - trivy@0.45.1
- trufflehog@3.48.0 - trufflehog@3.59.0
- taplo@0.8.1 - taplo@0.8.1
- ruff@0.0.284 - ruff@0.0.292
- yamllint@1.32.0 - yamllint@1.32.0
- isort@5.12.0 - isort@5.12.0
- markdownlint@0.35.0 - markdownlint@0.37.0
- oxipng@8.0.0 - oxipng@8.0.0
- svgo@3.0.2 - svgo@3.0.2
- actionlint@1.6.25 - actionlint@1.6.26
- flake8@6.1.0 - flake8@6.1.0
- hadolint@2.12.0 - hadolint@2.12.0
- shfmt@3.6.0 - shfmt@3.6.0
- shellcheck@0.9.0 - shellcheck@0.9.0
- black@23.7.0 - black@23.9.1
- git-diff-check - git-diff-check
- gitleaks@8.17.0 - gitleaks@8.18.0
- clang-format@16.0.3 - clang-format@16.0.3
- prettier@3.0.2 - prettier@3.0.3
disabled: disabled:
- taplo@0.8.1 - taplo@0.8.1
- shellcheck@0.9.0 - shellcheck@0.9.0
- shfmt@3.6.0 - shfmt@3.6.0
- oxipng@8.0.0 - oxipng@8.0.0
- actionlint@1.6.22 - actionlint@1.6.22
- markdownlint@0.35.0 - markdownlint@0.37.0
- hadolint@2.12.0 - hadolint@2.12.0
- svgo@3.0.2 - svgo@3.0.2
runtimes: runtimes:
enabled: enabled:
- python@3.10.8 - python@3.10.8
- go@1.19.5 - go@1.21.0
- node@18.12.1 - node@18.12.1
actions: actions:
disabled: disabled:
- trunk-announce - trunk-announce
- trunk-check-pre-push
- trunk-fmt-pre-commit
enabled: enabled:
- trunk-fmt-pre-commit
- trunk-check-pre-push
- trunk-upgrade-available - trunk-upgrade-available

View File

@@ -10,8 +10,8 @@
This repository contains the device firmware for the Meshtastic project. This repository contains the device firmware for the Meshtastic project.
**[Building Instructions](https://meshtastic.org/docs/development/firmware/build)** - **[Building Instructions](https://meshtastic.org/docs/development/firmware/build)**
**[Flashing Instructions](https://meshtastic.org/docs/getting-started/flashing-firmware/)** - **[Flashing Instructions](https://meshtastic.org/docs/getting-started/flashing-firmware/)**
## Stats ## Stats

View File

@@ -15,7 +15,7 @@
"f_cpu": "240000000L", "f_cpu": "240000000L",
"f_flash": "80000000L", "f_flash": "80000000L",
"flash_mode": "dio", "flash_mode": "dio",
"hwids": [["0X303A", "0x1001"]], "hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3", "mcu": "esp32s3",
"variant": "tbeam-s3-core" "variant": "tbeam-s3-core"
}, },

View File

@@ -70,7 +70,7 @@ lib_deps =
https://github.com/meshtastic/esp8266-oled-ssd1306.git#b38094e03dfa964fbc0e799bc374e91a605c1223 ; ESP8266_SSD1306 https://github.com/meshtastic/esp8266-oled-ssd1306.git#b38094e03dfa964fbc0e799bc374e91a605c1223 ; ESP8266_SSD1306
https://github.com/mathertel/OneButton#2.1.0 ; OneButton library for non-blocking button debounce https://github.com/mathertel/OneButton#2.1.0 ; OneButton library for non-blocking button debounce
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
https://github.com/meshtastic/TinyGPSPlus.git#127ad674ef85f0201cb68a065879653ed94792c4 https://github.com/meshtastic/TinyGPSPlus.git#076e8d2c8fb702d9be5b08c55b93ff76f8af7e61
https://github.com/meshtastic/ArduinoThread.git#72921ac222eed6f526ba1682023cee290d9aa1b3 https://github.com/meshtastic/ArduinoThread.git#72921ac222eed6f526ba1682023cee290d9aa1b3
nanopb/Nanopb@^0.4.7 nanopb/Nanopb@^0.4.7
erriez/ErriezCRC32@^1.0.1 erriez/ErriezCRC32@^1.0.1

34
src/DisplayFormatters.cpp Normal file
View File

@@ -0,0 +1,34 @@
#include "DisplayFormatters.h"
const char *DisplayFormatters::getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName)
{
switch (preset) {
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
return useShortName ? "ShortS" : "ShortSlow";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
return useShortName ? "ShortF" : "ShortFast";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
return useShortName ? "MedS" : "MediumSlow";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
return useShortName ? "MedF" : "MediumFast";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
return useShortName ? "LongS" : "LongSlow";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
return useShortName ? "LongF" : "LongFast";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
return useShortName ? "LongM" : "LongMod";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
return useShortName ? "VeryL" : "VLongSlow";
break;
default:
return useShortName ? "Custom" : "Invalid";
break;
}
}

8
src/DisplayFormatters.h Normal file
View File

@@ -0,0 +1,8 @@
#pragma once
#include "NodeDB.h"
class DisplayFormatters
{
public:
static const char *getModemPresetDisplayName(meshtastic_Config_LoRaConfig_ModemPreset preset, bool useShortName);
};

View File

@@ -175,9 +175,21 @@ class AnalogBatteryLevel : public HasBatteryLevel
uint32_t raw = 0; uint32_t raw = 0;
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
#ifndef BAT_MEASURE_ADC_UNIT // ADC1 #ifndef BAT_MEASURE_ADC_UNIT // ADC1
#ifdef ADC_CTRL
if (heltec_version == 5) {
pinMode(ADC_CTRL, OUTPUT);
digitalWrite(ADC_CTRL, HIGH);
delay(10);
}
#endif
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) { for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
raw += adc1_get_raw(adc_channel); raw += adc1_get_raw(adc_channel);
} }
#ifdef ADC_CTRL
if (heltec_version == 5) {
digitalWrite(ADC_CTRL, LOW);
}
#endif
#else // ADC2 #else // ADC2
int32_t adc_buf = 0; int32_t adc_buf = 0;
for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) { for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
@@ -269,9 +281,10 @@ class AnalogBatteryLevel : public HasBatteryLevel
#if defined(HAS_TELEMETRY) && (HAS_TELEMETRY == 1) && !defined(ARCH_PORTDUINO) #if defined(HAS_TELEMETRY) && (HAS_TELEMETRY == 1) && !defined(ARCH_PORTDUINO)
uint16_t getINAVoltage() uint16_t getINAVoltage()
{ {
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219] == config.power.device_battery_ina_address) { if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
return ina219Sensor.getBusVoltageMv(); return ina219Sensor.getBusVoltageMv();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260] == config.power.device_battery_ina_address) { } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
config.power.device_battery_ina_address) {
return ina260Sensor.getBusVoltageMv(); return ina260Sensor.getBusVoltageMv();
} }
return 0; return 0;
@@ -282,11 +295,12 @@ class AnalogBatteryLevel : public HasBatteryLevel
if (!config.power.device_battery_ina_address) { if (!config.power.device_battery_ina_address) {
return false; return false;
} }
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219] == config.power.device_battery_ina_address) { if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA219].first == config.power.device_battery_ina_address) {
if (!ina219Sensor.isInitialized()) if (!ina219Sensor.isInitialized())
return ina219Sensor.runOnce() > 0; return ina219Sensor.runOnce() > 0;
return ina219Sensor.isRunning(); return ina219Sensor.isRunning();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260] == config.power.device_battery_ina_address) { } else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA260].first ==
config.power.device_battery_ina_address) {
if (!ina260Sensor.isInitialized()) if (!ina260Sensor.isInitialized())
return ina260Sensor.runOnce() > 0; return ina260Sensor.runOnce() > 0;
return ina260Sensor.isRunning(); return ina260Sensor.isRunning();
@@ -414,7 +428,7 @@ void Power::shutdown()
#ifdef PIN_LED3 #ifdef PIN_LED3
ledOff(PIN_LED2); ledOff(PIN_LED2);
#endif #endif
doDeepSleep(DELAY_FOREVER); doDeepSleep(DELAY_FOREVER, false);
#endif #endif
} }

View File

@@ -45,7 +45,7 @@ static void sdsEnter()
{ {
LOG_DEBUG("Enter state: SDS\n"); LOG_DEBUG("Enter state: SDS\n");
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw // FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(getConfiguredOrDefaultMs(config.power.sds_secs)); doDeepSleep(getConfiguredOrDefaultMs(config.power.sds_secs), false);
} }
extern Power *power; extern Power *power;
@@ -154,9 +154,6 @@ static void darkEnter()
{ {
setBluetoothEnable(true); setBluetoothEnable(true);
screen->setOn(false); screen->setOn(false);
#ifdef KB_POWERON
digitalWrite(KB_POWERON, LOW);
#endif
} }
static void serialEnter() static void serialEnter()
@@ -184,9 +181,6 @@ static void powerEnter()
} else { } else {
screen->setOn(true); screen->setOn(true);
setBluetoothEnable(true); setBluetoothEnable(true);
#ifdef KB_POWERON
digitalWrite(KB_POWERON, HIGH);
#endif
// within enter() the function getState() returns the state we came from // within enter() the function getState() returns the state we came from
if (strcmp(powerFSM.getState()->name, "BOOT") != 0 && strcmp(powerFSM.getState()->name, "POWER") != 0 && if (strcmp(powerFSM.getState()->name, "BOOT") != 0 && strcmp(powerFSM.getState()->name, "POWER") != 0 &&
strcmp(powerFSM.getState()->name, "DARK") != 0) { strcmp(powerFSM.getState()->name, "DARK") != 0) {
@@ -217,9 +211,6 @@ static void onEnter()
LOG_DEBUG("Enter state: ON\n"); LOG_DEBUG("Enter state: ON\n");
screen->setOn(true); screen->setOn(true);
setBluetoothEnable(true); setBluetoothEnable(true);
#ifdef KB_POWERON
digitalWrite(KB_POWERON, HIGH);
#endif
} }
static void onIdle() static void onIdle()
@@ -254,6 +245,8 @@ Fsm powerFSM(&stateBOOT);
void PowerFSM_setup() void PowerFSM_setup()
{ {
bool isRouter = (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ? 1 : 0); bool isRouter = (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ? 1 : 0);
bool isTrackerOrSensor = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR;
bool hasPower = isPowered(); bool hasPower = isPowered();
LOG_INFO("PowerFSM init, USB power=%d\n", hasPower ? 1 : 0); LOG_INFO("PowerFSM init, USB power=%d\n", hasPower ? 1 : 0);
@@ -357,12 +350,12 @@ void PowerFSM_setup()
getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL, getConfiguredOrDefaultMs(config.display.screen_on_secs, default_screen_on_secs), NULL,
"Screen-on timeout"); "Screen-on timeout");
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
State *lowPowerState = &stateLS;
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
// See: https://github.com/meshtastic/firmware/issues/1071 // See: https://github.com/meshtastic/firmware/issues/1071
if (isRouter || config.power.is_power_saving) { // Don't add power saving transitions if we are a power saving tracker or sensor. Sleep will be initiatiated through the
// modules
if ((isRouter || config.power.is_power_saving) && !isTrackerOrSensor) {
powerFSM.add_timed_transition(&stateNB, &stateLS, powerFSM.add_timed_transition(&stateNB, &stateLS,
getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL, getConfiguredOrDefaultMs(config.power.min_wake_secs, default_min_wake_secs), NULL,
"Min wake timeout"); "Min wake timeout");

View File

@@ -67,6 +67,7 @@ class OSThread : public Thread
* Returns desired period for next invocation (or RUN_SAME for no change) * Returns desired period for next invocation (or RUN_SAME for no change)
*/ */
virtual int32_t runOnce() = 0; virtual int32_t runOnce() = 0;
bool sleepOnNextExecution = false;
// Do not override this // Do not override this
virtual void run(); virtual void run();

View File

@@ -18,6 +18,8 @@ class ScanI2CTwoWire : public ScanI2C
ScanI2C::FoundDevice find(ScanI2C::DeviceType) const override; ScanI2C::FoundDevice find(ScanI2C::DeviceType) const override;
TwoWire *fetchI2CBus(ScanI2C::DeviceAddress) const;
bool exists(ScanI2C::DeviceType) const override; bool exists(ScanI2C::DeviceType) const override;
size_t countDevices() const override; size_t countDevices() const override;
@@ -51,6 +53,4 @@ class ScanI2CTwoWire : public ScanI2C
uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const; uint16_t getRegisterValue(const RegisterLocation &, ResponseWidth) const;
DeviceType probeOLED(ScanI2C::DeviceAddress) const; DeviceType probeOLED(ScanI2C::DeviceAddress) const;
TwoWire *fetchI2CBus(ScanI2C::DeviceAddress) const;
}; };

View File

@@ -20,7 +20,7 @@ HardwareSerial *GPS::_serial_gps = &Serial1;
HardwareSerial *GPS::_serial_gps = NULL; HardwareSerial *GPS::_serial_gps = NULL;
#endif #endif
GPS *gps; GPS *gps = nullptr;
/// Multiple GPS instances might use the same serial port (in sequence), but we can /// Multiple GPS instances might use the same serial port (in sequence), but we can
/// only init that port once. /// only init that port once.
@@ -76,28 +76,25 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
while (millis() < startTimeout) { while (millis() < startTimeout) {
if (_serial_gps->available()) { if (_serial_gps->available()) {
b = _serial_gps->read(); b = _serial_gps->read();
#ifdef GPS_DEBUG
LOG_DEBUG("%02X", (char *)buffer);
#endif
buffer[bytesRead] = b; buffer[bytesRead] = b;
bytesRead++; bytesRead++;
if ((bytesRead == 767) || (b == '\r')) { if ((bytesRead == 767) || (b == '\r')) {
if (strnstr((char *)buffer, message, bytesRead) != nullptr) { if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
#ifdef GPS_DEBUG #ifdef GPS_DEBUG
buffer[bytesRead] = '\0'; LOG_DEBUG("\r");
LOG_DEBUG("%s\r", (char *)buffer);
#endif #endif
return GNSS_RESPONSE_OK; return GNSS_RESPONSE_OK;
} else { } else {
#ifdef GPS_DEBUG
buffer[bytesRead] = '\0';
LOG_INFO("Bytes read:%s\n", (char *)buffer);
#endif
bytesRead = 0; bytesRead = 0;
} }
} }
} }
} }
#ifdef GPS_DEBUG #ifdef GPS_DEBUG
buffer[bytesRead] = '\0'; LOG_DEBUG("\n");
LOG_INFO("Bytes read:%s\n", (char *)buffer);
#endif #endif
return GNSS_RESPONSE_NONE; return GNSS_RESPONSE_NONE;
} }
@@ -252,10 +249,18 @@ int GPS::getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t
bool GPS::setup() bool GPS::setup()
{ {
int msglen = 0; int msglen = 0;
bool isProblematicGPS = false;
if (!didSerialInit) { if (!didSerialInit) {
#if !defined(GPS_UC6580) #if !defined(GPS_UC6580)
if (tx_gpio) { #ifdef HAS_PMU
// The T-Beam 1.2 has issues with the GPS
if (HW_VENDOR == meshtastic_HardwareModel_TBEAM && PMU->getChipModel() == XPOWERS_AXP2101) {
gnssModel = GNSS_MODEL_UBLOX;
isProblematicGPS = true;
}
#endif
if (tx_gpio && gnssModel == GNSS_MODEL_UNKNOWN) {
LOG_DEBUG("Probing for GPS at %d \n", serialSpeeds[speedSelect]); LOG_DEBUG("Probing for GPS at %d \n", serialSpeeds[speedSelect]);
gnssModel = probe(serialSpeeds[speedSelect]); gnssModel = probe(serialSpeeds[speedSelect]);
if (gnssModel == GNSS_MODEL_UNKNOWN) { if (gnssModel == GNSS_MODEL_UNKNOWN) {
@@ -390,32 +395,36 @@ bool GPS::setup()
LOG_WARN("Unable to enable powersaving for GPS.\n"); LOG_WARN("Unable to enable powersaving for GPS.\n");
} }
} else { } else {
if (strncmp(info.hwVersion, "00040007", 8) == 0) { // This PSM mode has only been tested on this hardware if (!(isProblematicGPS)) {
msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_PSM); if (strncmp(info.hwVersion, "00040007", 8) == 0) { // This PSM mode has only been tested on this hardware
_serial_gps->write(UBXscratch, msglen); msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_PSM);
if (getACK(0x06, 0x11, 300) != GNSS_RESPONSE_OK) { _serial_gps->write(UBXscratch, msglen);
LOG_WARN("Unable to enable powersaving mode for GPS.\n"); 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); msglen = makeUBXPacket(0x06, 0x3B, 44, _message_CFG_PM2);
if (getACK(0x06, 0x3B, 300) != GNSS_RESPONSE_OK) { _serial_gps->write(UBXscratch, msglen);
LOG_WARN("Unable to enable powersaving details for GPS.\n"); 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); } else {
_serial_gps->write(UBXscratch, msglen); msglen = makeUBXPacket(0x06, 0x11, 0x2, _message_CFG_RXM_ECO);
if (getACK(0x06, 0x11, 300) != GNSS_RESPONSE_OK) { _serial_gps->write(UBXscratch, msglen);
LOG_WARN("Unable to enable powersaving ECO mode for GPS.\n"); if (getACK(0x06, 0x11, 300) != GNSS_RESPONSE_OK) {
LOG_WARN("Unable to enable powersaving ECO mode for GPS.\n");
}
} }
} }
} }
// The T-beam 1.2 has issues.
msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE); if (!(isProblematicGPS)) {
_serial_gps->write(UBXscratch, msglen); msglen = makeUBXPacket(0x06, 0x09, sizeof(_message_SAVE), _message_SAVE);
if (getACK(0x06, 0x09, 300) != GNSS_RESPONSE_OK) { _serial_gps->write(UBXscratch, msglen);
LOG_WARN("Unable to save GNSS module configuration.\n"); if (getACK(0x06, 0x09, 300) != GNSS_RESPONSE_OK) {
} else { LOG_WARN("Unable to save GNSS module configuration.\n");
LOG_INFO("GNSS module configuration saved!\n"); } else {
LOG_INFO("GNSS module configuration saved!\n");
}
} }
} }
didSerialInit = true; didSerialInit = true;
@@ -433,7 +442,7 @@ GPS::~GPS()
notifyGPSSleepObserver.observe(&notifyGPSSleep); notifyGPSSleepObserver.observe(&notifyGPSSleep);
} }
void GPS::setGPSPower(bool on, bool standbyOnly) void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
{ {
LOG_INFO("Setting GPS power=%d\n", on); LOG_INFO("Setting GPS power=%d\n", on);
if (on) { if (on) {
@@ -483,6 +492,10 @@ void GPS::setGPSPower(bool on, bool standbyOnly)
if (!on) { if (!on) {
if (gnssModel == GNSS_MODEL_UBLOX) { if (gnssModel == GNSS_MODEL_UBLOX) {
uint8_t msglen; uint8_t msglen;
LOG_DEBUG("Sleep Time: %i\n", sleepTime);
for (int i = 0; i < 4; i++) {
gps->_message_PMREQ[0 + i] = sleepTime >> (i * 8); // Encode the sleep time in millis into the packet
}
msglen = gps->makeUBXPacket(0x02, 0x41, 0x08, gps->_message_PMREQ); msglen = gps->makeUBXPacket(0x02, 0x41, 0x08, gps->_message_PMREQ);
gps->_serial_gps->write(gps->UBXscratch, msglen); gps->_serial_gps->write(gps->UBXscratch, msglen);
} }
@@ -514,7 +527,7 @@ void GPS::setAwake(bool on)
LOG_DEBUG("WANT GPS=%d\n", on); LOG_DEBUG("WANT GPS=%d\n", on);
isAwake = on; isAwake = on;
if (!enabled) { // short circuit if the user has disabled GPS if (!enabled) { // short circuit if the user has disabled GPS
setGPSPower(false, false); setGPSPower(false, false, 0);
return; return;
} }
@@ -532,12 +545,12 @@ void GPS::setAwake(bool on)
} }
if ((int32_t)getSleepTime() - averageLockTime > if ((int32_t)getSleepTime() - averageLockTime >
15 * 60 * 1000) { // 15 minutes is probably long enough to make a complete poweroff worth it. 15 * 60 * 1000) { // 15 minutes is probably long enough to make a complete poweroff worth it.
setGPSPower(on, false); setGPSPower(on, false, getSleepTime() - averageLockTime);
} else if ((int32_t)getSleepTime() - averageLockTime > 10000) { // 10 seconds is enough for standby } else if ((int32_t)getSleepTime() - averageLockTime > 10000) { // 10 seconds is enough for standby
#ifdef GPS_UC6580 #ifdef GPS_UC6580
setGPSPower(on, false); setGPSPower(on, false, getSleepTime() - averageLockTime);
#else #else
setGPSPower(on, true); setGPSPower(on, true, getSleepTime() - averageLockTime);
#endif #endif
} else if (averageLockTime > 20000) { } else if (averageLockTime > 20000) {
averageLockTime -= 1000; // eventually want to sleep again. averageLockTime -= 1000; // eventually want to sleep again.
@@ -598,7 +611,7 @@ int32_t GPS::runOnce()
return 2000; // Setup failed, re-run in two seconds return 2000; // Setup failed, re-run in two seconds
// We have now loaded our saved preferences from flash // We have now loaded our saved preferences from flash
if (config.position.gps_enabled == false && config.position.fixed_position == false) { if (config.position.gps_enabled == false) {
return disable(); return disable();
} }
// ONCE we will factory reset the GPS for bug #327 // ONCE we will factory reset the GPS for bug #327
@@ -631,6 +644,11 @@ int32_t GPS::runOnce()
} }
} }
} }
// At least one GPS has a bad habit of losing its mind from time to time
if (rebootsSeen > 2) {
rebootsSeen = 0;
gps->factoryReset();
}
// If we are overdue for an update, turn on the GPS and at least publish the current status // If we are overdue for an update, turn on the GPS and at least publish the current status
uint32_t now = millis(); uint32_t now = millis();
@@ -685,8 +703,8 @@ int32_t GPS::runOnce()
// If state has changed do a publish // If state has changed do a publish
publishUpdate(); publishUpdate();
if (config.position.gps_enabled == false) // This should trigger if GPS is disabled but fixed_position is true if (config.position.fixed_position == true && hasValidLocation)
return disable(); return disable(); // This should trigger when we have a fixed position, and get that first position
// 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms // 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. // if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake.
@@ -899,9 +917,7 @@ GPS *GPS::createGps()
LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n"); LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n");
#endif #endif
if (config.position.gps_enabled) { new_gps->setGPSPower(true, false, 0);
new_gps->setGPSPower(true, false);
}
#ifdef PIN_GPS_RESET #ifdef PIN_GPS_RESET
digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
@@ -956,11 +972,38 @@ bool GPS::factoryReset()
digitalWrite(PIN_GPS_REINIT, 1); digitalWrite(PIN_GPS_REINIT, 1);
#endif #endif
// send the UBLOX Factory Reset Command regardless of detect state, something is very wrong, just assume it's UBLOX. if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
// Factory Reset byte _message_reset1[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
byte _message_reset[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1C, 0xA2};
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x17, 0x2B, 0x7E}; _serial_gps->write(_message_reset1, sizeof(_message_reset1));
_serial_gps->write(_message_reset, sizeof(_message_reset)); if (getACK(0x05, 0x01, 10000)) {
LOG_INFO("Get ack success!\n");
}
delay(100);
byte _message_reset2[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x1B, 0xA1};
_serial_gps->write(_message_reset2, sizeof(_message_reset2));
if (getACK(0x05, 0x01, 10000)) {
LOG_INFO("Get ack success!\n");
}
delay(100);
byte _message_reset3[] = {0xB5, 0x62, 0x06, 0x09, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x03, 0x1D, 0xB3};
_serial_gps->write(_message_reset3, sizeof(_message_reset3));
if (getACK(0x05, 0x01, 10000)) {
LOG_INFO("Get ack success!\n");
}
// Reset device ram to COLDSTART state
// byte _message_CFG_RST_COLDSTART[] = {0xB5, 0x62, 0x06, 0x04, 0x04, 0x00, 0xFF, 0xB9, 0x00, 0x00, 0xC6, 0x8B};
// _serial_gps->write(_message_CFG_RST_COLDSTART, sizeof(_message_CFG_RST_COLDSTART));
// delay(1000);
} else {
// 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); delay(1000);
return true; return true;
} }
@@ -1157,6 +1200,7 @@ bool GPS::hasFlow()
bool GPS::whileIdle() bool GPS::whileIdle()
{ {
int charsInBuf = 0;
bool isValid = false; bool isValid = false;
if (!isAwake) { if (!isAwake) {
clearBuffer(); clearBuffer();
@@ -1173,10 +1217,20 @@ bool GPS::whileIdle()
// First consume any chars that have piled up at the receiver // First consume any chars that have piled up at the receiver
while (_serial_gps->available() > 0) { while (_serial_gps->available() > 0) {
int c = _serial_gps->read(); int c = _serial_gps->read();
// LOG_DEBUG("%c", c); UBXscratch[charsInBuf] = c;
#ifdef GPS_DEBUG
LOG_DEBUG("%c", c);
#endif
isValid |= reader.encode(c); isValid |= reader.encode(c);
if (charsInBuf > sizeof(UBXscratch) - 10 || c == '\r') {
if (strnstr((char *)UBXscratch, "$GPTXT,01,01,02,u-blox ag - www.u-blox.com*50", charsInBuf)) {
rebootsSeen++;
}
charsInBuf = 0;
} else {
charsInBuf++;
}
} }
return isValid; return isValid;
} }
void GPS::enable() void GPS::enable()

View File

@@ -94,7 +94,7 @@ class GPS : private concurrency::OSThread
/** If !NULL we will use this serial port to construct our GPS */ /** If !NULL we will use this serial port to construct our GPS */
static HardwareSerial *_serial_gps; static HardwareSerial *_serial_gps;
static const uint8_t _message_PMREQ[]; static uint8_t _message_PMREQ[];
static const uint8_t _message_CFG_RXM_PSM[]; static const uint8_t _message_CFG_RXM_PSM[];
static const uint8_t _message_CFG_RXM_ECO[]; static const uint8_t _message_CFG_RXM_ECO[];
static const uint8_t _message_CFG_PM2[]; static const uint8_t _message_CFG_PM2[];
@@ -132,7 +132,7 @@ class GPS : private concurrency::OSThread
// Disable the thread // Disable the thread
int32_t disable() override; int32_t disable() override;
void setGPSPower(bool on, bool standbyOnly); void setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime);
/// Returns true if we have acquired GPS lock. /// Returns true if we have acquired GPS lock.
virtual bool hasLock(); virtual bool hasLock();
@@ -154,6 +154,8 @@ class GPS : private concurrency::OSThread
// scratch space for creating ublox packets // scratch space for creating ublox packets
uint8_t UBXscratch[250] = {0}; uint8_t UBXscratch[250] = {0};
int rebootsSeen = 0;
int getACK(uint8_t *buffer, uint16_t size, uint8_t requestedClass, uint8_t requestedID, uint32_t waitMillis); 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(uint8_t c, uint8_t i, uint32_t waitMillis);
GPS_RESPONSE getACK(const char *message, uint32_t waitMillis); GPS_RESPONSE getACK(const char *message, uint32_t waitMillis);

View File

@@ -1,4 +1,4 @@
const uint8_t GPS::_message_PMREQ[] PROGMEM = { uint8_t GPS::_message_PMREQ[] PROGMEM = {
0x00, 0x00, // 4 bytes duration of request task 0x00, 0x00, // 4 bytes duration of request task
0x00, 0x00, // (milliseconds) 0x00, 0x00, // (milliseconds)
0x02, 0x00, // Task flag bitfield 0x02, 0x00, // Task flag bitfield
@@ -17,7 +17,7 @@ const uint8_t GPS::_message_CFG_RXM_ECO[] PROGMEM = {
const uint8_t GPS::_message_CFG_PM2[] PROGMEM = { const uint8_t GPS::_message_CFG_PM2[] PROGMEM = {
0x01, 0x06, 0x00, 0x00, // version, Reserved 0x01, 0x06, 0x00, 0x00, // version, Reserved
0x0e, 0x81, 0x42, 0x01, // flags 0x0E, 0x81, 0x43, 0x01, // flags
0xE8, 0x03, 0x00, 0x00, // update period 1000 ms 0xE8, 0x03, 0x00, 0x00, // update period 1000 ms
0x10, 0x27, 0x00, 0x00, // search period 10s 0x10, 0x27, 0x00, 0x00, // search period 10s
0x00, 0x00, 0x00, 0x00, // Grod offset 0 0x00, 0x00, 0x00, 0x00, // Grod offset 0
@@ -189,4 +189,4 @@ const uint8_t GPS::_message_SAVE[] = {
0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections 0xFF, 0xFF, 0x00, 0x00, // saveMask: save all sections
0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded 0x00, 0x00, 0x00, 0x00, // loadMask: no sections loaded
0x0F // deviceMask: BBR, Flash, EEPROM, and SPI Flash 0x0F // deviceMask: BBR, Flash, EEPROM, and SPI Flash
}; };

View File

@@ -24,6 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#if HAS_SCREEN #if HAS_SCREEN
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
#include "DisplayFormatters.h"
#include "GPS.h" #include "GPS.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
@@ -160,17 +161,9 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl
xstr(APP_VERSION_SHORT)); // Note: we don't bother printing region or now, it makes the string too long xstr(APP_VERSION_SHORT)); // Note: we don't bother printing region or now, it makes the string too long
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf); display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf);
screen->forceDisplay(); screen->forceDisplay();
// FIXME - draw serial # somewhere? // FIXME - draw serial # somewhere?
} }
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// Draw region in upper left
const char *region = myRegion ? myRegion->name : NULL;
drawIconScreen(region, display, state, x, y);
}
static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
// draw an xbm image. // draw an xbm image.
@@ -220,6 +213,28 @@ static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, i
drawOEMIconScreen(region, display, state, x, y); drawOEMIconScreen(region, display, state, x, y);
} }
static void drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message)
{
uint16_t x_offset = display->width() / 2;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, 26 + y, message);
}
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
#ifdef ARCH_ESP32
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER || wakeCause == ESP_SLEEP_WAKEUP_EXT1) {
drawFrameText(display, state, x, y, "Resuming...");
} else
#endif
{
// Draw region in upper left
const char *region = myRegion ? myRegion->name : NULL;
drawIconScreen(region, display, state, x, y);
}
}
// Used on boot when a certificate is being created // Used on boot when a certificate is being created
static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
@@ -320,22 +335,6 @@ static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state,
display->drawString(x_offset + x, y_offset + y, deviceName); display->drawString(x_offset + x, y_offset + y, deviceName);
} }
static void drawFrameShutdown(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
uint16_t x_offset = display->width() / 2;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, 26 + y, "Shutting down...");
}
static void drawFrameReboot(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
uint16_t x_offset = display->width() / 2;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, 26 + y, "Rebooting...");
}
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
@@ -906,20 +905,6 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
drawColumns(display, x, y, fields); drawColumns(display, x, y, fields);
} }
// #ifdef RAK4630
// Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl),
// dispdev_oled(address, sda, scl), ui(&dispdev)
// {
// address_found = address;
// cmdQueue.setReader(this);
// if (screen_found) {
// (void)dispdev;
// AutoOLEDWire dispdev = dispdev_oled;
// (void)ui;
// OLEDDisplayUi ui(&dispdev);
// }
// }
// #else
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry) Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32), : concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32),
dispdev(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE), dispdev(address.address, -1, -1, geometry, (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE),
@@ -927,7 +912,7 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
{ {
cmdQueue.setReader(this); cmdQueue.setReader(this);
} }
// #endif
/** /**
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just * Prepare the display for the unit going to the lowest power mode possible. Most screens will just
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code * poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
@@ -1244,11 +1229,8 @@ void Screen::setWelcomeFrames()
{ {
if (address_found.address) { if (address_found.address) {
// LOG_DEBUG("showing Welcome frames\n"); // LOG_DEBUG("showing Welcome frames\n");
ui.disableAllIndicators(); static FrameCallback frames[] = {drawWelcomeScreen};
setFrameImmediateDraw(frames);
static FrameCallback welcomeFrames[] = {drawWelcomeScreen};
ui.setFrames(welcomeFrames, 1);
ui.update();
} }
} }
@@ -1335,12 +1317,15 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
LOG_DEBUG("showing bluetooth screen\n"); LOG_DEBUG("showing bluetooth screen\n");
showingNormalScreen = false; showingNormalScreen = false;
static FrameCallback btFrames[] = {drawFrameBluetooth}; static FrameCallback frames[] = {drawFrameBluetooth};
snprintf(btPIN, sizeof(btPIN), "%06u", pin); snprintf(btPIN, sizeof(btPIN), "%06u", pin);
setFrameImmediateDraw(frames);
}
void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
{
ui.disableAllIndicators(); ui.disableAllIndicators();
ui.setFrames(btFrames, 1); ui.setFrames(drawFrames, 1);
setFastFramerate(); setFastFramerate();
} }
@@ -1349,11 +1334,12 @@ void Screen::handleShutdownScreen()
LOG_DEBUG("showing shutdown screen\n"); LOG_DEBUG("showing shutdown screen\n");
showingNormalScreen = false; showingNormalScreen = false;
static FrameCallback shutdownFrames[] = {drawFrameShutdown}; auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
drawFrameText(display, state, x, y, "Shutting down...");
};
static FrameCallback frames[] = {frame};
ui.disableAllIndicators(); setFrameImmediateDraw(frames);
ui.setFrames(shutdownFrames, 1);
setFastFramerate();
} }
void Screen::handleRebootScreen() void Screen::handleRebootScreen()
@@ -1361,11 +1347,11 @@ void Screen::handleRebootScreen()
LOG_DEBUG("showing reboot screen\n"); LOG_DEBUG("showing reboot screen\n");
showingNormalScreen = false; showingNormalScreen = false;
static FrameCallback rebootFrames[] = {drawFrameReboot}; auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
drawFrameText(display, state, x, y, "Rebooting...");
ui.disableAllIndicators(); };
ui.setFrames(rebootFrames, 1); static FrameCallback frames[] = {frame};
setFastFramerate(); setFrameImmediateDraw(frames);
} }
void Screen::handleStartFirmwareUpdateScreen() void Screen::handleStartFirmwareUpdateScreen()
@@ -1373,11 +1359,8 @@ void Screen::handleStartFirmwareUpdateScreen()
LOG_DEBUG("showing firmware screen\n"); LOG_DEBUG("showing firmware screen\n");
showingNormalScreen = false; showingNormalScreen = false;
static FrameCallback btFrames[] = {drawFrameFirmware}; static FrameCallback frames[] = {drawFrameFirmware};
setFrameImmediateDraw(frames);
ui.disableAllIndicators();
ui.setFrames(btFrames, 1);
setFastFramerate();
} }
void Screen::blink() void Screen::blink()
@@ -1638,65 +1621,8 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
} else { } else {
// Codes: // Codes:
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
if (getWifiDisconnectReason() == 2) { display->drawString(x, y + FONT_HEIGHT_SMALL * 1,
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Authentication Invalid"); WiFi.disconnectReasonName(static_cast<wifi_err_reason_t>(getWifiDisconnectReason())));
} else if (getWifiDisconnectReason() == 3) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "De-authenticated");
} else if (getWifiDisconnectReason() == 4) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Disassociated Expired");
} else if (getWifiDisconnectReason() == 5) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP - Too Many Clients");
} else if (getWifiDisconnectReason() == 6) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "NOT_AUTHED");
} else if (getWifiDisconnectReason() == 7) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "NOT_ASSOCED");
} else if (getWifiDisconnectReason() == 8) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Disassociated");
} else if (getWifiDisconnectReason() == 9) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "ASSOC_NOT_AUTHED");
} else if (getWifiDisconnectReason() == 10) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "DISASSOC_PWRCAP_BAD");
} else if (getWifiDisconnectReason() == 11) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "DISASSOC_SUPCHAN_BAD");
} else if (getWifiDisconnectReason() == 13) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IE_INVALID");
} else if (getWifiDisconnectReason() == 14) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "MIC_FAILURE");
} else if (getWifiDisconnectReason() == 15) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP Handshake Timeout");
} else if (getWifiDisconnectReason() == 16) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "GROUP_KEY_UPDATE_TIMEOUT");
} else if (getWifiDisconnectReason() == 17) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IE_IN_4WAY_DIFFERS");
} else if (getWifiDisconnectReason() == 18) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Invalid Group Cipher");
} else if (getWifiDisconnectReason() == 19) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Invalid Pairwise Cipher");
} else if (getWifiDisconnectReason() == 20) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AKMP_INVALID");
} else if (getWifiDisconnectReason() == 21) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "UNSUPP_RSN_IE_VERSION");
} else if (getWifiDisconnectReason() == 22) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "INVALID_RSN_IE_CAP");
} else if (getWifiDisconnectReason() == 23) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "802_1X_AUTH_FAILED");
} else if (getWifiDisconnectReason() == 24) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "CIPHER_SUITE_REJECTED");
} else if (getWifiDisconnectReason() == 200) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "BEACON_TIMEOUT");
} else if (getWifiDisconnectReason() == 201) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP Not Found");
} else if (getWifiDisconnectReason() == 202) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AUTH_FAIL");
} else if (getWifiDisconnectReason() == 203) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "ASSOC_FAIL");
} else if (getWifiDisconnectReason() == 204) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "HANDSHAKE_TIMEOUT");
} else if (getWifiDisconnectReason() == 205) {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Failed");
} else {
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Unknown Status");
}
} }
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName)); display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
@@ -1743,37 +1669,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
display->drawString(x + 1, y, String("USB")); display->drawString(x + 1, y, String("USB"));
} }
auto mode = ""; auto mode = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, true);
switch (config.lora.modem_preset) {
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
mode = "ShortS";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
mode = "ShortF";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
mode = "MedS";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
mode = "MedF";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
mode = "LongS";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
mode = "LongF";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
mode = "LongM";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
mode = "VeryL";
break;
default:
mode = "Custom";
break;
}
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode); display->drawString(x + SCREEN_WIDTH - display->getStringWidth(mode), y, mode);
if (config.display.heading_bold) if (config.display.heading_bold)
@@ -1840,23 +1736,6 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
heartbeat = !heartbeat; heartbeat = !heartbeat;
#endif #endif
} }
// adjust Brightness cycle through 1 to 254 as long as attachDuringLongPress is true
void Screen::adjustBrightness()
{
if (!useDisplay)
return;
if (brightness == 254) {
brightness = 0;
} else {
brightness++;
}
int width = brightness / (254.00 / SCREEN_WIDTH);
dispdev.drawRect(0, 30, SCREEN_WIDTH, 4);
dispdev.fillRect(0, 31, width, 2);
dispdev.display();
dispdev.setBrightness(brightness);
}
int Screen::handleStatusUpdate(const meshtastic::Status *arg) int Screen::handleStatusUpdate(const meshtastic::Status *arg)
{ {

View File

@@ -19,7 +19,6 @@ class Screen
void setup() {} void setup() {}
void setOn(bool) {} void setOn(bool) {}
void print(const char *) {} void print(const char *) {}
void adjustBrightness() {}
void doDeepSleep() {} void doDeepSleep() {}
void forceDisplay() {} void forceDisplay() {}
void startBluetoothPinScreen(uint32_t pin) {} void startBluetoothPinScreen(uint32_t pin) {}
@@ -161,7 +160,6 @@ class Screen : public concurrency::OSThread
void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); } void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); }
// Implementation to Adjust Brightness // Implementation to Adjust Brightness
void adjustBrightness();
uint8_t brightness = BRIGHTNESS_DEFAULT; uint8_t brightness = BRIGHTNESS_DEFAULT;
/// Starts showing the Bluetooth PIN screen. /// Starts showing the Bluetooth PIN screen.
@@ -363,6 +361,9 @@ class Screen : public concurrency::OSThread
/// Try to start drawing ASAP /// Try to start drawing ASAP
void setFastFramerate(); void setFastFramerate();
// Sets frame up for immediate drawing
void setFrameImmediateDraw(FrameCallback *drawFrames);
/// Called when debug screen is to be drawn, calls through to debugInfo.drawFrame. /// Called when debug screen is to be drawn, calls through to debugInfo.drawFrame.
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);

View File

@@ -1,4 +1,5 @@
#include "configuration.h" #include "configuration.h"
#include "main.h"
#ifndef TFT_BACKLIGHT_ON #ifndef TFT_BACKLIGHT_ON
#define TFT_BACKLIGHT_ON HIGH #define TFT_BACKLIGHT_ON HIGH
@@ -81,8 +82,16 @@ class LGFX : public lgfx::LGFX_Device
{ {
auto cfg = _light_instance.config(); // Gets a structure for backlight settings. auto cfg = _light_instance.config(); // Gets a structure for backlight settings.
#ifdef ST7735_BL_V03
if (heltec_version == 3) {
cfg.pin_bl = ST7735_BL_V03;
} else {
cfg.pin_bl = ST7735_BL_V05;
}
#else
cfg.pin_bl = ST7735_BL; // Pin number to which the backlight is connected cfg.pin_bl = ST7735_BL; // Pin number to which the backlight is connected
cfg.invert = true; // true to invert the brightness of the backlight #endif
cfg.invert = true; // true to invert the brightness of the backlight
// cfg.freq = 44100; // PWM frequency of backlight // cfg.freq = 44100; // PWM frequency of backlight
// cfg.pwm_channel = 1; // PWM channel number to use // cfg.pwm_channel = 1; // PWM channel number to use
@@ -364,9 +373,23 @@ void TFTDisplay::sendCommand(uint8_t com)
// handle display on/off directly // handle display on/off directly
switch (com) { switch (com) {
case DISPLAYON: { case DISPLAYON: {
#if defined(ST7735_BACKLIGHT_EN_V03) && defined(TFT_BACKLIGHT_ON)
if (heltec_version == 3) {
digitalWrite(ST7735_BACKLIGHT_EN_V03, TFT_BACKLIGHT_ON);
} else {
digitalWrite(ST7735_BACKLIGHT_EN_V05, TFT_BACKLIGHT_ON);
}
#endif
#if defined(TFT_BL) && defined(TFT_BACKLIGHT_ON) #if defined(TFT_BL) && defined(TFT_BACKLIGHT_ON)
digitalWrite(TFT_BL, TFT_BACKLIGHT_ON); digitalWrite(TFT_BL, TFT_BACKLIGHT_ON);
#endif #endif
#ifdef VTFT_CTRL_V03
if (heltec_version == 3) {
digitalWrite(VTFT_CTRL_V03, LOW);
} else {
digitalWrite(VTFT_CTRL_V05, LOW);
}
#endif
#ifdef VTFT_CTRL #ifdef VTFT_CTRL
digitalWrite(VTFT_CTRL, LOW); digitalWrite(VTFT_CTRL, LOW);
#endif #endif
@@ -376,9 +399,23 @@ void TFTDisplay::sendCommand(uint8_t com)
break; break;
} }
case DISPLAYOFF: { case DISPLAYOFF: {
#if defined(ST7735_BACKLIGHT_EN_V03) && defined(TFT_BACKLIGHT_ON)
if (heltec_version == 3) {
digitalWrite(ST7735_BACKLIGHT_EN_V03, !TFT_BACKLIGHT_ON);
} else {
digitalWrite(ST7735_BACKLIGHT_EN_V05, !TFT_BACKLIGHT_ON);
}
#endif
#if defined(TFT_BL) && defined(TFT_BACKLIGHT_ON) #if defined(TFT_BL) && defined(TFT_BACKLIGHT_ON)
digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON); digitalWrite(TFT_BL, !TFT_BACKLIGHT_ON);
#endif #endif
#ifdef VTFT_CTRL_V03
if (heltec_version == 3) {
digitalWrite(VTFT_CTRL_V03, HIGH);
} else {
digitalWrite(VTFT_CTRL_V05, HIGH);
}
#endif
#ifdef VTFT_CTRL #ifdef VTFT_CTRL
digitalWrite(VTFT_CTRL, HIGH); digitalWrite(VTFT_CTRL, HIGH);
#endif #endif
@@ -436,6 +473,16 @@ bool TFTDisplay::connect()
pinMode(TFT_BL, OUTPUT); pinMode(TFT_BL, OUTPUT);
#endif #endif
#ifdef ST7735_BACKLIGHT_EN_V03
if (heltec_version == 3) {
digitalWrite(ST7735_BACKLIGHT_EN_V03, TFT_BACKLIGHT_ON);
pinMode(ST7735_BACKLIGHT_EN_V03, OUTPUT);
} else {
digitalWrite(ST7735_BACKLIGHT_EN_V05, TFT_BACKLIGHT_ON);
pinMode(ST7735_BACKLIGHT_EN_V05, OUTPUT);
}
#endif
tft.init(); tft.init();
#if defined(M5STACK) #if defined(M5STACK)
tft.setRotation(0); tft.setRotation(0);

View File

@@ -29,6 +29,7 @@
#include "target_specific.h" #include "target_specific.h"
#include <Wire.h> #include <Wire.h>
#include <memory> #include <memory>
#include <utility>
// #include <driver/rtc_io.h> // #include <driver/rtc_io.h>
#include "mesh/eth/ethClient.h" #include "mesh/eth/ethClient.h"
@@ -122,9 +123,8 @@ uint32_t serialSinceMsec;
bool pmu_found; bool pmu_found;
// Array map of sensor types (as array index) and i2c address as value we'll find in the i2c scan // Array map of sensor types with i2c address and wire as we'll find in the i2c scan
uint8_t nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1] = { std::pair<uint8_t, TwoWire *> nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1] = {};
0}; // one is enough, missing elements will be initialized to 0 anyway.
Router *router = NULL; // Users of router don't care what sort of subclass implements that API Router *router = NULL; // Users of router don't care what sort of subclass implements that API
@@ -146,6 +146,25 @@ const char *getDeviceName()
return name; return name;
} }
#ifdef VEXT_ENABLE_V03
#include <soc/rtc.h>
static uint32_t calibrate_one(rtc_cal_sel_t cal_clk, const char *name)
{
const uint32_t cal_count = 1000;
uint32_t cali_val;
for (int i = 0; i < 5; ++i) {
cali_val = rtc_clk_cal(cal_clk, cal_count);
}
return cali_val;
}
int heltec_version = 3;
#define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk)
#endif
static int32_t ledBlinker() static int32_t ledBlinker()
{ {
static bool ledOn; static bool ledOn;
@@ -216,11 +235,63 @@ void setup()
digitalWrite(PIN_EINK_PWR_ON, HIGH); digitalWrite(PIN_EINK_PWR_ON, HIGH);
#endif #endif
#ifdef VEXT_ENABLE #ifdef ST7735_BL_V03 // Heltec Wireless Tracker PCB Change Detect/Hack
rtc_clk_32k_enable(true);
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
if (CALIBRATE_ONE(RTC_CAL_32K_XTAL) != 0) {
rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL);
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
CALIBRATE_ONE(RTC_CAL_32K_XTAL);
}
if (rtc_clk_slow_freq_get() != RTC_SLOW_FREQ_32K_XTAL) {
heltec_version = 3;
} else {
heltec_version = 5;
}
#endif
#if defined(VEXT_ENABLE_V03)
if (heltec_version == 3) {
pinMode(VEXT_ENABLE_V03, OUTPUT);
digitalWrite(VEXT_ENABLE_V03, 0); // turn on the display power
LOG_DEBUG("HELTEC Detect Tracker V1.0\n");
} else {
pinMode(VEXT_ENABLE_V05, OUTPUT);
digitalWrite(VEXT_ENABLE_V05, 1); // turn on the display power
LOG_DEBUG("HELTEC Detect Tracker V1.1\n");
}
#elif defined(VEXT_ENABLE)
pinMode(VEXT_ENABLE, OUTPUT); pinMode(VEXT_ENABLE, OUTPUT);
digitalWrite(VEXT_ENABLE, 0); // turn on the display power digitalWrite(VEXT_ENABLE, 0); // turn on the display power
#endif #endif
#if defined(VGNSS_CTRL_V03)
if (heltec_version == 3) {
pinMode(VGNSS_CTRL_V03, OUTPUT);
digitalWrite(VGNSS_CTRL_V03, LOW);
} else {
pinMode(VGNSS_CTRL_V05, OUTPUT);
digitalWrite(VGNSS_CTRL_V05, LOW);
}
#endif
#if defined(VTFT_CTRL_V03)
if (heltec_version == 3) {
pinMode(VTFT_CTRL_V03, OUTPUT);
digitalWrite(VTFT_CTRL_V03, LOW);
} else {
pinMode(VTFT_CTRL_V05, OUTPUT);
digitalWrite(VTFT_CTRL_V05, LOW);
}
#endif
#if defined(VGNSS_CTRL)
pinMode(VGNSS_CTRL, OUTPUT);
digitalWrite(VGNSS_CTRL, LOW);
#endif
#if defined(VTFT_CTRL) #if defined(VTFT_CTRL)
pinMode(VTFT_CTRL, OUTPUT); pinMode(VTFT_CTRL, OUTPUT);
digitalWrite(VTFT_CTRL, LOW); digitalWrite(VTFT_CTRL, LOW);
@@ -420,7 +491,8 @@ void setup()
{ \ { \
auto found = i2cScanner->find(SCANNER_T); \ auto found = i2cScanner->find(SCANNER_T); \
if (found.type != ScanI2C::DeviceType::NONE) { \ if (found.type != ScanI2C::DeviceType::NONE) { \
nodeTelemetrySensorsMap[PB_T] = found.address.address; \ nodeTelemetrySensorsMap[PB_T].first = found.address.address; \
nodeTelemetrySensorsMap[PB_T].second = i2cScanner->fetchI2CBus(found.address); \
LOG_DEBUG("found i2c sensor %s\n", STRING(PB_T)); \ LOG_DEBUG("found i2c sensor %s\n", STRING(PB_T)); \
} \ } \
} }

View File

@@ -64,6 +64,8 @@ extern uint32_t shutdownAtMsec;
extern uint32_t serialSinceMsec; extern uint32_t serialSinceMsec;
extern int heltec_version;
// If a thread does something that might need for it to be rescheduled ASAP it can set this flag // If a thread does something that might need for it to be rescheduled ASAP it can set this flag
// This will suppress the current delay and instead try to run ASAP. // This will suppress the current delay and instead try to run ASAP.
extern bool runASAP; extern bool runASAP;

View File

@@ -1,5 +1,6 @@
#include "Channels.h" #include "Channels.h"
#include "CryptoEngine.h" #include "CryptoEngine.h"
#include "DisplayFormatters.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "configuration.h" #include "configuration.h"
@@ -239,38 +240,9 @@ const char *Channels::getName(size_t chIndex)
const char *channelName = channelSettings.name; const char *channelName = channelSettings.name;
if (!*channelName) { // emptystring if (!*channelName) { // emptystring
// Per mesh.proto spec, if bandwidth is specified we must ignore modemPreset enum, we assume that in that case // Per mesh.proto spec, if bandwidth is specified we must ignore modemPreset enum, we assume that in that case
// the app fucked up and forgot to set channelSettings.name // the app effed up and forgot to set channelSettings.name
if (config.lora.use_preset) { if (config.lora.use_preset) {
switch (config.lora.modem_preset) { channelName = DisplayFormatters::getModemPresetDisplayName(config.lora.modem_preset, false);
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_SLOW:
channelName = "ShortSlow";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_SHORT_FAST:
channelName = "ShortFast";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_SLOW:
channelName = "MediumSlow";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_MEDIUM_FAST:
channelName = "MediumFast";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_SLOW:
channelName = "LongSlow";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_FAST:
channelName = "LongFast";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_LONG_MODERATE:
channelName = "LongMod";
break;
case meshtastic_Config_LoRaConfig_ModemPreset_VERY_LONG_SLOW:
channelName = "VLongSlow";
break;
default:
channelName = "Invalid";
break;
}
} else { } else {
channelName = "Custom"; channelName = "Custom";
} }

View File

@@ -267,14 +267,22 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
void MeshService::sendToPhone(meshtastic_MeshPacket *p) void MeshService::sendToPhone(meshtastic_MeshPacket *p)
{ {
perhapsDecode(p);
if (toPhoneQueue.numFree() == 0) { if (toPhoneQueue.numFree() == 0) {
LOG_WARN("ToPhone queue is full, discarding oldest\n"); if (p->decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP ||
meshtastic_MeshPacket *d = toPhoneQueue.dequeuePtr(0); p->decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP) {
if (d) LOG_WARN("ToPhone queue is full, discarding oldest\n");
releaseToPool(d); meshtastic_MeshPacket *d = toPhoneQueue.dequeuePtr(0);
if (d)
releaseToPool(d);
} else {
LOG_WARN("ToPhone queue is full, dropping packet.\n");
releaseToPool(p);
return;
}
} }
perhapsDecode(p);
assert(toPhoneQueue.enqueue(p, 0)); assert(toPhoneQueue.enqueue(p, 0));
fromNum++; fromNum++;
} }
@@ -335,7 +343,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
// Used fixed position if configured regalrdless of GPS lock // Used fixed position if configured regalrdless of GPS lock
if (config.position.fixed_position) { if (config.position.fixed_position) {
LOG_WARN("Using fixed position\n"); LOG_WARN("Using fixed position\n");
pos = ConvertToPosition(node->position); pos = TypeConversions::ConvertToPosition(node->position);
} }
// Add a fresh timestamp // Add a fresh timestamp

View File

@@ -254,6 +254,8 @@ void NodeDB::installDefaultModuleConfig()
strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(moduleConfig.mqtt.address)); strncpy(moduleConfig.mqtt.address, default_mqtt_address, sizeof(moduleConfig.mqtt.address));
strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(moduleConfig.mqtt.username)); strncpy(moduleConfig.mqtt.username, default_mqtt_username, sizeof(moduleConfig.mqtt.username));
strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(moduleConfig.mqtt.password)); strncpy(moduleConfig.mqtt.password, default_mqtt_password, sizeof(moduleConfig.mqtt.password));
strncpy(moduleConfig.mqtt.root, default_mqtt_root, sizeof(moduleConfig.mqtt.root));
moduleConfig.mqtt.encryption_enabled = true;
moduleConfig.has_neighbor_info = true; moduleConfig.has_neighbor_info = true;
moduleConfig.neighbor_info.enabled = false; moduleConfig.neighbor_info.enabled = false;
@@ -285,6 +287,15 @@ void NodeDB::installRoleDefaults(meshtastic_Config_DeviceConfig_Role role)
} else if (role == meshtastic_Config_DeviceConfig_Role_SENSOR) { } else if (role == meshtastic_Config_DeviceConfig_Role_SENSOR) {
moduleConfig.telemetry.environment_measurement_enabled = true; moduleConfig.telemetry.environment_measurement_enabled = true;
moduleConfig.telemetry.environment_update_interval = 300; moduleConfig.telemetry.environment_update_interval = 300;
} else if (role == meshtastic_Config_DeviceConfig_Role_TAK) {
config.device.node_info_broadcast_secs = ONE_DAY;
config.position.position_broadcast_smart_enabled = false;
config.position.position_broadcast_secs = ONE_DAY;
// Remove Altitude MSL from flags since CoTs use HAE (height above ellipsoid)
config.position.position_flags =
(meshtastic_Config_PositionConfig_PositionFlags_ALTITUDE | meshtastic_Config_PositionConfig_PositionFlags_SPEED |
meshtastic_Config_PositionConfig_PositionFlags_HEADING | meshtastic_Config_PositionConfig_PositionFlags_DOP);
moduleConfig.telemetry.device_update_interval = ONE_DAY;
} }
} }
@@ -687,8 +698,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
LOG_INFO("updatePosition LOCAL pos@%x, time=%u, latI=%d, lonI=%d, alt=%d\n", p.timestamp, p.time, p.latitude_i, LOG_INFO("updatePosition LOCAL pos@%x, time=%u, latI=%d, lonI=%d, alt=%d\n", p.timestamp, p.time, p.latitude_i,
p.longitude_i, p.altitude); p.longitude_i, p.altitude);
info->position = ConvertToPositionLite(p); setLocalPosition(p);
localPosition = p; info->position = TypeConversions::ConvertToPositionLite(p);
} else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) { } else if ((p.time > 0) && !p.latitude_i && !p.longitude_i && !p.timestamp && !p.location_source) {
// FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO // FIXME SPECIAL TIME SETTING PACKET FROM EUD TO RADIO
// (stop-gap fix for issue #900) // (stop-gap fix for issue #900)
@@ -706,7 +717,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
uint32_t tmp_time = info->position.time; uint32_t tmp_time = info->position.time;
// Next, update atomically // Next, update atomically
info->position = ConvertToPositionLite(p); info->position = TypeConversions::ConvertToPositionLite(p);
// Last, restore any fields that may have been overwritten // Last, restore any fields that may have been overwritten
if (!info->position.time) if (!info->position.time)

View File

@@ -131,6 +131,13 @@ class NodeDB
meshtastic_NodeInfoLite *getMeshNode(NodeNum n); meshtastic_NodeInfoLite *getMeshNode(NodeNum n);
size_t getNumMeshNodes() { return *numMeshNodes; } size_t getNumMeshNodes() { return *numMeshNodes; }
void setLocalPosition(meshtastic_Position position)
{
LOG_DEBUG("Setting local position: latitude=%i, longitude=%i, time=%i\n", position.latitude_i, position.longitude_i,
position.time);
localPosition = position;
}
private: private:
/// Find a node in our DB, create an empty NodeInfoLite if missing /// Find a node in our DB, create an empty NodeInfoLite if missing
meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n); meshtastic_NodeInfoLite *getOrCreateMeshNode(NodeNum n);
@@ -192,11 +199,12 @@ extern NodeDB nodeDB;
#define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep #define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep
#define default_ls_secs IF_ROUTER(ONE_DAY, 5 * 60) #define default_ls_secs IF_ROUTER(ONE_DAY, 5 * 60)
#define default_min_wake_secs 10 #define default_min_wake_secs 10
#define default_screen_on_secs 60 * 10 #define default_screen_on_secs IF_ROUTER(1, 60 * 10)
#define default_mqtt_address "mqtt.meshtastic.org" #define default_mqtt_address "mqtt.meshtastic.org"
#define default_mqtt_username "meshdev" #define default_mqtt_username "meshdev"
#define default_mqtt_password "large4cats" #define default_mqtt_password "large4cats"
#define default_mqtt_root "msh"
inline uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval) inline uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval)
{ {

View File

@@ -405,7 +405,7 @@ bool PhoneAPI::available()
if (nodeInfoForPhone.num == 0) { if (nodeInfoForPhone.num == 0) {
auto nextNode = nodeDB.readNextMeshNode(readIndex); auto nextNode = nodeDB.readNextMeshNode(readIndex);
if (nextNode) { if (nextNode) {
nodeInfoForPhone = ConvertToNodeInfo(nextNode); nodeInfoForPhone = TypeConversions::ConvertToNodeInfo(nextNode);
} }
} }
return true; // Always say we have something, because we might need to advance our state machine return true; // Always say we have something, because we might need to advance our state machine

View File

@@ -249,29 +249,12 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it
bool shouldActuallyEncrypt = true;
if (moduleConfig.mqtt.enabled) { if (moduleConfig.mqtt.enabled) {
// check if we should send decrypted packets to mqtt
// truth table: LOG_INFO("Should encrypt MQTT?: %d\n", moduleConfig.mqtt.encryption_enabled);
/* mqtt_server mqtt_encryption_enabled should_encrypt
* not set 0 1
* not set 1 1
* set 0 0
* set 1 1
*
* => so we only decrypt mqtt if they have a custom mqtt server AND mqtt_encryption_enabled is FALSE
*/
if (*moduleConfig.mqtt.address && !moduleConfig.mqtt.encryption_enabled) {
shouldActuallyEncrypt = false;
}
LOG_INFO("Should encrypt MQTT?: %d\n", shouldActuallyEncrypt);
// the packet is currently in a decrypted state. send it now if they want decrypted packets // the packet is currently in a decrypted state. send it now if they want decrypted packets
if (mqtt && !shouldActuallyEncrypt) if (mqtt && !moduleConfig.mqtt.encryption_enabled)
mqtt->onSend(*p, chIndex); mqtt->onSend(*p, chIndex);
} }
@@ -284,7 +267,7 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
if (moduleConfig.mqtt.enabled) { if (moduleConfig.mqtt.enabled) {
// the packet is now encrypted. // the packet is now encrypted.
// check if we should send encrypted packets to mqtt // check if we should send encrypted packets to mqtt
if (mqtt && shouldActuallyEncrypt) if (mqtt && moduleConfig.mqtt.encryption_enabled)
mqtt->onSend(*p, chIndex); mqtt->onSend(*p, chIndex);
} }
} }

View File

@@ -0,0 +1,55 @@
#include "TypeConversions.h"
#include "mesh/generated/meshtastic/deviceonly.pb.h"
#include "mesh/generated/meshtastic/mesh.pb.h"
meshtastic_NodeInfo TypeConversions::ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite)
{
meshtastic_NodeInfo info = meshtastic_NodeInfo_init_default;
info.num = lite->num;
info.snr = lite->snr;
info.last_heard = lite->last_heard;
info.channel = lite->channel;
if (lite->has_position) {
info.has_position = true;
info.position.latitude_i = lite->position.latitude_i;
info.position.longitude_i = lite->position.longitude_i;
info.position.altitude = lite->position.altitude;
info.position.location_source = lite->position.location_source;
info.position.time = lite->position.time;
}
if (lite->has_user) {
info.has_user = true;
info.user = lite->user;
}
if (lite->has_device_metrics) {
info.has_device_metrics = true;
info.device_metrics = lite->device_metrics;
}
return info;
}
meshtastic_PositionLite TypeConversions::ConvertToPositionLite(meshtastic_Position position)
{
meshtastic_PositionLite lite = meshtastic_PositionLite_init_default;
lite.latitude_i = position.latitude_i;
lite.longitude_i = position.longitude_i;
lite.altitude = position.altitude;
lite.location_source = position.location_source;
lite.time = position.time;
return lite;
}
meshtastic_Position TypeConversions::ConvertToPosition(meshtastic_PositionLite lite)
{
meshtastic_Position position = meshtastic_Position_init_default;
position.latitude_i = lite.latitude_i;
position.longitude_i = lite.longitude_i;
position.altitude = lite.altitude;
position.location_source = lite.location_source;
position.time = lite.time;
return position;
}

View File

@@ -1,54 +1,13 @@
#include "mesh/generated/meshtastic/deviceonly.pb.h" #include "mesh/generated/meshtastic/deviceonly.pb.h"
#include "mesh/generated/meshtastic/mesh.pb.h" #include "mesh/generated/meshtastic/mesh.pb.h"
inline static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite) #pragma once
#include "NodeDB.h"
class TypeConversions
{ {
meshtastic_NodeInfo info = meshtastic_NodeInfo_init_default; public:
static meshtastic_NodeInfo ConvertToNodeInfo(const meshtastic_NodeInfoLite *lite);
info.num = lite->num; static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position);
info.snr = lite->snr; static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite);
info.last_heard = lite->last_heard; };
info.channel = lite->channel;
if (lite->has_position) {
info.has_position = true;
info.position.latitude_i = lite->position.latitude_i;
info.position.longitude_i = lite->position.longitude_i;
info.position.altitude = lite->position.altitude;
info.position.location_source = lite->position.location_source;
info.position.time = lite->position.time;
}
if (lite->has_user) {
info.has_user = true;
info.user = lite->user;
}
if (lite->has_device_metrics) {
info.has_device_metrics = true;
info.device_metrics = lite->device_metrics;
}
return info;
}
inline static meshtastic_PositionLite ConvertToPositionLite(meshtastic_Position position)
{
meshtastic_PositionLite lite = meshtastic_PositionLite_init_default;
lite.latitude_i = position.latitude_i;
lite.longitude_i = position.longitude_i;
lite.altitude = position.altitude;
lite.location_source = position.location_source;
lite.time = position.time;
return lite;
}
inline static meshtastic_Position ConvertToPosition(meshtastic_PositionLite lite)
{
meshtastic_Position position = meshtastic_Position_init_default;
position.latitude_i = lite.latitude_i;
position.longitude_i = lite.longitude_i;
position.altitude = lite.altitude;
position.location_source = lite.location_source;
position.time = lite.time;
return position;
}

View File

@@ -30,11 +30,20 @@ typedef enum _meshtastic_Config_DeviceConfig_Role {
or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate. */ or any other packet type. They will simply rebroadcast any mesh packets on the same frequency, channel num, spread factor, and coding rate. */
meshtastic_Config_DeviceConfig_Role_REPEATER = 4, meshtastic_Config_DeviceConfig_Role_REPEATER = 4,
/* Tracker device role /* Tracker device role
Position Mesh packets will be prioritized higher and sent more frequently by default. */ Position Mesh packets will be prioritized higher and sent more frequently by default.
When used in conjunction with power.is_power_saving = true, nodes will wake up,
send position, and then sleep for position.position_broadcast_secs seconds. */
meshtastic_Config_DeviceConfig_Role_TRACKER = 5, meshtastic_Config_DeviceConfig_Role_TRACKER = 5,
/* Sensor device role /* Sensor device role
Telemetry Mesh packets will be prioritized higher and sent more frequently by default. */ Telemetry Mesh packets will be prioritized higher and sent more frequently by default.
meshtastic_Config_DeviceConfig_Role_SENSOR = 6 When used in conjunction with power.is_power_saving = true, nodes will wake up,
send environment telemetry, and then sleep for telemetry.environment_update_interval seconds. */
meshtastic_Config_DeviceConfig_Role_SENSOR = 6,
/* TAK device role
Used for nodes dedicated for connection to an ATAK EUD.
Turns off many of the routine broadcasts to favor CoT packet stream
from the Meshtastic ATAK plugin -> IMeshService -> Node */
meshtastic_Config_DeviceConfig_Role_TAK = 7
} meshtastic_Config_DeviceConfig_Role; } meshtastic_Config_DeviceConfig_Role;
/* Defines the device's behavior for how messages are rebroadcast */ /* Defines the device's behavior for how messages are rebroadcast */
@@ -470,8 +479,8 @@ extern "C" {
/* Helper constants for enums */ /* Helper constants for enums */
#define _meshtastic_Config_DeviceConfig_Role_MIN meshtastic_Config_DeviceConfig_Role_CLIENT #define _meshtastic_Config_DeviceConfig_Role_MIN meshtastic_Config_DeviceConfig_Role_CLIENT
#define _meshtastic_Config_DeviceConfig_Role_MAX meshtastic_Config_DeviceConfig_Role_SENSOR #define _meshtastic_Config_DeviceConfig_Role_MAX meshtastic_Config_DeviceConfig_Role_TAK
#define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_SENSOR+1)) #define _meshtastic_Config_DeviceConfig_Role_ARRAYSIZE ((meshtastic_Config_DeviceConfig_Role)(meshtastic_Config_DeviceConfig_Role_TAK+1))
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN meshtastic_Config_DeviceConfig_RebroadcastMode_ALL #define _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN meshtastic_Config_DeviceConfig_RebroadcastMode_ALL
#define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY #define _meshtastic_Config_DeviceConfig_RebroadcastMode_MAX meshtastic_Config_DeviceConfig_RebroadcastMode_LOCAL_ONLY

View File

@@ -113,6 +113,8 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_PICOMPUTER_S3 = 52, meshtastic_HardwareModel_PICOMPUTER_S3 = 52,
/* Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa */ /* Heltec HT-CT62 with ESP32-C3 CPU and SX1262 LoRa */
meshtastic_HardwareModel_HELTEC_HT62 = 53, meshtastic_HardwareModel_HELTEC_HT62 = 53,
/* EBYTE SPI LoRa module and ESP32-S3 */
meshtastic_HardwareModel_EBYTE_ESP32_S3 = 54,
/* ------------------------------------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------------------------------------
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. 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.
------------------------------------------------------------------------------------------------------------------------------------------ */ ------------------------------------------------------------------------------------------------------------------------------------------ */

View File

@@ -676,7 +676,7 @@ void AdminModule::handleSetHamMode(const meshtastic_HamParameters &p)
channels.onConfigChanged(); channels.onConfigChanged();
service.reloadOwner(false); service.reloadOwner(false);
service.reloadConfig(SEGMENT_CONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS); saveChanges(SEGMENT_CONFIG | SEGMENT_DEVICESTATE | SEGMENT_CHANNELS);
} }
AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_APP, &meshtastic_AdminMessage_msg) AdminModule::AdminModule() : ProtobufModule("Admin", meshtastic_PortNum_ADMIN_APP, &meshtastic_AdminMessage_msg)

View File

@@ -88,7 +88,7 @@ void setupModules()
#endif #endif
#if HAS_SENSOR #if HAS_SENSOR
new EnvironmentTelemetryModule(); new EnvironmentTelemetryModule();
if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I] > 0) { if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_PMSA003I].first > 0) {
new AirQualityTelemetryModule(); new AirQualityTelemetryModule();
} }
#endif #endif

View File

@@ -40,7 +40,9 @@ void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies, uint8_t cha
meshtastic_MeshPacket *p = allocReply(); meshtastic_MeshPacket *p = allocReply();
if (p) { // Check whether we didn't ignore it if (p) { // Check whether we didn't ignore it
p->to = dest; p->to = dest;
p->decoded.want_response = wantReplies; p->decoded.want_response = (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
wantReplies;
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND; p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
if (channel > 0) { if (channel > 0) {
LOG_DEBUG("sending ourNodeInfo to channel %d\n", channel); LOG_DEBUG("sending ourNodeInfo to channel %d\n", channel);

View File

@@ -1,4 +1,5 @@
#include "PositionModule.h" #include "PositionModule.h"
#include "GPS.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "RTC.h" #include "RTC.h"
@@ -7,6 +8,8 @@
#include "airtime.h" #include "airtime.h"
#include "configuration.h" #include "configuration.h"
#include "gps/GeoCoord.h" #include "gps/GeoCoord.h"
#include "sleep.h"
#include "target_specific.h"
PositionModule *positionModule; PositionModule *positionModule;
@@ -14,8 +17,25 @@ PositionModule::PositionModule()
: ProtobufModule("position", meshtastic_PortNum_POSITION_APP, &meshtastic_Position_msg), : ProtobufModule("position", meshtastic_PortNum_POSITION_APP, &meshtastic_Position_msg),
concurrency::OSThread("PositionModule") concurrency::OSThread("PositionModule")
{ {
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others 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) if (config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER)
setIntervalFromNow(60 * 1000);
// Power saving trackers should clear their position on startup to avoid waking up and sending a stale position
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER && config.power.is_power_saving) {
clearPosition();
}
}
void PositionModule::clearPosition()
{
LOG_DEBUG("Clearing position on startup for sleepy tracker (ー。ー) zzz\n");
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
node->position.latitude_i = 0;
node->position.longitude_i = 0;
node->position.altitude = 0;
node->position.time = 0;
nodeDB.setLocalPosition(meshtastic_Position_init_default);
} }
bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Position *pptr) bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Position *pptr)
@@ -27,10 +47,11 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
// FIXME this can in fact happen with packets sent from EUD (src=RX_SRC_USER) // FIXME this can in fact happen with packets sent from EUD (src=RX_SRC_USER)
// to set fixed location, EUD-GPS location or just the time (see also issue #900) // to set fixed location, EUD-GPS location or just the time (see also issue #900)
bool isLocal = false;
if (nodeDB.getNodeNum() == getFrom(&mp)) { if (nodeDB.getNodeNum() == getFrom(&mp)) {
LOG_DEBUG("Incoming update from MYSELF\n"); LOG_DEBUG("Incoming update from MYSELF\n");
// LOG_DEBUG("Ignored an incoming update from MYSELF\n"); isLocal = true;
// return false; nodeDB.setLocalPosition(p);
} }
// Log packet size and data fields // Log packet size and data fields
@@ -47,7 +68,8 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
tv.tv_sec = secs; tv.tv_sec = secs;
tv.tv_usec = 0; tv.tv_usec = 0;
perhapsSetRTC(RTCQualityFromNet, &tv); // Set from phone RTC Quality to RTCQualityNTP since it should be approximately so
perhapsSetRTC(isLocal ? RTCQualityNTP : RTCQualityFromNet, &tv);
} }
nodeDB.updatePosition(getFrom(&mp), p); nodeDB.updatePosition(getFrom(&mp), p);
@@ -77,8 +99,9 @@ meshtastic_MeshPacket *PositionModule::allocReply()
// Populate a Position struct with ONLY the requested fields // Populate a Position struct with ONLY the requested fields
meshtastic_Position p = meshtastic_Position_init_default; // Start with an empty structure meshtastic_Position p = meshtastic_Position_init_default; // Start with an empty structure
// if localPosition is totally empty, put our last saved position (lite) in there
if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) { if (localPosition.latitude_i == 0 && localPosition.longitude_i == 0) {
localPosition = ConvertToPosition(node->position); nodeDB.setLocalPosition(TypeConversions::ConvertToPosition(node->position));
} }
localPosition.seq_number++; localPosition.seq_number++;
@@ -148,7 +171,7 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha
} }
p->to = dest; p->to = dest;
p->decoded.want_response = wantReplies; p->decoded.want_response = config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ? false : wantReplies;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER) if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE; p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else else
@@ -159,70 +182,83 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies, uint8_t cha
p->channel = channel; p->channel = channel;
service.sendToMesh(p, RX_SRC_LOCAL, true); service.sendToMesh(p, RX_SRC_LOCAL, true);
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER && config.power.is_power_saving) {
LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n");
sleepOnNextExecution = true;
setIntervalFromNow(5000);
}
} }
#define RUNONCE_INTERVAL 5000;
int32_t PositionModule::runOnce() int32_t PositionModule::runOnce()
{ {
if (sleepOnNextExecution == true) {
sleepOnNextExecution = false;
uint32_t nightyNightMs = getConfiguredOrDefaultMs(config.position.position_broadcast_secs);
LOG_DEBUG("Sleeping for %ims, then awaking to send position again.\n", nightyNightMs);
doDeepSleep(nightyNightMs, false);
}
meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum()); meshtastic_NodeInfoLite *node = nodeDB.getMeshNode(nodeDB.getNodeNum());
// We limit our GPS broadcasts to a max rate // We limit our GPS broadcasts to a max rate
uint32_t now = millis(); uint32_t now = millis();
uint32_t intervalMs = getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs); uint32_t intervalMs = getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs);
uint32_t msSinceLastSend = now - lastGpsSend; uint32_t msSinceLastSend = now - lastGpsSend;
// Only send packets if the channel util. is less than 25% utilized or we're a tracker with less than 40% utilized.
if (!airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER)) {
return RUNONCE_INTERVAL;
}
if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) { if (lastGpsSend == 0 || msSinceLastSend >= intervalMs) {
// Only send packets if the channel is less than 40% utilized. if (hasValidPosition(node)) {
if (airTime->isTxAllowedChannelUtil()) { lastGpsSend = now;
if (hasValidPosition(node)) {
lastGpsSend = now;
lastGpsLatitude = node->position.latitude_i; lastGpsLatitude = node->position.latitude_i;
lastGpsLongitude = node->position.longitude_i; lastGpsLongitude = node->position.longitude_i;
// If we changed channels, ask everyone else for their latest info // If we changed channels, ask everyone else for their latest info
bool requestReplies = currentGeneration != radioGeneration;
currentGeneration = radioGeneration;
LOG_INFO("Sending pos@%x:6 to mesh (wantReplies=%d)\n", localPosition.timestamp, requestReplies);
sendOurPosition(NODENUM_BROADCAST, requestReplies);
}
} else if (config.position.position_broadcast_smart_enabled) {
const meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position
if (hasValidPosition(node2)) {
// 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);
auto smartPosition = getDistanceTraveledSinceLastSend(node->position);
if (smartPosition.hasTraveledOverThreshold && msSinceLastSend >= minimumTimeThreshold) {
bool requestReplies = currentGeneration != radioGeneration; bool requestReplies = currentGeneration != radioGeneration;
currentGeneration = radioGeneration; currentGeneration = radioGeneration;
LOG_INFO("Sending pos@%x:6 to mesh (wantReplies=%d)\n", localPosition.timestamp, requestReplies); LOG_INFO("Sending smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, "
"minTimeInterval=%ims)\n",
localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold,
msSinceLastSend, minimumTimeThreshold);
sendOurPosition(NODENUM_BROADCAST, requestReplies); sendOurPosition(NODENUM_BROADCAST, requestReplies);
}
}
} else if (config.position.position_broadcast_smart_enabled) {
// Only send packets if the channel is less than 25% utilized or we're a tracker.
if (airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_TRACKER)) {
const meshtastic_NodeInfoLite *node2 = service.refreshLocalMeshNode(); // should guarantee there is now a position
if (hasValidPosition(node2)) { // Set the current coords as our last ones, after we've compared distance with current and decided to send
// The minimum time (in seconds) that would pass before we are able to send a new position packet. lastGpsLatitude = node->position.latitude_i;
const uint32_t minimumTimeThreshold = lastGpsLongitude = node->position.longitude_i;
getConfiguredOrDefaultMs(config.position.broadcast_smart_minimum_interval_secs, 30);
auto smartPosition = getDistanceTraveledSinceLastSend(node->position); /* Update lastGpsSend to now. This means if the device is stationary, then
getPref_position_broadcast_secs will still apply.
if (smartPosition.hasTraveledOverThreshold && msSinceLastSend >= minimumTimeThreshold) { */
bool requestReplies = currentGeneration != radioGeneration; lastGpsSend = now;
currentGeneration = radioGeneration;
LOG_INFO("Sending smart pos@%x:6 to mesh (distanceTraveled=%fm, minDistanceThreshold=%im, timeElapsed=%ims, "
"minTimeInterval=%ims)\n",
localPosition.timestamp, smartPosition.distanceTraveled, smartPosition.distanceThreshold,
msSinceLastSend, minimumTimeThreshold);
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;
}
} }
} }
} }
return 5000; // to save power only wake for our callback occasionally return RUNONCE_INTERVAL; // to save power only wake for our callback occasionally
} }
struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition) struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition)
@@ -234,6 +270,23 @@ struct SmartPosition PositionModule::getDistanceTraveledSinceLastSend(meshtastic
float distanceTraveledSinceLastSend = GeoCoord::latLongToMeter( float distanceTraveledSinceLastSend = GeoCoord::latLongToMeter(
lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7, currentPosition.latitude_i * 1e-7, currentPosition.longitude_i * 1e-7); lastGpsLatitude * 1e-7, lastGpsLongitude * 1e-7, currentPosition.latitude_i * 1e-7, currentPosition.longitude_i * 1e-7);
#ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("--------LAST POSITION------------------------------------\n");
LOG_DEBUG("lastGpsLatitude=%i, lastGpsLatitude=%i\n", lastGpsLatitude, lastGpsLongitude);
LOG_DEBUG("--------CURRENT POSITION---------------------------------\n");
LOG_DEBUG("currentPosition.latitude_i=%i, currentPosition.longitude_i=%i\n", lastGpsLatitude, lastGpsLongitude);
LOG_DEBUG("--------SMART POSITION-----------------------------------\n");
LOG_DEBUG("hasTraveledOverThreshold=%i, distanceTraveled=%d, distanceThreshold=% u\n",
abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold, abs(distanceTraveledSinceLastSend),
distanceTravelThreshold);
if (abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold) {
LOG_DEBUG("\n\n\nSMART SEEEEEEEEENDING\n\n\n");
}
#endif
return SmartPosition{.distanceTraveled = abs(distanceTraveledSinceLastSend), return SmartPosition{.distanceTraveled = abs(distanceTraveledSinceLastSend),
.distanceThreshold = distanceTravelThreshold, .distanceThreshold = distanceTravelThreshold,
.hasTraveledOverThreshold = abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold}; .hasTraveledOverThreshold = abs(distanceTraveledSinceLastSend) >= distanceTravelThreshold};

View File

@@ -49,6 +49,9 @@ class PositionModule : public ProtobufModule<meshtastic_Position>, private concu
private: private:
struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition); struct SmartPosition getDistanceTraveledSinceLastSend(meshtastic_PositionLite currentPosition);
/** Only used in power saving trackers for now */
void clearPosition();
}; };
struct SmartPosition { struct SmartPosition {
@@ -57,4 +60,4 @@ struct SmartPosition {
bool hasTraveledOverThreshold; bool hasTraveledOverThreshold;
}; };
extern PositionModule *positionModule; extern PositionModule *positionModule;

View File

@@ -8,6 +8,8 @@
#include "configuration.h" #include "configuration.h"
#include "main.h" #include "main.h"
#include "power.h" #include "power.h"
#include "sleep.h"
#include "target_specific.h"
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
#include <OLEDDisplayUi.h> #include <OLEDDisplayUi.h>
@@ -51,6 +53,13 @@ SHT31Sensor sht31Sensor;
int32_t EnvironmentTelemetryModule::runOnce() int32_t EnvironmentTelemetryModule::runOnce()
{ {
if (sleepOnNextExecution == true) {
sleepOnNextExecution = false;
uint32_t nightyNightMs = getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval);
LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs);
doDeepSleep(nightyNightMs, true);
}
uint32_t result = UINT32_MAX; uint32_t result = UINT32_MAX;
/* /*
Uncomment the preferences below if you want to use the module Uncomment the preferences below if you want to use the module
@@ -266,6 +275,12 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
} else { } else {
LOG_INFO("Sending packet to mesh\n"); LOG_INFO("Sending packet to mesh\n");
service.sendToMesh(p, RX_SRC_LOCAL, true); service.sendToMesh(p, RX_SRC_LOCAL, true);
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR && config.power.is_power_saving) {
LOG_DEBUG("Starting next execution in 5 seconds and then going to sleep.\n");
sleepOnNextExecution = true;
setIntervalFromNow(5000);
}
} }
} }
return valid; return valid;

View File

@@ -13,7 +13,7 @@ int32_t BME280Sensor::runOnce()
if (!hasSensor()) { if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
} }
status = bme280.begin(nodeTelemetrySensorsMap[sensorType]); status = bme280.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
bme280.setSampling(Adafruit_BME280::MODE_FORCED, bme280.setSampling(Adafruit_BME280::MODE_FORCED,
Adafruit_BME280::SAMPLING_X1, // Temp. oversampling Adafruit_BME280::SAMPLING_X1, // Temp. oversampling

View File

@@ -2,7 +2,7 @@
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#include <Adafruit_BME280.h> #include <Adafruit_BME280.h>
class BME280Sensor : virtual public TelemetrySensor class BME280Sensor : public TelemetrySensor
{ {
private: private:
Adafruit_BME280 bme280; Adafruit_BME280 bme280;

View File

@@ -20,7 +20,7 @@ int32_t BME680Sensor::runOnce()
if (!hasSensor()) { if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
} }
if (!bme680.begin(nodeTelemetrySensorsMap[sensorType], Wire)) if (!bme680.begin(nodeTelemetrySensorsMap[sensorType].first, *nodeTelemetrySensorsMap[sensorType].second))
checkStatus("begin"); checkStatus("begin");
if (bme680.status == BSEC_OK) { if (bme680.status == BSEC_OK) {

View File

@@ -6,7 +6,7 @@
#include "bme680_iaq_33v_3s_4d/bsec_iaq.h" #include "bme680_iaq_33v_3s_4d/bsec_iaq.h"
class BME680Sensor : virtual public TelemetrySensor class BME680Sensor : public TelemetrySensor
{ {
private: private:
Bsec2 bme680; Bsec2 bme680;

View File

@@ -13,7 +13,8 @@ int32_t BMP280Sensor::runOnce()
if (!hasSensor()) { if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
} }
status = bmp280.begin(nodeTelemetrySensorsMap[sensorType]); bmp280 = Adafruit_BMP280(nodeTelemetrySensorsMap[sensorType].second);
status = bmp280.begin(nodeTelemetrySensorsMap[sensorType].first);
bmp280.setSampling(Adafruit_BMP280::MODE_FORCED, bmp280.setSampling(Adafruit_BMP280::MODE_FORCED,
Adafruit_BMP280::SAMPLING_X1, // Temp. oversampling Adafruit_BMP280::SAMPLING_X1, // Temp. oversampling

View File

@@ -2,7 +2,7 @@
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#include <Adafruit_BMP280.h> #include <Adafruit_BMP280.h>
class BMP280Sensor : virtual public TelemetrySensor class BMP280Sensor : public TelemetrySensor
{ {
private: private:
Adafruit_BMP280 bmp280; Adafruit_BMP280 bmp280;

View File

@@ -13,8 +13,8 @@ int32_t INA219Sensor::runOnce()
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
} }
if (!ina219.success()) { if (!ina219.success()) {
ina219 = Adafruit_INA219(nodeTelemetrySensorsMap[sensorType]); ina219 = Adafruit_INA219(nodeTelemetrySensorsMap[sensorType].first);
status = ina219.begin(); status = ina219.begin(nodeTelemetrySensorsMap[sensorType].second);
} else { } else {
status = ina219.success(); status = ina219.success();
} }

View File

@@ -3,7 +3,7 @@
#include "VoltageSensor.h" #include "VoltageSensor.h"
#include <Adafruit_INA219.h> #include <Adafruit_INA219.h>
class INA219Sensor : virtual public TelemetrySensor, VoltageSensor class INA219Sensor : public TelemetrySensor, VoltageSensor
{ {
private: private:
Adafruit_INA219 ina219; Adafruit_INA219 ina219;

View File

@@ -14,7 +14,7 @@ int32_t INA260Sensor::runOnce()
} }
if (!status) { if (!status) {
status = ina260.begin(nodeTelemetrySensorsMap[sensorType]); status = ina260.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
} }
return initI2CSensor(); return initI2CSensor();
} }

View File

@@ -3,7 +3,7 @@
#include "VoltageSensor.h" #include "VoltageSensor.h"
#include <Adafruit_INA260.h> #include <Adafruit_INA260.h>
class INA260Sensor : virtual public TelemetrySensor, VoltageSensor class INA260Sensor : public TelemetrySensor, VoltageSensor
{ {
private: private:
Adafruit_INA260 ina260 = Adafruit_INA260(); Adafruit_INA260 ina260 = Adafruit_INA260();

View File

@@ -13,7 +13,7 @@ int32_t LPS22HBSensor::runOnce()
if (!hasSensor()) { if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
} }
status = lps22hb.begin_I2C(nodeTelemetrySensorsMap[sensorType]); status = lps22hb.begin_I2C(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
return initI2CSensor(); return initI2CSensor();
} }

View File

@@ -3,7 +3,7 @@
#include <Adafruit_LPS2X.h> #include <Adafruit_LPS2X.h>
#include <Adafruit_Sensor.h> #include <Adafruit_Sensor.h>
class LPS22HBSensor : virtual public TelemetrySensor class LPS22HBSensor : public TelemetrySensor
{ {
private: private:
Adafruit_LPS22 lps22hb; Adafruit_LPS22 lps22hb;

View File

@@ -12,7 +12,7 @@ int32_t MCP9808Sensor::runOnce()
if (!hasSensor()) { if (!hasSensor()) {
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
} }
status = mcp9808.begin(nodeTelemetrySensorsMap[sensorType]); status = mcp9808.begin(nodeTelemetrySensorsMap[sensorType].first, nodeTelemetrySensorsMap[sensorType].second);
return initI2CSensor(); return initI2CSensor();
} }

View File

@@ -2,7 +2,7 @@
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#include <Adafruit_MCP9808.h> #include <Adafruit_MCP9808.h>
class MCP9808Sensor : virtual public TelemetrySensor class MCP9808Sensor : public TelemetrySensor
{ {
private: private:
Adafruit_MCP9808 mcp9808; Adafruit_MCP9808 mcp9808;

View File

@@ -2,7 +2,7 @@
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#include <Adafruit_SHT31.h> #include <Adafruit_SHT31.h>
class SHT31Sensor : virtual public TelemetrySensor class SHT31Sensor : public TelemetrySensor
{ {
private: private:
Adafruit_SHT31 sht31 = Adafruit_SHT31(); Adafruit_SHT31 sht31 = Adafruit_SHT31();

View File

@@ -2,7 +2,7 @@
#include "TelemetrySensor.h" #include "TelemetrySensor.h"
#include <Adafruit_SHTC3.h> #include <Adafruit_SHTC3.h>
class SHTC3Sensor : virtual public TelemetrySensor class SHTC3Sensor : public TelemetrySensor
{ {
private: private:
Adafruit_SHTC3 shtc3 = Adafruit_SHTC3(); Adafruit_SHTC3 shtc3 = Adafruit_SHTC3();

View File

@@ -1,9 +1,12 @@
#pragma once #pragma once
#include "../mesh/generated/meshtastic/telemetry.pb.h" #include "../mesh/generated/meshtastic/telemetry.pb.h"
#include "NodeDB.h" #include "NodeDB.h"
#include <utility>
class TwoWire;
#define DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000 #define DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
extern uint8_t nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1]; extern std::pair<uint8_t, TwoWire *> nodeTelemetrySensorsMap[_meshtastic_TelemetrySensorType_MAX + 1];
class TelemetrySensor class TelemetrySensor
{ {
@@ -16,7 +19,7 @@ class TelemetrySensor
} }
const char *sensorName; const char *sensorName;
meshtastic_TelemetrySensorType sensorType; meshtastic_TelemetrySensorType sensorType = meshtastic_TelemetrySensorType_SENSOR_UNSET;
unsigned status; unsigned status;
bool initialized = false; bool initialized = false;
@@ -24,9 +27,9 @@ class TelemetrySensor
{ {
if (!status) { if (!status) {
LOG_WARN("Could not connect to detected %s sensor.\n Removing from nodeTelemetrySensorsMap.\n", sensorName); LOG_WARN("Could not connect to detected %s sensor.\n Removing from nodeTelemetrySensorsMap.\n", sensorName);
nodeTelemetrySensorsMap[sensorType] = 0; nodeTelemetrySensorsMap[sensorType].first = 0;
} else { } else {
LOG_INFO("Opened %s sensor on default i2c bus\n", sensorName); LOG_INFO("Opened %s sensor on i2c bus\n", sensorName);
setup(); setup();
} }
initialized = true; initialized = true;
@@ -35,7 +38,7 @@ class TelemetrySensor
virtual void setup(); virtual void setup();
public: public:
bool hasSensor() { return sensorType < sizeof(nodeTelemetrySensorsMap) && nodeTelemetrySensorsMap[sensorType] > 0; } bool hasSensor() { return nodeTelemetrySensorsMap[sensorType].first > 0; }
virtual int32_t runOnce() = 0; virtual int32_t runOnce() = 0;
virtual bool isInitialized() { return initialized; } virtual bool isInitialized() { return initialized; }

View File

@@ -133,10 +133,16 @@ void MQTT::onReceive(char *topic, byte *payload, size_t length)
if (strcmp(e.gateway_id, owner.id) == 0) if (strcmp(e.gateway_id, owner.id) == 0)
LOG_INFO("Ignoring downlink message we originally sent.\n"); LOG_INFO("Ignoring downlink message we originally sent.\n");
else { else {
if (e.packet) { // Find channel by channel_id and check downlink_enabled
meshtastic_Channel ch = channels.getByName(e.channel_id);
if (strcmp(e.channel_id, channels.getGlobalId(ch.index)) == 0 && e.packet && ch.settings.downlink_enabled) {
LOG_INFO("Received MQTT topic %s, len=%u\n", topic, length); LOG_INFO("Received MQTT topic %s, len=%u\n", topic, length);
meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet); meshtastic_MeshPacket *p = packetPool.allocCopy(*e.packet);
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
p->channel = ch.index;
}
// ignore messages sent by us or if we don't have the channel key // ignore messages sent by us or if we don't have the channel key
if (router && p->from != nodeDB.getNodeNum() && perhapsDecode(p)) if (router && p->from != nodeDB.getNodeNum() && perhapsDecode(p))
router->enqueueReceivedMessage(p); router->enqueueReceivedMessage(p);
@@ -516,34 +522,34 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
JSONObject msgPayload; JSONObject msgPayload;
JSONObject jsonObj; JSONObject jsonObj;
switch (mp->decoded.portnum) { if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
case meshtastic_PortNum_TEXT_MESSAGE_APP: { switch (mp->decoded.portnum) {
msgType = "text"; case meshtastic_PortNum_TEXT_MESSAGE_APP: {
// convert bytes to string msgType = "text";
LOG_DEBUG("got text message of size %u\n", mp->decoded.payload.size); // convert bytes to string
char payloadStr[(mp->decoded.payload.size) + 1]; LOG_DEBUG("got text message of size %u\n", mp->decoded.payload.size);
memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size); char payloadStr[(mp->decoded.payload.size) + 1];
payloadStr[mp->decoded.payload.size] = 0; // null terminated string memcpy(payloadStr, mp->decoded.payload.bytes, mp->decoded.payload.size);
// check if this is a JSON payload payloadStr[mp->decoded.payload.size] = 0; // null terminated string
JSONValue *json_value = JSON::Parse(payloadStr); // check if this is a JSON payload
if (json_value != NULL) { JSONValue *json_value = JSON::Parse(payloadStr);
LOG_INFO("text message payload is of type json\n"); if (json_value != NULL) {
// if it is, then we can just use the json object LOG_INFO("text message payload is of type json\n");
jsonObj["payload"] = json_value; // if it is, then we can just use the json object
} else { jsonObj["payload"] = json_value;
// if it isn't, then we need to create a json object } else {
// with the string as the value // if it isn't, then we need to create a json object
LOG_INFO("text message payload is of type plaintext\n"); // with the string as the value
msgPayload["text"] = new JSONValue(payloadStr); LOG_INFO("text message payload is of type plaintext\n");
jsonObj["payload"] = new JSONValue(msgPayload); msgPayload["text"] = new JSONValue(payloadStr);
jsonObj["payload"] = new JSONValue(msgPayload);
}
break;
} }
break; case meshtastic_PortNum_TELEMETRY_APP: {
} msgType = "telemetry";
case meshtastic_PortNum_TELEMETRY_APP: { meshtastic_Telemetry scratch;
msgType = "telemetry"; meshtastic_Telemetry *decoded = NULL;
meshtastic_Telemetry scratch;
meshtastic_Telemetry *decoded = NULL;
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
memset(&scratch, 0, sizeof(scratch)); memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) { if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch; decoded = &scratch;
@@ -564,14 +570,12 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
} else { } else {
LOG_ERROR("Error decoding protobuf for telemetry message!\n"); LOG_ERROR("Error decoding protobuf for telemetry message!\n");
} }
}; break;
break; }
} case meshtastic_PortNum_NODEINFO_APP: {
case meshtastic_PortNum_NODEINFO_APP: { msgType = "nodeinfo";
msgType = "nodeinfo"; meshtastic_User scratch;
meshtastic_User scratch; meshtastic_User *decoded = NULL;
meshtastic_User *decoded = NULL;
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
memset(&scratch, 0, sizeof(scratch)); memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) { if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_User_msg, &scratch)) {
decoded = &scratch; decoded = &scratch;
@@ -583,14 +587,12 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
} else { } else {
LOG_ERROR("Error decoding protobuf for nodeinfo message!\n"); LOG_ERROR("Error decoding protobuf for nodeinfo message!\n");
} }
}; break;
break; }
} case meshtastic_PortNum_POSITION_APP: {
case meshtastic_PortNum_POSITION_APP: { msgType = "position";
msgType = "position"; meshtastic_Position scratch;
meshtastic_Position scratch; meshtastic_Position *decoded = NULL;
meshtastic_Position *decoded = NULL;
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
memset(&scratch, 0, sizeof(scratch)); memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) { if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Position_msg, &scratch)) {
decoded = &scratch; decoded = &scratch;
@@ -627,15 +629,12 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
} else { } else {
LOG_ERROR("Error decoding protobuf for position message!\n"); LOG_ERROR("Error decoding protobuf for position message!\n");
} }
}; break;
break; }
} case meshtastic_PortNum_WAYPOINT_APP: {
msgType = "position";
case meshtastic_PortNum_WAYPOINT_APP: { meshtastic_Waypoint scratch;
msgType = "position"; meshtastic_Waypoint *decoded = NULL;
meshtastic_Waypoint scratch;
meshtastic_Waypoint *decoded = NULL;
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
memset(&scratch, 0, sizeof(scratch)); memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) { if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
decoded = &scratch; decoded = &scratch;
@@ -650,14 +649,12 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
} else { } else {
LOG_ERROR("Error decoding protobuf for position message!\n"); LOG_ERROR("Error decoding protobuf for position message!\n");
} }
}; break;
break; }
} case meshtastic_PortNum_NEIGHBORINFO_APP: {
case meshtastic_PortNum_NEIGHBORINFO_APP: { msgType = "neighborinfo";
msgType = "neighborinfo"; meshtastic_NeighborInfo scratch;
meshtastic_NeighborInfo scratch; meshtastic_NeighborInfo *decoded = NULL;
meshtastic_NeighborInfo *decoded = NULL;
if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
memset(&scratch, 0, sizeof(scratch)); memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg, if (pb_decode_from_bytes(mp->decoded.payload.bytes, mp->decoded.payload.size, &meshtastic_NeighborInfo_msg,
&scratch)) { &scratch)) {
@@ -678,12 +675,14 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
} else { } else {
LOG_ERROR("Error decoding protobuf for neighborinfo message!\n"); LOG_ERROR("Error decoding protobuf for neighborinfo message!\n");
} }
}; break;
break; }
} // add more packet types here if needed
// add more packet types here if needed default:
default: break;
break; }
} else {
LOG_WARN("Couldn't convert encrypted payload of MeshPacket to JSON\n");
} }
jsonObj["id"] = new JSONValue((uint)mp->id); jsonObj["id"] = new JSONValue((uint)mp->id);
@@ -693,6 +692,10 @@ std::string MQTT::meshPacketToJson(meshtastic_MeshPacket *mp)
jsonObj["channel"] = new JSONValue((uint)mp->channel); jsonObj["channel"] = new JSONValue((uint)mp->channel);
jsonObj["type"] = new JSONValue(msgType.c_str()); jsonObj["type"] = new JSONValue(msgType.c_str());
jsonObj["sender"] = new JSONValue(owner.id); jsonObj["sender"] = new JSONValue(owner.id);
if (mp->rx_rssi != 0)
jsonObj["rssi"] = new JSONValue((int)mp->rx_rssi);
if (mp->rx_snr != 0)
jsonObj["snr"] = new JSONValue((float)mp->rx_snr);
// serialize and write it to the stream // serialize and write it to the stream
JSONValue *value = new JSONValue(jsonObj); JSONValue *value = new JSONValue(jsonObj);

View File

@@ -193,16 +193,12 @@ void cpuDeepSleep(uint32_t msecToWake)
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]); rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
#endif #endif
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using // FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
// to detect wake and in normal operation the external part drives them hard. // to detect wake and in normal operation the external part drives them hard.
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#ifdef BUTTON_PIN #ifdef BUTTON_PIN
// Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39. // Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39.
#if SOC_RTCIO_HOLD_SUPPORTED #if SOC_RTCIO_HOLD_SUPPORTED
uint64_t gpioMask = (1ULL << config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN); uint64_t gpioMask = (1ULL << (config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN));
#endif #endif
#ifdef BUTTON_NEED_PULLUP #ifdef BUTTON_NEED_PULLUP
@@ -218,6 +214,9 @@ void cpuDeepSleep(uint32_t msecToWake)
#endif #endif
#endif #endif
// We want RTC peripherals to stay on
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
esp_deep_sleep_start(); // TBD mA sleep current (battery) esp_deep_sleep_start(); // TBD mA sleep current (battery)
} }

View File

@@ -46,6 +46,8 @@
#define HW_VENDOR meshtastic_HardwareModel_RAK4631 #define HW_VENDOR meshtastic_HardwareModel_RAK4631
#elif defined(TTGO_T_ECHO) #elif defined(TTGO_T_ECHO)
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO #define HW_VENDOR meshtastic_HardwareModel_T_ECHO
#elif defined(NANO_G2_ULTRA)
#define HW_VENDOR meshtastic_HardwareModel_NANO_G2_ULTRA
#elif defined(NORDIC_PCA10059) #elif defined(NORDIC_PCA10059)
#define HW_VENDOR meshtastic_HardwareModel_NRF52840_PCA10059 #define HW_VENDOR meshtastic_HardwareModel_NRF52840_PCA10059
#elif defined(PRIVATE_HW) || defined(FEATHER_DIY) #elif defined(PRIVATE_HW) || defined(FEATHER_DIY)

View File

@@ -180,14 +180,24 @@ void cpuDeepSleep(uint32_t msecToWake)
digitalWrite(AQ_SET_PIN, LOW); digitalWrite(AQ_SET_PIN, LOW);
#endif #endif
#endif #endif
// FIXME, use system off mode with ram retention for key state? // Sleepy trackers or sensors can low power "sleep"
// FIXME, use non-init RAM per // Don't enter this if we're sleeping portMAX_DELAY, since that's a shutdown event
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled if (msecToWake != portMAX_DELAY &&
(config.device.role == meshtastic_Config_DeviceConfig_Role_TRACKER ||
auto ok = sd_power_system_off(); config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR) &&
if (ok != NRF_SUCCESS) { config.power.is_power_saving == true) {
LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!\n"); sd_power_mode_set(NRF_POWER_MODE_LOWPWR);
NRF_POWER->SYSTEMOFF = 1; delay(msecToWake);
NVIC_SystemReset();
} else {
// FIXME, use system off mode with ram retention for key state?
// FIXME, use non-init RAM per
// https://devzone.nordicsemi.com/f/nordic-q-a/48919/ram-retention-settings-with-softdevice-enabled
auto ok = sd_power_system_off();
if (ok != NRF_SUCCESS) {
LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!\n");
NRF_POWER->SYSTEMOFF = 1;
}
} }
// The following code should not be run, because we are off // The following code should not be run, because we are off

View File

@@ -95,7 +95,29 @@ void initDeepSleep()
{ {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
bootCount++; bootCount++;
const char *reason;
wakeCause = esp_sleep_get_wakeup_cause(); wakeCause = esp_sleep_get_wakeup_cause();
switch (wakeCause) {
case ESP_SLEEP_WAKEUP_EXT0:
reason = "ext0 RTC_IO";
break;
case ESP_SLEEP_WAKEUP_EXT1:
reason = "ext1 RTC_CNTL";
break;
case ESP_SLEEP_WAKEUP_TIMER:
reason = "timer";
break;
case ESP_SLEEP_WAKEUP_TOUCHPAD:
reason = "touchpad";
break;
case ESP_SLEEP_WAKEUP_ULP:
reason = "ULP program";
break;
default:
reason = "reset";
break;
}
/* /*
Not using yet because we are using wake on all buttons being low Not using yet because we are using wake on all buttons being low
@@ -106,7 +128,6 @@ void initDeepSleep()
#ifdef DEBUG_PORT #ifdef DEBUG_PORT
// If we booted because our timer ran out or the user pressed reset, send those as fake events // If we booted because our timer ran out or the user pressed reset, send those as fake events
const char *reason = "reset"; // our best guess
RESET_REASON hwReason = rtc_get_reset_reason(0); RESET_REASON hwReason = rtc_get_reset_reason(0);
if (hwReason == RTCWDT_BROWN_OUT_RESET) if (hwReason == RTCWDT_BROWN_OUT_RESET)
@@ -118,9 +139,6 @@ void initDeepSleep()
if (hwReason == TG1WDT_SYS_RESET) if (hwReason == TG1WDT_SYS_RESET)
reason = "intWatchdog"; reason = "intWatchdog";
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER)
reason = "timeout";
LOG_INFO("Booted, wake cause %d (boot count %d), reset_reason=%s\n", wakeCause, bootCount, reason); LOG_INFO("Booted, wake cause %d (boot count %d), reset_reason=%s\n", wakeCause, bootCount, reason);
#endif #endif
#endif #endif
@@ -135,16 +153,18 @@ bool doPreflightSleep()
} }
/// Tell devices we are going to sleep and wait for them to handle things /// Tell devices we are going to sleep and wait for them to handle things
static void waitEnterSleep() static void waitEnterSleep(bool skipPreflight = false)
{ {
uint32_t now = millis(); if (!skipPreflight) {
while (!doPreflightSleep()) { uint32_t now = millis();
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives) while (!doPreflightSleep()) {
delay(100); // Kinda yucky - wait until radio says say we can shutdown (finished in process sends/receives)
if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep if (millis() - now > 30 * 1000) { // If we wait too long just report an error and go to sleep
RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_SLEEP_ENTER_WAIT); RECORD_CRITICALERROR(meshtastic_CriticalErrorCode_SLEEP_ENTER_WAIT);
assert(0); // FIXME - for now we just restart, need to fix bug #167 assert(0); // FIXME - for now we just restart, need to fix bug #167
break; break;
}
} }
} }
@@ -155,7 +175,7 @@ static void waitEnterSleep()
notifySleep.notifyObservers(NULL); notifySleep.notifyObservers(NULL);
} }
void doDeepSleep(uint32_t msecToWake) void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false)
{ {
if (INCLUDE_vTaskSuspend && (msecToWake == portMAX_DELAY)) { if (INCLUDE_vTaskSuspend && (msecToWake == portMAX_DELAY)) {
LOG_INFO("Entering deep sleep forever\n"); LOG_INFO("Entering deep sleep forever\n");
@@ -165,7 +185,7 @@ void doDeepSleep(uint32_t msecToWake)
// not using wifi yet, but once we are this is needed to shutoff the radio hw // not using wifi yet, but once we are this is needed to shutoff the radio hw
// esp_wifi_stop(); // esp_wifi_stop();
waitEnterSleep(); waitEnterSleep(skipPreflight);
notifyDeepSleep.notifyObservers(NULL); notifyDeepSleep.notifyObservers(NULL);
screen->doDeepSleep(); // datasheet says this will draw only 10ua screen->doDeepSleep(); // datasheet says this will draw only 10ua
@@ -173,7 +193,8 @@ void doDeepSleep(uint32_t msecToWake)
nodeDB.saveToDisk(); nodeDB.saveToDisk();
// Kill GPS power completely (even if previously we just had it in sleep mode) // Kill GPS power completely (even if previously we just had it in sleep mode)
gps->setGPSPower(false, false); if (gps)
gps->setGPSPower(false, false, 0);
setLed(false); setLed(false);
@@ -181,7 +202,13 @@ void doDeepSleep(uint32_t msecToWake)
digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power digitalWrite(RESET_OLED, 1); // put the display in reset before killing its power
#endif #endif
#ifdef VEXT_ENABLE #if defined(VEXT_ENABLE_V03)
if (heltec_version == 3) {
digitalWrite(VEXT_ENABLE_V03, 1); // turn off the display power
} else {
digitalWrite(VEXT_ENABLE_V05, 0); // turn off the display power
}
#elif defined(VEXT_ENABLE)
digitalWrite(VEXT_ENABLE, 1); // turn off the display power digitalWrite(VEXT_ENABLE, 1); // turn off the display power
#endif #endif
@@ -227,7 +254,7 @@ esp_sleep_wakeup_cause_t doLightSleep(uint64_t sleepMsec) // FIXME, use a more r
{ {
// LOG_DEBUG("Enter light sleep\n"); // LOG_DEBUG("Enter light sleep\n");
waitEnterSleep(); waitEnterSleep(false);
uint64_t sleepUsec = sleepMsec * 1000LL; uint64_t sleepUsec = sleepMsec * 1000LL;

View File

@@ -4,7 +4,7 @@
#include "Observer.h" #include "Observer.h"
#include "configuration.h" #include "configuration.h"
void doDeepSleep(uint32_t msecToWake), cpuDeepSleep(uint32_t msecToWake); void doDeepSleep(uint32_t msecToWake, bool skipPreflight), cpuDeepSleep(uint32_t msecToWake);
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
#include "esp_sleep.h" #include "esp_sleep.h"

View File

@@ -11,4 +11,4 @@ lib_deps =
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
zinggjm/GxEPD2@^1.4.9 zinggjm/GxEPD2@^1.4.9
debug_tool = jlink debug_tool = jlink
upload_port = /dev/ttyACM4 ;upload_port = /dev/ttyACM4

View File

@@ -11,7 +11,7 @@ build_flags =
-I variants/betafpv_2400_tx_micro -I variants/betafpv_2400_tx_micro
board_build.f_cpu = 240000000L board_build.f_cpu = 240000000L
upload_protocol = esptool upload_protocol = esptool
upload_port = /dev/ttyUSB0 ;upload_port = /dev/ttyUSB0
upload_speed = 460800 upload_speed = 460800
lib_deps = lib_deps =
${esp32_base.lib_deps} ${esp32_base.lib_deps}

View File

@@ -10,7 +10,7 @@ build_flags =
-I variants/betafpv_900_tx_nano -I variants/betafpv_900_tx_nano
board_build.f_cpu = 240000000L board_build.f_cpu = 240000000L
upload_protocol = esptool upload_protocol = esptool
upload_port = /dev/ttyUSB0 ;upload_port = /dev/ttyUSB0
upload_speed = 460800 upload_speed = 460800
lib_deps = lib_deps =
${esp32_base.lib_deps} ${esp32_base.lib_deps}

View File

@@ -6,7 +6,7 @@ board_level = extra
;upload_protocol = esp-builtin ;upload_protocol = esp-builtin
;Normal method ;Normal method
upload_protocol = esptool upload_protocol = esptool
upload_port = /dev/ttyACM2 ;upload_port = /dev/ttyACM2
lib_deps = lib_deps =
${esp32_base.lib_deps} ${esp32_base.lib_deps}
caveman99/ESP32 Codec2@^1.0.1 caveman99/ESP32 Codec2@^1.0.1

View File

@@ -2,13 +2,11 @@
#define I2C_SDA 21 #define I2C_SDA 21
#define I2C_SCL 22 #define I2C_SCL 22
// GPS // For GPS, 'undef's not needed
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define GPS_RX_PIN 12
#define GPS_TX_PIN 15 #define GPS_TX_PIN 15
#define GPS_UBLOX #define GPS_RX_PIN 12
#define PIN_GPS_EN 4 #define PIN_GPS_EN 4
#define GPS_POWER_TOGGLE // Moved definition from platformio.ini to here
#define BUTTON_PIN 39 // The middle button GPIO on the T-Beam #define BUTTON_PIN 39 // The middle button GPIO on the T-Beam
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage #define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
@@ -18,28 +16,25 @@
#define EXT_NOTIFY_OUT 12 // Overridden default pin to use for Ext Notify Module (#975). #define EXT_NOTIFY_OUT 12 // Overridden default pin to use for Ext Notify Module (#975).
#define LED_PIN 2 // add status LED (compatible with core-pcb and DIY targets) #define LED_PIN 2 // add status LED (compatible with core-pcb and DIY targets)
#define LORA_DIO0 26 // a No connect on the SX1262/SX1268 module // Radio
#define LORA_RESET 23 // RST for SX1276, and for SX1262/SX1268 #define USE_SX1262 // E22-900M30S uses SX1262
#define LORA_DIO1 33 // IRQ for SX1262/SX1268 #define SX126X_MAX_POWER \
#define LORA_DIO2 32 // BUSY for SX1262/SX1268 22 // Outputting 22dBm from SX1262 results in ~30dBm E22-900M30S output (module only uses last stage of the YP2233W PA)
#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262/SX1268, if DIO3 is high the TXCO is enabled #define SX126X_DIO3_TCXO_VOLTAGE 1.8 // E22 series TCXO reference voltage is 1.8V
#define RF95_SCK 5 #define SX126X_CS 18 // EBYTE module's NSS pin
#define RF95_MISO 19 #define SX126X_SCK 5 // EBYTE module's SCK pin
#define RF95_MOSI 27 #define SX126X_MOSI 27 // EBYTE module's MOSI pin
#define RF95_NSS 18 #define SX126X_MISO 19 // EBYTE module's MISO pin
#define SX126X_RESET 23 // EBYTE module's NRST pin
#define SX126X_BUSY 32 // EBYTE module's BUSY pin
#define SX126X_DIO1 33 // EBYTE module's DIO1 pin
#define USE_SX1262 #define SX126X_TXEN 13 // Schematic connects EBYTE module's TXEN pin to MCU
#define SX126X_RXEN 14 // Schematic connects EBYTE module's RXEN pin to MCU
#define SX126X_CS 18 // NSS for SX126X #define RF95_NSS SX126X_CS // Compatibility with variant file configuration structure
#define SX126X_DIO1 LORA_DIO1 #define RF95_SCK SX126X_SCK // Compatibility with variant file configuration structure
#define SX126X_BUSY LORA_DIO2 #define RF95_MOSI SX126X_MOSI // Compatibility with variant file configuration structure
#define SX126X_RESET LORA_RESET #define RF95_MISO SX126X_MISO // Compatibility with variant file configuration structure
#define SX126X_RXEN 14 #define LORA_DIO1 SX126X_DIO1 // Compatibility with variant file configuration structure
#define SX126X_TXEN RADIOLIB_NC
#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_DIO3_TCXO_VOLTAGE 1.8

View File

@@ -43,6 +43,4 @@ board_level = extra
build_flags = build_flags =
${esp32_base.build_flags} ${esp32_base.build_flags}
-D DIY_V1 -D DIY_V1
-D EBYTE_E22
-D GPS_POWER_TOGGLE
-I variants/diy/hydra -I variants/diy/hydra

View File

@@ -8,5 +8,5 @@ build_flags =
-I variants/heltec_esp32c3 -I variants/heltec_esp32c3
monitor_speed = 115200 monitor_speed = 115200
upload_protocol = esptool upload_protocol = esptool
upload_port = /dev/ttyUSB0 ;upload_port = /dev/ttyUSB0
upload_speed = 921600 upload_speed = 921600

View File

@@ -9,9 +9,11 @@
#define ST7735_RESET 39 #define ST7735_RESET 39
#define ST7735_MISO -1 #define ST7735_MISO -1
#define ST7735_BUSY -1 #define ST7735_BUSY -1
#define ST7735_BL 45 #define ST7735_BL_V03 45
#define ST7735_BL_V05 21 /* V1.1 PCB marking */
#define ST7735_SPI_HOST SPI3_HOST #define ST7735_SPI_HOST SPI3_HOST
#define ST7735_BACKLIGHT_EN 45 #define ST7735_BACKLIGHT_EN_V03 45
#define ST7735_BACKLIGHT_EN_V05 21
#define SPI_FREQUENCY 40000000 #define SPI_FREQUENCY 40000000
#define SPI_READ_FREQUENCY 16000000 #define SPI_READ_FREQUENCY 16000000
#define SCREEN_ROTATE #define SCREEN_ROTATE
@@ -19,17 +21,20 @@
#define TFT_WIDTH 80 #define TFT_WIDTH 80
#define TFT_OFFSET_X 26 #define TFT_OFFSET_X 26
#define TFT_OFFSET_Y 0 #define TFT_OFFSET_Y 0
#define VTFT_CTRL 46 // Heltec Tracker needs this pulled low for TFT #define VTFT_CTRL_V03 46 // Heltec Tracker needs this pulled low for TFT
#define VTFT_CTRL_V05 -1
#define SCREEN_TRANSITION_FRAMERATE 1 // fps #define SCREEN_TRANSITION_FRAMERATE 1 // fps
#define DISPLAY_FORCE_SMALL_FONTS #define DISPLAY_FORCE_SMALL_FONTS
#define VEXT_ENABLE Vext // active low, powers the oled display and the lora antenna boost #define VEXT_ENABLE_V03 Vext // active low, powers the oled display and the lora antenna boost
#define VEXT_ENABLE_V05 3 // active HIGH, powers the oled display
#define BUTTON_PIN 0 #define BUTTON_PIN 0
#define BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage #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_CHANNEL ADC1_GPIO1_CHANNEL
#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider #define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider
#define ADC_MULTIPLIER 4.9 #define ADC_MULTIPLIER 4.9
#define ADC_CTRL 2 // active HIGH, powers the voltage divider. Only on 1.1
#undef GPS_RX_PIN #undef GPS_RX_PIN
#undef GPS_TX_PIN #undef GPS_TX_PIN
@@ -37,9 +42,12 @@
#define GPS_TX_PIN 34 #define GPS_TX_PIN 34
#define PIN_GPS_RESET 35 #define PIN_GPS_RESET 35
#define PIN_GPS_PPS 36 #define PIN_GPS_PPS 36
#define VGNSS_CTRL 37 // Heltec Tracker needs this pulled low for GPS
#define PIN_GPS_EN VGNSS_CTRL #define VGNSS_CTRL_V03 37 // Heltec Tracker needs this pulled low for GPS
#define VGNSS_CTRL_V05 -1 // Heltec Tracker needs this pulled low for GPS
#define PIN_GPS_EN VGNSS_CTRL_V03
#define GPS_EN_ACTIVE LOW #define GPS_EN_ACTIVE LOW
#define GPS_RESET_MODE LOW #define GPS_RESET_MODE LOW
#define GPS_UC6580 #define GPS_UC6580

View File

@@ -8,8 +8,6 @@
#define VEXT_ENABLE Vext // active low, powers the oled display and the lora antenna boost #define VEXT_ENABLE Vext // active low, powers the oled display and the lora antenna boost
#define BUTTON_PIN 0 #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 BATTERY_PIN 1 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
#define ADC_CHANNEL ADC1_GPIO1_CHANNEL #define ADC_CHANNEL ADC1_GPIO1_CHANNEL
#define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider #define ADC_ATTENUATION ADC_ATTEN_DB_2_5 // lower dB for high resistance voltage divider

View File

@@ -8,5 +8,5 @@ build_flags =
-I variants/m5stack-stamp-c3 -I variants/m5stack-stamp-c3
monitor_speed = 115200 monitor_speed = 115200
upload_protocol = esptool upload_protocol = esptool
upload_port = /dev/ttyACM2 ;upload_port = /dev/ttyACM2
upload_speed = 921600 upload_speed = 921600

View File

@@ -23,4 +23,4 @@ lib_ignore =
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder
board_build.f_cpu = 240000000L board_build.f_cpu = 240000000L
upload_protocol = esptool upload_protocol = esptool
upload_port = /dev/ttyACM0 ;upload_port = /dev/ttyACM0

View File

@@ -6,7 +6,7 @@ board_build.arduino.memory_type = dio_opi
board_build.mcu = esp32s3 board_build.mcu = esp32s3
board_build.f_cpu = 240000000L board_build.f_cpu = 240000000L
upload_protocol = esptool upload_protocol = esptool
upload_port = /dev/ttyACM1 ;upload_port = /dev/ttyACM1
upload_speed = 921600 upload_speed = 921600
platform_packages = platform_packages =
tool-esptoolpy@^1.40500.0 tool-esptoolpy@^1.40500.0

View File

@@ -6,7 +6,7 @@ board_build.arduino.memory_type = dio_opi
board_build.mcu = esp32s3 board_build.mcu = esp32s3
board_build.f_cpu = 240000000L board_build.f_cpu = 240000000L
upload_protocol = esptool upload_protocol = esptool
upload_port = /dev/ttyACM0 ;upload_port = /dev/ttyACM0
upload_speed = 921600 upload_speed = 921600
platform_packages = platform_packages =
tool-esptoolpy@^1.40500.0 tool-esptoolpy@^1.40500.0

View File

@@ -15,4 +15,4 @@ lib_deps =
debug_tool = jlink debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm) ; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink ;upload_protocol = jlink
upload_port = /dev/ttyACM3 ;upload_port = /dev/ttyACM3

View File

@@ -42,3 +42,4 @@
#define GPS_UBLOX #define GPS_UBLOX
#define GPS_RX_PIN 34 #define GPS_RX_PIN 34
#define GPS_TX_PIN 12 #define GPS_TX_PIN 12
// #define GPS_DEBUG

View File

@@ -0,0 +1,13 @@
[env:trackerd]
extends = esp32_base
;platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream
platform = espressif32
board = pico32
board_build.f_flash = 80000000L
board_level = extra
build_flags =
${esp32_base.build_flags} -D PRIVATE_HW -I variants/trackerd -D BSFILE=\"boards/dragino_lbt2.h\"
;board_build.partitions = no_ota.csv
;platform_packages =
; platformio/framework-arduinoespressif32@3
;platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.1-RC1

View File

@@ -0,0 +1,46 @@
// Initialize i2c bus on sd_dat and esp_led pins, respectively. We need a bus to not hang on boot
#define HAS_SCREEN 0
#define I2C_SDA 21
#define I2C_SCL 22
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define GPS_RX_PIN 9
#define GPS_TX_PIN 10
#define LED_PIN 13 // 13 red, 2 blue, 15 red
//#define HAS_BUTTON 0
#define BUTTON_PIN 0
#define BUTTON_NEED_PULLUP
#define USE_RF95
#define LORA_DIO0 26 // a No connect on the SX1262 module
#define LORA_RESET 23
#define LORA_DIO1 33
#define LORA_DIO2 32 // Not really used
#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage
// Battery
// The battery sense is hooked to pin A0 (4)
// it is defined in the anlaolgue pin section of this file
// and has 12 bit resolution
#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)
// Voltage divider value => 100K + 100K voltage divider on VBAT = (100K / (100K + 100K))
#define VBAT_DIVIDER (0.5F)
// Compensation factor for the VBAT divider
#define VBAT_DIVIDER_COMP (2.0)
// 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
#define VBAT_RAW_TO_SCALED(x) (REAL_VBAT_MV_PER_LSB * x)
//#define BATTERY_SENSE_SAMPLES 15 // Set the number of samples, It has an effect of increasing sensitivity.
//#define ADC_MULTIPLIER 3.3

View File

@@ -1,4 +1,4 @@
[VERSION] [VERSION]
major = 2 major = 2
minor = 2 minor = 2
build = 10 build = 13