Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
f99ea79
Fix ThinkNode M8 repeater/room server build: compile sensor helpers f…
marcelverdult Sep 26, 2026
f215e6d
Add T1000-E custom CLI command to toggle the buzzer
A13xB0 Oct 15, 2026
b21cc3e
Fix off-by-one when loading the T1000-E buzzer setting
A13xB0 Oct 15, 2026
d7de7f6
Report an error when the buzzer setting fails to save
A13xB0 Oct 15, 2026
b9c328a
Remove BLE screen when BLE not available
amdoolittle Oct 16, 2026
6e1f474
Update UITask.cpp
amdoolittle Oct 17, 2026
7a0fde2
timezone offset is now stored as float to allow half hour offsets
liamcottle Oct 17, 2026
0e63c18
tz_offset should be treated as float when down casting calculation re…
liamcottle Oct 17, 2026
b3d76af
Merge pull request #3610 from liamcottle/fix/timezone-offset
liamcottle Oct 17, 2026
428a95c
Merge pull request #3606 from amdoolittle/feature/ble-conditional-dis…
liamcottle Oct 17, 2026
9d93a72
Added WiFi to TFT BLE version of Heltec V4
amdoolittle Oct 17, 2026
85ad886
Fix unsigned conversion of negative TX power in CommonRadioPrefs
radek-sixi Oct 17, 2026
b738935
Show quarter-hour timezone offsets with two decimals
Confituurke Oct 17, 2026
e9c5a55
Remove the acknowledgement tones
A13xB0 Oct 17, 2026
2ed45f0
Move to companion firmware, remove from varient
A13xB0 Oct 17, 2026
332f298
Apply buzzer setting changes in the UI so no reboot needed.
A13xB0 Oct 17, 2026
933c0f6
added dynamic wifi to: lilygo t-beam supreme sx1262 ble companion
Oct 18, 2026
060fd21
Skip empty anon contact slots in peer/contact lookups (prevent null-k…
radek-sixi Oct 20, 2026
d7f0fb7
Merge pull request #3627 from radek-sixi/tbeam-supreme-ble-wifi
liamcottle Oct 21, 2026
dec8ad6
Merge pull request #3614 from amdoolittle/fix/heltec-v4-tft-wifi-enable
liamcottle Oct 21, 2026
87c76a8
implement new auto detect display for sh1106 and ssd1306
liamcottle Oct 21, 2026
a8e8d01
Use two decimals only for exact quarter-hour offsets
Confituurke Oct 21, 2026
a31e2c9
refactoring eink devices behaviour
fdlamotte Oct 21, 2026
408d674
DisplayDriver: fixing alignment issue in vtable for nrf52 and epd dis…
fdlamotte Oct 21, 2026
02c9922
Merge pull request #3651 from liamcottle/feature/auto-detect-display
ripplebiz Oct 21, 2026
56b5f4e
fix(chat): refuse a negative text length for a group message
marcelverdult Oct 22, 2026
83bd824
fix(radio): refuse "nan" for the airtime factor
marcelverdult Oct 22, 2026
4725963
Fix broken KISS modem build on 6 ESP32-S3 platforms
Blake-Latchford Oct 22, 2026
61363a3
Merge pull request #3646 from marcelverdult/upstream-candidate/af-nan…
ripplebiz Oct 24, 2026
cd9f97f
Merge pull request #3643 from marcelverdult/upstream-candidate/compan…
ripplebiz Oct 24, 2026
da98f53
UiTask: better cleaning of eink screens when hibernating
fdlamotte Oct 24, 2026
d7c0ead
thinknode_m1: fix button2 definition
fdlamotte Oct 24, 2026
b2106ea
ThinknodeM1: update eink driver
fdlamotte Oct 24, 2026
ea8e954
Merge pull request #3659 from fdlamotte/elecrow_eink_refactor
fdlamotte Oct 24, 2026
2cad4ee
ThinknodeM5: enable wifi in usb companion
fdlamotte Oct 25, 2026
36eb5eb
Merge pull request #3680 from fdlamotte/m5_wifi_usb_companion
ripplebiz Oct 26, 2026
55abbe7
Merge pull request #3667 from Blake-Latchford/fix/kiss-modem-usbcdc-t…
ripplebiz Oct 26, 2026
46216b2
Merge pull request #3617 from aXistem-dev/feat/tz-offset-quarter-hours
ripplebiz Oct 26, 2026
26e4389
Saturate duplicate packet stats instead of wrapping
Blake-Latchford Oct 26, 2026
bd882a2
* scope 0 rule fixes
ripplebiz Oct 26, 2026
55b961e
* unit test fix
ripplebiz Oct 26, 2026
e197e55
fix(esp32): define the RTC backup of ESP32RTCClock once
marcelverdult Oct 22, 2026
d694bd3
Merge pull request #3639 from radek-sixi/fix/skip-empty-anon-contact-…
ripplebiz Oct 27, 2026
b0bacf8
fix(radio): put LR2021 in standby before re-config in setParams
WayenWeng Oct 27, 2026
3e4f71d
Drop unit tests per review feedback
radek-sixi Oct 27, 2026
63bc8ed
bump RadioLib to 7.8.1
recrof Oct 27, 2026
b594cfd
Merge pull request #3693 from recrof/patch-4
liamcottle Oct 27, 2026
fd56b1a
Merge pull request #3615 from radek-sixi/fix/common-radio-prefs-signe…
liamcottle Oct 27, 2026
afa4b87
Merge pull request #3509 from marcelverdult/fix/thinknode-m8-sensors
fdlamotte Oct 27, 2026
492814b
T-Echo: use GxEPD2_154_D67 e-ink driver
akalinin-softvelum Oct 27, 2026
f869e56
Merge pull request #3702 from akalinin-softvelum/fix/techo-eink-driver
fdlamotte Oct 27, 2026
cedb805
Remove unused buttonStateChanged from some variants
fdlamotte Oct 27, 2026
6fd8e2f
Merge pull request #3703 from fdlamotte/remove_button_state_changed
fdlamotte Oct 27, 2026
93ddc86
Merge pull request #3692 from Seeed-Studio/dev
ripplebiz Oct 28, 2026
02c827b
Merge pull request #3686 from marcelverdult/upstream-pr/esp32-rtc-bac…
ripplebiz Oct 28, 2026
42ded8d
Merge pull request #3688 from Blake-Latchford/fix/stats-dup-count-wrap
ripplebiz Oct 28, 2026
cf28668
Merge pull request #3682 from meshcore-dev/fix-scope-zero
ripplebiz Oct 29, 2026
5d7100a
ui: add some parameters to the cli (mostly regarding eink behaviour)
fdlamotte Oct 26, 2026
04d72b1
Merge pull request #3687 from fdlamotte/display_prefs
fdlamotte Oct 29, 2026
017db61
Fix possible reply buffer overflow in 'sensor list'
AlexandrTyshchenko Oct 29, 2026
77d81d1
Merge pull request #3603 from A13xB0/companion-buzzer-cli
liamcottle Oct 29, 2026
61eb195
Merge pull request #3728 from AlexandrTyshchenko/fix/sensor-list-over…
ripplebiz Oct 30, 2026
92e5194
Dispatcher: skip the AGC reset while packets are being received
AlexandrTyshchenko Nov 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
472 changes: 472 additions & 0 deletions docs/companion_cli.md

Large diffs are not rendered by default.

128 changes: 125 additions & 3 deletions examples/companion_radio/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1175,7 +1175,9 @@ void MyMesh::handleCmdFrame(size_t len) {
? ERR_CODE_NOT_FOUND
: ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsupported TXT_TYPE_*
}
} else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel text msg
} else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG && len >= 7) { // send GroupChannel text msg
// 7 = code, txt_type, channel_idx and the 4 byte timestamp; the text may be empty.
// Without this the text length below is computed as len - 7 and turns negative.
int i = 1;
uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN
uint8_t channel_idx = cmd_frame[i++];
Expand Down Expand Up @@ -2122,6 +2124,23 @@ bool MyMesh::handleCommand(const char* command, uint32_t sender_timestamp, char*
return true;
}

#ifdef PIN_BUZZER
if (strcmp(command, "get buzzer") == 0) {
sprintf(reply, "> %s", _prefs.buzzer_quiet ? "off" : "on");
return true;
}
if (memcmp(command, "set buzzer ", 11) == 0) {
if (strcmp(&command[11], "on") == 0 || strcmp(&command[11], "off") == 0) {
_prefs.buzzer_quiet = strcmp(&command[11], "off") == 0;
savePrefs();
strcpy(reply, "OK");
} else {
strcpy(reply, "Error: state must be on or off");
}
return true;
}
#endif

if (memcmp(command, "set pin ", 8) == 0) {
_prefs.ble_pin = atoi(&command[8]);
savePrefs();
Expand Down Expand Up @@ -2192,11 +2211,12 @@ bool MyMesh::handleCommand(const char* command, uint32_t sender_timestamp, char*
}

if (strcmp(command, "get tz.offset") == 0) {
sprintf(reply, "> %d", _prefs.tz_offset);
float q = _prefs.tz_offset * 4;
sprintf(reply, (q == (int)q && ((int)q & 1)) ? "> %.2f" : "> %.1f", _prefs.tz_offset);
return true;
}
if (memcmp(command, "set tz.offset ", 14) == 0) {
int8_t tz = atof(&command[14]);
float tz = atof(&command[14]);
if (tz < -12 || tz > 14) {
strcpy(reply, "Error, must be from -12 to +14");
} else {
Expand All @@ -2206,6 +2226,108 @@ bool MyMesh::handleCommand(const char* command, uint32_t sender_timestamp, char*
}
return true;
}
#ifdef DISPLAY_CLASS
if (memcmp(command, "set ui.show_clock", 17) == 0) {
_prefs.ui_show_clock = atoi(&command[17]) ? 1 : 0;
savePrefs();
sprintf(reply, "> now %d", _prefs.ui_show_clock);
return true;
}
if (memcmp(command, "set ui.auto_off_delay", 21) == 0) {
_prefs.ui_auto_off_delay = atoi(&command[21]);
savePrefs();
sprintf(reply, "> now %d seconds", _prefs.ui_auto_off_delay);
return true;
}
if (memcmp(command, "set ui.backlight_delay", 22) == 0) {
_prefs.ui_backlight_delay = atoi(&command[22]);
savePrefs();
if (_prefs.ui_backlight_delay >= 0)
sprintf(reply, "> now %d seconds", _prefs.ui_backlight_delay);
else if (_prefs.ui_backlight_delay == -1)
sprintf(reply, "> now always on (-1)");
else
sprintf(reply, "> now disabled (<-1)");
return true;
}
if (memcmp(command, "set ui.backlight_on_keys", 24) == 0) {
_prefs.ui_backlight_on_keys = atoi(&command[24]);
savePrefs();
sprintf(reply, "> now %d", _prefs.ui_backlight_on_keys);
return true;
}
if (memcmp(command, "set ui.backlight_on_msgs", 24) == 0) {
_prefs.ui_backlight_on_msgs = atoi(&command[24]);
savePrefs();
sprintf(reply, "> now %d", _prefs.ui_backlight_on_msgs);
return true;
}
if (memcmp(command, "set ui.auto_off_blank", 21) == 0) {
_prefs.ui_auto_off_blank = atoi(&command[21]);
savePrefs();
sprintf(reply, "> now %d", _prefs.ui_auto_off_blank);
return true;
}
if (memcmp(command, "set ui.auto_off_keep_msgs", 25) == 0) {
_prefs.ui_auto_off_keep_msgs = atoi(&command[25]);
savePrefs();
sprintf(reply, "> now %d", _prefs.ui_auto_off_keep_msgs);
return true;
}
if (memcmp(command, "set ui.max_partial", 18) == 0) {
_prefs.ui_max_partial = atoi(&command[18]);
savePrefs();
sprintf(reply, "> now %d (applied at next screen wakeup)", _prefs.ui_max_partial);
return true;
}
if (memcmp(command, "set ui.nd_name", 14) == 0) {
_prefs.ui_nd_name = atoi(&command[14]);
savePrefs();
sprintf(reply, "> ui.nd_name is now %d", _prefs.ui_nd_name);
return true;
}
if (memcmp(command, "get ui.show_clock", 17) == 0) {
sprintf(reply, "> %d", _prefs.ui_show_clock);
return true;
}
if (memcmp(command, "get ui.auto_off_delay", 21) == 0) {
sprintf(reply, "> %d seconds", _prefs.ui_auto_off_delay);
return true;
}
if (memcmp(command, "get ui.backlight_delay", 22) == 0) {
if (_prefs.ui_backlight_delay >= 0)
sprintf(reply, "> %d seconds", _prefs.ui_backlight_delay);
else if (_prefs.ui_backlight_delay == -1)
sprintf(reply, "> always on (-1)");
else
sprintf(reply, "> disabled (<-1)");
return true;
}
if (memcmp(command, "get ui.backlight_on_keys", 24) == 0) {
sprintf(reply, "> %d", _prefs.ui_backlight_on_keys);
return true;
}
if (memcmp(command, "get ui.backlight_on_msgs", 24) == 0) {
sprintf(reply, "> %d", _prefs.ui_backlight_on_msgs);
return true;
}
if (memcmp(command, "get ui.auto_off_blank", 21) == 0) {
sprintf(reply, "> %d", _prefs.ui_auto_off_blank);
return true;
}
if (memcmp(command, "get ui.auto_off_keep_msgs", 25) == 0) {
sprintf(reply, "> %d", _prefs.ui_auto_off_keep_msgs);
return true;
}
if (memcmp(command, "get ui.max_partial", 18) == 0) {
sprintf(reply, "> %d", _prefs.ui_max_partial);
return true;
}
if (memcmp(command, "get ui.nd_name", 14) == 0) {
sprintf(reply, "> %d", _prefs.ui_nd_name);
return true;
}
#endif

return false; // not handled
}
Expand Down
61 changes: 58 additions & 3 deletions examples/companion_radio/NodePrefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class NodePrefs : public ConfigSerializer { // persisted to file
uint8_t agc_reset_interval = 0; // secs / 4
char default_scope_name[31];
uint8_t default_scope_key[16];
int8_t tz_offset = 0;
float tz_offset = 0;
#ifdef ENABLE_WIFI_INTERFACE
#ifndef WIFI_SSID
#define WIFI_SSID ""
Expand All @@ -58,6 +58,34 @@ class NodePrefs : public ConfigSerializer { // persisted to file
// use ssid from prefs, or fallback to ssid from build flags
const char* getWifiSSID() const { return wifi_ssid[0] ? wifi_ssid : WIFI_SSID; }
#endif
#ifdef DISPLAY_CLASS
#ifndef UI_SHOW_CLOCK
#define UI_SHOW_CLOCK 0
#endif
#ifndef AUTO_OFF_MILLIS
#define AUTO_OFF_MILLIS 15000
#endif
#ifndef EINK_MAX_PARTIAL_REFRESH
#define EINK_MAX_PARTIAL_REFRESH 0
#endif
uint8_t ui_show_clock = UI_SHOW_CLOCK;
uint32_t ui_auto_off_delay = AUTO_OFF_MILLIS / 1000;
int32_t ui_backlight_delay = 5;
#if defined(BACKLIGHT_BTN)
uint8_t ui_backlight_on_keys = 0;
#else
uint8_t ui_backlight_on_keys = 1;
#endif
uint8_t ui_backlight_on_msgs = 0;
#ifdef UI_SCREENOFF_BLANK
uint8_t ui_auto_off_blank = 1;
#else
uint8_t ui_auto_off_blank = 0;
#endif
uint8_t ui_auto_off_keep_msgs = 1;
uint8_t ui_max_partial = EINK_MAX_PARTIAL_REFRESH;
uint8_t ui_nd_name = 1;
#endif

private:
class RadioPrefs : public CommonRadioPrefs {
Expand Down Expand Up @@ -102,8 +130,8 @@ class NodePrefs : public ConfigSerializer { // persisted to file
void setIntThresh(uint8_t t) override { _parent->interference_threshold = t; markDirty(); }
uint8_t getRxGain() const override { return _parent->rx_boosted_gain; }
void setRxGain(uint8_t g) override { _parent->rx_boosted_gain = g; markDirty(); }
uint8_t getTxPower() const override { return _parent->tx_power_dbm; }
void setTxPower(uint8_t dbm) override { _parent->tx_power_dbm = dbm; markDirty(); }
int8_t getTxPower() const override { return _parent->tx_power_dbm; }
void setTxPower(int8_t dbm) override { _parent->tx_power_dbm = dbm; markDirty(); }
float getRxDelay() const override { return _parent->rx_delay_base; }
void setRxDelay(float d) override { _parent->rx_delay_base = d; markDirty(); }
uint16_t getAgcResetInt() const override { return _parent->agc_reset_interval * 4; }
Expand Down Expand Up @@ -189,6 +217,27 @@ class NodePrefs : public ConfigSerializer { // persisted to file
WiFiPrefs wifi;
#endif

#ifdef DISPLAY_CLASS
class UI_Prefs : public ConfigSerializer {
NodePrefs* _parent;
protected:
void structure() override {
def("show_clock", _parent->ui_show_clock);
def("auto_off", _parent->ui_auto_off_delay);
def("bl_dly", _parent->ui_backlight_delay);
def("bl_keys", _parent->ui_backlight_on_keys);
def("bl_msgs", _parent->ui_backlight_on_msgs);
def("max_partial", _parent->ui_max_partial);
def("off_blank", _parent->ui_auto_off_blank);
def("off_keep_msgs", _parent->ui_auto_off_keep_msgs);
def("nd_name", _parent->ui_nd_name);
}
public:
UI_Prefs(NodePrefs* parent) : _parent(parent) {}
};
UI_Prefs ui;
#endif

protected:
void structure() override {
def("name", node_name, sizeof(node_name));
Expand All @@ -203,12 +252,18 @@ class NodePrefs : public ConfigSerializer { // persisted to file
def("custom", custom);
#ifdef ENABLE_WIFI_INTERFACE
def("wifi", wifi);
#endif
#ifdef DISPLAY_CLASS
def("ui", ui);
#endif
}
public:
NodePrefs() : radio(this), gps(this), companion(this), custom(&radio)
#ifdef ENABLE_WIFI_INTERFACE
, wifi(this)
#endif
#ifdef DISPLAY_CLASS
, ui(this)
#endif
{
node_name[0] = 0;
Expand Down
Loading