mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-07 02:18:09 +00:00
@@ -9,11 +9,10 @@ COUNTRIES="US EU433 EU865 CN JP ANZ KR"
|
||||
#COUNTRIES=CN
|
||||
|
||||
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
|
||||
BOARDS_NRF52="lora-relay-v1"
|
||||
BOARDS="$BOARDS_ESP32 $BOARDS_NRF52"
|
||||
#BOARDS=tbeam
|
||||
|
||||
OUTDIR=release/latest
|
||||
|
||||
@@ -22,22 +21,61 @@ ARCHIVEDIR=release/archive
|
||||
|
||||
rm -f $OUTDIR/firmware*
|
||||
|
||||
mkdir -p $OUTDIR/bins $OUTDIR/elfs
|
||||
rm -f $OUTDIR/bins/*
|
||||
mkdir -p $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
|
||||
function do_build {
|
||||
echo "Building for $BOARD with $PLATFORMIO_BUILD_FLAGS"
|
||||
function do_build() {
|
||||
BOARD=$1
|
||||
COUNTRY=$2
|
||||
isNrf=$3
|
||||
|
||||
echo "Building $COUNTRY for $BOARD with $PLATFORMIO_BUILD_FLAGS"
|
||||
rm -f .pio/build/$BOARD/firmware.*
|
||||
|
||||
# The shell vars the build tool expects to find
|
||||
export HW_VERSION="1.0-$COUNTRY"
|
||||
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
|
||||
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
|
||||
@@ -46,26 +84,16 @@ git submodule update
|
||||
# Important to pull latest version of libs into all device flavors, otherwise some devices might be stale
|
||||
platformio lib update
|
||||
|
||||
for COUNTRY in $COUNTRIES; do
|
||||
for BOARD in $BOARDS; do
|
||||
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
|
||||
do_boards "$BOARDS_ESP32" "false"
|
||||
do_boards "$BOARDS_NRF52" "true"
|
||||
|
||||
# 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
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
@@ -79,6 +107,7 @@ Generated by bin/buildall.sh -->
|
||||
</resources>
|
||||
XML
|
||||
|
||||
echo Generating $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
|
||||
|
||||
|
||||
@@ -2,7 +2,20 @@
|
||||
|
||||
set -e
|
||||
|
||||
# dependencies
|
||||
# apt install srecord
|
||||
|
||||
BOOTDIR=/home/kevinh/development/meshtastic/Adafruit_nRF52_Bootloader
|
||||
BOARD=othernet_ppr1
|
||||
BOOTVER=0.3.2
|
||||
BOOTNUM=128
|
||||
BOOTSHA=gc01b9ea
|
||||
SDCODE=s113
|
||||
SDVER=7.2.0
|
||||
PROJ=ppr1
|
||||
|
||||
# FIXME for nRF52840 use 0xff000, for nRF52833 use 0x7f000
|
||||
BOOTSET=0x7f000
|
||||
|
||||
nrfjprog --eraseall -f nrf52
|
||||
|
||||
@@ -11,12 +24,12 @@ nrfjprog --eraseall -f nrf52
|
||||
# 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
|
||||
srec_cat /tmp/bootconf.bin -binary -offset $BOOTSET -output /tmp/bootconf.hex -intel
|
||||
|
||||
echo Generating merged hex file
|
||||
mergehex -m $BOOTDIR/_build/build-ttgo_eink/ttgo_eink_bootloader-0.3.2-124-g69bd8eb-dirty_s140_6.1.1.hex .pio/build/eink/firmware.hex /tmp/bootconf.hex -o ttgo_eink_full.hex
|
||||
echo Generating merged hex file from .pio/build/$PROJ/firmware.hex
|
||||
mergehex -o ${BOARD}_full.hex -m $BOOTDIR/_build/build-$BOARD/${BOARD}_bootloader-$BOOTVER-$BOOTNUM-$BOOTSHA-dirty_${SDCODE}_$SDVER.hex .pio/build/$PROJ/firmware.hex /tmp/bootconf.hex
|
||||
|
||||
echo Telling bootloader app region is valid and telling CPU to run
|
||||
nrfjprog --program ttgo_eink_full.hex -f nrf52 --reset
|
||||
nrfjprog --program ${BOARD}_full.hex -f nrf52 --reset
|
||||
|
||||
# nrfjprog --readuicr /tmp/uicr.hex; objdump -s /tmp/uicr.hex | less
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
|
||||
|
||||
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52840_XXAA
|
||||
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52832_XXAA
|
||||
|
||||
3
bin/nrf52833-gdbserver.sh
Executable file
3
bin/nrf52833-gdbserver.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
|
||||
|
||||
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52833_XXAA
|
||||
@@ -1,3 +1,3 @@
|
||||
|
||||
|
||||
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52832_XXAA
|
||||
JLinkGDBServerCLExe -if SWD -select USB -port 2331 -device NRF52840_XXAA
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
|
||||
echo "Converting to uf2 for NRF52 Adafruit bootloader"
|
||||
bin/uf2conv.py .pio/build/lora-relay-v1/firmware.hex -f 0xADA52840
|
||||
bin/uf2conv.py .pio/build/lora-relay-v2/firmware.hex -f 0xADA52840
|
||||
# cp flash.uf2 /media/kevinh/FTH*BOOT/
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
|
||||
|
||||
export VERSION=1.1.5
|
||||
export VERSION=1.1.7
|
||||
2
bin/view-map.sh
Executable file
2
bin/view-map.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
echo using amap tool to display memory map
|
||||
amap .pio/build/output.map
|
||||
@@ -5,7 +5,7 @@
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DARDUINO_NRF52840_LORA_RELAY_V1 -DNRF52840_XXAA",
|
||||
"extra_flags": "-DARDUINO_NRF52840_TTGO_EINK -DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [
|
||||
[
|
||||
|
||||
46
boards/lora-relay-v2.json
Normal file
46
boards/lora-relay-v2.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52840_s140_v6.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DARDUINO_NRF52840_LORA_RELAY_V2 -DNRF52840_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [["0x239A", "0x4406"]],
|
||||
"usb_product": "LORA_RELAY",
|
||||
"mcu": "nrf52840",
|
||||
"variant": "lora_relay_v2",
|
||||
"variants_dir": "variants",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS140",
|
||||
"sd_name": "s140",
|
||||
"sd_version": "6.1.1",
|
||||
"sd_fwid": "0x00B6"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
}
|
||||
},
|
||||
"connectivity": ["bluetooth"],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52840_xxAA",
|
||||
"onboard_tools": ["jlink"],
|
||||
"svd_path": "nrf52840.svd"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "Meshtastic Lora Relay V1 (Adafruit BSP)",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
"maximum_size": 815104,
|
||||
"require_upload_port": true,
|
||||
"speed": 115200,
|
||||
"protocol": "jlink",
|
||||
"protocols": ["jlink", "nrfjprog", "stlink"]
|
||||
},
|
||||
"url": "https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay",
|
||||
"vendor": "BigCorvus"
|
||||
}
|
||||
46
boards/ppr1.json
Normal file
46
boards/ppr1.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"build": {
|
||||
"arduino": {
|
||||
"ldscript": "nrf52833_s113_v7.ld"
|
||||
},
|
||||
"core": "nRF5",
|
||||
"cpu": "cortex-m4",
|
||||
"extra_flags": "-DARDUINO_NRF52833_PPR -DNRF52833_XXAA",
|
||||
"f_cpu": "64000000L",
|
||||
"hwids": [["0x239A", "0x4406"]],
|
||||
"usb_product": "PPR",
|
||||
"mcu": "nrf52833",
|
||||
"variant": "ppr",
|
||||
"variants_dir": "variants",
|
||||
"bsp": {
|
||||
"name": "adafruit"
|
||||
},
|
||||
"softdevice": {
|
||||
"sd_flags": "-DS113",
|
||||
"sd_name": "s113",
|
||||
"sd_version": "7.2.0",
|
||||
"sd_fwid": "0x00b6"
|
||||
},
|
||||
"bootloader": {
|
||||
"settings_addr": "0xFF000"
|
||||
}
|
||||
},
|
||||
"connectivity": ["bluetooth"],
|
||||
"debug": {
|
||||
"jlink_device": "nRF52833_xxAA",
|
||||
"onboard_tools": ["jlink"],
|
||||
"svd_path": "nrf52833.svd"
|
||||
},
|
||||
"frameworks": ["arduino"],
|
||||
"name": "Meshtastic PPR1 (Adafruit BSP)",
|
||||
"upload": {
|
||||
"maximum_ram_size": 248832,
|
||||
"maximum_size": 815104,
|
||||
"require_upload_port": true,
|
||||
"speed": 115200,
|
||||
"protocol": "jlink",
|
||||
"protocols": ["jlink", "nrfjprog", "stlink"]
|
||||
},
|
||||
"url": "https://meshtastic.org/",
|
||||
"vendor": "Othernet"
|
||||
}
|
||||
43
data/static/basic.js
Normal file
43
data/static/basic.js
Normal file
@@ -0,0 +1,43 @@
|
||||
var meshtasticClient;
|
||||
var connectionOne;
|
||||
|
||||
|
||||
// Important: the connect action must be called from a user interaction (e.g. button press), otherwise the browsers won't allow the connect
|
||||
function connect() {
|
||||
|
||||
// Create new connection
|
||||
var httpconn = new meshtasticjs.IHTTPConnection();
|
||||
|
||||
// Set connection params
|
||||
let sslActive;
|
||||
if (window.location.protocol === 'https:') {
|
||||
sslActive = true;
|
||||
} else {
|
||||
sslActive = false;
|
||||
}
|
||||
let deviceIp = window.location.hostname; // Your devices IP here
|
||||
|
||||
|
||||
// Add event listeners that get called when a new packet is received / state of device changes
|
||||
httpconn.addEventListener('fromRadio', function (packet) { console.log(packet) });
|
||||
|
||||
// Connect to the device async, then send a text message
|
||||
httpconn.connect(deviceIp, sslActive)
|
||||
.then(result => {
|
||||
|
||||
alert('device has been configured')
|
||||
// This gets called when the connection has been established
|
||||
// -> send a message over the mesh network. If no recipient node is provided, it gets sent as a broadcast
|
||||
return httpconn.sendText('meshtastic is awesome');
|
||||
|
||||
})
|
||||
.then(result => {
|
||||
|
||||
// This gets called when the message has been sucessfully sent
|
||||
console.log('Message sent!');
|
||||
})
|
||||
|
||||
.catch(error => { console.log(error); });
|
||||
|
||||
}
|
||||
|
||||
18
data/static/index.html
Normal file
18
data/static/index.html
Normal file
@@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<html class="no-js" lang="">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title></title>
|
||||
|
||||
<script src="/static/meshtastic.js"></script>
|
||||
<script src="/static/basic.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<button id="connect_button" onclick="connect()">Connect to Meshtastic device</button>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
BIN
data/static/meshtastic.js.gz
Normal file
BIN
data/static/meshtastic.js.gz
Normal file
Binary file not shown.
277
data/style.css
277
data/style.css
@@ -1,277 +0,0 @@
|
||||
/* 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;
|
||||
}
|
||||
Binary file not shown.
BIN
docs/hardware/pinetab/PineTab LoRa schematic.pdf
Normal file
BIN
docs/hardware/pinetab/PineTab LoRa schematic.pdf
Normal file
Binary file not shown.
990
docs/hardware/pinetab/ch341h_datasheet.pdf
Normal file
990
docs/hardware/pinetab/ch341h_datasheet.pdf
Normal file
File diff suppressed because one or more lines are too long
BIN
docs/hardware/pinetab/nfnfaceblhkepdph.jpg
Normal file
BIN
docs/hardware/pinetab/nfnfaceblhkepdph.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 272 KiB |
BIN
docs/hardware/pinetab/nhndndegjjflkibg.jpg
Normal file
BIN
docs/hardware/pinetab/nhndndegjjflkibg.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 417 KiB |
@@ -5,8 +5,24 @@
|
||||
|
||||
## RAK815
|
||||
|
||||
TODO:
|
||||
### PPR1 TODO
|
||||
|
||||
* V_BK for the GPS should probably be supplied from something always on
|
||||
|
||||
* use S113 soft device 7.2.0
|
||||
* properly test charge controller config and read battery/charge status
|
||||
* fix bluetooth
|
||||
* fix LCD max contrast (currently too high, needs to be about 40?)
|
||||
* save brightness settings in flash
|
||||
* make ST7567Wire driver less ugly, move OLED stuff into a common class treee
|
||||
* add LCD power save mode for lcd per page 31 of datasheet
|
||||
* add LCD power off sequence per datasheet to lcd driver
|
||||
* leave LCD screen on most of the time (because it needs little power)
|
||||
|
||||
### general nrf52 TODO:
|
||||
|
||||
- turn off transitions on eink screens
|
||||
- change update interval on eink from 1/sec frames to one frame every 5 mins
|
||||
- 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
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
These are **preliminary** notes on support for Meshtastic in the Pinetab.
|
||||
|
||||
A RF95 is connected via a CS341 USB-SPI chip.
|
||||
A RF95 is connected via a CH341 USB-SPI chip.
|
||||
|
||||
Pin assignments:
|
||||
CS0 from RF95 goes to CS0 on CS341
|
||||
DIO0 from RF95 goes to INT on CS341
|
||||
RST from RF95 goes to RST on CS341
|
||||
CS0 from RF95 goes to CS0 on CH341
|
||||
DIO0 from RF95 goes to INT on CH341
|
||||
RST from RF95 goes to RST on CH341
|
||||
|
||||
This linux driver claims to provide USB-SPI support: https://github.com/gschorcht/spi-ch341-usb
|
||||
Notes here on using that driver: https://www.linuxquestions.org/questions/linux-hardware-18/ch341-usb-to-spi-adaptor-driver-doesn%27t-work-4175622736/
|
||||
|
||||
@@ -36,7 +36,6 @@ build_flags = -Wno-missing-field-initializers -Isrc -Isrc/mesh -Isrc/gps -Ilib/n
|
||||
;upload_port = /dev/ttyUSB0
|
||||
;monitor_port = /dev/ttyUSB0
|
||||
|
||||
; geeksville: I think setting this should not be required - it breaks linux
|
||||
;upload_port = /dev/cu.SLAB_USBtoUART
|
||||
;monitor_port = /dev/cu.SLAB_USBtoUART
|
||||
|
||||
@@ -237,6 +236,15 @@ lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
UC1701
|
||||
|
||||
; The PPR board
|
||||
[env:ppr1]
|
||||
extends = nrf52_base
|
||||
board = ppr1
|
||||
build_flags = ${nrf52_base.build_flags} -Ivariants/ppr1
|
||||
src_filter = ${nrf52_base.src_filter} +<../variants/ppr1>
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
|
||||
; Prototype eink/nrf52840/sx1262 device
|
||||
[env:eink]
|
||||
extends = nrf52_base
|
||||
@@ -272,7 +280,30 @@ lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
SparkFun BQ27441 LiPo Fuel Gauge Arduino Library
|
||||
TFT_eSPI
|
||||
# Adafruit ST7735 and ST7789 Library
|
||||
|
||||
; The https://github.com/BigCorvus/LoRa-BLE-Relay-v2 board by @BigCorvus
|
||||
[env:lora-relay-v2]
|
||||
extends = nrf52_base
|
||||
board = lora-relay-v2
|
||||
# add our variants files to the include and src paths
|
||||
# define build flags for the TFT_eSPI library
|
||||
build_flags = ${nrf52_base.build_flags} -Ivariants/lora_relay_v2
|
||||
-DUSER_SETUP_LOADED
|
||||
-DTFT_WIDTH=80
|
||||
-DTFT_HEIGHT=160
|
||||
-DST7735_GREENTAB160x80
|
||||
-DST7735_DRIVER
|
||||
-DTFT_CS=ST7735_CS
|
||||
-DTFT_DC=ST7735_RS
|
||||
-DTFT_RST=ST7735_RESET
|
||||
-DSPI_FREQUENCY=27000000
|
||||
-DTFT_WR=ST7735_SDA
|
||||
-DTFT_SCLK=ST7735_SCK
|
||||
src_filter = ${nrf52_base.src_filter} +<../variants/lora_relay_v2>
|
||||
lib_deps =
|
||||
${arduino_base.lib_deps}
|
||||
SparkFun BQ27441 LiPo Fuel Gauge Arduino Library
|
||||
TFT_eSPI
|
||||
|
||||
; The Portduino based sim environment on top of linux
|
||||
[env:linux]
|
||||
|
||||
@@ -18,6 +18,21 @@ Power *power;
|
||||
|
||||
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
|
||||
*/
|
||||
@@ -37,10 +52,13 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
{
|
||||
float v = getBattVoltage() / 1000;
|
||||
|
||||
if (v < 2.1)
|
||||
if (v < noBatVolt)
|
||||
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()
|
||||
{
|
||||
// Tested ttgo eink nrf52 board and the reported value is perfect
|
||||
// DEBUG_MSG("raw val %u", raw);
|
||||
return
|
||||
#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
|
||||
NAN;
|
||||
#endif
|
||||
@@ -59,7 +79,20 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
||||
/**
|
||||
* 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;
|
||||
|
||||
Power::Power() : OSThread("Power") {}
|
||||
@@ -68,10 +101,18 @@ bool Power::analogInit()
|
||||
{
|
||||
#ifdef BATTERY_PIN
|
||||
DEBUG_MSG("Using analog input for battery level\n");
|
||||
|
||||
// disable any internal pullups
|
||||
pinMode(BATTERY_PIN, INPUT);
|
||||
|
||||
#ifndef NO_ESP32
|
||||
// ESP32 needs special analog stuff
|
||||
adcAttachPin(BATTERY_PIN);
|
||||
#endif
|
||||
#ifdef NRF52_SERIES
|
||||
analogReference(AR_INTERNAL); // 3.6V
|
||||
#endif
|
||||
|
||||
// adcStart(BATTERY_PIN);
|
||||
analogReadResolution(10); // Default of 12 is not very linear. Recommended to use 10 or 11 depending on needed resolution.
|
||||
batteryLevel = &analogLevel;
|
||||
@@ -121,7 +162,8 @@ void Power::readPowerStatus()
|
||||
const PowerStatus powerStatus =
|
||||
PowerStatus(hasBattery ? OptTrue : OptFalse, batteryLevel->isVBUSPlug() ? OptTrue : OptFalse,
|
||||
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);
|
||||
|
||||
// If we have a battery at all and it is less than 10% full, force deep sleep
|
||||
|
||||
@@ -82,7 +82,7 @@ class PowerStatus : public Status
|
||||
isCharging = newStatus->isCharging;
|
||||
}
|
||||
if (isDirty) {
|
||||
DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
|
||||
// DEBUG_MSG("Battery %dmV %d%%\n", batteryVoltageMv, batteryChargePercent);
|
||||
onNewStatus.notifyObservers(this);
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -139,6 +139,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
#define SSD1306_ADDRESS 0x3C
|
||||
#define ST7567_ADDRESS 0x3F
|
||||
|
||||
// The SH1106 controller is almost, but not quite, the same as SSD1306
|
||||
// Define this if you know you have that controller or your "SSD1306" misbehaves.
|
||||
@@ -146,7 +147,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Flip the screen upside down by default as it makes more sense on T-BEAM
|
||||
// devices. Comment this out to not rotate screen 180 degrees.
|
||||
#define FLIP_SCREEN_VERTICALLY
|
||||
#define SCREEN_FLIP_VERTICALLY
|
||||
|
||||
// Define if screen should be mirrored left to right
|
||||
// #define SCREEN_MIRROR
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// GPS
|
||||
@@ -430,3 +434,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#define GPS_POWER_CTRL_CH 3
|
||||
#define LORA_POWER_CTRL_CH 2
|
||||
|
||||
// Default Bluetooth PIN
|
||||
#define defaultBLEPin 123456
|
||||
|
||||
@@ -72,7 +72,7 @@ int update_data_callback(uint16_t conn_handle, uint16_t attr_handle, struct ble_
|
||||
crc.update(data, len);
|
||||
Update.write(data, 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;
|
||||
}
|
||||
|
||||
@@ -65,9 +65,8 @@ void Air530GPS::sendCommand(const char *cmd) {
|
||||
}
|
||||
|
||||
void Air530GPS::sleep() {
|
||||
NMEAGPS::sleep();
|
||||
#ifdef PIN_GPS_WAKE
|
||||
digitalWrite(PIN_GPS_WAKE, 0);
|
||||
pinMode(PIN_GPS_WAKE, OUTPUT);
|
||||
sendCommand("$PGKC105,4");
|
||||
#endif
|
||||
}
|
||||
@@ -76,10 +75,7 @@ void Air530GPS::sleep() {
|
||||
void Air530GPS::wake()
|
||||
{
|
||||
#if 1
|
||||
#ifdef PIN_GPS_WAKE
|
||||
digitalWrite(PIN_GPS_WAKE, 1);
|
||||
pinMode(PIN_GPS_WAKE, OUTPUT);
|
||||
#endif
|
||||
NMEAGPS::wake();
|
||||
#else
|
||||
// For power testing - keep GPS sleeping forever
|
||||
sleep();
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#ifdef GPS_RX_PIN
|
||||
HardwareSerial _serial_gps_real(GPS_SERIAL_NUM);
|
||||
HardwareSerial *GPS::_serial_gps = &_serial_gps_real;
|
||||
#elif defined(NRF52840_XXAA)
|
||||
#elif defined(NRF52840_XXAA) || defined(NRF52833_XXAA)
|
||||
// Assume NRF52840
|
||||
HardwareSerial *GPS::_serial_gps = &Serial1;
|
||||
#else
|
||||
@@ -25,8 +25,37 @@ uint8_t GPS::i2cAddress = 0;
|
||||
|
||||
GPS *gps;
|
||||
|
||||
bool GPS::setupGPS()
|
||||
{
|
||||
if (_serial_gps) {
|
||||
#ifdef GPS_RX_PIN
|
||||
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
|
||||
#else
|
||||
_serial_gps->begin(GPS_BAUDRATE);
|
||||
#endif
|
||||
#ifndef NO_ESP32
|
||||
_serial_gps->setRxBufferSize(2048); // the default is 256
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPS::setup()
|
||||
{
|
||||
// Master power for the GPS
|
||||
#ifdef PIN_GPS_EN
|
||||
digitalWrite(PIN_GPS_EN, PIN_GPS_EN);
|
||||
pinMode(PIN_GPS_EN, OUTPUT);
|
||||
#endif
|
||||
|
||||
#ifdef PIN_GPS_RESET
|
||||
digitalWrite(PIN_GPS_RESET, 1); // assert for 10ms
|
||||
pinMode(PIN_GPS_RESET, OUTPUT);
|
||||
delay(10);
|
||||
digitalWrite(PIN_GPS_RESET, 0);
|
||||
#endif
|
||||
|
||||
setAwake(true); // Wake GPS power before doing any init
|
||||
bool ok = setupGPS();
|
||||
|
||||
@@ -36,6 +65,27 @@ bool GPS::setup()
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Allow defining the polarity of the WAKE output. default is active high
|
||||
#ifndef GPS_WAKE_ACTIVE
|
||||
#define GPS_WAKE_ACTIVE 1
|
||||
#endif
|
||||
|
||||
void GPS::wake()
|
||||
{
|
||||
#ifdef PIN_GPS_WAKE
|
||||
digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE);
|
||||
pinMode(PIN_GPS_WAKE, OUTPUT);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void GPS::sleep() {
|
||||
#ifdef PIN_GPS_WAKE
|
||||
digitalWrite(PIN_GPS_WAKE, GPS_WAKE_ACTIVE ? 0 : 1);
|
||||
pinMode(PIN_GPS_WAKE, OUTPUT);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// Record that we have a GPS
|
||||
void GPS::setConnected()
|
||||
{
|
||||
|
||||
@@ -72,13 +72,13 @@ class GPS : private concurrency::OSThread
|
||||
|
||||
protected:
|
||||
/// Do gps chipset specific init, return true for success
|
||||
virtual bool setupGPS() = 0;
|
||||
virtual bool setupGPS();
|
||||
|
||||
/// If possible force the GPS into sleep/low power mode
|
||||
virtual void sleep() {}
|
||||
virtual void sleep();
|
||||
|
||||
/// wake the GPS into normal operation mode
|
||||
virtual void wake() {}
|
||||
virtual void wake();
|
||||
|
||||
/** Subclasses should look for serial rx characters here and feed it to their GPS parser
|
||||
*
|
||||
|
||||
@@ -13,6 +13,8 @@ static int32_t toDegInt(RawDegrees d)
|
||||
|
||||
bool NMEAGPS::setupGPS()
|
||||
{
|
||||
GPS::setupGPS();
|
||||
|
||||
#ifdef PIN_GPS_PPS
|
||||
// pulse per second
|
||||
// FIXME - move into shared GPS code
|
||||
@@ -102,7 +104,7 @@ bool NMEAGPS::whileIdle()
|
||||
// First consume any chars that have piled up at the receiver
|
||||
while (_serial_gps->available() > 0) {
|
||||
int c = _serial_gps->read();
|
||||
//DEBUG_MSG("%c", c);
|
||||
// DEBUG_MSG("%c", c);
|
||||
isValid |= reader.encode(c);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,16 +29,7 @@ bool UBloxGPS::tryConnect()
|
||||
|
||||
bool UBloxGPS::setupGPS()
|
||||
{
|
||||
if (_serial_gps) {
|
||||
#ifdef GPS_RX_PIN
|
||||
_serial_gps->begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN);
|
||||
#else
|
||||
_serial_gps->begin(GPS_BAUDRATE);
|
||||
#endif
|
||||
#ifndef NO_ESP32
|
||||
_serial_gps->setRxBufferSize(2048); // the default is 256
|
||||
#endif
|
||||
}
|
||||
GPS::setupGPS();
|
||||
|
||||
// uncomment to see debug info
|
||||
// ublox.enableDebugging(Serial);
|
||||
|
||||
@@ -58,10 +58,6 @@ static char ourId[5];
|
||||
static bool heartbeat = false;
|
||||
#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
|
||||
@@ -85,6 +81,10 @@ static uint16_t displayWidth, displayHeight;
|
||||
|
||||
#define getStringCenteredX(s) ((SCREEN_WIDTH - display->getStringWidth(s)) / 2)
|
||||
|
||||
#ifndef SCREEN_TRANSITION_MSECS
|
||||
#define SCREEN_TRANSITION_MSECS 300
|
||||
#endif
|
||||
|
||||
static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||
{
|
||||
// draw an xbm image.
|
||||
@@ -92,26 +92,26 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
|
||||
// needs to be drawn relative to x and y
|
||||
|
||||
// draw centered left to right and centered above the one line of app text
|
||||
display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2, icon_width,
|
||||
icon_height, (const uint8_t *)icon_bits);
|
||||
display->drawXbm(x + (SCREEN_WIDTH - icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - icon_height) / 2 + 2,
|
||||
icon_width, icon_height, (const uint8_t *)icon_bits);
|
||||
|
||||
display->setFont(FONT_MEDIUM);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
const char *title = "meshtastic.org";
|
||||
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
|
||||
display->setFont(FONT_SMALL);
|
||||
const char *region = xstr(HW_VERSION);
|
||||
if (*region && region[3] == '-') // Skip past 1.0- in the 1.0-EU865 string
|
||||
region += 4;
|
||||
|
||||
const char *region = myRegion ? myRegion->name : NULL;
|
||||
if (region)
|
||||
display->drawString(x + 0, y + 0, region);
|
||||
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof(buf), "%s",
|
||||
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)
|
||||
{
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
@@ -627,7 +627,8 @@ void Screen::setup()
|
||||
// is never found when probing i2c and therefore we don't call setup and never want to do (invalid) accesses to this device.
|
||||
useDisplay = true;
|
||||
|
||||
dispdev.resetOrientation();
|
||||
// I think this is not needed - redundant with ui.init
|
||||
// dispdev.resetOrientation();
|
||||
|
||||
// Initialising the UI will init the display too.
|
||||
ui.init();
|
||||
@@ -635,7 +636,8 @@ void Screen::setup()
|
||||
displayWidth = dispdev.width();
|
||||
displayHeight = dispdev.height();
|
||||
|
||||
ui.setTimePerTransition(300); // msecs
|
||||
ui.setTimePerTransition(SCREEN_TRANSITION_MSECS);
|
||||
|
||||
ui.setIndicatorPosition(BOTTOM);
|
||||
// Defines where the first frame is located in the bar.
|
||||
ui.setIndicatorDirection(LEFT_RIGHT);
|
||||
@@ -661,7 +663,9 @@ void Screen::setup()
|
||||
// Set up a log buffer with 3 lines, 32 chars each.
|
||||
dispdev.setLogBuffer(3, 32);
|
||||
|
||||
#ifdef FLIP_SCREEN_VERTICALLY
|
||||
#ifdef SCREEN_MIRROR
|
||||
dispdev.mirrorScreen();
|
||||
#elif defined(SCREEN_FLIP_VERTICALLY)
|
||||
dispdev.flipScreenVertically();
|
||||
#endif
|
||||
|
||||
@@ -874,12 +878,16 @@ void Screen::handleOnPress()
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SCREEN_TRANSITION_FRAMERATE
|
||||
#define SCREEN_TRANSITION_FRAMERATE 30 // fps
|
||||
#endif
|
||||
|
||||
void Screen::setFastFramerate()
|
||||
{
|
||||
DEBUG_MSG("Setting fast framerate\n");
|
||||
|
||||
// We are about to start a transition so speed up fps
|
||||
targetFramerate = TRANSITION_FRAMERATE;
|
||||
targetFramerate = SCREEN_TRANSITION_FRAMERATE;
|
||||
ui.setTargetFPS(targetFramerate);
|
||||
setInterval(0); // redraw ASAP
|
||||
}
|
||||
@@ -1044,8 +1052,12 @@ void DebugInfo::drawFrameWiFi(OLEDDisplay *display, OLEDDisplayUiState *state, i
|
||||
}
|
||||
}
|
||||
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2, "SSID: " + String(wifiName));
|
||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 3, "PWD: " + String(wifiPsw));
|
||||
if ((millis() / 10000) % 2) {
|
||||
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 */
|
||||
#ifdef SHOW_REDRAWS
|
||||
@@ -1100,9 +1112,8 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
|
||||
|
||||
#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");
|
||||
display->drawString(x + SCREEN_WIDTH - display->getStringWidth("CPU " + String(getCpuFrequencyMhz()) + "MHz"),
|
||||
y + FONT_HEIGHT_SMALL * 1, "CPU " + String(getCpuFrequencyMhz()) + "MHz");
|
||||
#endif
|
||||
|
||||
// Line 3
|
||||
@@ -1138,7 +1149,7 @@ int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
// DEBUG_MSG("Screen got status update %d\n", arg->getStatusType());
|
||||
switch (arg->getStatusType()) {
|
||||
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
|
||||
}
|
||||
nodeDB.updateGUI = false;
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
#ifdef USE_SH1106
|
||||
#include <SH1106Wire.h>
|
||||
#elif defined(USE_ST7567)
|
||||
#include <ST7567Wire.h>
|
||||
#else
|
||||
#include <SSD1306Wire.h>
|
||||
#endif
|
||||
@@ -19,6 +21,11 @@
|
||||
#include "power.h"
|
||||
#include <string>
|
||||
|
||||
// 0 to 255, though particular variants might define different defaults
|
||||
#ifndef BRIGHTNESS_DEFAULT
|
||||
#define BRIGHTNESS_DEFAULT 150
|
||||
#endif
|
||||
|
||||
namespace graphics
|
||||
{
|
||||
|
||||
@@ -97,7 +104,7 @@ class Screen : public concurrency::OSThread
|
||||
|
||||
// Implementation to Adjust Brightness
|
||||
void adjustBrightness();
|
||||
uint8_t brightness = 150;
|
||||
uint8_t brightness = BRIGHTNESS_DEFAULT;
|
||||
|
||||
/// Starts showing the Bluetooth PIN screen.
|
||||
//
|
||||
@@ -250,6 +257,8 @@ class Screen : public concurrency::OSThread
|
||||
EInkDisplay dispdev;
|
||||
#elif defined(USE_SH1106)
|
||||
SH1106Wire dispdev;
|
||||
#elif defined(USE_ST7567)
|
||||
ST7567Wire dispdev;
|
||||
#else
|
||||
SSD1306Wire dispdev;
|
||||
#endif
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
#include "fonts.h"
|
||||
|
||||
// This means the *visible* area (sh1106 can address 132, but shows 128 for example)
|
||||
#define TRANSITION_FRAMERATE 30 // fps
|
||||
#define IDLE_FRAMERATE 1 // in fps
|
||||
#define COMPASS_DIAM 44
|
||||
|
||||
|
||||
102
src/main.cpp
102
src/main.cpp
@@ -50,7 +50,9 @@ meshtastic::GPSStatus *gpsStatus = new meshtastic::GPSStatus();
|
||||
// Global Node status
|
||||
meshtastic::NodeStatus *nodeStatus = new meshtastic::NodeStatus();
|
||||
|
||||
bool ssd1306_found;
|
||||
/// The I2C address of our display (if found)
|
||||
uint8_t screen_found;
|
||||
|
||||
bool axp192_found;
|
||||
|
||||
Router *router = NULL; // Users of router don't care what sort of subclass implements that API
|
||||
@@ -72,9 +74,13 @@ void scanI2Cdevice(void)
|
||||
nDevices++;
|
||||
|
||||
if (addr == SSD1306_ADDRESS) {
|
||||
ssd1306_found = true;
|
||||
screen_found = addr;
|
||||
DEBUG_MSG("ssd1306 display found\n");
|
||||
}
|
||||
if (addr == ST7567_ADDRESS) {
|
||||
screen_found = addr;
|
||||
DEBUG_MSG("st7567 display found\n");
|
||||
}
|
||||
#ifdef AXP192_SLAVE_ADDRESS
|
||||
if (addr == AXP192_SLAVE_ADDRESS) {
|
||||
axp192_found = true;
|
||||
@@ -168,12 +174,14 @@ class ButtonThread : public OSThread
|
||||
userButton = OneButton(BUTTON_PIN, true, true);
|
||||
userButton.attachClick(userButtonPressed);
|
||||
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||
userButton.attachDoubleClick(userButtonDoublePressed);
|
||||
wakeOnIrq(BUTTON_PIN, FALLING);
|
||||
#endif
|
||||
#ifdef BUTTON_PIN_ALT
|
||||
userButtonAlt = OneButton(BUTTON_PIN_ALT, true, true);
|
||||
userButtonAlt.attachClick(userButtonPressed);
|
||||
userButton.attachDuringLongPress(userButtonPressedLong);
|
||||
userButtonAlt.attachDuringLongPress(userButtonPressedLong);
|
||||
userButtonAlt.attachDoubleClick(userButtonDoublePressed);
|
||||
wakeOnIrq(BUTTON_PIN_ALT, FALLING);
|
||||
#endif
|
||||
}
|
||||
@@ -190,9 +198,10 @@ class ButtonThread : public OSThread
|
||||
#endif
|
||||
#ifdef BUTTON_PIN_ALT
|
||||
userButtonAlt.tick();
|
||||
canSleep &= userButton.isIdle();
|
||||
canSleep &= userButtonAlt.isIdle();
|
||||
#endif
|
||||
// if(!canSleep) DEBUG_MSG("Supressing sleep!\n");
|
||||
// if (!canSleep) DEBUG_MSG("Supressing sleep!\n");
|
||||
//else DEBUG_MSG("sleep ok\n");
|
||||
|
||||
return 5;
|
||||
}
|
||||
@@ -203,7 +212,18 @@ class ButtonThread : public OSThread
|
||||
// DEBUG_MSG("press!\n");
|
||||
powerFSM.trigger(EVENT_PRESS);
|
||||
}
|
||||
static void userButtonPressedLong() { screen->adjustBrightness(); }
|
||||
static void userButtonPressedLong()
|
||||
{
|
||||
DEBUG_MSG("Long press!\n");
|
||||
screen->adjustBrightness();
|
||||
}
|
||||
|
||||
static void userButtonDoublePressed()
|
||||
{
|
||||
#ifndef NO_ESP32
|
||||
disablePin();
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
static Periodic *ledPeriodic;
|
||||
@@ -245,13 +265,21 @@ void setup()
|
||||
#else
|
||||
Wire.begin();
|
||||
#endif
|
||||
// i2c still busted on new board
|
||||
#ifndef ARDUINO_NRF52840_PPR
|
||||
scanI2Cdevice();
|
||||
|
||||
#ifdef PIN_LCD_RESET
|
||||
// FIXME - move this someplace better, LCD is at address 0x3F
|
||||
pinMode(PIN_LCD_RESET, OUTPUT);
|
||||
digitalWrite(PIN_LCD_RESET, 0);
|
||||
delay(1);
|
||||
digitalWrite(PIN_LCD_RESET, 1);
|
||||
delay(1);
|
||||
#endif
|
||||
|
||||
scanI2Cdevice();
|
||||
|
||||
// Buttons & LED
|
||||
buttonThread = new ButtonThread();
|
||||
|
||||
#ifdef LED_PIN
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
digitalWrite(LED_PIN, 1 ^ LED_INVERTED); // turn on for now
|
||||
@@ -263,7 +291,7 @@ void setup()
|
||||
#ifndef NO_ESP32
|
||||
// Don't init display if we don't have one or we are waking headless due to a timer event
|
||||
if (wakeCause == ESP_SLEEP_WAKEUP_TIMER)
|
||||
ssd1306_found = false; // forget we even have the hardware
|
||||
screen_found = 0; // forget we even have the hardware
|
||||
|
||||
esp32Setup();
|
||||
#endif
|
||||
@@ -289,55 +317,58 @@ void setup()
|
||||
#endif
|
||||
|
||||
// Initialize the screen first so we can show the logo while we start up everything else.
|
||||
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");
|
||||
screen = new graphics::Screen(screen_found);
|
||||
|
||||
readFromRTC(); // read the main CPU RTC at first (in case we can't get GPS time)
|
||||
|
||||
// If we know we have a L80 GPS, don't try UBLOX
|
||||
#ifndef L80_RESET
|
||||
// If we don't have bidirectional comms, we can't even try talking to UBLOX
|
||||
UBloxGPS *ublox = NULL;
|
||||
#ifdef GPS_TX_PIN
|
||||
// Init GPS - first try ublox
|
||||
auto ublox = new UBloxGPS();
|
||||
ublox = new UBloxGPS();
|
||||
gps = ublox;
|
||||
if (!gps->setup()) {
|
||||
DEBUG_MSG("ERROR: No UBLOX GPS found\n");
|
||||
|
||||
delete ublox;
|
||||
gps = ublox = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (GPS::_serial_gps) {
|
||||
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
|
||||
// assume NMEA at 9600 baud.
|
||||
// dumb NMEA access only work for serial GPSes)
|
||||
DEBUG_MSG("Hoping that NMEA might work\n");
|
||||
if (!gps && GPS::_serial_gps) {
|
||||
// Some boards might have only the TX line from the GPS connected, in that case, we can't configure it at all. Just
|
||||
// assume NMEA at 9600 baud.
|
||||
// dumb NMEA access only work for serial GPSes)
|
||||
DEBUG_MSG("Hoping that NMEA might work\n");
|
||||
|
||||
#ifdef HAS_AIR530_GPS
|
||||
gps = new Air530GPS();
|
||||
gps = new Air530GPS();
|
||||
#else
|
||||
gps = new NMEAGPS();
|
||||
gps = new NMEAGPS();
|
||||
#endif
|
||||
gps->setup();
|
||||
}
|
||||
gps->setup();
|
||||
}
|
||||
#else
|
||||
gps = new NMEAGPS();
|
||||
gps->setup();
|
||||
#endif
|
||||
|
||||
if (gps)
|
||||
gpsStatus->observe(&gps->newStatus);
|
||||
else
|
||||
DEBUG_MSG("Warning: No GPS found - running without GPS\n");
|
||||
|
||||
nodeStatus->observe(&nodeDB.newStatus);
|
||||
|
||||
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 (screen_found)
|
||||
screen->setup();
|
||||
#endif
|
||||
|
||||
screen->print("Started...\n");
|
||||
|
||||
// We have now loaded our saved preferences from flash
|
||||
|
||||
// ONCE we will factory reset the GPS for bug #327
|
||||
@@ -391,7 +422,6 @@ void setup()
|
||||
|
||||
// Initialize Wifi
|
||||
initWifi();
|
||||
|
||||
|
||||
if (!rIf)
|
||||
recordCriticalError(ErrNoRadio);
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "graphics/Screen.h"
|
||||
|
||||
extern bool axp192_found;
|
||||
extern bool ssd1306_found;
|
||||
extern bool isCharging;
|
||||
extern bool isUSBPowered;
|
||||
|
||||
|
||||
@@ -16,4 +16,7 @@ struct RegionInfo {
|
||||
const char *name; // EU433 etc
|
||||
};
|
||||
|
||||
extern const RegionInfo regions[];
|
||||
extern const RegionInfo regions[];
|
||||
extern const RegionInfo *myRegion;
|
||||
|
||||
extern void initRegion();
|
||||
@@ -198,7 +198,10 @@ void MeshService::loop()
|
||||
bool MeshService::reloadConfig()
|
||||
{
|
||||
// If we can successfully set this radio to these settings, save them to disk
|
||||
|
||||
// This will also update the region as needed
|
||||
bool didReset = nodeDB.resetRadioConfig(); // Don't let the phone send us fatally bad settings
|
||||
|
||||
configChanged.notifyObservers(NULL);
|
||||
nodeDB.saveToDisk();
|
||||
|
||||
|
||||
@@ -150,6 +150,9 @@ bool NodeDB::resetRadioConfig()
|
||||
radioConfig.preferences.region = RegionCode_TW;
|
||||
}
|
||||
|
||||
// Update the global myRegion
|
||||
initRegion();
|
||||
|
||||
return didFactoryReset;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,18 @@ const RegionInfo regions[] = {
|
||||
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()
|
||||
{
|
||||
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
|
||||
@@ -91,20 +102,10 @@ void printPacket(const char *prefix, const MeshPacket *p)
|
||||
DEBUG_MSG(")\n");
|
||||
}
|
||||
|
||||
RadioInterface::RadioInterface()
|
||||
RadioInterface::RadioInterface()
|
||||
{
|
||||
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
|
||||
// DEBUG_MSG("Set meshradio defaults name=%s\n", channelSettings.name);
|
||||
}
|
||||
@@ -120,7 +121,7 @@ bool RadioInterface::init()
|
||||
// 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
|
||||
// time.
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
#include "SX1262Interface.h"
|
||||
#include <configuration.h>
|
||||
|
||||
// Particular boards might define a different max power based on what their hardware can do
|
||||
#ifndef SX1262_MAX_POWER
|
||||
#define SX1262_MAX_POWER 22
|
||||
#endif
|
||||
|
||||
SX1262Interface::SX1262Interface(RADIOLIB_PIN_TYPE cs, RADIOLIB_PIN_TYPE irq, RADIOLIB_PIN_TYPE rst, RADIOLIB_PIN_TYPE busy,
|
||||
SPIClass &spi)
|
||||
: RadioLibInterface(cs, irq, rst, busy, spi, &lora), lora(&module)
|
||||
@@ -39,10 +44,10 @@ bool SX1262Interface::init()
|
||||
applyModemConfig();
|
||||
|
||||
if (power == 0)
|
||||
power = 22;
|
||||
power = SX1262_MAX_POWER;
|
||||
|
||||
if (power > 22) // This chip has lower power limits than some
|
||||
power = 22;
|
||||
if (power > SX1262_MAX_POWER) // This chip has lower power limits than some
|
||||
power = SX1262_MAX_POWER;
|
||||
|
||||
limitPower();
|
||||
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
#include "meshhttpStatic.h"
|
||||
#include "meshwifi/meshwifi.h"
|
||||
#include "sleep.h"
|
||||
#include <HTTPBodyParser.hpp>
|
||||
#include <HTTPMultipartBodyParser.hpp>
|
||||
#include <HTTPURLEncodedBodyParser.hpp>
|
||||
#include <SPIFFS.h>
|
||||
#include <WebServer.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
@@ -49,10 +53,11 @@ void handleStyleCSS(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleHotspot(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleFavicon(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleRoot(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleBasicHTML(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleBasicJS(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleStaticPost(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleStatic(HTTPRequest *req, HTTPResponse *res);
|
||||
void handle404(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleFormUpload(HTTPRequest *req, HTTPResponse *res);
|
||||
|
||||
void middlewareSpeedUp240(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
|
||||
void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<void()> next);
|
||||
@@ -63,6 +68,12 @@ bool isCertReady = 0;
|
||||
|
||||
uint32_t timeSpeedUp = 0;
|
||||
|
||||
// We need to specify some content-type mapping, so the resources get delivered with the
|
||||
// right content type and are displayed correctly in the browser
|
||||
char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"}, {".js", "text/javascript"},
|
||||
{".png", "image/png"}, {".jpg", "image/jpg"}, {".gz", "application/gzip"},
|
||||
{".gif", "image/gif"}, {".json", "application/json"}, {"", ""}};
|
||||
|
||||
void handleWebResponse()
|
||||
{
|
||||
if (isWifiAvailable() == 0) {
|
||||
@@ -78,9 +89,11 @@ void handleWebResponse()
|
||||
insecureServer->loop();
|
||||
}
|
||||
|
||||
// Slow down the CPU if we have not received a request within the last
|
||||
// 2 minutes.
|
||||
if (millis() - timeSpeedUp >= (2 * 60 * 1000)) {
|
||||
/*
|
||||
Slow down the CPU if we have not received a request within the last few
|
||||
seconds.
|
||||
*/
|
||||
if (millis() - timeSpeedUp >= (25 * 1000)) {
|
||||
setCpuFrequencyMhz(80);
|
||||
timeSpeedUp = millis();
|
||||
}
|
||||
@@ -216,10 +229,11 @@ void initWebServer()
|
||||
ResourceNode *nodeHotspot = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
|
||||
ResourceNode *nodeFavicon = new ResourceNode("/favicon.ico", "GET", &handleFavicon);
|
||||
ResourceNode *nodeRoot = new ResourceNode("/", "GET", &handleRoot);
|
||||
ResourceNode *nodeBasicHTML = new ResourceNode("/basic.html", "GET", &handleBasicHTML);
|
||||
ResourceNode *nodeBasicJS = new ResourceNode("/basic.js", "GET", &handleBasicJS);
|
||||
ResourceNode *nodeStaticBrowse = new ResourceNode("/static", "GET", &handleStaticBrowse);
|
||||
ResourceNode *nodeStaticPOST = new ResourceNode("/static", "POST", &handleStaticPost);
|
||||
ResourceNode *nodeStatic = new ResourceNode("/static/*", "GET", &handleStatic);
|
||||
ResourceNode *node404 = new ResourceNode("", "GET", &handle404);
|
||||
ResourceNode *nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
|
||||
|
||||
// Secure nodes
|
||||
secureServer->registerNode(nodeAPIv1ToRadioOptions);
|
||||
@@ -228,10 +242,11 @@ void initWebServer()
|
||||
secureServer->registerNode(nodeHotspot);
|
||||
secureServer->registerNode(nodeFavicon);
|
||||
secureServer->registerNode(nodeRoot);
|
||||
secureServer->registerNode(nodeBasicHTML);
|
||||
secureServer->registerNode(nodeBasicJS);
|
||||
secureServer->registerNode(nodeStaticBrowse);
|
||||
secureServer->registerNode(nodeStaticPOST);
|
||||
secureServer->registerNode(nodeStatic);
|
||||
secureServer->setDefaultNode(node404);
|
||||
secureServer->setDefaultNode(nodeFormUpload);
|
||||
|
||||
secureServer->addMiddleware(&middlewareSpeedUp240);
|
||||
|
||||
@@ -242,19 +257,22 @@ void initWebServer()
|
||||
insecureServer->registerNode(nodeHotspot);
|
||||
insecureServer->registerNode(nodeFavicon);
|
||||
insecureServer->registerNode(nodeRoot);
|
||||
insecureServer->registerNode(nodeBasicHTML);
|
||||
insecureServer->registerNode(nodeBasicJS);
|
||||
insecureServer->registerNode(nodeStaticBrowse);
|
||||
insecureServer->registerNode(nodeStaticPOST);
|
||||
insecureServer->registerNode(nodeStatic);
|
||||
insecureServer->setDefaultNode(node404);
|
||||
insecureServer->setDefaultNode(nodeFormUpload);
|
||||
|
||||
insecureServer->addMiddleware(&middlewareSpeedUp160);
|
||||
|
||||
DEBUG_MSG("Starting Web Server...\n");
|
||||
DEBUG_MSG("Starting Web Servers...\n");
|
||||
secureServer->start();
|
||||
insecureServer->start();
|
||||
if (secureServer->isRunning() && insecureServer->isRunning()) {
|
||||
DEBUG_MSG("Web Server Ready\n");
|
||||
DEBUG_MSG("HTTP and HTTPS Web Servers Ready! :-) \n");
|
||||
isWebServerReady = 1;
|
||||
} else {
|
||||
DEBUG_MSG("HTTP and HTTPS Web Servers Failed! ;-( \n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,40 +299,396 @@ void middlewareSpeedUp160(HTTPRequest *req, HTTPResponse *res, std::function<voi
|
||||
timeSpeedUp = millis();
|
||||
}
|
||||
|
||||
void handleStaticPost(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
// Assume POST request. Contains submitted data.
|
||||
res->println("<html><head><title>File Edited</title><meta http-equiv=\"refresh\" content=\"3;url=/static\" "
|
||||
"/><head><body><h1>File Edited</h1>");
|
||||
|
||||
// The form is submitted with the x-www-form-urlencoded content type, so we need the
|
||||
// HTTPURLEncodedBodyParser to read the fields.
|
||||
// Note that the content of the file's content comes from a <textarea>, so we
|
||||
// can use the URL encoding here, since no file upload from an <input type="file"
|
||||
// is involved.
|
||||
HTTPURLEncodedBodyParser parser(req);
|
||||
|
||||
// The bodyparser will consume the request body. That means you can iterate over the
|
||||
// fields only ones. For that reason, we need to create variables for all fields that
|
||||
// we expect. So when parsing is done, you can process the field values from your
|
||||
// temporary variables.
|
||||
std::string filename;
|
||||
bool savedFile = false;
|
||||
|
||||
// Iterate over the fields from the request body by calling nextField(). This function
|
||||
// will update the field name and value of the body parsers. If the last field has been
|
||||
// reached, it will return false and the while loop stops.
|
||||
while (parser.nextField()) {
|
||||
// Get the field name, so that we can decide what the value is for
|
||||
std::string name = parser.getFieldName();
|
||||
|
||||
if (name == "filename") {
|
||||
// Read the filename from the field's value, add the /public prefix and store it in
|
||||
// the filename variable.
|
||||
char buf[512];
|
||||
size_t readLength = parser.read((byte *)buf, 512);
|
||||
// filename = std::string("/public/") + std::string(buf, readLength);
|
||||
filename = std::string(buf, readLength);
|
||||
|
||||
} else if (name == "content") {
|
||||
// Browsers must return the fields in the order that they are placed in
|
||||
// the HTML form, so if the broweser behaves correctly, this condition will
|
||||
// never be true. We include it for safety reasons.
|
||||
if (filename == "") {
|
||||
res->println("<p>Error: form contained content before filename.</p>");
|
||||
break;
|
||||
}
|
||||
|
||||
// With parser.read() and parser.endOfField(), we can stream the field content
|
||||
// into a buffer. That allows handling arbitrarily-sized field contents. Here,
|
||||
// we use it and write the file contents directly to the SPIFFS:
|
||||
size_t fieldLength = 0;
|
||||
File file = SPIFFS.open(filename.c_str(), "w");
|
||||
savedFile = true;
|
||||
while (!parser.endOfField()) {
|
||||
byte buf[512];
|
||||
size_t readLength = parser.read(buf, 512);
|
||||
file.write(buf, readLength);
|
||||
fieldLength += readLength;
|
||||
}
|
||||
file.close();
|
||||
res->printf("<p>Saved %d bytes to %s</p>", int(fieldLength), filename.c_str());
|
||||
|
||||
} else {
|
||||
res->printf("<p>Unexpected field %s</p>", name.c_str());
|
||||
}
|
||||
}
|
||||
if (!savedFile) {
|
||||
res->println("<p>No file to save...</p>");
|
||||
}
|
||||
res->println("</body></html>");
|
||||
}
|
||||
|
||||
void handleStaticBrowse(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
// Get access to the parameters
|
||||
ResourceParameters *params = req->getParams();
|
||||
std::string paramValDelete;
|
||||
std::string paramValEdit;
|
||||
|
||||
// Set a default content type
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
|
||||
if (params->getQueryParameter("delete", paramValDelete)) {
|
||||
std::string pathDelete = "/" + paramValDelete;
|
||||
if (SPIFFS.remove(pathDelete.c_str())) {
|
||||
Serial.println(pathDelete.c_str());
|
||||
res->println("<html><head><meta http-equiv=\"refresh\" content=\"3;url=/static\" /><title>File "
|
||||
"deleted!</title></head><body><h1>File deleted!</h1>");
|
||||
res->println("<meta http-equiv=\"refresh\" content=\"2;url=/static\" />\n");
|
||||
res->println("</body></html>");
|
||||
|
||||
return;
|
||||
} else {
|
||||
Serial.println(pathDelete.c_str());
|
||||
res->println("<html><head><meta http-equiv=\"refresh\" content=\"3;url=/static\" /><title>Error deleteing "
|
||||
"file!</title></head><body><h1>Error deleteing file!</h1>");
|
||||
res->println("Error deleteing file!<br>");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (params->getQueryParameter("edit", paramValEdit)) {
|
||||
std::string pathEdit = "/" + paramValEdit;
|
||||
res->println("<html><head><title>Edit "
|
||||
"file</title></head><body><h1>Edit file - ");
|
||||
|
||||
res->println(pathEdit.c_str());
|
||||
|
||||
res->println("</h1>");
|
||||
res->println("<form method=post action=/static enctype=application/x-www-form-urlencoded>");
|
||||
res->printf("<input name=\"filename\" type=\"hidden\" value=\"%s\">", pathEdit.c_str());
|
||||
res->print("<textarea id=id name=content rows=20 cols=80>");
|
||||
|
||||
// Try to open the file from SPIFFS
|
||||
File file = SPIFFS.open(pathEdit.c_str());
|
||||
|
||||
if (file.available()) {
|
||||
// Read the file from SPIFFS and write it to the HTTP response body
|
||||
size_t length = 0;
|
||||
do {
|
||||
char buffer[256];
|
||||
length = file.read((uint8_t *)buffer, 256);
|
||||
std::string bufferString(buffer, length);
|
||||
|
||||
// Escape gt and lt
|
||||
replaceAll(bufferString, "<", "<");
|
||||
replaceAll(bufferString, ">", ">");
|
||||
|
||||
res->write((uint8_t *)bufferString.c_str(), bufferString.size());
|
||||
} while (length > 0);
|
||||
} else {
|
||||
res->println("Error: File not found");
|
||||
}
|
||||
|
||||
res->println("</textarea><br>");
|
||||
res->println("<input type=submit value=Submit>");
|
||||
res->println("</form>");
|
||||
res->println("</body></html>");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
res->println("<h2>Upload new file</h2>");
|
||||
res->println("<p><b>*** This interface is experimental ***</b></p>");
|
||||
res->println("<p>This form allows you to upload files. Keep your filenames very short and files small. Big filenames and big "
|
||||
"files are a known problem.</p>");
|
||||
res->println("<form method=\"POST\" action=\"/upload\" enctype=\"multipart/form-data\">");
|
||||
res->println("file: <input type=\"file\" name=\"file\"><br>");
|
||||
res->println("<input type=\"submit\" value=\"Upload\">");
|
||||
res->println("</form>");
|
||||
|
||||
res->println("<h2>All Files</h2>");
|
||||
|
||||
File root = SPIFFS.open("/");
|
||||
if (root.isDirectory()) {
|
||||
res->println("<script type=\"text/javascript\">function confirm_delete() {return confirm('Are you sure?');}</script>");
|
||||
|
||||
res->println("<table>");
|
||||
res->println("<tr>");
|
||||
res->println("<td>File");
|
||||
res->println("</td>");
|
||||
res->println("<td>Size");
|
||||
res->println("</td>");
|
||||
res->println("<td colspan=2>Actions");
|
||||
res->println("</td>");
|
||||
res->println("</tr>");
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
String filePath = String(file.name());
|
||||
if (filePath.indexOf("/static") == 0) {
|
||||
res->println("<tr>");
|
||||
res->println("<td>");
|
||||
|
||||
if (String(file.name()).substring(1).endsWith(".gz")) {
|
||||
String modifiedFile = String(file.name()).substring(1);
|
||||
modifiedFile.remove((modifiedFile.length() - 3), 3);
|
||||
res->print("<a href=\"" + modifiedFile + "\">" + String(file.name()).substring(1) + "</a>");
|
||||
} else {
|
||||
res->print("<a href=\"" + String(file.name()).substring(1) + "\">" + String(file.name()).substring(1) +
|
||||
"</a>");
|
||||
}
|
||||
res->println("</td>");
|
||||
res->println("<td>");
|
||||
res->print(String(file.size()));
|
||||
res->println("</td>");
|
||||
res->println("<td>");
|
||||
res->print("<a href=\"/static?delete=" + String(file.name()).substring(1) +
|
||||
"\" onclick=\"return confirm_delete()\">Delete</a> ");
|
||||
res->println("</td>");
|
||||
res->println("<td>");
|
||||
if (!String(file.name()).substring(1).endsWith(".gz")) {
|
||||
res->print("<a href=\"/static?edit=" + String(file.name()).substring(1) + "\">Edit</a>");
|
||||
}
|
||||
res->println("</td>");
|
||||
res->println("</tr>");
|
||||
}
|
||||
|
||||
file = root.openNextFile();
|
||||
}
|
||||
res->println("</table>");
|
||||
|
||||
res->print("<br>");
|
||||
// res->print("Total : " + String(SPIFFS.totalBytes()) + " Bytes<br>");
|
||||
res->print("Used : " + String(SPIFFS.usedBytes()) + " Bytes<br>");
|
||||
res->print("Free : " + String(SPIFFS.totalBytes() - SPIFFS.usedBytes()) + " Bytes<br>");
|
||||
}
|
||||
}
|
||||
|
||||
void handleStatic(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
// Get access to the parameters
|
||||
ResourceParameters *params = req->getParams();
|
||||
|
||||
// Set a default content type
|
||||
res->setHeader("Content-Type", "text/plain");
|
||||
res->setHeader("Content-Type", "application/octet-stream");
|
||||
|
||||
std::string parameter1;
|
||||
// Print the first parameter value
|
||||
if (params->getPathParameter(0, parameter1)) {
|
||||
if (parameter1 == "meshtastic.js") {
|
||||
res->setHeader("Content-Encoding", "gzip");
|
||||
res->setHeader("Content-Type", "application/json");
|
||||
res->write(STATIC_MESHTASTIC_JS_DATA, STATIC_MESHTASTIC_JS_LENGTH);
|
||||
return;
|
||||
|
||||
} else if (parameter1 == "style.css") {
|
||||
res->setHeader("Content-Encoding", "gzip");
|
||||
res->setHeader("Content-Type", "text/css");
|
||||
res->write(STATIC_STYLE_CSS_DATA, STATIC_STYLE_CSS_LENGTH);
|
||||
return;
|
||||
|
||||
} else {
|
||||
res->print("Parameter 1: ");
|
||||
res->printStd(parameter1);
|
||||
std::string filename = "/static/" + parameter1;
|
||||
std::string filenameGzip = "/static/" + parameter1 + ".gz";
|
||||
|
||||
if (!SPIFFS.exists(filename.c_str()) && !SPIFFS.exists(filenameGzip.c_str())) {
|
||||
// Send "404 Not Found" as response, as the file doesn't seem to exist
|
||||
res->setStatusCode(404);
|
||||
res->setStatusText("Not found");
|
||||
res->println("404 Not Found");
|
||||
res->printf("<p>File not found: %s</p>\n", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to open the file from SPIFFS
|
||||
File file;
|
||||
|
||||
if (SPIFFS.exists(filename.c_str())) {
|
||||
file = SPIFFS.open(filename.c_str());
|
||||
if (!file.available()) {
|
||||
DEBUG_MSG("File not available - %s\n", filename.c_str());
|
||||
}
|
||||
|
||||
} else if (SPIFFS.exists(filenameGzip.c_str())) {
|
||||
file = SPIFFS.open(filenameGzip.c_str());
|
||||
res->setHeader("Content-Encoding", "gzip");
|
||||
if (!file.available()) {
|
||||
DEBUG_MSG("File not available\n");
|
||||
}
|
||||
}
|
||||
|
||||
res->setHeader("Content-Length", httpsserver::intToString(file.size()));
|
||||
|
||||
// Content-Type is guessed using the definition of the contentTypes-table defined above
|
||||
int cTypeIdx = 0;
|
||||
do {
|
||||
if (filename.rfind(contentTypes[cTypeIdx][0]) != std::string::npos) {
|
||||
res->setHeader("Content-Type", contentTypes[cTypeIdx][1]);
|
||||
break;
|
||||
}
|
||||
cTypeIdx += 1;
|
||||
} while (strlen(contentTypes[cTypeIdx][0]) > 0);
|
||||
|
||||
// Read the file from SPIFFS and write it to the HTTP response body
|
||||
size_t length = 0;
|
||||
do {
|
||||
char buffer[256];
|
||||
length = file.read((uint8_t *)buffer, 256);
|
||||
std::string bufferString(buffer, length);
|
||||
res->write((uint8_t *)bufferString.c_str(), bufferString.size());
|
||||
} while (length > 0);
|
||||
|
||||
file.close();
|
||||
|
||||
return;
|
||||
|
||||
} else {
|
||||
res->println("ERROR: This should not have happened...");
|
||||
}
|
||||
}
|
||||
|
||||
void handleFormUpload(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
// First, we need to check the encoding of the form that we have received.
|
||||
// The browser will set the Content-Type request header, so we can use it for that purpose.
|
||||
// Then we select the body parser based on the encoding.
|
||||
// Actually we do this only for documentary purposes, we know the form is going
|
||||
// to be multipart/form-data.
|
||||
HTTPBodyParser *parser;
|
||||
std::string contentType = req->getHeader("Content-Type");
|
||||
|
||||
// The content type may have additional properties after a semicolon, for exampel:
|
||||
// Content-Type: text/html;charset=utf-8
|
||||
// Content-Type: multipart/form-data;boundary=------s0m3w31rdch4r4c73rs
|
||||
// As we're interested only in the actual mime _type_, we strip everything after the
|
||||
// first semicolon, if one exists:
|
||||
size_t semicolonPos = contentType.find(";");
|
||||
if (semicolonPos != std::string::npos) {
|
||||
contentType = contentType.substr(0, semicolonPos);
|
||||
}
|
||||
|
||||
// Now, we can decide based on the content type:
|
||||
if (contentType == "multipart/form-data") {
|
||||
parser = new HTTPMultipartBodyParser(req);
|
||||
} else {
|
||||
Serial.printf("Unknown POST Content-Type: %s\n", contentType.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
res->println("<html><head><meta http-equiv=\"refresh\" content=\"3;url=/static\" /><title>File "
|
||||
"Upload</title></head><body><h1>File Upload</h1>");
|
||||
|
||||
// We iterate over the fields. Any field with a filename is uploaded.
|
||||
// Note that the BodyParser consumes the request body, meaning that you can iterate over the request's
|
||||
// fields only a single time. The reason for this is that it allows you to handle large requests
|
||||
// which would not fit into memory.
|
||||
bool didwrite = false;
|
||||
|
||||
// parser->nextField() will move the parser to the next field in the request body (field meaning a
|
||||
// form field, if you take the HTML perspective). After the last field has been processed, nextField()
|
||||
// returns false and the while loop ends.
|
||||
while (parser->nextField()) {
|
||||
// For Multipart data, each field has three properties:
|
||||
// The name ("name" value of the <input> tag)
|
||||
// The filename (If it was a <input type="file">, this is the filename on the machine of the
|
||||
// user uploading it)
|
||||
// The mime type (It is determined by the client. So do not trust this value and blindly start
|
||||
// parsing files only if the type matches)
|
||||
std::string name = parser->getFieldName();
|
||||
std::string filename = parser->getFieldFilename();
|
||||
std::string mimeType = parser->getFieldMimeType();
|
||||
// We log all three values, so that you can observe the upload on the serial monitor:
|
||||
DEBUG_MSG("handleFormUpload: field name='%s', filename='%s', mimetype='%s'\n", name.c_str(), filename.c_str(),
|
||||
mimeType.c_str());
|
||||
|
||||
// Double check that it is what we expect
|
||||
if (name != "file") {
|
||||
DEBUG_MSG("Skipping unexpected field");
|
||||
res->println("<p>No file found.</p>");
|
||||
return;
|
||||
}
|
||||
|
||||
// Double check that it is what we expect
|
||||
if (filename == "") {
|
||||
DEBUG_MSG("Skipping unexpected field");
|
||||
res->println("<p>No file found.</p>");
|
||||
return;
|
||||
}
|
||||
|
||||
// SPIFFS limits the total lenth of a path + file to 31 characters.
|
||||
if (filename.length() + 8 > 31) {
|
||||
DEBUG_MSG("Uploaded filename too long!");
|
||||
res->println("<p>Uploaded filename too long! Limit of 23 characters.</p>");
|
||||
delete parser;
|
||||
return;
|
||||
}
|
||||
|
||||
// You should check file name validity and all that, but we skip that to make the core
|
||||
// concepts of the body parser functionality easier to understand.
|
||||
std::string pathname = "/static/" + filename;
|
||||
|
||||
// Create a new file on spiffs to stream the data into
|
||||
File file = SPIFFS.open(pathname.c_str(), "w");
|
||||
size_t fileLength = 0;
|
||||
didwrite = true;
|
||||
|
||||
// With endOfField you can check whether the end of field has been reached or if there's
|
||||
// still data pending. With multipart bodies, you cannot know the field size in advance.
|
||||
while (!parser->endOfField()) {
|
||||
byte buf[512];
|
||||
size_t readLength = parser->read(buf, 512);
|
||||
file.write(buf, readLength);
|
||||
fileLength += readLength;
|
||||
|
||||
// Abort the transfer if there is less than 50k space left on the filesystem.
|
||||
if (SPIFFS.totalBytes() - SPIFFS.usedBytes() < 51200) {
|
||||
file.close();
|
||||
res->println("<p>Write aborted! File is won't fit!</p>");
|
||||
|
||||
delete parser;
|
||||
return;
|
||||
}
|
||||
}
|
||||
file.close();
|
||||
res->printf("<p>Saved %d bytes to %s</p>", (int)fileLength, pathname.c_str());
|
||||
}
|
||||
if (!didwrite) {
|
||||
res->println("<p>Did not write any file</p>");
|
||||
}
|
||||
res->println("</body></html>");
|
||||
delete parser;
|
||||
}
|
||||
|
||||
void handle404(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
|
||||
@@ -389,15 +763,22 @@ void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res)
|
||||
uint32_t len = 1;
|
||||
|
||||
if (params->getQueryParameter("all", valueAll)) {
|
||||
|
||||
// If all is ture, return all the buffers we have available
|
||||
// to us at this point in time.
|
||||
if (valueAll == "true") {
|
||||
while (len) {
|
||||
len = webAPI.getFromRadio(txBuf);
|
||||
res->write(txBuf, len);
|
||||
}
|
||||
|
||||
// Otherwise, just return one protobuf
|
||||
} else {
|
||||
len = webAPI.getFromRadio(txBuf);
|
||||
res->write(txBuf, len);
|
||||
}
|
||||
|
||||
// the param "all" was not spcified. Return just one protobuf
|
||||
} else {
|
||||
len = webAPI.getFromRadio(txBuf);
|
||||
res->write(txBuf, len);
|
||||
@@ -450,248 +831,56 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
|
||||
*/
|
||||
void handleRoot(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
|
||||
String out = "";
|
||||
out +=
|
||||
"<!DOCTYPE html>\n"
|
||||
"<html lang=\"en\" >\n"
|
||||
"<!-- Updated 20200923 - Change JSON input -->\n"
|
||||
"<!-- Updated 20200924 - Replace FontAwesome with SVG -->\n"
|
||||
"<head>\n"
|
||||
" <meta charset=\"UTF-8\">\n"
|
||||
" <title>Meshtastic - Chat</title>\n"
|
||||
" <link rel=\"stylesheet\" href=\"static/style.css\">\n"
|
||||
"\n"
|
||||
"</head>\n"
|
||||
"<body>\n"
|
||||
"<center><h1>This area is under development. Please don't file bugs.</h1></center><!-- Add SVG for Symbols -->\n"
|
||||
"<svg aria-hidden=\"true\" style=\"position: absolute; width: 0; height: 0; overflow: hidden;\" version=\"1.1\" "
|
||||
"xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
|
||||
"<defs>\n"
|
||||
"<symbol id=\"icon-map-marker\" viewBox=\"0 0 16 28\">\n"
|
||||
"<path d=\"M12 10c0-2.203-1.797-4-4-4s-4 1.797-4 4 1.797 4 4 4 4-1.797 4-4zM16 10c0 0.953-0.109 1.937-0.516 2.797l-5.688 "
|
||||
"12.094c-0.328 0.688-1.047 1.109-1.797 1.109s-1.469-0.422-1.781-1.109l-5.703-12.094c-0.406-0.859-0.516-1.844-0.516-2.797 "
|
||||
"0-4.422 3.578-8 8-8s8 3.578 8 8z\"></path>\n"
|
||||
"</symbol>\n"
|
||||
"<symbol id=\"icon-circle\" viewBox=\"0 0 24 28\">\n"
|
||||
"<path d=\"M24 14c0 6.625-5.375 12-12 12s-12-5.375-12-12 5.375-12 12-12 12 5.375 12 12z\"></path>\n"
|
||||
"</symbol>\n"
|
||||
"</defs>\n"
|
||||
"</svg>\n"
|
||||
"<div class=\"grid\">\n"
|
||||
"\t<div class=\"top\">\n"
|
||||
"\t\t<div class=\"top-text\">Meshtastic - Chat</div>\n"
|
||||
"\t</div>\n"
|
||||
"\n"
|
||||
"\t<div class=\"side clearfix\">\n"
|
||||
" <div class=\"channel-list\" id=\"channel-list\">\n"
|
||||
"\t <div class=\"side-header\">\n"
|
||||
"\t\t<div class=\"side-text\">Users</div>\n"
|
||||
"\t </div>\n"
|
||||
" <ul class=\"list\" id='userlist-id'>\n"
|
||||
" </ul>\n"
|
||||
" </div>\n"
|
||||
" </div>\n"
|
||||
" <div class=\"content\">\n"
|
||||
" <div class=\"content-header clearfix\">\n"
|
||||
"<!-- <div class=\"content-about\"> -->\n"
|
||||
" <div class=\"content-from\">\n"
|
||||
"\t\t <span class=\"content-from-highlight\" id=\"content-from-id\">All Users</span>\n"
|
||||
"\t\t </div>\n"
|
||||
"<!-- </div> -->\n"
|
||||
" </div> <!-- end content-header -->\n"
|
||||
" \n"
|
||||
" <div class=\"content-history\" id='chat-div-id'>\n"
|
||||
" <ul id='chat-history-id'>\n"
|
||||
"\t\t</ul>\n"
|
||||
" \n"
|
||||
" </div> <!-- end content-history -->\n"
|
||||
" \n"
|
||||
" <div class=\"content-message clearfix\">\n"
|
||||
" <textarea name=\"message-to-send\" id=\"message-to-send\" placeholder =\"Type your message\" "
|
||||
"rows=\"3\"></textarea>\n"
|
||||
" \n"
|
||||
" \n"
|
||||
" <button>Send</button>\n"
|
||||
"\n"
|
||||
" </div> <!-- end content-message -->\n"
|
||||
" \n"
|
||||
" </div> <!-- end content -->\n"
|
||||
" \n"
|
||||
" </div> <!-- end container -->\n"
|
||||
"\n"
|
||||
"<script src=\"/scripts/script.js\"></script>\n"
|
||||
"\n"
|
||||
"</body>\n"
|
||||
"</html>\n"
|
||||
"";
|
||||
|
||||
// Status code is 200 OK by default.
|
||||
// We want to deliver a simple HTML page, so we send a corresponding content type:
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
|
||||
// The response implements the Print interface, so you can use it just like
|
||||
// you would write to Serial etc.
|
||||
res->print(out);
|
||||
}
|
||||
randomSeed(millis());
|
||||
|
||||
void handleScriptsScriptJS(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
String out = "";
|
||||
out += "String.prototype.toHHMMSS = function () {\n"
|
||||
" var sec_num = parseInt(this, 10); // don't forget the second param\n"
|
||||
" var hours = Math.floor(sec_num / 3600);\n"
|
||||
" var minutes = Math.floor((sec_num - (hours * 3600)) / 60);\n"
|
||||
" var seconds = sec_num - (hours * 3600) - (minutes * 60);\n"
|
||||
"\n"
|
||||
" if (hours < 10) {hours = \"0\"+hours;}\n"
|
||||
" if (minutes < 10) {minutes = \"0\"+minutes;}\n"
|
||||
" if (seconds < 10) {seconds = \"0\"+seconds;}\n"
|
||||
"// return hours+':'+minutes+':'+seconds;\n"
|
||||
"\treturn hours+'h'+minutes+'m';\n"
|
||||
"}\n"
|
||||
"String.prototype.padLeft = function (length, character) { \n"
|
||||
" return new Array(length - this.length + 1).join(character || ' ') + this; \n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"Date.prototype.toFormattedString = function () {\n"
|
||||
" return [String(this.getFullYear()).substr(2, 2),\n"
|
||||
"\t\t\tString(this.getMonth()+1).padLeft(2, '0'),\n"
|
||||
" String(this.getDate()).padLeft(2, '0')].join(\"/\") + \" \" +\n"
|
||||
" [String(this.getHours()).padLeft(2, '0'),\n"
|
||||
" String(this.getMinutes()).padLeft(2, '0')].join(\":\");\n"
|
||||
"};\n"
|
||||
"\n"
|
||||
"function getData(file) {\n"
|
||||
"\tfetch(file)\n"
|
||||
"\t.then(function (response) {\n"
|
||||
"\t\treturn response.json();\n"
|
||||
"\t})\n"
|
||||
"\t.then(function (datafile) {\n"
|
||||
"\t\tupdateData(datafile);\n"
|
||||
"\t})\n"
|
||||
"\t.catch(function (err) {\n"
|
||||
"\t\tconsole.log('error: ' + err);\n"
|
||||
"\t});\n"
|
||||
"}\n"
|
||||
"\t\n"
|
||||
"function updateData(datafile) {\n"
|
||||
"// Update System Details\n"
|
||||
"\tupdateSystem(datafile);\n"
|
||||
"//\tUpdate Userlist and message count\n"
|
||||
"\tupdateUsers(datafile);\n"
|
||||
"// Update Chat\n"
|
||||
"\tupdateChat(datafile);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function updateSystem(datafile) {\n"
|
||||
"// Update System Info \n"
|
||||
"\tvar sysContainer = document.getElementById(\"content-from-id\");\n"
|
||||
"\tvar newHTML = datafile.data.system.channel;\n"
|
||||
"\tvar myDate = new Date( datafile.data.system.timeGPS *1000);\n"
|
||||
"\tnewHTML += ' @' + myDate.toFormattedString();\n"
|
||||
"\tvar newSec = datafile.data.system.timeSinceStart;\n"
|
||||
"\tvar strsecondUp = newSec.toString();\n"
|
||||
"\tnewHTML += ' Up:' + strsecondUp.toHHMMSS();\n"
|
||||
"\tsysContainer.innerHTML = newHTML;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function updateUsers(datafile) {\n"
|
||||
"\tvar mainContainer = document.getElementById(\"userlist-id\");\n"
|
||||
"\tvar htmlUsers = '';\n"
|
||||
"\tvar timeBase = datafile.data.system.timeSinceStart;\n"
|
||||
"//\tvar lookup = {};\n"
|
||||
" for (var i = 0; i < datafile.data.users.length; i++) {\n"
|
||||
" htmlUsers += formatUsers(datafile.data.users[i],timeBase);\n"
|
||||
"\t}\n"
|
||||
"\tmainContainer.innerHTML = htmlUsers;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function formatUsers(user,timeBase) {\n"
|
||||
"\tnewHTML = '<li class=\"clearfix\">';\n"
|
||||
" newHTML += '<div class=\"channel-name clearfix\">' + user.NameLong + '(' + user.NameShort + ')</div>';\n"
|
||||
" newHTML += '<div class=\"message-count clearfix\">';\n"
|
||||
"\tvar secondsLS = timeBase - user.lastSeen;\n"
|
||||
"\tvar strsecondsLS = secondsLS.toString();\n"
|
||||
"\tnewHTML += '<svg class=\"icon icon-circle '+onlineStatus(secondsLS)+'\"><use "
|
||||
"xlink:href=\"#icon-circle\"></use></svg></i>Seen: '+strsecondsLS.toHHMMSS()+' ago ';\n"
|
||||
"\tif (user.lat == 0 || user.lon == 0) {\n"
|
||||
"\t\tnewHTML += '';\n"
|
||||
"\t} else {\n"
|
||||
"\t\tnewHTML += '<div class=\"tooltip\"><svg class=\"icon icon-map-marker\"><use "
|
||||
"xlink:href=\"#icon-map-marker\"></use></svg><span class=\"tooltiptext\">lat:' + user.lat + ' lon:'+ user.lon+ "
|
||||
"'</span>';\n"
|
||||
"\t}\n"
|
||||
" newHTML += '</div></div>';\n"
|
||||
" newHTML += '</li>';\n"
|
||||
"\treturn(newHTML);\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function onlineStatus(time) {\n"
|
||||
"\tif (time < 3600) {\n"
|
||||
"\t\treturn \"online\"\n"
|
||||
"\t} else {\n"
|
||||
"\t\treturn \"offline\"\n"
|
||||
"\t}\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function updateChat(datafile) {\n"
|
||||
"// Update Chat\n"
|
||||
"\tvar chatContainer = document.getElementById(\"chat-history-id\");\n"
|
||||
"\tvar htmlChat = '';\n"
|
||||
"\tvar timeBase = datafile.data.system.timeSinceStart;\n"
|
||||
"\tfor (var i = 0; i < datafile.data.chat.length; i++) {\n"
|
||||
"\t\thtmlChat += formatChat(datafile.data.chat[i],timeBase);\n"
|
||||
"\t}\n"
|
||||
"\tchatContainer.innerHTML = htmlChat;\n"
|
||||
"\tscrollHistory();\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function formatChat(data,timeBase) {\n"
|
||||
"\tvar secondsTS = timeBase - data.timestamp;\n"
|
||||
"\tvar strsecondsTS = secondsTS.toString();\n"
|
||||
"\tnewHTML = '<li class=\"clearfix\">';\n"
|
||||
"\tif (data.local == 1) {\n"
|
||||
"\t\tnewHTML += '<div class=\"message-data\">';\n"
|
||||
"\t\tnewHTML += '<span class=\"message-data-name\" >' + data.NameLong + '(' + data.NameShort + ')</span>';\n"
|
||||
"\t\tnewHTML += '<span class=\"message-data-time\" >' + strsecondsTS.toHHMMSS() + ' ago</span>';\n"
|
||||
"\t\tnewHTML += '</div>';\n"
|
||||
"\t\tnewHTML += '<div class=\"message my-message\">' + data.chatLine + '</div>';\n"
|
||||
"\t} else {\n"
|
||||
"\t\tnewHTML += '<div class=\"message-data align-right\">';\n"
|
||||
"\t\tnewHTML += '<span class=\"message-data-time\" >' + strsecondsTS.toHHMMSS() + ' ago</span> ';\n"
|
||||
"\t\tnewHTML += '<span class=\"message-data-name\" >' + data.NameLong + '(' + data.NameShort + ')</span>';\n"
|
||||
"//\t\tnewHTML += '<i class=\"fa fa-circle online\"></i>';\n"
|
||||
"\t\tnewHTML += '</div>';\n"
|
||||
"\t\tnewHTML += '<div class=\"message other-message float-right\">' + data.chatLine + '</div>';\n"
|
||||
"\t}\n"
|
||||
"\n"
|
||||
" newHTML += '</li>';\n"
|
||||
"\treturn(newHTML);\t\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"function scrollHistory() {\n"
|
||||
"\tvar chatContainer = document.getElementById(\"chat-div-id\");\n"
|
||||
"\tchatContainer.scrollTop = chatContainer.scrollHeight;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"getData('/json/chat/history/dummy');\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"//window.onload=function(){\n"
|
||||
"//\talert('onload');\n"
|
||||
"// Async - Run scroll 0.5sec after onload event\n"
|
||||
"//\tsetTimeout(scrollHistory(),500);\n"
|
||||
"// }";
|
||||
res->setHeader("Set-Cookie",
|
||||
"mt_session=" + httpsserver::intToString(random(1, 9999999)) + "; Expires=Wed, 20 Apr 2049 4:20:00 PST");
|
||||
|
||||
// Status code is 200 OK by default.
|
||||
// We want to deliver a simple HTML page, so we send a corresponding content type:
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
std::string cookie = req->getHeader("Cookie");
|
||||
//String cookieString = cookie.c_str();
|
||||
//uint8_t nameIndex = cookieString.indexOf("mt_session");
|
||||
//DEBUG_MSG(cookie.c_str());
|
||||
|
||||
// The response implements the Print interface, so you can use it just like
|
||||
// you would write to Serial etc.
|
||||
res->print(out);
|
||||
std::string filename = "/static/index.html";
|
||||
std::string filenameGzip = "/static/index.html.gz";
|
||||
|
||||
if (!SPIFFS.exists(filename.c_str()) && !SPIFFS.exists(filenameGzip.c_str())) {
|
||||
// Send "404 Not Found" as response, as the file doesn't seem to exist
|
||||
res->setStatusCode(404);
|
||||
res->setStatusText("Not found");
|
||||
res->println("404 Not Found");
|
||||
res->printf("<p>File not found: %s</p>\n", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to open the file from SPIFFS
|
||||
File file;
|
||||
|
||||
if (SPIFFS.exists(filename.c_str())) {
|
||||
file = SPIFFS.open(filename.c_str());
|
||||
if (!file.available()) {
|
||||
DEBUG_MSG("File not available - %s\n", filename.c_str());
|
||||
}
|
||||
|
||||
} else if (SPIFFS.exists(filenameGzip.c_str())) {
|
||||
file = SPIFFS.open(filenameGzip.c_str());
|
||||
res->setHeader("Content-Encoding", "gzip");
|
||||
if (!file.available()) {
|
||||
DEBUG_MSG("File not available\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Read the file from SPIFFS and write it to the HTTP response body
|
||||
size_t length = 0;
|
||||
do {
|
||||
char buffer[256];
|
||||
length = file.read((uint8_t *)buffer, 256);
|
||||
std::string bufferString(buffer, length);
|
||||
res->write((uint8_t *)bufferString.c_str(), bufferString.size());
|
||||
} while (length > 0);
|
||||
}
|
||||
|
||||
void handleFavicon(HTTPRequest *req, HTTPResponse *res)
|
||||
@@ -702,98 +891,15 @@ void handleFavicon(HTTPRequest *req, HTTPResponse *res)
|
||||
res->write(FAVICON_DATA, FAVICON_LENGTH);
|
||||
}
|
||||
|
||||
/*
|
||||
To convert text to c strings:
|
||||
|
||||
https://tomeko.net/online_tools/cpp_text_escape.php?lang=en
|
||||
*/
|
||||
void handleBasicJS(HTTPRequest *req, HTTPResponse *res)
|
||||
void replaceAll(std::string &str, const std::string &from, const std::string &to)
|
||||
{
|
||||
String out = "";
|
||||
out += "var meshtasticClient;\n"
|
||||
"var connectionOne;\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"// Important: the connect action must be called from a user interaction (e.g. button press), otherwise the browsers "
|
||||
"won't allow the connect\n"
|
||||
"function connect() {\n"
|
||||
"\n"
|
||||
" // Create new connection\n"
|
||||
" var httpconn = new meshtasticjs.IHTTPConnection();\n"
|
||||
"\n"
|
||||
" // Set connection params\n"
|
||||
" let sslActive;\n"
|
||||
" if (window.location.protocol === 'https:') {\n"
|
||||
" sslActive = true;\n"
|
||||
" } else {\n"
|
||||
" sslActive = false;\n"
|
||||
" }\n"
|
||||
" let deviceIp = window.location.hostname; // Your devices IP here\n"
|
||||
" \n"
|
||||
"\n"
|
||||
" // Add event listeners that get called when a new packet is received / state of device changes\n"
|
||||
" httpconn.addEventListener('fromRadio', function(packet) { console.log(packet)});\n"
|
||||
"\n"
|
||||
" // Connect to the device async, then send a text message\n"
|
||||
" httpconn.connect(deviceIp, sslActive)\n"
|
||||
" .then(result => { \n"
|
||||
"\n"
|
||||
" alert('device has been configured')\n"
|
||||
" // This gets called when the connection has been established\n"
|
||||
" // -> send a message over the mesh network. If no recipient node is provided, it gets sent as a broadcast\n"
|
||||
" return httpconn.sendText('meshtastic is awesome');\n"
|
||||
"\n"
|
||||
" })\n"
|
||||
" .then(result => { \n"
|
||||
"\n"
|
||||
" // This gets called when the message has been sucessfully sent\n"
|
||||
" console.log('Message sent!');})\n"
|
||||
"\n"
|
||||
" .catch(error => { console.log(error); });\n"
|
||||
"\n"
|
||||
"}";
|
||||
|
||||
// Status code is 200 OK by default.
|
||||
// We want to deliver a simple HTML page, so we send a corresponding content type:
|
||||
res->setHeader("Content-Type", "text/javascript");
|
||||
|
||||
// The response implements the Print interface, so you can use it just like
|
||||
// you would write to Serial etc.
|
||||
res->print(out);
|
||||
if (from.empty())
|
||||
return;
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
To convert text to c strings:
|
||||
|
||||
https://tomeko.net/online_tools/cpp_text_escape.php?lang=en
|
||||
*/
|
||||
void handleBasicHTML(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
String out = "";
|
||||
out += "<!doctype html>\n"
|
||||
"<html class=\"no-js\" lang=\"\">\n"
|
||||
"\n"
|
||||
"<head>\n"
|
||||
" <meta charset=\"utf-8\">\n"
|
||||
" <title></title>\n"
|
||||
"\n"
|
||||
" <script src=\"/static/meshtastic.js\"></script>\n"
|
||||
" <script src=\"basic.js\"></script>\n"
|
||||
"</head>\n"
|
||||
"\n"
|
||||
"<body>\n"
|
||||
"\n"
|
||||
" <button id=\"connect_button\" onclick=\"connect()\">Connect to Meshtastic device</button>\n"
|
||||
" \n"
|
||||
"</body>\n"
|
||||
"\n"
|
||||
"</html>";
|
||||
|
||||
// Status code is 200 OK by default.
|
||||
// We want to deliver a simple HTML page, so we send a corresponding content type:
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
|
||||
// The response implements the Print interface, so you can use it just like
|
||||
// you would write to Serial etc.
|
||||
res->print(out);
|
||||
}
|
||||
@@ -22,6 +22,7 @@ void handleRoot();
|
||||
void handleScriptsScriptJS();
|
||||
void handleJSONChatHistoryDummy();
|
||||
|
||||
void replaceAll(std::string& str, const std::string& from, const std::string& to);
|
||||
|
||||
class HttpAPI : public PhoneAPI
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@
|
||||
#include "meshwifi/meshhttp.h"
|
||||
#include "target_specific.h"
|
||||
#include <DNSServer.h>
|
||||
#include <ESPmDNS.h>
|
||||
#include <WiFi.h>
|
||||
|
||||
static void WiFiEvent(WiFiEvent_t event);
|
||||
@@ -18,7 +19,7 @@ static WiFiServerPort *apiPort;
|
||||
uint8_t wifiDisconnectReason = 0;
|
||||
|
||||
// Stores our hostname
|
||||
static char ourHost[16];
|
||||
char ourHost[16];
|
||||
|
||||
bool isWifiAvailable()
|
||||
{
|
||||
@@ -63,7 +64,6 @@ void initWifi()
|
||||
|
||||
createSSLCert();
|
||||
|
||||
|
||||
if (radioConfig.has_preferences) {
|
||||
const char *wifiName = radioConfig.preferences.wifi_ssid;
|
||||
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
|
||||
DEBUG_MSG("Not using WIFI\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void initApiServer()
|
||||
{
|
||||
// Start API server on port 4403
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <WiFi.h>
|
||||
|
||||
static bool pinShowing;
|
||||
static uint32_t doublepressed;
|
||||
|
||||
static void startCb(uint32_t pin)
|
||||
{
|
||||
@@ -123,6 +124,7 @@ static int gap_event(struct ble_gap_event *event, void *arg)
|
||||
{
|
||||
struct ble_gap_conn_desc desc;
|
||||
int rc;
|
||||
uint32_t now = millis();
|
||||
|
||||
switch (event->type) {
|
||||
case BLE_GAP_EVENT_CONNECT:
|
||||
@@ -221,8 +223,17 @@ static int gap_event(struct ble_gap_event *event, void *arg)
|
||||
|
||||
if (event->passkey.params.action == BLE_SM_IOACT_DISP) {
|
||||
pkey.action = event->passkey.params.action;
|
||||
pkey.passkey = random(
|
||||
100000, 999999); // This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
|
||||
DEBUG_MSG("dp: %d now:%d\n",doublepressed, now);
|
||||
if (doublepressed > 0 && (doublepressed + (30*1000)) > now)
|
||||
{
|
||||
DEBUG_MSG("User has overridden passkey or no display available\n");
|
||||
pkey.passkey = defaultBLEPin;
|
||||
}
|
||||
else {
|
||||
DEBUG_MSG("Using random passkey\n");
|
||||
pkey.passkey = random(
|
||||
100000, 999999); // This is the passkey to be entered on peer - we pick a number >100,000 to ensure 6 digits
|
||||
}
|
||||
DEBUG_MSG("*** Enter passkey %d on the peer side ***\n", pkey.passkey);
|
||||
|
||||
startCb(pkey.passkey);
|
||||
@@ -443,6 +454,13 @@ int chr_readwrite8(uint8_t *v, size_t vlen, struct ble_gatt_access_ctxt *ctxt)
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
void disablePin()
|
||||
{
|
||||
DEBUG_MSG("User Override, disabling bluetooth pin requirement\n");
|
||||
// keep track of when it was pressed, so we know it was within X seconds
|
||||
doublepressed = millis();
|
||||
}
|
||||
|
||||
// This routine is called multiple times, once each time we come back from sleep
|
||||
void reinitBluetooth()
|
||||
{
|
||||
|
||||
@@ -15,6 +15,7 @@ void updateBatteryLevel(uint8_t level);
|
||||
void deinitBLE();
|
||||
void loopBLE();
|
||||
void reinitBluetooth();
|
||||
void disablePin();
|
||||
|
||||
/**
|
||||
* A helper function that implements simple read and write handling for a uint32_t
|
||||
|
||||
100
src/nrf52/BQ25713.cpp
Normal file
100
src/nrf52/BQ25713.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "BQ25713.h"
|
||||
#include "configuration.h"
|
||||
|
||||
#include <Wire.h>
|
||||
|
||||
#ifdef BQ25703A_ADDR
|
||||
|
||||
const uint8_t BQ25713::devAddr = BQ25703A_ADDR;
|
||||
|
||||
bool BQ25713::setup()
|
||||
{
|
||||
DEBUG_MSG("Init BQ25713\n");
|
||||
|
||||
// if(!writeReg(0x34,0x9034)) return false;
|
||||
//
|
||||
// if(!writeReg(0x34,0x8034)) return false;
|
||||
|
||||
if (!writeReg(0x00, 0x0F0A))
|
||||
return false; // Config Charge Option 0
|
||||
|
||||
if (!writeReg(0x02, 0x0224))
|
||||
return false; // Config Charge Current
|
||||
|
||||
if (!writeReg(0x04, 0x1070))
|
||||
return false; // Config Charge Voltage
|
||||
|
||||
if (!writeReg(0x06, 0x099C))
|
||||
return false; // Config OTG Voltage
|
||||
|
||||
if (!writeReg(0x08, 0x5000))
|
||||
return false; // Config OTG Current
|
||||
|
||||
// if(!writeReg(0x0A,0x0100)) return false;//Config Input Voltage
|
||||
|
||||
if (!writeReg(0x0C, 0x1800))
|
||||
return false; // Config Minimum System Voltage
|
||||
|
||||
if (!writeReg(0x0E, 0x4900))
|
||||
return false; // Config Input Current
|
||||
|
||||
if (!writeReg(0x30, 0xE210))
|
||||
return false; // Config Charge Option 1
|
||||
|
||||
if (!writeReg(0x32, 0x32BF))
|
||||
return false; // Config Charge Option 2
|
||||
|
||||
if (!writeReg(0x34, 0x0834))
|
||||
return false; // Config Charge Option 3
|
||||
|
||||
if (!writeReg(0x36, 0x4A65))
|
||||
return false; // Config Prochot Option 0
|
||||
|
||||
if (!writeReg(0x38, 0x81FF))
|
||||
return false; // Config Prochot Option 1
|
||||
|
||||
if (!writeReg(0x3A, 0xA0FF))
|
||||
return false; // Config ADC Option
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t BQ25713::readReg(uint8_t reg)
|
||||
{
|
||||
Wire.beginTransmission(devAddr);
|
||||
Wire.write(reg);
|
||||
byte err = Wire.endTransmission();
|
||||
if (!err) {
|
||||
int readLen = 2;
|
||||
Wire.requestFrom(devAddr, (int)(readLen + 1));
|
||||
if (Wire.available() >= readLen) {
|
||||
uint8_t lsb = Wire.read(), msb = Wire.read();
|
||||
|
||||
return (((uint16_t)msb) << 8) + lsb;
|
||||
} else
|
||||
return 0;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool BQ25713::writeReg(uint8_t reg, uint16_t v)
|
||||
{
|
||||
Wire.beginTransmission(devAddr);
|
||||
Wire.write(reg);
|
||||
Wire.write(v & 0xff);
|
||||
Wire.write((v >> 8) & 0xff);
|
||||
byte err = Wire.endTransmission(); // 0 for success
|
||||
|
||||
if (!err) {
|
||||
// Do a test readback for early debugging
|
||||
uint16_t found = readReg(reg);
|
||||
if (found != v) {
|
||||
DEBUG_MSG("Readback reg=0x%0x test failed, expected 0x%0x, found 0x%0x!\n", reg, v, found);
|
||||
return true; // claim success - FIXME
|
||||
}
|
||||
}
|
||||
return !err;
|
||||
}
|
||||
|
||||
#endif
|
||||
22
src/nrf52/BQ25713.h
Normal file
22
src/nrf52/BQ25713.h
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
/**
|
||||
* Driver class to control/monitor BQ25713 charge controller
|
||||
*/
|
||||
class BQ25713 {
|
||||
static const uint8_t devAddr;
|
||||
|
||||
public:
|
||||
|
||||
/// Return true for success
|
||||
bool setup();
|
||||
|
||||
private:
|
||||
uint16_t readReg(uint8_t reg);
|
||||
|
||||
/// Return true for success
|
||||
bool writeReg(uint8_t reg, uint16_t v);
|
||||
};
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#include <OLEDDisplay.h>
|
||||
|
||||
class UC1701Spi : public OLEDDisplay
|
||||
{
|
||||
private:
|
||||
uint8_t _rst;
|
||||
uint8_t _dc;
|
||||
uint8_t _cs;
|
||||
|
||||
public:
|
||||
UC1701Spi() { setGeometry(GEOMETRY_128_64); }
|
||||
|
||||
bool connect()
|
||||
{
|
||||
/*
|
||||
pinMode(_dc, OUTPUT);
|
||||
pinMode(_cs, OUTPUT);
|
||||
pinMode(_rst, OUTPUT);
|
||||
|
||||
SPI.begin();
|
||||
SPI.setClockDivider(SPI_CLOCK_DIV2);
|
||||
|
||||
// Pulse Reset low for 10ms
|
||||
digitalWrite(_rst, HIGH);
|
||||
delay(1);
|
||||
digitalWrite(_rst, LOW);
|
||||
delay(10);
|
||||
digitalWrite(_rst, HIGH);
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void display(void) {}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
#include "variant.h"
|
||||
|
||||
#ifdef ERC12864_CS
|
||||
#include <UC1701.h>
|
||||
static UC1701 lcd(PIN_SPI_SCK, PIN_SPI_MOSI, ERC12864_CS, ERC12864_CD);
|
||||
|
||||
void testLCD()
|
||||
{
|
||||
// PCD8544-compatible displays may have a different resolution...
|
||||
lcd.begin();
|
||||
|
||||
// Write a piece of text on the first line...
|
||||
lcd.setCursor(0, 0);
|
||||
lcd.print("Hello, World!");
|
||||
}
|
||||
#endif
|
||||
@@ -82,6 +82,8 @@ int printf(const char *fmt, ...)
|
||||
return res;
|
||||
}
|
||||
|
||||
#include "BQ25713.h"
|
||||
|
||||
void nrf52Setup()
|
||||
{
|
||||
|
||||
@@ -93,8 +95,11 @@ void nrf52Setup()
|
||||
// This is the recommended setting for Monitor Mode Debugging
|
||||
NVIC_SetPriority(DebugMonitor_IRQn, 6UL);
|
||||
|
||||
// Not yet on board
|
||||
// pmu.init();
|
||||
#ifdef BQ25703A_ADDR
|
||||
auto *bq = new BQ25713();
|
||||
if(!bq->setup())
|
||||
DEBUG_MSG("ERROR! Charge controller init failed\n");
|
||||
#endif
|
||||
|
||||
// Init random seed
|
||||
// FIXME - use this to get random numbers
|
||||
|
||||
@@ -209,6 +209,9 @@ External serial flash WP25R1635FZUIL0
|
||||
|
||||
#define HAS_EINK
|
||||
|
||||
// No screen wipes on eink
|
||||
#define SCREEN_TRANSITION_MSECS 0
|
||||
|
||||
#define PIN_SPI1_MISO \
|
||||
(32 + 7) // FIXME not really needed, but for now the SPI code requires something to be defined, pick an used GPIO
|
||||
#define PIN_SPI1_MOSI PIN_EINK_MOSI
|
||||
|
||||
105
variants/lora_relay_v2/variant.cpp
Normal file
105
variants/lora_relay_v2/variant.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
|
||||
Copyright (c) 2016 Sandeep Mistry All right reserved.
|
||||
Copyright (c) 2018, Adafruit Industries (adafruit.com)
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "variant.h"
|
||||
#include "nrf.h"
|
||||
#include "wiring_constants.h"
|
||||
#include "wiring_digital.h"
|
||||
|
||||
const uint32_t g_ADigitalPinMap[] = {
|
||||
// D0 .. D13
|
||||
25, // D0 is P0.25 (UART TX)
|
||||
24, // D1 is P0.24 (UART RX
|
||||
10, // D2 is P0.10 (NFC2)
|
||||
47, // D3 is P1.15 (LED1)
|
||||
(32 + 10), // D4 is P1.10 (LED2)
|
||||
40, // D5 is P1.08
|
||||
7, // D6 is P0.07
|
||||
34, // D7 is P1.02 (Switch)
|
||||
16, // D8 is P0.16 (NeoPixel)
|
||||
26, // D9 is P0.26 D_RS (IPS data/command control)
|
||||
27, // D10 is P0.27
|
||||
6, // D11 is P0.06 D_RES (IPS display reset)
|
||||
8, // D12 is P0.08 D_CS (IPS display chip select)
|
||||
41, // D13 is P0.23 BLT (IPS display backlight)
|
||||
4, // D14 is P0.04 SX1262 RXEN
|
||||
5, // D15 is P0.05 BOOST_EN (5V buck converter enable for the the radio power)
|
||||
|
||||
// D14 .. D21 (aka A0 .. A7)
|
||||
30, // D16 is P0.30 (A0)
|
||||
28, // D17 is P0.28 (A1)
|
||||
2, // D18 is P0.02 (A2)
|
||||
3, // D19 is P0.03 (A3)
|
||||
29, // D20 is P0.29 (A4, Battery)
|
||||
31, // D21 is P0.31 (A5, ARef)
|
||||
|
||||
// D22 .. D23 (aka I2C pins)
|
||||
12, // D22 is P0.12 (SDA)
|
||||
11, // D23 is P0.11 (SCL)
|
||||
|
||||
// D24 .. D26 (aka SPI pins)
|
||||
15, // D24 is P0.15 (SPI MISO)
|
||||
13, // D25 is P0.13 (SPI MOSI)
|
||||
14, // D26 is P0.14 (SPI SCK )
|
||||
|
||||
// QSPI pins (not exposed via any header / test point)
|
||||
// 19, // P0.19 (QSPI CLK)
|
||||
// 20, // P0.20 (QSPI CS)
|
||||
// 17, // P0.17 (QSPI Data 0)
|
||||
// 22, // P0.22 (QSPI Data 1)
|
||||
// 23, // P0.23 (QSPI Data 2)
|
||||
// 21, // P0.21 (QSPI Data 3)
|
||||
|
||||
// The remaining NFC pin
|
||||
9, // D27 P0.09 (NFC1, exposed only via test point on bottom of board)
|
||||
|
||||
// The following pins were never listed as they were considered unusable
|
||||
// 0, // P0.00 is XL1 (attached to 32.768kHz crystal) Never expose as GPIOs
|
||||
// 1, // P0.01 is XL2 (attached to 32.768kHz crystal)
|
||||
18, // D28 P0.18 is RESET (attached to switch)
|
||||
// 32, // P1.00 is SWO (attached to debug header)
|
||||
|
||||
// D29-D43
|
||||
32 + 12, // D29 P0.27 E22-SX1262 DIO1
|
||||
28, // D30 P0.28 E22-SX1262 DIO2
|
||||
30, // D31 P0.30 E22-SX1262 TXEN
|
||||
35, // D32 P1.03 E22-SX1262 NSS
|
||||
32 + 8, // D33 P1.08 E22-SX1262 BUSY
|
||||
27, // D34 P0.27 E22-SX1262 RESET
|
||||
32 + 1, // D35 P1.01 BTN_UP
|
||||
32, // D36 P1.0 GPS power
|
||||
21, // D37 P0.21 disp_clk
|
||||
36, // P1.04 BTN_OK
|
||||
37, // D39 P0.19 disp_SDA
|
||||
38, // D40 P1.06 BUZZER
|
||||
39, // P1.07 is not connected per schematic
|
||||
43, // P1.11 is not connected per schematic
|
||||
45, // P1.13 is not connected per schematic
|
||||
};
|
||||
|
||||
void initVariant()
|
||||
{
|
||||
// LED1 & LED2
|
||||
pinMode(PIN_LED1, OUTPUT);
|
||||
ledOff(PIN_LED1);
|
||||
|
||||
pinMode(PIN_LED2, OUTPUT);
|
||||
ledOff(PIN_LED2);
|
||||
}
|
||||
178
variants/lora_relay_v2/variant.h
Normal file
178
variants/lora_relay_v2/variant.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
|
||||
Copyright (c) 2016 Sandeep Mistry All right reserved.
|
||||
Copyright (c) 2018, Adafruit Industries (adafruit.com)
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef _VARIANT_LORA_RELAY_V1_
|
||||
#define _VARIANT_LORA_RELAY_V1_
|
||||
|
||||
/** Master clock frequency */
|
||||
#define VARIANT_MCK (64000000ul)
|
||||
|
||||
#define USE_LFXO // Board uses 32khz crystal for LF
|
||||
// define USE_LFRC // Board uses RC for LF
|
||||
|
||||
/*
|
||||
kevinh todo
|
||||
|
||||
ok leds
|
||||
ok buttons
|
||||
ok gps power
|
||||
ok gps signal
|
||||
ok? lcd
|
||||
ok buzzer
|
||||
serial flash
|
||||
ok lora (inc boost en)
|
||||
|
||||
mention dat1 and dat2 on sd card
|
||||
use hardware spi controller for lcd - not bitbang
|
||||
|
||||
*/
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "WVariant.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// Number of pins defined in PinDescription array
|
||||
#define PINS_COUNT (43)
|
||||
#define NUM_DIGITAL_PINS (43)
|
||||
#define NUM_ANALOG_INPUTS (6) // A6 is used for battery, A7 is analog reference
|
||||
#define NUM_ANALOG_OUTPUTS (0)
|
||||
|
||||
// LEDs
|
||||
#define PIN_LED1 (3)
|
||||
#define PIN_LED2 (4)
|
||||
#define PIN_NEOPIXEL (8)
|
||||
#define PIN_BUZZER (40)
|
||||
|
||||
#define LED_BUILTIN PIN_LED1
|
||||
#define LED_CONN PIN_LED2
|
||||
|
||||
#define LED_RED PIN_LED1
|
||||
#define LED_BLUE PIN_LED2
|
||||
|
||||
#define LED_STATE_ON 1 // State when LED is litted
|
||||
|
||||
/*
|
||||
* Buttons
|
||||
*/
|
||||
#define PIN_BUTTON1 (7)
|
||||
#define PIN_BUTTON2 (35)
|
||||
#define PIN_BUTTON3 (37)
|
||||
|
||||
/*
|
||||
* Analog pins
|
||||
*/
|
||||
#define PIN_A0 (16)
|
||||
#define PIN_A1 (17)
|
||||
#define PIN_A2 (18)
|
||||
#define PIN_A3 (19)
|
||||
#define PIN_A4 (20)
|
||||
#define PIN_A5 (21)
|
||||
|
||||
static const uint8_t A0 = PIN_A0;
|
||||
static const uint8_t A1 = PIN_A1;
|
||||
static const uint8_t A2 = PIN_A2;
|
||||
static const uint8_t A3 = PIN_A3;
|
||||
static const uint8_t A4 = PIN_A4;
|
||||
static const uint8_t A5 = PIN_A5;
|
||||
#define ADC_RESOLUTION 14
|
||||
|
||||
// Other pins
|
||||
#define PIN_AREF PIN_A5
|
||||
#define PIN_VBAT PIN_A4
|
||||
#define PIN_NFC1 (33)
|
||||
#define PIN_NFC2 (2)
|
||||
#define PIN_PIEZO (37)
|
||||
static const uint8_t AREF = PIN_AREF;
|
||||
|
||||
/*
|
||||
* Serial interfaces
|
||||
*/
|
||||
#define PIN_SERIAL1_RX (1)
|
||||
#define PIN_SERIAL1_TX (0)
|
||||
|
||||
/*
|
||||
* SPI Interfaces
|
||||
*/
|
||||
#define SPI_INTERFACES_COUNT 1
|
||||
|
||||
#define PIN_SPI_MISO (24)
|
||||
#define PIN_SPI_MOSI (25)
|
||||
#define PIN_SPI_SCK (26)
|
||||
|
||||
static const uint8_t SS = (5);
|
||||
static const uint8_t MOSI = PIN_SPI_MOSI;
|
||||
static const uint8_t MISO = PIN_SPI_MISO;
|
||||
static const uint8_t SCK = PIN_SPI_SCK;
|
||||
|
||||
/*
|
||||
* Wire Interfaces
|
||||
*/
|
||||
#define WIRE_INTERFACES_COUNT 1
|
||||
|
||||
#define PIN_WIRE_SDA (22)
|
||||
#define PIN_WIRE_SCL (23)
|
||||
|
||||
// I2C device addresses
|
||||
#define I2C_ADDR_BQ27441 0x55 // Battery gauge
|
||||
|
||||
// CUSTOM GPIOs the SX1262
|
||||
#define SX1262_CS (32)
|
||||
|
||||
// If you would prefer to get console debug output over the JTAG ICE connection rather than the CDC-ACM USB serial device, just
|
||||
// define this. #define USE_SEGGER
|
||||
|
||||
#define SX1262_DIO1 (29)
|
||||
#define SX1262_DIO2 (30)
|
||||
#define SX1262_BUSY (33) // Supposed to be P0.18 but because of reworks, now on P0.31 (18)
|
||||
#define SX1262_RESET (34)
|
||||
// #define SX1262_ANT_SW (32 + 10)
|
||||
#define SX1262_RXEN (14)
|
||||
#define SX1262_TXEN (31)
|
||||
#define SX1262_POWER_EN \
|
||||
(15) // FIXME, see warning hre https://github.com/BigCorvus/SX1262-LoRa-BLE-Relay/blob/master/LORA_RELAY_NRF52840.ino
|
||||
#define SX1262_E22 // Indicates this SX1262 is inside of an ebyte E22 module and special config should be done for that
|
||||
|
||||
// ST7565 SPI
|
||||
#define ST7735_RESET (11) // Output
|
||||
#define ST7735_CS (12)
|
||||
#define ST7735_BACKLIGHT_EN (13)
|
||||
#define ST7735_RS (9)
|
||||
#define ST7735_SDA (39) // actually spi MOSI
|
||||
#define ST7735_SCK (37) // actually spi clk
|
||||
|
||||
#define PIN_GPS_WAKE 36 // Just kill GPS power when we want it to sleep? FIXME
|
||||
#define GPS_WAKE_ACTIVE 0 // GPS Power output is active low
|
||||
|
||||
// #define LORA_DISABLE_SENDING // The board can brownout during lora TX if you don't have a battery connected. Disable sending
|
||||
// to allow USB power only based debugging
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Arduino objects - C++ only
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#endif
|
||||
@@ -146,6 +146,9 @@ static const uint8_t SCK = PIN_SPI_SCK;
|
||||
#define SX1262_RESET (0 + 3) // P0.03
|
||||
#define SX1262_ANT_SW (32 + 10) // P1.10
|
||||
|
||||
// To debug via the segger JLINK console rather than the CDC-ACM serial device
|
||||
#define USE_SEGGER
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
43
variants/ppr1/variant.cpp
Normal file
43
variants/ppr1/variant.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
|
||||
Copyright (c) 2016 Sandeep Mistry All right reserved.
|
||||
Copyright (c) 2018, Adafruit Industries (adafruit.com)
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "variant.h"
|
||||
#include "nrf.h"
|
||||
#include "wiring_constants.h"
|
||||
#include "wiring_digital.h"
|
||||
|
||||
const uint32_t g_ADigitalPinMap[] = {
|
||||
// P0
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 31,
|
||||
|
||||
// P1
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45};
|
||||
|
||||
void initVariant()
|
||||
{
|
||||
// LED1 & LED2
|
||||
pinMode(PIN_LED1, OUTPUT);
|
||||
ledOff(PIN_LED1);
|
||||
|
||||
pinMode(PIN_LED2, OUTPUT);
|
||||
ledOff(PIN_LED2);
|
||||
}
|
||||
179
variants/ppr1/variant.h
Normal file
179
variants/ppr1/variant.h
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
Copyright (c) 2014-2015 Arduino LLC. All right reserved.
|
||||
Copyright (c) 2016 Sandeep Mistry All right reserved.
|
||||
Copyright (c) 2018, Adafruit Industries (adafruit.com)
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
See the GNU Lesser General Public License for more details.
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/** Master clock frequency */
|
||||
#define VARIANT_MCK (64000000ul)
|
||||
|
||||
// This board does have a 32khz crystal
|
||||
#define USE_LFXO // Board uses 32khz crystal for LF
|
||||
// #define USE_LFRC // Board uses RC for LF
|
||||
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Headers
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
#include "WVariant.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif // __cplusplus
|
||||
|
||||
// Number of pins defined in PinDescription array
|
||||
#define PINS_COUNT (46)
|
||||
#define NUM_DIGITAL_PINS (46)
|
||||
#define NUM_ANALOG_INPUTS (0)
|
||||
#define NUM_ANALOG_OUTPUTS (0)
|
||||
|
||||
// LEDs
|
||||
#define PIN_LED1 (25)
|
||||
#define PIN_LED2 (11)
|
||||
|
||||
#define LED_BUILTIN PIN_LED1
|
||||
#define LED_CONN PIN_LED2
|
||||
|
||||
#define LED_RED PIN_LED1
|
||||
#define LED_GREEN PIN_LED2
|
||||
|
||||
// FIXME, bluefruit automatically blinks this led while connected. call AdafruitBluefruit::autoConnLed to change this.
|
||||
#define LED_BLUE LED_GREEN
|
||||
|
||||
#define LED_STATE_ON 1 // State when LED is litted
|
||||
|
||||
/*
|
||||
* Buttons
|
||||
*/
|
||||
#define PIN_BUTTON1 4 // up
|
||||
#define PIN_BUTTON2 2 // left
|
||||
#define PIN_BUTTON3 3 // center
|
||||
#define PIN_BUTTON4 5 // right
|
||||
#define PIN_BUTTON5 6 // down
|
||||
|
||||
/*
|
||||
* Analog pins
|
||||
*/
|
||||
#define PIN_A0 (0xff)
|
||||
#define PIN_A1 (0xff)
|
||||
#define PIN_A2 (0xff)
|
||||
#define PIN_A3 (0xff)
|
||||
#define PIN_A4 (0xff)
|
||||
#define PIN_A5 (0xff)
|
||||
#define PIN_A6 (0xff)
|
||||
#define PIN_A7 (0xff)
|
||||
|
||||
static const uint8_t A0 = PIN_A0;
|
||||
static const uint8_t A1 = PIN_A1;
|
||||
static const uint8_t A2 = PIN_A2;
|
||||
static const uint8_t A3 = PIN_A3;
|
||||
static const uint8_t A4 = PIN_A4;
|
||||
static const uint8_t A5 = PIN_A5;
|
||||
static const uint8_t A6 = PIN_A6;
|
||||
static const uint8_t A7 = PIN_A7;
|
||||
#define ADC_RESOLUTION 14
|
||||
|
||||
// Other pins
|
||||
#define PIN_AREF (0xff)
|
||||
//#define PIN_NFC1 (9)
|
||||
//#define PIN_NFC2 (10)
|
||||
|
||||
static const uint8_t AREF = PIN_AREF;
|
||||
|
||||
/*
|
||||
* Serial interfaces
|
||||
*/
|
||||
|
||||
// GPS is on Serial1
|
||||
#define PIN_SERIAL1_RX (8)
|
||||
#define PIN_SERIAL1_TX (9)
|
||||
|
||||
// We intentionally leave this undefined so we don't even try to make a Ublox driver
|
||||
// #define GPS_TX_PIN PIN_SERIAL1_TX
|
||||
// #define GPS_RX_PIN PIN_SERIAL1_RX
|
||||
|
||||
#define PIN_GPS_RESET 29 // active high
|
||||
#define PIN_GPS_PPS 28
|
||||
// #define PIN_GPS_WAKE 20 // CELL_CTRL in schematic? based on their example code
|
||||
#define PIN_GPS_EN 7 // GPS_EN active high
|
||||
|
||||
#define PIN_VUSB_EN 21
|
||||
|
||||
// LCD
|
||||
|
||||
#define PIN_LCD_RESET 23 // active low, pulse low for 20ms at boot
|
||||
#define USE_ST7567
|
||||
|
||||
/// Charge controller I2C address
|
||||
#define BQ25703A_ADDR 0x6b
|
||||
|
||||
// Define if screen should be mirrored left to right
|
||||
#define SCREEN_MIRROR
|
||||
|
||||
// LCD screens are slow, so slowdown the wipe so it looks better
|
||||
#define SCREEN_TRANSITION_MSECS 1000
|
||||
#define SCREEN_TRANSITION_FRAMERATE 10 // fps
|
||||
|
||||
/*
|
||||
* SPI Interfaces
|
||||
*/
|
||||
#define SPI_INTERFACES_COUNT 1
|
||||
|
||||
#define PIN_SPI_MISO (15)
|
||||
#define PIN_SPI_MOSI (13)
|
||||
#define PIN_SPI_SCK (12)
|
||||
|
||||
// static const uint8_t SS = 44;
|
||||
static const uint8_t MOSI = PIN_SPI_MOSI;
|
||||
static const uint8_t MISO = PIN_SPI_MISO;
|
||||
static const uint8_t SCK = PIN_SPI_SCK;
|
||||
|
||||
/*
|
||||
* Wire Interfaces
|
||||
*/
|
||||
#define WIRE_INTERFACES_COUNT 1
|
||||
|
||||
#define PIN_WIRE_SDA (32 + 2)
|
||||
#define PIN_WIRE_SCL (32)
|
||||
|
||||
// CUSTOM GPIOs the SX1262
|
||||
#define SX1262_CS (0 + 10) // FIXME - we really should define LORA_CS instead
|
||||
#define SX1262_DIO1 (0 + 20)
|
||||
#define SX1262_DIO2 (0 + 26)
|
||||
#define SX1262_BUSY (0 + 19)
|
||||
#define SX1262_RESET (0 + 17)
|
||||
#define SX1262_TXEN (0 + 24)
|
||||
#define SX1262_RXEN (0 + 22)
|
||||
#define SX1262_E22 // Not really an E22 but this board clones using DIO3 for tcxo control
|
||||
|
||||
// FIXME, to prevent burning out parts I've set the power level super low, because I don't have
|
||||
// an antenna wired up
|
||||
#define SX1262_MAX_POWER 1
|
||||
|
||||
#define LORA_DISABLE_SENDING // Define this to disable transmission for testing (power testing etc...)
|
||||
|
||||
// To debug via the segger JLINK console rather than the CDC-ACM serial device
|
||||
#define USE_SEGGER
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
* Arduino objects - C++ only
|
||||
*----------------------------------------------------------------------------*/
|
||||
Reference in New Issue
Block a user