Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 38 additions & 4 deletions src/graphics/emotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ namespace graphics

// Always define Emote list and count
const Emote emotes[] = {
#ifndef EXCLUDE_EMOJI
#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_CORE
// --- Thumbs ---
{"\U0001F44D", thumbup, thumbs_width, thumbs_height}, // 👍 Thumbs Up
{"\U0001F44E", thumbdown, thumbs_width, thumbs_height}, // 👎 Thumbs Down
#endif // EMOTE_SET_CORE
Comment on lines +10 to +14

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -eu

tmp="$(mktemp --suffix=.cpp)"
trap 'rm -f "$tmp"' EXIT

cat >"$tmp" <<'EOF'
struct Emote {};
const Emote emotes[] = {};
EOF

"${CXX:-c++}" -std=c++17 -pedantic-errors -fsyntax-only "$tmp"

Repository: meshtastic/firmware

Length of output: 199


🏁 Script executed:

#!/usr/bin/env bash
set -eu

echo "CXX availability:"
command -v c++ || true
command -v clang++ || true
command -v g++ || true

echo
echo "Relevant files:"
fd -a 'emotes.cpp|platformio.ini' . | sed 's#^\./##' | head -100

echo
echo "Search for MESHTASTIC_EMOTE_SET definitions/usages:"
rg -n "MESHTASTIC_EMOTE_SET|EMOTE_SET_NONE|enum.*Emote|enum class Emote|struct Emote|const Emote emotes" .

Repository: meshtastic/firmware

Length of output: 9453


🏁 Script executed:

#!/usr/bin/env bash
set -eu

echo "emotes.cpp first 35 lines:"
sed -n '1,35p' src/graphics/emotes.cpp | cat -n

echo
echo "emotes.cpp lines 217-235:"
sed -n '217,235p' src/graphics/emotes.cpp | cat -n

echo
echo "emotes.h relevant section:"
sed -n '40,55p' src/graphics/emotes.h | cat -n

echo
echo "clang C++17 compile test for unknown-bound empty aggregate initializer:"
tmp="$(mktemp --suffix=.cpp)"
trap 'rm -f "$tmp"' EXIT
cat >"$tmp" <<'EOF'
struct Emote {
    const char* label;
};
const Emote emotes[] = {};
EOF
if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++17 -pedantic-errors -fsyntax-only "$tmp" || true
fi

echo
echo "clang C++17 compile test for bounded empty aggregate initializer:"
tmp2="$(mktemp --suffix=.cpp)"
trap 'rm -f "$tmp2"' EXIT
cat >"$tmp2" <<'EOF'
struct Emote {
    const char* label;
};
const Emote emotes[1] = {};
EOF
if command -v clang++ >/dev/null 2>&1; then
  clang++ -std=c++17 -pedantic-errors -fsyntax-only "$tmp2" || true
fi

Repository: meshtastic/firmware

Length of output: 4519


Fix the empty emotes[] definition for EMOTE_SET_NONE.

With MESHTASTIC_EMOTE_SET=EMOTE_SET_NONE, every initializer is removed, leaving an unknown-bound empty array declaration that fails before numEmotes or the static assertion are reached. Use a placeholder ONE-element array for the NONE branch and keep numEmotes at 0; use the current table for non-empty tiers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/graphics/emotes.cpp` around lines 10 - 14, Update the emotes[] definition
in src/graphics/emotes.cpp to provide a one-element placeholder array when
MESHTASTIC_EMOTE_SET is EMOTE_SET_NONE, while keeping numEmotes equal to 0 for
that branch. Preserve the existing emote table for all non-empty tiers so the
static assertion and normal counts remain unchanged.


#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_STANDARD
// --- Smileys (Multiple Unicode Aliases) ---
{"\U0001F60A", smiling_eyes, smiling_eyes_width, smiling_eyes_height}, // 😊 Smiling Eyes
{"\U0001F600", grinning, grinning_width, grinning_height}, // 😀 Grinning Face
Expand All @@ -24,13 +26,17 @@ const Emote emotes[] = {
{"\U0001F976", cold_face, cold_face_width, cold_face_height}, // 🥶 Cold Face
{"\U0001F629", weary_face, weary_face_width, weary_face_height}, // 😩 Weary Face
{"\U0001F62E", open_mouth, open_mouth_width, open_mouth_height}, // 😮 Open Mouth
#endif // EMOTE_SET_STANDARD

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_CORE
// --- Question/Alert ---
{"\u2753", question, question_width, question_height}, // ❓ Question Mark
{"\u203C\uFE0F", bang, bang_width, bang_height}, // ‼️ Double Exclamation Mark
{"\u26A0\uFE0F", caution, caution_width, caution_height}, // ⚠️ Warning Sign
{"\U0001F4F6", antenna_bars, antenna_bars_width, antenna_bars_height}, // 📶 Antenna Bars
#endif // EMOTE_SET_CORE

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_STANDARD
// --- Laughing Faces ---
{"\U0001F602", haha, haha_width, haha_height}, // 😂 Face with Tears of Joy
{"\U0001F923", rofl, rofl_width, rofl_height}, // 🤣 Rolling on the Floor Laughing
Expand All @@ -44,7 +50,9 @@ const Emote emotes[] = {
{"\U0001F634", sleeping_face, sleeping_face_width, sleeping_face_height}, // 😴 Sleeping Face
{"\U0001F440", eyes, eyes_width, eyes_height}, // 👀 Eyes
{"\U0001F441\uFE0F", eye, eye_width, eye_height}, // 👁️ Eye
#endif // EMOTE_SET_STANDARD

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_STANDARD
// --- Gestures and People ---
{"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // 👋 Waving Hand
{"\u270C\uFE0F", peace_sign, peace_sign_width, peace_sign_height}, // ✌️ Victory Hand
Expand All @@ -55,15 +63,19 @@ const Emote emotes[] = {
{"\U0001F937", shrug, shrug_width, shrug_height}, // 🤷 Person Shrugging
{"\U0001F920", cowboy, cowboy_width, cowboy_height}, // 🤠 Cowboy Hat Face
{"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // 🎧 Headphones
#endif // EMOTE_SET_STANDARD

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_CORE
// --- Symbols ---
{"\u2714\uFE0F", check_mark, check_mark_width, check_mark_height}, // ✔️ Check Mark
{"\u2705", check_mark, check_mark_width, check_mark_height}, // ✅ Check Mark Button
{"\u2611\uFE0F", check_mark, check_mark_width, check_mark_height}, // ☑️ Check Box with Check
{"\U0001F3E0", house, house_width, house_height}, // 🏠 House
{"\U0001F5FC", tower, tower_width, tower_height}, // 🗼 Tokyo Tower
{"\U0001F4AF", one_hundred, one_hundred_width, one_hundred_height}, // 💯 Hundred Points Symbol
#endif // EMOTE_SET_CORE

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_STANDARD
// --- Weather ---
{"\u2600", sun, sun_width, sun_height}, // ☀ Sun (without variation selector)
{"\u2600\uFE0F", sun, sun_width, sun_height}, // ☀️ Sun (with variation selector)
Expand All @@ -90,7 +102,9 @@ const Emote emotes[] = {
{"\U0001F304", sunrise, sunrise_width, sunrise_height}, // 🌄 Sunrise
{"\U0001F306", sunset, sunset_width, sunset_height}, // 🌆 Sunset
{"\U0001F307", sunset, sunset_width, sunset_height}, // 🌇 Sunset
#endif // EMOTE_SET_STANDARD

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_FULL
// --- Moon Phases ---
{"\U0001F311", new_moon, new_moon_width, new_moon_height}, // 🌑 New Moon
{"\U0001F312", waxing_crescent_moon, waxing_crescent_moon_width, waxing_crescent_moon_height}, // 🌒 Waxing Crescent Moon
Expand All @@ -102,13 +116,17 @@ const Emote emotes[] = {
{"\U0001F318", waning_crescent_moon, waning_crescent_moon_width, waning_crescent_moon_height}, // 🌘 Waning Crescent Moon
{"\U0001F31B", first_quarter_moon_face, first_quarter_moon_face_width,
first_quarter_moon_face_height}, // 🌛 First Quarter Moon Face
#endif // EMOTE_SET_FULL

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_STANDARD
// --- Misc Faces ---
{"\U0001F608", devil, devil_width, devil_height}, // 😈 Smiling Face with Horns
{"\U0001F921", clown, clown_width, clown_height}, // 🤡 Clown Face
{"\U0001F916", robo, robo_width, robo_height}, // 🤖 Robot Face
{"\U0001F479", ogre, ogre_width, ogre_height}, // 👹 Ogre
#endif // EMOTE_SET_STANDARD

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_CORE
// --- Hearts (Multiple Unicode Aliases) ---
{"\u2665", heart, heart_width, heart_height}, // ♥ Black Heart Suit
{"\u2665\uFE0F", heart, heart_width, heart_height}, // ♥️ Black Heart Suit (emoji presentation)
Expand All @@ -120,7 +138,9 @@ const Emote emotes[] = {
{"\U0001F496", heart, heart_width, heart_height}, // 💖 Sparkling Heart
{"\U0001F497", heart, heart_width, heart_height}, // 💗 Growing Heart
{"\U0001F498", heart, heart_width, heart_height}, // 💘 Heart with Arrow
#endif // EMOTE_SET_CORE

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_FULL
// --- Objects ---
{"\U0001F4A9", poo, poo_width, poo_height}, // 💩 Pile of Poo
{"\U0001F514", bell_icon, bell_icon_width, bell_icon_height}, // 🔔 Bell
Expand All @@ -138,7 +158,9 @@ const Emote emotes[] = {
{"\U0001F37A", beer, beer_width, beer_height},
{"\U0001F954", potato, potato_width, potato_height}, // 🥔 Potato
{"\U0001FAB5", wood, wood_width, wood_height}, // 🪵 Wood
#endif // EMOTE_SET_FULL

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_FULL
// --- Arrows ---
{"\u2193", downwards_arrow, downwards_arrow_width, downwards_arrow_height}, // ↓ Downwards Arrow
{"\u2193\uFE0E", downwards_arrow, downwards_arrow_width, downwards_arrow_height}, // ↓︎ Downwards Arrow (text)
Expand Down Expand Up @@ -172,12 +194,16 @@ const Emote emotes[] = {
{"\u2198", south_east_arrow, south_east_arrow_width, south_east_arrow_height}, // ↘ South East Arrow
{"\u2198\uFE0E", south_east_arrow, south_east_arrow_width, south_east_arrow_height}, // ↘︎ South East Arrow (text)
{"\u2198\uFE0F", south_east_arrow, south_east_arrow_width, south_east_arrow_height}, // ↘️ South East Arrow (emoji)
#endif // EMOTE_SET_FULL

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_FULL
// --- Halloween ---
{"\U0001F383", jack_o_lantern, jack_o_lantern_width, jack_o_lantern_height}, // 🎃 Jack-O-Lantern
{"\U0001F47B", ghost, ghost_width, ghost_height}, // 👻 Ghost
{"\U0001F480", skull, skull_width, skull_height}, // 💀 Skull
#endif // EMOTE_SET_FULL

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_FULL
// --- Holidays ---
{"\U0001F384", christmas_tree, christmas_tree_width, christmas_tree_height}, // 🎄 Christmas Tree
{"\U0001F381", wrapped_gift, wrapped_gift_width, wrapped_gift_height}, // 🎁 Wrapped Gift
Expand All @@ -186,15 +212,23 @@ const Emote emotes[] = {
{"\u2721\uFE0F", star_of_david, star_of_david_width, star_of_david_height}, // ✡️ Star of David
{"\u26C4", snow_man, snow_man_width, snow_man_height}, // ⛄ Snowman
{"\u2603\uFE0F", snow_man, snow_man_width, snow_man_height}, // ☃️ Snowman
#endif // EMOTE_SET_FULL

#if MESHTASTIC_EMOTE_SET >= EMOTE_SET_FULL
// --- Misc ---
{"\U0001F4A8", dashing_away, dashing_away_width, dashing_away_height} // 💨 Dashing Away
#endif
{"\U0001F4A8", dashing_away, dashing_away_width, dashing_away_height}, // 💨 Dashing Away
#endif // EMOTE_SET_FULL
};

const int numEmotes = sizeof(emotes) / sizeof(emotes[0]);

#ifndef EXCLUDE_EMOJI
#if MESHTASTIC_EMOTE_SET > EMOTE_SET_NONE
// A mis-gated section that empties the table above EMOTE_SET_NONE would not fail to
// build - it would ship a picker with nothing in it - so catch it here instead.
static_assert(sizeof(emotes) / sizeof(emotes[0]) > 0, "emote set is non-empty above EMOTE_SET_NONE");
#endif

#if MESHTASTIC_EMOTE_SET > EMOTE_SET_NONE
const unsigned char thumbup[] PROGMEM = {0x00, 0x03, 0x80, 0x04, 0x80, 0x04, 0x40, 0x04, 0x20, 0x02, 0x18,
0x02, 0x06, 0x3F, 0x06, 0x40, 0x06, 0x70, 0x06, 0x40, 0x06, 0x70,
0x06, 0x40, 0x06, 0x30, 0x08, 0x20, 0xF0, 0x1F, 0x00, 0x00};
Expand Down
39 changes: 37 additions & 2 deletions src/graphics/emotes.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,41 @@
#pragma once
#include "configuration.h"
#include "memory/FlashClass.h"
#include <Arduino.h>

// === Emote set ladder ===
// The emote table is pure flash: 16x16 XBM bitmaps in PROGMEM plus a 146-entry
// index. It costs no RAM, so it tiers off MESHTASTIC_FLASH_CLASS (memory/FlashClass.h)
// rather than the RAM ladder in memory/MemClass.h.
//
// EMOTE_SET_NONE no bitmaps; emoji render as their text label
// EMOTE_SET_CORE acknowledgement, alerts, mesh status, heart
// EMOTE_SET_STANDARD + faces, gestures and weather
// EMOTE_SET_FULL + moon phases, objects, arrows, seasonal
//
// Compare ordinally (>=). Each section of the table in emotes.cpp carries its own
// tier gate, so picker order is unchanged as tiers drop out. Dropping entries leaves
// their bitmaps unreferenced and -fdata-sections/--gc-sections discards them, so the
// saving is the index plus the bitmap data plus (at NONE, under LTO) the picker itself.
#define EMOTE_SET_NONE 0
#define EMOTE_SET_CORE 1
#define EMOTE_SET_STANDARD 2
#define EMOTE_SET_FULL 3

#ifndef MESHTASTIC_EMOTE_SET
#ifdef EXCLUDE_EMOJI // long-standing spelling of "no emotes at all"
#define MESHTASTIC_EMOTE_SET EMOTE_SET_NONE
#elif MESHTASTIC_FLASH_CLASS <= FLASH_CLASS_TINY
#define MESHTASTIC_EMOTE_SET EMOTE_SET_NONE
#elif MESHTASTIC_FLASH_CLASS == FLASH_CLASS_SMALL
// nRF52840 is the tightest screen-capable region we ship - rak4631 sits at 97% of it
// on develop - so this class trades the seasonal/decorative tail for headroom.
#define MESHTASTIC_EMOTE_SET EMOTE_SET_STANDARD
#else
#define MESHTASTIC_EMOTE_SET EMOTE_SET_FULL
#endif
#endif // MESHTASTIC_EMOTE_SET

namespace graphics
{

Expand All @@ -15,7 +50,7 @@ struct Emote {
extern const Emote emotes[/* numEmotes */];
extern const int numEmotes;

#ifndef EXCLUDE_EMOJI
#if MESHTASTIC_EMOTE_SET > EMOTE_SET_NONE
// === Emote Bitmaps ===
#define thumbs_height 16
#define thumbs_width 16
Expand Down Expand Up @@ -425,6 +460,6 @@ extern const unsigned char wood[] PROGMEM;
#define beer_width 16
#define beer_height 16
extern const unsigned char beer[] PROGMEM;
#endif // EXCLUDE_EMOJI
#endif // MESHTASTIC_EMOTE_SET > EMOTE_SET_NONE

} // namespace graphics
46 changes: 46 additions & 0 deletions src/memory/FlashClass.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#pragma once

// Central flash-class ladder: flash-sized tables key their per-platform tier off
// MESHTASTIC_FLASH_CLASS. Classes rank the *app flash region* a target actually has
// to spend - not raw part size, and deliberately not RAM.
//
// FLASH_CLASS_TINY <256 KB app region STM32WL
// FLASH_CLASS_SMALL ~784 KB nRF52840 (0x26000..0xEA000, warm store reserved)
// FLASH_CLASS_MEDIUM ~1.9-3 MB app part. classic ESP32 / S2 / C3, RP2040/RP2350
// FLASH_CLASS_LARGE 4 MB+ app, or host ESP32-S3 / C6 / P4, portduino
//
// This is the sibling of memory/MemClass.h, and the two ladders disagree on purpose.
// MemClass ranks usable app heap; a chip can be poor in one resource and rich in the
// other. Classic ESP32 and nRF52840 are both MEM_CLASS_SMALL, but the ESP32 has well
// over twice the app flash, so a flash-shaped decision keyed off MESHTASTIC_MEM_CLASS
// would trim the wrong targets. The reverse trap is just as real within a single class:
// rak4631 and nrf52_promicro_diy_tcxo are the same chip and the same FLASH_CLASS_SMALL,
// yet one ships with ~22 KB of headroom and the other does not fit at all - so a class
// is a *default*, never a verdict. Tight variants predefine their own value.
//
// Compare ordinally (>=). An unclassified chip lands in SMALL on purpose: a smaller
// table is a recoverable default, an image that overruns its region is not. Consumer
// ladders stay #ifndef-guarded so a variant can always override. Requires the ARCH_*
// macros, so include after (or via) configuration.h.

#define FLASH_CLASS_TINY 1
#define FLASH_CLASS_SMALL 2
#define FLASH_CLASS_MEDIUM 3
#define FLASH_CLASS_LARGE 4

#ifndef MESHTASTIC_FLASH_CLASS
#if defined(ARCH_STM32WL)
#define MESHTASTIC_FLASH_CLASS FLASH_CLASS_TINY
#elif defined(ARCH_NRF52) || defined(ARCH_NRF54L15)
#define MESHTASTIC_FLASH_CLASS FLASH_CLASS_SMALL
#elif defined(ARCH_PORTDUINO) || defined(CONFIG_IDF_TARGET_ESP32S3) || defined(CONFIG_IDF_TARGET_ESP32C6) || \
defined(CONFIG_IDF_TARGET_ESP32P4)
// S3/C6/P4 boards overwhelmingly ship 8-16 MB. The 4 MB-part S3 variants that run
// close to their app partition predefine MESHTASTIC_FLASH_CLASS instead.
#define MESHTASTIC_FLASH_CLASS FLASH_CLASS_LARGE
#elif defined(ARCH_ESP32) || defined(ARCH_RP2040)
#define MESHTASTIC_FLASH_CLASS FLASH_CLASS_MEDIUM
#else
#define MESHTASTIC_FLASH_CLASS FLASH_CLASS_SMALL
#endif
#endif // MESHTASTIC_FLASH_CLASS
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ board = promicro-nrf52840
build_flags = ${nrf52840_base.build_flags}
-I variants/nrf52840/diy/nrf52_promicro_diy_tcxo
-D NRF52_PROMICRO_DIY
-D EXCLUDE_EMOJI ; this variant builds 4 radio driver families and is the largest nrf52 image; emote bitmaps don't fit under the 0xEA000 warm-store cap
-D MESHTASTIC_EMOTE_SET=EMOTE_SET_NONE ; this variant builds 4 radio driver families and is the largest nrf52 image; even the CORE emote set doesn't fit under the 0xEA000 warm-store cap
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/diy/nrf52_promicro_diy_tcxo>
debug_tool = jlink

Expand Down
Loading