diff --git a/.github/workflows/release_channels.yml b/.github/workflows/release_channels.yml
index d5d642db4..4e5a48dfe 100644
--- a/.github/workflows/release_channels.yml
+++ b/.github/workflows/release_channels.yml
@@ -61,6 +61,9 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v5
+ with:
+ # Always use master branch for version bumps
+ ref: master
- name: Setup Python
uses: actions/setup-python@v6
diff --git a/README.md b/README.md
index a53fe9646..f34bf1839 100644
--- a/README.md
+++ b/README.md
@@ -37,4 +37,3 @@ Join our community and help improve Meshtastic! ๐
## Stats

-
diff --git a/arch/stm32/stm32.ini b/arch/stm32/stm32.ini
index 8b7d256b3..7732533c9 100644
--- a/arch/stm32/stm32.ini
+++ b/arch/stm32/stm32.ini
@@ -37,6 +37,9 @@ build_flags =
-DRADIOLIB_EXCLUDE_LR11X0=1
-DHAL_DAC_MODULE_ONLY
-DHAL_RNG_MODULE_ENABLED
+ -Wl,--wrap=__assert_func
+ -Wl,--wrap=strerror
+ -Wl,--wrap=_tzset_unlocked_r
build_src_filter =
${arduino_base.build_src_filter} - - - - - - - - - - - - - -
diff --git a/bin/org.meshtastic.meshtasticd.metainfo.xml b/bin/org.meshtastic.meshtasticd.metainfo.xml
index b59bb6202..243edca0c 100644
--- a/bin/org.meshtastic.meshtasticd.metainfo.xml
+++ b/bin/org.meshtastic.meshtasticd.metainfo.xml
@@ -87,6 +87,9 @@
+
+ https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.16
+
https://github.com/meshtastic/firmware/releases?q=tag%3Av2.7.15
diff --git a/debian/changelog b/debian/changelog
index 437e1645b..5a0f543eb 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+meshtasticd (2.7.16.0) unstable; urgency=medium
+
+ * Version 2.7.16
+
+ -- GitHub Actions Wed, 19 Nov 2025 16:12:32 +0000
+
+
meshtasticd (2.7.15.0) unstable; urgency=medium
* Version 2.7.15
diff --git a/meshtasticd.spec.rpkg b/meshtasticd.spec.rpkg
index e2da172c3..b9152c4a3 100644
--- a/meshtasticd.spec.rpkg
+++ b/meshtasticd.spec.rpkg
@@ -33,6 +33,7 @@ BuildRequires: python3dist(grpcio[protobuf])
BuildRequires: python3dist(grpcio-tools)
BuildRequires: git-core
BuildRequires: gcc-c++
+BuildRequires: (glibc-devel >= 2.38) or pkgconfig(libbsd-overlay)
BuildRequires: pkgconfig(yaml-cpp)
BuildRequires: pkgconfig(libgpiod)
BuildRequires: pkgconfig(bluez)
diff --git a/platformio.ini b/platformio.ini
index d62504ae3..d6ff155e4 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -62,7 +62,7 @@ monitor_speed = 115200
monitor_filters = direct
lib_deps =
# renovate: datasource=git-refs depName=meshtastic-esp8266-oled-ssd1306 packageName=https://github.com/meshtastic/esp8266-oled-ssd1306 gitBranch=master
- https://github.com/meshtastic/esp8266-oled-ssd1306/archive/0cbc26b1f8f61957af0475f486b362eafe7cc4e2.zip
+ https://github.com/meshtastic/esp8266-oled-ssd1306/archive/2887bf4a19f64d92c984dcc8fd5ca7429e425e4a.zip
# renovate: datasource=git-refs depName=meshtastic-OneButton packageName=https://github.com/meshtastic/OneButton gitBranch=master
https://github.com/meshtastic/OneButton/archive/fa352d668c53f290cfa480a5f79ad422cd828c70.zip
# renovate: datasource=git-refs depName=meshtastic-arduino-fsm packageName=https://github.com/meshtastic/arduino-fsm gitBranch=master
@@ -115,7 +115,8 @@ lib_deps =
[radiolib_base]
lib_deps =
# renovate: datasource=custom.pio depName=RadioLib packageName=jgromes/library/RadioLib
- jgromes/RadioLib@7.3.0
+ # jgromes/RadioLib@7.4.0
+ https://github.com/jgromes/RadioLib/archive/536c7267362e2c1345be7054ba45e503252975ff.zip
[device-ui_base]
lib_deps =
@@ -181,6 +182,8 @@ lib_deps =
dfrobot/DFRobot_BMM150@1.0.0
# renovate: datasource=custom.pio depName=Adafruit_TSL2561 packageName=adafruit/library/Adafruit TSL2561
adafruit/Adafruit TSL2561@1.1.2
+ # renovate: datasource=custom.pio depName=BH1750_WE packageName=wollewald/BH1750_WE@^1.1.10
+ wollewald/BH1750_WE@^1.1.10
; (not included in native / portduino)
[environmental_extra]
diff --git a/src/NodeStatus.h b/src/NodeStatus.h
index 60d1bdd98..550f6254a 100644
--- a/src/NodeStatus.h
+++ b/src/NodeStatus.h
@@ -14,16 +14,16 @@ class NodeStatus : public Status
CallbackObserver statusObserver =
CallbackObserver(this, &NodeStatus::updateStatus);
- uint8_t numOnline = 0;
- uint8_t numTotal = 0;
+ uint16_t numOnline = 0;
+ uint16_t numTotal = 0;
- uint8_t lastNumTotal = 0;
+ uint16_t lastNumTotal = 0;
public:
bool forceUpdate = false;
NodeStatus() { statusType = STATUS_TYPE_NODE; }
- NodeStatus(uint8_t numOnline, uint8_t numTotal, bool forceUpdate = false) : Status()
+ NodeStatus(uint16_t numOnline, uint16_t numTotal, bool forceUpdate = false) : Status()
{
this->forceUpdate = forceUpdate;
this->numOnline = numOnline;
@@ -34,11 +34,11 @@ class NodeStatus : public Status
void observe(Observable *source) { statusObserver.observe(source); }
- uint8_t getNumOnline() const { return numOnline; }
+ uint16_t getNumOnline() const { return numOnline; }
- uint8_t getNumTotal() const { return numTotal; }
+ uint16_t getNumTotal() const { return numTotal; }
- uint8_t getLastNumTotal() const { return lastNumTotal; }
+ uint16_t getLastNumTotal() const { return lastNumTotal; }
bool matches(const NodeStatus *newStatus) const
{
@@ -56,7 +56,7 @@ class NodeStatus : public Status
numTotal = newStatus->getNumTotal();
}
if (isDirty || newStatus->forceUpdate) {
- LOG_DEBUG("Node status update: %d online, %d total", numOnline, numTotal);
+ LOG_DEBUG("Node status update: %u online, %u total", numOnline, numTotal);
onNewStatus.notifyObservers(this);
}
return 0;
diff --git a/src/Power.cpp b/src/Power.cpp
index d7fd5b33b..fa8661d01 100644
--- a/src/Power.cpp
+++ b/src/Power.cpp
@@ -194,7 +194,7 @@ static HasBatteryLevel *batteryLevel; // Default to NULL for no battery level se
#ifdef BATTERY_PIN
-static void adcEnable()
+void battery_adcEnable()
{
#ifdef ADC_CTRL // enable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP
@@ -214,7 +214,7 @@ static void adcEnable()
#endif
}
-static void adcDisable()
+static void battery_adcDisable()
{
#ifdef ADC_CTRL // disable adc voltage divider when we need to read
#ifdef ADC_USE_PULLUP
@@ -320,7 +320,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
uint32_t raw = 0;
float scaled = 0;
- adcEnable();
+ battery_adcEnable();
#ifdef ARCH_ESP32 // ADC block for espressif platforms
raw = espAdcRead();
scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs);
@@ -332,7 +332,7 @@ class AnalogBatteryLevel : public HasBatteryLevel
raw = raw / BATTERY_SENSE_SAMPLES;
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
#endif
- adcDisable();
+ battery_adcDisable();
if (!initial_read_done) {
// Flush the smoothing filter with an ADC reading, if the reading is plausibly correct
@@ -906,13 +906,8 @@ void Power::readPowerStatus()
low_voltage_counter++;
LOG_DEBUG("Low voltage counter: %d/10", low_voltage_counter);
if (low_voltage_counter > 10) {
-#ifdef ARCH_NRF52
- // We can't trigger deep sleep on NRF52, it's freezing the board
- LOG_DEBUG("Low voltage detected, but not trigger deep sleep");
-#else
LOG_INFO("Low voltage detected, trigger deep sleep");
powerFSM.trigger(EVENT_LOW_BATTERY);
-#endif
}
} else {
low_voltage_counter = 0;
@@ -1552,4 +1547,4 @@ bool Power::meshSolarInit()
{
return false;
}
-#endif
\ No newline at end of file
+#endif
diff --git a/src/configuration.h b/src/configuration.h
index 75cdac552..8ec3b2211 100644
--- a/src/configuration.h
+++ b/src/configuration.h
@@ -228,6 +228,7 @@ along with this program. If not, see .
#define ICM20948_ADDR_ALT 0x68
#define BHI260AP_ADDR 0x28
#define BMM150_ADDR 0x13
+#define DA217_ADDR 0x26
// -----------------------------------------------------------------------------
// LED
diff --git a/src/detect/ScanI2CTwoWire.cpp b/src/detect/ScanI2CTwoWire.cpp
index 5372fbebd..167728ad3 100644
--- a/src/detect/ScanI2CTwoWire.cpp
+++ b/src/detect/ScanI2CTwoWire.cpp
@@ -106,6 +106,7 @@ uint16_t ScanI2CTwoWire::getRegisterValue(const ScanI2CTwoWire::RegisterLocation
if (i2cBus->available())
i2cBus->read();
}
+ LOG_DEBUG("Register value: 0x%x", value);
return value;
}
@@ -377,14 +378,13 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
}
case SHT31_4x_ADDR: // same as OPT3001_ADDR_ALT
case SHT31_4x_ADDR_ALT: // same as OPT3001_ADDR
- registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2);
- if (registerValue == 0x11a2 || registerValue == 0x11da || registerValue == 0x11f3 || registerValue == 0xe9c ||
- registerValue == 0xc8d) {
- type = SHT4X;
- logFoundDevice("SHT4X", (uint8_t)addr.address);
- } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2) == 0x5449) {
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x7E), 2);
+ if (registerValue == 0x5449) {
type = OPT3001;
logFoundDevice("OPT3001", (uint8_t)addr.address);
+ } else if (getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x89), 2) != 0) { // unique SHT4x serial number
+ type = SHT4X;
+ logFoundDevice("SHT4X", (uint8_t)addr.address);
} else {
type = SHT31;
logFoundDevice("SHT31", (uint8_t)addr.address);
@@ -465,8 +465,23 @@ void ScanI2CTwoWire::scanPort(I2CPort port, uint8_t *address, uint8_t asize)
break;
SCAN_SIMPLE_CASE(LSM6DS3_ADDR, LSM6DS3, "LSM6DS3", (uint8_t)addr.address);
- SCAN_SIMPLE_CASE(TCA9555_ADDR, TCA9555, "TCA9555", (uint8_t)addr.address);
SCAN_SIMPLE_CASE(VEML7700_ADDR, VEML7700, "VEML7700", (uint8_t)addr.address);
+ case TCA9555_ADDR:
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x01), 1);
+ if (registerValue == 0x13) {
+ registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x00), 1);
+ if (registerValue == 0x81) {
+ type = DA217;
+ logFoundDevice("DA217", (uint8_t)addr.address);
+ } else {
+ type = TCA9555;
+ logFoundDevice("TCA9555", (uint8_t)addr.address);
+ }
+ } else {
+ type = TCA9555;
+ logFoundDevice("TCA9555", (uint8_t)addr.address);
+ }
+ break;
case TSL25911_ADDR:
registerValue = getRegisterValue(ScanI2CTwoWire::RegisterLocation(addr, 0x12), 1);
if (registerValue == 0x50) {
diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp
index 6c6cdba6d..0404ae5f8 100644
--- a/src/gps/GPS.cpp
+++ b/src/gps/GPS.cpp
@@ -240,6 +240,9 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
buffer[bytesRead] = b;
bytesRead++;
if ((bytesRead == 767) || (b == '\r')) {
+#ifdef GPS_DEBUG
+ LOG_DEBUG(debugmsg.c_str());
+#endif
if (strnstr((char *)buffer, message, bytesRead) != nullptr) {
#ifdef GPS_DEBUG
LOG_DEBUG("Found: %s", message); // Log the found message
@@ -247,9 +250,6 @@ GPS_RESPONSE GPS::getACK(const char *message, uint32_t waitMillis)
return GNSS_RESPONSE_OK;
} else {
bytesRead = 0;
-#ifdef GPS_DEBUG
- LOG_DEBUG(debugmsg.c_str());
-#endif
}
}
}
@@ -1275,6 +1275,24 @@ GnssModel_t GPS::probe(int serialSpeed)
memset(&ublox_info, 0, sizeof(ublox_info));
delay(100);
+#if defined(PIN_GPS_RESET) && PIN_GPS_RESET != -1
+ digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
+ delay(10);
+ digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
+
+ // attempt to detect the chip based on boot messages
+ std::vector passive_detect = {
+ {"AG3335", "$PAIR021,AG3335", GNSS_MODEL_AG3335},
+ {"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
+ {"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352},
+ {"UC6580", "UC6580", GNSS_MODEL_UC6580},
+ // as L76K is sort of a last ditch effort, we won't attempt to detect it by startup messages for now.
+ /*{"L76K", "SW=URANUS", GNSS_MODEL_MTK}*/};
+ GnssModel_t detectedDriver = getProbeResponse(500, passive_detect, serialSpeed);
+ if (detectedDriver != GNSS_MODEL_UNKNOWN) {
+ return detectedDriver;
+ }
+#endif
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
_serial_gps->write("$PCAS03,0,0,0,0,0,0,0,0,0,0,,,0,0*02\r\n");
delay(20);
@@ -1473,12 +1491,12 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n')) {
-#ifdef GPS_DEBUG
- LOG_DEBUG(response);
-#endif
// check if we can see our chips
for (const auto &chipInfo : responseMap) {
if (strstr(response, chipInfo.detectionString.c_str()) != nullptr) {
+#ifdef GPS_DEBUG
+ LOG_DEBUG(response);
+#endif
LOG_INFO("%s detected", chipInfo.chipName.c_str());
delete[] response; // Cleanup before return
return chipInfo.driver;
@@ -1486,6 +1504,9 @@ GnssModel_t GPS::getProbeResponse(unsigned long timeout, const std::vector= 2 && response[responseLen - 2] == '\r' && response[responseLen - 1] == '\n') {
+#ifdef GPS_DEBUG
+ LOG_DEBUG(response);
+#endif
// Reset the response buffer for the next potential message
responseLen = 0;
response[0] = '\0';
@@ -1572,8 +1593,6 @@ GPS *GPS::createGps()
#ifdef PIN_GPS_RESET
pinMode(PIN_GPS_RESET, OUTPUT);
- digitalWrite(PIN_GPS_RESET, GPS_RESET_MODE); // assert for 10ms
- delay(10);
digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE);
#endif
diff --git a/src/gps/RTC.cpp b/src/gps/RTC.cpp
index 665a9aaa3..692f3c2d2 100644
--- a/src/gps/RTC.cpp
+++ b/src/gps/RTC.cpp
@@ -310,7 +310,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
#ifdef BUILD_EPOCH
if (tv.tv_sec < BUILD_EPOCH) {
if (Throttle::isWithinTimespanMs(lastTimeValidationWarning, TIME_VALIDATION_WARNING_INTERVAL_MS) == false) {
- LOG_WARN("Ignore time (%ld) before build epoch (%ld)!", printableEpoch, BUILD_EPOCH);
+ LOG_WARN("Ignore time (%lu) before build epoch (%lu)!", printableEpoch, BUILD_EPOCH);
lastTimeValidationWarning = millis();
}
return RTCSetResultInvalidTime;
@@ -319,7 +319,7 @@ RTCSetResult perhapsSetRTC(RTCQuality q, struct tm &t)
// Calculate max allowed time safely to avoid overflow in logging
uint64_t maxAllowedTime = (uint64_t)BUILD_EPOCH + FORTY_YEARS;
uint32_t maxAllowedPrintable = (maxAllowedTime > UINT32_MAX) ? UINT32_MAX : (uint32_t)maxAllowedTime;
- LOG_WARN("Ignore time (%ld) too far in the future (build epoch: %ld, max allowed: %ld)!", printableEpoch,
+ LOG_WARN("Ignore time (%lu) too far in the future (build epoch: %lu, max allowed: %lu)!", printableEpoch,
(uint32_t)BUILD_EPOCH, maxAllowedPrintable);
lastTimeValidationWarning = millis();
}
diff --git a/src/graphics/TFTDisplay.cpp b/src/graphics/TFTDisplay.cpp
index f7906c013..87593b0d4 100644
--- a/src/graphics/TFTDisplay.cpp
+++ b/src/graphics/TFTDisplay.cpp
@@ -483,8 +483,6 @@ class LGFX : public lgfx::LGFX_Device
lgfx::Touch_FT5x06 _touch_instance;
#elif defined(HELTEC_V4_TFT)
lgfx::TOUCH_CHSC6X _touch_instance;
-#elif defined(HELTEC_V4_TFT)
- lgfx::TOUCH_CHSC6X _touch_instance;
#else
lgfx::Touch_GT911 _touch_instance;
#endif
diff --git a/src/graphics/TimeFormatters.cpp b/src/graphics/TimeFormatters.cpp
index 47036078b..0a1c23341 100644
--- a/src/graphics/TimeFormatters.cpp
+++ b/src/graphics/TimeFormatters.cpp
@@ -101,3 +101,23 @@ void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength)
else
snprintf(timeStr, maxLength, "unknown age");
}
+
+void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs)
+{
+ uint32_t days = uptimeMillis / 86400000;
+ uint32_t hours = (uptimeMillis % 86400000) / 3600000;
+ uint32_t mins = (uptimeMillis % 3600000) / 60000;
+ uint32_t secs = (uptimeMillis % 60000) / 1000;
+
+ if (days) {
+ snprintf(uptimeStr, maxLength, "%s: %ud %uh", prefix, days, hours);
+ } else if (hours) {
+ snprintf(uptimeStr, maxLength, "%s: %uh %um", prefix, hours, mins);
+ } else if (!includeSecs) {
+ snprintf(uptimeStr, maxLength, "%s: %um", prefix, mins);
+ } else if (mins) {
+ snprintf(uptimeStr, maxLength, "%s: %um %us", prefix, mins, secs);
+ } else {
+ snprintf(uptimeStr, maxLength, "%s: %us", prefix, secs);
+ }
+}
diff --git a/src/graphics/TimeFormatters.h b/src/graphics/TimeFormatters.h
index b3d8413a2..f86c6725c 100644
--- a/src/graphics/TimeFormatters.h
+++ b/src/graphics/TimeFormatters.h
@@ -24,3 +24,10 @@ bool deltaToTimestamp(uint32_t secondsAgo, uint8_t *hours, uint8_t *minutes, int
* @param maxLength Maximum length of the resulting string buffer
*/
void getTimeAgoStr(uint32_t agoSecs, char *timeStr, uint8_t maxLength);
+
+/**
+ * Get a compact human-readable string that only shows the largest non-zero time components.
+ * For example, 0 days 1 hour 2 minutes will display as "1h 2m" but 1 day 2 hours 3 minutes
+ * will display as "1d 2h".
+ */
+void getUptimeStr(uint32_t uptimeMillis, const char *prefix, char *uptimeStr, uint8_t maxLength, bool includeSecs = false);
diff --git a/src/graphics/draw/ClockRenderer.cpp b/src/graphics/draw/ClockRenderer.cpp
index 97417571b..cc6a70957 100644
--- a/src/graphics/draw/ClockRenderer.cpp
+++ b/src/graphics/draw/ClockRenderer.cpp
@@ -194,17 +194,12 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
int line = 0;
-#ifdef T_WATCH_S3
- if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
- graphics::ClockRenderer::drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
- }
-#endif
-
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice, true); // Display local timezone
char timeString[16];
int hour = 0;
int minute = 0;
int second = 0;
+
if (rtc_sec > 0) {
long hms = rtc_sec % SEC_PER_DAY;
hms = (hms + SEC_PER_DAY) % SEC_PER_DAY;
@@ -215,11 +210,11 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
}
bool isPM = hour >= 12;
- // hour = hour > 12 ? hour - 12 : hour;
if (config.display.use_12h_clock) {
hour %= 12;
- if (hour == 0)
+ if (hour == 0) {
hour = 12;
+ }
snprintf(timeString, sizeof(timeString), "%d:%02d", hour, minute);
} else {
snprintf(timeString, sizeof(timeString), "%02d:%02d", hour, minute);
@@ -229,24 +224,56 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
char secondString[8];
snprintf(secondString, sizeof(secondString), "%02d", second);
-#ifdef T_WATCH_S3
- float scale = 1.5;
-#elif defined(CHATTER_2)
- float scale = 1.1;
-#else
- float scale = 0.75;
- if (isHighResolution) {
- scale = 1.5;
- }
-#endif
+ static bool scaleInitialized = false;
+ static float scale = 0.75f;
+ static float segmentWidth = SEGMENT_WIDTH * 0.75f;
+ static float segmentHeight = SEGMENT_HEIGHT * 0.75f;
- uint16_t segmentWidth = SEGMENT_WIDTH * scale;
- uint16_t segmentHeight = SEGMENT_HEIGHT * scale;
+ if (!scaleInitialized) {
+ float screenwidth_target_ratio = 0.80f; // Target 80% of display width (adjustable)
+ float max_scale = 3.5f; // Safety limit to avoid runaway scaling
+ float step = 0.05f; // Step increment per iteration
+
+ float target_width = display->getWidth() * screenwidth_target_ratio;
+ float target_height =
+ display->getHeight() -
+ (isHighResolution
+ ? 46
+ : 33); // Be careful adjusting this number, we have to account for header and the text under the time
+
+ float calculated_width_size = 0.0f;
+ float calculated_height_size = 0.0f;
+
+ while (true) {
+ segmentWidth = SEGMENT_WIDTH * scale;
+ segmentHeight = SEGMENT_HEIGHT * scale;
+
+ calculated_width_size = segmentHeight + ((segmentWidth + (segmentHeight * 2) + 4) * 4);
+ calculated_height_size = segmentHeight + ((segmentHeight + (segmentHeight * 2) + 4) * 2);
+
+ if (calculated_width_size >= target_width || calculated_height_size >= target_height || scale >= max_scale) {
+ break;
+ }
+
+ scale += step;
+ }
+
+ // If we overshot width, back off one step and recompute segment sizes
+ if (calculated_width_size > target_width || calculated_height_size > target_height) {
+ scale -= step;
+ segmentWidth = SEGMENT_WIDTH * scale;
+ segmentHeight = SEGMENT_HEIGHT * scale;
+ }
+
+ scaleInitialized = true;
+ }
+
+ size_t len = strlen(timeString);
// calculate hours:minutes string width
- uint16_t timeStringWidth = strlen(timeString) * 5;
+ uint16_t timeStringWidth = len * 5; // base spacing between characters
- for (uint8_t i = 0; i < strlen(timeString); i++) {
+ for (size_t i = 0; i < len; i++) {
char character = timeString[i];
if (character == ':') {
@@ -257,19 +284,21 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
}
uint16_t hourMinuteTextX = (display->getWidth() / 2) - (timeStringWidth / 2);
-
uint16_t startingHourMinuteTextX = hourMinuteTextX;
- uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2);
+ uint16_t hourMinuteTextY = (display->getHeight() / 2) - (((segmentWidth * 2) + (segmentHeight * 3) + 8) / 2) + 2;
// iterate over characters in hours:minutes string and draw segmented characters
- for (uint8_t i = 0; i < strlen(timeString); i++) {
+ for (size_t i = 0; i < len; i++) {
char character = timeString[i];
if (character == ':') {
drawSegmentedDisplayColon(display, hourMinuteTextX, hourMinuteTextY, scale);
hourMinuteTextX += segmentHeight + 6;
+ if (scale >= 2.0f) {
+ hourMinuteTextX += (uint16_t)(4.5f * scale);
+ }
} else {
drawSegmentedDisplayCharacter(display, hourMinuteTextX, hourMinuteTextY, character - '0', scale);
@@ -279,38 +308,29 @@ void drawDigitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int1
hourMinuteTextX += 5;
}
- // draw seconds string
+ // draw seconds string + AM/PM
display->setFont(FONT_SMALL);
int xOffset = (isHighResolution) ? 0 : -1;
if (hour >= 10) {
xOffset += (isHighResolution) ? 32 : 18;
}
- int yOffset = (isHighResolution) ? 3 : 1;
-#ifdef SENSECAP_INDICATOR
- yOffset -= 3;
-#endif
-#ifdef T_DECK
- yOffset -= 5;
-#endif
+
if (config.display.use_12h_clock) {
- display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - yOffset - 2,
- isPM ? "pm" : "am");
+ display->drawString(startingHourMinuteTextX + xOffset, (display->getHeight() - hourMinuteTextY) - 1, isPM ? "pm" : "am");
}
#ifndef USE_EINK
xOffset = (isHighResolution) ? 18 : 10;
- display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - yOffset,
+ if (scale >= 2.0f) {
+ xOffset -= (int)(4.5f * scale);
+ }
+ display->drawString(startingHourMinuteTextX + timeStringWidth - xOffset, (display->getHeight() - hourMinuteTextY) - 1,
secondString);
#endif
graphics::drawCommonFooter(display, x, y);
}
-void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y)
-{
- display->drawFastImage(x, y, 18, 14, bluetoothConnectedIcon);
-}
-
// Draw an analog clock
void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
{
@@ -321,11 +341,6 @@ void drawAnalogClockFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16
graphics::drawCommonHeader(display, x, y, titleStr, true, true);
int line = 0;
-#ifdef T_WATCH_S3
- if (nimbleBluetooth && nimbleBluetooth->isConnected()) {
- drawBluetoothConnectedIcon(display, display->getWidth() - 18, display->getHeight() - 14);
- }
-#endif
// clock face center coordinates
int16_t centerX = display->getWidth() / 2;
int16_t centerY = display->getHeight() / 2;
diff --git a/src/graphics/draw/ClockRenderer.h b/src/graphics/draw/ClockRenderer.h
index c8ba62868..eace26cf5 100644
--- a/src/graphics/draw/ClockRenderer.h
+++ b/src/graphics/draw/ClockRenderer.h
@@ -24,7 +24,6 @@ void drawVerticalSegment(OLEDDisplay *display, int x, int y, int width, int heig
// UI elements for clock displays
// void drawWatchFaceToggleButton(OLEDDisplay *display, int16_t x, int16_t y, bool digitalMode = true, float scale = 1);
-void drawBluetoothConnectedIcon(OLEDDisplay *display, int16_t x, int16_t y);
} // namespace ClockRenderer
diff --git a/src/graphics/draw/DebugRenderer.cpp b/src/graphics/draw/DebugRenderer.cpp
index d098fa304..79c1e7e61 100644
--- a/src/graphics/draw/DebugRenderer.cpp
+++ b/src/graphics/draw/DebugRenderer.cpp
@@ -11,6 +11,7 @@
#include "gps/RTC.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
+#include "graphics/TimeFormatters.h"
#include "graphics/images.h"
#include "main.h"
#include "mesh/Channels.h"
@@ -650,17 +651,7 @@ void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x
if (SCREEN_HEIGHT > 64 || (SCREEN_HEIGHT <= 64 && line <= 5)) { // Only show uptime if the screen can show it
char uptimeStr[32] = "";
- uint32_t uptime = millis() / 1000;
- uint32_t days = uptime / 86400;
- uint32_t hours = (uptime % 86400) / 3600;
- uint32_t mins = (uptime % 3600) / 60;
- // Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
- if (days)
- snprintf(uptimeStr, sizeof(uptimeStr), " Up: %ud %uh", days, hours);
- else if (hours)
- snprintf(uptimeStr, sizeof(uptimeStr), " Up: %uh %um", hours, mins);
- else
- snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
+ getUptimeStr(millis(), "Up", uptimeStr, sizeof(uptimeStr));
textWidth = display->getStringWidth(uptimeStr);
nameX = (SCREEN_WIDTH - textWidth) / 2;
display->drawString(nameX, getTextPositions(display)[line++], uptimeStr);
@@ -729,4 +720,4 @@ void drawChirpy(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int1
} // namespace DebugRenderer
} // namespace graphics
-#endif
\ No newline at end of file
+#endif
diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp
index 7d70ec35a..c22ff23f9 100644
--- a/src/graphics/draw/MenuHandler.cpp
+++ b/src/graphics/draw/MenuHandler.cpp
@@ -574,21 +574,16 @@ void menuHandler::textMessageBaseMenu()
void menuHandler::systemBaseMenu()
{
- enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, FrameToggles, Test, enumEnd };
+ enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, Test, enumEnd };
static const char *optionsArray[enumEnd] = {"Back"};
static int optionsEnumArray[enumEnd] = {Back};
int options = 1;
optionsArray[options] = "Notifications";
optionsEnumArray[options++] = Notifications;
-#if defined(ST7789_CS) || defined(ST7796_CS) || defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || \
- defined(USE_SH1107) || defined(HELTEC_MESH_NODE_T114) || defined(HELTEC_VISION_MASTER_T190) || HAS_TFT
- optionsArray[options] = "Screen Options";
+ optionsArray[options] = "Display Options";
optionsEnumArray[options++] = ScreenOptions;
-#endif
- optionsArray[options] = "Frame Visiblity Toggle";
- optionsEnumArray[options++] = FrameToggles;
#if defined(M5STACK_UNITC6L)
optionsArray[options] = "Bluetooth";
#else
@@ -626,9 +621,6 @@ void menuHandler::systemBaseMenu()
} else if (selected == PowerMenu) {
menuHandler::menuQueue = menuHandler::power_menu;
screen->runNow();
- } else if (selected == FrameToggles) {
- menuHandler::menuQueue = menuHandler::FrameToggles;
- screen->runNow();
} else if (selected == Test) {
menuHandler::menuQueue = menuHandler::test_menu;
screen->runNow();
@@ -1339,7 +1331,7 @@ void menuHandler::screenOptionsMenu()
hasSupportBrightness = false;
#endif
- enum optionsNumbers { Back, NodeNameLength, Brightness, ScreenColor };
+ enum optionsNumbers { Back, NodeNameLength, Brightness, ScreenColor, FrameToggles, DisplayUnits };
static const char *optionsArray[5] = {"Back"};
static int optionsEnumArray[5] = {Back};
int options = 1;
@@ -1361,8 +1353,14 @@ void menuHandler::screenOptionsMenu()
optionsEnumArray[options++] = ScreenColor;
#endif
+ optionsArray[options] = "Frame Visibility Toggle";
+ optionsEnumArray[options++] = FrameToggles;
+
+ optionsArray[options] = "Display Units";
+ optionsEnumArray[options++] = DisplayUnits;
+
BannerOverlayOptions bannerOptions;
- bannerOptions.message = "Screen Options";
+ bannerOptions.message = "Display Options";
bannerOptions.optionsArrayPtr = optionsArray;
bannerOptions.optionsCount = options;
bannerOptions.optionsEnumPtr = optionsEnumArray;
@@ -1376,6 +1374,12 @@ void menuHandler::screenOptionsMenu()
} else if (selected == NodeNameLength) {
menuHandler::menuQueue = menuHandler::node_name_length_menu;
screen->runNow();
+ } else if (selected == FrameToggles) {
+ menuHandler::menuQueue = menuHandler::FrameToggles;
+ screen->runNow();
+ } else if (selected == DisplayUnits) {
+ menuHandler::menuQueue = menuHandler::DisplayUnits;
+ screen->runNow();
} else {
menuQueue = system_base_menu;
screen->runNow();
@@ -1587,6 +1591,34 @@ void menuHandler::FrameToggles_menu()
screen->showOverlayBanner(bannerOptions);
}
+void menuHandler::DisplayUnits_menu()
+{
+ enum optionsNumbers { Back, MetricUnits, ImperialUnits };
+
+ static const char *optionsArray[] = {"Back", "Metric", "Imperial"};
+ BannerOverlayOptions bannerOptions;
+ bannerOptions.message = " Select display units";
+ bannerOptions.optionsArrayPtr = optionsArray;
+ bannerOptions.optionsCount = 3;
+ if (config.display.units == meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL)
+ bannerOptions.InitialSelected = 2;
+ else
+ bannerOptions.InitialSelected = 1;
+ bannerOptions.bannerCallback = [](int selected) -> void {
+ if (selected == MetricUnits) {
+ config.display.units = meshtastic_Config_DisplayConfig_DisplayUnits_METRIC;
+ service->reloadConfig(SEGMENT_CONFIG);
+ } else if (selected == ImperialUnits) {
+ config.display.units = meshtastic_Config_DisplayConfig_DisplayUnits_IMPERIAL;
+ service->reloadConfig(SEGMENT_CONFIG);
+ } else {
+ menuHandler::menuQueue = menuHandler::screen_options_menu;
+ screen->runNow();
+ }
+ };
+ screen->showOverlayBanner(bannerOptions);
+}
+
void menuHandler::handleMenuSwitch(OLEDDisplay *display)
{
if (menuQueue != menu_none)
@@ -1701,6 +1733,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display)
case FrameToggles:
FrameToggles_menu();
break;
+ case DisplayUnits:
+ DisplayUnits_menu();
+ break;
case throttle_message:
screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000);
break;
diff --git a/src/graphics/draw/MenuHandler.h b/src/graphics/draw/MenuHandler.h
index 1f7bbac8c..a611b7c9d 100644
--- a/src/graphics/draw/MenuHandler.h
+++ b/src/graphics/draw/MenuHandler.h
@@ -44,7 +44,8 @@ class menuHandler
trace_route_menu,
throttle_message,
node_name_length_menu,
- FrameToggles
+ FrameToggles,
+ DisplayUnits
};
static screenMenus menuQueue;
@@ -88,6 +89,7 @@ class menuHandler
static void powerMenu();
static void nodeNameLengthMenu();
static void FrameToggles_menu();
+ static void DisplayUnits_menu();
static void textMessageMenu();
private:
diff --git a/src/graphics/draw/NodeListRenderer.cpp b/src/graphics/draw/NodeListRenderer.cpp
index 80a85b8cf..600e81ef7 100644
--- a/src/graphics/draw/NodeListRenderer.cpp
+++ b/src/graphics/draw/NodeListRenderer.cpp
@@ -425,6 +425,12 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
{
const int COMMON_HEADER_HEIGHT = FONT_HEIGHT_SMALL - 1;
const int rowYOffset = FONT_HEIGHT_SMALL - 3;
+ bool locationScreen = false;
+
+ if (strcmp(title, "Bearings") == 0)
+ locationScreen = true;
+ else if (strcmp(title, "Distance") == 0)
+ locationScreen = true;
#if defined(M5STACK_UNITC6L)
int columnWidth = display->getWidth();
#else
@@ -440,7 +446,7 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
int totalEntries = nodeDB->getNumMeshNodes();
int totalRowsAvailable = (display->getHeight() - y) / rowYOffset;
-
+ int numskipped = 0;
int visibleNodeRows = totalRowsAvailable;
#if defined(M5STACK_UNITC6L)
int totalColumns = 1;
@@ -460,6 +466,10 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
int rowCount = 0;
for (int i = startIndex; i < endIndex; ++i) {
+ if (locationScreen && !nodeDB->getMeshNodeByIndex(i)->has_position) {
+ numskipped++;
+ continue;
+ }
int xPos = x + (col * columnWidth);
int yPos = y + yOffset;
renderer(display, nodeDB->getMeshNodeByIndex(i), xPos, yPos, columnWidth);
@@ -482,6 +492,9 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t
}
}
+ // This should correct the scrollbar
+ totalEntries -= numskipped;
+
#if !defined(M5STACK_UNITC6L)
// Draw column separator
if (shownCount > 0) {
diff --git a/src/graphics/draw/UIRenderer.cpp b/src/graphics/draw/UIRenderer.cpp
index e616c9688..9262074fb 100644
--- a/src/graphics/draw/UIRenderer.cpp
+++ b/src/graphics/draw/UIRenderer.cpp
@@ -11,6 +11,7 @@
#include "graphics/Screen.h"
#include "graphics/ScreenFonts.h"
#include "graphics/SharedUIDisplay.h"
+#include "graphics/TimeFormatters.h"
#include "graphics/images.h"
#include "main.h"
#include "target_specific.h"
@@ -383,17 +384,7 @@ void UIRenderer::drawNodeInfo(OLEDDisplay *display, const OLEDDisplayUiState *st
// === 4. Uptime (only show if metric is present) ===
char uptimeStr[32] = "";
if (node->has_device_metrics && node->device_metrics.has_uptime_seconds) {
- uint32_t uptime = node->device_metrics.uptime_seconds;
- uint32_t days = uptime / 86400;
- uint32_t hours = (uptime % 86400) / 3600;
- uint32_t mins = (uptime % 3600) / 60;
- // Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
- if (days)
- snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %ud %uh", days, hours);
- else if (hours)
- snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %uh %um", hours, mins);
- else
- snprintf(uptimeStr, sizeof(uptimeStr), " Uptime: %um", mins);
+ getUptimeStr(node->device_metrics.uptime_seconds * 1000, " Up", uptimeStr, sizeof(uptimeStr));
}
if (uptimeStr[0] && line < 5) {
display->drawString(x, getTextPositions(display)[line++], uptimeStr);
@@ -592,18 +583,8 @@ void UIRenderer::drawDeviceFocused(OLEDDisplay *display, OLEDDisplayUiState *sta
drawNodes(display, x + 1, getTextPositions(display)[line] + 2, nodeStatus, -1, false, "online");
#endif
char uptimeStr[32] = "";
- uint32_t uptime = millis() / 1000;
- uint32_t days = uptime / 86400;
- uint32_t hours = (uptime % 86400) / 3600;
- uint32_t mins = (uptime % 3600) / 60;
- // Show as "Up: 2d 3h", "Up: 5h 14m", or "Up: 37m"
#if !defined(M5STACK_UNITC6L)
- if (days)
- snprintf(uptimeStr, sizeof(uptimeStr), "Up: %ud %uh", days, hours);
- else if (hours)
- snprintf(uptimeStr, sizeof(uptimeStr), "Up: %uh %um", hours, mins);
- else
- snprintf(uptimeStr, sizeof(uptimeStr), "Up: %um", mins);
+ getUptimeStr(millis(), "Up", uptimeStr, sizeof(uptimeStr));
#endif
display->drawString(SCREEN_WIDTH - display->getStringWidth(uptimeStr), getTextPositions(display)[line++], uptimeStr);
@@ -1048,36 +1029,17 @@ void UIRenderer::drawCompassAndLocationScreen(OLEDDisplay *display, OLEDDisplayU
if (strcmp(displayLine, "GPS off") != 0 && strcmp(displayLine, "No GPS") != 0) {
// === Second Row: Last GPS Fix ===
if (gpsStatus->getLastFixMillis() > 0) {
- uint32_t delta = (millis() - gpsStatus->getLastFixMillis()) / 1000; // seconds since last fix
- uint32_t days = delta / 86400;
- uint32_t hours = (delta % 86400) / 3600;
- uint32_t mins = (delta % 3600) / 60;
- uint32_t secs = delta % 60;
-
- char buf[32];
+ uint32_t delta = millis() - gpsStatus->getLastFixMillis();
+ char uptimeStr[32];
#if defined(USE_EINK)
// E-Ink: skip seconds, show only days/hours/mins
- if (days > 0) {
- snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
- } else if (hours > 0) {
- snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
- } else {
- snprintf(buf, sizeof(buf), "Last: %um", mins);
- }
+ getUptimeStr(delta, "Last", uptimeStr, sizeof(uptimeStr), false);
#else
// Non E-Ink: include seconds where useful
- if (days > 0) {
- snprintf(buf, sizeof(buf), "Last: %ud %uh", days, hours);
- } else if (hours > 0) {
- snprintf(buf, sizeof(buf), "Last: %uh %um", hours, mins);
- } else if (mins > 0) {
- snprintf(buf, sizeof(buf), "Last: %um %us", mins, secs);
- } else {
- snprintf(buf, sizeof(buf), "Last: %us", secs);
- }
+ getUptimeStr(delta, "Last", uptimeStr, sizeof(uptimeStr), true);
#endif
- display->drawString(0, getTextPositions(display)[line++], buf);
+ display->drawString(0, getTextPositions(display)[line++], uptimeStr);
} else {
display->drawString(0, getTextPositions(display)[line++], "Last: ?");
}
@@ -1422,4 +1384,4 @@ std::string UIRenderer::drawTimeDelta(uint32_t days, uint32_t hours, uint32_t mi
} // namespace graphics
-#endif // HAS_SCREEN
\ No newline at end of file
+#endif // HAS_SCREEN
diff --git a/src/graphics/emotes.cpp b/src/graphics/emotes.cpp
index a0227d365..bed2b7b7c 100644
--- a/src/graphics/emotes.cpp
+++ b/src/graphics/emotes.cpp
@@ -18,6 +18,8 @@ const Emote emotes[] = {
{"\U0001F642", Slightly_Smiling, Slightly_Smiling_width, Slightly_Smiling_height}, // ๐ Slightly Smiling Face
{"\U0001F609", Winking_Face, Winking_Face_width, Winking_Face_height}, // ๐ Winking Face
{"\U0001F601", Grinning_Smiling_Eyes, Grinning_Smiling_Eyes_width, Grinning_Smiling_Eyes_height}, // ๐ Grinning Smiling Eyes
+ {"\U0001F60D", Heart_eyes, Heart_eyes_width, Heart_eyes_height}, // ๐ Heart Eyes
+ {"\U0001F970", heart_smile, heart_smile_width, heart_smile_height}, // ๐ฅฐ Smiling Face with Hearts
// --- Question/Alert ---
{"\u2753", question, question_width, question_height}, // โ Question Mark
@@ -30,11 +32,15 @@ const Emote emotes[] = {
{"\U0001F605", haha, haha_width, haha_height}, // ๐
Smiling with Sweat
{"\U0001F604", Grinning_SmilingEyes2, Grinning_SmilingEyes2_width,
Grinning_SmilingEyes2_height}, // ๐ Grinning Face with Smiling Eyes
+ {"\U0001F62D", Loudly_Crying_Face, Loudly_Crying_Face_width, Loudly_Crying_Face_height}, // ๐ญ Loudly Crying Face
// --- Gestures and People ---
- {"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // ๐ Waving Hand
- {"\U0001F920", cowboy, cowboy_width, cowboy_height}, // ๐ค Cowboy Hat Face
- {"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // ๐ง Headphones
+ {"\U0001F44B", wave_icon, wave_icon_width, wave_icon_height}, // ๐ Waving Hand
+ {"\u270C\uFE0F", peace_sign, peace_sign_width, peace_sign_height}, // โ๏ธ Victory Hand
+ {"\U0001F596", vulcan_salute, vulcan_salute_width, vulcan_salute_height}, // ๐ Vulcan Salute
+ {"\U0001F64F", Praying, Praying_width, Praying_height}, // ๐ Praying Hands
+ {"\U0001F920", cowboy, cowboy_width, cowboy_height}, // ๐ค Cowboy Hat Face
+ {"\U0001F3A7", deadmau5, deadmau5_width, deadmau5_height}, // ๐ง Headphones
// --- Weather ---
{"\u2600", sun, sun_width, sun_height}, // โ Sun (without variation selector)
@@ -45,8 +51,12 @@ const Emote emotes[] = {
// --- 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
// --- 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)
{"\u2764\uFE0F", heart, heart_width, heart_height}, // โค๏ธ Red Heart
{"\U0001F9E1", heart, heart_width, heart_height}, // ๐งก Orange Heart
{"\U00002763", heart, heart_width, heart_height}, // โฃ Heart Exclamation
@@ -57,223 +67,166 @@ const Emote emotes[] = {
{"\U0001F498", heart, heart_width, heart_height}, // ๐ Heart with Arrow
// --- Objects ---
- {"\U0001F4A9", poo, poo_width, poo_height}, // ๐ฉ Pile of Poo
- {"\U0001F514", bell_icon, bell_icon_width, bell_icon_height} // ๐ Bell
+ {"\U0001F4A9", poo, poo_width, poo_height}, // ๐ฉ Pile of Poo
+ {"\U0001F514", bell_icon, bell_icon_width, bell_icon_height}, // ๐ Bell
+ {"\U0001F36A", cookie, cookie_width, cookie_height}, // ๐ช Cookie
+ {"\U0001F525", Fire, Fire_width, Fire_height}, // ๐ฅ Fire
+ {"\u2728", Sparkles, Sparkles_width, Sparkles_height}, // โจ Sparkles
+ {"\U0001F573\uFE0F", hole, hole_width, hole_height}, // ๐ณ๏ธ Hole
+ {"\U0001F3B3", bowling, bowling_width, bowling_height} // ๐ณ Bowling
#endif
};
const int numEmotes = sizeof(emotes) / sizeof(emotes[0]);
#ifndef EXCLUDE_EMOJI
-const unsigned char thumbup[] PROGMEM = {
- 0x00, 0x1C, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x80, 0x09, 0x00, 0x00,
- 0xC0, 0x08, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00,
- 0x0C, 0xCE, 0x7F, 0x00, 0x04, 0x20, 0x80, 0x00, 0x02, 0x20, 0x80, 0x00, 0x02, 0x60, 0xC0, 0x00, 0x01, 0xF8, 0xFF, 0x01,
- 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0x18, 0x80, 0x00,
- 0x02, 0x30, 0xC0, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x38, 0x20, 0x10, 0x00, 0xE0, 0xCF, 0x1F, 0x00,
-};
+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};
-const unsigned char thumbdown[] PROGMEM = {
- 0xE0, 0xCF, 0x1F, 0x00, 0x38, 0x20, 0x10, 0x00, 0x0C, 0x20, 0x30, 0x00, 0x06, 0xE0, 0x3F, 0x00, 0x02, 0x30, 0xC0, 0x00,
- 0x01, 0x18, 0x80, 0x00, 0x01, 0x10, 0x80, 0x00, 0x01, 0xF8, 0xFF, 0x00, 0x01, 0x08, 0x00, 0x01, 0x01, 0x08, 0x00, 0x01,
- 0x01, 0xF8, 0xFF, 0x01, 0x02, 0x60, 0xC0, 0x00, 0x02, 0x20, 0x80, 0x00, 0x04, 0x20, 0x80, 0x00, 0x0C, 0xCE, 0x7F, 0x00,
- 0x18, 0x02, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x20, 0x04, 0x00, 0x00, 0x40, 0x08, 0x00, 0x00, 0xC0, 0x08, 0x00, 0x00,
- 0x80, 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00,
-};
+const unsigned char thumbdown[] PROGMEM = {0xF0, 0x1F, 0x08, 0x20, 0x06, 0x30, 0x06, 0x40, 0x06, 0x70, 0x06,
+ 0x40, 0x06, 0x70, 0x06, 0x40, 0x06, 0x3F, 0x18, 0x02, 0x20, 0x02,
+ 0x40, 0x04, 0x80, 0x04, 0x80, 0x04, 0x00, 0x03, 0x00, 0x00};
-const unsigned char Smiling_Eyes[] PROGMEM = {
- 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
- 0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xcf, 0xfc, 0xff, 0xff, 0xcf,
- 0x7e, 0xf8, 0xc3, 0xdf, 0x3e, 0xf0, 0x81, 0xdf, 0xbf, 0xf7, 0xbd, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9f, 0xff, 0x3f, 0xff,
- 0x6f, 0xff, 0xdf, 0xfe, 0x6f, 0xff, 0xdf, 0xfe, 0x9f, 0xff, 0x3f, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
- 0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
- 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x07, 0xc0};
+const unsigned char Smiling_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
+ 0x4A, 0x02, 0x40, 0x02, 0x40, 0x22, 0x44, 0x22, 0x44, 0xC2, 0x43,
+ 0x04, 0x20, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char Grinning[] PROGMEM = {
- 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
- 0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf9, 0xf3, 0xcf, 0xfc, 0xf0, 0xe1, 0xcf,
- 0xfe, 0xf0, 0xe1, 0xdf, 0xfe, 0xf0, 0xe1, 0xdf, 0xff, 0xf0, 0xe1, 0xff, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xbe, 0xff, 0xbf, 0xdf, 0x7e, 0x00, 0xc0, 0xdf,
- 0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
- 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+const unsigned char Grinning[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
+ 0x42, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
+ 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char Slightly_Smiling[] PROGMEM = {
- 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
- 0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf9, 0xf3, 0xcf, 0xfc, 0xf0, 0xe1, 0xcf,
- 0xfe, 0xf0, 0xe1, 0xdf, 0xfe, 0xf0, 0xe1, 0xdf, 0xff, 0xf0, 0xe1, 0xff, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
- 0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
- 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+const unsigned char Slightly_Smiling[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x22, 0x42,
+ 0x42, 0x02, 0x40, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
+ 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char Winking_Face[] PROGMEM = {
- 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
- 0xf0, 0xf0, 0xff, 0xc3, 0x78, 0xef, 0xc3, 0xc7, 0xb8, 0xdf, 0xbd, 0xcf, 0xfc, 0xf9, 0x7f, 0xcf, 0xfc, 0xf0, 0xff, 0xcf,
- 0xfe, 0xf0, 0xc3, 0xdf, 0xfe, 0xf0, 0x81, 0xdf, 0xff, 0xf0, 0xbf, 0xff, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0x7e, 0xff, 0xdf, 0xdf,
- 0x7c, 0xff, 0xdf, 0xcf, 0xfc, 0xfe, 0xef, 0xcf, 0xf8, 0xf9, 0xf7, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
- 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x07, 0xc0};
+const unsigned char Winking_Face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x44, 0x20, 0x42,
+ 0x46, 0x02, 0x40, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
+ 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char Grinning_Smiling_Eyes[] PROGMEM = {
- 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
- 0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0xfc, 0xf8, 0xe3, 0xcf, 0x7c, 0xf7, 0xdd, 0xcf,
- 0xbe, 0xef, 0xbe, 0xdf, 0xbe, 0xef, 0xbe, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0x5e, 0x55, 0x55, 0xdf, 0x5e, 0x55, 0x55, 0xdf,
- 0x3c, 0x00, 0x80, 0xcf, 0x7c, 0x55, 0xd5, 0xcf, 0xf8, 0x54, 0xe5, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
- 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+const unsigned char Grinning_Smiling_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
+ 0x4A, 0x02, 0x40, 0xFA, 0x5F, 0x0A, 0x50, 0x0A, 0x50, 0x12, 0x48,
+ 0x24, 0x24, 0xC4, 0x23, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char question[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x80, 0xFF, 0x01, 0x00, 0xC0, 0xFF, 0x07, 0x00, 0xE0, 0xFF, 0x07, 0x00,
- 0xE0, 0xC3, 0x0F, 0x00, 0xF0, 0x81, 0x0F, 0x00, 0xF0, 0x01, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x80, 0x0F, 0x00,
- 0x00, 0xC0, 0x0F, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xF8, 0x01, 0x00, 0x00, 0x7C, 0x00, 0x00,
- 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
+const unsigned char heart_smile[] PROGMEM = {0x00, 0x00, 0x6C, 0x07, 0x7C, 0x18, 0x7C, 0x20, 0x38, 0x24, 0x52,
+ 0x0A, 0x02, 0xD8, 0x02, 0xF8, 0x22, 0xFC, 0x20, 0x74, 0xDB, 0x23,
+ 0x1F, 0x00, 0x1F, 0x20, 0x0E, 0x18, 0xE4, 0x07, 0x00, 0x00};
-const unsigned char bang[] PROGMEM = {
- 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x0F, 0xFC, 0x3F, 0xFF, 0x07, 0xF8, 0x3F, 0xFF, 0x07, 0xF8, 0x3F,
- 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F,
- 0xFE, 0x03, 0xF0, 0x1F, 0xFE, 0x03, 0xF0, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F,
- 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x03, 0xF0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F, 0xFC, 0x01, 0xE0, 0x0F,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x00, 0xC0, 0x03, 0xFC, 0x03, 0xF0, 0x0F, 0xFE, 0x03, 0xF0, 0x1F,
- 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFE, 0x07, 0xF8, 0x1F, 0xFC, 0x03, 0xF0, 0x0F, 0xF8, 0x01, 0xE0, 0x07,
-};
+const unsigned char Heart_eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x54, 0x2A, 0xFA,
+ 0x5F, 0x72, 0x4E, 0x22, 0x44, 0x02, 0x40, 0x12, 0x48, 0x12, 0x48,
+ 0x24, 0x24, 0xC4, 0x23, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char haha[] PROGMEM = {
- 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0xe0, 0xf9, 0xf3, 0xc0,
- 0xf0, 0xfe, 0xef, 0xc1, 0x38, 0xff, 0x9f, 0xc3, 0xd8, 0xff, 0x7f, 0xc3, 0xfc, 0xf8, 0xe3, 0xc7, 0x7c, 0xf7, 0xdd, 0xcf,
- 0xbe, 0xef, 0xbe, 0xcf, 0xfe, 0xff, 0xff, 0xcf, 0xef, 0xff, 0xff, 0xde, 0xe7, 0xff, 0xff, 0xdc, 0xeb, 0xff, 0xff, 0xda,
- 0xed, 0xff, 0xff, 0xd6, 0xee, 0xff, 0xff, 0xce, 0x36, 0x00, 0x80, 0xcd, 0xb8, 0xff, 0xbf, 0xc3, 0x7e, 0x00, 0xc0, 0xdf,
- 0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
- 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+const unsigned char question[] PROGMEM = {0xE0, 0x07, 0x10, 0x08, 0x08, 0x10, 0x88, 0x11, 0x48, 0x12, 0x48,
+ 0x12, 0x48, 0x12, 0x30, 0x11, 0x80, 0x08, 0x40, 0x04, 0x40, 0x02,
+ 0xC0, 0x03, 0x00, 0x00, 0xC0, 0x03, 0x40, 0x02, 0x80, 0x01};
-const unsigned char ROFL[] PROGMEM = {
- 0x00, 0x00, 0x00, 0xc0, 0x00, 0xfc, 0x07, 0xc0, 0x00, 0xff, 0x1f, 0xc0, 0x80, 0xff, 0x7f, 0xc0, 0xc0, 0xff, 0xff, 0xc0,
- 0xe0, 0x9f, 0xff, 0xc1, 0xf0, 0x9f, 0xff, 0xc0, 0xf8, 0x9f, 0x7f, 0xcb, 0xf8, 0x9f, 0xbf, 0xcb, 0xfc, 0x9f, 0xdf, 0xdb,
- 0xfc, 0x1f, 0x08, 0xdc, 0xfe, 0x1f, 0xf8, 0xfe, 0xfe, 0xff, 0xff, 0xfe, 0x1e, 0xf0, 0x7f, 0xfe, 0x1e, 0xf0, 0xbf, 0xfe,
- 0xfe, 0xf3, 0xdf, 0xfe, 0xfe, 0xf3, 0x6f, 0xfe, 0xfe, 0xf3, 0x37, 0xfe, 0xfe, 0xeb, 0x1b, 0xfe, 0xfc, 0xef, 0x0d, 0xde,
- 0xfc, 0xe7, 0x06, 0xcf, 0xf8, 0x6b, 0x83, 0xcf, 0xf8, 0x0d, 0xc0, 0xc7, 0xf0, 0xed, 0xff, 0xc7, 0xe0, 0xee, 0xff, 0xc3,
- 0xc0, 0xee, 0xff, 0xc1, 0x80, 0xee, 0xff, 0xc0, 0x00, 0xe6, 0x3f, 0xc0, 0x00, 0xf0, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0xc0};
+const unsigned char bang[] PROGMEM = {0x30, 0x0C, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48,
+ 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x48, 0x12, 0x30, 0x0C,
+ 0x00, 0x00, 0x30, 0x0C, 0x48, 0x12, 0x30, 0x0C, 0x00, 0x00};
-const unsigned char Smiling_Closed_Eyes[] PROGMEM = {
- 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0xff, 0xc0, 0xe0, 0xff, 0xff, 0xc1,
- 0xf0, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xcf, 0x7c, 0xfe, 0xcf, 0xcf, 0xfc, 0xfc, 0xe7, 0xcf,
- 0xfe, 0xf9, 0xf3, 0xdf, 0xfe, 0xf3, 0xf9, 0xdf, 0xff, 0xf9, 0xf3, 0xff, 0xff, 0xfc, 0xe7, 0xff, 0x7f, 0xfe, 0xcf, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x80, 0xff, 0xbe, 0xff, 0xbf, 0xdf, 0x7e, 0x00, 0xc0, 0xdf,
- 0x7c, 0x00, 0xc0, 0xcf, 0xfc, 0x00, 0xe0, 0xcf, 0xf8, 0x01, 0xf0, 0xc7, 0xf8, 0x03, 0xf8, 0xc7, 0xf0, 0xff, 0xff, 0xc3,
- 0xe0, 0xff, 0xff, 0xc1, 0xc0, 0xff, 0xff, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+const unsigned char haha[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
+ 0x4A, 0x0A, 0x50, 0x0E, 0x70, 0xF2, 0x4F, 0x12, 0x48, 0x32, 0x44,
+ 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char Grinning_SmilingEyes2[] PROGMEM = {
- 0x00, 0xf8, 0x03, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0xe0, 0xff, 0xff, 0xc0,
- 0xf0, 0xff, 0xff, 0xc1, 0xf8, 0xff, 0xff, 0xc3, 0xf8, 0xff, 0xff, 0xc3, 0xfc, 0xf8, 0xe3, 0xc7, 0x7c, 0xf7, 0xdd, 0xc7,
- 0xbe, 0xef, 0xbe, 0xcf, 0xfe, 0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf,
- 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xdf, 0x3f, 0x00, 0x80, 0xdf, 0xbe, 0xff, 0xbf, 0xcf, 0x7e, 0x00, 0xc0, 0xcf,
- 0x7c, 0x00, 0xc0, 0xc7, 0xfc, 0x00, 0xe0, 0xc7, 0xf8, 0x01, 0xf0, 0xc3, 0xf8, 0x03, 0xf8, 0xc3, 0xf0, 0xff, 0xff, 0xc1,
- 0xe0, 0xff, 0xff, 0xc0, 0xc0, 0xff, 0x7f, 0xc0, 0x80, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0};
+const unsigned char ROFL[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x84, 0x21, 0x84, 0x20, 0x02,
+ 0x4C, 0x02, 0x4A, 0x1A, 0x49, 0x8A, 0x48, 0x42, 0x48, 0x22, 0x44,
+ 0xE4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char wave_icon[] PROGMEM = {
- 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0xc0, 0xc1, 0x00, 0x00, 0x00, 0xc7,
- 0x00, 0x00, 0x1e, 0xcc, 0x00, 0x00, 0x30, 0xc8, 0x00, 0x00, 0x60, 0xd8, 0x00, 0x08, 0xc0, 0xd0, 0x00, 0x1a, 0x81, 0xd1,
- 0x00, 0x36, 0x03, 0xd3, 0x80, 0x6d, 0x06, 0xd2, 0x00, 0xdb, 0x0c, 0xc2, 0x80, 0xb6, 0x1d, 0xc0, 0x80, 0x6d, 0x1f, 0xc0,
- 0x00, 0xdb, 0x3f, 0xc0, 0x00, 0xf6, 0x7f, 0xc0, 0x00, 0xfc, 0x7f, 0xc0, 0x08, 0xf8, 0x7f, 0xc0, 0x48, 0xf0, 0x7f, 0xc0,
- 0x48, 0xe0, 0x7f, 0xc0, 0xc8, 0xc0, 0x3f, 0xc0, 0x98, 0x81, 0x1f, 0xc0, 0x10, 0x03, 0x00, 0xc0, 0x30, 0x0e, 0x00, 0xc0,
- 0x20, 0x38, 0x00, 0xc0, 0xe0, 0x00, 0x00, 0xc0, 0x80, 0x07, 0x00, 0xc0, 0x00, 0x1e, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0};
+const unsigned char Smiling_Closed_Eyes[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x42,
+ 0x42, 0x22, 0x44, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
+ 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char cowboy[] PROGMEM = {
- 0x00, 0x0c, 0x0c, 0xc0, 0x00, 0x02, 0x10, 0xc0, 0x00, 0x01, 0x20, 0xc0, 0xbc, 0x00, 0x40, 0xcf, 0xc2, 0x01, 0xe0, 0xd0,
- 0x01, 0x01, 0x20, 0xe0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0,
- 0xc1, 0x3f, 0xff, 0xe0, 0xe1, 0xff, 0xff, 0xe1, 0xf2, 0xf3, 0xf3, 0xd3, 0xf4, 0xf1, 0xe3, 0xcb, 0xfc, 0xf1, 0xe3, 0xc7,
- 0xf8, 0xf1, 0xe3, 0xc7, 0xf8, 0xf1, 0xe3, 0xc7, 0xf8, 0xfb, 0xf7, 0xc7, 0xf8, 0xff, 0xff, 0xc7, 0xf8, 0xff, 0xff, 0xc7,
- 0x70, 0xf8, 0x8f, 0xc3, 0x70, 0x03, 0xb0, 0xc3, 0x70, 0xfe, 0xbf, 0xc3, 0x60, 0x00, 0x80, 0xc1, 0xc0, 0x00, 0xc0, 0xc0,
- 0x80, 0x01, 0x60, 0xc0, 0x00, 0x07, 0x38, 0xc0, 0x00, 0xfe, 0x1f, 0xc0, 0x00, 0xf0, 0x03, 0xc0, 0x00, 0x00, 0x00, 0xc0};
+const unsigned char Grinning_SmilingEyes2[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x24, 0x24, 0x52,
+ 0x4A, 0x02, 0x40, 0x02, 0x40, 0xF2, 0x4F, 0x12, 0x48, 0x22, 0x44,
+ 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char deadmau5[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x07, 0x00,
- 0x00, 0xFC, 0x03, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0x00, 0xC0, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0x00,
- 0xE0, 0xFF, 0xFF, 0x01, 0xF0, 0xFF, 0x7F, 0x00, 0xF0, 0xFF, 0xFF, 0x03, 0xF8, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x07,
- 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0x0F, 0xFE, 0xFF, 0xFF, 0x00,
- 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xF0, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xE0, 0xFF, 0x3F, 0xFC,
- 0x0F, 0xFF, 0x7F, 0x00, 0xC0, 0xFF, 0x1F, 0xF8, 0x0F, 0xFC, 0x3F, 0x00, 0x80, 0xFF, 0x0F, 0xF8, 0x1F, 0xFC, 0x1F, 0x00,
- 0x00, 0xFF, 0x0F, 0xFC, 0x3F, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x1F, 0xFF, 0xFF, 0xFE, 0x01, 0x00, 0x00, 0x00, 0xFC, 0xFF,
- 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,
- 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x00, 0xC0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x80, 0x07, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
+const unsigned char Loudly_Crying_Face[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x34, 0x2C, 0x4A,
+ 0x52, 0x12, 0x48, 0x12, 0x48, 0x92, 0x49, 0x52, 0x4A, 0x52, 0x4A,
+ 0x54, 0x2A, 0x94, 0x29, 0x18, 0x18, 0xF0, 0x0F, 0x00, 0x00};
-const unsigned char sun[] PROGMEM = {
- 0x00, 0xC0, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x30, 0xC0, 0x00, 0x03,
- 0x70, 0x00, 0x80, 0x03, 0xF0, 0x00, 0xC0, 0x03, 0xF0, 0xF8, 0xC7, 0x03, 0xE0, 0xFC, 0xCF, 0x01, 0x00, 0xFE, 0x1F, 0x00,
- 0x00, 0xFF, 0x3F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x8E, 0xFF, 0x7F, 0x1C, 0x9F, 0xFF, 0x7F, 0x3E,
- 0x9F, 0xFF, 0x7F, 0x3E, 0x8E, 0xFF, 0x7F, 0x1C, 0x80, 0xFF, 0x7F, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00,
- 0x00, 0xFE, 0x1F, 0x00, 0x00, 0xFC, 0x0F, 0x00, 0xC0, 0xF9, 0xE7, 0x00, 0xE0, 0x01, 0xE0, 0x01, 0xF0, 0x01, 0xE0, 0x03,
- 0xF0, 0xC0, 0xC0, 0x03, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0xC0, 0x00, 0x00,
-};
+const unsigned char wave_icon[] PROGMEM = {0x00, 0x00, 0xC0, 0x18, 0x30, 0x21, 0x48, 0x5A, 0x94, 0x64, 0x24,
+ 0x25, 0x4A, 0x24, 0x12, 0x44, 0x22, 0x44, 0x04, 0x40, 0x08, 0x40,
+ 0x12, 0x40, 0x22, 0x20, 0xC4, 0x10, 0x18, 0x0F, 0x00, 0x00};
-const unsigned char rain[] PROGMEM = {
- 0xC0, 0x0F, 0xC0, 0x00, 0x40, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x03, 0x38, 0x00,
- 0x00, 0x0E, 0x0C, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x30, 0x01, 0x00, 0x00, 0x20,
- 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x03, 0x00, 0x00, 0x30, 0x02, 0x00,
- 0x00, 0x10, 0x06, 0x00, 0x00, 0x08, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x01, 0x80, 0x00, 0x01, 0x00,
- 0xC0, 0xC0, 0x81, 0x03, 0xA0, 0x60, 0xC1, 0x03, 0x90, 0x20, 0x41, 0x01, 0xF0, 0xE0, 0xC0, 0x01, 0x60, 0x4C,
- 0x98, 0x00, 0x00, 0x0E, 0x1C, 0x00, 0x00, 0x0B, 0x12, 0x00, 0x00, 0x09, 0x1A, 0x00, 0x00, 0x06, 0x0E, 0x00,
-};
+const unsigned char cowboy[] PROGMEM = {0x70, 0x0E, 0x8F, 0xF1, 0x11, 0x88, 0x21, 0x84, 0xC2, 0x43, 0x1E,
+ 0x78, 0xE2, 0x47, 0x42, 0x42, 0x12, 0x48, 0x12, 0x48, 0x22, 0x44,
+ 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
-const unsigned char cloud[] PROGMEM = {
- 0x00, 0x80, 0x07, 0x00, 0x00, 0xE0, 0x1F, 0x00, 0x00, 0x70, 0x30, 0x00, 0x00, 0x10, 0x60, 0x00, 0x80, 0x1F, 0x40, 0x00,
- 0xC0, 0x0F, 0xC0, 0x00, 0xC0, 0x00, 0x80, 0x00, 0x60, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x00, 0x20, 0x00, 0x80, 0x01,
- 0x20, 0x00, 0x00, 0x07, 0x38, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x18, 0x02, 0x00, 0x00, 0x10,
- 0x02, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20,
- 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x30, 0x03, 0x00, 0x00, 0x10,
- 0x02, 0x00, 0x00, 0x10, 0x06, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x0C, 0xFC, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03,
-};
+const unsigned char deadmau5[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0xE4, 0x27, 0x12, 0x48, 0x0A,
+ 0x50, 0x0E, 0x70, 0x11, 0x88, 0x19, 0x98, 0x19, 0x98, 0x19, 0x98,
+ 0x19, 0x98, 0x19, 0x98, 0x11, 0x88, 0x0E, 0x70, 0x00, 0x00};
-const unsigned char fog[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01,
- 0x00, 0x38, 0x00, 0x00, 0xFC, 0x00, 0x7E, 0x00, 0xFF, 0x83, 0xFF, 0x01, 0x03, 0xFF, 0x81, 0x01, 0x00, 0x7C, 0x00, 0x00,
- 0xF8, 0x00, 0x3E, 0x00, 0xFE, 0x01, 0xFF, 0x00, 0x87, 0xC7, 0xC3, 0x01, 0x03, 0xFE, 0x80, 0x01, 0x00, 0x38, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-};
+const unsigned char sun[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0xEC, 0x37, 0xFC, 0x3F, 0xF8, 0x1F, 0xFC,
+ 0x3F, 0xFE, 0x7F, 0xFC, 0x3F, 0xFC, 0x3F, 0xFE, 0x7F, 0xFC, 0x3F,
+ 0xF8, 0x1F, 0xFC, 0x3F, 0xEC, 0x37, 0x80, 0x01, 0x00, 0x00};
-const unsigned char devil[] PROGMEM = {
- 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x01, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0xf0, 0x0f, 0xfc, 0x0f, 0xfc,
- 0x3f, 0xff, 0x3f, 0xff, 0xfe, 0xff, 0xff, 0xdf, 0xfe, 0xff, 0xff, 0xdf, 0xfe, 0xff, 0xff, 0xdf, 0xfc, 0xff, 0xff, 0xcf,
- 0xfc, 0xff, 0xff, 0xcf, 0xf8, 0xff, 0xff, 0xc7, 0xf0, 0xff, 0xff, 0xc3, 0xf0, 0xff, 0xff, 0xc3, 0xf0, 0xf1, 0xe3, 0xc3,
- 0xf0, 0xe7, 0xf9, 0xc3, 0xf0, 0xe7, 0xf9, 0xc3, 0xf0, 0xe3, 0xf1, 0xc3, 0xf0, 0xe3, 0xf1, 0xc3, 0xf0, 0xe7, 0xf9, 0xc3,
- 0xf0, 0xff, 0xff, 0xc3, 0xe0, 0xfd, 0xef, 0xc1, 0xe0, 0xf3, 0xf3, 0xc1, 0xc0, 0x07, 0xf8, 0xc0, 0x80, 0x1f, 0x7e, 0xc0,
- 0x00, 0xff, 0x3f, 0xc0, 0x00, 0xfe, 0x0f, 0xc0, 0x00, 0xf8, 0x03, 0xc0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0xc0};
+const unsigned char rain[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC, 0x3F, 0xFE,
+ 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0x00, 0x00, 0x48, 0x12,
+ 0x48, 0x12, 0x24, 0x09, 0x24, 0x09, 0x00, 0x00, 0x00, 0x00};
-const unsigned char heart[] PROGMEM = {
- 0x00, 0x00, 0x00, 0x00, 0xC0, 0x03, 0xF0, 0x00, 0xF8, 0x0F, 0xFC, 0x07, 0xFC, 0x1F, 0x06, 0x0E, 0xFE, 0x3F, 0x03, 0x18,
- 0xFE, 0xFF, 0x7F, 0x10, 0xFF, 0xFF, 0xFF, 0x31, 0xFF, 0xFF, 0xFF, 0x33, 0xFF, 0xFF, 0xFF, 0x37, 0xFF, 0xFF, 0xFF, 0x37,
- 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFF, 0xFF, 0xFF, 0x3F, 0xFE, 0xFF, 0xFF, 0x1F, 0xFE, 0xFF, 0xFF, 0x1F,
- 0xFC, 0xFF, 0xFF, 0x0F, 0xFC, 0xFF, 0xFF, 0x0F, 0xF8, 0xFF, 0xFF, 0x07, 0xF0, 0xFF, 0xFF, 0x03, 0xF0, 0xFF, 0xFF, 0x03,
- 0xE0, 0xFF, 0xFF, 0x01, 0xC0, 0xFF, 0xFF, 0x00, 0x80, 0xFF, 0x7F, 0x00, 0x00, 0xFF, 0x3F, 0x00, 0x00, 0xFE, 0x1F, 0x00,
- 0x00, 0xFC, 0x0F, 0x00, 0x00, 0xF8, 0x07, 0x00, 0x00, 0xF0, 0x03, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x80, 0x00, 0x00,
-};
+const unsigned char cloud[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x38, 0x1F, 0xFC,
+ 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0xF8, 0x1F,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-const unsigned char poo[] PROGMEM = {
- 0x00, 0x1c, 0x00, 0xc0, 0x00, 0x7c, 0x00, 0xc0, 0x00, 0xfc, 0x00, 0xc0, 0x00, 0x7c, 0x03, 0xc0, 0x00, 0xbe, 0x03, 0xc0,
- 0x00, 0xdf, 0x0f, 0xc0, 0x80, 0xcf, 0x0f, 0xc0, 0xc0, 0xf1, 0x0f, 0xc0, 0x60, 0xfc, 0x0f, 0xc0, 0x30, 0xff, 0x07, 0xc0,
- 0x90, 0xff, 0x3b, 0xc0, 0xc0, 0xff, 0x7d, 0xc0, 0xf8, 0xff, 0xfc, 0xc0, 0xf8, 0x3f, 0xf0, 0xc0, 0x78, 0x88, 0xc0, 0xc0,
- 0x20, 0xe3, 0x18, 0xc0, 0x98, 0xe7, 0xbc, 0xc1, 0x9c, 0x64, 0xa4, 0xc3, 0x9e, 0x64, 0xa4, 0xc7, 0xbe, 0xe4, 0xa4, 0xc7,
- 0xbc, 0x27, 0xbc, 0xc7, 0x38, 0x03, 0xd9, 0xc3, 0x00, 0xf0, 0x63, 0xc0, 0xf8, 0xfc, 0x3f, 0xcf, 0xfc, 0xff, 0x87, 0xdf,
- 0xfe, 0xff, 0xe0, 0xdf, 0xfc, 0x1f, 0xfe, 0xdf, 0xf8, 0x07, 0xf8, 0xcf, 0xf0, 0x03, 0xe0, 0xc7, 0x00, 0x00, 0x00, 0xc0};
+const unsigned char fog[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x88, 0x88, 0x54, 0x55, 0x22, 0x22, 0x00,
+ 0x00, 0x44, 0x44, 0xAA, 0x2A, 0x11, 0x11, 0x00, 0x00, 0x88, 0x88,
+ 0x54, 0x55, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-const unsigned char bell_icon[] PROGMEM = {
- 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b11110000,
- 0b00000011, 0b00000000, 0b00000000, 0b11111100, 0b00001111, 0b00000000, 0b00000000, 0b00001111, 0b00111100, 0b00000000,
- 0b00000000, 0b00000011, 0b00110000, 0b00000000, 0b10000000, 0b00000001, 0b01100000, 0b00000000, 0b11000000, 0b00000000,
- 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
- 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
- 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b11000000, 0b00000000,
- 0b11000000, 0b00000000, 0b11000000, 0b00000000, 0b01000000, 0b00000000, 0b10000000, 0b00000000, 0b01100000, 0b00000000,
- 0b10000000, 0b00000001, 0b01110000, 0b00000000, 0b10000000, 0b00000011, 0b00110000, 0b00000000, 0b00000000, 0b00000011,
- 0b00011000, 0b00000000, 0b00000000, 0b00000110, 0b11110000, 0b11111111, 0b11111111, 0b00000011, 0b00000000, 0b00001100,
- 0b00001100, 0b00000000, 0b00000000, 0b00011000, 0b00000110, 0b00000000, 0b00000000, 0b11111000, 0b00000111, 0b00000000,
- 0b00000000, 0b11100000, 0b00000001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000,
- 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000};
+const unsigned char devil[] PROGMEM = {0x06, 0x60, 0xCA, 0x53, 0x32, 0x4C, 0x22, 0x44, 0x44, 0x22, 0x3A,
+ 0x5C, 0x32, 0x4C, 0x52, 0x4A, 0x72, 0x4E, 0x02, 0x40, 0x22, 0x44,
+ 0xC4, 0x23, 0x04, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
+
+const unsigned char heart[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x3C, 0x3C, 0x7E, 0x7E, 0xFE, 0x7F, 0xFE,
+ 0x7F, 0xFE, 0x7F, 0xFE, 0x7F, 0xFC, 0x3F, 0xF8, 0x1F, 0xF8, 0x1F,
+ 0xF0, 0x0F, 0xE0, 0x07, 0xC0, 0x03, 0x80, 0x01, 0x00, 0x00};
+
+const unsigned char poo[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0x40, 0x02, 0x20, 0x04, 0x10, 0x04, 0xF0,
+ 0x08, 0x10, 0x10, 0x48, 0x12, 0x08, 0x18, 0xE8, 0x21, 0x1C, 0x40,
+ 0x42, 0x42, 0x82, 0x41, 0x02, 0x30, 0xFC, 0x0F, 0x00, 0x00};
+
+const unsigned char bell_icon[] PROGMEM = {0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0xE0, 0x07, 0xF0, 0x0F, 0xF0,
+ 0x0F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xF8, 0x1F, 0xFC, 0x3F,
+ 0xFC, 0x3F, 0xFE, 0x7F, 0xFE, 0x7F, 0x80, 0x01, 0x00, 0x00};
+
+const unsigned char cookie[] PROGMEM = {0x00, 0x00, 0xE0, 0x07, 0x18, 0x18, 0x04, 0x20, 0x34, 0x22, 0x32,
+ 0x40, 0x02, 0x58, 0x82, 0x5B, 0x92, 0x43, 0x82, 0x43, 0x02, 0x40,
+ 0x64, 0x28, 0x64, 0x20, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
+
+const unsigned char Fire[] PROGMEM = {0x30, 0x00, 0xF0, 0x00, 0xF8, 0x03, 0xF8, 0x07, 0xFC, 0x1F, 0xFC,
+ 0x1F, 0xFE, 0x3E, 0x7E, 0x3E, 0x3E, 0x7C, 0x1E, 0x78, 0x1E, 0x70,
+ 0x1C, 0x70, 0x1C, 0x70, 0x38, 0x38, 0x30, 0x38, 0x60, 0x0C};
+
+const unsigned char peace_sign[] PROGMEM = {0xC0, 0x30, 0x40, 0x29, 0x40, 0x25, 0x40, 0x15, 0x40, 0x12, 0x38,
+ 0x0A, 0x54, 0x68, 0x54, 0x58, 0x54, 0x44, 0x3C, 0x22, 0x04, 0x22,
+ 0x04, 0x12, 0x08, 0x10, 0x10, 0x08, 0xE0, 0x07, 0x00, 0x00};
+
+const unsigned char Praying[] PROGMEM = {0x00, 0x00, 0x40, 0x02, 0xA0, 0x05, 0x90, 0x09, 0x90, 0x09, 0x90,
+ 0x09, 0x98, 0x19, 0x94, 0x29, 0xA4, 0x25, 0xA4, 0x25, 0x84, 0x21,
+ 0x84, 0x21, 0x86, 0x61, 0x4E, 0x72, 0x7F, 0x7E, 0x3F, 0xFC};
+
+const unsigned char Sparkles[] PROGMEM = {0x00, 0x00, 0x10, 0x00, 0x38, 0x04, 0x10, 0x04, 0x00, 0x0E, 0x00,
+ 0x1F, 0x80, 0x3F, 0xE0, 0xFF, 0x80, 0x3F, 0x10, 0x1F, 0x10, 0x0E,
+ 0x38, 0x04, 0xFE, 0x04, 0x38, 0x00, 0x10, 0x00, 0x10, 0x00};
+
+const unsigned char clown[] PROGMEM = {0x00, 0x00, 0xEE, 0x77, 0x1A, 0x58, 0x06, 0x60, 0x24, 0x24, 0x72,
+ 0x4E, 0x22, 0x44, 0x82, 0x41, 0x82, 0x41, 0x1A, 0x58, 0xF2, 0x4F,
+ 0x14, 0x28, 0xE4, 0x27, 0x18, 0x18, 0xE0, 0x07, 0x00, 0x00};
+
+const unsigned char robo[] PROGMEM = {0x80, 0x01, 0xC0, 0x03, 0x80, 0x01, 0xFC, 0x3F, 0x04, 0x20, 0x74,
+ 0x2E, 0x52, 0x4A, 0x72, 0x4E, 0x02, 0x40, 0x02, 0x40, 0xA2, 0x4A,
+ 0x52, 0x45, 0x04, 0x20, 0x04, 0x20, 0xFC, 0x3F, 0x00, 0x00};
+
+const unsigned char hole[] PROGMEM = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x0F, 0x3C, 0x3C,
+ 0x06, 0x60, 0x0C, 0x30, 0xF0, 0x0F, 0x00, 0x00, 0x00, 0x00};
+
+const unsigned char bowling[] PROGMEM = {0x00, 0x38, 0x00, 0x44, 0x00, 0x44, 0x00, 0x44, 0x00, 0x28, 0x00,
+ 0x38, 0x00, 0x28, 0x78, 0x44, 0x84, 0x82, 0x22, 0x83, 0x52, 0x83,
+ 0x02, 0x83, 0x02, 0x45, 0x84, 0x44, 0x78, 0x38, 0x00, 0x00};
+
+const unsigned char vulcan_salute[] PROGMEM = {0x08, 0x02, 0x16, 0x0D, 0x15, 0x15, 0x15, 0x15, 0xA9, 0x12, 0x4A,
+ 0x0A, 0x02, 0x38, 0x04, 0x48, 0x04, 0x44, 0x04, 0x22, 0x04, 0x22,
+ 0x04, 0x12, 0x08, 0x10, 0x10, 0x08, 0xE0, 0x07, 0x00, 0x00};
#endif
} // namespace graphics
diff --git a/src/graphics/emotes.h b/src/graphics/emotes.h
index 30b164cbc..b1b2d16da 100644
--- a/src/graphics/emotes.h
+++ b/src/graphics/emotes.h
@@ -17,98 +17,150 @@ extern const int numEmotes;
#ifndef EXCLUDE_EMOJI
// === Emote Bitmaps ===
-#define thumbs_height 25
-#define thumbs_width 25
+#define thumbs_height 16
+#define thumbs_width 16
extern const unsigned char thumbup[] PROGMEM;
extern const unsigned char thumbdown[] PROGMEM;
-#define Smiling_Eyes_height 30
-#define Smiling_Eyes_width 30
+#define Smiling_Eyes_height 16
+#define Smiling_Eyes_width 16
extern const unsigned char Smiling_Eyes[] PROGMEM;
-#define Grinning_height 30
-#define Grinning_width 30
+#define Grinning_height 16
+#define Grinning_width 16
extern const unsigned char Grinning[] PROGMEM;
-#define Slightly_Smiling_height 30
-#define Slightly_Smiling_width 30
+#define Slightly_Smiling_height 16
+#define Slightly_Smiling_width 16
extern const unsigned char Slightly_Smiling[] PROGMEM;
-#define Winking_Face_height 30
-#define Winking_Face_width 30
+#define Winking_Face_height 16
+#define Winking_Face_width 16
extern const unsigned char Winking_Face[] PROGMEM;
-#define Grinning_Smiling_Eyes_height 30
-#define Grinning_Smiling_Eyes_width 30
+#define Grinning_Smiling_Eyes_height 16
+#define Grinning_Smiling_Eyes_width 16
extern const unsigned char Grinning_Smiling_Eyes[] PROGMEM;
-#define question_height 25
-#define question_width 25
+#define heart_smile_height 16
+#define heart_smile_width 16
+extern const unsigned char heart_smile[] PROGMEM;
+
+#define Heart_eyes_height 16
+#define Heart_eyes_width 16
+extern const unsigned char Heart_eyes[] PROGMEM;
+
+#define question_height 16
+#define question_width 16
extern const unsigned char question[] PROGMEM;
-#define bang_height 30
-#define bang_width 30
+#define bang_height 16
+#define bang_width 16
extern const unsigned char bang[] PROGMEM;
-#define haha_height 30
-#define haha_width 30
+#define haha_height 16
+#define haha_width 16
extern const unsigned char haha[] PROGMEM;
-#define ROFL_height 30
-#define ROFL_width 30
+#define ROFL_height 16
+#define ROFL_width 16
extern const unsigned char ROFL[] PROGMEM;
-#define Smiling_Closed_Eyes_height 30
-#define Smiling_Closed_Eyes_width 30
+#define Smiling_Closed_Eyes_height 16
+#define Smiling_Closed_Eyes_width 16
extern const unsigned char Smiling_Closed_Eyes[] PROGMEM;
-#define Grinning_SmilingEyes2_height 30
-#define Grinning_SmilingEyes2_width 30
+#define Grinning_SmilingEyes2_height 16
+#define Grinning_SmilingEyes2_width 16
extern const unsigned char Grinning_SmilingEyes2[] PROGMEM;
-#define wave_icon_height 30
-#define wave_icon_width 30
+#define Loudly_Crying_Face_height 16
+#define Loudly_Crying_Face_width 16
+extern const unsigned char Loudly_Crying_Face[] PROGMEM;
+
+#define wave_icon_height 16
+#define wave_icon_width 16
extern const unsigned char wave_icon[] PROGMEM;
-#define cowboy_height 30
-#define cowboy_width 30
+#define cowboy_height 16
+#define cowboy_width 16
extern const unsigned char cowboy[] PROGMEM;
-#define deadmau5_height 30
-#define deadmau5_width 60
+#define deadmau5_height 16
+#define deadmau5_width 16
extern const unsigned char deadmau5[] PROGMEM;
-#define sun_height 30
-#define sun_width 30
+#define sun_height 16
+#define sun_width 16
extern const unsigned char sun[] PROGMEM;
-#define rain_height 30
-#define rain_width 30
+#define rain_height 16
+#define rain_width 16
extern const unsigned char rain[] PROGMEM;
-#define cloud_height 30
-#define cloud_width 30
+#define cloud_height 16
+#define cloud_width 16
extern const unsigned char cloud[] PROGMEM;
-#define fog_height 25
-#define fog_width 25
+#define fog_height 16
+#define fog_width 16
extern const unsigned char fog[] PROGMEM;
-#define devil_height 30
-#define devil_width 30
+#define devil_height 16
+#define devil_width 16
extern const unsigned char devil[] PROGMEM;
-#define heart_height 30
-#define heart_width 30
+#define heart_height 16
+#define heart_width 16
extern const unsigned char heart[] PROGMEM;
-#define poo_height 30
-#define poo_width 30
+#define poo_height 16
+#define poo_width 16
extern const unsigned char poo[] PROGMEM;
-#define bell_icon_width 30
-#define bell_icon_height 30
+#define bell_icon_width 16
+#define bell_icon_height 16
extern const unsigned char bell_icon[] PROGMEM;
+
+#define cookie_width 16
+#define cookie_height 16
+extern const unsigned char cookie[] PROGMEM;
+
+#define Fire_width 16
+#define Fire_height 16
+extern const unsigned char Fire[] PROGMEM;
+
+#define peace_sign_width 16
+#define peace_sign_height 16
+extern const unsigned char peace_sign[] PROGMEM;
+
+#define Praying_width 16
+#define Praying_height 16
+extern const unsigned char Praying[] PROGMEM;
+
+#define Sparkles_width 16
+#define Sparkles_height 16
+extern const unsigned char Sparkles[] PROGMEM;
+
+#define clown_width 16
+#define clown_height 16
+extern const unsigned char clown[] PROGMEM;
+
+#define robo_width 16
+#define robo_height 16
+extern const unsigned char robo[] PROGMEM;
+
+#define hole_width 16
+#define hole_height 16
+extern const unsigned char hole[] PROGMEM;
+
+#define bowling_width 16
+#define bowling_height 16
+extern const unsigned char bowling[] PROGMEM;
+
+#define vulcan_salute_width 16
+#define vulcan_salute_height 16
+extern const unsigned char vulcan_salute[] PROGMEM;
#endif // EXCLUDE_EMOJI
-} // namespace graphics
+} // namespace graphics
\ No newline at end of file
diff --git a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp b/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp
index 818c68070..d383a11e4 100644
--- a/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp
+++ b/src/graphics/niche/InkHUD/Applets/Bases/Map/MapApplet.cpp
@@ -287,7 +287,7 @@ void InkHUD::MapApplet::getMapCenter(float *lat, float *lng)
float easternmost = lngCenter;
float westernmost = lngCenter;
- for (uint8_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
+ for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
// Skip if no position
@@ -474,8 +474,8 @@ void InkHUD::MapApplet::drawLabeledMarker(meshtastic_NodeInfoLite *node)
// Need at least two, to draw a sensible map
bool InkHUD::MapApplet::enoughMarkers()
{
- uint8_t count = 0;
- for (uint8_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
+ size_t count = 0;
+ for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) {
meshtastic_NodeInfoLite *node = nodeDB->getMeshNodeByIndex(i);
// Count nodes
diff --git a/src/graphics/niche/README.md b/src/graphics/niche/README.md
index e87464abc..e58578f6b 100644
--- a/src/graphics/niche/README.md
+++ b/src/graphics/niche/README.md
@@ -5,7 +5,6 @@ A pattern / collection of resources for creating custom UIs, to target small gro
For an example, see the `heltec-vision-master-e290-inkhud` platformio env.
- platformio.ini
-
- suppress default Meshtastic components (Screen, ButtonThread, etc)
- define `MESHTASTIC_INCLUDE_NICHE_GRAPHICS`
- (possibly) Edit `build_src_filter` to include our new nicheGraphics.h file
diff --git a/src/main.cpp b/src/main.cpp
index 805c0c42b..ea1e6472f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -487,6 +487,10 @@ void setup()
#ifdef RESET_OLED
pinMode(RESET_OLED, OUTPUT);
digitalWrite(RESET_OLED, 1);
+ delay(2);
+ digitalWrite(RESET_OLED, 0);
+ delay(10);
+ digitalWrite(RESET_OLED, 1);
#endif
#ifdef SENSOR_POWER_CTRL_PIN
@@ -1417,7 +1421,7 @@ void setup()
#endif
// check if the radio chip matches the selected region
- if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && (!rIf->wideLora())) {
+ if ((config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_LORA_24) && rIf && (!rIf->wideLora())) {
LOG_WARN("LoRa chip does not support 2.4GHz. Revert to unset");
config.lora.region = meshtastic_Config_LoRaConfig_RegionCode_UNSET;
nodeDB->saveToDisk(SEGMENT_CONFIG);
diff --git a/src/mesh/Default.h b/src/mesh/Default.h
index d0d4678ff..218d8d0fb 100644
--- a/src/mesh/Default.h
+++ b/src/mesh/Default.h
@@ -29,6 +29,7 @@
#else
#define default_ringtone_nag_secs 15
#endif
+#define default_network_ipv6_enabled false
#define default_mqtt_address "mqtt.meshtastic.org"
#define default_mqtt_username "meshdev"
@@ -46,12 +47,15 @@ class Default
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval);
static uint32_t getConfiguredOrDefaultMs(uint32_t configuredInterval, uint32_t defaultInterval);
static uint32_t getConfiguredOrDefault(uint32_t configured, uint32_t defaultValue);
+ // Note: numOnlineNodes uses uint32_t to match the public API and allow flexibility,
+ // even though internal node counts use uint16_t (max 65535 nodes)
static uint32_t getConfiguredOrDefaultMsScaled(uint32_t configured, uint32_t defaultValue, uint32_t numOnlineNodes);
static uint8_t getConfiguredOrDefaultHopLimit(uint8_t configured);
static uint32_t getConfiguredOrMinimumValue(uint32_t configured, uint32_t minValue);
private:
- static float congestionScalingCoefficient(int numOnlineNodes)
+ // Note: Kept as uint32_t to match the public API parameter type
+ static float congestionScalingCoefficient(uint32_t numOnlineNodes)
{
// Increase frequency of broadcasts for small networks regardless of preset
if (numOnlineNodes <= 10) {
diff --git a/src/mesh/NodeDB.cpp b/src/mesh/NodeDB.cpp
index d047bf163..d8146c4a3 100644
--- a/src/mesh/NodeDB.cpp
+++ b/src/mesh/NodeDB.cpp
@@ -718,6 +718,12 @@ void NodeDB::installDefaultConfig(bool preserveKey = false)
strncpy(config.network.wifi_psk, USERPREFS_NETWORK_WIFI_PSK, sizeof(config.network.wifi_psk));
#endif
+#if defined(USERPREFS_NETWORK_IPV6_ENABLED)
+ config.network.ipv6_enabled = USERPREFS_NETWORK_IPV6_ENABLED;
+#else
+ config.network.ipv6_enabled = default_network_ipv6_enabled;
+#endif
+
#ifdef DISPLAY_FLIP_SCREEN
config.display.flip_screen = true;
#endif
diff --git a/src/mesh/ProtobufModule.h b/src/mesh/ProtobufModule.h
index e038e9bb8..725477eae 100644
--- a/src/mesh/ProtobufModule.h
+++ b/src/mesh/ProtobufModule.h
@@ -13,7 +13,7 @@ template class ProtobufModule : protected SinglePortModule
const pb_msgdesc_t *fields;
public:
- uint8_t numOnlineNodes = 0;
+ uint16_t numOnlineNodes = 0;
/** Constructor
* name is for debugging output
*/
diff --git a/src/mesh/wifi/WiFiAPClient.cpp b/src/mesh/wifi/WiFiAPClient.cpp
index 18f67706a..45944872e 100644
--- a/src/mesh/wifi/WiFiAPClient.cpp
+++ b/src/mesh/wifi/WiFiAPClient.cpp
@@ -334,6 +334,23 @@ bool initWifi()
}
#ifdef ARCH_ESP32
+#if ESP_ARDUINO_VERSION <= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
+// Most of the next 12 lines of code are adapted from espressif/arduino-esp32
+// Licensed under the GNU Lesser General Public License v2.1
+// https://github.com/espressif/arduino-esp32/blob/1f038677eb2eaf5e9ca6b6074486803c15468bed/libraries/WiFi/src/WiFiSTA.cpp#L755
+esp_netif_t *get_esp_interface_netif(esp_interface_t interface);
+IPv6Address GlobalIPv6()
+{
+ esp_ip6_addr_t addr;
+ if (WiFiGenericClass::getMode() == WIFI_MODE_NULL) {
+ return IPv6Address();
+ }
+ if (esp_netif_get_ip6_global(get_esp_interface_netif(ESP_IF_WIFI_STA), &addr)) {
+ return IPv6Address();
+ }
+ return IPv6Address(addr.addr);
+}
+#endif
// Called by the Espressif SDK to
static void WiFiEvent(WiFiEvent_t event)
{
@@ -355,6 +372,17 @@ static void WiFiEvent(WiFiEvent_t event)
break;
case ARDUINO_EVENT_WIFI_STA_CONNECTED:
LOG_INFO("Connected to access point");
+ if (config.network.ipv6_enabled) {
+#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
+ if (!WiFi.enableIPv6()) {
+ LOG_WARN("Failed to enable IPv6");
+ }
+#else
+ if (!WiFi.enableIpV6()) {
+ LOG_WARN("Failed to enable IPv6");
+ }
+#endif
+ }
#ifdef WIFI_LED
digitalWrite(WIFI_LED, HIGH);
#endif
@@ -383,7 +411,8 @@ static void WiFiEvent(WiFiEvent_t event)
LOG_INFO("Obtained Local IP6 address: %s", WiFi.linkLocalIPv6().toString().c_str());
LOG_INFO("Obtained GlobalIP6 address: %s", WiFi.globalIPv6().toString().c_str());
#else
- LOG_INFO("Obtained IP6 address: %s", WiFi.localIPv6().toString().c_str());
+ LOG_INFO("Obtained Local IP6 address: %s", WiFi.localIPv6().toString().c_str());
+ LOG_INFO("Obtained GlobalIP6 address: %s", GlobalIPv6().toString().c_str());
#endif
break;
case ARDUINO_EVENT_WIFI_STA_LOST_IP:
@@ -514,4 +543,4 @@ uint8_t getWifiDisconnectReason()
{
return wifiDisconnectReason;
}
-#endif // HAS_WIFI
\ No newline at end of file
+#endif // HAS_WIFI
diff --git a/src/modules/NeighborInfoModule.cpp b/src/modules/NeighborInfoModule.cpp
index 97dc17001..936a7b44a 100644
--- a/src/modules/NeighborInfoModule.cpp
+++ b/src/modules/NeighborInfoModule.cpp
@@ -34,7 +34,8 @@ void NeighborInfoModule::printNodeDBNeighbors()
}
}
-/* Send our initial owner announcement 35 seconds after we start (to give network time to setup) */
+/* Send our initial owner announcement 35 seconds after we start (to give
+ * network time to setup) */
NeighborInfoModule::NeighborInfoModule()
: ProtobufModule("neighborinfo", meshtastic_PortNum_NEIGHBORINFO_APP, &meshtastic_NeighborInfo_msg),
concurrency::OSThread("NeighborInfo")
@@ -53,8 +54,8 @@ NeighborInfoModule::NeighborInfoModule()
}
/*
-Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
-Assumes that the neighborInfo packet has been allocated
+Collect neighbor info from the nodeDB's history, capping at a maximum number of
+entries and max time Assumes that the neighborInfo packet has been allocated
@returns the number of entries collected
*/
uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighborInfo)
@@ -71,8 +72,8 @@ uint32_t NeighborInfoModule::collectNeighborInfo(meshtastic_NeighborInfo *neighb
if ((neighborInfo->neighbors_count < MAX_NUM_NEIGHBORS) && (nbr.node_id != my_node_id)) {
neighborInfo->neighbors[neighborInfo->neighbors_count].node_id = nbr.node_id;
neighborInfo->neighbors[neighborInfo->neighbors_count].snr = nbr.snr;
- // Note: we don't set the last_rx_time and node_broadcast_intervals_secs here, because we don't want to send this over
- // the mesh
+ // Note: we don't set the last_rx_time and node_broadcast_intervals_secs
+ // here, because we don't want to send this over the mesh
neighborInfo->neighbors_count++;
}
}
@@ -88,8 +89,9 @@ void NeighborInfoModule::cleanUpNeighbors()
uint32_t now = getTime();
NodeNum my_node_id = nodeDB->getNodeNum();
for (auto it = neighbors.rbegin(); it != neighbors.rend();) {
- // We will remove a neighbor if we haven't heard from them in twice the broadcast interval
- // cannot use isWithinTimespanMs() as it->last_rx_time is seconds since 1970
+ // We will remove a neighbor if we haven't heard from them in twice the
+ // broadcast interval cannot use isWithinTimespanMs() as it->last_rx_time is
+ // seconds since 1970
if ((now - it->last_rx_time > it->node_broadcast_interval_secs * 2) && (it->node_id != my_node_id)) {
LOG_DEBUG("Remove neighbor with node ID 0x%x", it->node_id);
it = std::vector::reverse_iterator(
@@ -132,25 +134,55 @@ int32_t NeighborInfoModule::runOnce()
return Default::getConfiguredOrDefaultMs(moduleConfig.neighbor_info.update_interval, default_neighbor_info_broadcast_secs);
}
+meshtastic_MeshPacket *NeighborInfoModule::allocReply()
+{
+ LOG_INFO("NeighborInfoRequested.");
+ if (lastSentReply && Throttle::isWithinTimespanMs(lastSentReply, 3 * 60 * 1000)) {
+ LOG_DEBUG("Skip Neighbors reply since we sent a reply <3min ago");
+ ignoreRequest = true; // Mark it as ignored for MeshModule
+ return nullptr;
+ }
+
+ meshtastic_NeighborInfo neighborInfo = meshtastic_NeighborInfo_init_zero;
+ collectNeighborInfo(&neighborInfo);
+
+ meshtastic_MeshPacket *reply = allocDataProtobuf(neighborInfo);
+
+ if (reply) {
+ lastSentReply = millis(); // Track when we sent this reply
+ }
+ return reply;
+}
+
/*
Collect a received neighbor info packet from another node
Pass it to an upper client; do not persist this data on the mesh
*/
bool NeighborInfoModule::handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *np)
{
+ LOG_DEBUG("NeighborInfo: handleReceivedProtobuf");
if (np) {
printNeighborInfo("RECEIVED", np);
- updateNeighbors(mp, np);
+ // Ignore dummy/interceptable packets: single neighbor with nodeId 0 and snr 0
+ if (np->neighbors_count != 1 || np->neighbors[0].node_id != 0 || np->neighbors[0].snr != 0.0f) {
+ LOG_DEBUG(" Updating neighbours");
+ updateNeighbors(mp, np);
+ } else {
+ LOG_DEBUG(" Ignoring dummy neighbor info packet (single neighbor with nodeId 0, snr 0)");
+ }
} else if (mp.hop_start != 0 && mp.hop_start == mp.hop_limit) {
+ LOG_DEBUG("Get or create neighbor: %u with snr %f", mp.from, mp.rx_snr);
// If the hopLimit is the same as hopStart, then it is a neighbor
- getOrCreateNeighbor(mp.from, mp.from, 0, mp.rx_snr); // Set the broadcast interval to 0, as we don't know it
+ getOrCreateNeighbor(mp.from, mp.from, 0,
+ mp.rx_snr); // Set the broadcast interval to 0, as we don't know it
}
// Allow others to handle this packet
return false;
}
/*
-Copy the content of a current NeighborInfo packet into a new one and update the last_sent_by_id to our NodeNum
+Copy the content of a current NeighborInfo packet into a new one and update the
+last_sent_by_id to our NodeNum
*/
void NeighborInfoModule::alterReceivedProtobuf(meshtastic_MeshPacket &p, meshtastic_NeighborInfo *n)
{
@@ -168,8 +200,10 @@ void NeighborInfoModule::resetNeighbors()
void NeighborInfoModule::updateNeighbors(const meshtastic_MeshPacket &mp, const meshtastic_NeighborInfo *np)
{
- // The last sent ID will be 0 if the packet is from the phone, which we don't count as
- // an edge. So we assume that if it's zero, then this packet is from our node.
+ LOG_DEBUG("updateNeighbors");
+ // The last sent ID will be 0 if the packet is from the phone, which we don't
+ // count as an edge. So we assume that if it's zero, then this packet is from
+ // our node.
if (mp.which_payload_variant == meshtastic_MeshPacket_decoded_tag && mp.from) {
getOrCreateNeighbor(mp.from, np->last_sent_by_id, np->node_broadcast_interval_secs, mp.rx_snr);
}
@@ -188,7 +222,8 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen
// if found, update it
neighbors[i].snr = snr;
neighbors[i].last_rx_time = getTime();
- // Only if this is the original sender, the broadcast interval corresponds to it
+ // Only if this is the original sender, the broadcast interval corresponds
+ // to it
if (originalSender == n && node_broadcast_interval_secs != 0)
neighbors[i].node_broadcast_interval_secs = node_broadcast_interval_secs;
return &neighbors[i];
@@ -200,10 +235,12 @@ meshtastic_Neighbor *NeighborInfoModule::getOrCreateNeighbor(NodeNum originalSen
new_nbr.node_id = n;
new_nbr.snr = snr;
new_nbr.last_rx_time = getTime();
- // Only if this is the original sender, the broadcast interval corresponds to it
+ // Only if this is the original sender, the broadcast interval corresponds to
+ // it
if (originalSender == n && node_broadcast_interval_secs != 0)
new_nbr.node_broadcast_interval_secs = node_broadcast_interval_secs;
- else // Assume the same broadcast interval as us for the neighbor if we don't know it
+ else // Assume the same broadcast interval as us for the neighbor if we don't
+ // know it
new_nbr.node_broadcast_interval_secs = moduleConfig.neighbor_info.update_interval;
if (neighbors.size() < MAX_NUM_NEIGHBORS) {
diff --git a/src/modules/NeighborInfoModule.h b/src/modules/NeighborInfoModule.h
index aa76a2187..abb530329 100644
--- a/src/modules/NeighborInfoModule.h
+++ b/src/modules/NeighborInfoModule.h
@@ -28,6 +28,10 @@ class NeighborInfoModule : public ProtobufModule, priva
*/
virtual bool handleReceivedProtobuf(const meshtastic_MeshPacket &mp, meshtastic_NeighborInfo *nb) override;
+ /* Messages can be received that have the want_response bit set. If set, this callback will be invoked
+ * so that subclasses can (optionally) send a response back to the original sender. */
+ virtual meshtastic_MeshPacket *allocReply() override;
+
/*
* Collect neighbor info from the nodeDB's history, capping at a maximum number of entries and max time
* @return the number of entries collected
@@ -66,5 +70,8 @@ class NeighborInfoModule : public ProtobufModule, priva
/* These are for debugging only */
void printNeighborInfo(const char *header, const meshtastic_NeighborInfo *np);
void printNodeDBNeighbors();
+
+ private:
+ uint32_t lastSentReply = 0; // Last time we sent a position reply (used for reply throttling only)
};
extern NeighborInfoModule *neighborInfoModule;
\ No newline at end of file
diff --git a/src/modules/Telemetry/EnvironmentTelemetry.cpp b/src/modules/Telemetry/EnvironmentTelemetry.cpp
index e8c8172cb..29e815092 100644
--- a/src/modules/Telemetry/EnvironmentTelemetry.cpp
+++ b/src/modules/Telemetry/EnvironmentTelemetry.cpp
@@ -134,6 +134,10 @@ extern void drawCommonHeader(OLEDDisplay *display, int16_t x, int16_t y, const c
#include "Sensor/TSL2561Sensor.h"
#endif
+#if __has_include()
+#include "Sensor/BH1750Sensor.h"
+#endif
+
#define FAILED_STATE_SENSOR_READ_MULTIPLIER 10
#define DISPLAY_RECEIVEID_MEASUREMENTS_ON_SCREEN true
@@ -262,6 +266,9 @@ void EnvironmentTelemetryModule::i2cScanFinished(ScanI2C *i2cScanner)
#if __has_include()
addSensor(i2cScanner, ScanI2C::DeviceType::NAU7802);
#endif
+#if __has_include()
+ addSensor(i2cScanner, ScanI2C::DeviceType::BH1750);
+#endif
#endif
}
diff --git a/src/modules/Telemetry/Sensor/BH1750Sensor.cpp b/src/modules/Telemetry/Sensor/BH1750Sensor.cpp
new file mode 100644
index 000000000..b8790dcd5
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/BH1750Sensor.cpp
@@ -0,0 +1,54 @@
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include()
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "BH1750Sensor.h"
+#include "TelemetrySensor.h"
+#include
+
+#ifndef BH1750_SENSOR_MODE
+#define BH1750_SENSOR_MODE BH1750Mode::CHM
+#endif
+
+BH1750Sensor::BH1750Sensor() : TelemetrySensor(meshtastic_TelemetrySensorType_BH1750, "BH1750") {}
+
+bool BH1750Sensor::initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev)
+{
+ LOG_INFO("Init sensor: %s with mode %d", sensorName, BH1750_SENSOR_MODE);
+
+ bh1750 = BH1750_WE(bus, dev->address.address);
+ status = bh1750.init();
+ if (!status) {
+ return status;
+ }
+
+ bh1750.setMode(BH1750_SENSOR_MODE);
+
+ initI2CSensor();
+ return status;
+}
+
+bool BH1750Sensor::getMetrics(meshtastic_Telemetry *measurement)
+{
+
+ /* An OTH and OTH_2 measurement takes ~120 ms. I suggest to wait
+ 140 ms to be on the safe side.
+ An OTL measurement takes about 16 ms. I suggest to wait 20 ms
+ to be on the safe side. */
+ if (BH1750_SENSOR_MODE == BH1750Mode::OTH || BH1750_SENSOR_MODE == BH1750Mode::OTH_2) {
+ bh1750.setMode(BH1750_SENSOR_MODE);
+ delay(140); // wait for measurement to be completed
+ } else if (BH1750_SENSOR_MODE == BH1750Mode::OTL) {
+ bh1750.setMode(BH1750_SENSOR_MODE);
+ delay(20);
+ }
+
+ measurement->variant.environment_metrics.has_lux = true;
+ float lightIntensity = bh1750.getLux();
+
+ measurement->variant.environment_metrics.lux = lightIntensity;
+ return true;
+}
+
+#endif
diff --git a/src/modules/Telemetry/Sensor/BH1750Sensor.h b/src/modules/Telemetry/Sensor/BH1750Sensor.h
new file mode 100644
index 000000000..d9a4ded95
--- /dev/null
+++ b/src/modules/Telemetry/Sensor/BH1750Sensor.h
@@ -0,0 +1,21 @@
+#pragma once
+#include "configuration.h"
+
+#if !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && __has_include()
+
+#include "../mesh/generated/meshtastic/telemetry.pb.h"
+#include "TelemetrySensor.h"
+#include
+
+class BH1750Sensor : public TelemetrySensor
+{
+ private:
+ BH1750_WE bh1750;
+
+ public:
+ BH1750Sensor();
+ virtual bool getMetrics(meshtastic_Telemetry *measurement) override;
+ virtual bool initDevice(TwoWire *bus, ScanI2C::FoundDevice *dev) override;
+};
+
+#endif
diff --git a/src/motion/MotionSensor.cpp b/src/motion/MotionSensor.cpp
index b00460aff..d0bfe4e2c 100755
--- a/src/motion/MotionSensor.cpp
+++ b/src/motion/MotionSensor.cpp
@@ -69,7 +69,8 @@ void MotionSensor::wakeScreen()
{
if (powerFSM.getState() == &stateDARK) {
LOG_DEBUG("Motion wakeScreen detected");
- powerFSM.trigger(EVENT_INPUT);
+ if (config.display.wake_on_tap_or_motion)
+ powerFSM.trigger(EVENT_INPUT);
}
}
@@ -87,4 +88,4 @@ void MotionSensor::buttonPress() {}
#endif
-#endif
\ No newline at end of file
+#endif
diff --git a/src/mqtt/MQTT.cpp b/src/mqtt/MQTT.cpp
index 2dcfd80e1..ad35e152a 100644
--- a/src/mqtt/MQTT.cpp
+++ b/src/mqtt/MQTT.cpp
@@ -692,7 +692,7 @@ void MQTT::publishNodeInfo()
}
void MQTT::publishQueuedMessages()
{
- if (mqttQueue.isEmpty() || !isConnected)
+ if (mqttQueue.isEmpty())
return;
if (!moduleConfig.mqtt.proxy_to_client_enabled && !isConnected)
diff --git a/src/platform/nrf52/main-nrf52.cpp b/src/platform/nrf52/main-nrf52.cpp
index 8ce74d5f7..f29def72e 100644
--- a/src/platform/nrf52/main-nrf52.cpp
+++ b/src/platform/nrf52/main-nrf52.cpp
@@ -14,6 +14,9 @@
#include "error.h"
#include "main.h"
#include "meshUtils.h"
+#include "power.h"
+
+#include
#ifdef BQ25703A_ADDR
#include "BQ25713.h"
@@ -389,6 +392,23 @@ void cpuDeepSleep(uint32_t msecToWake)
nrf_gpio_cfg_sense_set(BUTTON_PIN, sense); // Apply SENSE to wake up the device from the deep sleep
#endif
+#ifdef BATTERY_LPCOMP_INPUT
+ // Wake up if power rises again
+ nrf_lpcomp_config_t c;
+ c.reference = BATTERY_LPCOMP_THRESHOLD;
+ c.detection = NRF_LPCOMP_DETECT_UP;
+ c.hyst = NRF_LPCOMP_HYST_NOHYST;
+ nrf_lpcomp_configure(NRF_LPCOMP, &c);
+ nrf_lpcomp_input_select(NRF_LPCOMP, BATTERY_LPCOMP_INPUT);
+ nrf_lpcomp_enable(NRF_LPCOMP);
+
+ battery_adcEnable();
+
+ nrf_lpcomp_task_trigger(NRF_LPCOMP, NRF_LPCOMP_TASK_START);
+ while (!nrf_lpcomp_event_check(NRF_LPCOMP, NRF_LPCOMP_EVENT_READY))
+ ;
+#endif
+
auto ok = sd_power_system_off();
if (ok != NRF_SUCCESS) {
LOG_ERROR("FIXME: Ignoring soft device (EasyDMA pending?) and forcing system-off!");
@@ -420,4 +440,4 @@ void enterDfuMode()
#else
enterUf2Dfu();
#endif
-}
\ No newline at end of file
+}
diff --git a/src/platform/portduino/PortduinoGlue.cpp b/src/platform/portduino/PortduinoGlue.cpp
index 53dece150..10b3a7fe4 100644
--- a/src/platform/portduino/PortduinoGlue.cpp
+++ b/src/platform/portduino/PortduinoGlue.cpp
@@ -37,6 +37,8 @@ bool yamlOnly = false;
const char *argp_program_version = optstr(APP_VERSION);
+char stdoutBuffer[512];
+
// FIXME - move setBluetoothEnable into a HALPlatform class
void setBluetoothEnable(bool enable)
{
@@ -144,6 +146,20 @@ void getMacAddr(uint8_t *dmac)
}
}
+std::string cleanupNameForAutoconf(std::string name)
+{
+ // Convert spaces -> dashes, lowercase
+
+ std::transform(name.begin(), name.end(), name.begin(), [](unsigned char c) {
+ if (c == ' ') {
+ return '-';
+ }
+ return (char)std::tolower(c);
+ });
+
+ return name;
+}
+
/** apps run under portduino can optionally define a portduinoSetup() to
* use portduino specific init code (such as gpioBind) to setup portduino on their host machine,
* before running 'arduino' code.
@@ -154,6 +170,9 @@ void portduinoSetup()
std::string gpioChipName = "gpiochip";
portduino_config.displayPanel = no_screen;
+ // Force stdout to be line buffered
+ setvbuf(stdout, stdoutBuffer, _IOLBF, sizeof(stdoutBuffer));
+
if (portduino_config.force_simradio == true) {
portduino_config.lora_module = use_simradio;
} else if (configPath != nullptr) {
@@ -213,6 +232,11 @@ void portduinoSetup()
// If LoRa `Module: auto` (default in config.yaml),
// attempt to auto config based on Product Strings
if (portduino_config.lora_module == use_autoconf) {
+ bool found_hat = false;
+ bool found_rak_eeprom = false;
+ bool found_ch341 = false;
+
+ char hat_vendor[96] = {0};
char autoconf_product[96] = {0};
// Try CH341
try {
@@ -222,21 +246,32 @@ void portduinoSetup()
ch341Hal->getProductString(autoconf_product, 95);
delete ch341Hal;
std::cout << "autoconf: Found CH341 device " << autoconf_product << std::endl;
+
+ found_ch341 = true;
} catch (...) {
std::cout << "autoconf: Could not locate CH341 device" << std::endl;
}
// Try Pi HAT+
if (strlen(autoconf_product) < 6) {
std::cout << "autoconf: Looking for Pi HAT+..." << std::endl;
+ if (access("/proc/device-tree/hat/vendor", R_OK) == 0) {
+ std::ifstream hatVendorFile("/proc/device-tree/hat/vendor");
+ if (hatVendorFile.is_open()) {
+ hatVendorFile.read(hat_vendor, 95);
+ hatVendorFile.close();
+ }
+ }
if (access("/proc/device-tree/hat/product", R_OK) == 0) {
std::ifstream hatProductFile("/proc/device-tree/hat/product");
if (hatProductFile.is_open()) {
hatProductFile.read(autoconf_product, 95);
hatProductFile.close();
}
- std::cout << "autoconf: Found Pi HAT+ " << autoconf_product << " at /proc/device-tree/hat/product" << std::endl;
+ std::cout << "autoconf: Found Pi HAT+ " << hat_vendor << " " << autoconf_product << " at /proc/device-tree/hat"
+ << std::endl;
+ found_hat = true;
} else {
- std::cout << "autoconf: Could not locate Pi HAT+ at /proc/device-tree/hat/product" << std::endl;
+ std::cout << "autoconf: Could not locate Pi HAT+ at /proc/device-tree/hat" << std::endl;
}
}
// attempt to load autoconf data from an EEPROM on 0x50
@@ -292,6 +327,7 @@ void portduinoSetup()
autoconf_product[0] = 0x0;
} else {
std::cout << "autoconf: Found eeprom data " << autoconf_raw << std::endl;
+ found_rak_eeprom = true;
if (mac_start != nullptr) {
std::cout << "autoconf: Found mac data " << mac_start << std::endl;
if (strlen(mac_start) == 12)
@@ -320,12 +356,29 @@ void portduinoSetup()
if (strlen(autoconf_product) > 0) {
// From configProducts map in PortduinoGlue.h
std::string product_config = "";
- try {
+
+ if (configProducts.find(autoconf_product) != configProducts.end()) {
product_config = configProducts.at(autoconf_product);
- } catch (std::out_of_range &e) {
- std::cerr << "autoconf: Unable to find config for " << autoconf_product << std::endl;
- exit(EXIT_FAILURE);
+ } else {
+ if (found_hat) {
+ product_config =
+ cleanupNameForAutoconf("lora-hat-" + std::string(hat_vendor) + "-" + autoconf_product + ".yaml");
+ } else if (found_ch341) {
+ product_config = cleanupNameForAutoconf("lora-usb-" + std::string(autoconf_product) + ".yaml");
+ }
+
+ // Don't try to automatically find config for a device with RAK eeprom.
+ if (found_rak_eeprom) {
+ std::cerr << "autoconf: Found unknown RAK product " << autoconf_product << std::endl;
+ exit(EXIT_FAILURE);
+ }
+ if (access((portduino_config.available_directory + product_config).c_str(), R_OK) != 0) {
+ std::cerr << "autoconf: Unable to find config for " << autoconf_product << "(tried " << product_config << ")"
+ << std::endl;
+ exit(EXIT_FAILURE);
+ }
}
+
if (loadConfig((portduino_config.available_directory + product_config).c_str())) {
std::cout << "autoconf: Using " << product_config << " as config file for " << autoconf_product << std::endl;
} else {
diff --git a/src/platform/stm32wl/main-stm32wl.cpp b/src/platform/stm32wl/main-stm32wl.cpp
index 3eddbb3cf..e841f8f29 100644
--- a/src/platform/stm32wl/main-stm32wl.cpp
+++ b/src/platform/stm32wl/main-stm32wl.cpp
@@ -26,3 +26,31 @@ void getMacAddr(uint8_t *dmac)
}
void cpuDeepSleep(uint32_t msecToWake) {}
+
+// Hacks to force more code and data out.
+
+// By default __assert_func uses fiprintf which pulls in stdio.
+extern "C" void __wrap___assert_func(const char *, int, const char *, const char *)
+{
+ while (true)
+ ;
+ return;
+}
+
+// By default strerror has a lot of strings we probably don't use. Make it return an empty string instead.
+char empty = 0;
+extern "C" char *__wrap_strerror(int)
+{
+ return ∅
+}
+
+#ifdef MESHTASTIC_EXCLUDE_TZ
+struct _reent;
+
+// Even if you don't use timezones, mktime will try to set the timezone anyway with _tzset_unlocked(), which pulls in scanf and
+// friends. The timezone is initialized to UTC by default.
+extern "C" void __wrap__tzset_unlocked_r(struct _reent *reent_ptr)
+{
+ return;
+}
+#endif
\ No newline at end of file
diff --git a/src/power.h b/src/power.h
index cdbdd3ea0..f9ccb08aa 100644
--- a/src/power.h
+++ b/src/power.h
@@ -144,4 +144,6 @@ class Power : private concurrency::OSThread
#endif
};
+void battery_adcEnable();
+
extern Power *power;
diff --git a/userPrefs.jsonc b/userPrefs.jsonc
index 464eb968c..0c92eabcf 100644
--- a/userPrefs.jsonc
+++ b/userPrefs.jsonc
@@ -56,5 +56,6 @@
// "USERPREFS_MQTT_ROOT_TOPIC": "event/REPLACEME",
// "USERPREFS_RINGTONE_NAG_SECS": "60",
"USERPREFS_RINGTONE_RTTTL": "24:d=32,o=5,b=565:f6,p,f6,4p,p,f6,p,f6,2p,p,b6,p,b6,p,b6,p,b6,p,b,p,b,p,b,p,b,p,b,p,b,p,b,p,b,1p.,2p.,p",
+ // "USERPREFS_NETWORK_IPV6_ENABLED": "1",
"USERPREFS_TZ_STRING": "tzplaceholder "
}
diff --git a/variants/esp32s3/tbeam-s3-core/rfswitch.h b/variants/esp32s3/tbeam-s3-core/rfswitch.h
new file mode 100644
index 000000000..19080cec6
--- /dev/null
+++ b/variants/esp32s3/tbeam-s3-core/rfswitch.h
@@ -0,0 +1,11 @@
+#include "RadioLib.h"
+
+static const uint32_t rfswitch_dio_pins[] = {RADIOLIB_LR11X0_DIO5, RADIOLIB_LR11X0_DIO6, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC};
+
+static const Module::RfSwitchMode_t rfswitch_table[] = {
+ // mode DIO5 DIO6
+ {LR11x0::MODE_STBY, {LOW, LOW}}, {LR11x0::MODE_RX, {HIGH, LOW}},
+ {LR11x0::MODE_TX, {LOW, HIGH}}, {LR11x0::MODE_TX_HP, {LOW, HIGH}},
+ {LR11x0::MODE_TX_HF, {LOW, LOW}}, {LR11x0::MODE_GNSS, {LOW, LOW}},
+ {LR11x0::MODE_WIFI, {LOW, LOW}}, END_OF_MODE_TABLE,
+};
\ No newline at end of file
diff --git a/variants/esp32s3/tbeam-s3-core/variant.h b/variants/esp32s3/tbeam-s3-core/variant.h
index 40ba0307a..1d99fbf14 100644
--- a/variants/esp32s3/tbeam-s3-core/variant.h
+++ b/variants/esp32s3/tbeam-s3-core/variant.h
@@ -15,6 +15,7 @@
// not found then probe for SX1262
#define USE_SX1262
#define USE_SX1268
+#define USE_LR1121
#define LORA_DIO0 -1 // a No connect on the SX1262 module
#define LORA_RESET 5
@@ -34,6 +35,19 @@
// code)
#endif
+// LR1121
+#ifdef USE_LR1121
+#define LR1121_IRQ_PIN 1
+#define LR1121_NRESET_PIN LORA_RESET
+#define LR1121_BUSY_PIN 4
+#define LR1121_SPI_NSS_PIN 10
+#define LR1121_SPI_SCK_PIN 12
+#define LR1121_SPI_MOSI_PIN 11
+#define LR1121_SPI_MISO_PIN 13
+#define LR11X0_DIO3_TCXO_VOLTAGE 3.0
+#define LR11X0_DIO_AS_RF_SWITCH
+#endif
+
// Leave undefined to disable our PMU IRQ handler. DO NOT ENABLE THIS because the pmuirq can cause sperious interrupts
// and waking from light sleep
// #define PMU_IRQ 40
@@ -64,4 +78,4 @@
// has 32768 Hz crystal
#define HAS_32768HZ 1
-#define USE_SH1106
+#define USE_SH1106
\ No newline at end of file
diff --git a/variants/nrf52840/heltec_mesh_node_t114/variant.h b/variants/nrf52840/heltec_mesh_node_t114/variant.h
index 7e82733aa..b6082fdc6 100644
--- a/variants/nrf52840/heltec_mesh_node_t114/variant.h
+++ b/variants/nrf52840/heltec_mesh_node_t114/variant.h
@@ -210,6 +210,16 @@ No longer populated on PCB
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER (4.916F)
+// rf52840 AIN2 = Pin 4
+#define BATTERY_LPCOMP_INPUT NRF_LPCOMP_INPUT_2
+
+// We have AIN2 with a VBAT divider so AIN2 = VBAT * (100/490)
+// We have the device going deep sleep under 3.1V, which is AIN2 = 0.63V
+// So we can wake up when VBAT>=VDD is restored to 3.3V, where AIN2 = 0.67V
+// Ratio 0.67/3.3 = 0.20, so we can pick a bit higher, 2/8 VDD, which means
+// VBAT=4.04V
+#define BATTERY_LPCOMP_THRESHOLD NRF_LPCOMP_REF_SUPPLY_2_8
+
#define HAS_RTC 0
#ifdef __cplusplus
}
diff --git a/variants/nrf52840/heltec_mesh_pocket/variant.h b/variants/nrf52840/heltec_mesh_pocket/variant.h
index 79f47bd0e..e765dab66 100644
--- a/variants/nrf52840/heltec_mesh_pocket/variant.h
+++ b/variants/nrf52840/heltec_mesh_pocket/variant.h
@@ -120,7 +120,7 @@ No longer populated on PCB
#undef AREF_VOLTAGE
#define AREF_VOLTAGE 3.0
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
-#define ADC_MULTIPLIER (4.90F)
+#define ADC_MULTIPLIER (4.6425F)
#undef HAS_GPS
#define HAS_GPS 0
@@ -129,4 +129,4 @@ No longer populated on PCB
}
#endif
-#endif
\ No newline at end of file
+#endif
diff --git a/variants/nrf52840/rak4631/variant.h b/variants/nrf52840/rak4631/variant.h
index f5ec11ef2..302e531d5 100644
--- a/variants/nrf52840/rak4631/variant.h
+++ b/variants/nrf52840/rak4631/variant.h
@@ -267,6 +267,20 @@ SO GPIO 39/TXEN MAY NOT BE DEFINED FOR SUCCESSFUL OPERATION OF THE SX1262 - TG
#define VBAT_AR_INTERNAL AR_INTERNAL_3_0
#define ADC_MULTIPLIER 1.73
+// RAK4630 AIN0 = nrf52840 AIN3 = Pin 5
+#define BATTERY_LPCOMP_INPUT NRF_LPCOMP_INPUT_3
+
+// We have AIN3 with a VBAT divider so AIN3 = VBAT * (1.5/2.5)
+// We have the device going deep sleep under 3.1V, which is AIN3 = 1.86V
+// So we can wake up when VBAT>=VDD is restored to 3.3V, where AIN3 = 1.98V
+// 1.98/3.3 = 6/10, but that's close to the VBAT divider, so we
+// pick 6/8VDD, which means VBAT=4.1V.
+// Reference:
+// VDD=3.3V AIN3=5/8*VDD=2.06V VBAT=1.66*AIN3=3.41V
+// VDD=3.3V AIN3=11/16*VDD=2.26V VBAT=1.66*AIN3=3.76V
+// VDD=3.3V AIN3=6/8*VDD=2.47V VBAT=1.66*AIN3=4.1V
+#define BATTERY_LPCOMP_THRESHOLD NRF_LPCOMP_REF_SUPPLY_11_16
+
#define HAS_RTC 1
#define HAS_ETHERNET 1
diff --git a/variants/stm32/wio-e5/variant.h b/variants/stm32/wio-e5/variant.h
index 6098b4ce6..a312b31bd 100644
--- a/variants/stm32/wio-e5/variant.h
+++ b/variants/stm32/wio-e5/variant.h
@@ -15,7 +15,7 @@ Do not expect a working Meshtastic device with this target.
#define USE_STM32WLx
#define LED_PIN PB5
-#define LED_STATE_ON 1
+#define LED_STATE_ON 0
#define WIO_E5
diff --git a/version.properties b/version.properties
index 165f476df..05d8a493f 100644
--- a/version.properties
+++ b/version.properties
@@ -1,4 +1,4 @@
[VERSION]
major = 2
minor = 7
-build = 15
+build = 16
\ No newline at end of file