This commit is contained in:
Joshua Pirihi
2022-03-06 05:56:34 +13:00
124 changed files with 3123 additions and 2377 deletions

76
.github/ISSUE_TEMPLATE/Bug Report.yml vendored Normal file
View File

@@ -0,0 +1,76 @@
name: Bug Report
description: File a bug report
title: "[Bug]: "
labels: ["bug", "triage"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: dropdown
id: category
attributes:
label: Category
description: How would you catagorize this issue?
multiple: true
options:
- Hardware Compatibility
- BLE
- Serial
- WiFi
- Other
validations:
required: true
- type: dropdown
id: hardware
attributes:
label: Hardware
description: What hardware are you encountering this issue on?
multiple: true
options:
- Not Applicable
- T-Beam
- T-Beam 0.7
- T-Lora v1
- T-Lora v1.3
- T-Lora v2 1.6
- T-Echo
- Rak4631
- Rak11200
- Heltec v1
- Heltec v2
- Heltec v2.1
- Relay v1
- Relay v2
- DIY
- Other
validations:
required: true
- type: input
id: version
attributes:
label: Firmware Version
description: This can be found on the device's screen or via one of the apps.
placeholder: x.x.x.yyyyyyy
validations:
required: true
- type: textarea
id: body
attributes:
label: Description
description: Please provide details on what steps you performed for this to happen.
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: If you have any log output to help in diagnosing your bug, please provide it here.
render: Shell
validations:
required: false

View File

@@ -1,38 +0,0 @@
---
name: Bug report or feature proposal
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
Please - if you just have a question (i.e. not a bug report or a feature proposal), post in our [forum](https://meshtastic.discourse.group/) instead.
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Device info:**
- Device model: [e.g. TBEAM]
- Software Version [e.g. 0.7.8]
**Smartphone information (if relevant):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- App Version [e.g. 0.7.2]
**Additional context**
Add any other context about the problem here.

View File

@@ -1,39 +0,0 @@
name: 'Common init'
# WARNING due to https://github.com/actions/runner/issues/646
# this code can't work - must copy and paste into workflows for now because 'uses' is not supported
runs:
using: "composite"
steps:
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.x
- name: Cache python libs
uses: actions/cache@v1
id: cache-pip # needed in if test
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip
- name: Upgrade python tools
# We actually want to run this every time
# if: steps.cache-pip.outputs.cache-hit != 'true'
run: |
python -m pip install --upgrade pip
pip install -U platformio meshtastic adafruit-nrfutil
# Don't cache for now because I want to be lazy with portuino updates githashes
# - name: Cache platformio
# uses: actions/cache@v1
# id: cache-platformio # needed in if test
# with:
# path: ~/.platformio
# key: ${{ runner.os }}-platformio
- name: Upgrade platformio
run: |
pio upgrade

View File

@@ -37,6 +37,7 @@ jobs:
- board: meshtastic-diy-v1
- board: rak4631_5005
- board: rak4631_19003
- board: rak4631_5005_eink
- board: t-echo
runs-on: ubuntu-latest
@@ -162,6 +163,7 @@ jobs:
include:
- board: rak4631_5005
- board: rak4631_19003
- board: rak4631_5005_eink
- board: t-echo
runs-on: ubuntu-latest

View File

@@ -37,7 +37,7 @@ jobs:
# if: steps.cache-pip.outputs.cache-hit != 'true'
run: |
python -m pip install --upgrade pip
pip install -U platformio meshtastic adafruit-nrfutil
pip install -U platformio meshtastic adafruit-nrfutil littlefs-python
- name: Upgrade platformio
run: |

View File

@@ -4,5 +4,8 @@
"recommendations": [
"platformio.platformio-ide",
"xaver.clang-format"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

72
README-docker.md Normal file
View File

@@ -0,0 +1,72 @@
## What is Docker used for
Developers can simulate Device hardware by compiling and running
a linux native binary application. If you do not own a Linux
machine, or you just want to separate things, you might want
to run simulator inside a docker container
## The Image
To build docker image, type
`docker build -t meshtastic/device .`
## Usage
To run a container, type
`docker run --rm -p 4403:4403 meshtastic/device`
or, to get an interactive shell on the docker created container:
`docker run -it -p 4403:4403 meshtastic/device bash`
You might want to mount your local development folder:
`docker run -it --mount type=bind,source=/PathToMyProjects/Meshtastic/Meshtastic-device-mybranch,target=/Meshtastic-device-mybranch -p 4403:4403 meshtastic/device bash`
## Build the native application
Linux native application should be built inside the container.
For this you must run container with interactive console
"-it", as seen above.
First, some environment variables need to be set up with command:
`. ~/.platformio/penv/bin/activate`
You also want to make some adjustments in the bin/build-all.sh to conform the amd64 build:
```
sed -i 's/^BOARDS_ESP32.*/BOARDS_ESP32=""/' bin/build-all.sh
sed -i 's/^BOARDS_NRF52.*/BOARDS_NRF52=""/' bin/build-all.sh
sed -i 's/echo "Building SPIFFS.*/exit/' bin/build-all.sh
```
You can build amd64 image with command
`bin/build-all.sh`
## Executing the application interactively
The built binary file should be found under name
`release/latest/bins/universal/meshtastic_linux_amd64`.
If this is not the case, you can also use direct program name:
`.pio/build/native/program`
To use python cli against exposed port 4403,
type this in the host machine:
`meshtastic --info --host localhost`
## Stop the container
Run this to get the ID:
`docker ps`
Stop the container with command:
`docker kill <id>`
> Tip: you can just use the first few characters of the ID in docker commands

View File

@@ -7,8 +7,12 @@
Update Instructions
[Using Meshtastic Flasher](https://meshtastic.org/docs/getting-started/meshtastic-flasher)
Manual Method
[For ESP32 devices click here](https://meshtastic.org/docs/getting-started/flashing-esp32)
[For nRF52 devices click here](https://meshtastic.org/docs/getting-started/flashing-nrf52)
For developer information and specific building instructions, please see the [developer doccumentation](https://meshtastic.org/docs/developers)
For developer information and specific building instructions, please see the [developer documentation](https://meshtastic.org/docs/developers)

View File

@@ -9,7 +9,7 @@ BOARDS_ESP32="rak11200 tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-
#BOARDS_ESP32=tbeam
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
BOARDS_NRF52="rak4631_5005 rak4631_19003 t-echo"
BOARDS_NRF52="rak4631_5005 rak4631_5005_eink rak4631_19003 t-echo"
#BOARDS_NRF52=""
OUTDIR=release/latest

View File

@@ -8,6 +8,10 @@ from readprops import readProps
Import("env")
env.Replace( MKSPIFFSTOOL=env.get("PROJECT_DIR") + '/bin/mklittlefs.py' )
try:
import littlefs
except ImportError:
env.Execute("$PYTHONEXE -m pip install littlefs-python")
Import("projenv")

View File

@@ -1,17 +0,0 @@
To build:
docker build -t meshtastic/device .
To run:
docker run --rm -p 4403:4403 meshtastic/device
or, to get a shell on the docker image:
docker run -it meshtastic/device bash
To use python cli against it:
meshtastic --info --host localhost
To stop:
# run this to get id
docker ps
# tip: you can just use the first few characters of the id in the next command
docker kill <id>

View File

@@ -63,7 +63,7 @@ You probably don't care about this section - skip to the next one.
## Multichannel support
* DONE cleanup the external notification and serial plugins
* DONE cleanup the external notification and serial modules
* non ack version of stress test fails sometimes!
* tx fault test has a bug #734 - * turn off fault 8: https://github.com/meshtastic/Meshtastic-device/issues/734
* DONE move device types into an enum in nodeinfo
@@ -71,7 +71,7 @@ You probably don't care about this section - skip to the next one.
* nrf52 should preserve local time across reset
* cdcacm bug on nrf52: emittx thinks it emitted but client sees nothing. works again later
* nrf52: segger logs have errors in formatting that should be impossible (because not going through serial, try stalling on segger)
* DONE call RouterPlugin for *all* packets - not just Router packets
* DONE call RouterModule for *all* packets - not just Router packets
* DONE generate channel hash from the name of the channel+the psk (not just one or the other)
* DONE send a hint that can be used to select which channel to try and hash against with each message
* DONE remove deprecated
@@ -79,13 +79,13 @@ You probably don't care about this section - skip to the next one.
* DONE set mynodeinfo.max_channels
* DONE set mynodeinfo.num_bands (formerly num_channels)
* DONE fix sniffing of non Routing packets
* DONE enable remote setttings access by moving settings operations into a regular plugin (move settings ops out of PhoneAPI)
* DONE enable remote setttings access by moving settings operations into a regular module (move settings ops out of PhoneAPI)
* DONE move portnum up?
* DONE remove region specific builds from the firmware
* DONE test single channel without python
* DONE Use "default" for name if name is empty
* DONE fix python data packet receiving (nothing showing in log?)
* DONE implement 'get channels' Admin plugin operation
* DONE implement 'get channels' Admin module operation
* DONE use get-channels from python
* DONE use get channels & get settings from android
* DONE use set-channel from python
@@ -98,7 +98,7 @@ You probably don't care about this section - skip to the next one.
* DONE fix setch-fast in python tool
* age out pendingrequests in the python API
* DONE stress test channel download from python, sometimes it seems like we don't get all replies, bug was due to simultaneous android connection
* DONE combine acks and responses in a single message if possible (do routing plugin LAST and drop ACK if someone else has already replied)
* DONE combine acks and responses in a single message if possible (do routing module LAST and drop ACK if someone else has already replied)
* DONE don't send packets we received from the phone BACK TOWARDS THE PHONE (possibly use fromnode 0 for packets the phone sends?)
* DONE fix 1.1.50 android debug panel display
* DONE test android channel setting
@@ -118,7 +118,7 @@ You probably don't care about this section - skip to the next one.
* use single byte 'well known' channel names for admin, gpio, etc...
* use presence of gpio channel to enable gpio ops, same for serial etc...
* DONE restrict gpio & serial & settings operations to the admin channel (unless local to the current node)
* DONE add channel restrictions for plugins (and restrict routing plugin to the "control" channel)
* DONE add channel restrictions for modules (and restrict routing module to the "control" channel)
* stress test multi channel
* DONE investigate @mc-hamster report of heap corruption
* DONE use set-user from android
@@ -134,7 +134,7 @@ You probably don't care about this section - skip to the next one.
* allow chaning packets in single transmission - to increase airtime efficiency and amortize packet overhead
* DONE move most parts of meshpacket into the Data packet, so that we can chain multiple Data for sending when they all have a common destination and key.
* when selecting a MeshPacket for transmit, scan the TX queue for any Data packets we can merge together as a WirePayload. In the low level send/rx code expand that into multiple MeshPackets as needed (thus 'hiding' from MeshPacket that over the wire we send multiple datapackets
* DONE confirm we are still calling the plugins for messages inbound from the phone (or generated locally)
* DONE confirm we are still calling the modules for messages inbound from the phone (or generated locally)
* DONE confirm we are still multi hop routing flood broadcasts
* DONE confirm we are still doing resends on unicast reliable packets
* add history to routed packets: https://meshtastic.discourse.group/t/packet-source-tracking/2764/2
@@ -142,7 +142,7 @@ You probably don't care about this section - skip to the next one.
* DONE move acks into routing
* DONE make all subpackets different versions of data
* DONE move routing control into a data packet
* have phoneapi done via plugin (will allow multiple simultaneous API clients - stop disabling BLE while using phone API)
* have phoneapi done via module (will allow multiple simultaneous API clients - stop disabling BLE while using phone API)
* use reference counting and dynamic sizing for meshpackets. - use https://docs.microsoft.com/en-us/cpp/cpp/how-to-create-and-use-shared-ptr-instances?view=msvc-160 (already used in arduino)
* let multiple PhoneAPI endpoints work at once
* allow multiple simultaneous bluetooth connections (create the bluetooth phoneapi instance dynamically based on client id)
@@ -182,13 +182,13 @@ For app cleanup:
* DONE require a recent python api to talk to these new device loads
* DONE require a recent android app to talk to these new device loads
* DONE fix handleIncomingPosition
* DONE move want_replies handling into plugins
* DONE move want_replies handling into modules
* DONE on android for received positions handle either old or new positions / user messages
* DONE on android side send old or new positions as needed / user messages
* DONE test python side handle new position/user messages
* DONE make a gpio example. --gpiowrb 4 1, --gpiord 0x444, --gpiowatch 0x3ff
* DONE fix position sending to use new plugin
* DONE Add SinglePortNumPlugin - as the new most useful baseclass
* DONE fix position sending to use new module
* DONE Add SinglePortNumModule - as the new most useful baseclass
* DONE move positions into regular data packets (use new app framework)
* DONE move user info into regular data packets (use new app framework)
* DONE test that positions, text messages and user info still work

View File

@@ -23,8 +23,9 @@ default_envs = tbeam
;default_envs = t-echo
;default_envs = nrf52840dk-geeksville
;default_envs = native # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
;default_envs = rak4631
;default_envs = rak4630
;default_envs = rak4631_5005
;default_envs = rak4631_5005_eink
;default_envs = rak4631_19003
;default_envs = meshtastic-diy-v1
;default_envs = meshtastic-diy-v1.1
@@ -195,7 +196,7 @@ build_flags =
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.7
;-DCFG_DEBUG=3
src_filter =
${arduino_base.src_filter} -<esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/> -<plugins/esp32> -<mqtt/>
${arduino_base.src_filter} -<esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mqtt/>
lib_ignore =
BluetoothOTA
; monitor_port = /dev/ttyACM1

2
proto

Submodule proto updated: 2930129e8e...f6ba3722be

220
src/ButtonThread.h Normal file
View File

@@ -0,0 +1,220 @@
#include "configuration.h"
#include "concurrency/OSThread.h"
#include "PowerFSM.h"
#include "RadioLibInterface.h"
#include "graphics/Screen.h"
#include "power.h"
#include "buzz.h"
#include <OneButton.h>
#ifndef NO_ESP32
#include "nimble/BluetoothUtil.h"
#endif
namespace concurrency
{
/**
* Watch a GPIO and if we get an IRQ, wake the main thread.
* Use to add wake on button press
*/
void wakeOnIrq(int irq, int mode)
{
attachInterrupt(
irq,
[] {
BaseType_t higherWake = 0;
mainDelay.interruptFromISR(&higherWake);
},
FALLING);
}
class ButtonThread : public concurrency::OSThread
{
// Prepare for button presses
#ifdef BUTTON_PIN
OneButton userButton;
#endif
#ifdef BUTTON_PIN_ALT
OneButton userButtonAlt;
#endif
#ifdef BUTTON_PIN_TOUCH
OneButton userButtonTouch;
#endif
static bool shutdown_on_long_stop;
public:
static uint32_t longPressTime;
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
ButtonThread() : OSThread("Button")
{
#ifdef BUTTON_PIN
userButton = OneButton(BUTTON_PIN, true, true);
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(BUTTON_PIN, INPUT_PULLUP_SENSE);
#endif
userButton.attachClick(userButtonPressed);
userButton.attachDuringLongPress(userButtonPressedLong);
userButton.attachDoubleClick(userButtonDoublePressed);
userButton.attachMultiClick(userButtonMultiPressed);
userButton.attachLongPressStart(userButtonPressedLongStart);
userButton.attachLongPressStop(userButtonPressedLongStop);
wakeOnIrq(BUTTON_PIN, FALLING);
#endif
#ifdef BUTTON_PIN_ALT
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(BUTTON_PIN_ALT, INPUT_PULLUP_SENSE);
#endif
userButtonAlt.attachClick(userButtonPressed);
userButtonAlt.attachDuringLongPress(userButtonPressedLong);
userButtonAlt.attachDoubleClick(userButtonDoublePressed);
userButtonAlt.attachLongPressStart(userButtonPressedLongStart);
userButtonAlt.attachLongPressStop(userButtonPressedLongStop);
wakeOnIrq(BUTTON_PIN_ALT, FALLING);
#endif
#ifdef BUTTON_PIN_TOUCH
userButtonTouch = OneButton(BUTTON_PIN_TOUCH, true, true);
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(BUTTON_PIN_TOUCH, INPUT_PULLUP_SENSE);
#endif
userButtonTouch.attachClick(touchPressed);
userButtonTouch.attachDuringLongPress(touchPressedLong);
userButtonTouch.attachDoubleClick(touchDoublePressed);
userButtonTouch.attachLongPressStart(touchPressedLongStart);
userButtonTouch.attachLongPressStop(touchPressedLongStop);
wakeOnIrq(BUTTON_PIN_TOUCH, FALLING);
#endif
}
protected:
/// If the button is pressed we suppress CPU sleep until release
int32_t runOnce() override
{
canSleep = true; // Assume we should not keep the board awake
#ifdef BUTTON_PIN
userButton.tick();
canSleep &= userButton.isIdle();
#endif
#ifdef BUTTON_PIN_ALT
userButtonAlt.tick();
canSleep &= userButtonAlt.isIdle();
#endif
#ifdef BUTTON_PIN_TOUCH
userButtonTouch.tick();
canSleep &= userButtonTouch.isIdle();
#endif
// if (!canSleep) DEBUG_MSG("Supressing sleep!\n");
// else DEBUG_MSG("sleep ok\n");
return 5;
}
private:
static void touchPressed()
{
screen->forceDisplay();
DEBUG_MSG("touch press!\n");
}
static void touchDoublePressed()
{
DEBUG_MSG("touch double press!\n");
}
static void touchPressedLong()
{
DEBUG_MSG("touch press long!\n");
}
static void touchDoublePressedLong()
{
DEBUG_MSG("touch double pressed!\n");
}
static void touchPressedLongStart()
{
DEBUG_MSG("touch long press start!\n");
}
static void touchPressedLongStop()
{
DEBUG_MSG("touch long press stop!\n");
}
static void userButtonPressed()
{
// DEBUG_MSG("press!\n");
powerFSM.trigger(EVENT_PRESS);
}
static void userButtonPressedLong()
{
// DEBUG_MSG("Long press!\n");
#ifndef NRF52_SERIES
screen->adjustBrightness();
#endif
// If user button is held down for 5 seconds, shutdown the device.
if (millis() - longPressTime > 5 * 1000) {
#ifdef TBEAM_V10
if (axp192_found == true) {
setLed(false);
power->shutdown();
}
#elif NRF52_SERIES
// Do actual shutdown when button released, otherwise the button release
// may wake the board immediatedly.
if (!shutdown_on_long_stop) {
screen->startShutdownScreen();
DEBUG_MSG("Shutdown from long press");
playBeep();
ledOff(PIN_LED1);
ledOff(PIN_LED2);
shutdown_on_long_stop = true;
}
#endif
} else {
// DEBUG_MSG("Long press %u\n", (millis() - longPressTime));
}
}
static void userButtonDoublePressed()
{
#ifndef NO_ESP32
disablePin();
#elif defined(HAS_EINK)
digitalWrite(PIN_EINK_EN,digitalRead(PIN_EINK_EN) == LOW);
#endif
}
static void userButtonMultiPressed()
{
#ifndef NO_ESP32
clearNVS();
#endif
#ifdef NRF52_SERIES
clearBonds();
#endif
}
static void userButtonPressedLongStart()
{
DEBUG_MSG("Long press start!\n");
longPressTime = millis();
}
static void userButtonPressedLongStop()
{
DEBUG_MSG("Long press stop!\n");
longPressTime = 0;
if (shutdown_on_long_stop) {
playShutdownMelody();
delay(3000);
power->shutdown();
}
}
};
}

38
src/PowerFSMThread.h Normal file
View File

@@ -0,0 +1,38 @@
#include "configuration.h"
#include "concurrency/OSThread.h"
#include "main.h"
#include "PowerFSM.h"
#include "power.h"
#include "NodeDB.h"
namespace concurrency
{
/// Wrapper to convert our powerFSM stuff into a 'thread'
class PowerFSMThread : public OSThread
{
public:
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
PowerFSMThread() : OSThread("PowerFSM") {}
protected:
int32_t runOnce() override
{
powerFSM.run_machine();
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake
/// cpu for serial rx - FIXME)
auto state = powerFSM.getState();
canSleep = (state != &statePOWER) && (state != &stateSERIAL);
if (powerStatus->getHasUSB()) {
timeLastPowered = millis();
} else if (radioConfig.preferences.on_battery_shutdown_after_secs > 0 &&
millis() > timeLastPowered + (1000 * radioConfig.preferences.on_battery_shutdown_after_secs)) { //shutdown after 30 minutes unpowered
powerFSM.trigger(EVENT_SHUTDOWN);
}
return 10;
}
};
}

View File

@@ -140,7 +140,7 @@ int32_t AirTime::runOnce()
}
// Init airtime windows to all 0
for (int i = 0; i < PERIODS_TO_LOG; i++) {
for (int i = 0; i < myNodeInfo.air_period_rx_count; i++) {
this->airtimes.periodTX[i] = 0;
this->airtimes.periodRX[i] = 0;
this->airtimes.periodRX_ALL[i] = 0;

View File

@@ -26,7 +26,7 @@
#define CHANNEL_UTILIZATION_PERIODS 6
#define SECONDS_PER_PERIOD 3600
#define PERIODS_TO_LOG 24
#define PERIODS_TO_LOG 8
#define MINUTES_IN_HOUR 60
#define SECONDS_IN_MINUTE 60
#define MS_IN_HOUR (MINUTES_IN_HOUR * SECONDS_IN_MINUTE * 1000)

19
src/debug/axpDebug.h Normal file
View File

@@ -0,0 +1,19 @@
#if 0
// Turn off for now
uint32_t axpDebugRead()
{
axp.debugCharging();
DEBUG_MSG("vbus current %f\n", axp.getVbusCurrent());
DEBUG_MSG("charge current %f\n", axp.getBattChargeCurrent());
DEBUG_MSG("bat voltage %f\n", axp.getBattVoltage());
DEBUG_MSG("batt pct %d\n", axp.getBattPercentage());
DEBUG_MSG("is battery connected %d\n", axp.isBatteryConnect());
DEBUG_MSG("is USB connected %d\n", axp.isVBUSPlug());
DEBUG_MSG("is charging %d\n", axp.isChargeing());
return 30 * 1000;
}
Periodic axpDebugOutput(axpDebugRead);
axpDebugOutput.setup();
#endif

44
src/debug/i2cScan.h Normal file
View File

@@ -0,0 +1,44 @@
#include "../configuration.h"
#include "../main.h"
#include <Wire.h>
#ifndef NO_WIRE
void scanI2Cdevice(void)
{
byte err, addr;
int nDevices = 0;
for (addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
err = Wire.endTransmission();
if (err == 0) {
DEBUG_MSG("I2C device found at address 0x%x\n", addr);
nDevices++;
if (addr == SSD1306_ADDRESS) {
screen_found = addr;
DEBUG_MSG("ssd1306 display found\n");
}
if (addr == ST7567_ADDRESS) {
screen_found = addr;
DEBUG_MSG("st7567 display found\n");
}
#ifdef AXP192_SLAVE_ADDRESS
if (addr == AXP192_SLAVE_ADDRESS) {
axp192_found = true;
DEBUG_MSG("axp192 PMU found\n");
}
#endif
} else if (err == 4) {
DEBUG_MSG("Unknow error at address 0x%x\n", addr);
}
}
if (nDevices == 0)
DEBUG_MSG("No I2C devices found\n");
else
DEBUG_MSG("done\n");
}
#else
void scanI2Cdevice(void) {}
#endif

View File

@@ -9,14 +9,45 @@
#define COLORED GxEPD_BLACK
#define UNCOLORED GxEPD_WHITE
#if defined(TTGO_T_ECHO)
#define TECHO_DISPLAY_MODEL GxEPD2_154_D67
#elif defined(RAK4630)
//GxEPD2_213_B74 - RAK14000 2.13 inch b/w 250x128
#define TECHO_DISPLAY_MODEL GxEPD2_213_B74
//4.2 inch 300x400 - GxEPD2_420_M01
//#define TECHO_DISPLAY_MODEL GxEPD2_420_M01
//2.9 inch 296x128 - GxEPD2_290_T5D
//#define TECHO_DISPLAY_MODEL GxEPD2_290_T5D
//1.54 inch 200x200 - GxEPD2_154_M09
//#define TECHO_DISPLAY_MODEL GxEPD2_154_M09
#endif
GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT> *adafruitDisplay;
EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl)
{
#if defined(TTGO_T_ECHO)
setGeometry(GEOMETRY_RAWMODE, TECHO_DISPLAY_MODEL::WIDTH, TECHO_DISPLAY_MODEL::HEIGHT);
#elif defined(RAK4630)
//GxEPD2_213_B74 - RAK14000 2.13 inch b/w 250x128
setGeometry(GEOMETRY_RAWMODE, 250, 122);
//GxEPD2_420_M01
//setGeometry(GEOMETRY_RAWMODE, 300, 400);
//GxEPD2_290_T5D
//setGeometry(GEOMETRY_RAWMODE, 296, 128);
//GxEPD2_154_M09
//setGeometry(GEOMETRY_RAWMODE, 200, 200);
#endif
// setGeometry(GEOMETRY_RAWMODE, 128, 64); // old resolution
// setGeometry(GEOMETRY_128_64); // We originally used this because I wasn't sure if rawmode worked - it does
}
@@ -40,8 +71,8 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
// tft.drawBitmap(0, 0, buffer, 128, 64, TFT_YELLOW, TFT_BLACK);
for (uint8_t y = 0; y < displayHeight; y++) {
for (uint8_t x = 0; x < displayWidth; x++) {
for (uint64_t y = 0; y < displayHeight; y++) {
for (uint64_t x = 0; x < displayWidth; x++) {
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
auto b = buffer[x + (y / 8) * displayWidth];
@@ -50,9 +81,28 @@ bool EInkDisplay::forceDisplay(uint32_t msecLimit)
}
}
DEBUG_MSG("Updating eink... ");
#if defined(TTGO_T_ECHO)
DEBUG_MSG("Updating T-ECHO E-Paper... ");
#elif defined(RAK4630)
DEBUG_MSG("Updating RAK4361_5005 E-Paper... ");
#endif
#if defined(TTGO_T_ECHO)
// ePaper.Reset(); // wake the screen from sleep
adafruitDisplay->display(false); // FIXME, use partial update mode
#elif defined(RAK4630)
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
adafruitDisplay->display(false); // FIXME, use partial update mode
//Only enable for e-Paper with support for partial updates and comment out above adafruitDisplay->display(false);
// 1.54 inch 200x200 - GxEPD2_154_M09
// 2.9 inch 296x128 - GxEPD2_290_T5D
// 4.2 inch 300x400 - GxEPD2_420_M01
//adafruitDisplay->nextPage();
#endif
// Put screen to sleep to save power (possibly not necessary because we already did poweroff inside of display)
adafruitDisplay->hibernate();
DEBUG_MSG("done\n");
@@ -97,14 +147,33 @@ bool EInkDisplay::connect()
pinMode(PIN_EINK_EN, OUTPUT);
#endif
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS,
PIN_EINK_DC,
PIN_EINK_RES,
PIN_EINK_BUSY, SPI1);
#if defined(TTGO_T_ECHO)
{
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY, SPI1);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init();
adafruitDisplay->setRotation(3);
}
#elif defined(RAK4630)
{
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
adafruitDisplay->setRotation(3);
//For 1.54, 2.9 and 4.2
//adafruitDisplay->setRotation(1);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
}
#endif
//adafruitDisplay->setFullWindow();
//adafruitDisplay->fillScreen(UNCOLORED);
//adafruitDisplay->drawCircle(100, 100, 20, COLORED);

View File

@@ -32,7 +32,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "main.h"
#include "mesh-pb-constants.h"
#include "mesh/Channels.h"
#include "plugins/TextMessagePlugin.h"
#include "modules/TextMessageModule.h"
#include "sleep.h"
#include "target_specific.h"
#include "utils.h"
@@ -67,9 +67,9 @@ uint8_t imgBattery[16] = {0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
// Threshold values for the GPS lock accuracy bar display
uint32_t dopThresholds[5] = {2000, 1000, 500, 200, 100};
// At some point, we're going to ask all of the plugins if they would like to display a screen frame
// we'll need to hold onto pointers for the plugins that can draw a frame.
std::vector<MeshPlugin *> pluginFrames;
// At some point, we're going to ask all of the modules if they would like to display a screen frame
// we'll need to hold onto pointers for the modules that can draw a frame.
std::vector<MeshPlugin *> moduleFrames;
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
static char ourId[5];
@@ -176,9 +176,9 @@ static void drawSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int
}
#endif
static void drawPluginFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
uint8_t plugin_frame;
uint8_t module_frame;
// there's a little but in the UI transition code
// where it invokes the function at the correct offset
// in the array of "drawScreen" functions; however,
@@ -187,14 +187,14 @@ static void drawPluginFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
if (state->frameState == IN_TRANSITION && state->transitionFrameRelationship == 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`
plugin_frame = state->transitionFrameTarget;
module_frame = state->transitionFrameTarget;
} else {
// otherwise, just display the plugin frame that's aligned with the current frame
plugin_frame = state->currentFrame;
// DEBUG_MSG("Screen is not in transition. Frame: %d\n\n", plugin_frame);
// otherwise, just display the module frame that's aligned with the current frame
module_frame = state->currentFrame;
// DEBUG_MSG("Screen is not in transition. Frame: %d\n\n", module_frame);
}
// DEBUG_MSG("Drawing Plugin Frame %d\n\n", plugin_frame);
MeshPlugin &pi = *pluginFrames.at(plugin_frame);
// DEBUG_MSG("Drawing Module Frame %d\n\n", module_frame);
MeshPlugin &pi = *moduleFrames.at(module_frame);
pi.drawFrame(display, state, x, y);
}
@@ -259,11 +259,11 @@ static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *sta
display->drawString(0 + x, FONT_HEIGHT_MEDIUM + y, "For help, please post on\nmeshtastic.discourse.group");
}
// Ignore messages orginating from phone (from the current node 0x0) unless range test or store and forward plugin are enabled
// Ignore messages orginating from phone (from the current node 0x0) unless range test or store and forward module are enabled
static bool shouldDrawMessage(const MeshPacket *packet)
{
return packet->from != 0 && !radioConfig.preferences.range_test_plugin_enabled &&
!radioConfig.preferences.store_forward_plugin_enabled;
return packet->from != 0 && !radioConfig.preferences.range_test_module_enabled &&
!radioConfig.preferences.store_forward_module_enabled;
}
/// Draw the last text message we received
@@ -824,10 +824,10 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
if (textMessagePlugin)
textMessageObserver.observe(textMessagePlugin);
if (textMessageModule)
textMessageObserver.observe(textMessageModule);
// Plugins can notify screen about refresh
// Modules can notify screen about refresh
MeshPlugin::observeUIEvents(&uiFrameEventObserver);
}
@@ -976,9 +976,9 @@ void Screen::setFrames()
DEBUG_MSG("showing standard frames\n");
showingNormalScreen = true;
pluginFrames = MeshPlugin::GetMeshPluginsWithUIFrames();
DEBUG_MSG("Showing %d plugin frames\n", pluginFrames.size());
int totalFrameCount = MAX_NUM_NODES + NUM_EXTRA_FRAMES + pluginFrames.size();
moduleFrames = MeshPlugin::GetMeshModulesWithUIFrames();
DEBUG_MSG("Showing %d module frames\n", moduleFrames.size());
int totalFrameCount = MAX_NUM_NODES + NUM_EXTRA_FRAMES + moduleFrames.size();
DEBUG_MSG("Total frame count: %d\n", totalFrameCount);
// We don't show the node info our our node (if we have it yet - we should)
@@ -988,23 +988,23 @@ void Screen::setFrames()
size_t numframes = 0;
// put all of the plugin frames first.
// put all of the module frames first.
// this is a little bit of a dirty hack; since we're going to call
// the same drawPluginFrame handler here for all of these plugin frames
// the same drawModuleFrame handler here for all of these module frames
// and then we'll just assume that the state->currentFrame value
// is the same offset into the pluginFrames vector
// so that we can invoke the plugin's callback
for (auto i = pluginFrames.begin(); i != pluginFrames.end(); ++i) {
normalFrames[numframes++] = drawPluginFrame;
// is the same offset into the moduleFrames vector
// so that we can invoke the module's callback
for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) {
normalFrames[numframes++] = drawModuleFrame;
}
DEBUG_MSG("Added plugins. numframes: %d\n", numframes);
DEBUG_MSG("Added modules. numframes: %d\n", numframes);
// If we have a critical fault, show it first
if (myNodeInfo.error_code)
normalFrames[numframes++] = drawCriticalFaultFrame;
// If we have a text message - show it next, unless it's a phone message and we aren't using any special plugins
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
normalFrames[numframes++] = drawTextMessageFrame;
}

View File

@@ -27,7 +27,7 @@ void RotaryEncoderInterruptImpl1::init()
char eventPressed =
static_cast<char>(radioConfig.preferences.rotary1_event_press);
//radioConfig.preferences.ext_notification_plugin_output
//radioConfig.preferences.ext_notification_module_output
RotaryEncoderInterruptBase::init(
pinA, pinB, pinPress,
eventCw, eventCcw, eventPressed,

View File

@@ -18,10 +18,12 @@
#include "concurrency/Periodic.h"
#include "graphics/Screen.h"
#include "main.h"
#include "plugins/Plugins.h"
#include "modules/Modules.h"
#include "sleep.h"
#include "shutdown.h"
#include "target_specific.h"
#include <OneButton.h>
#include "debug/i2cScan.h"
#include "debug/axpDebug.h"
#include <Wire.h>
// #include <driver/rtc_io.h>
@@ -48,6 +50,8 @@
#include "SX1268Interface.h"
#include "LLCC68Interface.h"
#include "ButtonThread.h"
#include "PowerFSMThread.h"
using namespace concurrency;
@@ -70,50 +74,6 @@ bool axp192_found;
Router *router = NULL; // Users of router don't care what sort of subclass implements that API
// -----------------------------------------------------------------------------
// Application
// -----------------------------------------------------------------------------
#ifndef NO_WIRE
void scanI2Cdevice(void)
{
byte err, addr;
int nDevices = 0;
for (addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
err = Wire.endTransmission();
if (err == 0) {
DEBUG_MSG("I2C device found at address 0x%x\n", addr);
nDevices++;
if (addr == SSD1306_ADDRESS) {
screen_found = addr;
DEBUG_MSG("ssd1306 display found\n");
}
if (addr == ST7567_ADDRESS) {
screen_found = addr;
DEBUG_MSG("st7567 display found\n");
}
#ifdef AXP192_SLAVE_ADDRESS
if (addr == AXP192_SLAVE_ADDRESS) {
axp192_found = true;
DEBUG_MSG("axp192 PMU found\n");
}
#endif
} else if (err == 4) {
DEBUG_MSG("Unknow error at address 0x%x\n", addr);
}
}
if (nDevices == 0)
DEBUG_MSG("No I2C devices found\n");
else
DEBUG_MSG("done\n");
}
#else
void scanI2Cdevice(void) {}
#endif
const char *getDeviceName()
{
uint8_t dmac[6];
@@ -139,238 +99,6 @@ static int32_t ledBlinker()
uint32_t timeLastPowered = 0;
/// Wrapper to convert our powerFSM stuff into a 'thread'
class PowerFSMThread : public OSThread
{
public:
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
PowerFSMThread() : OSThread("PowerFSM") {}
protected:
int32_t runOnce() override
{
powerFSM.run_machine();
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake
/// cpu for serial rx - FIXME)
auto state = powerFSM.getState();
canSleep = (state != &statePOWER) && (state != &stateSERIAL);
if (powerStatus->getHasUSB()) {
timeLastPowered = millis();
} else if (radioConfig.preferences.on_battery_shutdown_after_secs > 0 &&
millis() > timeLastPowered + (1000 * radioConfig.preferences.on_battery_shutdown_after_secs)) { //shutdown after 30 minutes unpowered
powerFSM.trigger(EVENT_SHUTDOWN);
}
return 10;
}
};
/**
* Watch a GPIO and if we get an IRQ, wake the main thread.
* Use to add wake on button press
*/
void wakeOnIrq(int irq, int mode)
{
attachInterrupt(
irq,
[] {
BaseType_t higherWake = 0;
mainDelay.interruptFromISR(&higherWake);
},
FALLING);
}
class ButtonThread : public OSThread
{
// Prepare for button presses
#ifdef BUTTON_PIN
OneButton userButton;
#endif
#ifdef BUTTON_PIN_ALT
OneButton userButtonAlt;
#endif
#ifdef BUTTON_PIN_TOUCH
OneButton userButtonTouch;
#endif
static bool shutdown_on_long_stop;
public:
static uint32_t longPressTime;
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
ButtonThread() : OSThread("Button")
{
#ifdef BUTTON_PIN
userButton = OneButton(BUTTON_PIN, true, true);
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(BUTTON_PIN, INPUT_PULLUP_SENSE);
#endif
userButton.attachClick(userButtonPressed);
userButton.attachDuringLongPress(userButtonPressedLong);
userButton.attachDoubleClick(userButtonDoublePressed);
userButton.attachMultiClick(userButtonMultiPressed);
userButton.attachLongPressStart(userButtonPressedLongStart);
userButton.attachLongPressStop(userButtonPressedLongStop);
wakeOnIrq(BUTTON_PIN, FALLING);
#endif
#ifdef BUTTON_PIN_ALT
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(BUTTON_PIN_ALT, INPUT_PULLUP_SENSE);
#endif
userButtonAlt.attachClick(userButtonPressed);
userButtonAlt.attachDuringLongPress(userButtonPressedLong);
userButtonAlt.attachDoubleClick(userButtonDoublePressed);
userButtonAlt.attachLongPressStart(userButtonPressedLongStart);
userButtonAlt.attachLongPressStop(userButtonPressedLongStop);
wakeOnIrq(BUTTON_PIN_ALT, FALLING);
#endif
#ifdef BUTTON_PIN_TOUCH
userButtonTouch = OneButton(BUTTON_PIN_TOUCH, true, true);
#ifdef INPUT_PULLUP_SENSE
// Some platforms (nrf52) have a SENSE variant which allows wake from sleep - override what OneButton did
pinMode(BUTTON_PIN_TOUCH, INPUT_PULLUP_SENSE);
#endif
userButtonTouch.attachClick(touchPressed);
userButtonTouch.attachDuringLongPress(touchPressedLong);
userButtonTouch.attachDoubleClick(touchDoublePressed);
userButtonTouch.attachLongPressStart(touchPressedLongStart);
userButtonTouch.attachLongPressStop(touchPressedLongStop);
wakeOnIrq(BUTTON_PIN_TOUCH, FALLING);
#endif
}
protected:
/// If the button is pressed we suppress CPU sleep until release
int32_t runOnce() override
{
canSleep = true; // Assume we should not keep the board awake
#ifdef BUTTON_PIN
userButton.tick();
canSleep &= userButton.isIdle();
#endif
#ifdef BUTTON_PIN_ALT
userButtonAlt.tick();
canSleep &= userButtonAlt.isIdle();
#endif
#ifdef BUTTON_PIN_TOUCH
userButtonTouch.tick();
canSleep &= userButtonTouch.isIdle();
#endif
// if (!canSleep) DEBUG_MSG("Supressing sleep!\n");
// else DEBUG_MSG("sleep ok\n");
return 5;
}
private:
static void touchPressed()
{
screen->forceDisplay();
DEBUG_MSG("touch press!\n");
}
static void touchDoublePressed()
{
DEBUG_MSG("touch double press!\n");
}
static void touchPressedLong()
{
DEBUG_MSG("touch press long!\n");
}
static void touchDoublePressedLong()
{
DEBUG_MSG("touch double pressed!\n");
}
static void touchPressedLongStart()
{
DEBUG_MSG("touch long press start!\n");
}
static void touchPressedLongStop()
{
DEBUG_MSG("touch long press stop!\n");
}
static void userButtonPressed()
{
// DEBUG_MSG("press!\n");
powerFSM.trigger(EVENT_PRESS);
}
static void userButtonPressedLong()
{
// DEBUG_MSG("Long press!\n");
#ifndef NRF52_SERIES
screen->adjustBrightness();
#endif
// If user button is held down for 5 seconds, shutdown the device.
if (millis() - longPressTime > 5 * 1000) {
#ifdef TBEAM_V10
if (axp192_found == true) {
setLed(false);
power->shutdown();
}
#elif NRF52_SERIES
// Do actual shutdown when button released, otherwise the button release
// may wake the board immediatedly.
if (!shutdown_on_long_stop) {
screen->startShutdownScreen();
DEBUG_MSG("Shutdown from long press");
playBeep();
ledOff(PIN_LED1);
ledOff(PIN_LED2);
shutdown_on_long_stop = true;
}
#endif
} else {
// DEBUG_MSG("Long press %u\n", (millis() - longPressTime));
}
}
static void userButtonDoublePressed()
{
#ifndef NO_ESP32
disablePin();
#elif defined(HAS_EINK)
digitalWrite(PIN_EINK_EN,digitalRead(PIN_EINK_EN) == LOW);
#endif
}
static void userButtonMultiPressed()
{
#ifndef NO_ESP32
clearNVS();
#endif
#ifdef NRF52_SERIES
clearBonds();
#endif
}
static void userButtonPressedLongStart()
{
DEBUG_MSG("Long press start!\n");
longPressTime = millis();
}
static void userButtonPressedLongStop()
{
DEBUG_MSG("Long press stop!\n");
longPressTime = 0;
if (shutdown_on_long_stop) {
playShutdownMelody();
delay(3000);
power->shutdown();
}
}
};
bool ButtonThread::shutdown_on_long_stop = false;
static Periodic *ledPeriodic;
@@ -516,22 +244,6 @@ void setup()
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
#ifdef GENIEBLOCKS
Im intentionally breaking your build so you see this note.Feel free to revert if not correct.I think you can
remove this GPS_RESET_N code by instead defining PIN_GPS_RESET and
use the shared code in GPS.cpp instead.- geeksville
// gps setup
pinMode(GPS_RESET_N, OUTPUT);
pinMode(GPS_EXTINT, OUTPUT);
digitalWrite(GPS_RESET_N, HIGH);
digitalWrite(GPS_EXTINT, LOW);
// battery setup
// If we want to read battery level, we need to set BATTERY_EN_PIN pin to low.
// ToDo: For low power consumption after read battery level, set that pin to high.
pinMode(BATTERY_EN_PIN, OUTPUT);
digitalWrite(BATTERY_EN_PIN, LOW);
#endif
gps = createGps();
if (gps)
@@ -543,8 +255,8 @@ void setup()
service.init();
// Now that the mesh service is created, create any plugins
setupPlugins();
// Now that the mesh service is created, create any modules
setupModules();
// Do this after service.init (because that clears error_code)
#ifdef AXP192_SLAVE_ADDRESS
@@ -686,66 +398,9 @@ void setup()
setCPUFast(false); // 80MHz is fine for our slow peripherals
}
#if 0
// Turn off for now
uint32_t axpDebugRead()
{
axp.debugCharging();
DEBUG_MSG("vbus current %f\n", axp.getVbusCurrent());
DEBUG_MSG("charge current %f\n", axp.getBattChargeCurrent());
DEBUG_MSG("bat voltage %f\n", axp.getBattVoltage());
DEBUG_MSG("batt pct %d\n", axp.getBattPercentage());
DEBUG_MSG("is battery connected %d\n", axp.isBatteryConnect());
DEBUG_MSG("is USB connected %d\n", axp.isVBUSPlug());
DEBUG_MSG("is charging %d\n", axp.isChargeing());
return 30 * 1000;
}
Periodic axpDebugOutput(axpDebugRead);
axpDebugOutput.setup();
#endif
uint32_t rebootAtMsec; // If not zero we will reboot at this time (used to reboot shortly after the update completes)
uint32_t shutdownAtMsec; // If not zero we will shutdown at this time (used to shutdown from python or mobile client)
void powerCommandsCheck()
{
if (rebootAtMsec && millis() > rebootAtMsec) {
#ifndef NO_ESP32
DEBUG_MSG("Rebooting for update\n");
ESP.restart();
#else
DEBUG_MSG("FIXME implement reboot for this platform");
#endif
}
#if NRF52_SERIES
if (shutdownAtMsec) {
screen->startShutdownScreen();
playBeep();
ledOff(PIN_LED1);
ledOff(PIN_LED2);
}
#endif
if (shutdownAtMsec && millis() > shutdownAtMsec) {
DEBUG_MSG("Shutting down from admin command\n");
#ifdef TBEAM_V10
if (axp192_found == true) {
setLed(false);
power->shutdown();
}
#elif NRF52_SERIES
playShutdownMelody();
power->shutdown();
#else
DEBUG_MSG("FIXME implement shutdown for this platform");
#endif
}
}
// If a thread does something that might need for it to be rescheduled ASAP it can set this flag
// This will supress the current delay and instead try to run ASAP.
bool runASAP;

View File

@@ -5,6 +5,7 @@
#include "PowerStatus.h"
#include "graphics/Screen.h"
extern uint8_t screen_found;
extern bool axp192_found;
extern bool isCharging;
extern bool isUSBPowered;
@@ -20,6 +21,8 @@ extern graphics::Screen *screen;
// Return a human readable string of the form "Meshtastic_ab13"
const char *getDeviceName();
extern uint32_t timeLastPowered;
extern uint32_t rebootAtMsec;
extern uint32_t shutdownAtMsec;

View File

@@ -7,7 +7,7 @@
/// 16 bytes of random PSK for our _public_ default channel that all devices power up on (AES128)
static const uint8_t defaultpsk[] = {0xd4, 0xf1, 0xbb, 0x3a, 0x20, 0x29, 0x07, 0x59,
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0xbf};
0xf0, 0xbc, 0xff, 0xab, 0xcf, 0x4e, 0x69, 0x01};
Channels channels;
@@ -84,10 +84,7 @@ void Channels::initDefaultChannel(ChannelIndex chIndex)
Channel &ch = getByIndex(chIndex);
ChannelSettings &channelSettings = ch.settings;
// radioConfig.modem_config = RadioConfig_ModemConfig_Bw125Cr45Sf128; // medium range and fast
// channelSettings.modem_config = ChannelSettings_ModemConfig_Bw500Cr45Sf128; // short range and fast, but wide
// bandwidth so incompatible radios can talk together
channelSettings.modem_config = ChannelSettings_ModemConfig_Bw125Cr48Sf4096; // slow and long range
channelSettings.modem_config = ChannelSettings_ModemConfig_LongFast; // Default to Long Range & Fast
channelSettings.tx_power = 0; // default
uint8_t defaultpskIndex = 1;
@@ -216,24 +213,27 @@ const char *Channels::getName(size_t chIndex)
channelName = "Unset";
else
switch (channelSettings.modem_config) {
case ChannelSettings_ModemConfig_Bw125Cr45Sf128:
case ChannelSettings_ModemConfig_ShortSlow:
channelName = "ShortSlow";
break;
case ChannelSettings_ModemConfig_Bw500Cr45Sf128:
case ChannelSettings_ModemConfig_ShortFast:
channelName = "ShortFast";
break;
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512:
channelName = "LongFast";
break;
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
channelName = "LongSlow";
break;
case ChannelSettings_ModemConfig_Bw250Cr46Sf2048:
case ChannelSettings_ModemConfig_MidSlow:
channelName = "MediumSlow";
break;
case ChannelSettings_ModemConfig_Bw250Cr47Sf1024:
case ChannelSettings_ModemConfig_MidFast:
channelName = "MediumFast";
break;
case ChannelSettings_ModemConfig_LongFast:
channelName = "LongFast";
break;
case ChannelSettings_ModemConfig_LongSlow:
channelName = "LongSlow";
break;
case ChannelSettings_ModemConfig_VLongSlow:
channelName = "VLongSlow";
break;
default:
channelName = "Invalid";
break;

View File

@@ -1,5 +1,5 @@
#include "configuration.h"
#include "FloodingRouter.h"
#include "configuration.h"
#include "mesh-pb-constants.h"
FloodingRouter::FloodingRouter() {}
@@ -27,11 +27,41 @@ bool FloodingRouter::shouldFilterReceived(MeshPacket *p)
return Router::shouldFilterReceived(p);
}
bool FloodingRouter::inRangeOfRouter()
{
uint32_t maximum_router_sec = 300;
// FIXME : Scale minimum_snr to accomodate different modem configurations.
float minimum_snr = 2;
for (int i = 0; i < myNodeInfo.router_count; i++) {
// A router has been seen and the heartbeat was heard within the last 300 seconds
if (
((myNodeInfo.router_sec[i] > 0) && (myNodeInfo.router_sec[i] < maximum_router_sec)) &&
(myNodeInfo.router_snr[i] > minimum_snr)
) {
return true;
}
}
return false;
}
void FloodingRouter::sniffReceived(const MeshPacket *p, const Routing *c)
{
// If a broadcast, possibly _also_ send copies out into the mesh.
// (FIXME, do something smarter than naive flooding here)
if (p->to == NODENUM_BROADCAST && p->hop_limit > 0 && getFrom(p) != getNodeNum()) {
bool rebroadcastPacket = true;
if (radioConfig.preferences.role == Role_Repeater || radioConfig.preferences.role == Role_Router) {
rebroadcastPacket = true;
} else if ((radioConfig.preferences.role == Role_Default) && inRangeOfRouter()) {
DEBUG_MSG("Role_Default - rx_snr > 13\n");
rebroadcastPacket = false;
}
if ((p->to == NODENUM_BROADCAST) && (p->hop_limit > 0) && (getFrom(p) != getNodeNum() && rebroadcastPacket)) {
if (p->id != 0) {
MeshPacket *tosend = packetPool.allocCopy(*p); // keep a copy because we will be sending it

View File

@@ -52,6 +52,14 @@ class FloodingRouter : public Router, protected PacketHistory
*/
virtual bool shouldFilterReceived(MeshPacket *p) override;
/**
* Are we in range of a router?
*
* "range" here may not be the right term.
* @return true if we're in range of a router
*/
virtual bool inRangeOfRouter();
/**
* Look for broadcasts we need to rebroadcast
*/

View File

@@ -3,15 +3,15 @@
#include "Channels.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "plugins/RoutingPlugin.h"
#include "modules/RoutingModule.h"
#include <assert.h>
std::vector<MeshPlugin *> *MeshPlugin::plugins;
std::vector<MeshPlugin *> *MeshPlugin::modules;
const MeshPacket *MeshPlugin::currentRequest;
/**
* If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow
* If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow
* the RoutingPlugin to avoid sending redundant acks
*/
MeshPacket *MeshPlugin::currentReply;
@@ -19,17 +19,17 @@ MeshPacket *MeshPlugin::currentReply;
MeshPlugin::MeshPlugin(const char *_name) : name(_name)
{
// Can't trust static initalizer order, so we check each time
if (!plugins)
plugins = new std::vector<MeshPlugin *>();
if (!modules)
modules = new std::vector<MeshPlugin *>();
plugins->push_back(this);
modules->push_back(this);
}
void MeshPlugin::setup() {}
MeshPlugin::~MeshPlugin()
{
assert(0); // FIXME - remove from list of plugins once someone needs this feature
assert(0); // FIXME - remove from list of modules once someone needs this feature
}
MeshPacket *MeshPlugin::allocAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
@@ -68,10 +68,10 @@ MeshPacket *MeshPlugin::allocErrorResponse(Routing_Error err, const MeshPacket *
void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
{
// DEBUG_MSG("In call plugins\n");
bool pluginFound = false;
// DEBUG_MSG("In call modules\n");
bool moduleFound = false;
// We now allow **encrypted** packets to pass through the plugins
// We now allow **encrypted** packets to pass through the modules
bool isDecoded = mp.which_payloadVariant == MeshPacket_decoded_tag;
currentReply = NULL; // No reply yet
@@ -80,12 +80,12 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
auto ourNodeNum = nodeDB.getNodeNum();
bool toUs = mp.to == NODENUM_BROADCAST || mp.to == ourNodeNum;
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;
pi.currentRequest = &mp;
/// We only call plugins that are interested in the packet (and the message is destined to us or we are promiscious)
/// We only call modules that are interested in the packet (and the message is destined to us or we are promiscious)
bool wantsPacket = (isDecoded || pi.encryptedOk) && (pi.isPromiscuous || toUs) && pi.wantPacket(&mp);
if ((src == RX_SRC_LOCAL) && !(pi.loopbackOk)) {
@@ -96,15 +96,15 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
assert(!pi.myReply); // If it is !null it means we have a bug, because it should have been sent the previous time
if (wantsPacket) {
DEBUG_MSG("Plugin '%s' wantsPacket=%d\n", pi.name, wantsPacket);
DEBUG_MSG("Module '%s' wantsPacket=%d\n", pi.name, wantsPacket);
pluginFound = true;
moduleFound = true;
/// received channel (or NULL if not decoded)
Channel *ch = isDecoded ? &channels.getByIndex(mp.channel) : NULL;
/// Is the channel this packet arrived on acceptable? (security check)
/// Note: we can't know channel names for encrypted packets, so those are NEVER sent to boundChannel plugins
/// Note: we can't know channel names for encrypted packets, so those are NEVER sent to boundChannel modules
/// Also: if a packet comes in on the local PC interface, we don't check for bound channels, because it is TRUSTED and it needs to
/// to be able to fetch the initial admin packets without yet knowing any channels.
@@ -128,7 +128,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
ProcessMessage handled = pi.handleReceived(mp);
// Possibly send replies (but only if the message was directed to us specifically, i.e. not for promiscious
// sniffing) also: we only let the one plugin send a reply, once that happens, remaining plugins are not
// sniffing) also: we only let the one module send a reply, once that happens, remaining modules are not
// considered
// NOTE: we send a reply *even if the (non broadcast) request was from us* which is unfortunate but necessary
@@ -137,9 +137,9 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
// any other node.
if (mp.decoded.want_response && toUs && (getFrom(&mp) != ourNodeNum || mp.to == ourNodeNum) && !currentReply) {
pi.sendResponse(mp);
DEBUG_MSG("Plugin '%s' sent a response\n", pi.name);
DEBUG_MSG("Module '%s' sent a response\n", pi.name);
} else {
DEBUG_MSG("Plugin '%s' considered\n", pi.name);
DEBUG_MSG("Module '%s' considered\n", pi.name);
}
// If the requester didn't ask for a response we might need to discard unused replies to prevent memory leaks
@@ -150,7 +150,7 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
}
if (handled == ProcessMessage::STOP) {
DEBUG_MSG("Plugin '%s' handled and skipped other processing\n", pi.name);
DEBUG_MSG("Module '%s' handled and skipped other processing\n", pi.name);
break;
}
}
@@ -173,12 +173,12 @@ void MeshPlugin::callPlugins(const MeshPacket &mp, RxSource src)
// SECURITY NOTE! I considered sending back a different error code if we didn't find the psk (i.e. !isDecoded)
// but opted NOT TO. Because it is not a good idea to let remote nodes 'probe' to find out which PSKs were "good" vs
// bad.
routingPlugin->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);
routingModule->sendAckNak(Routing_Error_NO_RESPONSE, getFrom(&mp), mp.id, mp.channel);
}
}
if (!pluginFound)
DEBUG_MSG("No plugins interested in portnum=%d, src=%s\n",
if (!moduleFound)
DEBUG_MSG("No modules interested in portnum=%d, src=%s\n",
mp.decoded.portnum,
(src == RX_SRC_LOCAL) ? "LOCAL":"REMOTE");
}
@@ -201,8 +201,8 @@ void MeshPlugin::sendResponse(const MeshPacket &req)
setReplyTo(r, req);
currentReply = r;
} else {
// Ignore - this is now expected behavior for routing plugin (because it ignores some replies)
// DEBUG_MSG("WARNING: Client requested response but this plugin did not provide\n");
// Ignore - this is now expected behavior for routing module (because it ignores some replies)
// DEBUG_MSG("WARNING: Client requested response but this module did not provide\n");
}
}
@@ -222,31 +222,61 @@ void setReplyTo(MeshPacket *p, const MeshPacket &to)
p->decoded.request_id = to.id;
}
std::vector<MeshPlugin *> MeshPlugin::GetMeshPluginsWithUIFrames()
std::vector<MeshPlugin *> MeshPlugin::GetMeshModulesWithUIFrames()
{
std::vector<MeshPlugin *> pluginsWithUIFrames;
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
std::vector<MeshPlugin *> modulesWithUIFrames;
if (modules) {
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;
if (pi.wantUIFrame()) {
DEBUG_MSG("Plugin wants a UI Frame\n");
pluginsWithUIFrames.push_back(&pi);
DEBUG_MSG("Module wants a UI Frame\n");
modulesWithUIFrames.push_back(&pi);
}
}
return pluginsWithUIFrames;
}
return modulesWithUIFrames;
}
void MeshPlugin::observeUIEvents(
Observer<const UIFrameEvent *> *observer)
{
std::vector<MeshPlugin *> pluginsWithUIFrames;
for (auto i = plugins->begin(); i != plugins->end(); ++i) {
if (modules) {
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;
Observable<const UIFrameEvent *> *observable =
pi.getUIFrameObservable();
if (observable != NULL) {
DEBUG_MSG("Plugin wants a UI Frame\n");
DEBUG_MSG("Module wants a UI Frame\n");
observer->observe(observable);
}
}
}
}
AdminMessageHandleResult MeshPlugin::handleAdminMessageForAllPlugins(const MeshPacket &mp, AdminMessage *request, AdminMessage *response)
{
AdminMessageHandleResult handled = AdminMessageHandleResult::NOT_HANDLED;
if (modules) {
for (auto i = modules->begin(); i != modules->end(); ++i) {
auto &pi = **i;
AdminMessageHandleResult h = pi.handleAdminMessageForModule(mp, request, response);
if (h == AdminMessageHandleResult::HANDLED_WITH_RESPONSE)
{
// In case we have a response it always has priority.
DEBUG_MSG("Reply prepared by module '%s' of variant: %d\n",
pi.name,
response->which_variant);
handled = h;
}
else if ((handled != AdminMessageHandleResult::HANDLED_WITH_RESPONSE) &&
(h == AdminMessageHandleResult::HANDLED))
{
// In case the message is handled it should be populated, but will not overwrite
// a result with response.
handled = h;
}
}
}
return handled;
}

View File

@@ -21,6 +21,19 @@ enum class ProcessMessage
STOP = 1,
};
/**
* Used by modules to return the result of the AdminMessage handling.
* If request is handled, then module should return HANDLED,
* If response is also prepared for the request, then HANDLED_WITH_RESPONSE
* should be returned.
*/
enum class AdminMessageHandleResult
{
NOT_HANDLED = 0,
HANDLED = 1,
HANDLED_WITH_RESPONSE = 2,
};
/*
* This struct is used by Screen to figure out whether screen frame should be updated.
*/
@@ -29,19 +42,19 @@ typedef struct _UIFrameEvent {
bool needRedraw;
} UIFrameEvent;
/** A baseclass for any mesh "plugin".
/** A baseclass for any mesh "module".
*
* A plugin allows you to add new features to meshtastic device code, without needing to know messaging details.
* A module allows you to add new features to meshtastic device code, without needing to know messaging details.
*
* A key concept for this is that your plugin should use a particular "portnum" for each message type you want to receive
* A key concept for this is that your module should use a particular "portnum" for each message type you want to receive
* and handle.
*
* Interally we use plugins to implement the core meshtastic text messaging and gps position sharing features. You
* can use these classes as examples for how to write your own custom plugin. See here: (FIXME)
* Interally we use modules to implement the core meshtastic text messaging and gps position sharing features. You
* can use these classes as examples for how to write your own custom module. See here: (FIXME)
*/
class MeshPlugin
{
static std::vector<MeshPlugin *> *plugins;
static std::vector<MeshPlugin *> *modules;
public:
/** Constructor
@@ -55,25 +68,27 @@ class MeshPlugin
*/
static void callPlugins(const MeshPacket &mp, RxSource src = RX_SRC_RADIO);
static std::vector<MeshPlugin *> GetMeshPluginsWithUIFrames();
static std::vector<MeshPlugin *> GetMeshModulesWithUIFrames();
static void observeUIEvents(Observer<const UIFrameEvent *> *observer);
static AdminMessageHandleResult handleAdminMessageForAllPlugins(
const MeshPacket &mp, AdminMessage *request, AdminMessage *response);
#ifndef NO_SCREEN
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; }
#endif
protected:
const char *name;
/** Most plugins only care about packets that are destined for their node (i.e. broadcasts or has their node as the specific
/** Most modules only care about packets that are destined for their node (i.e. broadcasts or has their node as the specific
recipient) But some plugs might want to 'sniff' packets that are merely being routed (passing through the current node). Those
plugins can set this to true and their handleReceived() will be called for every packet.
modules can set this to true and their handleReceived() will be called for every packet.
*/
bool isPromiscuous = false;
/** Also receive a copy of LOCALLY GENERATED messages - most plugins should leave
/** Also receive a copy of LOCALLY GENERATED messages - most modules should leave
* this setting disabled - see issue #877 */
bool loopbackOk = false;
/** Most plugins only understand decrypted packets. For plugins that also want to see encrypted packets, they should set this
/** Most modules only understand decrypted packets. For modules that also want to see encrypted packets, they should set this
* flag */
bool encryptedOk = false;
@@ -86,11 +101,11 @@ class MeshPlugin
const char *boundChannel = NULL;
/**
* If this plugin is currently handling a request currentRequest will be preset
* If this module is currently handling a request currentRequest will be preset
* to the packet with the request. This is mostly useful for reply handlers.
*
* Note: this can be static because we are guaranteed to be processing only one
* plugin at a time.
* plumodulegin at a time.
*/
static const MeshPacket *currentRequest;
@@ -100,7 +115,7 @@ class MeshPlugin
MeshPacket *myReply = NULL;
/**
* Initialize your plugin. This setup function is called once after all hardware and mesh protocol layers have
* Initialize your module. This setup function is called once after all hardware and mesh protocol layers have
* been initialized
*/
virtual void setup();
@@ -135,10 +150,23 @@ class MeshPlugin
/// Send an error response for the specified packet.
MeshPacket *allocErrorResponse(Routing_Error err, const MeshPacket *p);
/**
* @brief An admin message arrived to AdminModule. Module was asked whether it want to handle the request.
*
* @param mp The mesh packet arrived.
* @param request The AdminMessage request extracted from the packet.
* @param response The prepared response
* @return AdminMessageHandleResult
* HANDLED if message was handled
* HANDLED_WITH_RESPONSE if a response is also prepared and to be sent.
*/
virtual AdminMessageHandleResult handleAdminMessageForModule(
const MeshPacket &mp, AdminMessage *request, AdminMessage *response) { return AdminMessageHandleResult::NOT_HANDLED; };
private:
/**
* If any of the current chain of plugins has already sent a reply, it will be here. This is useful to allow
* the RoutingPlugin to avoid sending redundant acks
* If any of the current chain of modules has already sent a reply, it will be here. This is useful to allow
* the RoutingModule to avoid sending redundant acks
*/
static MeshPacket *currentReply;

View File

@@ -8,10 +8,13 @@
// Map from old region names to new region enums
struct RegionInfo {
RegionCode code;
uint8_t numChannels;
uint8_t powerLimit; // Or zero for not set
float freq;
float freqStart;
float freqEnd;
float dutyCycle;
float spacing;
uint8_t powerLimit; // Or zero for not set
bool audioPermitted;
bool freqSwitching;
const char *name; // EU433 etc
};

View File

@@ -12,8 +12,8 @@
#include "RTC.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "plugins/NodeInfoPlugin.h"
#include "plugins/PositionPlugin.h"
#include "modules/NodeInfoModule.h"
#include "modules/PositionModule.h"
#include "power.h"
/*
@@ -115,10 +115,10 @@ void MeshService::reloadOwner()
// DEBUG_MSG("reloadOwner()\n");
// update our local data directly
nodeDB.updateUser(nodeDB.getNodeNum(), owner);
assert(nodeInfoPlugin);
assert(nodeInfoModule);
// update everyone else
if (nodeInfoPlugin)
nodeInfoPlugin->sendOurNodeInfo();
if (nodeInfoModule)
nodeInfoModule->sendOurNodeInfo();
nodeDB.saveToDisk();
}
@@ -175,14 +175,14 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
assert(node);
if (node->has_position) {
if (positionPlugin) {
if (positionModule) {
DEBUG_MSG("Sending position ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
positionPlugin->sendOurPosition(dest, wantReplies);
positionModule->sendOurPosition(dest, wantReplies);
}
} else {
if (nodeInfoPlugin) {
if (nodeInfoModule) {
DEBUG_MSG("Sending nodeinfo ping to 0x%x, wantReplies=%d\n", dest, wantReplies);
nodeInfoPlugin->sendOurNodeInfo(dest, wantReplies);
nodeInfoModule->sendOurNodeInfo(dest, wantReplies);
}
}
}

View File

@@ -91,7 +91,7 @@ class MeshService
/// Handle a packet that just arrived from the radio. This method does _ReliableRouternot_ free the provided packet. If it
/// needs to keep the packet around it makes a copy
int handleFromRadio(const MeshPacket *p);
friend class RoutingPlugin;
friend class RoutingModule;
};
extern MeshService service;

View File

@@ -19,7 +19,7 @@
#ifndef NO_ESP32
#include "mesh/http/WiFiAPClient.h"
#include "plugins/esp32/StoreForwardPlugin.h"
#include "modules/esp32/StoreForwardModule.h"
#include <Preferences.h>
#include <nvs_flash.h>
#endif
@@ -227,35 +227,8 @@ void NodeDB::init()
info->user = owner;
info->has_user = true;
// removed from 1.2 (though we do use old values if found)
// We set these _after_ loading from disk - because they come from the build and are more trusted than
// what is stored in flash
// if (xstr(HW_VERSION)[0])
// strncpy(myNodeInfo.region, optstr(HW_VERSION), sizeof(myNodeInfo.region));
// else DEBUG_MSG("This build does not specify a HW_VERSION\n"); // Eventually new builds will no longer include this build
// flag
// DEBUG_MSG("legacy region %d\n", devicestate.legacyRadio.preferences.region);
if (radioConfig.preferences.region == RegionCode_Unset)
radioConfig.preferences.region = devicestate.legacyRadio.preferences.region;
// Check for the old style of region code strings, if found, convert to the new enum.
// Those strings will look like "1.0-EU433"
if (radioConfig.preferences.region == RegionCode_Unset && strncmp(myNodeInfo.region, "1.0-", 4) == 0) {
const char *regionStr = myNodeInfo.region + 4; // EU433 or whatever
for (const RegionInfo *r = regions; r->code != RegionCode_Unset; r++)
if (strcmp(r->name, regionStr) == 0) {
radioConfig.preferences.region = r->code;
break;
}
}
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
// hw_model is no longer stored in myNodeInfo (as of 1.2.11) - we now store it as an enum in nodeinfo
myNodeInfo.hw_model_deprecated[0] = '\0';
// strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
#ifndef NO_ESP32
Preferences preferences;
preferences.begin("meshtastic", false);

View File

@@ -1,4 +1,3 @@
#include "configuration.h"
#include "PhoneAPI.h"
#include "Channels.h"
#include "GPS.h"
@@ -6,6 +5,7 @@
#include "NodeDB.h"
#include "PowerFSM.h"
#include "RadioInterface.h"
#include "configuration.h"
#include <assert.h>
#if FromRadio_size > MAX_TO_FROM_RADIO_SIZE

View File

@@ -24,6 +24,7 @@ class PhoneAPI
STATE_SEND_NOTHING, // (Eventual) Initial state, don't send anything until the client starts asking for config
// (disconnected)
STATE_SEND_MY_INFO, // send our my info record
STATE_SEND_GROUPS,
// STATE_SEND_RADIO, // in 1.2 we now send this as a regular mesh packet
// STATE_SEND_OWNER, no need to send Owner specially, it is just part of the nodedb
STATE_SEND_NODEINFO, // states progress in this order as the device sends to to the client
@@ -58,7 +59,7 @@ class PhoneAPI
// Call this when the client drops the connection, resets the state to STATE_SEND_NOTHING
// Unregisters our observer. A closed connection **can** be reopened by calling init again.
void close();
virtual void close();
/**
* Handle a ToRadio protobuf

View File

@@ -2,10 +2,10 @@
#include "SinglePortPlugin.h"
/**
* A base class for mesh plugins that assume that they are sending/receiving one particular protobuf based
* A base class for mesh modules that assume that they are sending/receiving one particular protobuf based
* payload. Using one particular app ID.
*
* If you are using protobufs to encode your packets (recommended) you can use this as a baseclass for your plugin
* If you are using protobufs to encode your packets (recommended) you can use this as a baseclass for your module
* and avoid a bunch of boilerplate code.
*/
template <class T> class ProtobufPlugin : protected SinglePortPlugin
@@ -67,7 +67,7 @@ template <class T> class ProtobufPlugin : protected SinglePortPlugin
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, fields, &scratch))
decoded = &scratch;
else
DEBUG_MSG("Error decoding protobuf plugin!\n");
DEBUG_MSG("Error decoding protobuf module!\n");
}
return handleReceivedProtobuf(mp, decoded) ? ProcessMessage::STOP : ProcessMessage::CONTINUE;

View File

@@ -140,6 +140,8 @@ bool RF95Interface::reconfigure()
void RF95Interface::addReceiveMetadata(MeshPacket *mp)
{
mp->rx_snr = lora->getSNR();
mp->rx_rssi = lround(lora->getRSSI());
}
void RF95Interface::setStandby()

View File

@@ -1,4 +1,3 @@
#include "configuration.h"
#include "RadioInterface.h"
#include "Channels.h"
#include "MeshRadio.h"
@@ -6,45 +5,95 @@
#include "NodeDB.h"
#include "Router.h"
#include "assert.h"
#include "configuration.h"
#include "sleep.h"
#include <assert.h>
#include <pb_decode.h>
#include <pb_encode.h>
#define RDEF(name, freq, spacing, num_ch, power_limit) \
#define RDEF(name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, frequency_switching) \
{ \
RegionCode_##name, num_ch, power_limit, freq, spacing, #name \
RegionCode_##name, freq_start, freq_end, duty_cycle, spacing, power_limit, audio_permitted, frequency_switching, #name \
}
const RegionInfo regions[] = {
RDEF(US, 903.08f, 2.16f, 13, 0), RDEF(EU433, 433.175f, 0.2f, 8, 0), RDEF(EU865, 865.2f, 0.3f, 10, 0),
RDEF(CN, 470.0f, 2.0f, 20, 0),
RDEF(JP, 920.0f, 0.5f, 10, 13), // See https://github.com/meshtastic/Meshtastic-device/issues/346 power level 13
RDEF(ANZ, 916.0f, 0.5f, 20, 0), // AU/NZ channel settings 915-928MHz
RDEF(KR, 921.9f, 0.2f, 8, 0), // KR channel settings (KR920-923) Start from TTN download channel
// freq. (921.9f is for download, others are for uplink)
RDEF(TW, 923.0f, 0.2f, 10, 0), // TW channel settings (AS2 bandplan 923-925MHz)
RDEF(RU, 868.9f, 0.2f, 2, 20), // See notes below
RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last
/*
https://link.springer.com/content/pdf/bbm%3A978-1-4842-4357-2%2F1.pdf
https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/
*/
RDEF(US, 902.0f, 928.0f, 100, 0, 30, true, false),
/*
https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
*/
RDEF(EU433, 433.0f, 434.0f, 10, 0, 12, true, false),
/*
https://www.thethingsnetwork.org/docs/lorawan/duty-cycle/
https://www.thethingsnetwork.org/docs/lorawan/regional-parameters/
https://www.legislation.gov.uk/uksi/1999/930/schedule/6/part/III/made/data.xht?view=snippet&wrap=true
audio_permitted = false per regulation
*/
RDEF(EU868, 869.4f, 869.65f, 10, 0, 16, false, false),
/*
https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
*/
RDEF(CN, 470.0f, 510.0f, 100, 0, 19, true, false),
/*
https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
*/
RDEF(JP, 920.8f, 927.8f, 100, 0, 16, true, false),
/*
https://www.iot.org.au/wp/wp-content/uploads/2016/12/IoTSpectrumFactSheet.pdf
https://iotalliance.org.nz/wp-content/uploads/sites/4/2019/05/IoT-Spectrum-in-NZ-Briefing-Paper.pdf
*/
RDEF(ANZ, 915.0f, 928.0f, 100, 0, 30, true, false),
/*
https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf
Note:
- We do LBT, so 100% is allowed.
*/
RDEF(RU, 868.7f, 869.2f, 100, 0, 20, true, false),
/*
???
*/
RDEF(KR, 920.0f, 923.0f, 100, 0, 0, true, false),
/*
???
*/
RDEF(TW, 920.0f, 925.0f, 100, 0, 0, true, false),
/*
https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
*/
RDEF(IN, 865.0f, 867.0f, 100, 0, 30, true, false),
/*
https://rrf.rsm.govt.nz/smart-web/smart/page/-smart/domain/licence/LicenceSummary.wdk?id=219752
https://iotalliance.org.nz/wp-content/uploads/sites/4/2019/05/IoT-Spectrum-in-NZ-Briefing-Paper.pdf
*/
RDEF(NZ865, 864.0f, 868.0f, 100, 0, 0, true, false),
/*
https://lora-alliance.org/wp-content/uploads/2020/11/lorawan_regional_parameters_v1.0.3reva_0.pdf
*/
RDEF(TH, 920.0f, 925.0f, 100, 0, 16, true, false),
/*
This needs to be last. Same as US.
*/
RDEF(Unset, 902.0f, 928.0f, 100, 0, 30, true, false)
};
/* Notes about the RU bandplan (from @denis-d in https://meshtastic.discourse.group/t/russian-band-plan-proposal/2786/2):
According to Annex 12 to GKRCh (National Radio Frequency Commission) decision № 18-46-03-1 (September 11th 2018)
https://digital.gov.ru/uploaded/files/prilozhenie-12-k-reshenyu-gkrch-18-46-03-1.pdf 1 We have 3 options for 868 MHz:
864,0 - 865,0 MHz ERP 25mW, Duty Cycle 0.1% (3.6 sec in hour) or LBT (Listen Before Talk), prohibited in airports.
866,0 - 868,0 MHz ERP 25mW, Duty Cycle 1% or LBT, PSD (Power Spectrum Density) 1000mW/MHz, prohibited in airports
868,7 - 869,2 MHz ERP 100mW, Duty Cycle 10% or LBT, no resctrictions
and according to RP2-1.0.2 LoRaWAN® Regional Parameters RP2-1.0.2 LoRaWAN® Regional Parameters - LoRa Alliance®
I propose to use following meshtastic bandplan:
1 channel - central frequency 868.9MHz 125kHz band
Protective band - 75kHz
2 channel - central frequency 869.1MHz 125kHz band
RDEF(RU, 868.9f, 0.2f, 2, 20)
*/
const RegionInfo *myRegion;
void initRegion()
@@ -54,8 +103,6 @@ void initRegion()
;
myRegion = r;
DEBUG_MSG("Wanted region %d, using %s\n", radioConfig.preferences.region, r->name);
myNodeInfo.num_bands = myRegion->numChannels; // Tell our android app how many channels we have
}
/**
@@ -172,6 +219,9 @@ void printPacket(const char *prefix, const MeshPacket *p)
if (p->rx_snr != 0.0) {
DEBUG_MSG(" rxSNR=%g", p->rx_snr);
}
if (p->rx_rssi != 0) {
DEBUG_MSG(" rxSNR=%g", p->rx_rssi);
}
if (p->priority != 0)
DEBUG_MSG(" priority=%d", p->priority);
@@ -274,38 +324,40 @@ void RadioInterface::applyModemConfig()
auto channelSettings = channels.getPrimary();
if (channelSettings.spread_factor == 0) {
switch (channelSettings.modem_config) {
case ChannelSettings_ModemConfig_Bw125Cr45Sf128: ///< Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Default medium
///< range
bw = 125;
cr = 5;
case ChannelSettings_ModemConfig_ShortFast:
bw = 250;
cr = 8;
sf = 7;
break;
case ChannelSettings_ModemConfig_Bw500Cr45Sf128: ///< Bw = 500 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on. Fast+short
///< range
bw = 500;
cr = 5;
sf = 7;
case ChannelSettings_ModemConfig_ShortSlow:
bw = 250;
cr = 8;
sf = 8;
break;
case ChannelSettings_ModemConfig_Bw31_25Cr48Sf512: ///< Bw = 31.25 kHz, Cr = 4/8, Sf = 512chips/symbol, CRC on. Slow+long
///< range
bw = 31.25;
case ChannelSettings_ModemConfig_MidFast:
bw = 250;
cr = 8;
sf = 9;
break;
case ChannelSettings_ModemConfig_Bw125Cr48Sf4096:
case ChannelSettings_ModemConfig_MidSlow:
bw = 250;
cr = 8;
sf = 10;
break;
case ChannelSettings_ModemConfig_LongFast:
bw = 250;
cr = 8;
sf = 11;
break;
case ChannelSettings_ModemConfig_LongSlow:
bw = 125;
cr = 8;
sf = 12;
break;
case ChannelSettings_ModemConfig_Bw250Cr46Sf2048:
bw = 250;
cr = 6;
sf = 11;
break;
case ChannelSettings_ModemConfig_Bw250Cr47Sf1024:
bw = 250;
cr = 7;
sf = 10;
case ChannelSettings_ModemConfig_VLongSlow:
bw = 31.25;
cr = 8;
sf = 12;
break;
default:
assert(0); // Unknown enum
@@ -322,25 +374,26 @@ void RadioInterface::applyModemConfig()
}
power = channelSettings.tx_power;
shortPacketMsec = getPacketTime(sizeof(PacketHeader));
assert(myRegion); // Should have been found in init
// Calculate the number of channels
uint32_t numChannels = floor((myRegion->freqEnd - myRegion->freqStart) / (myRegion->spacing + (bw / 1000)));
// If user has manually specified a channel num, then use that, otherwise generate one by hashing the name
const char *channelName = channels.getName(channels.getPrimaryIndex());
int channel_num = channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelName) % myRegion->numChannels;
float freq = myRegion->freq + radioConfig.preferences.frequency_offset + myRegion->spacing * channel_num;
int channel_num = channelSettings.channel_num ? channelSettings.channel_num - 1 : hash(channelName) % numChannels;
float freq = myRegion->freqStart + ((((myRegion->freqEnd - myRegion->freqStart) / numChannels) / 2) * channel_num);
saveChannelNum(channel_num);
saveFreq(freq);
DEBUG_MSG("Set radio: name=%s, config=%u, ch=%d, power=%d\n", channelName, channelSettings.modem_config, channel_num, power);
DEBUG_MSG("Radio myRegion->freq: %f\n", myRegion->freq);
DEBUG_MSG("Radio myRegion->spacing: %f\n", myRegion->spacing);
DEBUG_MSG("Radio myRegion->numChannels: %d\n", myRegion->numChannels);
DEBUG_MSG("Radio myRegion->freqStart / myRegion->freqEnd: %f -> %f (%f mhz)\n", myRegion->freqStart, myRegion->freqEnd,
myRegion->freqEnd - myRegion->freqStart);
DEBUG_MSG("Radio myRegion->numChannels: %d\n", numChannels);
DEBUG_MSG("Radio channel_num: %d\n", channel_num);
DEBUG_MSG("Radio frequency: %f\n", getFreq()); // the frequency could be overridden in RadioInterface::getFreq() for some modules
DEBUG_MSG("Radio frequency: %f\n", getFreq());
DEBUG_MSG("Short packet time: %u msec\n", shortPacketMsec);
}

View File

@@ -6,7 +6,7 @@
#include "RTC.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "plugins/RoutingPlugin.h"
#include "modules/RoutingModule.h"
#if defined(HAS_WIFI) || defined(PORTDUINO)
#include "mqtt/MQTT.h"
@@ -132,7 +132,7 @@ MeshPacket *Router::allocForSending()
*/
void Router::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
{
routingPlugin->sendAckNak(err, to, idFrom, chIndex);
routingModule->sendAckNak(err, to, idFrom, chIndex);
}
void Router::abortSendAndNak(Routing_Error err, MeshPacket *p)
@@ -276,7 +276,7 @@ bool perhapsDecode(MeshPacket *p)
if (p->which_payloadVariant == MeshPacket_decoded_tag)
return true; // If packet was already decoded just return
assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
//assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
// Try to find a channel that works with this hash
for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
@@ -376,7 +376,7 @@ void Router::handleReceived(MeshPacket *p, RxSource src)
printPacket("packet decoding failed (no PSK?)", p);
}
// call plugins here
// call modules here
MeshPlugin::callPlugins(*p, src);
}

View File

@@ -71,7 +71,7 @@ class Router : protected concurrency::OSThread
void enqueueReceivedMessage(MeshPacket *p);
protected:
friend class RoutingPlugin;
friend class RoutingModule;
/**
* Send a packet on a suitable interface. This routine will
@@ -85,7 +85,7 @@ class Router : protected concurrency::OSThread
/**
* Should this incoming filter be dropped?
*
* FIXME, move this into the new RoutingPlugin and do the filtering there using the regular plugin logic
* FIXME, move this into the new RoutingModule and do the filtering there using the regular module logic
*
* Called immedately on receiption, before any further processing.
* @return true to abandon the packet

View File

@@ -3,7 +3,7 @@
#include "Router.h"
/**
* Most plugins are only interested in sending/receving one particular portnum. This baseclass simplifies that common
* Most modules are only interested in sending/receving one particular portnum. This baseclass simplifies that common
* case.
*/
class SinglePortPlugin : public MeshPlugin

View File

@@ -4,7 +4,6 @@
#ifndef PB_ADMIN_PB_H_INCLUDED
#define PB_ADMIN_PB_H_INCLUDED
#include <pb.h>
#include "cannedmessages.pb.h"
#include "channel.pb.h"
#include "mesh.pb.h"
#include "radioconfig.pb.h"
@@ -30,21 +29,18 @@ typedef struct _AdminMessage {
bool confirm_set_radio;
bool exit_simulator;
int32_t reboot_seconds;
bool get_canned_message_plugin_part1_request;
CannedMessagePluginMessagePart1 get_canned_message_plugin_part1_response;
bool get_canned_message_plugin_part2_request;
CannedMessagePluginMessagePart2 get_canned_message_plugin_part2_response;
bool get_canned_message_plugin_part3_request;
CannedMessagePluginMessagePart3 get_canned_message_plugin_part3_response;
bool get_canned_message_plugin_part4_request;
CannedMessagePluginMessagePart4 get_canned_message_plugin_part4_response;
bool get_canned_message_plugin_part5_request;
CannedMessagePluginMessagePart5 get_canned_message_plugin_part5_response;
CannedMessagePluginMessagePart1 set_canned_message_plugin_part1;
CannedMessagePluginMessagePart2 set_canned_message_plugin_part2;
CannedMessagePluginMessagePart3 set_canned_message_plugin_part3;
CannedMessagePluginMessagePart4 set_canned_message_plugin_part4;
CannedMessagePluginMessagePart5 set_canned_message_plugin_part5;
bool get_canned_message_module_part1_request;
char get_canned_message_module_part1_response[201];
bool get_canned_message_module_part2_request;
char get_canned_message_module_part2_response[201];
bool get_canned_message_module_part3_request;
char get_canned_message_module_part3_response[201];
bool get_canned_message_module_part4_request;
char get_canned_message_module_part4_response[201];
char set_canned_message_module_part1[201];
char set_canned_message_module_part2[201];
char set_canned_message_module_part3[201];
char set_canned_message_module_part4[201];
int32_t shutdown_seconds;
};
} AdminMessage;
@@ -72,21 +68,18 @@ extern "C" {
#define AdminMessage_confirm_set_radio_tag 33
#define AdminMessage_exit_simulator_tag 34
#define AdminMessage_reboot_seconds_tag 35
#define AdminMessage_get_canned_message_plugin_part1_request_tag 36
#define AdminMessage_get_canned_message_plugin_part1_response_tag 37
#define AdminMessage_get_canned_message_plugin_part2_request_tag 38
#define AdminMessage_get_canned_message_plugin_part2_response_tag 39
#define AdminMessage_get_canned_message_plugin_part3_request_tag 40
#define AdminMessage_get_canned_message_plugin_part3_response_tag 41
#define AdminMessage_get_canned_message_plugin_part4_request_tag 42
#define AdminMessage_get_canned_message_plugin_part4_response_tag 43
#define AdminMessage_get_canned_message_plugin_part5_request_tag 44
#define AdminMessage_get_canned_message_plugin_part5_response_tag 45
#define AdminMessage_set_canned_message_plugin_part1_tag 46
#define AdminMessage_set_canned_message_plugin_part2_tag 47
#define AdminMessage_set_canned_message_plugin_part3_tag 48
#define AdminMessage_set_canned_message_plugin_part4_tag 49
#define AdminMessage_set_canned_message_plugin_part5_tag 50
#define AdminMessage_get_canned_message_module_part1_request_tag 36
#define AdminMessage_get_canned_message_module_part1_response_tag 37
#define AdminMessage_get_canned_message_module_part2_request_tag 38
#define AdminMessage_get_canned_message_module_part2_response_tag 39
#define AdminMessage_get_canned_message_module_part3_request_tag 40
#define AdminMessage_get_canned_message_module_part3_response_tag 41
#define AdminMessage_get_canned_message_module_part4_request_tag 42
#define AdminMessage_get_canned_message_module_part4_response_tag 43
#define AdminMessage_set_canned_message_module_part1_tag 44
#define AdminMessage_set_canned_message_module_part2_tag 45
#define AdminMessage_set_canned_message_module_part3_tag 46
#define AdminMessage_set_canned_message_module_part4_tag 47
#define AdminMessage_shutdown_seconds_tag 51
/* Struct field encoding specification for nanopb */
@@ -104,21 +97,18 @@ X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_channel,confirm_set_chan
X(a, STATIC, ONEOF, BOOL, (variant,confirm_set_radio,confirm_set_radio), 33) \
X(a, STATIC, ONEOF, BOOL, (variant,exit_simulator,exit_simulator), 34) \
X(a, STATIC, ONEOF, INT32, (variant,reboot_seconds,reboot_seconds), 35) \
X(a, STATIC, ONEOF, BOOL, (variant,get_canned_message_plugin_part1_request,get_canned_message_plugin_part1_request), 36) \
X(a, STATIC, ONEOF, MESSAGE, (variant,get_canned_message_plugin_part1_response,get_canned_message_plugin_part1_response), 37) \
X(a, STATIC, ONEOF, BOOL, (variant,get_canned_message_plugin_part2_request,get_canned_message_plugin_part2_request), 38) \
X(a, STATIC, ONEOF, MESSAGE, (variant,get_canned_message_plugin_part2_response,get_canned_message_plugin_part2_response), 39) \
X(a, STATIC, ONEOF, BOOL, (variant,get_canned_message_plugin_part3_request,get_canned_message_plugin_part3_request), 40) \
X(a, STATIC, ONEOF, MESSAGE, (variant,get_canned_message_plugin_part3_response,get_canned_message_plugin_part3_response), 41) \
X(a, STATIC, ONEOF, BOOL, (variant,get_canned_message_plugin_part4_request,get_canned_message_plugin_part4_request), 42) \
X(a, STATIC, ONEOF, MESSAGE, (variant,get_canned_message_plugin_part4_response,get_canned_message_plugin_part4_response), 43) \
X(a, STATIC, ONEOF, BOOL, (variant,get_canned_message_plugin_part5_request,get_canned_message_plugin_part5_request), 44) \
X(a, STATIC, ONEOF, MESSAGE, (variant,get_canned_message_plugin_part5_response,get_canned_message_plugin_part5_response), 45) \
X(a, STATIC, ONEOF, MESSAGE, (variant,set_canned_message_plugin_part1,set_canned_message_plugin_part1), 46) \
X(a, STATIC, ONEOF, MESSAGE, (variant,set_canned_message_plugin_part2,set_canned_message_plugin_part2), 47) \
X(a, STATIC, ONEOF, MESSAGE, (variant,set_canned_message_plugin_part3,set_canned_message_plugin_part3), 48) \
X(a, STATIC, ONEOF, MESSAGE, (variant,set_canned_message_plugin_part4,set_canned_message_plugin_part4), 49) \
X(a, STATIC, ONEOF, MESSAGE, (variant,set_canned_message_plugin_part5,set_canned_message_plugin_part5), 50) \
X(a, STATIC, ONEOF, BOOL, (variant,get_canned_message_module_part1_request,get_canned_message_module_part1_request), 36) \
X(a, STATIC, ONEOF, STRING, (variant,get_canned_message_module_part1_response,get_canned_message_module_part1_response), 37) \
X(a, STATIC, ONEOF, BOOL, (variant,get_canned_message_module_part2_request,get_canned_message_module_part2_request), 38) \
X(a, STATIC, ONEOF, STRING, (variant,get_canned_message_module_part2_response,get_canned_message_module_part2_response), 39) \
X(a, STATIC, ONEOF, BOOL, (variant,get_canned_message_module_part3_request,get_canned_message_module_part3_request), 40) \
X(a, STATIC, ONEOF, STRING, (variant,get_canned_message_module_part3_response,get_canned_message_module_part3_response), 41) \
X(a, STATIC, ONEOF, BOOL, (variant,get_canned_message_module_part4_request,get_canned_message_module_part4_request), 42) \
X(a, STATIC, ONEOF, STRING, (variant,get_canned_message_module_part4_response,get_canned_message_module_part4_response), 43) \
X(a, STATIC, ONEOF, STRING, (variant,set_canned_message_module_part1,set_canned_message_module_part1), 44) \
X(a, STATIC, ONEOF, STRING, (variant,set_canned_message_module_part2,set_canned_message_module_part2), 45) \
X(a, STATIC, ONEOF, STRING, (variant,set_canned_message_module_part3,set_canned_message_module_part3), 46) \
X(a, STATIC, ONEOF, STRING, (variant,set_canned_message_module_part4,set_canned_message_module_part4), 47) \
X(a, STATIC, ONEOF, INT32, (variant,shutdown_seconds,shutdown_seconds), 51)
#define AdminMessage_CALLBACK NULL
#define AdminMessage_DEFAULT NULL
@@ -128,16 +118,6 @@ X(a, STATIC, ONEOF, INT32, (variant,shutdown_seconds,shutdown_seconds),
#define AdminMessage_variant_get_radio_response_MSGTYPE RadioConfig
#define AdminMessage_variant_get_channel_response_MSGTYPE Channel
#define AdminMessage_variant_get_owner_response_MSGTYPE User
#define AdminMessage_variant_get_canned_message_plugin_part1_response_MSGTYPE CannedMessagePluginMessagePart1
#define AdminMessage_variant_get_canned_message_plugin_part2_response_MSGTYPE CannedMessagePluginMessagePart2
#define AdminMessage_variant_get_canned_message_plugin_part3_response_MSGTYPE CannedMessagePluginMessagePart3
#define AdminMessage_variant_get_canned_message_plugin_part4_response_MSGTYPE CannedMessagePluginMessagePart4
#define AdminMessage_variant_get_canned_message_plugin_part5_response_MSGTYPE CannedMessagePluginMessagePart5
#define AdminMessage_variant_set_canned_message_plugin_part1_MSGTYPE CannedMessagePluginMessagePart1
#define AdminMessage_variant_set_canned_message_plugin_part2_MSGTYPE CannedMessagePluginMessagePart2
#define AdminMessage_variant_set_canned_message_plugin_part3_MSGTYPE CannedMessagePluginMessagePart3
#define AdminMessage_variant_set_canned_message_plugin_part4_MSGTYPE CannedMessagePluginMessagePart4
#define AdminMessage_variant_set_canned_message_plugin_part5_MSGTYPE CannedMessagePluginMessagePart5
extern const pb_msgdesc_t AdminMessage_msg;
@@ -145,7 +125,7 @@ extern const pb_msgdesc_t AdminMessage_msg;
#define AdminMessage_fields &AdminMessage_msg
/* Maximum encoded size of messages (where known) */
#define AdminMessage_size 804
#define AdminMessage_size 611
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -6,19 +6,7 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(CannedMessagePluginMessagePart1, CannedMessagePluginMessagePart1, AUTO)
PB_BIND(CannedMessagePluginMessagePart2, CannedMessagePluginMessagePart2, AUTO)
PB_BIND(CannedMessagePluginMessagePart3, CannedMessagePluginMessagePart3, AUTO)
PB_BIND(CannedMessagePluginMessagePart4, CannedMessagePluginMessagePart4, AUTO)
PB_BIND(CannedMessagePluginMessagePart5, CannedMessagePluginMessagePart5, AUTO)
PB_BIND(CannedMessageModuleConfig, CannedMessageModuleConfig, 2)

View File

@@ -10,25 +10,12 @@
#endif
/* Struct definitions */
typedef struct _CannedMessagePluginMessagePart1 {
char text[200];
} CannedMessagePluginMessagePart1;
typedef struct _CannedMessagePluginMessagePart2 {
char text[200];
} CannedMessagePluginMessagePart2;
typedef struct _CannedMessagePluginMessagePart3 {
char text[200];
} CannedMessagePluginMessagePart3;
typedef struct _CannedMessagePluginMessagePart4 {
char text[200];
} CannedMessagePluginMessagePart4;
typedef struct _CannedMessagePluginMessagePart5 {
char text[200];
} CannedMessagePluginMessagePart5;
typedef struct _CannedMessageModuleConfig {
char messagesPart1[201];
char messagesPart2[201];
char messagesPart3[201];
char messagesPart4[201];
} CannedMessageModuleConfig;
#ifdef __cplusplus
@@ -36,69 +23,31 @@ extern "C" {
#endif
/* Initializer values for message structs */
#define CannedMessagePluginMessagePart1_init_default {""}
#define CannedMessagePluginMessagePart2_init_default {""}
#define CannedMessagePluginMessagePart3_init_default {""}
#define CannedMessagePluginMessagePart4_init_default {""}
#define CannedMessagePluginMessagePart5_init_default {""}
#define CannedMessagePluginMessagePart1_init_zero {""}
#define CannedMessagePluginMessagePart2_init_zero {""}
#define CannedMessagePluginMessagePart3_init_zero {""}
#define CannedMessagePluginMessagePart4_init_zero {""}
#define CannedMessagePluginMessagePart5_init_zero {""}
#define CannedMessageModuleConfig_init_default {"", "", "", ""}
#define CannedMessageModuleConfig_init_zero {"", "", "", ""}
/* Field tags (for use in manual encoding/decoding) */
#define CannedMessagePluginMessagePart1_text_tag 1
#define CannedMessagePluginMessagePart2_text_tag 1
#define CannedMessagePluginMessagePart3_text_tag 1
#define CannedMessagePluginMessagePart4_text_tag 1
#define CannedMessagePluginMessagePart5_text_tag 1
#define CannedMessageModuleConfig_messagesPart1_tag 11
#define CannedMessageModuleConfig_messagesPart2_tag 12
#define CannedMessageModuleConfig_messagesPart3_tag 13
#define CannedMessageModuleConfig_messagesPart4_tag 14
/* Struct field encoding specification for nanopb */
#define CannedMessagePluginMessagePart1_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, text, 1)
#define CannedMessagePluginMessagePart1_CALLBACK NULL
#define CannedMessagePluginMessagePart1_DEFAULT NULL
#define CannedMessageModuleConfig_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, messagesPart1, 11) \
X(a, STATIC, SINGULAR, STRING, messagesPart2, 12) \
X(a, STATIC, SINGULAR, STRING, messagesPart3, 13) \
X(a, STATIC, SINGULAR, STRING, messagesPart4, 14)
#define CannedMessageModuleConfig_CALLBACK NULL
#define CannedMessageModuleConfig_DEFAULT NULL
#define CannedMessagePluginMessagePart2_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, text, 1)
#define CannedMessagePluginMessagePart2_CALLBACK NULL
#define CannedMessagePluginMessagePart2_DEFAULT NULL
#define CannedMessagePluginMessagePart3_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, text, 1)
#define CannedMessagePluginMessagePart3_CALLBACK NULL
#define CannedMessagePluginMessagePart3_DEFAULT NULL
#define CannedMessagePluginMessagePart4_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, text, 1)
#define CannedMessagePluginMessagePart4_CALLBACK NULL
#define CannedMessagePluginMessagePart4_DEFAULT NULL
#define CannedMessagePluginMessagePart5_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, text, 1)
#define CannedMessagePluginMessagePart5_CALLBACK NULL
#define CannedMessagePluginMessagePart5_DEFAULT NULL
extern const pb_msgdesc_t CannedMessagePluginMessagePart1_msg;
extern const pb_msgdesc_t CannedMessagePluginMessagePart2_msg;
extern const pb_msgdesc_t CannedMessagePluginMessagePart3_msg;
extern const pb_msgdesc_t CannedMessagePluginMessagePart4_msg;
extern const pb_msgdesc_t CannedMessagePluginMessagePart5_msg;
extern const pb_msgdesc_t CannedMessageModuleConfig_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define CannedMessagePluginMessagePart1_fields &CannedMessagePluginMessagePart1_msg
#define CannedMessagePluginMessagePart2_fields &CannedMessagePluginMessagePart2_msg
#define CannedMessagePluginMessagePart3_fields &CannedMessagePluginMessagePart3_msg
#define CannedMessagePluginMessagePart4_fields &CannedMessagePluginMessagePart4_msg
#define CannedMessagePluginMessagePart5_fields &CannedMessagePluginMessagePart5_msg
#define CannedMessageModuleConfig_fields &CannedMessageModuleConfig_msg
/* Maximum encoded size of messages (where known) */
#define CannedMessagePluginMessagePart1_size 202
#define CannedMessagePluginMessagePart2_size 202
#define CannedMessagePluginMessagePart3_size 202
#define CannedMessagePluginMessagePart4_size 202
#define CannedMessagePluginMessagePart5_size 202
#define CannedMessageModuleConfig_size 812
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -11,12 +11,13 @@
/* Enum definitions */
typedef enum _ChannelSettings_ModemConfig {
ChannelSettings_ModemConfig_Bw125Cr45Sf128 = 0,
ChannelSettings_ModemConfig_Bw500Cr45Sf128 = 1,
ChannelSettings_ModemConfig_Bw31_25Cr48Sf512 = 2,
ChannelSettings_ModemConfig_Bw125Cr48Sf4096 = 3,
ChannelSettings_ModemConfig_Bw250Cr46Sf2048 = 4,
ChannelSettings_ModemConfig_Bw250Cr47Sf1024 = 5
ChannelSettings_ModemConfig_VLongSlow = 0,
ChannelSettings_ModemConfig_LongSlow = 1,
ChannelSettings_ModemConfig_LongFast = 2,
ChannelSettings_ModemConfig_MidSlow = 3,
ChannelSettings_ModemConfig_MidFast = 4,
ChannelSettings_ModemConfig_ShortSlow = 5,
ChannelSettings_ModemConfig_ShortFast = 6
} ChannelSettings_ModemConfig;
typedef enum _Channel_Role {
@@ -50,9 +51,9 @@ typedef struct _Channel {
/* Helper constants for enums */
#define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_Bw125Cr45Sf128
#define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_Bw250Cr47Sf1024
#define _ChannelSettings_ModemConfig_ARRAYSIZE ((ChannelSettings_ModemConfig)(ChannelSettings_ModemConfig_Bw250Cr47Sf1024+1))
#define _ChannelSettings_ModemConfig_MIN ChannelSettings_ModemConfig_VLongSlow
#define _ChannelSettings_ModemConfig_MAX ChannelSettings_ModemConfig_ShortFast
#define _ChannelSettings_ModemConfig_ARRAYSIZE ((ChannelSettings_ModemConfig)(ChannelSettings_ModemConfig_ShortFast+1))
#define _Channel_Role_MIN Channel_Role_DISABLED
#define _Channel_Role_MAX Channel_Role_SECONDARY

View File

@@ -6,12 +6,6 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(LegacyRadioConfig, LegacyRadioConfig, AUTO)
PB_BIND(LegacyRadioConfig_LegacyPreferences, LegacyRadioConfig_LegacyPreferences, AUTO)
PB_BIND(DeviceState, DeviceState, 4)

View File

@@ -6,7 +6,6 @@
#include <pb.h>
#include "channel.pb.h"
#include "mesh.pb.h"
#include "radioconfig.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
@@ -18,18 +17,7 @@ typedef struct _ChannelFile {
Channel channels[8];
} ChannelFile;
typedef struct _LegacyRadioConfig_LegacyPreferences {
RegionCode region;
} LegacyRadioConfig_LegacyPreferences;
typedef struct _LegacyRadioConfig {
bool has_preferences;
LegacyRadioConfig_LegacyPreferences preferences;
} LegacyRadioConfig;
typedef struct _DeviceState {
bool has_legacyRadio;
LegacyRadioConfig legacyRadio;
bool has_my_node;
MyNodeInfo my_node;
bool has_owner;
@@ -43,11 +31,6 @@ typedef struct _DeviceState {
uint32_t version;
bool no_save;
bool did_gps_reset;
char canned_message_plugin_message_part1[200];
char canned_message_plugin_message_part2[200];
char canned_message_plugin_message_part3[200];
char canned_message_plugin_message_part4[200];
char canned_message_plugin_message_part5[200];
} DeviceState;
@@ -56,20 +39,13 @@ extern "C" {
#endif
/* Initializer values for message structs */
#define LegacyRadioConfig_init_default {false, LegacyRadioConfig_LegacyPreferences_init_default}
#define LegacyRadioConfig_LegacyPreferences_init_default {_RegionCode_MIN}
#define DeviceState_init_default {false, LegacyRadioConfig_init_default, false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0, "", "", "", "", ""}
#define DeviceState_init_default {false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0}
#define ChannelFile_init_default {0, {Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default}}
#define LegacyRadioConfig_init_zero {false, LegacyRadioConfig_LegacyPreferences_init_zero}
#define LegacyRadioConfig_LegacyPreferences_init_zero {_RegionCode_MIN}
#define DeviceState_init_zero {false, LegacyRadioConfig_init_zero, false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0, "", "", "", "", ""}
#define DeviceState_init_zero {false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0}
#define ChannelFile_init_zero {0, {Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero}}
/* Field tags (for use in manual encoding/decoding) */
#define ChannelFile_channels_tag 1
#define LegacyRadioConfig_LegacyPreferences_region_tag 15
#define LegacyRadioConfig_preferences_tag 1
#define DeviceState_legacyRadio_tag 1
#define DeviceState_my_node_tag 2
#define DeviceState_owner_tag 3
#define DeviceState_node_db_tag 4
@@ -78,26 +54,9 @@ extern "C" {
#define DeviceState_version_tag 8
#define DeviceState_no_save_tag 9
#define DeviceState_did_gps_reset_tag 11
#define DeviceState_canned_message_plugin_message_part1_tag 13
#define DeviceState_canned_message_plugin_message_part2_tag 14
#define DeviceState_canned_message_plugin_message_part3_tag 15
#define DeviceState_canned_message_plugin_message_part4_tag 16
#define DeviceState_canned_message_plugin_message_part5_tag 17
/* Struct field encoding specification for nanopb */
#define LegacyRadioConfig_FIELDLIST(X, a) \
X(a, STATIC, OPTIONAL, MESSAGE, preferences, 1)
#define LegacyRadioConfig_CALLBACK NULL
#define LegacyRadioConfig_DEFAULT NULL
#define LegacyRadioConfig_preferences_MSGTYPE LegacyRadioConfig_LegacyPreferences
#define LegacyRadioConfig_LegacyPreferences_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, region, 15)
#define LegacyRadioConfig_LegacyPreferences_CALLBACK NULL
#define LegacyRadioConfig_LegacyPreferences_DEFAULT NULL
#define DeviceState_FIELDLIST(X, a) \
X(a, STATIC, OPTIONAL, MESSAGE, legacyRadio, 1) \
X(a, STATIC, OPTIONAL, MESSAGE, my_node, 2) \
X(a, STATIC, OPTIONAL, MESSAGE, owner, 3) \
X(a, STATIC, REPEATED, MESSAGE, node_db, 4) \
@@ -105,15 +64,9 @@ X(a, STATIC, REPEATED, MESSAGE, receive_queue, 5) \
X(a, STATIC, OPTIONAL, MESSAGE, rx_text_message, 7) \
X(a, STATIC, SINGULAR, UINT32, version, 8) \
X(a, STATIC, SINGULAR, BOOL, no_save, 9) \
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11) \
X(a, STATIC, SINGULAR, STRING, canned_message_plugin_message_part1, 13) \
X(a, STATIC, SINGULAR, STRING, canned_message_plugin_message_part2, 14) \
X(a, STATIC, SINGULAR, STRING, canned_message_plugin_message_part3, 15) \
X(a, STATIC, SINGULAR, STRING, canned_message_plugin_message_part4, 16) \
X(a, STATIC, SINGULAR, STRING, canned_message_plugin_message_part5, 17)
X(a, STATIC, SINGULAR, BOOL, did_gps_reset, 11)
#define DeviceState_CALLBACK NULL
#define DeviceState_DEFAULT NULL
#define DeviceState_legacyRadio_MSGTYPE LegacyRadioConfig
#define DeviceState_my_node_MSGTYPE MyNodeInfo
#define DeviceState_owner_MSGTYPE User
#define DeviceState_node_db_MSGTYPE NodeInfo
@@ -126,21 +79,15 @@ X(a, STATIC, REPEATED, MESSAGE, channels, 1)
#define ChannelFile_DEFAULT NULL
#define ChannelFile_channels_MSGTYPE Channel
extern const pb_msgdesc_t LegacyRadioConfig_msg;
extern const pb_msgdesc_t LegacyRadioConfig_LegacyPreferences_msg;
extern const pb_msgdesc_t DeviceState_msg;
extern const pb_msgdesc_t ChannelFile_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define LegacyRadioConfig_fields &LegacyRadioConfig_msg
#define LegacyRadioConfig_LegacyPreferences_fields &LegacyRadioConfig_LegacyPreferences_msg
#define DeviceState_fields &DeviceState_msg
#define ChannelFile_fields &ChannelFile_msg
/* Maximum encoded size of messages (where known) */
#define LegacyRadioConfig_size 4
#define LegacyRadioConfig_LegacyPreferences_size 2
#define DeviceState_size 11014
#define DeviceState_size 9885
#define ChannelFile_size 832
#ifdef __cplusplus

View File

@@ -27,7 +27,7 @@ PB_BIND(MeshPacket, MeshPacket, 2)
PB_BIND(NodeInfo, NodeInfo, AUTO)
PB_BIND(MyNodeInfo, MyNodeInfo, 2)
PB_BIND(MyNodeInfo, MyNodeInfo, AUTO)
PB_BIND(LogRecord, LogRecord, AUTO)

View File

@@ -152,9 +152,7 @@ typedef struct _LogRecord {
typedef struct _MyNodeInfo {
uint32_t my_node_num;
bool has_gps;
uint32_t num_bands;
char region[12];
char hw_model_deprecated[16];
char firmware_version[18];
CriticalErrorCode error_code;
uint32_t error_address;
@@ -165,12 +163,18 @@ typedef struct _MyNodeInfo {
uint32_t min_app_version;
uint32_t max_channels;
pb_size_t air_period_tx_count;
uint32_t air_period_tx[24];
uint32_t air_period_tx[8];
pb_size_t air_period_rx_count;
uint32_t air_period_rx[24];
uint32_t air_period_rx[8];
bool has_wifi;
float channel_utilization;
float air_util_tx;
pb_size_t router_count;
uint32_t router[4];
pb_size_t router_snr_count;
float router_snr[4];
pb_size_t router_sec_count;
uint16_t router_sec[4];
} MyNodeInfo;
typedef struct _Position {
@@ -178,6 +182,7 @@ typedef struct _Position {
int32_t longitude_i;
int32_t altitude;
int32_t battery_level;
bool router_heartbeat;
uint32_t time;
Position_LocSource location_source;
Position_AltSource altitude_source;
@@ -332,26 +337,26 @@ extern "C" {
#endif
/* Initializer values for message structs */
#define Position_init_default {0, 0, 0, 0, 0, _Position_LocSource_MIN, _Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define Position_init_default {0, 0, 0, 0, 0, 0, _Position_LocSource_MIN, _Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define User_init_default {"", "", "", {0}, _HardwareModel_MIN, 0, _Team_MIN, 0, 0, 0}
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_default {0, {RouteDiscovery_init_default}}
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0}
#define MyNodeInfo_init_default {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0}
#define MyNodeInfo_init_default {0, 0, "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0, 0, {0, 0, 0, 0}, 0, {0, 0, 0, 0}, 0, {0, 0, 0, 0}}
#define LogRecord_init_default {"", 0, "", _LogRecord_Level_MIN}
#define FromRadio_init_default {0, 0, {MyNodeInfo_init_default}}
#define ToRadio_init_default {0, {MeshPacket_init_default}}
#define ToRadio_PeerInfo_init_default {0, 0}
#define Position_init_zero {0, 0, 0, 0, 0, _Position_LocSource_MIN, _Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define Position_init_zero {0, 0, 0, 0, 0, 0, _Position_LocSource_MIN, _Position_AltSource_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define User_init_zero {"", "", "", {0}, _HardwareModel_MIN, 0, _Team_MIN, 0, 0, 0}
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_zero {0, {RouteDiscovery_init_zero}}
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0}
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0}
#define MyNodeInfo_init_zero {0, 0, 0, "", "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0}
#define MyNodeInfo_init_zero {0, 0, "", "", _CriticalErrorCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, {0, 0, 0, 0, 0, 0, 0, 0}, 0, 0, 0, 0, {0, 0, 0, 0}, 0, {0, 0, 0, 0}, 0, {0, 0, 0, 0}}
#define LogRecord_init_zero {"", 0, "", _LogRecord_Level_MIN}
#define FromRadio_init_zero {0, 0, {MyNodeInfo_init_zero}}
#define ToRadio_init_zero {0, {MeshPacket_init_zero}}
@@ -372,9 +377,7 @@ extern "C" {
#define LogRecord_level_tag 4
#define MyNodeInfo_my_node_num_tag 1
#define MyNodeInfo_has_gps_tag 2
#define MyNodeInfo_num_bands_tag 3
#define MyNodeInfo_region_tag 4
#define MyNodeInfo_hw_model_deprecated_tag 5
#define MyNodeInfo_firmware_version_tag 6
#define MyNodeInfo_error_code_tag 7
#define MyNodeInfo_error_address_tag 8
@@ -389,10 +392,14 @@ extern "C" {
#define MyNodeInfo_has_wifi_tag 18
#define MyNodeInfo_channel_utilization_tag 19
#define MyNodeInfo_air_util_tx_tag 20
#define MyNodeInfo_router_tag 21
#define MyNodeInfo_router_snr_tag 22
#define MyNodeInfo_router_sec_tag 23
#define Position_latitude_i_tag 1
#define Position_longitude_i_tag 2
#define Position_altitude_tag 3
#define Position_battery_level_tag 4
#define Position_router_heartbeat_tag 5
#define Position_time_tag 9
#define Position_location_source_tag 10
#define Position_altitude_source_tag 11
@@ -464,6 +471,7 @@ X(a, STATIC, SINGULAR, SFIXED32, latitude_i, 1) \
X(a, STATIC, SINGULAR, SFIXED32, longitude_i, 2) \
X(a, STATIC, SINGULAR, INT32, altitude, 3) \
X(a, STATIC, SINGULAR, INT32, battery_level, 4) \
X(a, STATIC, SINGULAR, BOOL, router_heartbeat, 5) \
X(a, STATIC, SINGULAR, FIXED32, time, 9) \
X(a, STATIC, SINGULAR, UENUM, location_source, 10) \
X(a, STATIC, SINGULAR, UENUM, altitude_source, 11) \
@@ -558,9 +566,7 @@ X(a, STATIC, SINGULAR, FLOAT, snr, 7)
#define MyNodeInfo_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, my_node_num, 1) \
X(a, STATIC, SINGULAR, BOOL, has_gps, 2) \
X(a, STATIC, SINGULAR, UINT32, num_bands, 3) \
X(a, STATIC, SINGULAR, STRING, region, 4) \
X(a, STATIC, SINGULAR, STRING, hw_model_deprecated, 5) \
X(a, STATIC, SINGULAR, STRING, firmware_version, 6) \
X(a, STATIC, SINGULAR, UENUM, error_code, 7) \
X(a, STATIC, SINGULAR, UINT32, error_address, 8) \
@@ -574,7 +580,10 @@ X(a, STATIC, REPEATED, UINT32, air_period_tx, 16) \
X(a, STATIC, REPEATED, UINT32, air_period_rx, 17) \
X(a, STATIC, SINGULAR, BOOL, has_wifi, 18) \
X(a, STATIC, SINGULAR, FLOAT, channel_utilization, 19) \
X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 20)
X(a, STATIC, SINGULAR, FLOAT, air_util_tx, 20) \
X(a, STATIC, REPEATED, UINT32, router, 21) \
X(a, STATIC, REPEATED, FLOAT, router_snr, 22) \
X(a, STATIC, REPEATED, UINT32, router_sec, 23)
#define MyNodeInfo_CALLBACK NULL
#define MyNodeInfo_DEFAULT NULL
@@ -645,16 +654,16 @@ extern const pb_msgdesc_t ToRadio_PeerInfo_msg;
#define ToRadio_PeerInfo_fields &ToRadio_PeerInfo_msg
/* Maximum encoded size of messages (where known) */
#define Position_size 153
#define Position_size 155
#define User_size 97
#define RouteDiscovery_size 40
#define Routing_size 42
#define Data_size 267
#define MeshPacket_size 318
#define NodeInfo_size 271
#define MyNodeInfo_size 457
#define NodeInfo_size 273
#define MyNodeInfo_size 282
#define LogRecord_size 81
#define FromRadio_size 466
#define FromRadio_size 327
#define ToRadio_size 321
#define ToRadio_PeerInfo_size 8

View File

@@ -23,7 +23,7 @@ typedef enum _PortNum {
PortNum_SERIAL_APP = 64,
PortNum_STORE_FORWARD_APP = 65,
PortNum_RANGE_TEST_APP = 66,
PortNum_ENVIRONMENTAL_MEASUREMENT_APP = 67,
PortNum_TELEMETRY_APP = 67,
PortNum_ZPS_APP = 68,
PortNum_PRIVATE_APP = 256,
PortNum_ATAK_FORWARDER = 257,

View File

@@ -21,3 +21,4 @@ PB_BIND(RadioConfig_UserPreferences, RadioConfig_UserPreferences, 2)

View File

@@ -14,15 +14,24 @@ typedef enum _RegionCode {
RegionCode_Unset = 0,
RegionCode_US = 1,
RegionCode_EU433 = 2,
RegionCode_EU865 = 3,
RegionCode_EU868 = 3,
RegionCode_CN = 4,
RegionCode_JP = 5,
RegionCode_ANZ = 6,
RegionCode_KR = 7,
RegionCode_TW = 8,
RegionCode_RU = 9
RegionCode_RU = 9,
RegionCode_IN = 10,
RegionCode_NZ865 = 11,
RegionCode_TH = 12
} RegionCode;
typedef enum _Role {
Role_Default = 0,
Role_Router = 1,
Role_Repeater = 2
} Role;
typedef enum _ChargeCurrent {
ChargeCurrent_MAUnset = 0,
ChargeCurrent_MA100 = 1,
@@ -90,16 +99,17 @@ typedef enum _InputEventChar {
InputEventChar_KEY_CANCEL = 24
} InputEventChar;
typedef enum _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType {
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11 = 0,
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20 = 1,
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT12 = 2,
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT21 = 3,
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT22 = 4,
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_BME280 = 5,
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_BME680 = 6,
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MCP9808 = 7
} RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType;
typedef enum _RadioConfig_UserPreferences_TelemetrySensorType {
RadioConfig_UserPreferences_TelemetrySensorType_DHT11 = 0,
RadioConfig_UserPreferences_TelemetrySensorType_DS18B20 = 1,
RadioConfig_UserPreferences_TelemetrySensorType_DHT12 = 2,
RadioConfig_UserPreferences_TelemetrySensorType_DHT21 = 3,
RadioConfig_UserPreferences_TelemetrySensorType_DHT22 = 4,
RadioConfig_UserPreferences_TelemetrySensorType_BME280 = 5,
RadioConfig_UserPreferences_TelemetrySensorType_BME680 = 6,
RadioConfig_UserPreferences_TelemetrySensorType_MCP9808 = 7,
RadioConfig_UserPreferences_TelemetrySensorType_SHTC3 = 8
} RadioConfig_UserPreferences_TelemetrySensorType;
/* Struct definitions */
typedef struct _RadioConfig_UserPreferences {
@@ -119,6 +129,7 @@ typedef struct _RadioConfig_UserPreferences {
RegionCode region;
ChargeCurrent charge_current;
bool position_broadcast_smart;
Role role;
LocationSharing location_share;
GpsOperation gps_operation;
uint32_t gps_update_interval;
@@ -137,34 +148,34 @@ typedef struct _RadioConfig_UserPreferences {
bool debug_log_enabled;
pb_size_t ignore_incoming_count;
uint32_t ignore_incoming[3];
bool serialplugin_enabled;
bool serialplugin_echo;
uint32_t serialplugin_rxd;
uint32_t serialplugin_txd;
uint32_t serialplugin_timeout;
uint32_t serialplugin_mode;
bool ext_notification_plugin_enabled;
uint32_t ext_notification_plugin_output_ms;
uint32_t ext_notification_plugin_output;
bool ext_notification_plugin_active;
bool ext_notification_plugin_alert_message;
bool ext_notification_plugin_alert_bell;
bool range_test_plugin_enabled;
uint32_t range_test_plugin_sender;
bool range_test_plugin_save;
uint32_t store_forward_plugin_records;
uint32_t store_forward_plugin_history_return_max;
uint32_t store_forward_plugin_history_return_window;
bool environmental_measurement_plugin_measurement_enabled;
bool environmental_measurement_plugin_screen_enabled;
uint32_t environmental_measurement_plugin_read_error_count_threshold;
uint32_t environmental_measurement_plugin_update_interval;
uint32_t environmental_measurement_plugin_recovery_interval;
bool environmental_measurement_plugin_display_farenheit;
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType environmental_measurement_plugin_sensor_type;
uint32_t environmental_measurement_plugin_sensor_pin;
bool store_forward_plugin_enabled;
bool store_forward_plugin_heartbeat;
bool serialmodule_enabled;
bool serialmodule_echo;
uint32_t serialmodule_rxd;
uint32_t serialmodule_txd;
uint32_t serialmodule_timeout;
uint32_t serialmodule_mode;
bool ext_notification_module_enabled;
uint32_t ext_notification_module_output_ms;
uint32_t ext_notification_module_output;
bool ext_notification_module_active;
bool ext_notification_module_alert_message;
bool ext_notification_module_alert_bell;
bool range_test_module_enabled;
uint32_t range_test_module_sender;
bool range_test_module_save;
uint32_t store_forward_module_records;
uint32_t store_forward_module_history_return_max;
uint32_t store_forward_module_history_return_window;
bool telemetry_module_measurement_enabled;
bool telemetry_module_screen_enabled;
uint32_t telemetry_module_read_error_count_threshold;
uint32_t telemetry_module_update_interval;
uint32_t telemetry_module_recovery_interval;
bool telemetry_module_display_farenheit;
RadioConfig_UserPreferences_TelemetrySensorType telemetry_module_sensor_type;
uint32_t telemetry_module_sensor_pin;
bool store_forward_module_enabled;
bool store_forward_module_heartbeat;
uint32_t position_flags;
bool is_always_powered;
uint32_t auto_screen_carousel_secs;
@@ -181,12 +192,12 @@ typedef struct _RadioConfig_UserPreferences {
InputEventChar rotary1_event_cw;
InputEventChar rotary1_event_ccw;
InputEventChar rotary1_event_press;
bool canned_message_plugin_enabled;
char canned_message_plugin_allow_input_source[16];
char canned_message_plugin_messages[200];
bool canned_message_plugin_send_bell;
bool canned_message_module_enabled;
char canned_message_module_allow_input_source[16];
bool canned_message_module_send_bell;
bool mqtt_encryption_enabled;
float adc_multiplier_override;
uint32_t serialmodule_baud;
} RadioConfig_UserPreferences;
typedef struct _RadioConfig {
@@ -197,8 +208,12 @@ typedef struct _RadioConfig {
/* Helper constants for enums */
#define _RegionCode_MIN RegionCode_Unset
#define _RegionCode_MAX RegionCode_RU
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_RU+1))
#define _RegionCode_MAX RegionCode_TH
#define _RegionCode_ARRAYSIZE ((RegionCode)(RegionCode_TH+1))
#define _Role_MIN Role_Default
#define _Role_MAX Role_Repeater
#define _Role_ARRAYSIZE ((Role)(Role_Repeater+1))
#define _ChargeCurrent_MIN ChargeCurrent_MAUnset
#define _ChargeCurrent_MAX ChargeCurrent_MA1320
@@ -224,9 +239,9 @@ typedef struct _RadioConfig {
#define _InputEventChar_MAX InputEventChar_KEY_BACK
#define _InputEventChar_ARRAYSIZE ((InputEventChar)(InputEventChar_KEY_BACK+1))
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MAX RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MCP9808
#define _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_ARRAYSIZE ((RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType)(RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MCP9808+1))
#define _RadioConfig_UserPreferences_TelemetrySensorType_MIN RadioConfig_UserPreferences_TelemetrySensorType_DHT11
#define _RadioConfig_UserPreferences_TelemetrySensorType_MAX RadioConfig_UserPreferences_TelemetrySensorType_SHTC3
#define _RadioConfig_UserPreferences_TelemetrySensorType_ARRAYSIZE ((RadioConfig_UserPreferences_TelemetrySensorType)(RadioConfig_UserPreferences_TelemetrySensorType_SHTC3+1))
#ifdef __cplusplus
@@ -235,9 +250,9 @@ extern "C" {
/* Initializer values for message structs */
#define RadioConfig_init_default {false, RadioConfig_UserPreferences_init_default}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, 0, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, 0, 0, 0, 0, _InputEventChar_MIN, _InputEventChar_MIN, _InputEventChar_MIN, 0, "", "", 0, 0, 0}
#define RadioConfig_UserPreferences_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, 0, _Role_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_TelemetrySensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, 0, 0, 0, 0, _InputEventChar_MIN, _InputEventChar_MIN, _InputEventChar_MIN, 0, "", 0, 0, 0, 0}
#define RadioConfig_init_zero {false, RadioConfig_UserPreferences_init_zero}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, 0, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, 0, 0, 0, 0, _InputEventChar_MIN, _InputEventChar_MIN, _InputEventChar_MIN, 0, "", "", 0, 0, 0}
#define RadioConfig_UserPreferences_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, _RegionCode_MIN, _ChargeCurrent_MIN, 0, _Role_MIN, _LocationSharing_MIN, _GpsOperation_MIN, 0, 0, 0, 0, 0, 0, 0, "", 0, _GpsCoordinateFormat_MIN, 0, 0, 0, 0, 0, {0, 0, 0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _RadioConfig_UserPreferences_TelemetrySensorType_MIN, 0, 0, 0, 0, 0, 0, 0, 0, "", "", 0, 0, 0, 0, 0, 0, _InputEventChar_MIN, _InputEventChar_MIN, _InputEventChar_MIN, 0, "", 0, 0, 0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define RadioConfig_UserPreferences_position_broadcast_secs_tag 1
@@ -256,6 +271,7 @@ extern "C" {
#define RadioConfig_UserPreferences_region_tag 15
#define RadioConfig_UserPreferences_charge_current_tag 16
#define RadioConfig_UserPreferences_position_broadcast_smart_tag 17
#define RadioConfig_UserPreferences_role_tag 18
#define RadioConfig_UserPreferences_location_share_tag 32
#define RadioConfig_UserPreferences_gps_operation_tag 33
#define RadioConfig_UserPreferences_gps_update_interval_tag 34
@@ -273,34 +289,34 @@ extern "C" {
#define RadioConfig_UserPreferences_factory_reset_tag 100
#define RadioConfig_UserPreferences_debug_log_enabled_tag 101
#define RadioConfig_UserPreferences_ignore_incoming_tag 103
#define RadioConfig_UserPreferences_serialplugin_enabled_tag 120
#define RadioConfig_UserPreferences_serialplugin_echo_tag 121
#define RadioConfig_UserPreferences_serialplugin_rxd_tag 122
#define RadioConfig_UserPreferences_serialplugin_txd_tag 123
#define RadioConfig_UserPreferences_serialplugin_timeout_tag 124
#define RadioConfig_UserPreferences_serialplugin_mode_tag 125
#define RadioConfig_UserPreferences_ext_notification_plugin_enabled_tag 126
#define RadioConfig_UserPreferences_ext_notification_plugin_output_ms_tag 127
#define RadioConfig_UserPreferences_ext_notification_plugin_output_tag 128
#define RadioConfig_UserPreferences_ext_notification_plugin_active_tag 129
#define RadioConfig_UserPreferences_ext_notification_plugin_alert_message_tag 130
#define RadioConfig_UserPreferences_ext_notification_plugin_alert_bell_tag 131
#define RadioConfig_UserPreferences_range_test_plugin_enabled_tag 132
#define RadioConfig_UserPreferences_range_test_plugin_sender_tag 133
#define RadioConfig_UserPreferences_range_test_plugin_save_tag 134
#define RadioConfig_UserPreferences_store_forward_plugin_records_tag 137
#define RadioConfig_UserPreferences_store_forward_plugin_history_return_max_tag 138
#define RadioConfig_UserPreferences_store_forward_plugin_history_return_window_tag 139
#define RadioConfig_UserPreferences_environmental_measurement_plugin_measurement_enabled_tag 140
#define RadioConfig_UserPreferences_environmental_measurement_plugin_screen_enabled_tag 141
#define RadioConfig_UserPreferences_environmental_measurement_plugin_read_error_count_threshold_tag 142
#define RadioConfig_UserPreferences_environmental_measurement_plugin_update_interval_tag 143
#define RadioConfig_UserPreferences_environmental_measurement_plugin_recovery_interval_tag 144
#define RadioConfig_UserPreferences_environmental_measurement_plugin_display_farenheit_tag 145
#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_type_tag 146
#define RadioConfig_UserPreferences_environmental_measurement_plugin_sensor_pin_tag 147
#define RadioConfig_UserPreferences_store_forward_plugin_enabled_tag 148
#define RadioConfig_UserPreferences_store_forward_plugin_heartbeat_tag 149
#define RadioConfig_UserPreferences_serialmodule_enabled_tag 120
#define RadioConfig_UserPreferences_serialmodule_echo_tag 121
#define RadioConfig_UserPreferences_serialmodule_rxd_tag 122
#define RadioConfig_UserPreferences_serialmodule_txd_tag 123
#define RadioConfig_UserPreferences_serialmodule_timeout_tag 124
#define RadioConfig_UserPreferences_serialmodule_mode_tag 125
#define RadioConfig_UserPreferences_ext_notification_module_enabled_tag 126
#define RadioConfig_UserPreferences_ext_notification_module_output_ms_tag 127
#define RadioConfig_UserPreferences_ext_notification_module_output_tag 128
#define RadioConfig_UserPreferences_ext_notification_module_active_tag 129
#define RadioConfig_UserPreferences_ext_notification_module_alert_message_tag 130
#define RadioConfig_UserPreferences_ext_notification_module_alert_bell_tag 131
#define RadioConfig_UserPreferences_range_test_module_enabled_tag 132
#define RadioConfig_UserPreferences_range_test_module_sender_tag 133
#define RadioConfig_UserPreferences_range_test_module_save_tag 134
#define RadioConfig_UserPreferences_store_forward_module_records_tag 137
#define RadioConfig_UserPreferences_store_forward_module_history_return_max_tag 138
#define RadioConfig_UserPreferences_store_forward_module_history_return_window_tag 139
#define RadioConfig_UserPreferences_telemetry_module_measurement_enabled_tag 140
#define RadioConfig_UserPreferences_telemetry_module_screen_enabled_tag 141
#define RadioConfig_UserPreferences_telemetry_module_read_error_count_threshold_tag 142
#define RadioConfig_UserPreferences_telemetry_module_update_interval_tag 143
#define RadioConfig_UserPreferences_telemetry_module_recovery_interval_tag 144
#define RadioConfig_UserPreferences_telemetry_module_display_farenheit_tag 145
#define RadioConfig_UserPreferences_telemetry_module_sensor_type_tag 146
#define RadioConfig_UserPreferences_telemetry_module_sensor_pin_tag 147
#define RadioConfig_UserPreferences_store_forward_module_enabled_tag 148
#define RadioConfig_UserPreferences_store_forward_module_heartbeat_tag 149
#define RadioConfig_UserPreferences_position_flags_tag 150
#define RadioConfig_UserPreferences_is_always_powered_tag 151
#define RadioConfig_UserPreferences_auto_screen_carousel_secs_tag 152
@@ -317,12 +333,12 @@ extern "C" {
#define RadioConfig_UserPreferences_rotary1_event_cw_tag 164
#define RadioConfig_UserPreferences_rotary1_event_ccw_tag 165
#define RadioConfig_UserPreferences_rotary1_event_press_tag 166
#define RadioConfig_UserPreferences_canned_message_plugin_enabled_tag 170
#define RadioConfig_UserPreferences_canned_message_plugin_allow_input_source_tag 171
#define RadioConfig_UserPreferences_canned_message_plugin_messages_tag 172
#define RadioConfig_UserPreferences_canned_message_plugin_send_bell_tag 173
#define RadioConfig_UserPreferences_canned_message_module_enabled_tag 170
#define RadioConfig_UserPreferences_canned_message_module_allow_input_source_tag 171
#define RadioConfig_UserPreferences_canned_message_module_send_bell_tag 173
#define RadioConfig_UserPreferences_mqtt_encryption_enabled_tag 174
#define RadioConfig_UserPreferences_adc_multiplier_override_tag 175
#define RadioConfig_UserPreferences_serialmodule_baud_tag 176
#define RadioConfig_preferences_tag 1
/* Struct field encoding specification for nanopb */
@@ -349,6 +365,7 @@ X(a, STATIC, SINGULAR, BOOL, wifi_ap_mode, 14) \
X(a, STATIC, SINGULAR, UENUM, region, 15) \
X(a, STATIC, SINGULAR, UENUM, charge_current, 16) \
X(a, STATIC, SINGULAR, BOOL, position_broadcast_smart, 17) \
X(a, STATIC, SINGULAR, UENUM, role, 18) \
X(a, STATIC, SINGULAR, UENUM, location_share, 32) \
X(a, STATIC, SINGULAR, UENUM, gps_operation, 33) \
X(a, STATIC, SINGULAR, UINT32, gps_update_interval, 34) \
@@ -366,34 +383,34 @@ X(a, STATIC, SINGULAR, UINT32, gps_max_dop, 46) \
X(a, STATIC, SINGULAR, BOOL, factory_reset, 100) \
X(a, STATIC, SINGULAR, BOOL, debug_log_enabled, 101) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
X(a, STATIC, SINGULAR, BOOL, serialplugin_enabled, 120) \
X(a, STATIC, SINGULAR, BOOL, serialplugin_echo, 121) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_rxd, 122) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_txd, 123) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_timeout, 124) \
X(a, STATIC, SINGULAR, UINT32, serialplugin_mode, 125) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_enabled, 126) \
X(a, STATIC, SINGULAR, UINT32, ext_notification_plugin_output_ms, 127) \
X(a, STATIC, SINGULAR, UINT32, ext_notification_plugin_output, 128) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_active, 129) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_message, 130) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_plugin_alert_bell, 131) \
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_enabled, 132) \
X(a, STATIC, SINGULAR, UINT32, range_test_plugin_sender, 133) \
X(a, STATIC, SINGULAR, BOOL, range_test_plugin_save, 134) \
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_records, 137) \
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_history_return_max, 138) \
X(a, STATIC, SINGULAR, UINT32, store_forward_plugin_history_return_window, 139) \
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_measurement_enabled, 140) \
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_screen_enabled, 141) \
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_read_error_count_threshold, 142) \
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_update_interval, 143) \
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_recovery_interval, 144) \
X(a, STATIC, SINGULAR, BOOL, environmental_measurement_plugin_display_farenheit, 145) \
X(a, STATIC, SINGULAR, UENUM, environmental_measurement_plugin_sensor_type, 146) \
X(a, STATIC, SINGULAR, UINT32, environmental_measurement_plugin_sensor_pin, 147) \
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_enabled, 148) \
X(a, STATIC, SINGULAR, BOOL, store_forward_plugin_heartbeat, 149) \
X(a, STATIC, SINGULAR, BOOL, serialmodule_enabled, 120) \
X(a, STATIC, SINGULAR, BOOL, serialmodule_echo, 121) \
X(a, STATIC, SINGULAR, UINT32, serialmodule_rxd, 122) \
X(a, STATIC, SINGULAR, UINT32, serialmodule_txd, 123) \
X(a, STATIC, SINGULAR, UINT32, serialmodule_timeout, 124) \
X(a, STATIC, SINGULAR, UINT32, serialmodule_mode, 125) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_module_enabled, 126) \
X(a, STATIC, SINGULAR, UINT32, ext_notification_module_output_ms, 127) \
X(a, STATIC, SINGULAR, UINT32, ext_notification_module_output, 128) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_module_active, 129) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_module_alert_message, 130) \
X(a, STATIC, SINGULAR, BOOL, ext_notification_module_alert_bell, 131) \
X(a, STATIC, SINGULAR, BOOL, range_test_module_enabled, 132) \
X(a, STATIC, SINGULAR, UINT32, range_test_module_sender, 133) \
X(a, STATIC, SINGULAR, BOOL, range_test_module_save, 134) \
X(a, STATIC, SINGULAR, UINT32, store_forward_module_records, 137) \
X(a, STATIC, SINGULAR, UINT32, store_forward_module_history_return_max, 138) \
X(a, STATIC, SINGULAR, UINT32, store_forward_module_history_return_window, 139) \
X(a, STATIC, SINGULAR, BOOL, telemetry_module_measurement_enabled, 140) \
X(a, STATIC, SINGULAR, BOOL, telemetry_module_screen_enabled, 141) \
X(a, STATIC, SINGULAR, UINT32, telemetry_module_read_error_count_threshold, 142) \
X(a, STATIC, SINGULAR, UINT32, telemetry_module_update_interval, 143) \
X(a, STATIC, SINGULAR, UINT32, telemetry_module_recovery_interval, 144) \
X(a, STATIC, SINGULAR, BOOL, telemetry_module_display_farenheit, 145) \
X(a, STATIC, SINGULAR, UENUM, telemetry_module_sensor_type, 146) \
X(a, STATIC, SINGULAR, UINT32, telemetry_module_sensor_pin, 147) \
X(a, STATIC, SINGULAR, BOOL, store_forward_module_enabled, 148) \
X(a, STATIC, SINGULAR, BOOL, store_forward_module_heartbeat, 149) \
X(a, STATIC, SINGULAR, UINT32, position_flags, 150) \
X(a, STATIC, SINGULAR, BOOL, is_always_powered, 151) \
X(a, STATIC, SINGULAR, UINT32, auto_screen_carousel_secs, 152) \
@@ -410,12 +427,12 @@ X(a, STATIC, SINGULAR, UINT32, rotary1_pin_press, 163) \
X(a, STATIC, SINGULAR, UENUM, rotary1_event_cw, 164) \
X(a, STATIC, SINGULAR, UENUM, rotary1_event_ccw, 165) \
X(a, STATIC, SINGULAR, UENUM, rotary1_event_press, 166) \
X(a, STATIC, SINGULAR, BOOL, canned_message_plugin_enabled, 170) \
X(a, STATIC, SINGULAR, STRING, canned_message_plugin_allow_input_source, 171) \
X(a, STATIC, SINGULAR, STRING, canned_message_plugin_messages, 172) \
X(a, STATIC, SINGULAR, BOOL, canned_message_plugin_send_bell, 173) \
X(a, STATIC, SINGULAR, BOOL, canned_message_module_enabled, 170) \
X(a, STATIC, SINGULAR, STRING, canned_message_module_allow_input_source, 171) \
X(a, STATIC, SINGULAR, BOOL, canned_message_module_send_bell, 173) \
X(a, STATIC, SINGULAR, BOOL, mqtt_encryption_enabled, 174) \
X(a, STATIC, SINGULAR, FLOAT, adc_multiplier_override, 175)
X(a, STATIC, SINGULAR, FLOAT, adc_multiplier_override, 175) \
X(a, STATIC, SINGULAR, UINT32, serialmodule_baud, 176)
#define RadioConfig_UserPreferences_CALLBACK NULL
#define RadioConfig_UserPreferences_DEFAULT NULL
@@ -427,8 +444,8 @@ extern const pb_msgdesc_t RadioConfig_UserPreferences_msg;
#define RadioConfig_UserPreferences_fields &RadioConfig_UserPreferences_msg
/* Maximum encoded size of messages (where known) */
#define RadioConfig_size 801
#define RadioConfig_UserPreferences_size 798
#define RadioConfig_size 608
#define RadioConfig_UserPreferences_size 605
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -1,12 +1,12 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.4 */
#include "environmental_measurement.pb.h"
#include "telemetry.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(EnvironmentalMeasurement, EnvironmentalMeasurement, AUTO)
PB_BIND(Telemetry, Telemetry, AUTO)

View File

@@ -1,8 +1,8 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.4 */
#ifndef PB_ENVIRONMENTAL_MEASUREMENT_PB_H_INCLUDED
#define PB_ENVIRONMENTAL_MEASUREMENT_PB_H_INCLUDED
#ifndef PB_TELEMETRY_PB_H_INCLUDED
#define PB_TELEMETRY_PB_H_INCLUDED
#include <pb.h>
#if PB_PROTO_HEADER_VERSION != 40
@@ -10,14 +10,14 @@
#endif
/* Struct definitions */
typedef struct _EnvironmentalMeasurement {
typedef struct _Telemetry {
float temperature;
float relative_humidity;
float barometric_pressure;
float gas_resistance;
float voltage;
float current;
} EnvironmentalMeasurement;
} Telemetry;
#ifdef __cplusplus
@@ -25,35 +25,35 @@ extern "C" {
#endif
/* Initializer values for message structs */
#define EnvironmentalMeasurement_init_default {0, 0, 0, 0, 0, 0}
#define EnvironmentalMeasurement_init_zero {0, 0, 0, 0, 0, 0}
#define Telemetry_init_default {0, 0, 0, 0, 0, 0}
#define Telemetry_init_zero {0, 0, 0, 0, 0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define EnvironmentalMeasurement_temperature_tag 1
#define EnvironmentalMeasurement_relative_humidity_tag 2
#define EnvironmentalMeasurement_barometric_pressure_tag 3
#define EnvironmentalMeasurement_gas_resistance_tag 4
#define EnvironmentalMeasurement_voltage_tag 5
#define EnvironmentalMeasurement_current_tag 6
#define Telemetry_temperature_tag 1
#define Telemetry_relative_humidity_tag 2
#define Telemetry_barometric_pressure_tag 3
#define Telemetry_gas_resistance_tag 4
#define Telemetry_voltage_tag 5
#define Telemetry_current_tag 6
/* Struct field encoding specification for nanopb */
#define EnvironmentalMeasurement_FIELDLIST(X, a) \
#define Telemetry_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, FLOAT, temperature, 1) \
X(a, STATIC, SINGULAR, FLOAT, relative_humidity, 2) \
X(a, STATIC, SINGULAR, FLOAT, barometric_pressure, 3) \
X(a, STATIC, SINGULAR, FLOAT, gas_resistance, 4) \
X(a, STATIC, SINGULAR, FLOAT, voltage, 5) \
X(a, STATIC, SINGULAR, FLOAT, current, 6)
#define EnvironmentalMeasurement_CALLBACK NULL
#define EnvironmentalMeasurement_DEFAULT NULL
#define Telemetry_CALLBACK NULL
#define Telemetry_DEFAULT NULL
extern const pb_msgdesc_t EnvironmentalMeasurement_msg;
extern const pb_msgdesc_t Telemetry_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define EnvironmentalMeasurement_fields &EnvironmentalMeasurement_msg
#define Telemetry_fields &Telemetry_msg
/* Maximum encoded size of messages (where known) */
#define EnvironmentalMeasurement_size 30
#define Telemetry_size 30
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -390,6 +390,9 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
res->println("Web server is running.<br><br>The content you are looking for can't be found. Please see: <a "
"href=https://meshtastic.org/docs/getting-started/faq#wifi--web-browser>FAQ</a>.<br><br><a "
"href=/admin>admin</a>");
return;
} else {
res->setHeader("Content-Encoding", "gzip");
}
@@ -427,6 +430,7 @@ void handleStatic(HTTPRequest *req, HTTPResponse *res)
return;
} else {
DEBUG_MSG("ERROR: This should not have happened...\n");
res->println("ERROR: This should not have happened...");
}
}

View File

@@ -175,6 +175,9 @@ bool initWifi(bool forceSoftAP)
{
forcedSoftAP = forceSoftAP;
// strcpy(radioConfig.preferences.wifi_ssid, "meshtastic");
// strcpy(radioConfig.preferences.wifi_password, "meshtastic!");
if ((radioConfig.has_preferences && radioConfig.preferences.wifi_ssid[0]) || forceSoftAP) {
const char *wifiName = radioConfig.preferences.wifi_ssid;
const char *wifiPsw = radioConfig.preferences.wifi_password;

View File

@@ -1,5 +1,5 @@
#include "configuration.h"
#include "AdminPlugin.h"
#include "AdminModule.h"
#include "Channels.h"
#include "MeshService.h"
#include "NodeDB.h"
@@ -10,7 +10,7 @@
#include "unistd.h"
#endif
AdminPlugin *adminPlugin;
AdminModule *adminModule;
/// A special reserved string to indicate strings we can not share with external nodes. We will use this 'reserved' word instead.
/// Also, to make setting work correctly, if someone tries to set a string to this reserved value we assume they don't really want a change.
@@ -30,7 +30,7 @@ static void writeSecret(char *buf, const char *currentVal) {
}
}
void AdminPlugin::handleGetChannel(const MeshPacket &req, uint32_t channelIndex)
void AdminModule::handleGetChannel(const MeshPacket &req, uint32_t channelIndex)
{
if (req.decoded.want_response) {
// We create the reply here
@@ -41,7 +41,7 @@ void AdminPlugin::handleGetChannel(const MeshPacket &req, uint32_t channelIndex)
}
}
void AdminPlugin::handleGetRadio(const MeshPacket &req)
void AdminModule::handleGetRadio(const MeshPacket &req)
{
if (req.decoded.want_response) {
// We create the reply here
@@ -61,8 +61,11 @@ void AdminPlugin::handleGetRadio(const MeshPacket &req)
}
}
bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r)
bool AdminModule::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r)
{
// if handled == false, then let others look at this message also if they want
bool handled = false;
assert(r);
switch (r->which_variant) {
case AdminMessage_set_owner_tag:
@@ -119,14 +122,28 @@ bool AdminPlugin::handleReceivedProtobuf(const MeshPacket &mp, AdminMessage *r)
#endif
default:
AdminMessage response = AdminMessage_init_default;
AdminMessageHandleResult handleResult = MeshPlugin::handleAdminMessageForAllPlugins(mp, r, &response);
if (handleResult == AdminMessageHandleResult::HANDLED_WITH_RESPONSE)
{
myReply = allocDataProtobuf(response);
}
else if (mp.decoded.want_response)
{
DEBUG_MSG("We did not responded to a request that wanted a respond. req.variant=%d\n", r->which_variant);
}
else if (handleResult != AdminMessageHandleResult::HANDLED)
{
// Probably a message sent by us or sent to our local node. FIXME, we should avoid scanning these messages
DEBUG_MSG("Ignoring nonrelevant admin %d\n", r->which_variant);
}
break;
}
return false; // Let others look at this message also if they want
return handled;
}
void AdminPlugin::handleSetOwner(const User &o)
void AdminModule::handleSetOwner(const User &o)
{
int changed = 0;
@@ -156,7 +173,7 @@ void AdminPlugin::handleSetOwner(const User &o)
service.reloadOwner();
}
void AdminPlugin::handleSetChannel(const Channel &cc)
void AdminModule::handleSetChannel(const Channel &cc)
{
channels.setChannel(cc);
@@ -170,7 +187,7 @@ void AdminPlugin::handleSetChannel(const Channel &cc)
}
}
void AdminPlugin::handleSetRadio(RadioConfig &r)
void AdminModule::handleSetRadio(RadioConfig &r)
{
writeSecret(r.preferences.wifi_password, radioConfig.preferences.wifi_password);
radioConfig = r;
@@ -178,7 +195,7 @@ void AdminPlugin::handleSetRadio(RadioConfig &r)
service.reloadConfig();
}
AdminPlugin::AdminPlugin() : ProtobufPlugin("Admin", PortNum_ADMIN_APP, AdminMessage_fields)
AdminModule::AdminModule() : ProtobufPlugin("Admin", PortNum_ADMIN_APP, AdminMessage_fields)
{
// restrict to the admin channel for rx
boundChannel = Channels::adminChannel;

View File

@@ -2,15 +2,15 @@
#include "ProtobufPlugin.h"
/**
* Routing plugin for router control messages
* Routing module for router control messages
*/
class AdminPlugin : public ProtobufPlugin<AdminMessage>
class AdminModule : public ProtobufPlugin<AdminMessage>
{
public:
/** Constructor
* name is for debugging output
*/
AdminPlugin();
AdminModule();
protected:
/** Called to handle a particular incoming message
@@ -28,4 +28,4 @@ class AdminPlugin : public ProtobufPlugin<AdminMessage>
void handleGetRadio(const MeshPacket &req);
};
extern AdminPlugin *adminPlugin;
extern AdminModule *adminModule;

View File

@@ -0,0 +1,538 @@
#include "configuration.h"
#include "CannedMessageModule.h"
#include "MeshService.h"
#include "FSCommon.h"
#include "mesh/generated/cannedmessages.pb.h"
// TODO: reuse defined from Screen.cpp
#define FONT_SMALL ArialMT_Plain_10
#define FONT_MEDIUM ArialMT_Plain_16
#define FONT_LARGE ArialMT_Plain_24
// Remove Canned message screen if no action is taken for some milliseconds
#define INACTIVATE_AFTER_MS 20000
static const char *cannedMessagesConfigFile = "/prefs/cannedConf.proto";
CannedMessageModuleConfig cannedMessageModuleConfig;
CannedMessageModule *cannedMessageModule;
// TODO: move it into NodeDB.h!
extern bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct);
extern bool saveProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, const void *dest_struct);
CannedMessageModule::CannedMessageModule()
: SinglePortPlugin("canned", PortNum_TEXT_MESSAGE_APP),
concurrency::OSThread("CannedMessageModule")
{
if (radioConfig.preferences.canned_message_module_enabled)
{
this->loadProtoForModule();
if(this->splitConfiguredMessages() <= 0)
{
DEBUG_MSG("CannedMessageModule: No messages are configured. Module is disabled\n");
this->runState = CANNED_MESSAGE_RUN_STATE_DISABLED;
}
else
{
DEBUG_MSG("CannedMessageModule is enabled\n");
this->inputObserver.observe(inputBroker);
}
}
}
/**
* @brief Items in array this->messages will be set to be pointing on the right
* starting points of the string this->messageStore
*
* @return int Returns the number of messages found.
*/
int CannedMessageModule::splitConfiguredMessages()
{
int messageIndex = 0;
int i = 0;
// collect all the message parts
strcpy(
this->messageStore,
cannedMessageModuleConfig.messagesPart1);
strcat(
this->messageStore,
cannedMessageModuleConfig.messagesPart2);
strcat(
this->messageStore,
cannedMessageModuleConfig.messagesPart3);
strcat(
this->messageStore,
cannedMessageModuleConfig.messagesPart4);
// The first message points to the beginning of the store.
this->messages[messageIndex++] =
this->messageStore;
int upTo =
strlen(this->messageStore) - 1;
while (i < upTo)
{
if (this->messageStore[i] == '|')
{
// Message ending found, replace it with string-end character.
this->messageStore[i] = '\0';
DEBUG_MSG("CannedMessage %d is: '%s'\n",
messageIndex-1, this->messages[messageIndex-1]);
// hit our max messages, bail
if (messageIndex >= CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT)
{
this->messagesCount = messageIndex;
return this->messagesCount;
}
// Next message starts after pipe (|) just found.
this->messages[messageIndex++] =
(this->messageStore + i + 1);
}
i += 1;
}
if (strlen(this->messages[messageIndex-1]) > 0)
{
// We have a last message.
DEBUG_MSG("CannedMessage %d is: '%s'\n",
messageIndex-1, this->messages[messageIndex-1]);
this->messagesCount = messageIndex;
}
else
{
this->messagesCount = messageIndex-1;
}
return this->messagesCount;
}
int CannedMessageModule::handleInputEvent(const InputEvent *event)
{
if (
(strlen(radioConfig.preferences.canned_message_module_allow_input_source) > 0) &&
(strcmp(radioConfig.preferences.canned_message_module_allow_input_source, event->source) != 0) &&
(strcmp(radioConfig.preferences.canned_message_module_allow_input_source, "_any") != 0))
{
// Event source is not accepted.
// Event only accepted if source matches the configured one, or
// the configured one is "_any" (or if there is no configured
// source at all)
return 0;
}
bool validEvent = false;
if (event->inputEvent == static_cast<char>(InputEventChar_KEY_UP))
{
DEBUG_MSG("Canned message event UP\n");
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_UP;
validEvent = true;
}
if (event->inputEvent == static_cast<char>(InputEventChar_KEY_DOWN))
{
DEBUG_MSG("Canned message event DOWN\n");
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_DOWN;
validEvent = true;
}
if (event->inputEvent == static_cast<char>(InputEventChar_KEY_SELECT))
{
DEBUG_MSG("Canned message event Select\n");
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT;
validEvent = true;
}
if (validEvent)
{
// Let runOnce to be called immediately.
setIntervalFromNow(0);
}
return 0;
}
void CannedMessageModule::sendText(NodeNum dest,
const char* message,
bool wantReplies)
{
MeshPacket *p = allocDataPacket();
p->to = dest;
p->want_ack = true;
p->decoded.payload.size = strlen(message);
memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size);
if (radioConfig.preferences.canned_message_module_send_bell)
{
p->decoded.payload.bytes[p->decoded.payload.size-1] = 7; // Bell character
p->decoded.payload.bytes[p->decoded.payload.size] = '\0'; // Bell character
p->decoded.payload.size++;
}
DEBUG_MSG("Sending message id=%d, msg=%.*s\n",
p->id, p->decoded.payload.size, p->decoded.payload.bytes);
service.sendToMesh(p);
}
int32_t CannedMessageModule::runOnce()
{
if ((!radioConfig.preferences.canned_message_module_enabled)
|| (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED)
|| (this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE))
{
return 30000; // TODO: should return MAX_VAL
}
DEBUG_MSG("Check status\n");
UIFrameEvent e = {false, true};
if (this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE)
{
// TODO: might have some feedback of sendig state
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
e.frameChanged = true;
this->currentMessageIndex = -1;
this->notifyObservers(&e);
}
else if (
(this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE)
&& (millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)
{
// Reset module
DEBUG_MSG("Reset due the lack of activity.\n");
e.frameChanged = true;
this->currentMessageIndex = -1;
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
this->notifyObservers(&e);
}
else if (this->currentMessageIndex == -1)
{
this->currentMessageIndex = 0;
DEBUG_MSG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
e.frameChanged = true;
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
}
else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT)
{
sendText(
NODENUM_BROADCAST,
this->messages[this->currentMessageIndex],
true);
this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE;
this->currentMessageIndex = -1;
this->notifyObservers(&e);
return 2000;
}
else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP)
{
this->currentMessageIndex = getPrevIndex();
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
DEBUG_MSG("MOVE UP (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
}
else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_DOWN)
{
this->currentMessageIndex = this->getNextIndex();
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
DEBUG_MSG("MOVE DOWN (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
}
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE)
{
this->lastTouchMillis = millis();
this->notifyObservers(&e);
return INACTIVATE_AFTER_MS;
}
return 30000; // TODO: should return MAX_VAL
}
const char* CannedMessageModule::getCurrentMessage()
{
return this->messages[this->currentMessageIndex];
}
const char* CannedMessageModule::getPrevMessage()
{
return this->messages[this->getPrevIndex()];
}
const char* CannedMessageModule::getNextMessage()
{
return this->messages[this->getNextIndex()];
}
bool CannedMessageModule::shouldDraw()
{
if (!radioConfig.preferences.canned_message_module_enabled)
{
return false;
}
return (currentMessageIndex != -1) || (this->runState != CANNED_MESSAGE_RUN_STATE_INACTIVE);
}
int CannedMessageModule::getNextIndex()
{
if (this->currentMessageIndex >= (this->messagesCount -1))
{
return 0;
}
else
{
return this->currentMessageIndex + 1;
}
}
int CannedMessageModule::getPrevIndex()
{
if (this->currentMessageIndex <= 0)
{
return this->messagesCount - 1;
}
else
{
return this->currentMessageIndex - 1;
}
}
void CannedMessageModule::drawFrame(
OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
displayedNodeNum = 0; // Not currently showing a node pane
if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE)
{
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth()/2 + x, 0 + y + 12, "Sending...");
}
else
{
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
display->drawString(0 + x, 0 + y, cannedMessageModule->getPrevMessage());
display->setFont(FONT_MEDIUM);
display->drawString(0 + x, 0 + y + 8, cannedMessageModule->getCurrentMessage());
display->setFont(FONT_SMALL);
display->drawString(0 + x, 0 + y + 24, cannedMessageModule->getNextMessage());
}
}
void CannedMessageModule::loadProtoForModule()
{
if (!loadProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size, sizeof(cannedMessagesConfigFile), CannedMessageModuleConfig_fields, &cannedMessageModuleConfig)) {
installDefaultCannedMessageModuleConfig();
}
}
/**
* @brief Save the module config to file.
*
* @return true On success.
* @return false On error.
*/
bool CannedMessageModule::saveProtoForModule()
{
bool okay = true;
#ifdef FS
FS.mkdir("/prefs");
#endif
okay &= saveProto(cannedMessagesConfigFile, CannedMessageModuleConfig_size, sizeof(CannedMessageModuleConfig), CannedMessageModuleConfig_fields, &cannedMessageModuleConfig);
return okay;
}
/**
* @brief Fill configuration with default values.
*/
void CannedMessageModule::installDefaultCannedMessageModuleConfig()
{
memset(cannedMessageModuleConfig.messagesPart1, 0, sizeof(cannedMessageModuleConfig.messagesPart1));
memset(cannedMessageModuleConfig.messagesPart2, 0, sizeof(cannedMessageModuleConfig.messagesPart2));
memset(cannedMessageModuleConfig.messagesPart3, 0, sizeof(cannedMessageModuleConfig.messagesPart3));
memset(cannedMessageModuleConfig.messagesPart4, 0, sizeof(cannedMessageModuleConfig.messagesPart4));
}
/**
* @brief An admin message arrived to AdminModule. We are asked whether we want to handle that.
*
* @param mp The mesh packet arrived.
* @param request The AdminMessage request extracted from the packet.
* @param response The prepared response
* @return AdminMessageHandleResult HANDLED if message was handled
* HANDLED_WITH_RESULT if a result is also prepared.
*/
AdminMessageHandleResult CannedMessageModule::handleAdminMessageForModule(
const MeshPacket &mp, AdminMessage *request, AdminMessage *response)
{
AdminMessageHandleResult result;
switch (request->which_variant) {
case AdminMessage_get_canned_message_module_part1_request_tag:
DEBUG_MSG("Client is getting radio canned message part1\n");
this->handleGetCannedMessageModulePart1(mp, response);
result = AdminMessageHandleResult::HANDLED_WITH_RESPONSE;
break;
case AdminMessage_get_canned_message_module_part2_request_tag:
DEBUG_MSG("Client is getting radio canned message part2\n");
this->handleGetCannedMessageModulePart2(mp, response);
result = AdminMessageHandleResult::HANDLED_WITH_RESPONSE;
break;
case AdminMessage_get_canned_message_module_part3_request_tag:
DEBUG_MSG("Client is getting radio canned message part3\n");
this->handleGetCannedMessageModulePart3(mp, response);
result = AdminMessageHandleResult::HANDLED_WITH_RESPONSE;
break;
case AdminMessage_get_canned_message_module_part4_request_tag:
DEBUG_MSG("Client is getting radio canned message part4\n");
this->handleGetCannedMessageModulePart4(mp, response);
result = AdminMessageHandleResult::HANDLED_WITH_RESPONSE;
break;
case AdminMessage_set_canned_message_module_part1_tag:
DEBUG_MSG("Client is setting radio canned message part 1\n");
this->handleSetCannedMessageModulePart1(
request->set_canned_message_module_part1);
result = AdminMessageHandleResult::HANDLED;
break;
case AdminMessage_set_canned_message_module_part2_tag:
DEBUG_MSG("Client is setting radio canned message part 2\n");
this->handleSetCannedMessageModulePart2(
request->set_canned_message_module_part2);
result = AdminMessageHandleResult::HANDLED;
break;
case AdminMessage_set_canned_message_module_part3_tag:
DEBUG_MSG("Client is setting radio canned message part 3\n");
this->handleSetCannedMessageModulePart3(
request->set_canned_message_module_part3);
result = AdminMessageHandleResult::HANDLED;
break;
case AdminMessage_set_canned_message_module_part4_tag:
DEBUG_MSG("Client is setting radio canned message part 4\n");
this->handleSetCannedMessageModulePart4(
request->set_canned_message_module_part4);
result = AdminMessageHandleResult::HANDLED;
break;
default:
result = AdminMessageHandleResult::NOT_HANDLED;
}
return result;
}
void CannedMessageModule::handleGetCannedMessageModulePart1(
const MeshPacket &req, AdminMessage *response)
{
DEBUG_MSG("*** handleGetCannedMessageModulePart1\n");
assert(req.decoded.want_response);
response->which_variant = AdminMessage_get_canned_message_module_part1_response_tag;
strcpy(
response->get_canned_message_module_part1_response,
cannedMessageModuleConfig.messagesPart1);
}
void CannedMessageModule::handleGetCannedMessageModulePart2(
const MeshPacket &req, AdminMessage *response)
{
DEBUG_MSG("*** handleGetCannedMessageModulePart2\n");
assert(req.decoded.want_response);
response->which_variant = AdminMessage_get_canned_message_module_part2_response_tag;
strcpy(
response->get_canned_message_module_part2_response,
cannedMessageModuleConfig.messagesPart2);
}
void CannedMessageModule::handleGetCannedMessageModulePart3(
const MeshPacket &req, AdminMessage *response)
{
DEBUG_MSG("*** handleGetCannedMessageModulePart3\n");
assert(req.decoded.want_response);
response->which_variant = AdminMessage_get_canned_message_module_part3_response_tag;
strcpy(
response->get_canned_message_module_part3_response,
cannedMessageModuleConfig.messagesPart3);
}
void CannedMessageModule::handleGetCannedMessageModulePart4(
const MeshPacket &req, AdminMessage *response)
{
DEBUG_MSG("*** handleGetCannedMessageModulePart4\n");
assert(req.decoded.want_response);
response->which_variant = AdminMessage_get_canned_message_module_part4_response_tag;
strcpy(
response->get_canned_message_module_part4_response,
cannedMessageModuleConfig.messagesPart4);
}
void CannedMessageModule::handleSetCannedMessageModulePart1(const char *from_msg)
{
int changed = 0;
if (*from_msg)
{
changed |= strcmp(cannedMessageModuleConfig.messagesPart1, from_msg);
strcpy(cannedMessageModuleConfig.messagesPart1, from_msg);
DEBUG_MSG("*** from_msg.text:%s\n", from_msg);
}
if (changed)
{
this->saveProtoForModule();
}
}
void CannedMessageModule::handleSetCannedMessageModulePart2(const char *from_msg)
{
int changed = 0;
if (*from_msg)
{
changed |= strcmp(cannedMessageModuleConfig.messagesPart2, from_msg);
strcpy(cannedMessageModuleConfig.messagesPart2, from_msg);
}
if (changed)
{
this->saveProtoForModule();
}
}
void CannedMessageModule::handleSetCannedMessageModulePart3(const char *from_msg)
{
int changed = 0;
if (*from_msg)
{
changed |= strcmp(cannedMessageModuleConfig.messagesPart3, from_msg);
strcpy(cannedMessageModuleConfig.messagesPart3, from_msg);
}
if (changed)
{
this->saveProtoForModule();
}
}
void CannedMessageModule::handleSetCannedMessageModulePart4(const char *from_msg)
{
int changed = 0;
if (*from_msg)
{
changed |= strcmp(cannedMessageModuleConfig.messagesPart4, from_msg);
strcpy(cannedMessageModuleConfig.messagesPart4, from_msg);
}
if (changed)
{
this->saveProtoForModule();
}
}

View File

@@ -0,0 +1,86 @@
#pragma once
#include "ProtobufPlugin.h"
#include "input/InputBroker.h"
enum cannedMessageModuleRunState
{
CANNED_MESSAGE_RUN_STATE_DISABLED,
CANNED_MESSAGE_RUN_STATE_INACTIVE,
CANNED_MESSAGE_RUN_STATE_ACTIVE,
CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE,
CANNED_MESSAGE_RUN_STATE_ACTION_SELECT,
CANNED_MESSAGE_RUN_STATE_ACTION_UP,
CANNED_MESSAGE_RUN_STATE_ACTION_DOWN,
};
#define CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT 50
/**
* Sum of CannedMessageModuleConfig part sizes.
*/
#define CANNED_MESSAGE_MODULE_MESSAGES_SIZE 800
class CannedMessageModule :
public SinglePortPlugin,
public Observable<const UIFrameEvent *>,
private concurrency::OSThread
{
CallbackObserver<CannedMessageModule, const InputEvent *> inputObserver =
CallbackObserver<CannedMessageModule, const InputEvent *>(
this, &CannedMessageModule::handleInputEvent);
public:
CannedMessageModule();
const char* getCurrentMessage();
const char* getPrevMessage();
const char* getNextMessage();
bool shouldDraw();
void eventUp();
void eventDown();
void eventSelect();
void handleGetCannedMessageModulePart1(const MeshPacket &req, AdminMessage *response);
void handleGetCannedMessageModulePart2(const MeshPacket &req, AdminMessage *response);
void handleGetCannedMessageModulePart3(const MeshPacket &req, AdminMessage *response);
void handleGetCannedMessageModulePart4(const MeshPacket &req, AdminMessage *response);
void handleSetCannedMessageModulePart1(const char *from_msg);
void handleSetCannedMessageModulePart2(const char *from_msg);
void handleSetCannedMessageModulePart3(const char *from_msg);
void handleSetCannedMessageModulePart4(const char *from_msg);
protected:
virtual int32_t runOnce() override;
void sendText(
NodeNum dest,
const char* message,
bool wantReplies);
int splitConfiguredMessages();
int getNextIndex();
int getPrevIndex();
int handleInputEvent(const InputEvent *event);
virtual bool wantUIFrame() override { return this->shouldDraw(); }
virtual Observable<const UIFrameEvent *>* getUIFrameObservable() override { return this; }
virtual void drawFrame(
OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
virtual AdminMessageHandleResult handleAdminMessageForModule(
const MeshPacket &mp, AdminMessage *request, AdminMessage *response) override;
void loadProtoForModule();
bool saveProtoForModule();
void installDefaultCannedMessageModuleConfig();
int currentMessageIndex = -1;
cannedMessageModuleRunState runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
char messageStore[CANNED_MESSAGE_MODULE_MESSAGES_SIZE+1];
char *messages[CANNED_MESSAGE_MODULE_MESSAGE_MAX_COUNT];
int messagesCount = 0;
unsigned long lastTouchMillis = 0;
};
extern CannedMessageModule *cannedMessageModule;

View File

@@ -0,0 +1,187 @@
#include "configuration.h"
#include "ExternalNotificationModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include <Arduino.h>
//#include <assert.h>
/*
Documentation:
https://github.com/meshtastic/Meshtastic-device/blob/master/docs/software/modules/ExternalNotificationModule.md
This module supports:
https://github.com/meshtastic/Meshtastic-device/issues/654
Quick reference:
radioConfig.preferences.ext_notification_module_enabled
0 = Disabled (Default)
1 = Enabled
radioConfig.preferences.ext_notification_module_active
0 = Active Low (Default)
1 = Active High
radioConfig.preferences.ext_notification_module_alert_message
0 = Disabled (Default)
1 = Alert when a text message comes
radioConfig.preferences.ext_notification_module_alert_bell
0 = Disabled (Default)
1 = Alert when the bell character is received
radioConfig.preferences.ext_notification_module_output
GPIO of the output. (Default = 13)
radioConfig.preferences.ext_notification_module_output_ms
Amount of time in ms for the alert. Default is 1000.
*/
// Default configurations
#define EXT_NOTIFICATION_MODULE_OUTPUT EXT_NOTIFY_OUT
#define EXT_NOTIFICATION_MODULE_OUTPUT_MS 1000
#define ASCII_BELL 0x07
bool externalCurrentState = 0;
uint32_t externalTurnedOn = 0;
int32_t ExternalNotificationModule::runOnce()
{
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
// radioConfig.preferences.ext_notification_module_enabled = 1;
// radioConfig.preferences.ext_notification_module_alert_message = 1;
// radioConfig.preferences.ext_notification_module_active = 1;
// radioConfig.preferences.ext_notification_module_alert_bell = 1;
// radioConfig.preferences.ext_notification_module_output_ms = 1000;
// radioConfig.preferences.ext_notification_module_output = 13;
if (externalCurrentState) {
// If the output is turned on, turn it back off after the given period of time.
if (externalTurnedOn + (radioConfig.preferences.ext_notification_module_output_ms
? radioConfig.preferences.ext_notification_module_output_ms
: EXT_NOTIFICATION_MODULE_OUTPUT_MS) <
millis()) {
DEBUG_MSG("Turning off external notification\n");
setExternalOff();
}
}
return (25);
}
void ExternalNotificationModule::setExternalOn()
{
#ifdef EXT_NOTIFY_OUT
externalCurrentState = 1;
externalTurnedOn = millis();
digitalWrite((radioConfig.preferences.ext_notification_module_output ? radioConfig.preferences.ext_notification_module_output
: EXT_NOTIFICATION_MODULE_OUTPUT),
(radioConfig.preferences.ext_notification_module_active ? true : false));
#endif
}
void ExternalNotificationModule::setExternalOff()
{
#ifdef EXT_NOTIFY_OUT
externalCurrentState = 0;
digitalWrite((radioConfig.preferences.ext_notification_module_output ? radioConfig.preferences.ext_notification_module_output
: EXT_NOTIFICATION_MODULE_OUTPUT),
(radioConfig.preferences.ext_notification_module_active ? false : true));
#endif
}
// --------
ExternalNotificationModule::ExternalNotificationModule()
: SinglePortPlugin("ExternalNotificationModule", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread(
"ExternalNotificationModule")
{
// restrict to the admin channel for rx
boundChannel = Channels::gpioChannel;
#ifndef NO_ESP32
#ifdef EXT_NOTIFY_OUT
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
// radioConfig.preferences.ext_notification_module_enabled = 1;
// radioConfig.preferences.ext_notification_module_alert_message = 1;
// radioConfig.preferences.ext_notification_module_active = 1;
// radioConfig.preferences.ext_notification_module_alert_bell = 1;
// radioConfig.preferences.ext_notification_module_output_ms = 1000;
// radioConfig.preferences.ext_notification_module_output = 13;
if (radioConfig.preferences.ext_notification_module_enabled) {
DEBUG_MSG("Initializing External Notification Module\n");
// Set the direction of a pin
pinMode((radioConfig.preferences.ext_notification_module_output ? radioConfig.preferences.ext_notification_module_output
: EXT_NOTIFICATION_MODULE_OUTPUT),
OUTPUT);
// Turn off the pin
setExternalOff();
} else {
DEBUG_MSG("External Notification Module Disabled\n");
enabled = false;
}
#endif
#endif
}
ProcessMessage ExternalNotificationModule::handleReceived(const MeshPacket &mp)
{
#ifndef NO_ESP32
#ifdef EXT_NOTIFY_OUT
if (radioConfig.preferences.ext_notification_module_enabled) {
if (getFrom(&mp) != nodeDB.getNodeNum()) {
// TODO: This may be a problem if messages are sent in unicide, but I'm not sure if it will.
// Need to know if and how this could be a problem.
if (radioConfig.preferences.ext_notification_module_alert_bell) {
auto &p = mp.decoded;
DEBUG_MSG("externalNotificationModule - Notification Bell\n");
for (int i = 0; i < p.payload.size; i++) {
if (p.payload.bytes[i] == ASCII_BELL) {
setExternalOn();
}
}
}
if (radioConfig.preferences.ext_notification_module_alert_message) {
DEBUG_MSG("externalNotificationModule - Notification Module\n");
setExternalOn();
}
}
} else {
DEBUG_MSG("External Notification Module Disabled\n");
}
#endif
#endif
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}

View File

@@ -7,13 +7,13 @@
#include <functional>
/*
* Radio interface for ExternalNotificationPlugin
* Radio interface for ExternalNotificationModule
*
*/
class ExternalNotificationPlugin : public SinglePortPlugin, private concurrency::OSThread
class ExternalNotificationModule : public SinglePortPlugin, private concurrency::OSThread
{
public:
ExternalNotificationPlugin();
ExternalNotificationModule();
void setExternalOn();
void setExternalOff();

View File

@@ -2,9 +2,9 @@
/*
* To developers:
* Use this to enable / disable features in your plugin that you don't want to risk checking into GitHub.
* Use this to enable / disable features in your module that you don't want to risk checking into GitHub.
*
*/
// Enable development more for StoreForwardPlugin
// Enable development more for StoreForwardModule
bool StoreForward_Dev = false;

60
src/modules/Modules.cpp Normal file
View File

@@ -0,0 +1,60 @@
#include "configuration.h"
#include "input/InputBroker.h"
#include "input/RotaryEncoderInterruptImpl1.h"
#include "modules/AdminModule.h"
#include "modules/CannedMessageModule.h"
#include "modules/ExternalNotificationModule.h"
#include "modules/NodeInfoModule.h"
#include "modules/PositionModule.h"
#include "modules/RemoteHardwareModule.h"
#include "modules/ReplyModule.h"
#include "modules/RoutingModule.h"
#include "modules/TextMessageModule.h"
#ifndef PORTDUINO
#include "modules/Telemetry/Telemetry.h"
#endif
#ifndef NO_ESP32
#include "modules/esp32/RangeTestModule.h"
#include "modules/esp32/SerialModule.h"
#include "modules/esp32/StoreForwardModule.h"
#endif
/**
* Create module instances here. If you are adding a new module, you must 'new' it here (or somewhere else)
*/
void setupModules()
{
inputBroker = new InputBroker();
adminModule = new AdminModule();
nodeInfoModule = new NodeInfoModule();
positionModule = new PositionModule();
textMessageModule = new TextMessageModule();
// Note: if the rest of meshtastic doesn't need to explicitly use your module, you do not need to assign the instance
// to a global variable.
new RemoteHardwareModule();
new ReplyModule();
rotaryEncoderInterruptImpl1 = new RotaryEncoderInterruptImpl1();
rotaryEncoderInterruptImpl1->init();
cannedMessageModule = new CannedMessageModule();
#ifndef PORTDUINO
new TelemetryModule();
#endif
#ifndef NO_ESP32
// Only run on an esp32 based device.
/*
Maintained by MC Hamster (Jm Casler) jm@casler.org
*/
new SerialModule();
new ExternalNotificationModule();
storeForwardModule = new StoreForwardModule();
new RangeTestModule();
#endif
// NOTE! This module must be added LAST because it likes to check for replies from other modules and avoid sending extra acks
routingModule = new RoutingModule();
}

6
src/modules/Modules.h Normal file
View File

@@ -0,0 +1,6 @@
#pragma once
/**
* Create module instances here. If you are adding a new module, you must 'new' it here (or somewhere else)
*/
void setupModules();

View File

@@ -1,14 +1,14 @@
#include "configuration.h"
#include "NodeInfoPlugin.h"
#include "NodeInfoModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "main.h"
NodeInfoPlugin *nodeInfoPlugin;
NodeInfoModule *nodeInfoModule;
bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, User *pptr)
bool NodeInfoModule::handleReceivedProtobuf(const MeshPacket &mp, User *pptr)
{
auto p = *pptr;
@@ -27,7 +27,7 @@ bool NodeInfoPlugin::handleReceivedProtobuf(const MeshPacket &mp, User *pptr)
return false; // Let others look at this message also if they want
}
void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies)
void NodeInfoModule::sendOurNodeInfo(NodeNum dest, bool wantReplies)
{
// cancel any not yet sent (now stale) position packets
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
@@ -42,7 +42,7 @@ void NodeInfoPlugin::sendOurNodeInfo(NodeNum dest, bool wantReplies)
service.sendToMesh(p);
}
MeshPacket *NodeInfoPlugin::allocReply()
MeshPacket *NodeInfoModule::allocReply()
{
User &u = owner;
@@ -50,15 +50,15 @@ MeshPacket *NodeInfoPlugin::allocReply()
return allocDataProtobuf(u);
}
NodeInfoPlugin::NodeInfoPlugin()
: ProtobufPlugin("nodeinfo", PortNum_NODEINFO_APP, User_fields), concurrency::OSThread("NodeInfoPlugin")
NodeInfoModule::NodeInfoModule()
: ProtobufPlugin("nodeinfo", PortNum_NODEINFO_APP, User_fields), concurrency::OSThread("NodeInfoModule")
{
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
setIntervalFromNow(30 *
1000); // Send our initial owner announcement 30 seconds after we start (to give network time to setup)
}
int32_t NodeInfoPlugin::runOnce()
int32_t NodeInfoModule::runOnce()
{
static uint32_t currentGeneration;

View File

@@ -2,9 +2,9 @@
#include "ProtobufPlugin.h"
/**
* NodeInfo plugin for sending/receiving NodeInfos into the mesh
* NodeInfo module for sending/receiving NodeInfos into the mesh
*/
class NodeInfoPlugin : public ProtobufPlugin<User>, private concurrency::OSThread
class NodeInfoModule : public ProtobufPlugin<User>, private concurrency::OSThread
{
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher
PacketId prevPacketId = 0;
@@ -14,7 +14,7 @@ class NodeInfoPlugin : public ProtobufPlugin<User>, private concurrency::OSThrea
/** Constructor
* name is for debugging output
*/
NodeInfoPlugin();
NodeInfoModule();
/**
* Send our NodeInfo into the mesh
@@ -36,4 +36,4 @@ class NodeInfoPlugin : public ProtobufPlugin<User>, private concurrency::OSThrea
virtual int32_t runOnce() override;
};
extern NodeInfoPlugin *nodeInfoPlugin;
extern NodeInfoModule *nodeInfoModule;

View File

@@ -1,4 +1,4 @@
#include "PositionPlugin.h"
#include "PositionModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
@@ -7,16 +7,16 @@
#include "configuration.h"
#include "gps/GeoCoord.h"
PositionPlugin *positionPlugin;
PositionModule *positionModule;
PositionPlugin::PositionPlugin()
: ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields), concurrency::OSThread("PositionPlugin")
PositionModule::PositionModule()
: ProtobufPlugin("position", PortNum_POSITION_APP, Position_fields), concurrency::OSThread("PositionModule")
{
isPromiscuous = true; // We always want to update our nodedb, even if we are sniffing on others
setIntervalFromNow(60 * 1000); // Send our initial position 60 seconds after we start (to give GPS time to setup)
}
bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, Position *pptr)
bool PositionModule::handleReceivedProtobuf(const MeshPacket &mp, Position *pptr)
{
auto p = *pptr;
@@ -53,7 +53,7 @@ bool PositionPlugin::handleReceivedProtobuf(const MeshPacket &mp, Position *pptr
return false; // Let others look at this message also if they want
}
MeshPacket *PositionPlugin::allocReply()
MeshPacket *PositionModule::allocReply()
{
NodeInfo *node = service.refreshMyNodeInfo(); // should guarantee there is now a position
assert(node->has_position);
@@ -109,7 +109,7 @@ MeshPacket *PositionPlugin::allocReply()
return allocDataProtobuf(p);
}
void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies)
{
// cancel any not yet sent (now stale) position packets
if (prevPacketId) // if we wrap around to zero, we'll simply fail to cancel in that rare case (no big deal)
@@ -124,7 +124,7 @@ void PositionPlugin::sendOurPosition(NodeNum dest, bool wantReplies)
service.sendToMesh(p);
}
int32_t PositionPlugin::runOnce()
int32_t PositionModule::runOnce()
{
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());

View File

@@ -3,9 +3,9 @@
#include "concurrency/OSThread.h"
/**
* Position plugin for sending/receiving positions into the mesh
* Position module for sending/receiving positions into the mesh
*/
class PositionPlugin : public ProtobufPlugin<Position>, private concurrency::OSThread
class PositionModule : public ProtobufPlugin<Position>, private concurrency::OSThread
{
/// The id of the last packet we sent, to allow us to cancel it if we make something fresher
PacketId prevPacketId = 0;
@@ -24,7 +24,7 @@ class PositionPlugin : public ProtobufPlugin<Position>, private concurrency::OST
/** Constructor
* name is for debugging output
*/
PositionPlugin();
PositionModule();
/**
* Send our position into the mesh
@@ -47,4 +47,4 @@ class PositionPlugin : public ProtobufPlugin<Position>, private concurrency::OST
virtual int32_t runOnce() override;
};
extern PositionPlugin *positionPlugin;
extern PositionModule *positionModule;

View File

@@ -1,5 +1,5 @@
#include "configuration.h"
#include "RemoteHardwarePlugin.h"
#include "RemoteHardwareModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
@@ -46,13 +46,13 @@ static uint64_t digitalReads(uint64_t mask)
return res;
}
RemoteHardwarePlugin::RemoteHardwarePlugin()
RemoteHardwareModule::RemoteHardwareModule()
: ProtobufPlugin("remotehardware", PortNum_REMOTE_HARDWARE_APP, HardwareMessage_fields), concurrency::OSThread(
"remotehardware")
{
}
bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, HardwareMessage *pptr)
bool RemoteHardwareModule::handleReceivedProtobuf(const MeshPacket &req, HardwareMessage *pptr)
{
auto p = *pptr;
DEBUG_MSG("Received RemoteHardware typ=%d\n", p.typ);
@@ -111,7 +111,7 @@ bool RemoteHardwarePlugin::handleReceivedProtobuf(const MeshPacket &req, Hardwar
return false;
}
int32_t RemoteHardwarePlugin::runOnce()
int32_t RemoteHardwareModule::runOnce()
{
if (watchGpios) {
uint32_t now = millis();

View File

@@ -4,9 +4,9 @@
#include "concurrency/OSThread.h"
/**
* A plugin that provides easy low-level remote access to device hardware.
* A module that provides easy low-level remote access to device hardware.
*/
class RemoteHardwarePlugin : public ProtobufPlugin<HardwareMessage>, private concurrency::OSThread
class RemoteHardwareModule : public ProtobufPlugin<HardwareMessage>, private concurrency::OSThread
{
/// The current set of GPIOs we've been asked to watch for changes
uint64_t watchGpios = 0;
@@ -20,7 +20,7 @@ class RemoteHardwarePlugin : public ProtobufPlugin<HardwareMessage>, private con
/** Constructor
* name is for debugging output
*/
RemoteHardwarePlugin();
RemoteHardwareModule();
protected:
/** Called to handle a particular incoming message
@@ -40,4 +40,4 @@ class RemoteHardwarePlugin : public ProtobufPlugin<HardwareMessage>, private con
virtual int32_t runOnce() override;
};
extern RemoteHardwarePlugin remoteHardwarePlugin;
extern RemoteHardwareModule remoteHardwareModule;

View File

@@ -1,11 +1,11 @@
#include "configuration.h"
#include "ReplyPlugin.h"
#include "ReplyModule.h"
#include "MeshService.h"
#include "main.h"
#include <assert.h>
MeshPacket *ReplyPlugin::allocReply()
MeshPacket *ReplyModule::allocReply()
{
assert(currentRequest); // should always be !NULL
auto req = *currentRequest;

View File

@@ -3,19 +3,19 @@
/**
* A simple example plugin that just replies with "Message received" to any message it receives.
* A simple example module that just replies with "Message received" to any message it receives.
*/
class ReplyPlugin : public SinglePortPlugin
class ReplyModule : public SinglePortPlugin
{
public:
/** Constructor
* name is for debugging output
*/
ReplyPlugin() : SinglePortPlugin("reply", PortNum_REPLY_APP) {}
ReplyModule() : SinglePortPlugin("reply", PortNum_REPLY_APP) {}
protected:
/** For reply plugin we do all of our processing in the (normally optional)
/** For reply module we do all of our processing in the (normally optional)
* want_replies handling
*/
virtual MeshPacket *allocReply() override;

View File

@@ -1,18 +1,18 @@
#include "configuration.h"
#include "RoutingPlugin.h"
#include "RoutingModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "Router.h"
#include "main.h"
RoutingPlugin *routingPlugin;
RoutingModule *routingModule;
bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, Routing *r)
bool RoutingModule::handleReceivedProtobuf(const MeshPacket &mp, Routing *r)
{
printPacket("Routing sniffing", &mp);
router->sniffReceived(&mp, r);
// FIXME - move this to a non promsicious PhoneAPI plugin?
// FIXME - move this to a non promsicious PhoneAPI module?
// Note: we are careful not to send back packets that started with the phone back to the phone
if ((mp.to == NODENUM_BROADCAST || mp.to == nodeDB.getNodeNum()) && (mp.from != 0)) {
printPacket("Delivering rx packet", &mp);
@@ -22,7 +22,7 @@ bool RoutingPlugin::handleReceivedProtobuf(const MeshPacket &mp, Routing *r)
return false; // Let others look at this message also if they want
}
MeshPacket *RoutingPlugin::allocReply()
MeshPacket *RoutingModule::allocReply()
{
assert(currentRequest);
@@ -34,14 +34,14 @@ MeshPacket *RoutingPlugin::allocReply()
return NULL;
}
void RoutingPlugin::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
void RoutingModule::sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex)
{
auto p = allocAckNak(err, to, idFrom, chIndex);
router->sendLocal(p); // we sometimes send directly to the local node
}
RoutingPlugin::RoutingPlugin() : ProtobufPlugin("routing", PortNum_ROUTING_APP, Routing_fields)
RoutingModule::RoutingModule() : ProtobufPlugin("routing", PortNum_ROUTING_APP, Routing_fields)
{
isPromiscuous = true;
}

View File

@@ -3,15 +3,15 @@
#include "Channels.h"
/**
* Routing plugin for router control messages
* Routing module for router control messages
*/
class RoutingPlugin : public ProtobufPlugin<Routing>
class RoutingModule : public ProtobufPlugin<Routing>
{
public:
/** Constructor
* name is for debugging output
*/
RoutingPlugin();
RoutingModule();
void sendAckNak(Routing_Error err, NodeNum to, PacketId idFrom, ChannelIndex chIndex);
@@ -32,4 +32,4 @@ class RoutingPlugin : public ProtobufPlugin<Routing>
virtual bool wantPacket(const MeshPacket *p) override { return true; }
};
extern RoutingPlugin *routingPlugin;
extern RoutingModule *routingModule;

View File

@@ -1,10 +1,10 @@
#include "../mesh/generated/environmental_measurement.pb.h"
#include "../mesh/generated/telemetry.pb.h"
#include "configuration.h"
#include "EnvironmentalMeasurementSensor.h"
#include "TelemetrySensor.h"
#include "BME280Sensor.h"
#include <Adafruit_BME280.h>
BME280Sensor::BME280Sensor() : EnvironmentalMeasurementSensor {} {
BME280Sensor::BME280Sensor() : TelemetrySensor {} {
}
int32_t BME280Sensor::runOnce() {
@@ -15,12 +15,12 @@ int32_t BME280Sensor::runOnce() {
DEBUG_MSG("Could not find a valid BME280 sensor, check wiring, address, sensor ID!");
// TODO more verbose diagnostics
} else {
DEBUG_MSG("EnvironmentalMeasurement: Opened BME280 on default i2c bus");
DEBUG_MSG("Telemetry: Opened BME280 on default i2c bus");
}
return BME_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
}
bool BME280Sensor::getMeasurement(EnvironmentalMeasurement *measurement) {
bool BME280Sensor::getMeasurement(Telemetry *measurement) {
measurement->temperature = bme280.readTemperature();
measurement->relative_humidity = bme280.readHumidity();
measurement->barometric_pressure = bme280.readPressure() / 100.0F;

View File

@@ -0,0 +1,15 @@
#include "../mesh/generated/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <Adafruit_BME280.h>
#define BME_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
class BME280Sensor : virtual public TelemetrySensor {
private:
Adafruit_BME280 bme280;
public:
BME280Sensor();
virtual int32_t runOnce() override;
virtual bool getMeasurement(Telemetry *measurement) override;
};

View File

@@ -1,10 +1,10 @@
#include "../mesh/generated/environmental_measurement.pb.h"
#include "../mesh/generated/telemetry.pb.h"
#include "configuration.h"
#include "EnvironmentalMeasurementSensor.h"
#include "TelemetrySensor.h"
#include "BME680Sensor.h"
#include <Adafruit_BME680.h>
BME680Sensor::BME680Sensor() : EnvironmentalMeasurementSensor {} {
BME680Sensor::BME680Sensor() : TelemetrySensor {} {
}
int32_t BME680Sensor::runOnce() {
@@ -13,9 +13,9 @@ int32_t BME680Sensor::runOnce() {
bme680Status = bme680.begin(0x76);
if (!bme680Status) {
DEBUG_MSG("Could not find a valid BME680 sensor, check wiring, address, sensor ID!");
// TODO more verbose diagnosticsEnvironmentalMeasurementSensor
// TODO more verbose TelemetrySensor
} else {
DEBUG_MSG("EnvironmentalMeasurement: Opened BME680 on default i2c bus");
DEBUG_MSG("Telemetry: Opened BME680 on default i2c bus");
// Set up oversampling and filter initialization
bme680.setTemperatureOversampling(BME680_OS_8X);
bme680.setHumidityOversampling(BME680_OS_2X);
@@ -26,7 +26,7 @@ int32_t BME680Sensor::runOnce() {
return (BME_680_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
}
bool BME680Sensor::getMeasurement(EnvironmentalMeasurement *measurement) {
bool BME680Sensor::getMeasurement(Telemetry *measurement) {
measurement->temperature = bme680.readTemperature();
measurement->relative_humidity = bme680.readHumidity();
measurement->barometric_pressure = bme680.readPressure() / 100.0F;

View File

@@ -0,0 +1,15 @@
#include "../mesh/generated/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <Adafruit_BME680.h>
#define BME_680_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
class BME680Sensor : virtual public TelemetrySensor {
private:
Adafruit_BME680 bme680;
public:
BME680Sensor();
virtual int32_t runOnce() override;
virtual bool getMeasurement(Telemetry *measurement) override;
};

View File

@@ -0,0 +1,36 @@
#include "../mesh/generated/telemetry.pb.h"
#include "configuration.h"
#include "MeshService.h"
#include "TelemetrySensor.h"
#include "DHTSensor.h"
#include <DHT.h>
DHTSensor::DHTSensor() : TelemetrySensor {} {
}
int32_t DHTSensor::runOnce() {
if (RadioConfig_UserPreferences_TelemetrySensorType_DHT11 ||
RadioConfig_UserPreferences_TelemetrySensorType_DHT12) {
dht = new DHT(radioConfig.preferences.telemetry_module_sensor_pin, DHT11);
}
else {
dht = new DHT(radioConfig.preferences.telemetry_module_sensor_pin, DHT22);
}
dht->begin();
dht->read();
DEBUG_MSG("Telemetry: Opened DHT11/DHT12 on pin: %d\n",
radioConfig.preferences.telemetry_module_sensor_pin);
return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
}
bool DHTSensor::getMeasurement(Telemetry *measurement) {
if (!dht->read(true)) {
DEBUG_MSG("Telemetry: FAILED TO READ DATA\n");
return false;
}
measurement->relative_humidity = dht->readHumidity();
measurement->temperature = dht->readTemperature();
return true;
}

View File

@@ -0,0 +1,15 @@
#include "../mesh/generated/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <DHT.h>
#define DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
class DHTSensor : virtual public TelemetrySensor {
private:
DHT *dht = NULL;
public:
DHTSensor();
virtual int32_t runOnce() override;
virtual bool getMeasurement(Telemetry *measurement) override;
};

View File

@@ -1,26 +1,26 @@
#include "../mesh/generated/environmental_measurement.pb.h"
#include "../mesh/generated/telemetry.pb.h"
#include "configuration.h"
#include "MeshService.h"
#include "EnvironmentalMeasurementSensor.h"
#include "TelemetrySensor.h"
#include "DallasSensor.h"
#include <DS18B20.h>
#include <OneWire.h>
DallasSensor::DallasSensor() : EnvironmentalMeasurementSensor {} {
DallasSensor::DallasSensor() : TelemetrySensor {} {
}
int32_t DallasSensor::runOnce() {
oneWire = new OneWire(radioConfig.preferences.environmental_measurement_plugin_sensor_pin);
oneWire = new OneWire(radioConfig.preferences.telemetry_module_sensor_pin);
ds18b20 = new DS18B20(oneWire);
ds18b20->begin();
ds18b20->setResolution(12);
ds18b20->requestTemperatures();
DEBUG_MSG("EnvironmentalMeasurement: Opened DS18B20 on pin: %d\n",
radioConfig.preferences.environmental_measurement_plugin_sensor_pin);
DEBUG_MSG("Telemetry: Opened DS18B20 on pin: %d\n",
radioConfig.preferences.telemetry_module_sensor_pin);
return (DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
}
bool DallasSensor::getMeasurement(EnvironmentalMeasurement *measurement) {
bool DallasSensor::getMeasurement(Telemetry *measurement) {
if (ds18b20->isConversionComplete()) {
measurement->temperature = ds18b20->getTempC();
measurement->relative_humidity = 0;

View File

@@ -0,0 +1,17 @@
#include "../mesh/generated/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <DS18B20.h>
#include <OneWire.h>
#define DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
class DallasSensor : virtual public TelemetrySensor {
private:
OneWire *oneWire = NULL;
DS18B20 *ds18b20 = NULL;
public:
DallasSensor();
virtual int32_t runOnce() override;
virtual bool getMeasurement(Telemetry *measurement) override;
};

View File

@@ -1,10 +1,10 @@
#include "../mesh/generated/environmental_measurement.pb.h"
#include "../mesh/generated/telemetry.pb.h"
#include "configuration.h"
#include "EnvironmentalMeasurementSensor.h"
#include "TelemetrySensor.h"
#include "MCP9808Sensor.h"
#include <Adafruit_MCP9808.h>
MCP9808Sensor::MCP9808Sensor() : EnvironmentalMeasurementSensor {} {
MCP9808Sensor::MCP9808Sensor() : TelemetrySensor {} {
}
int32_t MCP9808Sensor::runOnce() {
@@ -14,14 +14,14 @@ int32_t MCP9808Sensor::runOnce() {
if (!mcp9808Status) {
DEBUG_MSG("Could not find a valid MCP9808 sensor, check wiring, address, sensor ID!");
} else {
DEBUG_MSG("EnvironmentalMeasurement: Opened MCP9808 on default i2c bus");
DEBUG_MSG("TelemetrySensor: Opened MCP9808 on default i2c bus");
// Reduce resolution from 0.0625 degrees (precision) to 0.125 degrees (high).
mcp9808.setResolution(2);
}
return (MCP_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
}
bool MCP9808Sensor::getMeasurement(EnvironmentalMeasurement *measurement) {
bool MCP9808Sensor::getMeasurement(Telemetry *measurement) {
measurement->temperature = mcp9808.readTempC();
return true;

View File

@@ -0,0 +1,15 @@
#include "../mesh/generated/telemetry.pb.h"
#include "TelemetrySensor.h"
#include <Adafruit_MCP9808.h>
#define MCP_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
class MCP9808Sensor : virtual public TelemetrySensor {
private:
Adafruit_MCP9808 mcp9808;
public:
MCP9808Sensor();
virtual int32_t runOnce() override;
virtual bool getMeasurement(Telemetry *measurement) override;
};

View File

@@ -0,0 +1,12 @@
#pragma once
#include "../mesh/generated/telemetry.pb.h"
#define DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS 1000
class TelemetrySensor {
protected:
TelemetrySensor() { }
public:
virtual int32_t runOnce() = 0;
virtual bool getMeasurement(Telemetry *measurement) = 0;
};

View File

@@ -0,0 +1,299 @@
#include "Telemetry.h"
#include "../mesh/generated/telemetry.pb.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
// Sensors
#include "Sensor/BME280Sensor.h"
#include "Sensor/BME680Sensor.h"
#include "Sensor/DHTSensor.h"
#include "Sensor/DallasSensor.h"
#include "Sensor/MCP9808Sensor.h"
BME280Sensor bme280Sensor;
BME680Sensor bme680Sensor;
DHTSensor dhtSensor;
DallasSensor dallasSensor;
MCP9808Sensor mcp9808Sensor;
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
#ifdef HAS_EINK
// The screen is bigger so use bigger fonts
#define FONT_SMALL ArialMT_Plain_16
#define FONT_MEDIUM ArialMT_Plain_24
#define FONT_LARGE ArialMT_Plain_24
#else
#define FONT_SMALL ArialMT_Plain_10
#define FONT_MEDIUM ArialMT_Plain_16
#define FONT_LARGE ArialMT_Plain_24
#endif
#define fontHeight(font) ((font)[1] + 1) // height is position 1
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
int32_t TelemetryModule::runOnce()
{
#ifndef PORTDUINO
/*
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
/*
radioConfig.preferences.telemetry_module_measurement_enabled = 1;
radioConfig.preferences.telemetry_module_screen_enabled = 1;
radioConfig.preferences.telemetry_module_read_error_count_threshold = 5;
radioConfig.preferences.telemetry_module_update_interval = 600;
radioConfig.preferences.telemetry_module_recovery_interval = 60;
radioConfig.preferences.telemetry_module_display_farenheit = false;
radioConfig.preferences.telemetry_module_sensor_pin = 13;
radioConfig.preferences.telemetry_module_sensor_type =
RadioConfig_UserPreferences_TelemetrySensorType::
RadioConfig_UserPreferences_TelemetrySensorType_BME280;
*/
if (!(radioConfig.preferences.telemetry_module_measurement_enabled ||
radioConfig.preferences.telemetry_module_screen_enabled)) {
// If this module is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
return (INT32_MAX);
}
if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup
firstTime = 0;
if (radioConfig.preferences.telemetry_module_measurement_enabled) {
DEBUG_MSG("Telemetry: Initializing\n");
// it's possible to have this module enabled, only for displaying values on the screen.
// therefore, we should only enable the sensor loop if measurement is also enabled
switch (radioConfig.preferences.telemetry_module_sensor_type) {
case RadioConfig_UserPreferences_TelemetrySensorType_DHT11:
case RadioConfig_UserPreferences_TelemetrySensorType_DHT12:
case RadioConfig_UserPreferences_TelemetrySensorType_DHT21:
case RadioConfig_UserPreferences_TelemetrySensorType_DHT22:
return dhtSensor.runOnce();
case RadioConfig_UserPreferences_TelemetrySensorType_DS18B20:
return dallasSensor.runOnce();
case RadioConfig_UserPreferences_TelemetrySensorType_BME280:
return bme280Sensor.runOnce();
case RadioConfig_UserPreferences_TelemetrySensorType_BME680:
return bme680Sensor.runOnce();
case RadioConfig_UserPreferences_TelemetrySensorType_MCP9808:
return mcp9808Sensor.runOnce();
default:
DEBUG_MSG("Telemetry: Invalid sensor type selected; Disabling module");
return (INT32_MAX);
break;
}
}
return (INT32_MAX);
} else {
// if we somehow got to a second run of this module with measurement disabled, then just wait forever
if (!radioConfig.preferences.telemetry_module_measurement_enabled)
return (INT32_MAX);
// this is not the first time OSThread library has called this function
// so just do what we intend to do on the interval
if (sensor_read_error_count > radioConfig.preferences.telemetry_module_read_error_count_threshold) {
if (radioConfig.preferences.telemetry_module_recovery_interval > 0) {
DEBUG_MSG("Telemetry: TEMPORARILY DISABLED; The "
"telemetry_module_read_error_count_threshold has been exceed: %d. Will retry reads in "
"%d seconds\n",
radioConfig.preferences.telemetry_module_read_error_count_threshold,
radioConfig.preferences.telemetry_module_recovery_interval);
sensor_read_error_count = 0;
return (radioConfig.preferences.telemetry_module_recovery_interval * 1000);
}
DEBUG_MSG("Telemetry: DISABLED; The telemetry_module_read_error_count_threshold has "
"been exceed: %d. Reads will not be retried until after device reset\n",
radioConfig.preferences.telemetry_module_read_error_count_threshold);
return (INT32_MAX);
} else if (sensor_read_error_count > 0) {
DEBUG_MSG("Telemetry: There have been %d sensor read failures. Will retry %d more times\n",
sensor_read_error_count, sensor_read_error_count, sensor_read_error_count,
radioConfig.preferences.telemetry_module_read_error_count_threshold -
sensor_read_error_count);
}
if (!sendOurTelemetry()) {
// if we failed to read the sensor, then try again
// as soon as we can according to the maximum polling frequency
switch (radioConfig.preferences.telemetry_module_sensor_type) {
case RadioConfig_UserPreferences_TelemetrySensorType_DHT11:
case RadioConfig_UserPreferences_TelemetrySensorType_DHT12:
case RadioConfig_UserPreferences_TelemetrySensorType_DHT21:
case RadioConfig_UserPreferences_TelemetrySensorType_DHT22:
return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
case RadioConfig_UserPreferences_TelemetrySensorType_DS18B20:
return (DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
case RadioConfig_UserPreferences_TelemetrySensorType_BME280:
case RadioConfig_UserPreferences_TelemetrySensorType_BME680:
return (BME_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
case RadioConfig_UserPreferences_TelemetrySensorType_MCP9808:
return (MCP_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
default:
return (DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
}
}
}
// The return of runOnce is an int32 representing the desired number of
// miliseconds until the function should be called again by the
// OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds
return (radioConfig.preferences.telemetry_module_update_interval * 1000);
#endif
}
bool TelemetryModule::wantUIFrame()
{
return radioConfig.preferences.telemetry_module_screen_enabled;
}
String GetSenderName(const MeshPacket &mp)
{
String sender;
auto node = nodeDB.getNode(getFrom(&mp));
if (node) {
sender = node->user.short_name;
} else {
sender = "UNK";
}
return sender;
}
uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp)
{
uint32_t now = getTime();
uint32_t last_seen = mp->rx_time;
int delta = (int)(now - last_seen);
if (delta < 0) // our clock must be slightly off still - not set from GPS yet
delta = 0;
return delta;
}
float TelemetryModule::CelsiusToFarenheit(float c)
{
return (c * 9) / 5 + 32;
}
void TelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_MEDIUM);
display->drawString(x, y, "Environment");
if (lastMeasurementPacket == nullptr) {
display->setFont(FONT_SMALL);
display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement");
return;
}
Telemetry lastMeasurement;
uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket);
String lastSender = GetSenderName(*lastMeasurementPacket);
auto &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, Telemetry_fields, &lastMeasurement)) {
display->setFont(FONT_SMALL);
display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
DEBUG_MSG("Telemetry: unable to decode last packet");
return;
}
display->setFont(FONT_SMALL);
String last_temp = String(lastMeasurement.temperature, 0) + "°C";
if (radioConfig.preferences.telemetry_module_display_farenheit) {
last_temp = String(CelsiusToFarenheit(lastMeasurement.temperature), 0) + "°F";
}
display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + lastSender + "(" + String(agoSecs) + "s)");
display->drawString(x, y += fontHeight(FONT_SMALL) - 2,"Temp/Hum: " + last_temp + " / " + String(lastMeasurement.relative_humidity, 0) + "%");
if (lastMeasurement.barometric_pressure != 0)
display->drawString(x, y += fontHeight(FONT_SMALL),"Press: " + String(lastMeasurement.barometric_pressure, 0) + "hPA");
}
bool TelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemetry *p)
{
if (!(radioConfig.preferences.telemetry_module_measurement_enabled ||
radioConfig.preferences.telemetry_module_screen_enabled)) {
// If this module is not enabled in any capacity, don't handle the packet, and allow other modules to consume
return false;
}
String sender = GetSenderName(mp);
DEBUG_MSG("Telemetry: Received data from %s\n", sender);
DEBUG_MSG("Telemetry->relative_humidity: %f\n", p->relative_humidity);
DEBUG_MSG("Telemetry->temperature: %f\n", p->temperature);
DEBUG_MSG("Telemetry->barometric_pressure: %f\n", p->barometric_pressure);
DEBUG_MSG("Telemetry->gas_resistance: %f\n", p->gas_resistance);
lastMeasurementPacket = packetPool.allocCopy(mp);
return false; // Let others look at this message also if they want
}
bool TelemetryModule::sendOurTelemetry(NodeNum dest, bool wantReplies)
{
Telemetry m;
m.barometric_pressure = 0;
m.gas_resistance = 0;
DEBUG_MSG("-----------------------------------------\n");
DEBUG_MSG("Telemetry: Read data\n");
switch (radioConfig.preferences.telemetry_module_sensor_type) {
case RadioConfig_UserPreferences_TelemetrySensorType_DS18B20:
if (!dallasSensor.getMeasurement(&m))
sensor_read_error_count++;
break;
case RadioConfig_UserPreferences_TelemetrySensorType_DHT11:
case RadioConfig_UserPreferences_TelemetrySensorType_DHT12:
case RadioConfig_UserPreferences_TelemetrySensorType_DHT21:
case RadioConfig_UserPreferences_TelemetrySensorType_DHT22:
if (!dhtSensor.getMeasurement(&m))
sensor_read_error_count++;
break;
case RadioConfig_UserPreferences_TelemetrySensorType_BME280:
bme280Sensor.getMeasurement(&m);
break;
case RadioConfig_UserPreferences_TelemetrySensorType_BME680:
bme680Sensor.getMeasurement(&m);
break;
case RadioConfig_UserPreferences_TelemetrySensorType_MCP9808:
mcp9808Sensor.getMeasurement(&m);
break;
default:
DEBUG_MSG("Telemetry: Invalid sensor type selected; Disabling module");
return false;
}
DEBUG_MSG("Telemetry->relative_humidity: %f\n", m.relative_humidity);
DEBUG_MSG("Telemetry->temperature: %f\n", m.temperature);
DEBUG_MSG("Telemetry->barometric_pressure: %f\n", m.barometric_pressure);
DEBUG_MSG("Telemetry->gas_resistance: %f\n", m.gas_resistance);
sensor_read_error_count = 0;
MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = wantReplies;
lastMeasurementPacket = packetPool.allocCopy(*p);
DEBUG_MSG("Telemetry: Sending packet to mesh");
service.sendToMesh(p);
return true;
}

View File

@@ -1,15 +1,15 @@
#pragma once
#include "../mesh/generated/environmental_measurement.pb.h"
#include "../mesh/generated/telemetry.pb.h"
#include "ProtobufPlugin.h"
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public ProtobufPlugin<EnvironmentalMeasurement>
class TelemetryModule : private concurrency::OSThread, public ProtobufPlugin<Telemetry>
{
public:
EnvironmentalMeasurementPlugin()
: concurrency::OSThread("EnvironmentalMeasurementPlugin"),
ProtobufPlugin("EnvironmentalMeasurement", PortNum_ENVIRONMENTAL_MEASUREMENT_APP, &EnvironmentalMeasurement_msg)
TelemetryModule()
: concurrency::OSThread("TelemetryModule"),
ProtobufPlugin("Telemetry", PortNum_TELEMETRY_APP, &Telemetry_msg)
{
lastMeasurementPacket = nullptr;
}
@@ -20,12 +20,12 @@ class EnvironmentalMeasurementPlugin : private concurrency::OSThread, public Pro
/** 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 MeshPacket &mp, EnvironmentalMeasurement *p) override;
virtual bool handleReceivedProtobuf(const MeshPacket &mp, Telemetry *p) override;
virtual int32_t runOnce() override;
/**
* Send our EnvironmentalMeasurement into the mesh
* Send our Telemetry into the mesh
*/
bool sendOurEnvironmentalMeasurement(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
bool sendOurTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
private:
float CelsiusToFarenheit(float c);

View File

@@ -1,11 +1,11 @@
#include "configuration.h"
#include "TextMessagePlugin.h"
#include "TextMessageModule.h"
#include "NodeDB.h"
#include "PowerFSM.h"
TextMessagePlugin *textMessagePlugin;
TextMessageModule *textMessageModule;
ProcessMessage TextMessagePlugin::handleReceived(const MeshPacket &mp)
ProcessMessage TextMessageModule::handleReceived(const MeshPacket &mp)
{
auto &p = mp.decoded;
DEBUG_MSG("Received text msg from=0x%0x, id=0x%x, msg=%.*s\n", mp.from, mp.id, p.payload.size, p.payload.bytes);

View File

@@ -5,13 +5,13 @@
/**
* Text message handling for meshtastic - draws on the OLED display the most recent received message
*/
class TextMessagePlugin : public SinglePortPlugin, public Observable<const MeshPacket *>
class TextMessageModule : public SinglePortPlugin, public Observable<const MeshPacket *>
{
public:
/** Constructor
* name is for debugging output
*/
TextMessagePlugin() : SinglePortPlugin("text", PortNum_TEXT_MESSAGE_APP) {}
TextMessageModule() : SinglePortPlugin("text", PortNum_TEXT_MESSAGE_APP) {}
protected:
@@ -22,4 +22,4 @@ class TextMessagePlugin : public SinglePortPlugin, public Observable<const MeshP
virtual ProcessMessage handleReceived(const MeshPacket &mp) override;
};
extern TextMessagePlugin *textMessagePlugin;
extern TextMessageModule *textMessageModule;

View File

@@ -1,4 +1,4 @@
#include "RangeTestPlugin.h"
#include "RangeTestModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "PowerFSM.h"
@@ -16,10 +16,10 @@
As a receiver, I can receive packets from multiple senders. These packets can be saved to the Filesystem.
*/
RangeTestPlugin *rangeTestPlugin;
RangeTestPluginRadio *rangeTestPluginRadio;
RangeTestModule *rangeTestModule;
RangeTestModuleRadio *rangeTestModuleRadio;
RangeTestPlugin::RangeTestPlugin() : concurrency::OSThread("RangeTestPlugin") {}
RangeTestModule::RangeTestModule() : concurrency::OSThread("RangeTestModule") {}
uint32_t packetSequence = 0;
@@ -27,44 +27,44 @@ uint32_t packetSequence = 0;
#define SEC_PER_HOUR 3600
#define SEC_PER_MIN 60
int32_t RangeTestPlugin::runOnce()
int32_t RangeTestModule::runOnce()
{
#ifndef NO_ESP32
/*
Uncomment the preferences below if you want to use the plugin
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
// radioConfig.preferences.range_test_plugin_enabled = 1;
// radioConfig.preferences.range_test_plugin_sender = 45;
// radioConfig.preferences.range_test_plugin_save = 1;
// radioConfig.preferences.range_test_module_enabled = 1;
// radioConfig.preferences.range_test_module_sender = 45;
// radioConfig.preferences.range_test_module_save = 1;
// Fixed position is useful when testing indoors.
// radioConfig.preferences.fixed_position = 1;
uint32_t senderHeartbeat = radioConfig.preferences.range_test_plugin_sender * 1000;
uint32_t senderHeartbeat = radioConfig.preferences.range_test_module_sender * 1000;
if (radioConfig.preferences.range_test_plugin_enabled) {
if (radioConfig.preferences.range_test_module_enabled) {
if (firstTime) {
rangeTestPluginRadio = new RangeTestPluginRadio();
rangeTestModuleRadio = new RangeTestModuleRadio();
firstTime = 0;
if (radioConfig.preferences.range_test_plugin_sender) {
DEBUG_MSG("Initializing Range Test Plugin -- Sender\n");
if (radioConfig.preferences.range_test_module_sender) {
DEBUG_MSG("Initializing Range Test Module -- Sender\n");
return (5000); // Sending first message 5 seconds after initilization.
} else {
DEBUG_MSG("Initializing Range Test Plugin -- Receiver\n");
DEBUG_MSG("Initializing Range Test Module -- Receiver\n");
return (500);
}
} else {
if (radioConfig.preferences.range_test_plugin_sender) {
if (radioConfig.preferences.range_test_module_sender) {
// If sender
DEBUG_MSG("Range Test Plugin - Sending heartbeat every %d ms\n", (senderHeartbeat));
DEBUG_MSG("Range Test Module - Sending heartbeat every %d ms\n", (senderHeartbeat));
DEBUG_MSG("gpsStatus->getLatitude() %d\n", gpsStatus->getLatitude());
DEBUG_MSG("gpsStatus->getLongitude() %d\n", gpsStatus->getLongitude());
@@ -75,7 +75,7 @@ int32_t RangeTestPlugin::runOnce()
// Only send packets if the channel is less than 25% utilized.
if (airTime->channelUtilizationPercent() < 25) {
rangeTestPluginRadio->sendPayload();
rangeTestModuleRadio->sendPayload();
} else {
DEBUG_MSG("rangeTest - Channel utilization is >25 percent. Skipping this opportunity to send.\n");
}
@@ -90,14 +90,14 @@ int32_t RangeTestPlugin::runOnce()
}
} else {
DEBUG_MSG("Range Test Plugin - Disabled\n");
DEBUG_MSG("Range Test Module - Disabled\n");
}
#endif
return (INT32_MAX);
}
MeshPacket *RangeTestPluginRadio::allocReply()
MeshPacket *RangeTestModuleRadio::allocReply()
{
auto reply = allocDataPacket(); // Allocate a packet for sending
@@ -105,7 +105,7 @@ MeshPacket *RangeTestPluginRadio::allocReply()
return reply;
}
void RangeTestPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
void RangeTestModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
{
MeshPacket *p = allocReply();
p->to = dest;
@@ -127,11 +127,11 @@ void RangeTestPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
}
ProcessMessage RangeTestPluginRadio::handleReceived(const MeshPacket &mp)
ProcessMessage RangeTestModuleRadio::handleReceived(const MeshPacket &mp)
{
#ifndef NO_ESP32
if (radioConfig.preferences.range_test_plugin_enabled) {
if (radioConfig.preferences.range_test_module_enabled) {
/*
auto &p = mp.decoded;
@@ -141,7 +141,7 @@ ProcessMessage RangeTestPluginRadio::handleReceived(const MeshPacket &mp)
if (getFrom(&mp) != nodeDB.getNodeNum()) {
if (radioConfig.preferences.range_test_plugin_save) {
if (radioConfig.preferences.range_test_module_save) {
appendFile(mp);
}
@@ -175,7 +175,7 @@ ProcessMessage RangeTestPluginRadio::handleReceived(const MeshPacket &mp)
}
} else {
DEBUG_MSG("Range Test Plugin Disabled\n");
DEBUG_MSG("Range Test Module Disabled\n");
}
#endif
@@ -183,7 +183,7 @@ ProcessMessage RangeTestPluginRadio::handleReceived(const MeshPacket &mp)
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
bool RangeTestPluginRadio::appendFile(const MeshPacket &mp)
bool RangeTestModuleRadio::appendFile(const MeshPacket &mp)
{
auto &p = mp.decoded;

View File

@@ -6,29 +6,29 @@
#include <Arduino.h>
#include <functional>
class RangeTestPlugin : private concurrency::OSThread
class RangeTestModule : private concurrency::OSThread
{
bool firstTime = 1;
public:
RangeTestPlugin();
RangeTestModule();
protected:
virtual int32_t runOnce() override;
};
extern RangeTestPlugin *rangeTestPlugin;
extern RangeTestModule *rangeTestModule;
/*
* Radio interface for RangeTestPlugin
* Radio interface for RangeTestModule
*
*/
class RangeTestPluginRadio : public SinglePortPlugin
class RangeTestModuleRadio : public SinglePortPlugin
{
uint32_t lastRxID = 0;
public:
RangeTestPluginRadio() : SinglePortPlugin("RangeTestPluginRadio", PortNum_TEXT_MESSAGE_APP) {}
RangeTestModuleRadio() : SinglePortPlugin("RangeTestModuleRadio", PortNum_TEXT_MESSAGE_APP) {}
/**
* Send our payload into the mesh
@@ -55,4 +55,4 @@ class RangeTestPluginRadio : public SinglePortPlugin
virtual ProcessMessage handleReceived(const MeshPacket &mp) override;
};
extern RangeTestPluginRadio *rangeTestPluginRadio;
extern RangeTestModuleRadio *rangeTestModuleRadio;

View File

@@ -1,5 +1,5 @@
#include "configuration.h"
#include "SerialPlugin.h"
#include "SerialModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
@@ -9,29 +9,29 @@
#include <assert.h>
/*
SerialPlugin
SerialModule
A simple interface to send messages over the mesh network by sending strings
over a serial port.
Default is to use RX GPIO 16 and TX GPIO 17.
Need help with this plugin? Post your question on the Meshtastic Discourse:
Need help with this module? Post your question on the Meshtastic Discourse:
https://meshtastic.discourse.group
Basic Usage:
1) Enable the plugin by setting serialplugin_enabled to 1.
2) Set the pins (serialplugin_rxd / serialplugin_rxd) for your preferred RX and TX GPIO pins.
1) Enable the module by setting serialmodule_enabled to 1.
2) Set the pins (serialmodule_rxd / serialmodule_rxd) for your preferred RX and TX GPIO pins.
On tbeam, recommend to use:
RXD 35
TXD 15
3) Set serialplugin_timeout to the amount of time to wait before we consider
3) Set serialmodule_timeout to the amount of time to wait before we consider
your packet as "done".
4) (Optional) In SerialPlugin.h set the port to PortNum_TEXT_MESSAGE_APP if you want to
4) (Optional) In SerialModule.h set the port to PortNum_TEXT_MESSAGE_APP if you want to
send messages to/from the general text message channel.
5) Connect to your device over the serial interface at 38400 8N1.
6) Send a packet up to 240 bytes in length. This will get relayed over the mesh network.
7) (Optional) Set serialplugin_echo to 1 and any message you send out will be echoed back
7) (Optional) Set serialmodule_echo to 1 and any message you send out will be echoed back
to your device.
TODO (in this order):
@@ -39,7 +39,7 @@
- This won't happen any time soon.
KNOWN PROBLEMS
* Until the plugin is initilized by the startup sequence, the TX pin is in a floating
* Until the module is initilized by the startup sequence, the TX pin is in a floating
state. Device connected to that pin may see this as "noise".
* Will not work on NRF and the Linux device targets.
@@ -48,66 +48,66 @@
#define RXD2 16
#define TXD2 17
#define SERIALPLUGIN_RX_BUFFER 128
#define SERIALPLUGIN_STRING_MAX Constants_DATA_PAYLOAD_LEN
#define SERIALPLUGIN_TIMEOUT 250
#define SERIALPLUGIN_BAUD 38400
#define SERIALPLUGIN_ACK 1
#define SERIALMODULE_RX_BUFFER 128
#define SERIALMODULE_STRING_MAX Constants_DATA_PAYLOAD_LEN
#define SERIALMODULE_TIMEOUT 250
#define SERIALMODULE_BAUD 38400
#define SERIALMODULE_ACK 1
SerialPlugin *serialPlugin;
SerialPluginRadio *serialPluginRadio;
SerialModule *serialModule;
SerialModuleRadio *serialModuleRadio;
SerialPlugin::SerialPlugin() : concurrency::OSThread("SerialPlugin") {}
SerialModule::SerialModule() : concurrency::OSThread("SerialModule") {}
char serialStringChar[Constants_DATA_PAYLOAD_LEN];
SerialPluginRadio::SerialPluginRadio() : SinglePortPlugin("SerialPluginRadio", PortNum_SERIAL_APP)
SerialModuleRadio::SerialModuleRadio() : SinglePortPlugin("SerialModuleRadio", PortNum_SERIAL_APP)
{
// restrict to the admin channel for rx
boundChannel = Channels::serialChannel;
}
int32_t SerialPlugin::runOnce()
int32_t SerialModule::runOnce()
{
#ifndef NO_ESP32
/*
Uncomment the preferences below if you want to use the plugin
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
// radioConfig.preferences.serialplugin_enabled = 1;
// radioConfig.preferences.serialplugin_rxd = 35;
// radioConfig.preferences.serialplugin_txd = 15;
// radioConfig.preferences.serialplugin_timeout = 1000;
// radioConfig.preferences.serialplugin_echo = 1;
// radioConfig.preferences.serialmodule_enabled = 1;
// radioConfig.preferences.serialmodule_rxd = 35;
// radioConfig.preferences.serialmodule_txd = 15;
// radioConfig.preferences.serialmodule_timeout = 1000;
// radioConfig.preferences.serialmodule_echo = 1;
if (radioConfig.preferences.serialplugin_enabled) {
if (radioConfig.preferences.serialmodule_enabled) {
if (firstTime) {
// Interface with the serial peripheral from in here.
DEBUG_MSG("Initializing serial peripheral interface\n");
if (radioConfig.preferences.serialplugin_rxd && radioConfig.preferences.serialplugin_txd) {
Serial2.begin(SERIALPLUGIN_BAUD, SERIAL_8N1, radioConfig.preferences.serialplugin_rxd,
radioConfig.preferences.serialplugin_txd);
if (radioConfig.preferences.serialmodule_rxd && radioConfig.preferences.serialmodule_txd) {
Serial2.begin(SERIALMODULE_BAUD, SERIAL_8N1, radioConfig.preferences.serialmodule_rxd,
radioConfig.preferences.serialmodule_txd);
} else {
Serial2.begin(SERIALPLUGIN_BAUD, SERIAL_8N1, RXD2, TXD2);
Serial2.begin(SERIALMODULE_BAUD, SERIAL_8N1, RXD2, TXD2);
}
if (radioConfig.preferences.serialplugin_timeout) {
if (radioConfig.preferences.serialmodule_timeout) {
Serial2.setTimeout(
radioConfig.preferences.serialplugin_timeout); // Number of MS to wait to set the timeout for the string.
radioConfig.preferences.serialmodule_timeout); // Number of MS to wait to set the timeout for the string.
} else {
Serial2.setTimeout(SERIALPLUGIN_TIMEOUT); // Number of MS to wait to set the timeout for the string.
Serial2.setTimeout(SERIALMODULE_TIMEOUT); // Number of MS to wait to set the timeout for the string.
}
Serial2.setRxBufferSize(SERIALPLUGIN_RX_BUFFER);
Serial2.setRxBufferSize(SERIALMODULE_RX_BUFFER);
serialPluginRadio = new SerialPluginRadio();
serialModuleRadio = new SerialModuleRadio();
firstTime = 0;
@@ -118,7 +118,7 @@ int32_t SerialPlugin::runOnce()
serialString = Serial2.readString();
serialString.toCharArray(serialStringChar, Constants_DATA_PAYLOAD_LEN);
serialPluginRadio->sendPayload();
serialModuleRadio->sendPayload();
DEBUG_MSG("Received: %s\n", serialStringChar);
}
@@ -126,7 +126,7 @@ int32_t SerialPlugin::runOnce()
return (10);
} else {
DEBUG_MSG("Serial Plugin Disabled\n");
DEBUG_MSG("Serial Module Disabled\n");
return (INT32_MAX);
}
@@ -135,7 +135,7 @@ int32_t SerialPlugin::runOnce()
#endif
}
MeshPacket *SerialPluginRadio::allocReply()
MeshPacket *SerialModuleRadio::allocReply()
{
auto reply = allocDataPacket(); // Allocate a packet for sending
@@ -143,13 +143,13 @@ MeshPacket *SerialPluginRadio::allocReply()
return reply;
}
void SerialPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
void SerialModuleRadio::sendPayload(NodeNum dest, bool wantReplies)
{
MeshPacket *p = allocReply();
p->to = dest;
p->decoded.want_response = wantReplies;
p->want_ack = SERIALPLUGIN_ACK;
p->want_ack = SERIALMODULE_ACK;
p->decoded.payload.size = strlen(serialStringChar); // You must specify how many bytes are in the reply
memcpy(p->decoded.payload.bytes, serialStringChar, p->decoded.payload.size);
@@ -157,11 +157,11 @@ void SerialPluginRadio::sendPayload(NodeNum dest, bool wantReplies)
service.sendToMesh(p);
}
ProcessMessage SerialPluginRadio::handleReceived(const MeshPacket &mp)
ProcessMessage SerialModuleRadio::handleReceived(const MeshPacket &mp)
{
#ifndef NO_ESP32
if (radioConfig.preferences.serialplugin_enabled) {
if (radioConfig.preferences.serialmodule_enabled) {
auto &p = mp.decoded;
// DEBUG_MSG("Received text msg self=0x%0x, from=0x%0x, to=0x%0x, id=%d, msg=%.*s\n",
@@ -170,10 +170,10 @@ ProcessMessage SerialPluginRadio::handleReceived(const MeshPacket &mp)
if (getFrom(&mp) == nodeDB.getNodeNum()) {
/*
* If radioConfig.preferences.serialplugin_echo is true, then echo the packets that are sent out back to the TX
* If radioConfig.preferences.serialmodule_echo is true, then echo the packets that are sent out back to the TX
* of the serial interface.
*/
if (radioConfig.preferences.serialplugin_echo) {
if (radioConfig.preferences.serialmodule_echo) {
// For some reason, we get the packet back twice when we send out of the radio.
// TODO: need to find out why.
@@ -187,12 +187,12 @@ ProcessMessage SerialPluginRadio::handleReceived(const MeshPacket &mp)
} else {
if (radioConfig.preferences.serialplugin_mode == 0 || radioConfig.preferences.serialplugin_mode == 1) {
if (radioConfig.preferences.serialmodule_mode == 0 || radioConfig.preferences.serialmodule_mode == 1) {
// DEBUG_MSG("* * Message came from the mesh\n");
// Serial2.println("* * Message came from the mesh");
Serial2.printf("%s", p.payload.bytes);
} else if (radioConfig.preferences.serialplugin_mode == 10) {
} else if (radioConfig.preferences.serialmodule_mode == 10) {
/*
@jobionekabnoi
Add code here to handle what gets sent out to the serial interface.
@@ -202,7 +202,7 @@ ProcessMessage SerialPluginRadio::handleReceived(const MeshPacket &mp)
}
} else {
DEBUG_MSG("Serial Plugin Disabled\n");
DEBUG_MSG("Serial Module Disabled\n");
}
#endif

View File

@@ -6,24 +6,24 @@
#include <Arduino.h>
#include <functional>
class SerialPlugin : private concurrency::OSThread
class SerialModule : private concurrency::OSThread
{
bool firstTime = 1;
public:
SerialPlugin();
SerialModule();
protected:
virtual int32_t runOnce() override;
};
extern SerialPlugin *serialPlugin;
extern SerialModule *serialModule;
/*
* Radio interface for SerialPlugin
* Radio interface for SerialModule
*
*/
class SerialPluginRadio : public SinglePortPlugin
class SerialModuleRadio : public SinglePortPlugin
{
uint32_t lastRxID = 0;
@@ -33,7 +33,7 @@ class SerialPluginRadio : public SinglePortPlugin
from the main code.
*/
SerialPluginRadio();
SerialModuleRadio();
/**
* Send our payload into the mesh
@@ -50,4 +50,4 @@ class SerialPluginRadio : public SinglePortPlugin
virtual ProcessMessage handleReceived(const MeshPacket &mp) override;
};
extern SerialPluginRadio *serialPluginRadio;
extern SerialModuleRadio *serialModuleRadio;

View File

@@ -1,4 +1,4 @@
#include "StoreForwardPlugin.h"
#include "StoreForwardModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
@@ -7,19 +7,19 @@
#include "configuration.h"
#include "mesh-pb-constants.h"
#include "mesh/generated/storeforward.pb.h"
#include "plugins/PluginDev.h"
#include "modules/ModuleDev.h"
#include <Arduino.h>
#include <iterator>
#include <map>
StoreForwardPlugin *storeForwardPlugin;
StoreForwardModule *storeForwardModule;
int32_t StoreForwardPlugin::runOnce()
int32_t StoreForwardModule::runOnce()
{
#ifndef NO_ESP32
if (radioConfig.preferences.store_forward_plugin_enabled) {
if (radioConfig.preferences.store_forward_module_enabled) {
if (radioConfig.preferences.is_router) {
@@ -31,11 +31,11 @@ int32_t StoreForwardPlugin::runOnce()
if (airTime->channelUtilizationPercent() < 25) {
// DEBUG_MSG("--- --- --- In busy loop 1 %d\n", this->packetHistoryTXQueue_index);
storeForwardPlugin->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
storeForwardModule->sendPayload(this->busyTo, this->packetHistoryTXQueue_index);
if (this->packetHistoryTXQueue_index == packetHistoryTXQueue_size) {
strcpy(this->routerMessage, "** S&F - Done");
storeForwardPlugin->sendMessage(this->busyTo, this->routerMessage);
storeForwardModule->sendMessage(this->busyTo, this->routerMessage);
// DEBUG_MSG("--- --- --- In busy loop - Done \n");
this->packetHistoryTXQueue_index = 0;
@@ -52,13 +52,13 @@ int32_t StoreForwardPlugin::runOnce()
return (this->packetTimeMax);
} else {
DEBUG_MSG("Store & Forward Plugin - Disabled (is_router = false)\n");
DEBUG_MSG("Store & Forward Module - Disabled (is_router = false)\n");
return (INT32_MAX);
}
} else {
DEBUG_MSG("Store & Forward Plugin - Disabled\n");
DEBUG_MSG("Store & Forward Module - Disabled\n");
return (INT32_MAX);
}
@@ -70,7 +70,7 @@ int32_t StoreForwardPlugin::runOnce()
/*
Create our data structure in the PSRAM.
*/
void StoreForwardPlugin::populatePSRAM()
void StoreForwardModule::populatePSRAM()
{
/*
For PSRAM usage, see:
@@ -104,7 +104,7 @@ void StoreForwardPlugin::populatePSRAM()
DEBUG_MSG(" numberOfPackets for packetHistory - %u\n", numberOfPackets);
}
void StoreForwardPlugin::historyReport()
void StoreForwardModule::historyReport()
{
DEBUG_MSG("Iterating through the message history...\n");
DEBUG_MSG("Message history contains %u records\n", this->packetHistoryCurrent);
@@ -113,27 +113,27 @@ void StoreForwardPlugin::historyReport()
/*
*
*/
void StoreForwardPlugin::historySend(uint32_t msAgo, uint32_t to)
void StoreForwardModule::historySend(uint32_t msAgo, uint32_t to)
{
// uint32_t packetsSent = 0;
uint32_t queueSize = storeForwardPlugin->historyQueueCreate(msAgo, to);
uint32_t queueSize = storeForwardModule->historyQueueCreate(msAgo, to);
if (queueSize) {
snprintf(this->routerMessage, 80, "** S&F - Sending %u message(s)", queueSize);
storeForwardPlugin->sendMessage(to, this->routerMessage);
storeForwardModule->sendMessage(to, this->routerMessage);
this->busy = true; // runOnce() will pickup the next steps once busy = true.
this->busyTo = to;
} else {
strcpy(this->routerMessage, "** S&F - No history to send");
storeForwardPlugin->sendMessage(to, this->routerMessage);
storeForwardModule->sendMessage(to, this->routerMessage);
}
}
uint32_t StoreForwardPlugin::historyQueueCreate(uint32_t msAgo, uint32_t to)
uint32_t StoreForwardModule::historyQueueCreate(uint32_t msAgo, uint32_t to)
{
// uint32_t packetHistoryTXQueueIndex = 0;
@@ -177,7 +177,7 @@ uint32_t StoreForwardPlugin::historyQueueCreate(uint32_t msAgo, uint32_t to)
return this->packetHistoryTXQueue_size;
}
void StoreForwardPlugin::historyAdd(const MeshPacket &mp)
void StoreForwardModule::historyAdd(const MeshPacket &mp)
{
const auto &p = mp.decoded;
@@ -191,13 +191,13 @@ void StoreForwardPlugin::historyAdd(const MeshPacket &mp)
this->packetHistoryCurrent++;
}
MeshPacket *StoreForwardPlugin::allocReply()
MeshPacket *StoreForwardModule::allocReply()
{
auto reply = allocDataPacket(); // Allocate a packet for sending
return reply;
}
void StoreForwardPlugin::sendPayload(NodeNum dest, uint32_t packetHistory_index)
void StoreForwardModule::sendPayload(NodeNum dest, uint32_t packetHistory_index)
{
DEBUG_MSG("Sending S&F Payload\n");
MeshPacket *p = allocReply();
@@ -218,7 +218,7 @@ void StoreForwardPlugin::sendPayload(NodeNum dest, uint32_t packetHistory_index)
service.sendToMesh(p);
}
void StoreForwardPlugin::sendMessage(NodeNum dest, char *str)
void StoreForwardModule::sendMessage(NodeNum dest, char *str)
{
MeshPacket *p = allocReply();
@@ -240,10 +240,10 @@ void StoreForwardPlugin::sendMessage(NodeNum dest, char *str)
// HardwareMessage_init_default
}
ProcessMessage StoreForwardPlugin::handleReceived(const MeshPacket &mp)
ProcessMessage StoreForwardModule::handleReceived(const MeshPacket &mp)
{
#ifndef NO_ESP32
if (radioConfig.preferences.store_forward_plugin_enabled) {
if (radioConfig.preferences.store_forward_module_enabled) {
DEBUG_MSG("--- S&F Received something\n");
@@ -261,9 +261,9 @@ ProcessMessage StoreForwardPlugin::handleReceived(const MeshPacket &mp)
// Send the last 60 minutes of messages.
if (this->busy) {
strcpy(this->routerMessage, "** S&F - Busy. Try again shortly.");
storeForwardPlugin->sendMessage(getFrom(&mp), this->routerMessage);
storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage);
} else {
storeForwardPlugin->historySend(1000 * 60, getFrom(&mp));
storeForwardModule->historySend(1000 * 60, getFrom(&mp));
}
} else if ((p.payload.bytes[0] == 'S') && (p.payload.bytes[1] == 'F') && (p.payload.bytes[2] == 'm') &&
(p.payload.bytes[3] == 0x00)) {
@@ -271,10 +271,10 @@ ProcessMessage StoreForwardPlugin::handleReceived(const MeshPacket &mp)
"01234567890123456789012345678901234567890123456789012345678901234567890123456789"
"01234567890123456789012345678901234567890123456789012345678901234567890123456",
sizeof(this->routerMessage));
storeForwardPlugin->sendMessage(getFrom(&mp), this->routerMessage);
storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage);
} else {
storeForwardPlugin->historyAdd(mp);
storeForwardModule->historyAdd(mp);
}
} else if (mp.decoded.portnum == PortNum_STORE_FORWARD_APP) {
@@ -285,7 +285,7 @@ ProcessMessage StoreForwardPlugin::handleReceived(const MeshPacket &mp)
}
} else {
DEBUG_MSG("Store & Forward Plugin - Disabled\n");
DEBUG_MSG("Store & Forward Module - Disabled\n");
}
#endif
@@ -293,10 +293,10 @@ ProcessMessage StoreForwardPlugin::handleReceived(const MeshPacket &mp)
return ProcessMessage::CONTINUE; // Let others look at this message also if they want
}
ProcessMessage StoreForwardPlugin::handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p)
ProcessMessage StoreForwardModule::handleReceivedProtobuf(const MeshPacket &mp, StoreAndForward *p)
{
if (!radioConfig.preferences.store_forward_plugin_enabled) {
// If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume
if (!radioConfig.preferences.store_forward_module_enabled) {
// If this module is not enabled in any capacity, don't handle the packet, and allow other modules to consume
return ProcessMessage::CONTINUE;
}
@@ -323,9 +323,9 @@ ProcessMessage StoreForwardPlugin::handleReceivedProtobuf(const MeshPacket &mp,
// Send the last 60 minutes of messages.
if (this->busy) {
strcpy(this->routerMessage, "** S&F - Busy. Try again shortly.");
storeForwardPlugin->sendMessage(getFrom(&mp), this->routerMessage);
storeForwardModule->sendMessage(getFrom(&mp), this->routerMessage);
} else {
storeForwardPlugin->historySend(1000 * 60, getFrom(&mp));
storeForwardModule->historySend(1000 * 60, getFrom(&mp));
}
break;
@@ -377,8 +377,8 @@ ProcessMessage StoreForwardPlugin::handleReceivedProtobuf(const MeshPacket &mp,
return ProcessMessage::STOP; // There's no need for others to look at this message.
}
StoreForwardPlugin::StoreForwardPlugin()
: SinglePortPlugin("StoreForwardPlugin", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("StoreForwardPlugin")
StoreForwardModule::StoreForwardModule()
: SinglePortPlugin("StoreForwardModule", PortNum_TEXT_MESSAGE_APP), concurrency::OSThread("StoreForwardModule")
{
#ifndef NO_ESP32
@@ -387,57 +387,57 @@ StoreForwardPlugin::StoreForwardPlugin()
if (StoreForward_Dev) {
/*
Uncomment the preferences below if you want to use the plugin
Uncomment the preferences below if you want to use the module
without having to configure it from the PythonAPI or WebUI.
*/
radioConfig.preferences.store_forward_plugin_enabled = 1;
radioConfig.preferences.store_forward_module_enabled = 1;
radioConfig.preferences.is_router = 1;
radioConfig.preferences.is_always_powered = 1;
}
if (radioConfig.preferences.store_forward_plugin_enabled) {
if (radioConfig.preferences.store_forward_module_enabled) {
// Router
if (radioConfig.preferences.is_router) {
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled as Router\n");
DEBUG_MSG("Initializing Store & Forward Module - Enabled as Router\n");
if (ESP.getPsramSize()) {
if (ESP.getFreePsram() >= 1024 * 1024) {
// Do the startup here
// Maximum number of records to return.
if (radioConfig.preferences.store_forward_plugin_history_return_max)
this->historyReturnMax = radioConfig.preferences.store_forward_plugin_history_return_max;
if (radioConfig.preferences.store_forward_module_history_return_max)
this->historyReturnMax = radioConfig.preferences.store_forward_module_history_return_max;
// Maximum time window for records to return (in minutes)
if (radioConfig.preferences.store_forward_plugin_history_return_window)
this->historyReturnWindow = radioConfig.preferences.store_forward_plugin_history_return_window;
if (radioConfig.preferences.store_forward_module_history_return_window)
this->historyReturnWindow = radioConfig.preferences.store_forward_module_history_return_window;
// Maximum number of records to store in memory
if (radioConfig.preferences.store_forward_plugin_records)
this->records = radioConfig.preferences.store_forward_plugin_records;
if (radioConfig.preferences.store_forward_module_records)
this->records = radioConfig.preferences.store_forward_module_records;
// Maximum number of records to store in memory
if (radioConfig.preferences.store_forward_plugin_heartbeat)
this->heartbeat = radioConfig.preferences.store_forward_plugin_heartbeat;
if (radioConfig.preferences.store_forward_module_heartbeat)
this->heartbeat = radioConfig.preferences.store_forward_module_heartbeat;
// Popupate PSRAM with our data structures.
this->populatePSRAM();
} else {
DEBUG_MSG("Device has less than 1M of PSRAM free. Aborting startup.\n");
DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n");
DEBUG_MSG("Store & Forward Module - Aborting Startup.\n");
}
} else {
DEBUG_MSG("Device doesn't have PSRAM.\n");
DEBUG_MSG("Store & Forward Plugin - Aborting Startup.\n");
DEBUG_MSG("Store & Forward Module - Aborting Startup.\n");
}
// Client
} else {
DEBUG_MSG("Initializing Store & Forward Plugin - Enabled as Client\n");
DEBUG_MSG("Initializing Store & Forward Module - Enabled as Client\n");
}
}
#endif

View File

@@ -18,7 +18,7 @@ struct PacketHistoryStruct {
pb_size_t payload_size;
};
class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThread
class StoreForwardModule : public SinglePortPlugin, private concurrency::OSThread
{
// bool firstTime = 1;
bool busy = 0;
@@ -37,7 +37,7 @@ class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThrea
uint32_t packetTimeMax = 2000;
public:
StoreForwardPlugin();
StoreForwardModule();
/**
Update our local reference of when we last saw that node.
@@ -82,4 +82,4 @@ class StoreForwardPlugin : public SinglePortPlugin, private concurrency::OSThrea
};
extern StoreForwardPlugin *storeForwardPlugin;
extern StoreForwardModule *storeForwardModule;

View File

@@ -1,288 +0,0 @@
#include "configuration.h"
#include "CannedMessagePlugin.h"
#include "MeshService.h"
// TODO: reuse defined from Screen.cpp
#define FONT_SMALL ArialMT_Plain_10
#define FONT_MEDIUM ArialMT_Plain_16
#define FONT_LARGE ArialMT_Plain_24
// Remove Canned message screen if no action is taken for some milliseconds
#define INACTIVATE_AFTER_MS 20000
CannedMessagePlugin *cannedMessagePlugin;
CannedMessagePlugin::CannedMessagePlugin()
: SinglePortPlugin("canned", PortNum_TEXT_MESSAGE_APP),
concurrency::OSThread("CannedMessagePlugin")
{
if (radioConfig.preferences.canned_message_plugin_enabled)
{
if(this->splitConfiguredMessages() <= 0)
{
radioConfig.preferences.canned_message_plugin_enabled = false;
DEBUG_MSG("CannedMessagePlugin: No messages are configured. Plugin is disabled\n");
return;
}
this->inputObserver.observe(inputBroker);
}
}
/**
* @brief Items in array this->messages will be set to be pointing on the right
* starting points of the string this->messageStore
*
* @return int Returns the number of messages found.
*/
int CannedMessagePlugin::splitConfiguredMessages()
{
int messageIndex = 0;
int i = 0;
strncpy(
this->messageStore,
radioConfig.preferences.canned_message_plugin_messages,
CANNED_MESSAGE_PLUGIN_MESSAGES_SIZE);
this->messages[messageIndex++] =
this->messageStore;
int upTo =
strlen(this->messageStore) - 1;
while (i < upTo)
{
if (this->messageStore[i] == '|')
{
// Message ending found, replace it with string-end character.
this->messageStore[i] = '\0';
DEBUG_MSG("CannedMessage %d is: '%s'\n",
messageIndex-1, this->messages[messageIndex-1]);
if (messageIndex >= CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_COUNT)
{
this->messagesCount = messageIndex;
return this->messagesCount;
}
// Next message starts after pipe (|) just found.
this->messages[messageIndex++] =
(this->messageStore + i + 1);
}
i += 1;
}
if (strlen(this->messages[messageIndex-1]) > 0)
{
DEBUG_MSG("CannedMessage %d is: '%s'\n",
messageIndex-1, this->messages[messageIndex-1]);
this->messagesCount = messageIndex;
}
else
{
this->messagesCount = messageIndex-1;
}
return this->messagesCount;
}
int CannedMessagePlugin::handleInputEvent(const InputEvent *event)
{
if (
(strlen(radioConfig.preferences.canned_message_plugin_allow_input_source) > 0) &&
(strcmp(radioConfig.preferences.canned_message_plugin_allow_input_source, event->source) != 0) &&
(strcmp(radioConfig.preferences.canned_message_plugin_allow_input_source, "_any") != 0))
{
// Event source is not accepted.
return 0;
}
bool validEvent = false;
if (event->inputEvent == static_cast<char>(InputEventChar_KEY_UP))
{
DEBUG_MSG("Canned message event UP\n");
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_UP;
validEvent = true;
}
if (event->inputEvent == static_cast<char>(InputEventChar_KEY_DOWN))
{
DEBUG_MSG("Canned message event DOWN\n");
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_DOWN;
validEvent = true;
}
if (event->inputEvent == static_cast<char>(InputEventChar_KEY_SELECT))
{
DEBUG_MSG("Canned message event Select\n");
this->runState = CANNED_MESSAGE_RUN_STATE_ACTION_SELECT;
validEvent = true;
}
if (validEvent)
{
// Let runOnce to be called immediately.
setIntervalFromNow(0);
}
return 0;
}
void CannedMessagePlugin::sendText(NodeNum dest,
const char* message,
bool wantReplies)
{
MeshPacket *p = allocDataPacket();
p->to = dest;
p->want_ack = true;
p->decoded.payload.size = strlen(message);
memcpy(p->decoded.payload.bytes, message, p->decoded.payload.size);
if (radioConfig.preferences.canned_message_plugin_send_bell)
{
p->decoded.payload.bytes[p->decoded.payload.size-1] = 7; // Bell character
p->decoded.payload.bytes[p->decoded.payload.size] = '\0'; // Bell character
p->decoded.payload.size++;
}
// PacketId prevPacketId = p->id; // In case we need it later.
DEBUG_MSG("Sending message id=%d, msg=%.*s\n",
p->id, p->decoded.payload.size, p->decoded.payload.bytes);
service.sendToMesh(p);
}
int32_t CannedMessagePlugin::runOnce()
{
if ((!radioConfig.preferences.canned_message_plugin_enabled)
|| (this->runState == CANNED_MESSAGE_RUN_STATE_INACTIVE))
{
return 30000; // TODO: should return MAX_VAL
}
DEBUG_MSG("Check status\n");
UIFrameEvent e = {false, true};
if (this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE)
{
// TODO: might have some feedback of sendig state
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
e.frameChanged = true;
this->currentMessageIndex = -1;
this->notifyObservers(&e);
}
else if (
(this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE)
&& (millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)
{
// Reset plugin
DEBUG_MSG("Reset due the lack of activity.\n");
e.frameChanged = true;
this->currentMessageIndex = -1;
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
this->notifyObservers(&e);
}
else if (this->currentMessageIndex == -1)
{
this->currentMessageIndex = 0;
DEBUG_MSG("First touch.\n");
e.frameChanged = true;
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
}
else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT)
{
sendText(
NODENUM_BROADCAST,
this->messages[this->currentMessageIndex],
true);
this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE;
this->currentMessageIndex = -1;
this->notifyObservers(&e);
return 2000;
}
else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP)
{
this->currentMessageIndex = getPrevIndex();
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
DEBUG_MSG("MOVE UP\n");
}
else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_DOWN)
{
this->currentMessageIndex = this->getNextIndex();
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
DEBUG_MSG("MOVE DOWN\n");
}
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE)
{
this->lastTouchMillis = millis();
this->notifyObservers(&e);
return INACTIVATE_AFTER_MS;
}
return 30000; // TODO: should return MAX_VAL
}
const char* CannedMessagePlugin::getCurrentMessage()
{
return this->messages[this->currentMessageIndex];
}
const char* CannedMessagePlugin::getPrevMessage()
{
return this->messages[this->getPrevIndex()];
}
const char* CannedMessagePlugin::getNextMessage()
{
return this->messages[this->getNextIndex()];
}
bool CannedMessagePlugin::shouldDraw()
{
if (!radioConfig.preferences.canned_message_plugin_enabled)
{
return false;
}
return (currentMessageIndex != -1) || (this->runState != CANNED_MESSAGE_RUN_STATE_INACTIVE);
}
int CannedMessagePlugin::getNextIndex()
{
if (this->currentMessageIndex >= (this->messagesCount -1))
{
return 0;
}
else
{
return this->currentMessageIndex + 1;
}
}
int CannedMessagePlugin::getPrevIndex()
{
if (this->currentMessageIndex <= 0)
{
return this->messagesCount - 1;
}
else
{
return this->currentMessageIndex - 1;
}
}
void CannedMessagePlugin::drawFrame(
OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
displayedNodeNum = 0; // Not currently showing a node pane
if (cannedMessagePlugin->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE)
{
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth()/2 + x, 0 + y + 12, "Sending...");
}
else
{
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
display->drawString(0 + x, 0 + y, cannedMessagePlugin->getPrevMessage());
display->setFont(FONT_MEDIUM);
display->drawString(0 + x, 0 + y + 8, cannedMessagePlugin->getCurrentMessage());
display->setFont(FONT_SMALL);
display->drawString(0 + x, 0 + y + 24, cannedMessagePlugin->getNextMessage());
}
}

View File

@@ -1,69 +0,0 @@
#pragma once
#include "SinglePortPlugin.h"
#include "input/InputBroker.h"
enum cannedMessagePluginRunState
{
CANNED_MESSAGE_RUN_STATE_INACTIVE,
CANNED_MESSAGE_RUN_STATE_ACTIVE,
CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE,
CANNED_MESSAGE_RUN_STATE_ACTION_SELECT,
CANNED_MESSAGE_RUN_STATE_ACTION_UP,
CANNED_MESSAGE_RUN_STATE_ACTION_DOWN
};
#define CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_COUNT 50
/**
* Due to config-packet size restrictions we cannot have user configuration bigger
* than Constants_DATA_PAYLOAD_LEN bytes.
*/
#define CANNED_MESSAGE_PLUGIN_MESSAGES_SIZE 200
class CannedMessagePlugin :
public SinglePortPlugin,
public Observable<const UIFrameEvent *>,
private concurrency::OSThread
{
CallbackObserver<CannedMessagePlugin, const InputEvent *> inputObserver =
CallbackObserver<CannedMessagePlugin, const InputEvent *>(
this, &CannedMessagePlugin::handleInputEvent);
public:
CannedMessagePlugin();
const char* getCurrentMessage();
const char* getPrevMessage();
const char* getNextMessage();
bool shouldDraw();
void eventUp();
void eventDown();
void eventSelect();
protected:
virtual int32_t runOnce() override;
void sendText(
NodeNum dest,
const char* message,
bool wantReplies);
int splitConfiguredMessages();
int getNextIndex();
int getPrevIndex();
int handleInputEvent(const InputEvent *event);
virtual bool wantUIFrame() override { return this->shouldDraw(); }
virtual Observable<const UIFrameEvent *>* getUIFrameObservable() override { return this; }
virtual void drawFrame(
OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) override;
int currentMessageIndex = -1;
cannedMessagePluginRunState runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
char messageStore[CANNED_MESSAGE_PLUGIN_MESSAGES_SIZE];
char *messages[CANNED_MESSAGE_PLUGIN_MESSAGE_MAX_COUNT];
int messagesCount = 0;
unsigned long lastTouchMillis = 0;
};
extern CannedMessagePlugin *cannedMessagePlugin;

View File

@@ -1,299 +0,0 @@
#include "EnvironmentalMeasurementPlugin.h"
#include "../mesh/generated/environmental_measurement.pb.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
#include <OLEDDisplay.h>
#include <OLEDDisplayUi.h>
// Sensors
#include "Sensor/BME280Sensor.h"
#include "Sensor/BME680Sensor.h"
#include "Sensor/DHTSensor.h"
#include "Sensor/DallasSensor.h"
#include "Sensor/MCP9808Sensor.h"
BME280Sensor bme280Sensor;
BME680Sensor bme680Sensor;
DHTSensor dhtSensor;
DallasSensor dallasSensor;
MCP9808Sensor mcp9808Sensor;
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
#ifdef HAS_EINK
// The screen is bigger so use bigger fonts
#define FONT_SMALL ArialMT_Plain_16
#define FONT_MEDIUM ArialMT_Plain_24
#define FONT_LARGE ArialMT_Plain_24
#else
#define FONT_SMALL ArialMT_Plain_10
#define FONT_MEDIUM ArialMT_Plain_16
#define FONT_LARGE ArialMT_Plain_24
#endif
#define fontHeight(font) ((font)[1] + 1) // height is position 1
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
int32_t EnvironmentalMeasurementPlugin::runOnce()
{
#ifndef PORTDUINO
/*
Uncomment the preferences below if you want to use the plugin
without having to configure it from the PythonAPI or WebUI.
*/
/*
radioConfig.preferences.environmental_measurement_plugin_measurement_enabled = 1;
radioConfig.preferences.environmental_measurement_plugin_screen_enabled = 1;
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold = 5;
radioConfig.preferences.environmental_measurement_plugin_update_interval = 600;
radioConfig.preferences.environmental_measurement_plugin_recovery_interval = 60;
radioConfig.preferences.environmental_measurement_plugin_display_farenheit = false;
radioConfig.preferences.environmental_measurement_plugin_sensor_pin = 13;
radioConfig.preferences.environmental_measurement_plugin_sensor_type =
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType::
RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_BME280;
*/
if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled ||
radioConfig.preferences.environmental_measurement_plugin_screen_enabled)) {
// If this plugin is not enabled, and the user doesn't want the display screen don't waste any OSThread time on it
return (INT32_MAX);
}
if (firstTime) {
// This is the first time the OSThread library has called this function, so do some setup
firstTime = 0;
if (radioConfig.preferences.environmental_measurement_plugin_measurement_enabled) {
DEBUG_MSG("EnvironmentalMeasurement: Initializing\n");
// it's possible to have this plugin enabled, only for displaying values on the screen.
// therefore, we should only enable the sensor loop if measurement is also enabled
switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11:
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT12:
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT21:
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT22:
return dhtSensor.runOnce();
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20:
return dallasSensor.runOnce();
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_BME280:
return bme280Sensor.runOnce();
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_BME680:
return bme680Sensor.runOnce();
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MCP9808:
return mcp9808Sensor.runOnce();
default:
DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin");
return (INT32_MAX);
break;
}
}
return (INT32_MAX);
} else {
// if we somehow got to a second run of this plugin with measurement disabled, then just wait forever
if (!radioConfig.preferences.environmental_measurement_plugin_measurement_enabled)
return (INT32_MAX);
// this is not the first time OSThread library has called this function
// so just do what we intend to do on the interval
if (sensor_read_error_count > radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold) {
if (radioConfig.preferences.environmental_measurement_plugin_recovery_interval > 0) {
DEBUG_MSG("EnvironmentalMeasurement: TEMPORARILY DISABLED; The "
"environmental_measurement_plugin_read_error_count_threshold has been exceed: %d. Will retry reads in "
"%d seconds\n",
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold,
radioConfig.preferences.environmental_measurement_plugin_recovery_interval);
sensor_read_error_count = 0;
return (radioConfig.preferences.environmental_measurement_plugin_recovery_interval * 1000);
}
DEBUG_MSG("EnvironmentalMeasurement: DISABLED; The environmental_measurement_plugin_read_error_count_threshold has "
"been exceed: %d. Reads will not be retried until after device reset\n",
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold);
return (INT32_MAX);
} else if (sensor_read_error_count > 0) {
DEBUG_MSG("EnvironmentalMeasurement: There have been %d sensor read failures. Will retry %d more times\n",
sensor_read_error_count, sensor_read_error_count, sensor_read_error_count,
radioConfig.preferences.environmental_measurement_plugin_read_error_count_threshold -
sensor_read_error_count);
}
if (!sendOurEnvironmentalMeasurement()) {
// if we failed to read the sensor, then try again
// as soon as we can according to the maximum polling frequency
switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11:
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT12:
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT21:
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT22:
return (DHT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20:
return (DS18B20_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_BME280:
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_BME680:
return (BME_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MCP9808:
return (MCP_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
default:
return (DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS);
}
}
}
// The return of runOnce is an int32 representing the desired number of
// miliseconds until the function should be called again by the
// OSThread library. Multiply the preference value by 1000 to convert seconds to miliseconds
return (radioConfig.preferences.environmental_measurement_plugin_update_interval * 1000);
#endif
}
bool EnvironmentalMeasurementPlugin::wantUIFrame()
{
return radioConfig.preferences.environmental_measurement_plugin_screen_enabled;
}
String GetSenderName(const MeshPacket &mp)
{
String sender;
auto node = nodeDB.getNode(getFrom(&mp));
if (node) {
sender = node->user.short_name;
} else {
sender = "UNK";
}
return sender;
}
uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp)
{
uint32_t now = getTime();
uint32_t last_seen = mp->rx_time;
int delta = (int)(now - last_seen);
if (delta < 0) // our clock must be slightly off still - not set from GPS yet
delta = 0;
return delta;
}
float EnvironmentalMeasurementPlugin::CelsiusToFarenheit(float c)
{
return (c * 9) / 5 + 32;
}
void EnvironmentalMeasurementPlugin::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_MEDIUM);
display->drawString(x, y, "Environment");
if (lastMeasurementPacket == nullptr) {
display->setFont(FONT_SMALL);
display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement");
return;
}
EnvironmentalMeasurement lastMeasurement;
uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket);
String lastSender = GetSenderName(*lastMeasurementPacket);
auto &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, EnvironmentalMeasurement_fields, &lastMeasurement)) {
display->setFont(FONT_SMALL);
display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
DEBUG_MSG("EnvironmentalMeasurement: unable to decode last packet");
return;
}
display->setFont(FONT_SMALL);
String last_temp = String(lastMeasurement.temperature, 0) + "°C";
if (radioConfig.preferences.environmental_measurement_plugin_display_farenheit) {
last_temp = String(CelsiusToFarenheit(lastMeasurement.temperature), 0) + "°F";
}
display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + lastSender + "(" + String(agoSecs) + "s)");
display->drawString(x, y += fontHeight(FONT_SMALL) - 2,"Temp/Hum: " + last_temp + " / " + String(lastMeasurement.relative_humidity, 0) + "%");
if (lastMeasurement.barometric_pressure != 0)
display->drawString(x, y += fontHeight(FONT_SMALL),"Press: " + String(lastMeasurement.barometric_pressure, 0) + "hPA");
}
bool EnvironmentalMeasurementPlugin::handleReceivedProtobuf(const MeshPacket &mp, EnvironmentalMeasurement *p)
{
if (!(radioConfig.preferences.environmental_measurement_plugin_measurement_enabled ||
radioConfig.preferences.environmental_measurement_plugin_screen_enabled)) {
// If this plugin is not enabled in any capacity, don't handle the packet, and allow other plugins to consume
return false;
}
String sender = GetSenderName(mp);
DEBUG_MSG("EnvironmentalMeasurement: Received data from %s\n", sender);
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", p->relative_humidity);
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", p->temperature);
DEBUG_MSG("EnvironmentalMeasurement->barometric_pressure: %f\n", p->barometric_pressure);
DEBUG_MSG("EnvironmentalMeasurement->gas_resistance: %f\n", p->gas_resistance);
lastMeasurementPacket = packetPool.allocCopy(mp);
return false; // Let others look at this message also if they want
}
bool EnvironmentalMeasurementPlugin::sendOurEnvironmentalMeasurement(NodeNum dest, bool wantReplies)
{
EnvironmentalMeasurement m;
m.barometric_pressure = 0;
m.gas_resistance = 0;
DEBUG_MSG("-----------------------------------------\n");
DEBUG_MSG("EnvironmentalMeasurement: Read data\n");
switch (radioConfig.preferences.environmental_measurement_plugin_sensor_type) {
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DS18B20:
if (!dallasSensor.getMeasurement(&m))
sensor_read_error_count++;
break;
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT11:
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT12:
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT21:
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_DHT22:
if (!dhtSensor.getMeasurement(&m))
sensor_read_error_count++;
break;
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_BME280:
bme280Sensor.getMeasurement(&m);
break;
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_BME680:
bme680Sensor.getMeasurement(&m);
break;
case RadioConfig_UserPreferences_EnvironmentalMeasurementSensorType_MCP9808:
mcp9808Sensor.getMeasurement(&m);
break;
default:
DEBUG_MSG("EnvironmentalMeasurement: Invalid sensor type selected; Disabling plugin");
return false;
}
DEBUG_MSG("EnvironmentalMeasurement->relative_humidity: %f\n", m.relative_humidity);
DEBUG_MSG("EnvironmentalMeasurement->temperature: %f\n", m.temperature);
DEBUG_MSG("EnvironmentalMeasurement->barometric_pressure: %f\n", m.barometric_pressure);
DEBUG_MSG("EnvironmentalMeasurement->gas_resistance: %f\n", m.gas_resistance);
sensor_read_error_count = 0;
MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = wantReplies;
lastMeasurementPacket = packetPool.allocCopy(*p);
DEBUG_MSG("EnvironmentalMeasurement: Sending packet to mesh");
service.sendToMesh(p);
return true;
}

Some files were not shown because too many files have changed in this diff Show More