mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-09 03:17:31 +00:00
Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4dd50df810 | ||
|
|
14c4022c18 | ||
|
|
a5d7bacdbf | ||
|
|
0b3c25f6d9 | ||
|
|
ad7a474a52 | ||
|
|
430186ec53 | ||
|
|
227c6fc27e | ||
|
|
a37844d7e5 | ||
|
|
ff20b29c3c | ||
|
|
64c29c4a35 | ||
|
|
d4df3f8a7e | ||
|
|
a16c3af30a | ||
|
|
3061860dab | ||
|
|
2450d98b59 | ||
|
|
a371592ad9 | ||
|
|
f7aaf48ae9 | ||
|
|
1fb604ebc8 | ||
|
|
df2733a3b5 | ||
|
|
8fd3cb1aac | ||
|
|
485c476f17 | ||
|
|
7dd4ce32d2 | ||
|
|
7f12af73d4 | ||
|
|
63113d57b3 | ||
|
|
32850ff39d | ||
|
|
2901f773a4 | ||
|
|
a7c54e4ad7 | ||
|
|
c2a1141dfa | ||
|
|
5b4472ab56 | ||
|
|
3262f732d8 | ||
|
|
cff21ca130 | ||
|
|
81ce04d3da | ||
|
|
f4d2b10840 | ||
|
|
2370cb8aac | ||
|
|
59ec87f5b0 | ||
|
|
0d9481b6ea | ||
|
|
8f0105ccd9 | ||
|
|
05ca3c3d56 | ||
|
|
ba549d8fcd | ||
|
|
b9df2c00fa | ||
|
|
d9dcb33576 | ||
|
|
f698231be7 | ||
|
|
8414f4a6a3 | ||
|
|
f3b93d55fb | ||
|
|
2b373048c6 | ||
|
|
b32e3f1269 | ||
|
|
68ddb712f5 | ||
|
|
2fb5cd8c1c | ||
|
|
79aea8231f | ||
|
|
b0837c10c6 | ||
|
|
cd811951b1 | ||
|
|
df2976dad0 | ||
|
|
4ccbe6ff71 | ||
|
|
038ddb887f | ||
|
|
9134faaed1 | ||
|
|
649a120fe0 | ||
|
|
4db0c4a563 | ||
|
|
3b2f5fa5e3 | ||
|
|
97adb598b6 | ||
|
|
7ef2cc8623 | ||
|
|
b41a32c6b6 | ||
|
|
1ebd7b0c3e | ||
|
|
5457541244 | ||
|
|
02e3438d5e | ||
|
|
02b1ece6ac | ||
|
|
9fdef366f7 | ||
|
|
284816229e | ||
|
|
10008d4eef | ||
|
|
58cfd1317c | ||
|
|
3d3f7869d4 | ||
|
|
ef325289eb | ||
|
|
40c63c0615 | ||
|
|
a9de8b9bb3 | ||
|
|
66a7f896c8 | ||
|
|
45a36f5571 | ||
|
|
876d32c9ee | ||
|
|
b9ce75b09c | ||
|
|
62493efc40 | ||
|
|
2848b76cc9 | ||
|
|
ef8bea478d | ||
|
|
a8e4bbbe65 | ||
|
|
9a414d9c77 | ||
|
|
48e6a60a07 | ||
|
|
ca48079545 | ||
|
|
76b4be3b87 | ||
|
|
d39cc3d57b | ||
|
|
b17a8d7a6a | ||
|
|
c16acb904e | ||
|
|
5b777219be | ||
|
|
32ea11d2af | ||
|
|
db8faa9faf | ||
|
|
6d178ebc91 | ||
|
|
f75a256631 | ||
|
|
4f659b7563 | ||
|
|
dffcea1f4d |
@@ -9,11 +9,10 @@ COUNTRIES="US EU433 EU865 CN JP ANZ KR"
|
|||||||
#COUNTRIES=CN
|
#COUNTRIES=CN
|
||||||
|
|
||||||
BOARDS_ESP32="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
|
BOARDS_ESP32="tlora-v2 tlora-v1 tlora-v2-1-1.6 tbeam heltec tbeam0.7"
|
||||||
|
# BOARDS_ESP32=tbeam
|
||||||
|
|
||||||
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
|
# FIXME note nrf52840dk build is for some reason only generating a BIN file but not a HEX file nrf52840dk-geeksville is fine
|
||||||
BOARDS_NRF52="lora-relay-v1"
|
BOARDS_NRF52="lora-relay-v1"
|
||||||
BOARDS="$BOARDS_ESP32 $BOARDS_NRF52"
|
|
||||||
#BOARDS=tbeam
|
|
||||||
|
|
||||||
OUTDIR=release/latest
|
OUTDIR=release/latest
|
||||||
|
|
||||||
@@ -22,22 +21,61 @@ ARCHIVEDIR=release/archive
|
|||||||
|
|
||||||
rm -f $OUTDIR/firmware*
|
rm -f $OUTDIR/firmware*
|
||||||
|
|
||||||
mkdir -p $OUTDIR/bins $OUTDIR/elfs
|
mkdir -p $OUTDIR/bins
|
||||||
rm -f $OUTDIR/bins/*
|
rm -r $OUTDIR/bins/*
|
||||||
|
mkdir -p $OUTDIR/bins/universal $OUTDIR/elfs/universal
|
||||||
|
|
||||||
# build the named environment and copy the bins to the release directory
|
# build the named environment and copy the bins to the release directory
|
||||||
function do_build {
|
function do_build() {
|
||||||
echo "Building for $BOARD with $PLATFORMIO_BUILD_FLAGS"
|
BOARD=$1
|
||||||
|
COUNTRY=$2
|
||||||
|
isNrf=$3
|
||||||
|
|
||||||
|
echo "Building $COUNTRY for $BOARD with $PLATFORMIO_BUILD_FLAGS"
|
||||||
rm -f .pio/build/$BOARD/firmware.*
|
rm -f .pio/build/$BOARD/firmware.*
|
||||||
|
|
||||||
# The shell vars the build tool expects to find
|
# The shell vars the build tool expects to find
|
||||||
export HW_VERSION="1.0-$COUNTRY"
|
|
||||||
export APP_VERSION=$VERSION
|
export APP_VERSION=$VERSION
|
||||||
export COUNTRY
|
|
||||||
|
# Are we building a universal/regionless rom?
|
||||||
|
if [ "x$COUNTRY" != "x" ]
|
||||||
|
then
|
||||||
|
export HW_VERSION="1.0-$COUNTRY"
|
||||||
|
export COUNTRY
|
||||||
|
basename=firmware-$BOARD-$COUNTRY-$VERSION
|
||||||
|
else
|
||||||
|
export HW_VERSION="1.0"
|
||||||
|
unset COUNTRY
|
||||||
|
basename=universal/firmware-$BOARD-$VERSION
|
||||||
|
fi
|
||||||
|
|
||||||
pio run --jobs 4 --environment $BOARD # -v
|
pio run --jobs 4 --environment $BOARD # -v
|
||||||
SRCELF=.pio/build/$BOARD/firmware.elf
|
SRCELF=.pio/build/$BOARD/firmware.elf
|
||||||
cp $SRCELF $OUTDIR/elfs/firmware-$BOARD-$COUNTRY-$VERSION.elf
|
cp $SRCELF $OUTDIR/elfs/$basename.elf
|
||||||
|
|
||||||
|
if [ "$isNrf" = "false" ]
|
||||||
|
then
|
||||||
|
echo "Copying ESP32 bin file"
|
||||||
|
SRCBIN=.pio/build/$BOARD/firmware.bin
|
||||||
|
cp $SRCBIN $OUTDIR/bins/$basename.bin
|
||||||
|
else
|
||||||
|
echo "Generating NRF52 uf2 file"
|
||||||
|
SRCHEX=.pio/build/$BOARD/firmware.hex
|
||||||
|
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/bins/$basename.uf2 -f 0xADA52840
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function do_boards() {
|
||||||
|
declare boards=$1
|
||||||
|
declare isNrf=$2
|
||||||
|
for board in $boards; do
|
||||||
|
for country in $COUNTRIES; do
|
||||||
|
do_build $board $country "$isNrf"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Build universal
|
||||||
|
do_build $board "" "$isNrf"
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
# Make sure our submodules are current
|
# Make sure our submodules are current
|
||||||
@@ -46,26 +84,16 @@ git submodule update
|
|||||||
# 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 lib update
|
platformio lib update
|
||||||
|
|
||||||
for COUNTRY in $COUNTRIES; do
|
do_boards "$BOARDS_ESP32" "false"
|
||||||
for BOARD in $BOARDS; do
|
do_boards "$BOARDS_NRF52" "true"
|
||||||
do_build $BOARD
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Copying ESP32 bin files"
|
|
||||||
for BOARD in $BOARDS_ESP32; do
|
|
||||||
SRCBIN=.pio/build/$BOARD/firmware.bin
|
|
||||||
cp $SRCBIN $OUTDIR/bins/firmware-$BOARD-$COUNTRY-$VERSION.bin
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "Generating NRF52 uf2 files"
|
|
||||||
for BOARD in $BOARDS_NRF52; do
|
|
||||||
SRCHEX=.pio/build/$BOARD/firmware.hex
|
|
||||||
bin/uf2conv.py $SRCHEX -c -o $OUTDIR/bins/firmware-$BOARD-$COUNTRY-$VERSION.uf2 -f 0xADA52840
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
# keep the bins in archive also
|
# keep the bins in archive also
|
||||||
cp $OUTDIR/bins/firmware* $OUTDIR/elfs/firmware* $ARCHIVEDIR
|
cp $OUTDIR/bins/firmware* $OUTDIR/elfs/firmware* $OUTDIR/bins/universal/firmware* $OUTDIR/elfs/universal/firmware* $ARCHIVEDIR
|
||||||
|
|
||||||
|
echo Updating android bins $OUTDIR/forandroid
|
||||||
|
rm -rf $OUTDIR/forandroid
|
||||||
|
mkdir -p $OUTDIR/forandroid
|
||||||
|
cp -a $OUTDIR/bins/universal/*.bin $OUTDIR/forandroid/
|
||||||
|
|
||||||
cat >$OUTDIR/curfirmwareversion.xml <<XML
|
cat >$OUTDIR/curfirmwareversion.xml <<XML
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
@@ -79,6 +107,7 @@ Generated by bin/buildall.sh -->
|
|||||||
</resources>
|
</resources>
|
||||||
XML
|
XML
|
||||||
|
|
||||||
|
echo Generating $ARCHIVEDIR/firmware-$VERSION.zip
|
||||||
rm -f $ARCHIVEDIR/firmware-$VERSION.zip
|
rm -f $ARCHIVEDIR/firmware-$VERSION.zip
|
||||||
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $OUTDIR/bins/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh bin/device-update.sh
|
zip --junk-paths $ARCHIVEDIR/firmware-$VERSION.zip $OUTDIR/bins/firmware-*-$VERSION.* images/system-info.bin bin/device-install.sh bin/device-update.sh
|
||||||
|
|
||||||
|
|||||||
22
bin/install-eink.sh
Executable file
22
bin/install-eink.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
# You probably don't want to use this script, it programs a custom bootloader build onto a nrf52 board
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
BOOTDIR=/home/kevinh/development/meshtastic/Adafruit_nRF52_Bootloader
|
||||||
|
|
||||||
|
nrfjprog --eraseall -f nrf52
|
||||||
|
|
||||||
|
# this generates an intel hex file that can be programmed into a NRF52 to tell the adafruit bootloader that the current app image is valid
|
||||||
|
# Bootloader settings are at BOOTLOADER_SETTINGS (rw) : ORIGIN = 0xFF000, LENGTH = 0x1000
|
||||||
|
# first 4 bytes should be 0x01 to indicate valid app image
|
||||||
|
# second 4 bytes should be 0x00 to indicate no CRC required for image
|
||||||
|
echo "01 00 00 00 00 00 00 00" | xxd -r -p - >/tmp/bootconf.bin
|
||||||
|
srec_cat /tmp/bootconf.bin -binary -offset 0xff000 -output /tmp/bootconf.hex -intel
|
||||||
|
|
||||||
|
echo Generating merged hex file
|
||||||
|
mergehex -m $BOOTDIR/_build/build-ttgo_eink/ttgo_eink_bootloader-0.3.2-125-gf38f8f4-dirty_s140_6.1.1.hex .pio/build/eink/firmware.hex /tmp/bootconf.hex -o ttgo_eink_full.hex
|
||||||
|
|
||||||
|
echo Telling bootloader app region is valid and telling CPU to run
|
||||||
|
nrfjprog --program ttgo_eink_full.hex -f nrf52 --reset
|
||||||
|
|
||||||
|
# nrfjprog --readuicr /tmp/uicr.hex; objdump -s /tmp/uicr.hex | less
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
export VERSION=1.1.3
|
export VERSION=1.1.6
|
||||||
277
data/style.css
Normal file
277
data/style.css
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lato';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('Lato Regular'), local('Lato-Regular'), url(./Google.woff2) format('woff2');
|
||||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: #C5DDEB;
|
||||||
|
font: 14px/20px "Lato", Arial, sans-serif;
|
||||||
|
padding: 40px 0;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns:
|
||||||
|
1fr 4fr;
|
||||||
|
grid-template-areas:
|
||||||
|
"header header"
|
||||||
|
"sidebar content";
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 750px;
|
||||||
|
background: #444753;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.top {grid-area: header;}
|
||||||
|
.side {grid-area: sidebar;}
|
||||||
|
.main {grid-area: content;}
|
||||||
|
|
||||||
|
.top {
|
||||||
|
border-bottom: 2px solid white;
|
||||||
|
}
|
||||||
|
.top-text {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 24px;
|
||||||
|
text-align: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side {
|
||||||
|
width: 260px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
.side .side-header {
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 2px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.side .side-header .side-text {
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 16px;
|
||||||
|
text-align: left;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-list ul {
|
||||||
|
padding: 20px;
|
||||||
|
height: 570px;
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
.channel-list ul li {
|
||||||
|
padding-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-list .channel-name {
|
||||||
|
font-size: 20px;
|
||||||
|
margin-top: 8px;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-list .message-count {
|
||||||
|
padding-left: 16px;
|
||||||
|
color: #92959E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: inline-block;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
stroke-width: 0;
|
||||||
|
stroke: currentColor;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-map-marker {
|
||||||
|
width: 0.5714285714285714em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-circle {
|
||||||
|
width: 0.8571428571428571em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
/* width: 490px; */
|
||||||
|
float: left;
|
||||||
|
background: #F2F5F8;
|
||||||
|
/* border-top-right-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px; */
|
||||||
|
color: #434651;
|
||||||
|
}
|
||||||
|
.content .content-header {
|
||||||
|
flex-grow: 0;
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 2px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content .content-header .content-from {
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 20px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
.content .content-header .content-from .content-from-highlight {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.content .content-header .content-num-messages {
|
||||||
|
color: #92959E;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content .content-history {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 20px 20px 20px;
|
||||||
|
border-bottom: 2px solid white;
|
||||||
|
overflow-y: scroll;
|
||||||
|
height: 375px;
|
||||||
|
}
|
||||||
|
.content .content-history ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-inline-start: 10px;
|
||||||
|
}
|
||||||
|
.content .content-history .message-data {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.content .content-history .message-data-time {
|
||||||
|
color: #a8aab1;
|
||||||
|
padding-left: 6px;
|
||||||
|
}
|
||||||
|
.content .content-history .message {
|
||||||
|
color: white;
|
||||||
|
padding: 8px 10px;
|
||||||
|
line-height: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: 7px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
width: 90%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.content .content-history .message:after {
|
||||||
|
bottom: 100%;
|
||||||
|
left: 7%;
|
||||||
|
border: solid transparent;
|
||||||
|
content: " ";
|
||||||
|
height: 0;
|
||||||
|
width: 0;
|
||||||
|
position: absolute;
|
||||||
|
pointer-events: none;
|
||||||
|
border-bottom-color: #86BB71;
|
||||||
|
border-width: 10px;
|
||||||
|
margin-left: -10px;
|
||||||
|
}
|
||||||
|
.content .content-history .my-message {
|
||||||
|
background: #86BB71;
|
||||||
|
}
|
||||||
|
.content .content-history .other-message {
|
||||||
|
background: #94C2ED;
|
||||||
|
}
|
||||||
|
.content .content-history .other-message:after {
|
||||||
|
border-bottom-color: #94C2ED;
|
||||||
|
left: 93%;
|
||||||
|
}
|
||||||
|
.content .content-message {
|
||||||
|
flex-grow: 0;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.content .content-message textarea {
|
||||||
|
width: 100%;
|
||||||
|
border: none;
|
||||||
|
padding: 10px 10px;
|
||||||
|
font: 14px/22px "Lato", Arial, sans-serif;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content .content-message button {
|
||||||
|
float: right;
|
||||||
|
color: #94C2ED;
|
||||||
|
font-size: 16px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
background: #F2F5F8;
|
||||||
|
}
|
||||||
|
.content .content-message button:hover {
|
||||||
|
color: #75b1e8;
|
||||||
|
}
|
||||||
|
/* Tooltip container */
|
||||||
|
.tooltip {
|
||||||
|
color: #86BB71;
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
border-bottom: 1px dotted black; /* If you want dots under the hoverable text */
|
||||||
|
}
|
||||||
|
/* Tooltip text */
|
||||||
|
.tooltip .tooltiptext {
|
||||||
|
visibility: hidden;
|
||||||
|
width: 120px;
|
||||||
|
background-color: #444753;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
padding: 5px 0;
|
||||||
|
border-radius: 6px;
|
||||||
|
/* Position the tooltip text - see examples below! */
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show the tooltip text when you mouse over the tooltip container */
|
||||||
|
.tooltip:hover .tooltiptext {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.online, .offline, .me {
|
||||||
|
margin-right: 3px;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.online {
|
||||||
|
color: #86BB71;
|
||||||
|
}
|
||||||
|
|
||||||
|
.offline {
|
||||||
|
color: #E38968;
|
||||||
|
}
|
||||||
|
|
||||||
|
.me {
|
||||||
|
color: #94C2ED;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.clearfix:after {
|
||||||
|
visibility: hidden;
|
||||||
|
display: block;
|
||||||
|
font-size: 0;
|
||||||
|
content: " ";
|
||||||
|
clear: both;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
BIN
data/style.css.gz
Normal file
BIN
data/style.css.gz
Normal file
Binary file not shown.
@@ -42,7 +42,6 @@ Expected sequence for initial download:
|
|||||||
- Read a RadioConfig from "radio" - used to get the channel and radio settings
|
- Read a RadioConfig from "radio" - used to get the channel and radio settings
|
||||||
- Read a User from "user" - to get the username for this node
|
- Read a User from "user" - to get the username for this node
|
||||||
- Read a MyNodeInfo from "mynode" to get information about this local device
|
- Read a MyNodeInfo from "mynode" to get information about this local device
|
||||||
- Write an empty record to "nodeinfo" to restart the nodeinfo reading state machine
|
|
||||||
- Read a series of NodeInfo packets to build the phone's copy of the current NodeDB for the mesh
|
- Read a series of NodeInfo packets to build the phone's copy of the current NodeDB for the mesh
|
||||||
- Read a endConfig packet that indicates that the entire state you need has been sent.
|
- Read a endConfig packet that indicates that the entire state you need has been sent.
|
||||||
- Read a series of MeshPackets until it returns empty to get any messages that arrived for this node while the phone was away
|
- Read a series of MeshPackets until it returns empty to get any messages that arrived for this node while the phone was away
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
|
||||||
|
- enter SDS state at correct time (to protect battery or loss of phone contact)
|
||||||
|
- show screen on eink when we enter SDS state (with app info and say sleeping)
|
||||||
|
- require button press to pair
|
||||||
|
|
||||||
- shrink soft device RAM usage
|
- shrink soft device RAM usage
|
||||||
- get nrf52832 working again (currently OOM)
|
- get nrf52832 working again (currently OOM)
|
||||||
- i2c gps comms not quite right
|
- i2c gps comms not quite right
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ debug_tool = jlink
|
|||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
https://github.com/meshtastic/esp8266-oled-ssd1306.git ; ESP8266_SSD1306
|
||||||
1260 ; OneButton library for non-blocking button debounce
|
https://github.com/geeksville/OneButton.git ; OneButton library for non-blocking button debounce
|
||||||
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
1202 ; CRC32, explicitly needed because dependency is missing in the ble ota update lib
|
||||||
https://github.com/meshtastic/arduino-fsm.git#2f106146071fc7bc620e1e8d4b88dc4e0266ce39
|
https://github.com/meshtastic/arduino-fsm.git#2f106146071fc7bc620e1e8d4b88dc4e0266ce39
|
||||||
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
|
https://github.com/meshtastic/SparkFun_Ublox_Arduino_Library.git#31015a55e630a2df77d9d714669c621a5bf355ad
|
||||||
@@ -95,6 +95,9 @@ build_flags =
|
|||||||
${arduino_base.build_flags} -Wall -Wextra -Isrc/esp32 -Isrc/esp32-mfix-esp32-psram-cache-issue -lnimble -std=c++11
|
${arduino_base.build_flags} -Wall -Wextra -Isrc/esp32 -Isrc/esp32-mfix-esp32-psram-cache-issue -lnimble -std=c++11
|
||||||
-DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
|
-DLOG_LOCAL_LEVEL=ESP_LOG_DEBUG -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_DEBUG
|
||||||
-DAXP_DEBUG_PORT=Serial
|
-DAXP_DEBUG_PORT=Serial
|
||||||
|
lib_deps =
|
||||||
|
${arduino_base.lib_deps}
|
||||||
|
https://github.com/meshtastic/esp32_https_server.git
|
||||||
# Hmm - this doesn't work yet
|
# Hmm - this doesn't work yet
|
||||||
# board_build.ldscript = linker/esp32.extram.bss.ld
|
# board_build.ldscript = linker/esp32.extram.bss.ld
|
||||||
lib_ignore = segger_rtt
|
lib_ignore = segger_rtt
|
||||||
@@ -117,7 +120,7 @@ board_build.partitions = partition-table.csv
|
|||||||
extends = esp32_base
|
extends = esp32_base
|
||||||
board = ttgo-t-beam
|
board = ttgo-t-beam
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${arduino_base.lib_deps}
|
${esp32_base.lib_deps}
|
||||||
build_flags =
|
build_flags =
|
||||||
${esp32_base.build_flags} -D TBEAM_V10
|
${esp32_base.build_flags} -D TBEAM_V10
|
||||||
|
|
||||||
|
|||||||
113
src/Power.cpp
113
src/Power.cpp
@@ -18,6 +18,21 @@ Power *power;
|
|||||||
|
|
||||||
using namespace meshtastic;
|
using namespace meshtastic;
|
||||||
|
|
||||||
|
#if defined(NRF52_SERIES)
|
||||||
|
/*
|
||||||
|
* Internal Reference is +/-0.6V, with an adjustable gain of 1/6, 1/5, 1/4,
|
||||||
|
* 1/3, 1/2 or 1, meaning 3.6, 3.0, 2.4, 1.8, 1.2 or 0.6V for the ADC levels.
|
||||||
|
*
|
||||||
|
* External Reference is VDD/4, with an adjustable gain of 1, 2 or 4, meaning
|
||||||
|
* VDD/4, VDD/2 or VDD for the ADC levels.
|
||||||
|
*
|
||||||
|
* Default settings are internal reference with 1/6 gain (GND..3.6V ADC range)
|
||||||
|
*/
|
||||||
|
#define AREF_VOLTAGE 3.6
|
||||||
|
#else
|
||||||
|
#define AREF_VOLTAGE 3.3
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If this board has a battery level sensor, set this to a valid implementation
|
* If this board has a battery level sensor, set this to a valid implementation
|
||||||
*/
|
*/
|
||||||
@@ -37,10 +52,13 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
{
|
{
|
||||||
float v = getBattVoltage() / 1000;
|
float v = getBattVoltage() / 1000;
|
||||||
|
|
||||||
if (v < 2.1)
|
if (v < noBatVolt)
|
||||||
return -1; // If voltage is super low assume no battery installed
|
return -1; // If voltage is super low assume no battery installed
|
||||||
|
|
||||||
return 100 * (v - 3.27) / (4.2 - 3.27);
|
if (v > chargingVolt)
|
||||||
|
return 0; // While charging we can't report % full on the battery
|
||||||
|
|
||||||
|
return 100 * (v - emptyVolt) / (fullVolt - emptyVolt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,9 +66,11 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
*/
|
*/
|
||||||
virtual float getBattVoltage()
|
virtual float getBattVoltage()
|
||||||
{
|
{
|
||||||
|
// Tested ttgo eink nrf52 board and the reported value is perfect
|
||||||
|
// DEBUG_MSG("raw val %u", raw);
|
||||||
return
|
return
|
||||||
#ifdef BATTERY_PIN
|
#ifdef BATTERY_PIN
|
||||||
1000.0 * analogRead(BATTERY_PIN) * 2.0 * (3.3 / 1024.0);
|
1000.0 * 2.0 * (AREF_VOLTAGE / 1024.0) * analogRead(BATTERY_PIN);
|
||||||
#else
|
#else
|
||||||
NAN;
|
NAN;
|
||||||
#endif
|
#endif
|
||||||
@@ -59,7 +79,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
/**
|
/**
|
||||||
* return true if there is a battery installed in this unit
|
* return true if there is a battery installed in this unit
|
||||||
*/
|
*/
|
||||||
virtual bool isBatteryConnect() { return getBattVoltage() != -1; }
|
virtual bool isBatteryConnect() { return getBattPercentage() != -1; }
|
||||||
|
|
||||||
|
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
||||||
|
/// in power
|
||||||
|
virtual bool isVBUSPlug() { return getBattVoltage() > chargingVolt; }
|
||||||
|
|
||||||
|
/// Assume charging if we have a battery and external power is connected.
|
||||||
|
/// we can't be smart enough to say 'full'?
|
||||||
|
virtual bool isChargeing() { return isBatteryConnect() && isVBUSPlug(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// If we see a battery voltage higher than physics allows - assume charger is pumping
|
||||||
|
/// in power
|
||||||
|
const float fullVolt = 4.2, emptyVolt = 3.27, chargingVolt = 4.3, noBatVolt = 2.1;
|
||||||
} analogLevel;
|
} analogLevel;
|
||||||
|
|
||||||
Power::Power() : OSThread("Power") {}
|
Power::Power() : OSThread("Power") {}
|
||||||
@@ -68,10 +101,18 @@ bool Power::analogInit()
|
|||||||
{
|
{
|
||||||
#ifdef BATTERY_PIN
|
#ifdef BATTERY_PIN
|
||||||
DEBUG_MSG("Using analog input for battery level\n");
|
DEBUG_MSG("Using analog input for battery level\n");
|
||||||
|
|
||||||
|
// disable any internal pullups
|
||||||
|
pinMode(BATTERY_PIN, INPUT);
|
||||||
|
|
||||||
#ifndef NO_ESP32
|
#ifndef NO_ESP32
|
||||||
// ESP32 needs special analog stuff
|
// ESP32 needs special analog stuff
|
||||||
adcAttachPin(BATTERY_PIN);
|
adcAttachPin(BATTERY_PIN);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef NRF52_SERIES
|
||||||
|
analogReference(AR_INTERNAL); // 3.6V
|
||||||
|
#endif
|
||||||
|
|
||||||
// adcStart(BATTERY_PIN);
|
// adcStart(BATTERY_PIN);
|
||||||
analogReadResolution(10); // Default of 12 is not very linear. Recommended to use 10 or 11 depending on needed resolution.
|
analogReadResolution(10); // Default of 12 is not very linear. Recommended to use 10 or 11 depending on needed resolution.
|
||||||
batteryLevel = &analogLevel;
|
batteryLevel = &analogLevel;
|
||||||
@@ -121,7 +162,8 @@ void Power::readPowerStatus()
|
|||||||
const PowerStatus powerStatus =
|
const PowerStatus powerStatus =
|
||||||
PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVBUSPlug() ? OptTrue : OptFalse,
|
PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVBUSPlug() ? OptTrue : OptFalse,
|
||||||
batteryLevel->isChargeing() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
|
batteryLevel->isChargeing() ? OptTrue : OptFalse, batteryVoltageMv, batteryChargePercent);
|
||||||
DEBUG_MSG("Read power stat %d\n", powerStatus.getHasUSB());
|
DEBUG_MSG("Battery: usbPower=%d, isCharging=%d, batMv=%d, batPct=%d\n", powerStatus.getHasUSB(),
|
||||||
|
powerStatus.getIsCharging(), powerStatus.getBatteryVoltageMv(), powerStatus.getBatteryChargePercent());
|
||||||
newStatus.notifyObservers(&powerStatus);
|
newStatus.notifyObservers(&powerStatus);
|
||||||
|
|
||||||
// If we have a battery at all and it is less than 10% full, force deep sleep
|
// If we have a battery at all and it is less than 10% full, force deep sleep
|
||||||
@@ -138,38 +180,39 @@ int32_t Power::runOnce()
|
|||||||
{
|
{
|
||||||
readPowerStatus();
|
readPowerStatus();
|
||||||
|
|
||||||
#ifdef PMU_IRQ
|
#ifdef TBEAM_V10
|
||||||
if (pmu_irq) {
|
// WE no longer use the IRQ line to wake the CPU (due to false wakes from sleep), but we do poll
|
||||||
pmu_irq = false;
|
// the IRQ status by reading the registers over I2C
|
||||||
axp.readIRQ();
|
axp.readIRQ();
|
||||||
|
|
||||||
DEBUG_MSG("pmu irq!\n");
|
if (axp.isVbusRemoveIRQ()) {
|
||||||
|
DEBUG_MSG("USB unplugged\n");
|
||||||
if (axp.isChargingIRQ()) {
|
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
||||||
DEBUG_MSG("Battery start charging\n");
|
|
||||||
}
|
|
||||||
if (axp.isChargingDoneIRQ()) {
|
|
||||||
DEBUG_MSG("Battery fully charged\n");
|
|
||||||
}
|
|
||||||
if (axp.isVbusRemoveIRQ()) {
|
|
||||||
DEBUG_MSG("USB unplugged\n");
|
|
||||||
powerFSM.trigger(EVENT_POWER_DISCONNECTED);
|
|
||||||
}
|
|
||||||
if (axp.isVbusPlugInIRQ()) {
|
|
||||||
DEBUG_MSG("USB plugged In\n");
|
|
||||||
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
|
||||||
}
|
|
||||||
if (axp.isBattPlugInIRQ()) {
|
|
||||||
DEBUG_MSG("Battery inserted\n");
|
|
||||||
}
|
|
||||||
if (axp.isBattRemoveIRQ()) {
|
|
||||||
DEBUG_MSG("Battery removed\n");
|
|
||||||
}
|
|
||||||
if (axp.isPEKShortPressIRQ()) {
|
|
||||||
DEBUG_MSG("PEK short button press\n");
|
|
||||||
}
|
|
||||||
axp.clearIRQ();
|
|
||||||
}
|
}
|
||||||
|
if (axp.isVbusPlugInIRQ()) {
|
||||||
|
DEBUG_MSG("USB plugged In\n");
|
||||||
|
powerFSM.trigger(EVENT_POWER_CONNECTED);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Other things we could check if we cared...
|
||||||
|
|
||||||
|
if (axp.isChargingIRQ()) {
|
||||||
|
DEBUG_MSG("Battery start charging\n");
|
||||||
|
}
|
||||||
|
if (axp.isChargingDoneIRQ()) {
|
||||||
|
DEBUG_MSG("Battery fully charged\n");
|
||||||
|
}
|
||||||
|
if (axp.isBattPlugInIRQ()) {
|
||||||
|
DEBUG_MSG("Battery inserted\n");
|
||||||
|
}
|
||||||
|
if (axp.isBattRemoveIRQ()) {
|
||||||
|
DEBUG_MSG("Battery removed\n");
|
||||||
|
}
|
||||||
|
if (axp.isPEKShortPressIRQ()) {
|
||||||
|
DEBUG_MSG("PEK short button press\n");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
axp.clearIRQ();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Only read once every 20 seconds once the power status for the app has been initialized
|
// Only read once every 20 seconds once the power status for the app has been initialized
|
||||||
|
|||||||
@@ -118,12 +118,21 @@ static void serialEnter()
|
|||||||
{
|
{
|
||||||
setBluetoothEnable(false);
|
setBluetoothEnable(false);
|
||||||
screen->setOn(true);
|
screen->setOn(true);
|
||||||
|
screen->print("Using API...\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void powerEnter()
|
static void powerEnter()
|
||||||
{
|
{
|
||||||
screen->setOn(true);
|
screen->setOn(true);
|
||||||
setBluetoothEnable(true);
|
setBluetoothEnable(true);
|
||||||
|
screen->print("Powered...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void powerExit()
|
||||||
|
{
|
||||||
|
screen->setOn(true);
|
||||||
|
setBluetoothEnable(true);
|
||||||
|
screen->print("Unpowered...\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onEnter()
|
static void onEnter()
|
||||||
@@ -140,6 +149,7 @@ static void onEnter()
|
|||||||
service.sendNetworkPing(displayedNodeNum, true); // Refresh the currently displayed node
|
service.sendNetworkPing(displayedNodeNum, true); // Refresh the currently displayed node
|
||||||
lastPingMs = now;
|
lastPingMs = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void screenPress()
|
static void screenPress()
|
||||||
@@ -156,7 +166,7 @@ State stateDARK(darkEnter, NULL, NULL, "DARK");
|
|||||||
State stateSERIAL(serialEnter, NULL, NULL, "SERIAL");
|
State stateSERIAL(serialEnter, NULL, NULL, "SERIAL");
|
||||||
State stateBOOT(bootEnter, NULL, NULL, "BOOT");
|
State stateBOOT(bootEnter, NULL, NULL, "BOOT");
|
||||||
State stateON(onEnter, NULL, NULL, "ON");
|
State stateON(onEnter, NULL, NULL, "ON");
|
||||||
State statePOWER(powerEnter, NULL, NULL, "POWER");
|
State statePOWER(powerEnter, NULL, powerExit, "POWER");
|
||||||
Fsm powerFSM(&stateBOOT);
|
Fsm powerFSM(&stateBOOT);
|
||||||
|
|
||||||
void PowerFSM_setup()
|
void PowerFSM_setup()
|
||||||
@@ -235,10 +245,16 @@ void PowerFSM_setup()
|
|||||||
|
|
||||||
powerFSM.add_timed_transition(&stateON, &stateDARK, getPref_screen_on_secs() * 1000, NULL, "Screen-on timeout");
|
powerFSM.add_timed_transition(&stateON, &stateDARK, getPref_screen_on_secs() * 1000, NULL, "Screen-on timeout");
|
||||||
|
|
||||||
powerFSM.add_timed_transition(&stateDARK, &stateNB, getPref_phone_timeout_secs() * 1000, NULL, "Phone timeout");
|
// On most boards we use light-sleep to be our main state, but on NRF52 we just stay in DARK
|
||||||
|
State *lowPowerState = &stateLS;
|
||||||
|
|
||||||
#ifndef NRF52_SERIES
|
#ifndef NRF52_SERIES
|
||||||
// We never enter light-sleep state on NRF52 (because the CPU uses so little power normally)
|
// We never enter light-sleep or NB states on NRF52 (because the CPU uses so little power normally)
|
||||||
|
|
||||||
|
lowPowerState = &stateDARK;
|
||||||
|
|
||||||
|
powerFSM.add_timed_transition(&stateDARK, &stateNB, getPref_phone_timeout_secs() * 1000, NULL, "Phone timeout");
|
||||||
|
|
||||||
powerFSM.add_timed_transition(&stateNB, &stateLS, getPref_min_wake_secs() * 1000, NULL, "Min wake timeout");
|
powerFSM.add_timed_transition(&stateNB, &stateLS, getPref_min_wake_secs() * 1000, NULL, "Min wake timeout");
|
||||||
|
|
||||||
powerFSM.add_timed_transition(&stateDARK, &stateLS, getPref_wait_bluetooth_secs() * 1000, NULL, "Bluetooth timeout");
|
powerFSM.add_timed_transition(&stateDARK, &stateLS, getPref_wait_bluetooth_secs() * 1000, NULL, "Bluetooth timeout");
|
||||||
@@ -246,9 +262,9 @@ void PowerFSM_setup()
|
|||||||
|
|
||||||
auto meshSds = getPref_mesh_sds_timeout_secs();
|
auto meshSds = getPref_mesh_sds_timeout_secs();
|
||||||
if (meshSds != UINT32_MAX)
|
if (meshSds != UINT32_MAX)
|
||||||
powerFSM.add_timed_transition(&stateLS, &stateSDS, meshSds * 1000, NULL, "mesh timeout");
|
powerFSM.add_timed_transition(lowPowerState, &stateSDS, meshSds * 1000, NULL, "mesh timeout");
|
||||||
// removing for now, because some users don't even have phones
|
// removing for now, because some users don't even have phones
|
||||||
// powerFSM.add_timed_transition(&stateLS, &stateSDS, getPref_phone_sds_timeout_sec() * 1000, NULL, "phone
|
// powerFSM.add_timed_transition(lowPowerState, &stateSDS, getPref_phone_sds_timeout_sec() * 1000, NULL, "phone
|
||||||
// timeout");
|
// timeout");
|
||||||
|
|
||||||
powerFSM.run_machine(); // run one interation of the state machine, so we run our on enter tasks for the initial DARK state
|
powerFSM.run_machine(); // run one interation of the state machine, so we run our on enter tasks for the initial DARK state
|
||||||
|
|||||||
@@ -20,6 +20,6 @@
|
|||||||
#define EVENT_POWER_DISCONNECTED 14
|
#define EVENT_POWER_DISCONNECTED 14
|
||||||
|
|
||||||
extern Fsm powerFSM;
|
extern Fsm powerFSM;
|
||||||
extern State statePOWER;
|
extern State statePOWER, stateSERIAL;
|
||||||
|
|
||||||
void PowerFSM_setup();
|
void PowerFSM_setup();
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ class PowerStatus : public Status
|
|||||||
isCharging = newStatus->isCharging;
|
isCharging = newStatus->isCharging;
|
||||||
}
|
}
|
||||||
if (isDirty) {
|
if (isDirty) {
|
||||||
DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
|
// DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
|
||||||
onNewStatus.notifyObservers(this);
|
onNewStatus.notifyObservers(this);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -4,30 +4,22 @@
|
|||||||
namespace concurrency
|
namespace concurrency
|
||||||
{
|
{
|
||||||
|
|
||||||
InterruptableDelay::InterruptableDelay()
|
InterruptableDelay::InterruptableDelay() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
InterruptableDelay::~InterruptableDelay()
|
InterruptableDelay::~InterruptableDelay() {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns false if we were interrupted
|
* Returns false if we were interrupted
|
||||||
*/
|
*/
|
||||||
bool InterruptableDelay::delay(uint32_t msec)
|
bool InterruptableDelay::delay(uint32_t msec)
|
||||||
{
|
{
|
||||||
if (msec) {
|
// DEBUG_MSG("delay %u ", msec);
|
||||||
// DEBUG_MSG("delay %u ", msec);
|
|
||||||
|
|
||||||
// sem take will return false if we timed out (i.e. were not interrupted)
|
// sem take will return false if we timed out (i.e. were not interrupted)
|
||||||
bool r = semaphore.take(msec);
|
bool r = semaphore.take(msec);
|
||||||
|
|
||||||
// DEBUG_MSG("interrupt=%d\n", r);
|
// DEBUG_MSG("interrupt=%d\n", r);
|
||||||
return !r;
|
return !r;
|
||||||
} else {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterruptableDelay::interrupt()
|
void InterruptableDelay::interrupt()
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
|
|||||||
crc.update(data, len);
|
crc.update(data, len);
|
||||||
Update.write(data, len);
|
Update.write(data, len);
|
||||||
updateActualSize += len;
|
updateActualSize += len;
|
||||||
powerFSM.trigger(EVENT_RECEIVED_TEXT_MSG); // Not exactly correct, but we want to force the device to not sleep now
|
powerFSM.trigger(EVENT_CONTACT_FROM_PHONE);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "NMEAGPS.h"
|
#include "NMEAGPS.h"
|
||||||
#include "configuration.h"
|
|
||||||
#include "RTC.h"
|
#include "RTC.h"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
static int32_t toDegInt(RawDegrees d)
|
static int32_t toDegInt(RawDegrees d)
|
||||||
{
|
{
|
||||||
@@ -32,7 +32,7 @@ bool NMEAGPS::lookForTime()
|
|||||||
{
|
{
|
||||||
auto ti = reader.time;
|
auto ti = reader.time;
|
||||||
auto d = reader.date;
|
auto d = reader.date;
|
||||||
if (ti.isUpdated() && ti.isValid() && d.isValid()) {
|
if (ti.isValid() && d.isValid()) { // Note: we don't check for updated, because we'll only be called if needed
|
||||||
/* Convert to unix time
|
/* Convert to unix time
|
||||||
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
|
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January 1, 1970
|
||||||
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
(midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
|
||||||
@@ -65,27 +65,27 @@ bool NMEAGPS::lookForLocation()
|
|||||||
// uint8_t fixtype = reader.fixQuality();
|
// uint8_t fixtype = reader.fixQuality();
|
||||||
// hasValidLocation = ((fixtype >= 1) && (fixtype <= 5));
|
// hasValidLocation = ((fixtype >= 1) && (fixtype <= 5));
|
||||||
|
|
||||||
|
if (reader.satellites.isUpdated()) {
|
||||||
|
setNumSatellites(reader.satellites.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
|
||||||
|
if (reader.hdop.isUpdated()) {
|
||||||
|
dop = reader.hdop.value();
|
||||||
|
}
|
||||||
|
if (reader.course.isUpdated()) {
|
||||||
|
heading = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.altitude.isUpdated())
|
||||||
|
altitude = reader.altitude.meters();
|
||||||
|
|
||||||
if (reader.location.isUpdated()) {
|
if (reader.location.isUpdated()) {
|
||||||
if (reader.altitude.isValid())
|
|
||||||
altitude = reader.altitude.meters();
|
|
||||||
|
|
||||||
if (reader.location.isValid()) {
|
auto loc = reader.location.value();
|
||||||
auto loc = reader.location.value();
|
latitude = toDegInt(loc.lat);
|
||||||
latitude = toDegInt(loc.lat);
|
longitude = toDegInt(loc.lng);
|
||||||
longitude = toDegInt(loc.lng);
|
foundLocation = true;
|
||||||
foundLocation = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Diminution of precision (an accuracy metric) is reported in 10^2 units, so we need to scale down when we use it
|
|
||||||
if (reader.hdop.isValid()) {
|
|
||||||
dop = reader.hdop.value();
|
|
||||||
}
|
|
||||||
if (reader.course.isValid()) {
|
|
||||||
heading = reader.course.value() * 1e3; // Scale the heading (in degrees * 10^-2) to match the expected degrees * 10^-5
|
|
||||||
}
|
|
||||||
if (reader.satellites.isValid()) {
|
|
||||||
setNumSatellites(reader.satellites.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
// expect gps pos lat=37.520825, lon=-122.309162, alt=158
|
||||||
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude,
|
DEBUG_MSG("new NMEA GPS pos lat=%f, lon=%f, alt=%d, hdop=%g, heading=%f\n", latitude * 1e-7, longitude * 1e-7, altitude,
|
||||||
@@ -102,7 +102,7 @@ bool NMEAGPS::whileIdle()
|
|||||||
// First consume any chars that have piled up at the receiver
|
// First consume any chars that have piled up at the receiver
|
||||||
while (_serial_gps->available() > 0) {
|
while (_serial_gps->available() > 0) {
|
||||||
int c = _serial_gps->read();
|
int c = _serial_gps->read();
|
||||||
// DEBUG_MSG("%c", c);
|
//DEBUG_MSG("%c", c);
|
||||||
isValid |= reader.encode(c);
|
isValid |= reader.encode(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,15 +46,18 @@ void updateDisplay(uint8_t *blackFrame = framePtr)
|
|||||||
|
|
||||||
EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl)
|
EInkDisplay::EInkDisplay(uint8_t address, int sda, int scl)
|
||||||
{
|
{
|
||||||
setGeometry(GEOMETRY_128_64); // FIXME - currently we lie and claim 128x64 because I'm not yet sure other resolutions will
|
setGeometry(GEOMETRY_RAWMODE, EPD_WIDTH, EPD_HEIGHT);
|
||||||
// work ie GEOMETRY_RAWMODE
|
// setGeometry(GEOMETRY_RAWMODE, 128, 64); // old resolution
|
||||||
|
// setGeometry(GEOMETRY_128_64); // We originally used this because I wasn't sure if rawmode worked - it does
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME quick hack to limit drawing to a very slow rate
|
// FIXME quick hack to limit drawing to a very slow rate
|
||||||
uint32_t lastDrawMsec;
|
uint32_t lastDrawMsec;
|
||||||
|
|
||||||
// Write the buffer to the display memory
|
/**
|
||||||
void EInkDisplay::display(void)
|
* Force a display update if we haven't drawn within the specified msecLimit
|
||||||
|
*/
|
||||||
|
bool EInkDisplay::forceDisplay(uint32_t msecLimit)
|
||||||
{
|
{
|
||||||
// No need to grab this lock because we are on our own SPI bus
|
// No need to grab this lock because we are on our own SPI bus
|
||||||
// concurrency::LockGuard g(spiLock);
|
// concurrency::LockGuard g(spiLock);
|
||||||
@@ -62,16 +65,16 @@ void EInkDisplay::display(void)
|
|||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
uint32_t sinceLast = now - lastDrawMsec;
|
uint32_t sinceLast = now - lastDrawMsec;
|
||||||
|
|
||||||
if (framePtr && (sinceLast > 60 * 1000 || lastDrawMsec == 0)) {
|
if (framePtr && (sinceLast > msecLimit || lastDrawMsec == 0)) {
|
||||||
lastDrawMsec = now;
|
lastDrawMsec = now;
|
||||||
|
|
||||||
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
|
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
|
||||||
// tft.drawBitmap(0, 0, buffer, 128, 64, TFT_YELLOW, TFT_BLACK);
|
// tft.drawBitmap(0, 0, buffer, 128, 64, TFT_YELLOW, TFT_BLACK);
|
||||||
for (uint8_t y = 0; y < SCREEN_HEIGHT; y++) {
|
for (uint8_t y = 0; y < displayHeight; y++) {
|
||||||
for (uint8_t x = 0; x < SCREEN_WIDTH; x++) {
|
for (uint8_t x = 0; x < displayWidth; x++) {
|
||||||
|
|
||||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
|
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
|
||||||
auto b = buffer[x + (y / 8) * SCREEN_WIDTH];
|
auto b = buffer[x + (y / 8) * displayWidth];
|
||||||
auto isset = b & (1 << (y & 7));
|
auto isset = b & (1 << (y & 7));
|
||||||
frame.drawPixel(x, y, isset ? INK : PAPER);
|
frame.drawPixel(x, y, isset ? INK : PAPER);
|
||||||
}
|
}
|
||||||
@@ -83,11 +86,25 @@ void EInkDisplay::display(void)
|
|||||||
updateDisplay(); // Send image to display and refresh
|
updateDisplay(); // Send image to display and refresh
|
||||||
DEBUG_MSG("done\n");
|
DEBUG_MSG("done\n");
|
||||||
|
|
||||||
// Put screen to sleep to save power
|
// Put screen to sleep to save power
|
||||||
ePaper.Sleep();
|
ePaper.Sleep();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// DEBUG_MSG("Skipping eink display\n");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write the buffer to the display memory
|
||||||
|
void EInkDisplay::display(void)
|
||||||
|
{
|
||||||
|
// We don't allow regular 'dumb' display() calls to draw on eink until we've shown
|
||||||
|
// at least one forceDisplay() keyframe. This prevents flashing when we should the critical
|
||||||
|
// bootscreen (that we want to look nice)
|
||||||
|
if (lastDrawMsec)
|
||||||
|
forceDisplay(slowUpdateMsec); // Show the first screen a few seconds after boot, then slower
|
||||||
|
}
|
||||||
|
|
||||||
// Send a command to the display (low level function)
|
// Send a command to the display (low level function)
|
||||||
void EInkDisplay::sendCommand(uint8_t com)
|
void EInkDisplay::sendCommand(uint8_t com)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,15 +14,26 @@
|
|||||||
*/
|
*/
|
||||||
class EInkDisplay : public OLEDDisplay
|
class EInkDisplay : public OLEDDisplay
|
||||||
{
|
{
|
||||||
|
/// How often should we update the display
|
||||||
|
/// thereafter we do once per 5 minutes
|
||||||
|
uint32_t slowUpdateMsec = 5 * 60 * 1000;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/* constructor
|
/* constructor
|
||||||
FIXME - the parameters are not used, just a temporary hack to keep working like the old displays
|
FIXME - the parameters are not used, just a temporary hack to keep working like the old displays
|
||||||
*/
|
*/
|
||||||
EInkDisplay(uint8_t address, int sda, int scl);
|
EInkDisplay(uint8_t address, int sda, int scl);
|
||||||
|
|
||||||
// Write the buffer to the display memory
|
// Write the buffer to the display memory (for eink we only do this occasionally)
|
||||||
virtual void display(void);
|
virtual void display(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Force a display update if we haven't drawn within the specified msecLimit
|
||||||
|
*
|
||||||
|
* @return true if we did draw the screen
|
||||||
|
*/
|
||||||
|
bool forceDisplay(uint32_t msecLimit = 1000);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// the header size of the buffer used, e.g. for the SPI command header
|
// the header size of the buffer used, e.g. for the SPI command header
|
||||||
virtual int getBufferOffset(void) { return 0; }
|
virtual int getBufferOffset(void) { return 0; }
|
||||||
@@ -33,3 +44,5 @@ class EInkDisplay : public OLEDDisplay
|
|||||||
// Connect to the display
|
// Connect to the display
|
||||||
virtual bool connect();
|
virtual bool connect();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -58,39 +58,73 @@ static char ourId[5];
|
|||||||
static bool heartbeat = false;
|
static bool heartbeat = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// We used to use constants for this - now we pull from the device at startup
|
||||||
|
//#define SCREEN_WIDTH 128
|
||||||
|
//#define SCREEN_HEIGHT 64
|
||||||
|
|
||||||
|
static uint16_t displayWidth, displayHeight;
|
||||||
|
|
||||||
|
#define SCREEN_WIDTH displayWidth
|
||||||
|
#define SCREEN_HEIGHT displayHeight
|
||||||
|
|
||||||
|
#ifdef HAS_EINK
|
||||||
|
// The screen is bigger so use bigger fonts
|
||||||
|
#define FONT_SMALL ArialMT_Plain_16
|
||||||
|
#define FONT_MEDIUM ArialMT_Plain_24
|
||||||
|
#define FONT_LARGE ArialMT_Plain_24
|
||||||
|
#else
|
||||||
|
#define FONT_SMALL ArialMT_Plain_10
|
||||||
|
#define FONT_MEDIUM ArialMT_Plain_16
|
||||||
|
#define FONT_LARGE ArialMT_Plain_24
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define fontHeight(font) ((font)[1] + 1) // height is position 1
|
||||||
|
|
||||||
|
#define FONT_HEIGHT_SMALL fontHeight(FONT_SMALL)
|
||||||
|
#define FONT_HEIGHT_MEDIUM fontHeight(FONT_MEDIUM)
|
||||||
|
|
||||||
|
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
|
||||||
|
|
||||||
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
// draw an xbm image.
|
// draw an xbm image.
|
||||||
// Please note that everything that should be transitioned
|
// Please note that everything that should be transitioned
|
||||||
// needs to be drawn relative to x and y
|
// needs to be drawn relative to x and y
|
||||||
display->drawXbm(x + 32, y, icon_width, icon_height, (const uint8_t *)icon_bits);
|
|
||||||
|
|
||||||
display->setFont(ArialMT_Plain_16);
|
// draw centered left to right and centered above the one line of app text
|
||||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2,
|
||||||
display->drawString(64 + x, SCREEN_HEIGHT - FONT_HEIGHT_16, "meshtastic.org");
|
icon_width, icon_height, (const uint8_t *)icon_bits);
|
||||||
display->setFont(ArialMT_Plain_10);
|
|
||||||
const char *region = xstr(HW_VERSION);
|
display->setFont(FONT_MEDIUM);
|
||||||
if (*region && region[3] == '-') // Skip past 1.0- in the 1.0-EU865 string
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
region += 4;
|
const char *title = "meshtastic.org";
|
||||||
|
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
|
||||||
|
display->setFont(FONT_SMALL);
|
||||||
|
|
||||||
|
const char *region = myRegion ? myRegion->name : NULL;
|
||||||
|
if (region)
|
||||||
|
display->drawString(x + 0, y + 0, region);
|
||||||
|
|
||||||
char buf[16];
|
char buf[16];
|
||||||
snprintf(buf, sizeof(buf), "%s",
|
snprintf(buf, sizeof(buf), "%s",
|
||||||
xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long
|
xstr(APP_VERSION)); // Note: we don't bother printing region or now, it makes the string too long
|
||||||
display->drawString(SCREEN_WIDTH - 20, 0, buf);
|
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf);
|
||||||
|
screen->forceDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawFrameBluetooth(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||||
display->setFont(ArialMT_Plain_16);
|
display->setFont(FONT_MEDIUM);
|
||||||
display->drawString(64 + x, y, "Bluetooth");
|
display->drawString(64 + x, y, "Bluetooth");
|
||||||
|
|
||||||
display->setFont(ArialMT_Plain_10);
|
display->setFont(FONT_SMALL);
|
||||||
display->drawString(64 + x, FONT_HEIGHT + y + 2, "Enter this code");
|
display->drawString(64 + x, FONT_HEIGHT_SMALL + y + 2, "Enter this code");
|
||||||
|
|
||||||
display->setFont(ArialMT_Plain_24);
|
display->setFont(FONT_LARGE);
|
||||||
display->drawString(64 + x, 26 + y, btPIN);
|
display->drawString(64 + x, 26 + y, btPIN);
|
||||||
|
|
||||||
display->setFont(ArialMT_Plain_10);
|
display->setFont(FONT_SMALL);
|
||||||
char buf[30];
|
char buf[30];
|
||||||
const char *name = "Name: ";
|
const char *name = "Name: ";
|
||||||
strcpy(buf, name);
|
strcpy(buf, name);
|
||||||
@@ -112,10 +146,10 @@ static void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state
|
|||||||
// with the third parameter you can define the width after which words will
|
// with the third parameter you can define the width after which words will
|
||||||
// be wrapped. Currently only spaces and "-" are allowed for wrapping
|
// be wrapped. Currently only spaces and "-" are allowed for wrapping
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||||
display->setFont(ArialMT_Plain_16);
|
display->setFont(FONT_MEDIUM);
|
||||||
String sender = (node && node->has_user) ? node->user.short_name : "???";
|
String sender = (node && node->has_user) ? node->user.short_name : "???";
|
||||||
display->drawString(0 + x, 0 + y, sender);
|
display->drawString(0 + x, 0 + y, sender);
|
||||||
display->setFont(ArialMT_Plain_10);
|
display->setFont(FONT_SMALL);
|
||||||
|
|
||||||
// the max length of this buffer is much longer than we can possibly print
|
// the max length of this buffer is much longer than we can possibly print
|
||||||
static char tempBuf[96];
|
static char tempBuf[96];
|
||||||
@@ -135,8 +169,8 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char *
|
|||||||
int xo = x, yo = y;
|
int xo = x, yo = y;
|
||||||
while (*f) {
|
while (*f) {
|
||||||
display->drawString(xo, yo, *f);
|
display->drawString(xo, yo, *f);
|
||||||
yo += FONT_HEIGHT;
|
yo += FONT_HEIGHT_SMALL;
|
||||||
if (yo > SCREEN_HEIGHT - FONT_HEIGHT) {
|
if (yo > SCREEN_HEIGHT - FONT_HEIGHT_SMALL) {
|
||||||
xo += SCREEN_WIDTH / 2;
|
xo += SCREEN_WIDTH / 2;
|
||||||
yo = 0;
|
yo = 0;
|
||||||
}
|
}
|
||||||
@@ -162,14 +196,14 @@ static void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char *
|
|||||||
// Wrap to next row, if needed.
|
// Wrap to next row, if needed.
|
||||||
if (++col >= COLUMNS) {
|
if (++col >= COLUMNS) {
|
||||||
xo = x;
|
xo = x;
|
||||||
yo += FONT_HEIGHT;
|
yo += FONT_HEIGHT_SMALL;
|
||||||
col = 0;
|
col = 0;
|
||||||
}
|
}
|
||||||
f++;
|
f++;
|
||||||
}
|
}
|
||||||
if (col != 0) {
|
if (col != 0) {
|
||||||
// Include last incomplete line in our total.
|
// Include last incomplete line in our total.
|
||||||
yo += FONT_HEIGHT;
|
yo += FONT_HEIGHT_SMALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return yo;
|
return yo;
|
||||||
@@ -236,7 +270,7 @@ static void drawGPS(OLEDDisplay *display, int16_t x, int16_t y, const GPSStatus
|
|||||||
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
|
display->drawFastImage(x + 24, y, 8, 8, imgSatellite);
|
||||||
|
|
||||||
// Draw the number of satellites
|
// Draw the number of satellites
|
||||||
sprintf(satsString, "%d", gps->getNumSatellites());
|
sprintf(satsString, "%lu", gps->getNumSatellites());
|
||||||
display->drawString(x + 34, y - 2, satsString);
|
display->drawString(x + 34, y - 2, satsString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -476,7 +510,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
|
|
||||||
NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
|
NodeInfo *node = nodeDB.getNodeByIndex(nodeIndex);
|
||||||
|
|
||||||
display->setFont(ArialMT_Plain_10);
|
display->setFont(FONT_SMALL);
|
||||||
|
|
||||||
// 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);
|
||||||
@@ -489,11 +523,11 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
uint32_t agoSecs = sinceLastSeen(node);
|
uint32_t agoSecs = sinceLastSeen(node);
|
||||||
static char lastStr[20];
|
static char lastStr[20];
|
||||||
if (agoSecs < 120) // last 2 mins?
|
if (agoSecs < 120) // last 2 mins?
|
||||||
snprintf(lastStr, sizeof(lastStr), "%u seconds ago", agoSecs);
|
snprintf(lastStr, sizeof(lastStr), "%lu seconds ago", agoSecs);
|
||||||
else if (agoSecs < 120 * 60) // last 2 hrs
|
else if (agoSecs < 120 * 60) // last 2 hrs
|
||||||
snprintf(lastStr, sizeof(lastStr), "%u minutes ago", agoSecs / 60);
|
snprintf(lastStr, sizeof(lastStr), "%lu minutes ago", agoSecs / 60);
|
||||||
else
|
else
|
||||||
snprintf(lastStr, sizeof(lastStr), "%u hours ago", agoSecs / 60 / 60);
|
snprintf(lastStr, sizeof(lastStr), "%lu hours ago", agoSecs / 60 / 60);
|
||||||
|
|
||||||
static char distStr[20];
|
static char distStr[20];
|
||||||
strcpy(distStr, "? km"); // might not have location data
|
strcpy(distStr, "? km"); // might not have location data
|
||||||
@@ -531,7 +565,7 @@ static void drawNodeInfo(OLEDDisplay *display, OLEDDisplayUiState *state, int16_
|
|||||||
// direction to node is unknown so display question mark
|
// direction to node is unknown so display question mark
|
||||||
// Debug info for gps lock errors
|
// Debug info for gps lock errors
|
||||||
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
|
// DEBUG_MSG("ourNode %d, ourPos %d, theirPos %d\n", !!ourNode, ourNode && hasPosition(ourNode), hasPosition(node));
|
||||||
display->drawString(compassX - FONT_HEIGHT / 4, compassY - FONT_HEIGHT / 2, "?");
|
display->drawString(compassX - FONT_HEIGHT_SMALL / 4, compassY - FONT_HEIGHT_SMALL / 2, "?");
|
||||||
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
display->drawCircle(compassX, compassY, COMPASS_DIAM / 2);
|
||||||
|
|
||||||
// Must be after distStr is populated
|
// Must be after distStr is populated
|
||||||
@@ -561,7 +595,8 @@ void _screen_header()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), ui(&dispdev) {
|
Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), ui(&dispdev)
|
||||||
|
{
|
||||||
cmdQueue.setReader(this);
|
cmdQueue.setReader(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -596,6 +631,10 @@ void Screen::setup()
|
|||||||
|
|
||||||
// Initialising the UI will init the display too.
|
// Initialising the UI will init the display too.
|
||||||
ui.init();
|
ui.init();
|
||||||
|
|
||||||
|
displayWidth = dispdev.width();
|
||||||
|
displayHeight = dispdev.height();
|
||||||
|
|
||||||
ui.setTimePerTransition(300); // msecs
|
ui.setTimePerTransition(300); // msecs
|
||||||
ui.setIndicatorPosition(BOTTOM);
|
ui.setIndicatorPosition(BOTTOM);
|
||||||
// Defines where the first frame is located in the bar.
|
// Defines where the first frame is located in the bar.
|
||||||
@@ -645,6 +684,14 @@ void Screen::setup()
|
|||||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Screen::forceDisplay()
|
||||||
|
{
|
||||||
|
// Nasty hack to force epaper updates for 'key' frames. FIXME, cleanup.
|
||||||
|
#ifdef HAS_EINK
|
||||||
|
dispdev.forceDisplay();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int32_t Screen::runOnce()
|
int32_t Screen::runOnce()
|
||||||
{
|
{
|
||||||
// If we don't have a screen, don't ever spend any CPU for us.
|
// If we don't have a screen, don't ever spend any CPU for us.
|
||||||
@@ -655,7 +702,8 @@ int32_t Screen::runOnce()
|
|||||||
|
|
||||||
// Show boot screen for first 3 seconds, then switch to normal operation.
|
// Show boot screen for first 3 seconds, then switch to normal operation.
|
||||||
static bool showingBootScreen = true;
|
static bool showingBootScreen = true;
|
||||||
if (showingBootScreen && (millis() > 3000)) {
|
if (showingBootScreen && (millis() > 5000)) {
|
||||||
|
DEBUG_MSG("Done with boot screen...\n");
|
||||||
stopBootScreen();
|
stopBootScreen();
|
||||||
showingBootScreen = false;
|
showingBootScreen = false;
|
||||||
}
|
}
|
||||||
@@ -701,6 +749,10 @@ int32_t Screen::runOnce()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this must be before the frameState == FIXED check, because we always
|
||||||
|
// want to draw at least one FIXED frame before doing forceDisplay
|
||||||
|
ui.update();
|
||||||
|
|
||||||
// Switch to a low framerate (to save CPU) when we are not in transition
|
// Switch to a low framerate (to save CPU) when we are not in transition
|
||||||
// but we should only call setTargetFPS when framestate changes, because
|
// but we should only call setTargetFPS when framestate changes, because
|
||||||
// otherwise that breaks animations.
|
// otherwise that breaks animations.
|
||||||
@@ -709,6 +761,7 @@ int32_t Screen::runOnce()
|
|||||||
DEBUG_MSG("Setting idle framerate\n");
|
DEBUG_MSG("Setting idle framerate\n");
|
||||||
targetFramerate = IDLE_FRAMERATE;
|
targetFramerate = IDLE_FRAMERATE;
|
||||||
ui.setTargetFPS(targetFramerate);
|
ui.setTargetFPS(targetFramerate);
|
||||||
|
forceDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
// While showing the bootscreen or Bluetooth pair screen all of our
|
// While showing the bootscreen or Bluetooth pair screen all of our
|
||||||
@@ -717,8 +770,6 @@ int32_t Screen::runOnce()
|
|||||||
// standard screen loop handling here
|
// standard screen loop handling here
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.update();
|
|
||||||
|
|
||||||
// DEBUG_MSG("want fps %d, fixed=%d\n", targetFramerate,
|
// DEBUG_MSG("want fps %d, fixed=%d\n", targetFramerate,
|
||||||
// ui.getUiState()->frameState); If we are scrolling we need to be called
|
// ui.getUiState()->frameState); If we are scrolling we need to be called
|
||||||
// soon, otherwise just 1 fps (to save CPU) We also ask to be called twice
|
// soon, otherwise just 1 fps (to save CPU) We also ask to be called twice
|
||||||
@@ -785,6 +836,8 @@ void Screen::setFrames()
|
|||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
setFastFramerate(); // Draw ASAP
|
||||||
}
|
}
|
||||||
|
|
||||||
void Screen::handleStartBluetoothPinScreen(uint32_t pin)
|
void Screen::handleStartBluetoothPinScreen(uint32_t pin)
|
||||||
@@ -794,16 +847,17 @@ void Screen::handleStartBluetoothPinScreen(uint32_t pin)
|
|||||||
|
|
||||||
static FrameCallback btFrames[] = {drawFrameBluetooth};
|
static FrameCallback btFrames[] = {drawFrameBluetooth};
|
||||||
|
|
||||||
snprintf(btPIN, sizeof(btPIN), "%06u", pin);
|
snprintf(btPIN, sizeof(btPIN), "%06lu", pin);
|
||||||
|
|
||||||
ui.disableAllIndicators();
|
ui.disableAllIndicators();
|
||||||
ui.setFrames(btFrames, 1);
|
ui.setFrames(btFrames, 1);
|
||||||
|
setFastFramerate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Screen::handlePrint(const char *text)
|
void Screen::handlePrint(const char *text)
|
||||||
{
|
{
|
||||||
DEBUG_MSG("Screen: %s", text);
|
DEBUG_MSG("Screen: %s", text);
|
||||||
if (!useDisplay)
|
if (!useDisplay || !showingNormalScreen)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dispdev.print(text);
|
dispdev.print(text);
|
||||||
@@ -814,22 +868,27 @@ void Screen::handleOnPress()
|
|||||||
// If screen was off, just wake it, otherwise advance to next frame
|
// If screen was off, just wake it, otherwise advance to next frame
|
||||||
// If we are in a transition, the press must have bounced, drop it.
|
// If we are in a transition, the press must have bounced, drop it.
|
||||||
if (ui.getUiState()->frameState == FIXED) {
|
if (ui.getUiState()->frameState == FIXED) {
|
||||||
setInterval(0); // redraw ASAP
|
|
||||||
ui.nextFrame();
|
ui.nextFrame();
|
||||||
|
|
||||||
DEBUG_MSG("Setting fast framerate\n");
|
setFastFramerate();
|
||||||
|
|
||||||
// We are about to start a transition so speed up fps
|
|
||||||
targetFramerate = TRANSITION_FRAMERATE;
|
|
||||||
ui.setTargetFPS(targetFramerate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Screen::setFastFramerate()
|
||||||
|
{
|
||||||
|
DEBUG_MSG("Setting fast framerate\n");
|
||||||
|
|
||||||
|
// We are about to start a transition so speed up fps
|
||||||
|
targetFramerate = TRANSITION_FRAMERATE;
|
||||||
|
ui.setTargetFPS(targetFramerate);
|
||||||
|
setInterval(0); // redraw ASAP
|
||||||
|
}
|
||||||
|
|
||||||
void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
{
|
{
|
||||||
displayedNodeNum = 0; // Not currently showing a node pane
|
displayedNodeNum = 0; // Not currently showing a node pane
|
||||||
|
|
||||||
display->setFont(ArialMT_Plain_10);
|
display->setFont(FONT_SMALL);
|
||||||
|
|
||||||
// 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);
|
||||||
@@ -851,13 +910,13 @@ void DebugInfo::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
|
|||||||
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
|
drawGPS(display, x + (SCREEN_WIDTH * 0.63), y + 2, gpsStatus);
|
||||||
|
|
||||||
// Draw the channel name
|
// Draw the channel name
|
||||||
display->drawString(x, y + FONT_HEIGHT, channelStr);
|
display->drawString(x, y + FONT_HEIGHT_SMALL, channelStr);
|
||||||
// Draw our hardware ID to assist with bluetooth pairing
|
// Draw our hardware ID to assist with bluetooth pairing
|
||||||
display->drawFastImage(x + SCREEN_WIDTH - (10) - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT, 8, 8, imgInfo);
|
display->drawFastImage(x + SCREEN_WIDTH - (10) - display->getStringWidth(ourId), y + 2 + FONT_HEIGHT_SMALL, 8, 8, imgInfo);
|
||||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(ourId), y + FONT_HEIGHT, ourId);
|
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(ourId), y + FONT_HEIGHT_SMALL, ourId);
|
||||||
|
|
||||||
// Draw any log messages
|
// Draw any log messages
|
||||||
display->drawLogBuffer(x, y + (FONT_HEIGHT * 2));
|
display->drawLogBuffer(x, y + (FONT_HEIGHT_SMALL * 2));
|
||||||
|
|
||||||
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
|
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
|
||||||
#ifdef SHOW_REDRAWS
|
#ifdef SHOW_REDRAWS
|
||||||
@@ -876,7 +935,7 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
|||||||
|
|
||||||
displayedNodeNum = 0; // Not currently showing a node pane
|
displayedNodeNum = 0; // Not currently showing a node pane
|
||||||
|
|
||||||
display->setFont(ArialMT_Plain_10);
|
display->setFont(FONT_SMALL);
|
||||||
|
|
||||||
// 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);
|
||||||
@@ -907,86 +966,90 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
|||||||
|
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
if (radioConfig.preferences.wifi_ap_mode) {
|
if (radioConfig.preferences.wifi_ap_mode) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "IP: " + String(WiFi.softAPIP().toString().c_str()));
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.softAPIP().toString().c_str()));
|
||||||
} else {
|
} else {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "IP: " + String(WiFi.localIP().toString().c_str()));
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IP: " + String(WiFi.localIP().toString().c_str()));
|
||||||
}
|
}
|
||||||
} else if (WiFi.status() == WL_NO_SSID_AVAIL) {
|
} else if (WiFi.status() == WL_NO_SSID_AVAIL) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "SSID Not Found");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "SSID Not Found");
|
||||||
} else if (WiFi.status() == WL_CONNECTION_LOST) {
|
} else if (WiFi.status() == WL_CONNECTION_LOST) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Lost");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Lost");
|
||||||
} else if (WiFi.status() == WL_CONNECT_FAILED) {
|
} else if (WiFi.status() == WL_CONNECT_FAILED) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Failed");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Failed");
|
||||||
//} else if (WiFi.status() == WL_DISCONNECTED) {
|
//} else if (WiFi.status() == WL_DISCONNECTED) {
|
||||||
// display->drawString(x, y + FONT_HEIGHT * 1, "Disconnected");
|
// display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Disconnected");
|
||||||
} else if (WiFi.status() == WL_IDLE_STATUS) {
|
} else if (WiFi.status() == WL_IDLE_STATUS) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "Idle ... Reconnecting");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Idle ... Reconnecting");
|
||||||
} else {
|
} else {
|
||||||
// Codes:
|
// Codes:
|
||||||
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
|
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-reason-code
|
||||||
if (getWifiDisconnectReason() == 2) {
|
if (getWifiDisconnectReason() == 2) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "Authentication Invalid");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Authentication Invalid");
|
||||||
} else if (getWifiDisconnectReason() == 3) {
|
} else if (getWifiDisconnectReason() == 3) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "De-authenticated");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "De-authenticated");
|
||||||
} else if (getWifiDisconnectReason() == 4) {
|
} else if (getWifiDisconnectReason() == 4) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "Disassociated Expired");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Disassociated Expired");
|
||||||
} else if (getWifiDisconnectReason() == 5) {
|
} else if (getWifiDisconnectReason() == 5) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "AP - Too Many Clients");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP - Too Many Clients");
|
||||||
} else if (getWifiDisconnectReason() == 6) {
|
} else if (getWifiDisconnectReason() == 6) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "NOT_AUTHED");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "NOT_AUTHED");
|
||||||
} else if (getWifiDisconnectReason() == 7) {
|
} else if (getWifiDisconnectReason() == 7) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "NOT_ASSOCED");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "NOT_ASSOCED");
|
||||||
} else if (getWifiDisconnectReason() == 8) {
|
} else if (getWifiDisconnectReason() == 8) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "Disassociated");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Disassociated");
|
||||||
} else if (getWifiDisconnectReason() == 9) {
|
} else if (getWifiDisconnectReason() == 9) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "ASSOC_NOT_AUTHED");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "ASSOC_NOT_AUTHED");
|
||||||
} else if (getWifiDisconnectReason() == 10) {
|
} else if (getWifiDisconnectReason() == 10) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "DISASSOC_PWRCAP_BAD");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "DISASSOC_PWRCAP_BAD");
|
||||||
} else if (getWifiDisconnectReason() == 11) {
|
} else if (getWifiDisconnectReason() == 11) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "DISASSOC_SUPCHAN_BAD");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "DISASSOC_SUPCHAN_BAD");
|
||||||
} else if (getWifiDisconnectReason() == 13) {
|
} else if (getWifiDisconnectReason() == 13) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "IE_INVALID");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IE_INVALID");
|
||||||
} else if (getWifiDisconnectReason() == 14) {
|
} else if (getWifiDisconnectReason() == 14) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "MIC_FAILURE");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "MIC_FAILURE");
|
||||||
} else if (getWifiDisconnectReason() == 15) {
|
} else if (getWifiDisconnectReason() == 15) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "AP Handshake Timeout");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP Handshake Timeout");
|
||||||
} else if (getWifiDisconnectReason() == 16) {
|
} else if (getWifiDisconnectReason() == 16) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "GROUP_KEY_UPDATE_TIMEOUT");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "GROUP_KEY_UPDATE_TIMEOUT");
|
||||||
} else if (getWifiDisconnectReason() == 17) {
|
} else if (getWifiDisconnectReason() == 17) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "IE_IN_4WAY_DIFFERS");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "IE_IN_4WAY_DIFFERS");
|
||||||
} else if (getWifiDisconnectReason() == 18) {
|
} else if (getWifiDisconnectReason() == 18) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "Invalid Group Cipher");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Invalid Group Cipher");
|
||||||
} else if (getWifiDisconnectReason() == 19) {
|
} else if (getWifiDisconnectReason() == 19) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "Invalid Pairwise Cipher");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Invalid Pairwise Cipher");
|
||||||
} else if (getWifiDisconnectReason() == 20) {
|
} else if (getWifiDisconnectReason() == 20) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "AKMP_INVALID");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AKMP_INVALID");
|
||||||
} else if (getWifiDisconnectReason() == 21) {
|
} else if (getWifiDisconnectReason() == 21) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "UNSUPP_RSN_IE_VERSION");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "UNSUPP_RSN_IE_VERSION");
|
||||||
} else if (getWifiDisconnectReason() == 22) {
|
} else if (getWifiDisconnectReason() == 22) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "INVALID_RSN_IE_CAP");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "INVALID_RSN_IE_CAP");
|
||||||
} else if (getWifiDisconnectReason() == 23) {
|
} else if (getWifiDisconnectReason() == 23) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "802_1X_AUTH_FAILED");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "802_1X_AUTH_FAILED");
|
||||||
} else if (getWifiDisconnectReason() == 24) {
|
} else if (getWifiDisconnectReason() == 24) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "CIPHER_SUITE_REJECTED");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "CIPHER_SUITE_REJECTED");
|
||||||
} else if (getWifiDisconnectReason() == 200) {
|
} else if (getWifiDisconnectReason() == 200) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "BEACON_TIMEOUT");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "BEACON_TIMEOUT");
|
||||||
} else if (getWifiDisconnectReason() == 201) {
|
} else if (getWifiDisconnectReason() == 201) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "AP Not Found");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AP Not Found");
|
||||||
} else if (getWifiDisconnectReason() == 202) {
|
} else if (getWifiDisconnectReason() == 202) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "AUTH_FAIL");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "AUTH_FAIL");
|
||||||
} else if (getWifiDisconnectReason() == 203) {
|
} else if (getWifiDisconnectReason() == 203) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "ASSOC_FAIL");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "ASSOC_FAIL");
|
||||||
} else if (getWifiDisconnectReason() == 204) {
|
} else if (getWifiDisconnectReason() == 204) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "HANDSHAKE_TIMEOUT");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "HANDSHAKE_TIMEOUT");
|
||||||
} else if (getWifiDisconnectReason() == 205) {
|
} else if (getWifiDisconnectReason() == 205) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "Connection Failed");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Connection Failed");
|
||||||
} else {
|
} else {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1, "Unknown Status");
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1, "Unknown Status");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
display->drawString(x, y + FONT_HEIGHT * 2, "SSID: " + String(wifiName));
|
if ((millis() / 1000) % 2) {
|
||||||
display->drawString(x, y + FONT_HEIGHT * 3, "PWD: " + String(wifiPsw));
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
|
||||||
|
} else {
|
||||||
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "PWD: " + String(wifiPsw));
|
||||||
|
}
|
||||||
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 3, "http://meshtastic.local");
|
||||||
|
|
||||||
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
|
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
|
||||||
#ifdef SHOW_REDRAWS
|
#ifdef SHOW_REDRAWS
|
||||||
@@ -1001,7 +1064,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
|||||||
{
|
{
|
||||||
displayedNodeNum = 0; // Not currently showing a node pane
|
displayedNodeNum = 0; // Not currently showing a node pane
|
||||||
|
|
||||||
display->setFont(ArialMT_Plain_10);
|
display->setFont(FONT_SMALL);
|
||||||
|
|
||||||
// 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);
|
||||||
@@ -1035,15 +1098,21 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
|||||||
minutes %= 60;
|
minutes %= 60;
|
||||||
hours %= 24;
|
hours %= 24;
|
||||||
|
|
||||||
display->drawString(x, y + FONT_HEIGHT * 1,
|
display->drawString(x, y + FONT_HEIGHT_SMALL * 1,
|
||||||
String(days) + "d " + (hours < 10 ? "0" : "") + String(hours) + ":" + (minutes < 10 ? "0" : "") +
|
String(days) + "d " + (hours < 10 ? "0" : "") + String(hours) + ":" + (minutes < 10 ? "0" : "") +
|
||||||
String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds));
|
String(minutes) + ":" + (seconds < 10 ? "0" : "") + String(seconds));
|
||||||
|
|
||||||
|
#ifndef NO_ESP32
|
||||||
|
// Show CPU Frequency.
|
||||||
|
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("CPU " + String(getCpuFrequencyMhz()) + "MHz"),
|
||||||
|
y + FONT_HEIGHT_SMALL * 1, "CPU " + String(getCpuFrequencyMhz()) + "MHz");
|
||||||
|
#endif
|
||||||
|
|
||||||
// Line 3
|
// Line 3
|
||||||
drawGPSAltitude(display, x, y + FONT_HEIGHT * 2, gpsStatus);
|
drawGPSAltitude(display, x, y + FONT_HEIGHT_SMALL * 2, gpsStatus);
|
||||||
|
|
||||||
// Line 4
|
// Line 4
|
||||||
drawGPScoordinates(display, x, y + FONT_HEIGHT * 3, gpsStatus);
|
drawGPScoordinates(display, x, y + FONT_HEIGHT_SMALL * 3, gpsStatus);
|
||||||
|
|
||||||
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
|
/* Display a heartbeat pixel that blinks every time the frame is redrawn */
|
||||||
#ifdef SHOW_REDRAWS
|
#ifdef SHOW_REDRAWS
|
||||||
@@ -1072,10 +1141,8 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
|||||||
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
|
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
|
||||||
switch (arg->getStatusType()) {
|
switch (arg->getStatusType()) {
|
||||||
case STATUS_TYPE_NODE:
|
case STATUS_TYPE_NODE:
|
||||||
if (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
|
if (showingNormalScreen && (nodeDB.updateTextMessage || nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal())) {
|
||||||
setFrames(); // Regen the list of screens
|
setFrames(); // Regen the list of screens
|
||||||
prevFrame = -1; // Force a GUI update
|
|
||||||
setInterval(0); // Update the screen right away
|
|
||||||
}
|
}
|
||||||
nodeDB.updateGUI = false;
|
nodeDB.updateGUI = false;
|
||||||
nodeDB.updateTextMessage = false;
|
nodeDB.updateTextMessage = false;
|
||||||
|
|||||||
@@ -180,6 +180,9 @@ class Screen : public concurrency::OSThread
|
|||||||
|
|
||||||
int handleStatusUpdate(const meshtastic::Status *arg);
|
int handleStatusUpdate(const meshtastic::Status *arg);
|
||||||
|
|
||||||
|
/// Used to force (super slow) eink displays to draw critical frames
|
||||||
|
void forceDisplay();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/// Updates the UI.
|
/// Updates the UI.
|
||||||
//
|
//
|
||||||
@@ -202,7 +205,7 @@ class Screen : public concurrency::OSThread
|
|||||||
return true; // claim success if our display is not in use
|
return true; // claim success if our display is not in use
|
||||||
else {
|
else {
|
||||||
bool success = cmdQueue.enqueue(cmd, 0);
|
bool success = cmdQueue.enqueue(cmd, 0);
|
||||||
setInterval(0); // handle ASAP
|
enabled = true; // handle ASAP (we are the registered reader for cmdQueue, but might have been disabled)
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,6 +219,9 @@ class Screen : public concurrency::OSThread
|
|||||||
/// Rebuilds our list of frames (screens) to default ones.
|
/// Rebuilds our list of frames (screens) to default ones.
|
||||||
void setFrames();
|
void setFrames();
|
||||||
|
|
||||||
|
/// Try to start drawing ASAP
|
||||||
|
void setFastFramerate();
|
||||||
|
|
||||||
/// Called when debug screen is to be drawn, calls through to debugInfo.drawFrame.
|
/// Called when debug screen is to be drawn, calls through to debugInfo.drawFrame.
|
||||||
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
static void drawDebugInfoTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ static TFT_eSPI tft = TFT_eSPI(); // Invoke library, pins defined in User_Setup.
|
|||||||
|
|
||||||
TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl)
|
TFTDisplay::TFTDisplay(uint8_t address, int sda, int scl)
|
||||||
{
|
{
|
||||||
setGeometry(GEOMETRY_128_64); // FIXME - currently we lie and claim 128x64 because I'm not yet sure other resolutions will
|
setGeometry(GEOMETRY_RAWMODE, 160, 80);
|
||||||
// work ie GEOMETRY_RAWMODE
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the buffer to the display memory
|
// Write the buffer to the display memory
|
||||||
@@ -22,11 +21,11 @@ void TFTDisplay::display(void)
|
|||||||
|
|
||||||
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
|
// FIXME - only draw bits have changed (use backbuf similar to the other displays)
|
||||||
// tft.drawBitmap(0, 0, buffer, 128, 64, TFT_YELLOW, TFT_BLACK);
|
// tft.drawBitmap(0, 0, buffer, 128, 64, TFT_YELLOW, TFT_BLACK);
|
||||||
for (uint8_t y = 0; y < SCREEN_HEIGHT; y++) {
|
for (uint8_t y = 0; y < displayHeight; y++) {
|
||||||
for (uint8_t x = 0; x < SCREEN_WIDTH; x++) {
|
for (uint8_t x = 0; x < displayWidth; x++) {
|
||||||
|
|
||||||
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
|
// get src pixel in the page based ordering the OLED lib uses FIXME, super inefficent
|
||||||
auto b = buffer[x + (y / 8) * SCREEN_WIDTH];
|
auto b = buffer[x + (y / 8) * displayWidth];
|
||||||
auto isset = b & (1 << (y & 7));
|
auto isset = b & (1 << (y & 7));
|
||||||
tft.drawPixel(x, y, isset ? TFT_WHITE : TFT_BLACK);
|
tft.drawPixel(x, y, isset ? TFT_WHITE : TFT_BLACK);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,7 @@
|
|||||||
|
|
||||||
#include "fonts.h"
|
#include "fonts.h"
|
||||||
|
|
||||||
#define FONT_HEIGHT 14 // actually 13 for "Arial 10" but want a little extra space
|
|
||||||
#define FONT_HEIGHT_16 (ArialMT_Plain_16[1] + 1)
|
|
||||||
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||||
#define SCREEN_WIDTH 128
|
|
||||||
#define SCREEN_HEIGHT 64
|
|
||||||
#define TRANSITION_FRAMERATE 30 // fps
|
#define TRANSITION_FRAMERATE 30 // fps
|
||||||
#define IDLE_FRAMERATE 1 // in fps
|
#define IDLE_FRAMERATE 1 // in fps
|
||||||
#define COMPASS_DIAM 44
|
#define COMPASS_DIAM 44
|
||||||
|
|||||||
129
src/main.cpp
129
src/main.cpp
@@ -128,31 +128,13 @@ class PowerFSMThread : public OSThread
|
|||||||
|
|
||||||
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake
|
/// If we are in power state we force the CPU to wake every 10ms to check for serial characters (we don't yet wake
|
||||||
/// cpu for serial rx - FIXME)
|
/// cpu for serial rx - FIXME)
|
||||||
canSleep = (powerFSM.getState() != &statePOWER);
|
auto state = powerFSM.getState();
|
||||||
|
canSleep = (state != &statePOWER) && (state != &stateSERIAL);
|
||||||
|
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static Periodic *ledPeriodic;
|
|
||||||
static OSThread *powerFSMthread;
|
|
||||||
|
|
||||||
// Prepare for button presses
|
|
||||||
#ifdef BUTTON_PIN
|
|
||||||
OneButton userButton;
|
|
||||||
#endif
|
|
||||||
#ifdef BUTTON_PIN_ALT
|
|
||||||
OneButton userButtonAlt;
|
|
||||||
#endif
|
|
||||||
void userButtonPressed()
|
|
||||||
{
|
|
||||||
powerFSM.trigger(EVENT_PRESS);
|
|
||||||
}
|
|
||||||
void userButtonPressedLong()
|
|
||||||
{
|
|
||||||
screen->adjustBrightness();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Watch a GPIO and if we get an IRQ, wake the main thread.
|
* Watch a GPIO and if we get an IRQ, wake the main thread.
|
||||||
* Use to add wake on button press
|
* Use to add wake on button press
|
||||||
@@ -168,6 +150,65 @@ void wakeOnIrq(int irq, int mode)
|
|||||||
FALLING);
|
FALLING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ButtonThread : public OSThread
|
||||||
|
{
|
||||||
|
// Prepare for button presses
|
||||||
|
#ifdef BUTTON_PIN
|
||||||
|
OneButton userButton;
|
||||||
|
#endif
|
||||||
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
OneButton userButtonAlt;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
// callback returns the period for the next callback invocation (or 0 if we should no longer be called)
|
||||||
|
ButtonThread() : OSThread("Button")
|
||||||
|
{
|
||||||
|
#ifdef BUTTON_PIN
|
||||||
|
userButton = OneButton(BUTTON_PIN, true, true);
|
||||||
|
userButton.attachClick(userButtonPressed);
|
||||||
|
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||||
|
wakeOnIrq(BUTTON_PIN, FALLING);
|
||||||
|
#endif
|
||||||
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
|
||||||
|
userButtonAlt.attachClick(userButtonPressed);
|
||||||
|
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||||
|
wakeOnIrq(BUTTON_PIN_ALT, FALLING);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// If the button is pressed we suppress CPU sleep until release
|
||||||
|
int32_t runOnce()
|
||||||
|
{
|
||||||
|
canSleep = true; // Assume we should not keep the board awake
|
||||||
|
|
||||||
|
#ifdef BUTTON_PIN
|
||||||
|
userButton.tick();
|
||||||
|
canSleep &= userButton.isIdle();
|
||||||
|
#endif
|
||||||
|
#ifdef BUTTON_PIN_ALT
|
||||||
|
userButtonAlt.tick();
|
||||||
|
canSleep &= userButton.isIdle();
|
||||||
|
#endif
|
||||||
|
// if(!canSleep) DEBUG_MSG("Supressing sleep!\n");
|
||||||
|
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void userButtonPressed()
|
||||||
|
{
|
||||||
|
// DEBUG_MSG("press!\n");
|
||||||
|
powerFSM.trigger(EVENT_PRESS);
|
||||||
|
}
|
||||||
|
static void userButtonPressedLong() { screen->adjustBrightness(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
static Periodic *ledPeriodic;
|
||||||
|
static OSThread *powerFSMthread, *buttonThread;
|
||||||
|
|
||||||
RadioInterface *rIf = NULL;
|
RadioInterface *rIf = NULL;
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
@@ -210,18 +251,7 @@ void setup()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Buttons & LED
|
// Buttons & LED
|
||||||
#ifdef BUTTON_PIN
|
buttonThread = new ButtonThread();
|
||||||
userButton = OneButton(BUTTON_PIN, true, true);
|
|
||||||
userButton.attachClick(userButtonPressed);
|
|
||||||
userButton.attachDuringLongPress(userButtonPressedLong);
|
|
||||||
wakeOnIrq(BUTTON_PIN, FALLING);
|
|
||||||
#endif
|
|
||||||
#ifdef BUTTON_PIN_ALT
|
|
||||||
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
|
|
||||||
userButtonAlt.attachClick(userButtonPressed);
|
|
||||||
userButton.attachDuringLongPress(userButtonPressedLong);
|
|
||||||
wakeOnIrq(BUTTON_PIN_ALT, FALLING);
|
|
||||||
#endif
|
|
||||||
#ifdef LED_PIN
|
#ifdef LED_PIN
|
||||||
pinMode(LED_PIN, OUTPUT);
|
pinMode(LED_PIN, OUTPUT);
|
||||||
digitalWrite(LED_PIN, 1 ^ LED_INVERTED); // turn on for now
|
digitalWrite(LED_PIN, 1 ^ LED_INVERTED); // turn on for now
|
||||||
@@ -260,14 +290,6 @@ void setup()
|
|||||||
|
|
||||||
// Initialize the screen first so we can show the logo while we start up everything else.
|
// Initialize the screen first so we can show the logo while we start up everything else.
|
||||||
screen = new graphics::Screen(SSD1306_ADDRESS);
|
screen = new graphics::Screen(SSD1306_ADDRESS);
|
||||||
#if defined(ST7735_CS) || defined(HAS_EINK)
|
|
||||||
screen->setup();
|
|
||||||
#else
|
|
||||||
if (ssd1306_found)
|
|
||||||
screen->setup();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
screen->print("Started...\n");
|
|
||||||
|
|
||||||
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
|
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
|
||||||
|
|
||||||
@@ -308,6 +330,17 @@ void setup()
|
|||||||
|
|
||||||
service.init();
|
service.init();
|
||||||
|
|
||||||
|
// Don't call screen setup until after nodedb is setup (because we need
|
||||||
|
// the current region name)
|
||||||
|
#if defined(ST7735_CS) || defined(HAS_EINK)
|
||||||
|
screen->setup();
|
||||||
|
#else
|
||||||
|
if (ssd1306_found)
|
||||||
|
screen->setup();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
screen->print("Started...\n");
|
||||||
|
|
||||||
// We have now loaded our saved preferences from flash
|
// We have now loaded our saved preferences from flash
|
||||||
|
|
||||||
// ONCE we will factory reset the GPS for bug #327
|
// ONCE we will factory reset the GPS for bug #327
|
||||||
@@ -361,6 +394,7 @@ void setup()
|
|||||||
|
|
||||||
// Initialize Wifi
|
// Initialize Wifi
|
||||||
initWifi();
|
initWifi();
|
||||||
|
|
||||||
|
|
||||||
if (!rIf)
|
if (!rIf)
|
||||||
recordCriticalError(ErrNoRadio);
|
recordCriticalError(ErrNoRadio);
|
||||||
@@ -410,13 +444,6 @@ void loop()
|
|||||||
esp32Loop();
|
esp32Loop();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef BUTTON_PIN
|
|
||||||
userButton.tick();
|
|
||||||
#endif
|
|
||||||
#ifdef BUTTON_PIN_ALT
|
|
||||||
userButtonAlt.tick();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// For debugging
|
// For debugging
|
||||||
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();
|
// if (rIf) ((RadioLibInterface *)rIf)->isActivelyReceiving();
|
||||||
|
|
||||||
@@ -437,9 +464,9 @@ void loop()
|
|||||||
|
|
||||||
/* if (mainController.nextThread && delayMsec)
|
/* if (mainController.nextThread && delayMsec)
|
||||||
DEBUG_MSG("Next %s in %ld\n", mainController.nextThread->ThreadName.c_str(),
|
DEBUG_MSG("Next %s in %ld\n", mainController.nextThread->ThreadName.c_str(),
|
||||||
mainController.nextThread->tillRun(millis()));
|
mainController.nextThread->tillRun(millis())); */
|
||||||
*/
|
|
||||||
|
|
||||||
// We want to sleep as long as possible here - because it saves power
|
// We want to sleep as long as possible here - because it saves power
|
||||||
mainDelay.delay(delayMsec);
|
mainDelay.delay(delayMsec);
|
||||||
|
// if (didWake) DEBUG_MSG("wake!\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,4 +16,7 @@ struct RegionInfo {
|
|||||||
const char *name; // EU433 etc
|
const char *name; // EU433 etc
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const RegionInfo regions[];
|
extern const RegionInfo regions[];
|
||||||
|
extern const RegionInfo *myRegion;
|
||||||
|
|
||||||
|
extern void initRegion();
|
||||||
@@ -147,6 +147,7 @@ bool NodeDB::resetRadioConfig()
|
|||||||
radioConfig.preferences.wait_bluetooth_secs = 30;
|
radioConfig.preferences.wait_bluetooth_secs = 30;
|
||||||
radioConfig.preferences.position_broadcast_secs = 6 * 60;
|
radioConfig.preferences.position_broadcast_secs = 6 * 60;
|
||||||
radioConfig.preferences.ls_secs = 60;
|
radioConfig.preferences.ls_secs = 60;
|
||||||
|
radioConfig.preferences.region = RegionCode_TW;
|
||||||
}
|
}
|
||||||
|
|
||||||
return didFactoryReset;
|
return didFactoryReset;
|
||||||
@@ -244,6 +245,9 @@ void NodeDB::init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the global myRegion
|
||||||
|
initRegion();
|
||||||
|
|
||||||
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
|
strncpy(myNodeInfo.firmware_version, optstr(APP_VERSION), sizeof(myNodeInfo.firmware_version));
|
||||||
strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
|
strncpy(myNodeInfo.hw_model, HW_VENDOR, sizeof(myNodeInfo.hw_model));
|
||||||
|
|
||||||
|
|||||||
@@ -144,11 +144,18 @@ const char *getChannelName();
|
|||||||
|
|
||||||
PREF_GET(send_owner_interval, 4)
|
PREF_GET(send_owner_interval, 4)
|
||||||
PREF_GET(position_broadcast_secs, 15 * 60)
|
PREF_GET(position_broadcast_secs, 15 * 60)
|
||||||
PREF_GET(wait_bluetooth_secs, 120)
|
|
||||||
|
// Each time we wake into the DARK state allow 1 minute to send and receive BLE packets to the phone
|
||||||
|
PREF_GET(wait_bluetooth_secs, 60)
|
||||||
|
|
||||||
PREF_GET(screen_on_secs, 60)
|
PREF_GET(screen_on_secs, 60)
|
||||||
PREF_GET(mesh_sds_timeout_secs, 2 * 60 * 60)
|
PREF_GET(mesh_sds_timeout_secs, 2 * 60 * 60)
|
||||||
PREF_GET(phone_sds_timeout_sec, 2 * 60 * 60)
|
PREF_GET(phone_sds_timeout_sec, 2 * 60 * 60)
|
||||||
PREF_GET(sds_secs, 365 * 24 * 60 * 60)
|
PREF_GET(sds_secs, 365 * 24 * 60 * 60)
|
||||||
PREF_GET(ls_secs, 60 * 60)
|
|
||||||
|
// We default to sleeping (with bluetooth off for 5 minutes at a time). This seems to be a good tradeoff between
|
||||||
|
// latency for the user sending messages and power savings because of not having to run (expensive) ESP32 bluetooth
|
||||||
|
PREF_GET(ls_secs, 5 * 60)
|
||||||
|
|
||||||
PREF_GET(phone_timeout_secs, 15 * 60)
|
PREF_GET(phone_timeout_secs, 15 * 60)
|
||||||
PREF_GET(min_wake_secs, 10)
|
PREF_GET(min_wake_secs, 10)
|
||||||
|
|||||||
@@ -121,7 +121,14 @@ size_t PhoneAPI::getFromRadio(uint8_t *buf)
|
|||||||
|
|
||||||
case STATE_SEND_RADIO:
|
case STATE_SEND_RADIO:
|
||||||
fromRadioScratch.which_variant = FromRadio_radio_tag;
|
fromRadioScratch.which_variant = FromRadio_radio_tag;
|
||||||
|
|
||||||
fromRadioScratch.variant.radio = radioConfig;
|
fromRadioScratch.variant.radio = radioConfig;
|
||||||
|
|
||||||
|
// NOTE: The phone app needs to know the ls_secs value so it can properly expect sleep behavior.
|
||||||
|
// So even if we internally use 0 to represent 'use default' we still need to send the value we are
|
||||||
|
// using to the app (so that even old phone apps work with new device loads).
|
||||||
|
fromRadioScratch.variant.radio.preferences.ls_secs = getPref_ls_secs();
|
||||||
|
|
||||||
state = STATE_SEND_NODEINFO;
|
state = STATE_SEND_NODEINFO;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,20 @@ const RegionInfo regions[] = {
|
|||||||
RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last
|
RDEF(Unset, 903.08f, 2.16f, 13, 0) // Assume US freqs if unset, Must be last
|
||||||
};
|
};
|
||||||
|
|
||||||
static const RegionInfo *myRegion;
|
const RegionInfo *myRegion;
|
||||||
|
|
||||||
|
void initRegion()
|
||||||
|
{
|
||||||
|
if (!myRegion) {
|
||||||
|
const RegionInfo *r = regions;
|
||||||
|
for (; r->code != RegionCode_Unset && r->code != radioConfig.preferences.region; r++)
|
||||||
|
;
|
||||||
|
myRegion = r;
|
||||||
|
DEBUG_MSG("Wanted region %d, using %s\n", radioConfig.preferences.region, r->name);
|
||||||
|
|
||||||
|
myNodeInfo.num_channels = myRegion->numChannels; // Tell our android app how many channels we have
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ## LoRaWAN for North America
|
* ## LoRaWAN for North America
|
||||||
@@ -91,20 +104,10 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
|||||||
DEBUG_MSG(")\n");
|
DEBUG_MSG(")\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioInterface::RadioInterface()
|
RadioInterface::RadioInterface()
|
||||||
{
|
{
|
||||||
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
|
assert(sizeof(PacketHeader) == 4 || sizeof(PacketHeader) == 16); // make sure the compiler did what we expected
|
||||||
|
|
||||||
if (!myRegion) {
|
|
||||||
const RegionInfo *r = regions;
|
|
||||||
for (; r->code != RegionCode_Unset && r->code != radioConfig.preferences.region; r++)
|
|
||||||
;
|
|
||||||
myRegion = r;
|
|
||||||
DEBUG_MSG("Wanted region %d, using %s\n", radioConfig.preferences.region, r->name);
|
|
||||||
|
|
||||||
myNodeInfo.num_channels = myRegion->numChannels; // Tell our android app how many channels we have
|
|
||||||
}
|
|
||||||
|
|
||||||
// Can't print strings this early - serial not setup yet
|
// Can't print strings this early - serial not setup yet
|
||||||
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
|
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
|
||||||
}
|
}
|
||||||
@@ -120,7 +123,7 @@ bool RadioInterface::init()
|
|||||||
// we now expect interfaces to operate in promiscous mode
|
// we now expect interfaces to operate in promiscous mode
|
||||||
// radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor
|
// radioIf.setThisAddress(nodeDB.getNodeNum()); // Note: we must do this here, because the nodenum isn't inited at constructor
|
||||||
// time.
|
// time.
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ bool SX1262Interface::reconfigure()
|
|||||||
assert(err == ERR_NONE);
|
assert(err == ERR_NONE);
|
||||||
|
|
||||||
// Hmm - seems to lower SNR when the signal levels are high. Leaving off for now...
|
// Hmm - seems to lower SNR when the signal levels are high. Leaving off for now...
|
||||||
// err = lora.setRxGain(true);
|
err = lora.setRxGain(true);
|
||||||
// assert(err == ERR_NONE);
|
assert(err == ERR_NONE);
|
||||||
|
|
||||||
err = lora.setSyncWord(syncWord);
|
err = lora.setSyncWord(syncWord);
|
||||||
assert(err == ERR_NONE);
|
assert(err == ERR_NONE);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "PhoneAPI.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
void initWebServer();
|
void initWebServer();
|
||||||
|
void createSSLCert();
|
||||||
|
|
||||||
void handleNotFound();
|
void handleNotFound();
|
||||||
|
|
||||||
@@ -15,8 +17,21 @@ void notifyWebUI();
|
|||||||
|
|
||||||
void handleHotspot();
|
void handleHotspot();
|
||||||
|
|
||||||
|
|
||||||
void handleStyleCSS();
|
void handleStyleCSS();
|
||||||
void handleRoot();
|
void handleRoot();
|
||||||
void handleScriptsScriptJS();
|
void handleScriptsScriptJS();
|
||||||
void handleJSONChatHistoryDummy();
|
void handleJSONChatHistoryDummy();
|
||||||
|
|
||||||
|
|
||||||
|
class HttpAPI : public PhoneAPI
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Nothing here yet
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Nothing here yet
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Nothing here yet
|
||||||
|
};
|
||||||
1346
src/meshwifi/meshhttpStatic.h
Normal file
1346
src/meshwifi/meshhttpStatic.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@
|
|||||||
#include "meshwifi/meshhttp.h"
|
#include "meshwifi/meshhttp.h"
|
||||||
#include "target_specific.h"
|
#include "target_specific.h"
|
||||||
#include <DNSServer.h>
|
#include <DNSServer.h>
|
||||||
|
#include <ESPmDNS.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
|
|
||||||
static void WiFiEvent(WiFiEvent_t event);
|
static void WiFiEvent(WiFiEvent_t event);
|
||||||
@@ -17,8 +18,8 @@ static WiFiServerPort *apiPort;
|
|||||||
|
|
||||||
uint8_t wifiDisconnectReason = 0;
|
uint8_t wifiDisconnectReason = 0;
|
||||||
|
|
||||||
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
|
// Stores our hostname
|
||||||
static char ourHost[16];
|
char ourHost[16];
|
||||||
|
|
||||||
bool isWifiAvailable()
|
bool isWifiAvailable()
|
||||||
{
|
{
|
||||||
@@ -29,9 +30,6 @@ bool isWifiAvailable()
|
|||||||
// strcpy(radioConfig.preferences.wifi_password, "");
|
// strcpy(radioConfig.preferences.wifi_password, "");
|
||||||
|
|
||||||
if (*wifiName && *wifiPsw) {
|
if (*wifiName && *wifiPsw) {
|
||||||
|
|
||||||
// Once every 10 seconds, try to reconnect.
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
@@ -64,6 +62,8 @@ void initWifi()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createSSLCert();
|
||||||
|
|
||||||
if (radioConfig.has_preferences) {
|
if (radioConfig.has_preferences) {
|
||||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||||
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
const char *wifiPsw = radioConfig.preferences.wifi_password;
|
||||||
@@ -117,12 +117,23 @@ void initWifi()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!MDNS.begin( "Meshtastic" )) {
|
||||||
|
DEBUG_MSG("Error setting up MDNS responder!\n");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
delay(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DEBUG_MSG("mDNS responder started\n");
|
||||||
|
DEBUG_MSG("mDNS Host: Meshtastic.local\n");
|
||||||
|
MDNS.addService("http", "tcp", 80);
|
||||||
|
MDNS.addService("https", "tcp", 443);
|
||||||
|
|
||||||
} else
|
} else
|
||||||
DEBUG_MSG("Not using WIFI\n");
|
DEBUG_MSG("Not using WIFI\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void initApiServer()
|
static void initApiServer()
|
||||||
{
|
{
|
||||||
// Start API server on port 4403
|
// Start API server on port 4403
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ External serial flash WP25R1635FZUIL0
|
|||||||
#define PIN_SPI_SCK (0 + 19)
|
#define PIN_SPI_SCK (0 + 19)
|
||||||
|
|
||||||
// To debug via the segger JLINK console rather than the CDC-ACM serial device
|
// To debug via the segger JLINK console rather than the CDC-ACM serial device
|
||||||
#define USE_SEGGER
|
// #define USE_SEGGER
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user