mirror of
https://github.com/meshtastic/firmware.git
synced 2026-01-15 06:17:47 +00:00
Compare commits
86 Commits
v1.3.6.f51
...
v1.3.10.cc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc2a84afcd | ||
|
|
6c1dc0d71a | ||
|
|
9e97fac252 | ||
|
|
3a9086dfc5 | ||
|
|
a0f34a8d0a | ||
|
|
359b41d869 | ||
|
|
04723bd1a0 | ||
|
|
89d6990a92 | ||
|
|
96f20287ff | ||
|
|
d8ba25747b | ||
|
|
e66c01f0e6 | ||
|
|
aaea2e7456 | ||
|
|
92185e763d | ||
|
|
4de5944474 | ||
|
|
76e48178c8 | ||
|
|
75e7bccdfb | ||
|
|
3786b1ee15 | ||
|
|
f2dec07c8d | ||
|
|
701707a01b | ||
|
|
d640478289 | ||
|
|
b8b1a5cfb7 | ||
|
|
770f17f382 | ||
|
|
8ea3ebf74b | ||
|
|
cbf238652e | ||
|
|
c17cd47689 | ||
|
|
d2c278a856 | ||
|
|
213d9512f1 | ||
|
|
81588d8bdc | ||
|
|
3c1407c7d2 | ||
|
|
98c8eaaaf0 | ||
|
|
e7a825d1ba | ||
|
|
1d2551350d | ||
|
|
a13157ebde | ||
|
|
a0971ebe9c | ||
|
|
8733bcb52e | ||
|
|
823e6cb1ed | ||
|
|
29e378a11e | ||
|
|
692278343b | ||
|
|
c60d4c1ecc | ||
|
|
6d01f9aa89 | ||
|
|
616c7d7b0e | ||
|
|
6b012ca5b0 | ||
|
|
93466baa87 | ||
|
|
137328f567 | ||
|
|
838f00c7d7 | ||
|
|
293921e95a | ||
|
|
b82bf5c729 | ||
|
|
90df7c2488 | ||
|
|
8c1a81c03a | ||
|
|
6fe9f0b42f | ||
|
|
43f6f61472 | ||
|
|
ed62b6916c | ||
|
|
cf45e4fce5 | ||
|
|
136e2e96f7 | ||
|
|
7c071e2361 | ||
|
|
0b85e97087 | ||
|
|
81d1cc1003 | ||
|
|
748416d9e3 | ||
|
|
da6d49385c | ||
|
|
f279f9614e | ||
|
|
9097475149 | ||
|
|
5327d6162a | ||
|
|
5d7990667d | ||
|
|
79a41bd81c | ||
|
|
6a3d81eff8 | ||
|
|
7cd66b2b68 | ||
|
|
8124ecbfd8 | ||
|
|
7df1a64b52 | ||
|
|
e51b7c3c32 | ||
|
|
0c285aac6e | ||
|
|
fbeb554186 | ||
|
|
87da779478 | ||
|
|
1082c5d771 | ||
|
|
3933d24d62 | ||
|
|
fc0508f254 | ||
|
|
bb22b6ec58 | ||
|
|
ddb9678377 | ||
|
|
dc20cbb672 | ||
|
|
a10ea604af | ||
|
|
fb1caa51d0 | ||
|
|
7e977aea00 | ||
|
|
ecc114f1cd | ||
|
|
b76424db50 | ||
|
|
b056081d3c | ||
|
|
a4bdef4151 | ||
|
|
478274aff1 |
19
.github/workflows/main_matrix.yml
vendored
19
.github/workflows/main_matrix.yml
vendored
@@ -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
3
.gitmodules
vendored
@@ -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
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# Meshtastic-device
|
||||
[](https://open.vscode.dev/meshtastic/Meshtastic-device)
|
||||
[](https://github.com/meshtastic/Meshtastic-device/actions/workflows/main.yml)
|
||||
[](https://github.com/meshtastic/Meshtastic-device/actions/workflows/main_matrix.yml)
|
||||

|
||||
[](https://cla-assistant.io/meshtastic/Meshtastic-device)
|
||||
[](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
|
||||
|
||||

|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
@@ -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
2
proto
Submodule proto updated: 870a62b27e...a578453b3c
Submodule sdk-nrfxlib deleted from e6e02cb83d
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
60
src/debug/einkScan.h
Normal 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
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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(¬ifySleep);
|
||||
notifyDeepSleepObserver.unobserve(¬ifyDeepSleep);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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") {}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */ }
|
||||
};
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
1344
src/mesh/compression/unishox2.c
Normal file
1344
src/mesh/compression/unishox2.c
Normal file
File diff suppressed because it is too large
Load Diff
278
src/mesh/compression/unishox2.h
Normal file
278
src/mesh/compression/unishox2.h
Normal 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
|
||||
@@ -12,4 +12,8 @@ PB_BIND(DeviceState, DeviceState, 4)
|
||||
PB_BIND(ChannelFile, ChannelFile, 2)
|
||||
|
||||
|
||||
PB_BIND(OEMStore, OEMStore, 2)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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" */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
229
src/nrf52/aes-256/tiny-aes.cpp
Normal file
229
src/nrf52/aes-256/tiny-aes.cpp
Normal 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]);
|
||||
}
|
||||
}
|
||||
23
src/nrf52/aes-256/tiny-aes.h
Normal file
23
src/nrf52/aes-256/tiny-aes.h
Normal 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_
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
12
variants/rak4631/platformio.ini
Normal file
12
variants/rak4631/platformio.ini
Normal 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
|
||||
@@ -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)
|
||||
13
variants/rak4631_epaper/platformio.ini
Normal file
13
variants/rak4631_epaper/platformio.ini
Normal 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
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -1,4 +1,4 @@
|
||||
[VERSION]
|
||||
major = 1
|
||||
minor = 3
|
||||
build = 6
|
||||
build = 10
|
||||
|
||||
Reference in New Issue
Block a user