mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-16 07:42:37 +00:00
Sync Wio lr1110 refresh with master (#4251)
* 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 --------- 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>
This commit is contained in:
1
.github/workflows/build_raspbian.yml
vendored
1
.github/workflows/build_raspbian.yml
vendored
@@ -13,6 +13,7 @@ jobs:
|
||||
- name: Install libbluetooth
|
||||
shell: bash
|
||||
run: |
|
||||
apt-get update -y
|
||||
apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev
|
||||
|
||||
- name: Checkout code
|
||||
|
||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -4,5 +4,8 @@
|
||||
"trunk.enableWindows": true,
|
||||
"files.insertFinalNewline": false,
|
||||
"files.trimFinalNewlines": false,
|
||||
"cmake.configureOnOpen": false
|
||||
"cmake.configureOnOpen": false,
|
||||
"[cpp]": {
|
||||
"editor.defaultFormatter": "trunk.io"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ lib_deps =
|
||||
${networking_base.lib_deps}
|
||||
${environmental_base.lib_deps}
|
||||
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
|
||||
h2zero/NimBLE-Arduino@^1.4.1
|
||||
h2zero/NimBLE-Arduino@^1.4.2
|
||||
https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587
|
||||
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
|
||||
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
set -e
|
||||
|
||||
VERSION=`bin/buildinfo.py long`
|
||||
SHORT_VERSION=`bin/buildinfo.py short`
|
||||
VERSION=$(bin/buildinfo.py long)
|
||||
SHORT_VERSION=$(bin/buildinfo.py short)
|
||||
|
||||
OUTDIR=release/
|
||||
|
||||
@@ -29,6 +29,15 @@ cp $DFUPKG $OUTDIR/$basename-ota.zip
|
||||
|
||||
echo "Generating NRF52 uf2 file"
|
||||
SRCHEX=.pio/build/$1/firmware.hex
|
||||
|
||||
# if WM1110 target, merge hex with softdevice 7.3.0
|
||||
if (echo $1 | grep -q "wio-sdk-wm1110"); then
|
||||
echo "Merging with softdevice"
|
||||
sudo chmod +x ./bin/mergehex
|
||||
bin/mergehex -m bin/s140_nrf52_7.3.0_softdevice.hex $SRCHEX -o .pio/build/$1/merged_fimware.hex
|
||||
SRCHEX=.pio/build/$1/merged_fimware.hex
|
||||
fi
|
||||
|
||||
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/$basename.uf2 -f 0xADA52840
|
||||
|
||||
cp bin/device-install.* $OUTDIR
|
||||
|
||||
BIN
bin/mergehex
Normal file
BIN
bin/mergehex
Normal file
Binary file not shown.
9726
bin/s140_nrf52_7.3.0_softdevice.hex
Normal file
9726
bin/s140_nrf52_7.3.0_softdevice.hex
Normal file
File diff suppressed because it is too large
Load Diff
12
bin/setup-python-for-esp-debug.sh
Normal file
12
bin/setup-python-for-esp-debug.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
# shellcheck shell=bash
|
||||
# (this minor script is actually shell agnostic, and is intended to be sourced rather than run in a subshell)
|
||||
|
||||
# This is a little script you can source if you want to make ESP debugging work on a modern (24.04) ubuntu machine
|
||||
# It assumes you have built and installed python 2.7 from source with:
|
||||
# ./configure --enable-optimizations --enable-shared --enable-unicode=ucs4
|
||||
# sudo make clean
|
||||
# make
|
||||
# sudo make altinstall
|
||||
|
||||
export LD_LIBRARY_PATH=$HOME/packages/python-2.7.18/
|
||||
export PYTHON_HOME=/usr/local/lib/python2.7/
|
||||
@@ -27,7 +27,7 @@
|
||||
"jlink_device": "nRF52840_xxAA",
|
||||
"svd_path": "nrf52840.svd"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"frameworks": ["arduino", "freertos"],
|
||||
"name": "Seeed WIO WM1110",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "7.3.0",
|
||||
"sd_fwid": "0x00B6"
|
||||
"sd_fwid": "0x0123"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"svd_path": "nrf52840.svd",
|
||||
"openocd_target": "nrf52840-mdk-rs"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"frameworks": ["arduino", "freertos"],
|
||||
"name": "WisCore RAK4631 Board",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
|
||||
@@ -77,10 +77,11 @@ build_flags = -Wno-missing-field-initializers
|
||||
-DMESHTASTIC_EXCLUDE_DROPZONE=1
|
||||
|
||||
monitor_speed = 115200
|
||||
monitor_filters = direct
|
||||
|
||||
lib_deps =
|
||||
jgromes/RadioLib@~6.6.0
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git#2b40affbe7f7dc63b6c00fa88e7e12ed1f8e1719 ; ESP8266_SSD1306
|
||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306
|
||||
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
|
||||
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
|
||||
https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4
|
||||
@@ -151,4 +152,3 @@ lib_deps =
|
||||
|
||||
|
||||
https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee
|
||||
|
||||
|
||||
Submodule protobufs updated: 1c3029f286...1198b7dbab
7
pyocd.yaml
Normal file
7
pyocd.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
# This is a config file to control pyocd ICE debugger probe options (only used for NRF52 targets with hardware debugging connections)
|
||||
# for more info see FIXMEURL
|
||||
|
||||
# console or telnet
|
||||
semihost_console_type: telnet
|
||||
enable_semihosting: True
|
||||
telnet_port: 4444
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <Wire.h>
|
||||
#ifdef RAK_4631
|
||||
#include "Fusion/Fusion.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "graphics/ScreenFonts.h"
|
||||
#include <Rak_BMX160.h>
|
||||
#endif
|
||||
|
||||
@@ -101,7 +103,11 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
bmx160.getAllData(&magAccel, NULL, &gAccel);
|
||||
|
||||
// expirimental calibrate routine. Limited to between 10 and 30 seconds after boot
|
||||
if (millis() > 10 * 1000 && millis() < 30 * 1000) {
|
||||
if (millis() > 12 * 1000 && millis() < 30 * 1000) {
|
||||
if (!showingScreen) {
|
||||
showingScreen = true;
|
||||
screen->startAlert((FrameCallback)drawFrameCalibration);
|
||||
}
|
||||
if (magAccel.x > highestX)
|
||||
highestX = magAccel.x;
|
||||
if (magAccel.x < lowestX)
|
||||
@@ -114,6 +120,9 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
highestZ = magAccel.z;
|
||||
if (magAccel.z < lowestZ)
|
||||
lowestZ = magAccel.z;
|
||||
} else if (showingScreen && millis() >= 30 * 1000) {
|
||||
showingScreen = false;
|
||||
screen->endAlert();
|
||||
}
|
||||
|
||||
int highestRealX = highestX - (highestX + lowestX) / 2;
|
||||
@@ -255,11 +264,34 @@ class AccelerometerThread : public concurrency::OSThread
|
||||
Adafruit_LIS3DH lis;
|
||||
Adafruit_LSM6DS3TRC lsm;
|
||||
SensorBMA423 bmaSensor;
|
||||
bool BMA_IRQ = false;
|
||||
#ifdef RAK_4631
|
||||
bool showingScreen = false;
|
||||
RAK_BMX160 bmx160;
|
||||
float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
|
||||
|
||||
static void drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
int x_offset = display->width() / 2;
|
||||
int y_offset = display->height() <= 80 ? 0 : 32;
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x, y, "Calibrating\nCompass");
|
||||
int16_t compassX = 0, compassY = 0;
|
||||
uint16_t compassDiam = graphics::Screen::getCompassDiam(display->getWidth(), display->getHeight());
|
||||
|
||||
// coordinates for the center of the compass/circle
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
||||
compassX = x + display->getWidth() - compassDiam / 2 - 5;
|
||||
compassY = y + display->getHeight() / 2;
|
||||
} else {
|
||||
compassX = x + display->getWidth() - compassDiam / 2 - 5;
|
||||
compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2;
|
||||
}
|
||||
display->drawCircle(compassX, compassY, compassDiam / 2);
|
||||
screen->drawCompassNorth(display, compassX, compassY, screen->getHeading() * PI / 180);
|
||||
}
|
||||
#endif
|
||||
bool BMA_IRQ = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -11,3 +11,7 @@ const uint8_t FROMRADIO_UUID_16[16u] = {0x02, 0x00, 0x12, 0xac, 0x42, 0x02, 0x78
|
||||
0xed, 0x11, 0x93, 0x49, 0x9e, 0xe6, 0x55, 0x2c};
|
||||
const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70, 0xa6,
|
||||
0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed};
|
||||
const uint8_t LEGACY_LOGRADIO_UUID_16[16u] = {0xe2, 0xf2, 0x1e, 0xbe, 0xc5, 0x15, 0xcf, 0xaa,
|
||||
0x6b, 0x43, 0xfa, 0x78, 0x38, 0xd2, 0x6f, 0x6c};
|
||||
const uint8_t LOGRADIO_UUID_16[16u] = {0x47, 0x95, 0xDF, 0x8C, 0xDE, 0xE9, 0x44, 0x99,
|
||||
0x23, 0x44, 0xE6, 0x06, 0x49, 0x6E, 0x3D, 0x5A};
|
||||
@@ -11,10 +11,12 @@
|
||||
#define TORADIO_UUID "f75c76d2-129e-4dad-a1dd-7866124401e7"
|
||||
#define FROMRADIO_UUID "2c55e69e-4993-11ed-b878-0242ac120002"
|
||||
#define FROMNUM_UUID "ed9da18c-a800-4f66-a670-aa7547e34453"
|
||||
#define LEGACY_LOGRADIO_UUID "6c6fd238-78fa-436b-aacf-15c5be1ef2e2"
|
||||
#define LOGRADIO_UUID "5a3d6e49-06e6-4423-9944-e9de8cdf9547"
|
||||
|
||||
// NRF52 wants these constants as byte arrays
|
||||
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
|
||||
extern const uint8_t MESH_SERVICE_UUID_16[], TORADIO_UUID_16[16u], FROMRADIO_UUID_16[], FROMNUM_UUID_16[];
|
||||
extern const uint8_t MESH_SERVICE_UUID_16[], TORADIO_UUID_16[16u], FROMRADIO_UUID_16[], FROMNUM_UUID_16[], LOGRADIO_UUID_16[];
|
||||
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level);
|
||||
|
||||
@@ -187,8 +187,9 @@ int32_t ButtonThread::runOnce()
|
||||
case BUTTON_EVENT_LONG_PRESSED: {
|
||||
LOG_BUTTON("Long press!\n");
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
if (screen)
|
||||
screen->startShutdownScreen();
|
||||
if (screen) {
|
||||
screen->startAlert("Shutting down...");
|
||||
}
|
||||
playBeep();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ SOFTWARE.*/
|
||||
|
||||
#include "DebugConfiguration.h"
|
||||
|
||||
#if HAS_WIFI || HAS_ETHERNET
|
||||
#if HAS_NETWORKING
|
||||
|
||||
Syslog::Syslog(UDP &client)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#ifndef SYSLOG_H
|
||||
#define SYSLOG_H
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
|
||||
// DEBUG LED
|
||||
#ifndef LED_INVERTED
|
||||
@@ -25,6 +26,14 @@
|
||||
|
||||
#include "SerialConsole.h"
|
||||
|
||||
// If defined we will include support for ARM ICE "semihosting" for a virtual
|
||||
// console over the JTAG port (to replace the normal serial port)
|
||||
// Note: Normally this flag is passed into the gcc commandline by platformio.ini.
|
||||
// for an example see env:rak4631_dap.
|
||||
// #ifndef USE_SEMIHOSTING
|
||||
// #define USE_SEMIHOSTING
|
||||
// #endif
|
||||
|
||||
#define DEBUG_PORT (*console) // Serial debug port
|
||||
|
||||
#ifdef USE_SEGGER
|
||||
@@ -117,7 +126,7 @@
|
||||
#include <WiFi.h>
|
||||
#endif // HAS_WIFI
|
||||
|
||||
#if HAS_WIFI || HAS_ETHERNET
|
||||
#if HAS_NETWORKING
|
||||
|
||||
class Syslog
|
||||
{
|
||||
@@ -153,5 +162,3 @@ class Syslog
|
||||
};
|
||||
|
||||
#endif // HAS_ETHERNET || HAS_WIFI
|
||||
|
||||
#endif // SYSLOG_H
|
||||
@@ -84,6 +84,58 @@ bool renameFile(const char *pathFrom, const char *pathTo)
|
||||
#endif
|
||||
}
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief Get the list of files in a directory.
|
||||
*
|
||||
* This function returns a list of files in a directory. The list includes the full path of each file.
|
||||
*
|
||||
* @param dirname The name of the directory.
|
||||
* @param levels The number of levels of subdirectories to list.
|
||||
* @return A vector of strings containing the full path of each file in the directory.
|
||||
*/
|
||||
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels)
|
||||
{
|
||||
std::vector<meshtastic_FileInfo> filenames = {};
|
||||
#ifdef FSCom
|
||||
File root = FSCom.open(dirname, FILE_O_READ);
|
||||
if (!root)
|
||||
return filenames;
|
||||
if (!root.isDirectory())
|
||||
return filenames;
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory() && !String(file.name()).endsWith(".")) {
|
||||
if (levels) {
|
||||
#ifdef ARCH_ESP32
|
||||
std::vector<meshtastic_FileInfo> subDirFilenames = getFiles(file.path(), levels - 1);
|
||||
#else
|
||||
std::vector<meshtastic_FileInfo> subDirFilenames = getFiles(file.name(), levels - 1);
|
||||
#endif
|
||||
filenames.insert(filenames.end(), subDirFilenames.begin(), subDirFilenames.end());
|
||||
file.close();
|
||||
}
|
||||
} else {
|
||||
meshtastic_FileInfo fileInfo = {"", file.size()};
|
||||
#ifdef ARCH_ESP32
|
||||
strcpy(fileInfo.file_name, file.path());
|
||||
#else
|
||||
strcpy(fileInfo.file_name, file.name());
|
||||
#endif
|
||||
if (!String(fileInfo.file_name).endsWith(".")) {
|
||||
filenames.push_back(fileInfo);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
root.close();
|
||||
#endif
|
||||
return filenames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the contents of a directory.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "configuration.h"
|
||||
#include <vector>
|
||||
|
||||
// Cross platform filesystem API
|
||||
|
||||
@@ -49,6 +50,7 @@ using namespace Adafruit_LittleFS_Namespace;
|
||||
void fsInit();
|
||||
bool copyFile(const char *from, const char *to);
|
||||
bool renameFile(const char *pathFrom, const char *pathTo);
|
||||
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels);
|
||||
void listDir(const char *dirname, uint8_t levels, bool del);
|
||||
void rmDir(const char *dirname);
|
||||
void setupSDCard();
|
||||
@@ -124,7 +124,7 @@ class GPSStatus : public Status
|
||||
if (isDirty) {
|
||||
if (hasLock) {
|
||||
// In debug logs, identify position by @timestamp:stage (stage 3 = notify)
|
||||
LOG_DEBUG("New GPS pos@%x:3 lat=%f, lon=%f, alt=%d, pdop=%.2f, track=%.2f, speed=%.2f, sats=%d\n", p.timestamp,
|
||||
LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d\n", p.timestamp,
|
||||
p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5,
|
||||
p.ground_speed * 1e-2, p.sats_in_view);
|
||||
} else {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#if defined(DEBUG_HEAP_MQTT) && !MESHTASTIC_EXCLUDE_MQTT
|
||||
#include "mqtt/MQTT.h"
|
||||
#include "target_specific.h"
|
||||
#if !MESTASTIC_EXCLUDE_WIFI
|
||||
#if HAS_WIFI
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "Default.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerMon.h"
|
||||
#include "configuration.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "main.h"
|
||||
@@ -49,6 +50,7 @@ static bool isPowered()
|
||||
static void sdsEnter()
|
||||
{
|
||||
LOG_DEBUG("Enter state: SDS\n");
|
||||
powerMon->setState(meshtastic_PowerMon_State_CPU_DeepSleep);
|
||||
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
|
||||
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false);
|
||||
}
|
||||
@@ -68,6 +70,7 @@ static uint32_t secsSlept;
|
||||
static void lsEnter()
|
||||
{
|
||||
LOG_INFO("lsEnter begin, ls_secs=%u\n", config.power.ls_secs);
|
||||
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
|
||||
screen->setOn(false);
|
||||
secsSlept = 0; // How long have we been sleeping this time
|
||||
|
||||
@@ -87,8 +90,10 @@ static void lsIdle()
|
||||
// Briefly come out of sleep long enough to blink the led once every few seconds
|
||||
uint32_t sleepTime = SLEEP_TIME;
|
||||
|
||||
powerMon->setState(meshtastic_PowerMon_State_CPU_LightSleep);
|
||||
setLed(false); // Never leave led on while in light sleep
|
||||
esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL);
|
||||
powerMon->clearState(meshtastic_PowerMon_State_CPU_LightSleep);
|
||||
|
||||
switch (wakeCause2) {
|
||||
case ESP_SLEEP_WAKEUP_TIMER:
|
||||
@@ -144,6 +149,7 @@ static void lsExit()
|
||||
static void nbEnter()
|
||||
{
|
||||
LOG_DEBUG("Enter state: NB\n");
|
||||
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
|
||||
screen->setOn(false);
|
||||
#ifdef ARCH_ESP32
|
||||
// Only ESP32 should turn off bluetooth
|
||||
@@ -155,6 +161,8 @@ static void nbEnter()
|
||||
|
||||
static void darkEnter()
|
||||
{
|
||||
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
|
||||
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
|
||||
setBluetoothEnable(true);
|
||||
screen->setOn(false);
|
||||
}
|
||||
@@ -162,6 +170,8 @@ static void darkEnter()
|
||||
static void serialEnter()
|
||||
{
|
||||
LOG_DEBUG("Enter state: SERIAL\n");
|
||||
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
|
||||
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
|
||||
setBluetoothEnable(false);
|
||||
screen->setOn(true);
|
||||
screen->print("Serial connected\n");
|
||||
@@ -170,6 +180,7 @@ static void serialEnter()
|
||||
static void serialExit()
|
||||
{
|
||||
// Turn bluetooth back on when we leave serial stream API
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
setBluetoothEnable(true);
|
||||
screen->print("Serial disconnected\n");
|
||||
}
|
||||
@@ -182,6 +193,8 @@ static void powerEnter()
|
||||
LOG_INFO("Loss of power in Powered\n");
|
||||
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
||||
} else {
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
|
||||
screen->setOn(true);
|
||||
setBluetoothEnable(true);
|
||||
// within enter() the function getState() returns the state we came from
|
||||
@@ -205,6 +218,8 @@ static void powerIdle()
|
||||
|
||||
static void powerExit()
|
||||
{
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
|
||||
screen->setOn(true);
|
||||
setBluetoothEnable(true);
|
||||
|
||||
@@ -216,6 +231,8 @@ static void powerExit()
|
||||
static void onEnter()
|
||||
{
|
||||
LOG_DEBUG("Enter state: ON\n");
|
||||
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
||||
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
|
||||
screen->setOn(true);
|
||||
setBluetoothEnable(true);
|
||||
}
|
||||
|
||||
45
src/PowerMon.cpp
Normal file
45
src/PowerMon.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include "PowerMon.h"
|
||||
#include "NodeDB.h"
|
||||
|
||||
// Use the 'live' config flag to figure out if we should be showing this message
|
||||
static bool is_power_enabled(uint64_t m)
|
||||
{
|
||||
return (m & config.power.powermon_enables) ? true : false;
|
||||
}
|
||||
|
||||
void PowerMon::setState(_meshtastic_PowerMon_State state, const char *reason)
|
||||
{
|
||||
#ifdef USE_POWERMON
|
||||
auto oldstates = states;
|
||||
states |= state;
|
||||
if (oldstates != states && is_power_enabled(state)) {
|
||||
emitLog(reason);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PowerMon::clearState(_meshtastic_PowerMon_State state, const char *reason)
|
||||
{
|
||||
#ifdef USE_POWERMON
|
||||
auto oldstates = states;
|
||||
states &= ~state;
|
||||
if (oldstates != states && is_power_enabled(state)) {
|
||||
emitLog(reason);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void PowerMon::emitLog(const char *reason)
|
||||
{
|
||||
#ifdef USE_POWERMON
|
||||
// The nrf52 printf doesn't understand 64 bit ints, so if we ever reach that point this function will need to change.
|
||||
LOG_INFO("S:PM:0x%08lx,%s\n", (uint32_t)states, reason);
|
||||
#endif
|
||||
}
|
||||
|
||||
PowerMon *powerMon;
|
||||
|
||||
void powerMonInit()
|
||||
{
|
||||
powerMon = new PowerMon();
|
||||
}
|
||||
34
src/PowerMon.h
Normal file
34
src/PowerMon.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "configuration.h"
|
||||
|
||||
#include "meshtastic/powermon.pb.h"
|
||||
|
||||
#ifndef MESHTASTIC_EXCLUDE_POWERMON
|
||||
#define USE_POWERMON // FIXME turn this only for certain builds
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The singleton class for monitoring power consumption of device
|
||||
* subsystems/modes.
|
||||
*
|
||||
* For more information see the PowerMon docs.
|
||||
*/
|
||||
class PowerMon
|
||||
{
|
||||
uint64_t states = 0UL;
|
||||
|
||||
public:
|
||||
PowerMon() {}
|
||||
|
||||
// Mark entry/exit of a power consuming state
|
||||
void setState(_meshtastic_PowerMon_State state, const char *reason = "");
|
||||
void clearState(_meshtastic_PowerMon_State state, const char *reason = "");
|
||||
|
||||
private:
|
||||
// Emit the coded log message
|
||||
void emitLog(const char *reason);
|
||||
};
|
||||
|
||||
extern PowerMon *powerMon;
|
||||
|
||||
void powerMonInit();
|
||||
@@ -3,6 +3,8 @@
|
||||
#include "RTC.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
#include <assert.h>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
@@ -14,12 +16,7 @@
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A printer that doesn't go anywhere
|
||||
*/
|
||||
NoopPrint noopPrint;
|
||||
|
||||
#if HAS_WIFI || HAS_ETHERNET
|
||||
#if HAS_NETWORKING
|
||||
extern Syslog syslog;
|
||||
#endif
|
||||
void RedirectablePrint::rpInit()
|
||||
@@ -38,7 +35,7 @@ void RedirectablePrint::setDestination(Print *_dest)
|
||||
size_t RedirectablePrint::write(uint8_t c)
|
||||
{
|
||||
// Always send the characters to our segger JTAG debugger
|
||||
#ifdef SEGGER_STDOUT_CH
|
||||
#ifdef USE_SEGGER
|
||||
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
|
||||
#endif
|
||||
|
||||
@@ -49,7 +46,7 @@ size_t RedirectablePrint::write(uint8_t c)
|
||||
// serial port said (which could be zero)
|
||||
}
|
||||
|
||||
size_t RedirectablePrint::vprintf(const char *format, va_list arg)
|
||||
size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_list arg)
|
||||
{
|
||||
va_list copy;
|
||||
static char printBuf[160];
|
||||
@@ -65,40 +62,42 @@ size_t RedirectablePrint::vprintf(const char *format, va_list arg)
|
||||
len = sizeof(printBuf) - 1;
|
||||
printBuf[sizeof(printBuf) - 2] = '\n';
|
||||
}
|
||||
|
||||
for (size_t f = 0; f < len; f++) {
|
||||
if (!std::isprint(static_cast<unsigned char>(printBuf[f])) && printBuf[f] != '\n')
|
||||
printBuf[f] = '#';
|
||||
}
|
||||
if (logLevel != nullptr) {
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
|
||||
Print::write("\u001b[34m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
|
||||
Print::write("\u001b[32m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
|
||||
Print::write("\u001b[33m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0)
|
||||
Print::write("\u001b[31m", 6);
|
||||
}
|
||||
len = Print::write(printBuf, len);
|
||||
Print::write("\u001b[0m", 5);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, va_list arg)
|
||||
{
|
||||
#ifdef ARCH_PORTDUINO
|
||||
if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
|
||||
return 0;
|
||||
else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
|
||||
return 0;
|
||||
else if (settingsMap[logoutputlevel] < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
|
||||
return 0;
|
||||
#endif
|
||||
if (moduleConfig.serial.override_console_serial_port && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
||||
return 0;
|
||||
}
|
||||
size_t r = 0;
|
||||
#ifdef HAS_FREE_RTOS
|
||||
if (inDebugPrint != nullptr && xSemaphoreTake(inDebugPrint, portMAX_DELAY) == pdTRUE) {
|
||||
#else
|
||||
if (!inDebugPrint) {
|
||||
inDebugPrint = true;
|
||||
#endif
|
||||
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
|
||||
// Cope with 0 len format strings, but look for new line terminator
|
||||
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
|
||||
|
||||
// If we are the first message on a report, include the header
|
||||
if (!isContinuationMessage) {
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
|
||||
Print::write("\u001b[34m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
|
||||
Print::write("\u001b[32m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
|
||||
Print::write("\u001b[33m", 6);
|
||||
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0)
|
||||
Print::write("\u001b[31m", 6);
|
||||
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile
|
||||
if (rtc_sec > 0) {
|
||||
long hms = rtc_sec % SEC_PER_DAY;
|
||||
@@ -112,15 +111,15 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
|
||||
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
|
||||
#ifdef ARCH_PORTDUINO
|
||||
r += ::printf("%s | %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
|
||||
::printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
|
||||
#else
|
||||
r += printf("%s | %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
|
||||
printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
|
||||
#endif
|
||||
} else
|
||||
#ifdef ARCH_PORTDUINO
|
||||
r += ::printf("%s | ??:??:?? %u ", logLevel, millis() / 1000);
|
||||
::printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000);
|
||||
#else
|
||||
r += printf("%s | ??:??:?? %u ", logLevel, millis() / 1000);
|
||||
printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000);
|
||||
#endif
|
||||
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
@@ -132,9 +131,14 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
print("] ");
|
||||
}
|
||||
}
|
||||
r += vprintf(format, arg);
|
||||
r += vprintf(logLevel, format, arg);
|
||||
|
||||
#if (HAS_WIFI || HAS_ETHERNET) && !defined(ARCH_PORTDUINO)
|
||||
isContinuationMessage = !hasNewline;
|
||||
}
|
||||
|
||||
void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format, va_list arg)
|
||||
{
|
||||
#if HAS_NETWORKING && !defined(ARCH_PORTDUINO)
|
||||
// if syslog is in use, collect the log messages and send them to syslog
|
||||
if (syslog.isEnabled()) {
|
||||
int ll = 0;
|
||||
@@ -165,10 +169,108 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg)
|
||||
{
|
||||
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
if (config.bluetooth.device_logging_enabled && !pauseBluetoothLogging) {
|
||||
bool isBleConnected = false;
|
||||
#ifdef ARCH_ESP32
|
||||
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();
|
||||
#elif defined(ARCH_NRF52)
|
||||
isBleConnected = nrf52Bluetooth != nullptr && nrf52Bluetooth->isConnected();
|
||||
#endif
|
||||
if (isBleConnected) {
|
||||
char *message;
|
||||
size_t initialLen;
|
||||
size_t len;
|
||||
initialLen = strlen(format);
|
||||
message = new char[initialLen + 1];
|
||||
len = vsnprintf(message, initialLen + 1, format, arg);
|
||||
if (len > initialLen) {
|
||||
delete[] message;
|
||||
message = new char[len + 1];
|
||||
vsnprintf(message, len + 1, format, arg);
|
||||
}
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
meshtastic_LogRecord logRecord = meshtastic_LogRecord_init_zero;
|
||||
logRecord.level = getLogLevel(logLevel);
|
||||
strcpy(logRecord.message, message);
|
||||
if (thread)
|
||||
strcpy(logRecord.source, thread->ThreadName.c_str());
|
||||
logRecord.time = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||
|
||||
uint8_t *buffer = new uint8_t[meshtastic_LogRecord_size];
|
||||
size_t size = pb_encode_to_bytes(buffer, meshtastic_LogRecord_size, meshtastic_LogRecord_fields, &logRecord);
|
||||
#ifdef ARCH_ESP32
|
||||
nimbleBluetooth->sendLog(buffer, size);
|
||||
#elif defined(ARCH_NRF52)
|
||||
nrf52Bluetooth->sendLog(buffer, size);
|
||||
#endif
|
||||
delete[] message;
|
||||
delete[] buffer;
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)logLevel;
|
||||
(void)format;
|
||||
(void)arg;
|
||||
#endif
|
||||
}
|
||||
|
||||
meshtastic_LogRecord_Level RedirectablePrint::getLogLevel(const char *logLevel)
|
||||
{
|
||||
meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset
|
||||
switch (logLevel[0]) {
|
||||
case 'D':
|
||||
ll = meshtastic_LogRecord_Level_DEBUG;
|
||||
break;
|
||||
case 'I':
|
||||
ll = meshtastic_LogRecord_Level_INFO;
|
||||
break;
|
||||
case 'W':
|
||||
ll = meshtastic_LogRecord_Level_WARNING;
|
||||
break;
|
||||
case 'E':
|
||||
ll = meshtastic_LogRecord_Level_ERROR;
|
||||
break;
|
||||
case 'C':
|
||||
ll = meshtastic_LogRecord_Level_CRITICAL;
|
||||
break;
|
||||
}
|
||||
return ll;
|
||||
}
|
||||
|
||||
void RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
{
|
||||
#ifdef ARCH_PORTDUINO
|
||||
if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
|
||||
return;
|
||||
else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
|
||||
return;
|
||||
else if (settingsMap[logoutputlevel] < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
|
||||
return;
|
||||
#endif
|
||||
if (moduleConfig.serial.override_console_serial_port && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
if (inDebugPrint != nullptr && xSemaphoreTake(inDebugPrint, portMAX_DELAY) == pdTRUE) {
|
||||
#else
|
||||
if (!inDebugPrint) {
|
||||
inDebugPrint = true;
|
||||
#endif
|
||||
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
|
||||
log_to_serial(logLevel, format, arg);
|
||||
log_to_syslog(logLevel, format, arg);
|
||||
log_to_ble(logLevel, format, arg);
|
||||
|
||||
va_end(arg);
|
||||
|
||||
isContinuationMessage = !hasNewline;
|
||||
#ifdef HAS_FREE_RTOS
|
||||
xSemaphoreGive(inDebugPrint);
|
||||
#else
|
||||
@@ -176,7 +278,7 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
#endif
|
||||
}
|
||||
|
||||
return r;
|
||||
return;
|
||||
}
|
||||
|
||||
void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../freertosinc.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
#include <Print.h>
|
||||
#include <stdarg.h>
|
||||
#include <string>
|
||||
@@ -41,23 +42,21 @@ class RedirectablePrint : public Print
|
||||
* log message. Otherwise we assume more prints will come before the log message ends. This
|
||||
* allows you to call logDebug a few times to build up a single log message line if you wish.
|
||||
*/
|
||||
size_t log(const char *logLevel, const char *format, ...) __attribute__((format(printf, 3, 4)));
|
||||
void log(const char *logLevel, const char *format, ...) __attribute__((format(printf, 3, 4)));
|
||||
|
||||
/** like printf but va_list based */
|
||||
size_t vprintf(const char *format, va_list arg);
|
||||
size_t vprintf(const char *logLevel, const char *format, va_list arg);
|
||||
|
||||
void hexDump(const char *logLevel, unsigned char *buf, uint16_t len);
|
||||
|
||||
std::string mt_sprintf(const std::string fmt_str, ...);
|
||||
};
|
||||
|
||||
class NoopPrint : public Print
|
||||
{
|
||||
public:
|
||||
virtual size_t write(uint8_t c) { return 1; }
|
||||
};
|
||||
protected:
|
||||
/// Subclasses can override if they need to change how we format over the serial port
|
||||
virtual void log_to_serial(const char *logLevel, const char *format, va_list arg);
|
||||
|
||||
/**
|
||||
* A printer that doesn't go anywhere
|
||||
*/
|
||||
extern NoopPrint noopPrint;
|
||||
private:
|
||||
void log_to_syslog(const char *logLevel, const char *format, va_list arg);
|
||||
void log_to_ble(const char *logLevel, const char *format, va_list arg);
|
||||
meshtastic_LogRecord_Level getLogLevel(const char *logLevel);
|
||||
};
|
||||
@@ -28,7 +28,7 @@ void consolePrintf(const char *format, ...)
|
||||
{
|
||||
va_list arg;
|
||||
va_start(arg, format);
|
||||
console->vprintf(format, arg);
|
||||
console->vprintf(nullptr, format, arg);
|
||||
va_end(arg);
|
||||
console->flush();
|
||||
}
|
||||
@@ -38,7 +38,6 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
|
||||
assert(!console);
|
||||
console = this;
|
||||
canWrite = false; // We don't send packets to our port until it has talked to us first
|
||||
// setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks
|
||||
|
||||
#ifdef RP2040_SLOW_CLOCK
|
||||
Port.setTX(SERIAL2_TX);
|
||||
@@ -85,9 +84,8 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
|
||||
{
|
||||
// only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled.
|
||||
if (config.has_lora && config.device.serial_enabled) {
|
||||
// Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
|
||||
if (!config.device.debug_log_enabled)
|
||||
setDestination(&noopPrint);
|
||||
// Switch to protobufs for log messages
|
||||
usingProtobufs = true;
|
||||
canWrite = true;
|
||||
|
||||
return StreamAPI::handleToRadio(buf, len);
|
||||
@@ -95,3 +93,31 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg)
|
||||
{
|
||||
if (usingProtobufs) {
|
||||
meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset
|
||||
switch (logLevel[0]) {
|
||||
case 'D':
|
||||
ll = meshtastic_LogRecord_Level_DEBUG;
|
||||
break;
|
||||
case 'I':
|
||||
ll = meshtastic_LogRecord_Level_INFO;
|
||||
break;
|
||||
case 'W':
|
||||
ll = meshtastic_LogRecord_Level_WARNING;
|
||||
break;
|
||||
case 'E':
|
||||
ll = meshtastic_LogRecord_Level_ERROR;
|
||||
break;
|
||||
case 'C':
|
||||
ll = meshtastic_LogRecord_Level_CRITICAL;
|
||||
break;
|
||||
}
|
||||
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
emitLogRecord(ll, thread ? thread->ThreadName.c_str() : "", format, arg);
|
||||
} else
|
||||
RedirectablePrint::log_to_serial(logLevel, format, arg);
|
||||
}
|
||||
@@ -8,6 +8,11 @@
|
||||
*/
|
||||
class SerialConsole : public StreamAPI, public RedirectablePrint, private concurrency::OSThread
|
||||
{
|
||||
/**
|
||||
* If true we are talking to a smart host and all messages (including log messages) must be framed as protobufs.
|
||||
*/
|
||||
bool usingProtobufs = false;
|
||||
|
||||
public:
|
||||
SerialConsole();
|
||||
|
||||
@@ -31,6 +36,9 @@ class SerialConsole : public StreamAPI, public RedirectablePrint, private concur
|
||||
protected:
|
||||
/// Check the current underlying physical link to see if the client is currently connected
|
||||
virtual bool checkIsConnected() override;
|
||||
|
||||
/// Possibly switch to protobufs if we see a valid protobuf message
|
||||
virtual void log_to_serial(const char *logLevel, const char *format, va_list arg);
|
||||
};
|
||||
|
||||
// A simple wrapper to allow non class aware code write to the console
|
||||
|
||||
@@ -8,13 +8,11 @@ enum class Cmd {
|
||||
SET_ON,
|
||||
SET_OFF,
|
||||
ON_PRESS,
|
||||
START_BLUETOOTH_PIN_SCREEN,
|
||||
START_ALERT_FRAME,
|
||||
STOP_ALERT_FRAME,
|
||||
START_FIRMWARE_UPDATE_SCREEN,
|
||||
STOP_BLUETOOTH_PIN_SCREEN,
|
||||
STOP_BOOT_SCREEN,
|
||||
PRINT,
|
||||
START_SHUTDOWN_SCREEN,
|
||||
START_REBOOT_SCREEN,
|
||||
SHOW_PREV_FRAME,
|
||||
SHOW_NEXT_FRAME
|
||||
};
|
||||
@@ -242,9 +242,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define HAS_BLUETOOTH 0
|
||||
#endif
|
||||
|
||||
#include "DebugConfiguration.h"
|
||||
#include "RF95Configuration.h"
|
||||
|
||||
#ifndef HW_VENDOR
|
||||
#error HW_VENDOR must be defined
|
||||
#endif
|
||||
@@ -261,6 +258,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MESHTASTIC_EXCLUDE_GPS 1
|
||||
#define MESHTASTIC_EXCLUDE_SCREEN 1
|
||||
#define MESHTASTIC_EXCLUDE_MQTT 1
|
||||
#define MESHTASTIC_EXCLUDE_POWERMON 1
|
||||
#endif
|
||||
|
||||
// Turn off all optional modules
|
||||
@@ -281,6 +279,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define MESHTASTIC_EXCLUDE_WAYPOINT 1
|
||||
#define MESHTASTIC_EXCLUDE_INPUTBROKER 1
|
||||
#define MESHTASTIC_EXCLUDE_SERIAL 1
|
||||
#define MESHTASTIC_EXCLUDE_POWERSTRESS 1
|
||||
#endif
|
||||
|
||||
// // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled)
|
||||
@@ -290,6 +289,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#define HAS_WIFI 0
|
||||
#endif
|
||||
|
||||
// Allow code that needs internet to just check HAS_NETWORKING rather than HAS_WIFI || HAS_ETHERNET
|
||||
#define HAS_NETWORKING (HAS_WIFI || HAS_ETHERNET)
|
||||
|
||||
// // Turn off Bluetooth
|
||||
#ifdef MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
#undef HAS_BLUETOOTH
|
||||
@@ -309,3 +311,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#undef HAS_SCREEN
|
||||
#define HAS_SCREEN 0
|
||||
#endif
|
||||
|
||||
#include "DebugConfiguration.h"
|
||||
#include "RF95Configuration.h"
|
||||
@@ -314,7 +314,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
|
||||
|
||||
case SHT31_4x_ADDR:
|
||||
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
|
||||
if (registerValue == 0x11a2) {
|
||||
if (registerValue == 0x11a2 || registerValue == 0x11da) {
|
||||
type = SHT4X;
|
||||
LOG_INFO("SHT4X sensor found\n");
|
||||
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Default.h"
|
||||
#include "GPS.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerMon.h"
|
||||
#include "RTC.h"
|
||||
|
||||
#include "main.h" // pmu_found
|
||||
@@ -784,6 +785,22 @@ GPS::~GPS()
|
||||
notifyGPSSleepObserver.observe(¬ifyGPSSleep);
|
||||
}
|
||||
|
||||
const char *GPS::powerStateToString()
|
||||
{
|
||||
switch (powerState) {
|
||||
case GPS_OFF:
|
||||
return "OFF";
|
||||
case GPS_IDLE:
|
||||
return "IDLE";
|
||||
case GPS_STANDBY:
|
||||
return "STANDBY";
|
||||
case GPS_ACTIVE:
|
||||
return "ACTIVE";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
|
||||
{
|
||||
// Record the current powerState
|
||||
@@ -798,16 +815,19 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
|
||||
else
|
||||
powerState = GPS_OFF;
|
||||
|
||||
LOG_DEBUG("GPS::powerState=%d\n", powerState);
|
||||
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)
|
||||
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 &&
|
||||
|
||||
@@ -289,6 +289,8 @@ class GPS : private concurrency::OSThread
|
||||
// delay counter to allow more sats before fixed position stops GPS thread
|
||||
uint8_t fixeddelayCtr = 0;
|
||||
|
||||
const char *powerStateToString();
|
||||
|
||||
protected:
|
||||
GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN;
|
||||
};
|
||||
|
||||
@@ -75,7 +75,6 @@ namespace graphics
|
||||
// A text message frame + debug frame + all the node infos
|
||||
FrameCallback *normalFrames;
|
||||
static uint32_t targetFramerate = IDLE_FRAMERATE;
|
||||
static char btPIN[16] = "888888";
|
||||
|
||||
uint32_t logo_timeout = 5000; // 4 seconds for EACH logo
|
||||
|
||||
@@ -108,15 +107,39 @@ GeoCoord geoCoord;
|
||||
static bool heartbeat = false;
|
||||
#endif
|
||||
|
||||
static uint16_t displayWidth, displayHeight;
|
||||
|
||||
#define SCREEN_WIDTH displayWidth
|
||||
#define SCREEN_HEIGHT displayHeight
|
||||
// Quick access to screen dimensions from static drawing functions
|
||||
// DEPRECATED. To-do: move static functions inside Screen class
|
||||
#define SCREEN_WIDTH display->getWidth()
|
||||
#define SCREEN_HEIGHT display->getHeight()
|
||||
|
||||
#include "graphics/ScreenFonts.h"
|
||||
|
||||
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
|
||||
|
||||
/// Check if the display can render a string (detect special chars; emoji)
|
||||
static bool haveGlyphs(const char *str)
|
||||
{
|
||||
#if defined(OLED_UA) || defined(OLED_RU)
|
||||
// Don't want to make any assumptions about custom language support
|
||||
return true;
|
||||
#endif
|
||||
|
||||
// Check each character with the lookup function for the OLED library
|
||||
// We're not really meant to use this directly..
|
||||
bool have = true;
|
||||
for (uint16_t i = 0; i < strlen(str); i++) {
|
||||
uint8_t result = Screen::customFontTableLookup((uint8_t)str[i]);
|
||||
// If font doesn't support a character, it is substituted for ¿
|
||||
if (result == 191 && (uint8_t)str[i] != 191) {
|
||||
have = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("haveGlyphs=%d\n", have);
|
||||
return have;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the icon with extra info printed around the corners
|
||||
*/
|
||||
@@ -140,13 +163,15 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl
|
||||
if (upperMsg)
|
||||
display->drawString(x + 0, y + 0, upperMsg);
|
||||
|
||||
// Draw version in upper right
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%s",
|
||||
xstr(APP_VERSION_SHORT)); // Note: we don't bother printing region or now, it makes the string too long
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf);
|
||||
// Draw version and short name in upper right
|
||||
char buf[25];
|
||||
snprintf(buf, sizeof(buf), "%s\n%s", xstr(APP_VERSION_SHORT), haveGlyphs(owner.short_name) ? owner.short_name : "");
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||
display->drawString(x + SCREEN_WIDTH, y + 0, buf);
|
||||
screen->forceDisplay();
|
||||
// FIXME - draw serial # somewhere?
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
|
||||
}
|
||||
|
||||
static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
@@ -181,14 +206,15 @@ static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDi
|
||||
if (upperMsg)
|
||||
display->drawString(x + 0, y + 0, upperMsg);
|
||||
|
||||
// Draw version in upper right
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%s",
|
||||
xstr(APP_VERSION_SHORT)); // Note: we don't bother printing region or now, it makes the string too long
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf);
|
||||
// Draw version and shortname in upper right
|
||||
char buf[25];
|
||||
snprintf(buf, sizeof(buf), "%s\n%s", xstr(APP_VERSION_SHORT), haveGlyphs(owner.short_name) ? owner.short_name : "");
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_RIGHT);
|
||||
display->drawString(x + SCREEN_WIDTH, y + 0, buf);
|
||||
screen->forceDisplay();
|
||||
|
||||
// FIXME - draw serial # somewhere?
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
|
||||
}
|
||||
|
||||
static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
@@ -198,7 +224,7 @@ static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
drawOEMIconScreen(region, display, state, x, y);
|
||||
}
|
||||
|
||||
static void drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message)
|
||||
void Screen::drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message)
|
||||
{
|
||||
uint16_t x_offset = display->width() / 2;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
@@ -206,20 +232,6 @@ static void drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
||||
display->drawString(x_offset + x, 26 + y, message);
|
||||
}
|
||||
|
||||
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
#ifdef ARCH_ESP32
|
||||
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER || wakeCause == ESP_SLEEP_WAKEUP_EXT1) {
|
||||
drawFrameText(display, state, x, y, "Resuming...");
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Draw region in upper left
|
||||
const char *region = myRegion ? myRegion->name : NULL;
|
||||
drawIconScreen(region, display, state, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Used on boot when a certificate is being created
|
||||
static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -277,40 +289,19 @@ static void drawFunctionOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the display can render a string (detect special chars; emoji)
|
||||
static bool haveGlyphs(const char *str)
|
||||
{
|
||||
#if defined(OLED_UA) || defined(OLED_RU)
|
||||
// Don't want to make any assumptions about custom language support
|
||||
return true;
|
||||
#endif
|
||||
|
||||
// Check each character with the lookup function for the OLED library
|
||||
// We're not really meant to use this directly..
|
||||
bool have = true;
|
||||
for (uint16_t i = 0; i < strlen(str); i++) {
|
||||
uint8_t result = Screen::customFontTableLookup((uint8_t)str[i]);
|
||||
// If font doesn't support a character, it is substituted for ¿
|
||||
if (result == 191 && (uint8_t)str[i] != 191) {
|
||||
have = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DEBUG("haveGlyphs=%d\n", have);
|
||||
return have;
|
||||
}
|
||||
|
||||
#ifdef USE_EINK
|
||||
/// Used on eink displays while in deep sleep
|
||||
static void drawDeepSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
|
||||
// Next frame should use full-refresh, and block while running, else device will sleep before async callback
|
||||
EINK_ADD_FRAMEFLAG(display, COSMETIC);
|
||||
EINK_ADD_FRAMEFLAG(display, BLOCKING);
|
||||
|
||||
LOG_DEBUG("Drawing deep sleep screen\n");
|
||||
drawIconScreen("Sleeping...", display, state, x, y);
|
||||
|
||||
// Display displayStr on the screen
|
||||
drawIconScreen("Sleeping", display, state, x, y);
|
||||
}
|
||||
|
||||
/// Used on eink displays when screen updates are paused
|
||||
@@ -375,7 +366,7 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
|
||||
// in the array of "drawScreen" functions; however,
|
||||
// the passed-state doesn't quite reflect the "current"
|
||||
// screen, so we have to detect it.
|
||||
if (state->frameState == IN_TRANSITION && state->transitionFrameRelationship == INCOMING) {
|
||||
if (state->frameState == IN_TRANSITION && state->transitionFrameRelationship == TransitionRelationship_INCOMING) {
|
||||
// if we're transitioning from the end of the frame list back around to the first
|
||||
// frame, then we want this to be `0`
|
||||
module_frame = state->transitionFrameTarget;
|
||||
@@ -389,31 +380,6 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
|
||||
pi.drawFrame(display, state, x, y);
|
||||
}
|
||||
|
||||
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
int x_offset = display->width() / 2;
|
||||
int y_offset = display->height() <= 80 ? 0 : 32;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, "Enter this code");
|
||||
|
||||
display->setFont(FONT_LARGE);
|
||||
String displayPin(btPIN);
|
||||
String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, pin);
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
String deviceName = "Name: ";
|
||||
deviceName.concat(getDeviceName());
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, deviceName);
|
||||
}
|
||||
|
||||
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
@@ -1091,45 +1057,8 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Draw the last waypoint we received
|
||||
static void drawWaypointFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
static char tempBuf[237];
|
||||
|
||||
meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
|
||||
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
|
||||
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
|
||||
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
|
||||
display->setColor(BLACK);
|
||||
}
|
||||
|
||||
uint32_t seconds = sinceReceived(&mp);
|
||||
uint32_t minutes = seconds / 60;
|
||||
uint32_t hours = minutes / 60;
|
||||
uint32_t days = hours / 24;
|
||||
|
||||
if (config.display.heading_bold) {
|
||||
display->drawStringf(1 + x, 0 + y, tempBuf, "%s ago from %s",
|
||||
screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
||||
(node && node->has_user) ? node->user.short_name : "???");
|
||||
}
|
||||
display->drawStringf(0 + x, 0 + y, tempBuf, "%s ago from %s", screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
|
||||
(node && node->has_user) ? node->user.short_name : "???");
|
||||
|
||||
display->setColor(WHITE);
|
||||
meshtastic_Waypoint scratch;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
|
||||
snprintf(tempBuf, sizeof(tempBuf), "Received waypoint: %s", scratch.name);
|
||||
display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw a series of fields in a column, wrapping to multiple columns if needed
|
||||
static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
||||
void Screen::drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
|
||||
{
|
||||
// The coordinates define the left starting point of the text
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
@@ -1309,56 +1238,13 @@ static void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const
|
||||
}
|
||||
}
|
||||
#endif
|
||||
namespace
|
||||
{
|
||||
|
||||
/// A basic 2D point class for drawing
|
||||
class Point
|
||||
{
|
||||
public:
|
||||
float x, y;
|
||||
|
||||
Point(float _x, float _y) : x(_x), y(_y) {}
|
||||
|
||||
/// Apply a rotation around zero (standard rotation matrix math)
|
||||
void rotate(float radian)
|
||||
{
|
||||
float cos = cosf(radian), sin = sinf(radian);
|
||||
float rx = x * cos + y * sin, ry = -x * sin + y * cos;
|
||||
|
||||
x = rx;
|
||||
y = ry;
|
||||
}
|
||||
|
||||
void translate(int16_t dx, int dy)
|
||||
{
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
|
||||
void scale(float f)
|
||||
{
|
||||
// We use -f here to counter the flip that happens
|
||||
// on the y axis when drawing and rotating on screen
|
||||
x *= f;
|
||||
y *= -f;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
static void drawLine(OLEDDisplay *d, const Point &p1, const Point &p2)
|
||||
{
|
||||
d->drawLine(p1.x, p1.y, p2.x, p2.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a recent lat/lon return a guess of the heading the user is walking on.
|
||||
*
|
||||
* We keep a series of "after you've gone 10 meters, what is your heading since
|
||||
* the last reference point?"
|
||||
*/
|
||||
static float estimatedHeading(double lat, double lon)
|
||||
float Screen::estimatedHeading(double lat, double lon)
|
||||
{
|
||||
static double oldLat, oldLon;
|
||||
static float b;
|
||||
@@ -1382,38 +1268,13 @@ static float estimatedHeading(double lat, double lon)
|
||||
return b;
|
||||
}
|
||||
|
||||
static uint16_t getCompassDiam(OLEDDisplay *display)
|
||||
{
|
||||
uint16_t diam = 0;
|
||||
uint16_t offset = 0;
|
||||
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT)
|
||||
offset = FONT_HEIGHT_SMALL;
|
||||
|
||||
// get the smaller of the 2 dimensions and subtract 20
|
||||
if (display->getWidth() > (display->getHeight() - offset)) {
|
||||
diam = display->getHeight() - offset;
|
||||
// if 2/3 of the other size would be smaller, use that
|
||||
if (diam > (display->getWidth() * 2 / 3)) {
|
||||
diam = display->getWidth() * 2 / 3;
|
||||
}
|
||||
} else {
|
||||
diam = display->getWidth();
|
||||
if (diam > ((display->getHeight() - offset) * 2 / 3)) {
|
||||
diam = (display->getHeight() - offset) * 2 / 3;
|
||||
}
|
||||
}
|
||||
|
||||
return diam - 20;
|
||||
};
|
||||
|
||||
/// We will skip one node - the one for us, so we just blindly loop over all
|
||||
/// nodes
|
||||
static size_t nodeIndex;
|
||||
static int8_t prevFrame = -1;
|
||||
|
||||
// Draw the arrow pointing to a node's location
|
||||
static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
|
||||
void Screen::drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian)
|
||||
{
|
||||
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
|
||||
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
|
||||
@@ -1423,16 +1284,45 @@ static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t comp
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
arrowPoints[i]->rotate(headingRadian);
|
||||
arrowPoints[i]->scale(getCompassDiam(display) * 0.6);
|
||||
arrowPoints[i]->scale(compassDiam * 0.6);
|
||||
arrowPoints[i]->translate(compassX, compassY);
|
||||
}
|
||||
drawLine(display, tip, tail);
|
||||
drawLine(display, leftArrow, tip);
|
||||
drawLine(display, rightArrow, tip);
|
||||
display->drawLine(tip.x, tip.y, tail.x, tail.y);
|
||||
display->drawLine(leftArrow.x, leftArrow.y, tip.x, tip.y);
|
||||
display->drawLine(rightArrow.x, rightArrow.y, tip.x, tip.y);
|
||||
}
|
||||
|
||||
// Draw north
|
||||
static void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
|
||||
// Get a string representation of the time passed since something happened
|
||||
void Screen::getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
|
||||
{
|
||||
// Use an absolute timestamp in some cases.
|
||||
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
|
||||
uint8_t timestampHours, timestampMinutes;
|
||||
int32_t daysAgo;
|
||||
bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo);
|
||||
|
||||
if (agoSecs < 120) // last 2 mins?
|
||||
snprintf(timeStr, maxLength, "%u seconds ago", agoSecs);
|
||||
// -- if suitable for timestamp --
|
||||
else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
|
||||
snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
|
||||
else if (useTimestamp && daysAgo == 0) // Today
|
||||
snprintf(timeStr, maxLength, "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
|
||||
else if (useTimestamp && daysAgo == 1) // Yesterday
|
||||
snprintf(timeStr, maxLength, "Seen yesterday");
|
||||
else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
|
||||
snprintf(timeStr, maxLength, "%li days ago", (long)daysAgo);
|
||||
// -- if using time delta instead --
|
||||
else if (agoSecs < 120 * 60) // last 2 hrs
|
||||
snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / 60);
|
||||
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
|
||||
else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
|
||||
snprintf(timeStr, maxLength, "%u hours ago", agoSecs / 60 / 60);
|
||||
else
|
||||
snprintf(timeStr, maxLength, "unknown age");
|
||||
}
|
||||
|
||||
void Screen::drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
|
||||
{
|
||||
// If north is supposed to be at the top of the compass we want rotation to be +0
|
||||
if (config.display.compass_north_top)
|
||||
@@ -1442,19 +1332,43 @@ static void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t com
|
||||
Point N3(-0.04f, 0.55f), N4(0.04f, 0.55f);
|
||||
Point *rosePoints[] = {&N1, &N2, &N3, &N4};
|
||||
|
||||
uint16_t compassDiam = Screen::getCompassDiam(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
// North on compass will be negative of heading
|
||||
rosePoints[i]->rotate(-myHeading);
|
||||
rosePoints[i]->scale(getCompassDiam(display));
|
||||
rosePoints[i]->scale(compassDiam);
|
||||
rosePoints[i]->translate(compassX, compassY);
|
||||
}
|
||||
drawLine(display, N1, N3);
|
||||
drawLine(display, N2, N4);
|
||||
drawLine(display, N1, N4);
|
||||
display->drawLine(N1.x, N1.y, N3.x, N3.y);
|
||||
display->drawLine(N2.x, N2.y, N4.x, N4.y);
|
||||
display->drawLine(N1.x, N1.y, N4.x, N4.y);
|
||||
}
|
||||
|
||||
/// Convert an integer GPS coords to a floating point
|
||||
#define DegD(i) (i * 1e-7)
|
||||
uint16_t Screen::getCompassDiam(uint32_t displayWidth, uint32_t displayHeight)
|
||||
{
|
||||
uint16_t diam = 0;
|
||||
uint16_t offset = 0;
|
||||
|
||||
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT)
|
||||
offset = FONT_HEIGHT_SMALL;
|
||||
|
||||
// get the smaller of the 2 dimensions and subtract 20
|
||||
if (displayWidth > (displayHeight - offset)) {
|
||||
diam = displayHeight - offset;
|
||||
// if 2/3 of the other size would be smaller, use that
|
||||
if (diam > (displayWidth * 2 / 3)) {
|
||||
diam = displayWidth * 2 / 3;
|
||||
}
|
||||
} else {
|
||||
diam = displayWidth;
|
||||
if (diam > ((displayHeight - offset) * 2 / 3)) {
|
||||
diam = (displayHeight - offset) * 2 / 3;
|
||||
}
|
||||
}
|
||||
|
||||
return diam - 20;
|
||||
};
|
||||
|
||||
static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
@@ -1494,34 +1408,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
|
||||
}
|
||||
|
||||
uint32_t agoSecs = sinceLastSeen(node);
|
||||
static char lastStr[20];
|
||||
|
||||
// Use an absolute timestamp in some cases.
|
||||
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
|
||||
uint8_t timestampHours, timestampMinutes;
|
||||
int32_t daysAgo;
|
||||
bool useTimestamp = deltaToTimestamp(agoSecs, ×tampHours, ×tampMinutes, &daysAgo);
|
||||
|
||||
if (agoSecs < 120) // last 2 mins?
|
||||
snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
|
||||
// -- if suitable for timestamp --
|
||||
else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
|
||||
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
|
||||
else if (useTimestamp && daysAgo == 0) // Today
|
||||
snprintf(lastStr, sizeof(lastStr), "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
|
||||
else if (useTimestamp && daysAgo == 1) // Yesterday
|
||||
snprintf(lastStr, sizeof(lastStr), "Seen yesterday");
|
||||
else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
|
||||
snprintf(lastStr, sizeof(lastStr), "%li days ago", (long)daysAgo);
|
||||
// -- if using time delta instead --
|
||||
else if (agoSecs < 120 * 60) // last 2 hrs
|
||||
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
|
||||
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
|
||||
else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
|
||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
||||
else
|
||||
snprintf(lastStr, sizeof(lastStr), "unknown age");
|
||||
screen->getTimeAgoStr(sinceLastSeen(node), lastStr, sizeof(lastStr));
|
||||
|
||||
static char distStr[20];
|
||||
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
||||
@@ -1532,13 +1420,14 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
const char *fields[] = {username, lastStr, signalStr, distStr, NULL};
|
||||
int16_t compassX = 0, compassY = 0;
|
||||
uint16_t compassDiam = Screen::getCompassDiam(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
|
||||
// coordinates for the center of the compass/circle
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
||||
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
|
||||
compassX = x + SCREEN_WIDTH - compassDiam / 2 - 5;
|
||||
compassY = y + SCREEN_HEIGHT / 2;
|
||||
} else {
|
||||
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
|
||||
compassX = x + SCREEN_WIDTH - compassDiam / 2 - 5;
|
||||
compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2;
|
||||
}
|
||||
bool hasNodeHeading = false;
|
||||
@@ -1549,8 +1438,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
if (screen->hasHeading())
|
||||
myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
|
||||
else
|
||||
myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
drawCompassNorth(display, compassX, compassY, myHeading);
|
||||
myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
screen->drawCompassNorth(display, compassX, compassY, myHeading);
|
||||
|
||||
if (hasValidPosition(node)) {
|
||||
// display direction toward node
|
||||
@@ -1577,7 +1466,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
// If the top of the compass is not a static north we need adjust bearingToOther based on heading
|
||||
if (!config.display.compass_north_top)
|
||||
bearingToOther -= myHeading;
|
||||
drawNodeHeading(display, compassX, compassY, bearingToOther);
|
||||
screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther);
|
||||
}
|
||||
}
|
||||
if (!hasNodeHeading) {
|
||||
@@ -1587,13 +1476,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
// hasValidPosition(node));
|
||||
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
|
||||
}
|
||||
display->drawCircle(compassX, compassY, getCompassDiam(display) / 2);
|
||||
display->drawCircle(compassX, compassY, compassDiam / 2);
|
||||
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
|
||||
display->setColor(BLACK);
|
||||
}
|
||||
// Must be after distStr is populated
|
||||
drawColumns(display, x, y, fields);
|
||||
screen->drawColumns(display, x, y, fields);
|
||||
}
|
||||
|
||||
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
|
||||
@@ -1741,9 +1630,19 @@ void Screen::setup()
|
||||
|
||||
// Add frames.
|
||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST);
|
||||
static FrameCallback bootFrames[] = {drawBootScreen};
|
||||
static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
|
||||
ui->setFrames(bootFrames, bootFrameCount);
|
||||
alertFrames[0] = [this](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
#ifdef ARCH_ESP32
|
||||
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER || wakeCause == ESP_SLEEP_WAKEUP_EXT1) {
|
||||
drawFrameText(display, state, x, y, "Resuming...");
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// Draw region in upper left
|
||||
const char *region = myRegion ? myRegion->name : NULL;
|
||||
drawIconScreen(region, display, state, x, y);
|
||||
}
|
||||
};
|
||||
ui->setFrames(alertFrames, 1);
|
||||
// No overlays.
|
||||
ui->setOverlays(nullptr, 0);
|
||||
|
||||
@@ -1916,13 +1815,22 @@ int32_t Screen::runOnce()
|
||||
case Cmd::SHOW_NEXT_FRAME:
|
||||
handleShowNextFrame();
|
||||
break;
|
||||
case Cmd::START_BLUETOOTH_PIN_SCREEN:
|
||||
handleStartBluetoothPinScreen(cmd.bluetooth_pin);
|
||||
case Cmd::START_ALERT_FRAME: {
|
||||
showingBootScreen = false; // this should avoid the edge case where an alert triggers before the boot screen goes away
|
||||
showingNormalScreen = false;
|
||||
alertFrames[0] = alertFrame;
|
||||
#ifdef USE_EINK
|
||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please
|
||||
EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update
|
||||
handleSetOn(true); // Ensure power-on to receive deep-sleep screensaver (PowerFSM should handle?)
|
||||
#endif
|
||||
setFrameImmediateDraw(alertFrames);
|
||||
break;
|
||||
}
|
||||
case Cmd::START_FIRMWARE_UPDATE_SCREEN:
|
||||
handleStartFirmwareUpdateScreen();
|
||||
break;
|
||||
case Cmd::STOP_BLUETOOTH_PIN_SCREEN:
|
||||
case Cmd::STOP_ALERT_FRAME:
|
||||
case Cmd::STOP_BOOT_SCREEN:
|
||||
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
|
||||
setFrames();
|
||||
@@ -1931,12 +1839,6 @@ int32_t Screen::runOnce()
|
||||
handlePrint(cmd.print_text);
|
||||
free(cmd.print_text);
|
||||
break;
|
||||
case Cmd::START_SHUTDOWN_SCREEN:
|
||||
handleShutdownScreen();
|
||||
break;
|
||||
case Cmd::START_REBOOT_SCREEN:
|
||||
handleRebootScreen();
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("Invalid screen cmd\n");
|
||||
}
|
||||
@@ -2133,10 +2035,6 @@ void Screen::setFrames()
|
||||
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
|
||||
normalFrames[numframes++] = drawTextMessageFrame;
|
||||
}
|
||||
// If we have a waypoint - show it next, unless it's a phone message and we aren't using any special modules
|
||||
if (devicestate.has_rx_waypoint && shouldDrawMessage(&devicestate.rx_waypoint)) {
|
||||
normalFrames[numframes++] = drawWaypointFrame;
|
||||
}
|
||||
|
||||
// then all the nodes
|
||||
// We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens
|
||||
@@ -2176,17 +2074,6 @@ void Screen::setFrames()
|
||||
setFastFramerate(); // Draw ASAP
|
||||
}
|
||||
|
||||
void Screen::handleStartBluetoothPinScreen(uint32_t pin)
|
||||
{
|
||||
LOG_DEBUG("showing bluetooth screen\n");
|
||||
showingNormalScreen = false;
|
||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
|
||||
|
||||
static FrameCallback frames[] = {drawFrameBluetooth};
|
||||
snprintf(btPIN, sizeof(btPIN), "%06u", pin);
|
||||
setFrameImmediateDraw(frames);
|
||||
}
|
||||
|
||||
void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
|
||||
{
|
||||
ui->disableAllIndicators();
|
||||
@@ -2194,41 +2081,6 @@ void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
void Screen::handleShutdownScreen()
|
||||
{
|
||||
LOG_DEBUG("showing shutdown screen\n");
|
||||
showingNormalScreen = false;
|
||||
#ifdef USE_EINK
|
||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please
|
||||
EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update
|
||||
handleSetOn(true); // Ensure power-on to receive deep-sleep screensaver (PowerFSM should handle?)
|
||||
#endif
|
||||
|
||||
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
drawFrameText(display, state, x, y, "Shutting down...");
|
||||
};
|
||||
static FrameCallback frames[] = {frame};
|
||||
|
||||
setFrameImmediateDraw(frames);
|
||||
}
|
||||
|
||||
void Screen::handleRebootScreen()
|
||||
{
|
||||
LOG_DEBUG("showing reboot screen\n");
|
||||
showingNormalScreen = false;
|
||||
#ifdef USE_EINK
|
||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please
|
||||
EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update
|
||||
handleSetOn(true); // Power-on to show rebooting screen (PowerFSM should handle?)
|
||||
#endif
|
||||
|
||||
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
drawFrameText(display, state, x, y, "Rebooting...");
|
||||
};
|
||||
static FrameCallback frames[] = {frame};
|
||||
setFrameImmediateDraw(frames);
|
||||
}
|
||||
|
||||
void Screen::handleStartFirmwareUpdateScreen()
|
||||
{
|
||||
LOG_DEBUG("showing firmware screen\n");
|
||||
@@ -2245,7 +2097,7 @@ void Screen::blink()
|
||||
uint8_t count = 10;
|
||||
dispdev->setBrightness(254);
|
||||
while (count > 0) {
|
||||
dispdev->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||
dispdev->fillRect(0, 0, dispdev->getWidth(), dispdev->getHeight());
|
||||
dispdev->display();
|
||||
delay(50);
|
||||
dispdev->clear();
|
||||
|
||||
@@ -21,11 +21,13 @@ class Screen
|
||||
void print(const char *) {}
|
||||
void doDeepSleep() {}
|
||||
void forceDisplay(bool forceUiUpdate = false) {}
|
||||
void startBluetoothPinScreen(uint32_t pin) {}
|
||||
void stopBluetoothPinScreen() {}
|
||||
void startRebootScreen() {}
|
||||
void startShutdownScreen() {}
|
||||
void startFirmwareUpdateScreen() {}
|
||||
void increaseBrightness() {}
|
||||
void decreaseBrightness() {}
|
||||
void setFunctionSymbal(std::string) {}
|
||||
void removeFunctionSymbal(std::string) {}
|
||||
void startAlert(const char *) {}
|
||||
void endAlert() {}
|
||||
};
|
||||
} // namespace graphics
|
||||
#else
|
||||
@@ -34,6 +36,8 @@ class Screen
|
||||
#include <OLEDDisplayUi.h>
|
||||
|
||||
#include "../configuration.h"
|
||||
#include "gps/GeoCoord.h"
|
||||
#include "graphics/ScreenFonts.h"
|
||||
|
||||
#ifdef USE_ST7567
|
||||
#include <ST7567Wire.h>
|
||||
@@ -82,6 +86,46 @@ class Screen
|
||||
#define SEGMENT_WIDTH 16
|
||||
#define SEGMENT_HEIGHT 4
|
||||
|
||||
/// Convert an integer GPS coords to a floating point
|
||||
#define DegD(i) (i * 1e-7)
|
||||
|
||||
namespace
|
||||
{
|
||||
/// A basic 2D point class for drawing
|
||||
class Point
|
||||
{
|
||||
public:
|
||||
float x, y;
|
||||
|
||||
Point(float _x, float _y) : x(_x), y(_y) {}
|
||||
|
||||
/// Apply a rotation around zero (standard rotation matrix math)
|
||||
void rotate(float radian)
|
||||
{
|
||||
float cos = cosf(radian), sin = sinf(radian);
|
||||
float rx = x * cos + y * sin, ry = -x * sin + y * cos;
|
||||
|
||||
x = rx;
|
||||
y = ry;
|
||||
}
|
||||
|
||||
void translate(int16_t dx, int dy)
|
||||
{
|
||||
x += dx;
|
||||
y += dy;
|
||||
}
|
||||
|
||||
void scale(float f)
|
||||
{
|
||||
// We use -f here to counter the flip that happens
|
||||
// on the y axis when drawing and rotating on screen
|
||||
x *= f;
|
||||
y *= -f;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
@@ -166,20 +210,49 @@ class Screen : public concurrency::OSThread
|
||||
|
||||
void blink();
|
||||
|
||||
void drawFrameText(OLEDDisplay *, OLEDDisplayUiState *, int16_t, int16_t, const char *);
|
||||
|
||||
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
|
||||
|
||||
// Draw north
|
||||
void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading);
|
||||
|
||||
static uint16_t getCompassDiam(uint32_t displayWidth, uint32_t displayHeight);
|
||||
|
||||
float estimatedHeading(double lat, double lon);
|
||||
|
||||
void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian);
|
||||
|
||||
void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields);
|
||||
|
||||
/// Handle button press, trackball or swipe action)
|
||||
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
|
||||
void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); }
|
||||
void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); }
|
||||
|
||||
/// Starts showing the Bluetooth PIN screen.
|
||||
//
|
||||
// Switches over to a static frame showing the Bluetooth pairing screen
|
||||
// with the PIN.
|
||||
void startBluetoothPinScreen(uint32_t pin)
|
||||
// generic alert start
|
||||
void startAlert(FrameCallback _alertFrame)
|
||||
{
|
||||
alertFrame = _alertFrame;
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::START_ALERT_FRAME;
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
void startAlert(const char *_alertMessage)
|
||||
{
|
||||
startAlert([_alertMessage](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
uint16_t x_offset = display->width() / 2;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, 26 + y, _alertMessage);
|
||||
});
|
||||
}
|
||||
|
||||
void endAlert()
|
||||
{
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN;
|
||||
cmd.bluetooth_pin = pin;
|
||||
cmd.cmd = Cmd::STOP_ALERT_FRAME;
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
@@ -190,20 +263,6 @@ class Screen : public concurrency::OSThread
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
void startShutdownScreen()
|
||||
{
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::START_SHUTDOWN_SCREEN;
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
void startRebootScreen()
|
||||
{
|
||||
ScreenCmd cmd;
|
||||
cmd.cmd = Cmd::START_REBOOT_SCREEN;
|
||||
enqueueCmd(cmd);
|
||||
}
|
||||
|
||||
// Function to allow the AccelerometerThread to set the heading if a sensor provides it
|
||||
// Mutex needed?
|
||||
void setHeading(long _heading)
|
||||
@@ -222,9 +281,6 @@ class Screen : public concurrency::OSThread
|
||||
void setFunctionSymbal(std::string sym);
|
||||
void removeFunctionSymbal(std::string sym);
|
||||
|
||||
/// Stops showing the bluetooth PIN screen.
|
||||
void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
|
||||
|
||||
/// Stops showing the boot screen.
|
||||
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
|
||||
|
||||
@@ -358,7 +414,13 @@ class Screen : public concurrency::OSThread
|
||||
|
||||
bool isAUTOOled = false;
|
||||
|
||||
// Screen dimensions (for convenience)
|
||||
// Defined during Screen::setup
|
||||
uint16_t displayWidth = 0;
|
||||
uint16_t displayHeight = 0;
|
||||
|
||||
private:
|
||||
FrameCallback alertFrames[1];
|
||||
struct ScreenCmd {
|
||||
Cmd cmd;
|
||||
union {
|
||||
@@ -384,11 +446,8 @@ class Screen : public concurrency::OSThread
|
||||
void handleOnPress();
|
||||
void handleShowNextFrame();
|
||||
void handleShowPrevFrame();
|
||||
void handleStartBluetoothPinScreen(uint32_t pin);
|
||||
void handlePrint(const char *text);
|
||||
void handleStartFirmwareUpdateScreen();
|
||||
void handleShutdownScreen();
|
||||
void handleRebootScreen();
|
||||
/// Rebuilds our list of frames (screens) to default ones.
|
||||
void setFrames();
|
||||
|
||||
@@ -426,6 +485,9 @@ class Screen : public concurrency::OSThread
|
||||
bool digitalWatchFace = true;
|
||||
#endif
|
||||
|
||||
/// callback for current alert frame
|
||||
FrameCallback alertFrame;
|
||||
|
||||
/// Queue of commands to execute in doTask.
|
||||
TypedQueue<ScreenCmd> cmdQueue;
|
||||
/// Whether we are using a display
|
||||
@@ -452,4 +514,5 @@ class Screen : public concurrency::OSThread
|
||||
};
|
||||
|
||||
} // namespace graphics
|
||||
|
||||
#endif
|
||||
@@ -28,8 +28,8 @@
|
||||
#define FONT_LARGE ArialMT_Plain_24 // Height: 28
|
||||
#endif
|
||||
|
||||
#define fontHeight(font) ((font)[1] + 1) // height is position 1
|
||||
#define _fontHeight(font) ((font)[1] + 1) // height is position 1
|
||||
|
||||
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
|
||||
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
|
||||
#define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE)
|
||||
#define FONT_HEIGHT_SMALL _fontHeight(FONT_SMALL)
|
||||
#define FONT_HEIGHT_MEDIUM _fontHeight(FONT_MEDIUM)
|
||||
#define FONT_HEIGHT_LARGE _fontHeight(FONT_LARGE)
|
||||
20
src/main.cpp
20
src/main.cpp
@@ -6,6 +6,7 @@
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "PowerMon.h"
|
||||
#include "ReliableRouter.h"
|
||||
#include "airtime.h"
|
||||
#include "buzz.h"
|
||||
@@ -48,7 +49,6 @@ NimbleBluetooth *nimbleBluetooth = nullptr;
|
||||
#ifdef ARCH_NRF52
|
||||
#include "NRF52Bluetooth.h"
|
||||
NRF52Bluetooth *nrf52Bluetooth = nullptr;
|
||||
;
|
||||
#endif
|
||||
|
||||
#if HAS_WIFI
|
||||
@@ -155,6 +155,7 @@ bool isVibrating = false;
|
||||
bool eink_found = true;
|
||||
|
||||
uint32_t serialSinceMsec;
|
||||
bool pauseBluetoothLogging = false;
|
||||
|
||||
bool pmu_found;
|
||||
|
||||
@@ -173,7 +174,7 @@ const char *getDeviceName()
|
||||
static char name[20];
|
||||
snprintf(name, sizeof(name), "%02x%02x", dmac[4], dmac[5]);
|
||||
// if the shortname exists and is NOT the new default of ab3c, use it for BLE name.
|
||||
if ((owner.short_name != NULL) && (strcmp(owner.short_name, name) != 0)) {
|
||||
if (strcmp(owner.short_name, name) != 0) {
|
||||
snprintf(name, sizeof(name), "%s_%02x%02x", owner.short_name, dmac[4], dmac[5]);
|
||||
} else {
|
||||
snprintf(name, sizeof(name), "Meshtastic_%02x%02x", dmac[4], dmac[5]);
|
||||
@@ -214,6 +215,14 @@ __attribute__((weak, noinline)) bool loopCanSleep()
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print info as a structured log message (for automated log processing)
|
||||
*/
|
||||
void printInfo()
|
||||
{
|
||||
LOG_INFO("S:B:%d,%s\n", HW_VENDOR, optstr(APP_VERSION));
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
concurrency::hasBeenSetup = true;
|
||||
@@ -221,7 +230,7 @@ void setup()
|
||||
meshtastic_Config_DisplayConfig_OledType::meshtastic_Config_DisplayConfig_OledType_OLED_AUTO;
|
||||
OLEDDISPLAY_GEOMETRY screen_geometry = GEOMETRY_128_64;
|
||||
|
||||
#ifdef SEGGER_STDOUT_CH
|
||||
#ifdef USE_SEGGER
|
||||
auto mode = false ? SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL : SEGGER_RTT_MODE_NO_BLOCK_TRIM;
|
||||
#ifdef NRF52840_XXAA
|
||||
auto buflen = 4096; // this board has a fair amount of ram
|
||||
@@ -234,6 +243,7 @@ void setup()
|
||||
#ifdef DEBUG_PORT
|
||||
consoleInit(); // Set serial baud rate and init our mesh console
|
||||
#endif
|
||||
powerMonInit();
|
||||
|
||||
serialSinceMsec = millis();
|
||||
|
||||
@@ -553,7 +563,7 @@ void setup()
|
||||
#endif
|
||||
|
||||
// Hello
|
||||
LOG_INFO("Meshtastic hwvendor=%d, swver=%s\n", HW_VENDOR, optstr(APP_VERSION));
|
||||
printInfo();
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
esp32Setup();
|
||||
@@ -930,7 +940,7 @@ void setup()
|
||||
nodeDB->saveToDisk(SEGMENT_CONFIG);
|
||||
if (!rIf->reconfigure()) {
|
||||
LOG_WARN("Reconfigure failed, rebooting\n");
|
||||
screen->startRebootScreen();
|
||||
screen->startAlert("Rebooting...");
|
||||
rebootAtMsec = millis() + 5000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,8 @@ extern uint32_t serialSinceMsec;
|
||||
// This will suppress the current delay and instead try to run ASAP.
|
||||
extern bool runASAP;
|
||||
|
||||
extern bool pauseBluetoothLogging;
|
||||
|
||||
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearBonds(), enterDfuMode();
|
||||
|
||||
meshtastic_DeviceMetadata getDeviceMetadata();
|
||||
|
||||
@@ -20,9 +20,8 @@ ErrorCode FloodingRouter::send(meshtastic_MeshPacket *p)
|
||||
bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
{
|
||||
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
|
||||
printPacket("Ignoring incoming msg, because we've already seen it", p);
|
||||
printPacket("Ignoring incoming msg we've already seen", p);
|
||||
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT &&
|
||||
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
|
||||
Router::cancelSending(p->from, p->id);
|
||||
|
||||
@@ -184,6 +184,7 @@ template <typename T> void LR11x0Interface<T>::setStandby()
|
||||
activeReceiveStart = 0;
|
||||
disableInterrupt();
|
||||
completeSending(); // If we were sending, not anymore
|
||||
RadioLibInterface::setStandby();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,7 +224,7 @@ template <typename T> void LR11x0Interface<T>::startReceive()
|
||||
0); // only RX_DONE IRQ is needed, we'll check for PREAMBLE_DETECTED and HEADER_VALID in isActivelyReceiving
|
||||
assert(err == RADIOLIB_ERR_NONE);
|
||||
|
||||
isReceiving = true;
|
||||
RadioLibInterface::startReceive();
|
||||
|
||||
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
|
||||
enableInterrupt(isrRxLevel0);
|
||||
|
||||
@@ -269,7 +269,7 @@ bool MeshService::trySendPosition(NodeNum dest, bool wantReplies)
|
||||
assert(node);
|
||||
|
||||
if (hasValidPosition(node)) {
|
||||
#if HAS_GPS
|
||||
#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS
|
||||
if (positionModule) {
|
||||
LOG_INFO("Sending position ping to 0x%x, wantReplies=%d, channel=%d\n", dest, wantReplies, node->channel);
|
||||
positionModule->sendOurPosition(dest, wantReplies, node->channel);
|
||||
@@ -299,6 +299,7 @@ void MeshService::sendToPhone(meshtastic_MeshPacket *p)
|
||||
} else {
|
||||
LOG_WARN("ToPhone queue is full, dropping packet.\n");
|
||||
releaseToPool(p);
|
||||
fromNum++; // Make sure to notify observers in case they are reconnected so they can get the packets
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -373,8 +374,8 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
|
||||
pos.time = getValidTime(RTCQualityFromNet);
|
||||
|
||||
// In debug logs, identify position by @timestamp:stage (stage 4 = nodeDB)
|
||||
LOG_DEBUG("onGPSChanged() pos@%x, time=%u, lat=%d, lon=%d, alt=%d\n", pos.timestamp, pos.time, pos.latitude_i,
|
||||
pos.longitude_i, pos.altitude);
|
||||
LOG_DEBUG("onGPSChanged() pos@%x time=%u lat=%d lon=%d alt=%d\n", pos.timestamp, pos.time, pos.latitude_i, pos.longitude_i,
|
||||
pos.altitude);
|
||||
|
||||
// Update our current position in the local DB
|
||||
nodeDB->updatePosition(nodeDB->getNodeNum(), pos, RX_SRC_LOCAL);
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <vector>
|
||||
|
||||
#ifdef ARCH_ESP32
|
||||
#if !MESHTASTIC_EXCLUDE_WIFI
|
||||
#if HAS_WIFI
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#endif
|
||||
#include "modules/esp32/StoreForwardModule.h"
|
||||
@@ -141,11 +141,6 @@ NodeDB::NodeDB()
|
||||
if (channelFileCRC != crc32Buffer(&channelFile, sizeof(channelFile)))
|
||||
saveWhat |= SEGMENT_CHANNELS;
|
||||
|
||||
if (!devicestate.node_remote_hardware_pins) {
|
||||
meshtastic_NodeRemoteHardwarePin empty[12] = {meshtastic_RemoteHardwarePin_init_default};
|
||||
memcpy(devicestate.node_remote_hardware_pins, empty, sizeof(empty));
|
||||
}
|
||||
|
||||
if (config.position.gps_enabled) {
|
||||
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
|
||||
config.position.gps_enabled = 0;
|
||||
@@ -185,7 +180,7 @@ bool NodeDB::resetRadioConfig(bool factory_reset)
|
||||
|
||||
if (didFactoryReset) {
|
||||
LOG_INFO("Rebooting due to factory reset");
|
||||
screen->startRebootScreen();
|
||||
screen->startAlert("Rebooting...");
|
||||
rebootAtMsec = millis() + (5 * 1000);
|
||||
}
|
||||
|
||||
@@ -826,8 +821,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
|
||||
if (src == RX_SRC_LOCAL) {
|
||||
// Local packet, fully authoritative
|
||||
LOG_INFO("updatePosition LOCAL pos@%x, time=%u, latI=%d, lonI=%d, alt=%d\n", p.timestamp, p.time, p.latitude_i,
|
||||
p.longitude_i, p.altitude);
|
||||
LOG_INFO("updatePosition LOCAL pos@%x time=%u lat=%d lon=%d alt=%d\n", p.timestamp, p.time, p.latitude_i, p.longitude_i,
|
||||
p.altitude);
|
||||
|
||||
setLocalPosition(p);
|
||||
info->position = TypeConversions::ConvertToPositionLite(p);
|
||||
@@ -842,7 +837,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
|
||||
// recorded based on the packet rxTime
|
||||
//
|
||||
// FIXME perhaps handle RX_SRC_USER separately?
|
||||
LOG_INFO("updatePosition REMOTE node=0x%x time=%u, latI=%d, lonI=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i);
|
||||
LOG_INFO("updatePosition REMOTE node=0x%x time=%u lat=%d lon=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i);
|
||||
|
||||
// First, back up fields that we want to protect from overwrite
|
||||
uint32_t tmp_time = info->position.time;
|
||||
|
||||
@@ -155,8 +155,8 @@ class NodeDB
|
||||
localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time;
|
||||
return;
|
||||
}
|
||||
LOG_DEBUG("Setting local position: latitude=%i, longitude=%i, time=%u, timestamp=%u\n", position.latitude_i,
|
||||
position.longitude_i, position.time, position.timestamp);
|
||||
LOG_DEBUG("Setting local position: lat=%i lon=%i time=%u timestamp=%u\n", position.latitude_i, position.longitude_i,
|
||||
position.time, position.timestamp);
|
||||
localPosition = position;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "Channels.h"
|
||||
#include "Default.h"
|
||||
#include "FSCommon.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PhoneAPI.h"
|
||||
@@ -46,6 +47,9 @@ void PhoneAPI::handleStartConfig()
|
||||
|
||||
// even if we were already connected - restart our state machine
|
||||
state = STATE_SEND_MY_INFO;
|
||||
pauseBluetoothLogging = true;
|
||||
filesManifest = getFiles("/", 10);
|
||||
LOG_DEBUG("Got %d files in manifest\n", filesManifest.size());
|
||||
|
||||
LOG_INFO("Starting API client config\n");
|
||||
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
|
||||
@@ -148,6 +152,7 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
|
||||
STATE_SEND_CONFIG,
|
||||
STATE_SEND_MODULE_CONFIG,
|
||||
STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to the client
|
||||
STATE_SEND_FILEMANIFEST,
|
||||
STATE_SEND_COMPLETE_ID,
|
||||
STATE_SEND_PACKETS // send packets or debug strings
|
||||
*/
|
||||
@@ -323,7 +328,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
// Advance when we have sent all of our ModuleConfig objects
|
||||
if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) {
|
||||
// Clients sending special nonce don't want to see other nodeinfos
|
||||
state = config_nonce == SPECIAL_NONCE ? STATE_SEND_COMPLETE_ID : STATE_SEND_OTHER_NODEINFOS;
|
||||
state = config_nonce == SPECIAL_NONCE ? STATE_SEND_FILEMANIFEST : STATE_SEND_OTHER_NODEINFOS;
|
||||
config_state = 0;
|
||||
}
|
||||
break;
|
||||
@@ -339,22 +344,36 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time
|
||||
} else {
|
||||
LOG_INFO("Done sending nodeinfos\n");
|
||||
state = STATE_SEND_COMPLETE_ID;
|
||||
state = STATE_SEND_FILEMANIFEST;
|
||||
// Go ahead and send that ID right now
|
||||
return getFromRadio(buf);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_SEND_FILEMANIFEST: {
|
||||
LOG_INFO("getFromRadio=STATE_SEND_FILEMANIFEST\n");
|
||||
// last element
|
||||
if (config_state == filesManifest.size()) { // also handles an empty filesManifest
|
||||
config_state = 0;
|
||||
filesManifest.clear();
|
||||
// Skip to complete packet
|
||||
sendConfigComplete();
|
||||
} else {
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_fileInfo_tag;
|
||||
fromRadioScratch.fileInfo = filesManifest.at(config_state);
|
||||
LOG_DEBUG("File: %s (%d) bytes\n", fromRadioScratch.fileInfo.file_name, fromRadioScratch.fileInfo.size_bytes);
|
||||
config_state++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case STATE_SEND_COMPLETE_ID:
|
||||
LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n");
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag;
|
||||
fromRadioScratch.config_complete_id = config_nonce;
|
||||
config_nonce = 0;
|
||||
state = STATE_SEND_PACKETS;
|
||||
sendConfigComplete();
|
||||
break;
|
||||
|
||||
case STATE_SEND_PACKETS:
|
||||
pauseBluetoothLogging = false;
|
||||
// Do we have a message from the mesh or packet from the local device?
|
||||
LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n");
|
||||
if (queueStatusPacketForPhone) {
|
||||
@@ -388,7 +407,9 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
// Encapsulate as a FromRadio packet
|
||||
size_t numbytes = pb_encode_to_bytes(buf, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch);
|
||||
|
||||
LOG_DEBUG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payload_variant, numbytes);
|
||||
// VERY IMPORTANT to not print debug messages while writing to fromRadioScratch - because we use that same buffer
|
||||
// for logging (when we are encapsulating with protobufs)
|
||||
// LOG_DEBUG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payload_variant, numbytes);
|
||||
return numbytes;
|
||||
}
|
||||
|
||||
@@ -396,8 +417,20 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PhoneAPI::sendConfigComplete()
|
||||
{
|
||||
LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n");
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag;
|
||||
fromRadioScratch.config_complete_id = config_nonce;
|
||||
config_nonce = 0;
|
||||
state = STATE_SEND_PACKETS;
|
||||
pauseBluetoothLogging = false;
|
||||
}
|
||||
|
||||
void PhoneAPI::handleDisconnect()
|
||||
{
|
||||
filesManifest.clear();
|
||||
pauseBluetoothLogging = false;
|
||||
LOG_INFO("PhoneAPI disconnect\n");
|
||||
}
|
||||
|
||||
@@ -439,6 +472,7 @@ bool PhoneAPI::available()
|
||||
case STATE_SEND_MODULECONFIG:
|
||||
case STATE_SEND_METADATA:
|
||||
case STATE_SEND_OWN_NODEINFO:
|
||||
case STATE_SEND_FILEMANIFEST:
|
||||
case STATE_SEND_COMPLETE_ID:
|
||||
return true;
|
||||
|
||||
@@ -453,7 +487,6 @@ bool PhoneAPI::available()
|
||||
}
|
||||
}
|
||||
return true; // Always say we have something, because we might need to advance our state machine
|
||||
|
||||
case STATE_SEND_PACKETS: {
|
||||
if (!queueStatusPacketForPhone)
|
||||
queueStatusPacketForPhone = service.getQueueStatusForPhone();
|
||||
|
||||
@@ -2,10 +2,20 @@
|
||||
|
||||
#include "Observer.h"
|
||||
#include "mesh-pb-constants.h"
|
||||
#include <iterator>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// Make sure that we never let our packets grow too large for one BLE packet
|
||||
#define MAX_TO_FROM_RADIO_SIZE 512
|
||||
|
||||
#if meshtastic_FromRadio_size > MAX_TO_FROM_RADIO_SIZE
|
||||
#error "meshtastic_FromRadio_size is too large for our BLE packets"
|
||||
#endif
|
||||
#if meshtastic_ToRadio_size > MAX_TO_FROM_RADIO_SIZE
|
||||
#error "meshtastic_ToRadio_size is too large for our BLE packets"
|
||||
#endif
|
||||
|
||||
#define SPECIAL_NONCE 69420
|
||||
|
||||
/**
|
||||
@@ -29,6 +39,7 @@ class PhoneAPI
|
||||
STATE_SEND_CONFIG, // Replacement for the old Radioconfig
|
||||
STATE_SEND_MODULECONFIG, // Send Module specific config
|
||||
STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to to the client
|
||||
STATE_SEND_FILEMANIFEST, // Send file manifest
|
||||
STATE_SEND_COMPLETE_ID,
|
||||
STATE_SEND_PACKETS // send packets or debug strings
|
||||
};
|
||||
@@ -65,6 +76,8 @@ class PhoneAPI
|
||||
uint32_t config_nonce = 0;
|
||||
uint32_t readIndex = 0;
|
||||
|
||||
std::vector<meshtastic_FileInfo> filesManifest = {};
|
||||
|
||||
void resetReadIndex() { readIndex = 0; }
|
||||
|
||||
public:
|
||||
@@ -91,6 +104,8 @@ class PhoneAPI
|
||||
*/
|
||||
size_t getFromRadio(uint8_t *buf);
|
||||
|
||||
void sendConfigComplete();
|
||||
|
||||
/**
|
||||
* Return true if we have data available to send to the phone
|
||||
*/
|
||||
@@ -98,8 +113,6 @@ class PhoneAPI
|
||||
|
||||
bool isConnected() { return state != STATE_SEND_NOTHING; }
|
||||
|
||||
void setInitialState() { state = STATE_SEND_MY_INFO; }
|
||||
|
||||
protected:
|
||||
/// Our fromradio packet while it is being assembled
|
||||
meshtastic_FromRadio fromRadioScratch = {};
|
||||
|
||||
@@ -25,7 +25,8 @@ typedef struct {
|
||||
} DACDB;
|
||||
|
||||
// Interpolation function
|
||||
DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val2) {
|
||||
DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val2)
|
||||
{
|
||||
DACDB result;
|
||||
double fraction = (double)(dbm - dbm1) / (dbm2 - dbm1);
|
||||
result.dac = (uint8_t)(val1.dac + fraction * (val2.dac - val1.dac));
|
||||
@@ -34,7 +35,8 @@ DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val
|
||||
}
|
||||
|
||||
// Function to find the correct DAC and DB values based on dBm using interpolation
|
||||
DACDB getDACandDB(uint8_t dbm) {
|
||||
DACDB getDACandDB(uint8_t dbm)
|
||||
{
|
||||
// Predefined values
|
||||
static const struct {
|
||||
uint8_t dbm;
|
||||
@@ -116,13 +118,13 @@ bool RF95Interface::init()
|
||||
// enable PA
|
||||
#ifdef RF95_PA_EN
|
||||
#if defined(RF95_PA_DAC_EN)
|
||||
#ifdef RADIOMASTER_900_BANDIT_NANO
|
||||
#ifdef RADIOMASTER_900_BANDIT_NANO
|
||||
// Use calculated DAC value
|
||||
dacWrite(RF95_PA_EN, powerDAC);
|
||||
#else
|
||||
#else
|
||||
// Use Value set in /*/variant.h
|
||||
dacWrite(RF95_PA_EN, RF95_PA_LEVEL);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -254,6 +256,7 @@ void RF95Interface::setStandby()
|
||||
isReceiving = false; // If we were receiving, not any more
|
||||
disableInterrupt();
|
||||
completeSending(); // If we were sending, not anymore
|
||||
RadioLibInterface::setStandby();
|
||||
}
|
||||
|
||||
/** We override to turn on transmitter power as needed.
|
||||
|
||||
@@ -261,7 +261,6 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
|
||||
uint8_t CWsize = map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax);
|
||||
// LOG_DEBUG("rx_snr of %f so setting CWsize to:%d\n", snr, CWsize);
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT ||
|
||||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
|
||||
delay = random(0, 2 * CWsize) * slotTimeMsec;
|
||||
LOG_DEBUG("rx_snr found in packet. As a router, setting tx delay:%d\n", delay);
|
||||
@@ -522,7 +521,7 @@ void RadioInterface::applyModemConfig()
|
||||
LOG_INFO("Radio freq=%.3f, config.lora.frequency_offset=%.3f\n", freq, loraConfig.frequency_offset);
|
||||
LOG_INFO("Set radio: region=%s, name=%s, config=%u, ch=%d, power=%d\n", myRegion->name, channelName, loraConfig.modem_preset,
|
||||
channel_num, power);
|
||||
LOG_INFO("Radio myRegion->freqStart -> myRegion->freqEnd: %f -> %f (%f mhz)\n", myRegion->freqStart, myRegion->freqEnd,
|
||||
LOG_INFO("Radio myRegion->freqStart -> myRegion->freqEnd: %f -> %f (%f MHz)\n", myRegion->freqStart, myRegion->freqEnd,
|
||||
myRegion->freqEnd - myRegion->freqStart);
|
||||
LOG_INFO("Radio myRegion->numChannels: %d x %.3fkHz\n", numChannels, bw);
|
||||
LOG_INFO("Radio channel_num: %d\n", channel_num + 1);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "RadioLibInterface.h"
|
||||
#include "MeshTypes.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerMon.h"
|
||||
#include "SPILock.h"
|
||||
#include "configuration.h"
|
||||
#include "error.h"
|
||||
@@ -317,6 +318,7 @@ void RadioLibInterface::handleTransmitInterrupt()
|
||||
// ignore the transmit interrupt
|
||||
if (sendingPacket)
|
||||
completeSending();
|
||||
powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // But our transmitter is deffinitely off now
|
||||
}
|
||||
|
||||
void RadioLibInterface::completeSending()
|
||||
@@ -412,6 +414,24 @@ void RadioLibInterface::handleReceiveInterrupt()
|
||||
}
|
||||
}
|
||||
|
||||
void RadioLibInterface::startReceive()
|
||||
{
|
||||
isReceiving = true;
|
||||
powerMon->setState(meshtastic_PowerMon_State_Lora_RXOn);
|
||||
}
|
||||
|
||||
void RadioLibInterface::configHardwareForSend()
|
||||
{
|
||||
powerMon->setState(meshtastic_PowerMon_State_Lora_TXOn);
|
||||
}
|
||||
|
||||
void RadioLibInterface::setStandby()
|
||||
{
|
||||
// neither sending nor receiving
|
||||
powerMon->clearState(meshtastic_PowerMon_State_Lora_RXOn);
|
||||
powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn);
|
||||
}
|
||||
|
||||
/** start an immediate transmit */
|
||||
void RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
|
||||
{
|
||||
@@ -431,6 +451,7 @@ void RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
|
||||
|
||||
// This send failed, but make sure to 'complete' it properly
|
||||
completeSending();
|
||||
powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // Transmitter off now
|
||||
startReceive(); // Restart receive mode (because startTransmit failed to put us in xmit mode)
|
||||
}
|
||||
|
||||
|
||||
@@ -126,8 +126,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
* Start waiting to receive a message
|
||||
*
|
||||
* External functions can call this method to wake the device from sleep.
|
||||
* Subclasses must override and call this base method
|
||||
*/
|
||||
virtual void startReceive() = 0;
|
||||
virtual void startReceive();
|
||||
|
||||
/** can we detect a LoRa preamble on the current channel? */
|
||||
virtual bool isChannelActive() = 0;
|
||||
@@ -166,8 +167,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
meshtastic_QueueStatus getQueueStatus();
|
||||
|
||||
protected:
|
||||
/** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */
|
||||
virtual void configHardwareForSend() {}
|
||||
/** Do any hardware setup needed on entry into send configuration for the radio.
|
||||
* Subclasses can customize, but must also call this base method */
|
||||
virtual void configHardwareForSend();
|
||||
|
||||
/** Could we send right now (i.e. either not actively receiving or transmitting)? */
|
||||
virtual bool canSendImmediately();
|
||||
@@ -186,5 +188,8 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
|
||||
*/
|
||||
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) = 0;
|
||||
|
||||
virtual void setStandby() = 0;
|
||||
/**
|
||||
* Subclasses must override, implement and then call into this base class implementation
|
||||
*/
|
||||
virtual void setStandby();
|
||||
};
|
||||
@@ -244,8 +244,10 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
|
||||
|
||||
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
|
||||
|
||||
assert(p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag ||
|
||||
p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now
|
||||
if (!(p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag ||
|
||||
p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)) {
|
||||
return meshtastic_Routing_Error_BAD_REQUEST;
|
||||
}
|
||||
|
||||
// If the packet is not yet encrypted, do so now
|
||||
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {
|
||||
|
||||
@@ -231,6 +231,7 @@ template <typename T> void SX126xInterface<T>::setStandby()
|
||||
activeReceiveStart = 0;
|
||||
disableInterrupt();
|
||||
completeSending(); // If we were sending, not anymore
|
||||
RadioLibInterface::setStandby();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,7 +271,7 @@ template <typename T> void SX126xInterface<T>::startReceive()
|
||||
LOG_ERROR("Radiolib error %d when attempting SX126X startReceiveDutyCycleAuto!\n", err);
|
||||
assert(err == RADIOLIB_ERR_NONE);
|
||||
|
||||
isReceiving = true;
|
||||
RadioLibInterface::startReceive();
|
||||
|
||||
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
|
||||
enableInterrupt(isrRxLevel0);
|
||||
|
||||
@@ -190,6 +190,7 @@ template <typename T> void SX128xInterface<T>::setStandby()
|
||||
activeReceiveStart = 0;
|
||||
disableInterrupt();
|
||||
completeSending(); // If we were sending, not anymore
|
||||
RadioLibInterface::setStandby();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,7 +264,7 @@ template <typename T> void SX128xInterface<T>::startReceive()
|
||||
LOG_ERROR("Radiolib error %d when attempting SX128X startReceive!\n", err);
|
||||
assert(err == RADIOLIB_ERR_NONE);
|
||||
|
||||
isReceiving = true;
|
||||
RadioLibInterface::startReceive();
|
||||
|
||||
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
|
||||
enableInterrupt(isrRxLevel0);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "StreamAPI.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "RTC.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#define START1 0x94
|
||||
@@ -96,7 +97,6 @@ void StreamAPI::writeStream()
|
||||
void StreamAPI::emitTxBuffer(size_t len)
|
||||
{
|
||||
if (len != 0) {
|
||||
// LOG_DEBUG("emit tx %d\n", len);
|
||||
txBuf[0] = START1;
|
||||
txBuf[1] = START2;
|
||||
txBuf[2] = (len >> 8) & 0xff;
|
||||
@@ -119,6 +119,25 @@ void StreamAPI::emitRebooted()
|
||||
emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch));
|
||||
}
|
||||
|
||||
void StreamAPI::emitLogRecord(meshtastic_LogRecord_Level level, const char *src, const char *format, va_list arg)
|
||||
{
|
||||
// In case we send a FromRadio packet
|
||||
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
|
||||
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_log_record_tag;
|
||||
fromRadioScratch.log_record.level = level;
|
||||
|
||||
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
|
||||
fromRadioScratch.log_record.time = rtc_sec;
|
||||
strncpy(fromRadioScratch.log_record.source, src, sizeof(fromRadioScratch.log_record.source) - 1);
|
||||
|
||||
auto num_printed =
|
||||
vsnprintf(fromRadioScratch.log_record.message, sizeof(fromRadioScratch.log_record.message) - 1, format, arg);
|
||||
if (num_printed > 0 && fromRadioScratch.log_record.message[num_printed - 1] ==
|
||||
'\n') // Strip any ending newline, because we have records for framing instead.
|
||||
fromRadioScratch.log_record.message[num_printed - 1] = '\0';
|
||||
emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch));
|
||||
}
|
||||
|
||||
/// Hookable to find out when connection changes
|
||||
void StreamAPI::onConnectionChanged(bool connected)
|
||||
{
|
||||
|
||||
@@ -82,4 +82,7 @@ class StreamAPI : public PhoneAPI
|
||||
|
||||
/// Subclasses can use this scratch buffer if they wish
|
||||
uint8_t txBuf[MAX_STREAM_BUF_SIZE] = {0};
|
||||
|
||||
/// Low level function to emit a protobuf encapsulated log record
|
||||
void emitLogRecord(meshtastic_LogRecord_Level level, const char *src, const char *format, va_list arg);
|
||||
};
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <RAK13800_W5100S.h>
|
||||
#include <SPI.h>
|
||||
|
||||
#if HAS_NETWORKING
|
||||
|
||||
#ifndef DISABLE_NTP
|
||||
#include <NTPClient.h>
|
||||
|
||||
@@ -183,3 +185,5 @@ bool isEthernetAvailable()
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -22,7 +22,6 @@ typedef enum _meshtastic_Config_DeviceConfig_Role {
|
||||
The wifi radio and the oled screen will be put to sleep.
|
||||
This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. */
|
||||
meshtastic_Config_DeviceConfig_Role_ROUTER = 2,
|
||||
/* Description: Combination of both ROUTER and CLIENT. Not for mobile devices. */
|
||||
meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT = 3,
|
||||
/* Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
|
||||
Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
|
||||
@@ -372,6 +371,9 @@ typedef struct _meshtastic_Config_PowerConfig {
|
||||
uint32_t min_wake_secs;
|
||||
/* I2C address of INA_2XX to use for reading device battery voltage */
|
||||
uint8_t device_battery_ina_address;
|
||||
/* If non-zero, we want powermon log outputs. With the particular (bitfield) sources enabled.
|
||||
Note: we picked an ID of 32 so that lower more efficient IDs can be used for more frequently used options. */
|
||||
uint64_t powermon_enables;
|
||||
} meshtastic_Config_PowerConfig;
|
||||
|
||||
typedef struct _meshtastic_Config_NetworkConfig_IpV4Config {
|
||||
@@ -612,7 +614,7 @@ extern "C" {
|
||||
#define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}}
|
||||
#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
|
||||
#define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN}
|
||||
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
|
||||
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
|
||||
#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}
|
||||
@@ -621,7 +623,7 @@ extern "C" {
|
||||
#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}
|
||||
#define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN}
|
||||
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
|
||||
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
|
||||
#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}
|
||||
@@ -662,6 +664,7 @@ extern "C" {
|
||||
#define meshtastic_Config_PowerConfig_ls_secs_tag 7
|
||||
#define meshtastic_Config_PowerConfig_min_wake_secs_tag 8
|
||||
#define meshtastic_Config_PowerConfig_device_battery_ina_address_tag 9
|
||||
#define meshtastic_Config_PowerConfig_powermon_enables_tag 32
|
||||
#define meshtastic_Config_NetworkConfig_IpV4Config_ip_tag 1
|
||||
#define meshtastic_Config_NetworkConfig_IpV4Config_gateway_tag 2
|
||||
#define meshtastic_Config_NetworkConfig_IpV4Config_subnet_tag 3
|
||||
@@ -773,7 +776,8 @@ X(a, STATIC, SINGULAR, UINT32, wait_bluetooth_secs, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, sds_secs, 6) \
|
||||
X(a, STATIC, SINGULAR, UINT32, ls_secs, 7) \
|
||||
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, device_battery_ina_address, 9)
|
||||
X(a, STATIC, SINGULAR, UINT32, device_battery_ina_address, 9) \
|
||||
X(a, STATIC, SINGULAR, UINT64, powermon_enables, 32)
|
||||
#define meshtastic_Config_PowerConfig_CALLBACK NULL
|
||||
#define meshtastic_Config_PowerConfig_DEFAULT NULL
|
||||
|
||||
@@ -871,7 +875,7 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg;
|
||||
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20
|
||||
#define meshtastic_Config_NetworkConfig_size 196
|
||||
#define meshtastic_Config_PositionConfig_size 62
|
||||
#define meshtastic_Config_PowerConfig_size 40
|
||||
#define meshtastic_Config_PowerConfig_size 52
|
||||
#define meshtastic_Config_size 199
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "meshtastic/channel.pb.h"
|
||||
#include "meshtastic/localonly.pb.h"
|
||||
#include "meshtastic/mesh.pb.h"
|
||||
#include "meshtastic/module_config.pb.h"
|
||||
#include "meshtastic/telemetry.pb.h"
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
@@ -308,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 3372
|
||||
#define meshtastic_OEMStore_size 3384
|
||||
#define meshtastic_PositionLite_size 28
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -181,7 +181,7 @@ 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 541
|
||||
#define meshtastic_LocalConfig_size 553
|
||||
#define meshtastic_LocalModuleConfig_size 685
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
@@ -36,7 +36,7 @@ PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, AUTO)
|
||||
PB_BIND(meshtastic_MyNodeInfo, meshtastic_MyNodeInfo, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_LogRecord, meshtastic_LogRecord, AUTO)
|
||||
PB_BIND(meshtastic_LogRecord, meshtastic_LogRecord, 2)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
|
||||
@@ -45,6 +45,9 @@ PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
|
||||
PB_BIND(meshtastic_FromRadio, meshtastic_FromRadio, 2)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_FileInfo, meshtastic_FileInfo, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_ToRadio, meshtastic_ToRadio, 2)
|
||||
|
||||
|
||||
|
||||
@@ -167,6 +167,15 @@ typedef enum _meshtastic_HardwareModel {
|
||||
meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO = 64,
|
||||
/* Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors */
|
||||
meshtastic_HardwareModel_HELTEC_CAPSULE_SENSOR_V3 = 65,
|
||||
/* Heltec Vision Master T190 with ESP32-S3 CPU, and a 1.90 inch TFT display */
|
||||
meshtastic_HardwareModel_HELTEC_VISION_MASTER_T190 = 66,
|
||||
/* Heltec Vision Master E213 with ESP32-S3 CPU, and a 2.13 inch E-Ink display */
|
||||
meshtastic_HardwareModel_HELTEC_VISION_MASTER_E213 = 67,
|
||||
/* Heltec Vision Master E290 with ESP32-S3 CPU, and a 2.9 inch E-Ink display */
|
||||
meshtastic_HardwareModel_HELTEC_VISION_MASTER_E290 = 68,
|
||||
/* Heltec Mesh Node T114 board with nRF52840 CPU, and a 1.14 inch TFT display, Ultimate low-power design,
|
||||
specifically adapted for the Meshtatic project */
|
||||
meshtastic_HardwareModel_HELTEC_MESH_NODE_T114 = 69,
|
||||
/* ------------------------------------------------------------------------------------------------------------------------------------------
|
||||
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
|
||||
------------------------------------------------------------------------------------------------------------------------------------------ */
|
||||
@@ -691,11 +700,11 @@ typedef struct _meshtastic_MyNodeInfo {
|
||||
and then extend as needed by emitting multiple records. */
|
||||
typedef struct _meshtastic_LogRecord {
|
||||
/* Log levels, chosen to match python logging conventions. */
|
||||
char message[64];
|
||||
char message[384];
|
||||
/* Seconds since 1970 - or 0 for unknown/unset */
|
||||
uint32_t time;
|
||||
/* Usually based on thread name - if known */
|
||||
char source[8];
|
||||
char source[32];
|
||||
/* Not yet set */
|
||||
meshtastic_LogRecord_Level level;
|
||||
} meshtastic_LogRecord;
|
||||
@@ -711,6 +720,14 @@ typedef struct _meshtastic_QueueStatus {
|
||||
uint32_t mesh_packet_id;
|
||||
} meshtastic_QueueStatus;
|
||||
|
||||
/* Individual File info for the device */
|
||||
typedef struct _meshtastic_FileInfo {
|
||||
/* The fully qualified path of the file */
|
||||
char file_name[228];
|
||||
/* The size of the file in bytes */
|
||||
uint32_t size_bytes;
|
||||
} meshtastic_FileInfo;
|
||||
|
||||
typedef PB_BYTES_ARRAY_T(237) meshtastic_Compressed_data_t;
|
||||
/* Compressed message payload */
|
||||
typedef struct _meshtastic_Compressed {
|
||||
@@ -815,6 +832,8 @@ typedef struct _meshtastic_FromRadio {
|
||||
meshtastic_DeviceMetadata metadata;
|
||||
/* MQTT Client Proxy Message (device sending to client / phone for publishing to MQTT) */
|
||||
meshtastic_MqttClientProxyMessage mqttClientProxyMessage;
|
||||
/* File system manifest messages */
|
||||
meshtastic_FileInfo fileInfo;
|
||||
};
|
||||
} meshtastic_FromRadio;
|
||||
|
||||
@@ -958,6 +977,7 @@ extern "C" {
|
||||
|
||||
|
||||
|
||||
|
||||
#define meshtastic_Compressed_portnum_ENUMTYPE meshtastic_PortNum
|
||||
|
||||
|
||||
@@ -985,6 +1005,7 @@ extern "C" {
|
||||
#define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN}
|
||||
#define meshtastic_QueueStatus_init_default {0, 0, 0, 0}
|
||||
#define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}}
|
||||
#define meshtastic_FileInfo_init_default {"", 0}
|
||||
#define meshtastic_ToRadio_init_default {0, {meshtastic_MeshPacket_init_default}}
|
||||
#define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}}
|
||||
#define meshtastic_NeighborInfo_init_default {0, 0, 0, 0, {meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default}}
|
||||
@@ -1008,6 +1029,7 @@ extern "C" {
|
||||
#define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN}
|
||||
#define meshtastic_QueueStatus_init_zero {0, 0, 0, 0}
|
||||
#define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}}
|
||||
#define meshtastic_FileInfo_init_zero {"", 0}
|
||||
#define meshtastic_ToRadio_init_zero {0, {meshtastic_MeshPacket_init_zero}}
|
||||
#define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}}
|
||||
#define meshtastic_NeighborInfo_init_zero {0, 0, 0, 0, {meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero}}
|
||||
@@ -1110,6 +1132,8 @@ extern "C" {
|
||||
#define meshtastic_QueueStatus_free_tag 2
|
||||
#define meshtastic_QueueStatus_maxlen_tag 3
|
||||
#define meshtastic_QueueStatus_mesh_packet_id_tag 4
|
||||
#define meshtastic_FileInfo_file_name_tag 1
|
||||
#define meshtastic_FileInfo_size_bytes_tag 2
|
||||
#define meshtastic_Compressed_portnum_tag 1
|
||||
#define meshtastic_Compressed_data_tag 2
|
||||
#define meshtastic_Neighbor_node_id_tag 1
|
||||
@@ -1144,6 +1168,7 @@ extern "C" {
|
||||
#define meshtastic_FromRadio_xmodemPacket_tag 12
|
||||
#define meshtastic_FromRadio_metadata_tag 13
|
||||
#define meshtastic_FromRadio_mqttClientProxyMessage_tag 14
|
||||
#define meshtastic_FromRadio_fileInfo_tag 15
|
||||
#define meshtastic_ToRadio_packet_tag 1
|
||||
#define meshtastic_ToRadio_want_config_id_tag 3
|
||||
#define meshtastic_ToRadio_disconnect_tag 4
|
||||
@@ -1321,7 +1346,8 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,channel,channel), 10) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 11) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 12) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14)
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15)
|
||||
#define meshtastic_FromRadio_CALLBACK NULL
|
||||
#define meshtastic_FromRadio_DEFAULT NULL
|
||||
#define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket
|
||||
@@ -1335,6 +1361,13 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttC
|
||||
#define meshtastic_FromRadio_payload_variant_xmodemPacket_MSGTYPE meshtastic_XModem
|
||||
#define meshtastic_FromRadio_payload_variant_metadata_MSGTYPE meshtastic_DeviceMetadata
|
||||
#define meshtastic_FromRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage
|
||||
#define meshtastic_FromRadio_payload_variant_fileInfo_MSGTYPE meshtastic_FileInfo
|
||||
|
||||
#define meshtastic_FileInfo_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, STRING, file_name, 1) \
|
||||
X(a, STATIC, SINGULAR, UINT32, size_bytes, 2)
|
||||
#define meshtastic_FileInfo_CALLBACK NULL
|
||||
#define meshtastic_FileInfo_DEFAULT NULL
|
||||
|
||||
#define meshtastic_ToRadio_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 1) \
|
||||
@@ -1434,6 +1467,7 @@ extern const pb_msgdesc_t meshtastic_MyNodeInfo_msg;
|
||||
extern const pb_msgdesc_t meshtastic_LogRecord_msg;
|
||||
extern const pb_msgdesc_t meshtastic_QueueStatus_msg;
|
||||
extern const pb_msgdesc_t meshtastic_FromRadio_msg;
|
||||
extern const pb_msgdesc_t meshtastic_FileInfo_msg;
|
||||
extern const pb_msgdesc_t meshtastic_ToRadio_msg;
|
||||
extern const pb_msgdesc_t meshtastic_Compressed_msg;
|
||||
extern const pb_msgdesc_t meshtastic_NeighborInfo_msg;
|
||||
@@ -1459,6 +1493,7 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_LogRecord_fields &meshtastic_LogRecord_msg
|
||||
#define meshtastic_QueueStatus_fields &meshtastic_QueueStatus_msg
|
||||
#define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg
|
||||
#define meshtastic_FileInfo_fields &meshtastic_FileInfo_msg
|
||||
#define meshtastic_ToRadio_fields &meshtastic_ToRadio_msg
|
||||
#define meshtastic_Compressed_fields &meshtastic_Compressed_msg
|
||||
#define meshtastic_NeighborInfo_fields &meshtastic_NeighborInfo_msg
|
||||
@@ -1478,9 +1513,10 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
|
||||
#define meshtastic_Compressed_size 243
|
||||
#define meshtastic_Data_size 270
|
||||
#define meshtastic_DeviceMetadata_size 46
|
||||
#define meshtastic_FileInfo_size 236
|
||||
#define meshtastic_FromRadio_size 510
|
||||
#define meshtastic_Heartbeat_size 0
|
||||
#define meshtastic_LogRecord_size 81
|
||||
#define meshtastic_LogRecord_size 426
|
||||
#define meshtastic_MeshPacket_size 326
|
||||
#define meshtastic_MqttClientProxyMessage_size 501
|
||||
#define meshtastic_MyNodeInfo_size 18
|
||||
|
||||
@@ -124,6 +124,8 @@ typedef enum _meshtastic_PortNum {
|
||||
meshtastic_PortNum_ATAK_PLUGIN = 72,
|
||||
/* Provides unencrypted information about a node for consumption by a map via MQTT */
|
||||
meshtastic_PortNum_MAP_REPORT_APP = 73,
|
||||
/* PowerStress based monitoring support (for automated power consumption testing) */
|
||||
meshtastic_PortNum_POWERSTRESS_APP = 74,
|
||||
/* Private applications should use portnums >= 256.
|
||||
To simplify initial development and testing you can use "PRIVATE_APP"
|
||||
in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) */
|
||||
|
||||
17
src/mesh/generated/meshtastic/powermon.pb.cpp
Normal file
17
src/mesh/generated/meshtastic/powermon.pb.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.8 */
|
||||
|
||||
#include "meshtastic/powermon.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(meshtastic_PowerMon, meshtastic_PowerMon, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_PowerStressMessage, meshtastic_PowerStressMessage, AUTO)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
138
src/mesh/generated/meshtastic/powermon.pb.h
Normal file
138
src/mesh/generated/meshtastic/powermon.pb.h
Normal file
@@ -0,0 +1,138 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.8 */
|
||||
|
||||
#ifndef PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
|
||||
#define PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
|
||||
#include <pb.h>
|
||||
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
/* Enum definitions */
|
||||
/* Any significant power changing event in meshtastic should be tagged with a powermon state transition.
|
||||
If you are making new meshtastic features feel free to add new entries at the end of this definition. */
|
||||
typedef enum _meshtastic_PowerMon_State {
|
||||
meshtastic_PowerMon_State_None = 0,
|
||||
meshtastic_PowerMon_State_CPU_DeepSleep = 1,
|
||||
meshtastic_PowerMon_State_CPU_LightSleep = 2,
|
||||
/* The external Vext1 power is on. Many boards have auxillary power rails that the CPU turns on only
|
||||
occasionally. In cases where that rail has multiple devices on it we usually want to have logging on
|
||||
the state of that rail as an independent record.
|
||||
For instance on the Heltec Tracker 1.1 board, this rail is the power source for the GPS and screen.
|
||||
|
||||
The log messages will be short and complete (see PowerMon.Event in the protobufs for details).
|
||||
something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of all current states.
|
||||
(We use a bitmask for states so that if a log message gets lost it won't be fatal) */
|
||||
meshtastic_PowerMon_State_Vext1_On = 4,
|
||||
meshtastic_PowerMon_State_Lora_RXOn = 8,
|
||||
meshtastic_PowerMon_State_Lora_TXOn = 16,
|
||||
meshtastic_PowerMon_State_Lora_RXActive = 32,
|
||||
meshtastic_PowerMon_State_BT_On = 64,
|
||||
meshtastic_PowerMon_State_LED_On = 128,
|
||||
meshtastic_PowerMon_State_Screen_On = 256,
|
||||
meshtastic_PowerMon_State_Screen_Drawing = 512,
|
||||
meshtastic_PowerMon_State_Wifi_On = 1024,
|
||||
/* GPS is actively trying to find our location
|
||||
See GPSPowerState for more details */
|
||||
meshtastic_PowerMon_State_GPS_Active = 2048
|
||||
} meshtastic_PowerMon_State;
|
||||
|
||||
/* What operation would we like the UUT to perform.
|
||||
note: senders should probably set want_response in their request packets, so that they can know when the state
|
||||
machine has started processing their request */
|
||||
typedef enum _meshtastic_PowerStressMessage_Opcode {
|
||||
/* Unset/unused */
|
||||
meshtastic_PowerStressMessage_Opcode_UNSET = 0,
|
||||
meshtastic_PowerStressMessage_Opcode_PRINT_INFO = 1, /* Print board version slog and send an ack that we are alive and ready to process commands */
|
||||
meshtastic_PowerStressMessage_Opcode_FORCE_QUIET = 2, /* Try to turn off all automatic processing of packets, screen, sleeping, etc (to make it easier to measure in isolation) */
|
||||
meshtastic_PowerStressMessage_Opcode_END_QUIET = 3, /* Stop powerstress processing - probably by just rebooting the board */
|
||||
meshtastic_PowerStressMessage_Opcode_SCREEN_ON = 16, /* Turn the screen on */
|
||||
meshtastic_PowerStressMessage_Opcode_SCREEN_OFF = 17, /* Turn the screen off */
|
||||
meshtastic_PowerStressMessage_Opcode_CPU_IDLE = 32, /* Let the CPU run but we assume mostly idling for num_seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_CPU_DEEPSLEEP = 33, /* Force deep sleep for FIXME seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_CPU_FULLON = 34, /* Spin the CPU as fast as possible for num_seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_LED_ON = 48, /* Turn the LED on for num_seconds (and leave it on - for baseline power measurement purposes) */
|
||||
meshtastic_PowerStressMessage_Opcode_LED_OFF = 49, /* Force the LED off for num_seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_LORA_OFF = 64, /* Completely turn off the LORA radio for num_seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_LORA_TX = 65, /* Send Lora packets for num_seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_LORA_RX = 66, /* Receive Lora packets for num_seconds (node will be mostly just listening, unless an external agent is helping stress this by sending packets on the current channel) */
|
||||
meshtastic_PowerStressMessage_Opcode_BT_OFF = 80, /* Turn off the BT radio for num_seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_BT_ON = 81, /* Turn on the BT radio for num_seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_WIFI_OFF = 96, /* Turn off the WIFI radio for num_seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_WIFI_ON = 97, /* Turn on the WIFI radio for num_seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_GPS_OFF = 112, /* Turn off the GPS radio for num_seconds */
|
||||
meshtastic_PowerStressMessage_Opcode_GPS_ON = 113 /* Turn on the GPS radio for num_seconds */
|
||||
} meshtastic_PowerStressMessage_Opcode;
|
||||
|
||||
/* Struct definitions */
|
||||
/* Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
|
||||
But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */
|
||||
typedef struct _meshtastic_PowerMon {
|
||||
char dummy_field;
|
||||
} meshtastic_PowerMon;
|
||||
|
||||
/* PowerStress testing support via the C++ PowerStress module */
|
||||
typedef struct _meshtastic_PowerStressMessage {
|
||||
/* What type of HardwareMessage is this? */
|
||||
meshtastic_PowerStressMessage_Opcode cmd;
|
||||
float num_seconds;
|
||||
} meshtastic_PowerStressMessage;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Helper constants for enums */
|
||||
#define _meshtastic_PowerMon_State_MIN meshtastic_PowerMon_State_None
|
||||
#define _meshtastic_PowerMon_State_MAX meshtastic_PowerMon_State_GPS_Active
|
||||
#define _meshtastic_PowerMon_State_ARRAYSIZE ((meshtastic_PowerMon_State)(meshtastic_PowerMon_State_GPS_Active+1))
|
||||
|
||||
#define _meshtastic_PowerStressMessage_Opcode_MIN meshtastic_PowerStressMessage_Opcode_UNSET
|
||||
#define _meshtastic_PowerStressMessage_Opcode_MAX meshtastic_PowerStressMessage_Opcode_GPS_ON
|
||||
#define _meshtastic_PowerStressMessage_Opcode_ARRAYSIZE ((meshtastic_PowerStressMessage_Opcode)(meshtastic_PowerStressMessage_Opcode_GPS_ON+1))
|
||||
|
||||
|
||||
#define meshtastic_PowerStressMessage_cmd_ENUMTYPE meshtastic_PowerStressMessage_Opcode
|
||||
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define meshtastic_PowerMon_init_default {0}
|
||||
#define meshtastic_PowerStressMessage_init_default {_meshtastic_PowerStressMessage_Opcode_MIN, 0}
|
||||
#define meshtastic_PowerMon_init_zero {0}
|
||||
#define meshtastic_PowerStressMessage_init_zero {_meshtastic_PowerStressMessage_Opcode_MIN, 0}
|
||||
|
||||
/* Field tags (for use in manual encoding/decoding) */
|
||||
#define meshtastic_PowerStressMessage_cmd_tag 1
|
||||
#define meshtastic_PowerStressMessage_num_seconds_tag 2
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define meshtastic_PowerMon_FIELDLIST(X, a) \
|
||||
|
||||
#define meshtastic_PowerMon_CALLBACK NULL
|
||||
#define meshtastic_PowerMon_DEFAULT NULL
|
||||
|
||||
#define meshtastic_PowerStressMessage_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UENUM, cmd, 1) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, num_seconds, 2)
|
||||
#define meshtastic_PowerStressMessage_CALLBACK NULL
|
||||
#define meshtastic_PowerStressMessage_DEFAULT NULL
|
||||
|
||||
extern const pb_msgdesc_t meshtastic_PowerMon_msg;
|
||||
extern const pb_msgdesc_t meshtastic_PowerStressMessage_msg;
|
||||
|
||||
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
|
||||
#define meshtastic_PowerMon_fields &meshtastic_PowerMon_msg
|
||||
#define meshtastic_PowerStressMessage_fields &meshtastic_PowerStressMessage_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define MESHTASTIC_MESHTASTIC_POWERMON_PB_H_MAX_SIZE meshtastic_PowerStressMessage_size
|
||||
#define meshtastic_PowerMon_size 0
|
||||
#define meshtastic_PowerStressMessage_size 7
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "main.h"
|
||||
#include "mesh/http/ContentHelper.h"
|
||||
#include "mesh/http/WebServer.h"
|
||||
#if !MESHTASTIC_EXCLUDE_WIFI
|
||||
#if HAS_WIFI
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#endif
|
||||
#include "mqtt/JSON.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "configuration.h"
|
||||
#if !MESHTASTIC_EXCLUDE_WIFI
|
||||
#if HAS_WIFI
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "concurrency/Periodic.h"
|
||||
|
||||
@@ -137,7 +137,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
if (BleOta::getOtaAppVersion().isEmpty()) {
|
||||
LOG_INFO("No OTA firmware available, scheduling regular reboot in %d seconds\n", s);
|
||||
screen->startRebootScreen();
|
||||
screen->startAlert("Rebooting...");
|
||||
} else {
|
||||
screen->startFirmwareUpdateScreen();
|
||||
BleOta::switchToOtaApp();
|
||||
@@ -145,7 +145,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
}
|
||||
#else
|
||||
LOG_INFO("Not on ESP32, scheduling regular reboot in %d seconds\n", s);
|
||||
screen->startRebootScreen();
|
||||
screen->startAlert("Rebooting...");
|
||||
#endif
|
||||
rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000);
|
||||
break;
|
||||
@@ -232,9 +232,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
|
||||
#if !MESHTASTIC_EXCLUDE_GPS
|
||||
if (gps != nullptr)
|
||||
gps->enable();
|
||||
#endif
|
||||
// Send our new fixed position to the mesh for good measure
|
||||
positionModule->sendOurPosition();
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -299,8 +299,8 @@ void AdminModule::handleGetModuleConfigResponse(const meshtastic_MeshPacket &mp,
|
||||
{
|
||||
// Skip if it's disabled or no pins are exposed
|
||||
if (!r->get_module_config_response.payload_variant.remote_hardware.enabled ||
|
||||
!r->get_module_config_response.payload_variant.remote_hardware.available_pins) {
|
||||
LOG_DEBUG("Remote hardware module disabled or no vailable_pins. Skipping...\n");
|
||||
r->get_module_config_response.payload_variant.remote_hardware.available_pins_count == 0) {
|
||||
LOG_DEBUG("Remote hardware module disabled or no available_pins. Skipping...\n");
|
||||
return;
|
||||
}
|
||||
for (uint8_t i = 0; i < devicestate.node_remote_hardware_pins_count; i++) {
|
||||
@@ -388,6 +388,10 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
|
||||
LOG_DEBUG("Tried to set node_info_broadcast_secs too low, setting to %d\n", min_node_info_broadcast_secs);
|
||||
config.device.node_info_broadcast_secs = min_node_info_broadcast_secs;
|
||||
}
|
||||
// Router Client is deprecated; Set it to client
|
||||
if (c.payload_variant.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT) {
|
||||
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
|
||||
}
|
||||
break;
|
||||
case meshtastic_Config_position_tag:
|
||||
LOG_INFO("Setting config: Position\n");
|
||||
@@ -811,7 +815,7 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch
|
||||
void AdminModule::reboot(int32_t seconds)
|
||||
{
|
||||
LOG_INFO("Rebooting in %d seconds\n", seconds);
|
||||
screen->startRebootScreen();
|
||||
screen->startAlert("Rebooting...");
|
||||
rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include "ProtobufModule.h"
|
||||
#if HAS_WIFI && !MESHTASTIC_EXCLUDE_WIFI
|
||||
#if HAS_WIFI
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#endif
|
||||
|
||||
|
||||
@@ -597,14 +597,14 @@ int32_t CannedMessageModule::runOnce()
|
||||
// handle fn+s for shutdown
|
||||
case 0x9b:
|
||||
if (screen)
|
||||
screen->startShutdownScreen();
|
||||
screen->startAlert("Shutting down...");
|
||||
shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000;
|
||||
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
break;
|
||||
// and fn+r for reboot
|
||||
case 0x90:
|
||||
if (screen)
|
||||
screen->startRebootScreen();
|
||||
screen->startAlert("Rebooting...");
|
||||
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
|
||||
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
break;
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE
|
||||
#include "modules/RemoteHardwareModule.h"
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_POWERSTRESS
|
||||
#include "modules/PowerStressModule.h"
|
||||
#endif
|
||||
#include "modules/RoutingModule.h"
|
||||
#include "modules/TextMessageModule.h"
|
||||
#if !MESHTASTIC_EXCLUDE_TRACEROUTE
|
||||
@@ -115,6 +118,9 @@ void setupModules()
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE
|
||||
new RemoteHardwareModule();
|
||||
#endif
|
||||
#if !MESHTASTIC_EXCLUDE_POWERSTRESS
|
||||
new PowerStressModule();
|
||||
#endif
|
||||
// Example: Put your module here
|
||||
// new ReplyModule();
|
||||
|
||||
@@ -73,7 +73,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
|
||||
}
|
||||
|
||||
// Log packet size and data fields
|
||||
LOG_DEBUG("POSITION node=%08x l=%d latI=%d lonI=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d "
|
||||
LOG_DEBUG("POSITION node=%08x l=%d lat=%d lon=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d "
|
||||
"time=%d\n",
|
||||
getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae,
|
||||
p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp,
|
||||
@@ -219,7 +219,7 @@ meshtastic_MeshPacket *PositionModule::allocReply()
|
||||
LOG_INFO("Providing time to mesh %u\n", p.time);
|
||||
}
|
||||
|
||||
LOG_INFO("Position reply: time=%i, latI=%i, lonI=%i\n", p.time, p.latitude_i, p.longitude_i);
|
||||
LOG_INFO("Position reply: time=%i lat=%i lon=%i\n", p.time, p.latitude_i, p.longitude_i);
|
||||
|
||||
// TAK Tracker devices should send their position in a TAK packet over the ATAK port
|
||||
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)
|
||||
|
||||
77
src/modules/PowerStressModule.cpp
Normal file
77
src/modules/PowerStressModule.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "PowerStressModule.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "RTC.h"
|
||||
#include "Router.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
|
||||
extern void printInfo();
|
||||
|
||||
PowerStressModule::PowerStressModule()
|
||||
: ProtobufModule("powerstress", meshtastic_PortNum_POWERSTRESS_APP, &meshtastic_PowerStressMessage_msg),
|
||||
concurrency::OSThread("PowerStressModule")
|
||||
{
|
||||
}
|
||||
|
||||
bool PowerStressModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, meshtastic_PowerStressMessage *pptr)
|
||||
{
|
||||
// We only respond to messages if powermon debugging is already on
|
||||
if (config.power.powermon_enables) {
|
||||
auto p = *pptr;
|
||||
LOG_INFO("Received PowerStress cmd=%d\n", p.cmd);
|
||||
|
||||
// Some commands we can handle immediately, anything else gets deferred to be handled by our thread
|
||||
switch (p.cmd) {
|
||||
case meshtastic_PowerStressMessage_Opcode_UNSET:
|
||||
LOG_ERROR("PowerStress operation unset\n");
|
||||
break;
|
||||
|
||||
case meshtastic_PowerStressMessage_Opcode_PRINT_INFO:
|
||||
printInfo();
|
||||
break;
|
||||
|
||||
default:
|
||||
if (currentMessage.cmd != meshtastic_PowerStressMessage_Opcode_UNSET)
|
||||
LOG_ERROR("PowerStress operation %d already in progress! Can't start new command\n", currentMessage.cmd);
|
||||
else
|
||||
currentMessage = p; // copy for use by thread (the message provided to us will be getting freed)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t PowerStressModule::runOnce()
|
||||
{
|
||||
|
||||
if (!config.power.powermon_enables) {
|
||||
// Powermon not enabled - stop using CPU/stop this thread
|
||||
return disable();
|
||||
}
|
||||
|
||||
int32_t sleep_msec = 10; // when not active check for new messages every 10ms
|
||||
|
||||
auto &p = currentMessage;
|
||||
|
||||
if (isRunningCommand) {
|
||||
// Done with the previous command - our sleep must have finished
|
||||
p.cmd = meshtastic_PowerStressMessage_Opcode_UNSET;
|
||||
p.num_seconds = 0;
|
||||
} else {
|
||||
sleep_msec = (int32_t)(p.num_seconds * 1000);
|
||||
isRunningCommand = !!sleep_msec; // if the command wants us to sleep, make sure to mark that we have something running
|
||||
|
||||
switch (p.cmd) {
|
||||
case meshtastic_PowerStressMessage_Opcode_UNSET: // No need to start a new command
|
||||
break;
|
||||
case meshtastic_PowerStressMessage_Opcode_LED_ON:
|
||||
break;
|
||||
default:
|
||||
LOG_ERROR("PowerStress operation %d not yet implemented!\n", p.cmd);
|
||||
sleep_msec = 0; // Don't do whatever sleep was requested...
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sleep_msec;
|
||||
}
|
||||
38
src/modules/PowerStressModule.h
Normal file
38
src/modules/PowerStressModule.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "ProtobufModule.h"
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "mesh/generated/meshtastic/powermon.pb.h"
|
||||
|
||||
/**
|
||||
* A module that provides easy low-level remote access to device hardware.
|
||||
*/
|
||||
class PowerStressModule : public ProtobufModule<meshtastic_PowerStressMessage>, private concurrency::OSThread
|
||||
{
|
||||
meshtastic_PowerStressMessage currentMessage = meshtastic_PowerStressMessage_init_default;
|
||||
bool isRunningCommand = false;
|
||||
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
PowerStressModule();
|
||||
|
||||
protected:
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_PowerStressMessage *p) override;
|
||||
|
||||
/**
|
||||
* Periodically read the gpios we have been asked to WATCH, if they have changed,
|
||||
* broadcast a message with the change information.
|
||||
*
|
||||
* The method that will be called each time our thread gets a chance to run
|
||||
*
|
||||
* Returns desired period for next invocation (or RUN_SAME for no change)
|
||||
*/
|
||||
virtual int32_t runOnce() override;
|
||||
};
|
||||
|
||||
extern PowerStressModule powerStressModule;
|
||||
@@ -85,32 +85,66 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m)
|
||||
{
|
||||
if (!aqi.read(&data)) {
|
||||
LOG_WARN("Skipping send measurements. Could not read AQIn\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||
m.time = getTime();
|
||||
m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
|
||||
m.variant.air_quality_metrics.pm10_standard = data.pm10_standard;
|
||||
m.variant.air_quality_metrics.pm25_standard = data.pm25_standard;
|
||||
m.variant.air_quality_metrics.pm100_standard = data.pm100_standard;
|
||||
m->time = getTime();
|
||||
m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
|
||||
m->variant.air_quality_metrics.pm10_standard = data.pm10_standard;
|
||||
m->variant.air_quality_metrics.pm25_standard = data.pm25_standard;
|
||||
m->variant.air_quality_metrics.pm100_standard = data.pm100_standard;
|
||||
|
||||
m.variant.air_quality_metrics.pm10_environmental = data.pm10_env;
|
||||
m.variant.air_quality_metrics.pm25_environmental = data.pm25_env;
|
||||
m.variant.air_quality_metrics.pm100_environmental = data.pm100_env;
|
||||
m->variant.air_quality_metrics.pm10_environmental = data.pm10_env;
|
||||
m->variant.air_quality_metrics.pm25_environmental = data.pm25_env;
|
||||
m->variant.air_quality_metrics.pm100_environmental = data.pm100_env;
|
||||
|
||||
LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i\n",
|
||||
m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard,
|
||||
m.variant.air_quality_metrics.pm100_standard);
|
||||
m->variant.air_quality_metrics.pm10_standard, m->variant.air_quality_metrics.pm25_standard,
|
||||
m->variant.air_quality_metrics.pm100_standard);
|
||||
|
||||
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n",
|
||||
m.variant.air_quality_metrics.pm10_environmental, m.variant.air_quality_metrics.pm25_environmental,
|
||||
m.variant.air_quality_metrics.pm100_environmental);
|
||||
m->variant.air_quality_metrics.pm10_environmental, m->variant.air_quality_metrics.pm25_environmental,
|
||||
m->variant.air_quality_metrics.pm100_environmental);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply()
|
||||
{
|
||||
if (currentRequest) {
|
||||
auto req = *currentRequest;
|
||||
const auto &p = req.decoded;
|
||||
meshtastic_Telemetry scratch;
|
||||
meshtastic_Telemetry *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
} else {
|
||||
LOG_ERROR("Error decoding AirQualityTelemetry module!\n");
|
||||
return NULL;
|
||||
}
|
||||
// Check for a request for air quality metrics
|
||||
if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
|
||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||
if (getAirQualityTelemetry(&m)) {
|
||||
LOG_INFO("Air quality telemetry replying to request\n");
|
||||
return allocDataProtobuf(m);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
{
|
||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||
if (getAirQualityTelemetry(&m)) {
|
||||
meshtastic_MeshPacket *p = allocDataProtobuf(m);
|
||||
p->to = dest;
|
||||
p->decoded.want_response = false;
|
||||
@@ -132,6 +166,9 @@ bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
service.sendToMesh(p, RX_SRC_LOCAL, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -26,6 +26,11 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
|
||||
virtual int32_t runOnce() override;
|
||||
/** Called to get current Air Quality data
|
||||
@return true if it contains valid data
|
||||
*/
|
||||
bool getAirQualityTelemetry(meshtastic_Telemetry *m);
|
||||
virtual meshtastic_MeshPacket *allocReply() override;
|
||||
/**
|
||||
* Send our Telemetry into the mesh
|
||||
*/
|
||||
|
||||
@@ -52,14 +52,27 @@ bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
|
||||
|
||||
meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
|
||||
{
|
||||
if (ignoreRequest) {
|
||||
if (currentRequest) {
|
||||
auto req = *currentRequest;
|
||||
const auto &p = req.decoded;
|
||||
meshtastic_Telemetry scratch;
|
||||
meshtastic_Telemetry *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
} else {
|
||||
LOG_ERROR("Error decoding DeviceTelemetry module!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Check for a request for device metrics
|
||||
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
|
||||
LOG_INFO("Device telemetry replying to request\n");
|
||||
|
||||
meshtastic_Telemetry telemetry = getDeviceTelemetry();
|
||||
return allocDataProtobuf(telemetry);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
|
||||
|
||||
@@ -198,7 +198,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
|
||||
if (lastMeasurementPacket == nullptr) {
|
||||
// If there's no valid packet, display "Environment"
|
||||
display->drawString(x, y, "Environment");
|
||||
display->drawString(x, y += fontHeight(FONT_SMALL), "No measurement");
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL), "No measurement");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -223,31 +223,31 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
|
||||
}
|
||||
|
||||
// Continue with the remaining details
|
||||
display->drawString(x, y += fontHeight(FONT_SMALL),
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Temp/Hum: " + last_temp + " / " +
|
||||
String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%");
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0) {
|
||||
display->drawString(x, y += fontHeight(FONT_SMALL),
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA");
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.voltage != 0) {
|
||||
display->drawString(x, y += fontHeight(FONT_SMALL),
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " +
|
||||
String(lastMeasurement.variant.environment_metrics.current, 0) + "mA");
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.iaq != 0) {
|
||||
display->drawString(x, y += fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq));
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq));
|
||||
}
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.distance != 0)
|
||||
display->drawString(x, y += fontHeight(FONT_SMALL),
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm");
|
||||
|
||||
if (lastMeasurement.variant.environment_metrics.weight != 0)
|
||||
display->drawString(x, y += fontHeight(FONT_SMALL),
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg");
|
||||
}
|
||||
|
||||
@@ -280,102 +280,138 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m)
|
||||
{
|
||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||
bool valid = true;
|
||||
bool hasSensor = false;
|
||||
m.time = getTime();
|
||||
m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
|
||||
m->time = getTime();
|
||||
m->which_variant = meshtastic_Telemetry_environment_metrics_tag;
|
||||
|
||||
#ifdef T1000X_SENSOR_EN // add by WayenWeng
|
||||
valid = valid && t1000xSensor.getMetrics(&m);
|
||||
hasSensor = true;
|
||||
#else
|
||||
if (dfRobotLarkSensor.hasSensor()) {
|
||||
valid = valid && dfRobotLarkSensor.getMetrics(&m);
|
||||
valid = valid && dfRobotLarkSensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (sht31Sensor.hasSensor()) {
|
||||
valid = valid && sht31Sensor.getMetrics(&m);
|
||||
valid = valid && sht31Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (sht4xSensor.hasSensor()) {
|
||||
valid = valid && sht4xSensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (lps22hbSensor.hasSensor()) {
|
||||
valid = valid && lps22hbSensor.getMetrics(&m);
|
||||
valid = valid && lps22hbSensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (shtc3Sensor.hasSensor()) {
|
||||
valid = valid && shtc3Sensor.getMetrics(&m);
|
||||
valid = valid && shtc3Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (bmp085Sensor.hasSensor()) {
|
||||
valid = valid && bmp085Sensor.getMetrics(&m);
|
||||
valid = valid && bmp085Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (bmp280Sensor.hasSensor()) {
|
||||
valid = valid && bmp280Sensor.getMetrics(&m);
|
||||
valid = valid && bmp280Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (bme280Sensor.hasSensor()) {
|
||||
valid = valid && bme280Sensor.getMetrics(&m);
|
||||
valid = valid && bme280Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (bme680Sensor.hasSensor()) {
|
||||
valid = valid && bme680Sensor.getMetrics(&m);
|
||||
valid = valid && bme680Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (mcp9808Sensor.hasSensor()) {
|
||||
valid = valid && mcp9808Sensor.getMetrics(&m);
|
||||
valid = valid && mcp9808Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (ina219Sensor.hasSensor()) {
|
||||
valid = valid && ina219Sensor.getMetrics(&m);
|
||||
valid = valid && ina219Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (ina260Sensor.hasSensor()) {
|
||||
valid = valid && ina260Sensor.getMetrics(&m);
|
||||
valid = valid && ina260Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (veml7700Sensor.hasSensor()) {
|
||||
valid = valid && veml7700Sensor.getMetrics(&m);
|
||||
valid = valid && veml7700Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (tsl2591Sensor.hasSensor()) {
|
||||
valid = valid && tsl2591Sensor.getMetrics(&m);
|
||||
valid = valid && tsl2591Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (opt3001Sensor.hasSensor()) {
|
||||
valid = valid && opt3001Sensor.getMetrics(&m);
|
||||
valid = valid && opt3001Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (mlx90632Sensor.hasSensor()) {
|
||||
valid = valid && mlx90632Sensor.getMetrics(&m);
|
||||
valid = valid && mlx90632Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (rcwl9620Sensor.hasSensor()) {
|
||||
valid = valid && rcwl9620Sensor.getMetrics(&m);
|
||||
valid = valid && rcwl9620Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (nau7802Sensor.hasSensor()) {
|
||||
valid = valid && nau7802Sensor.getMetrics(&m);
|
||||
valid = valid && nau7802Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
}
|
||||
if (aht10Sensor.hasSensor()) {
|
||||
if (!bmp280Sensor.hasSensor()) {
|
||||
valid = valid && aht10Sensor.getMetrics(&m);
|
||||
valid = valid && aht10Sensor.getMetrics(m);
|
||||
hasSensor = true;
|
||||
} else {
|
||||
// prefer bmp280 temp if both sensors are present, fetch only humidity
|
||||
meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero;
|
||||
LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n");
|
||||
aht10Sensor.getMetrics(&m_ahtx);
|
||||
m.variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
|
||||
m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
valid = valid && hasSensor;
|
||||
|
||||
if (valid) {
|
||||
return valid && hasSensor;
|
||||
}
|
||||
|
||||
meshtastic_MeshPacket *EnvironmentTelemetryModule::allocReply()
|
||||
{
|
||||
if (currentRequest) {
|
||||
auto req = *currentRequest;
|
||||
const auto &p = req.decoded;
|
||||
meshtastic_Telemetry scratch;
|
||||
meshtastic_Telemetry *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
} else {
|
||||
LOG_ERROR("Error decoding EnvironmentTelemetry module!\n");
|
||||
return NULL;
|
||||
}
|
||||
// Check for a request for environment metrics
|
||||
if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
|
||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||
if (getEnvironmentTelemetry(&m)) {
|
||||
LOG_INFO("Environment telemetry replying to request\n");
|
||||
return allocDataProtobuf(m);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
{
|
||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||
if (getEnvironmentTelemetry(&m)) {
|
||||
LOG_INFO("(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f\n",
|
||||
m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current,
|
||||
m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity,
|
||||
@@ -413,8 +449,9 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
setIntervalFromNow(5000);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return valid;
|
||||
return false;
|
||||
}
|
||||
|
||||
AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
|
||||
|
||||
@@ -32,6 +32,11 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
|
||||
virtual int32_t runOnce() override;
|
||||
/** Called to get current Environment telemetry data
|
||||
@return true if it contains valid data
|
||||
*/
|
||||
bool getEnvironmentTelemetry(meshtastic_Telemetry *m);
|
||||
virtual meshtastic_MeshPacket *allocReply() override;
|
||||
/**
|
||||
* Send our Telemetry into the mesh
|
||||
*/
|
||||
|
||||
@@ -108,7 +108,7 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s
|
||||
display->drawString(x, y, "Power Telemetry");
|
||||
if (lastMeasurementPacket == nullptr) {
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement");
|
||||
display->drawString(x, y += _fontHeight(FONT_MEDIUM), "No measurement");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -120,22 +120,22 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s
|
||||
auto &p = lastMeasurementPacket->decoded;
|
||||
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
|
||||
display->setFont(FONT_SMALL);
|
||||
display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
|
||||
display->drawString(x, y += _fontHeight(FONT_MEDIUM), "Measurement Error");
|
||||
LOG_ERROR("Unable to decode last packet");
|
||||
return;
|
||||
}
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C";
|
||||
display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
|
||||
display->drawString(x, y += _fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
|
||||
if (lastMeasurement.variant.power_metrics.ch1_voltage != 0) {
|
||||
display->drawString(x, y += fontHeight(FONT_SMALL),
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Ch 1 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 0) + "V / " +
|
||||
String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA");
|
||||
display->drawString(x, y += fontHeight(FONT_SMALL),
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Ch 2 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 0) + "V / " +
|
||||
String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA");
|
||||
display->drawString(x, y += fontHeight(FONT_SMALL),
|
||||
display->drawString(x, y += _fontHeight(FONT_SMALL),
|
||||
"Ch 3 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 0) + "V / " +
|
||||
String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA");
|
||||
}
|
||||
@@ -163,29 +163,63 @@ bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &m
|
||||
return false; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m)
|
||||
{
|
||||
bool valid = false;
|
||||
m->time = getTime();
|
||||
m->which_variant = meshtastic_Telemetry_power_metrics_tag;
|
||||
|
||||
m->variant.power_metrics.ch1_voltage = 0;
|
||||
m->variant.power_metrics.ch1_current = 0;
|
||||
m->variant.power_metrics.ch2_voltage = 0;
|
||||
m->variant.power_metrics.ch2_current = 0;
|
||||
m->variant.power_metrics.ch3_voltage = 0;
|
||||
m->variant.power_metrics.ch3_current = 0;
|
||||
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
|
||||
if (ina219Sensor.hasSensor())
|
||||
valid = ina219Sensor.getMetrics(m);
|
||||
if (ina260Sensor.hasSensor())
|
||||
valid = ina260Sensor.getMetrics(m);
|
||||
if (ina3221Sensor.hasSensor())
|
||||
valid = ina3221Sensor.getMetrics(m);
|
||||
#endif
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
meshtastic_MeshPacket *PowerTelemetryModule::allocReply()
|
||||
{
|
||||
if (currentRequest) {
|
||||
auto req = *currentRequest;
|
||||
const auto &p = req.decoded;
|
||||
meshtastic_Telemetry scratch;
|
||||
meshtastic_Telemetry *decoded = NULL;
|
||||
memset(&scratch, 0, sizeof(scratch));
|
||||
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
|
||||
decoded = &scratch;
|
||||
} else {
|
||||
LOG_ERROR("Error decoding PowerTelemetry module!\n");
|
||||
return NULL;
|
||||
}
|
||||
// Check for a request for power metrics
|
||||
if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
|
||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||
if (getPowerTelemetry(&m)) {
|
||||
LOG_INFO("Power telemetry replying to request\n");
|
||||
return allocDataProtobuf(m);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
{
|
||||
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
|
||||
bool valid = false;
|
||||
m.time = getTime();
|
||||
m.which_variant = meshtastic_Telemetry_power_metrics_tag;
|
||||
|
||||
m.variant.power_metrics.ch1_voltage = 0;
|
||||
m.variant.power_metrics.ch1_current = 0;
|
||||
m.variant.power_metrics.ch2_voltage = 0;
|
||||
m.variant.power_metrics.ch2_current = 0;
|
||||
m.variant.power_metrics.ch3_voltage = 0;
|
||||
m.variant.power_metrics.ch3_current = 0;
|
||||
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
|
||||
if (ina219Sensor.hasSensor())
|
||||
valid = ina219Sensor.getMetrics(&m);
|
||||
if (ina260Sensor.hasSensor())
|
||||
valid = ina260Sensor.getMetrics(&m);
|
||||
if (ina3221Sensor.hasSensor())
|
||||
valid = ina3221Sensor.getMetrics(&m);
|
||||
#endif
|
||||
|
||||
if (valid) {
|
||||
if (getPowerTelemetry(&m)) {
|
||||
LOG_INFO("(Sending): ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, "
|
||||
"ch3_voltage=%f, ch3_current=%f\n",
|
||||
m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage,
|
||||
@@ -218,8 +252,9 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
|
||||
setIntervalFromNow(5000);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return valid;
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -33,6 +33,11 @@ class PowerTelemetryModule : private concurrency::OSThread, public ProtobufModul
|
||||
*/
|
||||
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
|
||||
virtual int32_t runOnce() override;
|
||||
/** Called to get current Power telemetry data
|
||||
@return true if it contains valid data
|
||||
*/
|
||||
bool getPowerTelemetry(meshtastic_Telemetry *m);
|
||||
virtual meshtastic_MeshPacket *allocReply() override;
|
||||
/**
|
||||
* Send our Telemetry into the mesh
|
||||
*/
|
||||
|
||||
@@ -16,8 +16,7 @@ int32_t INA3221Sensor::runOnce()
|
||||
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
|
||||
}
|
||||
if (!status) {
|
||||
ina3221.setAddr(INA3221_ADDR42_SDA); // i2c address 0x42
|
||||
ina3221.begin();
|
||||
ina3221.begin(nodeTelemetrySensorsMap[sensorType].second);
|
||||
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
|
||||
status = true;
|
||||
} else {
|
||||
|
||||
@@ -2,6 +2,11 @@
|
||||
#include "NodeDB.h"
|
||||
#include "PowerFSM.h"
|
||||
#include "configuration.h"
|
||||
#if HAS_SCREEN
|
||||
#include "gps/RTC.h"
|
||||
#include "graphics/Screen.h"
|
||||
#include "main.h"
|
||||
#endif
|
||||
|
||||
WaypointModule *waypointModule;
|
||||
|
||||
@@ -11,14 +16,155 @@ 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);
|
||||
notifyObservers(&mp);
|
||||
notifyObservers(&e);
|
||||
|
||||
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
|
||||
}
|
||||
|
||||
#if HAS_SCREEN
|
||||
bool WaypointModule::shouldDraw()
|
||||
{
|
||||
#if !MESHTASTIC_EXCLUDE_WAYPOINT
|
||||
// If no waypoint to show
|
||||
if (!devicestate.has_rx_waypoint)
|
||||
return false;
|
||||
|
||||
// Decode the message, to find the expiration time (is waypoint still valid)
|
||||
// This handles "deletion" as well as expiration
|
||||
meshtastic_Waypoint wp;
|
||||
memset(&wp, 0, sizeof(wp));
|
||||
if (pb_decode_from_bytes(devicestate.rx_waypoint.decoded.payload.bytes, devicestate.rx_waypoint.decoded.payload.size,
|
||||
&meshtastic_Waypoint_msg, &wp)) {
|
||||
// Valid waypoint
|
||||
if (wp.expire > getTime())
|
||||
return devicestate.has_rx_waypoint = true;
|
||||
|
||||
// Expired, or deleted
|
||||
else
|
||||
return devicestate.has_rx_waypoint = false;
|
||||
}
|
||||
|
||||
// If decoding failed
|
||||
LOG_ERROR("Failed to decode waypoint\n");
|
||||
devicestate.has_rx_waypoint = false;
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Draw the last waypoint we received
|
||||
void WaypointModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
// Prepare to draw
|
||||
display->setFont(FONT_SMALL);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
// Handle inverted display
|
||||
// Unsure of expected behavior: for now, copy drawNodeInfo
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED)
|
||||
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
|
||||
|
||||
// Decode the waypoint
|
||||
meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
|
||||
meshtastic_Waypoint wp;
|
||||
memset(&wp, 0, sizeof(wp));
|
||||
if (!pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &wp)) {
|
||||
// This *should* be caught by shouldDrawWaypoint, but we'll short-circuit here just in case
|
||||
display->drawStringMaxWidth(0 + x, 0 + y, x + display->getWidth(), "Couldn't decode waypoint");
|
||||
devicestate.has_rx_waypoint = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get timestamp info. Will pass as a field to drawColumns
|
||||
static char lastStr[20];
|
||||
screen->getTimeAgoStr(sinceReceived(&mp), lastStr, sizeof(lastStr));
|
||||
|
||||
// Will contain distance information, passed as a field to drawColumns
|
||||
static char distStr[20];
|
||||
|
||||
// Get our node, to use our own position
|
||||
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
|
||||
|
||||
// Text fields to draw (left of compass)
|
||||
// Last element must be NULL. This signals the end of the char*[] to drawColumns
|
||||
const char *fields[] = {"Waypoint", lastStr, wp.name, distStr, NULL};
|
||||
|
||||
// Dimensions / co-ordinates for the compass/circle
|
||||
int16_t compassX = 0, compassY = 0;
|
||||
uint16_t compassDiam = graphics::Screen::getCompassDiam(display->getWidth(), display->getHeight());
|
||||
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
||||
compassX = x + display->getWidth() - compassDiam / 2 - 5;
|
||||
compassY = y + display->getHeight() / 2;
|
||||
} else {
|
||||
compassX = x + display->getWidth() - compassDiam / 2 - 5;
|
||||
compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2;
|
||||
}
|
||||
|
||||
// If our node has a position:
|
||||
if (ourNode && (hasValidPosition(ourNode) || screen->hasHeading())) {
|
||||
const meshtastic_PositionLite &op = ourNode->position;
|
||||
float myHeading;
|
||||
if (screen->hasHeading())
|
||||
myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
|
||||
else
|
||||
myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
screen->drawCompassNorth(display, compassX, compassY, myHeading);
|
||||
|
||||
// Distance to Waypoint
|
||||
float d = GeoCoord::latLongToMeter(DegD(wp.latitude_i), DegD(wp.longitude_i), DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
||||
if (d < (2 * MILES_TO_FEET))
|
||||
snprintf(distStr, sizeof(distStr), "%.0f ft", d * METERS_TO_FEET);
|
||||
else
|
||||
snprintf(distStr, sizeof(distStr), "%.1f mi", d * METERS_TO_FEET / MILES_TO_FEET);
|
||||
} else {
|
||||
if (d < 2000)
|
||||
snprintf(distStr, sizeof(distStr), "%.0f m", d);
|
||||
else
|
||||
snprintf(distStr, sizeof(distStr), "%.1f km", d / 1000);
|
||||
}
|
||||
|
||||
// Compass bearing to waypoint
|
||||
float bearingToOther =
|
||||
GeoCoord::bearing(DegD(op.latitude_i), DegD(op.longitude_i), DegD(wp.latitude_i), DegD(wp.longitude_i));
|
||||
// If the top of the compass is a static north then bearingToOther can be drawn on the compass directly
|
||||
// If the top of the compass is not a static north we need adjust bearingToOther based on heading
|
||||
if (!config.display.compass_north_top)
|
||||
bearingToOther -= myHeading;
|
||||
screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther);
|
||||
}
|
||||
|
||||
// If our node doesn't have position
|
||||
else {
|
||||
// ? in the compass
|
||||
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
|
||||
|
||||
// ? in the distance field
|
||||
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
|
||||
strncpy(distStr, "? mi", sizeof(distStr));
|
||||
else
|
||||
strncpy(distStr, "? km", sizeof(distStr));
|
||||
}
|
||||
|
||||
// Undo color-inversion, if set prior to drawing header
|
||||
// Unsure of expected behavior? For now: copy drawNodeInfo
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
|
||||
display->setColor(BLACK);
|
||||
}
|
||||
|
||||
// Draw compass circle
|
||||
display->drawCircle(compassX, compassY, compassDiam / 2);
|
||||
|
||||
// Must be after distStr is populated
|
||||
screen->drawColumns(display, x, y, fields);
|
||||
}
|
||||
#endif
|
||||
@@ -5,20 +5,28 @@
|
||||
/**
|
||||
* Waypoint message handling for meshtastic
|
||||
*/
|
||||
class WaypointModule : public SinglePortModule, public Observable<const meshtastic_MeshPacket *>
|
||||
class WaypointModule : public SinglePortModule, public Observable<const UIFrameEvent *>
|
||||
{
|
||||
public:
|
||||
/** Constructor
|
||||
* name is for debugging output
|
||||
*/
|
||||
WaypointModule() : SinglePortModule("waypoint", meshtastic_PortNum_WAYPOINT_APP) {}
|
||||
|
||||
#if HAS_SCREEN
|
||||
bool shouldDraw();
|
||||
#endif
|
||||
protected:
|
||||
/** Called to handle a particular incoming message
|
||||
|
||||
@return ProcessMessage::STOP if you've guaranteed you've handled this message and no other handlers should be considered for
|
||||
it
|
||||
*/
|
||||
|
||||
virtual Observable<const UIFrameEvent *> *getUIFrameObservable() override { return this; }
|
||||
#if HAS_SCREEN
|
||||
virtual bool wantUIFrame() override { return this->shouldDraw(); }
|
||||
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
|
||||
#endif
|
||||
virtual ProcessMessage handleReceived(const meshtastic_MeshPacket &mp) override;
|
||||
};
|
||||
|
||||
|
||||
@@ -66,10 +66,6 @@ bool PaxcounterModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, m
|
||||
|
||||
meshtastic_MeshPacket *PaxcounterModule::allocReply()
|
||||
{
|
||||
if (ignoreRequest) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
meshtastic_Paxcount pl = meshtastic_Paxcount_init_default;
|
||||
pl.wifi = count_from_libpax.wifi_count;
|
||||
pl.ble = count_from_libpax.ble_count;
|
||||
|
||||
@@ -319,8 +319,8 @@ ProcessMessage StoreForwardModule::handleReceived(const meshtastic_MeshPacket &m
|
||||
#ifdef ARCH_ESP32
|
||||
if (moduleConfig.store_forward.enabled) {
|
||||
|
||||
// The router node should not be sending messages as a client. Unless he is a ROUTER_CLIENT
|
||||
if ((getFrom(&mp) != nodeDB->getNodeNum()) || (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT)) {
|
||||
// The router node should not be sending messages as a client
|
||||
if ((getFrom(&mp) != nodeDB->getNodeNum())) {
|
||||
|
||||
if ((mp.decoded.portnum == meshtastic_PortNum_TEXT_MESSAGE_APP) && is_server) {
|
||||
auto &p = mp.decoded;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#endif
|
||||
#include "mesh/generated/meshtastic/remote_hardware.pb.h"
|
||||
#include "sleep.h"
|
||||
#if HAS_WIFI && !MESHTASTIC_EXCLUDE_WIFI
|
||||
#if HAS_WIFI
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#include <WiFi.h>
|
||||
#endif
|
||||
@@ -175,7 +175,7 @@ void mqttInit()
|
||||
new MQTT();
|
||||
}
|
||||
|
||||
#ifdef HAS_NETWORKING
|
||||
#if HAS_NETWORKING
|
||||
MQTT::MQTT() : concurrency::OSThread("mqtt"), pubSub(mqttClient), mqttQueue(MAX_MQTT_QUEUE)
|
||||
#else
|
||||
MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
|
||||
@@ -206,7 +206,7 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
|
||||
moduleConfig.mqtt.map_report_settings.publish_interval_secs, default_map_publish_interval_secs);
|
||||
}
|
||||
|
||||
#ifdef HAS_NETWORKING
|
||||
#if HAS_NETWORKING
|
||||
if (!moduleConfig.mqtt.proxy_to_client_enabled)
|
||||
pubSub.setCallback(mqttCallback);
|
||||
#endif
|
||||
@@ -226,7 +226,7 @@ MQTT::MQTT() : concurrency::OSThread("mqtt"), mqttQueue(MAX_MQTT_QUEUE)
|
||||
|
||||
bool MQTT::isConnectedDirectly()
|
||||
{
|
||||
#ifdef HAS_NETWORKING
|
||||
#if HAS_NETWORKING
|
||||
return pubSub.connected();
|
||||
#else
|
||||
return false;
|
||||
@@ -244,7 +244,7 @@ bool MQTT::publish(const char *topic, const char *payload, bool retained)
|
||||
service.sendMqttMessageToClientProxy(msg);
|
||||
return true;
|
||||
}
|
||||
#ifdef HAS_NETWORKING
|
||||
#if HAS_NETWORKING
|
||||
else if (isConnectedDirectly()) {
|
||||
return pubSub.publish(topic, payload, retained);
|
||||
}
|
||||
@@ -264,7 +264,7 @@ bool MQTT::publish(const char *topic, const uint8_t *payload, size_t length, boo
|
||||
service.sendMqttMessageToClientProxy(msg);
|
||||
return true;
|
||||
}
|
||||
#ifdef HAS_NETWORKING
|
||||
#if HAS_NETWORKING
|
||||
else if (isConnectedDirectly()) {
|
||||
return pubSub.publish(topic, payload, length, retained);
|
||||
}
|
||||
@@ -284,7 +284,7 @@ void MQTT::reconnect()
|
||||
publishStatus();
|
||||
return; // Don't try to connect directly to the server
|
||||
}
|
||||
#ifdef HAS_NETWORKING
|
||||
#if HAS_NETWORKING
|
||||
// Defaults
|
||||
int serverPort = 1883;
|
||||
const char *serverAddr = default_mqtt_address;
|
||||
@@ -357,7 +357,7 @@ void MQTT::reconnect()
|
||||
|
||||
void MQTT::sendSubscriptions()
|
||||
{
|
||||
#ifdef HAS_NETWORKING
|
||||
#if HAS_NETWORKING
|
||||
size_t numChan = channels.getNumChannels();
|
||||
for (size_t i = 0; i < numChan; i++) {
|
||||
const auto &ch = channels.getByIndex(i);
|
||||
@@ -396,7 +396,7 @@ bool MQTT::wantsLink() const
|
||||
|
||||
int32_t MQTT::runOnce()
|
||||
{
|
||||
#ifdef HAS_NETWORKING
|
||||
#if HAS_NETWORKING
|
||||
if (!moduleConfig.mqtt.enabled || !(moduleConfig.mqtt.map_reporting_enabled || channels.anyMqttEnabled()))
|
||||
return disable();
|
||||
|
||||
@@ -482,7 +482,12 @@ void MQTT::onSend(const meshtastic_MeshPacket &mp, const meshtastic_MeshPacket &
|
||||
|
||||
auto &ch = channels.getByIndex(chIndex);
|
||||
|
||||
if (&mp_decoded.decoded && strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 &&
|
||||
if (mp_decoded.which_payload_variant != meshtastic_MeshPacket_decoded_tag) {
|
||||
LOG_CRIT("MQTT::onSend(): mp_decoded isn't actually decoded\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(moduleConfig.mqtt.address, default_mqtt_address) == 0 &&
|
||||
(mp_decoded.decoded.portnum == meshtastic_PortNum_RANGE_TEST_APP ||
|
||||
mp_decoded.decoded.portnum == meshtastic_PortNum_DETECTION_SENSOR_APP)) {
|
||||
LOG_DEBUG("MQTT onSend - Ignoring range test or detection sensor message on public mqtt\n");
|
||||
|
||||
@@ -8,17 +8,15 @@
|
||||
#include "mqtt/JSON.h"
|
||||
#if HAS_WIFI
|
||||
#include <WiFiClient.h>
|
||||
#define HAS_NETWORKING 1
|
||||
#if !defined(ARCH_PORTDUINO)
|
||||
#include <WiFiClientSecure.h>
|
||||
#endif
|
||||
#endif
|
||||
#if HAS_ETHERNET
|
||||
#include <EthernetClient.h>
|
||||
#define HAS_NETWORKING 1
|
||||
#endif
|
||||
|
||||
#ifdef HAS_NETWORKING
|
||||
#if HAS_NETWORKING
|
||||
#include <PubSubClient.h>
|
||||
#endif
|
||||
|
||||
@@ -43,7 +41,7 @@ class MQTT : private concurrency::OSThread
|
||||
#endif
|
||||
|
||||
public:
|
||||
#ifdef HAS_NETWORKING
|
||||
#if HAS_NETWORKING
|
||||
PubSubClient pubSub;
|
||||
#endif
|
||||
MQTT();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
NimBLECharacteristic *fromNumCharacteristic;
|
||||
NimBLECharacteristic *BatteryCharacteristic;
|
||||
NimBLECharacteristic *logRadioCharacteristic;
|
||||
NimBLEServer *bleServer;
|
||||
|
||||
static bool passkeyShowing;
|
||||
@@ -58,7 +59,6 @@ class NimbleBluetoothFromRadioCallback : public NimBLECharacteristicCallbacks
|
||||
{
|
||||
virtual void onRead(NimBLECharacteristic *pCharacteristic)
|
||||
{
|
||||
LOG_INFO("From Radio onread\n");
|
||||
uint8_t fromRadioBytes[meshtastic_FromRadio_size];
|
||||
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
|
||||
|
||||
@@ -82,7 +82,33 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
LOG_INFO("*** Enter passkey %d on the peer side ***\n", passkey);
|
||||
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
screen->startBluetoothPinScreen(passkey);
|
||||
#if HAS_SCREEN
|
||||
screen->startAlert([passkey](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
char btPIN[16] = "888888";
|
||||
snprintf(btPIN, sizeof(btPIN), "%06u", passkey);
|
||||
int x_offset = display->width() / 2;
|
||||
int y_offset = display->height() <= 80 ? 0 : 32;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, "Enter this code");
|
||||
|
||||
display->setFont(FONT_LARGE);
|
||||
String displayPin(btPIN);
|
||||
String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, pin);
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
String deviceName = "Name: ";
|
||||
deviceName.concat(getDeviceName());
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, deviceName);
|
||||
});
|
||||
#endif
|
||||
passkeyShowing = true;
|
||||
|
||||
return passkey;
|
||||
@@ -94,7 +120,7 @@ class NimbleBluetoothServerCallback : public NimBLEServerCallbacks
|
||||
|
||||
if (passkeyShowing) {
|
||||
passkeyShowing = false;
|
||||
screen->stopBluetoothPinScreen();
|
||||
screen->endAlert();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,6 +206,8 @@ void NimbleBluetooth::setupService()
|
||||
ToRadioCharacteristic = bleService->createCharacteristic(TORADIO_UUID, NIMBLE_PROPERTY::WRITE);
|
||||
FromRadioCharacteristic = bleService->createCharacteristic(FROMRADIO_UUID, NIMBLE_PROPERTY::READ);
|
||||
fromNumCharacteristic = bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ);
|
||||
logRadioCharacteristic =
|
||||
bleService->createCharacteristic(LOGRADIO_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ, 512U);
|
||||
} else {
|
||||
ToRadioCharacteristic = bleService->createCharacteristic(
|
||||
TORADIO_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_AUTHEN | NIMBLE_PROPERTY::WRITE_ENC);
|
||||
@@ -188,6 +216,9 @@ void NimbleBluetooth::setupService()
|
||||
fromNumCharacteristic =
|
||||
bleService->createCharacteristic(FROMNUM_UUID, NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ |
|
||||
NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC);
|
||||
logRadioCharacteristic = bleService->createCharacteristic(
|
||||
LOGRADIO_UUID,
|
||||
NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC, 512U);
|
||||
}
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
|
||||
@@ -236,6 +267,14 @@ void NimbleBluetooth::clearBonds()
|
||||
NimBLEDevice::deleteAllBonds();
|
||||
}
|
||||
|
||||
void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
{
|
||||
if (!bleServer || !isConnected() || length > 512) {
|
||||
return;
|
||||
}
|
||||
logRadioCharacteristic->notify(logMessage, length, true);
|
||||
}
|
||||
|
||||
void clearNVS()
|
||||
{
|
||||
NimBLEDevice::deleteAllBonds();
|
||||
|
||||
@@ -11,6 +11,7 @@ class NimbleBluetooth : BluetoothApi
|
||||
bool isActive();
|
||||
bool isConnected();
|
||||
int getRssi();
|
||||
void sendLog(const uint8_t *logMessage, size_t length);
|
||||
|
||||
private:
|
||||
void setupService();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
#include "nimble/NimbleBluetooth.h"
|
||||
#endif
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_WIFI
|
||||
#if HAS_WIFI
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#endif
|
||||
|
||||
@@ -24,10 +24,9 @@
|
||||
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
void setBluetoothEnable(bool enable)
|
||||
{
|
||||
#ifndef MESHTASTIC_EXCLUDE_WIFI
|
||||
#if HAS_WIFI
|
||||
if (!isWifiAvailable() && config.bluetooth.enabled == true)
|
||||
#endif
|
||||
#ifdef MESHTASTIC_EXCLUDE_WIFI
|
||||
#else
|
||||
if (config.bluetooth.enabled == true)
|
||||
#endif
|
||||
{
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
#include "mesh/mesh-pb-constants.h"
|
||||
#include <bluefruit.h>
|
||||
#include <utility/bonding.h>
|
||||
|
||||
static BLEService meshBleService = BLEService(BLEUuid(MESH_SERVICE_UUID_16));
|
||||
static BLECharacteristic fromNum = BLECharacteristic(BLEUuid(FROMNUM_UUID_16));
|
||||
static BLECharacteristic fromRadio = BLECharacteristic(BLEUuid(FROMRADIO_UUID_16));
|
||||
static BLECharacteristic toRadio = BLECharacteristic(BLEUuid(TORADIO_UUID_16));
|
||||
static BLECharacteristic logRadio = BLECharacteristic(BLEUuid(LOGRADIO_UUID_16));
|
||||
|
||||
static BLEDis bledis; // DIS (Device Information Service) helper class instance
|
||||
static BLEBas blebas; // BAS (Battery Service) helper class instance
|
||||
@@ -52,16 +52,14 @@ static BluetoothPhoneAPI *bluetoothPhoneAPI;
|
||||
|
||||
void onConnect(uint16_t conn_handle)
|
||||
{
|
||||
|
||||
// Get the reference to current connection
|
||||
BLEConnection *connection = Bluefruit.Connection(conn_handle);
|
||||
connectionHandle = conn_handle;
|
||||
|
||||
char central_name[32] = {0};
|
||||
connection->getPeerName(central_name, sizeof(central_name));
|
||||
|
||||
LOG_INFO("BLE Connected to %s\n", central_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback invoked when a connection is dropped
|
||||
* @param conn_handle connection where this event happens
|
||||
@@ -72,37 +70,36 @@ void onDisconnect(uint16_t conn_handle, uint8_t reason)
|
||||
// FIXME - we currently assume only one active connection
|
||||
LOG_INFO("BLE Disconnected, reason = 0x%x\n", reason);
|
||||
}
|
||||
|
||||
void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
|
||||
{
|
||||
// Display the raw request packet
|
||||
LOG_INFO("CCCD Updated: %u\n", cccd_value);
|
||||
|
||||
// Check the characteristic this CCCD update is associated with in case
|
||||
// this handler is used for multiple CCCD records.
|
||||
if (chr->uuid == fromNum.uuid) {
|
||||
if (chr->notifyEnabled(conn_hdl)) {
|
||||
LOG_INFO("fromNum 'Notify' enabled\n");
|
||||
|
||||
// According to the GATT spec: cccd value = 0x0001 means notifications are enabled
|
||||
// and cccd value = 0x0002 means indications are enabled
|
||||
|
||||
if (chr->uuid == fromNum.uuid || chr->uuid == logRadio.uuid) {
|
||||
auto result = cccd_value == 2 ? chr->indicateEnabled(conn_hdl) : chr->notifyEnabled(conn_hdl);
|
||||
if (result) {
|
||||
LOG_INFO("Notify/Indicate enabled\n");
|
||||
} else {
|
||||
LOG_INFO("fromNum 'Notify' disabled\n");
|
||||
LOG_INFO("Notify/Indicate disabled\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void startAdv(void)
|
||||
{
|
||||
// Advertising packet
|
||||
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
|
||||
|
||||
// IncludeService UUID
|
||||
// Bluefruit.ScanResponse.addService(meshBleService);
|
||||
Bluefruit.ScanResponse.addTxPower();
|
||||
Bluefruit.ScanResponse.addName();
|
||||
|
||||
// Include Name
|
||||
// Bluefruit.Advertising.addName();
|
||||
Bluefruit.Advertising.addService(meshBleService);
|
||||
|
||||
/* Start Advertising
|
||||
* - Enable auto advertising if disconnected
|
||||
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
|
||||
@@ -117,7 +114,6 @@ void startAdv(void)
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0); // 0 = Don't stop advertising after n seconds. FIXME, we should stop advertising after X
|
||||
}
|
||||
|
||||
// Just ack that the caller is allowed to read
|
||||
static void authorizeRead(uint16_t conn_hdl)
|
||||
{
|
||||
@@ -125,7 +121,6 @@ static void authorizeRead(uint16_t conn_hdl)
|
||||
reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
|
||||
sd_ble_gatts_rw_authorize_reply(conn_hdl, &reply);
|
||||
}
|
||||
|
||||
/**
|
||||
* client is starting read, pull the bytes from our API class
|
||||
*/
|
||||
@@ -134,7 +129,6 @@ void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e
|
||||
if (request->offset == 0) {
|
||||
// If the read is long, we will get multiple authorize invocations - we only populate data on the first
|
||||
size_t numBytes = bluetoothPhoneAPI->getFromRadio(fromRadioBytes);
|
||||
|
||||
// Someone is going to read our value as soon as this callback returns. So fill it with the next message in the queue
|
||||
// or make empty if the queue is empty
|
||||
fromRadio.write(fromRadioBytes, numBytes);
|
||||
@@ -143,37 +137,22 @@ void onFromRadioAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_e
|
||||
}
|
||||
authorizeRead(conn_hdl);
|
||||
}
|
||||
|
||||
void onToRadioWrite(uint16_t conn_hdl, BLECharacteristic *chr, uint8_t *data, uint16_t len)
|
||||
{
|
||||
LOG_INFO("toRadioWriteCb data %p, len %u\n", data, len);
|
||||
|
||||
bluetoothPhoneAPI->handleToRadio(data, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* client is starting read, pull the bytes from our API class
|
||||
*/
|
||||
void onFromNumAuthorize(uint16_t conn_hdl, BLECharacteristic *chr, ble_gatts_evt_read_t *request)
|
||||
{
|
||||
LOG_INFO("fromNumAuthorizeCb\n");
|
||||
|
||||
authorizeRead(conn_hdl);
|
||||
}
|
||||
|
||||
void setupMeshService(void)
|
||||
{
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
|
||||
meshBleService.begin();
|
||||
|
||||
// Note: You must call .begin() on the BLEService before calling .begin() on
|
||||
// any characteristic(s) within that service definition.. Calling .begin() on
|
||||
// a BLECharacteristic will cause it to be added to the last BLEService that
|
||||
// was 'begin()'ed!
|
||||
auto secMode =
|
||||
config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN ? SECMODE_OPEN : SECMODE_ENC_NO_MITM;
|
||||
|
||||
fromNum.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
fromNum.setPermission(secMode, SECMODE_NO_ACCESS); // FIXME, secure this!!!
|
||||
fromNum.setFixedLen(
|
||||
@@ -203,10 +182,15 @@ void setupMeshService(void)
|
||||
// We don't call this callback via the adafruit queue, because we can safely run in the BLE context
|
||||
toRadio.setWriteCallback(onToRadioWrite, false);
|
||||
toRadio.begin();
|
||||
|
||||
logRadio.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
logRadio.setPermission(secMode, SECMODE_NO_ACCESS);
|
||||
logRadio.setMaxLen(512);
|
||||
logRadio.setCccdWriteCallback(onCccd);
|
||||
logRadio.write32(0);
|
||||
logRadio.begin();
|
||||
}
|
||||
|
||||
static uint32_t configuredPasskey;
|
||||
|
||||
void NRF52Bluetooth::shutdown()
|
||||
{
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
@@ -216,29 +200,23 @@ void NRF52Bluetooth::shutdown()
|
||||
}
|
||||
Bluefruit.Advertising.stop();
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::startDisabled()
|
||||
{
|
||||
// Setup Bluetooth
|
||||
nrf52Bluetooth->setup();
|
||||
|
||||
// Shutdown bluetooth for minimum power draw
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.setTxPower(-40); // Minimum power
|
||||
|
||||
LOG_INFO("Disabling NRF52 Bluetooth. (Workaround: tx power min, advertising stopped)\n");
|
||||
}
|
||||
|
||||
bool NRF52Bluetooth::isConnected()
|
||||
{
|
||||
return Bluefruit.connected(connectionHandle);
|
||||
}
|
||||
|
||||
int NRF52Bluetooth::getRssi()
|
||||
{
|
||||
return 0; // FIXME figure out where to source this
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::setup()
|
||||
{
|
||||
// Initialise the Bluefruit module
|
||||
@@ -246,12 +224,10 @@ void NRF52Bluetooth::setup()
|
||||
Bluefruit.autoConnLed(false);
|
||||
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
|
||||
Bluefruit.begin();
|
||||
|
||||
// Clear existing data.
|
||||
Bluefruit.Advertising.stop();
|
||||
Bluefruit.Advertising.clearData();
|
||||
Bluefruit.ScanResponse.clearData();
|
||||
|
||||
if (config.bluetooth.mode != meshtastic_Config_BluetoothConfig_PairingMode_NO_PIN) {
|
||||
configuredPasskey = config.bluetooth.mode == meshtastic_Config_BluetoothConfig_PairingMode_FIXED_PIN
|
||||
? config.bluetooth.fixed_pin
|
||||
@@ -270,7 +246,6 @@ void NRF52Bluetooth::setup()
|
||||
}
|
||||
// Set the advertised device name (keep it short!)
|
||||
Bluefruit.setName(getDeviceName());
|
||||
|
||||
// Set the connect/disconnect callback handlers
|
||||
Bluefruit.Periph.setConnectCallback(onConnect);
|
||||
Bluefruit.Periph.setDisconnectCallback(onDisconnect);
|
||||
@@ -287,24 +262,19 @@ void NRF52Bluetooth::setup()
|
||||
bledis.setModel(optstr(HW_VERSION));
|
||||
bledis.setFirmwareRev(optstr(APP_VERSION));
|
||||
bledis.begin();
|
||||
|
||||
// Start the BLE Battery Service and set it to 100%
|
||||
LOG_INFO("Configuring the Battery Service\n");
|
||||
blebas.begin();
|
||||
blebas.write(0); // Unknown battery level for now
|
||||
|
||||
// Setup the Heart Rate Monitor service using
|
||||
// BLEService and BLECharacteristic classes
|
||||
LOG_INFO("Configuring the Mesh bluetooth service\n");
|
||||
setupMeshService();
|
||||
|
||||
// Setup the advertising packet(s)
|
||||
LOG_INFO("Setting up the advertising payload(s)\n");
|
||||
startAdv();
|
||||
|
||||
LOG_INFO("Advertising\n");
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::resumeAdvertising()
|
||||
{
|
||||
Bluefruit.Advertising.restartOnDisconnect(true);
|
||||
@@ -312,34 +282,52 @@ void NRF52Bluetooth::resumeAdvertising()
|
||||
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
|
||||
Bluefruit.Advertising.start(0);
|
||||
}
|
||||
|
||||
/// Given a level between 0-100, update the BLE attribute
|
||||
void updateBatteryLevel(uint8_t level)
|
||||
{
|
||||
blebas.write(level);
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::clearBonds()
|
||||
{
|
||||
LOG_INFO("Clearing bluetooth bonds!\n");
|
||||
bond_print_list(BLE_GAP_ROLE_PERIPH);
|
||||
bond_print_list(BLE_GAP_ROLE_CENTRAL);
|
||||
|
||||
Bluefruit.Periph.clearBonds();
|
||||
Bluefruit.Central.clearBonds();
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::onConnectionSecured(uint16_t conn_handle)
|
||||
{
|
||||
LOG_INFO("BLE connection secured\n");
|
||||
}
|
||||
|
||||
bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request)
|
||||
{
|
||||
LOG_INFO("BLE pairing process started with passkey %.3s %.3s\n", passkey, passkey + 3);
|
||||
powerFSM.trigger(EVENT_BLUETOOTH_PAIR);
|
||||
screen->startBluetoothPinScreen(configuredPasskey);
|
||||
screen->startAlert([](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
|
||||
char btPIN[16] = "888888";
|
||||
snprintf(btPIN, sizeof(btPIN), "%06u", configuredPasskey);
|
||||
int x_offset = display->width() / 2;
|
||||
int y_offset = display->height() <= 80 ? 0 : 32;
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, "Enter this code");
|
||||
|
||||
display->setFont(FONT_LARGE);
|
||||
String displayPin(btPIN);
|
||||
String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, pin);
|
||||
|
||||
display->setFont(FONT_SMALL);
|
||||
String deviceName = "Name: ";
|
||||
deviceName.concat(getDeviceName());
|
||||
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
|
||||
display->drawString(x_offset + x, y_offset + y, deviceName);
|
||||
});
|
||||
if (match_request) {
|
||||
uint32_t start_time = millis();
|
||||
while (millis() < start_time + 30000) {
|
||||
@@ -350,13 +338,21 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke
|
||||
LOG_INFO("BLE passkey pairing: match_request=%i\n", match_request);
|
||||
return true;
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_status)
|
||||
{
|
||||
if (auth_status == BLE_GAP_SEC_STATUS_SUCCESS)
|
||||
LOG_INFO("BLE pairing success\n");
|
||||
else
|
||||
LOG_INFO("BLE pairing failed\n");
|
||||
|
||||
screen->stopBluetoothPinScreen();
|
||||
screen->endAlert();
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
{
|
||||
if (!isConnected() || length > 512)
|
||||
return;
|
||||
if (logRadio.indicateEnabled())
|
||||
logRadio.indicate(logMessage, (uint16_t)length);
|
||||
else
|
||||
logRadio.notify(logMessage, (uint16_t)length);
|
||||
}
|
||||
@@ -13,10 +13,10 @@ class NRF52Bluetooth : BluetoothApi
|
||||
void clearBonds();
|
||||
bool isConnected();
|
||||
int getRssi();
|
||||
void sendLog(const uint8_t *logMessage, size_t length);
|
||||
|
||||
private:
|
||||
static void onConnectionSecured(uint16_t conn_handle);
|
||||
void convertToUint8(uint8_t target[4], uint32_t source);
|
||||
static bool onPairingPasskey(uint16_t conn_handle, uint8_t const passkey[6], bool match_request);
|
||||
static void onPairingCompleted(uint16_t conn_handle, uint8_t auth_status);
|
||||
};
|
||||
@@ -63,7 +63,8 @@ static void initBrownout()
|
||||
// We don't bother with setting up brownout if soft device is disabled - because during production we always use softdevice
|
||||
}
|
||||
|
||||
static const bool useSoftDevice = true; // Set to false for easier debugging
|
||||
// This is a public global so that the debugger can set it to false automatically from our gdbinit
|
||||
bool useSoftDevice = true; // Set to false for easier debugging
|
||||
|
||||
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
|
||||
void setBluetoothEnable(bool enable)
|
||||
@@ -149,13 +150,43 @@ void nrf52Loop()
|
||||
checkSDEvents();
|
||||
}
|
||||
|
||||
#ifdef USE_SEMIHOSTING
|
||||
#include <SemihostingStream.h>
|
||||
|
||||
/**
|
||||
* Note: this variable is in BSS and therfore false by default. But the gdbinit
|
||||
* file will be installing a temporary breakpoint that changes wantSemihost to true.
|
||||
*/
|
||||
bool wantSemihost;
|
||||
|
||||
/**
|
||||
* Turn on semihosting if the ICE debugger wants it.
|
||||
*/
|
||||
void nrf52InitSemiHosting()
|
||||
{
|
||||
if (wantSemihost) {
|
||||
static SemihostingStream semiStream;
|
||||
// We must dynamically alloc because the constructor does semihost operations which
|
||||
// would crash any load not talking to a debugger
|
||||
semiStream.open();
|
||||
semiStream.println("Semihosting starts!");
|
||||
// Redirect our serial output to instead go via the ICE port
|
||||
console->setDestination(&semiStream);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void nrf52Setup()
|
||||
{
|
||||
auto why = NRF_POWER->RESETREAS;
|
||||
uint32_t why = NRF_POWER->RESETREAS;
|
||||
// per
|
||||
// https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fpower.html
|
||||
LOG_DEBUG("Reset reason: 0x%x\n", why);
|
||||
|
||||
#ifdef USE_SEMIHOSTING
|
||||
nrf52InitSemiHosting();
|
||||
#endif
|
||||
|
||||
// Per
|
||||
// https://devzone.nordicsemi.com/nordic/nordic-blog/b/blog/posts/monitor-mode-debugging-with-j-link-and-gdbeclipse
|
||||
// This is the recommended setting for Monitor Mode Debugging
|
||||
|
||||
@@ -38,7 +38,7 @@ void powerCommandsCheck()
|
||||
|
||||
#if defined(ARCH_ESP32) || defined(ARCH_NRF52)
|
||||
if (shutdownAtMsec) {
|
||||
screen->startShutdownScreen();
|
||||
screen->startAlert("Shutting down...");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "MeshRadio.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerMon.h"
|
||||
#include "detect/LoRaRadioType.h"
|
||||
#include "error.h"
|
||||
#include "main.h"
|
||||
@@ -17,7 +18,7 @@
|
||||
#ifdef ARCH_ESP32
|
||||
#include "esp32/pm.h"
|
||||
#include "esp_pm.h"
|
||||
#if !MESHTASTIC_EXCLUDE_WIFI
|
||||
#if HAS_WIFI
|
||||
#include "mesh/wifi/WiFiAPClient.h"
|
||||
#endif
|
||||
#include "rom/rtc.h"
|
||||
@@ -56,20 +57,20 @@ RTC_DATA_ATTR int bootCount = 0;
|
||||
*/
|
||||
void setCPUFast(bool on)
|
||||
{
|
||||
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_WIFI
|
||||
#if defined(ARCH_ESP32) && HAS_WIFI
|
||||
|
||||
if (isWifiAvailable()) {
|
||||
/*
|
||||
*
|
||||
* There's a newly introduced bug in the espressif framework where WiFi is
|
||||
* unstable when the frequency is less than 240mhz.
|
||||
* unstable when the frequency is less than 240MHz.
|
||||
*
|
||||
* This mostly impacts WiFi AP mode but we'll bump the frequency for
|
||||
* all WiFi use cases.
|
||||
* (Added: Dec 23, 2021 by Jm Casler)
|
||||
*/
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32C3
|
||||
LOG_DEBUG("Setting CPU to 240mhz because WiFi is in use.\n");
|
||||
LOG_DEBUG("Setting CPU to 240MHz because WiFi is in use.\n");
|
||||
setCpuFrequencyMhz(240);
|
||||
#endif
|
||||
return;
|
||||
@@ -85,6 +86,11 @@ void setCPUFast(bool on)
|
||||
|
||||
void setLed(bool ledOn)
|
||||
{
|
||||
if (ledOn)
|
||||
powerMon->setState(meshtastic_PowerMon_State_LED_On);
|
||||
else
|
||||
powerMon->clearState(meshtastic_PowerMon_State_LED_On);
|
||||
|
||||
#ifdef LED_PIN
|
||||
// toggle the led so we can get some rough sense of how often loop is pausing
|
||||
digitalWrite(LED_PIN, ledOn ^ LED_INVERTED);
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define WIFI_Kit_32 true
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#define DISPLAY_WIDTH 128
|
||||
|
||||
static const uint8_t LED_BUILTIN = 35;
|
||||
static const uint8_t LED_BUILTIN = 18;
|
||||
#define BUILTIN_LED LED_BUILTIN // backward compatibility
|
||||
#define LED_BUILTIN LED_BUILTIN
|
||||
|
||||
@@ -65,6 +61,6 @@ static const uint8_t LED = 18;
|
||||
|
||||
static const uint8_t RST_LoRa = 12;
|
||||
static const uint8_t BUSY_LoRa = 13;
|
||||
static const uint8_t DIO0 = 14;
|
||||
static const uint8_t DIO1 = 14;
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
#define LED_PIN 18
|
||||
#define BUTTON_PIN 0
|
||||
|
||||
// Enable bus for external periherals
|
||||
// I2C
|
||||
#define I2C_SDA SDA
|
||||
#define I2C_SCL SCL
|
||||
|
||||
// Display (E-Ink)
|
||||
#define USE_EINK
|
||||
|
||||
/*
|
||||
* eink display pins
|
||||
*/
|
||||
#define PIN_EINK_CS 4
|
||||
#define PIN_EINK_BUSY 7
|
||||
#define PIN_EINK_DC 5
|
||||
@@ -16,32 +14,28 @@
|
||||
#define PIN_EINK_SCLK 3
|
||||
#define PIN_EINK_MOSI 2
|
||||
|
||||
/*
|
||||
* SPI interfaces
|
||||
*/
|
||||
// SPI
|
||||
#define SPI_INTERFACES_COUNT 2
|
||||
#define PIN_SPI_MISO 10 // MISO
|
||||
#define PIN_SPI_MOSI 11 // MOSI
|
||||
#define PIN_SPI_SCK 9 // SCK
|
||||
|
||||
#define PIN_SPI_MISO 10 // MISO P0.17
|
||||
#define PIN_SPI_MOSI 11 // MOSI P0.15
|
||||
#define PIN_SPI_SCK 9 // SCK P0.13
|
||||
|
||||
#define VEXT_ENABLE 45 // active low, powers the oled display and the lora antenna boost
|
||||
#define BUTTON_PIN 0
|
||||
|
||||
// Power
|
||||
#define VEXT_ENABLE 45 // Active low, powers the E-Ink display
|
||||
#define ADC_CTRL 19
|
||||
#define BATTERY_PIN 20
|
||||
#define ADC_CHANNEL ADC2_GPIO20_CHANNEL
|
||||
#define ADC_MULTIPLIER 2 // Voltage divider is roughly 1:1
|
||||
#define BAT_MEASURE_ADC_UNIT 2 // Use ADC2
|
||||
#define ADC_ATTENUATION ADC_ATTEN_DB_11 // Voltage divider output is quite high
|
||||
#define ADC_ATTENUATION ADC_ATTEN_DB_12 // Voltage divider output is quite high
|
||||
|
||||
// LoRa
|
||||
#define USE_SX1262
|
||||
|
||||
#define LORA_DIO0 -1 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 12
|
||||
#define LORA_DIO1 14 // SX1262 IRQ
|
||||
#define LORA_DIO2 13 // SX1262 BUSY
|
||||
#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled
|
||||
|
||||
#define LORA_SCK 9
|
||||
#define LORA_MISO 11
|
||||
|
||||
@@ -3,11 +3,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define WIFI_Kit_32 true
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#define DISPLAY_WIDTH 128
|
||||
|
||||
static const uint8_t LED_BUILTIN = 35;
|
||||
static const uint8_t LED_BUILTIN = 18;
|
||||
#define BUILTIN_LED LED_BUILTIN // backward compatibility
|
||||
#define LED_BUILTIN LED_BUILTIN
|
||||
|
||||
@@ -65,6 +61,6 @@ static const uint8_t LED = 18;
|
||||
|
||||
static const uint8_t RST_LoRa = 12;
|
||||
static const uint8_t BUSY_LoRa = 13;
|
||||
static const uint8_t DIO0 = 14;
|
||||
static const uint8_t DIO1 = 14;
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
#define LED_PIN 18
|
||||
#define BUTTON_PIN 0
|
||||
|
||||
// Enable bus for external periherals
|
||||
// I2C
|
||||
#define I2C_SDA SDA
|
||||
#define I2C_SCL SCL
|
||||
|
||||
// Display (E-Ink)
|
||||
#define USE_EINK
|
||||
|
||||
/*
|
||||
* eink display pins
|
||||
*/
|
||||
#define PIN_EINK_CS 4
|
||||
#define PIN_EINK_BUSY 7
|
||||
#define PIN_EINK_DC 5
|
||||
@@ -16,32 +14,28 @@
|
||||
#define PIN_EINK_SCLK 3
|
||||
#define PIN_EINK_MOSI 2
|
||||
|
||||
/*
|
||||
* SPI interfaces
|
||||
*/
|
||||
// SPI
|
||||
#define SPI_INTERFACES_COUNT 2
|
||||
#define PIN_SPI_MISO 10 // MISO
|
||||
#define PIN_SPI_MOSI 11 // MOSI
|
||||
#define PIN_SPI_SCK 9 // SCK
|
||||
|
||||
#define PIN_SPI_MISO 10 // MISO P0.17
|
||||
#define PIN_SPI_MOSI 11 // MOSI P0.15
|
||||
#define PIN_SPI_SCK 9 // SCK P0.13
|
||||
|
||||
#define VEXT_ENABLE 45 // active low, powers the oled display and the lora antenna boost
|
||||
#define BUTTON_PIN 0
|
||||
|
||||
// Power
|
||||
#define VEXT_ENABLE 45 // Active low, powers the E-Ink display
|
||||
#define ADC_CTRL 19
|
||||
#define BATTERY_PIN 20
|
||||
#define ADC_CHANNEL ADC2_GPIO20_CHANNEL
|
||||
#define ADC_MULTIPLIER 2 // Voltage divider is roughly 1:1
|
||||
#define BAT_MEASURE_ADC_UNIT 2 // Use ADC2
|
||||
#define ADC_ATTENUATION ADC_ATTEN_DB_11 // Voltage divider output is quite high
|
||||
#define ADC_ATTENUATION ADC_ATTEN_DB_12 // Voltage divider output is quite high
|
||||
|
||||
// LoRa
|
||||
#define USE_SX1262
|
||||
|
||||
#define LORA_DIO0 -1 // a No connect on the SX1262 module
|
||||
#define LORA_RESET 12
|
||||
#define LORA_DIO1 14 // SX1262 IRQ
|
||||
#define LORA_DIO2 13 // SX1262 BUSY
|
||||
#define LORA_DIO3 // Not connected on PCB, but internally on the TTGO SX1262, if DIO3 is high the TXCO is enabled
|
||||
|
||||
#define LORA_SCK 9
|
||||
#define LORA_MISO 11
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user