mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-13 15:11:20 +10:00
* logging: strip redundant punctuation, level prefixes, and 'successfully' from log strings The logger already appends a newline and prints the level tag, so trailing '.', '!', '...', literal \n, and 'Error:'/'Warning:' prefixes inside format strings are wasted flash bytes. Same for 'successfully' (the affirmative form already implies it). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LBiZc9sfPrH1MZ2L3Fxgt1 * logging: tighten verbose log strings in modules, radio, platform, and system code Rewrite wordy log messages to terser equivalents - drop filler words (articles, 'attempting', 'due to', 'please'), use 'Can't X'/'X failed' phrasing, and abbreviate where the codebase already does (config, init, msg, BT). Format specifiers and argument lists are unchanged; distinctive greppable tokens are preserved. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LBiZc9sfPrH1MZ2L3Fxgt1 * logging: tighten verbose log strings in telemetry sensors and GPS Same terseness pass: drop filler, 'Can't X'/'X failed' phrasing, common abbreviations (temp, msg). Specifiers, arguments, and sensor-name prefixes unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LBiZc9sfPrH1MZ2L3Fxgt1 * logging: tighten verbose log strings in mesh core Same terseness pass over NodeDB, Router, MeshService, PhoneAPI, RadioInterface, NextHopRouter, and PacketHistory: 'X failed'/'Can't X' phrasing, imperative verbs, dropped filler. Specifiers and arguments unchanged; duplicate literals kept identical to preserve linker string dedup. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LBiZc9sfPrH1MZ2L3Fxgt1 * logging: 'Unable to/Could not/Cannot' -> "Can't" in log strings Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LBiZc9sfPrH1MZ2L3Fxgt1 * logging: clang-format rewrap after string shortening Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LBiZc9sfPrH1MZ2L3Fxgt1 * logging: restore boot-logo trailing newline and progress-dot strings The terseness pass over-trimmed: the Meshtastic ASCII boot logo kept its blank line via a trailing \n, and three bare "." progress ticks were reduced to empty strings. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LBiZc9sfPrH1MZ2L3Fxgt1 * Update src/mesh/wifi/WiFiAPClient.cpp Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * logging: address review feedback on the terseness audit - Node/packet IDs use the repo's 0x%08x convention in NextHopRouter, NodeDB, AdminModule and CannedMessageModule. The sibling log in each if/else pair is converted too, so a pair isn't split across two formats. next_hop stays 0x%x - it's the last-byte relay hint, not a NodeNum. - RTC: the read-path and set-path "not found" warnings were byte-identical, so the linker deduped them and the log couldn't say which one fired. Split into "RTC read:" / "RTC set:". (The four sites live in mutually exclusive #ifdef branches, so the RTC family was never ambiguous.) - SCD4X getAmbientPressure()/setAmbientPressure() logged "altitude", and SCD30 getASC() logged "Can't send command" for a read. Both now name the operation they actually perform. - LOG_ERROR already carries the level: ". Error: %u" -> ", rc=%u" (matching the existing rc=%d house style) and "Error executing X()" -> "X() failed". - Typos and wording: "OTA partiton. (Reason" -> "OTA partition (reason", "CST3530 not response ~" -> "CST3530 no response", "Packet received with to: of 0" -> "to=0", HostMetrics "Error decoding" -> "Can't decode", and the dangling ": " on the NextHopRouter retransmission line. Printf specifier sequences are byte-identical on all 37 touched lines apart from the 6 deliberate %x/%u -> %08x node-ID widenings, all on uint32_t args. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
396 lines
13 KiB
C++
396 lines
13 KiB
C++
#include "PowerFSM.h"
|
|
#include "PowerMon.h"
|
|
#include "configuration.h"
|
|
#include "esp_task_wdt.h"
|
|
#include "main.h"
|
|
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
|
|
#include "nimble/NimbleBluetooth.h"
|
|
#endif
|
|
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH && __has_include(<esp_bt.h>)
|
|
#include <esp_bt.h>
|
|
#define CAN_RELEASE_BT_MEMORY 1
|
|
#endif
|
|
|
|
#include <MeshtasticOTA.h>
|
|
|
|
#if HAS_WIFI
|
|
#include "mesh/wifi/WiFiAPClient.h"
|
|
#endif
|
|
|
|
#include "esp_mac.h"
|
|
#include "freertosinc.h"
|
|
#include "meshUtils.h"
|
|
#include "sleep.h"
|
|
#include "soc/rtc.h"
|
|
#include "target_specific.h"
|
|
#include <Preferences.h>
|
|
#include <driver/rtc_io.h>
|
|
#include <esp_efuse.h>
|
|
#include <esp_efuse_table.h>
|
|
#include <nvs.h>
|
|
#include <nvs_flash.h>
|
|
|
|
// Weak empty variant shutdown prep function.
|
|
// May be redefined by variant files.
|
|
void variant_shutdown() __attribute__((weak));
|
|
void variant_shutdown() {}
|
|
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
|
|
static bool bluetoothMemoryReleased;
|
|
static bool bluetoothMemoryReleaseWarned;
|
|
#endif
|
|
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
|
|
static bool isNetworkConfiguredToDisableBluetooth()
|
|
{
|
|
#if HAS_WIFI
|
|
return isWifiAvailable();
|
|
#elif defined(USE_WS5500) || defined(USE_CH390D)
|
|
return config.network.wifi_enabled;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
static bool isPaxcounterActiveForBoot()
|
|
{
|
|
#if !MESHTASTIC_EXCLUDE_PAXCOUNTER
|
|
return moduleConfig.has_paxcounter && moduleConfig.paxcounter.enabled && !config.bluetooth.enabled &&
|
|
!config.network.wifi_enabled;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
static bool shouldReleaseBluetoothMemory()
|
|
{
|
|
// Paxcounter disables the Meshtastic BLE service, but libpax still needs the
|
|
// ESP32 BLE controller memory for scanning.
|
|
if (isPaxcounterActiveForBoot()) {
|
|
LOG_DEBUG("Skip BT memory release: Paxcounter active");
|
|
return false;
|
|
}
|
|
|
|
// On ESP32 targets WiFi and BLE share radio resources. When WiFi is configured for this boot,
|
|
// BLE will not be started, so its reserved memory can be returned to the heap until reboot.
|
|
if (isNetworkConfiguredToDisableBluetooth()) {
|
|
return true;
|
|
}
|
|
return !config.bluetooth.enabled;
|
|
}
|
|
|
|
static const char *getBluetoothReleaseReason()
|
|
{
|
|
if (isNetworkConfiguredToDisableBluetooth()) {
|
|
return "WiFi is enabled";
|
|
}
|
|
return "Bluetooth is disabled";
|
|
}
|
|
#endif
|
|
|
|
#if !defined(CONFIG_IDF_TARGET_ESP32S2) && !MESHTASTIC_EXCLUDE_BLUETOOTH
|
|
void setBluetoothEnable(bool enable)
|
|
{
|
|
if (enable && bluetoothMemoryReleased) {
|
|
if (!shouldReleaseBluetoothMemory() && !bluetoothMemoryReleaseWarned) {
|
|
bluetoothMemoryReleaseWarned = true;
|
|
LOG_WARN("BT memory released; reboot to re-enable");
|
|
}
|
|
return;
|
|
}
|
|
|
|
#if defined(USE_WS5500) || defined(USE_CH390D)
|
|
if ((config.bluetooth.enabled == true) && (config.network.wifi_enabled == false))
|
|
#elif HAS_WIFI
|
|
if (!isWifiAvailable() && config.bluetooth.enabled == true)
|
|
#else
|
|
if (config.bluetooth.enabled == true)
|
|
#endif
|
|
{
|
|
if (!nimbleBluetooth) {
|
|
nimbleBluetooth = new NimbleBluetooth();
|
|
}
|
|
if (enable && !nimbleBluetooth->isActive()) {
|
|
powerMon->setState(meshtastic_PowerMon_State_BT_On);
|
|
nimbleBluetooth->setup();
|
|
}
|
|
// For ESP32, no way to recover from bluetooth shutdown without reboot
|
|
// BLE advertising automatically stops when MCU enters light-sleep(?)
|
|
// For deep-sleep, shutdown hardware with nimbleBluetooth->deinit(). Requires reboot to reverse
|
|
}
|
|
}
|
|
#else
|
|
void setBluetoothEnable(bool enable) {}
|
|
void updateBatteryLevel(uint8_t level) {}
|
|
#endif
|
|
|
|
void esp32ReleaseBluetoothMemoryIfUnused()
|
|
{
|
|
#ifdef CAN_RELEASE_BT_MEMORY
|
|
if (bluetoothMemoryReleased || !shouldReleaseBluetoothMemory()) {
|
|
return;
|
|
}
|
|
|
|
const int32_t heapBefore = ESP.getHeapSize();
|
|
const int32_t freeBefore = ESP.getFreeHeap();
|
|
|
|
// ESP_BT_MODE_BTDM releases all BT/BLE controller and host memory for this boot.
|
|
// It is intentionally irreversible until reboot, matching the runtime config behavior.
|
|
esp_err_t err = esp_bt_mem_release(ESP_BT_MODE_BTDM);
|
|
if (err == ESP_OK) {
|
|
bluetoothMemoryReleased = true;
|
|
LOG_INFO("Released BTDM memory because %s: heap %+d, free %+d", getBluetoothReleaseReason(),
|
|
(int32_t)ESP.getHeapSize() - heapBefore, (int32_t)ESP.getFreeHeap() - freeBefore);
|
|
} else {
|
|
LOG_WARN("BTDM memory release failed: %d", err);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void getMacAddr(uint8_t *dmac)
|
|
{
|
|
#if defined(CONFIG_IDF_TARGET_ESP32C6) && defined(CONFIG_SOC_IEEE802154_SUPPORTED)
|
|
auto res = esp_base_mac_addr_get(dmac);
|
|
assert(res == ESP_OK);
|
|
#else
|
|
auto res = esp_efuse_mac_get_default(dmac);
|
|
assert(res == ESP_OK);
|
|
#endif
|
|
}
|
|
|
|
bool getDeviceId(uint8_t *deviceId)
|
|
{
|
|
#if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) || \
|
|
defined(CONFIG_IDF_TARGET_ESP32C6)
|
|
// ESP32 factory burns a 128-bit unique id in efuse for S2+ and C3+ series (used for HMACs
|
|
// in the esp-rainmaker AIOT platform); a good stable hardware anchor for us too.
|
|
uint32_t unique_id[4];
|
|
esp_err_t err = esp_efuse_read_field_blob(ESP_EFUSE_OPTIONAL_UNIQUE_ID, unique_id, sizeof(unique_id) * 8);
|
|
if (err != ESP_OK) {
|
|
LOG_WARN("Failed to read unique id from efuse");
|
|
return false;
|
|
}
|
|
memcpy(deviceId, unique_id, sizeof(unique_id));
|
|
return true;
|
|
#else
|
|
// Classic ESP32 has no OPTIONAL_UNIQUE_ID efuse: fall back to the factory-burned MAC.
|
|
return getMacAddrDeviceId(deviceId);
|
|
#endif
|
|
}
|
|
|
|
#if HAS_32768HZ
|
|
#define CALIBRATE_ONE(cali_clk) calibrate_one(cali_clk, #cali_clk)
|
|
|
|
static uint32_t calibrate_one(rtc_cal_sel_t cal_clk, const char *name)
|
|
{
|
|
const uint32_t cal_count = 1000;
|
|
// const float factor = (1 << 19) * 1000.0f; unused var?
|
|
uint32_t cali_val;
|
|
for (int i = 0; i < 5; ++i) {
|
|
cali_val = rtc_clk_cal(cal_clk, cal_count);
|
|
}
|
|
return cali_val;
|
|
}
|
|
|
|
void enableSlowCLK()
|
|
{
|
|
rtc_clk_32k_enable(true);
|
|
|
|
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
|
|
uint32_t cal_32k = CALIBRATE_ONE(RTC_CAL_32K_XTAL);
|
|
|
|
if (cal_32k == 0) {
|
|
LOG_DEBUG("32k XTAL OSC has not started up");
|
|
} else {
|
|
rtc_clk_slow_freq_set(RTC_SLOW_FREQ_32K_XTAL);
|
|
LOG_DEBUG("RTC source now 32k XTAL");
|
|
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
|
|
CALIBRATE_ONE(RTC_CAL_32K_XTAL);
|
|
}
|
|
CALIBRATE_ONE(RTC_CAL_RTC_MUX);
|
|
CALIBRATE_ONE(RTC_CAL_32K_XTAL);
|
|
if (rtc_clk_slow_freq_get() != RTC_SLOW_FREQ_32K_XTAL) {
|
|
LOG_WARN("Failed to switch 32K XTAL RTC source to 32.768kHz !!! ");
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void esp32Setup()
|
|
{
|
|
/* We explicitly don't want to do call randomSeed,
|
|
// as that triggers the esp32 core to use a less secure pseudorandom function.
|
|
uint32_t seed = esp_random();
|
|
LOG_DEBUG("Set random seed %u", seed);
|
|
randomSeed(seed);
|
|
*/
|
|
|
|
#ifdef ADC_V
|
|
pinMode(ADC_V, INPUT);
|
|
#endif
|
|
|
|
LOG_DEBUG("Total heap: %d", ESP.getHeapSize());
|
|
LOG_DEBUG("Free heap: %d", ESP.getFreeHeap());
|
|
LOG_DEBUG("Total PSRAM: %d", ESP.getPsramSize());
|
|
LOG_DEBUG("Free PSRAM: %d", ESP.getFreePsram());
|
|
|
|
nvs_stats_t nvs_stats;
|
|
auto res = nvs_get_stats(NULL, &nvs_stats);
|
|
assert(res == ESP_OK);
|
|
LOG_DEBUG("NVS: UsedEntries %d, FreeEntries %d, AllEntries %d, NameSpaces %d", nvs_stats.used_entries, nvs_stats.free_entries,
|
|
nvs_stats.total_entries, nvs_stats.namespace_count);
|
|
|
|
LOG_DEBUG("Setup Preferences in Flash Storage");
|
|
|
|
// Create object to store our persistent data
|
|
Preferences preferences;
|
|
preferences.begin("meshtastic", false);
|
|
|
|
uint32_t rebootCounter = preferences.getUInt("rebootCounter", 0);
|
|
rebootCounter++;
|
|
preferences.putUInt("rebootCounter", rebootCounter);
|
|
// store firmware version and hwrevision for access from OTA firmware
|
|
String fwrev = preferences.getString("firmwareVersion", "");
|
|
if (fwrev.compareTo(optstr(APP_VERSION)) != 0)
|
|
preferences.putString("firmwareVersion", optstr(APP_VERSION));
|
|
uint8_t hwven = preferences.getUInt("hwVendor", 0);
|
|
if (hwven != HW_VENDOR)
|
|
preferences.putUInt("hwVendor", HW_VENDOR);
|
|
preferences.end();
|
|
LOG_DEBUG("Number of Device Reboots: %d", rebootCounter);
|
|
#if !MESHTASTIC_EXCLUDE_WIFI
|
|
String version = MeshtasticOTA::getVersion();
|
|
if (version.isEmpty()) {
|
|
LOG_INFO("MeshtasticOTA firmware not available");
|
|
} else {
|
|
LOG_INFO("MeshtasticOTA firmware version %s", version.c_str());
|
|
}
|
|
MeshtasticOTA::initialize();
|
|
#endif
|
|
|
|
// enableModemSleep();
|
|
|
|
// Since we are turning on watchdogs rather late in the release schedule, we really don't want to catch any
|
|
// false positives. The wait-to-sleep timeout for shutting down radios is 30 secs, so pick 45 for now.
|
|
// #define APP_WATCHDOG_SECS 45
|
|
#define APP_WATCHDOG_SECS 90
|
|
|
|
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
|
|
const esp_task_wdt_config_t wdt_config = {
|
|
.timeout_ms = APP_WATCHDOG_SECS * 1000,
|
|
.idle_core_mask = (1U << CONFIG_FREERTOS_NUMBER_OF_CORES) - 1U,
|
|
.trigger_panic = true,
|
|
};
|
|
res = esp_task_wdt_init(&wdt_config);
|
|
if (res == ESP_ERR_INVALID_STATE) {
|
|
LOG_WARN("Task watchdog already init, reconfiguring");
|
|
res = esp_task_wdt_reconfigure(&wdt_config);
|
|
}
|
|
assert(res == ESP_OK);
|
|
#else
|
|
res = esp_task_wdt_init(APP_WATCHDOG_SECS, true);
|
|
if (res == ESP_ERR_INVALID_STATE) {
|
|
LOG_WARN("Task watchdog already init, reusing");
|
|
res = ESP_OK;
|
|
}
|
|
assert(res == ESP_OK);
|
|
#endif
|
|
res = esp_task_wdt_status(NULL);
|
|
if (res == ESP_ERR_NOT_FOUND) {
|
|
res = esp_task_wdt_add(NULL);
|
|
}
|
|
assert(res == ESP_OK);
|
|
|
|
#if HAS_32768HZ
|
|
enableSlowCLK();
|
|
#endif
|
|
}
|
|
|
|
/// loop code specific to ESP32 targets
|
|
void esp32Loop()
|
|
{
|
|
esp_task_wdt_reset(); // service our app level watchdog
|
|
|
|
// for debug printing
|
|
// radio.radioIf.canSleep();
|
|
}
|
|
|
|
void cpuDeepSleep(uint32_t msecToWake)
|
|
{
|
|
/*
|
|
Some ESP32 IOs have internal pullups or pulldowns, which are enabled by default.
|
|
If an external circuit drives this pin in deep sleep mode, current consumption may
|
|
increase due to current flowing through these pullups and pulldowns.
|
|
|
|
To isolate a pin, preventing extra current draw, call rtc_gpio_isolate() function.
|
|
For example, on ESP32-WROVER module, GPIO12 is pulled up externally.
|
|
GPIO12 also has an internal pulldown in the ESP32 chip. This means that in deep sleep,
|
|
some current will flow through these external and internal resistors, increasing deep
|
|
sleep current above the minimal possible value.
|
|
|
|
Note: we don't isolate pins that are used for the LORA, LED, i2c, or ST7735 Display for the Chatter2, spi or the wake
|
|
button(s), maybe we should not include any other GPIOs...
|
|
*/
|
|
#if SOC_RTCIO_HOLD_SUPPORTED
|
|
static const uint8_t rtcGpios[] = {
|
|
#ifndef HELTEC_VISION_MASTER_E213
|
|
// For this variant, >20mA leaks through the display if pin 2 held
|
|
// Todo: check if it's safe to remove this pin for all variants
|
|
2,
|
|
#endif
|
|
#ifndef USE_JTAG
|
|
13,
|
|
#endif
|
|
34, 35, 37};
|
|
|
|
for (int i = 0; i < sizeof(rtcGpios); i++)
|
|
rtc_gpio_isolate((gpio_num_t)rtcGpios[i]);
|
|
#endif
|
|
|
|
// FIXME, disable internal rtc pullups/pulldowns on the non isolated pins. for inputs that we aren't using
|
|
// to detect wake and in normal operation the external part drives them hard.
|
|
#ifdef BUTTON_PIN
|
|
// Only GPIOs which are have RTC functionality can be used in this bit map: 0,2,4,12-15,25-27,32-39.
|
|
#if SOC_RTCIO_HOLD_SUPPORTED && SOC_PM_SUPPORT_EXT_WAKEUP
|
|
uint64_t gpioMask = (1ULL << (config.device.button_gpio ? config.device.button_gpio : BUTTON_PIN));
|
|
#endif
|
|
#ifdef ALT_BUTTON_WAKE
|
|
gpioMask |= (1ULL << BUTTON_PIN_ALT);
|
|
#endif
|
|
#ifdef BUTTON_NEED_PULLUP
|
|
gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
|
#endif
|
|
|
|
// Not needed because both of the current boards have external pullups
|
|
// FIXME change polarity in hw so we can wake on ANY_HIGH instead - that would allow us to use all three buttons (instead
|
|
// of just the first) gpio_pullup_en((gpio_num_t)BUTTON_PIN);
|
|
|
|
#ifdef ESP32S3_WAKE_TYPE
|
|
esp_sleep_enable_ext1_wakeup(gpioMask, ESP32S3_WAKE_TYPE);
|
|
#else
|
|
#if SOC_PM_SUPPORT_EXT_WAKEUP
|
|
#ifdef CONFIG_IDF_TARGET_ESP32
|
|
// ESP_EXT1_WAKEUP_ALL_LOW has been deprecated since esp-idf v5.4 for any other target.
|
|
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ALL_LOW);
|
|
#else
|
|
esp_sleep_enable_ext1_wakeup(gpioMask, ESP_EXT1_WAKEUP_ANY_LOW);
|
|
#endif
|
|
#endif
|
|
|
|
#endif // #end ESP32S3_WAKE_TYPE
|
|
#endif
|
|
variant_shutdown();
|
|
|
|
#if SOC_PM_SUPPORT_RTC_PERIPH_PD
|
|
// We want RTC peripherals to stay on
|
|
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
|
|
#endif
|
|
|
|
// User shutdown (DELAY_FOREVER / portMAX_DELAY): no RTC timer - align with nRF52 system_off semantics.
|
|
if (msecToWake != portMAX_DELAY)
|
|
esp_sleep_enable_timer_wakeup(msecToWake * 1000ULL); // call expects usecs
|
|
esp_deep_sleep_start();
|
|
}
|