mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-14 22:07:25 +00:00
Compare commits
41 Commits
v2.3.14.64
...
v2.3.15.de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
deb7c274c4 | ||
|
|
e1bf4c32f3 | ||
|
|
86ca81b555 | ||
|
|
f59d98482f | ||
|
|
27dfe10689 | ||
|
|
7b838d388d | ||
|
|
c3d3dfa8c8 | ||
|
|
8be378c227 | ||
|
|
ae420dcd21 | ||
|
|
c1df621711 | ||
|
|
2ba88d305f | ||
|
|
fc63d956e7 | ||
|
|
4b82634d1a | ||
|
|
8bca3e168d | ||
|
|
8785adf6e4 | ||
|
|
9c46bdad1a | ||
|
|
10b157a38d | ||
|
|
e65c309af6 | ||
|
|
9701f35a83 | ||
|
|
3219d65387 | ||
|
|
8177329eac | ||
|
|
469ae0ff84 | ||
|
|
b5d7718319 | ||
|
|
47a94d7a07 | ||
|
|
20c1d71214 | ||
|
|
6f3d7ca4d2 | ||
|
|
ca969e26a5 | ||
|
|
5263c738f3 | ||
|
|
9c232da00f | ||
|
|
0016e747e9 | ||
|
|
ce58a23f9b | ||
|
|
c95b2c2d3c | ||
|
|
f86a0e5228 | ||
|
|
51f3ce5e60 | ||
|
|
41d633bfd8 | ||
|
|
2cb6e7bd37 | ||
|
|
a966d84e3d | ||
|
|
0425551341 | ||
|
|
626aa762df | ||
|
|
aa12e28568 | ||
|
|
58c00d0447 |
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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
|
||||
rm -r $OUTDIR/* || true
|
||||
|
||||
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
|
||||
platformio pkg update
|
||||
platformio pkg update
|
||||
|
||||
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f .pio/build/$1/firmware.*
|
||||
@@ -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/
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v6.ld"
|
||||
"ldscript": "nrf52840_s140_v7.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
@@ -15,8 +15,8 @@
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "6.1.1",
|
||||
"sd_fwid": "0x00B6"
|
||||
"sd_version": "7.3.0",
|
||||
"sd_fwid": "0x0123"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
@@ -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,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v6.ld"
|
||||
"ldscript": "nrf52840_s140_v7.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
@@ -22,8 +22,8 @@
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "6.1.1",
|
||||
"sd_fwid": "0x00B6"
|
||||
"sd_version": "7.3.0",
|
||||
"sd_fwid": "0x0123"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
@@ -53,6 +53,6 @@
|
||||
"require_upload_port": true,
|
||||
"wait_for_upload_port": true
|
||||
},
|
||||
"url": "https://www.seeedstudio.com/Wio-WM1110-Dev-Kit-p-5677.html",
|
||||
"url": "https://www.seeedstudio.com/Wio-Tracker-1110-Dev-Board-p-5799.html",
|
||||
"vendor": "Seeed Studio"
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -150,5 +151,4 @@ lib_deps =
|
||||
mprograms/QMC5883LCompass@^1.2.0
|
||||
|
||||
|
||||
https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee
|
||||
|
||||
https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee
|
||||
Submodule protobufs updated: 4da558d0f7...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,5 +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 LOGRADIO_UUID_16[16u] = {0xe2, 0xf2, 0x1e, 0xbe, 0xc5, 0x15, 0xcf, 0xaa,
|
||||
0x6b, 0x43, 0xfa, 0x78, 0x38, 0xd2, 0x6f, 0x6c};
|
||||
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,7 +11,8 @@
|
||||
#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 LOGRADIO_UUID "6c6fd238-78fa-436b-aacf-15c5be1ef2e2"
|
||||
#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
|
||||
@@ -28,5 +29,4 @@ class BluetoothApi
|
||||
virtual void clearBonds();
|
||||
virtual bool isConnected();
|
||||
virtual int getRssi() = 0;
|
||||
virtual void sendLog(const char *logMessage);
|
||||
};
|
||||
@@ -181,8 +181,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;
|
||||
}
|
||||
@@ -322,4 +323,4 @@ void ButtonThread::userButtonPressedLongStop()
|
||||
if (millis() > c_holdOffTime) {
|
||||
btnEvent = BUTTON_EVENT_LONG_RELEASED;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
@@ -152,6 +161,4 @@ class Syslog
|
||||
bool vlogf(uint16_t pri, const char *appName, const char *fmt, va_list args) __attribute__((format(printf, 3, 0)));
|
||||
};
|
||||
|
||||
#endif // HAS_ETHERNET || HAS_WIFI
|
||||
|
||||
#endif // SYSLOG_H
|
||||
#endif // HAS_ETHERNET || HAS_WIFI
|
||||
@@ -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();
|
||||
@@ -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();
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "concurrency/OSThread.h"
|
||||
#include "configuration.h"
|
||||
#include "main.h"
|
||||
#include "mesh/generated/meshtastic/mesh.pb.h"
|
||||
#include <assert.h>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
@@ -15,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()
|
||||
@@ -39,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
|
||||
|
||||
@@ -50,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];
|
||||
@@ -66,25 +62,200 @@ 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)
|
||||
{
|
||||
size_t r = 0;
|
||||
|
||||
// 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;
|
||||
// hms += tz.tz_dsttime * SEC_PER_HOUR;
|
||||
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
|
||||
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
|
||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
|
||||
// Tear apart hms into h:m:s
|
||||
int hour = hms / SEC_PER_HOUR;
|
||||
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
|
||||
::printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
|
||||
#else
|
||||
printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
|
||||
#endif
|
||||
} else
|
||||
#ifdef ARCH_PORTDUINO
|
||||
::printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000);
|
||||
#else
|
||||
printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000);
|
||||
#endif
|
||||
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
if (thread) {
|
||||
print("[");
|
||||
// printf("%p ", thread);
|
||||
// assert(thread->ThreadName.length());
|
||||
print(thread->ThreadName);
|
||||
print("] ");
|
||||
}
|
||||
}
|
||||
r += vprintf(logLevel, format, arg);
|
||||
|
||||
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;
|
||||
switch (logLevel[0]) {
|
||||
case 'D':
|
||||
ll = SYSLOG_DEBUG;
|
||||
break;
|
||||
case 'I':
|
||||
ll = SYSLOG_INFO;
|
||||
break;
|
||||
case 'W':
|
||||
ll = SYSLOG_WARN;
|
||||
break;
|
||||
case 'E':
|
||||
ll = SYSLOG_ERR;
|
||||
break;
|
||||
case 'C':
|
||||
ll = SYSLOG_CRIT;
|
||||
break;
|
||||
default:
|
||||
ll = 0;
|
||||
}
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
if (thread) {
|
||||
syslog.vlogf(ll, thread->ThreadName.c_str(), format, arg);
|
||||
} else {
|
||||
syslog.vlogf(ll, format, arg);
|
||||
}
|
||||
}
|
||||
#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 0;
|
||||
return;
|
||||
else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
|
||||
return 0;
|
||||
return;
|
||||
else if (settingsMap[logoutputlevel] < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
|
||||
return 0;
|
||||
return;
|
||||
#endif
|
||||
if (moduleConfig.serial.override_console_serial_port && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
size_t r = 0;
|
||||
|
||||
#ifdef HAS_FREE_RTOS
|
||||
if (inDebugPrint != nullptr && xSemaphoreTake(inDebugPrint, portMAX_DELAY) == pdTRUE) {
|
||||
#else
|
||||
@@ -95,114 +266,10 @@ size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
|
||||
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';
|
||||
log_to_serial(logLevel, format, arg);
|
||||
log_to_syslog(logLevel, format, arg);
|
||||
log_to_ble(logLevel, format, arg);
|
||||
|
||||
// If we are the first message on a report, include the header
|
||||
if (!isContinuationMessage) {
|
||||
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile
|
||||
if (rtc_sec > 0) {
|
||||
long hms = rtc_sec % SEC_PER_DAY;
|
||||
// hms += tz.tz_dsttime * SEC_PER_HOUR;
|
||||
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
|
||||
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
|
||||
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
|
||||
|
||||
// Tear apart hms into h:m:s
|
||||
int hour = hms / SEC_PER_HOUR;
|
||||
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);
|
||||
#else
|
||||
r += printf("%s | %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
|
||||
#endif
|
||||
} else
|
||||
#ifdef ARCH_PORTDUINO
|
||||
r += ::printf("%s | ??:??:?? %u ", logLevel, millis() / 1000);
|
||||
#else
|
||||
r += printf("%s | ??:??:?? %u ", logLevel, millis() / 1000);
|
||||
#endif
|
||||
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
if (thread) {
|
||||
print("[");
|
||||
// printf("%p ", thread);
|
||||
// assert(thread->ThreadName.length());
|
||||
print(thread->ThreadName);
|
||||
print("] ");
|
||||
}
|
||||
}
|
||||
r += vprintf(format, arg);
|
||||
|
||||
#if (HAS_WIFI || HAS_ETHERNET) && !defined(ARCH_PORTDUINO)
|
||||
// if syslog is in use, collect the log messages and send them to syslog
|
||||
if (syslog.isEnabled()) {
|
||||
int ll = 0;
|
||||
switch (logLevel[0]) {
|
||||
case 'D':
|
||||
ll = SYSLOG_DEBUG;
|
||||
break;
|
||||
case 'I':
|
||||
ll = SYSLOG_INFO;
|
||||
break;
|
||||
case 'W':
|
||||
ll = SYSLOG_WARN;
|
||||
break;
|
||||
case 'E':
|
||||
ll = SYSLOG_ERR;
|
||||
break;
|
||||
case 'C':
|
||||
ll = SYSLOG_CRIT;
|
||||
break;
|
||||
default:
|
||||
ll = 0;
|
||||
}
|
||||
auto thread = concurrency::OSThread::currentThread;
|
||||
if (thread) {
|
||||
syslog.vlogf(ll, thread->ThreadName.c_str(), format, arg);
|
||||
} else {
|
||||
syslog.vlogf(ll, format, arg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
isContinuationMessage = !hasNewline;
|
||||
|
||||
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;
|
||||
#ifdef ARCH_ESP32
|
||||
if (thread)
|
||||
nimbleBluetooth->sendLog(mt_sprintf("%s | [%s] %s", logLevel, thread->ThreadName.c_str(), message).c_str());
|
||||
else
|
||||
nimbleBluetooth->sendLog(mt_sprintf("%s | %s", logLevel, message).c_str());
|
||||
#elif defined(ARCH_NRF52)
|
||||
if (thread)
|
||||
nrf52Bluetooth->sendLog(mt_sprintf("%s | [%s] %s", logLevel, thread->ThreadName.c_str(), message).c_str());
|
||||
else
|
||||
nrf52Bluetooth->sendLog(mt_sprintf("%s | %s", logLevel, message).c_str());
|
||||
#endif
|
||||
delete[] message;
|
||||
}
|
||||
}
|
||||
va_end(arg);
|
||||
#ifdef HAS_FREE_RTOS
|
||||
xSemaphoreGive(inDebugPrint);
|
||||
@@ -211,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);
|
||||
};
|
||||
@@ -24,7 +24,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();
|
||||
}
|
||||
@@ -34,7 +34,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);
|
||||
@@ -81,13 +80,40 @@ 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);
|
||||
} else {
|
||||
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,10 +36,13 @@ 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
|
||||
void consolePrintf(const char *format, ...);
|
||||
void consoleInit();
|
||||
|
||||
extern SerialConsole *console;
|
||||
extern SerialConsole *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
|
||||
@@ -308,4 +310,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifdef MESHTASTIC_EXCLUDE_SCREEN
|
||||
#undef HAS_SCREEN
|
||||
#define HAS_SCREEN 0
|
||||
#endif
|
||||
#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) {
|
||||
@@ -402,4 +402,4 @@ TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const
|
||||
size_t ScanI2CTwoWire::countDevices() const
|
||||
{
|
||||
return foundDevices.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Default.h"
|
||||
#include "GPS.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PowerMon.h"
|
||||
#include "RTC.h"
|
||||
|
||||
#include "main.h" // pmu_found
|
||||
@@ -815,9 +816,12 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
|
||||
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 &&
|
||||
|
||||
@@ -43,7 +43,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "meshUtils.h"
|
||||
#include "modules/ExternalNotificationModule.h"
|
||||
#include "modules/TextMessageModule.h"
|
||||
#include "modules/WaypointModule.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
|
||||
@@ -60,9 +59,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "platform/portduino/PortduinoGlue.h"
|
||||
#endif
|
||||
|
||||
/// Convert an integer GPS coords to a floating point
|
||||
#define DegD(i) (i * 1e-7)
|
||||
|
||||
using namespace meshtastic; /** @todo remove */
|
||||
|
||||
namespace graphics
|
||||
@@ -79,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
|
||||
|
||||
@@ -112,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
|
||||
*/
|
||||
@@ -144,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)
|
||||
@@ -185,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)
|
||||
@@ -202,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);
|
||||
@@ -210,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)
|
||||
{
|
||||
@@ -281,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
|
||||
@@ -379,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;
|
||||
@@ -393,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);
|
||||
@@ -450,37 +412,6 @@ static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
|
||||
return packet->from != 0 && !moduleConfig.store_forward.enabled;
|
||||
}
|
||||
|
||||
// Determine whether the waypoint frame should be drawn (waypoint deleted? expired?)
|
||||
static bool shouldDrawWaypoint(const meshtastic_MeshPacket *packet)
|
||||
{
|
||||
#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(packet->decoded.payload.bytes, packet->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 power bars or a charging indicator on an image of a battery, determined by battery charge voltage or percentage.
|
||||
static void drawBattery(OLEDDisplay *display, int16_t x, int16_t y, uint8_t *imgBuffer, const PowerStatus *powerStatus)
|
||||
{
|
||||
@@ -1127,7 +1058,7 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
||||
}
|
||||
|
||||
/// 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);
|
||||
@@ -1307,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;
|
||||
@@ -1380,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;
|
||||
@@ -1421,38 +1284,16 @@ 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);
|
||||
}
|
||||
|
||||
// Draw north
|
||||
static void 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)
|
||||
myHeading = -0;
|
||||
|
||||
Point N1(-0.04f, 0.65f), N2(0.04f, 0.65f);
|
||||
Point N3(-0.04f, 0.55f), N4(0.04f, 0.55f);
|
||||
Point *rosePoints[] = {&N1, &N2, &N3, &N4};
|
||||
|
||||
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]->translate(compassX, compassY);
|
||||
}
|
||||
drawLine(display, N1, N3);
|
||||
drawLine(display, N2, N4);
|
||||
drawLine(display, N1, N4);
|
||||
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);
|
||||
}
|
||||
|
||||
// Get a string representation of the time passed since something happened
|
||||
static void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
|
||||
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.
|
||||
@@ -1481,6 +1322,54 @@ static void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
|
||||
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)
|
||||
myHeading = -0;
|
||||
|
||||
Point N1(-0.04f, 0.65f), N2(0.04f, 0.65f);
|
||||
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(compassDiam);
|
||||
rosePoints[i]->translate(compassX, compassY);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// We only advance our nodeIndex if the frame # has changed - because
|
||||
@@ -1520,7 +1409,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
||||
}
|
||||
|
||||
static char lastStr[20];
|
||||
getTimeAgoStr(sinceLastSeen(node), lastStr, sizeof(lastStr));
|
||||
screen->getTimeAgoStr(sinceLastSeen(node), lastStr, sizeof(lastStr));
|
||||
|
||||
static char distStr[20];
|
||||
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
|
||||
@@ -1531,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;
|
||||
@@ -1548,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
|
||||
@@ -1576,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) {
|
||||
@@ -1586,119 +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);
|
||||
}
|
||||
|
||||
/// Draw the last waypoint we received
|
||||
static void drawWaypointFrame(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];
|
||||
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};
|
||||
|
||||
// Co-ordinates for the center of the compass/circle
|
||||
int16_t compassX = 0, compassY = 0;
|
||||
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
|
||||
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
|
||||
compassY = y + SCREEN_HEIGHT / 2;
|
||||
} else {
|
||||
compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
|
||||
compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - 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 = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
|
||||
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;
|
||||
drawNodeHeading(display, compassX, compassY, 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, getCompassDiam(display) / 2);
|
||||
|
||||
// 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)
|
||||
@@ -1846,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);
|
||||
|
||||
@@ -1911,8 +1705,6 @@ void Screen::setup()
|
||||
textMessageObserver.observe(textMessageModule);
|
||||
if (inputBroker)
|
||||
inputObserver.observe(inputBroker);
|
||||
if (waypointModule)
|
||||
waypointObserver.observe(waypointModule);
|
||||
|
||||
// Modules can notify screen about refresh
|
||||
MeshModule::observeUIEvents(&uiFrameEventObserver);
|
||||
@@ -2023,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();
|
||||
@@ -2038,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");
|
||||
}
|
||||
@@ -2241,11 +2036,6 @@ void Screen::setFrames()
|
||||
normalFrames[numframes++] = drawTextMessageFrame;
|
||||
}
|
||||
|
||||
// If we have a waypoint (not expired, not deleted)
|
||||
if (devicestate.has_rx_waypoint && shouldDrawWaypoint(&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
|
||||
size_t numToShow = min(numMeshNodes, 4U);
|
||||
@@ -2284,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();
|
||||
@@ -2302,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");
|
||||
@@ -2353,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();
|
||||
@@ -2844,13 +2588,6 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Screen::handleWaypoint(const meshtastic_MeshPacket *arg)
|
||||
{
|
||||
// TODO: move to appropriate frame when redrawing
|
||||
setFrames();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace graphics
|
||||
#else
|
||||
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -126,8 +170,6 @@ class Screen : public concurrency::OSThread
|
||||
CallbackObserver<Screen, const meshtastic::Status *>(this, &Screen::handleStatusUpdate);
|
||||
CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver =
|
||||
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
|
||||
CallbackObserver<Screen, const meshtastic_MeshPacket *> waypointObserver =
|
||||
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleWaypoint);
|
||||
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
|
||||
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent);
|
||||
CallbackObserver<Screen, const InputEvent *> inputObserver =
|
||||
@@ -168,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);
|
||||
}
|
||||
|
||||
@@ -192,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)
|
||||
@@ -224,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}); }
|
||||
|
||||
@@ -338,7 +392,6 @@ class Screen : public concurrency::OSThread
|
||||
int handleTextMessage(const meshtastic_MeshPacket *arg);
|
||||
int handleUIFrameEvent(const UIFrameEvent *arg);
|
||||
int handleInputEvent(const InputEvent *arg);
|
||||
int handleWaypoint(const meshtastic_MeshPacket *arg);
|
||||
|
||||
/// Used to force (super slow) eink displays to draw critical frames
|
||||
void forceDisplay(bool forceUiUpdate = false);
|
||||
@@ -361,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 {
|
||||
@@ -387,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();
|
||||
|
||||
@@ -429,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
|
||||
@@ -455,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)
|
||||
16
src/main.cpp
16
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"
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
|
||||
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -180,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include "Channels.h"
|
||||
#include "Default.h"
|
||||
#include "FSCommon.h"
|
||||
#include "MeshService.h"
|
||||
#include "NodeDB.h"
|
||||
#include "PhoneAPI.h"
|
||||
@@ -47,6 +48,8 @@ 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
|
||||
@@ -149,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
|
||||
*/
|
||||
@@ -324,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;
|
||||
@@ -340,20 +344,32 @@ 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;
|
||||
pauseBluetoothLogging = false;
|
||||
sendConfigComplete();
|
||||
break;
|
||||
|
||||
case STATE_SEND_PACKETS:
|
||||
@@ -391,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;
|
||||
}
|
||||
|
||||
@@ -399,8 +417,19 @@ 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");
|
||||
}
|
||||
@@ -443,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;
|
||||
|
||||
@@ -457,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
|
||||
*/
|
||||
|
||||
@@ -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,16 +35,17 @@ 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;
|
||||
DACDB values;
|
||||
} dbmToDACDB[] = {
|
||||
{20, {168, 2}}, // 100mW
|
||||
{24, {148, 6}}, // 250mW
|
||||
{27, {128, 9}}, // 500mW
|
||||
{30, {90, 12}} // 1000mW
|
||||
{20, {168, 2}}, // 100mW
|
||||
{24, {148, 6}}, // 250mW
|
||||
{27, {128, 9}}, // 500mW
|
||||
{30, {90, 12}} // 1000mW
|
||||
};
|
||||
const int numValues = sizeof(dbmToDACDB) / sizeof(dbmToDACDB[0]);
|
||||
|
||||
@@ -103,7 +105,7 @@ bool RF95Interface::init()
|
||||
|
||||
if (power > RF95_MAX_POWER) // This chip has lower power limits than some
|
||||
power = RF95_MAX_POWER;
|
||||
|
||||
|
||||
limitPower();
|
||||
|
||||
iface = lora = new RadioLibRF95(&module);
|
||||
@@ -116,13 +118,13 @@ bool RF95Interface::init()
|
||||
// enable PA
|
||||
#ifdef RF95_PA_EN
|
||||
#if defined(RF95_PA_DAC_EN)
|
||||
#ifdef RADIOMASTER_900_BANDIT_NANO
|
||||
// Use calculated DAC value
|
||||
dacWrite(RF95_PA_EN, powerDAC);
|
||||
#else
|
||||
// Use Value set in /*/variant.h
|
||||
dacWrite(RF95_PA_EN, RF95_PA_LEVEL);
|
||||
#endif
|
||||
#ifdef RADIOMASTER_900_BANDIT_NANO
|
||||
// Use calculated DAC value
|
||||
dacWrite(RF95_PA_EN, powerDAC);
|
||||
#else
|
||||
// Use Value set in /*/variant.h
|
||||
dacWrite(RF95_PA_EN, RF95_PA_LEVEL);
|
||||
#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)
|
||||
{
|
||||
@@ -131,4 +150,4 @@ void StreamAPI::onConnectionChanged(bool connected)
|
||||
// received a packet in a while
|
||||
powerFSM.trigger(EVENT_SERIAL_DISCONNECTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) */
|
||||
|
||||
@@ -9,5 +9,9 @@
|
||||
PB_BIND(meshtastic_PowerMon, meshtastic_PowerMon, AUTO)
|
||||
|
||||
|
||||
PB_BIND(meshtastic_PowerStressMessage, meshtastic_PowerStressMessage, AUTO)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -38,6 +38,33 @@ 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) */
|
||||
@@ -45,6 +72,13 @@ 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" {
|
||||
@@ -55,13 +89,23 @@ extern "C" {
|
||||
#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) \
|
||||
@@ -69,14 +113,23 @@ extern "C" {
|
||||
#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_PowerMon_size
|
||||
#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" */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
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;
|
||||
@@ -188,7 +188,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;
|
||||
}
|
||||
|
||||
@@ -213,31 +213,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");
|
||||
}
|
||||
|
||||
@@ -285,6 +285,10 @@ bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *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);
|
||||
hasSensor = true;
|
||||
@@ -533,4 +537,4 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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,21 +5,29 @@
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
extern WaypointModule *waypointModule;
|
||||
extern WaypointModule *waypointModule;
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,7 +219,6 @@ void NimbleBluetooth::setupService()
|
||||
logRadioCharacteristic = bleService->createCharacteristic(
|
||||
LOGRADIO_UUID,
|
||||
NIMBLE_PROPERTY::NOTIFY | NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_AUTHEN | NIMBLE_PROPERTY::READ_ENC, 512U);
|
||||
logRadioCharacteristic->setValue("Init");
|
||||
}
|
||||
bluetoothPhoneAPI = new BluetoothPhoneAPI();
|
||||
|
||||
@@ -242,12 +267,12 @@ void NimbleBluetooth::clearBonds()
|
||||
NimBLEDevice::deleteAllBonds();
|
||||
}
|
||||
|
||||
void NimbleBluetooth::sendLog(const char *logMessage)
|
||||
void NimbleBluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
{
|
||||
if (!bleServer || !isConnected() || strlen(logMessage) > 512) {
|
||||
if (!bleServer || !isConnected() || length > 512) {
|
||||
return;
|
||||
}
|
||||
logRadioCharacteristic->notify(reinterpret_cast<const uint8_t *>(logMessage), strlen(logMessage), true);
|
||||
logRadioCharacteristic->notify(logMessage, length, true);
|
||||
}
|
||||
|
||||
void clearNVS()
|
||||
|
||||
@@ -11,7 +11,7 @@ class NimbleBluetooth : BluetoothApi
|
||||
bool isActive();
|
||||
bool isConnected();
|
||||
int getRssi();
|
||||
void sendLog(const char *logMessage);
|
||||
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,23 +24,22 @@
|
||||
#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)
|
||||
#else
|
||||
if (config.bluetooth.enabled == true)
|
||||
#endif
|
||||
#ifdef MESHTASTIC_EXCLUDE_WIFI
|
||||
if (config.bluetooth.enabled == true)
|
||||
#endif
|
||||
{
|
||||
if (!nimbleBluetooth) {
|
||||
nimbleBluetooth = new NimbleBluetooth();
|
||||
}
|
||||
if (enable && !nimbleBluetooth->isActive()) {
|
||||
nimbleBluetooth->setup();
|
||||
}
|
||||
// For ESP32, no way to recover from bluetooth shutdown without reboot
|
||||
// BLE advertising automatically stops when MCU enters light-sleep(?)
|
||||
// For deep-sleep, shutdown hardware with nimbleBluetooth->deinit(). Requires reboot to reverse
|
||||
{
|
||||
if (!nimbleBluetooth) {
|
||||
nimbleBluetooth = new NimbleBluetooth();
|
||||
}
|
||||
if (enable && !nimbleBluetooth->isActive()) {
|
||||
nimbleBluetooth->setup();
|
||||
}
|
||||
// For ESP32, no way to recover from bluetooth shutdown without reboot
|
||||
// BLE advertising automatically stops when MCU enters light-sleep(?)
|
||||
// For deep-sleep, shutdown hardware with nimbleBluetooth->deinit(). Requires reboot to reverse
|
||||
}
|
||||
}
|
||||
#else
|
||||
void setBluetoothEnable(bool enable) {}
|
||||
|
||||
@@ -74,11 +74,16 @@ void onCccd(uint16_t conn_hdl, BLECharacteristic *chr, uint16_t cccd_value)
|
||||
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.
|
||||
|
||||
// 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) {
|
||||
if (chr->notifyEnabled(conn_hdl)) {
|
||||
LOG_INFO("fromNum 'Notify' enabled\n");
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -176,7 +181,7 @@ void setupMeshService(void)
|
||||
toRadio.setWriteCallback(onToRadioWrite, false);
|
||||
toRadio.begin();
|
||||
|
||||
logRadio.setProperties(CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
logRadio.setProperties(CHR_PROPS_INDICATE | CHR_PROPS_NOTIFY | CHR_PROPS_READ);
|
||||
logRadio.setPermission(secMode, SECMODE_NO_ACCESS);
|
||||
logRadio.setMaxLen(512);
|
||||
logRadio.setCccdWriteCallback(onCccd);
|
||||
@@ -290,7 +295,31 @@ bool NRF52Bluetooth::onPairingPasskey(uint16_t conn_handle, uint8_t const passke
|
||||
{
|
||||
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) {
|
||||
@@ -307,12 +336,15 @@ void NRF52Bluetooth::onPairingCompleted(uint16_t conn_handle, uint8_t auth_statu
|
||||
LOG_INFO("BLE pairing success\n");
|
||||
else
|
||||
LOG_INFO("BLE pairing failed\n");
|
||||
screen->stopBluetoothPinScreen();
|
||||
screen->endAlert();
|
||||
}
|
||||
|
||||
void NRF52Bluetooth::sendLog(const char *logMessage)
|
||||
void NRF52Bluetooth::sendLog(const uint8_t *logMessage, size_t length)
|
||||
{
|
||||
if (!isConnected() || strlen(logMessage) > 512)
|
||||
if (!isConnected() || length > 512)
|
||||
return;
|
||||
logRadio.notify(logMessage);
|
||||
if (logRadio.indicateEnabled())
|
||||
logRadio.indicate(logMessage, (uint16_t)length);
|
||||
else
|
||||
logRadio.notify(logMessage, (uint16_t)length);
|
||||
}
|
||||
@@ -13,7 +13,7 @@ class NRF52Bluetooth : BluetoothApi
|
||||
void clearBonds();
|
||||
bool isConnected();
|
||||
int getRssi();
|
||||
void sendLog(const char *logMessage);
|
||||
void sendLog(const uint8_t *logMessage, size_t length);
|
||||
|
||||
private:
|
||||
static void onConnectionSecured(uint16_t conn_handle);
|
||||
|
||||
@@ -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
|
||||
|
||||
380
src/platform/nrf52/softdevice/nrf_sdm.h
Normal file
380
src/platform/nrf52/softdevice/nrf_sdm.h
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* Copyright (c) Nordic Semiconductor ASA
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form, except as embedded into a Nordic
|
||||
* Semiconductor ASA integrated circuit in a product or a software update for
|
||||
* such product, must reproduce the above copyright notice, this list of
|
||||
* conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
*
|
||||
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* 4. This software, with or without modification, must only be used with a
|
||||
* Nordic Semiconductor ASA integrated circuit.
|
||||
*
|
||||
* 5. Any software provided in binary form under this license must not be reverse
|
||||
* engineered, decompiled, modified and/or disassembled.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
@defgroup nrf_sdm_api SoftDevice Manager API
|
||||
@{
|
||||
|
||||
@brief APIs for SoftDevice management.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef NRF_SDM_H__
|
||||
#define NRF_SDM_H__
|
||||
|
||||
#include "nrf.h"
|
||||
#include "nrf_error.h"
|
||||
#include "nrf_error_sdm.h"
|
||||
#include "nrf_soc.h"
|
||||
#include "nrf_svc.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** @addtogroup NRF_SDM_DEFINES Defines
|
||||
* @{ */
|
||||
#ifdef NRFSOC_DOXYGEN
|
||||
/// Declared in nrf_mbr.h
|
||||
#define MBR_SIZE 0
|
||||
#warning test
|
||||
#endif
|
||||
|
||||
/** @brief The major version for the SoftDevice binary distributed with this header file. */
|
||||
#define SD_MAJOR_VERSION (7)
|
||||
|
||||
/** @brief The minor version for the SoftDevice binary distributed with this header file. */
|
||||
#define SD_MINOR_VERSION (3)
|
||||
|
||||
/** @brief The bugfix version for the SoftDevice binary distributed with this header file. */
|
||||
#define SD_BUGFIX_VERSION (0)
|
||||
|
||||
/** @brief The SoftDevice variant of this firmware. */
|
||||
#define SD_VARIANT_ID 140
|
||||
|
||||
/** @brief The full version number for the SoftDevice binary this header file was distributed
|
||||
* with, as a decimal number in the form Mmmmbbb, where:
|
||||
* - M is major version (one or more digits)
|
||||
* - mmm is minor version (three digits)
|
||||
* - bbb is bugfix version (three digits). */
|
||||
#define SD_VERSION (SD_MAJOR_VERSION * 1000000 + SD_MINOR_VERSION * 1000 + SD_BUGFIX_VERSION)
|
||||
|
||||
/** @brief SoftDevice Manager SVC Base number. */
|
||||
#define SDM_SVC_BASE 0x10
|
||||
|
||||
/** @brief SoftDevice unique string size in bytes. */
|
||||
#define SD_UNIQUE_STR_SIZE 20
|
||||
|
||||
/** @brief Invalid info field. Returned when an info field does not exist. */
|
||||
#define SDM_INFO_FIELD_INVALID (0)
|
||||
|
||||
/** @brief Defines the SoftDevice Information Structure location (address) as an offset from
|
||||
the start of the SoftDevice (without MBR)*/
|
||||
#define SOFTDEVICE_INFO_STRUCT_OFFSET (0x2000)
|
||||
|
||||
/** @brief Defines the absolute SoftDevice Information Structure location (address) when the
|
||||
* SoftDevice is installed just above the MBR (the usual case). */
|
||||
#define SOFTDEVICE_INFO_STRUCT_ADDRESS (SOFTDEVICE_INFO_STRUCT_OFFSET + MBR_SIZE)
|
||||
|
||||
/** @brief Defines the offset for the SoftDevice Information Structure size value relative to the
|
||||
* SoftDevice base address. The size value is of type uint8_t. */
|
||||
#define SD_INFO_STRUCT_SIZE_OFFSET (SOFTDEVICE_INFO_STRUCT_OFFSET)
|
||||
|
||||
/** @brief Defines the offset for the SoftDevice size value relative to the SoftDevice base address.
|
||||
* The size value is of type uint32_t. */
|
||||
#define SD_SIZE_OFFSET (SOFTDEVICE_INFO_STRUCT_OFFSET + 0x08)
|
||||
|
||||
/** @brief Defines the offset for FWID value relative to the SoftDevice base address. The FWID value
|
||||
* is of type uint16_t. */
|
||||
#define SD_FWID_OFFSET (SOFTDEVICE_INFO_STRUCT_OFFSET + 0x0C)
|
||||
|
||||
/** @brief Defines the offset for the SoftDevice ID relative to the SoftDevice base address. The ID
|
||||
* is of type uint32_t. */
|
||||
#define SD_ID_OFFSET (SOFTDEVICE_INFO_STRUCT_OFFSET + 0x10)
|
||||
|
||||
/** @brief Defines the offset for the SoftDevice version relative to the SoftDevice base address in
|
||||
* the same format as @ref SD_VERSION, stored as an uint32_t. */
|
||||
#define SD_VERSION_OFFSET (SOFTDEVICE_INFO_STRUCT_OFFSET + 0x14)
|
||||
|
||||
/** @brief Defines the offset for the SoftDevice unique string relative to the SoftDevice base address.
|
||||
* The SD_UNIQUE_STR is stored as an array of uint8_t. The size of array is @ref SD_UNIQUE_STR_SIZE.
|
||||
*/
|
||||
#define SD_UNIQUE_STR_OFFSET (SOFTDEVICE_INFO_STRUCT_OFFSET + 0x18)
|
||||
|
||||
/** @brief Defines a macro for retrieving the actual SoftDevice Information Structure size value
|
||||
* from a given base address. Use @ref MBR_SIZE as the argument when the SoftDevice is
|
||||
* installed just above the MBR (the usual case). */
|
||||
#define SD_INFO_STRUCT_SIZE_GET(baseaddr) (*((uint8_t *)((baseaddr) + SD_INFO_STRUCT_SIZE_OFFSET)))
|
||||
|
||||
/** @brief Defines a macro for retrieving the actual SoftDevice size value from a given base
|
||||
* address. Use @ref MBR_SIZE as the argument when the SoftDevice is installed just above
|
||||
* the MBR (the usual case). */
|
||||
#define SD_SIZE_GET(baseaddr) (*((uint32_t *)((baseaddr) + SD_SIZE_OFFSET)))
|
||||
|
||||
/** @brief Defines the amount of flash that is used by the SoftDevice.
|
||||
* Add @ref MBR_SIZE to find the first available flash address when the SoftDevice is installed
|
||||
* just above the MBR (the usual case).
|
||||
*/
|
||||
#define SD_FLASH_SIZE 0x27000
|
||||
|
||||
/** @brief Defines a macro for retrieving the actual FWID value from a given base address. Use
|
||||
* @ref MBR_SIZE as the argument when the SoftDevice is installed just above the MBR (the usual
|
||||
* case). */
|
||||
#define SD_FWID_GET(baseaddr) (*((uint16_t *)((baseaddr) + SD_FWID_OFFSET)))
|
||||
|
||||
/** @brief Defines a macro for retrieving the actual SoftDevice ID from a given base address. Use
|
||||
* @ref MBR_SIZE as the argument when the SoftDevice is installed just above the MBR (the
|
||||
* usual case). */
|
||||
#define SD_ID_GET(baseaddr) \
|
||||
((SD_INFO_STRUCT_SIZE_GET(baseaddr) > (SD_ID_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) \
|
||||
? (*((uint32_t *)((baseaddr) + SD_ID_OFFSET))) \
|
||||
: SDM_INFO_FIELD_INVALID)
|
||||
|
||||
/** @brief Defines a macro for retrieving the actual SoftDevice version from a given base address.
|
||||
* Use @ref MBR_SIZE as the argument when the SoftDevice is installed just above the MBR
|
||||
* (the usual case). */
|
||||
#define SD_VERSION_GET(baseaddr) \
|
||||
((SD_INFO_STRUCT_SIZE_GET(baseaddr) > (SD_VERSION_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) \
|
||||
? (*((uint32_t *)((baseaddr) + SD_VERSION_OFFSET))) \
|
||||
: SDM_INFO_FIELD_INVALID)
|
||||
|
||||
/** @brief Defines a macro for retrieving the address of SoftDevice unique str based on a given base address.
|
||||
* Use @ref MBR_SIZE as the argument when the SoftDevice is installed just above the MBR
|
||||
* (the usual case). */
|
||||
#define SD_UNIQUE_STR_ADDR_GET(baseaddr) \
|
||||
((SD_INFO_STRUCT_SIZE_GET(baseaddr) > (SD_UNIQUE_STR_OFFSET - SOFTDEVICE_INFO_STRUCT_OFFSET)) \
|
||||
? (((uint8_t *)((baseaddr) + SD_UNIQUE_STR_OFFSET))) \
|
||||
: SDM_INFO_FIELD_INVALID)
|
||||
|
||||
/**@defgroup NRF_FAULT_ID_RANGES Fault ID ranges
|
||||
* @{ */
|
||||
#define NRF_FAULT_ID_SD_RANGE_START 0x00000000 /**< SoftDevice ID range start. */
|
||||
#define NRF_FAULT_ID_APP_RANGE_START 0x00001000 /**< Application ID range start. */
|
||||
/**@} */
|
||||
|
||||
/**@defgroup NRF_FAULT_IDS Fault ID types
|
||||
* @{ */
|
||||
#define NRF_FAULT_ID_SD_ASSERT \
|
||||
(NRF_FAULT_ID_SD_RANGE_START + 1) /**< SoftDevice assertion. The info parameter is reserved for future used. */
|
||||
#define NRF_FAULT_ID_APP_MEMACC \
|
||||
(NRF_FAULT_ID_APP_RANGE_START + 1) /**< Application invalid memory access. The info parameter will contain 0x00000000, \
|
||||
in case of SoftDevice RAM access violation. In case of SoftDevice peripheral \
|
||||
register violation the info parameter will contain the sub-region number of \
|
||||
PREGION[0], on whose address range the disallowed write access caused the \
|
||||
memory access fault. */
|
||||
/**@} */
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @addtogroup NRF_SDM_ENUMS Enumerations
|
||||
* @{ */
|
||||
|
||||
/**@brief nRF SoftDevice Manager API SVC numbers. */
|
||||
enum NRF_SD_SVCS {
|
||||
SD_SOFTDEVICE_ENABLE = SDM_SVC_BASE, /**< ::sd_softdevice_enable */
|
||||
SD_SOFTDEVICE_DISABLE, /**< ::sd_softdevice_disable */
|
||||
SD_SOFTDEVICE_IS_ENABLED, /**< ::sd_softdevice_is_enabled */
|
||||
SD_SOFTDEVICE_VECTOR_TABLE_BASE_SET, /**< ::sd_softdevice_vector_table_base_set */
|
||||
SVC_SDM_LAST /**< Placeholder for last SDM SVC */
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @addtogroup NRF_SDM_DEFINES Defines
|
||||
* @{ */
|
||||
|
||||
/**@defgroup NRF_CLOCK_LF_ACCURACY Clock accuracy
|
||||
* @{ */
|
||||
|
||||
#define NRF_CLOCK_LF_ACCURACY_250_PPM (0) /**< Default: 250 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_500_PPM (1) /**< 500 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_150_PPM (2) /**< 150 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_100_PPM (3) /**< 100 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_75_PPM (4) /**< 75 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_50_PPM (5) /**< 50 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_30_PPM (6) /**< 30 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_20_PPM (7) /**< 20 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_10_PPM (8) /**< 10 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_5_PPM (9) /**< 5 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_2_PPM (10) /**< 2 ppm */
|
||||
#define NRF_CLOCK_LF_ACCURACY_1_PPM (11) /**< 1 ppm */
|
||||
|
||||
/** @} */
|
||||
|
||||
/**@defgroup NRF_CLOCK_LF_SRC Possible LFCLK oscillator sources
|
||||
* @{ */
|
||||
|
||||
#define NRF_CLOCK_LF_SRC_RC (0) /**< LFCLK RC oscillator. */
|
||||
#define NRF_CLOCK_LF_SRC_XTAL (1) /**< LFCLK crystal oscillator. */
|
||||
#define NRF_CLOCK_LF_SRC_SYNTH (2) /**< LFCLK Synthesized from HFCLK. */
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @addtogroup NRF_SDM_TYPES Types
|
||||
* @{ */
|
||||
|
||||
/**@brief Type representing LFCLK oscillator source. */
|
||||
typedef struct {
|
||||
uint8_t source; /**< LF oscillator clock source, see @ref NRF_CLOCK_LF_SRC. */
|
||||
uint8_t rc_ctiv; /**< Only for ::NRF_CLOCK_LF_SRC_RC: Calibration timer interval in 1/4 second
|
||||
units (nRF52: 1-32).
|
||||
@note To avoid excessive clock drift, 0.5 degrees Celsius is the
|
||||
maximum temperature change allowed in one calibration timer
|
||||
interval. The interval should be selected to ensure this.
|
||||
|
||||
@note Must be 0 if source is not ::NRF_CLOCK_LF_SRC_RC. */
|
||||
uint8_t rc_temp_ctiv; /**< Only for ::NRF_CLOCK_LF_SRC_RC: How often (in number of calibration
|
||||
intervals) the RC oscillator shall be calibrated if the temperature
|
||||
hasn't changed.
|
||||
0: Always calibrate even if the temperature hasn't changed.
|
||||
1: Only calibrate if the temperature has changed (legacy - nRF51 only).
|
||||
2-33: Check the temperature and only calibrate if it has changed,
|
||||
however calibration will take place every rc_temp_ctiv
|
||||
intervals in any case.
|
||||
|
||||
@note Must be 0 if source is not ::NRF_CLOCK_LF_SRC_RC.
|
||||
|
||||
@note For nRF52, the application must ensure calibration at least once
|
||||
every 8 seconds to ensure +/-500 ppm clock stability. The
|
||||
recommended configuration for ::NRF_CLOCK_LF_SRC_RC on nRF52 is
|
||||
rc_ctiv=16 and rc_temp_ctiv=2. This will ensure calibration at
|
||||
least once every 8 seconds and for temperature changes of 0.5
|
||||
degrees Celsius every 4 seconds. See the Product Specification
|
||||
for the nRF52 device being used for more information.*/
|
||||
uint8_t accuracy; /**< External clock accuracy used in the LL to compute timing
|
||||
windows, see @ref NRF_CLOCK_LF_ACCURACY.*/
|
||||
} nrf_clock_lf_cfg_t;
|
||||
|
||||
/**@brief Fault Handler type.
|
||||
*
|
||||
* When certain unrecoverable errors occur within the application or SoftDevice the fault handler will be called back.
|
||||
* The protocol stack will be in an undefined state when this happens and the only way to recover will be to
|
||||
* perform a reset, using e.g. CMSIS NVIC_SystemReset().
|
||||
* If the application returns from the fault handler the SoftDevice will call NVIC_SystemReset().
|
||||
*
|
||||
* @note It is recommended to either perform a reset in the fault handler or to let the SoftDevice reset the device.
|
||||
* Otherwise SoC peripherals may behave in an undefined way. For example, the RADIO peripherial may
|
||||
* continously transmit packets.
|
||||
*
|
||||
* @note This callback is executed in HardFault context, thus SVC functions cannot be called from the fault callback.
|
||||
*
|
||||
* @param[in] id Fault identifier. See @ref NRF_FAULT_IDS.
|
||||
* @param[in] pc The program counter of the instruction that triggered the fault.
|
||||
* @param[in] info Optional additional information regarding the fault. Refer to each Fault identifier for details.
|
||||
*
|
||||
* @note When id is set to @ref NRF_FAULT_ID_APP_MEMACC, pc will contain the address of the instruction being executed at the time
|
||||
* when the fault is detected by the CPU. The CPU program counter may have advanced up to 2 instructions (no branching) after the
|
||||
* one that triggered the fault.
|
||||
*/
|
||||
typedef void (*nrf_fault_handler_t)(uint32_t id, uint32_t pc, uint32_t info);
|
||||
|
||||
/** @} */
|
||||
|
||||
/** @addtogroup NRF_SDM_FUNCTIONS Functions
|
||||
* @{ */
|
||||
|
||||
/**@brief Enables the SoftDevice and by extension the protocol stack.
|
||||
*
|
||||
* @note Some care must be taken if a low frequency clock source is already running when calling this function:
|
||||
* If the LF clock has a different source then the one currently running, it will be stopped. Then, the new
|
||||
* clock source will be started.
|
||||
*
|
||||
* @note This function has no effect when returning with an error.
|
||||
*
|
||||
* @post If return code is ::NRF_SUCCESS
|
||||
* - SoC library and protocol stack APIs are made available.
|
||||
* - A portion of RAM will be unavailable (see relevant SDS documentation).
|
||||
* - Some peripherals will be unavailable or available only through the SoC API (see relevant SDS documentation).
|
||||
* - Interrupts will not arrive from protected peripherals or interrupts.
|
||||
* - nrf_nvic_ functions must be used instead of CMSIS NVIC_ functions for reliable usage of the SoftDevice.
|
||||
* - Interrupt latency may be affected by the SoftDevice (see relevant SDS documentation).
|
||||
* - Chosen low frequency clock source will be running.
|
||||
*
|
||||
* @param p_clock_lf_cfg Low frequency clock source and accuracy.
|
||||
If NULL the clock will be configured as an RC source with rc_ctiv = 16 and .rc_temp_ctiv = 2
|
||||
In the case of XTAL source, the PPM accuracy of the chosen clock source must be greater than or equal to
|
||||
the actual characteristics of your XTAL clock.
|
||||
* @param fault_handler Callback to be invoked in case of fault, cannot be NULL.
|
||||
*
|
||||
* @retval ::NRF_SUCCESS
|
||||
* @retval ::NRF_ERROR_INVALID_ADDR Invalid or NULL pointer supplied.
|
||||
* @retval ::NRF_ERROR_INVALID_STATE SoftDevice is already enabled, and the clock source and fault handler cannot be updated.
|
||||
* @retval ::NRF_ERROR_SDM_INCORRECT_INTERRUPT_CONFIGURATION SoftDevice interrupt is already enabled, or an enabled interrupt has
|
||||
an illegal priority level.
|
||||
* @retval ::NRF_ERROR_SDM_LFCLK_SOURCE_UNKNOWN Unknown low frequency clock source selected.
|
||||
* @retval ::NRF_ERROR_INVALID_PARAM Invalid clock source configuration supplied in p_clock_lf_cfg.
|
||||
*/
|
||||
SVCALL(SD_SOFTDEVICE_ENABLE, uint32_t,
|
||||
sd_softdevice_enable(nrf_clock_lf_cfg_t const *p_clock_lf_cfg, nrf_fault_handler_t fault_handler));
|
||||
|
||||
/**@brief Disables the SoftDevice and by extension the protocol stack.
|
||||
*
|
||||
* Idempotent function to disable the SoftDevice.
|
||||
*
|
||||
* @post SoC library and protocol stack APIs are made unavailable.
|
||||
* @post All interrupts that was protected by the SoftDevice will be disabled and initialized to priority 0 (highest).
|
||||
* @post All peripherals used by the SoftDevice will be reset to default values.
|
||||
* @post All of RAM become available.
|
||||
* @post All interrupts are forwarded to the application.
|
||||
* @post LFCLK source chosen in ::sd_softdevice_enable will be left running.
|
||||
*
|
||||
* @retval ::NRF_SUCCESS
|
||||
*/
|
||||
SVCALL(SD_SOFTDEVICE_DISABLE, uint32_t, sd_softdevice_disable(void));
|
||||
|
||||
/**@brief Check if the SoftDevice is enabled.
|
||||
*
|
||||
* @param[out] p_softdevice_enabled If the SoftDevice is enabled: 1 else 0.
|
||||
*
|
||||
* @retval ::NRF_SUCCESS
|
||||
*/
|
||||
SVCALL(SD_SOFTDEVICE_IS_ENABLED, uint32_t, sd_softdevice_is_enabled(uint8_t *p_softdevice_enabled));
|
||||
|
||||
/**@brief Sets the base address of the interrupt vector table for interrupts forwarded from the SoftDevice
|
||||
*
|
||||
* This function is only intended to be called when a bootloader is enabled.
|
||||
*
|
||||
* @param[in] address The base address of the interrupt vector table for forwarded interrupts.
|
||||
|
||||
* @retval ::NRF_SUCCESS
|
||||
*/
|
||||
SVCALL(SD_SOFTDEVICE_VECTOR_TABLE_BASE_SET, uint32_t, sd_softdevice_vector_table_base_set(uint32_t address));
|
||||
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // NRF_SDM_H__
|
||||
|
||||
/**
|
||||
@}
|
||||
*/
|
||||
@@ -38,7 +38,7 @@ void powerCommandsCheck()
|
||||
|
||||
#if defined(ARCH_ESP32) || defined(ARCH_NRF52)
|
||||
if (shutdownAtMsec) {
|
||||
screen->startShutdownScreen();
|
||||
screen->startAlert("Shutting down...");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user