Revert "Sync Wio lr1110 refresh with master (#4288)"

This reverts commit 5cc8ca59a3.

Revert "Sync Wio lr1110 refresh with master (#4251)"

This reverts commit d97e6b86b8.

Revert "update SD_FLASH_SIZE to 0x27000 (#4232)"

This reverts commit 2df8093fef.
This commit is contained in:
Thomas Göttgens
2024-07-22 15:30:36 +02:00
parent 5cc8ca59a3
commit bdd1c53072
161 changed files with 1322 additions and 22200 deletions

View File

@@ -14,7 +14,7 @@ runs:
- name: Install dependencies - name: Install dependencies
shell: bash shell: bash
run: | run: |
sudo apt-get -y update --fix-missing sudo apt-get -y update
sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev sudo apt-get install -y cppcheck libbluetooth-dev libgpiod-dev libyaml-cpp-dev
- name: Setup Python - name: Setup Python

View File

@@ -13,7 +13,7 @@ jobs:
- name: Install libbluetooth - name: Install libbluetooth
shell: bash shell: bash
run: | run: |
sudo apt-get update --fix-missing sudo apt-get update
sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev sudo apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev
- name: Checkout code - name: Checkout code

View File

@@ -29,7 +29,6 @@ jobs:
name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip name: firmware-${{ inputs.board }}-${{ steps.version.outputs.version }}.zip
overwrite: true overwrite: true
path: | path: |
release/*.hex
release/*.uf2 release/*.uf2
release/*.elf release/*.elf
release/*.zip release/*.zip

View File

@@ -13,7 +13,6 @@ jobs:
- name: Install libbluetooth - name: Install libbluetooth
shell: bash shell: bash
run: | run: |
apt-get update -y --fix-missing
apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev
- name: Checkout code - name: Checkout code

View File

@@ -13,7 +13,6 @@ jobs:
- name: Install libbluetooth - name: Install libbluetooth
shell: bash shell: bash
run: | run: |
apt-get update -y --fix-missing
apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev apt-get install -y libbluetooth-dev libgpiod-dev libyaml-cpp-dev openssl libssl-dev libulfius-dev liborcania-dev
- name: Checkout code - name: Checkout code

View File

@@ -136,7 +136,7 @@ jobs:
build-rpi2040, build-rpi2040,
package-raspbian, package-raspbian,
package-raspbian-armv7l, package-raspbian-armv7l,
package-native, package-native
] ]
steps: steps:
- name: Checkout code - name: Checkout code
@@ -168,7 +168,6 @@ jobs:
path: | path: |
./firmware-*.bin ./firmware-*.bin
./firmware-*.uf2 ./firmware-*.uf2
./firmware-*.hex
./firmware-*-ota.zip ./firmware-*-ota.zip
./device-*.sh ./device-*.sh
./device-*.bat ./device-*.bat

View File

@@ -1,6 +1,6 @@
version: 0.1 version: 0.1
cli: cli:
version: 1.22.2 version: 1.22.1
plugins: plugins:
sources: sources:
- id: trunk - id: trunk
@@ -31,10 +31,6 @@ lint:
- gitleaks@8.18.2 - gitleaks@8.18.2
- clang-format@16.0.3 - clang-format@16.0.3
- prettier@3.2.5 - prettier@3.2.5
ignore:
- linters: [ALL]
paths:
- bin/**
runtimes: runtimes:
enabled: enabled:
- python@3.10.8 - python@3.10.8

View File

@@ -4,8 +4,5 @@
"trunk.enableWindows": true, "trunk.enableWindows": true,
"files.insertFinalNewline": false, "files.insertFinalNewline": false,
"files.trimFinalNewlines": false, "files.trimFinalNewlines": false,
"cmake.configureOnOpen": false, "cmake.configureOnOpen": false
"[cpp]": {
"editor.defaultFormatter": "trunk.io"
}
} }

View File

@@ -44,7 +44,7 @@ lib_deps =
${networking_base.lib_deps} ${networking_base.lib_deps}
${environmental_base.lib_deps} ${environmental_base.lib_deps}
https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2 https://github.com/meshtastic/esp32_https_server.git#23665b3adc080a311dcbb586ed5941b5f94d6ea2
h2zero/NimBLE-Arduino@^1.4.2 h2zero/NimBLE-Arduino@^1.4.1
https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587 https://github.com/dbSuS/libpax.git#7bcd3fcab75037505be9b122ab2b24cc5176b587
https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6 https://github.com/lewisxhe/XPowersLib.git#84b7373faea3118b6c37954d52f98b8a337148d6
https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f https://github.com/meshtastic/ESP32_Codec2.git#633326c78ac251c059ab3a8c430fcdf25b41672f

View File

@@ -2,8 +2,8 @@
set -e set -e
VERSION=$(bin/buildinfo.py long) VERSION=`bin/buildinfo.py long`
SHORT_VERSION=$(bin/buildinfo.py short) SHORT_VERSION=`bin/buildinfo.py short`
OUTDIR=release/ OUTDIR=release/
@@ -11,7 +11,7 @@ rm -f $OUTDIR/firmware*
rm -r $OUTDIR/* || true rm -r $OUTDIR/* || true
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale # Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
platformio pkg update platformio pkg update
echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS" echo "Building for $1 with $PLATFORMIO_BUILD_FLAGS"
rm -f .pio/build/$1/firmware.* rm -f .pio/build/$1/firmware.*
@@ -23,27 +23,14 @@ basename=firmware-$1-$VERSION
pio run --environment $1 # -v pio run --environment $1 # -v
SRCELF=.pio/build/$1/firmware.elf SRCELF=.pio/build/$1/firmware.elf
cp $SRCELF $OUTDIR/$basename.elf
echo "Generating NRF52 dfu file"
DFUPKG=.pio/build/$1/firmware.zip DFUPKG=.pio/build/$1/firmware.zip
cp $SRCELF $OUTDIR/$basename.elf
cp $DFUPKG $OUTDIR/$basename-ota.zip cp $DFUPKG $OUTDIR/$basename-ota.zip
echo "Generating NRF52 uf2 file" echo "Generating NRF52 uf2 file"
SRCHEX=.pio/build/$1/firmware.hex SRCHEX=.pio/build/$1/firmware.hex
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/$basename.uf2 -f 0xADA52840
# if WM1110 target, merge hex with softdevice 7.3.0 cp bin/device-install.* $OUTDIR
if (echo $1 | grep -q "wio-sdk-wm1110"); then cp bin/device-update.* $OUTDIR
echo "Merging with softdevice" cp bin/*.uf2 $OUTDIR
sudo chmod +x ./bin/mergehex
bin/mergehex -m bin/s140_nrf52_7.3.0_softdevice.hex $SRCHEX -o .pio/build/$1/$basename.hex
SRCHEX=.pio/build/$1/$basename.hex
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/$basename.uf2 -f 0xADA52840
cp $SRCHEX $OUTDIR
cp bin/*.uf2 $OUTDIR
else
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/$basename.uf2 -f 0xADA52840
cp bin/device-install.* $OUTDIR
cp bin/device-update.* $OUTDIR
cp bin/*.uf2 $OUTDIR
fi

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +0,0 @@
# shellcheck shell=bash
# (this minor script is actually shell agnostic, and is intended to be sourced rather than run in a subshell)
# This is a little script you can source if you want to make ESP debugging work on a modern (24.04) ubuntu machine
# It assumes you have built and installed python 2.7 from source with:
# ./configure --enable-optimizations --enable-shared --enable-unicode=ucs4
# sudo make clean
# make
# sudo make altinstall
export LD_LIBRARY_PATH=$HOME/packages/python-2.7.18/
export PYTHON_HOME=/usr/local/lib/python2.7/

View File

@@ -1,38 +1,39 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import sys
import os
import os.path
import re
import struct import struct
import subprocess import subprocess
import sys import re
import os
import os.path
import argparse
UF2_MAGIC_START0 = 0x0A324655 # "UF2\n"
UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected UF2_MAGIC_START0 = 0x0A324655 # "UF2\n"
UF2_MAGIC_END = 0x0AB16F30 # Ditto UF2_MAGIC_START1 = 0x9E5D5157 # Randomly selected
UF2_MAGIC_END = 0x0AB16F30 # Ditto
families = { families = {
"SAMD21": 0x68ED2B88, 'SAMD21': 0x68ed2b88,
"SAML21": 0x1851780A, 'SAML21': 0x1851780a,
"SAMD51": 0x55114460, 'SAMD51': 0x55114460,
"NRF52": 0x1B57745F, 'NRF52': 0x1b57745f,
"STM32F0": 0x647824B6, 'STM32F0': 0x647824b6,
"STM32F1": 0x5EE21072, 'STM32F1': 0x5ee21072,
"STM32F2": 0x5D1A0A2E, 'STM32F2': 0x5d1a0a2e,
"STM32F3": 0x6B846188, 'STM32F3': 0x6b846188,
"STM32F4": 0x57755A57, 'STM32F4': 0x57755a57,
"STM32F7": 0x53B80F00, 'STM32F7': 0x53b80f00,
"STM32G0": 0x300F5633, 'STM32G0': 0x300f5633,
"STM32G4": 0x4C71240A, 'STM32G4': 0x4c71240a,
"STM32H7": 0x6DB66082, 'STM32H7': 0x6db66082,
"STM32L0": 0x202E3A91, 'STM32L0': 0x202e3a91,
"STM32L1": 0x1E1F432D, 'STM32L1': 0x1e1f432d,
"STM32L4": 0x00FF6919, 'STM32L4': 0x00ff6919,
"STM32L5": 0x04240BDF, 'STM32L5': 0x04240bdf,
"STM32WB": 0x70D16653, 'STM32WB': 0x70d16653,
"STM32WL": 0x21460FF0, 'STM32WL': 0x21460ff0,
"ATMEGA32": 0x16573617, 'ATMEGA32': 0x16573617,
"MIMXRT10XX": 0x4FB2D5BD, 'MIMXRT10XX': 0x4FB2D5BD
} }
INFO_FILE = "/INFO_UF2.TXT" INFO_FILE = "/INFO_UF2.TXT"
@@ -45,17 +46,15 @@ def is_uf2(buf):
w = struct.unpack("<II", buf[0:8]) w = struct.unpack("<II", buf[0:8])
return w[0] == UF2_MAGIC_START0 and w[1] == UF2_MAGIC_START1 return w[0] == UF2_MAGIC_START0 and w[1] == UF2_MAGIC_START1
def is_hex(buf): def is_hex(buf):
try: try:
w = buf[0:30].decode("utf-8") w = buf[0:30].decode("utf-8")
except UnicodeDecodeError: except UnicodeDecodeError:
return False return False
if w[0] == ":" and re.match(b"^[:0-9a-fA-F\r\n]+$", buf): if w[0] == ':' and re.match(b"^[:0-9a-fA-F\r\n]+$", buf):
return True return True
return False return False
def convert_from_uf2(buf): def convert_from_uf2(buf):
global appstartaddr global appstartaddr
numblocks = len(buf) // 512 numblocks = len(buf) // 512
@@ -63,7 +62,7 @@ def convert_from_uf2(buf):
outp = b"" outp = b""
for blockno in range(numblocks): for blockno in range(numblocks):
ptr = blockno * 512 ptr = blockno * 512
block = buf[ptr : ptr + 512] block = buf[ptr:ptr + 512]
hd = struct.unpack(b"<IIIIIIII", block[0:32]) hd = struct.unpack(b"<IIIIIIII", block[0:32])
if hd[0] != UF2_MAGIC_START0 or hd[1] != UF2_MAGIC_START1: if hd[0] != UF2_MAGIC_START0 or hd[1] != UF2_MAGIC_START1:
print("Skipping block at " + ptr + "; bad magic") print("Skipping block at " + ptr + "; bad magic")
@@ -81,7 +80,7 @@ def convert_from_uf2(buf):
padding = newaddr - curraddr padding = newaddr - curraddr
if padding < 0: if padding < 0:
assert False, "Block out of order at " + ptr assert False, "Block out of order at " + ptr
if padding > 10 * 1024 * 1024: if padding > 10*1024*1024:
assert False, "More than 10M of padding needed at " + ptr assert False, "More than 10M of padding needed at " + ptr
if padding % 4 != 0: if padding % 4 != 0:
assert False, "Non-word padding size at " + ptr assert False, "Non-word padding size at " + ptr
@@ -92,7 +91,6 @@ def convert_from_uf2(buf):
curraddr = newaddr + datalen curraddr = newaddr + datalen
return outp return outp
def convert_to_carray(file_content): def convert_to_carray(file_content):
outp = "const unsigned char bindata[] __attribute__((aligned(16))) = {" outp = "const unsigned char bindata[] __attribute__((aligned(16))) = {"
for i in range(len(file_content)): for i in range(len(file_content)):
@@ -102,7 +100,6 @@ def convert_to_carray(file_content):
outp += "\n};\n" outp += "\n};\n"
return outp return outp
def convert_to_uf2(file_content): def convert_to_uf2(file_content):
global familyid global familyid
datapadding = b"" datapadding = b""
@@ -112,21 +109,13 @@ def convert_to_uf2(file_content):
outp = b"" outp = b""
for blockno in range(numblocks): for blockno in range(numblocks):
ptr = 256 * blockno ptr = 256 * blockno
chunk = file_content[ptr : ptr + 256] chunk = file_content[ptr:ptr + 256]
flags = 0x0 flags = 0x0
if familyid: if familyid:
flags |= 0x2000 flags |= 0x2000
hd = struct.pack( hd = struct.pack(b"<IIIIIIII",
b"<IIIIIIII", UF2_MAGIC_START0, UF2_MAGIC_START1,
UF2_MAGIC_START0, flags, ptr + appstartaddr, 256, blockno, numblocks, familyid)
UF2_MAGIC_START1,
flags,
ptr + appstartaddr,
256,
blockno,
numblocks,
familyid,
)
while len(chunk) < 256: while len(chunk) < 256:
chunk += b"\x00" chunk += b"\x00"
block = hd + chunk + datapadding + struct.pack(b"<I", UF2_MAGIC_END) block = hd + chunk + datapadding + struct.pack(b"<I", UF2_MAGIC_END)
@@ -134,7 +123,6 @@ def convert_to_uf2(file_content):
outp += block outp += block
return outp return outp
class Block: class Block:
def __init__(self, addr): def __init__(self, addr):
self.addr = addr self.addr = addr
@@ -145,44 +133,35 @@ class Block:
flags = 0x0 flags = 0x0
if familyid: if familyid:
flags |= 0x2000 flags |= 0x2000
hd = struct.pack( hd = struct.pack("<IIIIIIII",
"<IIIIIIII", UF2_MAGIC_START0, UF2_MAGIC_START1,
UF2_MAGIC_START0, flags, self.addr, 256, blockno, numblocks, familyid)
UF2_MAGIC_START1,
flags,
self.addr,
256,
blockno,
numblocks,
familyid,
)
hd += self.bytes[0:256] hd += self.bytes[0:256]
while len(hd) < 512 - 4: while len(hd) < 512 - 4:
hd += b"\x00" hd += b"\x00"
hd += struct.pack("<I", UF2_MAGIC_END) hd += struct.pack("<I", UF2_MAGIC_END)
return hd return hd
def convert_from_hex_to_uf2(buf): def convert_from_hex_to_uf2(buf):
global appstartaddr global appstartaddr
appstartaddr = None appstartaddr = None
upper = 0 upper = 0
currblock = None currblock = None
blocks = [] blocks = []
for line in buf.split("\n"): for line in buf.split('\n'):
if line[0] != ":": if line[0] != ":":
continue continue
i = 1 i = 1
rec = [] rec = []
while i < len(line) - 1: while i < len(line) - 1:
rec.append(int(line[i : i + 2], 16)) rec.append(int(line[i:i+2], 16))
i += 2 i += 2
tp = rec[3] tp = rec[3]
if tp == 4: if tp == 4:
upper = ((rec[4] << 8) | rec[5]) << 16 upper = ((rec[4] << 8) | rec[5]) << 16
elif tp == 2: elif tp == 2:
upper = ((rec[4] << 8) | rec[5]) << 4 upper = ((rec[4] << 8) | rec[5]) << 4
assert (upper & 0xFFFF) == 0 assert (upper & 0xffff) == 0
elif tp == 1: elif tp == 1:
break break
elif tp == 0: elif tp == 0:
@@ -191,10 +170,10 @@ def convert_from_hex_to_uf2(buf):
appstartaddr = addr appstartaddr = addr
i = 4 i = 4
while i < len(rec) - 1: while i < len(rec) - 1:
if not currblock or currblock.addr & ~0xFF != addr & ~0xFF: if not currblock or currblock.addr & ~0xff != addr & ~0xff:
currblock = Block(addr & ~0xFF) currblock = Block(addr & ~0xff)
blocks.append(currblock) blocks.append(currblock)
currblock.bytes[addr & 0xFF] = rec[i] currblock.bytes[addr & 0xff] = rec[i]
addr += 1 addr += 1
i += 1 i += 1
numblocks = len(blocks) numblocks = len(blocks)
@@ -203,28 +182,17 @@ def convert_from_hex_to_uf2(buf):
resfile += blocks[i].encode(i, numblocks) resfile += blocks[i].encode(i, numblocks)
return resfile return resfile
def to_str(b): def to_str(b):
return b.decode("utf-8") return b.decode("utf-8")
def get_drives(): def get_drives():
drives = [] drives = []
if sys.platform == "win32": if sys.platform == "win32":
r = subprocess.check_output( r = subprocess.check_output(["wmic", "PATH", "Win32_LogicalDisk",
[ "get", "DeviceID,", "VolumeName,",
"wmic", "FileSystem,", "DriveType"])
"PATH", for line in to_str(r).split('\n'):
"Win32_LogicalDisk", words = re.split('\s+', line)
"get",
"DeviceID,",
"VolumeName,",
"FileSystem,",
"DriveType",
]
)
for line in to_str(r).split("\n"):
words = re.split("\\s+", line)
if len(words) >= 3 and words[1] == "2" and words[2] == "FAT": if len(words) >= 3 and words[1] == "2" and words[2] == "FAT":
drives.append(words[0]) drives.append(words[0])
else: else:
@@ -238,6 +206,7 @@ def get_drives():
for d in os.listdir(rootpath): for d in os.listdir(rootpath):
drives.append(os.path.join(rootpath, d)) drives.append(os.path.join(rootpath, d))
def has_info(d): def has_info(d):
try: try:
return os.path.isfile(d + INFO_FILE) return os.path.isfile(d + INFO_FILE)
@@ -248,7 +217,7 @@ def get_drives():
def board_id(path): def board_id(path):
with open(path + INFO_FILE, mode="r") as file: with open(path + INFO_FILE, mode='r') as file:
file_content = file.read() file_content = file.read()
return re.search("Board-ID: ([^\r\n]*)", file_content).group(1) return re.search("Board-ID: ([^\r\n]*)", file_content).group(1)
@@ -266,61 +235,30 @@ def write_file(name, buf):
def main(): def main():
global appstartaddr, familyid global appstartaddr, familyid
def error(msg): def error(msg):
print(msg) print(msg)
sys.exit(1) sys.exit(1)
parser = argparse.ArgumentParser(description='Convert to UF2 or flash directly.')
parser = argparse.ArgumentParser(description="Convert to UF2 or flash directly.") parser.add_argument('input', metavar='INPUT', type=str, nargs='?',
parser.add_argument( help='input file (HEX, BIN or UF2)')
"input", parser.add_argument('-b' , '--base', dest='base', type=str,
metavar="INPUT", default="0x2000",
type=str, help='set base address of application for BIN format (default: 0x2000)')
nargs="?", parser.add_argument('-o' , '--output', metavar="FILE", dest='output', type=str,
help="input file (HEX, BIN or UF2)", help='write output to named file; defaults to "flash.uf2" or "flash.bin" where sensible')
) parser.add_argument('-d' , '--device', dest="device_path",
parser.add_argument( help='select a device path to flash')
"-b", parser.add_argument('-l' , '--list', action='store_true',
"--base", help='list connected devices')
dest="base", parser.add_argument('-c' , '--convert', action='store_true',
type=str, help='do not flash, just convert')
default="0x2000", parser.add_argument('-D' , '--deploy', action='store_true',
help="set base address of application for BIN format (default: 0x2000)", help='just flash, do not convert')
) parser.add_argument('-f' , '--family', dest='family', type=str,
parser.add_argument( default="0x0",
"-o", help='specify familyID - number or name (default: 0x0)')
"--output", parser.add_argument('-C' , '--carray', action='store_true',
metavar="FILE", help='convert binary file to a C array, not UF2')
dest="output",
type=str,
help='write output to named file; defaults to "flash.uf2" or "flash.bin" where sensible',
)
parser.add_argument(
"-d", "--device", dest="device_path", help="select a device path to flash"
)
parser.add_argument(
"-l", "--list", action="store_true", help="list connected devices"
)
parser.add_argument(
"-c", "--convert", action="store_true", help="do not flash, just convert"
)
parser.add_argument(
"-D", "--deploy", action="store_true", help="just flash, do not convert"
)
parser.add_argument(
"-f",
"--family",
dest="family",
type=str,
default="0x0",
help="specify familyID - number or name (default: 0x0)",
)
parser.add_argument(
"-C",
"--carray",
action="store_true",
help="convert binary file to a C array, not UF2",
)
args = parser.parse_args() args = parser.parse_args()
appstartaddr = int(args.base, 0) appstartaddr = int(args.base, 0)
@@ -330,17 +268,14 @@ def main():
try: try:
familyid = int(args.family, 0) familyid = int(args.family, 0)
except ValueError: except ValueError:
error( error("Family ID needs to be a number or one of: " + ", ".join(families.keys()))
"Family ID needs to be a number or one of: "
+ ", ".join(families.keys())
)
if args.list: if args.list:
list_drives() list_drives()
else: else:
if not args.input: if not args.input:
error("Need input file") error("Need input file")
with open(args.input, mode="rb") as f: with open(args.input, mode='rb') as f:
inpbuf = f.read() inpbuf = f.read()
from_uf2 = is_uf2(inpbuf) from_uf2 = is_uf2(inpbuf)
ext = "uf2" ext = "uf2"
@@ -356,10 +291,8 @@ def main():
ext = "h" ext = "h"
else: else:
outbuf = convert_to_uf2(inpbuf) outbuf = convert_to_uf2(inpbuf)
print( print("Converting to %s, output size: %d, start address: 0x%x" %
"Converting to %s, output size: %d, start address: 0x%x" (ext, len(outbuf), appstartaddr))
% (ext, len(outbuf), appstartaddr)
)
if args.convert or ext != "uf2": if args.convert or ext != "uf2":
drives = [] drives = []
if args.output == None: if args.output == None:

View File

@@ -1,53 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DARDUINO_NRF52840_PCA10056 -DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x4405"],
["0x239A", "0x0029"],
["0x239A", "0x002A"]
],
"usb_product": "HT-n5262",
"mcu": "nrf52840",
"variant": "heltec_mesh_node_t114",
"variants_dir": "variants",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": ["bluetooth"],
"debug": {
"jlink_device": "nRF52840_xxAA",
"onboard_tools": ["jlink"],
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs"
},
"frameworks": ["arduino"],
"name": "Heltec nrf (Adafruit BSP)",
"upload": {
"maximum_ram_size": 248832,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": ["jlink", "nrfjprog", "nrfutil", "stlink"],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "FIXME",
"vendor": "Heltec"
}

View File

@@ -27,7 +27,7 @@
"jlink_device": "nRF52840_xxAA", "jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd" "svd_path": "nrf52840.svd"
}, },
"frameworks": ["arduino", "freertos"], "frameworks": ["arduino"],
"name": "Seeed WIO WM1110", "name": "Seeed WIO WM1110",
"upload": { "upload": {
"maximum_ram_size": 248832, "maximum_ram_size": 248832,

View File

@@ -23,7 +23,7 @@
"sd_flags": "-DS140", "sd_flags": "-DS140",
"sd_name": "s140", "sd_name": "s140",
"sd_version": "7.3.0", "sd_version": "7.3.0",
"sd_fwid": "0x0123" "sd_fwid": "0x00B6"
}, },
"bootloader": { "bootloader": {
"settings_addr": "0xFF000" "settings_addr": "0xFF000"

View File

@@ -35,7 +35,7 @@
"svd_path": "nrf52840.svd", "svd_path": "nrf52840.svd",
"openocd_target": "nrf52840-mdk-rs" "openocd_target": "nrf52840-mdk-rs"
}, },
"frameworks": ["arduino", "freertos"], "frameworks": ["arduino"],
"name": "WisCore RAK4631 Board", "name": "WisCore RAK4631 Board",
"upload": { "upload": {
"maximum_ram_size": 248832, "maximum_ram_size": 248832,

View File

@@ -35,10 +35,6 @@ default_envs = tbeam
;default_envs = radiomaster_900_bandit_nano ;default_envs = radiomaster_900_bandit_nano
;default_envs = radiomaster_900_bandit_micro ;default_envs = radiomaster_900_bandit_micro
;default_envs = heltec_capsule_sensor_v3 ;default_envs = heltec_capsule_sensor_v3
;default_envs = heltec_vision_master_t190
;default_envs = heltec_vision_master_e213
;default_envs = heltec_vision_master_e290
;default_envs = heltec_mesh_node_t114
extra_configs = extra_configs =
arch/*/*.ini arch/*/*.ini
@@ -81,11 +77,10 @@ build_flags = -Wno-missing-field-initializers
-DMESHTASTIC_EXCLUDE_DROPZONE=1 -DMESHTASTIC_EXCLUDE_DROPZONE=1
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = direct
lib_deps = lib_deps =
jgromes/RadioLib@~6.6.0 jgromes/RadioLib@~6.6.0
https://github.com/meshtastic/esp8266-oled-ssd1306.git#e16cee124fe26490cb14880c679321ad8ac89c95 ; ESP8266_SSD1306 https://github.com/meshtastic/esp8266-oled-ssd1306.git#2b40affbe7f7dc63b6c00fa88e7e12ed1f8e1719 ; ESP8266_SSD1306
mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce mathertel/OneButton@^2.5.0 ; OneButton library for non-blocking button debounce
https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159 https://github.com/meshtastic/arduino-fsm.git#7db3702bf0cfe97b783d6c72595e3f38e0b19159
https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4 https://github.com/meshtastic/TinyGPSPlus.git#71a82db35f3b973440044c476d4bcdc673b104f4
@@ -155,4 +150,5 @@ lib_deps =
mprograms/QMC5883LCompass@^1.2.0 mprograms/QMC5883LCompass@^1.2.0
https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee https://github.com/meshtastic/DFRobot_LarkWeatherStation#dee914270dc7cb3e43fbf034edd85a63a16a12ee

View File

@@ -1,7 +0,0 @@
# This is a config file to control pyocd ICE debugger probe options (only used for NRF52 targets with hardware debugging connections)
# for more info see FIXMEURL
# console or telnet
semihost_console_type: telnet
enable_semihosting: True
telnet_port: 4444

View File

@@ -16,8 +16,6 @@
#include <Wire.h> #include <Wire.h>
#ifdef RAK_4631 #ifdef RAK_4631
#include "Fusion/Fusion.h" #include "Fusion/Fusion.h"
#include "graphics/Screen.h"
#include "graphics/ScreenFonts.h"
#include <Rak_BMX160.h> #include <Rak_BMX160.h>
#endif #endif
@@ -103,11 +101,7 @@ class AccelerometerThread : public concurrency::OSThread
bmx160.getAllData(&magAccel, NULL, &gAccel); bmx160.getAllData(&magAccel, NULL, &gAccel);
// expirimental calibrate routine. Limited to between 10 and 30 seconds after boot // expirimental calibrate routine. Limited to between 10 and 30 seconds after boot
if (millis() > 12 * 1000 && millis() < 30 * 1000) { if (millis() > 10 * 1000 && millis() < 30 * 1000) {
if (!showingScreen) {
showingScreen = true;
screen->startAlert((FrameCallback)drawFrameCalibration);
}
if (magAccel.x > highestX) if (magAccel.x > highestX)
highestX = magAccel.x; highestX = magAccel.x;
if (magAccel.x < lowestX) if (magAccel.x < lowestX)
@@ -120,9 +114,6 @@ class AccelerometerThread : public concurrency::OSThread
highestZ = magAccel.z; highestZ = magAccel.z;
if (magAccel.z < lowestZ) if (magAccel.z < lowestZ)
lowestZ = magAccel.z; lowestZ = magAccel.z;
} else if (showingScreen && millis() >= 30 * 1000) {
showingScreen = false;
screen->endAlert();
} }
int highestRealX = highestX - (highestX + lowestX) / 2; int highestRealX = highestX - (highestX + lowestX) / 2;
@@ -264,34 +255,11 @@ class AccelerometerThread : public concurrency::OSThread
Adafruit_LIS3DH lis; Adafruit_LIS3DH lis;
Adafruit_LSM6DS3TRC lsm; Adafruit_LSM6DS3TRC lsm;
SensorBMA423 bmaSensor; SensorBMA423 bmaSensor;
bool BMA_IRQ = false;
#ifdef RAK_4631 #ifdef RAK_4631
bool showingScreen = false;
RAK_BMX160 bmx160; RAK_BMX160 bmx160;
float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0; float highestX = 0, lowestX = 0, highestY = 0, lowestY = 0, highestZ = 0, lowestZ = 0;
static void drawFrameCalibration(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
int x_offset = display->width() / 2;
int y_offset = display->height() <= 80 ? 0 : 32;
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_MEDIUM);
display->drawString(x, y, "Calibrating\nCompass");
int16_t compassX = 0, compassY = 0;
uint16_t compassDiam = graphics::Screen::getCompassDiam(display->getWidth(), display->getHeight());
// coordinates for the center of the compass/circle
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
compassX = x + display->getWidth() - compassDiam / 2 - 5;
compassY = y + display->getHeight() / 2;
} else {
compassX = x + display->getWidth() - compassDiam / 2 - 5;
compassY = y + FONT_HEIGHT_SMALL + (display->getHeight() - FONT_HEIGHT_SMALL) / 2;
}
display->drawCircle(compassX, compassY, compassDiam / 2);
screen->drawCompassNorth(display, compassX, compassY, screen->getHeading() * PI / 180);
}
#endif #endif
bool BMA_IRQ = false;
}; };
#endif #endif

View File

@@ -10,8 +10,4 @@ const uint8_t TORADIO_UUID_16[16u] = {0xe7, 0x01, 0x44, 0x12, 0x66, 0x78, 0xdd,
const uint8_t FROMRADIO_UUID_16[16u] = {0x02, 0x00, 0x12, 0xac, 0x42, 0x02, 0x78, 0xb8, const uint8_t FROMRADIO_UUID_16[16u] = {0x02, 0x00, 0x12, 0xac, 0x42, 0x02, 0x78, 0xb8,
0xed, 0x11, 0x93, 0x49, 0x9e, 0xe6, 0x55, 0x2c}; 0xed, 0x11, 0x93, 0x49, 0x9e, 0xe6, 0x55, 0x2c};
const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70, 0xa6, const uint8_t FROMNUM_UUID_16[16u] = {0x53, 0x44, 0xe3, 0x47, 0x75, 0xaa, 0x70, 0xa6,
0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed}; 0x66, 0x4f, 0x00, 0xa8, 0x8c, 0xa1, 0x9d, 0xed};
const uint8_t LEGACY_LOGRADIO_UUID_16[16u] = {0xe2, 0xf2, 0x1e, 0xbe, 0xc5, 0x15, 0xcf, 0xaa,
0x6b, 0x43, 0xfa, 0x78, 0x38, 0xd2, 0x6f, 0x6c};
const uint8_t LOGRADIO_UUID_16[16u] = {0x47, 0x95, 0xDF, 0x8C, 0xDE, 0xE9, 0x44, 0x99,
0x23, 0x44, 0xE6, 0x06, 0x49, 0x6E, 0x3D, 0x5A};

View File

@@ -11,12 +11,10 @@
#define TORADIO_UUID "f75c76d2-129e-4dad-a1dd-7866124401e7" #define TORADIO_UUID "f75c76d2-129e-4dad-a1dd-7866124401e7"
#define FROMRADIO_UUID "2c55e69e-4993-11ed-b878-0242ac120002" #define FROMRADIO_UUID "2c55e69e-4993-11ed-b878-0242ac120002"
#define FROMNUM_UUID "ed9da18c-a800-4f66-a670-aa7547e34453" #define FROMNUM_UUID "ed9da18c-a800-4f66-a670-aa7547e34453"
#define LEGACY_LOGRADIO_UUID "6c6fd238-78fa-436b-aacf-15c5be1ef2e2"
#define LOGRADIO_UUID "5a3d6e49-06e6-4423-9944-e9de8cdf9547"
// NRF52 wants these constants as byte arrays // NRF52 wants these constants as byte arrays
// Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER // Generated here https://yupana-engineering.com/online-uuid-to-c-array-converter - but in REVERSE BYTE ORDER
extern const uint8_t MESH_SERVICE_UUID_16[], TORADIO_UUID_16[16u], FROMRADIO_UUID_16[], FROMNUM_UUID_16[], LOGRADIO_UUID_16[]; extern const uint8_t MESH_SERVICE_UUID_16[], TORADIO_UUID_16[16u], FROMRADIO_UUID_16[], FROMNUM_UUID_16[];
/// Given a level between 0-100, update the BLE attribute /// Given a level between 0-100, update the BLE attribute
void updateBatteryLevel(uint8_t level); void updateBatteryLevel(uint8_t level);

View File

@@ -187,9 +187,8 @@ int32_t ButtonThread::runOnce()
case BUTTON_EVENT_LONG_PRESSED: { case BUTTON_EVENT_LONG_PRESSED: {
LOG_BUTTON("Long press!\n"); LOG_BUTTON("Long press!\n");
powerFSM.trigger(EVENT_PRESS); powerFSM.trigger(EVENT_PRESS);
if (screen) { if (screen)
screen->startAlert("Shutting down..."); screen->startShutdownScreen();
}
playBeep(); playBeep();
break; break;
} }

View File

@@ -26,7 +26,7 @@ SOFTWARE.*/
#include "DebugConfiguration.h" #include "DebugConfiguration.h"
#if HAS_NETWORKING #if HAS_WIFI || HAS_ETHERNET
Syslog::Syslog(UDP &client) Syslog::Syslog(UDP &client)
{ {

View File

@@ -1,6 +1,5 @@
#pragma once #ifndef SYSLOG_H
#define SYSLOG_H
#include "configuration.h"
// DEBUG LED // DEBUG LED
#ifndef LED_INVERTED #ifndef LED_INVERTED
@@ -26,14 +25,6 @@
#include "SerialConsole.h" #include "SerialConsole.h"
// If defined we will include support for ARM ICE "semihosting" for a virtual
// console over the JTAG port (to replace the normal serial port)
// Note: Normally this flag is passed into the gcc commandline by platformio.ini.
// for an example see env:rak4631_dap.
// #ifndef USE_SEMIHOSTING
// #define USE_SEMIHOSTING
// #endif
#define DEBUG_PORT (*console) // Serial debug port #define DEBUG_PORT (*console) // Serial debug port
#ifdef USE_SEGGER #ifdef USE_SEGGER
@@ -126,7 +117,7 @@
#include <WiFi.h> #include <WiFi.h>
#endif // HAS_WIFI #endif // HAS_WIFI
#if HAS_NETWORKING #if HAS_WIFI || HAS_ETHERNET
class Syslog class Syslog
{ {
@@ -161,4 +152,6 @@ class Syslog
bool vlogf(uint16_t pri, const char *appName, const char *fmt, va_list args) __attribute__((format(printf, 3, 0))); bool vlogf(uint16_t pri, const char *appName, const char *fmt, va_list args) __attribute__((format(printf, 3, 0)));
}; };
#endif // HAS_ETHERNET || HAS_WIFI #endif // HAS_ETHERNET || HAS_WIFI
#endif // SYSLOG_H

View File

@@ -84,58 +84,6 @@ bool renameFile(const char *pathFrom, const char *pathTo)
#endif #endif
} }
#include <vector>
/**
* @brief Get the list of files in a directory.
*
* This function returns a list of files in a directory. The list includes the full path of each file.
*
* @param dirname The name of the directory.
* @param levels The number of levels of subdirectories to list.
* @return A vector of strings containing the full path of each file in the directory.
*/
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels)
{
std::vector<meshtastic_FileInfo> filenames = {};
#ifdef FSCom
File root = FSCom.open(dirname, FILE_O_READ);
if (!root)
return filenames;
if (!root.isDirectory())
return filenames;
File file = root.openNextFile();
while (file) {
if (file.isDirectory() && !String(file.name()).endsWith(".")) {
if (levels) {
#ifdef ARCH_ESP32
std::vector<meshtastic_FileInfo> subDirFilenames = getFiles(file.path(), levels - 1);
#else
std::vector<meshtastic_FileInfo> subDirFilenames = getFiles(file.name(), levels - 1);
#endif
filenames.insert(filenames.end(), subDirFilenames.begin(), subDirFilenames.end());
file.close();
}
} else {
meshtastic_FileInfo fileInfo = {"", file.size()};
#ifdef ARCH_ESP32
strcpy(fileInfo.file_name, file.path());
#else
strcpy(fileInfo.file_name, file.name());
#endif
if (!String(fileInfo.file_name).endsWith(".")) {
filenames.push_back(fileInfo);
}
file.close();
}
file = root.openNextFile();
}
root.close();
#endif
return filenames;
}
/** /**
* Lists the contents of a directory. * Lists the contents of a directory.
* *

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
#include "configuration.h" #include "configuration.h"
#include <vector>
// Cross platform filesystem API // Cross platform filesystem API
@@ -50,7 +49,6 @@ using namespace Adafruit_LittleFS_Namespace;
void fsInit(); void fsInit();
bool copyFile(const char *from, const char *to); bool copyFile(const char *from, const char *to);
bool renameFile(const char *pathFrom, const char *pathTo); bool renameFile(const char *pathFrom, const char *pathTo);
std::vector<meshtastic_FileInfo> getFiles(const char *dirname, uint8_t levels);
void listDir(const char *dirname, uint8_t levels, bool del); void listDir(const char *dirname, uint8_t levels, bool del);
void rmDir(const char *dirname); void rmDir(const char *dirname);
void setupSDCard(); void setupSDCard();

View File

@@ -124,7 +124,7 @@ class GPSStatus : public Status
if (isDirty) { if (isDirty) {
if (hasLock) { if (hasLock) {
// In debug logs, identify position by @timestamp:stage (stage 3 = notify) // In debug logs, identify position by @timestamp:stage (stage 3 = notify)
LOG_DEBUG("New GPS pos@%x:3 lat=%f lon=%f alt=%d pdop=%.2f track=%.2f speed=%.2f sats=%d\n", p.timestamp, LOG_DEBUG("New GPS pos@%x:3 lat=%f, lon=%f, alt=%d, pdop=%.2f, track=%.2f, speed=%.2f, sats=%d\n", p.timestamp,
p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5, p.latitude_i * 1e-7, p.longitude_i * 1e-7, p.altitude, p.PDOP * 1e-2, p.ground_track * 1e-5,
p.ground_speed * 1e-2, p.sats_in_view); p.ground_speed * 1e-2, p.sats_in_view);
} else { } else {

View File

@@ -27,7 +27,7 @@
#if defined(DEBUG_HEAP_MQTT) && !MESHTASTIC_EXCLUDE_MQTT #if defined(DEBUG_HEAP_MQTT) && !MESHTASTIC_EXCLUDE_MQTT
#include "mqtt/MQTT.h" #include "mqtt/MQTT.h"
#include "target_specific.h" #include "target_specific.h"
#if HAS_WIFI #if !MESTASTIC_EXCLUDE_WIFI
#include <WiFi.h> #include <WiFi.h>
#endif #endif
#endif #endif
@@ -232,20 +232,12 @@ class AnalogBatteryLevel : public HasBatteryLevel
raw = espAdcRead(); raw = espAdcRead();
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs); scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
scaled *= operativeAdcMultiplier; scaled *= operativeAdcMultiplier;
#else // block for all other platforms #else // block for all other platforms
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
pinMode(ADC_CTRL, OUTPUT);
digitalWrite(ADC_CTRL, ADC_CTRL_ENABLED);
delay(10);
#endif
for (uint32_t i = 0; i < BATTERY_SENSE_SAMPLES; i++) { for (uint32_t i = 0; i < BATTERY_SENSE_SAMPLES; i++) {
raw += analogRead(BATTERY_PIN); raw += analogRead(BATTERY_PIN);
} }
raw = raw / BATTERY_SENSE_SAMPLES; raw = raw / BATTERY_SENSE_SAMPLES;
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw; scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
digitalWrite(ADC_CTRL, !ADC_CTRL_ENABLED);
#endif
#endif #endif
if (!initial_read_done) { if (!initial_read_done) {
@@ -449,11 +441,6 @@ class AnalogBatteryLevel : public HasBatteryLevel
if (!ina260Sensor.isInitialized()) if (!ina260Sensor.isInitialized())
return ina260Sensor.runOnce() > 0; return ina260Sensor.runOnce() > 0;
return ina260Sensor.isRunning(); return ina260Sensor.isRunning();
} else if (nodeTelemetrySensorsMap[meshtastic_TelemetrySensorType_INA3221].first ==
config.power.device_battery_ina_address) {
if (!ina3221Sensor.isInitialized())
return ina3221Sensor.runOnce() > 0;
return ina3221Sensor.isRunning();
} }
return false; return false;
} }

View File

@@ -11,7 +11,6 @@
#include "Default.h" #include "Default.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerMon.h"
#include "configuration.h" #include "configuration.h"
#include "graphics/Screen.h" #include "graphics/Screen.h"
#include "main.h" #include "main.h"
@@ -50,7 +49,6 @@ static bool isPowered()
static void sdsEnter() static void sdsEnter()
{ {
LOG_DEBUG("Enter state: SDS\n"); LOG_DEBUG("Enter state: SDS\n");
powerMon->setState(meshtastic_PowerMon_State_CPU_DeepSleep);
// FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw // FIXME - make sure GPS and LORA radio are off first - because we want close to zero current draw
doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false); doDeepSleep(Default::getConfiguredOrDefaultMs(config.power.sds_secs), false);
} }
@@ -70,7 +68,6 @@ static uint32_t secsSlept;
static void lsEnter() static void lsEnter()
{ {
LOG_INFO("lsEnter begin, ls_secs=%u\n", config.power.ls_secs); LOG_INFO("lsEnter begin, ls_secs=%u\n", config.power.ls_secs);
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(false); screen->setOn(false);
secsSlept = 0; // How long have we been sleeping this time secsSlept = 0; // How long have we been sleeping this time
@@ -90,10 +87,8 @@ static void lsIdle()
// Briefly come out of sleep long enough to blink the led once every few seconds // Briefly come out of sleep long enough to blink the led once every few seconds
uint32_t sleepTime = SLEEP_TIME; uint32_t sleepTime = SLEEP_TIME;
powerMon->setState(meshtastic_PowerMon_State_CPU_LightSleep);
setLed(false); // Never leave led on while in light sleep setLed(false); // Never leave led on while in light sleep
esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL); esp_sleep_source_t wakeCause2 = doLightSleep(sleepTime * 1000LL);
powerMon->clearState(meshtastic_PowerMon_State_CPU_LightSleep);
switch (wakeCause2) { switch (wakeCause2) {
case ESP_SLEEP_WAKEUP_TIMER: case ESP_SLEEP_WAKEUP_TIMER:
@@ -149,7 +144,6 @@ static void lsExit()
static void nbEnter() static void nbEnter()
{ {
LOG_DEBUG("Enter state: NB\n"); LOG_DEBUG("Enter state: NB\n");
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
screen->setOn(false); screen->setOn(false);
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
// Only ESP32 should turn off bluetooth // Only ESP32 should turn off bluetooth
@@ -161,8 +155,6 @@ static void nbEnter()
static void darkEnter() static void darkEnter()
{ {
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
powerMon->clearState(meshtastic_PowerMon_State_Screen_On);
setBluetoothEnable(true); setBluetoothEnable(true);
screen->setOn(false); screen->setOn(false);
} }
@@ -170,8 +162,6 @@ static void darkEnter()
static void serialEnter() static void serialEnter()
{ {
LOG_DEBUG("Enter state: SERIAL\n"); LOG_DEBUG("Enter state: SERIAL\n");
powerMon->clearState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
setBluetoothEnable(false); setBluetoothEnable(false);
screen->setOn(true); screen->setOn(true);
screen->print("Serial connected\n"); screen->print("Serial connected\n");
@@ -180,7 +170,6 @@ static void serialEnter()
static void serialExit() static void serialExit()
{ {
// Turn bluetooth back on when we leave serial stream API // Turn bluetooth back on when we leave serial stream API
powerMon->setState(meshtastic_PowerMon_State_BT_On);
setBluetoothEnable(true); setBluetoothEnable(true);
screen->print("Serial disconnected\n"); screen->print("Serial disconnected\n");
} }
@@ -193,8 +182,6 @@ static void powerEnter()
LOG_INFO("Loss of power in Powered\n"); LOG_INFO("Loss of power in Powered\n");
powerFSM.trigger(EVENT_POWER_DISCONNECTED); powerFSM.trigger(EVENT_POWER_DISCONNECTED);
} else { } else {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true); screen->setOn(true);
setBluetoothEnable(true); setBluetoothEnable(true);
// within enter() the function getState() returns the state we came from // within enter() the function getState() returns the state we came from
@@ -218,8 +205,6 @@ static void powerIdle()
static void powerExit() static void powerExit()
{ {
powerMon->setState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true); screen->setOn(true);
setBluetoothEnable(true); setBluetoothEnable(true);
@@ -231,8 +216,6 @@ static void powerExit()
static void onEnter() static void onEnter()
{ {
LOG_DEBUG("Enter state: ON\n"); LOG_DEBUG("Enter state: ON\n");
powerMon->setState(meshtastic_PowerMon_State_BT_On);
powerMon->setState(meshtastic_PowerMon_State_Screen_On);
screen->setOn(true); screen->setOn(true);
setBluetoothEnable(true); setBluetoothEnable(true);
} }

View File

@@ -1,45 +0,0 @@
#include "PowerMon.h"
#include "NodeDB.h"
// Use the 'live' config flag to figure out if we should be showing this message
static bool is_power_enabled(uint64_t m)
{
return (m & config.power.powermon_enables) ? true : false;
}
void PowerMon::setState(_meshtastic_PowerMon_State state, const char *reason)
{
#ifdef USE_POWERMON
auto oldstates = states;
states |= state;
if (oldstates != states && is_power_enabled(state)) {
emitLog(reason);
}
#endif
}
void PowerMon::clearState(_meshtastic_PowerMon_State state, const char *reason)
{
#ifdef USE_POWERMON
auto oldstates = states;
states &= ~state;
if (oldstates != states && is_power_enabled(state)) {
emitLog(reason);
}
#endif
}
void PowerMon::emitLog(const char *reason)
{
#ifdef USE_POWERMON
// The nrf52 printf doesn't understand 64 bit ints, so if we ever reach that point this function will need to change.
LOG_INFO("S:PM:0x%08lx,%s\n", (uint32_t)states, reason);
#endif
}
PowerMon *powerMon;
void powerMonInit()
{
powerMon = new PowerMon();
}

View File

@@ -1,34 +0,0 @@
#pragma once
#include "configuration.h"
#include "meshtastic/powermon.pb.h"
#ifndef MESHTASTIC_EXCLUDE_POWERMON
#define USE_POWERMON // FIXME turn this only for certain builds
#endif
/**
* The singleton class for monitoring power consumption of device
* subsystems/modes.
*
* For more information see the PowerMon docs.
*/
class PowerMon
{
uint64_t states = 0UL;
public:
PowerMon() {}
// Mark entry/exit of a power consuming state
void setState(_meshtastic_PowerMon_State state, const char *reason = "");
void clearState(_meshtastic_PowerMon_State state, const char *reason = "");
private:
// Emit the coded log message
void emitLog(const char *reason);
};
extern PowerMon *powerMon;
void powerMonInit();

View File

@@ -3,8 +3,6 @@
#include "RTC.h" #include "RTC.h"
#include "concurrency/OSThread.h" #include "concurrency/OSThread.h"
#include "configuration.h" #include "configuration.h"
#include "main.h"
#include "mesh/generated/meshtastic/mesh.pb.h"
#include <assert.h> #include <assert.h>
#include <cstring> #include <cstring>
#include <memory> #include <memory>
@@ -16,7 +14,12 @@
#include "platform/portduino/PortduinoGlue.h" #include "platform/portduino/PortduinoGlue.h"
#endif #endif
#if HAS_NETWORKING /**
* A printer that doesn't go anywhere
*/
NoopPrint noopPrint;
#if HAS_WIFI || HAS_ETHERNET
extern Syslog syslog; extern Syslog syslog;
#endif #endif
void RedirectablePrint::rpInit() void RedirectablePrint::rpInit()
@@ -35,7 +38,7 @@ void RedirectablePrint::setDestination(Print *_dest)
size_t RedirectablePrint::write(uint8_t c) size_t RedirectablePrint::write(uint8_t c)
{ {
// Always send the characters to our segger JTAG debugger // Always send the characters to our segger JTAG debugger
#ifdef USE_SEGGER #ifdef SEGGER_STDOUT_CH
SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c); SEGGER_RTT_PutChar(SEGGER_STDOUT_CH, c);
#endif #endif
@@ -46,7 +49,7 @@ size_t RedirectablePrint::write(uint8_t c)
// serial port said (which could be zero) // serial port said (which could be zero)
} }
size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_list arg) size_t RedirectablePrint::vprintf(const char *format, va_list arg)
{ {
va_list copy; va_list copy;
static char printBuf[160]; static char printBuf[160];
@@ -62,200 +65,25 @@ size_t RedirectablePrint::vprintf(const char *logLevel, const char *format, va_l
len = sizeof(printBuf) - 1; len = sizeof(printBuf) - 1;
printBuf[sizeof(printBuf) - 2] = '\n'; printBuf[sizeof(printBuf) - 2] = '\n';
} }
for (size_t f = 0; f < len; f++) {
if (!std::isprint(static_cast<unsigned char>(printBuf[f])) && printBuf[f] != '\n')
printBuf[f] = '#';
}
if (logLevel != nullptr) {
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
Print::write("\u001b[34m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
Print::write("\u001b[32m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
Print::write("\u001b[33m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0)
Print::write("\u001b[31m", 6);
}
len = Print::write(printBuf, len); len = Print::write(printBuf, len);
Print::write("\u001b[0m", 5);
return len; return len;
} }
void RedirectablePrint::log_to_serial(const char *logLevel, const char *format, va_list arg) size_t RedirectablePrint::log(const char *logLevel, const char *format, ...)
{
size_t r = 0;
// Cope with 0 len format strings, but look for new line terminator
bool hasNewline = *format && format[strlen(format) - 1] == '\n';
// If we are the first message on a report, include the header
if (!isContinuationMessage) {
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
Print::write("\u001b[34m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
Print::write("\u001b[32m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
Print::write("\u001b[33m", 6);
if (strcmp(logLevel, MESHTASTIC_LOG_LEVEL_ERROR) == 0)
Print::write("\u001b[31m", 6);
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
// hms += tz.tz_dsttime * SEC_PER_HOUR;
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
// Tear apart hms into h:m:s
int hour = hms / SEC_PER_HOUR;
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
#ifdef ARCH_PORTDUINO
::printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
#else
printf("%s \u001b[0m| %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
#endif
} else
#ifdef ARCH_PORTDUINO
::printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000);
#else
printf("%s \u001b[0m| ??:??:?? %u ", logLevel, millis() / 1000);
#endif
auto thread = concurrency::OSThread::currentThread;
if (thread) {
print("[");
// printf("%p ", thread);
// assert(thread->ThreadName.length());
print(thread->ThreadName);
print("] ");
}
}
r += vprintf(logLevel, format, arg);
isContinuationMessage = !hasNewline;
}
void RedirectablePrint::log_to_syslog(const char *logLevel, const char *format, va_list arg)
{
#if HAS_NETWORKING && !defined(ARCH_PORTDUINO)
// if syslog is in use, collect the log messages and send them to syslog
if (syslog.isEnabled()) {
int ll = 0;
switch (logLevel[0]) {
case 'D':
ll = SYSLOG_DEBUG;
break;
case 'I':
ll = SYSLOG_INFO;
break;
case 'W':
ll = SYSLOG_WARN;
break;
case 'E':
ll = SYSLOG_ERR;
break;
case 'C':
ll = SYSLOG_CRIT;
break;
default:
ll = 0;
}
auto thread = concurrency::OSThread::currentThread;
if (thread) {
syslog.vlogf(ll, thread->ThreadName.c_str(), format, arg);
} else {
syslog.vlogf(ll, format, arg);
}
}
#endif
}
void RedirectablePrint::log_to_ble(const char *logLevel, const char *format, va_list arg)
{
#if !MESHTASTIC_EXCLUDE_BLUETOOTH
if (config.bluetooth.device_logging_enabled && !pauseBluetoothLogging) {
bool isBleConnected = false;
#ifdef ARCH_ESP32
isBleConnected = nimbleBluetooth && nimbleBluetooth->isActive() && nimbleBluetooth->isConnected();
#elif defined(ARCH_NRF52)
isBleConnected = nrf52Bluetooth != nullptr && nrf52Bluetooth->isConnected();
#endif
if (isBleConnected) {
char *message;
size_t initialLen;
size_t len;
initialLen = strlen(format);
message = new char[initialLen + 1];
len = vsnprintf(message, initialLen + 1, format, arg);
if (len > initialLen) {
delete[] message;
message = new char[len + 1];
vsnprintf(message, len + 1, format, arg);
}
auto thread = concurrency::OSThread::currentThread;
meshtastic_LogRecord logRecord = meshtastic_LogRecord_init_zero;
logRecord.level = getLogLevel(logLevel);
strcpy(logRecord.message, message);
if (thread)
strcpy(logRecord.source, thread->ThreadName.c_str());
logRecord.time = getValidTime(RTCQuality::RTCQualityDevice, true);
uint8_t *buffer = new uint8_t[meshtastic_LogRecord_size];
size_t size = pb_encode_to_bytes(buffer, meshtastic_LogRecord_size, meshtastic_LogRecord_fields, &logRecord);
#ifdef ARCH_ESP32
nimbleBluetooth->sendLog(buffer, size);
#elif defined(ARCH_NRF52)
nrf52Bluetooth->sendLog(buffer, size);
#endif
delete[] message;
delete[] buffer;
}
}
#else
(void)logLevel;
(void)format;
(void)arg;
#endif
}
meshtastic_LogRecord_Level RedirectablePrint::getLogLevel(const char *logLevel)
{
meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset
switch (logLevel[0]) {
case 'D':
ll = meshtastic_LogRecord_Level_DEBUG;
break;
case 'I':
ll = meshtastic_LogRecord_Level_INFO;
break;
case 'W':
ll = meshtastic_LogRecord_Level_WARNING;
break;
case 'E':
ll = meshtastic_LogRecord_Level_ERROR;
break;
case 'C':
ll = meshtastic_LogRecord_Level_CRITICAL;
break;
}
return ll;
}
void RedirectablePrint::log(const char *logLevel, const char *format, ...)
{ {
#ifdef ARCH_PORTDUINO #ifdef ARCH_PORTDUINO
if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) if (settingsMap[logoutputlevel] < level_debug && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0)
return; return 0;
else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0) else if (settingsMap[logoutputlevel] < level_info && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_INFO) == 0)
return; return 0;
else if (settingsMap[logoutputlevel] < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0) else if (settingsMap[logoutputlevel] < level_warn && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_WARN) == 0)
return; return 0;
#endif #endif
if (moduleConfig.serial.override_console_serial_port && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) { if (moduleConfig.serial.override_console_serial_port && strcmp(logLevel, MESHTASTIC_LOG_LEVEL_DEBUG) == 0) {
return; return 0;
} }
size_t r = 0;
#ifdef HAS_FREE_RTOS #ifdef HAS_FREE_RTOS
if (inDebugPrint != nullptr && xSemaphoreTake(inDebugPrint, portMAX_DELAY) == pdTRUE) { if (inDebugPrint != nullptr && xSemaphoreTake(inDebugPrint, portMAX_DELAY) == pdTRUE) {
#else #else
@@ -266,11 +94,81 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
va_list arg; va_list arg;
va_start(arg, format); va_start(arg, format);
log_to_serial(logLevel, format, arg); // Cope with 0 len format strings, but look for new line terminator
log_to_syslog(logLevel, format, arg); bool hasNewline = *format && format[strlen(format) - 1] == '\n';
log_to_ble(logLevel, format, arg);
// If we are the first message on a report, include the header
if (!isContinuationMessage) {
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // display local time on logfile
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
// hms += tz.tz_dsttime * SEC_PER_HOUR;
// hms -= tz.tz_minuteswest * SEC_PER_MIN;
// mod `hms` to ensure in positive range of [0...SEC_PER_DAY)
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
// Tear apart hms into h:m:s
int hour = hms / SEC_PER_HOUR;
int min = (hms % SEC_PER_HOUR) / SEC_PER_MIN;
int sec = (hms % SEC_PER_HOUR) % SEC_PER_MIN; // or hms % SEC_PER_MIN
#ifdef ARCH_PORTDUINO
r += ::printf("%s | %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
#else
r += printf("%s | %02d:%02d:%02d %u ", logLevel, hour, min, sec, millis() / 1000);
#endif
} else
#ifdef ARCH_PORTDUINO
r += ::printf("%s | ??:??:?? %u ", logLevel, millis() / 1000);
#else
r += printf("%s | ??:??:?? %u ", logLevel, millis() / 1000);
#endif
auto thread = concurrency::OSThread::currentThread;
if (thread) {
print("[");
// printf("%p ", thread);
// assert(thread->ThreadName.length());
print(thread->ThreadName);
print("] ");
}
}
r += vprintf(format, arg);
#if (HAS_WIFI || HAS_ETHERNET) && !defined(ARCH_PORTDUINO)
// if syslog is in use, collect the log messages and send them to syslog
if (syslog.isEnabled()) {
int ll = 0;
switch (logLevel[0]) {
case 'D':
ll = SYSLOG_DEBUG;
break;
case 'I':
ll = SYSLOG_INFO;
break;
case 'W':
ll = SYSLOG_WARN;
break;
case 'E':
ll = SYSLOG_ERR;
break;
case 'C':
ll = SYSLOG_CRIT;
break;
default:
ll = 0;
}
auto thread = concurrency::OSThread::currentThread;
if (thread) {
syslog.vlogf(ll, thread->ThreadName.c_str(), format, arg);
} else {
syslog.vlogf(ll, format, arg);
}
}
#endif
va_end(arg); va_end(arg);
isContinuationMessage = !hasNewline;
#ifdef HAS_FREE_RTOS #ifdef HAS_FREE_RTOS
xSemaphoreGive(inDebugPrint); xSemaphoreGive(inDebugPrint);
#else #else
@@ -278,7 +176,7 @@ void RedirectablePrint::log(const char *logLevel, const char *format, ...)
#endif #endif
} }
return; return r;
} }
void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len) void RedirectablePrint::hexDump(const char *logLevel, unsigned char *buf, uint16_t len)

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
#include "../freertosinc.h" #include "../freertosinc.h"
#include "mesh/generated/meshtastic/mesh.pb.h"
#include <Print.h> #include <Print.h>
#include <stdarg.h> #include <stdarg.h>
#include <string> #include <string>
@@ -42,21 +41,23 @@ class RedirectablePrint : public Print
* log message. Otherwise we assume more prints will come before the log message ends. This * log message. Otherwise we assume more prints will come before the log message ends. This
* allows you to call logDebug a few times to build up a single log message line if you wish. * allows you to call logDebug a few times to build up a single log message line if you wish.
*/ */
void log(const char *logLevel, const char *format, ...) __attribute__((format(printf, 3, 4))); size_t log(const char *logLevel, const char *format, ...) __attribute__((format(printf, 3, 4)));
/** like printf but va_list based */ /** like printf but va_list based */
size_t vprintf(const char *logLevel, const char *format, va_list arg); size_t vprintf(const char *format, va_list arg);
void hexDump(const char *logLevel, unsigned char *buf, uint16_t len); void hexDump(const char *logLevel, unsigned char *buf, uint16_t len);
std::string mt_sprintf(const std::string fmt_str, ...); std::string mt_sprintf(const std::string fmt_str, ...);
};
protected: class NoopPrint : public Print
/// Subclasses can override if they need to change how we format over the serial port {
virtual void log_to_serial(const char *logLevel, const char *format, va_list arg); public:
virtual size_t write(uint8_t c) { return 1; }
};
private: /**
void log_to_syslog(const char *logLevel, const char *format, va_list arg); * A printer that doesn't go anywhere
void log_to_ble(const char *logLevel, const char *format, va_list arg); */
meshtastic_LogRecord_Level getLogLevel(const char *logLevel); extern NoopPrint noopPrint;
};

View File

@@ -28,7 +28,7 @@ void consolePrintf(const char *format, ...)
{ {
va_list arg; va_list arg;
va_start(arg, format); va_start(arg, format);
console->vprintf(nullptr, format, arg); console->vprintf(format, arg);
va_end(arg); va_end(arg);
console->flush(); console->flush();
} }
@@ -38,6 +38,7 @@ SerialConsole::SerialConsole() : StreamAPI(&Port), RedirectablePrint(&Port), con
assert(!console); assert(!console);
console = this; console = this;
canWrite = false; // We don't send packets to our port until it has talked to us first canWrite = false; // We don't send packets to our port until it has talked to us first
// setDestination(&noopPrint); for testing, try turning off 'all' debug output and see what leaks
#ifdef RP2040_SLOW_CLOCK #ifdef RP2040_SLOW_CLOCK
Port.setTX(SERIAL2_TX); Port.setTX(SERIAL2_TX);
@@ -84,40 +85,13 @@ bool SerialConsole::handleToRadio(const uint8_t *buf, size_t len)
{ {
// only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled. // only talk to the API once the configuration has been loaded and we're sure the serial port is not disabled.
if (config.has_lora && config.device.serial_enabled) { if (config.has_lora && config.device.serial_enabled) {
// Switch to protobufs for log messages // Turn off debug serial printing once the API is activated, because other threads could print and corrupt packets
usingProtobufs = true; if (!config.device.debug_log_enabled)
setDestination(&noopPrint);
canWrite = true; canWrite = true;
return StreamAPI::handleToRadio(buf, len); return StreamAPI::handleToRadio(buf, len);
} else { } else {
return false; return false;
} }
}
void SerialConsole::log_to_serial(const char *logLevel, const char *format, va_list arg)
{
if (usingProtobufs) {
meshtastic_LogRecord_Level ll = meshtastic_LogRecord_Level_UNSET; // default to unset
switch (logLevel[0]) {
case 'D':
ll = meshtastic_LogRecord_Level_DEBUG;
break;
case 'I':
ll = meshtastic_LogRecord_Level_INFO;
break;
case 'W':
ll = meshtastic_LogRecord_Level_WARNING;
break;
case 'E':
ll = meshtastic_LogRecord_Level_ERROR;
break;
case 'C':
ll = meshtastic_LogRecord_Level_CRITICAL;
break;
}
auto thread = concurrency::OSThread::currentThread;
emitLogRecord(ll, thread ? thread->ThreadName.c_str() : "", format, arg);
} else
RedirectablePrint::log_to_serial(logLevel, format, arg);
} }

View File

@@ -8,11 +8,6 @@
*/ */
class SerialConsole : public StreamAPI, public RedirectablePrint, private concurrency::OSThread class SerialConsole : public StreamAPI, public RedirectablePrint, private concurrency::OSThread
{ {
/**
* If true we are talking to a smart host and all messages (including log messages) must be framed as protobufs.
*/
bool usingProtobufs = false;
public: public:
SerialConsole(); SerialConsole();
@@ -36,13 +31,10 @@ class SerialConsole : public StreamAPI, public RedirectablePrint, private concur
protected: protected:
/// Check the current underlying physical link to see if the client is currently connected /// Check the current underlying physical link to see if the client is currently connected
virtual bool checkIsConnected() override; virtual bool checkIsConnected() override;
/// Possibly switch to protobufs if we see a valid protobuf message
virtual void log_to_serial(const char *logLevel, const char *format, va_list arg);
}; };
// A simple wrapper to allow non class aware code write to the console // A simple wrapper to allow non class aware code write to the console
void consolePrintf(const char *format, ...); void consolePrintf(const char *format, ...);
void consoleInit(); void consoleInit();
extern SerialConsole *console; extern SerialConsole *console;

View File

@@ -8,11 +8,13 @@ enum class Cmd {
SET_ON, SET_ON,
SET_OFF, SET_OFF,
ON_PRESS, ON_PRESS,
START_ALERT_FRAME, START_BLUETOOTH_PIN_SCREEN,
STOP_ALERT_FRAME,
START_FIRMWARE_UPDATE_SCREEN, START_FIRMWARE_UPDATE_SCREEN,
STOP_BLUETOOTH_PIN_SCREEN,
STOP_BOOT_SCREEN, STOP_BOOT_SCREEN,
PRINT, PRINT,
START_SHUTDOWN_SCREEN,
START_REBOOT_SCREEN,
SHOW_PREV_FRAME, SHOW_PREV_FRAME,
SHOW_NEXT_FRAME SHOW_NEXT_FRAME
}; };

View File

@@ -242,6 +242,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define HAS_BLUETOOTH 0 #define HAS_BLUETOOTH 0
#endif #endif
#include "DebugConfiguration.h"
#include "RF95Configuration.h"
#ifndef HW_VENDOR #ifndef HW_VENDOR
#error HW_VENDOR must be defined #error HW_VENDOR must be defined
#endif #endif
@@ -258,7 +261,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MESHTASTIC_EXCLUDE_GPS 1 #define MESHTASTIC_EXCLUDE_GPS 1
#define MESHTASTIC_EXCLUDE_SCREEN 1 #define MESHTASTIC_EXCLUDE_SCREEN 1
#define MESHTASTIC_EXCLUDE_MQTT 1 #define MESHTASTIC_EXCLUDE_MQTT 1
#define MESHTASTIC_EXCLUDE_POWERMON 1
#endif #endif
// Turn off all optional modules // Turn off all optional modules
@@ -279,7 +281,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define MESHTASTIC_EXCLUDE_WAYPOINT 1 #define MESHTASTIC_EXCLUDE_WAYPOINT 1
#define MESHTASTIC_EXCLUDE_INPUTBROKER 1 #define MESHTASTIC_EXCLUDE_INPUTBROKER 1
#define MESHTASTIC_EXCLUDE_SERIAL 1 #define MESHTASTIC_EXCLUDE_SERIAL 1
#define MESHTASTIC_EXCLUDE_POWERSTRESS 1
#endif #endif
// // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled) // // Turn off wifi even if HW supports wifi (webserver relies on wifi and is also disabled)
@@ -289,9 +290,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define HAS_WIFI 0 #define HAS_WIFI 0
#endif #endif
// Allow code that needs internet to just check HAS_NETWORKING rather than HAS_WIFI || HAS_ETHERNET
#define HAS_NETWORKING (HAS_WIFI || HAS_ETHERNET)
// // Turn off Bluetooth // // Turn off Bluetooth
#ifdef MESHTASTIC_EXCLUDE_BLUETOOTH #ifdef MESHTASTIC_EXCLUDE_BLUETOOTH
#undef HAS_BLUETOOTH #undef HAS_BLUETOOTH
@@ -310,7 +308,4 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#ifdef MESHTASTIC_EXCLUDE_SCREEN #ifdef MESHTASTIC_EXCLUDE_SCREEN
#undef HAS_SCREEN #undef HAS_SCREEN
#define HAS_SCREEN 0 #define HAS_SCREEN 0
#endif #endif
#include "DebugConfiguration.h"
#include "RF95Configuration.h"

View File

@@ -314,7 +314,7 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
case SHT31_4x_ADDR: case SHT31_4x_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2); registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
if (registerValue == 0x11a2 || registerValue == 0x11da) { if (registerValue == 0x11a2) {
type = SHT4X; type = SHT4X;
LOG_INFO("SHT4X sensor found\n"); LOG_INFO("SHT4X sensor found\n");
} else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) { } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
@@ -402,4 +402,4 @@ TwoWire *ScanI2CTwoWire::fetchI2CBus(ScanI2C::DeviceAddress address) const
size_t ScanI2CTwoWire::countDevices() const size_t ScanI2CTwoWire::countDevices() const
{ {
return foundDevices.size(); return foundDevices.size();
} }

View File

@@ -3,13 +3,11 @@
#include "Default.h" #include "Default.h"
#include "GPS.h" #include "GPS.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerMon.h"
#include "RTC.h" #include "RTC.h"
#include "main.h" // pmu_found #include "main.h" // pmu_found
#include "sleep.h" #include "sleep.h"
#include "GPSUpdateScheduling.h"
#include "cas.h" #include "cas.h"
#include "ubx.h" #include "ubx.h"
@@ -23,6 +21,19 @@
#define GPS_RESET_MODE HIGH #define GPS_RESET_MODE HIGH
#endif #endif
// How many minutes of sleep make it worthwhile to power-off the GPS
// Shorter than this, and GPS will only enter standby
// Affected by lock-time, and config.position.gps_update_interval
#ifndef GPS_STANDBY_THRESHOLD_MINUTES
#define GPS_STANDBY_THRESHOLD_MINUTES 15
#endif
// How many seconds of sleep make it worthwhile for the GPS to use powered-on standby
// Shorter than this, and we'll just wait instead
#ifndef GPS_IDLE_THRESHOLD_SECONDS
#define GPS_IDLE_THRESHOLD_SECONDS 10
#endif
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) #if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
HardwareSerial *GPS::_serial_gps = &Serial1; HardwareSerial *GPS::_serial_gps = &Serial1;
#else #else
@@ -31,8 +42,6 @@ HardwareSerial *GPS::_serial_gps = NULL;
GPS *gps = nullptr; GPS *gps = nullptr;
GPSUpdateScheduling scheduling;
/// Multiple GPS instances might use the same serial port (in sequence), but we can /// Multiple GPS instances might use the same serial port (in sequence), but we can
/// only init that port once. /// only init that port once.
static bool didSerialInit; static bool didSerialInit;
@@ -42,25 +51,6 @@ uint8_t uBloxProtocolVersion;
#define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway #define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway
#define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc) #define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc)
// For logging
const char *getGPSPowerStateString(GPSPowerState state)
{
switch (state) {
case GPS_ACTIVE:
return "ACTIVE";
case GPS_IDLE:
return "IDLE";
case GPS_SOFTSLEEP:
return "SOFTSLEEP";
case GPS_HARDSLEEP:
return "HARDSLEEP";
case GPS_OFF:
return "OFF";
default:
assert(false); // Unhandled enum value..
}
}
void GPS::UBXChecksum(uint8_t *message, size_t length) void GPS::UBXChecksum(uint8_t *message, size_t length)
{ {
uint8_t CK_A = 0, CK_B = 0; uint8_t CK_A = 0, CK_B = 0;
@@ -782,6 +772,7 @@ bool GPS::setup()
} }
notifyDeepSleepObserver.observe(&notifyDeepSleep); notifyDeepSleepObserver.observe(&notifyDeepSleep);
notifyGPSSleepObserver.observe(&notifyGPSSleep);
return true; return true;
} }
@@ -790,192 +781,113 @@ GPS::~GPS()
{ {
// we really should unregister our sleep observer // we really should unregister our sleep observer
notifyDeepSleepObserver.unobserve(&notifyDeepSleep); notifyDeepSleepObserver.unobserve(&notifyDeepSleep);
notifyGPSSleepObserver.observe(&notifyGPSSleep);
} }
// Put the GPS hardware into a specified state void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime)
{ {
// Update the stored GPSPowerstate, and create local copies // Record the current powerState
GPSPowerState oldState = powerState; if (on)
powerState = newState; powerState = GPS_ACTIVE;
LOG_INFO("GPS power state moving from %s to %s\n", getGPSPowerStateString(oldState), getGPSPowerStateString(newState)); else if (!enabled) // User has disabled with triple press
powerState = GPS_OFF;
else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL)
powerState = GPS_IDLE;
else if (standbyOnly)
powerState = GPS_STANDBY;
else
powerState = GPS_OFF;
switch (newState) { LOG_DEBUG("GPS::powerState=%d\n", powerState);
case GPS_ACTIVE:
case GPS_IDLE:
if (oldState == GPS_ACTIVE || oldState == GPS_IDLE) // If hardware already awake, no changes needed
break;
if (oldState != GPS_ACTIVE && oldState != GPS_IDLE) // If hardware just waking now, clear buffer
clearBuffer();
powerMon->setState(meshtastic_PowerMon_State_GPS_Active); // Report change for power monitoring (during testing)
writePinEN(true); // Power (EN pin): on
setPowerPMU(true); // Power (PMU): on
writePinStandby(false); // Standby (pin): awake (not standby)
setPowerUBLOX(true); // Standby (UBLOX): awake
break;
case GPS_SOFTSLEEP: // If the next update is due *really soon*, don't actually power off or enter standby. Just wait it out.
powerMon->clearState(meshtastic_PowerMon_State_GPS_Active); // Report change for power monitoring (during testing) if (!on && powerState == GPS_IDLE)
writePinEN(true); // Power (EN pin): on
setPowerPMU(true); // Power (PMU): on
writePinStandby(true); // Standby (pin): asleep (not awake)
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
break;
case GPS_HARDSLEEP:
powerMon->clearState(meshtastic_PowerMon_State_GPS_Active); // Report change for power monitoring (during testing)
writePinEN(false); // Power (EN pin): off
setPowerPMU(false); // Power (PMU): off
writePinStandby(true); // Standby (pin): asleep (not awake)
setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed
break;
case GPS_OFF:
assert(sleepTime == 0); // This is an indefinite sleep
powerMon->clearState(meshtastic_PowerMon_State_GPS_Active); // Report change for power monitoring (during testing)
writePinEN(false); // Power (EN pin): off
setPowerPMU(false); // Power (PMU): off
writePinStandby(true); // Standby (pin): asleep
setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely
break;
}
}
// Set power with EN pin, if relevant
void GPS::writePinEN(bool on)
{
// Abort: if conflict with Canned Messages when using Wisblock(?)
if (HW_VENDOR == meshtastic_HardwareModel_RAK4631 && (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1))
return; return;
// Abort: if pin unset
if (!en_gpio)
return;
// Determine new value for the pin
bool val = GPS_EN_ACTIVE ? on : !on;
// Write and log
pinMode(en_gpio, OUTPUT);
digitalWrite(en_gpio, val);
#ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("Pin EN %s\n", val == HIGH ? "HIGH" : "LOW");
#endif
}
// Set the value of the STANDBY pin, if relevant
// true for standby state, false for awake
void GPS::writePinStandby(bool standby)
{
#ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76B, L76K and clones
// Determine the new value for the pin
// Normally: active HIGH for awake
#if PIN_GPS_STANDBY_INVERTED
bool val = standby;
#else
bool val = !standby;
#endif
// Write and log
pinMode(PIN_GPS_STANDBY, OUTPUT);
digitalWrite(PIN_GPS_STANDBY, val);
#ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("Pin STANDBY %s\n", val == HIGH ? "HIGH" : "LOW");
#endif
#endif
}
// Enable / Disable GPS with PMU, if present
void GPS::setPowerPMU(bool on)
{
// We only have PMUs on the T-Beam, and that board has a tiny battery to save GPS ephemera,
// so treat as a standby.
#ifdef HAS_PMU
// Abort: if no PMU
if (!pmu_found)
return;
// Abort: if PMU not initialized
if (!PMU)
return;
uint8_t model = PMU->getChipModel();
if (model == XPOWERS_AXP2101) {
if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
// t-beam v1.2 GNSS power channel
on ? PMU->enablePowerOutput(XPOWERS_ALDO3) : PMU->disablePowerOutput(XPOWERS_ALDO3);
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) {
// t-beam-s3-core GNSS power channel
on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4);
}
} else if (model == XPOWERS_AXP192) {
// t-beam v1.1 GNSS power channel
on ? PMU->enablePowerOutput(XPOWERS_LDO3) : PMU->disablePowerOutput(XPOWERS_LDO3);
}
#ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("PMU %s\n", on ? "on" : "off");
#endif
#endif
}
// Set UBLOX power, if relevant
void GPS::setPowerUBLOX(bool on, uint32_t sleepMs)
{
// Abort: if not UBLOX hardware
if (gnssModel != GNSS_MODEL_UBLOX)
return;
// If waking
if (on) { if (on) {
gps->_serial_gps->write(0xFF); clearBuffer(); // drop any old data waiting in the buffer before re-enabling
clearBuffer(); // This often returns old data, so drop it if (en_gpio)
#ifdef GPS_EXTRAVERBOSE digitalWrite(en_gpio, on ? GPS_EN_ACTIVE : !GPS_EN_ACTIVE); // turn this on if defined, every time
LOG_DEBUG("UBLOX: wake\n");
#endif
} }
isInPowersave = !on;
// If putting to sleep if (!standbyOnly && en_gpio != 0 &&
else { !(HW_VENDOR == meshtastic_HardwareModel_RAK4631 && (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1))) {
uint8_t msglen; LOG_DEBUG("GPS powerdown using GPS_EN_ACTIVE\n");
digitalWrite(en_gpio, on ? GPS_EN_ACTIVE : !GPS_EN_ACTIVE);
// If we're being asked to sleep indefinitely, make *sure* we're awake first, to process the new sleep command return;
if (sleepMs == 0) { }
setPowerUBLOX(true); #ifdef HAS_PMU // We only have PMUs on the T-Beam, and that board has a tiny battery to save GPS ephemera, so treat as a standby.
delay(500); if (pmu_found && PMU) {
uint8_t model = PMU->getChipModel();
if (model == XPOWERS_AXP2101) {
if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) {
// t-beam v1.2 GNSS power channel
on ? PMU->enablePowerOutput(XPOWERS_ALDO3) : PMU->disablePowerOutput(XPOWERS_ALDO3);
} else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) {
// t-beam-s3-core GNSS power channel
on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4);
}
} else if (model == XPOWERS_AXP192) {
// t-beam v1.1 GNSS power channel
on ? PMU->enablePowerOutput(XPOWERS_LDO3) : PMU->disablePowerOutput(XPOWERS_LDO3);
}
return;
}
#endif
#ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76B, L76K and clones
if (on) {
LOG_INFO("Waking GPS\n");
pinMode(PIN_GPS_STANDBY, OUTPUT);
// Some PCB's use an inverse logic due to a transistor driver
// Example for this is the Pico-Waveshare Lora+GPS HAT
#ifdef PIN_GPS_STANDBY_INVERTED
digitalWrite(PIN_GPS_STANDBY, 0);
#else
digitalWrite(PIN_GPS_STANDBY, 1);
#endif
return;
} else {
LOG_INFO("GPS entering sleep\n");
// notifyGPSSleep.notifyObservers(NULL);
pinMode(PIN_GPS_STANDBY, OUTPUT);
#ifdef PIN_GPS_STANDBY_INVERTED
digitalWrite(PIN_GPS_STANDBY, 1);
#else
digitalWrite(PIN_GPS_STANDBY, 0);
#endif
return;
}
#endif
if (!on) {
if (gnssModel == GNSS_MODEL_UBLOX) {
uint8_t msglen;
LOG_DEBUG("Sleep Time: %i\n", sleepTime);
if (strncmp(info.hwVersion, "000A0000", 8) != 0) {
for (int i = 0; i < 4; i++) {
gps->_message_PMREQ[0 + i] = sleepTime >> (i * 8); // Encode the sleep time in millis into the packet
}
msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ), gps->_message_PMREQ);
} else {
for (int i = 0; i < 4; i++) {
gps->_message_PMREQ_10[4 + i] = sleepTime >> (i * 8); // Encode the sleep time in millis into the packet
}
msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ_10), gps->_message_PMREQ_10);
}
gps->_serial_gps->write(gps->UBXscratch, msglen);
} }
// Determine hardware version
if (strncmp(info.hwVersion, "000A0000", 8) != 0) {
// Encode the sleep time in millis into the packet
for (int i = 0; i < 4; i++)
gps->_message_PMREQ[0 + i] = sleepMs >> (i * 8);
// Record the message length
msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ), gps->_message_PMREQ);
} else {
// Encode the sleep time in millis into the packet
for (int i = 0; i < 4; i++)
gps->_message_PMREQ_10[4 + i] = sleepMs >> (i * 8);
// Record the message length
msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ_10), gps->_message_PMREQ_10);
#ifdef GNSS_Airoha // add by WayenWeng #ifdef GNSS_Airoha // add by WayenWeng
else {
if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) { if ((config.position.gps_update_interval * 1000) >= (GPS_FIX_HOLD_TIME * 2)) {
// TODO, send rtc mode command // TODO, send rtc mode command
digitalWrite(PIN_GPS_EN, LOW); digitalWrite(PIN_GPS_EN, LOW);
} }
#endif
} }
// Send the UBX packet
gps->_serial_gps->write(gps->UBXscratch, msglen);
#ifdef GPS_EXTRAVERBOSE
LOG_DEBUG("UBLOX: sleep for %dmS\n", sleepMs);
#endif #endif
} else {
if (gnssModel == GNSS_MODEL_UBLOX) {
gps->_serial_gps->write(0xFF);
clearBuffer(); // This often returns old data, so drop it
}
} }
} }
@@ -988,55 +900,109 @@ void GPS::setConnected()
} }
} }
// We want a GPS lock. Wake the hardware /**
void GPS::up() * Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
*
* calls sleep/wake
*/
void GPS::setAwake(bool wantAwake)
{ {
scheduling.informSearching();
setPowerState(GPS_ACTIVE); // If user has disabled GPS, make sure it is off, not just in standby or idle
if (!wantAwake && !enabled && powerState != GPS_OFF) {
setGPSPower(false, false, 0);
return;
}
// If GPS power state needs to change
if ((wantAwake && powerState != GPS_ACTIVE) || (!wantAwake && powerState == GPS_ACTIVE)) {
LOG_DEBUG("WANT GPS=%d\n", wantAwake);
// Calculate how long it takes to get a GPS lock
if (wantAwake) {
// Record the time we start looking for a lock
lastWakeStartMsec = millis();
#ifdef GNSS_Airoha
lastFixStartMsec = 0;
#endif
} else {
// Record by how much we missed our ideal target postion.gps_update_interval (for logging only)
// Need to calculate this before we update lastSleepStartMsec, to make the new prediction
int32_t lateByMsec = (int32_t)(millis() - lastSleepStartMsec) - (int32_t)getSleepTime();
// Record the time we finish looking for a lock
lastSleepStartMsec = millis();
// How long did it take to get GPS lock this time?
uint32_t lockTime = lastSleepStartMsec - lastWakeStartMsec;
// Update the lock-time prediction
// Used pre-emptively, attempting to hit target of gps.position_update_interval
switch (GPSCycles) {
case 0:
LOG_DEBUG("Initial GPS lock took %ds\n", lockTime / 1000);
break;
case 1:
predictedLockTime = lockTime; // Avoid slow ramp-up - start with a real value
LOG_DEBUG("GPS Lock took %ds\n", lockTime / 1000);
break;
default:
// Predict lock-time using exponential smoothing: respond slowly to changes
predictedLockTime = (lockTime * 0.2) + (predictedLockTime * 0.8); // Latest lock time has 20% weight on prediction
LOG_INFO("GPS Lock took %ds. %s by %ds. Next lock predicted to take %ds.\n", lockTime / 1000,
(lateByMsec > 0) ? "Late" : "Early", abs(lateByMsec) / 1000, predictedLockTime / 1000);
}
GPSCycles++;
}
// How long to wait before attempting next GPS update
// Aims to hit position.gps_update_interval by using the lock-time prediction
uint32_t compensatedSleepTime = (getSleepTime() > predictedLockTime) ? (getSleepTime() - predictedLockTime) : 0;
// If long interval between updates: power off between updates
if (compensatedSleepTime > GPS_STANDBY_THRESHOLD_MINUTES * MS_IN_MINUTE) {
setGPSPower(wantAwake, false, getSleepTime() - predictedLockTime);
}
// If waking relatively frequently: don't power off. Would use more energy trying to reacquire lock each time
// We'll either use a "powered-on" standby, or just wait it out, depending on how soon the next update is due
// Will decide which inside setGPSPower method
else {
#ifdef GPS_UC6580
setGPSPower(wantAwake, false, compensatedSleepTime);
#else
setGPSPower(wantAwake, true, compensatedSleepTime);
#endif
}
}
} }
// We've got a GPS lock. Enter a low power state, potentially. /** Get how long we should stay looking for each acquisition in msecs
void GPS::down() */
uint32_t GPS::getWakeTime() const
{ {
scheduling.informGotLock(); uint32_t t = config.position.position_broadcast_secs;
uint32_t predictedSearchDuration = scheduling.predictedSearchDurationMs();
uint32_t sleepTime = scheduling.msUntilNextSearch();
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval);
LOG_DEBUG("%us until next search\n", sleepTime / 1000); if (t == UINT32_MAX)
return t; // already maxint
#ifdef GNSS_Airoha return Default::getConfiguredOrDefaultMs(t, default_broadcast_interval_secs);
lastFixStartMsec = 0; }
#endif
// If update interval less than 10 seconds, no attempt to sleep /** Get how long we should sleep between aqusition attempts in msecs
if (updateInterval <= 10 * 1000UL) */
setPowerState(GPS_IDLE); uint32_t GPS::getSleepTime() const
else { {
// Check whether the GPS hardware is capable of GPS_SOFTSLEEP uint32_t t = config.position.gps_update_interval;
// If not, fallback to GPS_HARDSLEEP instead
bool softsleepSupported = false;
if (gnssModel == GNSS_MODEL_UBLOX) // U-blox is supported via PMREQ
softsleepSupported = true;
#ifdef PIN_GPS_STANDBY // L76B, L76K and clones have a standby pin
softsleepSupported = true;
#endif
// How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP? // We'll not need the GPS thread to wake up again after first acq. with fixed position.
// Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050 if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED || config.position.fixed_position)
// https://www.desmos.com/calculator/6gvjghoumr t = UINT32_MAX; // Sleep forever now
// This is not particularly accurate, but probably an impromevement over a single, fixed threshold
uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22));
LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000);
// If update interval too short: softsleep (if supported by hardware) if (t == UINT32_MAX)
if (softsleepSupported && updateInterval < hardsleepThreshold) return t; // already maxint
setPowerState(GPS_SOFTSLEEP, sleepTime);
// If update interval long enough (or softsleep unsupported): hardsleep instead return Default::getConfiguredOrDefaultMs(t, default_gps_update_interval);
else
setPowerState(GPS_HARDSLEEP, sleepTime);
}
} }
void GPS::publishUpdate() void GPS::publishUpdate()
@@ -1087,13 +1053,13 @@ int32_t GPS::runOnce()
return disable(); return disable();
} }
if (whileActive()) { if (whileIdle()) {
// if we have received valid NMEA claim we are connected // if we have received valid NMEA claim we are connected
setConnected(); setConnected();
} else { } else {
if ((config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) && (gnssModel == GNSS_MODEL_UBLOX)) { if ((config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) && (gnssModel == GNSS_MODEL_UBLOX)) {
// reset the GPS on next bootup // reset the GPS on next bootup
if (devicestate.did_gps_reset && scheduling.elapsedSearchMs() > 60 * 1000UL && !hasFlow()) { if (devicestate.did_gps_reset && (millis() - lastWakeStartMsec > 60000) && !hasFlow()) {
LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n"); LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n");
devicestate.did_gps_reset = false; devicestate.did_gps_reset = false;
nodeDB->saveDeviceStateToDisk(); nodeDB->saveDeviceStateToDisk();
@@ -1108,43 +1074,54 @@ int32_t GPS::runOnce()
// gps->factoryReset(); // gps->factoryReset();
} }
// If we're due for an update, wake the GPS // If we are overdue for an update, turn on the GPS and at least publish the current status
if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue()) uint32_t now = millis();
up(); uint32_t timeAsleep = now - lastSleepStartMsec;
// If we've already set time from the GPS, no need to ask the GPS auto sleepTime = getSleepTime();
bool gotTime = (getRTCQuality() >= RTCQualityGPS); if (powerState != GPS_ACTIVE && (sleepTime != UINT32_MAX) &&
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time ((timeAsleep > sleepTime) || (isInPowersave && timeAsleep > (sleepTime - predictedLockTime)))) {
gotTime = true; // We now want to be awake - so wake up the GPS
shouldPublish = true; setAwake(true);
} }
bool gotLoc = lookForLocation(); // While we are awake
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP if (powerState == GPS_ACTIVE) {
LOG_DEBUG("hasValidLocation RISING EDGE\n"); // LOG_DEBUG("looking for location\n");
hasValidLocation = true; // If we've already set time from the GPS, no need to ask the GPS
shouldPublish = true; bool gotTime = (getRTCQuality() >= RTCQualityGPS);
} if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
gotTime = true;
bool tooLong = scheduling.searchedTooLong(); shouldPublish = true;
if (tooLong)
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time.\n");
// Once we get a location we no longer desperately want an update
// LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime);
if ((gotLoc && gotTime) || tooLong) {
if (tooLong) {
// we didn't get a location during this ack window, therefore declare loss of lock
if (hasValidLocation) {
LOG_DEBUG("hasValidLocation FALLING EDGE\n");
}
p = meshtastic_Position_init_default;
hasValidLocation = false;
} }
down(); bool gotLoc = lookForLocation();
shouldPublish = true; // publish our update for this just finished acquisition window if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
LOG_DEBUG("hasValidLocation RISING EDGE\n");
hasValidLocation = true;
shouldPublish = true;
}
now = millis();
auto wakeTime = getWakeTime();
bool tooLong = wakeTime != UINT32_MAX && (now - lastWakeStartMsec) > wakeTime;
// Once we get a location we no longer desperately want an update
// LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime);
if ((gotLoc && gotTime) || tooLong) {
if (tooLong) {
// we didn't get a location during this ack window, therefore declare loss of lock
if (hasValidLocation) {
LOG_DEBUG("hasValidLocation FALLING EDGE (last read: %d)\n", gotLoc);
}
p = meshtastic_Position_init_default;
hasValidLocation = false;
}
setAwake(false);
shouldPublish = true; // publish our update for this just finished acquisition window
}
} }
// If state has changed do a publish // If state has changed do a publish
@@ -1170,7 +1147,9 @@ void GPS::clearBuffer()
int GPS::prepareDeepSleep(void *unused) int GPS::prepareDeepSleep(void *unused)
{ {
LOG_INFO("GPS deep sleep!\n"); LOG_INFO("GPS deep sleep!\n");
disable();
setAwake(false);
return 0; return 0;
} }
@@ -1370,6 +1349,12 @@ GPS *GPS::createGps()
new_gps->tx_gpio = _tx_gpio; new_gps->tx_gpio = _tx_gpio;
new_gps->en_gpio = _en_gpio; new_gps->en_gpio = _en_gpio;
if (_en_gpio != 0) {
LOG_DEBUG("Setting %d to output.\n", _en_gpio);
pinMode(_en_gpio, OUTPUT);
digitalWrite(_en_gpio, !GPS_EN_ACTIVE);
}
#ifdef PIN_GPS_PPS #ifdef PIN_GPS_PPS
// pulse per second // pulse per second
pinMode(PIN_GPS_PPS, INPUT); pinMode(PIN_GPS_PPS, INPUT);
@@ -1384,8 +1369,7 @@ GPS *GPS::createGps()
LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n"); LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n");
#endif #endif
// Make sure the GPS is awake before performing any init. new_gps->setGPSPower(true, false, 0);
new_gps->up();
#ifdef PIN_GPS_RESET #ifdef PIN_GPS_RESET
pinMode(PIN_GPS_RESET, OUTPUT); pinMode(PIN_GPS_RESET, OUTPUT);
@@ -1393,6 +1377,7 @@ GPS *GPS::createGps()
delay(10); delay(10);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE); digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
#endif #endif
new_gps->setAwake(true); // Wake GPS power before doing any init
if (_serial_gps) { if (_serial_gps) {
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
@@ -1718,13 +1703,13 @@ bool GPS::hasFlow()
return reader.passedChecksum() > 0; return reader.passedChecksum() > 0;
} }
bool GPS::whileActive() bool GPS::whileIdle()
{ {
unsigned int charsInBuf = 0; unsigned int charsInBuf = 0;
bool isValid = false; bool isValid = false;
if (powerState != GPS_ACTIVE) { if (powerState != GPS_ACTIVE) {
clearBuffer(); clearBuffer();
return false; return (powerState == GPS_ACTIVE);
} }
#ifdef SERIAL_BUFFER_SIZE #ifdef SERIAL_BUFFER_SIZE
if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) { if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) {
@@ -1755,21 +1740,20 @@ bool GPS::whileActive()
} }
void GPS::enable() void GPS::enable()
{ {
// Clear the old scheduling info (reset the lock-time prediction) // Clear the old lock-time prediction
scheduling.reset(); GPSCycles = 0;
predictedLockTime = 0;
enabled = true; enabled = true;
setInterval(GPS_THREAD_INTERVAL); setInterval(GPS_THREAD_INTERVAL);
setAwake(true);
scheduling.informSearching();
setPowerState(GPS_ACTIVE);
} }
int32_t GPS::disable() int32_t GPS::disable()
{ {
enabled = false; enabled = false;
setInterval(INT32_MAX); setInterval(INT32_MAX);
setPowerState(GPS_OFF); setAwake(false);
return INT32_MAX; return INT32_MAX;
} }
@@ -1778,7 +1762,7 @@ void GPS::toggleGpsMode()
{ {
if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) { if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED; config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED;
LOG_INFO("User toggled GpsMode. Now DISABLED.\n"); LOG_DEBUG("Flag set to false for gps power. GpsMode: DISABLED\n");
#ifdef GNSS_Airoha #ifdef GNSS_Airoha
if (powerState != GPS_ACTIVE) { if (powerState != GPS_ACTIVE) {
LOG_DEBUG("User power Off GPS\n"); LOG_DEBUG("User power Off GPS\n");
@@ -1788,8 +1772,8 @@ void GPS::toggleGpsMode()
disable(); disable();
} else if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_DISABLED) { } else if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_DISABLED) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
LOG_INFO("User toggled GpsMode. Now ENABLED\n"); LOG_DEBUG("Flag set to true to restore power. GpsMode: ENABLED\n");
enable(); enable();
} }
} }
#endif // Exclude GPS #endif // Exclude GPS

View File

@@ -39,11 +39,10 @@ typedef enum {
} GPS_RESPONSE; } GPS_RESPONSE;
enum GPSPowerState : uint8_t { enum GPSPowerState : uint8_t {
GPS_ACTIVE, // Awake and want a position GPS_OFF = 0, // Physically powered off
GPS_IDLE, // Awake, but not wanting another position yet GPS_ACTIVE = 1, // Awake and want a position
GPS_SOFTSLEEP, // Physically powered on, but soft-sleeping GPS_STANDBY = 2, // Physically powered on, but soft-sleeping
GPS_HARDSLEEP, // Physically powered off, but scheduled to wake GPS_IDLE = 3, // Awake, but not wanting another position yet
GPS_OFF // Powered off indefinitely
}; };
// Generate a string representation of DOP // Generate a string representation of DOP
@@ -74,6 +73,8 @@ class GPS : private concurrency::OSThread
uint32_t rx_gpio = 0; uint32_t rx_gpio = 0;
uint32_t tx_gpio = 0; uint32_t tx_gpio = 0;
uint32_t en_gpio = 0; uint32_t en_gpio = 0;
uint32_t predictedLockTime = 0;
uint32_t GPSCycles = 0;
int speedSelect = 0; int speedSelect = 0;
int probeTries = 2; int probeTries = 2;
@@ -98,6 +99,7 @@ class GPS : private concurrency::OSThread
uint8_t numSatellites = 0; uint8_t numSatellites = 0;
CallbackObserver<GPS, void *> notifyDeepSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep); CallbackObserver<GPS, void *> notifyDeepSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
CallbackObserver<GPS, void *> notifyGPSSleepObserver = CallbackObserver<GPS, void *>(this, &GPS::prepareDeepSleep);
public: public:
/** If !NULL we will use this serial port to construct our GPS */ /** If !NULL we will use this serial port to construct our GPS */
@@ -173,8 +175,7 @@ class GPS : private concurrency::OSThread
// toggle between enabled/disabled // toggle between enabled/disabled
void toggleGpsMode(); void toggleGpsMode();
// Change the power state of the GPS - for power saving / shutdown void setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime);
void setPowerState(GPSPowerState newState, uint32_t sleepMs = 0);
/// Returns true if we have acquired GPS lock. /// Returns true if we have acquired GPS lock.
virtual bool hasLock(); virtual bool hasLock();
@@ -205,18 +206,18 @@ class GPS : private concurrency::OSThread
GPS_RESPONSE getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis); GPS_RESPONSE getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis);
/**
* Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode
*
* calls sleep/wake
*/
void setAwake(bool on);
virtual bool factoryReset(); virtual bool factoryReset();
// Creates an instance of the GPS class. // Creates an instance of the GPS class.
// Returns the new instance or null if the GPS is not present. // Returns the new instance or null if the GPS is not present.
static GPS *createGps(); static GPS *createGps();
// Wake the GPS hardware - ready for an update
void up();
// Let the GPS hardware save power between updates
void down();
protected: protected:
/** /**
* Perform any processing that should be done only while the GPS is awake and looking for a fix. * Perform any processing that should be done only while the GPS is awake and looking for a fix.
@@ -239,7 +240,7 @@ class GPS : private concurrency::OSThread
* *
* Return true if we received a valid message from the GPS * Return true if we received a valid message from the GPS
*/ */
virtual bool whileActive(); virtual bool whileIdle();
/** /**
* Perform any processing that should be done only while the GPS is awake and looking for a fix. * Perform any processing that should be done only while the GPS is awake and looking for a fix.
@@ -266,21 +267,13 @@ class GPS : private concurrency::OSThread
void UBXChecksum(uint8_t *message, size_t length); void UBXChecksum(uint8_t *message, size_t length);
void CASChecksum(uint8_t *message, size_t length); void CASChecksum(uint8_t *message, size_t length);
/** Set power with EN pin, if relevant /** Get how long we should stay looking for each aquisition
*/ */
void writePinEN(bool on); uint32_t getWakeTime() const;
/** Set the value of the STANDBY pin, if relevant /** Get how long we should sleep between aqusition attempts
*/ */
void writePinStandby(bool standby); uint32_t getSleepTime() const;
/** Set GPS power with PMU, if relevant
*/
void setPowerPMU(bool on);
/** Set UBLOX power, if relevant
*/
void setPowerUBLOX(bool on, uint32_t sleepMs = 0);
/** /**
* Tell users we have new GPS readings * Tell users we have new GPS readings
@@ -296,11 +289,9 @@ class GPS : private concurrency::OSThread
// delay counter to allow more sats before fixed position stops GPS thread // delay counter to allow more sats before fixed position stops GPS thread
uint8_t fixeddelayCtr = 0; uint8_t fixeddelayCtr = 0;
const char *powerStateToString();
protected: protected:
GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN; GnssModel_t gnssModel = GNSS_MODEL_UNKNOWN;
}; };
extern GPS *gps; extern GPS *gps;
#endif // Exclude GPS #endif // Exclude GPS

View File

@@ -1,118 +0,0 @@
#include "GPSUpdateScheduling.h"
#include "Default.h"
// Mark the time when searching for GPS position begins
void GPSUpdateScheduling::informSearching()
{
searchStartedMs = millis();
}
// Mark the time when searching for GPS is complete,
// then update the predicted lock-time
void GPSUpdateScheduling::informGotLock()
{
searchEndedMs = millis();
LOG_DEBUG("Took %us to get lock\n", (searchEndedMs - searchStartedMs) / 1000);
updateLockTimePrediction();
}
// Clear old lock-time prediction data.
// When re-enabling GPS with user button.
void GPSUpdateScheduling::reset()
{
searchStartedMs = 0;
searchEndedMs = 0;
searchCount = 0;
predictedMsToGetLock = 0;
}
// How many milliseconds before we should next search for GPS position
// Used by GPS hardware directly, to enter timed hardware sleep
uint32_t GPSUpdateScheduling::msUntilNextSearch()
{
uint32_t now = millis();
// Target interval (seconds), between GPS updates
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval, default_gps_update_interval);
// Check how long until we should start searching, to hopefully hit our target interval
uint32_t dueAtMs = searchEndedMs + updateInterval;
uint32_t compensatedStart = dueAtMs - predictedMsToGetLock;
int32_t remainingMs = compensatedStart - now;
// If we should have already started (negative value), start ASAP
if (remainingMs < 0)
remainingMs = 0;
return (uint32_t)remainingMs;
}
// How long have we already been searching?
// Used to abort a search in progress, if it runs unnaceptably long
uint32_t GPSUpdateScheduling::elapsedSearchMs()
{
// If searching
if (searchStartedMs > searchEndedMs)
return millis() - searchStartedMs;
// If not searching - 0ms. We shouldn't really consume this value
else
return 0;
}
// Is it now time to begin searching for a GPS position?
bool GPSUpdateScheduling::isUpdateDue()
{
return (msUntilNextSearch() == 0);
}
// Have we been searching for a GPS position for too long?
bool GPSUpdateScheduling::searchedTooLong()
{
uint32_t maxSearchMs =
Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs);
// If broadcast interval set to max, no such thing as "too long"
if (maxSearchMs == UINT32_MAX)
return false;
// If we've been searching longer than our position broadcast interval: that's too long
else if (elapsedSearchMs() > maxSearchMs)
return true;
// Otherwise, not too long yet!
else
return false;
}
// Updates the predicted time-to-get-lock, by exponentially smoothing the latest observation
void GPSUpdateScheduling::updateLockTimePrediction()
{
// How long did it take to get GPS lock this time?
// Duration between down() calls
int32_t lockTime = searchEndedMs - searchStartedMs;
if (lockTime < 0)
lockTime = 0;
// Ignore the first lock-time: likely to be long, will skew data
// Second locktime: likely stable. Use to intialize the smoothing filter
if (searchCount == 1)
predictedMsToGetLock = lockTime;
// Third locktime and after: predict using exponential smoothing. Respond slowly to changes
else if (searchCount > 1)
predictedMsToGetLock = (lockTime * weighting) + (predictedMsToGetLock * (1 - weighting));
searchCount++; // Only tracked so we can diregard initial lock-times
LOG_DEBUG("Predicting %us to get next lock\n", predictedMsToGetLock / 1000);
}
// How long do we expect to spend searching for a lock?
uint32_t GPSUpdateScheduling::predictedSearchDurationMs()
{
return GPSUpdateScheduling::predictedMsToGetLock;
}

View File

@@ -1,29 +0,0 @@
#pragma once
#include "configuration.h"
// Encapsulates code responsible for the timing of GPS updates
class GPSUpdateScheduling
{
public:
// Marks the time of these events, for calculation use
void informSearching();
void informGotLock(); // Predicted lock-time is recalculated here
void reset(); // Reset the prediction - after GPS::disable() / GPS::enable()
bool isUpdateDue(); // Is it time to begin searching for a GPS position?
bool searchedTooLong(); // Have we been searching for too long?
uint32_t msUntilNextSearch(); // How long until we need to begin searching for a GPS? Info provided to GPS hardware for sleep
uint32_t elapsedSearchMs(); // How long have we been searching so far?
uint32_t predictedSearchDurationMs(); // How long do we expect to spend searching for a lock?
private:
void updateLockTimePrediction(); // Called from informGotLock
uint32_t searchStartedMs = 0;
uint32_t searchEndedMs = 0;
uint32_t searchCount = 0;
uint32_t predictedMsToGetLock = 0;
const float weighting = 0.2; // Controls exponential smoothing of lock-times prediction. 20% weighting of "latest lock-time".
};

View File

@@ -156,8 +156,7 @@ bool EInkDisplay::connect()
} }
} }
#elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213) || \ #elif defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER)
defined(HELTEC_VISION_MASTER_E290)
{ {
// Start HSPI // Start HSPI
hspi = new SPIClass(HSPI); hspi = new SPIClass(HSPI);

View File

@@ -5,6 +5,11 @@
#include "GxEPD2_BW.h" #include "GxEPD2_BW.h"
#include <OLEDDisplay.h> #include <OLEDDisplay.h>
#if defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_WIRELESS_PAPER)
// Re-enable SPI after deep sleep: rtc_gpio_hold_dis()
#include "driver/rtc_io.h"
#endif
/** /**
* An adapter class that allows using the GxEPD2 library as if it was an OLEDDisplay implementation. * An adapter class that allows using the GxEPD2 library as if it was an OLEDDisplay implementation.
* *
@@ -67,8 +72,7 @@ class EInkDisplay : public OLEDDisplay
GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> *adafruitDisplay = NULL; GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT> *adafruitDisplay = NULL;
// If display uses HSPI // If display uses HSPI
#if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0) || defined(HELTEC_VISION_MASTER_E213) || \ #if defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_WIRELESS_PAPER_V1_0)
defined(HELTEC_VISION_MASTER_E290)
SPIClass *hspi = NULL; SPIClass *hspi = NULL;
#endif #endif

View File

@@ -41,7 +41,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "mesh/Channels.h" #include "mesh/Channels.h"
#include "mesh/generated/meshtastic/deviceonly.pb.h" #include "mesh/generated/meshtastic/deviceonly.pb.h"
#include "meshUtils.h" #include "meshUtils.h"
#include "modules/AdminModule.h"
#include "modules/ExternalNotificationModule.h" #include "modules/ExternalNotificationModule.h"
#include "modules/TextMessageModule.h" #include "modules/TextMessageModule.h"
#include "sleep.h" #include "sleep.h"
@@ -76,6 +75,7 @@ namespace graphics
// A text message frame + debug frame + all the node infos // A text message frame + debug frame + all the node infos
FrameCallback *normalFrames; FrameCallback *normalFrames;
static uint32_t targetFramerate = IDLE_FRAMERATE; static uint32_t targetFramerate = IDLE_FRAMERATE;
static char btPIN[16] = "888888";
uint32_t logo_timeout = 5000; // 4 seconds for EACH logo uint32_t logo_timeout = 5000; // 4 seconds for EACH logo
@@ -108,39 +108,15 @@ GeoCoord geoCoord;
static bool heartbeat = false; static bool heartbeat = false;
#endif #endif
// Quick access to screen dimensions from static drawing functions static uint16_t displayWidth, displayHeight;
// DEPRECATED. To-do: move static functions inside Screen class
#define SCREEN_WIDTH display->getWidth() #define SCREEN_WIDTH displayWidth
#define SCREEN_HEIGHT display->getHeight() #define SCREEN_HEIGHT displayHeight
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2) #define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
/// Check if the display can render a string (detect special chars; emoji)
static bool haveGlyphs(const char *str)
{
#if defined(OLED_UA) || defined(OLED_RU)
// Don't want to make any assumptions about custom language support
return true;
#endif
// Check each character with the lookup function for the OLED library
// We're not really meant to use this directly..
bool have = true;
for (uint16_t i = 0; i < strlen(str); i++) {
uint8_t result = Screen::customFontTableLookup((uint8_t)str[i]);
// If font doesn't support a character, it is substituted for ¿
if (result == 191 && (uint8_t)str[i] != 191) {
have = false;
break;
}
}
LOG_DEBUG("haveGlyphs=%d\n", have);
return have;
}
/** /**
* Draw the icon with extra info printed around the corners * Draw the icon with extra info printed around the corners
*/ */
@@ -164,15 +140,13 @@ static void drawIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDispl
if (upperMsg) if (upperMsg)
display->drawString(x + 0, y + 0, upperMsg); display->drawString(x + 0, y + 0, upperMsg);
// Draw version and short name in upper right // Draw version in upper right
char buf[25]; char buf[16];
snprintf(buf, sizeof(buf), "%s\n%s", xstr(APP_VERSION_SHORT), haveGlyphs(owner.short_name) ? owner.short_name : ""); snprintf(buf, sizeof(buf), "%s",
xstr(APP_VERSION_SHORT)); // Note: we don't bother printing region or now, it makes the string too long
display->setTextAlignment(TEXT_ALIGN_RIGHT); display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf);
display->drawString(x + SCREEN_WIDTH, y + 0, buf);
screen->forceDisplay(); screen->forceDisplay();
// FIXME - draw serial # somewhere?
display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code
} }
static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
@@ -207,15 +181,14 @@ static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDi
if (upperMsg) if (upperMsg)
display->drawString(x + 0, y + 0, upperMsg); display->drawString(x + 0, y + 0, upperMsg);
// Draw version and shortname in upper right // Draw version in upper right
char buf[25]; char buf[16];
snprintf(buf, sizeof(buf), "%s\n%s", xstr(APP_VERSION_SHORT), haveGlyphs(owner.short_name) ? owner.short_name : ""); snprintf(buf, sizeof(buf), "%s",
xstr(APP_VERSION_SHORT)); // Note: we don't bother printing region or now, it makes the string too long
display->setTextAlignment(TEXT_ALIGN_RIGHT); display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf);
display->drawString(x + SCREEN_WIDTH, y + 0, buf);
screen->forceDisplay(); screen->forceDisplay();
display->setTextAlignment(TEXT_ALIGN_LEFT); // Restore left align, just to be kind to any other unsuspecting code // FIXME - draw serial # somewhere?
} }
static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
@@ -225,7 +198,7 @@ static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, i
drawOEMIconScreen(region, display, state, x, y); drawOEMIconScreen(region, display, state, x, y);
} }
void Screen::drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message) static void drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *message)
{ {
uint16_t x_offset = display->width() / 2; uint16_t x_offset = display->width() / 2;
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
@@ -233,6 +206,20 @@ void Screen::drawFrameText(OLEDDisplay *display, OLEDDisplayUiState *state, int1
display->drawString(x_offset + x, 26 + y, message); display->drawString(x_offset + x, 26 + y, message);
} }
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
#ifdef ARCH_ESP32
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER || wakeCause == ESP_SLEEP_WAKEUP_EXT1) {
drawFrameText(display, state, x, y, "Resuming...");
} else
#endif
{
// Draw region in upper left
const char *region = myRegion ? myRegion->name : NULL;
drawIconScreen(region, display, state, x, y);
}
}
// Used on boot when a certificate is being created // Used on boot when a certificate is being created
static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
@@ -290,19 +277,40 @@ static void drawFunctionOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
} }
} }
/// Check if the display can render a string (detect special chars; emoji)
static bool haveGlyphs(const char *str)
{
#if defined(OLED_UA) || defined(OLED_RU)
// Don't want to make any assumptions about custom language support
return true;
#endif
// Check each character with the lookup function for the OLED library
// We're not really meant to use this directly..
bool have = true;
for (uint16_t i = 0; i < strlen(str); i++) {
uint8_t result = Screen::customFontTableLookup((uint8_t)str[i]);
// If font doesn't support a character, it is substituted for ¿
if (result == 191 && (uint8_t)str[i] != 191) {
have = false;
break;
}
}
LOG_DEBUG("haveGlyphs=%d\n", have);
return have;
}
#ifdef USE_EINK #ifdef USE_EINK
/// Used on eink displays while in deep sleep /// Used on eink displays while in deep sleep
static void drawDeepSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawDeepSleepScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
// Next frame should use full-refresh, and block while running, else device will sleep before async callback // Next frame should use full-refresh, and block while running, else device will sleep before async callback
EINK_ADD_FRAMEFLAG(display, COSMETIC); EINK_ADD_FRAMEFLAG(display, COSMETIC);
EINK_ADD_FRAMEFLAG(display, BLOCKING); EINK_ADD_FRAMEFLAG(display, BLOCKING);
LOG_DEBUG("Drawing deep sleep screen\n"); LOG_DEBUG("Drawing deep sleep screen\n");
drawIconScreen("Sleeping...", display, state, x, y);
// Display displayStr on the screen
drawIconScreen("Sleeping", display, state, x, y);
} }
/// Used on eink displays when screen updates are paused /// Used on eink displays when screen updates are paused
@@ -367,7 +375,7 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
// in the array of "drawScreen" functions; however, // in the array of "drawScreen" functions; however,
// the passed-state doesn't quite reflect the "current" // the passed-state doesn't quite reflect the "current"
// screen, so we have to detect it. // screen, so we have to detect it.
if (state->frameState == IN_TRANSITION && state->transitionFrameRelationship == TransitionRelationship_INCOMING) { if (state->frameState == IN_TRANSITION && state->transitionFrameRelationship == INCOMING) {
// if we're transitioning from the end of the frame list back around to the first // if we're transitioning from the end of the frame list back around to the first
// frame, then we want this to be `0` // frame, then we want this to be `0`
module_frame = state->transitionFrameTarget; module_frame = state->transitionFrameTarget;
@@ -381,6 +389,31 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
pi.drawFrame(display, state, x, y); pi.drawFrame(display, state, x, y);
} }
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
int x_offset = display->width() / 2;
int y_offset = display->height() <= 80 ? 0 : 32;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, y_offset + y, "Bluetooth");
display->setFont(FONT_SMALL);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_MEDIUM - 4 : y_offset + FONT_HEIGHT_MEDIUM + 5;
display->drawString(x_offset + x, y_offset + y, "Enter this code");
display->setFont(FONT_LARGE);
String displayPin(btPIN);
String pin = displayPin.substring(0, 3) + " " + displayPin.substring(3, 6);
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_SMALL - 5 : y_offset + FONT_HEIGHT_SMALL + 5;
display->drawString(x_offset + x, y_offset + y, pin);
display->setFont(FONT_SMALL);
String deviceName = "Name: ";
deviceName.concat(getDeviceName());
y_offset = display->height() == 64 ? y_offset + FONT_HEIGHT_LARGE - 6 : y_offset + FONT_HEIGHT_LARGE + 5;
display->drawString(x_offset + x, y_offset + y, deviceName);
}
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
@@ -1058,8 +1091,45 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
#endif #endif
} }
/// Draw the last waypoint we received
static void drawWaypointFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
static char tempBuf[237];
meshtastic_MeshPacket &mp = devicestate.rx_waypoint;
meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(getFrom(&mp));
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setFont(FONT_SMALL);
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->fillRect(0 + x, 0 + y, x + display->getWidth(), y + FONT_HEIGHT_SMALL);
display->setColor(BLACK);
}
uint32_t seconds = sinceReceived(&mp);
uint32_t minutes = seconds / 60;
uint32_t hours = minutes / 60;
uint32_t days = hours / 24;
if (config.display.heading_bold) {
display->drawStringf(1 + x, 0 + y, tempBuf, "%s ago from %s",
screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
(node && node->has_user) ? node->user.short_name : "???");
}
display->drawStringf(0 + x, 0 + y, tempBuf, "%s ago from %s", screen->drawTimeDelta(days, hours, minutes, seconds).c_str(),
(node && node->has_user) ? node->user.short_name : "???");
display->setColor(WHITE);
meshtastic_Waypoint scratch;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, &meshtastic_Waypoint_msg, &scratch)) {
snprintf(tempBuf, sizeof(tempBuf), "Received waypoint: %s", scratch.name);
display->drawStringMaxWidth(0 + x, 0 + y + FONT_HEIGHT_SMALL, x + display->getWidth(), tempBuf);
}
}
/// Draw a series of fields in a column, wrapping to multiple columns if needed /// Draw a series of fields in a column, wrapping to multiple columns if needed
void Screen::drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields) static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields)
{ {
// The coordinates define the left starting point of the text // The coordinates define the left starting point of the text
display->setTextAlignment(TEXT_ALIGN_LEFT); display->setTextAlignment(TEXT_ALIGN_LEFT);
@@ -1239,13 +1309,56 @@ static void drawGPScoordinates(OLEDDisplay *display, int16_t x, int16_t y, const
} }
} }
#endif #endif
namespace
{
/// A basic 2D point class for drawing
class Point
{
public:
float x, y;
Point(float _x, float _y) : x(_x), y(_y) {}
/// Apply a rotation around zero (standard rotation matrix math)
void rotate(float radian)
{
float cos = cosf(radian), sin = sinf(radian);
float rx = x * cos + y * sin, ry = -x * sin + y * cos;
x = rx;
y = ry;
}
void translate(int16_t dx, int dy)
{
x += dx;
y += dy;
}
void scale(float f)
{
// We use -f here to counter the flip that happens
// on the y axis when drawing and rotating on screen
x *= f;
y *= -f;
}
};
} // namespace
static void drawLine(OLEDDisplay *d, const Point &p1, const Point &p2)
{
d->drawLine(p1.x, p1.y, p2.x, p2.y);
}
/** /**
* Given a recent lat/lon return a guess of the heading the user is walking on. * Given a recent lat/lon return a guess of the heading the user is walking on.
* *
* We keep a series of "after you've gone 10 meters, what is your heading since * We keep a series of "after you've gone 10 meters, what is your heading since
* the last reference point?" * the last reference point?"
*/ */
float Screen::estimatedHeading(double lat, double lon) static float estimatedHeading(double lat, double lon)
{ {
static double oldLat, oldLon; static double oldLat, oldLon;
static float b; static float b;
@@ -1269,13 +1382,38 @@ float Screen::estimatedHeading(double lat, double lon)
return b; return b;
} }
static uint16_t getCompassDiam(OLEDDisplay *display)
{
uint16_t diam = 0;
uint16_t offset = 0;
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT)
offset = FONT_HEIGHT_SMALL;
// get the smaller of the 2 dimensions and subtract 20
if (display->getWidth() > (display->getHeight() - offset)) {
diam = display->getHeight() - offset;
// if 2/3 of the other size would be smaller, use that
if (diam > (display->getWidth() * 2 / 3)) {
diam = display->getWidth() * 2 / 3;
}
} else {
diam = display->getWidth();
if (diam > ((display->getHeight() - offset) * 2 / 3)) {
diam = (display->getHeight() - offset) * 2 / 3;
}
}
return diam - 20;
};
/// We will skip one node - the one for us, so we just blindly loop over all /// We will skip one node - the one for us, so we just blindly loop over all
/// nodes /// nodes
static size_t nodeIndex; static size_t nodeIndex;
static int8_t prevFrame = -1; static int8_t prevFrame = -1;
// Draw the arrow pointing to a node's location // Draw the arrow pointing to a node's location
void Screen::drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian) static void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, float headingRadian)
{ {
Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially Point tip(0.0f, 0.5f), tail(0.0f, -0.5f); // pointing up initially
float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f; float arrowOffsetX = 0.2f, arrowOffsetY = 0.2f;
@@ -1285,45 +1423,16 @@ void Screen::drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t com
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
arrowPoints[i]->rotate(headingRadian); arrowPoints[i]->rotate(headingRadian);
arrowPoints[i]->scale(compassDiam * 0.6); arrowPoints[i]->scale(getCompassDiam(display) * 0.6);
arrowPoints[i]->translate(compassX, compassY); arrowPoints[i]->translate(compassX, compassY);
} }
display->drawLine(tip.x, tip.y, tail.x, tail.y); drawLine(display, tip, tail);
display->drawLine(leftArrow.x, leftArrow.y, tip.x, tip.y); drawLine(display, leftArrow, tip);
display->drawLine(rightArrow.x, rightArrow.y, tip.x, tip.y); drawLine(display, rightArrow, tip);
} }
// Get a string representation of the time passed since something happened // Draw north
void Screen::getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength) static void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
{
// Use an absolute timestamp in some cases.
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
uint8_t timestampHours, timestampMinutes;
int32_t daysAgo;
bool useTimestamp = deltaToTimestamp(agoSecs, &timestampHours, &timestampMinutes, &daysAgo);
if (agoSecs < 120) // last 2 mins?
snprintf(timeStr, maxLength, "%u seconds ago", agoSecs);
// -- if suitable for timestamp --
else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
else if (useTimestamp && daysAgo == 0) // Today
snprintf(timeStr, maxLength, "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
else if (useTimestamp && daysAgo == 1) // Yesterday
snprintf(timeStr, maxLength, "Seen yesterday");
else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
snprintf(timeStr, maxLength, "%li days ago", (long)daysAgo);
// -- if using time delta instead --
else if (agoSecs < 120 * 60) // last 2 hrs
snprintf(timeStr, maxLength, "%u minutes ago", agoSecs / 60);
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
snprintf(timeStr, maxLength, "%u hours ago", agoSecs / 60 / 60);
else
snprintf(timeStr, maxLength, "unknown age");
}
void Screen::drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading)
{ {
// If north is supposed to be at the top of the compass we want rotation to be +0 // If north is supposed to be at the top of the compass we want rotation to be +0
if (config.display.compass_north_top) if (config.display.compass_north_top)
@@ -1333,43 +1442,19 @@ void Screen::drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t co
Point N3(-0.04f, 0.55f), N4(0.04f, 0.55f); Point N3(-0.04f, 0.55f), N4(0.04f, 0.55f);
Point *rosePoints[] = {&N1, &N2, &N3, &N4}; Point *rosePoints[] = {&N1, &N2, &N3, &N4};
uint16_t compassDiam = Screen::getCompassDiam(SCREEN_WIDTH, SCREEN_HEIGHT);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
// North on compass will be negative of heading // North on compass will be negative of heading
rosePoints[i]->rotate(-myHeading); rosePoints[i]->rotate(-myHeading);
rosePoints[i]->scale(compassDiam); rosePoints[i]->scale(getCompassDiam(display));
rosePoints[i]->translate(compassX, compassY); rosePoints[i]->translate(compassX, compassY);
} }
display->drawLine(N1.x, N1.y, N3.x, N3.y); drawLine(display, N1, N3);
display->drawLine(N2.x, N2.y, N4.x, N4.y); drawLine(display, N2, N4);
display->drawLine(N1.x, N1.y, N4.x, N4.y); drawLine(display, N1, N4);
} }
uint16_t Screen::getCompassDiam(uint32_t displayWidth, uint32_t displayHeight) /// Convert an integer GPS coords to a floating point
{ #define DegD(i) (i * 1e-7)
uint16_t diam = 0;
uint16_t offset = 0;
if (config.display.displaymode != meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT)
offset = FONT_HEIGHT_SMALL;
// get the smaller of the 2 dimensions and subtract 20
if (displayWidth > (displayHeight - offset)) {
diam = displayHeight - offset;
// if 2/3 of the other size would be smaller, use that
if (diam > (displayWidth * 2 / 3)) {
diam = displayWidth * 2 / 3;
}
} else {
diam = displayWidth;
if (diam > ((displayHeight - offset) * 2 / 3)) {
diam = (displayHeight - offset) * 2 / 3;
}
}
return diam - 20;
};
static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{ {
@@ -1409,8 +1494,34 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100)); snprintf(signalStr, sizeof(signalStr), "Signal: %d%%", clamp((int)((node->snr + 10) * 5), 0, 100));
} }
uint32_t agoSecs = sinceLastSeen(node);
static char lastStr[20]; static char lastStr[20];
screen->getTimeAgoStr(sinceLastSeen(node), lastStr, sizeof(lastStr));
// Use an absolute timestamp in some cases.
// Particularly useful with E-Ink displays. Static UI, fewer refreshes.
uint8_t timestampHours, timestampMinutes;
int32_t daysAgo;
bool useTimestamp = deltaToTimestamp(agoSecs, &timestampHours, &timestampMinutes, &daysAgo);
if (agoSecs < 120) // last 2 mins?
snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
// -- if suitable for timestamp --
else if (useTimestamp && agoSecs < 15 * SECONDS_IN_MINUTE) // Last 15 minutes
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / SECONDS_IN_MINUTE);
else if (useTimestamp && daysAgo == 0) // Today
snprintf(lastStr, sizeof(lastStr), "Last seen: %02u:%02u", (unsigned int)timestampHours, (unsigned int)timestampMinutes);
else if (useTimestamp && daysAgo == 1) // Yesterday
snprintf(lastStr, sizeof(lastStr), "Seen yesterday");
else if (useTimestamp && daysAgo > 1) // Last six months (capped by deltaToTimestamp method)
snprintf(lastStr, sizeof(lastStr), "%li days ago", (long)daysAgo);
// -- if using time delta instead --
else if (agoSecs < 120 * 60) // last 2 hrs
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
// Only show hours ago if it's been less than 6 months. Otherwise, we may have bad data.
else if ((agoSecs / 60 / 60) < (hours_in_month * 6))
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
else
snprintf(lastStr, sizeof(lastStr), "unknown age");
static char distStr[20]; static char distStr[20];
if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) { if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL) {
@@ -1421,14 +1532,13 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum()); meshtastic_NodeInfoLite *ourNode = nodeDB->getMeshNode(nodeDB->getNodeNum());
const char *fields[] = {username, lastStr, signalStr, distStr, NULL}; const char *fields[] = {username, lastStr, signalStr, distStr, NULL};
int16_t compassX = 0, compassY = 0; int16_t compassX = 0, compassY = 0;
uint16_t compassDiam = Screen::getCompassDiam(SCREEN_WIDTH, SCREEN_HEIGHT);
// coordinates for the center of the compass/circle // coordinates for the center of the compass/circle
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_DEFAULT) {
compassX = x + SCREEN_WIDTH - compassDiam / 2 - 5; compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
compassY = y + SCREEN_HEIGHT / 2; compassY = y + SCREEN_HEIGHT / 2;
} else { } else {
compassX = x + SCREEN_WIDTH - compassDiam / 2 - 5; compassX = x + SCREEN_WIDTH - getCompassDiam(display) / 2 - 5;
compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2; compassY = y + FONT_HEIGHT_SMALL + (SCREEN_HEIGHT - FONT_HEIGHT_SMALL) / 2;
} }
bool hasNodeHeading = false; bool hasNodeHeading = false;
@@ -1439,8 +1549,8 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
if (screen->hasHeading()) if (screen->hasHeading())
myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians myHeading = (screen->getHeading()) * PI / 180; // gotta convert compass degrees to Radians
else else
myHeading = screen->estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i)); myHeading = estimatedHeading(DegD(op.latitude_i), DegD(op.longitude_i));
screen->drawCompassNorth(display, compassX, compassY, myHeading); drawCompassNorth(display, compassX, compassY, myHeading);
if (hasValidPosition(node)) { if (hasValidPosition(node)) {
// display direction toward node // display direction toward node
@@ -1467,7 +1577,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
// If the top of the compass is not a static north we need adjust bearingToOther based on heading // If the top of the compass is not a static north we need adjust bearingToOther based on heading
if (!config.display.compass_north_top) if (!config.display.compass_north_top)
bearingToOther -= myHeading; bearingToOther -= myHeading;
screen->drawNodeHeading(display, compassX, compassY, compassDiam, bearingToOther); drawNodeHeading(display, compassX, compassY, bearingToOther);
} }
} }
if (!hasNodeHeading) { if (!hasNodeHeading) {
@@ -1477,19 +1587,15 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
// hasValidPosition(node)); // hasValidPosition(node));
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?"); display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
} }
display->drawCircle(compassX, compassY, compassDiam / 2); display->drawCircle(compassX, compassY, getCompassDiam(display) / 2);
if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) { if (config.display.displaymode == meshtastic_Config_DisplayConfig_DisplayMode_INVERTED) {
display->setColor(BLACK); display->setColor(BLACK);
} }
// Must be after distStr is populated // Must be after distStr is populated
screen->drawColumns(display, x, y, fields); drawColumns(display, x, y, fields);
} }
#if defined(ESP_PLATFORM) && defined(USE_ST7789)
SPIClass SPI1(HSPI);
#endif
Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry) Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_OledType screenType, OLEDDISPLAY_GEOMETRY geometry)
: concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32) : concurrency::OSThread("Screen"), address_found(address), model(screenType), geometry(geometry), cmdQueue(32)
{ {
@@ -1497,13 +1603,6 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
#if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64) #if defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SH1107_128_64)
dispdev = new SH1106Wire(address.address, -1, -1, geometry, dispdev = new SH1106Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
#elif defined(USE_ST7789)
#ifdef ESP_PLATFORM
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT, ST7789_SDA,
ST7789_MISO, ST7789_SCK);
#else
dispdev = new ST7789Spi(&SPI1, ST7789_RESET, ST7789_RS, ST7789_NSS, GEOMETRY_RAWMODE, TFT_WIDTH, TFT_HEIGHT);
#endif
#elif defined(USE_SSD1306) #elif defined(USE_SSD1306)
dispdev = new SSD1306Wire(address.address, -1, -1, geometry, dispdev = new SSD1306Wire(address.address, -1, -1, geometry,
(address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE); (address.port == ScanI2C::I2CPort::WIRE1) ? HW_I2C::I2C_TWO : HW_I2C::I2C_ONE);
@@ -1582,14 +1681,7 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
#endif #endif
dispdev->displayOn(); dispdev->displayOn();
#ifdef USE_ST7789
#ifdef ESP_PLATFORM
analogWrite(VTFT_LEDA, BRIGHTNESS_DEFAULT);
#else
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, TFT_BACKLIGHT_ON);
#endif
#endif
enabled = true; enabled = true;
setInterval(0); // Draw ASAP setInterval(0); // Draw ASAP
runASAP = true; runASAP = true;
@@ -1600,12 +1692,6 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
#endif #endif
LOG_INFO("Turning off screen\n"); LOG_INFO("Turning off screen\n");
dispdev->displayOff(); dispdev->displayOff();
#ifdef USE_ST7789
pinMode(VTFT_LEDA, OUTPUT);
digitalWrite(VTFT_LEDA, !TFT_BACKLIGHT_ON);
#endif
#ifdef T_WATCH_S3 #ifdef T_WATCH_S3
PMU->disablePowerOutput(XPOWERS_ALDO2); PMU->disablePowerOutput(XPOWERS_ALDO2);
#endif #endif
@@ -1655,19 +1741,9 @@ void Screen::setup()
// Add frames. // Add frames.
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST);
alertFrames[0] = [this](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void { static FrameCallback bootFrames[] = {drawBootScreen};
#ifdef ARCH_ESP32 static const int bootFrameCount = sizeof(bootFrames) / sizeof(bootFrames[0]);
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER || wakeCause == ESP_SLEEP_WAKEUP_EXT1) { ui->setFrames(bootFrames, bootFrameCount);
drawFrameText(display, state, x, y, "Resuming...");
} else
#endif
{
// Draw region in upper left
const char *region = myRegion ? myRegion->name : NULL;
drawIconScreen(region, display, state, x, y);
}
};
ui->setFrames(alertFrames, 1);
// No overlays. // No overlays.
ui->setOverlays(nullptr, 0); ui->setOverlays(nullptr, 0);
@@ -1726,7 +1802,6 @@ void Screen::setup()
powerStatusObserver.observe(&powerStatus->onNewStatus); powerStatusObserver.observe(&powerStatus->onNewStatus);
gpsStatusObserver.observe(&gpsStatus->onNewStatus); gpsStatusObserver.observe(&gpsStatus->onNewStatus);
nodeStatusObserver.observe(&nodeStatus->onNewStatus); nodeStatusObserver.observe(&nodeStatus->onNewStatus);
adminMessageObserver.observe(adminModule);
if (textMessageModule) if (textMessageModule)
textMessageObserver.observe(textMessageModule); textMessageObserver.observe(textMessageModule);
if (inputBroker) if (inputBroker)
@@ -1841,22 +1916,13 @@ int32_t Screen::runOnce()
case Cmd::SHOW_NEXT_FRAME: case Cmd::SHOW_NEXT_FRAME:
handleShowNextFrame(); handleShowNextFrame();
break; break;
case Cmd::START_ALERT_FRAME: { case Cmd::START_BLUETOOTH_PIN_SCREEN:
showingBootScreen = false; // this should avoid the edge case where an alert triggers before the boot screen goes away handleStartBluetoothPinScreen(cmd.bluetooth_pin);
showingNormalScreen = false;
alertFrames[0] = alertFrame;
#ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please
EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update
handleSetOn(true); // Ensure power-on to receive deep-sleep screensaver (PowerFSM should handle?)
#endif
setFrameImmediateDraw(alertFrames);
break; break;
}
case Cmd::START_FIRMWARE_UPDATE_SCREEN: case Cmd::START_FIRMWARE_UPDATE_SCREEN:
handleStartFirmwareUpdateScreen(); handleStartFirmwareUpdateScreen();
break; break;
case Cmd::STOP_ALERT_FRAME: case Cmd::STOP_BLUETOOTH_PIN_SCREEN:
case Cmd::STOP_BOOT_SCREEN: case Cmd::STOP_BOOT_SCREEN:
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
setFrames(); setFrames();
@@ -1865,6 +1931,12 @@ int32_t Screen::runOnce()
handlePrint(cmd.print_text); handlePrint(cmd.print_text);
free(cmd.print_text); free(cmd.print_text);
break; break;
case Cmd::START_SHUTDOWN_SCREEN:
handleShutdownScreen();
break;
case Cmd::START_REBOOT_SCREEN:
handleRebootScreen();
break;
default: default:
LOG_ERROR("Invalid screen cmd\n"); LOG_ERROR("Invalid screen cmd\n");
} }
@@ -1955,6 +2027,9 @@ void Screen::setWelcomeFrames()
/// Determine which screensaver frame to use, then set the FrameCallback /// Determine which screensaver frame to use, then set the FrameCallback
void Screen::setScreensaverFrames(FrameCallback einkScreensaver) void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
{ {
// Remember current frame, restore position at power-on
uint8_t frameNumber = ui->getUiState()->currentFrame;
// Retain specified frame / overlay callback beyond scope of this method // Retain specified frame / overlay callback beyond scope of this method
static FrameCallback screensaverFrame; static FrameCallback screensaverFrame;
static OverlayCallback screensaverOverlay; static OverlayCallback screensaverOverlay;
@@ -1992,8 +2067,9 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
#endif #endif
// Prepare now for next frame, shown when display wakes // Prepare now for next frame, shown when display wakes
ui->setOverlays(NULL, 0); // Clear overlay ui->setOverlays(NULL, 0); // Clear overlay
setFrames(FOCUS_PRESERVE); // Return to normal display updates, showing same frame as before screensaver, ideally setFrames(); // Return to normal display updates
ui->switchToFrame(frameNumber); // Attempt to return to same frame after power-on
// Pick a refresh method, for when display wakes // Pick a refresh method, for when display wakes
#ifdef EINK_HASQUIRK_GHOSTING #ifdef EINK_HASQUIRK_GHOSTING
@@ -2004,13 +2080,9 @@ void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
} }
#endif #endif
// Regenerate the normal set of frames, focusing a specific frame if requested // restore our regular frame list
// Called when a frame should be added / removed, or custom frames should be cleared void Screen::setFrames()
void Screen::setFrames(FrameFocus focus)
{ {
uint8_t originalPosition = ui->getUiState()->currentFrame;
FramesetInfo fsi; // Location of specific frames, for applying focus parameter
LOG_DEBUG("showing standard frames\n"); LOG_DEBUG("showing standard frames\n");
showingNormalScreen = true; showingNormalScreen = true;
@@ -2044,36 +2116,27 @@ void Screen::setFrames(FrameFocus focus)
// is the same offset into the moduleFrames vector // is the same offset into the moduleFrames vector
// so that we can invoke the module's callback // so that we can invoke the module's callback
for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) { for (auto i = moduleFrames.begin(); i != moduleFrames.end(); ++i) {
// Draw the module frame, using the hack described above normalFrames[numframes++] = drawModuleFrame;
normalFrames[numframes] = drawModuleFrame;
// Check if the module being drawn has requested focus
// We will honor this request later, if setFrames was triggered by a UIFrameEvent
MeshModule *m = *i;
if (m->isRequestingFocus())
fsi.positions.focusedModule = numframes;
numframes++;
} }
LOG_DEBUG("Added modules. numframes: %d\n", numframes); LOG_DEBUG("Added modules. numframes: %d\n", numframes);
// If we have a critical fault, show it first // If we have a critical fault, show it first
fsi.positions.fault = numframes; if (error_code)
if (error_code) {
normalFrames[numframes++] = drawCriticalFaultFrame; normalFrames[numframes++] = drawCriticalFaultFrame;
focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame
}
#ifdef T_WATCH_S3 #ifdef T_WATCH_S3
normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame; normalFrames[numframes++] = screen->digitalWatchFace ? &Screen::drawDigitalClockFrame : &Screen::drawAnalogClockFrame;
#endif #endif
// If we have a text message - show it next, unless it's a phone message and we aren't using any special modules // If we have a text message - show it next, unless it's a phone message and we aren't using any special modules
fsi.positions.textMessage = numframes;
if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) { if (devicestate.has_rx_text_message && shouldDrawMessage(&devicestate.rx_text_message)) {
normalFrames[numframes++] = drawTextMessageFrame; normalFrames[numframes++] = drawTextMessageFrame;
} }
// If we have a waypoint - show it next, unless it's a phone message and we aren't using any special modules
if (devicestate.has_rx_waypoint && shouldDrawMessage(&devicestate.rx_waypoint)) {
normalFrames[numframes++] = drawWaypointFrame;
}
// then all the nodes // then all the nodes
// We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens // We only show a few nodes in our scrolling list - because meshes with many nodes would have too many screens
@@ -2085,14 +2148,11 @@ void Screen::setFrames(FrameFocus focus)
// //
// Since frames are basic function pointers, we have to use a helper to // Since frames are basic function pointers, we have to use a helper to
// call a method on debugInfo object. // call a method on debugInfo object.
fsi.positions.log = numframes;
normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline; normalFrames[numframes++] = &Screen::drawDebugInfoTrampoline;
// call a method on debugInfoScreen object (for more details) // call a method on debugInfoScreen object (for more details)
fsi.positions.settings = numframes;
normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline; normalFrames[numframes++] = &Screen::drawDebugInfoSettingsTrampoline;
fsi.positions.wifi = numframes;
#if HAS_WIFI && !defined(ARCH_PORTDUINO) #if HAS_WIFI && !defined(ARCH_PORTDUINO)
if (isWifiAvailable()) { if (isWifiAvailable()) {
// call a method on debugInfoScreen object (for more details) // call a method on debugInfoScreen object (for more details)
@@ -2100,7 +2160,6 @@ void Screen::setFrames(FrameFocus focus)
} }
#endif #endif
fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
LOG_DEBUG("Finished building frames. numframes: %d\n", numframes); LOG_DEBUG("Finished building frames. numframes: %d\n", numframes);
ui->setFrames(normalFrames, numframes); ui->setFrames(normalFrames, numframes);
@@ -2114,58 +2173,20 @@ void Screen::setFrames(FrameFocus focus)
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
// just changed) // just changed)
// Focus on a specific frame, in the frame set we just created
switch (focus) {
case FOCUS_DEFAULT:
ui->switchToFrame(0); // First frame
break;
case FOCUS_FAULT:
ui->switchToFrame(fsi.positions.fault);
break;
case FOCUS_TEXTMESSAGE:
ui->switchToFrame(fsi.positions.textMessage);
break;
case FOCUS_MODULE:
// Whichever frame was marked by MeshModule::requestFocus(), if any
// If no module requested focus, will show the first frame instead
ui->switchToFrame(fsi.positions.focusedModule);
break;
case FOCUS_PRESERVE:
// If we can identify which type of frame "originalPosition" was, can move directly to it in the new frameset
FramesetInfo &oldFsi = this->framesetInfo;
if (originalPosition == oldFsi.positions.log)
ui->switchToFrame(fsi.positions.log);
else if (originalPosition == oldFsi.positions.settings)
ui->switchToFrame(fsi.positions.settings);
else if (originalPosition == oldFsi.positions.wifi)
ui->switchToFrame(fsi.positions.wifi);
// If frame count has decreased
else if (fsi.frameCount < oldFsi.frameCount) {
uint8_t numDropped = oldFsi.frameCount - fsi.frameCount;
// Move n frames backwards
if (numDropped <= originalPosition)
ui->switchToFrame(originalPosition - numDropped);
// Unless that would put us "out of bounds" (< 0)
else
ui->switchToFrame(0);
}
// If we're not sure exactly which frame we were on, at least return to the same frame number
// (node frames; module frames)
else
ui->switchToFrame(originalPosition);
break;
}
// Store the info about this frameset, for future setFrames calls
this->framesetInfo = fsi;
setFastFramerate(); // Draw ASAP setFastFramerate(); // Draw ASAP
} }
void Screen::handleStartBluetoothPinScreen(uint32_t pin)
{
LOG_DEBUG("showing bluetooth screen\n");
showingNormalScreen = false;
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
static FrameCallback frames[] = {drawFrameBluetooth};
snprintf(btPIN, sizeof(btPIN), "%06u", pin);
setFrameImmediateDraw(frames);
}
void Screen::setFrameImmediateDraw(FrameCallback *drawFrames) void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
{ {
ui->disableAllIndicators(); ui->disableAllIndicators();
@@ -2173,6 +2194,41 @@ void Screen::setFrameImmediateDraw(FrameCallback *drawFrames)
setFastFramerate(); setFastFramerate();
} }
void Screen::handleShutdownScreen()
{
LOG_DEBUG("showing shutdown screen\n");
showingNormalScreen = false;
#ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please
EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update
handleSetOn(true); // Ensure power-on to receive deep-sleep screensaver (PowerFSM should handle?)
#endif
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
drawFrameText(display, state, x, y, "Shutting down...");
};
static FrameCallback frames[] = {frame};
setFrameImmediateDraw(frames);
}
void Screen::handleRebootScreen()
{
LOG_DEBUG("showing reboot screen\n");
showingNormalScreen = false;
#ifdef USE_EINK
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Use fast-refresh for next frame, no skip please
EINK_ADD_FRAMEFLAG(dispdev, BLOCKING); // Edge case: if this frame is promoted to COSMETIC, wait for update
handleSetOn(true); // Power-on to show rebooting screen (PowerFSM should handle?)
#endif
auto frame = [](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
drawFrameText(display, state, x, y, "Rebooting...");
};
static FrameCallback frames[] = {frame};
setFrameImmediateDraw(frames);
}
void Screen::handleStartFirmwareUpdateScreen() void Screen::handleStartFirmwareUpdateScreen()
{ {
LOG_DEBUG("showing firmware screen\n"); LOG_DEBUG("showing firmware screen\n");
@@ -2189,7 +2245,7 @@ void Screen::blink()
uint8_t count = 10; uint8_t count = 10;
dispdev->setBrightness(254); dispdev->setBrightness(254);
while (count > 0) { while (count > 0) {
dispdev->fillRect(0, 0, dispdev->getWidth(), dispdev->getHeight()); dispdev->fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
dispdev->display(); dispdev->display();
delay(50); delay(50);
dispdev->clear(); dispdev->clear();
@@ -2617,7 +2673,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
switch (arg->getStatusType()) { switch (arg->getStatusType()) {
case STATUS_TYPE_NODE: case STATUS_TYPE_NODE:
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) { if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
setFrames(FOCUS_PRESERVE); // Regen the list of screen frames (returning to same frame, if possible) setFrames(); // Regen the list of screens
} }
nodeDB->updateGUI = false; nodeDB->updateGUI = false;
break; break;
@@ -2629,33 +2685,23 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
{ {
if (showingNormalScreen) { if (showingNormalScreen) {
// Outgoing message setFrames(); // Regen the list of screens (will show new text message)
if (packet->from == 0)
setFrames(FOCUS_PRESERVE); // Return to same frame (quietly hiding the rx text message frame)
// Incoming message
else
setFrames(FOCUS_TEXTMESSAGE); // Focus on the new message
} }
return 0; return 0;
} }
// Triggered by MeshModules
int Screen::handleUIFrameEvent(const UIFrameEvent *event) int Screen::handleUIFrameEvent(const UIFrameEvent *event)
{ {
if (showingNormalScreen) { if (showingNormalScreen) {
// Regenerate the frameset, potentially honoring a module's internal requestFocus() call if (event->frameChanged) {
if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET) setFrames(); // Regen the list of screens (will show new text message)
setFrames(FOCUS_MODULE); } else if (event->needRedraw) {
// Regenerate the frameset, while attempting to maintain focus on the current frame
else if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND)
setFrames(FOCUS_PRESERVE);
// Don't regenerate the frameset, just re-draw whatever is on screen ASAP
else if (event->action == UIFrameEvent::Action::REDRAW_ONLY)
setFastFramerate(); setFastFramerate();
// TODO: We might also want switch to corresponding frame,
// but we don't know the exact frame number.
// ui->switchToFrame(0);
}
} }
return 0; return 0;
@@ -2690,24 +2736,6 @@ int Screen::handleInputEvent(const InputEvent *event)
return 0; return 0;
} }
int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
{
// Note: only selected admin messages notify this observer
// If you wish to handle a new type of message, you should modify AdminModule.cpp first
switch (arg->which_payload_variant) {
// Node removed manually (i.e. via app)
case meshtastic_AdminMessage_remove_by_nodenum_tag:
setFrames(FOCUS_PRESERVE);
break;
// Default no-op, in case the admin message observable gets used by other classes in future
default:
break;
}
return 0;
}
} // namespace graphics } // namespace graphics
#else #else
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {} graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}

View File

@@ -21,13 +21,11 @@ class Screen
void print(const char *) {} void print(const char *) {}
void doDeepSleep() {} void doDeepSleep() {}
void forceDisplay(bool forceUiUpdate = false) {} void forceDisplay(bool forceUiUpdate = false) {}
void startBluetoothPinScreen(uint32_t pin) {}
void stopBluetoothPinScreen() {}
void startRebootScreen() {}
void startShutdownScreen() {}
void startFirmwareUpdateScreen() {} void startFirmwareUpdateScreen() {}
void increaseBrightness() {}
void decreaseBrightness() {}
void setFunctionSymbal(std::string) {}
void removeFunctionSymbal(std::string) {}
void startAlert(const char *) {}
void endAlert() {}
}; };
} // namespace graphics } // namespace graphics
#else #else
@@ -36,8 +34,6 @@ class Screen
#include <OLEDDisplayUi.h> #include <OLEDDisplayUi.h>
#include "../configuration.h" #include "../configuration.h"
#include "gps/GeoCoord.h"
#include "graphics/ScreenFonts.h"
#ifdef USE_ST7567 #ifdef USE_ST7567
#include <ST7567Wire.h> #include <ST7567Wire.h>
@@ -45,8 +41,6 @@ class Screen
#include <SH1106Wire.h> #include <SH1106Wire.h>
#elif defined(USE_SSD1306) #elif defined(USE_SSD1306)
#include <SSD1306Wire.h> #include <SSD1306Wire.h>
#elif defined(USE_ST7789)
#include <ST7789Spi.h>
#else #else
// the SH1106/SSD1306 variant is auto-detected // the SH1106/SSD1306 variant is auto-detected
#include <AutoOLEDWire.h> #include <AutoOLEDWire.h>
@@ -88,46 +82,6 @@ class Screen
#define SEGMENT_WIDTH 16 #define SEGMENT_WIDTH 16
#define SEGMENT_HEIGHT 4 #define SEGMENT_HEIGHT 4
/// Convert an integer GPS coords to a floating point
#define DegD(i) (i * 1e-7)
namespace
{
/// A basic 2D point class for drawing
class Point
{
public:
float x, y;
Point(float _x, float _y) : x(_x), y(_y) {}
/// Apply a rotation around zero (standard rotation matrix math)
void rotate(float radian)
{
float cos = cosf(radian), sin = sinf(radian);
float rx = x * cos + y * sin, ry = -x * sin + y * cos;
x = rx;
y = ry;
}
void translate(int16_t dx, int dy)
{
x += dx;
y += dy;
}
void scale(float f)
{
// We use -f here to counter the flip that happens
// on the y axis when drawing and rotating on screen
x *= f;
y *= -f;
}
};
} // namespace
namespace graphics namespace graphics
{ {
@@ -173,11 +127,9 @@ class Screen : public concurrency::OSThread
CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver = CallbackObserver<Screen, const meshtastic_MeshPacket *> textMessageObserver =
CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage); CallbackObserver<Screen, const meshtastic_MeshPacket *>(this, &Screen::handleTextMessage);
CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver = CallbackObserver<Screen, const UIFrameEvent *> uiFrameEventObserver =
CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent); // Sent by Mesh Modules CallbackObserver<Screen, const UIFrameEvent *>(this, &Screen::handleUIFrameEvent);
CallbackObserver<Screen, const InputEvent *> inputObserver = CallbackObserver<Screen, const InputEvent *> inputObserver =
CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent); CallbackObserver<Screen, const InputEvent *>(this, &Screen::handleInputEvent);
CallbackObserver<Screen, const meshtastic_AdminMessage *> adminMessageObserver =
CallbackObserver<Screen, const meshtastic_AdminMessage *>(this, &Screen::handleAdminMessage);
public: public:
explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY); explicit Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY);
@@ -214,49 +166,20 @@ class Screen : public concurrency::OSThread
void blink(); void blink();
void drawFrameText(OLEDDisplay *, OLEDDisplayUiState *, int16_t, int16_t, const char *);
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
// Draw north
void drawCompassNorth(OLEDDisplay *display, int16_t compassX, int16_t compassY, float myHeading);
static uint16_t getCompassDiam(uint32_t displayWidth, uint32_t displayHeight);
float estimatedHeading(double lat, double lon);
void drawNodeHeading(OLEDDisplay *display, int16_t compassX, int16_t compassY, uint16_t compassDiam, float headingRadian);
void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields);
/// Handle button press, trackball or swipe action) /// Handle button press, trackball or swipe action)
void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); } void onPress() { enqueueCmd(ScreenCmd{.cmd = Cmd::ON_PRESS}); }
void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); } void showPrevFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_PREV_FRAME}); }
void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); } void showNextFrame() { enqueueCmd(ScreenCmd{.cmd = Cmd::SHOW_NEXT_FRAME}); }
// generic alert start /// Starts showing the Bluetooth PIN screen.
void startAlert(FrameCallback _alertFrame) //
{ // Switches over to a static frame showing the Bluetooth pairing screen
alertFrame = _alertFrame; // with the PIN.
ScreenCmd cmd; void startBluetoothPinScreen(uint32_t pin)
cmd.cmd = Cmd::START_ALERT_FRAME;
enqueueCmd(cmd);
}
void startAlert(const char *_alertMessage)
{
startAlert([_alertMessage](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) -> void {
uint16_t x_offset = display->width() / 2;
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM);
display->drawString(x_offset + x, 26 + y, _alertMessage);
});
}
void endAlert()
{ {
ScreenCmd cmd; ScreenCmd cmd;
cmd.cmd = Cmd::STOP_ALERT_FRAME; cmd.cmd = Cmd::START_BLUETOOTH_PIN_SCREEN;
cmd.bluetooth_pin = pin;
enqueueCmd(cmd); enqueueCmd(cmd);
} }
@@ -267,6 +190,20 @@ class Screen : public concurrency::OSThread
enqueueCmd(cmd); enqueueCmd(cmd);
} }
void startShutdownScreen()
{
ScreenCmd cmd;
cmd.cmd = Cmd::START_SHUTDOWN_SCREEN;
enqueueCmd(cmd);
}
void startRebootScreen()
{
ScreenCmd cmd;
cmd.cmd = Cmd::START_REBOOT_SCREEN;
enqueueCmd(cmd);
}
// Function to allow the AccelerometerThread to set the heading if a sensor provides it // Function to allow the AccelerometerThread to set the heading if a sensor provides it
// Mutex needed? // Mutex needed?
void setHeading(long _heading) void setHeading(long _heading)
@@ -285,6 +222,9 @@ class Screen : public concurrency::OSThread
void setFunctionSymbal(std::string sym); void setFunctionSymbal(std::string sym);
void removeFunctionSymbal(std::string sym); void removeFunctionSymbal(std::string sym);
/// Stops showing the bluetooth PIN screen.
void stopBluetoothPinScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BLUETOOTH_PIN_SCREEN}); }
/// Stops showing the boot screen. /// Stops showing the boot screen.
void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); } void stopBootScreen() { enqueueCmd(ScreenCmd{.cmd = Cmd::STOP_BOOT_SCREEN}); }
@@ -396,7 +336,6 @@ class Screen : public concurrency::OSThread
int handleTextMessage(const meshtastic_MeshPacket *arg); int handleTextMessage(const meshtastic_MeshPacket *arg);
int handleUIFrameEvent(const UIFrameEvent *arg); int handleUIFrameEvent(const UIFrameEvent *arg);
int handleInputEvent(const InputEvent *arg); int handleInputEvent(const InputEvent *arg);
int handleAdminMessage(const meshtastic_AdminMessage *arg);
/// Used to force (super slow) eink displays to draw critical frames /// Used to force (super slow) eink displays to draw critical frames
void forceDisplay(bool forceUiUpdate = false); void forceDisplay(bool forceUiUpdate = false);
@@ -419,13 +358,7 @@ class Screen : public concurrency::OSThread
bool isAUTOOled = false; bool isAUTOOled = false;
// Screen dimensions (for convenience)
// Defined during Screen::setup
uint16_t displayWidth = 0;
uint16_t displayHeight = 0;
private: private:
FrameCallback alertFrames[1];
struct ScreenCmd { struct ScreenCmd {
Cmd cmd; Cmd cmd;
union { union {
@@ -451,36 +384,13 @@ class Screen : public concurrency::OSThread
void handleOnPress(); void handleOnPress();
void handleShowNextFrame(); void handleShowNextFrame();
void handleShowPrevFrame(); void handleShowPrevFrame();
void handleStartBluetoothPinScreen(uint32_t pin);
void handlePrint(const char *text); void handlePrint(const char *text);
void handleStartFirmwareUpdateScreen(); void handleStartFirmwareUpdateScreen();
void handleShutdownScreen();
// Info collected by setFrames method. void handleRebootScreen();
// Index location of specific frames. Used to apply the FrameFocus parameter of setFrames /// Rebuilds our list of frames (screens) to default ones.
struct FramesetInfo { void setFrames();
struct FramePositions {
uint8_t fault = 0;
uint8_t textMessage = 0;
uint8_t focusedModule = 0;
uint8_t log = 0;
uint8_t settings = 0;
uint8_t wifi = 0;
} positions;
uint8_t frameCount = 0;
} framesetInfo;
// Which frame we want to be displayed, after we regen the frameset by calling setFrames
enum FrameFocus : uint8_t {
FOCUS_DEFAULT, // No specific frame
FOCUS_PRESERVE, // Return to the previous frame
FOCUS_FAULT,
FOCUS_TEXTMESSAGE,
FOCUS_MODULE, // Note: target module should call requestFocus(), otherwise no info about which module to focus
};
// Regenerate the normal set of frames, focusing a specific frame if requested
// Call when a frame should be added / removed, or custom frames should be cleared
void setFrames(FrameFocus focus = FOCUS_DEFAULT);
/// Try to start drawing ASAP /// Try to start drawing ASAP
void setFastFramerate(); void setFastFramerate();
@@ -516,9 +426,6 @@ class Screen : public concurrency::OSThread
bool digitalWatchFace = true; bool digitalWatchFace = true;
#endif #endif
/// callback for current alert frame
FrameCallback alertFrame;
/// Queue of commands to execute in doTask. /// Queue of commands to execute in doTask.
TypedQueue<ScreenCmd> cmdQueue; TypedQueue<ScreenCmd> cmdQueue;
/// Whether we are using a display /// Whether we are using a display
@@ -545,5 +452,4 @@ class Screen : public concurrency::OSThread
}; };
} // namespace graphics } // namespace graphics
#endif #endif

View File

@@ -28,8 +28,8 @@
#define FONT_LARGE ArialMT_Plain_24 // Height: 28 #define FONT_LARGE ArialMT_Plain_24 // Height: 28
#endif #endif
#define _fontHeight(font) ((font)[1] + 1) // height is position 1 #define fontHeight(font) ((font)[1] + 1) // height is position 1
#define FONT_HEIGHT_SMALL _fontHeight(FONT_SMALL) #define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
#define FONT_HEIGHT_MEDIUM _fontHeight(FONT_MEDIUM) #define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
#define FONT_HEIGHT_LARGE _fontHeight(FONT_LARGE) #define FONT_HEIGHT_LARGE fontHeight(FONT_LARGE)

View File

@@ -6,7 +6,6 @@
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "PowerMon.h"
#include "ReliableRouter.h" #include "ReliableRouter.h"
#include "airtime.h" #include "airtime.h"
#include "buzz.h" #include "buzz.h"
@@ -49,6 +48,7 @@ NimbleBluetooth *nimbleBluetooth = nullptr;
#ifdef ARCH_NRF52 #ifdef ARCH_NRF52
#include "NRF52Bluetooth.h" #include "NRF52Bluetooth.h"
NRF52Bluetooth *nrf52Bluetooth = nullptr; NRF52Bluetooth *nrf52Bluetooth = nullptr;
;
#endif #endif
#if HAS_WIFI #if HAS_WIFI
@@ -155,7 +155,6 @@ bool isVibrating = false;
bool eink_found = true; bool eink_found = true;
uint32_t serialSinceMsec; uint32_t serialSinceMsec;
bool pauseBluetoothLogging = false;
bool pmu_found; bool pmu_found;
@@ -174,7 +173,7 @@ const char *getDeviceName()
static char name[20]; static char name[20];
snprintf(name, sizeof(name), "%02x%02x", dmac[4], dmac[5]); snprintf(name, sizeof(name), "%02x%02x", dmac[4], dmac[5]);
// if the shortname exists and is NOT the new default of ab3c, use it for BLE name. // if the shortname exists and is NOT the new default of ab3c, use it for BLE name.
if (strcmp(owner.short_name, name) != 0) { if ((owner.short_name != NULL) && (strcmp(owner.short_name, name) != 0)) {
snprintf(name, sizeof(name), "%s_%02x%02x", owner.short_name, dmac[4], dmac[5]); snprintf(name, sizeof(name), "%s_%02x%02x", owner.short_name, dmac[4], dmac[5]);
} else { } else {
snprintf(name, sizeof(name), "Meshtastic_%02x%02x", dmac[4], dmac[5]); snprintf(name, sizeof(name), "Meshtastic_%02x%02x", dmac[4], dmac[5]);
@@ -215,14 +214,6 @@ __attribute__((weak, noinline)) bool loopCanSleep()
return true; return true;
} }
/**
* Print info as a structured log message (for automated log processing)
*/
void printInfo()
{
LOG_INFO("S:B:%d,%s\n", HW_VENDOR, optstr(APP_VERSION));
}
void setup() void setup()
{ {
concurrency::hasBeenSetup = true; concurrency::hasBeenSetup = true;
@@ -230,7 +221,7 @@ void setup()
meshtastic_Config_DisplayConfig_OledType::meshtastic_Config_DisplayConfig_OledType_OLED_AUTO; meshtastic_Config_DisplayConfig_OledType::meshtastic_Config_DisplayConfig_OledType_OLED_AUTO;
OLEDDISPLAY_GEOMETRY screen_geometry = GEOMETRY_128_64; OLEDDISPLAY_GEOMETRY screen_geometry = GEOMETRY_128_64;
#ifdef USE_SEGGER #ifdef SEGGER_STDOUT_CH
auto mode = false ? SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL : SEGGER_RTT_MODE_NO_BLOCK_TRIM; auto mode = false ? SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL : SEGGER_RTT_MODE_NO_BLOCK_TRIM;
#ifdef NRF52840_XXAA #ifdef NRF52840_XXAA
auto buflen = 4096; // this board has a fair amount of ram auto buflen = 4096; // this board has a fair amount of ram
@@ -243,7 +234,6 @@ void setup()
#ifdef DEBUG_PORT #ifdef DEBUG_PORT
consoleInit(); // Set serial baud rate and init our mesh console consoleInit(); // Set serial baud rate and init our mesh console
#endif #endif
powerMonInit();
serialSinceMsec = millis(); serialSinceMsec = millis();
@@ -275,9 +265,6 @@ void setup()
digitalWrite(VEXT_ENABLE_V05, 1); // turn on the lora antenna boost digitalWrite(VEXT_ENABLE_V05, 1); // turn on the lora antenna boost
digitalWrite(ST7735_BL_V05, 1); // turn on display backligth digitalWrite(ST7735_BL_V05, 1); // turn on display backligth
LOG_DEBUG("HELTEC Detect Tracker V1.1\n"); LOG_DEBUG("HELTEC Detect Tracker V1.1\n");
#elif defined(VEXT_ENABLE) && defined(VEXT_ON_VALUE)
pinMode(VEXT_ENABLE, OUTPUT);
digitalWrite(VEXT_ENABLE, VEXT_ON_VALUE); // turn on the display power
#elif defined(VEXT_ENABLE) #elif defined(VEXT_ENABLE)
pinMode(VEXT_ENABLE, OUTPUT); pinMode(VEXT_ENABLE, OUTPUT);
digitalWrite(VEXT_ENABLE, 0); // turn on the display power digitalWrite(VEXT_ENABLE, 0); // turn on the display power
@@ -566,7 +553,7 @@ void setup()
#endif #endif
// Hello // Hello
printInfo(); LOG_INFO("Meshtastic hwvendor=%d, swver=%s\n", HW_VENDOR, optstr(APP_VERSION));
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
esp32Setup(); esp32Setup();
@@ -716,8 +703,7 @@ void setup()
// Don't call screen setup until after nodedb is setup (because we need // Don't call screen setup until after nodedb is setup (because we need
// the current region name) // the current region name)
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS) || \ #if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS)
defined(USE_ST7789)
screen->setup(); screen->setup();
#elif defined(ARCH_PORTDUINO) #elif defined(ARCH_PORTDUINO)
if (screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) { if (screen_found.port != ScanI2C::I2CPort::NO_I2C || settingsMap[displayPanel]) {
@@ -944,7 +930,7 @@ void setup()
nodeDB->saveToDisk(SEGMENT_CONFIG); nodeDB->saveToDisk(SEGMENT_CONFIG);
if (!rIf->reconfigure()) { if (!rIf->reconfigure()) {
LOG_WARN("Reconfigure failed, rebooting\n"); LOG_WARN("Reconfigure failed, rebooting\n");
screen->startAlert("Rebooting..."); screen->startRebootScreen();
rebootAtMsec = millis() + 5000; rebootAtMsec = millis() + 5000;
} }
} }

View File

@@ -85,8 +85,6 @@ extern uint32_t serialSinceMsec;
// This will suppress the current delay and instead try to run ASAP. // This will suppress the current delay and instead try to run ASAP.
extern bool runASAP; extern bool runASAP;
extern bool pauseBluetoothLogging;
void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearBonds(), enterDfuMode(); void nrf52Setup(), esp32Setup(), nrf52Loop(), esp32Loop(), rp2040Setup(), clearBonds(), enterDfuMode();
meshtastic_DeviceMetadata getDeviceMetadata(); meshtastic_DeviceMetadata getDeviceMetadata();

View File

@@ -5,7 +5,6 @@
#define ONE_MINUTE_MS 60 * 1000 #define ONE_MINUTE_MS 60 * 1000
#define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60) #define default_gps_update_interval IF_ROUTER(ONE_DAY, 2 * 60)
#define default_telemetry_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 30 * 60)
#define default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 15 * 60) #define default_broadcast_interval_secs IF_ROUTER(ONE_DAY / 2, 15 * 60)
#define default_wait_bluetooth_secs IF_ROUTER(1, 60) #define default_wait_bluetooth_secs IF_ROUTER(1, 60)
#define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep #define default_sds_secs IF_ROUTER(ONE_DAY, UINT32_MAX) // Default to forever super deep sleep

View File

@@ -20,8 +20,9 @@ ErrorCode FloodingRouter::send(meshtastic_MeshPacket *p)
bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p) bool FloodingRouter::shouldFilterReceived(const meshtastic_MeshPacket *p)
{ {
if (wasSeenRecently(p)) { // Note: this will also add a recent packet record if (wasSeenRecently(p)) { // Note: this will also add a recent packet record
printPacket("Ignoring incoming msg we've already seen", p); printPacket("Ignoring incoming msg, because we've already seen it", p);
if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER && if (config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT &&
config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) { config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER) {
// cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater! // cancel rebroadcast of this message *if* there was already one, unless we're a router/repeater!
Router::cancelSending(p->from, p->id); Router::cancelSending(p->from, p->id);

View File

@@ -184,7 +184,6 @@ template <typename T> void LR11x0Interface<T>::setStandby()
activeReceiveStart = 0; activeReceiveStart = 0;
disableInterrupt(); disableInterrupt();
completeSending(); // If we were sending, not anymore completeSending(); // If we were sending, not anymore
RadioLibInterface::setStandby();
} }
/** /**
@@ -224,7 +223,7 @@ template <typename T> void LR11x0Interface<T>::startReceive()
0); // only RX_DONE IRQ is needed, we'll check for PREAMBLE_DETECTED and HEADER_VALID in isActivelyReceiving 0); // only RX_DONE IRQ is needed, we'll check for PREAMBLE_DETECTED and HEADER_VALID in isActivelyReceiving
assert(err == RADIOLIB_ERR_NONE); assert(err == RADIOLIB_ERR_NONE);
RadioLibInterface::startReceive(); isReceiving = true;
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0); enableInterrupt(isrRxLevel0);

View File

@@ -284,17 +284,4 @@ AdminMessageHandleResult MeshModule::handleAdminMessageForAllModules(const mesht
} }
} }
return handled; return handled;
} }
#if HAS_SCREEN
// Would our module like its frame to be focused after Screen::setFrames has regenerated the list of frames?
// Only considered if setFrames is triggered by a UIFrameEvent
bool MeshModule::isRequestingFocus()
{
if (_requestingFocus) {
_requestingFocus = false; // Consume the request
return true;
} else
return false;
}
#endif

View File

@@ -35,16 +35,10 @@ enum class AdminMessageHandleResult {
/* /*
* This struct is used by Screen to figure out whether screen frame should be updated. * This struct is used by Screen to figure out whether screen frame should be updated.
*/ */
struct UIFrameEvent { typedef struct _UIFrameEvent {
// What do we actually want to happen? bool frameChanged;
enum Action { bool needRedraw;
REDRAW_ONLY, // Don't change which frames are show, just redraw, asap } UIFrameEvent;
REGENERATE_FRAMESET, // Regenerate (change? add? remove?) screen frames, honoring requestFocus()
REGENERATE_FRAMESET_BACKGROUND, // Regenerate screen frames, attempting to remain on the same frame throughout
} action = REDRAW_ONLY;
// We might want to pass additional data inside this struct at some point
};
/** A baseclass for any mesh "module". /** A baseclass for any mesh "module".
* *
@@ -79,7 +73,6 @@ class MeshModule
meshtastic_AdminMessage *response); meshtastic_AdminMessage *response);
#if HAS_SCREEN #if HAS_SCREEN
virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; } virtual void drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { return; }
virtual bool isRequestingFocus(); // Checked by screen, when regenerating frameset
#endif #endif
protected: protected:
const char *name; const char *name;
@@ -183,19 +176,6 @@ class MeshModule
return AdminMessageHandleResult::NOT_HANDLED; return AdminMessageHandleResult::NOT_HANDLED;
}; };
#if HAS_SCREEN
/** Request that our module's screen frame be focused when Screen::setFrames runs
* Only considered if Screen::setFrames is triggered via a UIFrameEvent
*
* Having this as a separate call, instead of part of the UIFrameEvent, allows the module to delay decision
* until drawFrame() is called. This required less restructuring.
*/
bool _requestingFocus = false;
void requestFocus() { _requestingFocus = true; }
#else
void requestFocus(){}; // No-op
#endif
private: private:
/** /**
* If any of the current chain of modules 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

View File

@@ -94,11 +94,7 @@ int MeshService::handleFromRadio(const meshtastic_MeshPacket *mp)
} else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user && } else if (mp->which_payload_variant == meshtastic_MeshPacket_decoded_tag && !nodeDB->getMeshNode(mp->from)->has_user &&
nodeInfoModule) { nodeInfoModule) {
LOG_INFO("Heard a node on channel %d we don't know, sending NodeInfo and asking for a response.\n", mp->channel); LOG_INFO("Heard a node on channel %d we don't know, sending NodeInfo and asking for a response.\n", mp->channel);
if (airTime->isTxAllowedChannelUtil(true)) { nodeInfoModule->sendOurNodeInfo(mp->from, true, mp->channel);
nodeInfoModule->sendOurNodeInfo(mp->from, true, mp->channel);
} else {
LOG_DEBUG("Skip sending NodeInfo due to > 25 percent channel util.\n");
}
} }
printPacket("Forwarding to phone", mp); printPacket("Forwarding to phone", mp);
@@ -273,7 +269,7 @@ bool MeshService::trySendPosition(NodeNum dest, bool wantReplies)
assert(node); assert(node);
if (hasValidPosition(node)) { if (hasValidPosition(node)) {
#if HAS_GPS && !MESHTASTIC_EXCLUDE_GPS #if HAS_GPS
if (positionModule) { if (positionModule) {
LOG_INFO("Sending position ping to 0x%x, wantReplies=%d, channel=%d\n", dest, wantReplies, node->channel); LOG_INFO("Sending position ping to 0x%x, wantReplies=%d, channel=%d\n", dest, wantReplies, node->channel);
positionModule->sendOurPosition(dest, wantReplies, node->channel); positionModule->sendOurPosition(dest, wantReplies, node->channel);
@@ -303,7 +299,6 @@ void MeshService::sendToPhone(meshtastic_MeshPacket *p)
} else { } else {
LOG_WARN("ToPhone queue is full, dropping packet.\n"); LOG_WARN("ToPhone queue is full, dropping packet.\n");
releaseToPool(p); releaseToPool(p);
fromNum++; // Make sure to notify observers in case they are reconnected so they can get the packets
return; return;
} }
} }
@@ -378,8 +373,8 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
pos.time = getValidTime(RTCQualityFromNet); pos.time = getValidTime(RTCQualityFromNet);
// In debug logs, identify position by @timestamp:stage (stage 4 = nodeDB) // In debug logs, identify position by @timestamp:stage (stage 4 = nodeDB)
LOG_DEBUG("onGPSChanged() pos@%x time=%u lat=%d lon=%d alt=%d\n", pos.timestamp, pos.time, pos.latitude_i, pos.longitude_i, LOG_DEBUG("onGPSChanged() pos@%x, time=%u, lat=%d, lon=%d, alt=%d\n", pos.timestamp, pos.time, pos.latitude_i,
pos.altitude); pos.longitude_i, pos.altitude);
// Update our current position in the local DB // Update our current position in the local DB
nodeDB->updatePosition(nodeDB->getNodeNum(), pos, RX_SRC_LOCAL); nodeDB->updatePosition(nodeDB->getNodeNum(), pos, RX_SRC_LOCAL);

View File

@@ -26,7 +26,7 @@
#include <vector> #include <vector>
#ifdef ARCH_ESP32 #ifdef ARCH_ESP32
#if HAS_WIFI #if !MESHTASTIC_EXCLUDE_WIFI
#include "mesh/wifi/WiFiAPClient.h" #include "mesh/wifi/WiFiAPClient.h"
#endif #endif
#include "modules/esp32/StoreForwardModule.h" #include "modules/esp32/StoreForwardModule.h"
@@ -141,6 +141,11 @@ NodeDB::NodeDB()
if (channelFileCRC != crc32Buffer(&channelFile, sizeof(channelFile))) if (channelFileCRC != crc32Buffer(&channelFile, sizeof(channelFile)))
saveWhat |= SEGMENT_CHANNELS; saveWhat |= SEGMENT_CHANNELS;
if (!devicestate.node_remote_hardware_pins) {
meshtastic_NodeRemoteHardwarePin empty[12] = {meshtastic_RemoteHardwarePin_init_default};
memcpy(devicestate.node_remote_hardware_pins, empty, sizeof(empty));
}
if (config.position.gps_enabled) { if (config.position.gps_enabled) {
config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED;
config.position.gps_enabled = 0; config.position.gps_enabled = 0;
@@ -180,7 +185,7 @@ bool NodeDB::resetRadioConfig(bool factory_reset)
if (didFactoryReset) { if (didFactoryReset) {
LOG_INFO("Rebooting due to factory reset"); LOG_INFO("Rebooting due to factory reset");
screen->startAlert("Rebooting..."); screen->startRebootScreen();
rebootAtMsec = millis() + (5 * 1000); rebootAtMsec = millis() + (5 * 1000);
} }
@@ -268,8 +273,7 @@ void NodeDB::installDefaultConfig()
// FIXME: Default to bluetooth capability of platform as default // FIXME: Default to bluetooth capability of platform as default
config.bluetooth.enabled = true; config.bluetooth.enabled = true;
config.bluetooth.fixed_pin = defaultBLEPin; config.bluetooth.fixed_pin = defaultBLEPin;
#if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS) || \ #if defined(ST7735_CS) || defined(USE_EINK) || defined(ILI9341_DRIVER) || defined(ST7789_CS) || defined(HX8357_CS)
defined(USE_ST7789)
bool hasScreen = true; bool hasScreen = true;
#elif ARCH_PORTDUINO #elif ARCH_PORTDUINO
bool hasScreen = false; bool hasScreen = false;
@@ -822,8 +826,8 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
if (src == RX_SRC_LOCAL) { if (src == RX_SRC_LOCAL) {
// Local packet, fully authoritative // Local packet, fully authoritative
LOG_INFO("updatePosition LOCAL pos@%x time=%u lat=%d lon=%d alt=%d\n", p.timestamp, p.time, p.latitude_i, p.longitude_i, LOG_INFO("updatePosition LOCAL pos@%x, time=%u, latI=%d, lonI=%d, alt=%d\n", p.timestamp, p.time, p.latitude_i,
p.altitude); p.longitude_i, p.altitude);
setLocalPosition(p); setLocalPosition(p);
info->position = TypeConversions::ConvertToPositionLite(p); info->position = TypeConversions::ConvertToPositionLite(p);
@@ -838,7 +842,7 @@ void NodeDB::updatePosition(uint32_t nodeId, const meshtastic_Position &p, RxSou
// recorded based on the packet rxTime // recorded based on the packet rxTime
// //
// FIXME perhaps handle RX_SRC_USER separately? // FIXME perhaps handle RX_SRC_USER separately?
LOG_INFO("updatePosition REMOTE node=0x%x time=%u lat=%d lon=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i); LOG_INFO("updatePosition REMOTE node=0x%x time=%u, latI=%d, lonI=%d\n", nodeId, p.time, p.latitude_i, p.longitude_i);
// First, back up fields that we want to protect from overwrite // First, back up fields that we want to protect from overwrite
uint32_t tmp_time = info->position.time; uint32_t tmp_time = info->position.time;

View File

@@ -155,8 +155,8 @@ class NodeDB
localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time; localPosition.timestamp = position.timestamp > 0 ? position.timestamp : position.time;
return; return;
} }
LOG_DEBUG("Setting local position: lat=%i lon=%i time=%u timestamp=%u\n", position.latitude_i, position.longitude_i, LOG_DEBUG("Setting local position: latitude=%i, longitude=%i, time=%u, timestamp=%u\n", position.latitude_i,
position.time, position.timestamp); position.longitude_i, position.time, position.timestamp);
localPosition = position; localPosition = position;
} }

View File

@@ -5,7 +5,6 @@
#include "Channels.h" #include "Channels.h"
#include "Default.h" #include "Default.h"
#include "FSCommon.h"
#include "MeshService.h" #include "MeshService.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PhoneAPI.h" #include "PhoneAPI.h"
@@ -47,9 +46,6 @@ void PhoneAPI::handleStartConfig()
// even if we were already connected - restart our state machine // even if we were already connected - restart our state machine
state = STATE_SEND_MY_INFO; state = STATE_SEND_MY_INFO;
pauseBluetoothLogging = true;
filesManifest = getFiles("/", 10);
LOG_DEBUG("Got %d files in manifest\n", filesManifest.size());
LOG_INFO("Starting API client config\n"); LOG_INFO("Starting API client config\n");
nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos nodeInfoForPhone.num = 0; // Don't keep returning old nodeinfos
@@ -152,7 +148,6 @@ bool PhoneAPI::handleToRadio(const uint8_t *buf, size_t bufLength)
STATE_SEND_CONFIG, STATE_SEND_CONFIG,
STATE_SEND_MODULE_CONFIG, STATE_SEND_MODULE_CONFIG,
STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to the client STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to the client
STATE_SEND_FILEMANIFEST,
STATE_SEND_COMPLETE_ID, STATE_SEND_COMPLETE_ID,
STATE_SEND_PACKETS // send packets or debug strings STATE_SEND_PACKETS // send packets or debug strings
*/ */
@@ -328,7 +323,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
// Advance when we have sent all of our ModuleConfig objects // Advance when we have sent all of our ModuleConfig objects
if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) { if (config_state > (_meshtastic_AdminMessage_ModuleConfigType_MAX + 1)) {
// Clients sending special nonce don't want to see other nodeinfos // Clients sending special nonce don't want to see other nodeinfos
state = config_nonce == SPECIAL_NONCE ? STATE_SEND_FILEMANIFEST : STATE_SEND_OTHER_NODEINFOS; state = config_nonce == SPECIAL_NONCE ? STATE_SEND_COMPLETE_ID : STATE_SEND_OTHER_NODEINFOS;
config_state = 0; config_state = 0;
} }
break; break;
@@ -344,36 +339,22 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time nodeInfoForPhone.num = 0; // We just consumed a nodeinfo, will need a new one next time
} else { } else {
LOG_INFO("Done sending nodeinfos\n"); LOG_INFO("Done sending nodeinfos\n");
state = STATE_SEND_FILEMANIFEST; state = STATE_SEND_COMPLETE_ID;
// Go ahead and send that ID right now // Go ahead and send that ID right now
return getFromRadio(buf); return getFromRadio(buf);
} }
break; break;
} }
case STATE_SEND_FILEMANIFEST: {
LOG_INFO("getFromRadio=STATE_SEND_FILEMANIFEST\n");
// last element
if (config_state == filesManifest.size()) { // also handles an empty filesManifest
config_state = 0;
filesManifest.clear();
// Skip to complete packet
sendConfigComplete();
} else {
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_fileInfo_tag;
fromRadioScratch.fileInfo = filesManifest.at(config_state);
LOG_DEBUG("File: %s (%d) bytes\n", fromRadioScratch.fileInfo.file_name, fromRadioScratch.fileInfo.size_bytes);
config_state++;
}
break;
}
case STATE_SEND_COMPLETE_ID: case STATE_SEND_COMPLETE_ID:
sendConfigComplete(); LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n");
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag;
fromRadioScratch.config_complete_id = config_nonce;
config_nonce = 0;
state = STATE_SEND_PACKETS;
break; break;
case STATE_SEND_PACKETS: case STATE_SEND_PACKETS:
pauseBluetoothLogging = false;
// Do we have a message from the mesh or packet from the local device? // Do we have a message from the mesh or packet from the local device?
LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n"); LOG_INFO("getFromRadio=STATE_SEND_PACKETS\n");
if (queueStatusPacketForPhone) { if (queueStatusPacketForPhone) {
@@ -407,9 +388,7 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
// Encapsulate as a FromRadio packet // Encapsulate as a FromRadio packet
size_t numbytes = pb_encode_to_bytes(buf, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch); size_t numbytes = pb_encode_to_bytes(buf, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch);
// VERY IMPORTANT to not print debug messages while writing to fromRadioScratch - because we use that same buffer LOG_DEBUG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payload_variant, numbytes);
// for logging (when we are encapsulating with protobufs)
// LOG_DEBUG("encoding toPhone packet to phone variant=%d, %d bytes\n", fromRadioScratch.which_payload_variant, numbytes);
return numbytes; return numbytes;
} }
@@ -417,20 +396,8 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
return 0; return 0;
} }
void PhoneAPI::sendConfigComplete()
{
LOG_INFO("getFromRadio=STATE_SEND_COMPLETE_ID\n");
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_config_complete_id_tag;
fromRadioScratch.config_complete_id = config_nonce;
config_nonce = 0;
state = STATE_SEND_PACKETS;
pauseBluetoothLogging = false;
}
void PhoneAPI::handleDisconnect() void PhoneAPI::handleDisconnect()
{ {
filesManifest.clear();
pauseBluetoothLogging = false;
LOG_INFO("PhoneAPI disconnect\n"); LOG_INFO("PhoneAPI disconnect\n");
} }
@@ -472,7 +439,6 @@ bool PhoneAPI::available()
case STATE_SEND_MODULECONFIG: case STATE_SEND_MODULECONFIG:
case STATE_SEND_METADATA: case STATE_SEND_METADATA:
case STATE_SEND_OWN_NODEINFO: case STATE_SEND_OWN_NODEINFO:
case STATE_SEND_FILEMANIFEST:
case STATE_SEND_COMPLETE_ID: case STATE_SEND_COMPLETE_ID:
return true; return true;
@@ -487,6 +453,7 @@ bool PhoneAPI::available()
} }
} }
return true; // Always say we have something, because we might need to advance our state machine return true; // Always say we have something, because we might need to advance our state machine
case STATE_SEND_PACKETS: { case STATE_SEND_PACKETS: {
if (!queueStatusPacketForPhone) if (!queueStatusPacketForPhone)
queueStatusPacketForPhone = service.getQueueStatusForPhone(); queueStatusPacketForPhone = service.getQueueStatusForPhone();

View File

@@ -2,20 +2,10 @@
#include "Observer.h" #include "Observer.h"
#include "mesh-pb-constants.h" #include "mesh-pb-constants.h"
#include <iterator>
#include <string> #include <string>
#include <vector>
// Make sure that we never let our packets grow too large for one BLE packet // Make sure that we never let our packets grow too large for one BLE packet
#define MAX_TO_FROM_RADIO_SIZE 512 #define MAX_TO_FROM_RADIO_SIZE 512
#if meshtastic_FromRadio_size > MAX_TO_FROM_RADIO_SIZE
#error "meshtastic_FromRadio_size is too large for our BLE packets"
#endif
#if meshtastic_ToRadio_size > MAX_TO_FROM_RADIO_SIZE
#error "meshtastic_ToRadio_size is too large for our BLE packets"
#endif
#define SPECIAL_NONCE 69420 #define SPECIAL_NONCE 69420
/** /**
@@ -39,7 +29,6 @@ class PhoneAPI
STATE_SEND_CONFIG, // Replacement for the old Radioconfig STATE_SEND_CONFIG, // Replacement for the old Radioconfig
STATE_SEND_MODULECONFIG, // Send Module specific config STATE_SEND_MODULECONFIG, // Send Module specific config
STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to to the client STATE_SEND_OTHER_NODEINFOS, // states progress in this order as the device sends to to the client
STATE_SEND_FILEMANIFEST, // Send file manifest
STATE_SEND_COMPLETE_ID, STATE_SEND_COMPLETE_ID,
STATE_SEND_PACKETS // send packets or debug strings STATE_SEND_PACKETS // send packets or debug strings
}; };
@@ -76,8 +65,6 @@ class PhoneAPI
uint32_t config_nonce = 0; uint32_t config_nonce = 0;
uint32_t readIndex = 0; uint32_t readIndex = 0;
std::vector<meshtastic_FileInfo> filesManifest = {};
void resetReadIndex() { readIndex = 0; } void resetReadIndex() { readIndex = 0; }
public: public:
@@ -104,8 +91,6 @@ class PhoneAPI
*/ */
size_t getFromRadio(uint8_t *buf); size_t getFromRadio(uint8_t *buf);
void sendConfigComplete();
/** /**
* Return true if we have data available to send to the phone * Return true if we have data available to send to the phone
*/ */
@@ -113,6 +98,8 @@ class PhoneAPI
bool isConnected() { return state != STATE_SEND_NOTHING; } bool isConnected() { return state != STATE_SEND_NOTHING; }
void setInitialState() { state = STATE_SEND_MY_INFO; }
protected: protected:
/// Our fromradio packet while it is being assembled /// Our fromradio packet while it is being assembled
meshtastic_FromRadio fromRadioScratch = {}; meshtastic_FromRadio fromRadioScratch = {};

View File

@@ -25,8 +25,7 @@ typedef struct {
} DACDB; } DACDB;
// Interpolation function // Interpolation function
DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val2) DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val2) {
{
DACDB result; DACDB result;
double fraction = (double)(dbm - dbm1) / (dbm2 - dbm1); double fraction = (double)(dbm - dbm1) / (dbm2 - dbm1);
result.dac = (uint8_t)(val1.dac + fraction * (val2.dac - val1.dac)); result.dac = (uint8_t)(val1.dac + fraction * (val2.dac - val1.dac));
@@ -35,17 +34,16 @@ DACDB interpolate(uint8_t dbm, uint8_t dbm1, uint8_t dbm2, DACDB val1, DACDB val
} }
// Function to find the correct DAC and DB values based on dBm using interpolation // Function to find the correct DAC and DB values based on dBm using interpolation
DACDB getDACandDB(uint8_t dbm) DACDB getDACandDB(uint8_t dbm) {
{
// Predefined values // Predefined values
static const struct { static const struct {
uint8_t dbm; uint8_t dbm;
DACDB values; DACDB values;
} dbmToDACDB[] = { } dbmToDACDB[] = {
{20, {168, 2}}, // 100mW {20, {168, 2}}, // 100mW
{24, {148, 6}}, // 250mW {24, {148, 6}}, // 250mW
{27, {128, 9}}, // 500mW {27, {128, 9}}, // 500mW
{30, {90, 12}} // 1000mW {30, {90, 12}} // 1000mW
}; };
const int numValues = sizeof(dbmToDACDB) / sizeof(dbmToDACDB[0]); const int numValues = sizeof(dbmToDACDB) / sizeof(dbmToDACDB[0]);
@@ -105,7 +103,7 @@ bool RF95Interface::init()
if (power > RF95_MAX_POWER) // This chip has lower power limits than some if (power > RF95_MAX_POWER) // This chip has lower power limits than some
power = RF95_MAX_POWER; power = RF95_MAX_POWER;
limitPower(); limitPower();
iface = lora = new RadioLibRF95(&module); iface = lora = new RadioLibRF95(&module);
@@ -118,13 +116,13 @@ bool RF95Interface::init()
// enable PA // enable PA
#ifdef RF95_PA_EN #ifdef RF95_PA_EN
#if defined(RF95_PA_DAC_EN) #if defined(RF95_PA_DAC_EN)
#ifdef RADIOMASTER_900_BANDIT_NANO #ifdef RADIOMASTER_900_BANDIT_NANO
// Use calculated DAC value // Use calculated DAC value
dacWrite(RF95_PA_EN, powerDAC); dacWrite(RF95_PA_EN, powerDAC);
#else #else
// Use Value set in /*/variant.h // Use Value set in /*/variant.h
dacWrite(RF95_PA_EN, RF95_PA_LEVEL); dacWrite(RF95_PA_EN, RF95_PA_LEVEL);
#endif #endif
#endif #endif
#endif #endif
@@ -256,7 +254,6 @@ void RF95Interface::setStandby()
isReceiving = false; // If we were receiving, not any more isReceiving = false; // If we were receiving, not any more
disableInterrupt(); disableInterrupt();
completeSending(); // If we were sending, not anymore completeSending(); // If we were sending, not anymore
RadioLibInterface::setStandby();
} }
/** We override to turn on transmitter power as needed. /** We override to turn on transmitter power as needed.

View File

@@ -261,6 +261,7 @@ uint32_t RadioInterface::getTxDelayMsecWeighted(float snr)
uint8_t CWsize = map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax); uint8_t CWsize = map(snr, SNR_MIN, SNR_MAX, CWmin, CWmax);
// LOG_DEBUG("rx_snr of %f so setting CWsize to:%d\n", snr, CWsize); // LOG_DEBUG("rx_snr of %f so setting CWsize to:%d\n", snr, CWsize);
if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER || if (config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER ||
config.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT ||
config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) { config.device.role == meshtastic_Config_DeviceConfig_Role_REPEATER) {
delay = random(0, 2 * CWsize) * slotTimeMsec; delay = random(0, 2 * CWsize) * slotTimeMsec;
LOG_DEBUG("rx_snr found in packet. As a router, setting tx delay:%d\n", delay); LOG_DEBUG("rx_snr found in packet. As a router, setting tx delay:%d\n", delay);
@@ -521,7 +522,7 @@ void RadioInterface::applyModemConfig()
LOG_INFO("Radio freq=%.3f, config.lora.frequency_offset=%.3f\n", freq, loraConfig.frequency_offset); LOG_INFO("Radio freq=%.3f, config.lora.frequency_offset=%.3f\n", freq, loraConfig.frequency_offset);
LOG_INFO("Set radio: region=%s, name=%s, config=%u, ch=%d, power=%d\n", myRegion->name, channelName, loraConfig.modem_preset, LOG_INFO("Set radio: region=%s, name=%s, config=%u, ch=%d, power=%d\n", myRegion->name, channelName, loraConfig.modem_preset,
channel_num, power); channel_num, power);
LOG_INFO("Radio myRegion->freqStart -> myRegion->freqEnd: %f -> %f (%f MHz)\n", myRegion->freqStart, myRegion->freqEnd, LOG_INFO("Radio myRegion->freqStart -> myRegion->freqEnd: %f -> %f (%f mhz)\n", myRegion->freqStart, myRegion->freqEnd,
myRegion->freqEnd - myRegion->freqStart); myRegion->freqEnd - myRegion->freqStart);
LOG_INFO("Radio myRegion->numChannels: %d x %.3fkHz\n", numChannels, bw); LOG_INFO("Radio myRegion->numChannels: %d x %.3fkHz\n", numChannels, bw);
LOG_INFO("Radio channel_num: %d\n", channel_num + 1); LOG_INFO("Radio channel_num: %d\n", channel_num + 1);

View File

@@ -1,7 +1,6 @@
#include "RadioLibInterface.h" #include "RadioLibInterface.h"
#include "MeshTypes.h" #include "MeshTypes.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "PowerMon.h"
#include "SPILock.h" #include "SPILock.h"
#include "configuration.h" #include "configuration.h"
#include "error.h" #include "error.h"
@@ -318,7 +317,6 @@ void RadioLibInterface::handleTransmitInterrupt()
// ignore the transmit interrupt // ignore the transmit interrupt
if (sendingPacket) if (sendingPacket)
completeSending(); completeSending();
powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // But our transmitter is deffinitely off now
} }
void RadioLibInterface::completeSending() void RadioLibInterface::completeSending()
@@ -414,24 +412,6 @@ void RadioLibInterface::handleReceiveInterrupt()
} }
} }
void RadioLibInterface::startReceive()
{
isReceiving = true;
powerMon->setState(meshtastic_PowerMon_State_Lora_RXOn);
}
void RadioLibInterface::configHardwareForSend()
{
powerMon->setState(meshtastic_PowerMon_State_Lora_TXOn);
}
void RadioLibInterface::setStandby()
{
// neither sending nor receiving
powerMon->clearState(meshtastic_PowerMon_State_Lora_RXOn);
powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn);
}
/** start an immediate transmit */ /** start an immediate transmit */
void RadioLibInterface::startSend(meshtastic_MeshPacket *txp) void RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
{ {
@@ -451,7 +431,6 @@ void RadioLibInterface::startSend(meshtastic_MeshPacket *txp)
// This send failed, but make sure to 'complete' it properly // This send failed, but make sure to 'complete' it properly
completeSending(); completeSending();
powerMon->clearState(meshtastic_PowerMon_State_Lora_TXOn); // Transmitter off now
startReceive(); // Restart receive mode (because startTransmit failed to put us in xmit mode) startReceive(); // Restart receive mode (because startTransmit failed to put us in xmit mode)
} }

View File

@@ -126,9 +126,8 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
* Start waiting to receive a message * Start waiting to receive a message
* *
* External functions can call this method to wake the device from sleep. * External functions can call this method to wake the device from sleep.
* Subclasses must override and call this base method
*/ */
virtual void startReceive(); virtual void startReceive() = 0;
/** can we detect a LoRa preamble on the current channel? */ /** can we detect a LoRa preamble on the current channel? */
virtual bool isChannelActive() = 0; virtual bool isChannelActive() = 0;
@@ -167,9 +166,8 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
meshtastic_QueueStatus getQueueStatus(); meshtastic_QueueStatus getQueueStatus();
protected: protected:
/** Do any hardware setup needed on entry into send configuration for the radio. /** Do any hardware setup needed on entry into send configuration for the radio. Subclasses can customize */
* Subclasses can customize, but must also call this base method */ virtual void configHardwareForSend() {}
virtual void configHardwareForSend();
/** Could we send right now (i.e. either not actively receiving or transmitting)? */ /** Could we send right now (i.e. either not actively receiving or transmitting)? */
virtual bool canSendImmediately(); virtual bool canSendImmediately();
@@ -188,8 +186,5 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/ */
virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) = 0; virtual void addReceiveMetadata(meshtastic_MeshPacket *mp) = 0;
/** virtual void setStandby() = 0;
* Subclasses must override, implement and then call into this base class implementation
*/
virtual void setStandby();
}; };

View File

@@ -244,10 +244,8 @@ ErrorCode Router::send(meshtastic_MeshPacket *p)
// If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it) // If the packet hasn't yet been encrypted, do so now (it might already be encrypted if we are just forwarding it)
if (!(p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag || assert(p->which_payload_variant == meshtastic_MeshPacket_encrypted_tag ||
p->which_payload_variant == meshtastic_MeshPacket_decoded_tag)) { p->which_payload_variant == meshtastic_MeshPacket_decoded_tag); // I _think_ all packets should have a payload by now
return meshtastic_Routing_Error_BAD_REQUEST;
}
// If the packet is not yet encrypted, do so now // If the packet is not yet encrypted, do so now
if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) { if (p->which_payload_variant == meshtastic_MeshPacket_decoded_tag) {

View File

@@ -231,7 +231,6 @@ template <typename T> void SX126xInterface<T>::setStandby()
activeReceiveStart = 0; activeReceiveStart = 0;
disableInterrupt(); disableInterrupt();
completeSending(); // If we were sending, not anymore completeSending(); // If we were sending, not anymore
RadioLibInterface::setStandby();
} }
/** /**
@@ -271,7 +270,7 @@ template <typename T> void SX126xInterface<T>::startReceive()
LOG_ERROR("Radiolib error %d when attempting SX126X startReceiveDutyCycleAuto!\n", err); LOG_ERROR("Radiolib error %d when attempting SX126X startReceiveDutyCycleAuto!\n", err);
assert(err == RADIOLIB_ERR_NONE); assert(err == RADIOLIB_ERR_NONE);
RadioLibInterface::startReceive(); isReceiving = true;
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0); enableInterrupt(isrRxLevel0);

View File

@@ -190,7 +190,6 @@ template <typename T> void SX128xInterface<T>::setStandby()
activeReceiveStart = 0; activeReceiveStart = 0;
disableInterrupt(); disableInterrupt();
completeSending(); // If we were sending, not anymore completeSending(); // If we were sending, not anymore
RadioLibInterface::setStandby();
} }
/** /**
@@ -264,7 +263,7 @@ template <typename T> void SX128xInterface<T>::startReceive()
LOG_ERROR("Radiolib error %d when attempting SX128X startReceive!\n", err); LOG_ERROR("Radiolib error %d when attempting SX128X startReceive!\n", err);
assert(err == RADIOLIB_ERR_NONE); assert(err == RADIOLIB_ERR_NONE);
RadioLibInterface::startReceive(); isReceiving = true;
// Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits // Must be done AFTER, starting transmit, because startTransmit clears (possibly stale) interrupt pending register bits
enableInterrupt(isrRxLevel0); enableInterrupt(isrRxLevel0);

View File

@@ -1,6 +1,5 @@
#include "StreamAPI.h" #include "StreamAPI.h"
#include "PowerFSM.h" #include "PowerFSM.h"
#include "RTC.h"
#include "configuration.h" #include "configuration.h"
#define START1 0x94 #define START1 0x94
@@ -97,6 +96,7 @@ void StreamAPI::writeStream()
void StreamAPI::emitTxBuffer(size_t len) void StreamAPI::emitTxBuffer(size_t len)
{ {
if (len != 0) { if (len != 0) {
// LOG_DEBUG("emit tx %d\n", len);
txBuf[0] = START1; txBuf[0] = START1;
txBuf[1] = START2; txBuf[1] = START2;
txBuf[2] = (len >> 8) & 0xff; txBuf[2] = (len >> 8) & 0xff;
@@ -119,25 +119,6 @@ void StreamAPI::emitRebooted()
emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch)); emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch));
} }
void StreamAPI::emitLogRecord(meshtastic_LogRecord_Level level, const char *src, const char *format, va_list arg)
{
// In case we send a FromRadio packet
memset(&fromRadioScratch, 0, sizeof(fromRadioScratch));
fromRadioScratch.which_payload_variant = meshtastic_FromRadio_log_record_tag;
fromRadioScratch.log_record.level = level;
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true);
fromRadioScratch.log_record.time = rtc_sec;
strncpy(fromRadioScratch.log_record.source, src, sizeof(fromRadioScratch.log_record.source) - 1);
auto num_printed =
vsnprintf(fromRadioScratch.log_record.message, sizeof(fromRadioScratch.log_record.message) - 1, format, arg);
if (num_printed > 0 && fromRadioScratch.log_record.message[num_printed - 1] ==
'\n') // Strip any ending newline, because we have records for framing instead.
fromRadioScratch.log_record.message[num_printed - 1] = '\0';
emitTxBuffer(pb_encode_to_bytes(txBuf + HEADER_LEN, meshtastic_FromRadio_size, &meshtastic_FromRadio_msg, &fromRadioScratch));
}
/// Hookable to find out when connection changes /// Hookable to find out when connection changes
void StreamAPI::onConnectionChanged(bool connected) void StreamAPI::onConnectionChanged(bool connected)
{ {
@@ -150,4 +131,4 @@ void StreamAPI::onConnectionChanged(bool connected)
// received a packet in a while // received a packet in a while
powerFSM.trigger(EVENT_SERIAL_DISCONNECTED); powerFSM.trigger(EVENT_SERIAL_DISCONNECTED);
} }
} }

View File

@@ -82,7 +82,4 @@ class StreamAPI : public PhoneAPI
/// Subclasses can use this scratch buffer if they wish /// Subclasses can use this scratch buffer if they wish
uint8_t txBuf[MAX_STREAM_BUF_SIZE] = {0}; uint8_t txBuf[MAX_STREAM_BUF_SIZE] = {0};
};
/// Low level function to emit a protobuf encapsulated log record
void emitLogRecord(meshtastic_LogRecord_Level level, const char *src, const char *format, va_list arg);
};

View File

@@ -12,8 +12,6 @@
#include <RAK13800_W5100S.h> #include <RAK13800_W5100S.h>
#include <SPI.h> #include <SPI.h>
#if HAS_NETWORKING
#ifndef DISABLE_NTP #ifndef DISABLE_NTP
#include <NTPClient.h> #include <NTPClient.h>
@@ -185,5 +183,3 @@ bool isEthernetAvailable()
return true; return true;
} }
} }
#endif

View File

@@ -55,7 +55,7 @@ extern const pb_msgdesc_t meshtastic_ChannelSet_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_APPONLY_PB_H_MAX_SIZE meshtastic_ChannelSet_size #define MESHTASTIC_MESHTASTIC_APPONLY_PB_H_MAX_SIZE meshtastic_ChannelSet_size
#define meshtastic_ChannelSet_size 676 #define meshtastic_ChannelSet_size 674
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -22,6 +22,7 @@ typedef enum _meshtastic_Config_DeviceConfig_Role {
The wifi radio and the oled screen will be put to sleep. The wifi radio and the oled screen will be put to sleep.
This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. */ This mode may still potentially have higher power usage due to it's preference in message rebroadcasting on the mesh. */
meshtastic_Config_DeviceConfig_Role_ROUTER = 2, meshtastic_Config_DeviceConfig_Role_ROUTER = 2,
/* Description: Combination of both ROUTER and CLIENT. Not for mobile devices. */
meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT = 3, meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT = 3,
/* Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list. /* Description: Infrastructure node for extending network coverage by relaying messages with minimal overhead. Not visible in Nodes list.
Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry Technical Details: Mesh packets will simply be rebroadcasted over this node. Nodes configured with this role will not originate NodeInfo, Position, Telemetry
@@ -371,9 +372,6 @@ typedef struct _meshtastic_Config_PowerConfig {
uint32_t min_wake_secs; uint32_t min_wake_secs;
/* I2C address of INA_2XX to use for reading device battery voltage */ /* I2C address of INA_2XX to use for reading device battery voltage */
uint8_t device_battery_ina_address; uint8_t device_battery_ina_address;
/* If non-zero, we want powermon log outputs. With the particular (bitfield) sources enabled.
Note: we picked an ID of 32 so that lower more efficient IDs can be used for more frequently used options. */
uint64_t powermon_enables;
} meshtastic_Config_PowerConfig; } meshtastic_Config_PowerConfig;
typedef struct _meshtastic_Config_NetworkConfig_IpV4Config { typedef struct _meshtastic_Config_NetworkConfig_IpV4Config {
@@ -497,8 +495,6 @@ typedef struct _meshtastic_Config_LoRaConfig {
Please respect your local laws and regulations. If you are a HAM, make sure you Please respect your local laws and regulations. If you are a HAM, make sure you
enable HAM mode and turn off encryption. */ enable HAM mode and turn off encryption. */
float override_frequency; float override_frequency;
/* If true, disable the build-in PA FAN using pin define in RF95_FAN_EN. */
bool pa_fan_disabled;
/* For testing it is useful sometimes to force a node to never listen to /* For testing it is useful sometimes to force a node to never listen to
particular other nodes (simulating radio out of range). All nodenums listed particular other nodes (simulating radio out of range). All nodenums listed
in ignore_incoming will have packets they send dropped on receive (by router.cpp) */ in ignore_incoming will have packets they send dropped on receive (by router.cpp) */
@@ -616,20 +612,20 @@ extern "C" {
#define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}} #define meshtastic_Config_init_default {0, {meshtastic_Config_DeviceConfig_init_default}}
#define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_DeviceConfig_init_default {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
#define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PositionConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN}
#define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_PowerConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""} #define meshtastic_Config_NetworkConfig_init_default {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_default, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_IpV4Config_init_default {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_DisplayConfig_init_default {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
#define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_LoRaConfig_init_default {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
#define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} #define meshtastic_Config_BluetoothConfig_init_default {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0}
#define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}} #define meshtastic_Config_init_zero {0, {meshtastic_Config_DeviceConfig_init_zero}}
#define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0} #define meshtastic_Config_DeviceConfig_init_zero {_meshtastic_Config_DeviceConfig_Role_MIN, 0, 0, 0, 0, _meshtastic_Config_DeviceConfig_RebroadcastMode_MIN, 0, 0, 0, 0, "", 0}
#define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN} #define meshtastic_Config_PositionConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, _meshtastic_Config_PositionConfig_GpsMode_MIN}
#define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_Config_PowerConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""} #define meshtastic_Config_NetworkConfig_init_zero {0, "", "", "", 0, _meshtastic_Config_NetworkConfig_AddressMode_MIN, false, meshtastic_Config_NetworkConfig_IpV4Config_init_zero, ""}
#define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0} #define meshtastic_Config_NetworkConfig_IpV4Config_init_zero {0, 0, 0, 0}
#define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN} #define meshtastic_Config_DisplayConfig_init_zero {0, _meshtastic_Config_DisplayConfig_GpsCoordinateFormat_MIN, 0, 0, 0, _meshtastic_Config_DisplayConfig_DisplayUnits_MIN, _meshtastic_Config_DisplayConfig_OledType_MIN, _meshtastic_Config_DisplayConfig_DisplayMode_MIN, 0, 0, _meshtastic_Config_DisplayConfig_CompassOrientation_MIN}
#define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0} #define meshtastic_Config_LoRaConfig_init_zero {0, _meshtastic_Config_LoRaConfig_ModemPreset_MIN, 0, 0, 0, 0, _meshtastic_Config_LoRaConfig_RegionCode_MIN, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0}, 0}
#define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0} #define meshtastic_Config_BluetoothConfig_init_zero {0, _meshtastic_Config_BluetoothConfig_PairingMode_MIN, 0, 0}
/* Field tags (for use in manual encoding/decoding) */ /* Field tags (for use in manual encoding/decoding) */
@@ -666,7 +662,6 @@ extern "C" {
#define meshtastic_Config_PowerConfig_ls_secs_tag 7 #define meshtastic_Config_PowerConfig_ls_secs_tag 7
#define meshtastic_Config_PowerConfig_min_wake_secs_tag 8 #define meshtastic_Config_PowerConfig_min_wake_secs_tag 8
#define meshtastic_Config_PowerConfig_device_battery_ina_address_tag 9 #define meshtastic_Config_PowerConfig_device_battery_ina_address_tag 9
#define meshtastic_Config_PowerConfig_powermon_enables_tag 32
#define meshtastic_Config_NetworkConfig_IpV4Config_ip_tag 1 #define meshtastic_Config_NetworkConfig_IpV4Config_ip_tag 1
#define meshtastic_Config_NetworkConfig_IpV4Config_gateway_tag 2 #define meshtastic_Config_NetworkConfig_IpV4Config_gateway_tag 2
#define meshtastic_Config_NetworkConfig_IpV4Config_subnet_tag 3 #define meshtastic_Config_NetworkConfig_IpV4Config_subnet_tag 3
@@ -704,7 +699,6 @@ extern "C" {
#define meshtastic_Config_LoRaConfig_override_duty_cycle_tag 12 #define meshtastic_Config_LoRaConfig_override_duty_cycle_tag 12
#define meshtastic_Config_LoRaConfig_sx126x_rx_boosted_gain_tag 13 #define meshtastic_Config_LoRaConfig_sx126x_rx_boosted_gain_tag 13
#define meshtastic_Config_LoRaConfig_override_frequency_tag 14 #define meshtastic_Config_LoRaConfig_override_frequency_tag 14
#define meshtastic_Config_LoRaConfig_pa_fan_disabled_tag 15
#define meshtastic_Config_LoRaConfig_ignore_incoming_tag 103 #define meshtastic_Config_LoRaConfig_ignore_incoming_tag 103
#define meshtastic_Config_LoRaConfig_ignore_mqtt_tag 104 #define meshtastic_Config_LoRaConfig_ignore_mqtt_tag 104
#define meshtastic_Config_BluetoothConfig_enabled_tag 1 #define meshtastic_Config_BluetoothConfig_enabled_tag 1
@@ -779,8 +773,7 @@ X(a, STATIC, SINGULAR, UINT32, wait_bluetooth_secs, 4) \
X(a, STATIC, SINGULAR, UINT32, sds_secs, 6) \ X(a, STATIC, SINGULAR, UINT32, sds_secs, 6) \
X(a, STATIC, SINGULAR, UINT32, ls_secs, 7) \ X(a, STATIC, SINGULAR, UINT32, ls_secs, 7) \
X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 8) \ X(a, STATIC, SINGULAR, UINT32, min_wake_secs, 8) \
X(a, STATIC, SINGULAR, UINT32, device_battery_ina_address, 9) \ X(a, STATIC, SINGULAR, UINT32, device_battery_ina_address, 9)
X(a, STATIC, SINGULAR, UINT64, powermon_enables, 32)
#define meshtastic_Config_PowerConfig_CALLBACK NULL #define meshtastic_Config_PowerConfig_CALLBACK NULL
#define meshtastic_Config_PowerConfig_DEFAULT NULL #define meshtastic_Config_PowerConfig_DEFAULT NULL
@@ -835,7 +828,6 @@ X(a, STATIC, SINGULAR, UINT32, channel_num, 11) \
X(a, STATIC, SINGULAR, BOOL, override_duty_cycle, 12) \ X(a, STATIC, SINGULAR, BOOL, override_duty_cycle, 12) \
X(a, STATIC, SINGULAR, BOOL, sx126x_rx_boosted_gain, 13) \ X(a, STATIC, SINGULAR, BOOL, sx126x_rx_boosted_gain, 13) \
X(a, STATIC, SINGULAR, FLOAT, override_frequency, 14) \ X(a, STATIC, SINGULAR, FLOAT, override_frequency, 14) \
X(a, STATIC, SINGULAR, BOOL, pa_fan_disabled, 15) \
X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \ X(a, STATIC, REPEATED, UINT32, ignore_incoming, 103) \
X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104) X(a, STATIC, SINGULAR, BOOL, ignore_mqtt, 104)
#define meshtastic_Config_LoRaConfig_CALLBACK NULL #define meshtastic_Config_LoRaConfig_CALLBACK NULL
@@ -875,11 +867,11 @@ extern const pb_msgdesc_t meshtastic_Config_BluetoothConfig_msg;
#define meshtastic_Config_BluetoothConfig_size 12 #define meshtastic_Config_BluetoothConfig_size 12
#define meshtastic_Config_DeviceConfig_size 100 #define meshtastic_Config_DeviceConfig_size 100
#define meshtastic_Config_DisplayConfig_size 30 #define meshtastic_Config_DisplayConfig_size 30
#define meshtastic_Config_LoRaConfig_size 82 #define meshtastic_Config_LoRaConfig_size 80
#define meshtastic_Config_NetworkConfig_IpV4Config_size 20 #define meshtastic_Config_NetworkConfig_IpV4Config_size 20
#define meshtastic_Config_NetworkConfig_size 196 #define meshtastic_Config_NetworkConfig_size 196
#define meshtastic_Config_PositionConfig_size 62 #define meshtastic_Config_PositionConfig_size 62
#define meshtastic_Config_PowerConfig_size 52 #define meshtastic_Config_PowerConfig_size 40
#define meshtastic_Config_size 199 #define meshtastic_Config_size 199
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -8,6 +8,7 @@
#include "meshtastic/channel.pb.h" #include "meshtastic/channel.pb.h"
#include "meshtastic/localonly.pb.h" #include "meshtastic/localonly.pb.h"
#include "meshtastic/mesh.pb.h" #include "meshtastic/mesh.pb.h"
#include "meshtastic/module_config.pb.h"
#include "meshtastic/telemetry.pb.h" #include "meshtastic/telemetry.pb.h"
#if PB_PROTO_HEADER_VERSION != 40 #if PB_PROTO_HEADER_VERSION != 40
@@ -307,7 +308,7 @@ extern const pb_msgdesc_t meshtastic_OEMStore_msg;
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_OEMStore_size
#define meshtastic_ChannelFile_size 718 #define meshtastic_ChannelFile_size 718
#define meshtastic_NodeInfoLite_size 166 #define meshtastic_NodeInfoLite_size 166
#define meshtastic_OEMStore_size 3388 #define meshtastic_OEMStore_size 3372
#define meshtastic_PositionLite_size 28 #define meshtastic_PositionLite_size 28
#ifdef __cplusplus #ifdef __cplusplus

View File

@@ -181,8 +181,8 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size
#define meshtastic_LocalConfig_size 555 #define meshtastic_LocalConfig_size 541
#define meshtastic_LocalModuleConfig_size 687 #define meshtastic_LocalModuleConfig_size 685
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -36,7 +36,7 @@ PB_BIND(meshtastic_NodeInfo, meshtastic_NodeInfo, AUTO)
PB_BIND(meshtastic_MyNodeInfo, meshtastic_MyNodeInfo, AUTO) PB_BIND(meshtastic_MyNodeInfo, meshtastic_MyNodeInfo, AUTO)
PB_BIND(meshtastic_LogRecord, meshtastic_LogRecord, 2) PB_BIND(meshtastic_LogRecord, meshtastic_LogRecord, AUTO)
PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO) PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
@@ -45,9 +45,6 @@ PB_BIND(meshtastic_QueueStatus, meshtastic_QueueStatus, AUTO)
PB_BIND(meshtastic_FromRadio, meshtastic_FromRadio, 2) PB_BIND(meshtastic_FromRadio, meshtastic_FromRadio, 2)
PB_BIND(meshtastic_FileInfo, meshtastic_FileInfo, AUTO)
PB_BIND(meshtastic_ToRadio, meshtastic_ToRadio, 2) PB_BIND(meshtastic_ToRadio, meshtastic_ToRadio, 2)

View File

@@ -167,15 +167,6 @@ typedef enum _meshtastic_HardwareModel {
meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO = 64, meshtastic_HardwareModel_RADIOMASTER_900_BANDIT_NANO = 64,
/* Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors */ /* Heltec Capsule Sensor V3 with ESP32-S3 CPU, Portable LoRa device that can replace GNSS modules or sensors */
meshtastic_HardwareModel_HELTEC_CAPSULE_SENSOR_V3 = 65, meshtastic_HardwareModel_HELTEC_CAPSULE_SENSOR_V3 = 65,
/* Heltec Vision Master T190 with ESP32-S3 CPU, and a 1.90 inch TFT display */
meshtastic_HardwareModel_HELTEC_VISION_MASTER_T190 = 66,
/* Heltec Vision Master E213 with ESP32-S3 CPU, and a 2.13 inch E-Ink display */
meshtastic_HardwareModel_HELTEC_VISION_MASTER_E213 = 67,
/* Heltec Vision Master E290 with ESP32-S3 CPU, and a 2.9 inch E-Ink display */
meshtastic_HardwareModel_HELTEC_VISION_MASTER_E290 = 68,
/* Heltec Mesh Node T114 board with nRF52840 CPU, and a 1.14 inch TFT display, Ultimate low-power design,
specifically adapted for the Meshtatic project */
meshtastic_HardwareModel_HELTEC_MESH_NODE_T114 = 69,
/* ------------------------------------------------------------------------------------------------------------------------------------------ /* ------------------------------------------------------------------------------------------------------------------------------------------
Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits. Reserved ID For developing private Ports. These will show up in live traffic sparsely, so we can use a high number. Keep it within 8 bits.
------------------------------------------------------------------------------------------------------------------------------------------ */ ------------------------------------------------------------------------------------------------------------------------------------------ */
@@ -700,11 +691,11 @@ typedef struct _meshtastic_MyNodeInfo {
and then extend as needed by emitting multiple records. */ and then extend as needed by emitting multiple records. */
typedef struct _meshtastic_LogRecord { typedef struct _meshtastic_LogRecord {
/* Log levels, chosen to match python logging conventions. */ /* Log levels, chosen to match python logging conventions. */
char message[384]; char message[64];
/* Seconds since 1970 - or 0 for unknown/unset */ /* Seconds since 1970 - or 0 for unknown/unset */
uint32_t time; uint32_t time;
/* Usually based on thread name - if known */ /* Usually based on thread name - if known */
char source[32]; char source[8];
/* Not yet set */ /* Not yet set */
meshtastic_LogRecord_Level level; meshtastic_LogRecord_Level level;
} meshtastic_LogRecord; } meshtastic_LogRecord;
@@ -720,14 +711,6 @@ typedef struct _meshtastic_QueueStatus {
uint32_t mesh_packet_id; uint32_t mesh_packet_id;
} meshtastic_QueueStatus; } meshtastic_QueueStatus;
/* Individual File info for the device */
typedef struct _meshtastic_FileInfo {
/* The fully qualified path of the file */
char file_name[228];
/* The size of the file in bytes */
uint32_t size_bytes;
} meshtastic_FileInfo;
typedef PB_BYTES_ARRAY_T(237) meshtastic_Compressed_data_t; typedef PB_BYTES_ARRAY_T(237) meshtastic_Compressed_data_t;
/* Compressed message payload */ /* Compressed message payload */
typedef struct _meshtastic_Compressed { typedef struct _meshtastic_Compressed {
@@ -832,8 +815,6 @@ typedef struct _meshtastic_FromRadio {
meshtastic_DeviceMetadata metadata; meshtastic_DeviceMetadata metadata;
/* MQTT Client Proxy Message (device sending to client / phone for publishing to MQTT) */ /* MQTT Client Proxy Message (device sending to client / phone for publishing to MQTT) */
meshtastic_MqttClientProxyMessage mqttClientProxyMessage; meshtastic_MqttClientProxyMessage mqttClientProxyMessage;
/* File system manifest messages */
meshtastic_FileInfo fileInfo;
}; };
} meshtastic_FromRadio; } meshtastic_FromRadio;
@@ -977,7 +958,6 @@ extern "C" {
#define meshtastic_Compressed_portnum_ENUMTYPE meshtastic_PortNum #define meshtastic_Compressed_portnum_ENUMTYPE meshtastic_PortNum
@@ -1005,7 +985,6 @@ extern "C" {
#define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_LogRecord_init_default {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_default {0, 0, 0, 0} #define meshtastic_QueueStatus_init_default {0, 0, 0, 0}
#define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}} #define meshtastic_FromRadio_init_default {0, 0, {meshtastic_MeshPacket_init_default}}
#define meshtastic_FileInfo_init_default {"", 0}
#define meshtastic_ToRadio_init_default {0, {meshtastic_MeshPacket_init_default}} #define meshtastic_ToRadio_init_default {0, {meshtastic_MeshPacket_init_default}}
#define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}} #define meshtastic_Compressed_init_default {_meshtastic_PortNum_MIN, {0, {0}}}
#define meshtastic_NeighborInfo_init_default {0, 0, 0, 0, {meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default}} #define meshtastic_NeighborInfo_init_default {0, 0, 0, 0, {meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default, meshtastic_Neighbor_init_default}}
@@ -1029,7 +1008,6 @@ extern "C" {
#define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN} #define meshtastic_LogRecord_init_zero {"", 0, "", _meshtastic_LogRecord_Level_MIN}
#define meshtastic_QueueStatus_init_zero {0, 0, 0, 0} #define meshtastic_QueueStatus_init_zero {0, 0, 0, 0}
#define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}} #define meshtastic_FromRadio_init_zero {0, 0, {meshtastic_MeshPacket_init_zero}}
#define meshtastic_FileInfo_init_zero {"", 0}
#define meshtastic_ToRadio_init_zero {0, {meshtastic_MeshPacket_init_zero}} #define meshtastic_ToRadio_init_zero {0, {meshtastic_MeshPacket_init_zero}}
#define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}} #define meshtastic_Compressed_init_zero {_meshtastic_PortNum_MIN, {0, {0}}}
#define meshtastic_NeighborInfo_init_zero {0, 0, 0, 0, {meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero}} #define meshtastic_NeighborInfo_init_zero {0, 0, 0, 0, {meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero, meshtastic_Neighbor_init_zero}}
@@ -1132,8 +1110,6 @@ extern "C" {
#define meshtastic_QueueStatus_free_tag 2 #define meshtastic_QueueStatus_free_tag 2
#define meshtastic_QueueStatus_maxlen_tag 3 #define meshtastic_QueueStatus_maxlen_tag 3
#define meshtastic_QueueStatus_mesh_packet_id_tag 4 #define meshtastic_QueueStatus_mesh_packet_id_tag 4
#define meshtastic_FileInfo_file_name_tag 1
#define meshtastic_FileInfo_size_bytes_tag 2
#define meshtastic_Compressed_portnum_tag 1 #define meshtastic_Compressed_portnum_tag 1
#define meshtastic_Compressed_data_tag 2 #define meshtastic_Compressed_data_tag 2
#define meshtastic_Neighbor_node_id_tag 1 #define meshtastic_Neighbor_node_id_tag 1
@@ -1168,7 +1144,6 @@ extern "C" {
#define meshtastic_FromRadio_xmodemPacket_tag 12 #define meshtastic_FromRadio_xmodemPacket_tag 12
#define meshtastic_FromRadio_metadata_tag 13 #define meshtastic_FromRadio_metadata_tag 13
#define meshtastic_FromRadio_mqttClientProxyMessage_tag 14 #define meshtastic_FromRadio_mqttClientProxyMessage_tag 14
#define meshtastic_FromRadio_fileInfo_tag 15
#define meshtastic_ToRadio_packet_tag 1 #define meshtastic_ToRadio_packet_tag 1
#define meshtastic_ToRadio_want_config_id_tag 3 #define meshtastic_ToRadio_want_config_id_tag 3
#define meshtastic_ToRadio_disconnect_tag 4 #define meshtastic_ToRadio_disconnect_tag 4
@@ -1346,8 +1321,7 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,channel,channel), 10) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 11) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,queueStatus,queueStatus), 11) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 12) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,xmodemPacket,xmodemPacket), 12) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,metadata,metadata), 13) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,mqttClientProxyMessage,mqttClientProxyMessage), 14)
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15)
#define meshtastic_FromRadio_CALLBACK NULL #define meshtastic_FromRadio_CALLBACK NULL
#define meshtastic_FromRadio_DEFAULT NULL #define meshtastic_FromRadio_DEFAULT NULL
#define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket #define meshtastic_FromRadio_payload_variant_packet_MSGTYPE meshtastic_MeshPacket
@@ -1361,13 +1335,6 @@ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,fileInfo,fileInfo), 15)
#define meshtastic_FromRadio_payload_variant_xmodemPacket_MSGTYPE meshtastic_XModem #define meshtastic_FromRadio_payload_variant_xmodemPacket_MSGTYPE meshtastic_XModem
#define meshtastic_FromRadio_payload_variant_metadata_MSGTYPE meshtastic_DeviceMetadata #define meshtastic_FromRadio_payload_variant_metadata_MSGTYPE meshtastic_DeviceMetadata
#define meshtastic_FromRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage #define meshtastic_FromRadio_payload_variant_mqttClientProxyMessage_MSGTYPE meshtastic_MqttClientProxyMessage
#define meshtastic_FromRadio_payload_variant_fileInfo_MSGTYPE meshtastic_FileInfo
#define meshtastic_FileInfo_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, STRING, file_name, 1) \
X(a, STATIC, SINGULAR, UINT32, size_bytes, 2)
#define meshtastic_FileInfo_CALLBACK NULL
#define meshtastic_FileInfo_DEFAULT NULL
#define meshtastic_ToRadio_FIELDLIST(X, a) \ #define meshtastic_ToRadio_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 1) \ X(a, STATIC, ONEOF, MESSAGE, (payload_variant,packet,packet), 1) \
@@ -1467,7 +1434,6 @@ extern const pb_msgdesc_t meshtastic_MyNodeInfo_msg;
extern const pb_msgdesc_t meshtastic_LogRecord_msg; extern const pb_msgdesc_t meshtastic_LogRecord_msg;
extern const pb_msgdesc_t meshtastic_QueueStatus_msg; extern const pb_msgdesc_t meshtastic_QueueStatus_msg;
extern const pb_msgdesc_t meshtastic_FromRadio_msg; extern const pb_msgdesc_t meshtastic_FromRadio_msg;
extern const pb_msgdesc_t meshtastic_FileInfo_msg;
extern const pb_msgdesc_t meshtastic_ToRadio_msg; extern const pb_msgdesc_t meshtastic_ToRadio_msg;
extern const pb_msgdesc_t meshtastic_Compressed_msg; extern const pb_msgdesc_t meshtastic_Compressed_msg;
extern const pb_msgdesc_t meshtastic_NeighborInfo_msg; extern const pb_msgdesc_t meshtastic_NeighborInfo_msg;
@@ -1493,7 +1459,6 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_LogRecord_fields &meshtastic_LogRecord_msg #define meshtastic_LogRecord_fields &meshtastic_LogRecord_msg
#define meshtastic_QueueStatus_fields &meshtastic_QueueStatus_msg #define meshtastic_QueueStatus_fields &meshtastic_QueueStatus_msg
#define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg #define meshtastic_FromRadio_fields &meshtastic_FromRadio_msg
#define meshtastic_FileInfo_fields &meshtastic_FileInfo_msg
#define meshtastic_ToRadio_fields &meshtastic_ToRadio_msg #define meshtastic_ToRadio_fields &meshtastic_ToRadio_msg
#define meshtastic_Compressed_fields &meshtastic_Compressed_msg #define meshtastic_Compressed_fields &meshtastic_Compressed_msg
#define meshtastic_NeighborInfo_fields &meshtastic_NeighborInfo_msg #define meshtastic_NeighborInfo_fields &meshtastic_NeighborInfo_msg
@@ -1513,10 +1478,9 @@ extern const pb_msgdesc_t meshtastic_ChunkedPayloadResponse_msg;
#define meshtastic_Compressed_size 243 #define meshtastic_Compressed_size 243
#define meshtastic_Data_size 270 #define meshtastic_Data_size 270
#define meshtastic_DeviceMetadata_size 46 #define meshtastic_DeviceMetadata_size 46
#define meshtastic_FileInfo_size 236
#define meshtastic_FromRadio_size 510 #define meshtastic_FromRadio_size 510
#define meshtastic_Heartbeat_size 0 #define meshtastic_Heartbeat_size 0
#define meshtastic_LogRecord_size 426 #define meshtastic_LogRecord_size 81
#define meshtastic_MeshPacket_size 326 #define meshtastic_MeshPacket_size 326
#define meshtastic_MqttClientProxyMessage_size 501 #define meshtastic_MqttClientProxyMessage_size 501
#define meshtastic_MyNodeInfo_size 18 #define meshtastic_MyNodeInfo_size 18

View File

@@ -60,9 +60,7 @@ typedef enum _meshtastic_ModuleConfig_SerialConfig_Serial_Mode {
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG = 3, meshtastic_ModuleConfig_SerialConfig_Serial_Mode_TEXTMSG = 3,
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA = 4, meshtastic_ModuleConfig_SerialConfig_Serial_Mode_NMEA = 4,
/* NMEA messages specifically tailored for CalTopo */ /* NMEA messages specifically tailored for CalTopo */
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO = 5, meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO = 5
/* Ecowitt WS85 weather station */
meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85 = 6
} meshtastic_ModuleConfig_SerialConfig_Serial_Mode; } meshtastic_ModuleConfig_SerialConfig_Serial_Mode;
/* TODO: REPLACE */ /* TODO: REPLACE */
@@ -275,8 +273,6 @@ typedef struct _meshtastic_ModuleConfig_StoreForwardConfig {
uint32_t history_return_max; uint32_t history_return_max;
/* TODO: REPLACE */ /* TODO: REPLACE */
uint32_t history_return_window; uint32_t history_return_window;
/* Set to true to let this node act as a server that stores received messages and resends them upon request. */
bool is_server;
} meshtastic_ModuleConfig_StoreForwardConfig; } meshtastic_ModuleConfig_StoreForwardConfig;
/* Preferences for the RangeTestModule */ /* Preferences for the RangeTestModule */
@@ -436,8 +432,8 @@ extern "C" {
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Baud)(meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600+1)) #define _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Baud)(meshtastic_ModuleConfig_SerialConfig_Serial_Baud_BAUD_921600+1))
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT #define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN meshtastic_ModuleConfig_SerialConfig_Serial_Mode_DEFAULT
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MAX meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85 #define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MAX meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO
#define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Mode)(meshtastic_ModuleConfig_SerialConfig_Serial_Mode_WS85+1)) #define _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_ARRAYSIZE ((meshtastic_ModuleConfig_SerialConfig_Serial_Mode)(meshtastic_ModuleConfig_SerialConfig_Serial_Mode_CALTOPO+1))
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE #define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE
#define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MAX meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK #define _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MAX meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_BACK
@@ -478,7 +474,7 @@ extern "C" {
#define meshtastic_ModuleConfig_PaxcounterConfig_init_default {0, 0, 0, 0} #define meshtastic_ModuleConfig_PaxcounterConfig_init_default {0, 0, 0, 0}
#define meshtastic_ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0} #define meshtastic_ModuleConfig_SerialConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0}
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_ExternalNotificationConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_StoreForwardConfig_init_default {0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_RangeTestConfig_init_default {0, 0, 0} #define meshtastic_ModuleConfig_RangeTestConfig_init_default {0, 0, 0}
#define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_TelemetryConfig_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0} #define meshtastic_ModuleConfig_CannedMessageConfig_init_default {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
@@ -494,7 +490,7 @@ extern "C" {
#define meshtastic_ModuleConfig_PaxcounterConfig_init_zero {0, 0, 0, 0} #define meshtastic_ModuleConfig_PaxcounterConfig_init_zero {0, 0, 0, 0}
#define meshtastic_ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0} #define meshtastic_ModuleConfig_SerialConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Baud_MIN, 0, _meshtastic_ModuleConfig_SerialConfig_Serial_Mode_MIN, 0}
#define meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_StoreForwardConfig_init_zero {0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_RangeTestConfig_init_zero {0, 0, 0} #define meshtastic_ModuleConfig_RangeTestConfig_init_zero {0, 0, 0}
#define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_ModuleConfig_TelemetryConfig_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0} #define meshtastic_ModuleConfig_CannedMessageConfig_init_zero {0, 0, 0, 0, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, _meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_MIN, 0, 0, "", 0}
@@ -564,7 +560,6 @@ extern "C" {
#define meshtastic_ModuleConfig_StoreForwardConfig_records_tag 3 #define meshtastic_ModuleConfig_StoreForwardConfig_records_tag 3
#define meshtastic_ModuleConfig_StoreForwardConfig_history_return_max_tag 4 #define meshtastic_ModuleConfig_StoreForwardConfig_history_return_max_tag 4
#define meshtastic_ModuleConfig_StoreForwardConfig_history_return_window_tag 5 #define meshtastic_ModuleConfig_StoreForwardConfig_history_return_window_tag 5
#define meshtastic_ModuleConfig_StoreForwardConfig_is_server_tag 6
#define meshtastic_ModuleConfig_RangeTestConfig_enabled_tag 1 #define meshtastic_ModuleConfig_RangeTestConfig_enabled_tag 1
#define meshtastic_ModuleConfig_RangeTestConfig_sender_tag 2 #define meshtastic_ModuleConfig_RangeTestConfig_sender_tag 2
#define meshtastic_ModuleConfig_RangeTestConfig_save_tag 3 #define meshtastic_ModuleConfig_RangeTestConfig_save_tag 3
@@ -748,8 +743,7 @@ X(a, STATIC, SINGULAR, BOOL, enabled, 1) \
X(a, STATIC, SINGULAR, BOOL, heartbeat, 2) \ X(a, STATIC, SINGULAR, BOOL, heartbeat, 2) \
X(a, STATIC, SINGULAR, UINT32, records, 3) \ X(a, STATIC, SINGULAR, UINT32, records, 3) \
X(a, STATIC, SINGULAR, UINT32, history_return_max, 4) \ X(a, STATIC, SINGULAR, UINT32, history_return_max, 4) \
X(a, STATIC, SINGULAR, UINT32, history_return_window, 5) \ X(a, STATIC, SINGULAR, UINT32, history_return_window, 5)
X(a, STATIC, SINGULAR, BOOL, is_server, 6)
#define meshtastic_ModuleConfig_StoreForwardConfig_CALLBACK NULL #define meshtastic_ModuleConfig_StoreForwardConfig_CALLBACK NULL
#define meshtastic_ModuleConfig_StoreForwardConfig_DEFAULT NULL #define meshtastic_ModuleConfig_StoreForwardConfig_DEFAULT NULL
@@ -854,7 +848,7 @@ extern const pb_msgdesc_t meshtastic_RemoteHardwarePin_msg;
#define meshtastic_ModuleConfig_RangeTestConfig_size 10 #define meshtastic_ModuleConfig_RangeTestConfig_size 10
#define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96 #define meshtastic_ModuleConfig_RemoteHardwareConfig_size 96
#define meshtastic_ModuleConfig_SerialConfig_size 28 #define meshtastic_ModuleConfig_SerialConfig_size 28
#define meshtastic_ModuleConfig_StoreForwardConfig_size 24 #define meshtastic_ModuleConfig_StoreForwardConfig_size 22
#define meshtastic_ModuleConfig_TelemetryConfig_size 36 #define meshtastic_ModuleConfig_TelemetryConfig_size 36
#define meshtastic_ModuleConfig_size 257 #define meshtastic_ModuleConfig_size 257
#define meshtastic_RemoteHardwarePin_size 21 #define meshtastic_RemoteHardwarePin_size 21

View File

@@ -124,8 +124,6 @@ typedef enum _meshtastic_PortNum {
meshtastic_PortNum_ATAK_PLUGIN = 72, meshtastic_PortNum_ATAK_PLUGIN = 72,
/* Provides unencrypted information about a node for consumption by a map via MQTT */ /* Provides unencrypted information about a node for consumption by a map via MQTT */
meshtastic_PortNum_MAP_REPORT_APP = 73, meshtastic_PortNum_MAP_REPORT_APP = 73,
/* PowerStress based monitoring support (for automated power consumption testing) */
meshtastic_PortNum_POWERSTRESS_APP = 74,
/* Private applications should use portnums >= 256. /* Private applications should use portnums >= 256.
To simplify initial development and testing you can use "PRIVATE_APP" To simplify initial development and testing you can use "PRIVATE_APP"
in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) */ in your code without needing to rebuild protobuf files (via [regen-protos.sh](https://github.com/meshtastic/firmware/blob/master/bin/regen-protos.sh)) */

View File

@@ -1,17 +0,0 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.8 */
#include "meshtastic/powermon.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(meshtastic_PowerMon, meshtastic_PowerMon, AUTO)
PB_BIND(meshtastic_PowerStressMessage, meshtastic_PowerStressMessage, AUTO)

View File

@@ -1,138 +0,0 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.8 */
#ifndef PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
#define PB_MESHTASTIC_MESHTASTIC_POWERMON_PB_H_INCLUDED
#include <pb.h>
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Enum definitions */
/* Any significant power changing event in meshtastic should be tagged with a powermon state transition.
If you are making new meshtastic features feel free to add new entries at the end of this definition. */
typedef enum _meshtastic_PowerMon_State {
meshtastic_PowerMon_State_None = 0,
meshtastic_PowerMon_State_CPU_DeepSleep = 1,
meshtastic_PowerMon_State_CPU_LightSleep = 2,
/* The external Vext1 power is on. Many boards have auxillary power rails that the CPU turns on only
occasionally. In cases where that rail has multiple devices on it we usually want to have logging on
the state of that rail as an independent record.
For instance on the Heltec Tracker 1.1 board, this rail is the power source for the GPS and screen.
The log messages will be short and complete (see PowerMon.Event in the protobufs for details).
something like "S:PM:C,0x00001234,REASON" where the hex number is the bitmask of all current states.
(We use a bitmask for states so that if a log message gets lost it won't be fatal) */
meshtastic_PowerMon_State_Vext1_On = 4,
meshtastic_PowerMon_State_Lora_RXOn = 8,
meshtastic_PowerMon_State_Lora_TXOn = 16,
meshtastic_PowerMon_State_Lora_RXActive = 32,
meshtastic_PowerMon_State_BT_On = 64,
meshtastic_PowerMon_State_LED_On = 128,
meshtastic_PowerMon_State_Screen_On = 256,
meshtastic_PowerMon_State_Screen_Drawing = 512,
meshtastic_PowerMon_State_Wifi_On = 1024,
/* GPS is actively trying to find our location
See GPSPowerState for more details */
meshtastic_PowerMon_State_GPS_Active = 2048
} meshtastic_PowerMon_State;
/* What operation would we like the UUT to perform.
note: senders should probably set want_response in their request packets, so that they can know when the state
machine has started processing their request */
typedef enum _meshtastic_PowerStressMessage_Opcode {
/* Unset/unused */
meshtastic_PowerStressMessage_Opcode_UNSET = 0,
meshtastic_PowerStressMessage_Opcode_PRINT_INFO = 1, /* Print board version slog and send an ack that we are alive and ready to process commands */
meshtastic_PowerStressMessage_Opcode_FORCE_QUIET = 2, /* Try to turn off all automatic processing of packets, screen, sleeping, etc (to make it easier to measure in isolation) */
meshtastic_PowerStressMessage_Opcode_END_QUIET = 3, /* Stop powerstress processing - probably by just rebooting the board */
meshtastic_PowerStressMessage_Opcode_SCREEN_ON = 16, /* Turn the screen on */
meshtastic_PowerStressMessage_Opcode_SCREEN_OFF = 17, /* Turn the screen off */
meshtastic_PowerStressMessage_Opcode_CPU_IDLE = 32, /* Let the CPU run but we assume mostly idling for num_seconds */
meshtastic_PowerStressMessage_Opcode_CPU_DEEPSLEEP = 33, /* Force deep sleep for FIXME seconds */
meshtastic_PowerStressMessage_Opcode_CPU_FULLON = 34, /* Spin the CPU as fast as possible for num_seconds */
meshtastic_PowerStressMessage_Opcode_LED_ON = 48, /* Turn the LED on for num_seconds (and leave it on - for baseline power measurement purposes) */
meshtastic_PowerStressMessage_Opcode_LED_OFF = 49, /* Force the LED off for num_seconds */
meshtastic_PowerStressMessage_Opcode_LORA_OFF = 64, /* Completely turn off the LORA radio for num_seconds */
meshtastic_PowerStressMessage_Opcode_LORA_TX = 65, /* Send Lora packets for num_seconds */
meshtastic_PowerStressMessage_Opcode_LORA_RX = 66, /* Receive Lora packets for num_seconds (node will be mostly just listening, unless an external agent is helping stress this by sending packets on the current channel) */
meshtastic_PowerStressMessage_Opcode_BT_OFF = 80, /* Turn off the BT radio for num_seconds */
meshtastic_PowerStressMessage_Opcode_BT_ON = 81, /* Turn on the BT radio for num_seconds */
meshtastic_PowerStressMessage_Opcode_WIFI_OFF = 96, /* Turn off the WIFI radio for num_seconds */
meshtastic_PowerStressMessage_Opcode_WIFI_ON = 97, /* Turn on the WIFI radio for num_seconds */
meshtastic_PowerStressMessage_Opcode_GPS_OFF = 112, /* Turn off the GPS radio for num_seconds */
meshtastic_PowerStressMessage_Opcode_GPS_ON = 113 /* Turn on the GPS radio for num_seconds */
} meshtastic_PowerStressMessage_Opcode;
/* Struct definitions */
/* Note: There are no 'PowerMon' messages normally in use (PowerMons are sent only as structured logs - slogs).
But we wrap our State enum in this message to effectively nest a namespace (without our linter yelling at us) */
typedef struct _meshtastic_PowerMon {
char dummy_field;
} meshtastic_PowerMon;
/* PowerStress testing support via the C++ PowerStress module */
typedef struct _meshtastic_PowerStressMessage {
/* What type of HardwareMessage is this? */
meshtastic_PowerStressMessage_Opcode cmd;
float num_seconds;
} meshtastic_PowerStressMessage;
#ifdef __cplusplus
extern "C" {
#endif
/* Helper constants for enums */
#define _meshtastic_PowerMon_State_MIN meshtastic_PowerMon_State_None
#define _meshtastic_PowerMon_State_MAX meshtastic_PowerMon_State_GPS_Active
#define _meshtastic_PowerMon_State_ARRAYSIZE ((meshtastic_PowerMon_State)(meshtastic_PowerMon_State_GPS_Active+1))
#define _meshtastic_PowerStressMessage_Opcode_MIN meshtastic_PowerStressMessage_Opcode_UNSET
#define _meshtastic_PowerStressMessage_Opcode_MAX meshtastic_PowerStressMessage_Opcode_GPS_ON
#define _meshtastic_PowerStressMessage_Opcode_ARRAYSIZE ((meshtastic_PowerStressMessage_Opcode)(meshtastic_PowerStressMessage_Opcode_GPS_ON+1))
#define meshtastic_PowerStressMessage_cmd_ENUMTYPE meshtastic_PowerStressMessage_Opcode
/* Initializer values for message structs */
#define meshtastic_PowerMon_init_default {0}
#define meshtastic_PowerStressMessage_init_default {_meshtastic_PowerStressMessage_Opcode_MIN, 0}
#define meshtastic_PowerMon_init_zero {0}
#define meshtastic_PowerStressMessage_init_zero {_meshtastic_PowerStressMessage_Opcode_MIN, 0}
/* Field tags (for use in manual encoding/decoding) */
#define meshtastic_PowerStressMessage_cmd_tag 1
#define meshtastic_PowerStressMessage_num_seconds_tag 2
/* Struct field encoding specification for nanopb */
#define meshtastic_PowerMon_FIELDLIST(X, a) \
#define meshtastic_PowerMon_CALLBACK NULL
#define meshtastic_PowerMon_DEFAULT NULL
#define meshtastic_PowerStressMessage_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, cmd, 1) \
X(a, STATIC, SINGULAR, FLOAT, num_seconds, 2)
#define meshtastic_PowerStressMessage_CALLBACK NULL
#define meshtastic_PowerStressMessage_DEFAULT NULL
extern const pb_msgdesc_t meshtastic_PowerMon_msg;
extern const pb_msgdesc_t meshtastic_PowerStressMessage_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define meshtastic_PowerMon_fields &meshtastic_PowerMon_msg
#define meshtastic_PowerStressMessage_fields &meshtastic_PowerStressMessage_msg
/* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_POWERMON_PB_H_MAX_SIZE meshtastic_PowerStressMessage_size
#define meshtastic_PowerMon_size 0
#define meshtastic_PowerStressMessage_size 7
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -115,10 +115,6 @@ typedef struct _meshtastic_EnvironmentMetrics {
float wind_speed; float wind_speed;
/* Weight in KG */ /* Weight in KG */
float weight; float weight;
/* Wind gust in m/s */
float wind_gust;
/* Wind lull in m/s */
float wind_lull;
} meshtastic_EnvironmentMetrics; } meshtastic_EnvironmentMetrics;
/* Power Metrics (voltage / current / etc) */ /* Power Metrics (voltage / current / etc) */
@@ -209,13 +205,13 @@ extern "C" {
/* Initializer values for message structs */ /* Initializer values for message structs */
#define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0} #define meshtastic_DeviceMetrics_init_default {0, 0, 0, 0, 0}
#define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_EnvironmentMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0} #define meshtastic_PowerMetrics_init_default {0, 0, 0, 0, 0, 0}
#define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_AirQualityMetrics_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
#define meshtastic_Nau7802Config_init_default {0, 0} #define meshtastic_Nau7802Config_init_default {0, 0}
#define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0} #define meshtastic_DeviceMetrics_init_zero {0, 0, 0, 0, 0}
#define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_EnvironmentMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0} #define meshtastic_PowerMetrics_init_zero {0, 0, 0, 0, 0, 0}
#define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_AirQualityMetrics_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}} #define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}}
@@ -242,8 +238,6 @@ extern "C" {
#define meshtastic_EnvironmentMetrics_wind_direction_tag 13 #define meshtastic_EnvironmentMetrics_wind_direction_tag 13
#define meshtastic_EnvironmentMetrics_wind_speed_tag 14 #define meshtastic_EnvironmentMetrics_wind_speed_tag 14
#define meshtastic_EnvironmentMetrics_weight_tag 15 #define meshtastic_EnvironmentMetrics_weight_tag 15
#define meshtastic_EnvironmentMetrics_wind_gust_tag 16
#define meshtastic_EnvironmentMetrics_wind_lull_tag 17
#define meshtastic_PowerMetrics_ch1_voltage_tag 1 #define meshtastic_PowerMetrics_ch1_voltage_tag 1
#define meshtastic_PowerMetrics_ch1_current_tag 2 #define meshtastic_PowerMetrics_ch1_current_tag 2
#define meshtastic_PowerMetrics_ch2_voltage_tag 3 #define meshtastic_PowerMetrics_ch2_voltage_tag 3
@@ -295,9 +289,7 @@ X(a, STATIC, SINGULAR, FLOAT, ir_lux, 11) \
X(a, STATIC, SINGULAR, FLOAT, uv_lux, 12) \ X(a, STATIC, SINGULAR, FLOAT, uv_lux, 12) \
X(a, STATIC, SINGULAR, UINT32, wind_direction, 13) \ X(a, STATIC, SINGULAR, UINT32, wind_direction, 13) \
X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14) \ X(a, STATIC, SINGULAR, FLOAT, wind_speed, 14) \
X(a, STATIC, SINGULAR, FLOAT, weight, 15) \ X(a, STATIC, SINGULAR, FLOAT, weight, 15)
X(a, STATIC, SINGULAR, FLOAT, wind_gust, 16) \
X(a, STATIC, SINGULAR, FLOAT, wind_lull, 17)
#define meshtastic_EnvironmentMetrics_CALLBACK NULL #define meshtastic_EnvironmentMetrics_CALLBACK NULL
#define meshtastic_EnvironmentMetrics_DEFAULT NULL #define meshtastic_EnvironmentMetrics_DEFAULT NULL
@@ -365,10 +357,10 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
#define MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_MAX_SIZE meshtastic_Telemetry_size #define MESHTASTIC_MESHTASTIC_TELEMETRY_PB_H_MAX_SIZE meshtastic_Telemetry_size
#define meshtastic_AirQualityMetrics_size 72 #define meshtastic_AirQualityMetrics_size 72
#define meshtastic_DeviceMetrics_size 27 #define meshtastic_DeviceMetrics_size 27
#define meshtastic_EnvironmentMetrics_size 85 #define meshtastic_EnvironmentMetrics_size 73
#define meshtastic_Nau7802Config_size 16 #define meshtastic_Nau7802Config_size 16
#define meshtastic_PowerMetrics_size 30 #define meshtastic_PowerMetrics_size 30
#define meshtastic_Telemetry_size 92 #define meshtastic_Telemetry_size 80
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -6,7 +6,7 @@
#include "main.h" #include "main.h"
#include "mesh/http/ContentHelper.h" #include "mesh/http/ContentHelper.h"
#include "mesh/http/WebServer.h" #include "mesh/http/WebServer.h"
#if HAS_WIFI #if !MESHTASTIC_EXCLUDE_WIFI
#include "mesh/wifi/WiFiAPClient.h" #include "mesh/wifi/WiFiAPClient.h"
#endif #endif
#include "mqtt/JSON.h" #include "mqtt/JSON.h"

View File

@@ -1,5 +1,5 @@
#include "configuration.h" #include "configuration.h"
#if HAS_WIFI #if !MESHTASTIC_EXCLUDE_WIFI
#include "NodeDB.h" #include "NodeDB.h"
#include "RTC.h" #include "RTC.h"
#include "concurrency/Periodic.h" #include "concurrency/Periodic.h"

View File

@@ -137,7 +137,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
#if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH #if defined(ARCH_ESP32) && !MESHTASTIC_EXCLUDE_BLUETOOTH
if (BleOta::getOtaAppVersion().isEmpty()) { if (BleOta::getOtaAppVersion().isEmpty()) {
LOG_INFO("No OTA firmware available, scheduling regular reboot in %d seconds\n", s); LOG_INFO("No OTA firmware available, scheduling regular reboot in %d seconds\n", s);
screen->startAlert("Rebooting..."); screen->startRebootScreen();
} else { } else {
screen->startFirmwareUpdateScreen(); screen->startFirmwareUpdateScreen();
BleOta::switchToOtaApp(); BleOta::switchToOtaApp();
@@ -145,7 +145,7 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
} }
#else #else
LOG_INFO("Not on ESP32, scheduling regular reboot in %d seconds\n", s); LOG_INFO("Not on ESP32, scheduling regular reboot in %d seconds\n", s);
screen->startAlert("Rebooting..."); screen->startRebootScreen();
#endif #endif
rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000); rebootAtMsec = (s < 0) ? 0 : (millis() + s * 1000);
break; break;
@@ -200,7 +200,6 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
case meshtastic_AdminMessage_remove_by_nodenum_tag: { case meshtastic_AdminMessage_remove_by_nodenum_tag: {
LOG_INFO("Client is receiving a remove_nodenum command.\n"); LOG_INFO("Client is receiving a remove_nodenum command.\n");
nodeDB->removeNodeByNum(r->remove_by_nodenum); nodeDB->removeNodeByNum(r->remove_by_nodenum);
this->notifyObservers(r); // Observed by screen
break; break;
} }
case meshtastic_AdminMessage_set_favorite_node_tag: { case meshtastic_AdminMessage_set_favorite_node_tag: {
@@ -233,9 +232,9 @@ bool AdminModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshta
#if !MESHTASTIC_EXCLUDE_GPS #if !MESHTASTIC_EXCLUDE_GPS
if (gps != nullptr) if (gps != nullptr)
gps->enable(); gps->enable();
#endif
// Send our new fixed position to the mesh for good measure // Send our new fixed position to the mesh for good measure
positionModule->sendOurPosition(); positionModule->sendOurPosition();
#endif
} }
break; break;
} }
@@ -300,8 +299,8 @@ void AdminModule::handleGetModuleConfigResponse(const meshtastic_MeshPacket &mp,
{ {
// Skip if it's disabled or no pins are exposed // Skip if it's disabled or no pins are exposed
if (!r->get_module_config_response.payload_variant.remote_hardware.enabled || if (!r->get_module_config_response.payload_variant.remote_hardware.enabled ||
r->get_module_config_response.payload_variant.remote_hardware.available_pins_count == 0) { !r->get_module_config_response.payload_variant.remote_hardware.available_pins) {
LOG_DEBUG("Remote hardware module disabled or no available_pins. Skipping...\n"); LOG_DEBUG("Remote hardware module disabled or no vailable_pins. Skipping...\n");
return; return;
} }
for (uint8_t i = 0; i < devicestate.node_remote_hardware_pins_count; i++) { for (uint8_t i = 0; i < devicestate.node_remote_hardware_pins_count; i++) {
@@ -389,10 +388,6 @@ void AdminModule::handleSetConfig(const meshtastic_Config &c)
LOG_DEBUG("Tried to set node_info_broadcast_secs too low, setting to %d\n", min_node_info_broadcast_secs); LOG_DEBUG("Tried to set node_info_broadcast_secs too low, setting to %d\n", min_node_info_broadcast_secs);
config.device.node_info_broadcast_secs = min_node_info_broadcast_secs; config.device.node_info_broadcast_secs = min_node_info_broadcast_secs;
} }
// Router Client is deprecated; Set it to client
if (c.payload_variant.device.role == meshtastic_Config_DeviceConfig_Role_ROUTER_CLIENT) {
config.device.role = meshtastic_Config_DeviceConfig_Role_CLIENT;
}
break; break;
case meshtastic_Config_position_tag: case meshtastic_Config_position_tag:
LOG_INFO("Setting config: Position\n"); LOG_INFO("Setting config: Position\n");
@@ -816,7 +811,7 @@ void AdminModule::handleGetChannel(const meshtastic_MeshPacket &req, uint32_t ch
void AdminModule::reboot(int32_t seconds) void AdminModule::reboot(int32_t seconds)
{ {
LOG_INFO("Rebooting in %d seconds\n", seconds); LOG_INFO("Rebooting in %d seconds\n", seconds);
screen->startAlert("Rebooting..."); screen->startRebootScreen();
rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000); rebootAtMsec = (seconds < 0) ? 0 : (millis() + seconds * 1000);
} }

View File

@@ -1,13 +1,13 @@
#pragma once #pragma once
#include "ProtobufModule.h" #include "ProtobufModule.h"
#if HAS_WIFI #if HAS_WIFI && !MESHTASTIC_EXCLUDE_WIFI
#include "mesh/wifi/WiFiAPClient.h" #include "mesh/wifi/WiFiAPClient.h"
#endif #endif
/** /**
* Admin module for admin messages * Admin module for admin messages
*/ */
class AdminModule : public ProtobufModule<meshtastic_AdminMessage>, public Observable<const meshtastic_AdminMessage *> class AdminModule : public ProtobufModule<meshtastic_AdminMessage>
{ {
public: public:
/** Constructor /** Constructor

View File

@@ -148,9 +148,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
if (this->currentMessageIndex == 0) { if (this->currentMessageIndex == 0) {
this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT; this->runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs UIFrameEvent e = {false, true};
UIFrameEvent e; e.frameChanged = true;
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen
this->notifyObservers(&e); this->notifyObservers(&e);
return 0; return 0;
@@ -167,8 +166,8 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
} }
} }
if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) { if (event->inputEvent == static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_CANCEL)) {
UIFrameEvent e; UIFrameEvent e = {false, true};
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen e.frameChanged = true;
this->currentMessageIndex = -1; this->currentMessageIndex = -1;
#if !defined(T_WATCH_S3) && !defined(RAK14014) #if !defined(T_WATCH_S3) && !defined(RAK14014)
@@ -354,8 +353,6 @@ int CannedMessageModule::handleInputEvent(const InputEvent *event)
} }
if (validEvent) { if (validEvent) {
requestFocus(); // Tell Screen::setFrames to move to our module's frame, next time it runs
// Let runOnce to be called immediately. // Let runOnce to be called immediately.
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) { if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_SELECT) {
setIntervalFromNow(0); // on fast keypresses, this isn't fast enough. setIntervalFromNow(0); // on fast keypresses, this isn't fast enough.
@@ -381,11 +378,6 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
p->decoded.payload.size++; p->decoded.payload.size++;
} }
// Only receive routing messages when expecting ACK for a canned message
// Prevents the canned message module from regenerating the screen's frameset at unexpected times,
// or raising a UIFrameEvent before another module has the chance
this->waitingForAck = true;
LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes); LOG_INFO("Sending message id=%d, dest=%x, msg=%.*s\n", p->id, p->to, p->decoded.payload.size, p->decoded.payload.bytes);
service.sendToMesh( service.sendToMesh(
@@ -401,13 +393,13 @@ int32_t CannedMessageModule::runOnce()
return INT32_MAX; return INT32_MAX;
} }
// LOG_DEBUG("Check status\n"); // LOG_DEBUG("Check status\n");
UIFrameEvent e; UIFrameEvent e = {false, true};
if ((this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) || if ((this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) ||
(this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) || (this->runState == CANNED_MESSAGE_RUN_STATE_MESSAGE)) { (this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) || (this->runState == CANNED_MESSAGE_RUN_STATE_MESSAGE)) {
// TODO: might have some feedback of sending state // TODO: might have some feedback of sending state
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
temporaryMessage = ""; temporaryMessage = "";
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen e.frameChanged = true;
this->currentMessageIndex = -1; this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext this->freetext = ""; // clear freetext
this->cursor = 0; this->cursor = 0;
@@ -420,7 +412,7 @@ int32_t CannedMessageModule::runOnce()
} else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) && } else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) &&
((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) { ((millis() - this->lastTouchMillis) > INACTIVATE_AFTER_MS)) {
// Reset module // Reset module
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen e.frameChanged = true;
this->currentMessageIndex = -1; this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext this->freetext = ""; // clear freetext
this->cursor = 0; this->cursor = 0;
@@ -457,7 +449,7 @@ int32_t CannedMessageModule::runOnce()
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
} }
} }
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen e.frameChanged = true;
this->currentMessageIndex = -1; this->currentMessageIndex = -1;
this->freetext = ""; // clear freetext this->freetext = ""; // clear freetext
this->cursor = 0; this->cursor = 0;
@@ -471,7 +463,7 @@ int32_t CannedMessageModule::runOnce()
} else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) { } else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) {
this->currentMessageIndex = 0; this->currentMessageIndex = 0;
LOG_DEBUG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage()); LOG_DEBUG("First touch (%d):%s\n", this->currentMessageIndex, this->getCurrentMessage());
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen e.frameChanged = true;
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE; this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) { } else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) {
if (this->messagesCount > 0) { if (this->messagesCount > 0) {
@@ -575,7 +567,7 @@ int32_t CannedMessageModule::runOnce()
break; break;
} }
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen e.frameChanged = true;
switch (this->payload) { // code below all trigger the freetext window (where you type to send a message) or reset the switch (this->payload) { // code below all trigger the freetext window (where you type to send a message) or reset the
// display back to the default window // display back to the default window
case 0x08: // backspace case 0x08: // backspace
@@ -605,14 +597,14 @@ int32_t CannedMessageModule::runOnce()
// handle fn+s for shutdown // handle fn+s for shutdown
case 0x9b: case 0x9b:
if (screen) if (screen)
screen->startAlert("Shutting down..."); screen->startShutdownScreen();
shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000; shutdownAtMsec = millis() + DEFAULT_SHUTDOWN_SECONDS * 1000;
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
break; break;
// and fn+r for reboot // and fn+r for reboot
case 0x90: case 0x90:
if (screen) if (screen)
screen->startAlert("Rebooting..."); screen->startRebootScreen();
rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000; rebootAtMsec = millis() + DEFAULT_REBOOT_SECONDS * 1000;
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE; runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
break; break;
@@ -714,8 +706,8 @@ int CannedMessageModule::getPrevIndex()
void CannedMessageModule::showTemporaryMessage(const String &message) void CannedMessageModule::showTemporaryMessage(const String &message)
{ {
temporaryMessage = message; temporaryMessage = message;
UIFrameEvent e; UIFrameEvent e = {false, true};
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen e.frameChanged = true;
notifyObservers(&e); notifyObservers(&e);
runState = CANNED_MESSAGE_RUN_STATE_MESSAGE; runState = CANNED_MESSAGE_RUN_STATE_MESSAGE;
// run this loop again in 2 seconds, next iteration will clear the display // run this loop again in 2 seconds, next iteration will clear the display
@@ -922,13 +914,11 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
char buffer[50]; char buffer[50];
if (temporaryMessage.length() != 0) { if (temporaryMessage.length() != 0) {
requestFocus(); // Tell Screen::setFrames to move to our module's frame
LOG_DEBUG("Drawing temporary message: %s", temporaryMessage.c_str()); LOG_DEBUG("Drawing temporary message: %s", temporaryMessage.c_str());
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM); display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage); display->drawString(display->getWidth() / 2 + x, 0 + y + 12, temporaryMessage);
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) { } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) {
requestFocus(); // Tell Screen::setFrames to move to our module's frame
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM); display->setFont(FONT_MEDIUM);
String displayString; String displayString;
@@ -950,7 +940,6 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi); display->drawStringf(display->getWidth() / 2 + x, y + 130, buffer, rssiString, this->lastRxRssi);
} }
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) { } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
requestFocus(); // Tell Screen::setFrames to move to our module's frame
display->setTextAlignment(TEXT_ALIGN_CENTER); display->setTextAlignment(TEXT_ALIGN_CENTER);
display->setFont(FONT_MEDIUM); display->setFont(FONT_MEDIUM);
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending..."); display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending...");
@@ -959,7 +948,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled."); display->drawString(10 + x, 0 + y + FONT_HEIGHT_SMALL, "Canned Message\nModule disabled.");
} else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) { } else if (cannedMessageModule->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
requestFocus(); // Tell Screen::setFrames to move to our module's frame
#if defined(T_WATCH_S3) || defined(RAK14014) #if defined(T_WATCH_S3) || defined(RAK14014)
drawKeyboard(display, state, 0, 0); drawKeyboard(display, state, 0, 0);
#else #else
@@ -1041,18 +1030,16 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &mp) ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &mp)
{ {
if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP && waitingForAck) { if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP) {
// look for a request_id // look for a request_id
if (mp.decoded.request_id != 0) { if (mp.decoded.request_id != 0) {
UIFrameEvent e; UIFrameEvent e = {false, true};
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET; // We want to change the list of frames shown on-screen e.frameChanged = true;
requestFocus(); // Tell Screen::setFrames that our module's frame should be shown, even if not "first" in the frameset
this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED; this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED;
this->incoming = service.getNodenumFromRequestId(mp.decoded.request_id); this->incoming = service.getNodenumFromRequestId(mp.decoded.request_id);
meshtastic_Routing decoded = meshtastic_Routing_init_default; meshtastic_Routing decoded = meshtastic_Routing_init_default;
pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded); pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded);
this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE; this->ack = decoded.error_reason == meshtastic_Routing_Error_NONE;
waitingForAck = false; // No longer want routing packets
this->notifyObservers(&e); this->notifyObservers(&e);
// run the next time 2 seconds later // run the next time 2 seconds later
setIntervalFromNow(2000); setIntervalFromNow(2000);

View File

@@ -81,8 +81,9 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
} }
switch (p->decoded.portnum) { switch (p->decoded.portnum) {
case meshtastic_PortNum_TEXT_MESSAGE_APP:
case meshtastic_PortNum_ROUTING_APP: case meshtastic_PortNum_ROUTING_APP:
return waitingForAck; return true;
default: default:
return false; return false;
} }
@@ -139,8 +140,7 @@ class CannedMessageModule : public SinglePortModule, public Observable<const UIF
uint8_t numChannels = 0; uint8_t numChannels = 0;
ChannelIndex indexChannels[MAX_NUM_CHANNELS] = {0}; ChannelIndex indexChannels[MAX_NUM_CHANNELS] = {0};
NodeNum incoming = NODENUM_BROADCAST; NodeNum incoming = NODENUM_BROADCAST;
bool ack = false; // True means ACK, false means NAK (error_reason != NONE) bool ack = false; // True means ACK, false means NAK (error_reason != NONE)
bool waitingForAck = false; // Are currently interested in routing packets?
float lastRxSnr = 0; float lastRxSnr = 0;
int32_t lastRxRssi = 0; int32_t lastRxRssi = 0;

View File

@@ -27,9 +27,6 @@
#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE #if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE
#include "modules/RemoteHardwareModule.h" #include "modules/RemoteHardwareModule.h"
#endif #endif
#if !MESHTASTIC_EXCLUDE_POWERSTRESS
#include "modules/PowerStressModule.h"
#endif
#include "modules/RoutingModule.h" #include "modules/RoutingModule.h"
#include "modules/TextMessageModule.h" #include "modules/TextMessageModule.h"
#if !MESHTASTIC_EXCLUDE_TRACEROUTE #if !MESHTASTIC_EXCLUDE_TRACEROUTE
@@ -118,9 +115,6 @@ void setupModules()
#if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE #if !MESHTASTIC_EXCLUDE_REMOTEHARDWARE
new RemoteHardwareModule(); new RemoteHardwareModule();
#endif
#if !MESHTASTIC_EXCLUDE_POWERSTRESS
new PowerStressModule();
#endif #endif
// Example: Put your module here // Example: Put your module here
// new ReplyModule(); // new ReplyModule();

View File

@@ -73,7 +73,7 @@ bool PositionModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, mes
} }
// Log packet size and data fields // Log packet size and data fields
LOG_DEBUG("POSITION node=%08x l=%d lat=%d lon=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d " LOG_DEBUG("POSITION node=%08x l=%d latI=%d lonI=%d msl=%d hae=%d geo=%d pdop=%d hdop=%d vdop=%d siv=%d fxq=%d fxt=%d pts=%d "
"time=%d\n", "time=%d\n",
getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae, getFrom(&mp), mp.decoded.payload.size, p.latitude_i, p.longitude_i, p.altitude, p.altitude_hae,
p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp, p.altitude_geoidal_separation, p.PDOP, p.HDOP, p.VDOP, p.sats_in_view, p.fix_quality, p.fix_type, p.timestamp,
@@ -219,7 +219,7 @@ meshtastic_MeshPacket *PositionModule::allocReply()
LOG_INFO("Providing time to mesh %u\n", p.time); LOG_INFO("Providing time to mesh %u\n", p.time);
} }
LOG_INFO("Position reply: time=%i lat=%i lon=%i\n", p.time, p.latitude_i, p.longitude_i); LOG_INFO("Position reply: time=%i, latI=%i, lonI=%i\n", p.time, p.latitude_i, p.longitude_i);
// TAK Tracker devices should send their position in a TAK packet over the ATAK port // TAK Tracker devices should send their position in a TAK packet over the ATAK port
if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER) if (config.device.role == meshtastic_Config_DeviceConfig_Role_TAK_TRACKER)

View File

@@ -1,77 +0,0 @@
#include "PowerStressModule.h"
#include "MeshService.h"
#include "NodeDB.h"
#include "RTC.h"
#include "Router.h"
#include "configuration.h"
#include "main.h"
extern void printInfo();
PowerStressModule::PowerStressModule()
: ProtobufModule("powerstress", meshtastic_PortNum_POWERSTRESS_APP, &meshtastic_PowerStressMessage_msg),
concurrency::OSThread("PowerStressModule")
{
}
bool PowerStressModule::handleReceivedProtobuf(const meshtastic_MeshPacket &req, meshtastic_PowerStressMessage *pptr)
{
// We only respond to messages if powermon debugging is already on
if (config.power.powermon_enables) {
auto p = *pptr;
LOG_INFO("Received PowerStress cmd=%d\n", p.cmd);
// Some commands we can handle immediately, anything else gets deferred to be handled by our thread
switch (p.cmd) {
case meshtastic_PowerStressMessage_Opcode_UNSET:
LOG_ERROR("PowerStress operation unset\n");
break;
case meshtastic_PowerStressMessage_Opcode_PRINT_INFO:
printInfo();
break;
default:
if (currentMessage.cmd != meshtastic_PowerStressMessage_Opcode_UNSET)
LOG_ERROR("PowerStress operation %d already in progress! Can't start new command\n", currentMessage.cmd);
else
currentMessage = p; // copy for use by thread (the message provided to us will be getting freed)
break;
}
}
return true;
}
int32_t PowerStressModule::runOnce()
{
if (!config.power.powermon_enables) {
// Powermon not enabled - stop using CPU/stop this thread
return disable();
}
int32_t sleep_msec = 10; // when not active check for new messages every 10ms
auto &p = currentMessage;
if (isRunningCommand) {
// Done with the previous command - our sleep must have finished
p.cmd = meshtastic_PowerStressMessage_Opcode_UNSET;
p.num_seconds = 0;
} else {
sleep_msec = (int32_t)(p.num_seconds * 1000);
isRunningCommand = !!sleep_msec; // if the command wants us to sleep, make sure to mark that we have something running
switch (p.cmd) {
case meshtastic_PowerStressMessage_Opcode_UNSET: // No need to start a new command
break;
case meshtastic_PowerStressMessage_Opcode_LED_ON:
break;
default:
LOG_ERROR("PowerStress operation %d not yet implemented!\n", p.cmd);
sleep_msec = 0; // Don't do whatever sleep was requested...
break;
}
}
return sleep_msec;
}

View File

@@ -1,38 +0,0 @@
#pragma once
#include "ProtobufModule.h"
#include "concurrency/OSThread.h"
#include "mesh/generated/meshtastic/powermon.pb.h"
/**
* A module that provides easy low-level remote access to device hardware.
*/
class PowerStressModule : public ProtobufModule<meshtastic_PowerStressMessage>, private concurrency::OSThread
{
meshtastic_PowerStressMessage currentMessage = meshtastic_PowerStressMessage_init_default;
bool isRunningCommand = false;
public:
/** Constructor
* name is for debugging output
*/
PowerStressModule();
protected:
/** Called to handle a particular incoming message
@return true if you've guaranteed you've handled this message and no other handlers should be considered for it
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_PowerStressMessage *p) override;
/**
* Periodically read the gpios we have been asked to WATCH, if they have changed,
* broadcast a message with the change information.
*
* The method that will be called each time our thread gets a chance to run
*
* Returns desired period for next invocation (or RUN_SAME for no change)
*/
virtual int32_t runOnce() override;
};
extern PowerStressModule powerStressModule;

View File

@@ -47,8 +47,7 @@ int32_t AirQualityTelemetryModule::runOnce()
uint32_t now = millis(); uint32_t now = millis();
if (((lastSentToMesh == 0) || if (((lastSentToMesh == 0) ||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.air_quality_interval, ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.air_quality_interval))) &&
default_telemetry_broadcast_interval_secs))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) { airTime->isTxAllowedAirUtil()) {
sendTelemetry(); sendTelemetry();
@@ -86,90 +85,53 @@ bool AirQualityTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPack
return false; // Let others look at this message also if they want return false; // Let others look at this message also if they want
} }
bool AirQualityTelemetryModule::getAirQualityTelemetry(meshtastic_Telemetry *m) bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{ {
if (!aqi.read(&data)) { if (!aqi.read(&data)) {
LOG_WARN("Skipping send measurements. Could not read AQIn\n"); LOG_WARN("Skipping send measurements. Could not read AQIn\n");
return false; return false;
} }
m->time = getTime(); meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
m->which_variant = meshtastic_Telemetry_air_quality_metrics_tag; m.time = getTime();
m->variant.air_quality_metrics.pm10_standard = data.pm10_standard; m.which_variant = meshtastic_Telemetry_air_quality_metrics_tag;
m->variant.air_quality_metrics.pm25_standard = data.pm25_standard; m.variant.air_quality_metrics.pm10_standard = data.pm10_standard;
m->variant.air_quality_metrics.pm100_standard = data.pm100_standard; m.variant.air_quality_metrics.pm25_standard = data.pm25_standard;
m.variant.air_quality_metrics.pm100_standard = data.pm100_standard;
m->variant.air_quality_metrics.pm10_environmental = data.pm10_env; m.variant.air_quality_metrics.pm10_environmental = data.pm10_env;
m->variant.air_quality_metrics.pm25_environmental = data.pm25_env; m.variant.air_quality_metrics.pm25_environmental = data.pm25_env;
m->variant.air_quality_metrics.pm100_environmental = data.pm100_env; m.variant.air_quality_metrics.pm100_environmental = data.pm100_env;
LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i\n", LOG_INFO("(Sending): PM1.0(Standard)=%i, PM2.5(Standard)=%i, PM10.0(Standard)=%i\n",
m->variant.air_quality_metrics.pm10_standard, m->variant.air_quality_metrics.pm25_standard, m.variant.air_quality_metrics.pm10_standard, m.variant.air_quality_metrics.pm25_standard,
m->variant.air_quality_metrics.pm100_standard); m.variant.air_quality_metrics.pm100_standard);
LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n", LOG_INFO(" | PM1.0(Environmental)=%i, PM2.5(Environmental)=%i, PM10.0(Environmental)=%i\n",
m->variant.air_quality_metrics.pm10_environmental, m->variant.air_quality_metrics.pm25_environmental, m.variant.air_quality_metrics.pm10_environmental, m.variant.air_quality_metrics.pm25_environmental,
m->variant.air_quality_metrics.pm100_environmental); m.variant.air_quality_metrics.pm100_environmental);
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = false;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(*p);
if (phoneOnly) {
LOG_INFO("Sending packet to phone\n");
service.sendToPhone(p);
} else {
LOG_INFO("Sending packet to mesh\n");
service.sendToMesh(p, RX_SRC_LOCAL, true);
}
return true; return true;
} }
meshtastic_MeshPacket *AirQualityTelemetryModule::allocReply()
{
if (currentRequest) {
auto req = *currentRequest;
const auto &p = req.decoded;
meshtastic_Telemetry scratch;
meshtastic_Telemetry *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch;
} else {
LOG_ERROR("Error decoding AirQualityTelemetry module!\n");
return NULL;
}
// Check for a request for air quality metrics
if (decoded->which_variant == meshtastic_Telemetry_air_quality_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getAirQualityTelemetry(&m)) {
LOG_INFO("Air quality telemetry replying to request\n");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
}
return NULL;
}
bool AirQualityTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getAirQualityTelemetry(&m)) {
meshtastic_MeshPacket *p = allocDataProtobuf(m);
p->to = dest;
p->decoded.want_response = false;
if (config.device.role == meshtastic_Config_DeviceConfig_Role_SENSOR)
p->priority = meshtastic_MeshPacket_Priority_RELIABLE;
else
p->priority = meshtastic_MeshPacket_Priority_BACKGROUND;
// release previous packet before occupying a new spot
if (lastMeasurementPacket != nullptr)
packetPool.release(lastMeasurementPacket);
lastMeasurementPacket = packetPool.allocCopy(*p);
if (phoneOnly) {
LOG_INFO("Sending packet to phone\n");
service.sendToPhone(p);
} else {
LOG_INFO("Sending packet to mesh\n");
service.sendToMesh(p, RX_SRC_LOCAL, true);
}
return true;
}
return false;
}
#endif #endif

View File

@@ -26,11 +26,6 @@ class AirQualityTelemetryModule : private concurrency::OSThread, public Protobuf
*/ */
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override; virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
/** Called to get current Air Quality data
@return true if it contains valid data
*/
bool getAirQualityTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/** /**
* Send our Telemetry into the mesh * Send our Telemetry into the mesh
*/ */

View File

@@ -17,8 +17,7 @@ int32_t DeviceTelemetryModule::runOnce()
{ {
refreshUptime(); refreshUptime();
if (((lastSentToMesh == 0) || if (((lastSentToMesh == 0) ||
((uptimeLastMs - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval, ((uptimeLastMs - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.device_update_interval))) &&
default_telemetry_broadcast_interval_secs))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil() && config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER && airTime->isTxAllowedAirUtil() && config.device.role != meshtastic_Config_DeviceConfig_Role_REPEATER &&
config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) { config.device.role != meshtastic_Config_DeviceConfig_Role_CLIENT_HIDDEN) {
@@ -53,27 +52,14 @@ bool DeviceTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &
meshtastic_MeshPacket *DeviceTelemetryModule::allocReply() meshtastic_MeshPacket *DeviceTelemetryModule::allocReply()
{ {
if (currentRequest) { if (ignoreRequest) {
auto req = *currentRequest; return NULL;
const auto &p = req.decoded;
meshtastic_Telemetry scratch;
meshtastic_Telemetry *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch;
} else {
LOG_ERROR("Error decoding DeviceTelemetry module!\n");
return NULL;
}
// Check for a request for device metrics
if (decoded->which_variant == meshtastic_Telemetry_device_metrics_tag) {
LOG_INFO("Device telemetry replying to request\n");
meshtastic_Telemetry telemetry = getDeviceTelemetry();
return allocDataProtobuf(telemetry);
}
} }
return NULL;
LOG_INFO("Device telemetry replying to request\n");
meshtastic_Telemetry telemetry = getDeviceTelemetry();
return allocDataProtobuf(telemetry);
} }
meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry() meshtastic_Telemetry DeviceTelemetryModule::getDeviceTelemetry()
@@ -118,4 +104,4 @@ bool DeviceTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
service.sendToMesh(p, RX_SRC_LOCAL, true); service.sendToMesh(p, RX_SRC_LOCAL, true);
} }
return true; return true;
} }

View File

@@ -69,8 +69,7 @@ int32_t EnvironmentTelemetryModule::runOnce()
{ {
if (sleepOnNextExecution == true) { if (sleepOnNextExecution == true) {
sleepOnNextExecution = false; sleepOnNextExecution = false;
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval, uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval);
default_telemetry_broadcast_interval_secs);
LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs); LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs);
doDeepSleep(nightyNightMs, true); doDeepSleep(nightyNightMs, true);
} }
@@ -125,8 +124,6 @@ int32_t EnvironmentTelemetryModule::runOnce()
result = ina219Sensor.runOnce(); result = ina219Sensor.runOnce();
if (ina260Sensor.hasSensor()) if (ina260Sensor.hasSensor())
result = ina260Sensor.runOnce(); result = ina260Sensor.runOnce();
if (ina3221Sensor.hasSensor())
result = ina3221Sensor.runOnce();
if (veml7700Sensor.hasSensor()) if (veml7700Sensor.hasSensor())
result = veml7700Sensor.runOnce(); result = veml7700Sensor.runOnce();
if (tsl2591Sensor.hasSensor()) if (tsl2591Sensor.hasSensor())
@@ -155,8 +152,7 @@ int32_t EnvironmentTelemetryModule::runOnce()
uint32_t now = millis(); uint32_t now = millis();
if (((lastSentToMesh == 0) || if (((lastSentToMesh == 0) ||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval, ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.environment_update_interval))) &&
default_telemetry_broadcast_interval_secs))) &&
airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) && airTime->isTxAllowedChannelUtil(config.device.role != meshtastic_Config_DeviceConfig_Role_SENSOR) &&
airTime->isTxAllowedAirUtil()) { airTime->isTxAllowedAirUtil()) {
sendTelemetry(); sendTelemetry();
@@ -202,7 +198,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
if (lastMeasurementPacket == nullptr) { if (lastMeasurementPacket == nullptr) {
// If there's no valid packet, display "Environment" // If there's no valid packet, display "Environment"
display->drawString(x, y, "Environment"); display->drawString(x, y, "Environment");
display->drawString(x, y += _fontHeight(FONT_SMALL), "No measurement"); display->drawString(x, y += fontHeight(FONT_SMALL), "No measurement");
return; return;
} }
@@ -227,31 +223,31 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
} }
// Continue with the remaining details // Continue with the remaining details
display->drawString(x, y += _fontHeight(FONT_SMALL), display->drawString(x, y += fontHeight(FONT_SMALL),
"Temp/Hum: " + last_temp + " / " + "Temp/Hum: " + last_temp + " / " +
String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%"); String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%");
if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0) { if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0) {
display->drawString(x, y += _fontHeight(FONT_SMALL), display->drawString(x, y += fontHeight(FONT_SMALL),
"Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA"); "Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA");
} }
if (lastMeasurement.variant.environment_metrics.voltage != 0) { if (lastMeasurement.variant.environment_metrics.voltage != 0) {
display->drawString(x, y += _fontHeight(FONT_SMALL), display->drawString(x, y += fontHeight(FONT_SMALL),
"Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " + "Volt/Cur: " + String(lastMeasurement.variant.environment_metrics.voltage, 0) + "V / " +
String(lastMeasurement.variant.environment_metrics.current, 0) + "mA"); String(lastMeasurement.variant.environment_metrics.current, 0) + "mA");
} }
if (lastMeasurement.variant.environment_metrics.iaq != 0) { if (lastMeasurement.variant.environment_metrics.iaq != 0) {
display->drawString(x, y += _fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq)); display->drawString(x, y += fontHeight(FONT_SMALL), "IAQ: " + String(lastMeasurement.variant.environment_metrics.iaq));
} }
if (lastMeasurement.variant.environment_metrics.distance != 0) if (lastMeasurement.variant.environment_metrics.distance != 0)
display->drawString(x, y += _fontHeight(FONT_SMALL), display->drawString(x, y += fontHeight(FONT_SMALL),
"Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm"); "Water Level: " + String(lastMeasurement.variant.environment_metrics.distance, 0) + "mm");
if (lastMeasurement.variant.environment_metrics.weight != 0) if (lastMeasurement.variant.environment_metrics.weight != 0)
display->drawString(x, y += _fontHeight(FONT_SMALL), display->drawString(x, y += fontHeight(FONT_SMALL),
"Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg"); "Weight: " + String(lastMeasurement.variant.environment_metrics.weight, 0) + "kg");
} }
@@ -284,142 +280,102 @@ bool EnvironmentTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPac
return false; // Let others look at this message also if they want return false; // Let others look at this message also if they want
} }
bool EnvironmentTelemetryModule::getEnvironmentTelemetry(meshtastic_Telemetry *m) bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{ {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
bool valid = true; bool valid = true;
bool hasSensor = false; bool hasSensor = false;
m->time = getTime(); m.time = getTime();
m->which_variant = meshtastic_Telemetry_environment_metrics_tag; m.which_variant = meshtastic_Telemetry_environment_metrics_tag;
#ifdef T1000X_SENSOR_EN // add by WayenWeng #ifdef T1000X_SENSOR_EN // add by WayenWeng
valid = valid && t1000xSensor.getMetrics(&m); valid = valid && t1000xSensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
#else #else
if (dfRobotLarkSensor.hasSensor()) { if (dfRobotLarkSensor.hasSensor()) {
valid = valid && dfRobotLarkSensor.getMetrics(m); valid = valid && dfRobotLarkSensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (sht31Sensor.hasSensor()) { if (sht31Sensor.hasSensor()) {
valid = valid && sht31Sensor.getMetrics(m); valid = valid && sht31Sensor.getMetrics(&m);
hasSensor = true;
}
if (sht4xSensor.hasSensor()) {
valid = valid && sht4xSensor.getMetrics(m);
hasSensor = true; hasSensor = true;
} }
if (lps22hbSensor.hasSensor()) { if (lps22hbSensor.hasSensor()) {
valid = valid && lps22hbSensor.getMetrics(m); valid = valid && lps22hbSensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (shtc3Sensor.hasSensor()) { if (shtc3Sensor.hasSensor()) {
valid = valid && shtc3Sensor.getMetrics(m); valid = valid && shtc3Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (bmp085Sensor.hasSensor()) { if (bmp085Sensor.hasSensor()) {
valid = valid && bmp085Sensor.getMetrics(m); valid = valid && bmp085Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (bmp280Sensor.hasSensor()) { if (bmp280Sensor.hasSensor()) {
valid = valid && bmp280Sensor.getMetrics(m); valid = valid && bmp280Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (bme280Sensor.hasSensor()) { if (bme280Sensor.hasSensor()) {
valid = valid && bme280Sensor.getMetrics(m); valid = valid && bme280Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (bme680Sensor.hasSensor()) { if (bme680Sensor.hasSensor()) {
valid = valid && bme680Sensor.getMetrics(m); valid = valid && bme680Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (mcp9808Sensor.hasSensor()) { if (mcp9808Sensor.hasSensor()) {
valid = valid && mcp9808Sensor.getMetrics(m); valid = valid && mcp9808Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (ina219Sensor.hasSensor()) { if (ina219Sensor.hasSensor()) {
valid = valid && ina219Sensor.getMetrics(m); valid = valid && ina219Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (ina260Sensor.hasSensor()) { if (ina260Sensor.hasSensor()) {
valid = valid && ina260Sensor.getMetrics(m); valid = valid && ina260Sensor.getMetrics(&m);
hasSensor = true;
}
if (ina3221Sensor.hasSensor()) {
valid = valid && ina3221Sensor.getMetrics(m);
hasSensor = true; hasSensor = true;
} }
if (veml7700Sensor.hasSensor()) { if (veml7700Sensor.hasSensor()) {
valid = valid && veml7700Sensor.getMetrics(m); valid = valid && veml7700Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (tsl2591Sensor.hasSensor()) { if (tsl2591Sensor.hasSensor()) {
valid = valid && tsl2591Sensor.getMetrics(m); valid = valid && tsl2591Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (opt3001Sensor.hasSensor()) { if (opt3001Sensor.hasSensor()) {
valid = valid && opt3001Sensor.getMetrics(m); valid = valid && opt3001Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (mlx90632Sensor.hasSensor()) { if (mlx90632Sensor.hasSensor()) {
valid = valid && mlx90632Sensor.getMetrics(m); valid = valid && mlx90632Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (rcwl9620Sensor.hasSensor()) { if (rcwl9620Sensor.hasSensor()) {
valid = valid && rcwl9620Sensor.getMetrics(m); valid = valid && rcwl9620Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (nau7802Sensor.hasSensor()) { if (nau7802Sensor.hasSensor()) {
valid = valid && nau7802Sensor.getMetrics(m); valid = valid && nau7802Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} }
if (aht10Sensor.hasSensor()) { if (aht10Sensor.hasSensor()) {
if (!bmp280Sensor.hasSensor()) { if (!bmp280Sensor.hasSensor()) {
valid = valid && aht10Sensor.getMetrics(m); valid = valid && aht10Sensor.getMetrics(&m);
hasSensor = true; hasSensor = true;
} else { } else {
// prefer bmp280 temp if both sensors are present, fetch only humidity // prefer bmp280 temp if both sensors are present, fetch only humidity
meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero; meshtastic_Telemetry m_ahtx = meshtastic_Telemetry_init_zero;
LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n"); LOG_INFO("AHTX0+BMP280 module detected: using temp from BMP280 and humy from AHTX0\n");
aht10Sensor.getMetrics(&m_ahtx); aht10Sensor.getMetrics(&m_ahtx);
m->variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity; m.variant.environment_metrics.relative_humidity = m_ahtx.variant.environment_metrics.relative_humidity;
} }
} }
#endif #endif
valid = valid && hasSensor;
return valid && hasSensor; if (valid) {
}
meshtastic_MeshPacket *EnvironmentTelemetryModule::allocReply()
{
if (currentRequest) {
auto req = *currentRequest;
const auto &p = req.decoded;
meshtastic_Telemetry scratch;
meshtastic_Telemetry *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch;
} else {
LOG_ERROR("Error decoding EnvironmentTelemetry module!\n");
return NULL;
}
// Check for a request for environment metrics
if (decoded->which_variant == meshtastic_Telemetry_environment_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getEnvironmentTelemetry(&m)) {
LOG_INFO("Environment telemetry replying to request\n");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
}
return NULL;
}
bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getEnvironmentTelemetry(&m)) {
LOG_INFO("(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f\n", LOG_INFO("(Sending): barometric_pressure=%f, current=%f, gas_resistance=%f, relative_humidity=%f, temperature=%f\n",
m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current, m.variant.environment_metrics.barometric_pressure, m.variant.environment_metrics.current,
m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity, m.variant.environment_metrics.gas_resistance, m.variant.environment_metrics.relative_humidity,
@@ -457,9 +413,8 @@ bool EnvironmentTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
setIntervalFromNow(5000); setIntervalFromNow(5000);
} }
} }
return true;
} }
return false; return valid;
} }
AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp, AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule(const meshtastic_MeshPacket &mp,
@@ -522,11 +477,6 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
if (result != AdminMessageHandleResult::NOT_HANDLED) if (result != AdminMessageHandleResult::NOT_HANDLED)
return result; return result;
} }
if (ina3221Sensor.hasSensor()) {
result = ina3221Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED)
return result;
}
if (veml7700Sensor.hasSensor()) { if (veml7700Sensor.hasSensor()) {
result = veml7700Sensor.handleAdminMessage(mp, request, response); result = veml7700Sensor.handleAdminMessage(mp, request, response);
if (result != AdminMessageHandleResult::NOT_HANDLED) if (result != AdminMessageHandleResult::NOT_HANDLED)
@@ -565,4 +515,4 @@ AdminMessageHandleResult EnvironmentTelemetryModule::handleAdminMessageForModule
return result; return result;
} }
#endif #endif

View File

@@ -32,11 +32,6 @@ class EnvironmentTelemetryModule : private concurrency::OSThread, public Protobu
*/ */
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override; virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
/** Called to get current Environment telemetry data
@return true if it contains valid data
*/
bool getEnvironmentTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/** /**
* Send our Telemetry into the mesh * Send our Telemetry into the mesh
*/ */

View File

@@ -24,8 +24,7 @@ int32_t PowerTelemetryModule::runOnce()
{ {
if (sleepOnNextExecution == true) { if (sleepOnNextExecution == true) {
sleepOnNextExecution = false; sleepOnNextExecution = false;
uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval, uint32_t nightyNightMs = Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval);
default_telemetry_broadcast_interval_secs);
LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs); LOG_DEBUG("Sleeping for %ims, then awaking to send metrics again.\n", nightyNightMs);
doDeepSleep(nightyNightMs, true); doDeepSleep(nightyNightMs, true);
} }
@@ -71,8 +70,7 @@ int32_t PowerTelemetryModule::runOnce()
uint32_t now = millis(); uint32_t now = millis();
if (((lastSentToMesh == 0) || if (((lastSentToMesh == 0) ||
((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval, ((now - lastSentToMesh) >= Default::getConfiguredOrDefaultMs(moduleConfig.telemetry.power_update_interval))) &&
default_telemetry_broadcast_interval_secs))) &&
airTime->isTxAllowedAirUtil()) { airTime->isTxAllowedAirUtil()) {
sendTelemetry(); sendTelemetry();
lastSentToMesh = now; lastSentToMesh = now;
@@ -110,7 +108,7 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s
display->drawString(x, y, "Power Telemetry"); display->drawString(x, y, "Power Telemetry");
if (lastMeasurementPacket == nullptr) { if (lastMeasurementPacket == nullptr) {
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
display->drawString(x, y += _fontHeight(FONT_MEDIUM), "No measurement"); display->drawString(x, y += fontHeight(FONT_MEDIUM), "No measurement");
return; return;
} }
@@ -122,22 +120,22 @@ void PowerTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *s
auto &p = lastMeasurementPacket->decoded; auto &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) { if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &lastMeasurement)) {
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
display->drawString(x, y += _fontHeight(FONT_MEDIUM), "Measurement Error"); display->drawString(x, y += fontHeight(FONT_MEDIUM), "Measurement Error");
LOG_ERROR("Unable to decode last packet"); LOG_ERROR("Unable to decode last packet");
return; return;
} }
display->setFont(FONT_SMALL); display->setFont(FONT_SMALL);
String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C"; String last_temp = String(lastMeasurement.variant.environment_metrics.temperature, 0) + "°C";
display->drawString(x, y += _fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)"); display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
if (lastMeasurement.variant.power_metrics.ch1_voltage != 0) { if (lastMeasurement.variant.power_metrics.ch1_voltage != 0) {
display->drawString(x, y += _fontHeight(FONT_SMALL), display->drawString(x, y += fontHeight(FONT_SMALL),
"Ch 1 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 0) + "V / " + "Ch 1 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch1_voltage, 0) + "V / " +
String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA"); String(lastMeasurement.variant.power_metrics.ch1_current, 0) + "mA");
display->drawString(x, y += _fontHeight(FONT_SMALL), display->drawString(x, y += fontHeight(FONT_SMALL),
"Ch 2 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 0) + "V / " + "Ch 2 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch2_voltage, 0) + "V / " +
String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA"); String(lastMeasurement.variant.power_metrics.ch2_current, 0) + "mA");
display->drawString(x, y += _fontHeight(FONT_SMALL), display->drawString(x, y += fontHeight(FONT_SMALL),
"Ch 3 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 0) + "V / " + "Ch 3 Volt/Cur: " + String(lastMeasurement.variant.power_metrics.ch3_voltage, 0) + "V / " +
String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA"); String(lastMeasurement.variant.power_metrics.ch3_current, 0) + "mA");
} }
@@ -165,63 +163,29 @@ bool PowerTelemetryModule::handleReceivedProtobuf(const meshtastic_MeshPacket &m
return false; // Let others look at this message also if they want return false; // Let others look at this message also if they want
} }
bool PowerTelemetryModule::getPowerTelemetry(meshtastic_Telemetry *m)
{
bool valid = false;
m->time = getTime();
m->which_variant = meshtastic_Telemetry_power_metrics_tag;
m->variant.power_metrics.ch1_voltage = 0;
m->variant.power_metrics.ch1_current = 0;
m->variant.power_metrics.ch2_voltage = 0;
m->variant.power_metrics.ch2_current = 0;
m->variant.power_metrics.ch3_voltage = 0;
m->variant.power_metrics.ch3_current = 0;
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
if (ina219Sensor.hasSensor())
valid = ina219Sensor.getMetrics(m);
if (ina260Sensor.hasSensor())
valid = ina260Sensor.getMetrics(m);
if (ina3221Sensor.hasSensor())
valid = ina3221Sensor.getMetrics(m);
#endif
return valid;
}
meshtastic_MeshPacket *PowerTelemetryModule::allocReply()
{
if (currentRequest) {
auto req = *currentRequest;
const auto &p = req.decoded;
meshtastic_Telemetry scratch;
meshtastic_Telemetry *decoded = NULL;
memset(&scratch, 0, sizeof(scratch));
if (pb_decode_from_bytes(p.payload.bytes, p.payload.size, &meshtastic_Telemetry_msg, &scratch)) {
decoded = &scratch;
} else {
LOG_ERROR("Error decoding PowerTelemetry module!\n");
return NULL;
}
// Check for a request for power metrics
if (decoded->which_variant == meshtastic_Telemetry_power_metrics_tag) {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getPowerTelemetry(&m)) {
LOG_INFO("Power telemetry replying to request\n");
return allocDataProtobuf(m);
} else {
return NULL;
}
}
}
return NULL;
}
bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly) bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
{ {
meshtastic_Telemetry m = meshtastic_Telemetry_init_zero; meshtastic_Telemetry m = meshtastic_Telemetry_init_zero;
if (getPowerTelemetry(&m)) { bool valid = false;
m.time = getTime();
m.which_variant = meshtastic_Telemetry_power_metrics_tag;
m.variant.power_metrics.ch1_voltage = 0;
m.variant.power_metrics.ch1_current = 0;
m.variant.power_metrics.ch2_voltage = 0;
m.variant.power_metrics.ch2_current = 0;
m.variant.power_metrics.ch3_voltage = 0;
m.variant.power_metrics.ch3_current = 0;
#if HAS_TELEMETRY && !defined(ARCH_PORTDUINO)
if (ina219Sensor.hasSensor())
valid = ina219Sensor.getMetrics(&m);
if (ina260Sensor.hasSensor())
valid = ina260Sensor.getMetrics(&m);
if (ina3221Sensor.hasSensor())
valid = ina3221Sensor.getMetrics(&m);
#endif
if (valid) {
LOG_INFO("(Sending): ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, " LOG_INFO("(Sending): ch1_voltage=%f, ch1_current=%f, ch2_voltage=%f, ch2_current=%f, "
"ch3_voltage=%f, ch3_current=%f\n", "ch3_voltage=%f, ch3_current=%f\n",
m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage, m.variant.power_metrics.ch1_voltage, m.variant.power_metrics.ch1_current, m.variant.power_metrics.ch2_voltage,
@@ -254,9 +218,8 @@ bool PowerTelemetryModule::sendTelemetry(NodeNum dest, bool phoneOnly)
setIntervalFromNow(5000); setIntervalFromNow(5000);
} }
} }
return true;
} }
return false; return valid;
} }
#endif #endif

View File

@@ -33,11 +33,6 @@ class PowerTelemetryModule : private concurrency::OSThread, public ProtobufModul
*/ */
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override; virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_Telemetry *p) override;
virtual int32_t runOnce() override; virtual int32_t runOnce() override;
/** Called to get current Power telemetry data
@return true if it contains valid data
*/
bool getPowerTelemetry(meshtastic_Telemetry *m);
virtual meshtastic_MeshPacket *allocReply() override;
/** /**
* Send our Telemetry into the mesh * Send our Telemetry into the mesh
*/ */

View File

@@ -16,7 +16,8 @@ int32_t INA3221Sensor::runOnce()
return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS; return DEFAULT_SENSOR_MINIMUM_WAIT_TIME_BETWEEN_READS;
} }
if (!status) { if (!status) {
ina3221.begin(nodeTelemetrySensorsMap[sensorType].second); ina3221.setAddr(INA3221_ADDR42_SDA); // i2c address 0x42
ina3221.begin();
ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors ina3221.setShuntRes(100, 100, 100); // 0.1 Ohm shunt resistors
status = true; status = true;
} else { } else {
@@ -27,69 +28,22 @@ int32_t INA3221Sensor::runOnce()
void INA3221Sensor::setup() {} void INA3221Sensor::setup() {}
struct _INA3221Measurement INA3221Sensor::getMeasurement(ina3221_ch_t ch)
{
struct _INA3221Measurement measurement;
measurement.voltage = ina3221.getVoltage(ch);
measurement.current = ina3221.getCurrent(ch);
return measurement;
}
struct _INA3221Measurements INA3221Sensor::getMeasurements()
{
struct _INA3221Measurements measurements;
// INA3221 has 3 channels starting from 0
for (int i = 0; i < 3; i++) {
measurements.measurements[i] = getMeasurement((ina3221_ch_t)i);
}
return measurements;
}
bool INA3221Sensor::getMetrics(meshtastic_Telemetry *measurement) bool INA3221Sensor::getMetrics(meshtastic_Telemetry *measurement)
{ {
switch (measurement->which_variant) { measurement->variant.environment_metrics.voltage = ina3221.getVoltage(INA3221_CH1);
case meshtastic_Telemetry_environment_metrics_tag: measurement->variant.environment_metrics.current = ina3221.getCurrent(INA3221_CH1);
return getEnvironmentMetrics(measurement); measurement->variant.power_metrics.ch1_voltage = ina3221.getVoltage(INA3221_CH1);
measurement->variant.power_metrics.ch1_current = ina3221.getCurrent(INA3221_CH1);
case meshtastic_Telemetry_power_metrics_tag: measurement->variant.power_metrics.ch2_voltage = ina3221.getVoltage(INA3221_CH2);
return getPowerMetrics(measurement); measurement->variant.power_metrics.ch2_current = ina3221.getCurrent(INA3221_CH2);
} measurement->variant.power_metrics.ch3_voltage = ina3221.getVoltage(INA3221_CH3);
measurement->variant.power_metrics.ch3_current = ina3221.getCurrent(INA3221_CH3);
// unsupported metric
return false;
}
bool INA3221Sensor::getEnvironmentMetrics(meshtastic_Telemetry *measurement)
{
struct _INA3221Measurement m = getMeasurement(ENV_CH);
measurement->variant.environment_metrics.voltage = m.voltage;
measurement->variant.environment_metrics.current = m.current;
return true;
}
bool INA3221Sensor::getPowerMetrics(meshtastic_Telemetry *measurement)
{
struct _INA3221Measurements m = getMeasurements();
measurement->variant.power_metrics.ch1_voltage = m.measurements[INA3221_CH1].voltage;
measurement->variant.power_metrics.ch1_current = m.measurements[INA3221_CH1].current;
measurement->variant.power_metrics.ch2_voltage = m.measurements[INA3221_CH2].voltage;
measurement->variant.power_metrics.ch2_current = m.measurements[INA3221_CH2].current;
measurement->variant.power_metrics.ch3_voltage = m.measurements[INA3221_CH3].voltage;
measurement->variant.power_metrics.ch3_current = m.measurements[INA3221_CH3].current;
return true; return true;
} }
uint16_t INA3221Sensor::getBusVoltageMv() uint16_t INA3221Sensor::getBusVoltageMv()
{ {
return lround(ina3221.getVoltage(BAT_CH) * 1000); return lround(ina3221.getVoltage(INA3221_CH1) * 1000);
} }
#endif #endif

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