Compare commits

..

14 Commits

Author SHA1 Message Date
Jason P
34ad7809f5 Correctly calculate text width for the header and shrink Channel Name is on OLED 2026-01-20 13:57:08 -06:00
Jason P
f0e751a592 Merge branch 'Message_Bubbles' of https://github.com/meshtastic/firmware into Message_Bubbles 2026-01-20 13:20:19 -06:00
Jason P
a122b49504 Fix message header line width 2026-01-20 13:20:15 -06:00
HarukiToreda
5b3df5adeb Merge branch 'develop' into Message_Bubbles 2026-01-20 13:43:01 -05:00
HarukiToreda
94d85b3f42 Proper indent for messages inside the bubble 2026-01-20 13:24:35 -05:00
github-actions[bot]
eefc08087d Update protobufs (#9371)
Co-authored-by: jp-bennett <5630967+jp-bennett@users.noreply.github.com>
2026-01-20 10:29:11 -06:00
Jason P
726a5ccfeb Merge branch 'develop' into Message_Bubbles 2026-01-20 10:16:13 -06:00
Andrew Yong
fb6d199d36 feat: Add Russell, a board designed to go Up! on a balloon (#9079)
Hardware repository: https://github.com/Meshtastic-Malaysia/russell
- Designed to mount on an ER34615/IFR32700 cell
- RAK3172 STM32WLE5CCU6 MCU + integrated SX1262 LoRa
- CDtop CD-PA1010D GPS
- Bosch Sensortec BME280 sensor
- Consonance CN3158 LiFePO4 solar charger

Signed-off-by: Andrew Yong <me@ndoo.sg>
2026-01-20 06:38:04 -06:00
HarukiToreda
bfe206d9b7 Angled edges 2026-01-20 00:52:32 -05:00
HarukiToreda
b446c3453d Message Bubbles 2026-01-20 00:24:01 -05:00
Ben Meadors
7d4600f8c2 Merge branch 'master' into develop 2026-01-19 12:13:52 -06:00
Ben Meadors
ff50ba4002 Remove bsec from OG ESP32 to fix DRAM overflow 2026-01-19 12:12:14 -06:00
github-actions[bot]
5c401b8e34 Update protobufs (#9362)
Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com>
2026-01-19 12:11:13 -06:00
Ben Meadors
c96ebf15fd Merge remote-tracking branch 'origin/master' into develop 2026-01-19 07:57:36 -06:00
17 changed files with 412 additions and 236 deletions

View File

@@ -1,40 +0,0 @@
{
"build": {
"arduino": {
"ldscript": "esp32s3_out.ld",
"partitions": "default.csv"
},
"core": "esp32",
"extra_flags": [
"-DBOARD_HAS_PSRAM",
"-DARDUINO_ESP32S3_DEV",
"-DARDUINO_USB_MODE=1",
"-DARDUINO_USB_CDC_ON_BOOT=1",
"-DARDUINO_RUNNING_CORE=1",
"-DARDUINO_EVENT_RUNNING_CORE=1"
],
"f_cpu": "240000000L",
"f_flash": "80000000L",
"flash_mode": "qio",
"hwids": [["0x303A", "0x1001"]],
"mcu": "esp32s3",
"variant": "esp32s3"
},
"connectivity": ["wifi"],
"debug": {
"default_tool": "esp-builtin",
"onboard_tools": ["esp-builtin"],
"openocd_target": "esp32s3.cfg"
},
"frameworks": ["arduino", "espidf"],
"name": "LilyGo Mini-Epapaer-S3 (4 MB Flash, 2MB PSRAM)",
"upload": {
"flash_size": "4MB",
"maximum_ram_size": 327680,
"maximum_size": 4194304,
"require_upload_port": true,
"speed": 460800
},
"url": "https://www.lilygo.cc",
"vendor": "LilyGo"
}

View File

@@ -212,3 +212,30 @@ lib_deps =
sensirion/Sensirion Core@0.7.2 sensirion/Sensirion Core@0.7.2
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x # renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
sensirion/Sensirion I2C SCD4x@1.1.0 sensirion/Sensirion I2C SCD4x@1.1.0
; Same as environmental_extra but without BSEC (saves ~3.5KB DRAM for original ESP32 targets)
[environmental_extra_no_bsec]
lib_deps =
# renovate: datasource=custom.pio depName=Adafruit BMP3XX packageName=adafruit/library/Adafruit BMP3XX Library
adafruit/Adafruit BMP3XX Library@2.1.6
# renovate: datasource=custom.pio depName=Adafruit MAX1704X packageName=adafruit/library/Adafruit MAX1704X
adafruit/Adafruit MAX1704X@1.0.3
# renovate: datasource=custom.pio depName=Adafruit SHTC3 packageName=adafruit/library/Adafruit SHTC3 Library
adafruit/Adafruit SHTC3 Library@1.0.2
# renovate: datasource=custom.pio depName=Adafruit LPS2X packageName=adafruit/library/Adafruit LPS2X
adafruit/Adafruit LPS2X@2.0.6
# renovate: datasource=custom.pio depName=Adafruit SHT31 packageName=adafruit/library/Adafruit SHT31 Library
adafruit/Adafruit SHT31 Library@2.2.2
# renovate: datasource=custom.pio depName=Adafruit VEML7700 packageName=adafruit/library/Adafruit VEML7700 Library
adafruit/Adafruit VEML7700 Library@2.1.6
# renovate: datasource=custom.pio depName=Adafruit SHT4x packageName=adafruit/library/Adafruit SHT4x Library
adafruit/Adafruit SHT4x Library@1.0.5
# renovate: datasource=custom.pio depName=SparkFun Qwiic Scale NAU7802 packageName=sparkfun/library/SparkFun Qwiic Scale NAU7802 Arduino Library
sparkfun/SparkFun Qwiic Scale NAU7802 Arduino Library@1.0.6
# renovate: datasource=custom.pio depName=ClosedCube OPT3001 packageName=closedcube/library/ClosedCube OPT3001
closedcube/ClosedCube OPT3001@1.1.2
# renovate: datasource=git-refs depName=meshtastic-DFRobot_LarkWeatherStation packageName=https://github.com/meshtastic/DFRobot_LarkWeatherStation gitBranch=master
https://github.com/meshtastic/DFRobot_LarkWeatherStation/archive/4de3a9cadef0f6a5220a8a906cf9775b02b0040d.zip
# renovate: datasource=custom.pio depName=Sensirion Core packageName=sensirion/library/Sensirion Core
sensirion/Sensirion Core@0.7.2
# renovate: datasource=custom.pio depName=Sensirion I2C SCD4x packageName=sensirion/library/Sensirion I2C SCD4x
sensirion/Sensirion I2C SCD4x@1.1.0

View File

@@ -259,18 +259,6 @@ bool EInkDisplay::connect()
adafruitDisplay->setRotation(3); adafruitDisplay->setRotation(3);
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT); adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
} }
#elif defined(MINI_EPAPER_S3)
spi1 = new SPIClass(HSPI);
spi1->begin(PIN_SPI1_SCK, PIN_SPI1_MISO, PIN_SPI1_MOSI, PIN_EINK_CS);
// Create GxEPD2 objects
auto lowLevel = new EINK_DISPLAY_MODEL(PIN_EINK_CS, PIN_EINK_DC, PIN_EINK_RES, PIN_EINK_BUSY);
adafruitDisplay = new GxEPD2_BW<EINK_DISPLAY_MODEL, EINK_DISPLAY_MODEL::HEIGHT>(*lowLevel);
// Init GxEPD2
adafruitDisplay->init();
adafruitDisplay->setRotation(0);
adafruitDisplay->setPartialWindow(0, 0, EINK_WIDTH, EINK_HEIGHT);
#elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213) #elif defined(HELTEC_WIRELESS_PAPER) || defined(HELTEC_VISION_MASTER_E213)
// Detect display model, before starting SPI // Detect display model, before starting SPI

View File

@@ -93,8 +93,7 @@ class EInkDisplay : public OLEDDisplay
SPIClass *hspi = NULL; SPIClass *hspi = NULL;
#endif #endif
#if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK) || defined(HELTEC_MESH_SOLAR_EINK) || \ #if defined(HELTEC_MESH_POCKET) || defined(SEEED_WIO_TRACKER_L1_EINK) || defined(HELTEC_MESH_SOLAR_EINK)
defined(MINI_EPAPER_S3)
SPIClass *spi1 = NULL; SPIClass *spi1 = NULL;
#endif #endif

View File

@@ -6,7 +6,6 @@
#include "MessageStore.h" #include "MessageStore.h"
#include "NodeDB.h" #include "NodeDB.h"
#include "UIRenderer.h" #include "UIRenderer.h"
#include "configuration.h"
#include "gps/RTC.h" #include "gps/RTC.h"
#include "graphics/Screen.h" #include "graphics/Screen.h"
#include "graphics/ScreenFonts.h" #include "graphics/ScreenFonts.h"
@@ -20,7 +19,6 @@
// External declarations // External declarations
extern bool hasUnreadMessage; extern bool hasUnreadMessage;
extern meshtastic_DeviceState devicestate;
extern graphics::Screen *screen; extern graphics::Screen *screen;
using graphics::Emote; using graphics::Emote;
@@ -49,7 +47,7 @@ static inline size_t utf8CharLen(uint8_t c)
} }
// Remove variation selectors (FE0F) and skin tone modifiers from emoji so they match your labels // Remove variation selectors (FE0F) and skin tone modifiers from emoji so they match your labels
std::string normalizeEmoji(const std::string &s) static std::string normalizeEmoji(const std::string &s)
{ {
std::string out; std::string out;
for (size_t i = 0; i < s.size();) { for (size_t i = 0; i < s.size();) {
@@ -82,6 +80,7 @@ uint32_t pauseStart = 0;
bool waitingToReset = false; bool waitingToReset = false;
bool scrollStarted = false; bool scrollStarted = false;
static bool didReset = false; static bool didReset = false;
static constexpr int MESSAGE_BLOCK_GAP = 6;
void scrollUp() void scrollUp()
{ {
@@ -111,22 +110,6 @@ void scrollDown()
void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string &line, const Emote *emotes, int emoteCount) void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string &line, const Emote *emotes, int emoteCount)
{ {
std::string renderLine;
for (size_t i = 0; i < line.size();) {
uint8_t c = (uint8_t)line[i];
size_t len = utf8CharLen(c);
if (c == 0xEF && i + 2 < line.size() && (uint8_t)line[i + 1] == 0xB8 && (uint8_t)line[i + 2] == 0x8F) {
i += 3;
continue;
}
if (c == 0xF0 && i + 3 < line.size() && (uint8_t)line[i + 1] == 0x9F && (uint8_t)line[i + 2] == 0x8F &&
((uint8_t)line[i + 3] >= 0xBB && (uint8_t)line[i + 3] <= 0xBF)) {
i += 4;
continue;
}
renderLine.append(line, i, len);
i += len;
}
int cursorX = x; int cursorX = x;
const int fontHeight = FONT_HEIGHT_SMALL; const int fontHeight = FONT_HEIGHT_SMALL;
@@ -203,8 +186,7 @@ void drawStringWithEmotes(OLEDDisplay *display, int x, int y, const std::string
// Render the emote (if found) // Render the emote (if found)
if (matchedEmote && i == nextEmotePos) { if (matchedEmote && i == nextEmotePos) {
// Vertically center emote relative to font baseline (not just midline) int iconY = y + (lineHeight - matchedEmote->height) / 2;
int iconY = fontY + (fontHeight - matchedEmote->height) / 2;
display->drawXbm(cursorX, iconY, matchedEmote->width, matchedEmote->height, matchedEmote->bitmap); display->drawXbm(cursorX, iconY, matchedEmote->width, matchedEmote->height, matchedEmote->bitmap);
cursorX += matchedEmote->width + 1; cursorX += matchedEmote->width + 1;
i += emojiLen; i += emojiLen;
@@ -423,6 +405,102 @@ static inline int getRenderedLineWidth(OLEDDisplay *display, const std::string &
return totalWidth; return totalWidth;
} }
struct MessageBlock {
size_t start;
size_t end;
bool mine;
};
static int getDrawnLinePixelBottom(int lineTopY, const std::string &line, bool isHeaderLine)
{
if (isHeaderLine) {
return lineTopY + (FONT_HEIGHT_SMALL - 1);
}
int tallest = FONT_HEIGHT_SMALL;
for (int e = 0; e < numEmotes; ++e) {
if (line.find(emotes[e].label) != std::string::npos) {
if (emotes[e].height > tallest)
tallest = emotes[e].height;
}
}
const int lineHeight = std::max(FONT_HEIGHT_SMALL, tallest);
const int iconTop = lineTopY + (lineHeight - tallest) / 2;
return iconTop + tallest - 1;
}
static void drawRoundedRectOutline(OLEDDisplay *display, int x, int y, int w, int h, int r)
{
if (w <= 1 || h <= 1)
return;
if (r < 0)
r = 0;
int maxR = (std::min(w, h) / 2) - 1;
if (r > maxR)
r = maxR;
if (r == 0) {
display->drawRect(x, y, w, h);
return;
}
const int x0 = x;
const int y0 = y;
const int x1 = x + w - 1;
const int y1 = y + h - 1;
// sides
if (x0 + r <= x1 - r) {
display->drawLine(x0 + r, y0, x1 - r, y0); // top
display->drawLine(x0 + r, y1, x1 - r, y1); // bottom
}
if (y0 + r <= y1 - r) {
display->drawLine(x0, y0 + r, x0, y1 - r); // left
display->drawLine(x1, y0 + r, x1, y1 - r); // right
}
// corner arcs
display->drawCircleQuads(x0 + r, y0 + r, r, 2); // top left
display->drawCircleQuads(x1 - r, y0 + r, r, 1); // top right
display->drawCircleQuads(x1 - r, y1 - r, r, 8); // bottom right
display->drawCircleQuads(x0 + r, y1 - r, r, 4); // bottom left
}
static std::vector<MessageBlock> buildMessageBlocks(const std::vector<bool> &isHeaderVec, const std::vector<bool> &isMineVec)
{
std::vector<MessageBlock> blocks;
if (isHeaderVec.empty())
return blocks;
size_t start = 0;
bool mine = isMineVec[0];
for (size_t i = 1; i < isHeaderVec.size(); ++i) {
if (isHeaderVec[i]) {
MessageBlock b;
b.start = start;
b.end = i - 1;
b.mine = mine;
blocks.push_back(b);
start = i;
mine = isMineVec[i];
}
}
MessageBlock last;
last.start = start;
last.end = isHeaderVec.size() - 1;
last.mine = mine;
blocks.push_back(last);
return blocks;
}
static void drawMessageScrollbar(OLEDDisplay *display, int visibleHeight, int totalHeight, int scrollOffset, int startY) static void drawMessageScrollbar(OLEDDisplay *display, int visibleHeight, int totalHeight, int scrollOffset, int startY)
{ {
if (totalHeight <= visibleHeight) if (totalHeight <= visibleHeight)
@@ -482,9 +560,14 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
constexpr int LEFT_MARGIN = 2; constexpr int LEFT_MARGIN = 2;
constexpr int RIGHT_MARGIN = 2; constexpr int RIGHT_MARGIN = 2;
constexpr int SCROLLBAR_WIDTH = 3; constexpr int SCROLLBAR_WIDTH = 3;
constexpr int BUBBLE_PAD_X = 3;
constexpr int BUBBLE_PAD_Y = 4;
constexpr int BUBBLE_RADIUS = 4;
constexpr int BUBBLE_MIN_W = 24;
constexpr int BUBBLE_TEXT_INDENT = 2;
const int leftTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN; // Derived widths
const int leftTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - (BUBBLE_PAD_X * 2);
const int rightTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - SCROLLBAR_WIDTH; const int rightTextWidth = SCREEN_WIDTH - LEFT_MARGIN - RIGHT_MARGIN - SCROLLBAR_WIDTH;
// Title string depending on mode // Title string depending on mode
@@ -547,7 +630,28 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
char chanType[32] = ""; char chanType[32] = "";
if (currentMode == ThreadMode::ALL) { if (currentMode == ThreadMode::ALL) {
if (m.dest == NODENUM_BROADCAST) { if (m.dest == NODENUM_BROADCAST) {
snprintf(chanType, sizeof(chanType), "#%s", channels.getName(m.channelIndex)); const char *name = channels.getName(m.channelIndex);
if (currentResolution == ScreenResolution::Low || currentResolution == ScreenResolution::UltraLow) {
if (strcmp(name, "ShortTurbo") == 0)
name = "ShortT";
else if (strcmp(name, "ShortSlow") == 0)
name = "ShortS";
else if (strcmp(name, "ShortFast") == 0)
name = "ShortF";
else if (strcmp(name, "MediumSlow") == 0)
name = "MedS";
else if (strcmp(name, "MediumFast") == 0)
name = "MedF";
else if (strcmp(name, "LongSlow") == 0)
name = "LongS";
else if (strcmp(name, "LongFast") == 0)
name = "LongF";
else if (strcmp(name, "LongTurbo") == 0)
name = "LongT";
else if (strcmp(name, "LongMod") == 0)
name = "LongM";
}
snprintf(chanType, sizeof(chanType), "#%s", name);
} else { } else {
snprintf(chanType, sizeof(chanType), "(DM)"); snprintf(chanType, sizeof(chanType), "(DM)");
} }
@@ -614,8 +718,8 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
} }
// Shrink Sender name if needed // Shrink Sender name if needed
int availWidth = SCREEN_WIDTH - display->getStringWidth(timeBuf) - display->getStringWidth(chanType) - int availWidth = (mine ? rightTextWidth : leftTextWidth) - display->getStringWidth(timeBuf) -
display->getStringWidth(" @...") - 10; display->getStringWidth(chanType) - display->getStringWidth(" @...");
if (availWidth < 0) if (availWidth < 0)
availWidth = 0; availWidth = 0;
@@ -667,6 +771,8 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
cachedLines = allLines; cachedLines = allLines;
cachedHeights = calculateLineHeights(cachedLines, emotes, isHeader); cachedHeights = calculateLineHeights(cachedLines, emotes, isHeader);
std::vector<MessageBlock> blocks = buildMessageBlocks(isHeader, isMine);
// Scrolling logic (unchanged) // Scrolling logic (unchanged)
int totalHeight = 0; int totalHeight = 0;
for (size_t i = 0; i < cachedHeights.size(); ++i) for (size_t i = 0; i < cachedHeights.size(); ++i)
@@ -714,12 +820,123 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
int finalScroll = (int)scrollY; int finalScroll = (int)scrollY;
int yOffset = -finalScroll + getTextPositions(display)[1]; int yOffset = -finalScroll + getTextPositions(display)[1];
const int contentTop = getTextPositions(display)[1];
const int contentBottom = scrollBottom; // already excludes nav line
const int rightEdge = SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN;
const int bubbleGapY = std::max(1, MESSAGE_BLOCK_GAP / 2);
std::vector<int> lineTop;
lineTop.resize(cachedLines.size());
{
int acc = 0;
for (size_t i = 0; i < cachedLines.size(); ++i) {
lineTop[i] = yOffset + acc;
acc += cachedHeights[i];
}
}
// Draw bubbles
for (size_t bi = 0; bi < blocks.size(); ++bi) {
const auto &b = blocks[bi];
if (b.start >= cachedLines.size() || b.end >= cachedLines.size() || b.start > b.end)
continue;
int visualTop = lineTop[b.start];
int topY;
if (isHeader[b.start]) {
// Header start
constexpr int BUBBLE_PAD_TOP_HEADER = 1; // try 1 or 2
topY = visualTop - BUBBLE_PAD_TOP_HEADER;
} else {
// Body start
bool thisLineHasEmote = false;
for (int e = 0; e < numEmotes; ++e) {
if (cachedLines[b.start].find(emotes[e].label) != std::string::npos) {
thisLineHasEmote = true;
break;
}
}
if (thisLineHasEmote) {
constexpr int EMOTE_PADDING_ABOVE = 4;
visualTop -= EMOTE_PADDING_ABOVE;
}
topY = visualTop - BUBBLE_PAD_Y;
}
int visualBottom = getDrawnLinePixelBottom(lineTop[b.end], cachedLines[b.end], isHeader[b.end]);
int bottomY = visualBottom + BUBBLE_PAD_Y;
if (bi + 1 < blocks.size()) {
int nextHeaderIndex = (int)blocks[bi + 1].start;
int nextTop = lineTop[nextHeaderIndex];
int maxBottom = nextTop - 1 - bubbleGapY;
if (bottomY > maxBottom)
bottomY = maxBottom;
}
if (bottomY <= topY + 2)
continue;
if (bottomY < contentTop || topY > contentBottom - 1)
continue;
int maxLineW = 0;
for (size_t i = b.start; i <= b.end; ++i) {
int w = 0;
if (isHeader[i]) {
w = display->getStringWidth(cachedLines[i].c_str());
if (b.mine)
w += 12; // room for ACK/NACK/relay mark
} else {
w = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes);
}
if (w > maxLineW)
maxLineW = w;
}
int bubbleW = std::max(BUBBLE_MIN_W, maxLineW + (BUBBLE_PAD_X * 2));
int bubbleH = (bottomY - topY) + 1;
int bubbleX = 0;
if (b.mine) {
bubbleX = rightEdge - bubbleW;
} else {
bubbleX = x;
}
if (bubbleX < x)
bubbleX = x;
if (bubbleX + bubbleW > rightEdge)
bubbleW = std::max(1, rightEdge - bubbleX);
if (bubbleW > 1 && bubbleH > 1) {
int r = BUBBLE_RADIUS;
int maxR = (std::min(bubbleW, bubbleH) / 2) - 1;
if (maxR < 0)
maxR = 0;
if (r > maxR)
r = maxR;
drawRoundedRectOutline(display, bubbleX, topY, bubbleW, bubbleH, r);
const int extra = 3;
const int rr = r + extra;
int x1 = bubbleX + bubbleW - 1;
int y1 = topY + bubbleH - 1;
if (!b.mine) {
// top-left corner square
display->drawLine(bubbleX, topY, bubbleX + rr, topY);
display->drawLine(bubbleX, topY, bubbleX, topY + rr);
} else {
// bottom-right corner square
display->drawLine(x1 - rr, y1, x1, y1);
display->drawLine(x1, y1 - rr, x1, y1);
}
}
}
// Render visible lines // Render visible lines
int lineY = yOffset;
for (size_t i = 0; i < cachedLines.size(); ++i) { for (size_t i = 0; i < cachedLines.size(); ++i) {
int lineY = yOffset;
for (size_t j = 0; j < i; ++j)
lineY += cachedHeights[j];
if (lineY > -cachedHeights[i] && lineY < scrollBottom) { if (lineY > -cachedHeights[i] && lineY < scrollBottom) {
if (isHeader[i]) { if (isHeader[i]) {
@@ -728,14 +945,28 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
int headerX; int headerX;
if (isMine[i]) { if (isMine[i]) {
// push header left to avoid overlap with scrollbar // push header left to avoid overlap with scrollbar
headerX = SCREEN_WIDTH - w - SCROLLBAR_WIDTH - RIGHT_MARGIN; headerX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - w - BUBBLE_TEXT_INDENT;
if (headerX < LEFT_MARGIN) if (headerX < LEFT_MARGIN)
headerX = LEFT_MARGIN; headerX = LEFT_MARGIN;
} else { } else {
headerX = x; headerX = x + BUBBLE_PAD_X + BUBBLE_TEXT_INDENT;
} }
display->drawString(headerX, lineY, cachedLines[i].c_str()); display->drawString(headerX, lineY, cachedLines[i].c_str());
// Draw underline just under header text
int underlineY = lineY + FONT_HEIGHT_SMALL;
int underlineW = w;
int maxW = rightEdge - headerX;
if (maxW < 0)
maxW = 0;
if (underlineW > maxW)
underlineW = maxW;
for (int px = 0; px < underlineW; ++px) {
display->setPixel(headerX + px, underlineY);
}
// Draw ACK/NACK mark for our own messages // Draw ACK/NACK mark for our own messages
if (isMine[i]) { if (isMine[i]) {
int markX = headerX - 10; int markX = headerX - 10;
@@ -753,32 +984,28 @@ void drawTextMessageFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
// AckStatus::NONE → show nothing // AckStatus::NONE → show nothing
} }
// Draw underline just under header text
int underlineY = lineY + FONT_HEIGHT_SMALL;
for (int px = 0; px < w; ++px) {
display->setPixel(headerX + px, underlineY);
}
} else { } else {
// Render message line // Render message line
if (isMine[i]) { if (isMine[i]) {
// Calculate actual rendered width including emotes // Calculate actual rendered width including emotes
int renderedWidth = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes); int renderedWidth = getRenderedLineWidth(display, cachedLines[i], emotes, numEmotes);
int rightX = SCREEN_WIDTH - renderedWidth - SCROLLBAR_WIDTH - RIGHT_MARGIN; int rightX = (SCREEN_WIDTH - SCROLLBAR_WIDTH - RIGHT_MARGIN) - renderedWidth - BUBBLE_TEXT_INDENT;
if (rightX < LEFT_MARGIN) if (rightX < LEFT_MARGIN)
rightX = LEFT_MARGIN; rightX = LEFT_MARGIN;
drawStringWithEmotes(display, rightX, lineY, cachedLines[i], emotes, numEmotes); drawStringWithEmotes(display, rightX, lineY, cachedLines[i], emotes, numEmotes);
} else { } else {
drawStringWithEmotes(display, x, lineY, cachedLines[i], emotes, numEmotes); drawStringWithEmotes(display, x + BUBBLE_PAD_X + BUBBLE_TEXT_INDENT, lineY, cachedLines[i], emotes,
numEmotes);
} }
} }
} }
lineY += cachedHeights[i];
} }
int totalContentHeight = totalHeight;
int visibleHeight = usableHeight;
// Draw scrollbar // Draw scrollbar
drawMessageScrollbar(display, visibleHeight, totalContentHeight, finalScroll, getTextPositions(display)[1]); drawMessageScrollbar(display, usableHeight, totalHeight, finalScroll, getTextPositions(display)[1]);
graphics::drawCommonHeader(display, x, y, titleStr); graphics::drawCommonHeader(display, x, y, titleStr);
graphics::drawCommonFooter(display, x, y); graphics::drawCommonFooter(display, x, y);
} }
@@ -841,7 +1068,6 @@ std::vector<int> calculateLineHeights(const std::vector<std::string> &lines, con
constexpr int HEADER_UNDERLINE_GAP = 0; // space between underline and first body line constexpr int HEADER_UNDERLINE_GAP = 0; // space between underline and first body line
constexpr int HEADER_UNDERLINE_PIX = 1; // underline thickness (1px row drawn) constexpr int HEADER_UNDERLINE_PIX = 1; // underline thickness (1px row drawn)
constexpr int BODY_LINE_LEADING = -4; // default vertical leading for normal body lines constexpr int BODY_LINE_LEADING = -4; // default vertical leading for normal body lines
constexpr int MESSAGE_BLOCK_GAP = 4; // gap after a message block before a new header
constexpr int EMOTE_PADDING_ABOVE = 4; // space above emote line (added to line above) constexpr int EMOTE_PADDING_ABOVE = 4; // space above emote line (added to line above)
constexpr int EMOTE_PADDING_BELOW = 3; // space below emote line (added to emote line) constexpr int EMOTE_PADDING_BELOW = 3; // space below emote line (added to emote line)
@@ -851,6 +1077,7 @@ std::vector<int> calculateLineHeights(const std::vector<std::string> &lines, con
for (size_t idx = 0; idx < lines.size(); ++idx) { for (size_t idx = 0; idx < lines.size(); ++idx) {
const auto &line = lines[idx]; const auto &line = lines[idx];
const int baseHeight = FONT_HEIGHT_SMALL; const int baseHeight = FONT_HEIGHT_SMALL;
int lineHeight = baseHeight;
// Detect if THIS line or NEXT line contains an emote // Detect if THIS line or NEXT line contains an emote
bool hasEmote = false; bool hasEmote = false;
@@ -872,8 +1099,6 @@ std::vector<int> calculateLineHeights(const std::vector<std::string> &lines, con
} }
} }
int lineHeight = baseHeight;
if (isHeaderVec[idx]) { if (isHeaderVec[idx]) {
// Header line spacing // Header line spacing
lineHeight = baseHeight + HEADER_UNDERLINE_PIX + HEADER_UNDERLINE_GAP; lineHeight = baseHeight + HEADER_UNDERLINE_PIX + HEADER_UNDERLINE_GAP;
@@ -922,7 +1147,7 @@ void handleNewMessage(OLEDDisplay *display, const StoredMessage &sm, const mesht
// Banner logic // Banner logic
const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet.from); const meshtastic_NodeInfoLite *node = nodeDB->getMeshNode(packet.from);
char longName[48] = "???"; char longName[48] = "?";
if (node && node->user.long_name) { if (node && node->user.long_name) {
strncpy(longName, node->user.long_name, sizeof(longName) - 1); strncpy(longName, node->user.long_name, sizeof(longName) - 1);
longName[sizeof(longName) - 1] = '\0'; longName[sizeof(longName) - 1] = '\0';

View File

@@ -77,7 +77,9 @@ typedef enum _meshtastic_AdminMessage_ModuleConfigType {
/* TODO: REPLACE */ /* TODO: REPLACE */
meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG = 11, meshtastic_AdminMessage_ModuleConfigType_DETECTIONSENSOR_CONFIG = 11,
/* TODO: REPLACE */ /* TODO: REPLACE */
meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG = 12 meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG = 12,
/* TODO: REPLACE */
meshtastic_AdminMessage_ModuleConfigType_STATUSMESSAGE_CONFIG = 13
} meshtastic_AdminMessage_ModuleConfigType; } meshtastic_AdminMessage_ModuleConfigType;
typedef enum _meshtastic_AdminMessage_BackupLocation { typedef enum _meshtastic_AdminMessage_BackupLocation {
@@ -323,8 +325,8 @@ extern "C" {
#define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG+1)) #define _meshtastic_AdminMessage_ConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ConfigType)(meshtastic_AdminMessage_ConfigType_DEVICEUI_CONFIG+1))
#define _meshtastic_AdminMessage_ModuleConfigType_MIN meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG #define _meshtastic_AdminMessage_ModuleConfigType_MIN meshtastic_AdminMessage_ModuleConfigType_MQTT_CONFIG
#define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG #define _meshtastic_AdminMessage_ModuleConfigType_MAX meshtastic_AdminMessage_ModuleConfigType_STATUSMESSAGE_CONFIG
#define _meshtastic_AdminMessage_ModuleConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ModuleConfigType)(meshtastic_AdminMessage_ModuleConfigType_PAXCOUNTER_CONFIG+1)) #define _meshtastic_AdminMessage_ModuleConfigType_ARRAYSIZE ((meshtastic_AdminMessage_ModuleConfigType)(meshtastic_AdminMessage_ModuleConfigType_STATUSMESSAGE_CONFIG+1))
#define _meshtastic_AdminMessage_BackupLocation_MIN meshtastic_AdminMessage_BackupLocation_FLASH #define _meshtastic_AdminMessage_BackupLocation_MIN meshtastic_AdminMessage_BackupLocation_FLASH
#define _meshtastic_AdminMessage_BackupLocation_MAX meshtastic_AdminMessage_BackupLocation_SD #define _meshtastic_AdminMessage_BackupLocation_MAX meshtastic_AdminMessage_BackupLocation_SD

View File

@@ -361,7 +361,7 @@ extern const pb_msgdesc_t meshtastic_BackupPreferences_msg;
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
/* meshtastic_NodeDatabase_size depends on runtime parameters */ /* meshtastic_NodeDatabase_size depends on runtime parameters */
#define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size #define MESHTASTIC_MESHTASTIC_DEVICEONLY_PB_H_MAX_SIZE meshtastic_BackupPreferences_size
#define meshtastic_BackupPreferences_size 2279 #define meshtastic_BackupPreferences_size 2362
#define meshtastic_ChannelFile_size 718 #define meshtastic_ChannelFile_size 718
#define meshtastic_DeviceState_size 1737 #define meshtastic_DeviceState_size 1737
#define meshtastic_NodeInfoLite_size 196 #define meshtastic_NodeInfoLite_size 196

View File

@@ -87,6 +87,9 @@ typedef struct _meshtastic_LocalModuleConfig {
/* Paxcounter Config */ /* Paxcounter Config */
bool has_paxcounter; bool has_paxcounter;
meshtastic_ModuleConfig_PaxcounterConfig paxcounter; meshtastic_ModuleConfig_PaxcounterConfig paxcounter;
/* StatusMessage Config */
bool has_statusmessage;
meshtastic_ModuleConfig_StatusMessageConfig statusmessage;
} meshtastic_LocalModuleConfig; } meshtastic_LocalModuleConfig;
@@ -96,9 +99,9 @@ extern "C" {
/* Initializer values for message structs */ /* Initializer values for message structs */
#define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0, false, meshtastic_Config_SecurityConfig_init_default} #define meshtastic_LocalConfig_init_default {false, meshtastic_Config_DeviceConfig_init_default, false, meshtastic_Config_PositionConfig_init_default, false, meshtastic_Config_PowerConfig_init_default, false, meshtastic_Config_NetworkConfig_init_default, false, meshtastic_Config_DisplayConfig_init_default, false, meshtastic_Config_LoRaConfig_init_default, false, meshtastic_Config_BluetoothConfig_init_default, 0, false, meshtastic_Config_SecurityConfig_init_default}
#define meshtastic_LocalModuleConfig_init_default {false, meshtastic_ModuleConfig_MQTTConfig_init_default, false, meshtastic_ModuleConfig_SerialConfig_init_default, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_default, false, meshtastic_ModuleConfig_StoreForwardConfig_init_default, false, meshtastic_ModuleConfig_RangeTestConfig_init_default, false, meshtastic_ModuleConfig_TelemetryConfig_init_default, false, meshtastic_ModuleConfig_CannedMessageConfig_init_default, 0, false, meshtastic_ModuleConfig_AudioConfig_init_default, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_default, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_default, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_default, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_default, false, meshtastic_ModuleConfig_PaxcounterConfig_init_default} #define meshtastic_LocalModuleConfig_init_default {false, meshtastic_ModuleConfig_MQTTConfig_init_default, false, meshtastic_ModuleConfig_SerialConfig_init_default, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_default, false, meshtastic_ModuleConfig_StoreForwardConfig_init_default, false, meshtastic_ModuleConfig_RangeTestConfig_init_default, false, meshtastic_ModuleConfig_TelemetryConfig_init_default, false, meshtastic_ModuleConfig_CannedMessageConfig_init_default, 0, false, meshtastic_ModuleConfig_AudioConfig_init_default, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_default, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_default, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_default, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_default, false, meshtastic_ModuleConfig_PaxcounterConfig_init_default, false, meshtastic_ModuleConfig_StatusMessageConfig_init_default}
#define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0, false, meshtastic_Config_SecurityConfig_init_zero} #define meshtastic_LocalConfig_init_zero {false, meshtastic_Config_DeviceConfig_init_zero, false, meshtastic_Config_PositionConfig_init_zero, false, meshtastic_Config_PowerConfig_init_zero, false, meshtastic_Config_NetworkConfig_init_zero, false, meshtastic_Config_DisplayConfig_init_zero, false, meshtastic_Config_LoRaConfig_init_zero, false, meshtastic_Config_BluetoothConfig_init_zero, 0, false, meshtastic_Config_SecurityConfig_init_zero}
#define meshtastic_LocalModuleConfig_init_zero {false, meshtastic_ModuleConfig_MQTTConfig_init_zero, false, meshtastic_ModuleConfig_SerialConfig_init_zero, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero, false, meshtastic_ModuleConfig_StoreForwardConfig_init_zero, false, meshtastic_ModuleConfig_RangeTestConfig_init_zero, false, meshtastic_ModuleConfig_TelemetryConfig_init_zero, false, meshtastic_ModuleConfig_CannedMessageConfig_init_zero, 0, false, meshtastic_ModuleConfig_AudioConfig_init_zero, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_zero, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_zero, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_zero, false, meshtastic_ModuleConfig_PaxcounterConfig_init_zero} #define meshtastic_LocalModuleConfig_init_zero {false, meshtastic_ModuleConfig_MQTTConfig_init_zero, false, meshtastic_ModuleConfig_SerialConfig_init_zero, false, meshtastic_ModuleConfig_ExternalNotificationConfig_init_zero, false, meshtastic_ModuleConfig_StoreForwardConfig_init_zero, false, meshtastic_ModuleConfig_RangeTestConfig_init_zero, false, meshtastic_ModuleConfig_TelemetryConfig_init_zero, false, meshtastic_ModuleConfig_CannedMessageConfig_init_zero, 0, false, meshtastic_ModuleConfig_AudioConfig_init_zero, false, meshtastic_ModuleConfig_RemoteHardwareConfig_init_zero, false, meshtastic_ModuleConfig_NeighborInfoConfig_init_zero, false, meshtastic_ModuleConfig_AmbientLightingConfig_init_zero, false, meshtastic_ModuleConfig_DetectionSensorConfig_init_zero, false, meshtastic_ModuleConfig_PaxcounterConfig_init_zero, false, meshtastic_ModuleConfig_StatusMessageConfig_init_zero}
/* Field tags (for use in manual encoding/decoding) */ /* Field tags (for use in manual encoding/decoding) */
#define meshtastic_LocalConfig_device_tag 1 #define meshtastic_LocalConfig_device_tag 1
@@ -124,6 +127,7 @@ extern "C" {
#define meshtastic_LocalModuleConfig_ambient_lighting_tag 12 #define meshtastic_LocalModuleConfig_ambient_lighting_tag 12
#define meshtastic_LocalModuleConfig_detection_sensor_tag 13 #define meshtastic_LocalModuleConfig_detection_sensor_tag 13
#define meshtastic_LocalModuleConfig_paxcounter_tag 14 #define meshtastic_LocalModuleConfig_paxcounter_tag 14
#define meshtastic_LocalModuleConfig_statusmessage_tag 15
/* Struct field encoding specification for nanopb */ /* Struct field encoding specification for nanopb */
#define meshtastic_LocalConfig_FIELDLIST(X, a) \ #define meshtastic_LocalConfig_FIELDLIST(X, a) \
@@ -161,7 +165,8 @@ X(a, STATIC, OPTIONAL, MESSAGE, remote_hardware, 10) \
X(a, STATIC, OPTIONAL, MESSAGE, neighbor_info, 11) \ X(a, STATIC, OPTIONAL, MESSAGE, neighbor_info, 11) \
X(a, STATIC, OPTIONAL, MESSAGE, ambient_lighting, 12) \ X(a, STATIC, OPTIONAL, MESSAGE, ambient_lighting, 12) \
X(a, STATIC, OPTIONAL, MESSAGE, detection_sensor, 13) \ X(a, STATIC, OPTIONAL, MESSAGE, detection_sensor, 13) \
X(a, STATIC, OPTIONAL, MESSAGE, paxcounter, 14) X(a, STATIC, OPTIONAL, MESSAGE, paxcounter, 14) \
X(a, STATIC, OPTIONAL, MESSAGE, statusmessage, 15)
#define meshtastic_LocalModuleConfig_CALLBACK NULL #define meshtastic_LocalModuleConfig_CALLBACK NULL
#define meshtastic_LocalModuleConfig_DEFAULT NULL #define meshtastic_LocalModuleConfig_DEFAULT NULL
#define meshtastic_LocalModuleConfig_mqtt_MSGTYPE meshtastic_ModuleConfig_MQTTConfig #define meshtastic_LocalModuleConfig_mqtt_MSGTYPE meshtastic_ModuleConfig_MQTTConfig
@@ -177,6 +182,7 @@ X(a, STATIC, OPTIONAL, MESSAGE, paxcounter, 14)
#define meshtastic_LocalModuleConfig_ambient_lighting_MSGTYPE meshtastic_ModuleConfig_AmbientLightingConfig #define meshtastic_LocalModuleConfig_ambient_lighting_MSGTYPE meshtastic_ModuleConfig_AmbientLightingConfig
#define meshtastic_LocalModuleConfig_detection_sensor_MSGTYPE meshtastic_ModuleConfig_DetectionSensorConfig #define meshtastic_LocalModuleConfig_detection_sensor_MSGTYPE meshtastic_ModuleConfig_DetectionSensorConfig
#define meshtastic_LocalModuleConfig_paxcounter_MSGTYPE meshtastic_ModuleConfig_PaxcounterConfig #define meshtastic_LocalModuleConfig_paxcounter_MSGTYPE meshtastic_ModuleConfig_PaxcounterConfig
#define meshtastic_LocalModuleConfig_statusmessage_MSGTYPE meshtastic_ModuleConfig_StatusMessageConfig
extern const pb_msgdesc_t meshtastic_LocalConfig_msg; extern const pb_msgdesc_t meshtastic_LocalConfig_msg;
extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg; extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
@@ -186,9 +192,9 @@ extern const pb_msgdesc_t meshtastic_LocalModuleConfig_msg;
#define meshtastic_LocalModuleConfig_fields &meshtastic_LocalModuleConfig_msg #define meshtastic_LocalModuleConfig_fields &meshtastic_LocalModuleConfig_msg
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
#define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalConfig_size #define MESHTASTIC_MESHTASTIC_LOCALONLY_PB_H_MAX_SIZE meshtastic_LocalModuleConfig_size
#define meshtastic_LocalConfig_size 749 #define meshtastic_LocalConfig_size 749
#define meshtastic_LocalModuleConfig_size 675 #define meshtastic_LocalModuleConfig_size 758
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -361,6 +361,8 @@ typedef struct _meshtastic_LocalStats {
uint32_t heap_free_bytes; uint32_t heap_free_bytes;
/* Number of packets that were dropped because the transmit queue was full. */ /* Number of packets that were dropped because the transmit queue was full. */
uint16_t num_tx_dropped; uint16_t num_tx_dropped;
/* Noise floor value measured in dBm */
int32_t noise_floor;
} meshtastic_LocalStats; } meshtastic_LocalStats;
/* Health telemetry metrics */ /* Health telemetry metrics */
@@ -458,7 +460,7 @@ extern "C" {
#define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_EnvironmentMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_PowerMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_AirQualityMetrics_init_default {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_LocalStats_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_HealthMetrics_init_default {false, 0, false, 0, false, 0} #define meshtastic_HealthMetrics_init_default {false, 0, false, 0, false, 0}
#define meshtastic_HostMetrics_init_default {0, 0, 0, false, 0, false, 0, 0, 0, 0, false, ""} #define meshtastic_HostMetrics_init_default {0, 0, 0, false, 0, false, 0, 0, 0, 0, false, ""}
#define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}} #define meshtastic_Telemetry_init_default {0, 0, {meshtastic_DeviceMetrics_init_default}}
@@ -467,7 +469,7 @@ extern "C" {
#define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_EnvironmentMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_PowerMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0} #define meshtastic_AirQualityMetrics_init_zero {false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0, false, 0}
#define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} #define meshtastic_LocalStats_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define meshtastic_HealthMetrics_init_zero {false, 0, false, 0, false, 0} #define meshtastic_HealthMetrics_init_zero {false, 0, false, 0, false, 0}
#define meshtastic_HostMetrics_init_zero {0, 0, 0, false, 0, false, 0, 0, 0, 0, false, ""} #define meshtastic_HostMetrics_init_zero {0, 0, 0, false, 0, false, 0, 0, 0, 0, false, ""}
#define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}} #define meshtastic_Telemetry_init_zero {0, 0, {meshtastic_DeviceMetrics_init_zero}}
@@ -556,6 +558,7 @@ extern "C" {
#define meshtastic_LocalStats_heap_total_bytes_tag 12 #define meshtastic_LocalStats_heap_total_bytes_tag 12
#define meshtastic_LocalStats_heap_free_bytes_tag 13 #define meshtastic_LocalStats_heap_free_bytes_tag 13
#define meshtastic_LocalStats_num_tx_dropped_tag 14 #define meshtastic_LocalStats_num_tx_dropped_tag 14
#define meshtastic_LocalStats_noise_floor_tag 15
#define meshtastic_HealthMetrics_heart_bpm_tag 1 #define meshtastic_HealthMetrics_heart_bpm_tag 1
#define meshtastic_HealthMetrics_spO2_tag 2 #define meshtastic_HealthMetrics_spO2_tag 2
#define meshtastic_HealthMetrics_temperature_tag 3 #define meshtastic_HealthMetrics_temperature_tag 3
@@ -678,7 +681,8 @@ X(a, STATIC, SINGULAR, UINT32, num_tx_relay, 10) \
X(a, STATIC, SINGULAR, UINT32, num_tx_relay_canceled, 11) \ X(a, STATIC, SINGULAR, UINT32, num_tx_relay_canceled, 11) \
X(a, STATIC, SINGULAR, UINT32, heap_total_bytes, 12) \ X(a, STATIC, SINGULAR, UINT32, heap_total_bytes, 12) \
X(a, STATIC, SINGULAR, UINT32, heap_free_bytes, 13) \ X(a, STATIC, SINGULAR, UINT32, heap_free_bytes, 13) \
X(a, STATIC, SINGULAR, UINT32, num_tx_dropped, 14) X(a, STATIC, SINGULAR, UINT32, num_tx_dropped, 14) \
X(a, STATIC, SINGULAR, INT32, noise_floor, 15)
#define meshtastic_LocalStats_CALLBACK NULL #define meshtastic_LocalStats_CALLBACK NULL
#define meshtastic_LocalStats_DEFAULT NULL #define meshtastic_LocalStats_DEFAULT NULL
@@ -755,7 +759,7 @@ extern const pb_msgdesc_t meshtastic_Nau7802Config_msg;
#define meshtastic_EnvironmentMetrics_size 113 #define meshtastic_EnvironmentMetrics_size 113
#define meshtastic_HealthMetrics_size 11 #define meshtastic_HealthMetrics_size 11
#define meshtastic_HostMetrics_size 264 #define meshtastic_HostMetrics_size 264
#define meshtastic_LocalStats_size 76 #define meshtastic_LocalStats_size 87
#define meshtastic_Nau7802Config_size 16 #define meshtastic_Nau7802Config_size 16
#define meshtastic_PowerMetrics_size 81 #define meshtastic_PowerMetrics_size 81
#define meshtastic_Telemetry_size 272 #define meshtastic_Telemetry_size 272

View File

@@ -7,3 +7,22 @@ custom_esp32_kind = esp32
build_flags = build_flags =
${esp32_common.build_flags} ${esp32_common.build_flags}
-DMESHTASTIC_EXCLUDE_AUDIO=1 -DMESHTASTIC_EXCLUDE_AUDIO=1
; Override lib_deps to use environmental_extra_no_bsec instead of environmental_extra
; BSEC library uses ~3.5KB DRAM which causes overflow on original ESP32 targets
lib_deps =
${arduino_base.lib_deps}
${networking_base.lib_deps}
${networking_extra.lib_deps}
${environmental_base.lib_deps}
${environmental_extra_no_bsec.lib_deps}
${radiolib_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-esp32_https_server packageName=https://github.com/meshtastic/esp32_https_server gitBranch=master
https://github.com/meshtastic/esp32_https_server/archive/3223704846752e6d545139204837bdb2a55459ca.zip
# renovate: datasource=custom.pio depName=NimBLE-Arduino packageName=h2zero/library/NimBLE-Arduino
h2zero/NimBLE-Arduino@^1.4.3
# renovate: datasource=git-refs depName=libpax packageName=https://github.com/dbinfrago/libpax gitBranch=master
https://github.com/dbinfrago/libpax/archive/3cdc0371c375676a97967547f4065607d4c53fd1.zip
# renovate: datasource=github-tags depName=XPowersLib packageName=lewisxhe/XPowersLib
https://github.com/lewisxhe/XPowersLib/archive/v0.3.2.zip
# renovate: datasource=custom.pio depName=rweather/Crypto packageName=rweather/library/Crypto
rweather/Crypto@0.4.0

View File

@@ -1,26 +0,0 @@
#ifndef Pins_Arduino_h
#define Pins_Arduino_h
#include <stdint.h>
#define USB_VID 0x303a
#define USB_PID 0x1001
// The default Wire will be mapped to PMU and RTC
static const uint8_t SDA = 18;
static const uint8_t SCL = 9;
// Default SPI will be mapped to Radio
static const uint8_t SS = -1;
static const uint8_t MOSI = 17;
static const uint8_t MISO = 6;
static const uint8_t SCK = 8;
#define SPI_MOSI (39)
#define SPI_SCK (41)
#define SPI_MISO (38)
#define SPI_CS (40)
#define SDCARD_CS SPI_CS
#endif /* Pins_Arduino_h */

View File

@@ -1,41 +0,0 @@
[env:mini-epaper-s3]
;custom_meshtastic_hw_model =
custom_meshtastic_hw_model_slug = MINI_EPAPER_S3
custom_meshtastic_architecture = esp32-s3
custom_meshtastic_actively_supported = true
custom_meshtastic_support_level = 1
custom_meshtastic_display_name = LILYGO Mini ePaper S3 E-Ink
custom_meshtastic_images = mini-epaper-s3.svg
custom_meshtastic_tags = LilyGo
custom_meshtastic_requires_dfu = no
extends = esp32s3_base
board = mini-epaper-s3
board_check = true
upload_protocol = esptool
build_flags =
${esp32s3_base.build_flags}
-I variants/esp32s3/mini-epaper-s3
-DMINI_EPAPER_S3
-DPRIVATE_HW ; TODO
-DUSE_EINK
-DEINK_DISPLAY_MODEL=GxEPD2_102
-DEINK_WIDTH=80
-DEINK_HEIGHT=128
; -DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk
; -DEINK_LIMIT_FASTREFRESH=0 ; How many consecutive fast-refreshes are permitted //20
; -DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates //30
; -DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates
; -DEINK_HASQUIRK_VICIOUSFASTREFRESH ; Identify that pixels drawn by fast-refresh are harder to clear
; -DEINK_LIMIT_GHOSTING_PX=1500 ; (Optional) How much image ghosting is tolerated
; -DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached.
lib_deps =
${esp32s3_base.lib_deps}
# renovate: datasource=custom.pio depName=GxEPD2 packageName=zinggjm/library/GxEPD2
zinggjm/GxEPD2@1.6.5
;# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
;https://github.com/meshtastic/GxEPD2/archive/a05c11c02862624266b61599b0d6ba93e33c6f24.zip
# renovate: datasource=custom.pio depName=SensorLib packageName=lewisxhe/library/SensorLib
lewisxhe/SensorLib@0.3.3

View File

@@ -1,56 +0,0 @@
// Display (E-Ink)
#define PIN_EINK_CS 13
#define PIN_EINK_BUSY 10
#define PIN_EINK_RES 11
#define PIN_EINK_SCLK 14
#define PIN_EINK_MOSI 15
#define PIN_EINK_DC 12
#define PIN_EINK_EN 42
#define SPI_INTERFACES_COUNT 2
#define PIN_SPI1_MISO -1
#define PIN_SPI1_MOSI PIN_EINK_MOSI
#define PIN_SPI1_SCK PIN_EINK_SCLK
#define I2C_SDA SDA
#define I2C_SCL SCL
#define BATTERY_PIN 2 // A battery voltage measurement pin, voltage divider connected here to
// measure battery voltage ratio of voltage divider = 2.0 (assumption)
#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage.
#define ADC_CHANNEL ADC1_GPIO2_CHANNEL
#define HAS_GPS 0
#undef GPS_RX_PIN
#undef GPS_TX_PIN
#define BUTTON_PIN 3
#define BUTTON_NEED_PULLUP
#define ALT_BUTTON_PIN 4
#define ALT_BUTTON_ACTIVE_LOW true
#define ALT_BUTTON_ACTIVE_PULLUP true
#define PIN_BUTTON3 0
// #define HAS_SDCARD 1
// #define SDCARD_USE_SOFT_SPI
// PCF85063 RTC Module
#define PCF85063_RTC 0x51
#define HAS_RTC 1
#define USE_SX1262
#define LORA_DIO1 5
#define LORA_SCK 8
#define LORA_MISO 6
#define LORA_MOSI 17
#define LORA_CS 7 // CS not connected; IO7 is free
#define LORA_RESET 21
#ifdef USE_SX1262
#define SX126X_CS LORA_CS
#define SX126X_DIO1 5
#define SX126X_BUSY 16
#define SX126X_RESET LORA_RESET
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
#endif

View File

@@ -0,0 +1,21 @@
; Russell is a board designed to mount on an ER34615/IFR32700 cell and go Up! on a balloon
; Hardware repository: https://github.com/Meshtastic-Malaysia/russell
; - RAK3172 STM32WLE5CCU6 MCU + integrated SX1262 LoRa
; - CDtop CD-PA1010D GPS
; - Bosch Sensortec BME280 sensor
; - Consonance CN3158 LiFePO4 solar charger
[env:russell]
extends = stm32_base
board = wiscore_rak3172
board_level = extra
board_upload.maximum_size = 233472 ; reserve the last 28KB for filesystem
build_flags =
${stm32_base.build_flags}
-Ivariants/stm32/russell
-DPRIVATE_HW
lib_deps =
${stm32_base.lib_deps}
# renovate: datasource=custom.pio depName=Adafruit BME280 packageName=adafruit/library/Adafruit BME280 Library
adafruit/Adafruit BME280 Library@2.3.0
upload_port = stlink

View File

@@ -0,0 +1,7 @@
// Pins from https://forum.rakwireless.com/t/rak3172-internal-schematic/4557/2
// PB8, PC13
static const RADIOLIB_PIN_TYPE rfswitch_pins[5] = {PB8, PC13, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
static const Module::RfSwitchMode_t rfswitch_table[4] = {
{STM32WLx::MODE_IDLE, {LOW, LOW}}, {STM32WLx::MODE_RX, {HIGH, LOW}}, {STM32WLx::MODE_TX_HP, {LOW, HIGH}}, END_OF_MODE_TABLE};

View File

@@ -0,0 +1,41 @@
#ifndef _VARIANT_RUSSELL_
#define _VARIANT_RUSSELL_
#define USE_STM32WLx
// I/O
#define LED_PIN PA0 // Red LED
#define LED_STATE_ON 1
#define BUTTON_PIN PH3 // Shared with BOOT0
#define BUTTON_NEED_PULLUP
// Charger IC charge/standby pins are open-drain with no hardware pull-up:
// Internal pull-up is needed on STM32 (TODO)
// #define EXT_CHRG_DETECT PA5
// #define EXT_PWR_DETECT PA4
// Bosch Sensortec BME280
#define HAS_SENSOR 1
// CDtop CD-PA1010D
#define ENABLE_HWSERIAL1
#define PIN_SERIAL1_RX PB7
#define PIN_SERIAL1_TX PB6
#define HAS_GPS 1
#define PIN_GPS_STANDBY PA15
#define GPS_RX_PIN PB7
#define GPS_TX_PIN PB6
// LoRa
/*
* RAK3172 (-2085°C) -> No TCXO
* RAK3172-T (-4085°C) -> 3.0V TCXO
* https://github.com/RAKWireless/RAK-STM32-RUI/blob/e5a28be8fab1a492bd9223dd425ca33a8a297d90/variants/WisDuo_RAK3172-T_Board/radio_conf.h#L91
*/
#define TCXO_OPTIONAL
#define SX126X_DIO3_TCXO_VOLTAGE 3.0
// Required to avoid Serial1 conflicts due to board definition here:
// https://github.com/stm32duino/Arduino_Core_STM32/blob/main/variants/STM32WLxx/WL54CCU_WL55CCU_WLE4C(8-B-C)U_WLE5C(8-B-C)U/variant_RAK3172_MODULE.h
#define RAK3172
#endif