Merge branch 'dev' into trace

# Conflicts:
#	examples/companion_radio/main.cpp
#	src/Dispatcher.cpp
This commit is contained in:
Scott Powell 2025-03-15 15:12:50 +11:00
commit 9aa2edf9ba
37 changed files with 1558 additions and 390 deletions

View file

@ -4,6 +4,10 @@
#define AUTO_OFF_MILLIS 15000 // 15 seconds
#ifndef USER_BTN_PRESSED
#define USER_BTN_PRESSED LOW
#endif
// '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,
@ -22,13 +26,21 @@ static const uint8_t meshcore_logo [] PROGMEM = {
};
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();
if (_display != NULL) {
_display->turnOn();
}
}
void UITask::msgRead(int msgcount) {
_msgcount = msgcount;
if (msgcount == 0) {
clearMsgPreview();
}
}
void UITask::clearMsgPreview() {
@ -36,7 +48,9 @@ void UITask::clearMsgPreview() {
_msg[0] = 0;
}
void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char* text) {
void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) {
_msgcount = msgcount;
if (path_len == 0xFF) {
sprintf(_origin, "(F) %s", from_name);
} else {
@ -44,11 +58,15 @@ void UITask::showMsgPreview(uint8_t path_len, const char* from_name, const char*
}
StrHelper::strncpy(_msg, text, sizeof(_msg));
if (!_display->isOn()) _display->turnOn();
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
if (_display != NULL) {
if (!_display->isOn()) _display->turnOn();
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
}
}
void UITask::renderCurrScreen() {
if (_display == NULL) return; // assert() ??
char tmp[80];
if (_origin[0] && _msg[0]) {
// render message preview
@ -61,9 +79,10 @@ void UITask::renderCurrScreen() {
_display->setCursor(0, 24);
_display->print(_msg);
//_display->setCursor(100, 9); TODO
//_display->setTextSize(2);
//_display->printf("%d", msgs);
_display->setCursor(100, 9);
_display->setTextSize(2);
sprintf(tmp, "%d", _msgcount);
_display->print(tmp);
} else {
// render 'home' screen
_display->drawXbm(0, 0, meshcore_logo, 128, 13);
@ -87,24 +106,72 @@ void UITask::renderCurrScreen() {
}
}
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
void UITask::userLedHandler() {
#ifdef PIN_STATUS_LED
static int state = 0;
static int next_change = 0;
int cur_time = millis();
if (cur_time > next_change) {
if (state == 0) {
state = 1; // led on, short = unread msg
if (_msgcount > 0) {
next_change = cur_time + 500;
} else {
next_change = cur_time + 2000;
}
} else {
state = 0;
if (_board->getBattMilliVolts() > 3800) {
next_change = cur_time + 2000;
} else {
next_change = cur_time + 4000; // 4s blank if bat level low
}
_prevBtnState = btnState;
}
_next_read = millis() + 100; // 10 reads per second
digitalWrite(PIN_STATUS_LED, state);
}
#endif
}
if (_display->isOn()) {
void UITask::buttonHandler() {
#ifdef PIN_USER_BTN
static int prev_btn_state = !USER_BTN_PRESSED;
static unsigned long btn_state_change_time = 0;
static unsigned long next_read = 0;
int cur_time = millis();
if (cur_time >= next_read) {
int btn_state = digitalRead(PIN_USER_BTN);
if (btn_state != prev_btn_state) {
if (btn_state == USER_BTN_PRESSED) { // pressed?
if (_display != NULL) {
if (_display->isOn()) {
clearMsgPreview();
} else {
_display->turnOn();
}
_auto_off = cur_time + AUTO_OFF_MILLIS; // extend auto-off timer
}
} else { // unpressed ? check pressed time ...
if ((cur_time - btn_state_change_time) > 5000) {
#ifdef PIN_STATUS_LED
digitalWrite(PIN_STATUS_LED, LOW);
delay(10);
#endif
_board->powerOff();
}
}
btn_state_change_time = millis();
prev_btn_state = btn_state;
}
next_read = millis() + 100; // 10 reads per second
}
#endif
}
void UITask::loop() {
buttonHandler();
userLedHandler();
if (_display != NULL && _display->isOn()) {
if (millis() >= _next_refresh) {
_display->startFrame();
renderCurrScreen();

View file

@ -1,25 +1,35 @@
#pragma once
#include <MeshCore.h>
#include <helpers/ui/DisplayDriver.h>
class UITask {
DisplayDriver* _display;
unsigned long _next_read, _next_refresh, _auto_off;
int _prevBtnState;
mesh::MainBoard* _board;
unsigned long _next_refresh, _auto_off;
bool _connected;
uint32_t _pin_code;
const char* _node_name;
const char* _build_date;
char _origin[62];
char _msg[80];
int _msgcount;
void renderCurrScreen();
void buttonHandler();
void userLedHandler();
public:
UITask(DisplayDriver& display) : _display(&display) { _next_read = _next_refresh = 0; _connected = false; }
UITask(mesh::MainBoard* board, DisplayDriver* display) : _board(board), _display(display){
_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 msgRead(int msgcount);
void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount);
void loop();
};
};

View file

@ -46,6 +46,10 @@
#define OFFLINE_QUEUE_SIZE 16
#endif
#ifndef BLE_NAME_PREFIX
#define BLE_NAME_PREFIX "MeshCore-"
#endif
#include <helpers/BaseChatMesh.h>
#define SEND_TIMEOUT_BASE_MILLIS 500
@ -92,17 +96,25 @@
#include <helpers/nrf52/TechoBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
static TechoBoard board;
#elif defined(FAKETEC)
#include <helpers/nrf52/faketecBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
static faketecBoard board;
#else
#error "need to provide a 'board' object"
#endif
#ifdef DISPLAY_CLASS
#include "UITask.h"
#include <helpers/ui/SSD1306Display.h>
static DISPLAY_CLASS display;
static UITask ui_task(&board, &display);
#define HAS_UI
#elif defined(HAS_UI)
#include "UITask.h"
static UITask ui_task(display);
static UITask ui_task(&board, NULL);
#endif
// Believe it or not, this std C function is busted on some platforms!
@ -120,11 +132,11 @@ static uint32_t _atoi(const char* sp) {
#define FIRMWARE_VER_CODE 3
#ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "3 Mar 2025"
#define FIRMWARE_BUILD_DATE "13 Mar 2025"
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.0.0"
#define FIRMWARE_VERSION "v1.3.0"
#endif
#define CMD_APP_START 1
@ -157,6 +169,8 @@ static uint32_t _atoi(const char* sp) {
#define CMD_HAS_CONNECTION 28
#define CMD_LOGOUT 29 // 'Disconnect'
#define CMD_GET_CONTACT_BY_KEY 30
#define CMD_GET_CHANNEL 31
#define CMD_SET_CHANNEL 32
#define RESP_CODE_OK 0
#define RESP_CODE_ERR 1
@ -176,6 +190,7 @@ static uint32_t _atoi(const char* sp) {
#define RESP_CODE_DISABLED 15
#define RESP_CODE_CONTACT_MSG_RECV_V3 16 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3)
#define RESP_CODE_CHANNEL_MSG_RECV_V3 17 // a reply to CMD_SYNC_NEXT_MESSAGE (ver >= 3)
#define RESP_CODE_CHANNEL_INFO 18 // a reply to CMD_GET_CHANNEL
// these are _pushed_ to client app at any time
#define PUSH_CODE_ADVERT 0x80
@ -186,6 +201,7 @@ static uint32_t _atoi(const char* sp) {
#define PUSH_CODE_LOGIN_SUCCESS 0x85
#define PUSH_CODE_LOGIN_FAIL 0x86
#define PUSH_CODE_STATUS_RESPONSE 0x87
#define PUSH_CODE_LOG_RX_DATA 0x88
/* -------------------------------------------------------------------------------------- */
@ -212,7 +228,6 @@ class MyMesh : public BaseChatMesh {
NodePrefs _prefs;
uint32_t pending_login;
uint32_t pending_status;
mesh::GroupChannel* _public;
BaseSerialInterface* _serial;
ContactsIterator _iter;
uint32_t _iter_filter_since;
@ -314,6 +329,58 @@ class MyMesh : public BaseChatMesh {
}
}
void loadChannels() {
if (_fs->exists("/channels2")) {
File file = _fs->open("/channels2");
if (file) {
bool full = false;
uint8_t channel_idx = 0;
while (!full) {
ChannelDetails ch;
uint8_t unused[4];
bool success = (file.read(unused, 4) == 4);
success = success && (file.read((uint8_t *) ch.name, 32) == 32);
success = success && (file.read((uint8_t *) ch.channel.secret, 32) == 32);
if (!success) break; // EOF
if (setChannel(channel_idx, ch)) {
channel_idx++;
} else {
full = true;
}
}
file.close();
}
}
}
void saveChannels() {
#if defined(NRF52_PLATFORM)
File file = _fs->open("/channels2", FILE_O_WRITE);
if (file) { file.seek(0); file.truncate(); }
#else
File file = _fs->open("/channels2", "w", true);
#endif
if (file) {
uint8_t channel_idx = 0;
ChannelDetails ch;
uint8_t unused[4];
memset(unused, 0, 4);
while (getChannel(channel_idx, ch)) {
bool success = (file.write(unused, 4) == 4);
success = success && (file.write((uint8_t *) ch.name, 32) == 32);
success = success && (file.write((uint8_t *) ch.channel.secret, 32) == 32);
if (!success) break; // write failed
channel_idx++;
}
file.close();
}
}
int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override {
char path[64];
char fname[18];
@ -443,6 +510,18 @@ protected:
return (int) ((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time);
}
void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override {
if (_serial->isConnected()) {
int i = 0;
out_frame[i++] = PUSH_CODE_LOG_RX_DATA;
out_frame[i++] = (int8_t)(snr * 4);
out_frame[i++] = (int8_t)(rssi);
memcpy(&out_frame[i], raw, len); i += len;
_serial->writeFrame(out_frame, i);
}
}
void onDiscoveredContact(ContactInfo& contact, bool is_new) override {
if (_serial->isConnected()) {
out_frame[0] = PUSH_CODE_ADVERT;
@ -512,8 +591,8 @@ protected:
} else {
soundBuzzer();
}
#ifdef DISPLAY_CLASS
ui_task.showMsgPreview(path_len, from.name, text);
#ifdef HAS_UI
ui_task.newMsg(path_len, from.name, text, offline_queue_len);
#endif
}
@ -544,8 +623,9 @@ protected:
out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV;
}
out_frame[i++] = 0; // FUTURE: channel_idx (will just be 'public' for now)
out_frame[i++] = findChannelIdx(channel);
uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF;
out_frame[i++] = TXT_TYPE_PLAIN;
memcpy(&out_frame[i], &timestamp, 4); i += 4;
int tlen = strlen(text); // TODO: UTF-8 ??
@ -562,8 +642,8 @@ protected:
} else {
soundBuzzer();
}
#ifdef DISPLAY_CLASS
ui_task.showMsgPreview(path_len, "Public", text);
#ifdef HAS_UI
ui_task.newMsg(path_len, "Public", text, offline_queue_len);
#endif
}
@ -726,7 +806,8 @@ public:
_fs->mkdir("/bl");
loadContacts();
_public = addChannel(PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
loadChannels();
_phy->setFrequency(_prefs.freq);
_phy->setSpreadingFactor(_prefs.sf);
@ -782,7 +863,9 @@ public:
int i = 0;
out_frame[i++] = RESP_CODE_DEVICE_INFO;
out_frame[i++] = FIRMWARE_VER_CODE;
memset(&out_frame[i], 0, 6); i += 6; // reserved
out_frame[i++] = MAX_CONTACTS / 2; // v3+
out_frame[i++] = MAX_GROUP_CHANNELS; // v3+
memset(&out_frame[i], 0, 4); i += 4; // reserved
memset(&out_frame[i], 0, 12);
strcpy((char *) &out_frame[i], FIRMWARE_BUILD_DATE); i += 12;
StrHelper::strzcpy((char *) &out_frame[i], board.getManufacturerName(), 40); i += 40;
@ -862,12 +945,14 @@ public:
} else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel msg
int i = 1;
uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN
uint8_t channel_idx = cmd_frame[i++]; // reserved future
uint8_t channel_idx = cmd_frame[i++];
uint32_t msg_timestamp;
memcpy(&msg_timestamp, &cmd_frame[i], 4); i += 4;
const char *text = (char *) &cmd_frame[i];
if (txt_type == TXT_TYPE_PLAIN && sendGroupMessage(msg_timestamp, *_public, _prefs.node_name, text, len - i)) { // hard-coded to 'public' channel for now
ChannelDetails channel;
bool success = getChannel(channel_idx, channel);
if (success && txt_type == TXT_TYPE_PLAIN && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) {
writeOKFrame();
} else {
writeErrFrame();
@ -1032,6 +1117,9 @@ public:
int out_len;
if ((out_len = getFromOfflineQueue(out_frame)) > 0) {
_serial->writeFrame(out_frame, out_len);
#ifdef HAS_UI
ui_task.msgRead(offline_queue_len);
#endif
} else {
out_frame[0] = RESP_CODE_NO_MORE_MESSAGES;
_serial->writeFrame(out_frame, 1);
@ -1179,6 +1267,33 @@ public:
uint8_t* pub_key = &cmd_frame[1];
stopConnection(pub_key);
writeOKFrame();
} else if (cmd_frame[0] == CMD_GET_CHANNEL && len >= 2) {
uint8_t channel_idx = cmd_frame[1];
ChannelDetails channel;
if (getChannel(channel_idx, channel)) {
int i = 0;
out_frame[i++] = RESP_CODE_CHANNEL_INFO;
out_frame[i++] = channel_idx;
strcpy((char *)&out_frame[i], channel.name); i += 32;
memcpy(&out_frame[i], channel.channel.secret, 16); i += 16; // NOTE: only 128-bit supported
_serial->writeFrame(out_frame, i);
} else {
writeErrFrame();
}
} else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2+32+32) {
writeErrFrame(); // not supported (yet)
} else if (cmd_frame[0] == CMD_SET_CHANNEL && len >= 2+32+16) {
uint8_t channel_idx = cmd_frame[1];
ChannelDetails channel;
StrHelper::strncpy(channel.name, (char *) &cmd_frame[2], 32);
memset(channel.channel.secret, 0, sizeof(channel.channel.secret));
memcpy(channel.channel.secret, &cmd_frame[2+32], 16); // NOTE: only 128-bit supported
if (setChannel(channel_idx, channel)) {
saveChannels();
writeOKFrame();
} else {
writeErrFrame();
}
} else {
writeErrFrame();
MESH_DEBUG_PRINTLN("ERROR: unknown command: %02X", cmd_frame[0]);
@ -1212,7 +1327,7 @@ public:
checkConnections();
}
#ifdef DISPLAY_CLASS
#ifdef HAS_UI
ui_task.setHasConnection(_serial->isConnected());
ui_task.loop();
#endif
@ -1274,10 +1389,6 @@ void setup() {
float tcxo = 1.6f;
#endif
#ifdef DISPLAY_CLASS
display.begin();
#endif
#if defined(NRF52_PLATFORM)
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
@ -1285,21 +1396,30 @@ void setup() {
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
#if defined(FAKETEC)
if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) {
#define SX126X_DIO3_TCXO_VOLTAGE (0.0f);
tcxo = SX126X_DIO3_TCXO_VOLTAGE;
status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
}
#endif
if (status != RADIOLIB_ERR_NONE) {
Serial.print("ERROR: radio init failed: ");
Serial.println(status);
halt();
}
radio.setCRC(0);
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
fast_rng.begin(radio.random(0x7FFFFFFF));
@ -1310,11 +1430,10 @@ void setup() {
the_mesh.begin(InternalFS, trng);
#ifdef BLE_PIN_CODE
char dev_name[32+10];
sprintf(dev_name, "MeshCore-%s", the_mesh.getNodeName());
char dev_name[32+16];
sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName());
serial_interface.begin(dev_name, the_mesh.getBLEPin());
#else
pinMode(WB_IO2, OUTPUT);
serial_interface.begin(Serial);
#endif
the_mesh.startInterface(serial_interface);
@ -1326,8 +1445,8 @@ void setup() {
WiFi.begin(WIFI_SSID, WIFI_PWD);
serial_interface.begin(TCP_PORT);
#elif defined(BLE_PIN_CODE)
char dev_name[32+10];
sprintf(dev_name, "MeshCore-%s", the_mesh.getNodeName());
char dev_name[32+16];
sprintf(dev_name, "%s%s", BLE_NAME_PREFIX, the_mesh.getNodeName());
serial_interface.begin(dev_name, the_mesh.getBLEPin());
#else
serial_interface.begin(Serial);
@ -1338,6 +1457,9 @@ void setup() {
#endif
#ifdef DISPLAY_CLASS
display.begin();
#endif
#ifdef HAS_UI
ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE, the_mesh.getBLEPin());
#endif
}

View file

@ -22,11 +22,11 @@
/* ------------------------------ Config -------------------------------- */
#ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "3 Mar 2025"
#define FIRMWARE_BUILD_DATE "13 Mar 2025"
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.0.0"
#define FIRMWARE_VERSION "v1.3.0"
#endif
#ifndef LORA_FREQ
@ -76,10 +76,14 @@
#include <helpers/ESP32Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static ESP32Board board;
#elif defined(LILYGO_TLORA)
#elif defined(LILYGO_TLORA)
#include <helpers/LilyGoTLoraBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
static LilyGoTLoraBoard board;
#elif defined(STATION_G2)
#include <helpers/StationG2Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static StationG2Board board;
#elif defined(RAK_4631)
#include <helpers/nrf52/RAK4631Board.h>
#include <helpers/CustomSX1262Wrapper.h>
@ -92,6 +96,10 @@
#include <helpers/nrf52/TechoBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
static TechoBoard board;
#elif defined(FAKETEC)
#include <helpers/nrf52/faketecBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
static faketecBoard board;
#else
#error "need to provide a 'board' object"
#endif
@ -231,7 +239,9 @@ protected:
}
bool allowPacketForward(const mesh::Packet* packet) override {
return !_prefs.disable_fwd;
if (_prefs.disable_fwd) return false;
if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false;
return true;
}
const char* getLogDateTime() override {
@ -242,6 +252,15 @@ protected:
return tmp;
}
void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override {
#if MESH_PACKET_LOGGING
Serial.print(getLogDateTime());
Serial.print(" RAW: ");
mesh::Utils::printHex(Serial, raw, len);
Serial.println();
#endif
}
void logRx(mesh::Packet* pkt, int len, float score) override {
if (_logging) {
File f = openAppend(PACKET_LOG_FILE);
@ -525,6 +544,7 @@ public:
_prefs.cr = LORA_CR;
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_max = 64;
}
CommonCLI* getCLI() { return &_cli; }
@ -664,10 +684,6 @@ void setup() {
float tcxo = 1.6f;
#endif
#ifdef DISPLAY_CLASS
display.begin();
#endif
#if defined(NRF52_PLATFORM)
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
@ -675,6 +691,13 @@ void setup() {
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
#if defined(FAKETEC)
if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) {
#define SX126X_DIO3_TCXO_VOLTAGE (0.0f);
tcxo = SX126X_DIO3_TCXO_VOLTAGE;
status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
}
#endif
if (status != RADIOLIB_ERR_NONE) {
delay(5000);
Serial.print("ERROR: radio init failed: ");
@ -682,15 +705,17 @@ void setup() {
halt();
}
radio.setCRC(0);
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
fast_rng.begin(radio.random(0x7FFFFFFF));
@ -721,6 +746,7 @@ void setup() {
the_mesh.begin(fs);
#ifdef DISPLAY_CLASS
display.begin();
ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE);
#endif

View file

@ -22,11 +22,11 @@
/* ------------------------------ Config -------------------------------- */
#ifndef FIRMWARE_BUILD_DATE
#define FIRMWARE_BUILD_DATE "6 Mar 2025"
#define FIRMWARE_BUILD_DATE "13 Mar 2025"
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "v1.1.0"
#define FIRMWARE_VERSION "v1.3.0"
#endif
#ifndef LORA_FREQ
@ -59,7 +59,7 @@
#define ADMIN_PASSWORD "password"
#endif
#ifndef MAX_CLIENTS
#ifndef MAX_CLIENTS
#define MAX_CLIENTS 32
#endif
@ -84,10 +84,26 @@
#include <helpers/LilyGoTLoraBoard.h>
#include <helpers/CustomSX1276Wrapper.h>
static LilyGoTLoraBoard board;
#elif defined(STATION_G2)
#include <helpers/StationG2Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static StationG2Board board;
#elif defined(RAK_4631)
#include <helpers/nrf52/RAK4631Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static RAK4631Board board;
#elif defined(HELTEC_T114)
#include <helpers/nrf52/T114Board.h>
#include <helpers/CustomSX1262Wrapper.h>
static T114Board board;
#elif defined(LILYGO_TECHO)
#include <helpers/nrf52/TechoBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
static TechoBoard board;
#elif defined(FAKETEC)
#include <helpers/nrf52/faketecBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
static faketecBoard board;
#else
#error "need to provide a 'board' object"
#endif
@ -260,12 +276,21 @@ protected:
return _prefs.airtime_factor;
}
void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override {
#if MESH_PACKET_LOGGING
Serial.print(getLogDateTime());
Serial.print(" RAW: ");
mesh::Utils::printHex(Serial, raw, len);
Serial.println();
#endif
}
int calcRxDelay(float score, uint32_t air_time) const override {
if (_prefs.rx_delay_base <= 0.0f) return 0;
return (int) ((pow(_prefs.rx_delay_base, 0.85f - score) - 1.0) * air_time);
}
const char* getLogDateTime() override {
const char* getLogDateTime() override {
static char tmp[32];
uint32_t now = getRTCClock()->getCurrentTime();
DateTime dt = DateTime(now);
@ -283,7 +308,9 @@ protected:
}
bool allowPacketForward(const mesh::Packet* packet) override {
return !_prefs.disable_fwd;
if (_prefs.disable_fwd) return false;
if (packet->isRouteFlood() && packet->path_len >= _prefs.flood_max) return false;
return true;
}
void onAnonDataRecv(mesh::Packet* packet, uint8_t type, const mesh::Identity& sender, uint8_t* data, size_t len) override {
@ -470,7 +497,7 @@ protected:
} else {
memcpy(&data[5], &forceSince, 4); // make sure there are zeroes in payload (for ack_hash calc below)
}
if (forceSince > 0) {
if (forceSince > 0) {
client->sync_since = forceSince; // force-update the 'sync since'
}
@ -525,7 +552,7 @@ protected:
public:
MyMesh(RADIO_CLASS& phy, mesh::MainBoard& board, RadioLibWrapper& radio, mesh::MillisecondClock& ms, mesh::RNG& rng, mesh::RTCClock& rtc, mesh::MeshTables& tables)
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
: mesh::Mesh(radio, ms, rng, rtc, *new StaticPoolPacketManager(32), tables),
_phy(&phy), _board(&board), _cli(board, this, &_prefs, this)
{
my_radio = &radio;
@ -547,6 +574,7 @@ public:
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.disable_fwd = 1;
_prefs.advert_interval = 1; // default to 2 minutes for NEW installs
_prefs.flood_max = 64;
#ifdef ROOM_PASSWORD
StrHelper::strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password));
#endif
@ -593,7 +621,7 @@ public:
return false;
#endif
}
void sendSelfAdvertisement(int delay_millis) override {
mesh::Packet* pkt = createSelfAdvert();
if (pkt) {
@ -688,7 +716,7 @@ SimpleMeshTables tables;
#ifdef ESP32
ESP32RTCClock fallback_clock;
#else
VolatileRTCClock fallback_clock;
VolatileRTCClock fallback_clock;
#endif
AutoDiscoverRTCClock rtc_clock(fallback_clock);
@ -716,10 +744,6 @@ void setup() {
float tcxo = 1.6f;
#endif
#ifdef DISPLAY_CLASS
display.begin();
#endif
#if defined(NRF52_PLATFORM)
SPI.setPins(P_LORA_MISO, P_LORA_SCLK, P_LORA_MOSI);
SPI.begin();
@ -727,6 +751,13 @@ void setup() {
spi.begin(P_LORA_SCLK, P_LORA_MISO, P_LORA_MOSI);
#endif
int status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
#if defined(FAKETEC)
if (status == RADIOLIB_ERR_SPI_CMD_FAILED || status == RADIOLIB_ERR_SPI_CMD_INVALID) {
#define SX126X_DIO3_TCXO_VOLTAGE (0.0f);
tcxo = SX126X_DIO3_TCXO_VOLTAGE;
status = radio.begin(LORA_FREQ, LORA_BW, LORA_SF, LORA_CR, RADIOLIB_SX126X_SYNC_WORD_PRIVATE, LORA_TX_POWER, 8, tcxo);
}
#endif
if (status != RADIOLIB_ERR_NONE) {
delay(5000);
Serial.print("ERROR: radio init failed: ");
@ -734,15 +765,17 @@ void setup() {
halt();
}
radio.setCRC(0);
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);
#endif
#ifdef SX126X_DIO2_AS_RF_SWITCH
radio.setDio2AsRfSwitch(SX126X_DIO2_AS_RF_SWITCH);
#endif
#ifdef SX126X_RX_BOOSTED_GAIN
radio.setRxBoostedGainMode(SX126X_RX_BOOSTED_GAIN);
#endif
fast_rng.begin(radio.random(0x7FFFFFFF));
@ -772,6 +805,7 @@ void setup() {
the_mesh.begin(fs);
#ifdef DISPLAY_CLASS
display.begin();
ui_task.begin(the_mesh.getNodeName(), FIRMWARE_BUILD_DATE);
#endif
@ -783,7 +817,7 @@ void loop() {
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read();
if (c != '\n') {
if (c != '\n') {
command[len++] = c;
command[len] = 0;
}

View file

@ -78,6 +78,10 @@
#include <helpers/nrf52/T1000eBoard.h>
#include <helpers/CustomLR1110Wrapper.h>
static T1000eBoard board;
#elif defined(FAKETEC)
#include <helpers/nrf52/faketecBoard.h>
#include <helpers/CustomSX1262Wrapper.h>
static faketecBoard board;
#else
#error "need to provide a 'board' object"
#endif
@ -107,7 +111,7 @@ class MyMesh : public BaseChatMesh, ContactVisitor {
FILESYSTEM* _fs;
NodePrefs _prefs;
uint32_t expected_ack_crc;
mesh::GroupChannel* _public;
ChannelDetails* _public;
unsigned long last_msg_sent;
ContactInfo* curr_recipient;
char command[512+10];
@ -341,7 +345,7 @@ public:
}
loadContacts();
_public = addChannel(PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
_public = addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
}
void savePrefs() {
@ -409,7 +413,7 @@ public:
temp[5 + MAX_TEXT_LEN] = 0; // truncate if too long
int len = strlen((char *) &temp[5]);
auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, *_public, temp, 5 + len);
auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, _public->channel, temp, 5 + len);
if (pkt) {
sendFlood(pkt);
Serial.println(" Sent.");
@ -589,7 +593,7 @@ void setup() {
halt();
}
radio.setCRC(0);
radio.setCRC(1);
#ifdef SX126X_CURRENT_LIMIT
radio.setCurrentLimit(SX126X_CURRENT_LIMIT);