Merge remote-tracking branch 'origin/dev' into awolden/t3lora
This commit is contained in:
119
examples/companion_radio/UITask.cpp
Normal file
119
examples/companion_radio/UITask.cpp
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#include "UITask.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <helpers/TxtDataHelpers.h>
|
||||||
|
|
||||||
|
#define AUTO_OFF_MILLIS 15000 // 15 seconds
|
||||||
|
|
||||||
|
// 'meshcore', 128x13px
|
||||||
|
static const uint8_t meshcore_logo [] PROGMEM = {
|
||||||
|
0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe,
|
||||||
|
0x3c, 0x03, 0xe3, 0xff, 0xc7, 0xff, 0x8e, 0x03, 0x8f, 0xfe, 0x3f, 0xfe, 0x1f, 0xff, 0x1f, 0xfe,
|
||||||
|
0x3e, 0x03, 0xc3, 0xff, 0x8f, 0xff, 0x0e, 0x07, 0x8f, 0xfe, 0x7f, 0xfe, 0x1f, 0xff, 0x1f, 0xfc,
|
||||||
|
0x3e, 0x07, 0xc7, 0x80, 0x0e, 0x00, 0x0e, 0x07, 0x9e, 0x00, 0x78, 0x0e, 0x3c, 0x0f, 0x1c, 0x00,
|
||||||
|
0x3e, 0x0f, 0xc7, 0x80, 0x1e, 0x00, 0x0e, 0x07, 0x1e, 0x00, 0x70, 0x0e, 0x38, 0x0f, 0x3c, 0x00,
|
||||||
|
0x7f, 0x0f, 0xc7, 0xfe, 0x1f, 0xfc, 0x1f, 0xff, 0x1c, 0x00, 0x70, 0x0e, 0x38, 0x0e, 0x3f, 0xf8,
|
||||||
|
0x7f, 0x1f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x0e, 0x38, 0x0e, 0x3f, 0xf8,
|
||||||
|
0x7f, 0x3f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x1e, 0x3f, 0xfe, 0x3f, 0xf0,
|
||||||
|
0x77, 0x3b, 0x87, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xfc, 0x38, 0x00,
|
||||||
|
0x77, 0xfb, 0x8f, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xf8, 0x38, 0x00,
|
||||||
|
0x73, 0xf3, 0x8f, 0xff, 0x0f, 0xff, 0x1c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x78, 0x7f, 0xf8,
|
||||||
|
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfe, 0x3c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x3c, 0x7f, 0xf8,
|
||||||
|
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8,
|
||||||
|
};
|
||||||
|
|
||||||
|
void UITask::begin(const char* node_name, const char* build_date, uint32_t pin_code) {
|
||||||
|
_prevBtnState = HIGH;
|
||||||
|
_auto_off = millis() + AUTO_OFF_MILLIS;
|
||||||
|
clearMsgPreview();
|
||||||
|
_node_name = node_name;
|
||||||
|
_build_date = build_date;
|
||||||
|
_pin_code = pin_code;
|
||||||
|
_display->turnOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UITask::clearMsgPreview() {
|
||||||
|
_origin[0] = 0;
|
||||||
|
_msg[0] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char* text) {
|
||||||
|
if (path_len == 0xFF) {
|
||||||
|
sprintf(_origin, "(F) %s", from_name);
|
||||||
|
} else {
|
||||||
|
sprintf(_origin, "(%d) %s", (uint32_t) path_len, from_name);
|
||||||
|
}
|
||||||
|
StrHelper::strncpy(_msg, text, sizeof(_msg));
|
||||||
|
|
||||||
|
if (!_display->isOn()) _display->turnOn();
|
||||||
|
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
|
||||||
|
}
|
||||||
|
|
||||||
|
void UITask::renderCurrScreen() {
|
||||||
|
char tmp[80];
|
||||||
|
if (_origin[0] && _msg[0]) {
|
||||||
|
// render message preview
|
||||||
|
_display->setCursor(0, 0);
|
||||||
|
_display->setTextSize(1);
|
||||||
|
_display->print(_node_name);
|
||||||
|
|
||||||
|
_display->setCursor(0, 12);
|
||||||
|
_display->print(_origin);
|
||||||
|
_display->setCursor(0, 24);
|
||||||
|
_display->print(_msg);
|
||||||
|
|
||||||
|
//_display->setCursor(100, 9); TODO
|
||||||
|
//_display->setTextSize(2);
|
||||||
|
//_display->printf("%d", msgs);
|
||||||
|
} else {
|
||||||
|
// render 'home' screen
|
||||||
|
_display->drawXbm(0, 0, meshcore_logo, 128, 13);
|
||||||
|
_display->setCursor(0, 20);
|
||||||
|
_display->setTextSize(1);
|
||||||
|
_display->print(_node_name);
|
||||||
|
|
||||||
|
sprintf(tmp, "Build: %s", _build_date);
|
||||||
|
_display->setCursor(0, 32);
|
||||||
|
_display->print(tmp);
|
||||||
|
|
||||||
|
if (_connected) {
|
||||||
|
//_display->printf("freq : %03.2f sf %d\n", _prefs.freq, _prefs.sf);
|
||||||
|
//_display->printf("bw : %03.2f cr %d\n", _prefs.bw, _prefs.cr);
|
||||||
|
} else {
|
||||||
|
_display->setTextSize(2);
|
||||||
|
_display->setCursor(0, 43);
|
||||||
|
sprintf(tmp, "Pin:%d", _pin_code);
|
||||||
|
_display->print(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UITask::loop() {
|
||||||
|
if (millis() >= _next_read) {
|
||||||
|
int btnState = digitalRead(PIN_USER_BTN);
|
||||||
|
if (btnState != _prevBtnState) {
|
||||||
|
if (btnState == LOW) { // pressed?
|
||||||
|
if (_display->isOn()) {
|
||||||
|
clearMsgPreview();
|
||||||
|
} else {
|
||||||
|
_display->turnOn();
|
||||||
|
}
|
||||||
|
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
|
||||||
|
}
|
||||||
|
_prevBtnState = btnState;
|
||||||
|
}
|
||||||
|
_next_read = millis() + 100; // 10 reads per second
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_display->isOn()) {
|
||||||
|
if (millis() >= _next_refresh) {
|
||||||
|
_display->startFrame();
|
||||||
|
renderCurrScreen();
|
||||||
|
_display->endFrame();
|
||||||
|
|
||||||
|
_next_refresh = millis() + 1000; // refresh every second
|
||||||
|
}
|
||||||
|
if (millis() > _auto_off) {
|
||||||
|
_display->turnOff();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
examples/companion_radio/UITask.h
Normal file
25
examples/companion_radio/UITask.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <helpers/ui/DisplayDriver.h>
|
||||||
|
|
||||||
|
class UITask {
|
||||||
|
DisplayDriver* _display;
|
||||||
|
unsigned long _next_read, _next_refresh, _auto_off;
|
||||||
|
int _prevBtnState;
|
||||||
|
bool _connected;
|
||||||
|
uint32_t _pin_code;
|
||||||
|
const char* _node_name;
|
||||||
|
const char* _build_date;
|
||||||
|
char _origin[62];
|
||||||
|
char _msg[80];
|
||||||
|
|
||||||
|
void renderCurrScreen();
|
||||||
|
public:
|
||||||
|
UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; _connected = false; }
|
||||||
|
void begin(const char* node_name, const char* build_date, uint32_t pin_code);
|
||||||
|
|
||||||
|
void setHasConnection(bool connected) { _connected = connected; }
|
||||||
|
void clearMsgPreview();
|
||||||
|
void showMsgPreview(uint8_t path_len, const char* from_name, const char* text);
|
||||||
|
void loop();
|
||||||
|
};
|
||||||
@@ -88,6 +88,15 @@
|
|||||||
#error "need to provide a 'board' object"
|
#error "need to provide a 'board' object"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
#include <helpers/ui/SSD1306Display.h>
|
||||||
|
|
||||||
|
static DISPLAY_CLASS display;
|
||||||
|
|
||||||
|
#include "UITask.h"
|
||||||
|
static UITask ui_task(display);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Believe it or not, this std C function is busted on some platforms!
|
// Believe it or not, this std C function is busted on some platforms!
|
||||||
static uint32_t _atoi(const char* sp) {
|
static uint32_t _atoi(const char* sp) {
|
||||||
uint32_t n = 0;
|
uint32_t n = 0;
|
||||||
@@ -182,6 +191,7 @@ struct NodePrefs { // persisted to file
|
|||||||
uint8_t tx_power_dbm;
|
uint8_t tx_power_dbm;
|
||||||
uint8_t unused[3];
|
uint8_t unused[3];
|
||||||
float rx_delay_base;
|
float rx_delay_base;
|
||||||
|
uint32_t ble_pin;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MyMesh : public BaseChatMesh {
|
class MyMesh : public BaseChatMesh {
|
||||||
@@ -214,6 +224,11 @@ class MyMesh : public BaseChatMesh {
|
|||||||
if (!_identity_store->load("_main", self_id)) {
|
if (!_identity_store->load("_main", self_id)) {
|
||||||
self_id = mesh::LocalIdentity(&trng); // create new random identity
|
self_id = mesh::LocalIdentity(&trng); // create new random identity
|
||||||
saveMainIdentity(self_id);
|
saveMainIdentity(self_id);
|
||||||
|
|
||||||
|
#if defined(BLE_PIN_CODE) && defined(DISPLAY_CLASS)
|
||||||
|
// start with randomly assigned BLE pin
|
||||||
|
_prefs.ble_pin = trng.nextInt(100000, 999999);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -475,6 +490,9 @@ protected:
|
|||||||
} else {
|
} else {
|
||||||
soundBuzzer();
|
soundBuzzer();
|
||||||
}
|
}
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
ui_task.showMsgPreview(path_len, from.name, text);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void onMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override {
|
void onMessageRecv(const ContactInfo& from, uint8_t path_len, uint32_t sender_timestamp, const char *text) override {
|
||||||
@@ -514,6 +532,9 @@ protected:
|
|||||||
} else {
|
} else {
|
||||||
soundBuzzer();
|
soundBuzzer();
|
||||||
}
|
}
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
ui_task.showMsgPreview(in_path_len < 0 ? 0xFF : in_path_len, "Public", text);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) override {
|
void onContactResponse(const ContactInfo& contact, const uint8_t* data, uint8_t len) override {
|
||||||
@@ -600,6 +621,9 @@ public:
|
|||||||
_prefs.bw = LORA_BW;
|
_prefs.bw = LORA_BW;
|
||||||
_prefs.cr = LORA_CR;
|
_prefs.cr = LORA_CR;
|
||||||
_prefs.tx_power_dbm = LORA_TX_POWER;
|
_prefs.tx_power_dbm = LORA_TX_POWER;
|
||||||
|
#ifdef BLE_PIN_CODE
|
||||||
|
_prefs.ble_pin = BLE_PIN_CODE;
|
||||||
|
#endif
|
||||||
//_prefs.rx_delay_base = 10.0f; enable once new algo fixed
|
//_prefs.rx_delay_base = 10.0f; enable once new algo fixed
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -620,7 +644,34 @@ public:
|
|||||||
if (_fs->exists("/node_prefs")) {
|
if (_fs->exists("/node_prefs")) {
|
||||||
File file = _fs->open("/node_prefs");
|
File file = _fs->open("/node_prefs");
|
||||||
if (file) {
|
if (file) {
|
||||||
file.read((uint8_t *) &_prefs, sizeof(_prefs));
|
uint8_t pad[8];
|
||||||
|
|
||||||
|
file.read((uint8_t *) &_prefs.airtime_factor, sizeof(float)); // 0
|
||||||
|
file.read((uint8_t *) _prefs.node_name, sizeof(_prefs.node_name)); // 4
|
||||||
|
file.read(pad, 4); // 36
|
||||||
|
file.read((uint8_t *) &_prefs.node_lat, sizeof(_prefs.node_lat)); // 40
|
||||||
|
file.read((uint8_t *) &_prefs.node_lon, sizeof(_prefs.node_lon)); // 48
|
||||||
|
file.read((uint8_t *) &_prefs.freq, sizeof(_prefs.freq)); // 56
|
||||||
|
file.read((uint8_t *) &_prefs.sf, sizeof(_prefs.sf)); // 60
|
||||||
|
file.read((uint8_t *) &_prefs.cr, sizeof(_prefs.cr)); // 61
|
||||||
|
file.read((uint8_t *) &_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
|
||||||
|
file.read((uint8_t *) &_prefs.reserved2, sizeof(_prefs.reserved2)); // 63
|
||||||
|
file.read((uint8_t *) &_prefs.bw, sizeof(_prefs.bw)); // 64
|
||||||
|
file.read((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
|
||||||
|
file.read((uint8_t *) _prefs.unused, sizeof(_prefs.unused)); // 69
|
||||||
|
file.read((uint8_t *) &_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
|
||||||
|
file.read(pad, 4); // 76
|
||||||
|
file.read((uint8_t *) &_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
|
||||||
|
|
||||||
|
// sanitise bad pref values
|
||||||
|
_prefs.rx_delay_base = constrain(_prefs.rx_delay_base, 0, 20.0f);
|
||||||
|
_prefs.airtime_factor = constrain(_prefs.airtime_factor, 0, 9.0f);
|
||||||
|
_prefs.freq = constrain(_prefs.freq, 400.0f, 2500.0f);
|
||||||
|
_prefs.bw = constrain(_prefs.bw, 62.5f, 500.0f);
|
||||||
|
_prefs.sf = constrain(_prefs.sf, 7, 12);
|
||||||
|
_prefs.cr = constrain(_prefs.cr, 5, 8);
|
||||||
|
_prefs.tx_power_dbm = constrain(_prefs.tx_power_dbm, 1, MAX_LORA_TX_POWER);
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -639,6 +690,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char* getNodeName() { return _prefs.node_name; }
|
const char* getNodeName() { return _prefs.node_name; }
|
||||||
|
uint32_t getBLEPin() { return _prefs.ble_pin; }
|
||||||
|
|
||||||
void startInterface(BaseSerialInterface& serial) {
|
void startInterface(BaseSerialInterface& serial) {
|
||||||
_serial = &serial;
|
_serial = &serial;
|
||||||
@@ -653,7 +705,26 @@ public:
|
|||||||
File file = _fs->open("/node_prefs", "w", true);
|
File file = _fs->open("/node_prefs", "w", true);
|
||||||
#endif
|
#endif
|
||||||
if (file) {
|
if (file) {
|
||||||
file.write((const uint8_t *)&_prefs, sizeof(_prefs));
|
uint8_t pad[8];
|
||||||
|
memset(pad, 0, sizeof(pad));
|
||||||
|
|
||||||
|
file.write((uint8_t *) &_prefs.airtime_factor, sizeof(float)); // 0
|
||||||
|
file.write((uint8_t *) _prefs.node_name, sizeof(_prefs.node_name)); // 4
|
||||||
|
file.write(pad, 4); // 36
|
||||||
|
file.write((uint8_t *) &_prefs.node_lat, sizeof(_prefs.node_lat)); // 40
|
||||||
|
file.write((uint8_t *) &_prefs.node_lon, sizeof(_prefs.node_lon)); // 48
|
||||||
|
file.write((uint8_t *) &_prefs.freq, sizeof(_prefs.freq)); // 56
|
||||||
|
file.write((uint8_t *) &_prefs.sf, sizeof(_prefs.sf)); // 60
|
||||||
|
file.write((uint8_t *) &_prefs.cr, sizeof(_prefs.cr)); // 61
|
||||||
|
file.write((uint8_t *) &_prefs.reserved1, sizeof(_prefs.reserved1)); // 62
|
||||||
|
file.write((uint8_t *) &_prefs.reserved2, sizeof(_prefs.reserved2)); // 63
|
||||||
|
file.write((uint8_t *) &_prefs.bw, sizeof(_prefs.bw)); // 64
|
||||||
|
file.write((uint8_t *) &_prefs.tx_power_dbm, sizeof(_prefs.tx_power_dbm)); // 68
|
||||||
|
file.write((uint8_t *) _prefs.unused, sizeof(_prefs.unused)); // 69
|
||||||
|
file.write((uint8_t *) &_prefs.rx_delay_base, sizeof(_prefs.rx_delay_base)); // 72
|
||||||
|
file.write(pad, 4); // 76
|
||||||
|
file.write((uint8_t *) &_prefs.ble_pin, sizeof(_prefs.ble_pin)); // 80
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1081,6 +1152,11 @@ public:
|
|||||||
} else if (!_serial->isWriteBusy()) {
|
} else if (!_serial->isWriteBusy()) {
|
||||||
checkConnections();
|
checkConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
ui_task.setHasConnection(_serial->isConnected());
|
||||||
|
ui_task.loop();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1112,7 +1188,7 @@ public:
|
|||||||
|
|
||||||
#if defined(NRF52_PLATFORM)
|
#if defined(NRF52_PLATFORM)
|
||||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
||||||
#elif defined(LILYGO_TLORA) || defined(HELTEC_LORA_V2) // ESP32 with SX1276
|
#elif defined(LILYGO_TLORA)
|
||||||
SPIClass spi;
|
SPIClass spi;
|
||||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
|
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
|
||||||
#elif defined(P_LORA_SCLK)
|
#elif defined(P_LORA_SCLK)
|
||||||
@@ -1139,6 +1215,10 @@ void setup() {
|
|||||||
float tcxo = 1.6f;
|
float tcxo = 1.6f;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
display.begin();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(NRF52_PLATFORM)
|
#if defined(NRF52_PLATFORM)
|
||||||
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
|
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
|
||||||
SPI.begin();
|
SPI.begin();
|
||||||
@@ -1173,7 +1253,7 @@ void setup() {
|
|||||||
#ifdef BLE_PIN_CODE
|
#ifdef BLE_PIN_CODE
|
||||||
char dev_name[32+10];
|
char dev_name[32+10];
|
||||||
sprintf(dev_name, "MeshCore-%s", the_mesh.getNodeName());
|
sprintf(dev_name, "MeshCore-%s", the_mesh.getNodeName());
|
||||||
serial_interface.begin(dev_name, BLE_PIN_CODE);
|
serial_interface.begin(dev_name, the_mesh.getBLEPin());
|
||||||
#else
|
#else
|
||||||
pinMode(WB_IO2, OUTPUT);
|
pinMode(WB_IO2, OUTPUT);
|
||||||
serial_interface.begin(Serial);
|
serial_interface.begin(Serial);
|
||||||
@@ -1189,7 +1269,7 @@ void setup() {
|
|||||||
#elif defined(BLE_PIN_CODE)
|
#elif defined(BLE_PIN_CODE)
|
||||||
char dev_name[32+10];
|
char dev_name[32+10];
|
||||||
sprintf(dev_name, "MeshCore-%s", the_mesh.getNodeName());
|
sprintf(dev_name, "MeshCore-%s", the_mesh.getNodeName());
|
||||||
serial_interface.begin(dev_name, BLE_PIN_CODE);
|
serial_interface.begin(dev_name, the_mesh.getBLEPin());
|
||||||
#else
|
#else
|
||||||
serial_interface.begin(Serial);
|
serial_interface.begin(Serial);
|
||||||
#endif
|
#endif
|
||||||
@@ -1197,6 +1277,10 @@ void setup() {
|
|||||||
#else
|
#else
|
||||||
#error "need to define filesystem"
|
#error "need to define filesystem"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE, the_mesh.getBLEPin());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
|||||||
79
examples/simple_repeater/UITask.cpp
Normal file
79
examples/simple_repeater/UITask.cpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#include "UITask.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define AUTO_OFF_MILLIS 20000 // 20 seconds
|
||||||
|
|
||||||
|
// 'meshcore', 128x13px
|
||||||
|
static const uint8_t meshcore_logo [] PROGMEM = {
|
||||||
|
0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe,
|
||||||
|
0x3c, 0x03, 0xe3, 0xff, 0xc7, 0xff, 0x8e, 0x03, 0x8f, 0xfe, 0x3f, 0xfe, 0x1f, 0xff, 0x1f, 0xfe,
|
||||||
|
0x3e, 0x03, 0xc3, 0xff, 0x8f, 0xff, 0x0e, 0x07, 0x8f, 0xfe, 0x7f, 0xfe, 0x1f, 0xff, 0x1f, 0xfc,
|
||||||
|
0x3e, 0x07, 0xc7, 0x80, 0x0e, 0x00, 0x0e, 0x07, 0x9e, 0x00, 0x78, 0x0e, 0x3c, 0x0f, 0x1c, 0x00,
|
||||||
|
0x3e, 0x0f, 0xc7, 0x80, 0x1e, 0x00, 0x0e, 0x07, 0x1e, 0x00, 0x70, 0x0e, 0x38, 0x0f, 0x3c, 0x00,
|
||||||
|
0x7f, 0x0f, 0xc7, 0xfe, 0x1f, 0xfc, 0x1f, 0xff, 0x1c, 0x00, 0x70, 0x0e, 0x38, 0x0e, 0x3f, 0xf8,
|
||||||
|
0x7f, 0x1f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x0e, 0x38, 0x0e, 0x3f, 0xf8,
|
||||||
|
0x7f, 0x3f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x1e, 0x3f, 0xfe, 0x3f, 0xf0,
|
||||||
|
0x77, 0x3b, 0x87, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xfc, 0x38, 0x00,
|
||||||
|
0x77, 0xfb, 0x8f, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xf8, 0x38, 0x00,
|
||||||
|
0x73, 0xf3, 0x8f, 0xff, 0x0f, 0xff, 0x1c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x78, 0x7f, 0xf8,
|
||||||
|
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfe, 0x3c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x3c, 0x7f, 0xf8,
|
||||||
|
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8,
|
||||||
|
};
|
||||||
|
|
||||||
|
void UITask::begin(const char* node_name, const char* build_date) {
|
||||||
|
_prevBtnState = HIGH;
|
||||||
|
_auto_off = millis() + AUTO_OFF_MILLIS;
|
||||||
|
_node_name = node_name;
|
||||||
|
_build_date = build_date;
|
||||||
|
_display->turnOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UITask::renderCurrScreen() {
|
||||||
|
char tmp[80];
|
||||||
|
// render 'home' screen
|
||||||
|
_display->drawXbm(0, 0, meshcore_logo, 128, 13);
|
||||||
|
_display->setCursor(0, 20);
|
||||||
|
_display->setTextSize(1);
|
||||||
|
_display->print(_node_name);
|
||||||
|
|
||||||
|
sprintf(tmp, "Build: %s", _build_date);
|
||||||
|
_display->setCursor(0, 32);
|
||||||
|
_display->print(tmp);
|
||||||
|
_display->setCursor(0, 43);
|
||||||
|
_display->print("< Repeater >");
|
||||||
|
//_display->printf("freq : %03.2f sf %d\n", _prefs.freq, _prefs.sf);
|
||||||
|
//_display->printf("bw : %03.2f cr %d\n", _prefs.bw, _prefs.cr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UITask::loop() {
|
||||||
|
#ifdef PIN_USER_BTN
|
||||||
|
if (millis() >= _next_read) {
|
||||||
|
int btnState = digitalRead(PIN_USER_BTN);
|
||||||
|
if (btnState != _prevBtnState) {
|
||||||
|
if (btnState == LOW) { // pressed?
|
||||||
|
if (_display->isOn()) {
|
||||||
|
// TODO: any action ?
|
||||||
|
} else {
|
||||||
|
_display->turnOn();
|
||||||
|
}
|
||||||
|
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
|
||||||
|
}
|
||||||
|
_prevBtnState = btnState;
|
||||||
|
}
|
||||||
|
_next_read = millis() + 200; // 5 reads per second
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (_display->isOn()) {
|
||||||
|
if (millis() >= _next_refresh) {
|
||||||
|
_display->startFrame();
|
||||||
|
renderCurrScreen();
|
||||||
|
_display->endFrame();
|
||||||
|
|
||||||
|
_next_refresh = millis() + 1000; // refresh every second
|
||||||
|
}
|
||||||
|
if (millis() > _auto_off) {
|
||||||
|
_display->turnOff();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
examples/simple_repeater/UITask.h
Normal file
18
examples/simple_repeater/UITask.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <helpers/ui/DisplayDriver.h>
|
||||||
|
|
||||||
|
class UITask {
|
||||||
|
DisplayDriver* _display;
|
||||||
|
unsigned long _next_read, _next_refresh, _auto_off;
|
||||||
|
int _prevBtnState;
|
||||||
|
const char* _node_name;
|
||||||
|
const char* _build_date;
|
||||||
|
|
||||||
|
void renderCurrScreen();
|
||||||
|
public:
|
||||||
|
UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; }
|
||||||
|
void begin(const char* node_name, const char* build_date);
|
||||||
|
|
||||||
|
void loop();
|
||||||
|
};
|
||||||
@@ -88,6 +88,15 @@
|
|||||||
#error "need to provide a 'board' object"
|
#error "need to provide a 'board' object"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
#include <helpers/ui/SSD1306Display.h>
|
||||||
|
|
||||||
|
static DISPLAY_CLASS display;
|
||||||
|
|
||||||
|
#include "UITask.h"
|
||||||
|
static UITask ui_task(display);
|
||||||
|
#endif
|
||||||
|
|
||||||
#define PACKET_LOG_FILE "/packet_log"
|
#define PACKET_LOG_FILE "/packet_log"
|
||||||
|
|
||||||
/* ------------------------------ Code -------------------------------- */
|
/* ------------------------------ Code -------------------------------- */
|
||||||
@@ -107,7 +116,8 @@ struct RepeaterStats {
|
|||||||
uint32_t total_up_time_secs;
|
uint32_t total_up_time_secs;
|
||||||
uint32_t n_sent_flood, n_sent_direct;
|
uint32_t n_sent_flood, n_sent_direct;
|
||||||
uint32_t n_recv_flood, n_recv_direct;
|
uint32_t n_recv_flood, n_recv_direct;
|
||||||
uint16_t n_full_events, reserved1;
|
uint16_t n_full_events;
|
||||||
|
int16_t last_snr; // x 4
|
||||||
uint16_t n_direct_dups, n_flood_dups;
|
uint16_t n_direct_dups, n_flood_dups;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -175,7 +185,7 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks {
|
|||||||
stats.n_recv_flood = getNumRecvFlood();
|
stats.n_recv_flood = getNumRecvFlood();
|
||||||
stats.n_recv_direct = getNumRecvDirect();
|
stats.n_recv_direct = getNumRecvDirect();
|
||||||
stats.n_full_events = getNumFullEvents();
|
stats.n_full_events = getNumFullEvents();
|
||||||
stats.reserved1 = 0;
|
stats.last_snr = (int16_t)(my_radio->getLastSNR() * 4);
|
||||||
stats.n_direct_dups = ((SimpleMeshTables *)getTables())->getNumDirectDups();
|
stats.n_direct_dups = ((SimpleMeshTables *)getTables())->getNumDirectDups();
|
||||||
stats.n_flood_dups = ((SimpleMeshTables *)getTables())->getNumFloodDups();
|
stats.n_flood_dups = ((SimpleMeshTables *)getTables())->getNumFloodDups();
|
||||||
|
|
||||||
@@ -411,27 +421,34 @@ protected:
|
|||||||
|
|
||||||
if (!(flags == TXT_TYPE_PLAIN || flags == TXT_TYPE_CLI_DATA)) {
|
if (!(flags == TXT_TYPE_PLAIN || flags == TXT_TYPE_CLI_DATA)) {
|
||||||
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported text type received: flags=%02x", (uint32_t)flags);
|
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported text type received: flags=%02x", (uint32_t)flags);
|
||||||
} else if (sender_timestamp > client->last_timestamp) { // prevent replay attacks
|
} else if (sender_timestamp >= client->last_timestamp) { // prevent replay attacks
|
||||||
|
bool is_retry = (sender_timestamp == client->last_timestamp);
|
||||||
client->last_timestamp = sender_timestamp;
|
client->last_timestamp = sender_timestamp;
|
||||||
client->last_activity = getRTCClock()->getCurrentTime();
|
client->last_activity = getRTCClock()->getCurrentTime();
|
||||||
|
|
||||||
// len can be > original length, but 'text' will be padded with zeroes
|
// len can be > original length, but 'text' will be padded with zeroes
|
||||||
data[len] = 0; // need to make a C string again, with null terminator
|
data[len] = 0; // need to make a C string again, with null terminator
|
||||||
|
|
||||||
uint32_t ack_hash; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it
|
if (flags == TXT_TYPE_PLAIN) { // for legacy CLI, send Acks
|
||||||
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 5 + strlen((char *)&data[5]), client->id.pub_key, PUB_KEY_SIZE);
|
uint32_t ack_hash; // calc truncated hash of the message timestamp + text + sender pub_key, to prove to sender that we got it
|
||||||
|
mesh::Utils::sha256((uint8_t *) &ack_hash, 4, data, 5 + strlen((char *)&data[5]), client->id.pub_key, PUB_KEY_SIZE);
|
||||||
|
|
||||||
mesh::Packet* ack = createAck(ack_hash);
|
mesh::Packet* ack = createAck(ack_hash);
|
||||||
if (ack) {
|
if (ack) {
|
||||||
if (client->out_path_len < 0) {
|
if (client->out_path_len < 0) {
|
||||||
sendFlood(ack);
|
sendFlood(ack);
|
||||||
} else {
|
} else {
|
||||||
sendDirect(ack, client->out_path, client->out_path_len);
|
sendDirect(ack, client->out_path, client->out_path_len);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t temp[166];
|
uint8_t temp[166];
|
||||||
_cli.handleCommand(sender_timestamp, (const char *) &data[5], (char *) &temp[5]);
|
if (is_retry) {
|
||||||
|
temp[0] = 0;
|
||||||
|
} else {
|
||||||
|
_cli.handleCommand(sender_timestamp, (const char *) &data[5], (char *) &temp[5]);
|
||||||
|
}
|
||||||
int text_len = strlen((char *) &temp[5]);
|
int text_len = strlen((char *) &temp[5]);
|
||||||
if (text_len > 0) {
|
if (text_len > 0) {
|
||||||
uint32_t timestamp = getRTCClock()->getCurrentTimeUnique();
|
uint32_t timestamp = getRTCClock()->getCurrentTimeUnique();
|
||||||
@@ -442,9 +459,6 @@ protected:
|
|||||||
memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique
|
memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique
|
||||||
temp[4] = (TXT_TYPE_CLI_DATA << 2); // NOTE: legacy was: TXT_TYPE_PLAIN
|
temp[4] = (TXT_TYPE_CLI_DATA << 2); // NOTE: legacy was: TXT_TYPE_PLAIN
|
||||||
|
|
||||||
// calc expected ACK reply
|
|
||||||
//mesh::Utils::sha256((uint8_t *)&expected_ack_crc, 4, temp, 5 + text_len, self_id.pub_key, PUB_KEY_SIZE);
|
|
||||||
|
|
||||||
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len);
|
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len);
|
||||||
if (reply) {
|
if (reply) {
|
||||||
if (client->out_path_len < 0) {
|
if (client->out_path_len < 0) {
|
||||||
@@ -509,13 +523,7 @@ public:
|
|||||||
mesh::Mesh::begin();
|
mesh::Mesh::begin();
|
||||||
_fs = fs;
|
_fs = fs;
|
||||||
// load persisted prefs
|
// load persisted prefs
|
||||||
if (_fs->exists("/node_prefs")) {
|
_cli.loadPrefs(_fs);
|
||||||
File file = _fs->open("/node_prefs");
|
|
||||||
if (file) {
|
|
||||||
file.read((uint8_t *) &_prefs, sizeof(_prefs));
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_phy->setFrequency(_prefs.freq);
|
_phy->setFrequency(_prefs.freq);
|
||||||
_phy->setSpreadingFactor(_prefs.sf);
|
_phy->setSpreadingFactor(_prefs.sf);
|
||||||
@@ -528,18 +536,10 @@ public:
|
|||||||
|
|
||||||
const char* getFirmwareVer() override { return FIRMWARE_VERSION; }
|
const char* getFirmwareVer() override { return FIRMWARE_VERSION; }
|
||||||
const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; }
|
const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; }
|
||||||
|
const char* getNodeName() { return _prefs.node_name; }
|
||||||
|
|
||||||
void savePrefs() override {
|
void savePrefs() override {
|
||||||
#if defined(NRF52_PLATFORM)
|
_cli.savePrefs(_fs);
|
||||||
File file = _fs->open("/node_prefs", FILE_O_WRITE);
|
|
||||||
if (file) { file.seek(0); file.truncate(); }
|
|
||||||
#else
|
|
||||||
File file = _fs->open("/node_prefs", "w", true);
|
|
||||||
#endif
|
|
||||||
if (file) {
|
|
||||||
file.write((const uint8_t *)&_prefs, sizeof(_prefs));
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool formatFileSystem() override {
|
bool formatFileSystem() override {
|
||||||
@@ -603,12 +603,15 @@ public:
|
|||||||
|
|
||||||
updateAdvertTimer(); // schedule next local advert
|
updateAdvertTimer(); // schedule next local advert
|
||||||
}
|
}
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
ui_task.loop();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(NRF52_PLATFORM)
|
#if defined(NRF52_PLATFORM)
|
||||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
||||||
#elif defined(LILYGO_TLORA) || defined(HELTEC_LORA_V2) // ESP32 with SX1276
|
#elif defined(LILYGO_TLORA)
|
||||||
SPIClass spi;
|
SPIClass spi;
|
||||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
|
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
|
||||||
#elif defined(P_LORA_SCLK)
|
#elif defined(P_LORA_SCLK)
|
||||||
@@ -650,6 +653,11 @@ void setup() {
|
|||||||
#else
|
#else
|
||||||
float tcxo = 1.6f;
|
float tcxo = 1.6f;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
display.begin();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(NRF52_PLATFORM)
|
#if defined(NRF52_PLATFORM)
|
||||||
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
|
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
|
||||||
SPI.begin();
|
SPI.begin();
|
||||||
@@ -702,6 +710,10 @@ void setup() {
|
|||||||
|
|
||||||
the_mesh.begin(fs);
|
the_mesh.begin(fs);
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE);
|
||||||
|
#endif
|
||||||
|
|
||||||
// send out initial Advertisement to the mesh
|
// send out initial Advertisement to the mesh
|
||||||
the_mesh.sendSelfAdvertisement(2000);
|
the_mesh.sendSelfAdvertisement(2000);
|
||||||
}
|
}
|
||||||
|
|||||||
79
examples/simple_room_server/UITask.cpp
Normal file
79
examples/simple_room_server/UITask.cpp
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
#include "UITask.h"
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#define AUTO_OFF_MILLIS 20000 // 20 seconds
|
||||||
|
|
||||||
|
// 'meshcore', 128x13px
|
||||||
|
static const uint8_t meshcore_logo [] PROGMEM = {
|
||||||
|
0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe,
|
||||||
|
0x3c, 0x03, 0xe3, 0xff, 0xc7, 0xff, 0x8e, 0x03, 0x8f, 0xfe, 0x3f, 0xfe, 0x1f, 0xff, 0x1f, 0xfe,
|
||||||
|
0x3e, 0x03, 0xc3, 0xff, 0x8f, 0xff, 0x0e, 0x07, 0x8f, 0xfe, 0x7f, 0xfe, 0x1f, 0xff, 0x1f, 0xfc,
|
||||||
|
0x3e, 0x07, 0xc7, 0x80, 0x0e, 0x00, 0x0e, 0x07, 0x9e, 0x00, 0x78, 0x0e, 0x3c, 0x0f, 0x1c, 0x00,
|
||||||
|
0x3e, 0x0f, 0xc7, 0x80, 0x1e, 0x00, 0x0e, 0x07, 0x1e, 0x00, 0x70, 0x0e, 0x38, 0x0f, 0x3c, 0x00,
|
||||||
|
0x7f, 0x0f, 0xc7, 0xfe, 0x1f, 0xfc, 0x1f, 0xff, 0x1c, 0x00, 0x70, 0x0e, 0x38, 0x0e, 0x3f, 0xf8,
|
||||||
|
0x7f, 0x1f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x0e, 0x38, 0x0e, 0x3f, 0xf8,
|
||||||
|
0x7f, 0x3f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x1e, 0x3f, 0xfe, 0x3f, 0xf0,
|
||||||
|
0x77, 0x3b, 0x87, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xfc, 0x38, 0x00,
|
||||||
|
0x77, 0xfb, 0x8f, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xf8, 0x38, 0x00,
|
||||||
|
0x73, 0xf3, 0x8f, 0xff, 0x0f, 0xff, 0x1c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x78, 0x7f, 0xf8,
|
||||||
|
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfe, 0x3c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x3c, 0x7f, 0xf8,
|
||||||
|
0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8,
|
||||||
|
};
|
||||||
|
|
||||||
|
void UITask::begin(const char* node_name, const char* build_date) {
|
||||||
|
_prevBtnState = HIGH;
|
||||||
|
_auto_off = millis() + AUTO_OFF_MILLIS;
|
||||||
|
_node_name = node_name;
|
||||||
|
_build_date = build_date;
|
||||||
|
_display->turnOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UITask::renderCurrScreen() {
|
||||||
|
char tmp[80];
|
||||||
|
// render 'home' screen
|
||||||
|
_display->drawXbm(0, 0, meshcore_logo, 128, 13);
|
||||||
|
_display->setCursor(0, 20);
|
||||||
|
_display->setTextSize(1);
|
||||||
|
_display->print(_node_name);
|
||||||
|
|
||||||
|
sprintf(tmp, "Build: %s", _build_date);
|
||||||
|
_display->setCursor(0, 32);
|
||||||
|
_display->print(tmp);
|
||||||
|
_display->setCursor(0, 43);
|
||||||
|
_display->print("< Room Server >");
|
||||||
|
//_display->printf("freq : %03.2f sf %d\n", _prefs.freq, _prefs.sf);
|
||||||
|
//_display->printf("bw : %03.2f cr %d\n", _prefs.bw, _prefs.cr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UITask::loop() {
|
||||||
|
#ifdef PIN_USER_BTN
|
||||||
|
if (millis() >= _next_read) {
|
||||||
|
int btnState = digitalRead(PIN_USER_BTN);
|
||||||
|
if (btnState != _prevBtnState) {
|
||||||
|
if (btnState == LOW) { // pressed?
|
||||||
|
if (_display->isOn()) {
|
||||||
|
// TODO: any action ?
|
||||||
|
} else {
|
||||||
|
_display->turnOn();
|
||||||
|
}
|
||||||
|
_auto_off = millis() + AUTO_OFF_MILLIS; // extend auto-off timer
|
||||||
|
}
|
||||||
|
_prevBtnState = btnState;
|
||||||
|
}
|
||||||
|
_next_read = millis() + 200; // 5 reads per second
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (_display->isOn()) {
|
||||||
|
if (millis() >= _next_refresh) {
|
||||||
|
_display->startFrame();
|
||||||
|
renderCurrScreen();
|
||||||
|
_display->endFrame();
|
||||||
|
|
||||||
|
_next_refresh = millis() + 1000; // refresh every second
|
||||||
|
}
|
||||||
|
if (millis() > _auto_off) {
|
||||||
|
_display->turnOff();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
examples/simple_room_server/UITask.h
Normal file
18
examples/simple_room_server/UITask.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <helpers/ui/DisplayDriver.h>
|
||||||
|
|
||||||
|
class UITask {
|
||||||
|
DisplayDriver* _display;
|
||||||
|
unsigned long _next_read, _next_refresh, _auto_off;
|
||||||
|
int _prevBtnState;
|
||||||
|
const char* _node_name;
|
||||||
|
const char* _build_date;
|
||||||
|
|
||||||
|
void renderCurrScreen();
|
||||||
|
public:
|
||||||
|
UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; }
|
||||||
|
void begin(const char* node_name, const char* build_date);
|
||||||
|
|
||||||
|
void loop();
|
||||||
|
};
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
#include <helpers/CustomSX1262Wrapper.h>
|
#include <helpers/CustomSX1262Wrapper.h>
|
||||||
#include <helpers/CustomSX1268Wrapper.h>
|
#include <helpers/CustomSX1268Wrapper.h>
|
||||||
static XiaoC3Board board;
|
static XiaoC3Board board;
|
||||||
#elif defined(SEEED_XIAO_S3) || defined(LILYGO_T3S3)
|
#elif defined(SEEED_XIAO_S3)
|
||||||
#include <helpers/ESP32Board.h>
|
#include <helpers/ESP32Board.h>
|
||||||
#include <helpers/CustomSX1262Wrapper.h>
|
#include <helpers/CustomSX1262Wrapper.h>
|
||||||
static ESP32Board board;
|
static ESP32Board board;
|
||||||
@@ -92,6 +92,15 @@
|
|||||||
#error "need to provide a 'board' object"
|
#error "need to provide a 'board' object"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
#include <helpers/ui/SSD1306Display.h>
|
||||||
|
|
||||||
|
static DISPLAY_CLASS display;
|
||||||
|
|
||||||
|
#include "UITask.h"
|
||||||
|
static UITask ui_task(display);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ------------------------------ Code -------------------------------- */
|
/* ------------------------------ Code -------------------------------- */
|
||||||
|
|
||||||
struct ClientInfo {
|
struct ClientInfo {
|
||||||
@@ -376,7 +385,8 @@ protected:
|
|||||||
|
|
||||||
if (!(flags == TXT_TYPE_PLAIN || flags == TXT_TYPE_CLI_DATA)) {
|
if (!(flags == TXT_TYPE_PLAIN || flags == TXT_TYPE_CLI_DATA)) {
|
||||||
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported command flags received: flags=%02x", (uint32_t)flags);
|
MESH_DEBUG_PRINTLN("onPeerDataRecv: unsupported command flags received: flags=%02x", (uint32_t)flags);
|
||||||
} else if (sender_timestamp > client->last_timestamp) { // prevent replay attacks
|
} else if (sender_timestamp >= client->last_timestamp) { // prevent replay attacks, but send Acks for retries
|
||||||
|
bool is_retry = (sender_timestamp == client->last_timestamp);
|
||||||
client->last_timestamp = sender_timestamp;
|
client->last_timestamp = sender_timestamp;
|
||||||
|
|
||||||
uint32_t now = getRTCClock()->getCurrentTimeUnique();
|
uint32_t now = getRTCClock()->getCurrentTimeUnique();
|
||||||
@@ -393,19 +403,26 @@ protected:
|
|||||||
bool send_ack;
|
bool send_ack;
|
||||||
if (flags == TXT_TYPE_CLI_DATA) {
|
if (flags == TXT_TYPE_CLI_DATA) {
|
||||||
if (client->is_admin) {
|
if (client->is_admin) {
|
||||||
_cli.handleCommand(sender_timestamp, (const char *) &data[5], (char *) &temp[5]);
|
if (is_retry) {
|
||||||
temp[4] = (TXT_TYPE_CLI_DATA << 2); // attempt and flags, (NOTE: legacy was: TXT_TYPE_PLAIN)
|
temp[5] = 0; // no reply
|
||||||
send_ack = true;
|
} else {
|
||||||
|
_cli.handleCommand(sender_timestamp, (const char *) &data[5], (char *) &temp[5]);
|
||||||
|
temp[4] = (TXT_TYPE_CLI_DATA << 2); // attempt and flags, (NOTE: legacy was: TXT_TYPE_PLAIN)
|
||||||
|
}
|
||||||
|
send_ack = false;
|
||||||
} else {
|
} else {
|
||||||
temp[5] = 0; // no reply
|
temp[5] = 0; // no reply
|
||||||
send_ack = false; // and no ACK... user shoudn't be sending these
|
send_ack = false; // and no ACK... user shoudn't be sending these
|
||||||
}
|
}
|
||||||
} else { // TXT_TYPE_PLAIN
|
} else { // TXT_TYPE_PLAIN
|
||||||
addPost(client, (const char *) &data[5]);
|
if (!is_retry) {
|
||||||
|
addPost(client, (const char *) &data[5]);
|
||||||
|
}
|
||||||
temp[5] = 0; // no reply (ACK is enough)
|
temp[5] = 0; // no reply (ACK is enough)
|
||||||
send_ack = true;
|
send_ack = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t delay_millis;
|
||||||
if (send_ack) {
|
if (send_ack) {
|
||||||
mesh::Packet* ack = createAck(ack_hash);
|
mesh::Packet* ack = createAck(ack_hash);
|
||||||
if (ack) {
|
if (ack) {
|
||||||
@@ -415,6 +432,9 @@ protected:
|
|||||||
sendDirect(ack, client->out_path, client->out_path_len);
|
sendDirect(ack, client->out_path, client->out_path_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delay_millis = REPLY_DELAY_MILLIS;
|
||||||
|
} else {
|
||||||
|
delay_millis = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int text_len = strlen((char *) &temp[5]);
|
int text_len = strlen((char *) &temp[5]);
|
||||||
@@ -431,9 +451,9 @@ protected:
|
|||||||
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len);
|
auto reply = createDatagram(PAYLOAD_TYPE_TXT_MSG, client->id, secret, temp, 5 + text_len);
|
||||||
if (reply) {
|
if (reply) {
|
||||||
if (client->out_path_len < 0) {
|
if (client->out_path_len < 0) {
|
||||||
sendFlood(reply, REPLY_DELAY_MILLIS);
|
sendFlood(reply, delay_millis);
|
||||||
} else {
|
} else {
|
||||||
sendDirect(reply, client->out_path, client->out_path_len, REPLY_DELAY_MILLIS);
|
sendDirect(reply, client->out_path, client->out_path_len, delay_millis);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -544,13 +564,7 @@ public:
|
|||||||
mesh::Mesh::begin();
|
mesh::Mesh::begin();
|
||||||
_fs = fs;
|
_fs = fs;
|
||||||
// load persisted prefs
|
// load persisted prefs
|
||||||
if (_fs->exists("/node_prefs")) {
|
_cli.loadPrefs(_fs);
|
||||||
File file = _fs->open("/node_prefs");
|
|
||||||
if (file) {
|
|
||||||
file.read((uint8_t *) &_prefs, sizeof(_prefs));
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_phy->setFrequency(_prefs.freq);
|
_phy->setFrequency(_prefs.freq);
|
||||||
_phy->setSpreadingFactor(_prefs.sf);
|
_phy->setSpreadingFactor(_prefs.sf);
|
||||||
@@ -563,18 +577,10 @@ public:
|
|||||||
|
|
||||||
const char* getFirmwareVer() override { return FIRMWARE_VERSION; }
|
const char* getFirmwareVer() override { return FIRMWARE_VERSION; }
|
||||||
const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; }
|
const char* getBuildDate() override { return FIRMWARE_BUILD_DATE; }
|
||||||
|
const char* getNodeName() { return _prefs.node_name; }
|
||||||
|
|
||||||
void savePrefs() override {
|
void savePrefs() override {
|
||||||
#if defined(NRF52_PLATFORM)
|
_cli.savePrefs(_fs);
|
||||||
File file = _fs->open("/node_prefs", FILE_O_WRITE);
|
|
||||||
if (file) { file.seek(0); file.truncate(); }
|
|
||||||
#else
|
|
||||||
File file = _fs->open("/node_prefs", "w", true);
|
|
||||||
#endif
|
|
||||||
if (file) {
|
|
||||||
file.write((const uint8_t *)&_prefs, sizeof(_prefs));
|
|
||||||
file.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool formatFileSystem() override {
|
bool formatFileSystem() override {
|
||||||
@@ -657,13 +663,17 @@ public:
|
|||||||
updateAdvertTimer(); // schedule next local advert
|
updateAdvertTimer(); // schedule next local advert
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
ui_task.loop();
|
||||||
|
#endif
|
||||||
|
|
||||||
// TODO: periodically check for OLD/inactive entries in known_clients[], and evict
|
// TODO: periodically check for OLD/inactive entries in known_clients[], and evict
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(NRF52_PLATFORM)
|
#if defined(NRF52_PLATFORM)
|
||||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
||||||
#elif defined(LILYGO_TLORA) || defined(HELTEC_LORA_V2) // ESP32 with SX1276
|
#elif defined(LILYGO_TLORA)
|
||||||
SPIClass spi;
|
SPIClass spi;
|
||||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
|
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
|
||||||
#elif defined(P_LORA_SCLK)
|
#elif defined(P_LORA_SCLK)
|
||||||
@@ -706,6 +716,10 @@ void setup() {
|
|||||||
float tcxo = 1.6f;
|
float tcxo = 1.6f;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
display.begin();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(NRF52_PLATFORM)
|
#if defined(NRF52_PLATFORM)
|
||||||
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
|
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
|
||||||
SPI.begin();
|
SPI.begin();
|
||||||
@@ -757,6 +771,10 @@ void setup() {
|
|||||||
|
|
||||||
the_mesh.begin(fs);
|
the_mesh.begin(fs);
|
||||||
|
|
||||||
|
#ifdef DISPLAY_CLASS
|
||||||
|
ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE);
|
||||||
|
#endif
|
||||||
|
|
||||||
// send out initial Advertisement to the mesh
|
// send out initial Advertisement to the mesh
|
||||||
the_mesh.sendSelfAdvertisement(2000);
|
the_mesh.sendSelfAdvertisement(2000);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -545,7 +545,7 @@ public:
|
|||||||
|
|
||||||
#if defined(NRF52_PLATFORM)
|
#if defined(NRF52_PLATFORM)
|
||||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, SPI);
|
||||||
#elif defined(LILYGO_TLORA) || defined(HELTEC_LORA_V2) // ESP32 with SX1276
|
#elif defined(LILYGO_TLORA)
|
||||||
SPIClass spi;
|
SPIClass spi;
|
||||||
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
|
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_0, P_LORA_RESET, P_LORA_DIO_1, spi);
|
||||||
#elif defined(P_LORA_SCLK)
|
#elif defined(P_LORA_SCLK)
|
||||||
|
|||||||
@@ -113,28 +113,34 @@ build_flags =
|
|||||||
-D P_LORA_TX_LED=35
|
-D P_LORA_TX_LED=35
|
||||||
-D PIN_BOARD_SDA=17
|
-D PIN_BOARD_SDA=17
|
||||||
-D PIN_BOARD_SCL=18
|
-D PIN_BOARD_SCL=18
|
||||||
|
-D PIN_USER_BTN=0
|
||||||
-D SX126X_DIO2_AS_RF_SWITCH=true
|
-D SX126X_DIO2_AS_RF_SWITCH=true
|
||||||
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
-D SX126X_DIO3_TCXO_VOLTAGE=1.8
|
||||||
-D SX126X_CURRENT_LIMIT=130.0f ; for best TX power!
|
-D SX126X_CURRENT_LIMIT=130.0f ; for best TX power!
|
||||||
build_src_filter = ${esp32_base.build_src_filter}
|
build_src_filter = ${esp32_base.build_src_filter}
|
||||||
|
lib_deps =
|
||||||
|
${esp32_base.lib_deps}
|
||||||
|
adafruit/Adafruit SSD1306 @ ^2.5.13
|
||||||
|
|
||||||
[env:Heltec_v3_repeater]
|
[env:Heltec_v3_repeater]
|
||||||
extends = Heltec_lora32_v3
|
extends = Heltec_lora32_v3
|
||||||
build_flags =
|
build_flags =
|
||||||
${Heltec_lora32_v3.build_flags}
|
${Heltec_lora32_v3.build_flags}
|
||||||
|
-D DISPLAY_CLASS=SSD1306Display
|
||||||
-D ADVERT_NAME="\"Heltec Repeater\""
|
-D ADVERT_NAME="\"Heltec Repeater\""
|
||||||
-D ADVERT_LAT=-37.0
|
-D ADVERT_LAT=-37.0
|
||||||
-D ADVERT_LON=145.0
|
-D ADVERT_LON=145.0
|
||||||
-D ADMIN_PASSWORD="\"password\""
|
-D ADMIN_PASSWORD="\"password\""
|
||||||
-D MESH_PACKET_LOGGING=1
|
-D MESH_PACKET_LOGGING=1
|
||||||
; -D MESH_DEBUG=1
|
; -D MESH_DEBUG=1
|
||||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_repeater/main.cpp>
|
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<helpers/ui/*.cpp> +<../examples/simple_repeater>
|
||||||
|
|
||||||
[env:Heltec_v3_room_server]
|
[env:Heltec_v3_room_server]
|
||||||
extends = Heltec_lora32_v3
|
extends = Heltec_lora32_v3
|
||||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/simple_room_server/main.cpp>
|
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<helpers/ui/*.cpp> +<../examples/simple_room_server>
|
||||||
build_flags =
|
build_flags =
|
||||||
${Heltec_lora32_v3.build_flags}
|
${Heltec_lora32_v3.build_flags}
|
||||||
|
-D DISPLAY_CLASS=SSD1306Display
|
||||||
-D ADVERT_NAME="\"Heltec Room\""
|
-D ADVERT_NAME="\"Heltec Room\""
|
||||||
-D ADVERT_LAT=-37.0
|
-D ADVERT_LAT=-37.0
|
||||||
-D ADVERT_LON=145.0
|
-D ADVERT_LON=145.0
|
||||||
@@ -162,11 +168,12 @@ build_flags =
|
|||||||
${Heltec_lora32_v3.build_flags}
|
${Heltec_lora32_v3.build_flags}
|
||||||
-D MAX_CONTACTS=100
|
-D MAX_CONTACTS=100
|
||||||
-D MAX_GROUP_CHANNELS=1
|
-D MAX_GROUP_CHANNELS=1
|
||||||
|
-D DISPLAY_CLASS=SSD1306Display
|
||||||
; -D ENABLE_PRIVATE_KEY_IMPORT=1
|
; -D ENABLE_PRIVATE_KEY_IMPORT=1
|
||||||
; -D ENABLE_PRIVATE_KEY_EXPORT=1
|
; -D ENABLE_PRIVATE_KEY_EXPORT=1
|
||||||
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
|
; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1
|
||||||
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
|
; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1
|
||||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<../examples/companion_radio/main.cpp>
|
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<helpers/ui/*.cpp> +<../examples/companion_radio>
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${Heltec_lora32_v3.lib_deps}
|
${Heltec_lora32_v3.lib_deps}
|
||||||
densaugeo/base64 @ ~1.4.0
|
densaugeo/base64 @ ~1.4.0
|
||||||
@@ -177,13 +184,14 @@ build_flags =
|
|||||||
${Heltec_lora32_v3.build_flags}
|
${Heltec_lora32_v3.build_flags}
|
||||||
-D MAX_CONTACTS=100
|
-D MAX_CONTACTS=100
|
||||||
-D MAX_GROUP_CHANNELS=1
|
-D MAX_GROUP_CHANNELS=1
|
||||||
|
-D DISPLAY_CLASS=SSD1306Display
|
||||||
-D BLE_PIN_CODE=123456
|
-D BLE_PIN_CODE=123456
|
||||||
-D BLE_DEBUG_LOGGING=1
|
-D BLE_DEBUG_LOGGING=1
|
||||||
; -D ENABLE_PRIVATE_KEY_IMPORT=1
|
; -D ENABLE_PRIVATE_KEY_IMPORT=1
|
||||||
; -D ENABLE_PRIVATE_KEY_EXPORT=1
|
; -D ENABLE_PRIVATE_KEY_EXPORT=1
|
||||||
; -D MESH_PACKET_LOGGING=1
|
; -D MESH_PACKET_LOGGING=1
|
||||||
; -D MESH_DEBUG=1
|
; -D MESH_DEBUG=1
|
||||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<helpers/esp32/*.cpp> +<../examples/companion_radio/main.cpp>
|
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<helpers/ui/*.cpp> +<helpers/esp32/*.cpp> +<../examples/companion_radio>
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${Heltec_lora32_v3.lib_deps}
|
${Heltec_lora32_v3.lib_deps}
|
||||||
densaugeo/base64 @ ~1.4.0
|
densaugeo/base64 @ ~1.4.0
|
||||||
@@ -194,6 +202,7 @@ build_flags =
|
|||||||
${Heltec_lora32_v3.build_flags}
|
${Heltec_lora32_v3.build_flags}
|
||||||
-D MAX_CONTACTS=100
|
-D MAX_CONTACTS=100
|
||||||
-D MAX_GROUP_CHANNELS=1
|
-D MAX_GROUP_CHANNELS=1
|
||||||
|
-D DISPLAY_CLASS=SSD1306Display
|
||||||
-D WIFI_DEBUG_LOGGING=1
|
-D WIFI_DEBUG_LOGGING=1
|
||||||
-D WIFI_SSID="\"myssid\""
|
-D WIFI_SSID="\"myssid\""
|
||||||
-D WIFI_PWD="\"mypwd\""
|
-D WIFI_PWD="\"mypwd\""
|
||||||
@@ -201,7 +210,7 @@ build_flags =
|
|||||||
; -D ENABLE_PRIVATE_KEY_EXPORT=1
|
; -D ENABLE_PRIVATE_KEY_EXPORT=1
|
||||||
; -D MESH_PACKET_LOGGING=1
|
; -D MESH_PACKET_LOGGING=1
|
||||||
; -D MESH_DEBUG=1
|
; -D MESH_DEBUG=1
|
||||||
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<helpers/esp32/*.cpp> +<../examples/companion_radio/main.cpp>
|
build_src_filter = ${Heltec_lora32_v3.build_src_filter} +<helpers/ui/*.cpp> +<helpers/esp32/*.cpp> +<../examples/companion_radio>
|
||||||
lib_deps =
|
lib_deps =
|
||||||
${Heltec_lora32_v3.lib_deps}
|
${Heltec_lora32_v3.lib_deps}
|
||||||
densaugeo/base64 @ ~1.4.0
|
densaugeo/base64 @ ~1.4.0
|
||||||
@@ -619,6 +628,7 @@ board_build.ldscript = boards/nrf52840_s140_v7.ld
|
|||||||
build_flags = ${nrf52840_t1000e.build_flags}
|
build_flags = ${nrf52840_t1000e.build_flags}
|
||||||
-Ivariants/t1000-e
|
-Ivariants/t1000-e
|
||||||
-DT1000_E
|
-DT1000_E
|
||||||
|
-D PIN_USER_BTN=6
|
||||||
-D RADIO_CLASS=CustomLR1110
|
-D RADIO_CLASS=CustomLR1110
|
||||||
-D WRAPPER_CLASS=CustomLR1110Wrapper
|
-D WRAPPER_CLASS=CustomLR1110Wrapper
|
||||||
build_src_filter = ${nrf52840_t1000e.build_src_filter}
|
build_src_filter = ${nrf52840_t1000e.build_src_filter}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#define ED25519_NO_SEED 1
|
#define ED25519_NO_SEED 1
|
||||||
#include <ed_25519.h>
|
#include <ed_25519.h>
|
||||||
|
|
||||||
// For ESP32, we use libsodium for cryptographic operations to reduce stack usage
|
// For weaker ESP32 boards, we use libsodium for cryptographic operations to reduce stack usage
|
||||||
#ifdef USE_ESP32_ENCRYPTION
|
#ifdef USE_ESP32_ENCRYPTION
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -20,9 +20,6 @@ Identity::Identity(const char* pub_hex) {
|
|||||||
|
|
||||||
bool Identity::verify(const uint8_t* sig, const uint8_t* message, int msg_len) const {
|
bool Identity::verify(const uint8_t* sig, const uint8_t* message, int msg_len) const {
|
||||||
#ifdef USE_ESP32_ENCRYPTION
|
#ifdef USE_ESP32_ENCRYPTION
|
||||||
// Using libsodium for verification on ESP32 to reduce stack usage
|
|
||||||
// This function performs signature verification with much lower stack requirements
|
|
||||||
// than the default implementation
|
|
||||||
return crypto_sign_ed25519_verify_detached(sig, message, msg_len, pub_key) == 0;
|
return crypto_sign_ed25519_verify_detached(sig, message, msg_len, pub_key) == 0;
|
||||||
#else
|
#else
|
||||||
return ed25519_verify(sig, message, msg_len, pub_key);
|
return ed25519_verify(sig, message, msg_len, pub_key);
|
||||||
@@ -108,7 +105,6 @@ void LocalIdentity::readFrom(const uint8_t* src, size_t len) {
|
|||||||
#ifdef USE_ESP32_ENCRYPTION
|
#ifdef USE_ESP32_ENCRYPTION
|
||||||
// In libsodium, the private key already contains the public key in its last 32 bytes
|
// In libsodium, the private key already contains the public key in its last 32 bytes
|
||||||
// We can just extract it directly, avoiding the expensive derivation calculation
|
// We can just extract it directly, avoiding the expensive derivation calculation
|
||||||
// This significantly reduces stack usage on ESP32
|
|
||||||
memcpy(pub_key, prv_key + 32, 32);
|
memcpy(pub_key, prv_key + 32, 32);
|
||||||
#else
|
#else
|
||||||
// now need to re-calculate the pub_key
|
// now need to re-calculate the pub_key
|
||||||
@@ -119,8 +115,6 @@ void LocalIdentity::readFrom(const uint8_t* src, size_t len) {
|
|||||||
|
|
||||||
void LocalIdentity::sign(uint8_t* sig, const uint8_t* message, int msg_len) const {
|
void LocalIdentity::sign(uint8_t* sig, const uint8_t* message, int msg_len) const {
|
||||||
#ifdef USE_ESP32_ENCRYPTION
|
#ifdef USE_ESP32_ENCRYPTION
|
||||||
// Use libsodium for signing on ESP32 to reduce stack usage
|
|
||||||
// The libsodium implementation uses less stack space than the default ed25519 implementation
|
|
||||||
crypto_sign_ed25519_detached(sig, NULL, message, msg_len, prv_key);
|
crypto_sign_ed25519_detached(sig, NULL, message, msg_len, prv_key);
|
||||||
#else
|
#else
|
||||||
ed25519_sign(sig, message, msg_len, pub_key, prv_key);
|
ed25519_sign(sig, message, msg_len, pub_key, prv_key);
|
||||||
@@ -130,13 +124,11 @@ void LocalIdentity::sign(uint8_t* sig, const uint8_t* message, int msg_len) cons
|
|||||||
void LocalIdentity::calcSharedSecret(uint8_t* secret, const uint8_t* other_pub_key) {
|
void LocalIdentity::calcSharedSecret(uint8_t* secret, const uint8_t* other_pub_key) {
|
||||||
#ifdef USE_ESP32_ENCRYPTION
|
#ifdef USE_ESP32_ENCRYPTION
|
||||||
// NOTE: To calculate a shared secret with Ed25519 keys and libsodium, we need to:
|
// NOTE: To calculate a shared secret with Ed25519 keys and libsodium, we need to:
|
||||||
// 1. Convert the Ed25519 keys to Curve25519 (X25519) format
|
// Convert the Ed25519 keys to Curve25519 (X25519) format
|
||||||
// 2. Perform the key exchange using the converted keys
|
// Perform the key exchange using the converted keys
|
||||||
//
|
//
|
||||||
// The default implementation handles this conversion internally,
|
// The default implementation handles this conversion internally,
|
||||||
// but with libsodium we need to do these steps explicitly.
|
// but with libsodium we need to do these steps explicitly.
|
||||||
// This approach uses less stack space compared to the original implementation.
|
|
||||||
|
|
||||||
unsigned char x25519_pk[crypto_scalarmult_curve25519_BYTES];
|
unsigned char x25519_pk[crypto_scalarmult_curve25519_BYTES];
|
||||||
unsigned char x25519_sk[crypto_scalarmult_curve25519_BYTES];
|
unsigned char x25519_sk[crypto_scalarmult_curve25519_BYTES];
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
|
|||||||
// len can be > original length, but 'text' will be padded with zeroes
|
// len can be > original length, but 'text' will be padded with zeroes
|
||||||
data[len] = 0; // need to make a C string again, with null terminator
|
data[len] = 0; // need to make a C string again, with null terminator
|
||||||
|
|
||||||
//if ( ! alreadyReceived timestamp ) {
|
|
||||||
if (flags == TXT_TYPE_PLAIN) {
|
if (flags == TXT_TYPE_PLAIN) {
|
||||||
onMessageRecv(from, packet->isRouteFlood() ? packet->path_len : 0xFF, timestamp, (const char *) &data[5]); // let UI know
|
onMessageRecv(from, packet->isRouteFlood() ? packet->path_len : 0xFF, timestamp, (const char *) &data[5]); // let UI know
|
||||||
|
|
||||||
@@ -131,7 +130,7 @@ void BaseChatMesh::onPeerDataRecv(mesh::Packet* packet, uint8_t type, int sender
|
|||||||
// NOTE: no ack expected for CLI_DATA replies
|
// NOTE: no ack expected for CLI_DATA replies
|
||||||
|
|
||||||
if (packet->isRouteFlood()) {
|
if (packet->isRouteFlood()) {
|
||||||
// let this sender know path TO here, so they can use sendDirect(), and ALSO encode the ACK
|
// let this sender know path TO here, so they can use sendDirect() (NOTE: no ACK as extra)
|
||||||
mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, 0, NULL, 0);
|
mesh::Packet* path = createPathReturn(from.id, secret, packet->path, packet->path_len, 0, NULL, 0);
|
||||||
if (path) sendFlood(path);
|
if (path) sendFlood(path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,89 @@ static uint32_t _atoi(const char* sp) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CommonCLI::loadPrefs(FILESYSTEM* fs) {
|
||||||
|
if (fs->exists("/node_prefs")) {
|
||||||
|
File file = fs->open("/node_prefs");
|
||||||
|
if (file) {
|
||||||
|
uint8_t pad[8];
|
||||||
|
|
||||||
|
file.read((uint8_t *) &_prefs->airtime_factor, sizeof(_prefs->airtime_factor)); // 0
|
||||||
|
file.read((uint8_t *) &_prefs->node_name, sizeof(_prefs->node_name)); // 4
|
||||||
|
file.read(pad, 4); // 36
|
||||||
|
file.read((uint8_t *) &_prefs->node_lat, sizeof(_prefs->node_lat)); // 40
|
||||||
|
file.read((uint8_t *) &_prefs->node_lon, sizeof(_prefs->node_lon)); // 48
|
||||||
|
file.read((uint8_t *) &_prefs->password[0], sizeof(_prefs->password)); // 56
|
||||||
|
file.read((uint8_t *) &_prefs->freq, sizeof(_prefs->freq)); // 72
|
||||||
|
file.read((uint8_t *) &_prefs->tx_power_dbm, sizeof(_prefs->tx_power_dbm)); // 76
|
||||||
|
file.read((uint8_t *) &_prefs->disable_fwd, sizeof(_prefs->disable_fwd)); // 77
|
||||||
|
file.read((uint8_t *) &_prefs->advert_interval, sizeof(_prefs->advert_interval)); // 78
|
||||||
|
file.read((uint8_t *) &_prefs->unused, sizeof(_prefs->unused)); // 79
|
||||||
|
file.read((uint8_t *) &_prefs->rx_delay_base, sizeof(_prefs->rx_delay_base)); // 80
|
||||||
|
file.read((uint8_t *) &_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84
|
||||||
|
file.read((uint8_t *) &_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88
|
||||||
|
file.read((uint8_t *) &_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104
|
||||||
|
file.read(pad, 4); // 108
|
||||||
|
file.read((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112
|
||||||
|
file.read((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113
|
||||||
|
file.read((uint8_t *) &_prefs->reserved1, sizeof(_prefs->reserved1)); // 114
|
||||||
|
file.read((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115
|
||||||
|
file.read((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116
|
||||||
|
file.read(pad, 4); // 120
|
||||||
|
|
||||||
|
// sanitise bad pref values
|
||||||
|
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
|
||||||
|
_prefs->tx_delay_factor = constrain(_prefs->tx_delay_factor, 0, 2.0f);
|
||||||
|
_prefs->direct_tx_delay_factor = constrain(_prefs->direct_tx_delay_factor, 0, 2.0f);
|
||||||
|
_prefs->airtime_factor = constrain(_prefs->airtime_factor, 0, 9.0f);
|
||||||
|
_prefs->freq = constrain(_prefs->freq, 400.0f, 2500.0f);
|
||||||
|
_prefs->bw = constrain(_prefs->bw, 62.5f, 500.0f);
|
||||||
|
_prefs->sf = constrain(_prefs->sf, 7, 12);
|
||||||
|
_prefs->cr = constrain(_prefs->cr, 5, 8);
|
||||||
|
_prefs->tx_power_dbm = constrain(_prefs->tx_power_dbm, 1, 30);
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommonCLI::savePrefs(FILESYSTEM* fs) {
|
||||||
|
#if defined(NRF52_PLATFORM)
|
||||||
|
File file = fs->open("/node_prefs", FILE_O_WRITE);
|
||||||
|
if (file) { file.seek(0); file.truncate(); }
|
||||||
|
#else
|
||||||
|
File file = fs->open("/node_prefs", "w", true);
|
||||||
|
#endif
|
||||||
|
if (file) {
|
||||||
|
uint8_t pad[8];
|
||||||
|
memset(pad, 0, sizeof(pad));
|
||||||
|
|
||||||
|
file.write((uint8_t *) &_prefs->airtime_factor, sizeof(_prefs->airtime_factor)); // 0
|
||||||
|
file.write((uint8_t *) &_prefs->node_name, sizeof(_prefs->node_name)); // 4
|
||||||
|
file.write(pad, 4); // 36
|
||||||
|
file.write((uint8_t *) &_prefs->node_lat, sizeof(_prefs->node_lat)); // 40
|
||||||
|
file.write((uint8_t *) &_prefs->node_lon, sizeof(_prefs->node_lon)); // 48
|
||||||
|
file.write((uint8_t *) &_prefs->password[0], sizeof(_prefs->password)); // 56
|
||||||
|
file.write((uint8_t *) &_prefs->freq, sizeof(_prefs->freq)); // 72
|
||||||
|
file.write((uint8_t *) &_prefs->tx_power_dbm, sizeof(_prefs->tx_power_dbm)); // 76
|
||||||
|
file.write((uint8_t *) &_prefs->disable_fwd, sizeof(_prefs->disable_fwd)); // 77
|
||||||
|
file.write((uint8_t *) &_prefs->advert_interval, sizeof(_prefs->advert_interval)); // 78
|
||||||
|
file.write((uint8_t *) &_prefs->unused, sizeof(_prefs->unused)); // 79
|
||||||
|
file.write((uint8_t *) &_prefs->rx_delay_base, sizeof(_prefs->rx_delay_base)); // 80
|
||||||
|
file.write((uint8_t *) &_prefs->tx_delay_factor, sizeof(_prefs->tx_delay_factor)); // 84
|
||||||
|
file.write((uint8_t *) &_prefs->guest_password[0], sizeof(_prefs->guest_password)); // 88
|
||||||
|
file.write((uint8_t *) &_prefs->direct_tx_delay_factor, sizeof(_prefs->direct_tx_delay_factor)); // 104
|
||||||
|
file.write(pad, 4); // 108
|
||||||
|
file.write((uint8_t *) &_prefs->sf, sizeof(_prefs->sf)); // 112
|
||||||
|
file.write((uint8_t *) &_prefs->cr, sizeof(_prefs->cr)); // 113
|
||||||
|
file.write((uint8_t *) &_prefs->reserved1, sizeof(_prefs->reserved1)); // 114
|
||||||
|
file.write((uint8_t *) &_prefs->reserved2, sizeof(_prefs->reserved2)); // 115
|
||||||
|
file.write((uint8_t *) &_prefs->bw, sizeof(_prefs->bw)); // 116
|
||||||
|
file.write(pad, 4); // 120
|
||||||
|
|
||||||
|
file.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#define MIN_LOCAL_ADVERT_INTERVAL 60
|
#define MIN_LOCAL_ADVERT_INTERVAL 60
|
||||||
|
|
||||||
void CommonCLI::checkAdvertInterval() {
|
void CommonCLI::checkAdvertInterval() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Mesh.h"
|
#include "Mesh.h"
|
||||||
|
#include <helpers/IdentityStore.h>
|
||||||
|
|
||||||
struct NodePrefs { // persisted to file
|
struct NodePrefs { // persisted to file
|
||||||
float airtime_factor;
|
float airtime_factor;
|
||||||
@@ -54,5 +55,7 @@ public:
|
|||||||
CommonCLI(mesh::MainBoard& board, mesh::Mesh* mesh, NodePrefs* prefs, CommonCLICallbacks* callbacks)
|
CommonCLI(mesh::MainBoard& board, mesh::Mesh* mesh, NodePrefs* prefs, CommonCLICallbacks* callbacks)
|
||||||
: _board(&board), _mesh(mesh), _prefs(prefs), _callbacks(callbacks) { }
|
: _board(&board), _mesh(mesh), _prefs(prefs), _callbacks(callbacks) { }
|
||||||
|
|
||||||
|
void loadPrefs(FILESYSTEM* _fs);
|
||||||
|
void savePrefs(FILESYSTEM* _fs);
|
||||||
void handleCommand(uint32_t sender_timestamp, const char* command, char* reply);
|
void handleCommand(uint32_t sender_timestamp, const char* command, char* reply);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#define PIN_ADC_CTRL_ACTIVE LOW
|
#define PIN_ADC_CTRL_ACTIVE LOW
|
||||||
#define PIN_ADC_CTRL_INACTIVE HIGH
|
#define PIN_ADC_CTRL_INACTIVE HIGH
|
||||||
#define PIN_LED_BUILTIN 35
|
#define PIN_LED_BUILTIN 35
|
||||||
|
#define PIN_VEXT_EN 36
|
||||||
|
|
||||||
#include "ESP32Board.h"
|
#include "ESP32Board.h"
|
||||||
|
|
||||||
@@ -28,6 +29,7 @@ public:
|
|||||||
ESP32Board::begin();
|
ESP32Board::begin();
|
||||||
|
|
||||||
pinMode(PIN_ADC_CTRL, OUTPUT);
|
pinMode(PIN_ADC_CTRL, OUTPUT);
|
||||||
|
//pinMode(PIN_VEXT_EN, OUTPUT);
|
||||||
|
|
||||||
esp_reset_reason_t reason = esp_reset_reason();
|
esp_reset_reason_t reason = esp_reset_reason();
|
||||||
if (reason == ESP_RST_DEEPSLEEP) {
|
if (reason == ESP_RST_DEEPSLEEP) {
|
||||||
|
|||||||
27
src/helpers/ui/DisplayDriver.h
Normal file
27
src/helpers/ui/DisplayDriver.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class DisplayDriver {
|
||||||
|
int _w, _h;
|
||||||
|
protected:
|
||||||
|
DisplayDriver(int w, int h) { _w = w; _h = h; }
|
||||||
|
public:
|
||||||
|
enum Color { DARK, LIGHT };
|
||||||
|
|
||||||
|
int width() const { return _w; }
|
||||||
|
int height() const { return _h; }
|
||||||
|
|
||||||
|
virtual bool isOn() = 0;
|
||||||
|
virtual void turnOn() = 0;
|
||||||
|
virtual void turnOff() = 0;
|
||||||
|
virtual void startFrame(Color bkg = DARK) = 0;
|
||||||
|
virtual void setTextSize(int sz) = 0;
|
||||||
|
virtual void setColor(Color c) = 0;
|
||||||
|
virtual void setCursor(int x, int y) = 0;
|
||||||
|
virtual void print(const char* str) = 0;
|
||||||
|
virtual void fillRect(int x, int y, int w, int h) = 0;
|
||||||
|
virtual void drawRect(int x, int y, int w, int h) = 0;
|
||||||
|
virtual void drawXbm(int x, int y, const uint8_t* bits, int w, int h) = 0;
|
||||||
|
virtual void endFrame() = 0;
|
||||||
|
};
|
||||||
56
src/helpers/ui/SSD1306Display.cpp
Normal file
56
src/helpers/ui/SSD1306Display.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include "SSD1306Display.h"
|
||||||
|
|
||||||
|
bool SSD1306Display::begin() {
|
||||||
|
return display.begin(SSD1306_SWITCHCAPVCC, DISPLAY_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::turnOn() {
|
||||||
|
display.ssd1306_command(SSD1306_DISPLAYON);
|
||||||
|
_isOn = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::turnOff() {
|
||||||
|
display.ssd1306_command(SSD1306_DISPLAYOFF);
|
||||||
|
_isOn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::startFrame(Color bkg) {
|
||||||
|
display.clearDisplay(); // TODO: apply 'bkg'
|
||||||
|
_color = SSD1306_WHITE;
|
||||||
|
display.setTextColor(_color);
|
||||||
|
display.setTextSize(1);
|
||||||
|
display.cp437(true); // Use full 256 char 'Code Page 437' font
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::setTextSize(int sz) {
|
||||||
|
display.setTextSize(sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::setColor(Color c) {
|
||||||
|
_color = (c == LIGHT) ? SSD1306_WHITE : SSD1306_BLACK;
|
||||||
|
display.setTextColor(_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::setCursor(int x, int y) {
|
||||||
|
display.setCursor(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::print(const char* str) {
|
||||||
|
display.print(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::fillRect(int x, int y, int w, int h) {
|
||||||
|
display.fillRect(x, y, w, h, _color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::drawRect(int x, int y, int w, int h) {
|
||||||
|
display.drawRect(x, y, w, h, _color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
|
||||||
|
display.drawBitmap(x, y, bits, w, h, SSD1306_WHITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SSD1306Display::endFrame() {
|
||||||
|
display.display();
|
||||||
|
}
|
||||||
37
src/helpers/ui/SSD1306Display.h
Normal file
37
src/helpers/ui/SSD1306Display.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "DisplayDriver.h"
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <Adafruit_GFX.h>
|
||||||
|
#include <Adafruit_SSD1306.h>
|
||||||
|
|
||||||
|
#ifndef PIN_OLED_RESET
|
||||||
|
#define PIN_OLED_RESET 21 // Reset pin # (or -1 if sharing Arduino reset pin)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef DISPLAY_ADDRESS
|
||||||
|
#define DISPLAY_ADDRESS 0x3C
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SSD1306Display : public DisplayDriver {
|
||||||
|
Adafruit_SSD1306 display;
|
||||||
|
bool _isOn;
|
||||||
|
uint8_t _color;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SSD1306Display() : DisplayDriver(128, 64), display(128, 64, &Wire, PIN_OLED_RESET) { _isOn = false; }
|
||||||
|
bool begin();
|
||||||
|
|
||||||
|
bool isOn() override { return _isOn; }
|
||||||
|
void turnOn() override;
|
||||||
|
void turnOff() override;
|
||||||
|
void startFrame(Color bkg = DARK) override;
|
||||||
|
void setTextSize(int sz) override;
|
||||||
|
void setColor(Color c) override;
|
||||||
|
void setCursor(int x, int y) override;
|
||||||
|
void print(const char* str) override;
|
||||||
|
void fillRect(int x, int y, int w, int h) override;
|
||||||
|
void drawRect(int x, int y, int w, int h) override;
|
||||||
|
void drawXbm(int x, int y, const uint8_t* bits, int w, int h) override;
|
||||||
|
void endFrame() override;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user