Added new emoji to emoji picker; bug fixes for larger battery capacities recognition regression; updated readme for clarity on contacts add; updated firmware version; fix stupid blob storage size & buffer issue so now you can add contacts from last heard finally!!! Updated readme accordingly

This commit is contained in:
pelgraine
2026-04-12 18:15:33 +10:00
parent c578dcadc8
commit 0c032429eb
11 changed files with 367 additions and 46 deletions
+34 -14
View File
@@ -2,7 +2,7 @@
#include "DataStore.h"
#if defined(EXTRAFS) || defined(QSPIFLASH)
#define MAX_BLOBRECS 100
#define MAX_BLOBRECS 1000
#else
#define MAX_BLOBRECS 20
#endif
@@ -691,16 +691,27 @@ struct BlobRec {
};
void DataStore::checkAdvBlobFile() {
if (!_getContactsChannelsFS()->exists("/adv_blobs")) {
File file = openWrite(_getContactsChannelsFS(), "/adv_blobs");
if (file) {
BlobRec zeroes;
memset(&zeroes, 0, sizeof(zeroes));
for (int i = 0; i < MAX_BLOBRECS; i++) { // pre-allocate to fixed size
file.write((uint8_t *) &zeroes, sizeof(zeroes));
}
file.close();
FILESYSTEM* fs = _getContactsChannelsFS();
size_t expectedSize = (size_t)MAX_BLOBRECS * sizeof(BlobRec);
if (fs->exists("/adv_blobs")) {
File existing = openRead(fs, "/adv_blobs");
size_t actualSize = existing ? (size_t)existing.size() : 0;
if (existing) existing.close();
if (actualSize == expectedSize) return; // already correct size
Serial.printf("[DataStore] adv_blobs wrong size (%u vs %u) — recreating\n",
(unsigned)actualSize, (unsigned)expectedSize);
fs->remove("/adv_blobs"); // delete undersized (or oversized) file
}
File file = openWrite(fs, "/adv_blobs");
if (file) {
BlobRec zeroes;
memset(&zeroes, 0, sizeof(zeroes));
for (int i = 0; i < MAX_BLOBRECS; i++) {
file.write((uint8_t *) &zeroes, sizeof(zeroes));
}
file.close();
}
}
@@ -873,10 +884,14 @@ uint8_t DataStore::getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_b
if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix)
mesh::Utils::toHex(fname, key, key_len);
// Prefer SD card (_fsExtra) — unlimited file count vs SPIFFS ~100-200 file limit.
// Fall back to SPIFFS (_fs) for devices without SD.
FILESYSTEM* blobFs = (_fsExtra != nullptr) ? _fsExtra : _fs;
sprintf(path, "/bl/%s", fname);
if (_fs->exists(path)) {
File f = openRead(_fs, path);
if (blobFs->exists(path)) {
File f = openRead(blobFs, path);
if (f) {
int len = f.read(dest_buf, 255); // currently MAX 255 byte blob len supported!!
f.close();
@@ -892,15 +907,20 @@ bool DataStore::putBlobByKey(const uint8_t key[], int key_len, const uint8_t src
if (key_len > 8) key_len = 8; // just use first 8 bytes (prefix)
mesh::Utils::toHex(fname, key, key_len);
// Prefer SD card (_fsExtra) — unlimited file count vs SPIFFS ~100-200 file limit.
FILESYSTEM* blobFs = (_fsExtra != nullptr) ? _fsExtra : _fs;
blobFs->mkdir("/bl"); // ensure directory exists on chosen filesystem
sprintf(path, "/bl/%s", fname);
File f = openWrite(_fs, path);
File f = openWrite(blobFs, path);
if (f) {
int n = f.write(src_buf, len);
f.close();
if (n == len) return true; // success!
_fs->remove(path); // blob was only partially written!
blobFs->remove(path); // blob was only partially written!
}
return false; // error
}
+2 -1
View File
@@ -1265,7 +1265,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
next_ack_idx = 0;
sign_data = NULL;
dirty_contacts_expiry = 0;
memset(advert_paths, 0, sizeof(advert_paths));
advert_paths = nullptr; // PSRAM-allocated in begin()
memset(send_scope.key, 0, sizeof(send_scope.key));
memset(_sent_track, 0, sizeof(_sent_track));
_sent_track_idx = 0;
@@ -1292,6 +1292,7 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
}
void MyMesh::begin(bool has_display) {
advert_paths = (AdvertPath*)ps_calloc(ADVERT_PATH_TABLE_SIZE, sizeof(AdvertPath));
BaseChatMesh::begin();
if (!_store->loadMainIdentity(self_id)) {
+4 -4
View File
@@ -12,7 +12,7 @@
#endif
#ifndef FIRMWARE_VERSION
#define FIRMWARE_VERSION "Meck v1.6.1"
#define FIRMWARE_VERSION "Meck v1.7"
#endif
#if defined(NRF52_PLATFORM) || defined(STM32_PLATFORM)
@@ -82,7 +82,7 @@
#define REQ_TYPE_GET_TELEMETRY_DATA 0x03
struct AdvertPath {
uint8_t pubkey_prefix[7];
uint8_t pubkey_prefix[8];
uint8_t path_len;
uint8_t type; // ADV_TYPE_* (Chat/Repeater/Room/Sensor)
char name[32];
@@ -305,8 +305,8 @@ private:
AckTableEntry expected_ack_table[EXPECTED_ACK_TABLE_SIZE]; // circular table
int next_ack_idx;
#define ADVERT_PATH_TABLE_SIZE 40
AdvertPath advert_paths[ADVERT_PATH_TABLE_SIZE]; // circular table
#define ADVERT_PATH_TABLE_SIZE 1000
AdvertPath* advert_paths; // PSRAM-allocated in begin(), size = ADVERT_PATH_TABLE_SIZE
// Sent message repeat tracking
#define SENT_TRACK_SIZE 4
+69 -1
View File
@@ -38,7 +38,7 @@
static uint8_t composeChannelIdx = 0;
static unsigned long lastComposeRefresh = 0;
static bool composeNeedsRefresh = false;
#define COMPOSE_REFRESH_INTERVAL 100 // ms before starting e-ink refresh after keypress (refresh itself takes ~644ms)
#define COMPOSE_REFRESH_INTERVAL 800 // ms — must exceed e-ink partial refresh time (~644ms) to prevent back-to-back refreshes
// Phone dialer debounce — independent from compose/smsSuppressLoop to avoid
// interfering with call view rendering and alert display
@@ -804,6 +804,22 @@
}
return false;
}
// Read up to 2 touch points — returns actual finger count (0, 1 or 2)
static int readTouchMulti(int16_t* outX0, int16_t* outY0, int16_t* outX1, int16_t* outY1) {
if (!gt911Ready) return 0;
int16_t xs[2], ys[2];
uint8_t count = gt911Touch.getPoint(xs, ys, 2);
if (count == 0) return 0;
if (display.isPortraitMode()) {
*outX0 = xs[0]; *outY0 = ys[0];
if (count >= 2) { *outX1 = xs[1]; *outY1 = ys[1]; }
} else {
*outX0 = ys[0]; *outY0 = EPD_HEIGHT - 1 - xs[0];
if (count >= 2) { *outX1 = ys[1]; *outY1 = EPD_HEIGHT - 1 - xs[1]; }
}
return (int)count;
}
#endif
// --- Shared touch state machine variables ---
@@ -828,6 +844,13 @@
static bool touchCooldown = false;
static unsigned long lastTouchEventMs = 0;
#if defined(LilyGo_T5S3_EPaper_Pro)
// Two-finger tap detection state
static bool twoFingerDown = false;
static unsigned long twoFingerDownTime = 0;
#define TWO_FINGER_TAP_MS 350 // max ms from first finger down to both up
#endif
// Unified touch reader — returns physical screen coordinates
static bool readTouch(int16_t* outX, int16_t* outY) {
#if defined(LilyGo_T5S3_EPaper_Pro)
@@ -2882,6 +2905,51 @@ void loop() {
lastTouchSeenMs = now;
}
#if defined(LilyGo_T5S3_EPaper_Pro)
// Two-finger tap detection — must run before single-finger state machine
// so it can suppress the single-finger path when two fingers are present.
{
int16_t x0, y0, x1, y1;
int fingers = readTouchMulti(&x0, &y0, &x1, &y1);
if (fingers >= 2) {
if (!twoFingerDown) {
twoFingerDown = true;
twoFingerDownTime = now;
}
// Suppress single-finger tracking while two fingers are down
lastTouchSeenMs = now;
touchDown = false;
longPressHandled = true; // prevent long-press triggering
swipeHandled = true; // prevent swipe triggering
} else if (twoFingerDown) {
// Both fingers lifted — check if it was a quick tap
if ((now - twoFingerDownTime) < TWO_FINGER_TAP_MS) {
if (ui_task.isOnContactsScreen()) {
ContactsScreen* cs = (ContactsScreen*)ui_task.getContactsScreen();
if (cs) {
if (cs->isInSelectMode()) {
cs->exitSelectMode();
Serial.println("[Touch] Two-finger tap: exited contacts select mode");
} else {
cs->enterSelectMode();
Serial.println("[Touch] Two-finger tap: entered contacts select mode");
}
ui_task.forceRefresh();
touchCooldown = true;
lastTouchEventMs = now;
}
}
}
twoFingerDown = false;
longPressHandled = false;
swipeHandled = false;
touchDown = false;
touchCooldown = true;
lastTouchEventMs = now;
}
}
#endif
bool fingerPresent = (now - lastTouchSeenMs) < TOUCH_LIFT_DEBOUNCE_MS;
if (touchCooldown) {
@@ -39,7 +39,7 @@ private:
uint16_t* _filteredIdx; // indices into contact table
uint32_t* _filteredTs; // cached lastmod for sorting
int _filteredCount; // how many contacts match current filter
AdvertPath _hopBuf[12]; // recently heard advert paths for hop-count display
AdvertPath _hopBuf[40]; // recently heard advert paths for hop-count display
int _hopBufCount;
bool _cacheValid;
@@ -120,7 +120,7 @@ private:
}
_cacheValid = true;
// Refresh hop-count cache from the 12 most recently heard adverts
_hopBufCount = the_mesh.getRecentlyHeard(_hopBuf, 12);
_hopBufCount = the_mesh.getRecentlyHeard(_hopBuf, 40);
// Clamp scroll position
if (_scrollPos >= _filteredCount) {
_scrollPos = (_filteredCount > 0) ? _filteredCount - 1 : 0;
+117 -2
View File
@@ -15,11 +15,11 @@
#define EMOJI_SM_W 10
#define EMOJI_SM_H 10
#define EMOJI_COUNT 65
#define EMOJI_COUNT 76
// Escape codes in 0x80+ range - safe from keyboard ASCII (32-126)
#define EMOJI_ESCAPE_START 0x80
#define EMOJI_ESCAPE_END 0xC0 // 0x80 + 64
#define EMOJI_ESCAPE_END 0xCB // 0x80 + 75
#define EMOJI_PAD_BYTE 0x7F // DEL, not typeable (key < 127 guard)
// ======== LARGE 12x12 SPRITES ========
@@ -36,6 +36,14 @@ static const uint8_t emoji_lg_thumbsup[] PROGMEM = {
static const uint8_t emoji_lg_frown[] PROGMEM = {
0x1F,0x80, 0x20,0x40, 0x59,0xA0, 0x59,0xA0, 0x80,0x10, 0x9F,0x90, 0xA0,0x50, 0x40,0x20, 0x20,0x40, 0x1F,0x80, 0x00,0x00, 0x00,0x00,
};
// [65] loudly_crying 😭
static const uint8_t emoji_lg_loudly_crying[] PROGMEM = {
0x1F,0x80, 0x20,0x40, 0x5B,0x40, 0x5B,0x20, 0x80,0x10, 0x9F,0x10, 0xA0,0x90, 0x60,0xC0, 0xA0,0xA0, 0x1F,0x00, 0x40,0x40, 0x00,0x00,
};
// [66] heart ♥️
static const uint8_t emoji_lg_heart[] PROGMEM = {
0x00,0x00, 0x73,0x80, 0xFF,0xC0, 0xFF,0xC0, 0xFF,0xC0, 0x7F,0x80, 0x3F,0x00, 0x1E,0x00, 0x0C,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
};
// [3] wireless
static const uint8_t emoji_lg_wireless[] PROGMEM = {
0x00,0x00, 0x3F,0xC0, 0x60,0x60, 0xC0,0x30, 0x0F,0x00, 0x19,0x80, 0x30,0xC0, 0x00,0x00, 0x06,0x00, 0x0F,0x00, 0x06,0x00, 0x00,0x00,
@@ -284,6 +292,43 @@ static const uint8_t emoji_lg_tipping[] PROGMEM = {
static const uint8_t emoji_lg_hedgehog[] PROGMEM = {
0x00,0x00, 0x0A,0x80, 0x15,0x40, 0x2A,0xA0, 0x55,0x60, 0x7E,0xF0, 0xDB,0x90, 0xFF,0xD0, 0x7F,0xE0, 0x3F,0xC0, 0x24,0x80, 0x00,0x00,
};
// [67] diamond_suit ♦️
static const uint8_t emoji_lg_diamond_suit[] PROGMEM = {
0x00,0x00, 0x04,0x00, 0x0E,0x00, 0x1F,0x00, 0x3F,0x80, 0x7F,0xC0, 0x3F,0x80, 0x1F,0x00, 0x0E,0x00, 0x04,0x00, 0x00,0x00, 0x00,0x00,
};
// [68] spade_suit ♠️
static const uint8_t emoji_lg_spade_suit[] PROGMEM = {
0x04,0x00, 0x0E,0x00, 0x1F,0x00, 0x3F,0x80, 0x7F,0xC0, 0xFF,0xE0, 0xFF,0xE0, 0x7F,0xC0, 0x15,0x00, 0x04,0x00, 0x0E,0x00, 0x00,0x00,
};
// [69] pizza 🍕
static const uint8_t emoji_lg_pizza[] PROGMEM = {
0x02,0x00, 0x06,0x00, 0x0F,0x00, 0x0B,0x00, 0x1F,0x80, 0x1D,0x80, 0x3F,0xC0, 0x2F,0x40, 0x7F,0xE0, 0x7F,0xE0, 0xFF,0xF0, 0x00,0x00,
};
// [70] four_leaf_clover 🍀
static const uint8_t emoji_lg_four_leaf_clover[] PROGMEM = {
0x0C,0x00, 0x1E,0x00, 0x1E,0x00, 0x6D,0x80, 0xF3,0xC0, 0xF3,0xC0, 0x6D,0x80, 0x1E,0x00, 0x1E,0x00, 0x0C,0x00, 0x06,0x00, 0x00,0x00,
};
// [71] cloud ☁️
static const uint8_t emoji_lg_cloud[] PROGMEM = {
0x00,0x00, 0x1C,0x00, 0x3E,0x00, 0x7F,0x80, 0xFF,0xC0, 0xFF,0xE0, 0xFF,0xE0, 0x7F,0xC0, 0x00,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
};
// [72] rocket 🚀
static const uint8_t emoji_lg_rocket[] PROGMEM = {
0x04,0x00, 0x0E,0x00, 0x1F,0x00, 0x1B,0x00, 0x1B,0x00, 0x3F,0x80, 0x7F,0xC0, 0x5F,0x40, 0x9F,0x20, 0x0E,0x00, 0x15,0x00, 0x00,0x00,
};
// [73] passport_control 🛂
static const uint8_t emoji_lg_passport_control[] PROGMEM = {
0x3F,0xC0, 0x40,0x20, 0x46,0x20, 0x4F,0x20, 0x46,0x20, 0x40,0x20, 0x44,0x20, 0x4E,0x20, 0x5F,0x20, 0x40,0x20, 0x3F,0xC0, 0x00,0x00,
};
// [74] eight_spoked_asterisk ✳️
static const uint8_t emoji_lg_eight_spoked_asterisk[] PROGMEM = {
0x7F,0xE0, 0x84,0x10, 0xA4,0x90, 0x95,0x10, 0x8E,0x10, 0xFF,0xD0, 0x8E,0x10, 0x95,0x10, 0xA4,0x90, 0x84,0x10, 0x7F,0xE0, 0x00,0x00,
};
// [75] signal_strength 📶
static const uint8_t emoji_lg_signal_strength[] PROGMEM = {
0x00,0x20, 0x00,0x20, 0x00,0xA0, 0x00,0xA0, 0x02,0xA0, 0x02,0xA0, 0x0A,0xA0, 0x0A,0xA0, 0x2A,0xA0, 0x2A,0xA0, 0xAA,0xA0, 0xAA,0xA0,
};
static const uint8_t* const EMOJI_SPRITES_LG[] PROGMEM = {
emoji_lg_joy, emoji_lg_thumbsup, emoji_lg_frown,
@@ -301,6 +346,10 @@ static const uint8_t* const EMOJI_SPRITES_LG[] PROGMEM = {
emoji_lg_bulb, emoji_lg_cat, emoji_lg_fleur, emoji_lg_moon,
emoji_lg_coffee, emoji_lg_tooth, emoji_lg_pretzel, emoji_lg_abacus,
emoji_lg_moai, emoji_lg_tipping, emoji_lg_hedgehog,
emoji_lg_loudly_crying, emoji_lg_heart, emoji_lg_diamond_suit,
emoji_lg_spade_suit, emoji_lg_pizza, emoji_lg_four_leaf_clover,
emoji_lg_cloud, emoji_lg_rocket, emoji_lg_passport_control,
emoji_lg_eight_spoked_asterisk, emoji_lg_signal_strength,
};
// ======== SMALL 10x10 SPRITES ========
@@ -519,6 +568,50 @@ static const uint8_t emoji_sm_tipping[] PROGMEM = {
static const uint8_t emoji_sm_hedgehog[] PROGMEM = {
0x15,0x00, 0x2A,0x80, 0x55,0x40, 0xFF,0xC0, 0xDB,0x40, 0xFF,0x80, 0x7F,0x80, 0x3F,0x00, 0x24,0x00, 0x00,0x00,
};
// [65] loudly_crying 😭
static const uint8_t emoji_sm_loudly_crying[] PROGMEM = {
0x3E,0x00, 0x41,0x00, 0xB6,0x80, 0x80,0x40, 0xBE,0x40, 0x81,0x40, 0x63,0x00, 0x9C,0x80, 0x00,0x00, 0x00,0x00,
};
// [66] heart ♥️
static const uint8_t emoji_sm_heart[] PROGMEM = {
0x00,0x00, 0x6C,0x00, 0xFE,0x00, 0xFE,0x00, 0x7C,0x00, 0x38,0x00, 0x10,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
};
// [67] diamond_suit ♦️
static const uint8_t emoji_sm_diamond_suit[] PROGMEM = {
0x00,0x00, 0x08,0x00, 0x1C,0x00, 0x3E,0x00, 0x7F,0x00, 0x3E,0x00, 0x1C,0x00, 0x08,0x00, 0x00,0x00, 0x00,0x00,
};
// [68] spade_suit ♠️
static const uint8_t emoji_sm_spade_suit[] PROGMEM = {
0x08,0x00, 0x1C,0x00, 0x3E,0x00, 0x7F,0x00, 0xFF,0x80, 0xFF,0x80, 0x2A,0x00, 0x08,0x00, 0x1C,0x00, 0x00,0x00,
};
// [69] pizza 🍕
static const uint8_t emoji_sm_pizza[] PROGMEM = {
0x08,0x00, 0x1C,0x00, 0x14,0x00, 0x3E,0x00, 0x36,0x00, 0x6D,0x00, 0x7F,0x00, 0xFF,0x80, 0xFF,0x80, 0x00,0x00,
};
// [70] four_leaf_clover 🍀
static const uint8_t emoji_sm_four_leaf_clover[] PROGMEM = {
0x18,0x00, 0x3C,0x00, 0xDB,0x00, 0xE7,0x00, 0xE7,0x00, 0xDB,0x00, 0x3C,0x00, 0x18,0x00, 0x0C,0x00, 0x00,0x00,
};
// [71] cloud ☁️
static const uint8_t emoji_sm_cloud[] PROGMEM = {
0x00,0x00, 0x38,0x00, 0x7E,0x00, 0xFF,0x00, 0xFF,0x80, 0xFF,0x80, 0x7F,0x00, 0x00,0x00, 0x00,0x00, 0x00,0x00,
};
// [72] rocket 🚀
static const uint8_t emoji_sm_rocket[] PROGMEM = {
0x08,0x00, 0x1C,0x00, 0x36,0x00, 0x36,0x00, 0x7F,0x00, 0xBE,0x80, 0x3E,0x00, 0x1C,0x00, 0x2A,0x00, 0x00,0x00,
};
// [73] passport_control 🛂
static const uint8_t emoji_sm_passport_control[] PROGMEM = {
0x7F,0x80, 0x80,0x40, 0x8C,0x40, 0x9E,0x40, 0x8C,0x40, 0x80,0x40, 0x9C,0x40, 0xBE,0x40, 0x80,0x40, 0x7F,0x80,
};
// [74] eight_spoked_asterisk ✳️
static const uint8_t emoji_sm_eight_spoked_asterisk[] PROGMEM = {
0x7F,0x80, 0x84,0x40, 0xA5,0x40, 0x9E,0x40, 0xFF,0xC0, 0x9E,0x40, 0xA5,0x40, 0x84,0x40, 0x7F,0x80, 0x00,0x00,
};
// [75] signal_strength 📶
static const uint8_t emoji_sm_signal_strength[] PROGMEM = {
0x00,0x80, 0x00,0x80, 0x02,0x80, 0x02,0x80, 0x0A,0x80, 0x0A,0x80, 0x2A,0x80, 0x2A,0x80, 0xAA,0x80, 0xAA,0x80,
};
static const uint8_t* const EMOJI_SPRITES_SM[] PROGMEM = {
emoji_sm_joy, emoji_sm_thumbsup, emoji_sm_frown,
@@ -536,6 +629,17 @@ static const uint8_t* const EMOJI_SPRITES_SM[] PROGMEM = {
emoji_sm_bulb, emoji_sm_cat, emoji_sm_fleur, emoji_sm_moon,
emoji_sm_coffee, emoji_sm_tooth, emoji_sm_pretzel, emoji_sm_abacus,
emoji_sm_moai, emoji_sm_tipping, emoji_sm_hedgehog,
emoji_sm_loudly_crying,
emoji_sm_heart,
emoji_sm_diamond_suit,
emoji_sm_spade_suit,
emoji_sm_pizza,
emoji_sm_four_leaf_clover,
emoji_sm_cloud,
emoji_sm_rocket,
emoji_sm_passport_control,
emoji_sm_eight_spoked_asterisk,
emoji_sm_signal_strength,
};
// ---- Codepoint lookup for UTF-8 conversion ----
@@ -607,6 +711,17 @@ static const EmojiCodepoint EMOJI_CODEPOINTS[EMOJI_COUNT] = {
{ 0x1F5FF, 0x0000, 0xBE }, // moai
{ 0x1F481, 0x0000, 0xBF }, // tipping
{ 0x1F994, 0x0000, 0xC0 }, // hedgehog
{ 0x1F62D , 0x0000 , 0xC1 }, // loudly_crying
{ 0x2665 , 0x0000 , 0xC2 }, // heart
{ 0x2666 , 0x0000 , 0xC3 }, // diamond_suit
{ 0x2660 , 0x0000 , 0xC4 }, // spade_suit
{ 0x1F355 , 0x0000 , 0xC5 }, // pizza
{ 0x1F340 , 0x0000 , 0xC6 }, // four_leaf_clover
{ 0x2601 , 0x0000 , 0xC7 }, // cloud
{ 0x1F680 , 0x0000 , 0xC8 }, // rocket
{ 0x1F6C2 , 0x0000 , 0xC9 }, // passport_control
{ 0x2733 , 0x0000 , 0xCA }, // eight_spoked_asterisk
{ 0x1F4F6 , 0x0000 , 0xCB }, // signal_strength
};
// ---- Helper functions ----
@@ -13,12 +13,15 @@ extern MyMesh the_mesh;
// recency. Unlike Discovery (active zero-hop scan), this is purely passive
// — it shows nodes whose adverts have been received over time.
// ==========================================================================
// Display cap — we never need to show all 200 storage entries at once
#define LAST_HEARD_DISPLAY_SIZE 100
class LastHeardScreen : public UIScreen {
mesh::RTCClock* _rtc;
int _scrollPos;
// Local sorted copy of advert paths (refreshed each render)
AdvertPath _entries[ADVERT_PATH_TABLE_SIZE];
// Local sorted copy of advert paths (PSRAM-allocated, refreshed each render)
AdvertPath* _entries;
int _count;
static char typeChar(uint8_t adv_type) {
@@ -46,7 +49,9 @@ class LastHeardScreen : public UIScreen {
public:
LastHeardScreen(mesh::RTCClock* rtc)
: _rtc(rtc), _scrollPos(0), _count(0) {}
: _rtc(rtc), _scrollPos(0), _count(0) {
_entries = (AdvertPath*)ps_calloc(LAST_HEARD_DISPLAY_SIZE, sizeof(AdvertPath));
}
void resetScroll() { _scrollPos = 0; }
@@ -91,7 +96,7 @@ public:
int render(DisplayDriver& display) override {
// Refresh sorted list from mesh
_count = the_mesh.getRecentlyHeard(_entries, ADVERT_PATH_TABLE_SIZE);
_count = the_mesh.getRecentlyHeard(_entries, LAST_HEARD_DISPLAY_SIZE);
// Filter out empty entries (recv_timestamp == 0)
int validCount = 0;
+12 -1
View File
@@ -1,7 +1,7 @@
#pragma once
// Emoji Picker with scrolling grid and scroll bar
// 5 columns, 4 visible rows, scrollable through all 65 emoji
// 5 columns, 4 visible rows, scrollable through all 76 emoji
// WASD navigation, Enter to select, $/Q/Backspace to cancel
#include <helpers/ui/DisplayDriver.h>
@@ -77,6 +77,17 @@ static const char* EMOJI_LABELS[EMOJI_COUNT] = {
"Moai", // 62 moai
"Hiii", // 63 tipping
"Hedg", // 64 hedgehog
"Cry", // 65 loudly_crying
"Love", // 66 heart
"Diam", // 67 diamond_suit
"Spde", // 68 spade_suit
"Piza", // 69 pizza
"Luck", // 70 four_leaf_clover
"Cld", // 71 cloud
"Rckt", // 72 rocket
"Visa", // 73 passport_control
"Star", // 74 eight_spoked_asterisk
"Sig", // 75 signal_strength
};
struct EmojiPicker {