Compare commits

...

86 Commits

Author SHA1 Message Date
Ben Meadors
cc2a84afcd Bump version for release 2022-04-27 07:03:09 -05:00
Thomas Göttgens
6c1dc0d71a Merge pull request #1415 from meshtastic/RAK12002
I2C RTC Support (RAK12002)
2022-04-27 11:17:31 +02:00
Thomas Göttgens
9e97fac252 - implement generic support for on-device battery powered RTC Modules.
- implement support for I2C RV-3028 based RTC modules like the RAK12002
- pretty print some debug timestamps
2022-04-27 11:05:08 +02:00
Thomas Göttgens
3a9086dfc5 We may have RAK modules in Slot D pulling IO5 to Low permanently (like the RAK12002 RTC Module). React to assumed Long presses of the device button only 30 seconds after bootup to prevent a reboot loop. This is Particularly important for button-less RAK19003 Baseboard. 2022-04-27 11:02:45 +02:00
Thomas Göttgens
a0f34a8d0a Make Debug Log less spammy 2022-04-27 11:00:26 +02:00
Ben Meadors
359b41d869 Position fwd phone (#1413)
* Correct factory reset code for NRF (from 1.2)

* Changes from 1.2

* Update proto ref

* Whoops

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2022-04-26 19:40:24 -05:00
Thomas Göttgens
04723bd1a0 Merge pull request #1412 from meshtastic/nrf-softcrypt
use a tiny software AES lib if user wants AES-256
2022-04-26 21:45:54 +02:00
Thomas Göttgens
89d6990a92 Merge branch 'nrf-softcrypt' of github.com:meshtastic/Meshtastic-device into nrf-softcrypt 2022-04-26 21:30:21 +02:00
Thomas Göttgens
96f20287ff fix scope error 2022-04-26 21:30:14 +02:00
Thomas Göttgens
d8ba25747b Merge branch 'master' into nrf-softcrypt 2022-04-26 21:25:01 +02:00
Thomas Göttgens
e66c01f0e6 Merge branch 'nrf-softcrypt' of github.com:meshtastic/Meshtastic-device into nrf-softcrypt 2022-04-26 21:24:29 +02:00
Thomas Göttgens
aaea2e7456 make cppcheck happy 2022-04-26 21:24:21 +02:00
Ben Meadors
92185e763d Bump version for another 1.3 release 2022-04-26 14:10:17 -05:00
Thomas Göttgens
4de5944474 Merge branch 'master' into nrf-softcrypt 2022-04-26 17:52:33 +02:00
Thomas Göttgens
76e48178c8 use a tiny software AES lib if user needs AES-256 2022-04-26 17:50:50 +02:00
Ben Meadors
75e7bccdfb Fix assert execution halt in nrf devices (#1410) 2022-04-26 06:49:05 -05:00
Ben Meadors
3786b1ee15 Reboot implementation for nrf devices (#1411) 2022-04-26 06:48:26 -05:00
Thomas Göttgens
f2dec07c8d Merge pull request #1409 from meshtastic/gps-check
GPS Fixes
2022-04-26 13:17:04 +02:00
Thomas Göttgens
701707a01b - Bounds Check is working on big integers, don't throw away valid coordinates.
- Set ublox chips back to NMEA mode if they have been configured wrongly before.
2022-04-26 13:00:11 +02:00
Thomas Göttgens
d640478289 Merge pull request #1405 from meshtastic/rak-hw-crypto
use nRF52 Hardware Cryptography
2022-04-25 11:43:05 +02:00
Thomas Göttgens
b8b1a5cfb7 Merge branch 'master' into rak-hw-crypto 2022-04-25 11:14:04 +02:00
Thomas Göttgens
770f17f382 use nRF Hardware Cryptography. Removes the need for the sdk-nrfxlib submodule 2022-04-25 11:01:54 +02:00
Sacha Weatherstone
8ea3ebf74b Update README.md 2022-04-25 18:45:47 +10:00
Sacha Weatherstone
cbf238652e Update README.md 2022-04-25 16:51:39 +10:00
Thomas Göttgens
c17cd47689 Update main_matrix.yml (#1402) 2022-04-25 16:48:04 +10:00
Thomas Göttgens
d2c278a856 Update main_matrix.yml (#1401) 2022-04-25 16:24:08 +10:00
Thomas Göttgens
213d9512f1 WIP: unify the RAK targets into one firmware (#1350)
* First steps to unify GPS Lib for RAK 1910 and RAK 12500

* Technicalities. Out with the old and build the new.

* Adapt Matrix

* We use 0.4.5 now

* While we're at it, yank the RAK815, it's EOL

* Satisfy CI - for now

* - yank UBX library, talk to GPS chip with NMEA only.
- more autodetect going on, this time for the Eink Display.

TODO: actually do something with the scan findings.

* i swear this works on windows! :-)

* these are only there to make CI happy

* don't update eink display if not detected.

* Replace Oberon Crypt Library with modified Adafruit Library. This elimintaes the need for the sdk-nfxlib submodule.

* - Revert auto screen selection (incomplete)
- Revert nrF crypto engine (needs more work)
- add separate defines for not-auto-selecting screen lib.

* Define 2 new variants for RAK - with or without epaper

* Update variants

Co-authored-by: Sacha Weatherstone <sachaw100@hotmail.com>
Co-authored-by: Ben Meadors <thebentern@tuta.io>
2022-04-25 15:13:41 +10:00
Andre Kirchhoff
81588d8bdc Merge pull request #1400 from meshtastic/min_app_version-1.3
update minimum_app_version to 1.3.0
2022-04-24 20:17:23 -03:00
Andre Kirchhoff
3c1407c7d2 bump minimum app version requirement to 1.3.0 2022-04-24 19:52:32 -03:00
Ben Meadors
98c8eaaaf0 Moved sender short name method into protobuf module (#1398)
* Moved sender short name method into protobuf module

* Correct factory reset code for NRF (from 1.2)

* Use the correct fs abstraction

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
2022-04-24 16:12:25 -05:00
Jm Casler
e7a825d1ba Merge pull request #1394 from GUVWAF/master
Implement listen-before-talk mechanism
2022-04-23 11:08:48 -07:00
GUVWAF
1d2551350d Merge branch 'master' of https://github.com/GUVWAF/Meshtastic-device 2022-04-23 19:01:53 +02:00
GUVWAF
a13157ebde Rename setRandomDelay() function 2022-04-23 18:57:45 +02:00
Ben Meadors
a0971ebe9c Merge branch 'master' into master 2022-04-23 08:46:02 -05:00
Ben Meadors
8733bcb52e Peg espressif platform version to 3.5.0 2022-04-23 08:42:26 -05:00
Ben Meadors
823e6cb1ed Merge branch 'master' into master 2022-04-22 07:56:50 -05:00
Ben Meadors
29e378a11e Link to open collective from contributors badge 2022-04-22 07:49:26 -05:00
GUVWAF
692278343b Merge branch 'master' of https://github.com/GUVWAF/Meshtastic-device 2022-04-20 20:16:39 +02:00
GUVWAF
c60d4c1ecc Implement listen-before-talk mechanism
- Function setRandomDelay() calls either startTransmitTimer() or startTransmitTimerSNR()
- After coming back from Rx/Tx-ing, call setRandomDelay()
- If channel is currently busy, call setRandomDelay()
2022-04-20 20:09:12 +02:00
GUVWAF
6d01f9aa89 Add isChannelActive() function to radio interface 2022-04-20 20:04:44 +02:00
GUVWAF
616c7d7b0e Expose front() function in MeshPacketQueue 2022-04-20 19:58:52 +02:00
Jm Casler
6b012ca5b0 Add fiscal contributors badge 2022-04-19 22:41:00 -07:00
Thomas Göttgens
93466baa87 Merge pull request #1393 from meshtastic/create-pull-request/patch
Changes by create-pull-request action
2022-04-19 23:08:29 +02:00
Thomas Göttgens
137328f567 Merge branch 'master' into create-pull-request/patch 2022-04-19 22:52:44 +02:00
Thomas Göttgens
838f00c7d7 Merge pull request #1391 from D4rk4/master
Brother EP-44 support
2022-04-19 22:52:16 +02:00
Dmitry Galenko
293921e95a Brother EP-44 support 2022-04-19 22:37:04 +02:00
caveman99
b82bf5c729 [create-pull-request] automated change 2022-04-19 20:26:22 +00:00
Jm Casler
90df7c2488 bump to 1.3.8 2022-04-18 17:20:03 -07:00
Jm Casler
8c1a81c03a Merge branch 'compression' into master 2022-04-18 17:02:45 -07:00
Jm Casler
6fe9f0b42f Disable compression / decompression (for testing) 2022-04-18 17:00:36 -07:00
Thomas Göttgens
43f6f61472 Merge pull request #1387 from meshtastic/patch-1
tryfix #1363
2022-04-18 23:01:07 +02:00
Thomas Göttgens
ed62b6916c tryfix #1363
Ask for the register several times and only go on if the answer is the same in 2 consecutive tries.
2022-04-18 22:46:45 +02:00
Thomas Göttgens
cf45e4fce5 Merge pull request #1386 from meshtastic/patch-1
Remove References to Pre-1.2 Preffile
2022-04-18 22:19:58 +02:00
Thomas Göttgens
136e2e96f7 Remove References to Pre-1.2 Preffile
1.3+ need a clean install anyway, so no point migrating these any more.
2022-04-18 21:55:58 +02:00
Jm Casler
7c071e2361 updating proto submodule to latest 2022-04-18 12:29:20 -07:00
Thomas Göttgens
0b85e97087 Merge pull request #1385 from meshtastic/oem-screen
Enable OEM Bootlogo, needs protobuf update
2022-04-18 18:43:47 +02:00
Thomas Göttgens
81d1cc1003 Merge branch 'oem-screen' of github.com:meshtastic/Meshtastic-device into oem-screen 2022-04-18 18:27:37 +02:00
Thomas Göttgens
748416d9e3 protobuf update 2022-04-18 18:27:17 +02:00
Thomas Göttgens
da6d49385c Merge branch 'master' into oem-screen 2022-04-18 18:18:19 +02:00
Thomas Göttgens
f279f9614e Enable OEM Bootlogo, needs protobuf update 2022-04-18 18:11:17 +02:00
Sacha Weatherstone
9097475149 Add Repobeats stats image to readme 2022-04-18 23:56:16 +10:00
Jm Casler
5327d6162a remove targz library 2022-04-15 23:17:30 -07:00
Jm Casler
5d7990667d Max nodes to 64 and remove targz 2022-04-15 23:16:40 -07:00
Jm Casler
79a41bd81c updating proto submodule to latest 2022-04-15 22:30:21 -07:00
Jm Casler
6a3d81eff8 Merge pull request #1383 from mc-hamster/compression
Change to unishox library
2022-04-15 20:46:04 -07:00
Jm Casler
7cd66b2b68 Merge branch 'meshtastic:master' into compression 2022-04-15 20:45:44 -07:00
Jm Casler
8124ecbfd8 Change to unishox library 2022-04-15 18:11:17 -07:00
Jm Casler
7df1a64b52 Merge pull request #1382 from mc-hamster/compression
Updated welcome screen w/ text, pages and logo
2022-04-14 21:33:19 -07:00
Jm Casler
e51b7c3c32 Merge branch 'meshtastic:master' into compression 2022-04-14 21:32:52 -07:00
Jm Casler
0c285aac6e enable welcome text 2022-04-14 21:32:00 -07:00
Jm Casler
fbeb554186 Merge branch 'compression' of https://github.com/mc-hamster/Meshtastic-device into compression 2022-04-14 21:31:38 -07:00
Jm Casler
87da779478 Updated welcome screen w/ text, pages and logo
Updated welcome screen w/ text, pages and logo
2022-04-14 21:31:31 -07:00
Jm Casler
1082c5d771 Merge pull request #1381 from mc-hamster/compression
Fix for welcome screen going over ble pairing
2022-04-14 21:05:21 -07:00
Jm Casler
3933d24d62 Merge branch 'meshtastic:master' into compression 2022-04-14 21:03:18 -07:00
Jm Casler
fc0508f254 Fix for welcome screen going over ble pairing 2022-04-14 21:02:52 -07:00
Jm Casler
bb22b6ec58 bump to 1.3.7 2022-04-14 16:15:19 -07:00
Jm Casler
ddb9678377 Merge pull request #1380 from mc-hamster/compression
Compression WIP
2022-04-14 16:14:31 -07:00
Jm Casler
dc20cbb672 one more fix for cppcheck 2022-04-14 15:57:31 -07:00
Jm Casler
a10ea604af Fixes for cppcheck errors 2022-04-14 15:51:48 -07:00
Jm Casler
fb1caa51d0 Merge branch 'meshtastic:master' into compression 2022-04-13 21:59:42 -07:00
Jm Casler
7e977aea00 Add welcome screen feature toggle 2022-04-13 21:59:25 -07:00
Jm Casler
ecc114f1cd temp work on compression 2022-04-13 19:23:35 -07:00
Balázs Kelemen
b76424db50 Make Observer to be able to observe multiple Observables. (#1234)
* Make Observer to be able to observe multiple Observables.

* Fix Observer destructor cleanup.

Co-authored-by: Sacha Weatherstone <sachaw100@hotmail.com>
2022-04-14 08:43:06 +10:00
Jm Casler
b056081d3c Merge branch 'meshtastic:master' into compression 2022-04-12 21:14:25 -07:00
Jm Casler
a4bdef4151 compression WIP
compression works. next is to store it in the proto as a oneof and then decompress it on use.
2022-04-11 22:12:04 -07:00
Jm Casler
478274aff1 Beginning of compression 2022-04-11 20:09:48 -07:00
70 changed files with 2785 additions and 947 deletions

View File

@@ -34,9 +34,8 @@ jobs:
- board: heltec-v2.1
- board: tbeam0.7
- board: meshtastic-diy-v1
- board: rak4631_5005
- board: rak4631_19003
- board: rak4631_5005_eink
- board: rak4631
- board: rak4631_eink
- board: t-echo
runs-on: ubuntu-latest
@@ -117,7 +116,8 @@ jobs:
- name: Upgrade python tools
run: |
python -m pip install --upgrade pip
pip install -U platformio meshtastic adafruit-nrfutil littlefs-python
pip install -U platformio adafruit-nrfutil littlefs-python
pip install -U --pre meshtastic
- name: Upgrade platformio
run: |
@@ -158,9 +158,8 @@ jobs:
max-parallel: 2
matrix:
include:
- board: rak4631_5005
- board: rak4631_19003
- board: rak4631_5005_eink
- board: rak4631
- board: rak4631_eink
- board: t-echo
- board: pca10059_diy_eink
@@ -188,7 +187,8 @@ jobs:
- name: Upgrade python tools
run: |
python -m pip install --upgrade pip
pip install -U platformio meshtastic adafruit-nrfutil
pip install -U platformio adafruit-nrfutil
pip install -U --pre meshtastic
- name: Upgrade platformio
run: |
@@ -235,7 +235,8 @@ jobs:
- name: Upgrade python tools
run: |
python -m pip install --upgrade pip
pip install -U platformio meshtastic adafruit-nrfutil
pip install -U platformio adafruit-nrfutil
pip install -U --pre meshtastic
- name: Upgrade platformio
run: |

3
.gitmodules vendored
View File

@@ -1,9 +1,6 @@
[submodule "proto"]
path = proto
url = https://github.com/meshtastic/Meshtastic-protobufs.git
[submodule "sdk-nrfxlib"]
path = sdk-nrfxlib
url = https://github.com/nrfconnect/sdk-nrfxlib.git
[submodule "design"]
path = design
url = https://github.com/meshtastic/meshtastic-design.git

View File

@@ -1,8 +1,9 @@
# Meshtastic-device
[![Open in Visual Studio Code](https://open.vscode.dev/badges/open-in-vscode.svg)](https://open.vscode.dev/meshtastic/Meshtastic-device)
[![Continuous Integration](https://github.com/meshtastic/Meshtastic-device/actions/workflows/main.yml/badge.svg)](https://github.com/meshtastic/Meshtastic-device/actions/workflows/main.yml)
[![Continuous Integration](https://github.com/meshtastic/Meshtastic-device/actions/workflows/main_matrix.yml/badge.svg)](https://github.com/meshtastic/Meshtastic-device/actions/workflows/main_matrix.yml)
![GitHub all releases](https://img.shields.io/github/downloads/meshtastic/meshtastic-device/total)
[![CLA assistant](https://cla-assistant.io/readme/badge/meshtastic/Meshtastic-device)](https://cla-assistant.io/meshtastic/Meshtastic-device)
[![Fiscal Contributors](https://opencollective.com/meshtastic/tiers/badge.svg)](https://opencollective.com/meshtastic/)
## This repository contains the device firmware used in the [Meshtastic](https://meshtastic.org) project.
@@ -17,3 +18,7 @@ Manual Method
[For nRF52 devices click here](https://meshtastic.org/docs/getting-started/flashing-nrf52)
For developer information and specific building instructions, please see the [developer documentation](https://meshtastic.org/docs/developers)
## Stats
![Alt](https://repobeats.axiom.co/api/embed/99a2cf5622bb4807f9e8c3b86589f1133cce58a2.svg 'Repobeats analytics image')

View File

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

View File

@@ -13,7 +13,7 @@ if [[ $# -gt 0 ]]; then
# can override which environment by passing arg
BOARDS="$@"
else
BOARDS="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-v1 heltec-v2.0 heltec-v2.1 tbeam0.7 meshtastic-diy-v1 rak4631_5005 rak4631_19003 rak11200 t-echo pca10059_diy_eink"
BOARDS="tlora-v2 tlora-v1 tlora_v1_3 tlora-v2-1-1.6 tbeam heltec-v1 heltec-v2.0 heltec-v2.1 tbeam0.7 meshtastic-diy-v1 rak4631 rak4631_eink rak11200 t-echo pca10059_diy_eink"
fi
echo "BOARDS:${BOARDS}"

View File

@@ -1,55 +0,0 @@
{
"build": {
"arduino":{
"ldscript": "nrf52832_s132_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DNRF52832_XXAA -DNRF52",
"f_cpu": "64000000L",
"hwids": [
[
"0x10c4",
"0xea60"
]
],
"usb_product": "RAK815",
"mcu": "nrf52832",
"variant": "rak815",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS132",
"sd_name": "s132",
"sd_version": "6.1.1",
"sd_fwid": "0x00B7"
}
},
"connectivity": [
"bluetooth"
],
"debug": {
"jlink_device": "nRF52832_xxAA",
"svd_path": "nrf52.svd"
},
"frameworks": [
"arduino"
],
"name": "RAK RAK815",
"upload": {
"maximum_ram_size": 65536,
"maximum_size": 524288,
"require_upload_port": true,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
]
},
"url": "https://store.rakwireless.com/products/rak815-hybrid-location-tracker",
"vendor": "RAK"
}

View File

@@ -16,9 +16,6 @@ default_envs = tbeam
;default_envs = t-echo
;default_envs = nrf52840dk-geeksville
;default_envs = native # lora-relay-v1 # nrf52840dk-geeksville # linux # or if you'd like to change the default to something like lora-relay-v1 put that here
;default_envs = rak4631_5005
;default_envs = rak4631_5005_eink
;default_envs = rak4631_19003
;default_envs = nano-g1
;default_envs = pca10059_diy_eink
;default_envs = meshtastic-diy-v1
@@ -84,7 +81,7 @@ lib_deps =
; Common settings for ESP targes, mixin with extends = esp32_base
[esp32_base]
extends = arduino_base
platform = espressif32
platform = espressif32@3.5.0
src_filter =
${arduino_base.src_filter} -<nrf52/>
upload_speed = 921600
@@ -102,7 +99,6 @@ lib_deps =
${environmental.lib_deps}
https://github.com/meshtastic/esp32_https_server.git
h2zero/NimBLE-Arduino@1.3.7
tobozo/ESP32-targz@^1.1.4
arduino-libraries/NTPClient@^3.1.0
lorol/LittleFS_esp32@^1.0.6
lib_ignore =
@@ -110,11 +106,15 @@ lib_ignore =
ESP32 BLE Arduino
platform_packages =
framework-arduinoespressif32@https://github.com/meshtastic/arduino-esp32.git#4cde0f5d412d2695184f32e8a47e9bea57b45276
; leave this commented out to avoid breaking Windows
; leave this commented out to avoid breaking Windows
;upload_port = /dev/ttyUSB0
;monitor_port = /dev/ttyUSB0
; Please don't delete these lines. JM uses them.
;upload_port = /dev/cu.SLAB_USBtoUART
;monitor_port = /dev/cu.SLAB_USBtoUART
; customize the partition table
; http://docs.platformio.org/en/latest/platforms/espressif32.html#partition-tables
board_build.partitions = partition-table.csv
@@ -129,8 +129,7 @@ build_type = debug ; I'm debugging with ICE a lot now
; note: liboberon provides the AES256 implementation for NRF52 (though not using the hardware acceleration of the NRF52840 - FIXME)
build_flags =
${arduino_base.build_flags} -Wno-unused-variable
-Isrc/nrf52
-Isdk-nrfxlib/crypto/nrf_oberon/include -Lsdk-nrfxlib/crypto/nrf_oberon/lib/cortex-m4/hard-float/ -lliboberon_3.0.7
-Isrc/nrf52
src_filter =
${arduino_base.src_filter} -<esp32/> -<nimble/> -<mesh/wifi/> -<mesh/http/> -<modules/esp32> -<mqtt/>
lib_ignore =
@@ -142,7 +141,7 @@ build_flags = ${nrf52_base.build_flags}
lib_deps =
${arduino_base.lib_deps}
${environmental.lib_deps}
Adafruit nRFCrypto
https://github.com/Kongduino/Adafruit_nRFCrypto.git
; Note: By default no lora device is created for this build - it uses a simulated interface
[env:nrf52840dk]
@@ -153,10 +152,3 @@ board = nrf52840_dk
[env:feather_nrf52832]
extends = nrf52_base
board = adafruit_feather_nrf52832
[env:rak815]
extends = nrf52_base
board = rak815
debug_tool = jlink
upload_protocol = jlink
monitor_speed = 115200

2
proto

Submodule proto updated: 870a62b27e...a578453b3c

Submodule sdk-nrfxlib deleted from e6e02cb83d

View File

@@ -135,7 +135,7 @@ class ButtonThread : public concurrency::OSThread
screen->adjustBrightness();
#endif
// If user button is held down for 5 seconds, shutdown the device.
if (millis() - longPressTime > 5 * 1000) {
if ((millis() - longPressTime > 5 * 1000) && (longPressTime > 0)) {
#ifdef TBEAM_V10
if (axp192_found == true) {
setLed(false);
@@ -144,7 +144,7 @@ class ButtonThread : public concurrency::OSThread
#elif NRF52_SERIES
// Do actual shutdown when button released, otherwise the button release
// may wake the board immediatedly.
if (!shutdown_on_long_stop) {
if ((!shutdown_on_long_stop) && (millis() > 30 * 1000)) {
screen->startShutdownScreen();
DEBUG_MSG("Shutdown from long press");
playBeep();
@@ -180,18 +180,22 @@ class ButtonThread : public concurrency::OSThread
static void userButtonPressedLongStart()
{
DEBUG_MSG("Long press start!\n");
longPressTime = millis();
if (millis() > 30 * 1000) {
DEBUG_MSG("Long press start!\n");
longPressTime = millis();
}
}
static void userButtonPressedLongStop()
{
DEBUG_MSG("Long press stop!\n");
longPressTime = 0;
if (shutdown_on_long_stop) {
playShutdownMelody();
delay(3000);
power->shutdown();
if (millis() > 30 * 1000){
DEBUG_MSG("Long press stop!\n");
longPressTime = 0;
if (shutdown_on_long_stop) {
playShutdownMelody();
delay(3000);
power->shutdown();
}
}
}
};

View File

@@ -63,7 +63,7 @@ class GPSStatus : public Status
int32_t getLatitude() const {
if (radioConfig.preferences.fixed_position){
#if GPS_EXTRAVERBOSE
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("WARNING: Using fixed latitude\n");
#endif
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
@@ -75,7 +75,7 @@ class GPSStatus : public Status
int32_t getLongitude() const {
if (radioConfig.preferences.fixed_position){
#if GPS_EXTRAVERBOSE
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("WARNING: Using fixed longitude\n");
#endif
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
@@ -87,7 +87,7 @@ class GPSStatus : public Status
int32_t getAltitude() const {
if (radioConfig.preferences.fixed_position){
#if GPS_EXTRAVERBOSE
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("WARNING: Using fixed altitude\n");
#endif
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
@@ -105,7 +105,7 @@ class GPSStatus : public Status
bool matches(const GPSStatus *newStatus) const
{
#if GPS_EXTRAVERBOSE
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("GPSStatus.match() new pos@%x to old pos@%x\n",
newStatus->p.pos_timestamp, p.pos_timestamp);
#endif

View File

@@ -11,13 +11,13 @@ template <class T> class Observable;
*/
template <class T> class Observer
{
Observable<T> *observed = NULL;
std::list<Observable<T> *> observed;
public:
virtual ~Observer();
/// Stop watching our current obserable
void unobserve();
/// Stop watching the obserable
void unobserve(Observable<T> *o);
/// Start watching a specified observable
void observe(Observable<T> *o);
@@ -87,21 +87,21 @@ template <class T> class Observable
template <class T> Observer<T>::~Observer()
{
unobserve();
for (typename std::list<Observable<T> *>::const_iterator iterator = observed.begin(); iterator != observed.end();
++iterator) {
(*iterator)->removeObserver(this);
}
observed.clear();
}
template <class T> void Observer<T>::unobserve()
template <class T> void Observer<T>::unobserve(Observable<T> *o)
{
if (observed)
observed->removeObserver(this);
observed = NULL;
o->removeObserver(this);
observed.remove(o);
}
template <class T> void Observer<T>::observe(Observable<T> *o)
{
// We can only watch one thing at a time
assert(!observed);
observed = o;
observed.push_back(o);
o->addObserver(this);
}

View File

@@ -72,7 +72,7 @@ size_t RedirectablePrint::logDebug(const char *format, ...)
// If we are the first message on a report, include the header
if (!isContinuationMessage) {
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityFromNet);
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice);
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
// hms += tz.tz_dsttime * SEC_PER_HOUR;

View File

@@ -25,6 +25,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <Arduino.h>
#ifdef RV3028_RTC
#include "Melopero_RV3028.h"
#endif
// -----------------------------------------------------------------------------
// Version
// -----------------------------------------------------------------------------
@@ -100,6 +104,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define GPS_TX_PIN 12
#endif
#ifndef TTGO_T_ECHO
#define GPS_UBLOX
#endif
// -----------------------------------------------------------------------------
// LoRa SPI
// -----------------------------------------------------------------------------
@@ -125,6 +133,16 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#define RTC_DATA_ATTR
#endif
// -----------------------------------------------------------------------------
// Feature toggles
// -----------------------------------------------------------------------------
// Disable use of the NTP library and related features
//#define DISABLE_NTP
// Disable the welcome screen and allow
// #define DISABLE_WELCOME_UNSET
// -----------------------------------------------------------------------------
// OLED & Input
// -----------------------------------------------------------------------------

60
src/debug/einkScan.h Normal file
View File

@@ -0,0 +1,60 @@
#include "../configuration.h"
#ifdef RAK4630
#include "../main.h"
#include <SPI.h>
void d_writeCommand(uint8_t c)
{
SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
if (PIN_EINK_DC >= 0) digitalWrite(PIN_EINK_DC, LOW);
if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, LOW);
SPI1.transfer(c);
if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, HIGH);
if (PIN_EINK_DC >= 0) digitalWrite(PIN_EINK_DC, HIGH);
SPI1.endTransaction();
}
void d_writeData(uint8_t d)
{
SPI1.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, LOW);
SPI1.transfer(d);
if (PIN_EINK_CS >= 0) digitalWrite(PIN_EINK_CS, HIGH);
SPI1.endTransaction();
}
unsigned long d_waitWhileBusy(uint16_t busy_time)
{
if (PIN_EINK_BUSY >= 0)
{
delay(1); // add some margin to become active
unsigned long start = micros();
while (1)
{
if (digitalRead(PIN_EINK_BUSY) != HIGH) break;
delay(1);
if (digitalRead(PIN_EINK_BUSY) != HIGH) break;
if (micros() - start > 10000000) break;
}
unsigned long elapsed = micros() - start;
(void) start;
return elapsed;
}
else return busy_time;
}
void scanEInkDevice(void)
{
SPI1.begin();
d_writeCommand(0x22);
d_writeData(0x83);
d_writeCommand(0x20);
eink_found = (d_waitWhileBusy(150) > 0) ? true : false;
if(eink_found)
DEBUG_MSG("EInk display found\n");
else
DEBUG_MSG("EInk display not found\n");
SPI1.end();
}
#endif

View File

@@ -6,21 +6,28 @@
uint8_t oled_probe(byte addr)
{
uint8_t r = 0;
uint8_t r_prev = 0;
uint8_t c = 0;
uint8_t o_probe = 0;
Wire.beginTransmission(addr);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom((int)addr, 1);
if (Wire.available()) {
r = Wire.read();
}
r &= 0x0f;
if (r == 0x08 || r == 0x00) {
o_probe = 2; // SH1106
} else if ( r == 0x03 || r == 0x06 || r == 0x07) {
o_probe = 1; // SSD1306
}
DEBUG_MSG("0x%x subtype probed\n", r);
do {
r_prev = r;
Wire.beginTransmission(addr);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom((int)addr, 1);
if (Wire.available()) {
r = Wire.read();
}
r &= 0x0f;
if (r == 0x08 || r == 0x00) {
o_probe = 2; // SH1106
} else if ( r == 0x03 || r == 0x06 || r == 0x07) {
o_probe = 1; // SSD1306
}
c++;
} while ((r != r_prev) && (c < 4));
DEBUG_MSG("0x%x subtype probed in %i tries \n", r, c);
return o_probe;
}
@@ -47,6 +54,16 @@ void scanI2Cdevice(void)
DEBUG_MSG("unknown display found\n");
}
}
#ifdef RV3028_RTC
if (addr == RV3028_RTC){
rtc_found = addr;
DEBUG_MSG("RV3028 RTC found\n");
Melopero_RV3028 rtc;
rtc.initI2C();
rtc.writeToRegister(0x35,0x07); // no Clkout
rtc.writeToRegister(0x37,0xB4);
}
#endif
if (addr == CARDKB_ADDR) {
cardkb_found = addr;
DEBUG_MSG("m5 cardKB found\n");
@@ -73,7 +90,7 @@ void scanI2Cdevice(void)
if (nDevices == 0)
DEBUG_MSG("No I2C devices found\n");
else
DEBUG_MSG("done\n");
DEBUG_MSG("%i I2C devices found\n",nDevices);
}
#else
void scanI2Cdevice(void) {}

View File

@@ -16,12 +16,6 @@ HardwareSerial *GPS::_serial_gps = &Serial1;
HardwareSerial *GPS::_serial_gps = NULL;
#endif
#ifdef GPS_I2C_ADDRESS
uint8_t GPS::i2cAddress = GPS_I2C_ADDRESS;
#else
uint8_t GPS::i2cAddress = 0;
#endif
GPS *gps;
/// Multiple GPS instances might use the same serial port (in sequence), but we can
@@ -60,6 +54,56 @@ bool GPS::setupGPS()
_serial_gps->write("$PCAS11,3*1E\r\n");
delay(250);
#endif
#ifdef GPS_UBLOX
// Set the UART port to output NMEA only
byte _message_nmea[] = {0xB5, 0x62, 0x06, 0x00, 0x14, 0x00,
0x01, 0x00, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0x07, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x91, 0xAF};
_serial_gps->write(_message_nmea,sizeof(_message_nmea));
delay(250);
// disable GGL
byte _message_GGL[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00,
0xF0, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
0x05,0x3A};
_serial_gps->write(_message_GGL,sizeof(_message_GGL));
delay(250);
// disable GSA
byte _message_GSA[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00,
0xF0, 0x02, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
0x06,0x41};
_serial_gps->write(_message_GSA,sizeof(_message_GSA));
delay(250);
// disable GSV
byte _message_GSV[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00,
0xF0, 0x03, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
0x07,0x48};
_serial_gps->write(_message_GSV,sizeof(_message_GSV));
delay(250);
// disable VTG
byte _message_VTG[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00,
0xF0, 0x05, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01,
0x09,0x56};
_serial_gps->write(_message_VTG,sizeof(_message_VTG));
delay(250);
// enable RMC
byte _message_RMC[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00,
0xF0, 0x04, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x09,0x54};
_serial_gps->write(_message_RMC,sizeof(_message_RMC));
delay(250);
// enable GGA
byte _message_GGA[] = {0xB5, 0x62, 0x06, 0x01, 0x08, 0x00,
0xF0, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x05, 0x38};
_serial_gps->write(_message_GGA,sizeof(_message_GGA));
delay(250);
#endif
}
@@ -95,8 +139,8 @@ bool GPS::setup()
GPS::~GPS()
{
// we really should unregister our sleep observer
notifySleepObserver.unobserve();
notifyDeepSleepObserver.unobserve();
notifySleepObserver.unobserve(&notifySleep);
notifyDeepSleepObserver.unobserve(&notifyDeepSleep);
}
bool GPS::hasLock() { return hasValidLocation; }
@@ -325,10 +369,6 @@ int GPS::prepareDeepSleep(void *unused)
return 0;
}
#ifdef GPS_TX_PIN
#include "UBloxGPS.h"
#endif
#ifndef NO_GPS
#include "NMEAGPS.h"
#endif
@@ -345,25 +385,9 @@ GPS *createGps()
#else
DEBUG_MSG("Using MSL altitude model\n");
#endif
// If we don't have bidirectional comms, we can't even try talking to UBLOX
#ifdef GPS_TX_PIN
// Init GPS - first try ublox
UBloxGPS *ublox = new UBloxGPS();
if (!ublox->setup()) {
DEBUG_MSG("ERROR: No UBLOX GPS found\n");
delete ublox;
ublox = NULL;
} else {
DEBUG_MSG("Using UBLOX Mode\n");
return ublox;
}
#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.
DEBUG_MSG("Using NMEA Mode\n");
GPS *new_gps = new NMEAGPS();
new_gps->setup();
return new_gps;

View File

@@ -40,9 +40,6 @@ class GPS : private concurrency::OSThread
/** If !NULL we will use this serial port to construct our GPS */
static HardwareSerial *_serial_gps;
/** If !0 we will attempt to connect to the GPS over I2C */
static uint8_t i2cAddress;
Position p = Position_init_default;
GPS() : concurrency::OSThread("GPS") {}

View File

@@ -64,11 +64,12 @@ The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of s
t.tm_mon = d.month() - 1;
t.tm_year = d.year() - 1900;
t.tm_isdst = false;
DEBUG_MSG("NMEA GPS time %d-%d-%d %d:%d:%d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
perhapsSetRTC(RTCQualityGPS, t);
return true;
if (t.tm_mon > -1){
DEBUG_MSG("NMEA GPS time %02d-%02d-%02d %02d:%02d:%02d\n", d.year(), d.month(), t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
perhapsSetRTC(RTCQualityGPS, t);
return true;
} else
return false;
} else
return false;
}
@@ -128,8 +129,16 @@ bool NMEAGPS::lookForLocation()
auto loc = reader.location.value();
// Bail out EARLY to avoid overwriting previous good data (like #857)
if(toDegInt(loc.lat) == 0) {
DEBUG_MSG("Ignoring bogus NMEA position\n");
if (toDegInt(loc.lat) > 900000000) {
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("Bail out EARLY on LAT %i\n",toDegInt(loc.lat));
#endif
return false;
}
if (toDegInt(loc.lng) > 1800000000) {
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("Bail out EARLY on LNG %i\n",toDegInt(loc.lng));
#endif
return false;
}

View File

@@ -1,5 +1,6 @@
#include "RTC.h"
#include "configuration.h"
#include "main.h"
#include <sys/time.h>
#include <time.h>
@@ -18,14 +19,35 @@ static uint64_t zeroOffsetSecs; // GPS based time in secs since 1970 - only upda
void readFromRTC()
{
struct timeval tv; /* btw settimeofday() is helpfull here too*/
#ifdef RV3028_RTC
if(rtc_found == RV3028_RTC) {
uint32_t now = millis();
Melopero_RV3028 rtc;
rtc.initI2C();
tm t;
t.tm_year = rtc.getYear() - 1900;
t.tm_mon = rtc.getMonth() - 1;
t.tm_mday = rtc.getDate();
t.tm_hour = rtc.getHour();
t.tm_min = rtc.getMinute();
t.tm_sec = rtc.getSecond();
tv.tv_sec = mktime(&t);
tv.tv_usec = 0;
DEBUG_MSG("Read RTC time from RV3028 as %ld\n", tv.tv_sec);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
if (currentQuality == RTCQualityNone) {
currentQuality = RTCQualityDevice;
}
}
#else
if (!gettimeofday(&tv, NULL)) {
uint32_t now = millis();
DEBUG_MSG("Read RTC time as %ld (cur millis %u) quality=%d\n", tv.tv_sec, now, currentQuality);
DEBUG_MSG("Read RTC time as %ld\n", tv.tv_sec);
timeStartMsec = now;
zeroOffsetSecs = tv.tv_sec;
}
#endif
}
/// If we haven't yet set our RTC this boot, set it from a GPS derived time
@@ -55,12 +77,20 @@ bool perhapsSetRTC(RTCQuality q, const struct timeval *tv)
zeroOffsetSecs = tv->tv_sec;
// If this platform has a setable RTC, set it
#ifndef NO_ESP32
#ifdef RV3028_RTC
if(rtc_found == RV3028_RTC) {
Melopero_RV3028 rtc;
rtc.initI2C();
tm *t = localtime(&tv->tv_sec);
rtc.setTime(t->tm_year + 1900, t->tm_mon + 1, t->tm_wday, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
DEBUG_MSG("RV3028_RTC setTime %02d-%02d-%02d %02d:%02d:%02d %ld\n", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, tv->tv_sec);
}
#elif !defined(NO_ESP32)
settimeofday(tv, NULL);
#endif
// nrf52 doesn't have a readable RTC (yet - software not written)
#if defined(PORTDUINO) || !defined(NO_ESP32)
#if defined(PORTDUINO) || !defined(NO_ESP32) || defined(RV3028_RTC)
readFromRTC();
#endif

View File

@@ -5,17 +5,21 @@
#include <Arduino.h>
enum RTCQuality {
/// We haven't had our RTC set yet
RTCQualityNone = 0,
/// We got time from an onboard peripheral after boot.
RTCQualityDevice = 1,
/// Some other node gave us a time we can use
RTCQualityFromNet = 1,
RTCQualityFromNet = 2,
/// Our time is based on NTP
RTCQualityNTP= 2,
RTCQualityNTP= 3,
/// Our time is based on our own GPS
RTCQualityGPS = 3
RTCQualityGPS = 4
};
RTCQuality getRTCQuality();

View File

@@ -1,328 +0,0 @@
#include "configuration.h"
#include "UBloxGPS.h"
#include "RTC.h"
#include "error.h"
#include "sleep.h"
#include <assert.h>
// if gps_update_interval below this value, do not powercycle the GPS
#define UBLOX_POWEROFF_THRESHOLD 90
#define PDOP_INVALID 9999
// #define UBX_MODE_NMEA
extern RadioConfig radioConfig;
UBloxGPS::UBloxGPS() {}
bool UBloxGPS::tryConnect()
{
bool c = false;
if (_serial_gps)
c = ublox.begin(*_serial_gps);
if (!c && i2cAddress) {
extern bool neo6M; // Super skanky - if we are talking to the device i2c we assume it is a neo7 on a RAK815, which
// supports the newer API
neo6M = true;
c = ublox.begin(Wire, i2cAddress);
}
if (c)
setConnected();
return c;
}
bool UBloxGPS::setupGPS()
{
GPS::setupGPS();
// uncomment to see debug info
// ublox.enableDebugging(Serial);
// try a second time, the ublox lib serial parsing is buggy?
// see https://github.com/meshtastic/Meshtastic-device/issues/376
for (int i = 0; (i < 3) && !tryConnect(); i++)
delay(500);
if (isConnected()) {
#ifdef UBX_MODE_NMEA
DEBUG_MSG("Connected to UBLOX GPS, downgrading to NMEA mode\n");
DEBUG_MSG("- GPS errors below are related and safe to ignore\n");
#else
DEBUG_MSG("Connected to UBLOX GPS successfully\n");
#endif
if (!setUBXMode())
RECORD_CRITICALERROR(CriticalErrorCode_UBloxInitFailed); // Don't halt the boot if saving the config fails, but do report the bug
#ifdef UBX_MODE_NMEA
return false;
#else
return true;
#endif
} else {
return false;
}
}
bool UBloxGPS::setUBXMode()
{
#ifdef UBX_MODE_NMEA
if (_serial_gps) {
ublox.setUART1Output(COM_TYPE_NMEA, 1000);
}
if (i2cAddress) {
ublox.setI2COutput(COM_TYPE_NMEA, 1000);
}
return false; // pretend initialization failed to force NMEA mode
#endif
if (_serial_gps) {
if (!ublox.setUART1Output(COM_TYPE_UBX, 1000)) // Use native API
return false;
}
if (i2cAddress) {
if (!ublox.setI2COutput(COM_TYPE_UBX, 1000))
return false;
}
if (!ublox.setNavigationFrequency(1, 1000)) // Produce 4x/sec to keep the amount of time we stall in getPVT low
return false;
// ok = ublox.setAutoPVT(false); // Not implemented on NEO-6M
// assert(ok);
// ok = ublox.setDynamicModel(DYN_MODEL_BIKE); // probably PEDESTRIAN but just in case assume bike speeds
// assert(ok);
// per https://github.com/meshtastic/Meshtastic-device/issues/376 powerSaveMode might not work with the marginal
// TTGO antennas
// if (!ublox.powerSaveMode(true, 2000)) // use power save mode, the default timeout (1100ms seems a bit too tight)
// return false;
if (!ublox.saveConfiguration(3000))
return false;
return true;
}
/**
* Reset our GPS back to factory settings
*
* @return true for success
*/
bool UBloxGPS::factoryReset()
{
bool ok = false;
// It is useful to force back into factory defaults (9600baud, NMEA to test the behavior of boards that don't have
// GPS_TX connected)
ublox.factoryReset();
delay(5000);
tryConnect(); // sets isConnected
// try a second time, the ublox lib serial parsing is buggy?
for (int i = 0; (i < 3) && !tryConnect(); i++)
delay(500);
DEBUG_MSG("GPS Factory reset success=%d\n", isConnected());
if (isConnected())
ok = setUBXMode();
return ok;
}
/** Idle processing while GPS is looking for lock */
void UBloxGPS::whileActive()
{
ublox.flushPVT(); // reset ALL freshness flags first
ublox.getT(maxWait()); // ask for new time data - hopefully ready when we come back
// Ask for a new position fix - hopefully it will have results ready by next time
// the order here is important, because we only check for has latitude when reading
//ublox.getSIV(maxWait()); // redundant with getPDOP below
ublox.getPDOP(maxWait()); // will trigger getSOL on NEO6, getP on others
ublox.getP(maxWait()); // will trigger getPosLLH on NEO6, getP on others
// the fixType flag will be checked and updated in lookForLocation()
}
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
*/
bool UBloxGPS::lookForTime()
{
if (ublox.moduleQueried.gpsSecond) {
/* Convert to unix time
The Unix epoch (or Unix time or POSIX time or Unix timestamp) is the number of seconds that have elapsed since January
1, 1970 (midnight UTC/GMT), not counting leap seconds (in ISO 8601: 1970-01-01T00:00:00Z).
*/
struct tm t;
t.tm_sec = ublox.getSecond(0);
t.tm_min = ublox.getMinute(0);
t.tm_hour = ublox.getHour(0);
t.tm_mday = ublox.getDay(0);
t.tm_mon = ublox.getMonth(0) - 1;
t.tm_year = ublox.getYear(0) - 1900;
t.tm_isdst = false;
perhapsSetRTC(RTCQualityGPS, t);
return true;
}
return false;
}
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
*/
bool UBloxGPS::lookForLocation()
{
bool foundLocation = false;
// check if a complete GPS solution set is available for reading
// (some of these, like lat/lon are redundant and can be removed)
if ( ! (ublox.moduleQueried.fixType &&
ublox.moduleQueried.latitude &&
ublox.moduleQueried.longitude &&
ublox.moduleQueried.altitude &&
ublox.moduleQueried.pDOP &&
ublox.moduleQueried.SIV &&
ublox.moduleQueried.gpsDay))
{
// Not ready? No problem! We'll try again later.
return false;
}
fixType = ublox.getFixType();
#ifdef UBLOX_EXTRAVERBOSE
DEBUG_MSG("FixType=%d\n", fixType);
#endif
// check if GPS has an acceptable lock
if (! hasLock()) {
ublox.flushPVT(); // reset ALL freshness flags
return false;
}
// read lat/lon/alt/dop data into temporary variables to avoid
// overwriting global variables with potentially invalid data
int32_t tmp_dop = ublox.getPDOP(0); // PDOP (an accuracy metric) is reported in 10^2 units so we have to scale down when we use it
int32_t tmp_lat = ublox.getLatitude(0);
int32_t tmp_lon = ublox.getLongitude(0);
int32_t tmp_alt_msl = ublox.getAltitudeMSL(0);
int32_t tmp_alt_hae = ublox.getAltitude(0);
int32_t max_dop = PDOP_INVALID;
if (radioConfig.preferences.gps_max_dop)
max_dop = radioConfig.preferences.gps_max_dop * 100; // scaling
// Note: heading is only currently implmented in the ublox for the 8m chipset - therefore
// don't read it here - it will generate an ignored getPVT command on the 6ms
// heading = ublox.getHeading(0);
// read positional timestamp
struct tm t;
t.tm_sec = ublox.getSecond(0);
t.tm_min = ublox.getMinute(0);
t.tm_hour = ublox.getHour(0);
t.tm_mday = ublox.getDay(0);
t.tm_mon = ublox.getMonth(0) - 1;
t.tm_year = ublox.getYear(0) - 1900;
t.tm_isdst = false;
time_t tmp_ts = mktime(&t);
// FIXME - can opportunistically attempt to set RTC from GPS timestamp?
// bogus lat lon is reported as 0 or 0 (can be bogus just for one)
// Also: apparently when the GPS is initially reporting lock it can output a bogus latitude > 90 deg!
// FIXME - NULL ISLAND is a real location on Earth!
foundLocation = (tmp_lat != 0) && (tmp_lon != 0) &&
(tmp_lat <= 900000000) && (tmp_lat >= -900000000) &&
(tmp_dop < max_dop);
// only if entire dataset is valid, update globals from temp vars
if (foundLocation) {
p.location_source = Position_LocSource_LOCSRC_GPS_INTERNAL;
p.longitude_i = tmp_lon;
p.latitude_i = tmp_lat;
if (fixType > 2) {
// if fix is 2d, ignore altitude data
p.altitude = tmp_alt_msl / 1000;
p.altitude_hae = tmp_alt_hae / 1000;
p.alt_geoid_sep = (tmp_alt_hae - tmp_alt_msl) / 1000;
} else {
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("no altitude data (fixType=%d)\n", fixType);
#endif
// clean up old values in case it's a 3d-2d fix transition
p.altitude = p.altitude_hae = p.alt_geoid_sep = 0;
}
p.pos_timestamp = tmp_ts;
p.PDOP = tmp_dop;
p.fix_type = fixType;
p.sats_in_view = ublox.getSIV(0);
// In debug logs, identify position by @timestamp:stage (stage 1 = birth)
DEBUG_MSG("lookForLocation() new pos@%x:1\n", tmp_ts);
} else {
// INVALID solution - should never happen
DEBUG_MSG("Invalid location lat/lon/hae/dop %d/%d/%d/%d - discarded\n",
tmp_lat, tmp_lon, tmp_alt_hae, tmp_dop);
}
ublox.flushPVT(); // reset ALL freshness flags at the end
return foundLocation;
}
bool UBloxGPS::hasLock()
{
if (radioConfig.preferences.gps_accept_2d)
return (fixType >= 2 && fixType <= 4);
else
return (fixType >= 3 && fixType <= 4);
}
bool UBloxGPS::whileIdle()
{
// if using i2c or serial look too see if any chars are ready
return ublox.checkUblox(); // See if new data is available. Process bytes as they come in.
}
/// If possible force the GPS into sleep/low power mode
/// Note: ublox doesn't need a wake method, because as soon as we send chars to the GPS it will wake up
void UBloxGPS::sleep()
{
if (radioConfig.preferences.gps_update_interval > UBLOX_POWEROFF_THRESHOLD) {
// Tell GPS to power down until we send it characters on serial port (we leave vcc connected)
ublox.powerOff();
// setGPSPower(false);
}
}
void UBloxGPS::wake()
{
if (radioConfig.preferences.gps_update_interval > UBLOX_POWEROFF_THRESHOLD) {
fixType = 0; // assume we have no fix yet
}
// this is idempotent
setGPSPower(true);
// Note: no delay needed because now we leave gps power on always and instead use ublox.powerOff()
// Give time for the GPS to boot
// delay(200);
}

View File

@@ -1,72 +0,0 @@
#pragma once
#include "GPS.h"
#include "Observer.h"
#include "SparkFun_Ublox_Arduino_Library.h"
/**
* A gps class that only reads from the GPS periodically (and FIXME - eventually keeps the gps powered down except when reading)
*
* When new data is available it will notify observers.
*/
class UBloxGPS : public GPS
{
SFE_UBLOX_GPS ublox;
uint8_t fixType = 0;
public:
UBloxGPS();
/**
* Reset our GPS back to factory settings
*
* @return true for success
*/
bool factoryReset() override;
protected:
/**
* Returns true if we succeeded
*/
virtual bool setupGPS() override;
/** Subclasses should look for serial rx characters here and feed it to their GPS parser
*
* Return true if we received a valid message from the GPS
*/
virtual bool whileIdle() override;
/** Idle processing while GPS is looking for lock */
virtual void whileActive() override;
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a time
*/
virtual bool lookForTime() override;
/**
* Perform any processing that should be done only while the GPS is awake and looking for a fix.
* Override this method to check for new locations
*
* @return true if we've acquired a new location
*/
virtual bool lookForLocation() override;
virtual bool hasLock() override;
/// If possible force the GPS into sleep/low power mode
virtual void sleep() override;
virtual void wake() override;
private:
/// Attempt to connect to our GPS, returns false if no gps is present
bool tryConnect();
/// Switch to our desired operating mode and save the settings to flash
/// returns true for success
bool setUBXMode();
uint16_t maxWait() const { return i2cAddress ? 300 : 0; /*If using i2c we must poll with wait */ }
};

View File

@@ -1,6 +1,7 @@
#include "configuration.h"
#ifdef HAS_EINK
#include "main.h"
#include "EInkDisplay2.h"
#include "SPILock.h"
#include <SPI.h>
@@ -171,18 +172,46 @@ bool EInkDisplay::connect()
}
#elif defined(RAK4630)
{
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
if (eink_found) {
auto lowLevel = new TECHO_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
adafruitDisplay = new GxEPD2_BW<TECHO_DISPLAY_MODEL, TECHO_DISPLAY_MODEL::HEIGHT>(*lowLevel);
adafruitDisplay->init(115200, true, 10, false, SPI1, SPISettings(4000000, MSBFIRST, SPI_MODE0));
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
adafruitDisplay->setRotation(3);
//For 1.54, 2.9 and 4.2
//adafruitDisplay->setRotation(1);
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
//RAK14000 2.13 inch b/w 250x122 does not support partial updates
adafruitDisplay->setRotation(3);
//For 1.54, 2.9 and 4.2
//adafruitDisplay->setRotation(1);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
adafruitDisplay->setPartialWindow(0, 0, displayWidth, displayHeight);
} else {
(void)adafruitDisplay;
}
}
#elif defined(PCA10059)
{

View File

@@ -31,6 +31,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "graphics/images.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "mesh/generated/deviceonly.pb.h"
#include "mesh/Channels.h"
#include "modules/TextMessageModule.h"
#include "sleep.h"
@@ -44,6 +45,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
using namespace meshtastic; /** @todo remove */
extern bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct);
namespace graphics
{
@@ -77,6 +80,10 @@ static char ourId[5];
// GeoCoord object for the screen
GeoCoord geoCoord;
// OEM Config File
static const char *oemConfigFile = "/prefs/oem.proto";
OEMStore oemStore;
#ifdef SHOW_REDRAWS
static bool heartbeat = false;
#endif
@@ -148,6 +155,54 @@ static void drawBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int1
drawIconScreen(region, display, state, x, y);
}
static void drawOEMIconScreen(const char *upperMsg, OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// draw an xbm image.
// Please note that everything that should be transitioned
// needs to be drawn relative to x and y
// draw centered icon left to right and centered above the one line of app text
display->drawXbm(x + (SCREEN_WIDTH - oemStore.oem_icon_width) / 2, y + (SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM - oemStore.oem_icon_height) / 2 + 2,
oemStore.oem_icon_width, oemStore.oem_icon_height, (const uint8_t *)oemStore.oem_icon_bits.bytes);
switch(oemStore.oem_font){
case 0:
display->setFont(FONT_SMALL);
break;
case 2:
display->setFont(FONT_LARGE);
break;
default:
display->setFont(FONT_MEDIUM);
break;
}
display->setTextAlignment(TEXT_ALIGN_LEFT);
const char *title = oemStore.oem_text;
display->drawString(x + getStringCenteredX(title), y + SCREEN_HEIGHT - FONT_HEIGHT_MEDIUM, title);
display->setFont(FONT_SMALL);
// Draw region in upper left
if (upperMsg)
display->drawString(x + 0, y + 0, upperMsg);
// Draw version in upper right
char buf[16];
snprintf(buf, sizeof(buf), "%s",
xstr(APP_VERSION_SHORT)); // Note: we don't bother printing region or now, it makes the string too long
display->drawString(x + SCREEN_WIDTH - display->getStringWidth(buf), y + 0, buf);
screen->forceDisplay();
// FIXME - draw serial # somewhere?
}
static void drawOEMBootScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
// Draw region in upper left
const char *region = myRegion ? myRegion->name : NULL;
drawOEMIconScreen(region, display, state, x, y);
}
// Used on boot when a certificate is being created
static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -172,22 +227,34 @@ static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16
static void drawWelcomeScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
display->setFont(FONT_SMALL);
if ((millis() / 10000) % 2) {
display->setFont(FONT_SMALL);
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(64 + x, y, "Meshtastic :)");
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(64 + x, y, "//\\ E S H T /\\ S T / C");
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(x, y + FONT_HEIGHT_SMALL * 2 - 3, "Set the region using the");
display->drawString(x, y + FONT_HEIGHT_SMALL * 3 - 3, "Meshtastic Android, iOS,");
display->drawString(x, y + FONT_HEIGHT_SMALL * 4 - 3, "Flasher or CLI client.");
display->drawString(x, y + FONT_HEIGHT_SMALL * 2 - 3, "Set the region using the");
display->drawString(x, y + FONT_HEIGHT_SMALL * 3 - 3, "Meshtastic Android, iOS,");
display->drawString(x, y + FONT_HEIGHT_SMALL * 4 - 3, "Flasher or CLI client.");
} else {
display->setFont(FONT_SMALL);
display->setTextAlignment(TEXT_ALIGN_CENTER);
display->drawString(64 + x, y, "//\\ E S H T /\\ S T / C");
display->setTextAlignment(TEXT_ALIGN_LEFT);
display->drawString(x, y + FONT_HEIGHT_SMALL * 2 - 3, "Visit meshtastic.org");
display->drawString(x, y + FONT_HEIGHT_SMALL * 3 - 3, "for more information.");
display->drawString(x, y + FONT_HEIGHT_SMALL * 4 - 3, "");
}
#ifndef NO_ESP32
yield();
esp_task_wdt_reset();
#endif
}
#ifdef HAS_EINK
@@ -748,12 +815,25 @@ void _screen_header()
}
#endif
// #ifdef RAK4630
// Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), dispdev_oled(address, sda, scl), ui(&dispdev)
// {
// address_found = address;
// cmdQueue.setReader(this);
// if (screen_found) {
// (void)dispdev;
// AutoOLEDWire dispdev = dispdev_oled;
// (void)ui;
// OLEDDisplayUi ui(&dispdev);
// }
// }
// #else
Screen::Screen(uint8_t address, int sda, int scl) : OSThread("Screen"), cmdQueue(32), dispdev(address, sda, scl), ui(&dispdev)
{
address_found = address;
cmdQueue.setReader(this);
}
// #endif
/**
* Prepare the display for the unit going to the lowest power mode possible. Most screens will just
* poweroff, but eink screens will show a "I'm sleeping" graphic, possibly with a QR code
@@ -801,8 +881,8 @@ void Screen::setup()
dispdev.setDetected(screen_model);
#endif
// I think this is not needed - redundant with ui.init
// dispdev.resetOrientation();
// Load OEM config from Proto file if existent
loadProto(oemConfigFile, OEMStore_size, sizeof(oemConfigFile), OEMStore_fields, &oemStore);
// Initialising the UI will init the display too.
ui.init();
@@ -855,6 +935,7 @@ void Screen::setup()
// twice initially.
ui.update();
ui.update();
serialSinceMsec = millis();
// Subscribe to status updates
powerStatusObserver.observe(&powerStatus->onNewStatus);
@@ -885,7 +966,7 @@ int32_t Screen::runOnce()
return RUN_SAME;
}
// Show boot screen for first 3 seconds, then switch to normal operation.
// Show boot screen for first 5 seconds, then switch to normal operation.
// serialSinceMsec adjusts for additional serial wait time during nRF52 bootup
static bool showingBootScreen = true;
if (showingBootScreen && (millis() > (5000 + serialSinceMsec))) {
@@ -894,9 +975,26 @@ int32_t Screen::runOnce()
showingBootScreen = false;
}
if (radioConfig.preferences.region == RegionCode_Unset) {
// If we have an OEM Boot screen, toggle after 2,5 seconds
if(strlen(oemStore.oem_text) > 0){
static bool showingOEMBootScreen = true;
if (showingOEMBootScreen && (millis() > (2500 + serialSinceMsec))) {
DEBUG_MSG("Switch to OEM screen...\n");
// Change frames.
static FrameCallback bootOEMFrames[] = {drawOEMBootScreen};
static const int bootOEMFrameCount = sizeof(bootOEMFrames) / sizeof(bootOEMFrames[0]);
ui.setFrames(bootOEMFrames, bootOEMFrameCount);
ui.update();
ui.update();
showingOEMBootScreen = false;
}
}
#ifndef DISABLE_WELCOME_UNSET
if (showingNormalScreen && radioConfig.preferences.region == RegionCode_Unset) {
setWelcomeFrames();
}
#endif
// Process incoming commands.
for (;;) {
@@ -1459,7 +1557,7 @@ void DebugInfo::drawFrameSettings(OLEDDisplay *display, OLEDDisplayUiState *stat
else
uptime += String(seconds) + "s ";
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityFromNet);
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice);
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
// hms += tz.tz_dsttime * SEC_PER_HOUR;

View File

@@ -27,6 +27,10 @@ class Screen
#ifdef USE_ST7567
#include <ST7567Wire.h>
#elif defined(USE_SH1106)
#include <SH1106Wire.h>
#elif defined(USE_SSD1306)
#include <SSD1306Wire.h>
#else
// the SH1106/SSD1306 variant is auto-detected
#include <AutoOLEDWire.h>
@@ -297,9 +301,16 @@ class Screen : public concurrency::OSThread
/// Holds state for debug information
DebugInfo debugInfo;
/// Display device
/** FIXME cleanup display abstraction */
#ifdef ST7735_CS
/// Display device
// #ifdef RAK4630
// EInkDisplay dispdev;
// AutoOLEDWire dispdev_oled;
#ifdef USE_SH1106
SH1106Wire dispdev;
#elif defined(USE_SSD1306)
SSD1306Wire dispdev;
#elif defined(ST7735_CS)
TFTDisplay dispdev;
#elif defined(HAS_EINK)
EInkDisplay dispdev;

View File

@@ -24,6 +24,7 @@
#include "shutdown.h"
#include "target_specific.h"
#include "debug/i2cScan.h"
#include "debug/einkScan.h"
#include "debug/axpDebug.h"
#include <Wire.h>
// #include <driver/rtc_io.h>
@@ -78,6 +79,11 @@ uint8_t cardkb_found;
// The I2C address of the Faces Keyboard (if found)
uint8_t faceskb_found;
// The I2C address of the RTC Module (if found)
uint8_t rtc_found;
bool eink_found = true;
uint32_t serialSinceMsec;
bool axp192_found;
@@ -208,6 +214,9 @@ void setup()
#endif
scanI2Cdevice();
#ifdef RAK4630
// scanEInkDevice();
#endif
// Buttons & LED
buttonThread = new ButtonThread();

View File

@@ -9,7 +9,9 @@ extern uint8_t screen_found;
extern uint8_t screen_model;
extern uint8_t cardkb_found;
extern uint8_t faceskb_found;
extern uint8_t rtc_found;
extern bool eink_found;
extern bool axp192_found;
extern bool isCharging;
extern bool isUSBPowered;

View File

@@ -72,6 +72,16 @@ MeshPacket *MeshPacketQueue::dequeue()
return p;
}
MeshPacket *MeshPacketQueue::getFront()
{
if (empty()) {
return NULL;
}
auto *p = queue.front();
return p;
}
/** Attempt to find and remove a packet from this queue. Returns a pointer to the removed packet, or NULL if not found */
MeshPacket *MeshPacketQueue::remove(NodeNum from, PacketId id)
{

View File

@@ -28,6 +28,8 @@ class MeshPacketQueue
MeshPacket *dequeue();
MeshPacket *getFront();
/** Attempt to find and remove a packet from this queue. Returns the packet which was removed from the queue */
MeshPacket *remove(NodeNum from, PacketId id);
};

View File

@@ -71,17 +71,7 @@ int MeshService::handleFromRadio(const MeshPacket *mp)
printPacket("Forwarding to phone", mp);
nodeDB.updateFrom(*mp); // update our DB state based off sniffing every RX packet from the radio
fromNum++;
if (toPhoneQueue.numFree() == 0) {
DEBUG_MSG("NOTE: tophone queue is full, discarding oldest\n");
MeshPacket *d = toPhoneQueue.dequeuePtr(0);
if (d)
releaseToPool(d);
}
MeshPacket *copied = packetPool.allocCopy(*mp);
assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages
sendToPhone((MeshPacket *)mp);
return 0;
}
@@ -161,12 +151,16 @@ bool MeshService::cancelSending(PacketId id)
return router->cancelSending(nodeDB.getNodeNum(), id);
}
void MeshService::sendToMesh(MeshPacket *p, RxSource src)
void MeshService::sendToMesh(MeshPacket *p, RxSource src, bool ccToPhone)
{
nodeDB.updateFrom(*p); // update our local DB for this packet (because phone might have sent position packets etc...)
// Note: We might return !OK if our fifo was full, at that point the only option we have is to drop it
router->sendLocal(p, src);
if (ccToPhone) {
sendToPhone(p);
}
}
void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
@@ -187,6 +181,20 @@ void MeshService::sendNetworkPing(NodeNum dest, bool wantReplies)
}
}
void MeshService::sendToPhone(MeshPacket *p) {
if (toPhoneQueue.numFree() == 0) {
DEBUG_MSG("NOTE: tophone queue is full, discarding oldest\n");
MeshPacket *d = toPhoneQueue.dequeuePtr(0);
if (d)
releaseToPool(d);
}
MeshPacket *copied = packetPool.allocCopy(*p);
perhapsDecode(copied);
assert(toPhoneQueue.enqueue(copied, 0)); // FIXME, instead of failing for full queue, delete the oldest mssages
fromNum++;
}
NodeInfo *MeshService::refreshMyNodeInfo()
{
NodeInfo *node = nodeDB.getNode(nodeDB.getNodeNum());
@@ -224,7 +232,7 @@ int MeshService::onGPSChanged(const meshtastic::GPSStatus *newStatus)
} else {
// The GPS has lost lock, if we are fixed position we should just keep using
// the old position
#if GPS_EXTRAVERBOSE
#ifdef GPS_EXTRAVERBOSE
DEBUG_MSG("onGPSchanged() - lost validLocation\n");
#endif
if (radioConfig.preferences.fixed_position) {

View File

@@ -75,7 +75,7 @@ class MeshService
/// Send a packet into the mesh - note p must have been allocated from packetPool. We will return it to that pool after
/// sending. This is the ONLY function you should use for sending messages into the mesh, because it also updates the nodedb
/// cache
void sendToMesh(MeshPacket *p, RxSource src = RX_SRC_LOCAL);
void sendToMesh(MeshPacket *p, RxSource src = RX_SRC_LOCAL, bool ccToPhone = false);
/** Attempt to cancel a previously sent packet from this _local_ node. Returns true if a packet was found we could cancel */
bool cancelSending(PacketId id);
@@ -83,6 +83,9 @@ class MeshService
/// Pull the latest power and time info into my nodeinfo
NodeInfo *refreshMyNodeInfo();
/// Send a packet to the phone
void sendToPhone(MeshPacket *p);
private:
/// Called when our gps position has changed - updates nodedb and sends Location message out into the mesh
/// returns 0 to allow futher processing

View File

@@ -95,14 +95,16 @@ bool NodeDB::resetRadioConfig()
nvs_flash_erase();
#endif
#ifdef NRF52_SERIES
// first, remove the "/prefs" (this removes most prefs)
FSCom.rmdir_r("/prefs");
// second, install default state (this will deal with the duplicate mac address issue)
installDefaultDeviceState();
// third, write to disk
saveToDisk();
Bluefruit.begin();
DEBUG_MSG("Clearing bluetooth bonds!\n");
bond_print_list(BLE_GAP_ROLE_PERIPH);
bond_print_list(BLE_GAP_ROLE_CENTRAL);
Bluefruit.Periph.clearBonds();
Bluefruit.Central.clearBonds();
#endif
@@ -215,7 +217,7 @@ void NodeDB::init()
myNodeInfo.error_address = 0;
// likewise - we always want the app requirements to come from the running appload
myNodeInfo.min_app_version = 20200; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
myNodeInfo.min_app_version = 20300; // format is Mmmss (where M is 1+the numeric major number. i.e. 20120 means 1.1.20
// Note! We do this after loading saved settings, so that if somehow an invalid nodenum was stored in preferences we won't
// keep using that nodenum forever. Crummy guess at our nodenum (but we will check against the nodedb to avoid conflicts)
@@ -276,11 +278,9 @@ void NodeDB::pickNewNodeNum()
myNodeInfo.my_node_num = r;
}
static const char *preffileOld = "/db.proto";
static const char *preffile = "/prefs/db.proto";
static const char *radiofile = "/prefs/radio.proto";
static const char *channelfile = "/prefs/channels.proto";
// const char *preftmp = "/db.proto.tmp";
/** Load a protobuf from a file, return true for success */
bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_msgdesc_t *fields, void *dest_struct)
@@ -290,13 +290,6 @@ bool loadProto(const char *filename, size_t protoSize, size_t objSize, const pb_
auto f = FSCom.open(filename);
// FIXME, temporary hack until every node in the universe is 1.2 or later - look for prefs in the old location (so we can
// preserve region)
if (!f && filename == preffile) {
filename = preffileOld;
f = FSCom.open(filename);
}
bool okay = false;
if (f) {
DEBUG_MSG("Loading %s\n", filename);
@@ -399,8 +392,6 @@ void NodeDB::saveToDisk()
saveProto(radiofile, RadioConfig_size, sizeof(RadioConfig), RadioConfig_fields, &radioConfig);
saveChannelsToDisk();
// remove any pre 1.2 pref files, turn on after 1.2 is in beta
// if(okay) FSCom.remove(preffileOld);
} else {
DEBUG_MSG("***** DEVELOPMENT MODE - DO NOT RELEASE - not saving to flash *****\n");
}

View File

@@ -48,7 +48,7 @@ void PhoneAPI::close()
if (state != STATE_SEND_NOTHING) {
state = STATE_SEND_NOTHING;
unobserve();
unobserve(&service.fromNumChanged);
releasePhonePacket(); // Don't leak phone packets on shutdown
onConnectionChanged(false);

View File

@@ -48,6 +48,17 @@ template <class T> class ProtobufModule : protected SinglePortModule
return p;
}
/**
* Gets the short name from the sender of the mesh packet
* Returns "???" if unknown sender
*/
const char *getSenderShortName(const MeshPacket &mp)
{
auto node = nodeDB.getNode(getFrom(&mp));
const char *sender = (node) ? node->user.short_name : "???";
return sender;
}
private:
/** Called to handle a particular incoming message

View File

@@ -176,6 +176,25 @@ void RF95Interface::startReceive()
enableInterrupt(isrRxLevel0);
}
bool RF95Interface::isChannelActive()
{
// check if we can detect a LoRa preamble on the current channel
int16_t result;
setTransmitEnable(false);
setStandby(); // needed for smooth transition
result = lora->scanChannel();
if (result == PREAMBLE_DETECTED) {
// DEBUG_MSG("Channel is busy!\n");
return true;
}
assert(result != ERR_WRONG_MODEM);
// DEBUG_MSG("Channel is free!\n");
return false;
}
/** Could we send right now (i.e. either not actively receving or transmitting)? */
bool RF95Interface::isActivelyReceiving()
{

View File

@@ -40,6 +40,9 @@ class RF95Interface : public RadioLibInterface
*/
virtual void enableInterrupt(void (*callback)()) { lora->setDio0Action(callback); }
/** can we detect a LoRa preamble on the current channel? */
virtual bool isChannelActive() override;
/** are we actively receiving a packet (only called during receiving state) */
virtual bool isActivelyReceiving() override;

View File

@@ -118,20 +118,10 @@ ErrorCode RadioLibInterface::send(MeshPacket *p)
return res;
}
// We want all sending/receiving to be done by our daemon thread, We use a delay here because this packet might have been sent
// in response to a packet we just received. So we want to make sure the other side has had a chance to reconfigure its radio
/* We assume if rx_snr = 0 and rx_rssi = 0, the packet was not generated locally.
* This assumption is valid because of the offset generated by the radio to account for the noise
* floor.
*/
if (p->rx_snr == 0 && p->rx_rssi == 0) {
startTransmitTimer(true);
} else {
// If there is a SNR, start a timer scaled based on that SNR.
DEBUG_MSG("rx_snr found. hop_limit:%d rx_snr:%f\n", p->hop_limit, p->rx_snr);
startTransmitTimerSNR(p->rx_snr);
}
// set (random) transmit delay to let others reconfigure their radio,
// to avoid collisions and implement timing-based flooding
// DEBUG_MSG("Set random delay before transmitting.\n");
setTransmitDelay();
return res;
#else
@@ -164,8 +154,8 @@ bool RadioLibInterface::cancelSending(NodeNum from, PacketId id)
/** radio helper thread callback.
We never immediately transmit after any operation (either rx or tx). Instead we should start receiving and
wait a random delay of 50 to 200 ms to make sure we are not stomping on someone else. The 50ms delay at the beginning ensures all
possible listeners have had time to finish processing the previous packet and now have their radio in RX state. The up to 200ms
wait a random delay of 100ms to 100ms+shortPacketMsec to make sure we are not stomping on someone else. The 100ms delay at the beginning ensures all
possible listeners have had time to finish processing the previous packet and now have their radio in RX state. The up to 100ms+shortPacketMsec
random delay gives a chance for all possible senders to have high odds of detecting that someone else started transmitting first
and then they will wait until that packet finishes.
@@ -192,20 +182,26 @@ void RadioLibInterface::onNotify(uint32_t notification)
case TRANSMIT_DELAY_COMPLETED:
// DEBUG_MSG("delay done\n");
// If we are not currently in receive mode, then restart the timer and try again later (this can happen if the main thread
// If we are not currently in receive mode, then restart the random delay (this can happen if the main thread
// has placed the unit into standby) FIXME, how will this work if the chipset is in sleep mode?
if (!txQueue.empty()) {
if (!canSendImmediately()) {
startTransmitTimer(); // try again in a little while
// DEBUG_MSG("Currently Rx/Tx-ing: set random delay\n");
setTransmitDelay(); // currently Rx/Tx-ing: reset random delay
} else {
// Send any outgoing packets we have ready
MeshPacket *txp = txQueue.dequeue();
assert(txp);
startSend(txp);
if (isChannelActive()) { // check if there is currently a LoRa packet on the channel
// DEBUG_MSG("Channel is active: set random delay\n");
setTransmitDelay(); // reset random delay
} else {
// Send any outgoing packets we have ready
MeshPacket *txp = txQueue.dequeue();
assert(txp);
startSend(txp);
// Packet has been sent, count it toward our TX airtime utilization.
uint32_t xmitMsec = getPacketTime(txp);
airTime->logAirtime(TX_LOG, xmitMsec);
// Packet has been sent, count it toward our TX airtime utilization.
uint32_t xmitMsec = getPacketTime(txp);
airTime->logAirtime(TX_LOG, xmitMsec);
}
}
} else {
// DEBUG_MSG("done with txqueue\n");
@@ -216,6 +212,26 @@ void RadioLibInterface::onNotify(uint32_t notification)
}
}
void RadioLibInterface::setTransmitDelay()
{
MeshPacket *p = txQueue.getFront();
// We want all sending/receiving to be done by our daemon thread.
// We use a delay here because this packet might have been sent in response to a packet we just received.
// So we want to make sure the other side has had a chance to reconfigure its radio.
/* We assume if rx_snr = 0 and rx_rssi = 0, the packet was generated locally.
* This assumption is valid because of the offset generated by the radio to account for the noise
* floor.
*/
if (p->rx_snr == 0 && p->rx_rssi == 0) {
startTransmitTimer(true);
} else {
// If there is a SNR, start a timer scaled based on that SNR.
DEBUG_MSG("rx_snr found. hop_limit:%d rx_snr:%f\n", p->hop_limit, p->rx_snr);
startTransmitTimerSNR(p->rx_snr);
}
}
void RadioLibInterface::startTransmitTimer(bool withDelay)
{
// If we have work to do and the timer wasn't already scheduled, schedule it now

View File

@@ -132,6 +132,9 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
*/
virtual void startReceive() = 0;
/** can we detect a LoRa preamble on the current channel? */
virtual bool isChannelActive() = 0;
/** are we actively receiving a packet (only called during receiving state)
* This method is only public to facilitate debugging. Do not call.
*/
@@ -141,18 +144,14 @@ class RadioLibInterface : public RadioInterface, protected concurrency::Notified
virtual bool cancelSending(NodeNum from, PacketId id) override;
private:
/** if we have something waiting to send, start a short random timer so we can come check for collision before actually doing
* the transmit
*
* If the timer was already running, we just wait for that one to occur.
* */
/** if we have something waiting to send, start a short (random) timer so we can come check for collision before actually doing
* the transmit */
void setTransmitDelay();
/** random timer with certain min. and max. settings */
void startTransmitTimer(bool withDelay = true);
/** if we have something waiting to send, start a short scaled timer based on SNR so we can come check for collision before actually doing
* the transmit
*
* If the timer was already running, we just wait for that one to occur.
* */
/** timer scaled to SNR of to be flooded packet */
void startTransmitTimerSNR(float snr);
void handleTransmitInterrupt();

View File

@@ -1,12 +1,15 @@
#include "configuration.h"
#include "Router.h"
#include "Channels.h"
#include "CryptoEngine.h"
#include "NodeDB.h"
#include "RTC.h"
#include "configuration.h"
#include "main.h"
#include "mesh-pb-constants.h"
#include "modules/RoutingModule.h"
extern "C" {
#include "mesh/compression/unishox2.h"
}
#if defined(HAS_WIFI) || defined(PORTDUINO)
#include "mqtt/MQTT.h"
@@ -210,29 +213,27 @@ ErrorCode Router::send(MeshPacket *p)
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
ChannelIndex chIndex = p->channel; // keep as a local because we are about to change it
#if defined(HAS_WIFI) || defined(PORTDUINO)
//check if we should send decrypted packets to mqtt
// check if we should send decrypted packets to mqtt
//truth table:
// truth table:
/* mqtt_server mqtt_encryption_enabled should_encrypt
* not set 0 1
* not set 1 1
* set 0 0
* set 1 1
*
*
* => so we only decrypt mqtt if they have a custom mqtt server AND mqtt_encryption_enabled is FALSE
*/
*/
bool shouldActuallyEncrypt = true;
if (*radioConfig.preferences.mqtt_server && !radioConfig.preferences.mqtt_encryption_enabled) {
shouldActuallyEncrypt = false;
}
DEBUG_MSG("Should encrypt MQTT?: %d\n", shouldActuallyEncrypt);
//the packet is currently in a decrypted state. send it now if they want decrypted packets
// the packet is currently in a decrypted state. send it now if they want decrypted packets
if (mqtt && !shouldActuallyEncrypt)
mqtt->onSend(*p, chIndex);
#endif
@@ -244,8 +245,8 @@ ErrorCode Router::send(MeshPacket *p)
}
#if defined(HAS_WIFI) || defined(PORTDUINO)
//the packet is now encrypted.
//check if we should send encrypted packets to mqtt
// the packet is now encrypted.
// check if we should send encrypted packets to mqtt
if (mqtt && shouldActuallyEncrypt)
mqtt->onSend(*p, chIndex);
#endif
@@ -276,7 +277,7 @@ bool perhapsDecode(MeshPacket *p)
if (p->which_payloadVariant == MeshPacket_decoded_tag)
return true; // If packet was already decoded just return
//assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
// assert(p->which_payloadVariant == MeshPacket_encrypted_tag);
// Try to find a channel that works with this hash
for (ChannelIndex chIndex = 0; chIndex < channels.getNumChannels(); chIndex++) {
@@ -302,6 +303,14 @@ bool perhapsDecode(MeshPacket *p)
// parsing was successful
p->which_payloadVariant = MeshPacket_decoded_tag; // change type to decoded
p->channel = chIndex; // change to store the index instead of the hash
// Decompress if needed. jm
if (p->decoded.which_payloadVariant == Data_payload_compressed_tag) {
// Decompress the file
}
printPacket("decoded message", p);
return true;
}
@@ -320,10 +329,56 @@ Routing_Error perhapsEncode(MeshPacket *p)
if (p->which_payloadVariant == MeshPacket_decoded_tag) {
static uint8_t bytes[MAX_RHPACKETLEN]; // we have to use a scratch buffer because a union
// printPacket("pre encrypt", p); // portnum valid here
size_t numbytes = pb_encode_to_bytes(bytes, sizeof(bytes), Data_fields, &p->decoded);
// Only allow encryption on the text message app.
// TODO: Allow modules to opt into compression.
if (p->decoded.portnum == PortNum_TEXT_MESSAGE_APP) {
char original_payload[Constants_DATA_PAYLOAD_LEN];
memcpy(original_payload, p->decoded.payload.bytes, p->decoded.payload.size);
char compressed_out[Constants_DATA_PAYLOAD_LEN] = {0};
int compressed_len;
// compressed_len = unishox2_compress_simple(original_payload, p->decoded.payload.size, compressed_out);
Serial.print("Original length - ");
Serial.println(p->decoded.payload.size);
Serial.print("Compressed length - ");
Serial.println(compressed_len);
// Serial.println(compressed_out);
// If the compressed length is greater than or equal to the original size, don't use the compressed form
if (compressed_len >= p->decoded.payload.size) {
DEBUG_MSG("Not compressing message. Not enough benefit from doing so.\n");
// Set the uncompressed payload varient anyway. Shouldn't hurt?
p->decoded.which_payloadVariant = Data_payload_tag;
// Otherwise we use the compressor
} else {
DEBUG_MSG("Compressing message.\n");
// Copy the compressed data into the meshpacket
//p->decoded.payload_compressed.size = compressed_len;
//memcpy(p->decoded.payload_compressed.bytes, compressed_out, compressed_len);
//p->decoded.which_payloadVariant = Data_payload_compressed_tag;
}
if (0) {
char decompressed_out[Constants_DATA_PAYLOAD_LEN] = {};
int decompressed_len;
// decompressed_len = unishox2_decompress_simple(compressed_out, compressed_len, decompressed_out);
Serial.print("Decompressed length - ");
Serial.println(decompressed_len);
Serial.println(decompressed_out);
}
}
if (numbytes > MAX_RHPACKETLEN)
return Routing_Error_TOO_LARGE;

View File

@@ -226,6 +226,23 @@ void SX126xInterface<T>::startReceive()
#endif
}
/** Could we send right now (i.e. either not actively receving or transmitting)? */
template<typename T>
bool SX126xInterface<T>::isChannelActive()
{
// check if we can detect a LoRa preamble on the current channel
int16_t result;
setStandby();
result = lora.scanChannel();
if (result == PREAMBLE_DETECTED)
return true;
assert(result != ERR_WRONG_MODEM);
return false;
}
/** Could we send right now (i.e. either not actively receving or transmitting)? */
template<typename T>
bool SX126xInterface<T>::isActivelyReceiving()

View File

@@ -46,6 +46,9 @@ class SX126xInterface : public RadioLibInterface
*/
virtual void enableInterrupt(void (*callback)()) { lora.setDio1Action(callback); }
/** can we detect a LoRa preamble on the current channel? */
virtual bool isChannelActive() override;
/** are we actively receiving a packet (only called during receiving state) */
virtual bool isActivelyReceiving() override;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,278 @@
/*
* Copyright (C) 2020 Siara Logics (cc)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Arundale Ramanathan
*
*/
/**
* @file unishox2.h
* @author Arundale Ramanathan, James Z. M. Gao
* @brief API for Unishox2 Compression and Decompression
*
* This file describes each function of the Unishox2 API \n
* For finding out how this API can be used in your program, \n
* please see test_unishox2.c.
*/
#ifndef unishox2
#define unishox2
#define UNISHOX_VERSION "2.0" ///< Unicode spec version
/**
* Macro switch to enable/disable output buffer length parameter in low level api \n
* Disabled by default \n
* When this macro is defined, the all the API functions \n
* except the simple API functions accept an additional parameter olen \n
* that enables the developer to pass the size of the output buffer provided \n
* so that the api function may not write beyond that length. \n
* This can be disabled if the developer knows that the buffer provided is sufficient enough \n
* so no additional parameter is passed and the program is faster since additional check \n
* for output length is not performed at each step \n
* The simple api, i.e. unishox2_(de)compress_simple will always omit the buffer length
*/
#ifndef UNISHOX_API_WITH_OUTPUT_LEN
# define UNISHOX_API_WITH_OUTPUT_LEN 0
#endif
/// Upto 8 bits of initial magic bit sequence can be included. Bit count can be specified with UNISHOX_MAGIC_BIT_LEN
#ifndef UNISHOX_MAGIC_BITS
# define UNISHOX_MAGIC_BITS 0xFF
#endif
/// Desired length of Magic bits defined by UNISHOX_MAGIC_BITS
#ifdef UNISHOX_MAGIC_BIT_LEN
# if UNISHOX_MAGIC_BIT_LEN < 0 || 9 <= UNISHOX_MAGIC_BIT_LEN
# error "UNISHOX_MAGIC_BIT_LEN need between [0, 8)"
# endif
#else
# define UNISHOX_MAGIC_BIT_LEN 1
#endif
//enum {USX_ALPHA = 0, USX_SYM, USX_NUM, USX_DICT, USX_DELTA};
/// Default Horizontal codes. When composition of text is know beforehand, the other hcodes in this section can be used to achieve more compression.
#define USX_HCODES_DFLT (const unsigned char[]) {0x00, 0x40, 0x80, 0xC0, 0xE0}
/// Length of each default hcode
#define USX_HCODE_LENS_DFLT (const unsigned char[]) {2, 2, 2, 3, 3}
/// Horizontal codes preset for English Alphabet content only
#define USX_HCODES_ALPHA_ONLY (const unsigned char[]) {0x00, 0x00, 0x00, 0x00, 0x00}
/// Length of each Alpha only hcode
#define USX_HCODE_LENS_ALPHA_ONLY (const unsigned char[]) {0, 0, 0, 0, 0}
/// Horizontal codes preset for Alpha Numeric content only
#define USX_HCODES_ALPHA_NUM_ONLY (const unsigned char[]) {0x00, 0x00, 0x80, 0x00, 0x00}
/// Length of each Alpha numeric hcode
#define USX_HCODE_LENS_ALPHA_NUM_ONLY (const unsigned char[]) {1, 0, 1, 0, 0}
/// Horizontal codes preset for Alpha Numeric and Symbol content only
#define USX_HCODES_ALPHA_NUM_SYM_ONLY (const unsigned char[]) {0x00, 0x80, 0xC0, 0x00, 0x00}
/// Length of each Alpha numeric and symbol hcodes
#define USX_HCODE_LENS_ALPHA_NUM_SYM_ONLY (const unsigned char[]) {1, 2, 2, 0, 0}
/// Horizontal codes preset favouring Alphabet content
#define USX_HCODES_FAVOR_ALPHA (const unsigned char[]) {0x00, 0x80, 0xA0, 0xC0, 0xE0}
/// Length of each hcode favouring Alpha content
#define USX_HCODE_LENS_FAVOR_ALPHA (const unsigned char[]) {1, 3, 3, 3, 3}
/// Horizontal codes preset favouring repeating sequences
#define USX_HCODES_FAVOR_DICT (const unsigned char[]) {0x00, 0x40, 0xC0, 0x80, 0xE0}
/// Length of each hcode favouring repeating sequences
#define USX_HCODE_LENS_FAVOR_DICT (const unsigned char[]) {2, 2, 3, 2, 3}
/// Horizontal codes preset favouring symbols
#define USX_HCODES_FAVOR_SYM (const unsigned char[]) {0x80, 0x00, 0xA0, 0xC0, 0xE0}
/// Length of each hcode favouring symbols
#define USX_HCODE_LENS_FAVOR_SYM (const unsigned char[]) {3, 1, 3, 3, 3}
//#define USX_HCODES_FAVOR_UMLAUT {0x00, 0x40, 0xE0, 0xC0, 0x80}
//#define USX_HCODE_LENS_FAVOR_UMLAUT {2, 2, 3, 3, 2}
/// Horizontal codes preset favouring umlaut letters
#define USX_HCODES_FAVOR_UMLAUT (const unsigned char[]) {0x80, 0xA0, 0xC0, 0xE0, 0x00}
/// Length of each hcode favouring umlaut letters
#define USX_HCODE_LENS_FAVOR_UMLAUT (const unsigned char[]) {3, 3, 3, 3, 1}
/// Horizontal codes preset for no repeating sequences
#define USX_HCODES_NO_DICT (const unsigned char[]) {0x00, 0x40, 0x80, 0x00, 0xC0}
/// Length of each hcode for no repeating sequences
#define USX_HCODE_LENS_NO_DICT (const unsigned char[]) {2, 2, 2, 0, 2}
/// Horizontal codes preset for no Unicode characters
#define USX_HCODES_NO_UNI (const unsigned char[]) {0x00, 0x40, 0x80, 0xC0, 0x00}
/// Length of each hcode for no Unicode characters
#define USX_HCODE_LENS_NO_UNI (const unsigned char[]) {2, 2, 2, 2, 0}
/// Default frequently occuring sequences. When composition of text is know beforehand, the other sequences in this section can be used to achieve more compression.
#define USX_FREQ_SEQ_DFLT (const char *[]) {"\": \"", "\": ", "</", "=\"", "\":\"", "://"}
/// Frequently occuring sequences in text content
#define USX_FREQ_SEQ_TXT (const char *[]) {" the ", " and ", "tion", " with", "ing", "ment"}
/// Frequently occuring sequences in URL content
#define USX_FREQ_SEQ_URL (const char *[]) {"https://", "www.", ".com", "http://", ".org", ".net"}
/// Frequently occuring sequences in JSON content
#define USX_FREQ_SEQ_JSON (const char *[]) {"\": \"", "\": ", "\",", "}}}", "\":\"", "}}"}
/// Frequently occuring sequences in HTML content
#define USX_FREQ_SEQ_HTML (const char *[]) {"</", "=\"", "div", "href", "class", "<p>"}
/// Frequently occuring sequences in XML content
#define USX_FREQ_SEQ_XML (const char *[]) {"</", "=\"", "\">", "<?xml version=\"1.0\"", "xmlns:", "://"}
/// Commonly occuring templates (ISO Date/Time, ISO Date, US Phone number, ISO Time, Unused)
#define USX_TEMPLATES (const char *[]) {"tfff-of-tfTtf:rf:rf.fffZ", "tfff-of-tf", "(fff) fff-ffff", "tf:rf:rf", 0}
/// Default preset parameter set. When composition of text is know beforehand, the other parameter sets in this section can be used to achieve more compression.
#define USX_PSET_DFLT USX_HCODES_DFLT, USX_HCODE_LENS_DFLT, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
/// Preset parameter set for English Alphabet only content
#define USX_PSET_ALPHA_ONLY USX_HCODES_ALPHA_ONLY, USX_HCODE_LENS_ALPHA_ONLY, USX_FREQ_SEQ_TXT, USX_TEMPLATES
/// Preset parameter set for Alpha numeric content
#define USX_PSET_ALPHA_NUM_ONLY USX_HCODES_ALPHA_NUM_ONLY, USX_HCODE_LENS_ALPHA_NUM_ONLY, USX_FREQ_SEQ_TXT, USX_TEMPLATES
/// Preset parameter set for Alpha numeric and symbol content
#define USX_PSET_ALPHA_NUM_SYM_ONLY USX_HCODES_ALPHA_NUM_SYM_ONLY, USX_HCODE_LENS_ALPHA_NUM_SYM_ONLY, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
/// Preset parameter set for Alpha numeric symbol content having predominantly text
#define USX_PSET_ALPHA_NUM_SYM_ONLY_TXT USX_HCODES_ALPHA_NUM_SYM_ONLY, USX_HCODE_LENS_ALPHA_NUM_SYM_ONLY, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
/// Preset parameter set favouring Alphabet content
#define USX_PSET_FAVOR_ALPHA USX_HCODES_FAVOR_ALPHA, USX_HCODE_LENS_FAVOR_ALPHA, USX_FREQ_SEQ_TXT, USX_TEMPLATES
/// Preset parameter set favouring repeating sequences
#define USX_PSET_FAVOR_DICT USX_HCODES_FAVOR_DICT, USX_HCODE_LENS_FAVOR_DICT, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
/// Preset parameter set favouring symbols
#define USX_PSET_FAVOR_SYM USX_HCODES_FAVOR_SYM, USX_HCODE_LENS_FAVOR_SYM, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
/// Preset parameter set favouring unlaut letters
#define USX_PSET_FAVOR_UMLAUT USX_HCODES_FAVOR_UMLAUT, USX_HCODE_LENS_FAVOR_UMLAUT, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
/// Preset parameter set for when there are no repeating sequences
#define USX_PSET_NO_DICT USX_HCODES_NO_DICT, USX_HCODE_LENS_NO_DICT, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
/// Preset parameter set for when there are no unicode symbols
#define USX_PSET_NO_UNI USX_HCODES_NO_UNI, USX_HCODE_LENS_NO_UNI, USX_FREQ_SEQ_DFLT, USX_TEMPLATES
/// Preset parameter set for when there are no unicode symbols favouring text
#define USX_PSET_NO_UNI_FAVOR_TEXT USX_HCODES_NO_UNI, USX_HCODE_LENS_NO_UNI, USX_FREQ_SEQ_TXT, USX_TEMPLATES
/// Preset parameter set favouring URL content
#define USX_PSET_URL USX_HCODES_DFLT, USX_HCODE_LENS_DFLT, USX_FREQ_SEQ_URL, USX_TEMPLATES
/// Preset parameter set favouring JSON content
#define USX_PSET_JSON USX_HCODES_DFLT, USX_HCODE_LENS_DFLT, USX_FREQ_SEQ_JSON, USX_TEMPLATES
/// Preset parameter set favouring JSON content having no Unicode symbols
#define USX_PSET_JSON_NO_UNI USX_HCODES_NO_UNI, USX_HCODE_LENS_NO_UNI, USX_FREQ_SEQ_JSON, USX_TEMPLATES
/// Preset parameter set favouring XML content
#define USX_PSET_XML USX_HCODES_DFLT, USX_HCODE_LENS_DFLT, USX_FREQ_SEQ_XML, USX_TEMPLATES
/// Preset parameter set favouring HTML content
#define USX_PSET_HTML USX_HCODES_DFLT, USX_HCODE_LENS_DFLT, USX_FREQ_SEQ_HTML, USX_TEMPLATES
/**
* This structure is used when a string array needs to be compressed.
* This is passed as a parameter to the unishox2_decompress_lines() function
*/
struct us_lnk_lst {
char *data;
struct us_lnk_lst *previous;
};
/**
* This macro is for internal use, but builds upon the macro UNISHOX_API_WITH_OUTPUT_LEN
* When the macro UNISHOX_API_WITH_OUTPUT_LEN is defined, the all the API functions
* except the simple API functions accept an additional parameter olen
* that enables the developer to pass the size of the output buffer provided
* so that the api function may not write beyond that length.
* This can be disabled if the developer knows that the buffer provided is sufficient enough
* so no additional parameter is passed and the program is faster since additional check
* for output length is not performed at each step
*/
#if defined(UNISHOX_API_WITH_OUTPUT_LEN) && UNISHOX_API_WITH_OUTPUT_LEN != 0
# define UNISHOX_API_OUT_AND_LEN(out, olen) out, olen
#else
# define UNISHOX_API_OUT_AND_LEN(out, olen) out
#endif
/**
* Simple API for compressing a string
* @param[in] in Input ASCII / UTF-8 string
* @param[in] len length in bytes
* @param[out] out output buffer - should be large enough to hold compressed output
*/
extern int unishox2_compress_simple(const char *in, int len, char *out);
/**
* Simple API for decompressing a string
* @param[in] in Input compressed bytes (output of unishox2_compress functions)
* @param[in] len length of 'in' in bytes
* @param[out] out output buffer for ASCII / UTF-8 string - should be large enough
*/
extern int unishox2_decompress_simple(const char *in, int len, char *out);
/**
* Comprehensive API for compressing a string
*
* Presets are available for the last four parameters so they can be passed as single parameter. \n
* See USX_PSET_* macros. Example call: \n
* unishox2_compress(in, len, out, olen, USX_PSET_ALPHA_ONLY);
*
* @param[in] in Input ASCII / UTF-8 string
* @param[in] len length in bytes
* @param[out] out output buffer - should be large enough to hold compressed output
* @param[in] olen length of 'out' buffer in bytes. Can be omitted if sufficient buffer is provided
* @param[in] usx_hcodes Horizontal codes (array of bytes). See macro section for samples.
* @param[in] usx_hcode_lens Length of each element in usx_hcodes array
* @param[in] usx_freq_seq Frequently occuring sequences. See USX_FREQ_SEQ_* macros for samples
* @param[in] usx_templates Templates of frequently occuring patterns. See USX_TEMPLATES macro.
*/
extern int unishox2_compress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen),
const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[],
const char *usx_freq_seq[], const char *usx_templates[]);
/**
* Comprehensive API for de-compressing a string
*
* Presets are available for the last four parameters so they can be passed as single parameter. \n
* See USX_PSET_* macros. Example call: \n
* unishox2_decompress(in, len, out, olen, USX_PSET_ALPHA_ONLY);
*
* @param[in] in Input compressed bytes (output of unishox2_compress functions)
* @param[in] len length of 'in' in bytes
* @param[out] out output buffer - should be large enough to hold de-compressed output
* @param[in] olen length of 'out' buffer in bytes. Can be omitted if sufficient buffer is provided
* @param[in] usx_hcodes Horizontal codes (array of bytes). See macro section for samples.
* @param[in] usx_hcode_lens Length of each element in usx_hcodes array
* @param[in] usx_freq_seq Frequently occuring sequences. See USX_FREQ_SEQ_* macros for samples
* @param[in] usx_templates Templates of frequently occuring patterns. See USX_TEMPLATES macro.
*/
extern int unishox2_decompress(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen),
const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[],
const char *usx_freq_seq[], const char *usx_templates[]);
/**
* More Comprehensive API for compressing array of strings
*
* See unishox2_compress() function for parameter definitions. \n
* This function takes an additional parameter, i.e. 'prev_lines' - the usx_lnk_lst structure \n
* See -g parameter in test_unishox2.c to find out how this can be used. \n
* This function is used when an array of strings need to be compressed \n
* and stored in a compressed array of bytes for use as a constant in other programs \n
* where each element of the array can be decompressed and used at runtime.
*/
extern int unishox2_compress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen),
const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[],
const char *usx_freq_seq[], const char *usx_templates[],
struct us_lnk_lst *prev_lines);
/**
* More Comprehensive API for de-compressing array of strings \n
* This function is not be used in conjuction with unishox2_compress_lines()
*
* See unishox2_decompress() function for parameter definitions. \n
* Typically an array is compressed using unishox2_compress_lines() and \n
* a header (.h) file is generated using the resultant compressed array. \n
* This header file can be used in another program with another decompress \n
* routine which takes this compressed array as parameter and index to be \n
* decompressed.
*/
extern int unishox2_decompress_lines(const char *in, int len, UNISHOX_API_OUT_AND_LEN(char *out, int olen),
const unsigned char usx_hcodes[], const unsigned char usx_hcode_lens[],
const char *usx_freq_seq[], const char *usx_templates[],
struct us_lnk_lst *prev_lines);
#endif

View File

@@ -12,4 +12,8 @@ PB_BIND(DeviceState, DeviceState, 4)
PB_BIND(ChannelFile, ChannelFile, 2)
PB_BIND(OEMStore, OEMStore, 2)

View File

@@ -11,6 +11,17 @@
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Enum definitions */
/* TODO: REPLACE */
typedef enum _ScreenFonts {
/* TODO: REPLACE */
ScreenFonts_FONT_SMALL = 0,
/* TODO: REPLACE */
ScreenFonts_FONT_MEDIUM = 1,
/* TODO: REPLACE */
ScreenFonts_FONT_LARGE = 2
} ScreenFonts;
/* Struct definitions */
/* The on-disk saved channels */
typedef struct _ChannelFile {
@@ -33,7 +44,7 @@ typedef struct _DeviceState {
User owner;
/* TODO: REPLACE */
pb_size_t node_db_count;
NodeInfo node_db[80];
NodeInfo node_db[64];
/* Received packets saved for delivery to the phone */
pb_size_t receive_queue_count;
MeshPacket receive_queue[1];
@@ -53,16 +64,40 @@ typedef struct _DeviceState {
bool did_gps_reset;
} DeviceState;
typedef PB_BYTES_ARRAY_T(2048) OEMStore_oem_icon_bits_t;
/* This can be used for customizing the firmware distribution. If populated,
show a secondary bootup screen with cuatom logo and text for 2.5 seconds. */
typedef struct _OEMStore {
/* The Logo width in Px */
uint32_t oem_icon_width;
/* The Logo height in Px */
uint32_t oem_icon_height;
/* The Logo in xbm bytechar format */
OEMStore_oem_icon_bits_t oem_icon_bits;
/* Use this font for the OEM text. */
ScreenFonts oem_font;
/* Use this font for the OEM text. */
char oem_text[40];
} OEMStore;
/* Helper constants for enums */
#define _ScreenFonts_MIN ScreenFonts_FONT_SMALL
#define _ScreenFonts_MAX ScreenFonts_FONT_LARGE
#define _ScreenFonts_ARRAYSIZE ((ScreenFonts)(ScreenFonts_FONT_LARGE+1))
#ifdef __cplusplus
extern "C" {
#endif
/* Initializer values for message structs */
#define DeviceState_init_default {false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0}
#define DeviceState_init_default {false, MyNodeInfo_init_default, false, User_init_default, 0, {NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default, NodeInfo_init_default}, 0, {MeshPacket_init_default}, false, MeshPacket_init_default, 0, 0, 0}
#define ChannelFile_init_default {0, {Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default, Channel_init_default}}
#define DeviceState_init_zero {false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0}
#define OEMStore_init_default {0, 0, {0, {0}}, _ScreenFonts_MIN, ""}
#define DeviceState_init_zero {false, MyNodeInfo_init_zero, false, User_init_zero, 0, {NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero, NodeInfo_init_zero}, 0, {MeshPacket_init_zero}, false, MeshPacket_init_zero, 0, 0, 0}
#define ChannelFile_init_zero {0, {Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero, Channel_init_zero}}
#define OEMStore_init_zero {0, 0, {0, {0}}, _ScreenFonts_MIN, ""}
/* Field tags (for use in manual encoding/decoding) */
#define ChannelFile_channels_tag 1
@@ -74,6 +109,11 @@ extern "C" {
#define DeviceState_version_tag 8
#define DeviceState_no_save_tag 9
#define DeviceState_did_gps_reset_tag 11
#define OEMStore_oem_icon_width_tag 1
#define OEMStore_oem_icon_height_tag 2
#define OEMStore_oem_icon_bits_tag 3
#define OEMStore_oem_font_tag 4
#define OEMStore_oem_text_tag 5
/* Struct field encoding specification for nanopb */
#define DeviceState_FIELDLIST(X, a) \
@@ -99,16 +139,28 @@ X(a, STATIC, REPEATED, MESSAGE, channels, 1)
#define ChannelFile_DEFAULT NULL
#define ChannelFile_channels_MSGTYPE Channel
#define OEMStore_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, oem_icon_width, 1) \
X(a, STATIC, SINGULAR, UINT32, oem_icon_height, 2) \
X(a, STATIC, SINGULAR, BYTES, oem_icon_bits, 3) \
X(a, STATIC, SINGULAR, UENUM, oem_font, 4) \
X(a, STATIC, SINGULAR, STRING, oem_text, 5)
#define OEMStore_CALLBACK NULL
#define OEMStore_DEFAULT NULL
extern const pb_msgdesc_t DeviceState_msg;
extern const pb_msgdesc_t ChannelFile_msg;
extern const pb_msgdesc_t OEMStore_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define DeviceState_fields &DeviceState_msg
#define ChannelFile_fields &ChannelFile_msg
#define OEMStore_fields &OEMStore_msg
/* Maximum encoded size of messages (where known) */
#define ChannelFile_size 832
#define DeviceState_size 23903
#define DeviceState_size 19327
#define OEMStore_size 2106
#ifdef __cplusplus
} /* extern "C" */

View File

@@ -500,6 +500,7 @@ typedef struct _User {
} User;
typedef PB_BYTES_ARRAY_T(237) Data_payload_t;
typedef PB_BYTES_ARRAY_T(237) Data_payload_compressed_t;
/* (Formerly called SubPacket)
The payload portion fo a packet, this is the actual bytes that are sent
inside a radio packet (because from/to are broken out by the comms library) */
@@ -507,30 +508,34 @@ typedef struct _Data {
/* Formerly named typ and of type Type */
PortNum portnum;
/* TODO: REPLACE */
Data_payload_t payload;
pb_size_t which_payloadVariant;
union {
Data_payload_t payload;
Data_payload_compressed_t payload_compressed;
};
/* TODO: REPLACE */
bool want_response;
/* Not normally used, but for testing a sender can request that recipient
responds in kind (i.e. if it received a position, it should unicast back it's position).
Note: that if you set this on a broadcast you will receive many replies. */
bool want_response;
uint32_t dest;
/* The address of the destination node.
This field is is filled in by the mesh radio device software, application
layer software should never need it.
RouteDiscovery messages _must_ populate this.
Other message types might need to if they are doing multihop routing. */
uint32_t dest;
uint32_t source;
/* The address of the original sender for this message.
This field should _only_ be populated for reliable multihop packets (to keep
packets small). */
uint32_t source;
uint32_t request_id;
/* Only used in routing or response messages.
Indicates the original message ID that this message is reporting failure on. (formerly called original_id) */
uint32_t request_id;
/* If set, this message is intened to be a reply to a previously sent message with the defined id. */
uint32_t reply_id;
/* If set, this message is intened to be a reply to a previously sent message with the defined id. */
uint32_t emoji;
/* Defaults to false. If true, then what is in the payload should be treated as an emoji like giving
a message a heart or poop emoji. */
uint32_t emoji;
/* Location structure */
bool has_location;
Location location;
} Data;
@@ -738,7 +743,7 @@ extern "C" {
#define User_init_default {"", "", "", {0}, _HardwareModel_MIN, 0, _Team_MIN, 0, 0, 0}
#define RouteDiscovery_init_default {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_default {0, {RouteDiscovery_init_default}}
#define Data_init_default {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, Location_init_default}
#define Data_init_default {_PortNum_MIN, 0, {{0, {0}}}, 0, 0, 0, 0, 0, 0, false, Location_init_default}
#define Location_init_default {0, 0, 0, 0, 0}
#define MeshPacket_init_default {0, 0, 0, 0, {Data_init_default}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
#define NodeInfo_init_default {0, false, User_init_default, false, Position_init_default, 0, 0, false, DeviceMetrics_init_default}
@@ -751,7 +756,7 @@ extern "C" {
#define User_init_zero {"", "", "", {0}, _HardwareModel_MIN, 0, _Team_MIN, 0, 0, 0}
#define RouteDiscovery_init_zero {0, {0, 0, 0, 0, 0, 0, 0, 0}}
#define Routing_init_zero {0, {RouteDiscovery_init_zero}}
#define Data_init_zero {_PortNum_MIN, {0, {0}}, 0, 0, 0, 0, 0, 0, false, Location_init_zero}
#define Data_init_zero {_PortNum_MIN, 0, {{0, {0}}}, 0, 0, 0, 0, 0, 0, false, Location_init_zero}
#define Location_init_zero {0, 0, 0, 0, 0}
#define MeshPacket_init_zero {0, 0, 0, 0, {Data_init_zero}, 0, 0, 0, 0, 0, _MeshPacket_Priority_MIN, 0, _MeshPacket_Delayed_MIN}
#define NodeInfo_init_zero {0, false, User_init_zero, false, Position_init_zero, 0, 0, false, DeviceMetrics_init_zero}
@@ -825,6 +830,7 @@ extern "C" {
#define User_ant_azimuth_tag 12
#define Data_portnum_tag 1
#define Data_payload_tag 2
#define Data_payload_compressed_tag 10
#define Data_want_response_tag 3
#define Data_dest_tag 4
#define Data_source_tag 5
@@ -923,14 +929,15 @@ X(a, STATIC, ONEOF, UENUM, (variant,error_reason,error_reason), 3)
#define Data_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UENUM, portnum, 1) \
X(a, STATIC, SINGULAR, BYTES, payload, 2) \
X(a, STATIC, ONEOF, BYTES, (payloadVariant,payload,payload), 2) \
X(a, STATIC, SINGULAR, BOOL, want_response, 3) \
X(a, STATIC, SINGULAR, FIXED32, dest, 4) \
X(a, STATIC, SINGULAR, FIXED32, source, 5) \
X(a, STATIC, SINGULAR, FIXED32, request_id, 6) \
X(a, STATIC, SINGULAR, FIXED32, reply_id, 7) \
X(a, STATIC, SINGULAR, FIXED32, emoji, 8) \
X(a, STATIC, OPTIONAL, MESSAGE, location, 9)
X(a, STATIC, OPTIONAL, MESSAGE, location, 9) \
X(a, STATIC, ONEOF, BYTES, (payloadVariant,payload_compressed,payload_compressed), 10)
#define Data_CALLBACK NULL
#define Data_DEFAULT NULL
#define Data_location_MSGTYPE Location

View File

@@ -210,7 +210,12 @@ typedef enum _RadioConfig_UserPreferences_Serial_Baud {
/* TODO: REPLACE */
RadioConfig_UserPreferences_Serial_Baud_BAUD_576000 = 10,
/* TODO: REPLACE */
RadioConfig_UserPreferences_Serial_Baud_BAUD_921600 = 11
RadioConfig_UserPreferences_Serial_Baud_BAUD_921600 = 11,
/* TODO: REPLACE */
RadioConfig_UserPreferences_Serial_Baud_BAUD_110 = 12,
RadioConfig_UserPreferences_Serial_Baud_BAUD_300 = 13,
RadioConfig_UserPreferences_Serial_Baud_BAUD_600 = 14,
RadioConfig_UserPreferences_Serial_Baud_BAUD_1200 = 15
} RadioConfig_UserPreferences_Serial_Baud;
/* Defines the device's role on the Mesh network
@@ -381,8 +386,8 @@ typedef struct _RadioConfig {
#define _InputEventChar_ARRAYSIZE ((InputEventChar)(InputEventChar_KEY_BACK+1))
#define _RadioConfig_UserPreferences_Serial_Baud_MIN RadioConfig_UserPreferences_Serial_Baud_BAUD_Default
#define _RadioConfig_UserPreferences_Serial_Baud_MAX RadioConfig_UserPreferences_Serial_Baud_BAUD_921600
#define _RadioConfig_UserPreferences_Serial_Baud_ARRAYSIZE ((RadioConfig_UserPreferences_Serial_Baud)(RadioConfig_UserPreferences_Serial_Baud_BAUD_921600+1))
#define _RadioConfig_UserPreferences_Serial_Baud_MAX RadioConfig_UserPreferences_Serial_Baud_BAUD_1200
#define _RadioConfig_UserPreferences_Serial_Baud_ARRAYSIZE ((RadioConfig_UserPreferences_Serial_Baud)(RadioConfig_UserPreferences_Serial_Baud_BAUD_1200+1))
#define _RadioConfig_UserPreferences_Serial_Mode_MIN RadioConfig_UserPreferences_Serial_Mode_MODE_Default
#define _RadioConfig_UserPreferences_Serial_Mode_MAX RadioConfig_UserPreferences_Serial_Mode_MODE_PROTO

View File

@@ -40,10 +40,10 @@ typedef struct _EnvironmentMetrics {
/* Types of Measurements the telemetry module is equipped to handle */
typedef struct _Telemetry {
/* This is usually not sent over the mesh (to save space), but it is sent
from the phone so that the local device can set its RTC If it is sent over
the mesh (because there are devices on the mesh without GPS), it will only
be sent by devices which has a hardware GPS clock (IE Mobile Phone).
/* This is usually not sent over the mesh (to save space), but it is sent
from the phone so that the local device can set its RTC If it is sent over
the mesh (because there are devices on the mesh without GPS), it will only
be sent by devices which has a hardware GPS clock (IE Mobile Phone).
seconds since 1970 */
uint32_t time;
/* Key native device metrics such as battery level */

View File

@@ -47,11 +47,9 @@ using namespace httpsserver;
#include <WiFiClientSecure.h>
HTTPClient httpClient;
// needed for ESP32-targz
#define DEST_FS_USES_LITTLEFS
#define ESP_ARDUINO_VERSION_VAL(major, minor, patch) ((major << 16) | (minor << 8) | (patch))
#define ESP_ARDUINO_VERSION ESP_ARDUINO_VERSION_VAL(1, 0, 4)
#include <ESP32-targz.h>
// 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
@@ -63,57 +61,13 @@ char contentTypes[][2][32] = {{".txt", "text/plain"}, {".html", "text/html"}
{".svg", "image/svg+xml"}, {"", ""}};
// const char *tarURL = "https://www.casler.org/temp/meshtastic-web.tar";
const char *tarURL = "https://api-production-871d.up.railway.app/mirror/webui";
const char *certificate = NULL; // change this as needed, leave as is for no TLS check (yolo security)
// const char *tarURL = "https://api-production-871d.up.railway.app/mirror/webui";
// const char *certificate = NULL; // change this as needed, leave as is for no TLS check (yolo security)
// Our API to handle messages to and from the radio.
HttpAPI webAPI;
WiFiClient *getTarHTTPClientPtr(WiFiClientSecure *client, const char *url, const char *cert = NULL)
{
if (cert == NULL) {
// New versions don't have setInsecure
// client->setInsecure();
} else {
client->setCACert(cert);
}
const char *UserAgent = "ESP32-HTTP-GzUpdater-Client";
httpClient.setReuse(true); // handle 301 redirects gracefully
httpClient.setUserAgent(UserAgent);
httpClient.setConnectTimeout(10000); // 10s timeout = 10000
if (!httpClient.begin(*client, url)) {
log_e("Can't open url %s", url);
return nullptr;
}
const char *headerKeys[] = {"location", "redirect", "Content-Type", "Content-Length", "Content-Disposition"};
const size_t numberOfHeaders = 5;
httpClient.collectHeaders(headerKeys, numberOfHeaders);
int httpCode = httpClient.GET();
// file found at server
if (httpCode == HTTP_CODE_FOUND || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
String newlocation = "";
String headerLocation = httpClient.header("location");
String headerRedirect = httpClient.header("redirect");
if (headerLocation != "") {
newlocation = headerLocation;
Serial.printf("302 (location): %s => %s\n", url, headerLocation.c_str());
} else if (headerRedirect != "") {
Serial.printf("301 (redirect): %s => %s\n", url, headerLocation.c_str());
newlocation = headerRedirect;
}
httpClient.end();
if (newlocation != "") {
log_w("Found 302/301 location header: %s", newlocation.c_str());
return getTarHTTPClientPtr(client, newlocation.c_str(), cert);
} else {
log_e("Empty redirect !!");
return nullptr;
}
}
if (httpCode != 200)
return nullptr;
return httpClient.getStreamPtr();
}
void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
{
@@ -131,9 +85,9 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
ResourceNode *nodeAdmin = new ResourceNode("/admin", "GET", &handleAdmin);
ResourceNode *nodeAdminSettings = new ResourceNode("/admin/settings", "GET", &handleAdminSettings);
ResourceNode *nodeAdminSettingsApply = new ResourceNode("/admin/settings/apply", "POST", &handleAdminSettingsApply);
ResourceNode *nodeAdminFs = new ResourceNode("/admin/fs", "GET", &handleFs);
ResourceNode *nodeUpdateFs = new ResourceNode("/admin/fs/update", "POST", &handleUpdateFs);
ResourceNode *nodeDeleteFs = new ResourceNode("/admin/fs/delete", "GET", &handleDeleteFsContent);
// ResourceNode *nodeAdminFs = new ResourceNode("/admin/fs", "GET", &handleFs);
// ResourceNode *nodeUpdateFs = new ResourceNode("/admin/fs/update", "POST", &handleUpdateFs);
// ResourceNode *nodeDeleteFs = new ResourceNode("/admin/fs/delete", "GET", &handleDeleteFsContent);
ResourceNode *nodeRestart = new ResourceNode("/restart", "POST", &handleRestart);
ResourceNode *nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
@@ -160,10 +114,10 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
secureServer->registerNode(nodeJsonFsBrowseStatic);
secureServer->registerNode(nodeJsonDelete);
secureServer->registerNode(nodeJsonReport);
secureServer->registerNode(nodeUpdateFs);
secureServer->registerNode(nodeDeleteFs);
// secureServer->registerNode(nodeUpdateFs);
// secureServer->registerNode(nodeDeleteFs);
secureServer->registerNode(nodeAdmin);
secureServer->registerNode(nodeAdminFs);
// secureServer->registerNode(nodeAdminFs);
secureServer->registerNode(nodeAdminSettings);
secureServer->registerNode(nodeAdminSettingsApply);
secureServer->registerNode(nodeRoot); // This has to be last
@@ -181,10 +135,10 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
insecureServer->registerNode(nodeJsonFsBrowseStatic);
insecureServer->registerNode(nodeJsonDelete);
insecureServer->registerNode(nodeJsonReport);
insecureServer->registerNode(nodeUpdateFs);
insecureServer->registerNode(nodeDeleteFs);
// insecureServer->registerNode(nodeUpdateFs);
// insecureServer->registerNode(nodeDeleteFs);
insecureServer->registerNode(nodeAdmin);
insecureServer->registerNode(nodeAdminFs);
// insecureServer->registerNode(nodeAdminFs);
insecureServer->registerNode(nodeAdminSettings);
insecureServer->registerNode(nodeAdminSettingsApply);
insecureServer->registerNode(nodeRoot); // This has to be last
@@ -727,86 +681,6 @@ void handleHotspot(HTTPRequest *req, HTTPResponse *res)
res->println("<meta http-equiv=\"refresh\" content=\"0;url=/\" />\n");
}
void handleUpdateFs(HTTPRequest *req, HTTPResponse *res)
{
res->setHeader("Content-Type", "text/html");
res->setHeader("Access-Control-Allow-Origin", "*");
// res->setHeader("Access-Control-Allow-Methods", "POST");
res->println("<h1>Meshtastic</h1>\n");
res->println("Downloading Meshtastic Web Content...");
WiFiClientSecure *client = new WiFiClientSecure;
Stream *streamptr = getTarHTTPClientPtr(client, tarURL, certificate);
delay(5); // Let other network operations run
if (streamptr != nullptr) {
DEBUG_MSG("Connection to content server ... success!\n");
DEBUG_MSG("Deleting files from /static : \n");
htmlDeleteDir("/static");
delay(5); // Let other network operations run
TarUnpacker *TARUnpacker = new TarUnpacker();
TARUnpacker->haltOnError(false); // stop on fail (manual restart/reset required)
TARUnpacker->setTarVerify(false); // true = enables health checks but slows down the overall process
TARUnpacker->setupFSCallbacks(targzTotalBytesFn, targzFreeBytesFn); // prevent the partition from exploding, recommended
TARUnpacker->setLoggerCallback(BaseUnpacker::targzPrintLoggerCallback); // gz log verbosity
TARUnpacker->setTarProgressCallback(
BaseUnpacker::defaultProgressCallback); // prints the untarring progress for each individual file
TARUnpacker->setTarStatusProgressCallback(
BaseUnpacker::defaultTarStatusProgressCallback); // print the filenames as they're expanded
TARUnpacker->setTarMessageCallback(BaseUnpacker::targzPrintLoggerCallback); // tar log verbosity
String contentLengthStr = httpClient.header("Content-Length");
contentLengthStr.trim();
int64_t streamSize = -1;
if (contentLengthStr != "") {
streamSize = atoi(contentLengthStr.c_str());
Serial.printf("Stream size %d\n", streamSize);
res->printf("Stream size %d<br><br>\n", streamSize);
}
if (!TARUnpacker->tarStreamExpander(streamptr, streamSize, FSCom, "/static")) {
res->printf("tarStreamExpander failed with return code #%d\n", TARUnpacker->tarGzGetError());
Serial.printf("tarStreamExpander failed with return code #%d\n", TARUnpacker->tarGzGetError());
// Close the connection on error and free up memory
client->stop();
return;
} else {
/*
// print leftover bytes if any (probably zero-fill from the server)
while (httpClient.connected()) {
size_t streamSize = streamptr->available();
if (streamSize) {
Serial.printf("%02x ", streamptr->read());
} else
break;
}
*/
}
} else {
res->printf("Failed to establish http connection\n");
Serial.println("Failed to establish http connection");
return;
// Close the connection on error and free up memory
client->stop();
}
res->println("Done! Restarting the device. <a href=/>Click this in 10 seconds</a>");
/*
* This is a work around for a bug where we run out of memory.
* TODO: Fixme!
*/
// ESP.restart();
webServerThread->requestRestart = (millis() / 1000) + 5;
}
void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res)
{

View File

@@ -10,10 +10,13 @@
#include "target_specific.h"
#include <DNSServer.h>
#include <ESPmDNS.h>
#include <NTPClient.h>
#include <WiFi.h>
#include <WiFiUdp.h>
#ifndef DISABLE_NTP
#include <NTPClient.h>
#endif
using namespace concurrency;
static void WiFiEvent(WiFiEvent_t event);
@@ -23,7 +26,10 @@ DNSServer dnsServer;
// NTP
WiFiUDP ntpUDP;
#ifndef DISABLE_NTP
NTPClient timeClient(ntpUDP, "0.pool.ntp.org");
#endif
uint8_t wifiDisconnectReason = 0;
@@ -67,13 +73,13 @@ static int32_t reconnectWiFi()
DEBUG_MSG("... Reconnecting to WiFi access point\n");
WiFi.mode(WIFI_MODE_STA);
WiFi.begin(wifiName, wifiPsw);
// Starting timeClient;
}
}
//if (*wifiName) {
#ifndef DISABLE_NTP
// if (*wifiName) {
if (WiFi.isConnected()) {
DEBUG_MSG("Updating NTP time\n");
if (timeClient.update()) {
@@ -89,6 +95,7 @@ static int32_t reconnectWiFi()
DEBUG_MSG("NTP Update failed\n");
}
}
#endif
return 30 * 1000; // every 30 seconds
}
@@ -155,9 +162,11 @@ static void onNetworkConnected()
MDNS.addService("https", "tcp", 443);
}
#ifndef DISABLE_NTP
DEBUG_MSG("Starting NTP time client\n");
timeClient.begin();
timeClient.setUpdateInterval(60*60); // Update once an hour
timeClient.setUpdateInterval(60 * 60); // Update once an hour
#endif
initWebServer();
initApiServer();

View File

@@ -118,7 +118,7 @@ void PositionModule::sendOurPosition(NodeNum dest, bool wantReplies)
p->priority = MeshPacket_Priority_BACKGROUND;
prevPacketId = p->id;
service.sendToMesh(p);
service.sendToMesh(p, RX_SRC_LOCAL, true);
}
int32_t PositionModule::runOnce()

View File

@@ -27,7 +27,7 @@ int32_t DeviceTelemetryModule::runOnce()
bool DeviceTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemetry *t)
{
if (t->which_variant == Telemetry_device_metrics_tag) {
String sender = getSenderName(mp);
const char *sender = getSenderShortName(mp);
DEBUG_MSG("-----------------------------------------\n");
DEBUG_MSG("Device Telemetry: Received data from %s\n", sender);
@@ -44,19 +44,6 @@ bool DeviceTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemet
return false; // Let others look at this message also if they want
}
String DeviceTelemetryModule::getSenderName(const MeshPacket &mp)
{
String sender;
auto node = nodeDB.getNode(getFrom(&mp));
if (node) {
sender = node->user.short_name;
} else {
sender = "UNK";
}
return sender;
}
bool DeviceTelemetryModule::sendOurTelemetry(NodeNum dest, bool wantReplies)
{
Telemetry t;

View File

@@ -27,7 +27,6 @@ class DeviceTelemetryModule : private concurrency::OSThread, public ProtobufModu
bool sendOurTelemetry(NodeNum dest = NODENUM_BROADCAST, bool wantReplies = false);
private:
String getSenderName(const MeshPacket &mp);
bool firstTime = 1;
const MeshPacket *lastMeasurementPacket;
};

View File

@@ -149,19 +149,6 @@ int32_t EnvironmentTelemetryModule::runOnce()
#endif
}
String GetSenderName(const MeshPacket &mp)
{
String sender;
auto node = nodeDB.getNode(getFrom(&mp));
if (node) {
sender = node->user.short_name;
} else {
sender = "UNK";
}
return sender;
}
uint32_t GetTimeSinceMeshPacket(const MeshPacket *mp)
{
uint32_t now = getTime();
@@ -198,7 +185,7 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
Telemetry lastMeasurement;
uint32_t agoSecs = GetTimeSinceMeshPacket(lastMeasurementPacket);
String lastSender = GetSenderName(*lastMeasurementPacket);
const char *lastSender = getSenderShortName(*lastMeasurementPacket);
auto &p = lastMeasurementPacket->decoded;
if (!pb_decode_from_bytes(p.payload.bytes, p.payload.size, Telemetry_fields, &lastMeasurement)) {
@@ -213,16 +200,16 @@ void EnvironmentTelemetryModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiSt
if (radioConfig.preferences.telemetry_module_environment_display_fahrenheit) {
last_temp = String(CelsiusToFahrenheit(lastMeasurement.variant.environment_metrics.temperature), 0) + "°F";
}
display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + lastSender + "(" + String(agoSecs) + "s)");
display->drawString(x, y += fontHeight(FONT_SMALL) - 2,"Temp/Hum: " + last_temp + " / " + String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%");
display->drawString(x, y += fontHeight(FONT_MEDIUM) - 2, "From: " + String(lastSender) + "(" + String(agoSecs) + "s)");
display->drawString(x, y += fontHeight(FONT_SMALL) - 2, "Temp/Hum: " + last_temp + " / " + String(lastMeasurement.variant.environment_metrics.relative_humidity, 0) + "%");
if (lastMeasurement.variant.environment_metrics.barometric_pressure != 0)
display->drawString(x, y += fontHeight(FONT_SMALL),"Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA");
display->drawString(x, y += fontHeight(FONT_SMALL), "Press: " + String(lastMeasurement.variant.environment_metrics.barometric_pressure, 0) + "hPA");
}
bool EnvironmentTelemetryModule::handleReceivedProtobuf(const MeshPacket &mp, Telemetry *t)
{
if (t->which_variant == Telemetry_environment_metrics_tag) {
String sender = GetSenderName(mp);
const char *sender = getSenderShortName(mp);
DEBUG_MSG("-----------------------------------------\n");
DEBUG_MSG("Environment Telemetry: Received data from %s\n", sender);

View File

@@ -93,6 +93,18 @@ int32_t SerialModule::runOnce()
if (radioConfig.preferences.serial_module_baud == RadioConfig_UserPreferences_Serial_Baud_BAUD_Default) {
baud = 38400;
} else if (radioConfig.preferences.serial_module_baud == RadioConfig_UserPreferences_Serial_Baud_BAUD_110) {
baud = 110;
} else if (radioConfig.preferences.serial_module_baud == RadioConfig_UserPreferences_Serial_Baud_BAUD_300) {
baud = 300;
} else if (radioConfig.preferences.serial_module_baud == RadioConfig_UserPreferences_Serial_Baud_BAUD_600) {
baud = 600;
} else if (radioConfig.preferences.serial_module_baud == RadioConfig_UserPreferences_Serial_Baud_BAUD_1200) {
baud = 1200;
} else if (radioConfig.preferences.serial_module_baud == RadioConfig_UserPreferences_Serial_Baud_BAUD_2400) {
baud = 2400;

View File

@@ -1,12 +1,9 @@
#include "configuration.h"
#include "CryptoEngine.h"
#include "ocrypto_aes_ctr.h"
#include <Adafruit_nRFCrypto.h>
#include "aes-256/tiny-aes.h"
class NRF52CryptoEngine : public CryptoEngine
{
public:
NRF52CryptoEngine() {}
@@ -19,29 +16,47 @@ class NRF52CryptoEngine : public CryptoEngine
*/
virtual void encrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
// DEBUG_MSG("NRF52 encrypt!\n");
if (key.length > 0) {
ocrypto_aes_ctr_ctx ctx;
// DEBUG_MSG("NRF52 encrypt!\n");
if (key.length > 16) {
AES_ctx ctx;
initNonce(fromNode, packetId);
ocrypto_aes_ctr_init(&ctx, key.bytes, key.length, nonce);
ocrypto_aes_ctr_encrypt(&ctx, bytes, bytes, numBytes);
AES_init_ctx_iv(&ctx, key.bytes, nonce);
AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes);
} else if (key.length > 0) {
nRFCrypto.begin();
nRFCrypto_AES ctx;
uint8_t myLen = ctx.blockLen(numBytes);
char encBuf[myLen] = {0};
memcpy(encBuf, bytes, numBytes);
initNonce(fromNode, packetId);
ctx.begin();
ctx.Process(encBuf, numBytes, nonce, key.bytes, key.length, (char*)bytes, ctx.encryptFlag, ctx.ctrMode);
ctx.end();
nRFCrypto.end();
}
}
virtual void decrypt(uint32_t fromNode, uint64_t packetId, size_t numBytes, uint8_t *bytes) override
{
// DEBUG_MSG("NRF52 decrypt!\n");
if (key.length > 0) {
ocrypto_aes_ctr_ctx ctx;
// DEBUG_MSG("NRF52 decrypt!\n");
if (key.length > 16) {
AES_ctx ctx;
initNonce(fromNode, packetId);
ocrypto_aes_ctr_init(&ctx, key.bytes, key.length, nonce);
ocrypto_aes_ctr_decrypt(&ctx, bytes, bytes, numBytes);
AES_init_ctx_iv(&ctx, key.bytes, nonce);
AES_CTR_xcrypt_buffer(&ctx, bytes, numBytes);
} else if (key.length > 0) {
nRFCrypto.begin();
nRFCrypto_AES ctx;
uint8_t myLen = ctx.blockLen(numBytes);
char decBuf[myLen] = {0};
memcpy(decBuf, bytes, numBytes);
initNonce(fromNode, packetId);
ctx.begin();
ctx.Process(decBuf, numBytes, nonce, key.bytes, key.length, (char*)bytes, ctx.decryptFlag, ctx.ctrMode);
ctx.end();
nRFCrypto.end();
}
}

View File

@@ -0,0 +1,229 @@
/*
AES-256 Software Implementation
based on https://github.com/kokke/tiny-AES-C/ which is in public domain
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the key size is proportionally larger.
*/
#include <string.h>
#include "tiny-aes.h"
#define Nb 4
#define Nk 8
#define Nr 14
typedef uint8_t state_t[4][4];
static const uint8_t sbox[256] = {
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16 };
static const uint8_t Rcon[11] = {
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36 };
#define getSBoxValue(num) (sbox[(num)])
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
uint8_t tempa[4];
for (unsigned i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
for (unsigned i = Nk; i < Nb * (Nr + 1); ++i)
{
unsigned k = (i - 1) * 4;
tempa[0]=RoundKey[k + 0];
tempa[1]=RoundKey[k + 1];
tempa[2]=RoundKey[k + 2];
tempa[3]=RoundKey[k + 3];
if (i % Nk == 0)
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
tempa[0] = tempa[0] ^ Rcon[i/Nk];
}
if (i % Nk == 4)
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
unsigned j = i * 4; k=(i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
for (uint8_t i = 0; i < 4; ++i)
{
for (uint8_t j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
static void SubBytes(state_t* state)
{
for (uint8_t i = 0; i < 4; ++i)
{
for (uint8_t j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
static void ShiftRows(state_t* state)
{
uint8_t temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x<<1) ^ (((x>>7) & 1) * 0x1b));
}
static void MixColumns(state_t* state)
{
for (uint8_t i = 0; i < 4; ++i)
{
uint8_t t = (*state)[i][0];
uint8_t Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
uint8_t Tm = (*state)[i][0] ^ (*state)[i][1] ; Tm = xtime(Tm); (*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ; Tm = xtime(Tm); (*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ; Tm = xtime(Tm); (*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ; Tm = xtime(Tm); (*state)[i][3] ^= Tm ^ Tmp ;
}
}
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
static void Cipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
AddRoundKey(0, state, RoundKey);
for (round = 1; ; ++round)
{
SubBytes(state);
ShiftRows(state);
if (round == Nr) {
break;
}
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
AddRoundKey(Nr, state, RoundKey);
}
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length)
{
uint8_t buffer[AES_BLOCKLEN];
size_t i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
{
if (bi == AES_BLOCKLEN)
{
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer,ctx->RoundKey);
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
if (ctx->Iv[bi] == 255)
{
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}

View File

@@ -0,0 +1,23 @@
#ifndef _TINY_AES_H_
#define _TINY_AES_H_
#include <stdint.h>
#include <stddef.h>
#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only
// #define AES_KEYLEN 32
#define AES_keyExpSize 240
struct AES_ctx
{
uint8_t RoundKey[AES_keyExpSize];
uint8_t Iv[AES_BLOCKLEN];
};
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, size_t length);
#endif // _TINY_AES_H_

View File

@@ -5,6 +5,7 @@
#include <ble_gap.h>
#include <memory.h>
#include <stdio.h>
#include <Adafruit_nRFCrypto.h>
// #include <Adafruit_USBD_Device.h>
#include "NRF52Bluetooth.h"
@@ -32,8 +33,8 @@ void __attribute__((noreturn)) __assert_func(const char *file, int line, const c
{
DEBUG_MSG("assert failed %s: %d, %s, test=%s\n", file, line, func, failedexpr);
// debugger_break(); FIXME doesn't work, possibly not for segger
while (1)
; // FIXME, reboot!
// Reboot cpu
NVIC_SystemReset();
}
void getMacAddr(uint8_t *dmac)
@@ -145,13 +146,15 @@ void nrf52Setup()
#endif
// Init random seed
// FIXME - use this to get random numbers
// #include "nrf_rng.h"
// uint32_t r;
// ble_controller_rand_vector_get_blocking(&r, sizeof(r));
// randomSeed(r);
DEBUG_MSG("FIXME, call randomSeed\n");
// ::printf("TESTING PRINTF\n");
union seedParts {
uint32_t seed32;
uint8_t seed8[4];
} seed;
nRFCrypto.begin();
nRFCrypto.Random.generate(seed.seed8, sizeof(seed.seed8));
DEBUG_MSG("Setting random seed %u\n", seed.seed32);
randomSeed(seed.seed32);
nRFCrypto.end();
}
void cpuDeepSleep(uint64_t msecToWake)
@@ -193,4 +196,4 @@ void clearBonds() {
nrf52Bluetooth->setup();
}
nrf52Bluetooth->clearBonds();
}
}

View File

@@ -7,9 +7,11 @@
void powerCommandsCheck()
{
if (rebootAtMsec && millis() > rebootAtMsec) {
DEBUG_MSG("Rebooting\n");
#ifndef NO_ESP32
DEBUG_MSG("Rebooting for update\n");
ESP.restart();
#elif NRF52_SERIES
NVIC_SystemReset();
#else
DEBUG_MSG("FIXME implement reboot for this platform");
#endif

View File

@@ -1,20 +0,0 @@
; The very slick RAK wireless RAK 4631 / 4630 board
[env:rak4631_5005]
extends = nrf52840_base
board = wiscore_rak4631
# add our variants files to the include and src paths
# define build flags for the TFT_eSPI library
build_flags = ${nrf52840_base.build_flags} -Ivariants/WisCore_RAK4631_Board -D RAK_BASE_5005
src_filter = ${nrf52_base.src_filter} +<../variants/WisCore_RAK4631_Board>
debug_tool = jlink
[env:rak4631_19003]
extends = nrf52840_base
board = wiscore_rak4631
# add our variants files to the include and src paths
# define build flags for the TFT_eSPI library
build_flags = ${nrf52840_base.build_flags} -Ivariants/WisCore_RAK4631_Board -D RAK_BASE_19003
src_filter = ${nrf52_base.src_filter} +<../variants/WisCore_RAK4631_Board>
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink

View File

@@ -1,9 +0,0 @@
[env:rak4631_5005_eink]
extends = nrf52840_base
board = wiscore_rak4631
build_flags = ${nrf52840_base.build_flags} -Ivariants/WisCore_RAK4631_E-Paper_Board -D RAK_BASE_5005
src_filter = ${nrf52_base.src_filter} +<../variants/WisCore_RAK4631_E-Paper_Board>
lib_deps =
${nrf52840_base.lib_deps}
https://github.com/ZinggJM/GxEPD2.git
debug_tool = jlink

View File

@@ -0,0 +1,12 @@
; The very slick RAK wireless RAK 4631 / 4630 board - Unified firmare for 5005/19003, with or without OLED RAK 1921
[env:rak4631]
extends = nrf52840_base
board = wiscore_rak4631
build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631 -D RAK_4631
src_filter = ${nrf52_base.src_filter} +<../variants/rak4631>
lib_deps =
${nrf52840_base.lib_deps}
melopero/Melopero RV3028@^1.1.0
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink

View File

@@ -59,10 +59,8 @@ extern "C" {
* Buttons
*/
#ifdef RAK_BASE_5005
#define PIN_BUTTON1 9 // Pin for button on E-ink button module or IO expansion
#define BUTTON_NEED_PULLUP
#endif
#define PIN_BUTTON2 12
#define PIN_BUTTON3 24
#define PIN_BUTTON4 25
@@ -110,17 +108,39 @@ static const uint8_t AREF = PIN_AREF;
/*
* SPI Interfaces
*/
#define SPI_INTERFACES_COUNT 1
#define SPI_INTERFACES_COUNT 2
#define PIN_SPI_MISO (45)
#define PIN_SPI_MOSI (44)
#define PIN_SPI_SCK (43)
#define PIN_SPI1_MISO (29) // (0 + 29)
#define PIN_SPI1_MOSI (30) // (0 + 30)
#define PIN_SPI1_SCK (3) // (0 + 3)
static const uint8_t SS = 42;
static const uint8_t MOSI = PIN_SPI_MOSI;
static const uint8_t MISO = PIN_SPI_MISO;
static const uint8_t SCK = PIN_SPI_SCK;
/*
* eink display pins
*/
#define PIN_EINK_EN (0 + 2) // (0 + 2) Note: this is really just backlight power
#define PIN_EINK_CS (0 + 26)
#define PIN_EINK_BUSY (0 + 4)
#define PIN_EINK_DC (0 + 17)
#define PIN_EINK_RES (-1)
#define PIN_EINK_SCLK (0 + 3)
#define PIN_EINK_MOSI (0 + 30) // also called SDI
// Controls power for the eink display - Board power is enabled either by VBUS from USB or the CPU asserting PWR_ON
// FIXME - I think this is actually just the board power enable - it enables power to the CPU also
//#define PIN_EINK_PWR_ON (-1)
// #define HAS_EINK
/*
* Wire Interfaces
*/
@@ -175,10 +195,11 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define PIN_GPS_EN (34)
#define PIN_GPS_PPS (17) // Pulse per second input from the GPS
#ifdef RAK_BASE_5005
#define GPS_RX_PIN PIN_SERIAL1_RX
#define GPS_TX_PIN PIN_SERIAL1_TX
#endif
// RAK12002 RTC Module
#define RV3028_RTC (uint8_t) 0b1010010
// Battery
// The battery sense is hooked to pin A0 (5)

View File

@@ -0,0 +1,13 @@
; The very slick RAK wireless RAK 4631 / 4630 board - Firmware for 5005 with the RAK 14000 ePaper
[env:rak4631_eink]
extends = nrf52840_base
board = wiscore_rak4631
build_flags = ${nrf52840_base.build_flags} -Ivariants/rak4631_epaper -D RAK_4631
src_filter = ${nrf52_base.src_filter} +<../variants/rak4631_epaper>
lib_deps =
${nrf52840_base.lib_deps}
https://github.com/ZinggJM/GxEPD2.git
melopero/Melopero RV3028@^1.1.0
debug_tool = jlink
; If not set we will default to uploading over serial (first it forces bootloader entry by talking 1200bps to cdcacm)
;upload_protocol = jlink

View File

@@ -2,14 +2,17 @@
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

View File

@@ -2,6 +2,7 @@
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
@@ -58,10 +59,8 @@ extern "C" {
* Buttons
*/
#ifdef RAK_BASE_5005
#define PIN_BUTTON1 9 // Pin for button on E-ink button module or IO expansion
#define BUTTON_NEED_PULLUP
#endif
#define PIN_BUTTON2 12
#define PIN_BUTTON3 24
#define PIN_BUTTON4 25
@@ -196,10 +195,11 @@ static const uint8_t SCK = PIN_SPI_SCK;
#define PIN_GPS_EN (34)
#define PIN_GPS_PPS (17) // Pulse per second input from the GPS
#ifdef RAK_BASE_5005
#define GPS_RX_PIN PIN_SERIAL1_RX
#define GPS_TX_PIN PIN_SERIAL1_TX
#endif
// RAK12002 RTC Module
#define RV3028_RTC (uint8_t) 0b1010010
// Battery
// The battery sense is hooked to pin A0 (5)

View File

@@ -1,4 +1,4 @@
[VERSION]
major = 1
minor = 3
build = 6
build = 10