mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-15 07:51:19 +10:00
Sync Wio lr1110 refresh with master (#4288)
* Fix protobuf structs handling (#4140) * Fix protobuf structs handling * Log instead of assert --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> * BLE based logging (#4146) * WIP log characteristic * Bluetooth logging plumbing * Characteristic * Callback * Check for nullptr * Esp32 bluetooth impl * Formatting * Add thread name and log level * Add settings guard * Remove comments * Field name * Fixes esp32 * Open it up * Whoops * Move va_end past our logic * Use `upload_protocol = esptool` as with the other heltec devices instead of `esp-builtin` (#4151) * Standardize lat/lon position logs (#4156) * Standardize lat/lon position logs * Missed sone and condensed logs * [create-pull-request] automated change (#4157) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Pause BLE logging during want_config flow (#4162) * Update NimBLE to 1.4.2 (#4163) * Implement replies for all telemetry types based on variant tag (#4164) * Implement replies for all telemetry types based on variant tag * Remove check for `ignoreRequest`: modules can set this, don't need to check --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> * Esptool is better * Explicitly set characteristic * fix INA3221 sensor (#4168) - pass wire to begin() - remove redundant setAddr() (already set in header) * Show compass on waypoint frame; clear when waypoint deleted (#4116) * Clear expired or deleted waypoint frame * Return 0 to CallbackObserver * Add a missing comment * Draw compass for waypoint frame * Display our own waypoints * [create-pull-request] automated change (#4171) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Add semihosting support for nrf52 devices (#4137) * Turn off vscode cmake prompt - we don't use cmake on meshtastic * Add rak4631_dap variant for debugging with NanoDAP debug probe device. * The rak device can also run freertos (which is underneath nrf52 arduino) * Add semihosting support for nrf52840 devices Initial platformio.ini file only supports rak4630 Default to non TCP for the semihosting log output for now... Fixes https://github.com/meshtastic/firmware/issues/4135 * fix my botched merge - keep board_level = extra flag for rak3631_dbg --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> * [create-pull-request] automated change (#4174) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Display alerts (#4170) * Move static functions into Screen.h, show compass during calibration * Move to _fontHeight macro to avoid collision * Move some alert functions to new alert handler * Catch missed reboot code * ESP32 fixes * Bump esp8266-oled-ssd1306 * Fixes for when a device has no screen * Use new startAlert(char*) helper class * Add EINK bits back to alert handling * Add noop class for no-display devices --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> * Send file system manifest up on want_config (#4176) * Send file system manifest up on want_config * Platform specific methods * Helps to actually make the change * Clear * tell vscode, if formatting, use whatever our trunk formatter wants (#4186) without this flag if the user has set some other formatter (clang) in their user level settings, it will be looking in the wrong directory for the clang options (we want the options in .trunk/clang) Note: formatOnSave is true in master, which means a bunch of our older files are non compliant and if you edit them it will generate lots of formatting related diffs. I guess I'll start letting that happen with my future commits ;-). * fix the build - would loop forever if there were no files to send (#4188) * Show owner.short_name on boot (and E-Ink sleep screen) (#4134) * Show owner.short_name on boot and sleep screen (on e-ink) * Update Screen.cpp - new line for short_name Boot screen short_name now below the region setting. Looks better on small screens. * Draw short_name on right --------- Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com> Co-authored-by: todd-herbert <herbert.todd@gmail.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> * nrf52 soft device will watchdog if you use ICE while BT on... (#4189) so have debugger disable bluetooth. * correct xiao_ble build preventing sx1262 init (#4191) * Force a compile time failur if FromRadio or ToRadio get larger than (#4190) a BLE packet size. We are actually very close to this threshold so important to make sure we don't accidentally pass it. * Clear vector after complete config state (#4194) * Clear after complete config * Don't collect . entries * Log file name and size * [create-pull-request] automated change (#4200) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Make the logs Colorful! (#4199) * Squash needlessly static functions (#4183) * Trim extra vprintf and filter for unprintable characters * Deprecate Router Client role (and make it Client) (#4201) * [create-pull-request] automated change (#4205) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Move waypoint (#4202) * Move waypoint screen draw into the waypoint module * Get the observer set up for the waypoint screen draw * Static squashing: screen dimensions Macros moved back to Screen.cpp, as a band-aid until we eventually move all those static functions into the Screen class. * Move getCompassDiam into Screen class (supress compiler warnings) At this stage, the method is still static, because it's used by drawNodeInfo, which has no tidy reference to our screen instance. This is probably just another band-aid until these static functions all move. * Use new getCompassDiam function in AccelerometerThread * Properly gate display code in WaypointModule --------- Co-authored-by: Todd Herbert <herbert.todd@gmail.com> * Fix flakey phone api transition from file manifest to complete (#4209) * Try fix flakey phone api transition from file manifest to complete * Skip * enable colors in platformio serial monitor (#4217) * When talking via serial, encapsulate log messages in protobufs if necessary (#4187) * clean up RedirectablePrint::log so it doesn't have three very different implementations inline. * remove NoopPrint - it is no longer needed * when talking to API clients via serial, don't turn off log msgs instead encapsuate them * fix the build - would loop forever if there were no files to send * don't use Segger code if not talking to a Segger debugger * when encapsulating logs, make sure the strings always has nul terminators * nrf52 soft device will watchdog if you use ICE while BT on... so have debugger disable bluetooth. * Important to not print debug messages while writing to the toPhone scratch buffer * don't include newlines if encapsulating log records as protobufs --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> * [create-pull-request] automated change (#4218) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Fix SHT41 support (#4222) * Add SHT41 Serial to I2c Detection Code On the Seeed Wio-WM1110 Dev Kit board, the SHT41 chip was being incorrectly detected as SHT31. This patch adds the necessary serial number for the SHT41 chip to be correctly detected. fixes meshtastic/firmware#4221 * Add missing sensor read for SHT41 * Typo fix in logs - mhz - MHz (#4225) As reported by karamo, a few different places in our logs had incorrect capitalization of MHz. fixes meshtastic/firmware#4126 * New new BLE logging characteristic with LogRecord protos (#4220) * New UUID * New log radio characteristic with LogRecord protobuf * LogRecord * Merge derp * How did you get there * Trunk * Fix length * Remove assert * minor cleanup proposal (#4169) * MESHTASTIC_EXCLUDE_WIFI and HAS_WIFI cleanup... Our code was checking HAS_WIFI and the new MESHTASTIC_EXCLUDE_WIFI flags in various places (even though EXCLUDE_WIFI forces HAS_WIFI to 0). Instead just check HAS_WIFI, only use EXCLUDE_WIFI inside configuration.h * cleanup: use HAS_NETWORKING instead of HAS_WIFI || HAS_ETHERNET We already had HAS_NETWORKING as flag in MQTT to mean 'we have tcpip'. Generallize that and move it into configuration.h so that we can use it elsewhere. * Use #pragma once, because supported by gcc and all modern compilers instead of #ifdef DOTHFILE_H etc... --------- Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz> * Add PowerMon support (#4155) * Turn off vscode cmake prompt - we don't use cmake on meshtastic * Add rak4631_dap variant for debugging with NanoDAP debug probe device. * The rak device can also run freertos (which is underneath nrf52 arduino) * Add semihosting support for nrf52840 devices Initial platformio.ini file only supports rak4630 Default to non TCP for the semihosting log output for now... Fixes https://github.com/meshtastic/firmware/issues/4135 * powermon WIP (for https://github.com/meshtastic/firmware/issues/4136 ) * oops - mean't to mark the _dbg variant as an 'extra' board. * powermon wip * Make serial port on wio-sdk-wm1110 board work By disabling the (inaccessible) adafruit USB * Instrument (radiolib only for now) lora for powermon per https://github.com/meshtastic/firmware/issues/4136 * powermon gps support https://github.com/meshtastic/firmware/issues/4136 * Add CPU deep and light sleep powermon states https://github.com/meshtastic/firmware/issues/4136 * Change the board/swversion bootstring so it is a new "structured" log msg. * powermon wip * add example script for getting esp S3 debugging working Not yet used but I didn't want these nasty tricks to get lost yet. * Add PowerMon reporting for screen and bluetooth pwr. * make power.powermon_enables config setting work. * update to latest protobufs * fix bogus shellcheck warning * make powermon optional (but default enabled because tiny and no runtime impact) * tell vscode, if formatting, use whatever our trunk formatter wants without this flag if the user has set some other formatter (clang) in their user level settings, it will be looking in the wrong directory for the clang options (we want the options in .trunk/clang) Note: formatOnSave is true in master, which means a bunch of our older files are non compliant and if you edit them it will generate lots of formatting related diffs. I guess I'll start letting that happen with my future commits ;-). * add PowerStress module * nrf52 arduino is built upon freertos, so let platformio debug it * don't accidentally try to Segger ICE if we are using another ICE * clean up RedirectablePrint::log so it doesn't have three very different implementations inline. * remove NoopPrint - it is no longer needed * when talking to API clients via serial, don't turn off log msgs instead encapsuate them * fix the build - would loop forever if there were no files to send * don't use Segger code if not talking to a Segger debugger * when encapsulating logs, make sure the strings always has nul terminators * nrf52 soft device will watchdog if you use ICE while BT on... so have debugger disable bluetooth. * Important to not print debug messages while writing to the toPhone scratch buffer * don't include newlines if encapsulating log records as protobufs * update to latest protobufs (needed for powermon goo) * PowerStress WIP * fix linter warning * Cleanup buffer * Merge hex for wm1110 target(s) * Only sdk * Sudo * Fix exclude macros (#4233) * fix MESHTASTIC_EXCLUDE_BLUETOOTH * fix HAS_SCREEN=0 * fix MESHTASTIC_EXCLUDE_GPS * fix typo in build-nrf52.sh (#4231) chmod is the command, '+x' is the argument. * Tidy Wireless Paper variant files (#4238) * Quick tidy of pins_arduino.h Matches requests made at https://github.com/meshtastic/firmware/pull/4226#discussion_r1664183480) * Tidy variant.h * Change deprecated ADC attenuation parameter From 11dB to 12dB. Resolves compiler warning. Allegly, no impact on function: `This is deprecated, it behaves the same as `ADC_ATTEN_DB_12` * Updated raspbian CI to update apt repository ahead of libbluetooth. (#4243) * Fix BLE logging on nrf52 (#4244) * allow ble logrecords to be fetched either by NOTIFY or INDICATE ble types This allows 'lossless' log reading. If client has requested INDICATE (rather than NOTIFY) each log record emitted via log() will have to fetched by the client device before the meshtastic node can continue. * Fix serious problem with nrf52 BLE logging. When doing notifies of LogRecords it is important to use the binary write routines - writing using the 'string' write won't work. Because protobufs can contain \0 nuls inside of them which if being parsed as a string will cause only a portion of the protobuf to be sent. I noticed this because some log messages were not getting through. --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> * Fix build when HAS_NETWORKING is false on nrf52 (#4237) (tested on a rak4631 by setting HAS_ETHERNET false when shrinking image) * If `toPhoneQueue` is full, still increment `fromNum` to avoid client never getting packets (#4246) * Update to SoftDevice 7.3.0 for wio-sdk-wm1110 and wio-tracker-wm1110 (#4248) * Update variant.h * Update wio-tracker-wm1110.json * Update wio-sdk-wm1110.json * Update platformio.ini * Update platformio.ini * Add files via upload * Add files via upload * Update variant.h * Cleanup NRF s140 Softdevice variants (#4252) Note: This idea is originally from @caveman99 and should be credited as such. Submitting as a separate PR so the work in meshtastic/firmware#4148 can be a bit cleaner and Seeed boards can build while that work is ongoing. The nrf52 boards that depend on the v7 softdevice all use the same code and linker files. Rather than duplicate the code, keep it all together with the platform. * Remove tracker variant specific soft device headers (#4255) * [create-pull-request] automated change (#4247) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Add wio-sdk-wm1110 to build. (#4258) The wio-sdk-wm1110 is distinct from the wio-tracker-wm1110, with different platformio build options and pin config. This change adds the wio-sdk-wm1110 to the CI matrix so firmware is built as part of release. * fix python warning in uf2conf (#4235) the old regex worked but was technically incorrect. fixes: Generating NRF52 uf2 file /home/kevinh/development/meshtastic/firmware/bin/uf2conv.py:195: SyntaxWarning: invalid escape sequence '\s' words = re.split('\s+', line) Converting to uf2, output size: 1458688, start address: 0x26000 * Collect hex files and specifically wm1110 sdk * Skip dfu file for sdk (for now) * Helps if you remove the original clause * Add Heltec new boards. (#4226) * Add Heltec new boards * Update variant.h disable RTC by default * Add Heltec New boards * Add Heltec new boards * Update Heltec Mesh Node definition. * Update Heltec Vision Mater E290 * [create-pull-request] automated change (#4259) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Trunk fmt * Fix macros * Move e290 to board level extra while CI is broken * Tell trunk to ignore bin folder * Fix missing * Update trunk.yaml, fix whitespace * Update trunk.yaml * Update build_raspbian_armv7l.yml --fix-missing * [create-pull-request] automated change (#4263) Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * GPS Power State tidy-up (#4161) * Refactor GPSPowerState enum Identifies a case where the GPS hardware is awake, but an update is not yet desired * Change terminology * Clear old lock-time prediction on triple press * Use exponential smoothing to predict lock time * Rename averageLockTime to predictedLockTime * Attempt: Send PMREQ with duration 0 on MCU deep-sleep * Attempt 2: Send PMREQ with duration 0 on MCU deep-sleep * Revert "Attempt 2: Send PMREQ with duration 0 on MCU deep-sleep" This reverts commit 8b697cd2a445355dcfab5b33e0ce7a3128cab151. * Revert "Attempt: Send PMREQ with duration 0 on MCU deep-sleep" This reverts commit 9d29ec7603a88056b9115796b29b5023165a93bb. * Remove unused notifyGPSSleep Observable Handled with notifyDeepSleep, and enable() / disable() * WIP: simplify GPS power management An initial attempt only. * Honor #3e9e0fd * No-op when moving between GPS_IDLE and GPS_ACTIVE * Ensure U-blox GPS is awake to receive indefinite sleep command * Longer pause when waking U-blox to send sleep command * Actually implement soft and hard sleep.. * Dynamically estimate the threshold for GPS_HARDSLEEP * Fallback to GPS_HARDSLEEP, if GPS_SOFTSLEEP unsupported * Move "excessive search time" behavior to scheduler class * Minor logging adjustments * Promote log to warning * Gratuitous buffer clearing on boot * Fix inverted standby pin logic Specifically the standby pin for L76B, L76K and clones Discovered during T-Echo testing: totally broken function, probe method failing. * Remove redundant pin init Now handled by setPowerState * Replace max() with if statements Avoid those platform specific implementations.. * Trunk formatting New round of settings.json changes keep catching me out, have to remember to re-enable my "clang-format" for windows workaround. * Remove some asserts from setPowerState Original aim was to prevent sending a 0 second PMREQ to U-blox hardware as part of a timed sleep (GPS_HARDSLEEP, GPS_SOFTSLEEP). I'm not sure this is super important, and it feels tidier to just allow the 0 second sleeptime here, rather than fudge the sleeptime further up. * Fix an error determining whether GPS_SOFTSLEEP is supported * Clarify a log entry * Set PIN_STANDBY for MCU deep-sleep Required to reach TTGO's advertised 0.25mA sleep current for T-Echo. Without this change: ~6mA. * Optimize the shutdown current of RAK10701 to around 25uA (#4260) Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz> * INA3221 sensor: use for bus voltage & environment metrics (#4215) * use INA3221 for bus voltage; fixes for telemetry variants - add to sensors available for environment telemetry (to report voltage/current) - add vars to define channels to use for battery voltage (for getBusVoltage) and environment metrics (default to CH1 for both) - write to the correct fields on the measurement struct depending on the measurement variant, and DRY up the sensor measurement collection code a bit - this might be suitable for a common implementation for the INA* sensors in a future PR... * formatting * derp --------- Co-authored-by: Ben Meadors <benmmeadors@gmail.com> * WM1110 SDK kit enter serial DFU and add deployment packages (#4266) * Switch default upload protocol to nrfutil so that pio generates zip deploy packages * Enter serial DFU on SDK board * Remove guard for DFU zip from SDK build * NRF_USE_SERIAL_DFU macro instead * Show specific frame when updating screen (#4264) * Updated setFrames in Screen.cpp Added code to attempt to revert back to the same frame that user was on prior to setFrame reload. * Space added Screen.cpp * Update Screen.cpp Make screen to revert to Frame 0 if the originally displayed frame is no longer there. * Update Screen.cpp Inserted boolean holdPosition into setFrames to indicate the requirement to stay on the same frame ( if =true) or else it will switch to new frame . Only Screen::handleStatusUpdate calls with setFrame(true). ( For Node Updates) All other types of updates call as before setFrame(), so it will change focus as needed. * Hold position, even if number of frames increases * Hold position, if handling an outgoing text message * Update Screen.cpp * Reverted chnages related to devicestate.has_rx_text_message * Reset to master * CannedMessages only handles routing packets when waiting for ACK Previously, this was calling Screen::setFrames at unexpected times * Gather position info about screen frames while regenerating * Make admin module observable Notify only when relevant. Currently: only to handle remove_nodenum. * Optionally specify which frame to focus when setFrames runs * UIFrameEvent uses enum instead of multiple booleans * Allow modules to request their own frame to be focussed This is done internally by calling MeshModule::requestFocus() Easier this way, insteady of passing the info in the UIFrameEvent: * Modules don't always know whether they should be focussed until after the UIFrameEvent has been raised, in dramFrame * Don't have to pass reference to module instance as parameter though several methods * E-Ink screensaver uses FOCUS_PRESERVE Previously, it had its own basic implementation of this. * Spelling: regional variant * trunk * Fix HAS_SCREEN guarding * More HAS_SCREEN guarding --------- Co-authored-by: BIST <77391720+slash-bit@users.noreply.github.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: slash-bit <v-b2@live.com> * Move up telemetry defaults to every 30 minutes (#4274) * Don't send node info interrogation when ch. util is >25% (#4273) * Moar LR1110 Targets * update SD_FLASH_SIZE to 0x27000 (#4232) The 7.3.0 softdevice needs the extra 1000 :) * Fix spacing. --------- Co-authored-by: Mike <mikhael.skvortsov@gmail.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Mike G <mkgin@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> Co-authored-by: GUVWAF <78759985+GUVWAF@users.noreply.github.com> Co-authored-by: Warren Guy <5602790+warrenguy@users.noreply.github.com> Co-authored-by: todd-herbert <herbert.todd@gmail.com> Co-authored-by: geeksville <kevinh@geeksville.com> Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz> Co-authored-by: Alexander <156134901+Dorn8010@users.noreply.github.com> Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com> Co-authored-by: quimnut <github@dopegoat.com> Co-authored-by: Manuel <71137295+mverch67@users.noreply.github.com> Co-authored-by: Agent Blu, 006 <blu006@ucr.edu> Co-authored-by: Mark Trevor Birss <markbirss@gmail.com> Co-authored-by: Aaron.Lee <32860565+Heltec-Aaron-Lee@users.noreply.github.com> Co-authored-by: Daniel.Cao <144674500+DanielCao0@users.noreply.github.com> Co-authored-by: BIST <77391720+slash-bit@users.noreply.github.com> Co-authored-by: slash-bit <v-b2@live.com>
This commit is contained in:
co-authored by
BIST
Ben Meadors
slash-bit
Mike
Mike G
github-actions[bot]
thebentern
GUVWAF
Warren Guy
todd-herbert
geeksville
Jonathan Bennett
Alexander
Thomas Göttgens
quimnut
Manuel
Agent Blu, 006
Mark Trevor Birss
Aaron.Lee
Daniel.Cao
parent
d97e6b86b8
commit
5cc8ca59a3
+14
-1
@@ -232,12 +232,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
raw = espAdcRead();
|
||||
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
|
||||
scaled *= operativeAdcMultiplier;
|
||||
#else // block for all other platforms
|
||||
#else // block for all other platforms
|
||||
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
|
||||
pinMode(ADC_CTRL, OUTPUT);
|
||||
digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED);
|
||||
delay(10);
|
||||
#endif
|
||||
for (uint32_t i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
|
||||
raw += analogRead(BATTERY_PIN);
|
||||
}
|
||||
raw = raw / BATTERY_SENSE_SAMPLES;
|
||||
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
|
||||
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
|
||||
digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!initial_read_done) {
|
||||
@@ -441,6 +449,11 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
if (!ina260Sensor.isInitialized())
|
||||
return ina260Sensor.runOnce() > 0;
|
||||
return ina260Sensor.isRunning();
|
||||
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first ==
|
||||
config.power.device_battery_ina_address) {
|
||||
if (!ina3221Sensor.isInitialized())
|
||||
return ina3221Sensor.runOnce() > 0;
|
||||
return ina3221Sensor.isRunning();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+268
-272
@@ -9,6 +9,7 @@
|
||||
#include "main.h" // pmu_found
|
||||
#include "sleep.h"
|
||||
|
||||
#include "GPSUpdateScheduling.h"
|
||||
#include "cas.h"
|
||||
#include "ubx.h"
|
||||
|
||||
@@ -22,19 +23,6 @@
|
||||
#define GPS_RESET_MODE HIGH
|
||||
#endif
|
||||
|
||||
// How many minutes of sleep make it worthwhile to power-off the GPS
|
||||
// Shorter than this, and GPS will only enter standby
|
||||
// Affected by lock-time, and config.position.gps_update_interval
|
||||
#ifndef GPS_STANDBY_THRESHOLD_MINUTES
|
||||
#define GPS_STANDBY_THRESHOLD_MINUTES 15
|
||||
#endif
|
||||
|
||||
// How many seconds of sleep make it worthwhile for the GPS to use powered-on standby
|
||||
// Shorter than this, and we'll just wait instead
|
||||
#ifndef GPS_IDLE_THRESHOLD_SECONDS
|
||||
#define GPS_IDLE_THRESHOLD_SECONDS 10
|
||||
#endif
|
||||
|
||||
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
|
||||
HardwareSerial *GPS::_serial_gps = &Serial1;
|
||||
#else
|
||||
@@ -43,6 +31,8 @@ HardwareSerial *GPS::_serial_gps = NULL;
|
||||
|
||||
GPS *gps = nullptr;
|
||||
|
||||
GPSUpdateScheduling scheduling;
|
||||
|
||||
/// Multiple GPS instances might use the same serial port (in sequence), but we can
|
||||
/// only init that port once.
|
||||
static bool didSerialInit;
|
||||
@@ -52,6 +42,25 @@ uint8_t uBloxProtocolVersion;
|
||||
#define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway
|
||||
#define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc)
|
||||
|
||||
// For logging
|
||||
const char *getGPSPowerStateString(GPSPowerState state)
|
||||
{
|
||||
switch (state) {
|
||||
case GPS_ACTIVE:
|
||||
return "ACTIVE";
|
||||
case GPS_IDLE:
|
||||
return "IDLE";
|
||||
case GPS_SOFTSLEEP:
|
||||
return "SOFTSLEEP";
|
||||
case GPS_HARDSLEEP:
|
||||
return "HARDSLEEP";
|
||||
case GPS_OFF:
|
||||
return "OFF";
|
||||
default:
|
||||
assert(false); // Unhandled enum value..
|
||||
}
|
||||
}
|
||||
|
||||
void GPS::UBXChecksum(uint8_t *message, size_t length)
|
||||
{
|
||||
uint8_t CK_A = 0, CK_B = 0;
|
||||
@@ -773,7 +782,6 @@ bool GPS::setup()
|
||||
}
|
||||
|
||||
notifyDeepSleepObserver.observe(¬ifyDeepSleep);
|
||||
notifyGPSSleepObserver.observe(¬ifyGPSSleep);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -782,132 +790,192 @@ GPS::~GPS()
|
||||
{
|
||||
// we really should unregister our sleep observer
|
||||
notifyDeepSleepObserver.unobserve(¬ifyDeepSleep);
|
||||
notifyGPSSleepObserver.observe(¬ifyGPSSleep);
|
||||
}
|
||||
|
||||
const char *GPS::powerStateToString()
|
||||
// Put the GPS hardware into a specified state
|
||||
void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
|
||||
{
|
||||
switch (powerState) {
|
||||
case GPS_OFF:
|
||||
return "OFF";
|
||||
case GPS_IDLE:
|
||||
return "IDLE";
|
||||
case GPS_STANDBY:
|
||||
return "STANDBY";
|
||||
// Update the stored GPSPowerstate, and create local copies
|
||||
GPSPowerState oldState = powerState;
|
||||
powerState = newState;
|
||||
LOG_INFO("GPS power state moving from %s to %s\n", getGPSPowerStateString(oldState), getGPSPowerStateString(newState));
|
||||
|
||||
switch (newState) {
|
||||
case GPS_ACTIVE:
|
||||
return "ACTIVE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
case GPS_IDLE:
|
||||
if (oldState == GPS_ACTIVE || oldState == GPS_IDLE) // If hardware already awake, no changes needed
|
||||
break;
|
||||
if (oldState != GPS_ACTIVE && oldState != GPS_IDLE) // If hardware just waking now, clear buffer
|
||||
clearBuffer();
|
||||
powerMon->setState(meshtastic_PowerMon_State_GPS_Active); // Report change for power monitoring (during testing)
|
||||
writePinEN(true); // Power (EN pin): on
|
||||
setPowerPMU(true); // Power (PMU): on
|
||||
writePinStandby(false); // Standby (pin): awake (not standby)
|
||||
setPowerUBLOX(true); // Standby (UBLOX): awake
|
||||
break;
|
||||
|
||||
case GPS_SOFTSLEEP:
|
||||
powerMon->clearState(meshtastic_PowerMon_State_GPS_Active); // Report change for power monitoring (during testing)
|
||||
writePinEN(true); // Power (EN pin): on
|
||||
setPowerPMU(true); // Power (PMU): on
|
||||
writePinStandby(true); // Standby (pin): asleep (not awake)
|
||||
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
|
||||
break;
|
||||
|
||||
case GPS_HARDSLEEP:
|
||||
powerMon->clearState(meshtastic_PowerMon_State_GPS_Active); // Report change for power monitoring (during testing)
|
||||
writePinEN(false); // Power (EN pin): off
|
||||
setPowerPMU(false); // Power (PMU): off
|
||||
writePinStandby(true); // Standby (pin): asleep (not awake)
|
||||
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
|
||||
break;
|
||||
|
||||
case GPS_OFF:
|
||||
assert(sleepTime == 0); // This is an indefinite sleep
|
||||
powerMon->clearState(meshtastic_PowerMon_State_GPS_Active); // Report change for power monitoring (during testing)
|
||||
writePinEN(false); // Power (EN pin): off
|
||||
setPowerPMU(false); // Power (PMU): off
|
||||
writePinStandby(true); // Standby (pin): asleep
|
||||
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
|
||||
// Set power with EN pin, if relevant
|
||||
void GPS::writePinEN(bool on)
|
||||
{
|
||||
// Record the current powerState
|
||||
if (on)
|
||||
powerState = GPS_ACTIVE;
|
||||
else if (!enabled) // User has disabled with triple press
|
||||
powerState = GPS_OFF;
|
||||
else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL)
|
||||
powerState = GPS_IDLE;
|
||||
else if (standbyOnly)
|
||||
powerState = GPS_STANDBY;
|
||||
else
|
||||
powerState = GPS_OFF;
|
||||
|
||||
LOG_DEBUG("GPS::powerState=%s\n", powerStateToString());
|
||||
|
||||
// If the next update is due *really soon*, don't actually power off or enter standby. Just wait it out.
|
||||
if (!on && powerState == GPS_IDLE)
|
||||
// Abort: if conflict with Canned Messages when using Wisblock(?)
|
||||
if (HW_VENDOR == meshtastic_HardwareModel_RAK4631 && (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1))
|
||||
return;
|
||||
|
||||
if (on) {
|
||||
powerMon->setState(meshtastic_PowerMon_State_GPS_Active);
|
||||
clearBuffer(); // drop any old data waiting in the buffer before re-enabling
|
||||
if (en_gpio)
|
||||
digitalWrite(en_gpio, on ? GPS_EN_ACTIVE : !GPS_EN_ACTIVE); // turn this on if defined, every time
|
||||
} else {
|
||||
powerMon->clearState(meshtastic_PowerMon_State_GPS_Active);
|
||||
}
|
||||
isInPowersave = !on;
|
||||
if (!standbyOnly && en_gpio != 0 &&
|
||||
!(HW_VENDOR == meshtastic_HardwareModel_RAK4631 && (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1))) {
|
||||
LOG_DEBUG("GPS powerdown using GPS_EN_ACTIVE\n");
|
||||
digitalWrite(en_gpio, on ? GPS_EN_ACTIVE : !GPS_EN_ACTIVE);
|
||||
// Abort: if pin unset
|
||||
if (!en_gpio)
|
||||
return;
|
||||
}
|
||||
#ifdef HAS_PMU // We only have PMUs on the T-Beam, and that board has a tiny battery to save GPS ephemera, so treat as a standby.
|
||||
if (pmu_found && PMU) {
|
||||
uint8_t model = PMU->getChipModel();
|
||||
if (model == XPOWERS_AXP2101) {
|
||||
if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
|
||||
// t-beam v1.2 GNSS power channel
|
||||
on ? PMU->enablePowerOutput(XPOWERS_ALDO3) : PMU->disablePowerOutput(XPOWERS_ALDO3);
|
||||
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) {
|
||||
// t-beam-s3-core GNSS power channel
|
||||
on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4);
|
||||
}
|
||||
} else if (model == XPOWERS_AXP192) {
|
||||
// t-beam v1.1 GNSS power channel
|
||||
on ? PMU->enablePowerOutput(XPOWERS_LDO3) : PMU->disablePowerOutput(XPOWERS_LDO3);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine new value for the pin
|
||||
bool val = GPS_EN_ACTIVE ? on : !on;
|
||||
|
||||
// Write and log
|
||||
pinMode(en_gpio, OUTPUT);
|
||||
digitalWrite(en_gpio, val);
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("Pin EN %s\n", val == HIGH ? "HIGH" : "LOW");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Set the value of the STANDBY pin, if relevant
|
||||
// true for standby state, false for awake
|
||||
void GPS::writePinStandby(bool standby)
|
||||
{
|
||||
#ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76B, L76K and clones
|
||||
if (on) {
|
||||
LOG_INFO("Waking GPS\n");
|
||||
pinMode(PIN_GPS_STANDBY, OUTPUT);
|
||||
// Some PCB's use an inverse logic due to a transistor driver
|
||||
// Example for this is the Pico-Waveshare Lora+GPS HAT
|
||||
#ifdef PIN_GPS_STANDBY_INVERTED
|
||||
digitalWrite(PIN_GPS_STANDBY, 0);
|
||||
|
||||
// Determine the new value for the pin
|
||||
// Normally: active HIGH for awake
|
||||
#if PIN_GPS_STANDBY_INVERTED
|
||||
bool val = standby;
|
||||
#else
|
||||
digitalWrite(PIN_GPS_STANDBY, 1);
|
||||
bool val = !standby;
|
||||
#endif
|
||||
|
||||
// Write and log
|
||||
pinMode(PIN_GPS_STANDBY, OUTPUT);
|
||||
digitalWrite(PIN_GPS_STANDBY, val);
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("Pin STANDBY %s\n", val == HIGH ? "HIGH" : "LOW");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Enable / Disable GPS with PMU, if present
|
||||
void GPS::setPowerPMU(bool on)
|
||||
{
|
||||
// We only have PMUs on the T-Beam, and that board has a tiny battery to save GPS ephemera,
|
||||
// so treat as a standby.
|
||||
#ifdef HAS_PMU
|
||||
// Abort: if no PMU
|
||||
if (!pmu_found)
|
||||
return;
|
||||
} else {
|
||||
LOG_INFO("GPS entering sleep\n");
|
||||
// notifyGPSSleep.notifyObservers(NULL);
|
||||
pinMode(PIN_GPS_STANDBY, OUTPUT);
|
||||
#ifdef PIN_GPS_STANDBY_INVERTED
|
||||
digitalWrite(PIN_GPS_STANDBY, 1);
|
||||
#else
|
||||
digitalWrite(PIN_GPS_STANDBY, 0);
|
||||
#endif
|
||||
|
||||
// Abort: if PMU not initialized
|
||||
if (!PMU)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
if (!on) {
|
||||
if (gnssModel == GNSS_MODEL_UBLOX) {
|
||||
uint8_t msglen;
|
||||
LOG_DEBUG("Sleep Time: %i\n", sleepTime);
|
||||
if (strncmp(info.hwVersion, "000A0000", 8) != 0) {
|
||||
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, sizeof(_message_PMREQ), gps->_message_PMREQ);
|
||||
} else {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gps->_message_PMREQ_10[4 + i] = sleepTime >> (i * 8); // Encode the sleep time in millis into the packet
|
||||
}
|
||||
msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ_10), gps->_message_PMREQ_10);
|
||||
}
|
||||
gps->_serial_gps->write(gps->UBXscratch, msglen);
|
||||
|
||||
uint8_t model = PMU->getChipModel();
|
||||
if (model == XPOWERS_AXP2101) {
|
||||
if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
|
||||
// t-beam v1.2 GNSS power channel
|
||||
on ? PMU->enablePowerOutput(XPOWERS_ALDO3) : PMU->disablePowerOutput(XPOWERS_ALDO3);
|
||||
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) {
|
||||
// t-beam-s3-core GNSS power channel
|
||||
on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4);
|
||||
}
|
||||
} else if (model == XPOWERS_AXP192) {
|
||||
// t-beam v1.1 GNSS power channel
|
||||
on ? PMU->enablePowerOutput(XPOWERS_LDO3) : PMU->disablePowerOutput(XPOWERS_LDO3);
|
||||
}
|
||||
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("PMU %s\n", on ? "on" : "off");
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
// Set UBLOX power, if relevant
|
||||
void GPS::setPowerUBLOX(bool on, uint32_t sleepMs)
|
||||
{
|
||||
// Abort: if not UBLOX hardware
|
||||
if (gnssModel != GNSS_MODEL_UBLOX)
|
||||
return;
|
||||
|
||||
// If waking
|
||||
if (on) {
|
||||
gps->_serial_gps->write(0xFF);
|
||||
clearBuffer(); // This often returns old data, so drop it
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("UBLOX: wake\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
// If putting to sleep
|
||||
else {
|
||||
uint8_t msglen;
|
||||
|
||||
// If we're being asked to sleep indefinitely, make *sure* we're awake first, to process the new sleep command
|
||||
if (sleepMs == 0) {
|
||||
setPowerUBLOX(true);
|
||||
delay(500);
|
||||
}
|
||||
|
||||
// Determine hardware version
|
||||
if (strncmp(info.hwVersion, "000A0000", 8) != 0) {
|
||||
// Encode the sleep time in millis into the packet
|
||||
for (int i = 0; i < 4; i++)
|
||||
gps->_message_PMREQ[0 + i] = sleepMs >> (i * 8);
|
||||
|
||||
// Record the message length
|
||||
msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ), gps->_message_PMREQ);
|
||||
} else {
|
||||
// Encode the sleep time in millis into the packet
|
||||
for (int i = 0; i < 4; i++)
|
||||
gps->_message_PMREQ_10[4 + i] = sleepMs >> (i * 8);
|
||||
|
||||
// Record the message length
|
||||
msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ_10), gps->_message_PMREQ_10);
|
||||
|
||||
#ifdef GNSS_Airoha // add by WayenWeng
|
||||
else {
|
||||
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
|
||||
// TODO, send rtc mode command
|
||||
digitalWrite(PIN_GPS_EN, LOW);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
if (gnssModel == GNSS_MODEL_UBLOX) {
|
||||
gps->_serial_gps->write(0xFF);
|
||||
clearBuffer(); // This often returns old data, so drop it
|
||||
}
|
||||
|
||||
// Send the UBX packet
|
||||
gps->_serial_gps->write(gps->UBXscratch, msglen);
|
||||
|
||||
#ifdef GPS_EXTRAVERBOSE
|
||||
LOG_DEBUG("UBLOX: sleep for %dmS\n", sleepMs);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -920,111 +988,57 @@ void GPS::setConnected()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
|
||||
*
|
||||
* calls sleep/wake
|
||||
*/
|
||||
void GPS::setAwake(bool wantAwake)
|
||||
// We want a GPS lock. Wake the hardware
|
||||
void GPS::up()
|
||||
{
|
||||
scheduling.informSearching();
|
||||
setPowerState(GPS_ACTIVE);
|
||||
}
|
||||
|
||||
// If user has disabled GPS, make sure it is off, not just in standby or idle
|
||||
if (!wantAwake && !enabled && powerState != GPS_OFF) {
|
||||
setGPSPower(false, false, 0);
|
||||
return;
|
||||
}
|
||||
// We've got a GPS lock. Enter a low power state, potentially.
|
||||
void GPS::down()
|
||||
{
|
||||
scheduling.informGotLock();
|
||||
uint32_t predictedSearchDuration = scheduling.predictedSearchDurationMs();
|
||||
uint32_t sleepTime = scheduling.msUntilNextSearch();
|
||||
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval);
|
||||
|
||||
// If GPS power state needs to change
|
||||
if ((wantAwake && powerState != GPS_ACTIVE) || (!wantAwake && powerState == GPS_ACTIVE)) {
|
||||
LOG_DEBUG("WANT GPS=%d\n", wantAwake);
|
||||
LOG_DEBUG("%us until next search\n", sleepTime / 1000);
|
||||
|
||||
// Calculate how long it takes to get a GPS lock
|
||||
if (wantAwake) {
|
||||
// Record the time we start looking for a lock
|
||||
lastWakeStartMsec = millis();
|
||||
#ifdef GNSS_Airoha
|
||||
lastFixStartMsec = 0;
|
||||
lastFixStartMsec = 0;
|
||||
#endif
|
||||
} else {
|
||||
// Record by how much we missed our ideal target postion.gps_update_interval (for logging only)
|
||||
// Need to calculate this before we update lastSleepStartMsec, to make the new prediction
|
||||
int32_t lateByMsec = (int32_t)(millis() - lastSleepStartMsec) - (int32_t)getSleepTime();
|
||||
|
||||
// Record the time we finish looking for a lock
|
||||
lastSleepStartMsec = millis();
|
||||
|
||||
// How long did it take to get GPS lock this time?
|
||||
uint32_t lockTime = lastSleepStartMsec - lastWakeStartMsec;
|
||||
|
||||
// Update the lock-time prediction
|
||||
// Used pre-emptively, attempting to hit target of gps.position_update_interval
|
||||
switch (GPSCycles) {
|
||||
case 0:
|
||||
LOG_DEBUG("Initial GPS lock took %ds\n", lockTime / 1000);
|
||||
break;
|
||||
case 1:
|
||||
predictedLockTime = lockTime; // Avoid slow ramp-up - start with a real value
|
||||
LOG_DEBUG("GPS Lock took %ds\n", lockTime / 1000);
|
||||
break;
|
||||
default:
|
||||
// Predict lock-time using exponential smoothing: respond slowly to changes
|
||||
predictedLockTime = (lockTime * 0.2) + (predictedLockTime * 0.8); // Latest lock time has 20% weight on prediction
|
||||
LOG_INFO("GPS Lock took %ds. %s by %ds. Next lock predicted to take %ds.\n", lockTime / 1000,
|
||||
(lateByMsec > 0) ? "Late" : "Early", abs(lateByMsec) / 1000, predictedLockTime / 1000);
|
||||
}
|
||||
GPSCycles++;
|
||||
}
|
||||
|
||||
// How long to wait before attempting next GPS update
|
||||
// Aims to hit position.gps_update_interval by using the lock-time prediction
|
||||
uint32_t compensatedSleepTime = (getSleepTime() > predictedLockTime) ? (getSleepTime() - predictedLockTime) : 0;
|
||||
|
||||
// If long interval between updates: power off between updates
|
||||
if (compensatedSleepTime > GPS_STANDBY_THRESHOLD_MINUTES * MS_IN_MINUTE) {
|
||||
setGPSPower(wantAwake, false, getSleepTime() - predictedLockTime);
|
||||
}
|
||||
|
||||
// If waking relatively frequently: don't power off. Would use more energy trying to reacquire lock each time
|
||||
// We'll either use a "powered-on" standby, or just wait it out, depending on how soon the next update is due
|
||||
// Will decide which inside setGPSPower method
|
||||
else {
|
||||
#ifdef GPS_UC6580
|
||||
setGPSPower(wantAwake, false, compensatedSleepTime);
|
||||
#else
|
||||
setGPSPower(wantAwake, true, compensatedSleepTime);
|
||||
// If update interval less than 10 seconds, no attempt to sleep
|
||||
if (updateInterval <= 10 * 1000UL)
|
||||
setPowerState(GPS_IDLE);
|
||||
else {
|
||||
// Check whether the GPS hardware is capable of GPS_SOFTSLEEP
|
||||
// If not, fallback to GPS_HARDSLEEP instead
|
||||
bool softsleepSupported = false;
|
||||
if (gnssModel == GNSS_MODEL_UBLOX) // U-blox is supported via PMREQ
|
||||
softsleepSupported = true;
|
||||
#ifdef PIN_GPS_STANDBY // L76B, L76K and clones have a standby pin
|
||||
softsleepSupported = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
// How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP?
|
||||
// Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050
|
||||
// https://www.desmos.com/calculator/6gvjghoumr
|
||||
// This is not particularly accurate, but probably an impromevement over a single, fixed threshold
|
||||
uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22));
|
||||
LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000);
|
||||
|
||||
// If update interval too short: softsleep (if supported by hardware)
|
||||
if (softsleepSupported && updateInterval < hardsleepThreshold)
|
||||
setPowerState(GPS_SOFTSLEEP, sleepTime);
|
||||
|
||||
// If update interval long enough (or softsleep unsupported): hardsleep instead
|
||||
else
|
||||
setPowerState(GPS_HARDSLEEP, sleepTime);
|
||||
}
|
||||
}
|
||||
|
||||
/** Get how long we should stay looking for each acquisition in msecs
|
||||
*/
|
||||
uint32_t GPS::getWakeTime() const
|
||||
{
|
||||
uint32_t t = config.position.position_broadcast_secs;
|
||||
|
||||
if (t == UINT32_MAX)
|
||||
return t; // already maxint
|
||||
|
||||
return Default::getConfiguredOrDefaultMs(t, default_broadcast_interval_secs);
|
||||
}
|
||||
|
||||
/** Get how long we should sleep between aqusition attempts in msecs
|
||||
*/
|
||||
uint32_t GPS::getSleepTime() const
|
||||
{
|
||||
uint32_t t = config.position.gps_update_interval;
|
||||
|
||||
// We'll not need the GPS thread to wake up again after first acq. with fixed position.
|
||||
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED || config.position.fixed_position)
|
||||
t = UINT32_MAX; // Sleep forever now
|
||||
|
||||
if (t == UINT32_MAX)
|
||||
return t; // already maxint
|
||||
|
||||
return Default::getConfiguredOrDefaultMs(t, default_gps_update_interval);
|
||||
}
|
||||
|
||||
void GPS::publishUpdate()
|
||||
{
|
||||
if (shouldPublish) {
|
||||
@@ -1073,13 +1087,13 @@ int32_t GPS::runOnce()
|
||||
return disable();
|
||||
}
|
||||
|
||||
if (whileIdle()) {
|
||||
if (whileActive()) {
|
||||
// if we have received valid NMEA claim we are connected
|
||||
setConnected();
|
||||
} else {
|
||||
if ((config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) && (gnssModel == GNSS_MODEL_UBLOX)) {
|
||||
// reset the GPS on next bootup
|
||||
if (devicestate.did_gps_reset && (millis() - lastWakeStartMsec > 60000) && !hasFlow()) {
|
||||
if (devicestate.did_gps_reset && scheduling.elapsedSearchMs() > 60 * 1000UL && !hasFlow()) {
|
||||
LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n");
|
||||
devicestate.did_gps_reset = false;
|
||||
nodeDB->saveDeviceStateToDisk();
|
||||
@@ -1094,54 +1108,43 @@ int32_t GPS::runOnce()
|
||||
// gps->factoryReset();
|
||||
}
|
||||
|
||||
// If we are overdue for an update, turn on the GPS and at least publish the current status
|
||||
uint32_t now = millis();
|
||||
uint32_t timeAsleep = now - lastSleepStartMsec;
|
||||
// If we're due for an update, wake the GPS
|
||||
if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue())
|
||||
up();
|
||||
|
||||
auto sleepTime = getSleepTime();
|
||||
if (powerState != GPS_ACTIVE && (sleepTime != UINT32_MAX) &&
|
||||
((timeAsleep > sleepTime) || (isInPowersave && timeAsleep > (sleepTime - predictedLockTime)))) {
|
||||
// We now want to be awake - so wake up the GPS
|
||||
setAwake(true);
|
||||
// If we've already set time from the GPS, no need to ask the GPS
|
||||
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||
gotTime = true;
|
||||
shouldPublish = true;
|
||||
}
|
||||
|
||||
// While we are awake
|
||||
if (powerState == GPS_ACTIVE) {
|
||||
// LOG_DEBUG("looking for location\n");
|
||||
// If we've already set time from the GPS, no need to ask the GPS
|
||||
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
||||
gotTime = true;
|
||||
shouldPublish = true;
|
||||
}
|
||||
bool gotLoc = lookForLocation();
|
||||
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
||||
LOG_DEBUG("hasValidLocation RISING EDGE\n");
|
||||
hasValidLocation = true;
|
||||
shouldPublish = true;
|
||||
}
|
||||
|
||||
bool gotLoc = lookForLocation();
|
||||
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
||||
LOG_DEBUG("hasValidLocation RISING EDGE\n");
|
||||
hasValidLocation = true;
|
||||
shouldPublish = true;
|
||||
}
|
||||
bool tooLong = scheduling.searchedTooLong();
|
||||
if (tooLong)
|
||||
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time.\n");
|
||||
|
||||
now = millis();
|
||||
auto wakeTime = getWakeTime();
|
||||
bool tooLong = wakeTime != UINT32_MAX && (now - lastWakeStartMsec) > wakeTime;
|
||||
// Once we get a location we no longer desperately want an update
|
||||
// LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime);
|
||||
if ((gotLoc && gotTime) || tooLong) {
|
||||
|
||||
// Once we get a location we no longer desperately want an update
|
||||
// LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime);
|
||||
if ((gotLoc && gotTime) || tooLong) {
|
||||
|
||||
if (tooLong) {
|
||||
// we didn't get a location during this ack window, therefore declare loss of lock
|
||||
if (hasValidLocation) {
|
||||
LOG_DEBUG("hasValidLocation FALLING EDGE (last read: %d)\n", gotLoc);
|
||||
}
|
||||
p = meshtastic_Position_init_default;
|
||||
hasValidLocation = false;
|
||||
if (tooLong) {
|
||||
// we didn't get a location during this ack window, therefore declare loss of lock
|
||||
if (hasValidLocation) {
|
||||
LOG_DEBUG("hasValidLocation FALLING EDGE\n");
|
||||
}
|
||||
|
||||
setAwake(false);
|
||||
shouldPublish = true; // publish our update for this just finished acquisition window
|
||||
p = meshtastic_Position_init_default;
|
||||
hasValidLocation = false;
|
||||
}
|
||||
|
||||
down();
|
||||
shouldPublish = true; // publish our update for this just finished acquisition window
|
||||
}
|
||||
|
||||
// If state has changed do a publish
|
||||
@@ -1167,9 +1170,7 @@ void GPS::clearBuffer()
|
||||
int GPS::prepareDeepSleep(void *unused)
|
||||
{
|
||||
LOG_INFO("GPS deep sleep!\n");
|
||||
|
||||
setAwake(false);
|
||||
|
||||
disable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1369,12 +1370,6 @@ GPS *GPS::createGps()
|
||||
new_gps->tx_gpio = _tx_gpio;
|
||||
new_gps->en_gpio = _en_gpio;
|
||||
|
||||
if (_en_gpio != 0) {
|
||||
LOG_DEBUG("Setting %d to output.\n", _en_gpio);
|
||||
pinMode(_en_gpio, OUTPUT);
|
||||
digitalWrite(_en_gpio, !GPS_EN_ACTIVE);
|
||||
}
|
||||
|
||||
#ifdef PIN_GPS_PPS
|
||||
// pulse per second
|
||||
pinMode(PIN_GPS_PPS, INPUT);
|
||||
@@ -1389,7 +1384,8 @@ GPS *GPS::createGps()
|
||||
LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n");
|
||||
#endif
|
||||
|
||||
new_gps->setGPSPower(true, false, 0);
|
||||
// Make sure the GPS is awake before performing any init.
|
||||
new_gps->up();
|
||||
|
||||
#ifdef PIN_GPS_RESET
|
||||
pinMode(PIN_GPS_RESET, OUTPUT);
|
||||
@@ -1397,7 +1393,6 @@ GPS *GPS::createGps()
|
||||
delay(10);
|
||||
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
|
||||
#endif
|
||||
new_gps->setAwake(true); // Wake GPS power before doing any init
|
||||
|
||||
if (_serial_gps) {
|
||||
#ifdef ARCH_ESP32
|
||||
@@ -1723,13 +1718,13 @@ bool GPS::hasFlow()
|
||||
return reader.passedChecksum() > 0;
|
||||
}
|
||||
|
||||
bool GPS::whileIdle()
|
||||
bool GPS::whileActive()
|
||||
{
|
||||
unsigned int charsInBuf = 0;
|
||||
bool isValid = false;
|
||||
if (powerState != GPS_ACTIVE) {
|
||||
clearBuffer();
|
||||
return (powerState == GPS_ACTIVE);
|
||||
return false;
|
||||
}
|
||||
#ifdef SERIAL_BUFFER_SIZE
|
||||
if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) {
|
||||
@@ -1760,20 +1755,21 @@ bool GPS::whileIdle()
|
||||
}
|
||||
void GPS::enable()
|
||||
{
|
||||
// Clear the old lock-time prediction
|
||||
GPSCycles = 0;
|
||||
predictedLockTime = 0;
|
||||
// Clear the old scheduling info (reset the lock-time prediction)
|
||||
scheduling.reset();
|
||||
|
||||
enabled = true;
|
||||
setInterval(GPS_THREAD_INTERVAL);
|
||||
setAwake(true);
|
||||
|
||||
scheduling.informSearching();
|
||||
setPowerState(GPS_ACTIVE);
|
||||
}
|
||||
|
||||
int32_t GPS::disable()
|
||||
{
|
||||
enabled = false;
|
||||
setInterval(INT32_MAX);
|
||||
setAwake(false);
|
||||
setPowerState(GPS_OFF);
|
||||
|
||||
return INT32_MAX;
|
||||
}
|
||||
@@ -1782,7 +1778,7 @@ void GPS::toggleGpsMode()
|
||||
{
|
||||
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
||||
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
|
||||
LOG_DEBUG("Flag set to false for gps power. GpsMode: DISABLED\n");
|
||||
LOG_INFO("User toggled GpsMode. Now DISABLED.\n");
|
||||
#ifdef GNSS_Airoha
|
||||
if (powerState != GPS_ACTIVE) {
|
||||
LOG_DEBUG("User power Off GPS\n");
|
||||
@@ -1792,8 +1788,8 @@ void GPS::toggleGpsMode()
|
||||
disable();
|
||||
} else if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_DISABLED) {
|
||||
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
|
||||
LOG_DEBUG("Flag set to true to restore power. GpsMode: ENABLED\n");
|
||||
LOG_INFO("User toggled GpsMode. Now ENABLED\n");
|
||||
enable();
|
||||
}
|
||||
}
|
||||
#endif // Exclude GPS
|
||||
#endif // Exclude GPS
|
||||
|
||||
+27
-20
@@ -39,10 +39,11 @@ typedef enum {
|
||||
} GPS_RESPONSE;
|
||||
|
||||
enum GPSPowerState : uint8_t {
|
||||
GPS_OFF = 0, // Physically powered off
|
||||
GPS_ACTIVE = 1, // Awake and want a position
|
||||
GPS_STANDBY = 2, // Physically powered on, but soft-sleeping
|
||||
GPS_IDLE = 3, // Awake, but not wanting another position yet
|
||||
GPS_ACTIVE, // Awake and want a position
|
||||
GPS_IDLE, // Awake, but not wanting another position yet
|
||||
GPS_SOFTSLEEP, // Physically powered on, but soft-sleeping
|
||||
GPS_HARDSLEEP, // Physically powered off, but scheduled to wake
|
||||
GPS_OFF // Powered off indefinitely
|
||||
};
|
||||
|
||||
// Generate a string representation of DOP
|
||||
@@ -73,8 +74,6 @@ class GPS : private concurrency::OSThread
|
||||
uint32_t rx_gpio = 0;
|
||||
uint32_t tx_gpio = 0;
|
||||
uint32_t en_gpio = 0;
|
||||
uint32_t predictedLockTime = 0;
|
||||
uint32_t GPSCycles = 0;
|
||||
|
||||
int speedSelect = 0;
|
||||
int probeTries = 2;
|
||||
@@ -99,7 +98,6 @@ class GPS : private concurrency::OSThread
|
||||
uint8_t numSatellites = 0;
|
||||
|
||||
CallbackObserver<GPS, void *> notifyDeepSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
|
||||
CallbackObserver<GPS, void *> notifyGPSSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
|
||||
|
||||
public:
|
||||
/** If !NULL we will use this serial port to construct our GPS */
|
||||
@@ -175,7 +173,8 @@ class GPS : private concurrency::OSThread
|
||||
// toggle between enabled/disabled
|
||||
void toggleGpsMode();
|
||||
|
||||
void setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime);
|
||||
// Change the power state of the GPS - for power saving / shutdown
|
||||
void setPowerState(GPSPowerState newState, uint32_t sleepMs = 0);
|
||||
|
||||
/// Returns true if we have acquired GPS lock.
|
||||
virtual bool hasLock();
|
||||
@@ -206,18 +205,18 @@ class GPS : private concurrency::OSThread
|
||||
|
||||
GPS_RESPONSE getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis);
|
||||
|
||||
/**
|
||||
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
|
||||
*
|
||||
* calls sleep/wake
|
||||
*/
|
||||
void setAwake(bool on);
|
||||
virtual bool factoryReset();
|
||||
|
||||
// Creates an instance of the GPS class.
|
||||
// Returns the new instance or null if the GPS is not present.
|
||||
static GPS *createGps();
|
||||
|
||||
// Wake the GPS hardware - ready for an update
|
||||
void up();
|
||||
|
||||
// Let the GPS hardware save power between updates
|
||||
void down();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
@@ -240,7 +239,7 @@ class GPS : private concurrency::OSThread
|
||||
*
|
||||
* Return true if we received a valid message from the GPS
|
||||
*/
|
||||
virtual bool whileIdle();
|
||||
virtual bool whileActive();
|
||||
|
||||
/**
|
||||
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
|
||||
@@ -267,13 +266,21 @@ class GPS : private concurrency::OSThread
|
||||
void UBXChecksum(uint8_t *message, size_t length);
|
||||
void CASChecksum(uint8_t *message, size_t length);
|
||||
|
||||
/** Get how long we should stay looking for each aquisition
|
||||
/** Set power with EN pin, if relevant
|
||||
*/
|
||||
uint32_t getWakeTime() const;
|
||||
void writePinEN(bool on);
|
||||
|
||||
/** Get how long we should sleep between aqusition attempts
|
||||
/** Set the value of the STANDBY pin, if relevant
|
||||
*/
|
||||
uint32_t getSleepTime() const;
|
||||
void writePinStandby(bool standby);
|
||||
|
||||
/** Set GPS power with PMU, if relevant
|
||||
*/
|
||||
void setPowerPMU(bool on);
|
||||
|
||||
/** Set UBLOX power, if relevant
|
||||
*/
|
||||
void setPowerUBLOX(bool on, uint32_t sleepMs = 0);
|
||||
|
||||
/**
|
||||
* Tell users we have new GPS readings
|
||||
@@ -296,4 +303,4 @@ class GPS : private concurrency::OSThread
|
||||
};
|
||||
|
||||
extern GPS *gps;
|
||||
#endif // Exclude GPS
|
||||
#endif // Exclude GPS
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
#include "GPSUpdateScheduling.h"
|
||||
|
||||
#include "Default.h"
|
||||
|
||||
// Mark the time when searching for GPS position begins
|
||||
void GPSUpdateScheduling::informSearching()
|
||||
{
|
||||
searchStartedMs = millis();
|
||||
}
|
||||
|
||||
// Mark the time when searching for GPS is complete,
|
||||
// then update the predicted lock-time
|
||||
void GPSUpdateScheduling::informGotLock()
|
||||
{
|
||||
searchEndedMs = millis();
|
||||
LOG_DEBUG("Took %us to get lock\n", (searchEndedMs - searchStartedMs) / 1000);
|
||||
updateLockTimePrediction();
|
||||
}
|
||||
|
||||
// Clear old lock-time prediction data.
|
||||
// When re-enabling GPS with user button.
|
||||
void GPSUpdateScheduling::reset()
|
||||
{
|
||||
searchStartedMs = 0;
|
||||
searchEndedMs = 0;
|
||||
searchCount = 0;
|
||||
predictedMsToGetLock = 0;
|
||||
}
|
||||
|
||||
// How many milliseconds before we should next search for GPS position
|
||||
// Used by GPS hardware directly, to enter timed hardware sleep
|
||||
uint32_t GPSUpdateScheduling::msUntilNextSearch()
|
||||
{
|
||||
uint32_t now = millis();
|
||||
|
||||
// Target interval (seconds), between GPS updates
|
||||
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval, default_gps_update_interval);
|
||||
|
||||
// Check how long until we should start searching, to hopefully hit our target interval
|
||||
uint32_t dueAtMs = searchEndedMs + updateInterval;
|
||||
uint32_t compensatedStart = dueAtMs - predictedMsToGetLock;
|
||||
int32_t remainingMs = compensatedStart - now;
|
||||
|
||||
// If we should have already started (negative value), start ASAP
|
||||
if (remainingMs < 0)
|
||||
remainingMs = 0;
|
||||
|
||||
return (uint32_t)remainingMs;
|
||||
}
|
||||
|
||||
// How long have we already been searching?
|
||||
// Used to abort a search in progress, if it runs unnaceptably long
|
||||
uint32_t GPSUpdateScheduling::elapsedSearchMs()
|
||||
{
|
||||
// If searching
|
||||
if (searchStartedMs > searchEndedMs)
|
||||
return millis() - searchStartedMs;
|
||||
|
||||
// If not searching - 0ms. We shouldn't really consume this value
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Is it now time to begin searching for a GPS position?
|
||||
bool GPSUpdateScheduling::isUpdateDue()
|
||||
{
|
||||
return (msUntilNextSearch() == 0);
|
||||
}
|
||||
|
||||
// Have we been searching for a GPS position for too long?
|
||||
bool GPSUpdateScheduling::searchedTooLong()
|
||||
{
|
||||
uint32_t maxSearchMs =
|
||||
Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs);
|
||||
|
||||
// If broadcast interval set to max, no such thing as "too long"
|
||||
if (maxSearchMs == UINT32_MAX)
|
||||
return false;
|
||||
|
||||
// If we've been searching longer than our position broadcast interval: that's too long
|
||||
else if (elapsedSearchMs() > maxSearchMs)
|
||||
return true;
|
||||
|
||||
// Otherwise, not too long yet!
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Updates the predicted time-to-get-lock, by exponentially smoothing the latest observation
|
||||
void GPSUpdateScheduling::updateLockTimePrediction()
|
||||
{
|
||||
|
||||
// How long did it take to get GPS lock this time?
|
||||
// Duration between down() calls
|
||||
int32_t lockTime = searchEndedMs - searchStartedMs;
|
||||
if (lockTime < 0)
|
||||
lockTime = 0;
|
||||
|
||||
// Ignore the first lock-time: likely to be long, will skew data
|
||||
|
||||
// Second locktime: likely stable. Use to intialize the smoothing filter
|
||||
if (searchCount == 1)
|
||||
predictedMsToGetLock = lockTime;
|
||||
|
||||
// Third locktime and after: predict using exponential smoothing. Respond slowly to changes
|
||||
else if (searchCount > 1)
|
||||
predictedMsToGetLock = (lockTime * weighting) + (predictedMsToGetLock * (1 - weighting));
|
||||
|
||||
searchCount++; // Only tracked so we can diregard initial lock-times
|
||||
|
||||
LOG_DEBUG("Predicting %us to get next lock\n", predictedMsToGetLock / 1000);
|
||||
}
|
||||
|
||||
// How long do we expect to spend searching for a lock?
|
||||
uint32_t GPSUpdateScheduling::predictedSearchDurationMs()
|
||||
{
|
||||
return GPSUpdateScheduling::predictedMsToGetLock;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
|
||||
// Encapsulates code responsible for the timing of GPS updates
|
||||
class GPSUpdateScheduling
|
||||
{
|
||||
public:
|
||||
// Marks the time of these events, for calculation use
|
||||
void informSearching();
|
||||
void informGotLock(); // Predicted lock-time is recalculated here
|
||||
|
||||
void reset(); // Reset the prediction - after GPS::disable() / GPS::enable()
|
||||
bool isUpdateDue(); // Is it time to begin searching for a GPS position?
|
||||
bool searchedTooLong(); // Have we been searching for too long?
|
||||
|
||||
uint32_t msUntilNextSearch(); // How long until we need to begin searching for a GPS? Info provided to GPS hardware for sleep
|
||||
uint32_t elapsedSearchMs(); // How long have we been searching so far?
|
||||
uint32_t predictedSearchDurationMs(); // How long do we expect to spend searching for a lock?
|
||||
|
||||
private:
|
||||
void updateLockTimePrediction(); // Called from informGotLock
|
||||
uint32_t searchStartedMs = 0;
|
||||
uint32_t searchEndedMs = 0;
|
||||
uint32_t searchCount = 0;
|
||||
uint32_t predictedMsToGetLock = 0;
|
||||
|
||||
const float weighting = 0.2; // Controls exponential smoothing of lock-times prediction. 20% weighting of "latest lock-time".
|
||||
};
|
||||
@@ -156,7 +156,8 @@ bool EInkDisplay::connect()
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER)
|
||||
#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213) || \
|
||||
defined(HELTEC_VISION_MASTER_E290)
|
||||
{
|
||||
// Start HSPI
|
||||
hspi = new SPIClass(HSPI);
|
||||
|
||||
@@ -5,11 +5,6 @@
|
||||
#include "GxEPD2_BW.h"
|
||||
#include <OLEDDisplay.h>
|
||||
|
||||
#if defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER)
|
||||
// Re-enable SPI after deep sleep: rtc_gpio_hold_dis()
|
||||
#include "driver/rtc_io.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* An adapter class that allows using the GxEPD2 library as if it was an OLEDDisplay implementation.
|
||||
*
|
||||
@@ -72,7 +67,8 @@ class EInkDisplay : public OLEDDisplay
|
||||
GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> *adafruitDisplay = NULL;
|
||||
|
||||
// If display uses HSPI
|
||||
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0)
|
||||
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \
|
||||
defined(HELTEC_VISION_MASTER_E290)
|
||||
SPIClass *hspi = NULL;
|
||||
#endif
|
||||
|
||||
|
||||
+140
-20
@@ -41,6 +41,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "mesh/Channels.h"
|
||||
#include "mesh/generated/meshtastic/deviceonly.pb.h"
|
||||
#include "meshUtils.h"
|
||||
#include "modules/AdminModule.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
#include "modules/TextMessageModule.h"
|
||||
#include "sleep.h"
|
||||
@@ -1485,6 +1486,10 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
screen->drawColumns(display, x, y, fields);
|
||||
}
|
||||
|
||||
#if defined(ESP_PLATFORM) && defined(USE_ST7789)
|
||||
SPIClass SPI1(HSPI);
|
||||
#endif
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -1492,6 +1497,13 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
||||
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
|
||||
dispdev = new SH1106Wire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
#elif defined(USE_ST7789)
|
||||
#ifdef ESP_PLATFORM
|
||||
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7789_SDA,
|
||||
ST7789_MISO, ST7789_SCK);
|
||||
#else
|
||||
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
|
||||
#endif
|
||||
#elif defined(USE_SSD1306)
|
||||
dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
|
||||
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
|
||||
@@ -1570,7 +1582,14 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
#endif
|
||||
|
||||
dispdev->displayOn();
|
||||
|
||||
#ifdef USE_ST7789
|
||||
#ifdef ESP_PLATFORM
|
||||
analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT);
|
||||
#else
|
||||
pinMode(VTFT_LEDA, OUTPUT);
|
||||
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
|
||||
#endif
|
||||
#endif
|
||||
enabled = true;
|
||||
setInterval(0); // Draw ASAP
|
||||
runASAP = true;
|
||||
@@ -1581,6 +1600,12 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
#endif
|
||||
LOG_INFO("Turning off screen\n");
|
||||
dispdev->displayOff();
|
||||
|
||||
#ifdef USE_ST7789
|
||||
pinMode(VTFT_LEDA, OUTPUT);
|
||||
digitalWrite(VTFT_LEDA, !TFT_BACKLIGHT_ON);
|
||||
#endif
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
PMU->disablePowerOutput(XPOWERS_ALDO2);
|
||||
#endif
|
||||
@@ -1701,6 +1726,7 @@ void Screen::setup()
|
||||
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
||||
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
|
||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||
adminMessageObserver.observe(adminModule);
|
||||
if (textMessageModule)
|
||||
textMessageObserver.observe(textMessageModule);
|
||||
if (inputBroker)
|
||||
@@ -1929,9 +1955,6 @@ void Screen::setWelcomeFrames()
|
||||
/// Determine which screensaver frame to use, then set the FrameCallback
|
||||
void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
|
||||
{
|
||||
// Remember current frame, restore position at power-on
|
||||
uint8_t frameNumber = ui->getUiState()->currentFrame;
|
||||
|
||||
// Retain specified frame / overlay callback beyond scope of this method
|
||||
static FrameCallback screensaverFrame;
|
||||
static OverlayCallback screensaverOverlay;
|
||||
@@ -1969,9 +1992,8 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
|
||||
#endif
|
||||
|
||||
// Prepare now for next frame, shown when display wakes
|
||||
ui->setOverlays(NULL, 0); // Clear overlay
|
||||
setFrames(); // Return to normal display updates
|
||||
ui->switchToFrame(frameNumber); // Attempt to return to same frame after power-on
|
||||
ui->setOverlays(NULL, 0); // Clear overlay
|
||||
setFrames(FOCUS_PRESERVE); // Return to normal display updates, showing same frame as before screensaver, ideally
|
||||
|
||||
// Pick a refresh method, for when display wakes
|
||||
#ifdef EINK_HASQUIRK_GHOSTING
|
||||
@@ -1982,9 +2004,13 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
|
||||
}
|
||||
#endif
|
||||
|
||||
// restore our regular frame list
|
||||
void Screen::setFrames()
|
||||
// Regenerate the normal set of frames, focusing a specific frame if requested
|
||||
// Called when a frame should be added / removed, or custom frames should be cleared
|
||||
void Screen::setFrames(FrameFocus focus)
|
||||
{
|
||||
uint8_t originalPosition = ui->getUiState()->currentFrame;
|
||||
FramesetInfo fsi; // Location of specific frames, for applying focus parameter
|
||||
|
||||
LOG_DEBUG("showing standard frames\n");
|
||||
showingNormalScreen = true;
|
||||
|
||||
@@ -2018,20 +2044,33 @@ void Screen::setFrames()
|
||||
// is the same offset into the moduleFrames vector
|
||||
// so that we can invoke the module's callback
|
||||
for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) {
|
||||
normalFrames[numframes++] = drawModuleFrame;
|
||||
// Draw the module frame, using the hack described above
|
||||
normalFrames[numframes] = drawModuleFrame;
|
||||
|
||||
// Check if the module being drawn has requested focus
|
||||
// We will honor this request later, if setFrames was triggered by a UIFrameEvent
|
||||
MeshModule *m = *i;
|
||||
if (m->isRequestingFocus())
|
||||
fsi.positions.focusedModule = numframes;
|
||||
|
||||
numframes++;
|
||||
}
|
||||
|
||||
LOG_DEBUG("Added modules. numframes: %d\n", numframes);
|
||||
|
||||
// If we have a critical fault, show it first
|
||||
if (error_code)
|
||||
fsi.positions.fault = numframes;
|
||||
if (error_code) {
|
||||
normalFrames[numframes++] = drawCriticalFaultFrame;
|
||||
focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame
|
||||
}
|
||||
|
||||
#ifdef T_WATCH_S3
|
||||
normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame;
|
||||
#endif
|
||||
|
||||
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
|
||||
fsi.positions.textMessage = numframes;
|
||||
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
|
||||
normalFrames[numframes++] = drawTextMessageFrame;
|
||||
}
|
||||
@@ -2046,11 +2085,14 @@ void Screen::setFrames()
|
||||
//
|
||||
// Since frames are basic function pointers, we have to use a helper to
|
||||
// call a method on debugInfo object.
|
||||
fsi.positions.log = numframes;
|
||||
normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline;
|
||||
|
||||
// call a method on debugInfoScreen object (for more details)
|
||||
fsi.positions.settings = numframes;
|
||||
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
|
||||
|
||||
fsi.positions.wifi = numframes;
|
||||
#if HAS_WIFI && !defined(ARCH_PORTDUINO)
|
||||
if (isWifiAvailable()) {
|
||||
// call a method on debugInfoScreen object (for more details)
|
||||
@@ -2058,6 +2100,7 @@ void Screen::setFrames()
|
||||
}
|
||||
#endif
|
||||
|
||||
fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
|
||||
LOG_DEBUG("Finished building frames. numframes: %d\n", numframes);
|
||||
|
||||
ui->setFrames(normalFrames, numframes);
|
||||
@@ -2071,6 +2114,55 @@ void Screen::setFrames()
|
||||
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
|
||||
// just changed)
|
||||
|
||||
// Focus on a specific frame, in the frame set we just created
|
||||
switch (focus) {
|
||||
case FOCUS_DEFAULT:
|
||||
ui->switchToFrame(0); // First frame
|
||||
break;
|
||||
case FOCUS_FAULT:
|
||||
ui->switchToFrame(fsi.positions.fault);
|
||||
break;
|
||||
case FOCUS_TEXTMESSAGE:
|
||||
ui->switchToFrame(fsi.positions.textMessage);
|
||||
break;
|
||||
case FOCUS_MODULE:
|
||||
// Whichever frame was marked by MeshModule::requestFocus(), if any
|
||||
// If no module requested focus, will show the first frame instead
|
||||
ui->switchToFrame(fsi.positions.focusedModule);
|
||||
break;
|
||||
|
||||
case FOCUS_PRESERVE:
|
||||
// If we can identify which type of frame "originalPosition" was, can move directly to it in the new frameset
|
||||
FramesetInfo &oldFsi = this->framesetInfo;
|
||||
if (originalPosition == oldFsi.positions.log)
|
||||
ui->switchToFrame(fsi.positions.log);
|
||||
else if (originalPosition == oldFsi.positions.settings)
|
||||
ui->switchToFrame(fsi.positions.settings);
|
||||
else if (originalPosition == oldFsi.positions.wifi)
|
||||
ui->switchToFrame(fsi.positions.wifi);
|
||||
|
||||
// If frame count has decreased
|
||||
else if (fsi.frameCount < oldFsi.frameCount) {
|
||||
uint8_t numDropped = oldFsi.frameCount - fsi.frameCount;
|
||||
// Move n frames backwards
|
||||
if (numDropped <= originalPosition)
|
||||
ui->switchToFrame(originalPosition - numDropped);
|
||||
// Unless that would put us "out of bounds" (< 0)
|
||||
else
|
||||
ui->switchToFrame(0);
|
||||
}
|
||||
|
||||
// If we're not sure exactly which frame we were on, at least return to the same frame number
|
||||
// (node frames; module frames)
|
||||
else
|
||||
ui->switchToFrame(originalPosition);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Store the info about this frameset, for future setFrames calls
|
||||
this->framesetInfo = fsi;
|
||||
|
||||
setFastFramerate(); // Draw ASAP
|
||||
}
|
||||
|
||||
@@ -2525,7 +2617,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
switch (arg->getStatusType()) {
|
||||
case STATUS_TYPE_NODE:
|
||||
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
|
||||
setFrames(); // Regen the list of screens
|
||||
setFrames(FOCUS_PRESERVE); // Regen the list of screen frames (returning to same frame, if possible)
|
||||
}
|
||||
nodeDB->updateGUI = false;
|
||||
break;
|
||||
@@ -2537,23 +2629,33 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
{
|
||||
if (showingNormalScreen) {
|
||||
setFrames(); // Regen the list of screens (will show new text message)
|
||||
// Outgoing message
|
||||
if (packet->from == 0)
|
||||
setFrames(FOCUS_PRESERVE); // Return to same frame (quietly hiding the rx text message frame)
|
||||
|
||||
// Incoming message
|
||||
else
|
||||
setFrames(FOCUS_TEXTMESSAGE); // Focus on the new message
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Triggered by MeshModules
|
||||
int Screen::handleUIFrameEvent(const UIFrameEvent *event)
|
||||
{
|
||||
if (showingNormalScreen) {
|
||||
if (event->frameChanged) {
|
||||
setFrames(); // Regen the list of screens (will show new text message)
|
||||
} else if (event->needRedraw) {
|
||||
// Regenerate the frameset, potentially honoring a module's internal requestFocus() call
|
||||
if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET)
|
||||
setFrames(FOCUS_MODULE);
|
||||
|
||||
// Regenerate the frameset, while attempting to maintain focus on the current frame
|
||||
else if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND)
|
||||
setFrames(FOCUS_PRESERVE);
|
||||
|
||||
// Don't regenerate the frameset, just re-draw whatever is on screen ASAP
|
||||
else if (event->action == UIFrameEvent::Action::REDRAW_ONLY)
|
||||
setFastFramerate();
|
||||
// TODO: We might also want switch to corresponding frame,
|
||||
// but we don't know the exact frame number.
|
||||
// ui->switchToFrame(0);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -2588,6 +2690,24 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
|
||||
{
|
||||
// Note: only selected admin messages notify this observer
|
||||
// If you wish to handle a new type of message, you should modify AdminModule.cpp first
|
||||
|
||||
switch (arg->which_payload_variant) {
|
||||
// Node removed manually (i.e. via app)
|
||||
case meshtastic_AdminMessage_remove_by_nodenum_tag:
|
||||
setFrames(FOCUS_PRESERVE);
|
||||
break;
|
||||
|
||||
// Default no-op, in case the admin message observable gets used by other classes in future
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
#else
|
||||
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
|
||||
|
||||
+34
-3
@@ -45,6 +45,8 @@ class Screen
|
||||
#include <SH1106Wire.h>
|
||||
#elif defined(USE_SSD1306)
|
||||
#include <SSD1306Wire.h>
|
||||
#elif defined(USE_ST7789)
|
||||
#include <ST7789Spi.h>
|
||||
#else
|
||||
// the SH1106/SSD1306 variant is auto-detected
|
||||
#include <AutoOLEDWire.h>
|
||||
@@ -171,9 +173,11 @@ class Screen : public concurrency::OSThread
|
||||
CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver =
|
||||
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
|
||||
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
|
||||
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent);
|
||||
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules
|
||||
CallbackObserver<Screen, const InputEvent *> inputObserver =
|
||||
CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent);
|
||||
CallbackObserver<Screen, const meshtastic_AdminMessage *> adminMessageObserver =
|
||||
CallbackObserver<Screen, const meshtastic_AdminMessage *>(this, &Screen::handleAdminMessage);
|
||||
|
||||
public:
|
||||
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
|
||||
@@ -392,6 +396,7 @@ class Screen : public concurrency::OSThread
|
||||
int handleTextMessage(const meshtastic_MeshPacket *arg);
|
||||
int handleUIFrameEvent(const UIFrameEvent *arg);
|
||||
int handleInputEvent(const InputEvent *arg);
|
||||
int handleAdminMessage(const meshtastic_AdminMessage *arg);
|
||||
|
||||
/// Used to force (super slow) eink displays to draw critical frames
|
||||
void forceDisplay(bool forceUiUpdate = false);
|
||||
@@ -448,8 +453,34 @@ class Screen : public concurrency::OSThread
|
||||
void handleShowPrevFrame();
|
||||
void handlePrint(const char *text);
|
||||
void handleStartFirmwareUpdateScreen();
|
||||
/// Rebuilds our list of frames (screens) to default ones.
|
||||
void setFrames();
|
||||
|
||||
// Info collected by setFrames method.
|
||||
// Index location of specific frames. Used to apply the FrameFocus parameter of setFrames
|
||||
struct FramesetInfo {
|
||||
struct FramePositions {
|
||||
uint8_t fault = 0;
|
||||
uint8_t textMessage = 0;
|
||||
uint8_t focusedModule = 0;
|
||||
uint8_t log = 0;
|
||||
uint8_t settings = 0;
|
||||
uint8_t wifi = 0;
|
||||
} positions;
|
||||
|
||||
uint8_t frameCount = 0;
|
||||
} framesetInfo;
|
||||
|
||||
// Which frame we want to be displayed, after we regen the frameset by calling setFrames
|
||||
enum FrameFocus : uint8_t {
|
||||
FOCUS_DEFAULT, // No specific frame
|
||||
FOCUS_PRESERVE, // Return to the previous frame
|
||||
FOCUS_FAULT,
|
||||
FOCUS_TEXTMESSAGE,
|
||||
FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
|
||||
};
|
||||
|
||||
// Regenerate the normal set of frames, focusing a specific frame if requested
|
||||
// Call when a frame should be added / removed, or custom frames should be cleared
|
||||
void setFrames(FrameFocus focus = FOCUS_DEFAULT);
|
||||
|
||||
/// Try to start drawing ASAP
|
||||
void setFastFramerate();
|
||||
|
||||
+5
-1
@@ -275,6 +275,9 @@ void setup()
|
||||
digitalWrite(VEXT_ENABLE_V05, 1); // turn on the lora antenna boost
|
||||
digitalWrite(ST7735_BL_V05, 1); // turn on display backligth
|
||||
LOG_DEBUG("HELTEC Detect Tracker V1.1\n");
|
||||
#elif defined(VEXT_ENABLE) && defined(VEXT_ON_VALUE)
|
||||
pinMode(VEXT_ENABLE, OUTPUT);
|
||||
digitalWrite(VEXT_ENABLE, VEXT_ON_VALUE); // turn on the display power
|
||||
#elif defined(VEXT_ENABLE)
|
||||
pinMode(VEXT_ENABLE, OUTPUT);
|
||||
digitalWrite(VEXT_ENABLE, 0); // turn on the display power
|
||||
@@ -713,7 +716,8 @@ void setup()
|
||||
|
||||
// Don't call screen setup until after nodedb is setup (because we need
|
||||
// the current region name)
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS)
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS) || \
|
||||
defined(USE_ST7789)
|
||||
screen->setup();
|
||||
#elif defined(ARCH_PORTDUINO)
|
||||
if (screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#define ONE_MINUTE_MS 60 * 1000
|
||||
|
||||
#define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60)
|
||||
#define default_telemetry_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 30 * 60)
|
||||
#define default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 15 * 60)
|
||||
#define default_wait_bluetooth_secs IF_ROUTER(1, 60)
|
||||
#define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep
|
||||
|
||||
+14
-1
@@ -284,4 +284,17 @@ AdminMessageHandleResult MeshModule::handleAdminMessageForAllModules(const mesht
|
||||
}
|
||||
}
|
||||
return handled;
|
||||
}
|
||||
}
|
||||
|
||||
#if HAS_SCREEN
|
||||
// Would our module like its frame to be focused after Screen::setFrames has regenerated the list of frames?
|
||||
// Only considered if setFrames is triggered by a UIFrameEvent
|
||||
bool MeshModule::isRequestingFocus()
|
||||
{
|
||||
if (_requestingFocus) {
|
||||
_requestingFocus = false; // Consume the request
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
+24
-4
@@ -35,10 +35,16 @@ enum class AdminMessageHandleResult {
|
||||
/*
|
||||
* This struct is used by Screen to figure out whether screen frame should be updated.
|
||||
*/
|
||||
typedef struct _UIFrameEvent {
|
||||
bool frameChanged;
|
||||
bool needRedraw;
|
||||
} UIFrameEvent;
|
||||
struct UIFrameEvent {
|
||||
// What do we actually want to happen?
|
||||
enum Action {
|
||||
REDRAW_ONLY, // Don't change which frames are show, just redraw, asap
|
||||
REGENERATE_FRAMESET, // Regenerate (change? add? remove?) screen frames, honoring requestFocus()
|
||||
REGENERATE_FRAMESET_BACKGROUND, // Regenerate screen frames, attempting to remain on the same frame throughout
|
||||
} action = REDRAW_ONLY;
|
||||
|
||||
// We might want to pass additional data inside this struct at some point
|
||||
};
|
||||
|
||||
/** A baseclass for any mesh "module".
|
||||
*
|
||||
@@ -73,6 +79,7 @@ class MeshModule
|
||||
meshtastic_AdminMessage *response);
|
||||
#if HAS_SCREEN
|
||||
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; }
|
||||
virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset
|
||||
#endif
|
||||
protected:
|
||||
const char *name;
|
||||
@@ -176,6 +183,19 @@ class MeshModule
|
||||
return AdminMessageHandleResult::NOT_HANDLED;
|
||||
};
|
||||
|
||||
#if HAS_SCREEN
|
||||
/** Request that our module's screen frame be focused when Screen::setFrames runs
|
||||
* Only considered if Screen::setFrames is triggered via a UIFrameEvent
|
||||
*
|
||||
* Having this as a separate call, instead of part of the UIFrameEvent, allows the module to delay decision
|
||||
* until drawFrame() is called. This required less restructuring.
|
||||
*/
|
||||
bool _requestingFocus = false;
|
||||
void requestFocus() { _requestingFocus = true; }
|
||||
#else
|
||||
void requestFocus(){}; // No-op
|
||||
#endif
|
||||
|
||||
private:
|
||||
/**
|
||||
* If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow
|
||||
|
||||
@@ -94,7 +94,11 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
|
||||
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user &&
|
||||
nodeInfoModule) {
|
||||
LOG_INFO("Heard a node on channel %d we don't know, sending NodeInfo and asking for a response.\n", mp->channel);
|
||||
nodeInfoModule->sendOurNodeInfo(mp->from, true, mp->channel);
|
||||
if (airTime->isTxAllowedChannelUtil(true)) {
|
||||
nodeInfoModule->sendOurNodeInfo(mp->from, true, mp->channel);
|
||||
} else {
|
||||
LOG_DEBUG("Skip sending NodeInfo due to > 25 percent channel util.\n");
|
||||
}
|
||||
}
|
||||
|
||||
printPacket("Forwarding to phone", mp);
|
||||
|
||||
+2
-1
@@ -268,7 +268,8 @@ void NodeDB::installDefaultConfig()
|
||||
// FIXME: Default to bluetooth capability of platform as default
|
||||
config.bluetooth.enabled = true;
|
||||
config.bluetooth.fixed_pin = defaultBLEPin;
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS)
|
||||
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS) || \
|
||||
defined(USE_ST7789)
|
||||
bool hasScreen = true;
|
||||
#elif ARCH_PORTDUINO
|
||||
bool hasScreen = false;
|
||||
|
||||
@@ -55,7 +55,7 @@ extern const pb_msgdesc_t meshtastic_ChannelSet_msg;
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define MESHTASTIC_MESHTASTIC_APPONLY_PB_H_MAX_SIZE meshtastic_ChannelSet_size
|
||||
#define meshtastic_ChannelSet_size 674
|
||||
#define meshtastic_ChannelSet_size 676
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -497,6 +497,8 @@ typedef struct _meshtastic_Config_LoRaConfig {
|
||||
Please respect your local laws and regulations. If you are a HAM, make sure you
|
||||
enable HAM mode and turn off encryption. */
|
||||
float override_frequency;
|
||||
/* If true, disable the build-in PA FAN using pin define in RF95_FAN_EN. */
|
||||
bool pa_fan_disabled;
|
||||
/* For testing it is useful sometimes to force a node to never listen to
|
||||
particular other nodes (simulating radio out of range). All nodenums listed
|
||||
in ignore_incoming will have packets they send dropped on receive (by router.cpp) */
|
||||
@@ -618,7 +620,7 @@ extern "C" {
|
||||
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
|
||||
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
|
||||
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
|
||||
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
|
||||
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
|
||||
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0}
|
||||
#define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}}
|
||||
#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
|
||||
@@ -627,7 +629,7 @@ extern "C" {
|
||||
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
|
||||
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
|
||||
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
|
||||
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
|
||||
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
|
||||
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
@@ -702,6 +704,7 @@ extern "C" {
|
||||
#define meshtastic_Config_LoRaConfig_override_duty_cycle_tag 12
|
||||
#define meshtastic_Config_LoRaConfig_sx126x_rx_boosted_gain_tag 13
|
||||
#define meshtastic_Config_LoRaConfig_override_frequency_tag 14
|
||||
#define meshtastic_Config_LoRaConfig_pa_fan_disabled_tag 15
|
||||
#define meshtastic_Config_LoRaConfig_ignore_incoming_tag 103
|
||||
#define meshtastic_Config_LoRaConfig_ignore_mqtt_tag 104
|
||||
#define meshtastic_Config_BluetoothConfig_enabled_tag 1
|
||||
@@ -832,6 +835,7 @@ X(a, STATIC, SINGULAR, UINT32, channel_num, 11) \
|
||||
X(a, STATIC, SINGULAR, BOOL, override_duty_cycle, 12) \
|
||||
X(a, STATIC, SINGULAR, BOOL, sx126x_rx_boosted_gain, 13) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, override_frequency, 14) \
|
||||
X(a, STATIC, SINGULAR, BOOL, pa_fan_disabled, 15) \
|
||||
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
|
||||
X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104)
|
||||
#define meshtastic_Config_LoRaConfig_CALLBACK NULL
|
||||
@@ -871,7 +875,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg;
|
||||
#define meshtastic_Config_BluetoothConfig_size 12
|
||||
#define meshtastic_Config_DeviceConfig_size 100
|
||||
#define meshtastic_Config_DisplayConfig_size 30
|
||||
#define meshtastic_Config_LoRaConfig_size 80
|
||||
#define meshtastic_Config_LoRaConfig_size 82
|
||||
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
|
||||
#define meshtastic_Config_NetworkConfig_size 196
|
||||
#define meshtastic_Config_PositionConfig_size 62
|
||||
|
||||
@@ -307,7 +307,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg;
|
||||
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size
|
||||
#define meshtastic_ChannelFile_size 718
|
||||
#define meshtastic_NodeInfoLite_size 166
|
||||
#define meshtastic_OEMStore_size 3384
|
||||
#define meshtastic_OEMStore_size 3388
|
||||
#define meshtastic_PositionLite_size 28
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -181,8 +181,8 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size
|
||||
#define meshtastic_LocalConfig_size 553
|
||||
#define meshtastic_LocalModuleConfig_size 685
|
||||
#define meshtastic_LocalConfig_size 555
|
||||
#define meshtastic_LocalModuleConfig_size 687
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -60,7 +60,9 @@ typedef enum _meshtastic_ModuleConfig_SerialConfig_Serial_Mode {
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG = 3,
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA = 4,
|
||||
/* NMEA messages specifically tailored for CalTopo */
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO = 5
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO = 5,
|
||||
/* Ecowitt WS85 weather station */
|
||||
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85 = 6
|
||||
} meshtastic_ModuleConfig_SerialConfig_Serial_Mode;
|
||||
|
||||
/* TODO: REPLACE */
|
||||
@@ -273,6 +275,8 @@ typedef struct _meshtastic_ModuleConfig_StoreForwardConfig {
|
||||
uint32_t history_return_max;
|
||||
/* TODO: REPLACE */
|
||||
uint32_t history_return_window;
|
||||
/* Set to true to let this node act as a server that stores received messages and resends them upon request. */
|
||||
bool is_server;
|
||||
} meshtastic_ModuleConfig_StoreForwardConfig;
|
||||
|
||||
/* Preferences for the RangeTestModule */
|
||||
@@ -432,8 +436,8 @@ extern "C" {
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Baud)(meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600+1))
|
||||
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MAX meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Mode)(meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO+1))
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MAX meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85
|
||||
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Mode)(meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85+1))
|
||||
|
||||
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE
|
||||
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MAX meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK
|
||||
@@ -474,7 +478,7 @@ extern "C" {
|
||||
#define meshtastic_ModuleConfig_PaxcounterConfig_init_default {0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0}
|
||||
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_RangeTestConfig_init_default {0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
|
||||
@@ -490,7 +494,7 @@ extern "C" {
|
||||
#define meshtastic_ModuleConfig_PaxcounterConfig_init_zero {0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0}
|
||||
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_RangeTestConfig_init_zero {0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
|
||||
@@ -560,6 +564,7 @@ extern "C" {
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_records_tag 3
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_history_return_max_tag 4
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_history_return_window_tag 5
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_is_server_tag 6
|
||||
#define meshtastic_ModuleConfig_RangeTestConfig_enabled_tag 1
|
||||
#define meshtastic_ModuleConfig_RangeTestConfig_sender_tag 2
|
||||
#define meshtastic_ModuleConfig_RangeTestConfig_save_tag 3
|
||||
@@ -743,7 +748,8 @@ X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
|
||||
X(a, STATIC, SINGULAR, BOOL, heartbeat, 2) \
|
||||
X(a, STATIC, SINGULAR, UINT32, records, 3) \
|
||||
X(a, STATIC, SINGULAR, UINT32, history_return_max, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, history_return_window, 5)
|
||||
X(a, STATIC, SINGULAR, UINT32, history_return_window, 5) \
|
||||
X(a, STATIC, SINGULAR, BOOL, is_server, 6)
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_CALLBACK NULL
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_DEFAULT NULL
|
||||
|
||||
@@ -848,7 +854,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
|
||||
#define meshtastic_ModuleConfig_RangeTestConfig_size 10
|
||||
#define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96
|
||||
#define meshtastic_ModuleConfig_SerialConfig_size 28
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_size 22
|
||||
#define meshtastic_ModuleConfig_StoreForwardConfig_size 24
|
||||
#define meshtastic_ModuleConfig_TelemetryConfig_size 36
|
||||
#define meshtastic_ModuleConfig_size 257
|
||||
#define meshtastic_RemoteHardwarePin_size 21
|
||||
|
||||
@@ -115,6 +115,10 @@ typedef struct _meshtastic_EnvironmentMetrics {
|
||||
float wind_speed;
|
||||
/* Weight in KG */
|
||||
float weight;
|
||||
/* Wind gust in m/s */
|
||||
float wind_gust;
|
||||
/* Wind lull in m/s */
|
||||
float wind_lull;
|
||||
} meshtastic_EnvironmentMetrics;
|
||||
|
||||
/* Power Metrics (voltage / current / etc) */
|
||||
@@ -205,13 +209,13 @@ extern "C" {
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0}
|
||||
#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
|
||||
#define meshtastic_Nau7802Config_init_default {0, 0}
|
||||
#define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0}
|
||||
#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}}
|
||||
@@ -238,6 +242,8 @@ extern "C" {
|
||||
#define meshtastic_EnvironmentMetrics_wind_direction_tag 13
|
||||
#define meshtastic_EnvironmentMetrics_wind_speed_tag 14
|
||||
#define meshtastic_EnvironmentMetrics_weight_tag 15
|
||||
#define meshtastic_EnvironmentMetrics_wind_gust_tag 16
|
||||
#define meshtastic_EnvironmentMetrics_wind_lull_tag 17
|
||||
#define meshtastic_PowerMetrics_ch1_voltage_tag 1
|
||||
#define meshtastic_PowerMetrics_ch1_current_tag 2
|
||||
#define meshtastic_PowerMetrics_ch2_voltage_tag 3
|
||||
@@ -289,7 +295,9 @@ X(a, STATIC, SINGULAR, FLOAT, ir_lux, 11) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, uv_lux, 12) \
|
||||
X(a, STATIC, SINGULAR, UINT32, wind_direction, 13) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, weight, 15)
|
||||
X(a, STATIC, SINGULAR, FLOAT, weight, 15) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, wind_gust, 16) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, wind_lull, 17)
|
||||
#define meshtastic_EnvironmentMetrics_CALLBACK NULL
|
||||
#define meshtastic_EnvironmentMetrics_DEFAULT NULL
|
||||
|
||||
@@ -357,10 +365,10 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
|
||||
#define MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_MAX_SIZE meshtastic_Telemetry_size
|
||||
#define meshtastic_AirQualityMetrics_size 72
|
||||
#define meshtastic_DeviceMetrics_size 27
|
||||
#define meshtastic_EnvironmentMetrics_size 73
|
||||
#define meshtastic_EnvironmentMetrics_size 85
|
||||
#define meshtastic_Nau7802Config_size 16
|
||||
#define meshtastic_PowerMetrics_size 30
|
||||
#define meshtastic_Telemetry_size 80
|
||||
#define meshtastic_Telemetry_size 92
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
|
||||
@@ -200,6 +200,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
case meshtastic_AdminMessage_remove_by_nodenum_tag: {
|
||||
LOG_INFO("Client is receiving a remove_nodenum command.\n");
|
||||
nodeDB->removeNodeByNum(r->remove_by_nodenum);
|
||||
this->notifyObservers(r); // Observed by screen
|
||||
break;
|
||||
}
|
||||
case meshtastic_AdminMessage_set_favorite_node_tag: {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
/**
|
||||
* Admin module for admin messages
|
||||
*/
|
||||
class AdminModule : public ProtobufModule<meshtastic_AdminMessage>
|
||||
class AdminModule : public ProtobufModule<meshtastic_AdminMessage>, public Observable<const meshtastic_AdminMessage *>
|
||||
{
|
||||
public:
|
||||
/** Constructor
|
||||
|
||||
@@ -148,8 +148,9 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
||||
if (this->currentMessageIndex == 0) {
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
|
||||
|
||||
UIFrameEvent e = {false, true};
|
||||
e.frameChanged = true;
|
||||
requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
this->notifyObservers(&e);
|
||||
|
||||
return 0;
|
||||
@@ -166,8 +167,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
||||
}
|
||||
}
|
||||
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) {
|
||||
UIFrameEvent e = {false, true};
|
||||
e.frameChanged = true;
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
this->currentMessageIndex = -1;
|
||||
|
||||
#if !defined(T_WATCH_S3) && !defined(RAK14014)
|
||||
@@ -353,6 +354,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
|
||||
}
|
||||
|
||||
if (validEvent) {
|
||||
requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs
|
||||
|
||||
// Let runOnce to be called immediately.
|
||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) {
|
||||
setIntervalFromNow(0); // on fast keypresses, this isn't fast enough.
|
||||
@@ -378,6 +381,11 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
|
||||
p->decoded.payload.size++;
|
||||
}
|
||||
|
||||
// Only receive routing messages when expecting ACK for a canned message
|
||||
// Prevents the canned message module from regenerating the screen's frameset at unexpected times,
|
||||
// or raising a UIFrameEvent before another module has the chance
|
||||
this->waitingForAck = true;
|
||||
|
||||
LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
|
||||
|
||||
service.sendToMesh(
|
||||
@@ -393,13 +401,13 @@ int32_t CannedMessageModule::runOnce()
|
||||
return INT32_MAX;
|
||||
}
|
||||
// LOG_DEBUG("Check status\n");
|
||||
UIFrameEvent e = {false, true};
|
||||
UIFrameEvent e;
|
||||
if ((this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) ||
|
||||
(this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) || (this->runState == CANNED_MESSAGE_RUN_STATE_MESSAGE)) {
|
||||
// TODO: might have some feedback of sending state
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
temporaryMessage = "";
|
||||
e.frameChanged = true;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
this->currentMessageIndex = -1;
|
||||
this->freetext = ""; // clear freetext
|
||||
this->cursor = 0;
|
||||
@@ -412,7 +420,7 @@ int32_t CannedMessageModule::runOnce()
|
||||
} else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) &&
|
||||
((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) {
|
||||
// Reset module
|
||||
e.frameChanged = true;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
this->currentMessageIndex = -1;
|
||||
this->freetext = ""; // clear freetext
|
||||
this->cursor = 0;
|
||||
@@ -449,7 +457,7 @@ int32_t CannedMessageModule::runOnce()
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
}
|
||||
}
|
||||
e.frameChanged = true;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
this->currentMessageIndex = -1;
|
||||
this->freetext = ""; // clear freetext
|
||||
this->cursor = 0;
|
||||
@@ -463,7 +471,7 @@ int32_t CannedMessageModule::runOnce()
|
||||
} else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) {
|
||||
this->currentMessageIndex = 0;
|
||||
LOG_DEBUG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
|
||||
e.frameChanged = true;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
|
||||
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) {
|
||||
if (this->messagesCount > 0) {
|
||||
@@ -567,7 +575,7 @@ int32_t CannedMessageModule::runOnce()
|
||||
break;
|
||||
}
|
||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
||||
e.frameChanged = true;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
switch (this->payload) { // code below all trigger the freetext window (where you type to send a message) or reset the
|
||||
// display back to the default window
|
||||
case 0x08: // backspace
|
||||
@@ -706,8 +714,8 @@ int CannedMessageModule::getPrevIndex()
|
||||
void CannedMessageModule::showTemporaryMessage(const String &message)
|
||||
{
|
||||
temporaryMessage = message;
|
||||
UIFrameEvent e = {false, true};
|
||||
e.frameChanged = true;
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
notifyObservers(&e);
|
||||
runState = CANNED_MESSAGE_RUN_STATE_MESSAGE;
|
||||
// run this loop again in 2 seconds, next iteration will clear the display
|
||||
@@ -914,11 +922,13 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
char buffer[50];
|
||||
|
||||
if (temporaryMessage.length() != 0) {
|
||||
requestFocus(); // Tell Screen::setFrames to move to our module's frame
|
||||
LOG_DEBUG("Drawing temporary message: %s", temporaryMessage.c_str());
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage);
|
||||
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) {
|
||||
requestFocus(); // Tell Screen::setFrames to move to our module's frame
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
String displayString;
|
||||
@@ -940,6 +950,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi);
|
||||
}
|
||||
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
|
||||
requestFocus(); // Tell Screen::setFrames to move to our module's frame
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending...");
|
||||
@@ -948,7 +959,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled.");
|
||||
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
||||
|
||||
requestFocus(); // Tell Screen::setFrames to move to our module's frame
|
||||
#if defined(T_WATCH_S3) || defined(RAK14014)
|
||||
drawKeyboard(display, state, 0, 0);
|
||||
#else
|
||||
@@ -1030,16 +1041,18 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
|
||||
ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
|
||||
{
|
||||
if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP) {
|
||||
if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP && waitingForAck) {
|
||||
// look for a request_id
|
||||
if (mp.decoded.request_id != 0) {
|
||||
UIFrameEvent e = {false, true};
|
||||
e.frameChanged = true;
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
requestFocus(); // Tell Screen::setFrames that our module's frame should be shown, even if not "first" in the frameset
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED;
|
||||
this->incoming = service.getNodenumFromRequestId(mp.decoded.request_id);
|
||||
meshtastic_Routing decoded = meshtastic_Routing_init_default;
|
||||
pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded);
|
||||
this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE;
|
||||
waitingForAck = false; // No longer want routing packets
|
||||
this->notifyObservers(&e);
|
||||
// run the next time 2 seconds later
|
||||
setIntervalFromNow(2000);
|
||||
|
||||
@@ -81,9 +81,8 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
|
||||
}
|
||||
|
||||
switch (p->decoded.portnum) {
|
||||
case meshtastic_PortNum_TEXT_MESSAGE_APP:
|
||||
case meshtastic_PortNum_ROUTING_APP:
|
||||
return true;
|
||||
return waitingForAck;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -140,7 +139,8 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
|
||||
uint8_t numChannels = 0;
|
||||
ChannelIndex indexChannels[MAX_NUM_CHANNELS] = {0};
|
||||
NodeNum incoming = NODENUM_BROADCAST;
|
||||
bool ack = false; // True means ACK, false means NAK (error_reason != NONE)
|
||||
bool ack = false; // True means ACK, false means NAK (error_reason != NONE)
|
||||
bool waitingForAck = false; // Are currently interested in routing packets?
|
||||
float lastRxSnr = 0;
|
||||
int32_t lastRxRssi = 0;
|
||||
|
||||
|
||||
@@ -47,7 +47,8 @@ int32_t AirQualityTelemetryModule::runOnce()
|
||||
|
||||
uint32_t now = millis();
|
||||
if (((lastSentToMesh == 0) ||
|
||||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.air_quality_interval))) &&
|
||||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.air_quality_interval,
|
||||
default_telemetry_broadcast_interval_secs))) &&
|
||||
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
|
||||
airTime->isTxAllowedAirUtil()) {
|
||||
sendTelemetry();
|
||||
|
||||
@@ -17,7 +17,8 @@ int32_t DeviceTelemetryModule::runOnce()
|
||||
{
|
||||
refreshUptime();
|
||||
if (((lastSentToMesh == 0) ||
|
||||
((uptimeLastMs - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval))) &&
|
||||
((uptimeLastMs - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval,
|
||||
default_telemetry_broadcast_interval_secs))) &&
|
||||
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
|
||||
airTime->isTxAllowedAirUtil() && config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) {
|
||||
|
||||
@@ -69,7 +69,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
|
||||
{
|
||||
if (sleepOnNextExecution == true) {
|
||||
sleepOnNextExecution = false;
|
||||
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval);
|
||||
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval,
|
||||
default_telemetry_broadcast_interval_secs);
|
||||
LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs);
|
||||
doDeepSleep(nightyNightMs, true);
|
||||
}
|
||||
@@ -124,6 +125,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
|
||||
result = ina219Sensor.runOnce();
|
||||
if (ina260Sensor.hasSensor())
|
||||
result = ina260Sensor.runOnce();
|
||||
if (ina3221Sensor.hasSensor())
|
||||
result = ina3221Sensor.runOnce();
|
||||
if (veml7700Sensor.hasSensor())
|
||||
result = veml7700Sensor.runOnce();
|
||||
if (tsl2591Sensor.hasSensor())
|
||||
@@ -152,7 +155,8 @@ int32_t EnvironmentTelemetryModule::runOnce()
|
||||
|
||||
uint32_t now = millis();
|
||||
if (((lastSentToMesh == 0) ||
|
||||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval))) &&
|
||||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval,
|
||||
default_telemetry_broadcast_interval_secs))) &&
|
||||
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
|
||||
airTime->isTxAllowedAirUtil()) {
|
||||
sendTelemetry();
|
||||
@@ -339,6 +343,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m
|
||||
valid = valid && ina260Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (ina3221Sensor.hasSensor()) {
|
||||
valid = valid && ina3221Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (veml7700Sensor.hasSensor()) {
|
||||
valid = valid && veml7700Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
@@ -514,6 +522,11 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
|
||||
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||
return result;
|
||||
}
|
||||
if (ina3221Sensor.hasSensor()) {
|
||||
result = ina3221Sensor.handleAdminMessage(mp, request, response);
|
||||
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||
return result;
|
||||
}
|
||||
if (veml7700Sensor.hasSensor()) {
|
||||
result = veml7700Sensor.handleAdminMessage(mp, request, response);
|
||||
if (result != AdminMessageHandleResult::NOT_HANDLED)
|
||||
|
||||
@@ -24,7 +24,8 @@ int32_t PowerTelemetryModule::runOnce()
|
||||
{
|
||||
if (sleepOnNextExecution == true) {
|
||||
sleepOnNextExecution = false;
|
||||
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval);
|
||||
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval,
|
||||
default_telemetry_broadcast_interval_secs);
|
||||
LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs);
|
||||
doDeepSleep(nightyNightMs, true);
|
||||
}
|
||||
@@ -70,7 +71,8 @@ int32_t PowerTelemetryModule::runOnce()
|
||||
|
||||
uint32_t now = millis();
|
||||
if (((lastSentToMesh == 0) ||
|
||||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval))) &&
|
||||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval,
|
||||
default_telemetry_broadcast_interval_secs))) &&
|
||||
airTime->isTxAllowedAirUtil()) {
|
||||
sendTelemetry();
|
||||
lastSentToMesh = now;
|
||||
|
||||
@@ -27,22 +27,69 @@ int32_t INA3221Sensor::runOnce()
|
||||
|
||||
void INA3221Sensor::setup() {}
|
||||
|
||||
struct _INA3221Measurement INA3221Sensor::getMeasurement(ina3221_ch_t ch)
|
||||
{
|
||||
struct _INA3221Measurement measurement;
|
||||
|
||||
measurement.voltage = ina3221.getVoltage(ch);
|
||||
measurement.current = ina3221.getCurrent(ch);
|
||||
|
||||
return measurement;
|
||||
}
|
||||
|
||||
struct _INA3221Measurements INA3221Sensor::getMeasurements()
|
||||
{
|
||||
struct _INA3221Measurements measurements;
|
||||
|
||||
// INA3221 has 3 channels starting from 0
|
||||
for (int i = 0; i < 3; i++) {
|
||||
measurements.measurements[i] = getMeasurement((ina3221_ch_t)i);
|
||||
}
|
||||
|
||||
return measurements;
|
||||
}
|
||||
|
||||
bool INA3221Sensor::getMetrics(meshtastic_Telemetry *measurement)
|
||||
{
|
||||
measurement->variant.environment_metrics.voltage = ina3221.getVoltage(INA3221_CH1);
|
||||
measurement->variant.environment_metrics.current = ina3221.getCurrent(INA3221_CH1);
|
||||
measurement->variant.power_metrics.ch1_voltage = ina3221.getVoltage(INA3221_CH1);
|
||||
measurement->variant.power_metrics.ch1_current = ina3221.getCurrent(INA3221_CH1);
|
||||
measurement->variant.power_metrics.ch2_voltage = ina3221.getVoltage(INA3221_CH2);
|
||||
measurement->variant.power_metrics.ch2_current = ina3221.getCurrent(INA3221_CH2);
|
||||
measurement->variant.power_metrics.ch3_voltage = ina3221.getVoltage(INA3221_CH3);
|
||||
measurement->variant.power_metrics.ch3_current = ina3221.getCurrent(INA3221_CH3);
|
||||
switch (measurement->which_variant) {
|
||||
case meshtastic_Telemetry_environment_metrics_tag:
|
||||
return getEnvironmentMetrics(measurement);
|
||||
|
||||
case meshtastic_Telemetry_power_metrics_tag:
|
||||
return getPowerMetrics(measurement);
|
||||
}
|
||||
|
||||
// unsupported metric
|
||||
return false;
|
||||
}
|
||||
|
||||
bool INA3221Sensor::getEnvironmentMetrics(meshtastic_Telemetry *measurement)
|
||||
{
|
||||
struct _INA3221Measurement m = getMeasurement(ENV_CH);
|
||||
|
||||
measurement->variant.environment_metrics.voltage = m.voltage;
|
||||
measurement->variant.environment_metrics.current = m.current;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool INA3221Sensor::getPowerMetrics(meshtastic_Telemetry *measurement)
|
||||
{
|
||||
struct _INA3221Measurements m = getMeasurements();
|
||||
|
||||
measurement->variant.power_metrics.ch1_voltage = m.measurements[INA3221_CH1].voltage;
|
||||
measurement->variant.power_metrics.ch1_current = m.measurements[INA3221_CH1].current;
|
||||
measurement->variant.power_metrics.ch2_voltage = m.measurements[INA3221_CH2].voltage;
|
||||
measurement->variant.power_metrics.ch2_current = m.measurements[INA3221_CH2].current;
|
||||
measurement->variant.power_metrics.ch3_voltage = m.measurements[INA3221_CH3].voltage;
|
||||
measurement->variant.power_metrics.ch3_current = m.measurements[INA3221_CH3].current;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t INA3221Sensor::getBusVoltageMv()
|
||||
{
|
||||
return lround(ina3221.getVoltage(INA3221_CH1) * 1000);
|
||||
return lround(ina3221.getVoltage(BAT_CH) * 1000);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -12,6 +12,21 @@ class INA3221Sensor : public TelemetrySensor, VoltageSensor
|
||||
private:
|
||||
INA3221 ina3221 = INA3221(INA3221_ADDR42_SDA);
|
||||
|
||||
// channel to report voltage/current for environment metrics
|
||||
ina3221_ch_t ENV_CH = INA3221_CH1;
|
||||
|
||||
// channel to report battery voltage for device_battery_ina_address
|
||||
ina3221_ch_t BAT_CH = INA3221_CH1;
|
||||
|
||||
// get a single measurement for a channel
|
||||
struct _INA3221Measurement getMeasurement(ina3221_ch_t ch);
|
||||
|
||||
// get all measurements for all channels
|
||||
struct _INA3221Measurements getMeasurements();
|
||||
|
||||
bool getEnvironmentMetrics(meshtastic_Telemetry *measurement);
|
||||
bool getPowerMetrics(meshtastic_Telemetry *measurement);
|
||||
|
||||
protected:
|
||||
void setup() override;
|
||||
|
||||
@@ -22,4 +37,14 @@ class INA3221Sensor : public TelemetrySensor, VoltageSensor
|
||||
virtual uint16_t getBusVoltageMv() override;
|
||||
};
|
||||
|
||||
struct _INA3221Measurement {
|
||||
float voltage;
|
||||
float current;
|
||||
};
|
||||
|
||||
struct _INA3221Measurements {
|
||||
// INA3221 has 3 channels
|
||||
struct _INA3221Measurement measurements[3];
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -16,15 +16,31 @@ ProcessMessage WaypointModule::handleReceived(const meshtastic_MeshPacket &mp)
|
||||
auto &p = mp.decoded;
|
||||
LOG_INFO("Received waypoint msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);
|
||||
#endif
|
||||
UIFrameEvent e = {true, true};
|
||||
// We only store/display messages destined for us.
|
||||
// Keep a copy of the most recent text message.
|
||||
devicestate.rx_waypoint = mp;
|
||||
devicestate.has_rx_waypoint = true;
|
||||
|
||||
powerFSM.trigger(EVENT_RECEIVED_MSG);
|
||||
|
||||
#if HAS_SCREEN
|
||||
|
||||
UIFrameEvent e;
|
||||
|
||||
// New or updated waypoint: focus on this frame next time Screen::setFrames runs
|
||||
if (shouldDraw()) {
|
||||
requestFocus();
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
}
|
||||
|
||||
// Deleting an old waypoint: remove the frame quietly, don't change frame position if possible
|
||||
else
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND;
|
||||
|
||||
notifyObservers(&e);
|
||||
|
||||
#endif
|
||||
|
||||
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
|
||||
@@ -190,13 +190,13 @@ int32_t AudioModule::runOnce()
|
||||
|
||||
firstTime = false;
|
||||
} else {
|
||||
UIFrameEvent e = {false, true};
|
||||
UIFrameEvent e;
|
||||
// Check if PTT is pressed. TODO hook that into Onebutton/Interrupt drive.
|
||||
if (digitalRead(moduleConfig.audio.ptt_pin ? moduleConfig.audio.ptt_pin : PTT_PIN) == HIGH) {
|
||||
if (radio_state == RadioState::rx) {
|
||||
LOG_INFO("PTT pressed, switching to TX\n");
|
||||
radio_state = RadioState::tx;
|
||||
e.frameChanged = true;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
} else {
|
||||
@@ -209,7 +209,7 @@ int32_t AudioModule::runOnce()
|
||||
}
|
||||
tx_encode_frame_index = sizeof(tx_header);
|
||||
radio_state = RadioState::rx;
|
||||
e.frameChanged = true;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
|
||||
this->notifyObservers(&e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ int32_t PaxcounterModule::runOnce()
|
||||
sendInfo(NODENUM_BROADCAST);
|
||||
}
|
||||
return Default::getConfiguredOrDefaultMs(moduleConfig.paxcounter.paxcounter_update_interval,
|
||||
default_broadcast_interval_secs);
|
||||
default_telemetry_broadcast_interval_secs);
|
||||
} else {
|
||||
return disable();
|
||||
}
|
||||
|
||||
@@ -151,6 +151,14 @@
|
||||
#define HW_VENDOR meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO
|
||||
#elif defined(HELTEC_CAPSULE_SENSOR_V3)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_CAPSULE_SENSOR_V3
|
||||
#elif defined(HELTEC_VISION_MASTER_T190)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_VISION_MASTER_T190
|
||||
#elif defined(HELTEC_VISION_MASTER_E213)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_VISION_MASTER_E213
|
||||
#elif defined(HELTEC_VISION_MASTER_E290)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_VISION_MASTER_E290
|
||||
#elif defined(HELTEC_MESH_NODE_T114)
|
||||
#define HW_VENDOR meshtastic_HardwareModel_HELTEC_MESH_NODE_T114
|
||||
#endif
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
@@ -19,6 +19,7 @@ static BLEBas blebas; // BAS (Battery Service) helper class instance
|
||||
static BLEDfu bledfu; // DFU software update helper service
|
||||
static BLEDfuSecure bledfusecure; // DFU software update helper service
|
||||
|
||||
static BLEDfu bledfu; // DFU software update helper service
|
||||
// This scratch buffer is used for various bluetooth reads/writes - but it is safe because only one bt operation can be in
|
||||
// process at once
|
||||
// static uint8_t trBytes[_max(_max(_max(_max(ToRadio_size, RadioConfig_size), User_size), MyNodeInfo_size), FromRadio_size)];
|
||||
|
||||
@@ -234,6 +234,18 @@ void cpuDeepSleep(uint32_t msecToWake)
|
||||
// RAK-12039 set pin for Air quality sensor
|
||||
digitalWrite(AQ_SET_PIN, LOW);
|
||||
#endif
|
||||
#ifdef RAK14014
|
||||
// GPIO restores input status, otherwise there will be leakage current
|
||||
nrf_gpio_cfg_default(TFT_BL);
|
||||
nrf_gpio_cfg_default(TFT_DC);
|
||||
nrf_gpio_cfg_default(TFT_CS);
|
||||
nrf_gpio_cfg_default(TFT_SCLK);
|
||||
nrf_gpio_cfg_default(TFT_MOSI);
|
||||
nrf_gpio_cfg_default(TFT_MISO);
|
||||
nrf_gpio_cfg_default(SCREEN_TOUCH_INT);
|
||||
nrf_gpio_cfg_default(WB_I2C1_SCL);
|
||||
nrf_gpio_cfg_default(WB_I2C1_SDA);
|
||||
#endif
|
||||
#endif
|
||||
// Sleepy trackers or sensors can low power "sleep"
|
||||
// Don't enter this if we're sleeping portMAX_DELAY, since that's a shutdown event
|
||||
@@ -274,5 +286,10 @@ void clearBonds()
|
||||
|
||||
void enterDfuMode()
|
||||
{
|
||||
// SDK kit does not have native USB like almost all other NRF52 boards
|
||||
#ifdef NRF_USE_SERIAL_DFU
|
||||
enterSerialDfu();
|
||||
#else
|
||||
enterUf2Dfu();
|
||||
#endif
|
||||
}
|
||||
+3
-3
@@ -37,10 +37,7 @@ Observable<void *> preflightSleep;
|
||||
|
||||
/// Called to tell observers we are now entering sleep and you should prepare. Must return 0
|
||||
/// notifySleep will be called for light or deep sleep, notifyDeepSleep is only called for deep sleep
|
||||
/// notifyGPSSleep will be called when config.position.gps_enabled is set to 0 or from buttonthread when GPS_POWER_TOGGLE is
|
||||
/// enabled.
|
||||
Observable<void *> notifySleep, notifyDeepSleep;
|
||||
Observable<void *> notifyGPSSleep;
|
||||
|
||||
// deep sleep support
|
||||
RTC_DATA_ATTR int bootCount = 0;
|
||||
@@ -241,6 +238,7 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false)
|
||||
pinMode(PIN_POWER_EN, INPUT); // power off peripherals
|
||||
// pinMode(PIN_POWER_EN1, INPUT_PULLDOWN);
|
||||
#endif
|
||||
|
||||
#if HAS_GPS
|
||||
// Kill GPS power completely (even if previously we just had it in sleep mode)
|
||||
if (gps)
|
||||
@@ -275,6 +273,8 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false)
|
||||
#elif defined(VEXT_ENABLE_V05)
|
||||
digitalWrite(VEXT_ENABLE_V05, 0); // turn off the lora amplifier power
|
||||
digitalWrite(ST7735_BL_V05, 0); // turn off the display power
|
||||
#elif defined(VEXT_ENABLE) && defined(VEXT_ON_VALUE)
|
||||
digitalWrite(VEXT_ENABLE, !VEXT_ON_VALUE); // turn on the display power
|
||||
#elif defined(VEXT_ENABLE)
|
||||
digitalWrite(VEXT_ENABLE, 1); // turn off the display power
|
||||
#endif
|
||||
|
||||
@@ -41,8 +41,6 @@ extern Observable<void *> notifySleep;
|
||||
/// Called to tell observers we are now entering (deep) sleep and you should prepare. Must return 0
|
||||
extern Observable<void *> notifyDeepSleep;
|
||||
|
||||
/// Called to tell GPS thread to enter deep sleep independently of LoRa/MCU sleep, prior to full poweroff. Must return 0
|
||||
extern Observable<void *> notifyGPSSleep;
|
||||
void enableModemSleep();
|
||||
#ifdef ARCH_ESP32
|
||||
void enableLoraInterrupt();
|
||||
|
||||
Reference in New Issue
Block a user