From 1b3d378c678ab988338151be65a1db3969934ee2 Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Fri, 9 Oct 2026 12:27:14 +0200 Subject: [PATCH] enable MCU temp for ESP --- docs/ARCHITECTURE.md | 2 +- releasenotes/RELEASE_NOTES_1.17.7-zephcore.md | 5 +++++ zephcore/CMakeLists.txt | 7 +++++++ zephcore/adapters/board/ZephyrBoard.cpp | 9 +++++++-- zephcore/boards/common/esp32_coretemp.overlay | 16 ++++++++++++++++ 5 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 zephcore/boards/common/esp32_coretemp.overlay diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 3fc762b..2d9ec19 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -858,7 +858,7 @@ Repeaters and room servers default to `CONFIG_ZEPHCORE_REPEATER_GPS_INTERVAL_SEC - `powerOff()` (CLI `poweroff`/`shutdown`, UI power-off, low-battery shutdown all end in `zephcore_power_off()`, `board/zephyr_poweroff.c`): UI hook (heartbeat, display), GPS/sensor/buzzer rails off, LoRa held in reset, sw0 SENSE armed (nRF), power latch released, System OFF - Upstream's pwrmgt getters: reset cause from `boot_info`, the last fatal error from `fatal_reboot.c`'s `__noinit` record (reason, pc, lr, thread; resolve pc with `addr2line` on the same build), shutdown reason from the `/lfs/shutdn` marker, battery voltage at boot -**Telemetry** (`REQ_TYPE_GET_TELEMETRY_DATA`, every role, upstream's shape): battery on channel 1, then `sensors.querySensors()` (`adapters/sensors/ZephyrSensorManager.cpp`): GPS position on channel 1 while the GPS is on and has a fix, each environment sensor and power-monitor channel found at boot on its own channel from 2 up (probe order), board-local analog sensors (T1000-E) on channel 1; then the MCU temperature on channel 1. Servers give guests battery + MCU temperature only and honour the requester's inverse permission mask, as upstream. Encoded by `helpers/compat/CayenneLPP.h`: upstream's library API and wire format, but values round to the nearest step where the library truncates. +**Telemetry** (`REQ_TYPE_GET_TELEMETRY_DATA`, every role, upstream's shape): battery on channel 1, then `sensors.querySensors()` (`adapters/sensors/ZephyrSensorManager.cpp`): GPS position on channel 1 while the GPS is on and has a fix, each environment sensor and power-monitor channel found at boot on its own channel from 2 up (probe order), board-local analog sensors (T1000-E) on channel 1; then the MCU temperature on channel 1 (the SoC's die sensor: the `die-temp0` alias, which is `coretemp` on ESP32-S3/C3/C6, else the Nordic nodelabel `temp`; omitted where there is none: classic ESP32, STM32WL, MG24). Servers give guests battery + MCU temperature only and honour the requester's inverse permission mask, as upstream. Encoded by `helpers/compat/CayenneLPP.h`: upstream's library API and wire format, but values round to the nearest step where the library truncates. **Clock** (`adapters/clock/ZephyrRTCClock`): `setCurrentTime()` also writes the hardware RTC when the board has one (coalesced on the system work queue; a time outside 2000-2099 is not written), as upstream's `AutoDiscoverRTCClock` writes its chip; `seedCurrentTime()` is the boot restore, not written back. A chip is adopted at boot only once it is identified (`adapters/clock/ZephyrRTCDiscover.c`), because other parts can answer at the same address: a device is passed over when two reads each show a bit set that the chip's data sheet fixes at zero, or a time field out of range, and a time is restored only from a read in which every field is valid. An RV-3028 cleanly identified in 12-hour mode has its `twelve-hour-bit` cleared first, so the chip converts its own hours. No time is taken from a cleanly identified RV-3028 whose `twelve-hour-bit` register cannot be read or reads FFh, or whose clear or re-read fails. The time is written in each chip's own encoding (the RX8130CE takes a one-hot weekday, `weekday-one-hot` in its descriptor), and an RV-3028 whose descriptor carries `rv3028-eeprom-config` (the RAK12002 on a RAK4631) has that configuration, its backup switchover, stored in the chip's EEPROM. Without a hardware RTC, the last time read is kept in no-init memory (RTC slow memory on ESP32) and restored after a reset that keeps RAM (reboot, crash, watchdog), as upstream's `ESP32RTCClock`; a power cycle starts from 1970 again. diff --git a/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md b/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md index 76b5539..88beb98 100644 --- a/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md +++ b/releasenotes/RELEASE_NOTES_1.17.7-zephcore.md @@ -175,6 +175,11 @@ matches the SoftDevice version of your bootloader. puts it back to 47 hours. - **Malformed encrypted packets are rejected earlier**, before any cryptography runs. - **Large contact lists**: up to 24 contacts can share the same one-byte hash, up from 8. +- **ESP32-S3, ESP32-C3 and ESP32-C6 boards report the MCU temperature** in telemetry, as nRF52 boards + already did ([#107](https://github.com/liquidraver/ZephCore/issues/107), thanks to **svenlange2**). + The older ESP32 in the TTGO LoRa32 and T-Beam has no usable sensor and still reports none. On the + ThinkNode M9 and Meshnology W12 the radio now also recalibrates itself when the temperature has + moved by 5 C or more, as it already does on nRF boards with an LR1110 or LR2021. - **`sensor list`** with a negative start index is rejected instead of being used ([PR #100](https://github.com/liquidraver/ZephCore/pull/100), thanks to **ptr727**). - **T1000-E**: a pin that was wrongly driven as a sensor enable is left alone. Sensor readings are diff --git a/zephcore/CMakeLists.txt b/zephcore/CMakeLists.txt index 013bdbf..1f4d06f 100644 --- a/zephcore/CMakeLists.txt +++ b/zephcore/CMakeLists.txt @@ -181,6 +181,13 @@ if(ZEPHCORE_PLATFORM STREQUAL "esp32") message(STATUS " ESP32 CPU: 80 MHz (observers keep the SoC maximum)") endif() + # SoC temperature sensor (MCU temperature in telemetry, image-calibration + # drift on LR11xx/LR2021): every role, every SoC that has the node. The + # classic ESP32 (".../esp32/...") has neither the node nor a driver. + if(NOT BOARD MATCHES "/esp32/") + zephcore_add_overlay(${_zc}/esp32_coretemp.overlay) + endif() + # Light sleep: REPEATERS on boards that declare it. 80 MHz cuts the idle draw; # light sleep is what removes it (mechanism and requirements: # boards/common/pm_esp32.conf). Repeaters only: the Espressif HCI driver takes diff --git a/zephcore/adapters/board/ZephyrBoard.cpp b/zephcore/adapters/board/ZephyrBoard.cpp index b00645a..e706a4e 100644 --- a/zephcore/adapters/board/ZephyrBoard.cpp +++ b/zephcore/adapters/board/ZephyrBoard.cpp @@ -368,9 +368,14 @@ float ZephyrBoard::getAdcMultiplier() const float ZephyrBoard::getMCUTemperature() { - /* nRF52840 die temperature sensor - "nordic,nrf-temp" at 0x4000c000 - * Nodelabel "temp" is defined in nrf52840.dtsi, status="okay" by default */ + /* SoC die temperature sensor: the `die-temp0` alias where the SoC dtsi + * declares one (Espressif `coretemp`), else the nodelabel `temp` (Nordic). + * NULL when the node is absent or disabled. */ +#if DT_NODE_EXISTS(DT_ALIAS(die_temp0)) + const struct device *dev = DEVICE_DT_GET_OR_NULL(DT_ALIAS(die_temp0)); +#else const struct device *dev = DEVICE_DT_GET_OR_NULL(DT_NODELABEL(temp)); +#endif if (!dev || !device_is_ready(dev)) { return NAN; } diff --git a/zephcore/boards/common/esp32_coretemp.overlay b/zephcore/boards/common/esp32_coretemp.overlay new file mode 100644 index 0000000..7c3961e --- /dev/null +++ b/zephcore/boards/common/esp32_coretemp.overlay @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: MIT + * ESP32-S3 / C3 / C6: enable the SoC's internal temperature sensor. + * + * Every Espressif SoC dtsi that has the sensor declares it as `coretemp`, + * disabled, behind the alias `die-temp0`. Enabling the node is all it takes: + * CONFIG_ESP32_TEMP defaults on with it, and ZephyrBoard::getMCUTemperature() + * finds it through the alias. + * + * Not for the classic ESP32, which has no such node (a reference to &coretemp + * is a dtc error there) and no driver. CMakeLists.txt skips it for that SoC. + */ + +&coretemp { + status = "okay"; +};