Merge pull request #3431 from meshcore-dev/abstract-ui-overhaul

Abstract UI overhaul
This commit is contained in:
ripplebiz
2026-09-20 18:08:29 +10:00
committed by GitHub
10 changed files with 310 additions and 213 deletions
+4 -7
View File
@@ -12,6 +12,7 @@
#endif
#include "NodePrefs.h"
#include "MyMesh.h"
enum class UIEventType {
none,
@@ -22,25 +23,21 @@ enum class UIEventType {
ack
};
class AbstractUITask {
class AbstractUITask : public MyMesh::Listener {
protected:
mesh::MainBoard* _board;
MultiSerialInterface* _interfaceManager;
bool _connected;
AbstractUITask(mesh::MainBoard* board, MultiSerialInterface* interfaceManager) : _board(board), _interfaceManager(interfaceManager) {
_connected = false;
}
public:
void setHasConnection(bool connected) { _connected = connected; }
bool hasConnection() const { return _connected; }
bool hasConnection() const { return _interfaceManager->isConnected(); }
uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); }
bool isBluetoothEnabled() const { return _interfaceManager->isBluetoothEnabled(); }
void enableBluetooth() { _interfaceManager->enableBluetooth(); }
void disableBluetooth() { _interfaceManager->disableBluetooth(); }
virtual void msgRead(int msgcount) = 0;
virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) = 0;
virtual void notify(UIEventType t = UIEventType::none) = 0;
virtual void loop() = 0;
};
+42 -108
View File
@@ -139,10 +139,6 @@
#define ERR_CODE_FILE_IO_ERROR 5
#define ERR_CODE_ILLEGAL_ARG 6
// Copied from simple_repeater (could probably be shared)
#define CTL_TYPE_NODE_DISCOVER_REQ 0x80
#define CTL_TYPE_NODE_DISCOVER_RESP 0x90
#define MAX_SIGN_DATA_LEN (8 * 1024) // 8K
// Auto-add config bitmask
@@ -368,10 +364,6 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE);
_serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE);
}
} else {
#ifdef DISPLAY_CLASS
if (_ui) _ui->notify(UIEventType::newContactMessage);
#endif
}
// add inbound-path to mem cache
@@ -395,6 +387,8 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path
p->path_len = mesh::Packet::copyPath(p->path, path, path_len);
}
if (_listener) _listener->onDiscoveredContact(contact, is_new, path_len, path);
if (!is_new) dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY); // only schedule lazy write for contacts that are in contacts[]
}
@@ -412,53 +406,6 @@ int MyMesh::getRecentlyHeard(AdvertPath dest[], int max_num) {
return max_num;
}
#if defined(DISPLAY_CLASS) && !(defined(UI_NO_DISCOVER_SCREEN) && (UI_NO_DISCOVER_SCREEN + 0 != 0))
int MyMesh::getDiscoveredNodes(DiscoveredNode nodes[], int max_num) {
if (max_num > DISCOVERED_NODES_TABLE_SIZE) max_num = DISCOVERED_NODES_TABLE_SIZE;
if (max_num > disc_nodes_count) max_num = disc_nodes_count;
for (int i = 0; i < max_num; i++) {
nodes[i] = discovered_nodes[i];
}
return max_num;
}
bool MyMesh::requestRepeatersDiscovery() {
uint8_t cmd_bytes[6];
cmd_bytes[0] = CTL_TYPE_NODE_DISCOVER_REQ | 1; // DISCOVER_REQ | prefix only
cmd_bytes[1] = 0xFF; // Repeaters
getRNG()->random(&cmd_bytes[2], 4); // tag
disc_nodes_count = 0;
disc_node_req_tag = *((uint32_t*)&cmd_bytes[2]);
mesh::Packet* req = createControlData(cmd_bytes, sizeof(cmd_bytes));
if (req) {
sendZeroHop(req);
return true;
}
return false;
}
void MyMesh::checkControlDataForPendingDiscovery(uint8_t payload[], size_t p_len) {
if ((p_len < 12)
|| (payload[0] & 0xF0 != CTL_TYPE_NODE_DISCOVER_RESP)
|| (disc_nodes_count >= DISCOVERED_NODES_TABLE_SIZE)
|| (memcmp(&payload[2], &disc_node_req_tag, 4))) {
return;
}
memcpy(&discovered_nodes[disc_nodes_count].pubkey_prefix, &payload[6], 8);
discovered_nodes[disc_nodes_count].type = payload[0] & 0xF;
discovered_nodes[disc_nodes_count].snr_out = ((int8_t)payload[1]) / 4.0;
discovered_nodes[disc_nodes_count].snr_in = _radio->getLastSNR();
ContactInfo* c = lookupContactByPubKey(&payload[6], 8);
if (c != NULL) {
strncpy(discovered_nodes[disc_nodes_count].name, c->name, 32);
} else {
discovered_nodes[disc_nodes_count].name[0] = 0;
}
disc_nodes_count ++;
}
#endif
void MyMesh::onContactPathUpdated(const ContactInfo &contact) {
out_frame[0] = PUSH_CODE_PATH_UPDATED;
memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE);
@@ -468,6 +415,11 @@ void MyMesh::onContactPathUpdated(const ContactInfo &contact) {
}
ContactInfo* MyMesh::processAck(const uint8_t *data) {
if (_listener) {
uint32_t ack_crc;
memcpy(&ack_crc, data, 4);
_listener->onACKRecv(ack_crc);
}
// see if matches any in a table
for (int i = 0; i < EXPECTED_ACK_TABLE_SIZE; i++) {
if (memcmp(data, &expected_ack_table[i].ack, 4) == 0) { // got an ACK from recipient
@@ -520,16 +472,10 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe
_serial->writeFrame(frame, 1);
}
#ifdef DISPLAY_CLASS
// we only want to show text messages on display, not cli data
bool should_display = txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_SIGNED_PLAIN;
if (should_display && _ui) {
_ui->newMsg(path_len, from.name, text, offline_queue_len);
if (!_serial->isConnected()) {
_ui->notify(UIEventType::contactMessage);
}
if (_listener) {
_listener->onMessageRecv(pkt, from, txt_type, sender_timestamp, text);
_listener->onQueueSizeChanged(offline_queue_len);
}
#endif
}
bool MyMesh::filterRecvFloodPacket(mesh::Packet* packet) {
@@ -640,20 +586,16 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe
uint8_t frame[1];
frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle'
_serial->writeFrame(frame, 1);
} else {
#ifdef DISPLAY_CLASS
if (_ui) _ui->notify(UIEventType::channelMessage);
#endif
}
#ifdef DISPLAY_CLASS
// Get the channel name from the channel index
const char *channel_name = "Unknown";
ChannelDetails channel_details;
if (getChannel(channel_idx, channel_details)) {
channel_name = channel_details.name;
if (_listener) {
// Get the channel name from the channel index
ChannelDetails channel_details;
if (!getChannel(channel_idx, channel_details)) {
strcpy(channel_details.name, "Unknown");
}
_listener->onChannelMessageRecv(pkt, channel_details, text);
_listener->onQueueSizeChanged(offline_queue_len);
}
if (_ui) _ui->newMsg(path_len, channel_name, text, offline_queue_len);
#endif
}
void MyMesh::onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *pkt, uint16_t data_type,
@@ -689,6 +631,9 @@ void MyMesh::onChannelDataRecv(const mesh::GroupChannel &channel, mesh::Packet *
frame[0] = PUSH_CODE_MSG_WAITING; // send push 'tickle'
_serial->writeFrame(frame, 1);
}
if (_listener) {
_listener->onChannelDataRecv(pkt, channel, data_type, data, data_len);
}
}
uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data,
@@ -736,6 +681,8 @@ uint8_t MyMesh::onContactRequest(const ContactInfo &contact, uint32_t sender_tim
memcpy(&reply[4], telemetry.getBuffer(), tlen);
return 4 + tlen;
}
} else if (_listener) {
return _listener->onUnhandledRequest(contact, sender_timestamp, data, len, reply);
}
return 0; // unknown
}
@@ -811,6 +758,8 @@ void MyMesh::onContactResponse(const ContactInfo &contact, const uint8_t *data,
memcpy(&out_frame[i], &data[4], len - 4);
i += (len - 4);
_serial->writeFrame(out_frame, i);
} else if (_listener && len > 4) {
_listener->onUnhandledResponse(contact, tag, &data[4], len - 4);
}
}
@@ -850,9 +799,6 @@ void MyMesh::onControlDataRecv(mesh::Packet *packet) {
MESH_DEBUG_PRINTLN("onControlDataRecv(), payload_len too long: %d", packet->payload_len);
return;
}
#if defined(DISPLAY_CLASS) && !(defined(UI_NO_DISCOVER_SCREEN) && (UI_NO_DISCOVER_SCREEN + 0 != 0))
checkControlDataForPendingDiscovery(packet->payload, packet->payload_len);
#endif
int i = 0;
out_frame[i++] = PUSH_CODE_CONTROL_DATA;
out_frame[i++] = (int8_t)(_radio->getLastSNR() * 4);
@@ -866,6 +812,8 @@ void MyMesh::onControlDataRecv(mesh::Packet *packet) {
} else {
MESH_DEBUG_PRINTLN("onControlDataRecv(), data received while app offline");
}
if (_listener) _listener->onControlDataRecv(packet);
}
void MyMesh::onRawDataRecv(mesh::Packet *packet) {
@@ -886,6 +834,9 @@ void MyMesh::onRawDataRecv(mesh::Packet *packet) {
} else {
MESH_DEBUG_PRINTLN("onRawDataRecv(), data received while app offline");
}
if (_listener) {
_listener->onRawDataRecv(packet);
}
}
void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code, uint8_t flags,
@@ -916,6 +867,9 @@ void MyMesh::onTraceRecv(mesh::Packet *packet, uint32_t tag, uint32_t auth_code,
} else {
MESH_DEBUG_PRINTLN("onTraceRecv(), data received while app offline");
}
if (_listener) {
_listener->onTraceRecv(packet, tag, auth_code, flags, path_snrs, path_hashes, path_len);
}
}
uint32_t MyMesh::calcFloodTimeoutMillisFor(uint32_t pkt_airtime_millis) const {
@@ -930,9 +884,9 @@ uint32_t MyMesh::calcDirectTimeoutMillisFor(uint32_t pkt_airtime_millis, uint8_t
void MyMesh::onSendTimeout() {}
MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store, AbstractUITask* ui)
MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store)
: BaseChatMesh(radio, *new ArduinoMillis(), rng, rtc, *new StaticPoolPacketManager(16), tables),
_serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store), _ui(ui), _iter(0) {
_serial(NULL), telemetry(MAX_PACKET_PAYLOAD - 4), _store(&store), _listener(NULL), _iter(0) {
_iter_started = false;
_cli_rescue = false;
cli_command[0] = 0;
@@ -975,7 +929,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
#endif
}
void MyMesh::begin(bool has_display) {
void MyMesh::begin() {
BaseChatMesh::begin();
if (!_store->loadMainIdentity(self_id)) {
@@ -1027,29 +981,12 @@ void MyMesh::begin(bool has_display) {
_prefs.gps_enabled = constrain(_prefs.gps_enabled, 0, 1); // Ensure boolean 0 or 1
_prefs.gps_interval = constrain(_prefs.gps_interval, 0, 86400); // Max 24 hours
#ifdef BLE_PIN_CODE // 123456 by default
if (_prefs.ble_pin == 0) {
#ifdef DISPLAY_CLASS
if (has_display && BLE_PIN_CODE == 123456) {
StdRNG rng;
_active_ble_pin = rng.nextInt(100000, 999999); // random pin each session
} else {
_active_ble_pin = BLE_PIN_CODE; // otherwise static pin
}
#else
_active_ble_pin = BLE_PIN_CODE; // otherwise static pin
#endif
} else {
_active_ble_pin = _prefs.ble_pin;
}
#else
_active_ble_pin = 0;
#endif
_active_ble_pin = _prefs.ble_pin;
resetContacts();
_store->loadContacts(this);
bootstrapRTCfromContacts();
addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure Andy's public channel
addChannel("Public", PUBLIC_GROUP_PSK); // pre-configure public channel
_store->loadChannels(this);
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
@@ -1071,6 +1008,9 @@ NodePrefs *MyMesh::getNodePrefs() {
uint32_t MyMesh::getBLEPin() {
return _active_ble_pin;
}
void MyMesh::setBLEPin(uint32_t active_pin) {
_active_ble_pin = active_pin;
}
struct FreqRange {
uint32_t lower_freq, upper_freq;
@@ -1463,9 +1403,7 @@ void MyMesh::handleCmdFrame(size_t len) {
int out_len;
if ((out_len = getFromOfflineQueue(out_frame)) > 0) {
_serial->writeFrame(out_frame, out_len);
#ifdef DISPLAY_CLASS
if (_ui) _ui->msgRead(offline_queue_len);
#endif
if (_listener) _listener->onQueueSizeChanged(offline_queue_len);
} else {
out_frame[0] = RESP_CODE_NO_MORE_MESSAGES;
_serial->writeFrame(out_frame, 1);
@@ -2480,10 +2418,6 @@ void MyMesh::loop() {
saveContacts();
dirty_contacts_expiry = 0;
}
#ifdef DISPLAY_CLASS
if (_ui) _ui->setHasConnection(_serial->isConnected());
#endif
}
bool MyMesh::advert() {
+28 -32
View File
@@ -2,7 +2,6 @@
#include <Arduino.h>
#include <Mesh.h>
#include "AbstractUITask.h"
/*------------ Frame Protocol --------------*/
#define FIRMWARE_VER_CODE 14
@@ -76,6 +75,10 @@
#define REQ_TYPE_KEEP_ALIVE 0x02
#define REQ_TYPE_GET_TELEMETRY_DATA 0x03
// Copied from simple_repeater
#define CTL_TYPE_NODE_DISCOVER_REQ 0x80
#define CTL_TYPE_NODE_DISCOVER_RESP 0x90
struct AdvertPath {
uint8_t pubkey_prefix[7];
uint8_t path_len;
@@ -84,26 +87,37 @@ struct AdvertPath {
uint8_t path[MAX_PATH_SIZE];
};
#if defined(DISPLAY_CLASS) && !(defined(UI_NO_DISCOVER_SCREEN) && (UI_NO_DISCOVER_SCREEN + 0 != 0))
struct DiscoveredNode {
uint8_t pubkey_prefix[9];
float snr_in;
float snr_out;
char name[32];
uint8_t type;
};
#endif
class MyMesh : public BaseChatMesh, public DataStoreHost {
public:
MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store, AbstractUITask* ui=NULL);
class Listener {
public:
virtual void onMessageRecv(mesh::Packet *pkt, const ContactInfo &from, uint8_t txt_type, uint32_t sender_timestamp, const char* text) = 0;
virtual void onChannelMessageRecv(mesh::Packet *pkt, ChannelDetails& channel_details, const char* text) = 0;
virtual void onQueueSizeChanged(int msgcount) = 0;
virtual void onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) { }
virtual void onControlDataRecv(const mesh::Packet* pkt) { }
virtual void onChannelDataRecv(mesh::Packet *pkt, const mesh::GroupChannel &channel, uint16_t data_type,
const uint8_t *data, size_t data_len) { }
virtual void onACKRecv(uint32_t ack_crc) { }
virtual uint8_t onUnhandledRequest(const ContactInfo &contact, uint32_t sender_timestamp, const uint8_t *data,
uint8_t len, uint8_t *reply) { return 0; /* unknown request type */ }
virtual void onUnhandledResponse(const ContactInfo &from, uint32_t tag, const uint8_t* data, uint8_t len) { }
virtual void onTraceRecv(mesh::Packet *pkt, uint32_t tag, uint32_t auth_code, uint8_t flags,
const uint8_t *path_snrs, const uint8_t *path_hashes, uint8_t path_len) { }
virtual void onRawDataRecv(mesh::Packet *pkt) { }
virtual ~Listener() { }
};
void begin(bool has_display);
MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMeshTables &tables, DataStore& store);
void begin();
void startInterface(BaseSerialInterface &serial);
void setListener(Listener* listener) { _listener = listener; }
const char *getNodeName();
NodePrefs *getNodePrefs();
uint32_t getBLEPin();
void setBLEPin(uint32_t active_pin);
void loop();
void handleCmdFrame(size_t len);
@@ -112,11 +126,6 @@ public:
int getRecentlyHeard(AdvertPath dest[], int max_num);
#if defined(DISPLAY_CLASS) && !(defined(UI_NO_DISCOVER_SCREEN) && (UI_NO_DISCOVER_SCREEN + 0 != 0))
bool requestRepeatersDiscovery();
int getDiscoveredNodes(DiscoveredNode nodes[], int max_num);
#endif
protected:
float getAirtimeBudgetFactor() const override;
int getInterferenceThreshold() const override;
@@ -237,7 +246,7 @@ private:
uint32_t pending_telemetry, pending_discovery; // pending _TELEMETRY_REQ
uint32_t pending_req; // pending _BINARY_REQ
BaseSerialInterface *_serial;
AbstractUITask* _ui;
Listener* _listener;
ContactsIterator _iter;
uint32_t _iter_filter_since;
@@ -279,19 +288,6 @@ private:
#define ADVERT_PATH_TABLE_SIZE 16
AdvertPath advert_paths[ADVERT_PATH_TABLE_SIZE]; // circular table
#if defined(DISPLAY_CLASS) && !(defined(UI_NO_DISCOVER_SCREEN) && (UI_NO_DISCOVER_SCREEN + 0 != 0))
#ifdef UI_RECENT_LIST_SIZE
#define DISCOVERED_NODES_TABLE_SIZE UI_RECENT_LIST_SIZE
#else
#define DISCOVERED_NODES_TABLE_SIZE 4
#endif
DiscoveredNode discovered_nodes[DISCOVERED_NODES_TABLE_SIZE]; // not circular, latest discovered nodes are not kept
uint32_t disc_node_req_tag = 0;
uint32_t disc_nodes_count = 0;
void checkControlDataForPendingDiscovery(uint8_t payload[], size_t p_len);
#endif
};
extern MyMesh the_mesh;
+24 -26
View File
@@ -114,11 +114,7 @@ MultiSerialInterface interface_manager;
StdRNG fast_rng;
SimpleMeshTables tables;
MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store
#ifdef DISPLAY_CLASS
, &ui_task
#endif
);
MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store);
/* END GLOBAL OBJECTS */
@@ -176,37 +172,38 @@ void setup() {
#endif
#endif
store.begin();
the_mesh.begin(
#ifdef DISPLAY_CLASS
disp != NULL
#else
false
#endif
);
the_mesh.begin();
#elif defined(RP2040_PLATFORM)
LittleFS.begin();
store.begin();
the_mesh.begin(
#ifdef DISPLAY_CLASS
disp != NULL
#else
false
#endif
);
the_mesh.begin();
#elif defined(ESP32)
SPIFFS.begin(true);
store.begin();
the_mesh.begin(
#ifdef DISPLAY_CLASS
disp != NULL
#else
false
#endif
);
the_mesh.begin();
#else
#error "need to define filesystem"
#endif
#ifdef BLE_PIN_CODE // 123456 by default
if (the_mesh.getNodePrefs()->ble_pin == 0) {
#ifdef DISPLAY_CLASS
if (disp != NULL && BLE_PIN_CODE == 123456) {
StdRNG rng;
the_mesh.setBLEPin(rng.nextInt(100000, 999999)); // random pin each session
} else {
the_mesh.setBLEPin(BLE_PIN_CODE); // otherwise static pin
}
#else
the_mesh.setBLEPin(BLE_PIN_CODE); // otherwise static pin
#endif
} else {
the_mesh.setBLEPin(the_mesh.getNodePrefs()->ble_pin);
}
#else
the_mesh.setBLEPin(0);
#endif
// add bluetooth interface
#if defined(BLE_PIN_CODE)
bluetooth_interface.begin(BLE_NAME_PREFIX, the_mesh.getNodePrefs()->node_name, the_mesh.getBLEPin());
@@ -286,6 +283,7 @@ void setup() {
#ifdef DISPLAY_CLASS
ui_task.begin(disp, &sensors, the_mesh.getNodePrefs()); // still want to pass this in as dependency, as prefs might be moved
the_mesh.setListener(&ui_task);
#endif
board.onBootComplete();
+96 -22
View File
@@ -99,7 +99,7 @@ class HomeScreen : public UIScreen {
#if UI_SENSORS_PAGE == 1
SENSORS,
#endif
#if !(defined(UI_NO_DISCOVER_SCREEN) && (UI_NO_DISCOVER_SCREEN + 0 != 0))
#if UI_DISCOVER_SCREEN
DISCOVERY,
#endif
#ifndef UI_NO_HIBERNATE
@@ -115,8 +115,10 @@ class HomeScreen : public UIScreen {
uint8_t _page;
bool _shutdown_init;
AdvertPath recent[UI_RECENT_LIST_SIZE];
#if !(defined(UI_NO_DISCOVER_SCREEN) && (UI_NO_DISCOVER_SCREEN + 0 != 0))
DiscoveredNode discovered[UI_RECENT_LIST_SIZE];
#if UI_DISCOVER_SCREEN
DiscoveredNode discovered[DISCOVERED_NODES_TABLE_SIZE]; // not circular, latest discovered nodes are not kept
uint32_t disc_node_req_tag = 0;
uint32_t disc_nodes_count = 0;
uint32_t discovery_req_time = 0;
bool discovery_disp_names = true; // by default desplay names if available (removes SNR_O)
#endif
@@ -206,6 +208,40 @@ public:
: _task(task), _rtc(rtc), _sensors(sensors), _node_prefs(node_prefs), _page(0),
_shutdown_init(false), sensors_lpp(200) { }
#if UI_DISCOVER_SCREEN
bool sendDiscoverRequest() {
uint8_t cmd_bytes[6];
cmd_bytes[0] = CTL_TYPE_NODE_DISCOVER_REQ | 1; // DISCOVER_REQ | prefix only
cmd_bytes[1] = 0xFF; // Repeaters
the_mesh.getRNG()->random((uint8_t *) &disc_node_req_tag, 4); // generate random tag
memcpy(&cmd_bytes[2], &disc_node_req_tag, 4);
disc_nodes_count = 0;
mesh::Packet* req = the_mesh.createControlData(cmd_bytes, sizeof(cmd_bytes));
if (req) {
the_mesh.sendZeroHop(req);
discovery_req_time = millis();
return true;
}
return false;
}
void handleDiscoverResponse(const mesh::Packet* packet) {
if (disc_nodes_count < DISCOVERED_NODES_TABLE_SIZE && memcmp(&packet->payload[2], &disc_node_req_tag, 4) == 0) {
auto d = &discovered[disc_nodes_count++];
memcpy(d->pubkey_prefix, &packet->payload[6], 8);
d->type = packet->payload[0] & 0xF;
d->snr_out = ((int8_t)packet->payload[1]) / 4.0;
d->snr_in = radio_driver.getLastSNR();
ContactInfo* c = the_mesh.lookupContactByPubKey(&packet->payload[6], 8);
if (c != NULL) {
strncpy(d->name, c->name, 32);
} else {
d->name[0] = 0;
}
}
}
#endif
void poll() override {
if (_shutdown_init && !_task->isButtonPressed()) { // must wait for USR button to be released
_task->shutdown();
@@ -464,9 +500,9 @@ public:
if (sensors_scroll) sensors_scroll_offset = (sensors_scroll_offset+1)%sensors_nb;
else sensors_scroll_offset = 0;
#endif
#if !(defined(UI_NO_DISCOVER_SCREEN) && (UI_NO_DISCOVER_SCREEN + 0 != 0))
#if UI_DISCOVER_SCREEN
} else if (_page == HomePage::DISCOVERY) {
int count = the_mesh.getDiscoveredNodes(discovered, UI_RECENT_LIST_SIZE);
int count = disc_nodes_count;
display.setColor(UIColor::primary_txt);
int y = 20;
for (int i = 0; i < count; i++, y += 11) {
@@ -493,8 +529,8 @@ public:
}
if (millis() < discovery_req_time + 5000) {
return 1000; // more frequent updates just after req
} else if (count < UI_RECENT_LIST_SIZE -1) { // show only 5 sec after last disc
y = 10 + 11 * UI_RECENT_LIST_SIZE;
} else if (count < DISCOVERED_NODES_TABLE_SIZE -1) { // show only 5 sec after last disc
y = 10 + 11 * DISCOVERED_NODES_TABLE_SIZE;
display.drawTextCentered(display.width() / 2, y, "discover: " PRESS_LABEL);
}
#endif
@@ -525,7 +561,7 @@ public:
if (_page == HomePage::RECENT) {
_task->showAlert("Recent adverts", 800);
}
#if !(defined(UI_NO_DISCOVER_SCREEN) && (UI_NO_DISCOVER_SCREEN + 0 != 0))
#if UI_DISCOVER_SCREEN
if (_page == HomePage::DISCOVERY) {
_task->showAlert("Repeater disc", 800);
}
@@ -562,11 +598,10 @@ public:
return true;
}
#endif
#if !(defined(UI_NO_DISCOVER_SCREEN) && (UI_NO_DISCOVER_SCREEN + 0 != 0))
#if UI_DISCOVER_SCREEN
if (c == KEY_ENTER && _page == HomePage::DISCOVERY) {
if (millis() > discovery_req_time + 5000) { // rate limiter
the_mesh.requestRepeatersDiscovery();
discovery_req_time = millis();
sendDiscoverRequest();
}
return true;
}
@@ -751,18 +786,12 @@ switch(t){
#endif
}
void UITask::onMessageRecv(mesh::Packet *pkt, const ContactInfo &from, uint8_t txt_type, uint32_t sender_timestamp, const char* text) {
// we only want to show text messages on display, not cli data
if (!(txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_SIGNED_PLAIN)) return;
void UITask::msgRead(int msgcount) {
_msgcount = msgcount;
if (msgcount == 0) {
gotoHomeScreen();
}
}
void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) {
_msgcount = msgcount;
((MsgPreviewScreen *) msg_preview)->addPreview(path_len, from_name, text);
uint8_t path_len = pkt->isRouteFlood() ? pkt->path_len : 0xFF;
((MsgPreviewScreen *) msg_preview)->addPreview(path_len, from.name, text);
setCurrScreen(msg_preview);
if (_display != NULL) {
@@ -774,6 +803,51 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i
_next_refresh = 100; // trigger refresh
}
}
if (!hasConnection()) {
notify(UIEventType::contactMessage);
}
}
void UITask::onChannelMessageRecv(mesh::Packet *pkt, ChannelDetails& channel_details, const char* text) {
uint8_t path_len = pkt->isRouteFlood() ? pkt->path_len : 0xFF;
((MsgPreviewScreen *) msg_preview)->addPreview(path_len, channel_details.name, text);
setCurrScreen(msg_preview);
if (_display != NULL) {
if (!_display->isOn() && !hasConnection()) {
_display->turnOn();
}
if (_display->isOn()) {
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
_next_refresh = 100; // trigger refresh
}
}
if (!hasConnection()) {
notify(UIEventType::channelMessage);
}
}
void UITask::onQueueSizeChanged(int msgcount) {
_msgcount = msgcount;
if (msgcount == 0) {
gotoHomeScreen();
}
}
void UITask::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) {
if (!hasConnection()) {
notify(UIEventType::newContactMessage);
}
}
void UITask::onControlDataRecv(const mesh::Packet* packet) {
#if UI_DISCOVER_SCREEN
if (packet->payload_len >= 14 && (packet->payload[0] & 0xF0) == CTL_TYPE_NODE_DISCOVER_RESP) {
((HomeScreen *) home)->handleDiscoverResponse(packet);
}
#endif
}
void UITask::userLedHandler() {
+30 -2
View File
@@ -22,6 +22,30 @@
#include "../AbstractUITask.h"
#include "../NodePrefs.h"
#ifdef UI_NO_DISCOVER_SCREEN
#error "UI_NO_DISCOVER_SCREEN is obsolete - use -D UI_DISCOVER_SCREEN=0 instead"
#endif
#ifndef UI_DISCOVER_SCREEN
#define UI_DISCOVER_SCREEN 1
#endif
#if UI_DISCOVER_SCREEN
#ifdef UI_RECENT_LIST_SIZE
#define DISCOVERED_NODES_TABLE_SIZE UI_RECENT_LIST_SIZE
#else
#define DISCOVERED_NODES_TABLE_SIZE 4
#endif
struct DiscoveredNode {
float snr_in;
float snr_out;
uint8_t pubkey_prefix[9];
uint8_t type;
char name[32];
};
#endif
class UITask : public AbstractUITask {
DisplayDriver* _display;
SensorManager* _sensors;
@@ -90,10 +114,14 @@ public:
bool getGPSState();
void toggleGPS();
// MyMesh::Listener
void onMessageRecv(mesh::Packet *pkt, const ContactInfo &from, uint8_t txt_type, uint32_t sender_timestamp, const char* text) override;
void onChannelMessageRecv(mesh::Packet *pkt, ChannelDetails& channel_details, const char* text) override;
void onQueueSizeChanged(int offline_queue_size) override;
void onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) override;
void onControlDataRecv(const mesh::Packet* pkt) override;
// from AbstractUITask
void msgRead(int msgcount) override;
void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override;
void notify(UIEventType t = UIEventType::none) override;
void loop() override;
+47 -9
View File
@@ -126,7 +126,7 @@ switch(t){
// Serial.println((int) t);
}
void UITask::msgRead(int msgcount) {
void UITask::onQueueSizeChanged(int msgcount) {
_msgcount = msgcount;
if (msgcount == 0) {
clearMsgPreview();
@@ -139,17 +139,19 @@ void UITask::clearMsgPreview() {
_need_refresh = true;
}
void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) {
_msgcount = msgcount;
void UITask::onMessageRecv(mesh::Packet *pkt, const ContactInfo &from, uint8_t txt_type, uint32_t sender_timestamp, const char* text) {
// we only want to show text messages on display, not cli data
if (!(txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_SIGNED_PLAIN)) return;
#ifdef HAS_DRV2605
vibration.trigger(); // vibrate even while the app is connected (honors quiet + cooldown)
#endif
uint8_t path_len = pkt->isRouteFlood() ? pkt->path_len : 0xFF;
if (path_len == 0xFF) {
sprintf(_origin, "(F) %s", from_name);
sprintf(_origin, "(F) %s", from.name);
} else {
sprintf(_origin, "(%d) %s", (uint32_t) path_len, from_name);
sprintf(_origin, "(%d) %s", (uint32_t) path_len, from.name);
}
StrHelper::strncpy(_msg, text, sizeof(_msg));
@@ -158,10 +160,46 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i
_display->turnOn();
}
if (_display->isOn()) {
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
_need_refresh = true;
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
_need_refresh = true;
}
}
if (!hasConnection()) {
notify(UIEventType::contactMessage);
}
}
void UITask::onChannelMessageRecv(mesh::Packet *pkt, ChannelDetails& channel_details, const char* text) {
#ifdef HAS_DRV2605
vibration.trigger(); // vibrate even while the app is connected (honors quiet + cooldown)
#endif
uint8_t path_len = pkt->isRouteFlood() ? pkt->path_len : 0xFF;
if (path_len == 0xFF) {
sprintf(_origin, "(F) %s", channel_details.name);
} else {
sprintf(_origin, "(%d) %s", (uint32_t) path_len, channel_details.name);
}
StrHelper::strncpy(_msg, text, sizeof(_msg));
if (_display != NULL) {
if (!_display->isOn() && !hasConnection()) {
_display->turnOn();
}
if (_display->isOn()) {
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
_need_refresh = true;
}
}
if (!hasConnection()) {
notify(UIEventType::channelMessage);
}
}
void UITask::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) {
if (!hasConnection()) {
notify(UIEventType::newContactMessage);
}
}
void UITask::renderBatteryIndicator(uint16_t batteryMilliVolts) {
@@ -262,7 +300,7 @@ void UITask::renderCurrScreen() {
_display->print(tmp);
// BT pin
if (!_connected && the_mesh.getBLEPin() != 0) {
if (!hasConnection() && the_mesh.getBLEPin() != 0) {
_display->setColor(UIColor::warning_txt);
_display->setTextSize(2);
_display->setCursor(0, 43);
@@ -372,7 +410,7 @@ void UITask::userLedHandler() {
statusLedWrite(255, 0, 0); // red: battery low
} else if (_msgcount > 0) {
statusLedWrite(255, 90, 0); // amber: unread messages
} else if (_connected) {
} else if (hasConnection()) {
statusLedWrite(0, 0, 255); // blue: app connected
} else {
statusLedWrite(0, 255, 0); // green: heartbeat
+6 -2
View File
@@ -70,9 +70,13 @@ public:
bool hasDisplay() const { return _display != NULL; }
void clearMsgPreview();
// MyMesh::Listener
void onMessageRecv(mesh::Packet *pkt, const ContactInfo &from, uint8_t txt_type, uint32_t sender_timestamp, const char* text) override;
void onChannelMessageRecv(mesh::Packet *pkt, ChannelDetails& channel_details, const char* text) override;
void onQueueSizeChanged(int offline_queue_size) override;
void onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) override;
// from AbstractUITask
void msgRead(int msgcount) override;
void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override;
void notify(UIEventType t = UIEventType::none) override;
void loop() override;
+28 -3
View File
@@ -503,15 +503,16 @@ switch(t){
}
void UITask::msgRead(int msgcount) {
void UITask::onQueueSizeChanged(int msgcount) {
_msgcount = msgcount;
if (msgcount == 0) {
gotoHomeScreen();
}
}
void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) {
_msgcount = msgcount;
void UITask::onMessageRecv(mesh::Packet *pkt, const ContactInfo &from, uint8_t txt_type, uint32_t sender_timestamp, const char* text) {
// we only want to show text messages on display, not cli data
if (!(txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_SIGNED_PLAIN)) return;
if (_display != NULL) {
if (!_display->isOn() && !hasConnection()) {
@@ -522,6 +523,30 @@ void UITask::newMsg(uint8_t path_len, const char* from_name, const char* text, i
_next_refresh = 100; // trigger refresh
}
}
if (!hasConnection()) {
notify(UIEventType::contactMessage);
}
}
void UITask::onChannelMessageRecv(mesh::Packet *pkt, ChannelDetails& channel_details, const char* text) {
if (_display != NULL) {
if (!_display->isOn() && !hasConnection()) {
_display->turnOn();
}
if (_display->isOn()) {
_auto_off = millis() + AUTO_OFF_MILLIS; // extend the auto-off timer
_next_refresh = 100; // trigger refresh
}
}
if (!hasConnection()) {
notify(UIEventType::channelMessage);
}
}
void UITask::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) {
if (!hasConnection()) {
notify(UIEventType::newContactMessage);
}
}
void UITask::userLedHandler() {
+5 -2
View File
@@ -98,10 +98,13 @@ public:
bool getGPSState();
void toggleGPS();
// MyMesh::Listener
void onMessageRecv(mesh::Packet *pkt, const ContactInfo &from, uint8_t txt_type, uint32_t sender_timestamp, const char* text) override;
void onChannelMessageRecv(mesh::Packet *pkt, ChannelDetails& channel_details, const char* text) override;
void onQueueSizeChanged(int offline_queue_size) override;
void onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) override;
// from AbstractUITask
void msgRead(int msgcount) override;
void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) override;
void notify(UIEventType t = UIEventType::none) override;
void loop() override;