* fix(modules): null-check packetPool/clientNotificationPool/mqttClientProxyMessagePool allocations
Follow-up to fix/meshpacket-alloc-null-checks (PR meshtastic/firmware#10948),
which fixed the core routing path (Router/NextHopRouter/ReliableRouter/
MeshService/RadioLibInterface) after reproducing a HardFault on STM32WL
hardware under real mesh traffic. This covers the same allocCopy()/
allocZeroed() unchecked-return pattern in lower-frequency paths that were
out of scope for that PR: SimRadio.cpp (portduino sim RX/TX), NodeInfoModule,
the Telemetry modules' power-saving-sleep notifications, MQTT (map report,
client-proxy messages, config-validation notifications), PositionModule,
SerialModule, and KeyVerificationModule.
Sites where the allocation result was already read back through an
existing null check downstream (e.g. Telemetry's lastMeasurementPacket,
SimRadio's receivingPacket, AdminModule::sendWarning) were left as-is -
verified safe, not touched.
Router::allocForSending() (and everything that funnels through it, e.g.
allocDataProtobuf()) remains unguarded and out of scope here too - it's
called from 30+ sites across the codebase and needs its own audit.
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
* fix(KeyVerificationModule): bound cn->message writes with snprintf
CodeRabbit flagged unbounded sprintf() writes into the fixed-size
ClientNotification.message buffer as a static-analysis nitpick while
reviewing this PR's allocation null-checks. Align these four sites with
the snprintf(dest, sizeof(dest), ...) pattern already used for the same
field elsewhere in this PR (SerialModule.cpp, MQTT.cpp).
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
---------
Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
On platforms where these pools are heap-backed (MemoryDynamic - used
whenever there isn't enough static RAM for a fixed pool, e.g.
ARCH_STM32WL or BOARD_HAS_PSRAM), allocCopy()/allocZeroed() return
nullptr on allocation failure and already log a warning, but most
callers dereferenced the result unconditionally. Under real heap
pressure this reliably produced a HardFault - reproduced on STM32WL
hardware under sustained mesh traffic, including the RX entry point
(RadioLibInterface::handleReceiveInterrupt) where every received
packet is allocated.
Adds null checks at all call sites that were missing one, mirroring
the guard pattern already used correctly elsewhere in the same files
(e.g. RadioInterface.cpp's sendErrorNotification). On allocation
failure, callers now skip the send/retransmission/notification and
log nothing further (the allocator already did) rather than crash.
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
isCharging()/getHasUSB() were hard-compiled to always return false on
this board: the nrfx_power_usbstatus_get() block in Power.cpp that
derives them is gated behind #ifdef NRF_APM, and this board's variant.h
never defined it - unlike tracker-t1000-e and wio-tracker-wm1110, which
do. Confirmed on hardware: before this change, `Battery: usbPower=0,
isCharging=0` regardless of actual USB/charge state; after, `usbPower=1,
isCharging=1` while genuinely on USB power with a battery attached.
Same underlying gap as #4367, fixed for tracker-t1000-e and
wio-tracker-wm1110 in #4376 (which also fixed a deeper bug where
PowerStatus notifications were gated behind a battery-level null check -
that part is already fixed generically in Power.cpp today, so this board
only needed the define). Looks like a simple oversight from when this
board was added, since it's newer than the two boards #4376 touched.
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
Every other .cpp/.h pair in src/ (350 of 351) uses identical
capitalization between the two files. Power.cpp/power.h was the sole
outlier; this aligns it with the rest of the codebase. No functional
change — all includes already resolved this file the same way on
case-sensitive filesystems.
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* fix(power): check EXT_CHRG_DETECT with defined(), not truthiness
#elif EXT_CHRG_DETECT tests the macro's numeric value rather than
whether it's defined. Every existing board happens to assign it to a
nonzero pin, so this never misfired, but a board using pin 0 (e.g.
PA0 on STM32) would silently skip this branch even though the pin is
defined and configured.
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
* fix(power): consider EXT_CHRG_DETECT in isVbusIn()
isVbusIn() used EXT_PWR_DETECT alone. On boards where that pin is a
charge-complete/standby signal rather than a continuous power-present
signal, isVbusIn() reports false for the entire duration of an active
charge. If EXT_CHRG_DETECT is also defined, OR it in: charging can
only happen when power is present, so this only adds correct true
results and is a no-op for boards where EXT_PWR_DETECT already means
"power present" continuously.
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
---------
Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* stm32wl: check HAL_FLASH_Unlock() return in _internal_flash_erase
_internal_flash_prog already checks HAL_FLASH_Unlock() and returns
LFS_ERR_IO on failure. _internal_flash_erase discarded the return
value, proceeding to erase even if the flash was not unlocked.
Apply the same check for consistency and safety.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* stm32wl: fix _internal_flash_prog to abort on first write error
Previously the programming loop continued to the next doubleword after
HAL_FLASH_Program() failed, potentially writing to invalid addresses
and returning a misleading error code only at the end (last iteration).
HAL_FLASH_Lock() was also skipped on the mid-loop early return path.
- Move bounds check before the loop (validate full range at once)
- Break on first HAL error so subsequent doublewords are not written
- Move HAL_FLASH_Lock() after the loop so it always runs
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* stm32wl: clear stale flash SR error flags before erase and program
Stale error flags in FLASH->SR from a previous failed operation can
cause HAL_FLASH_Program() or HAL_FLASHEx_Erase() to return HAL_ERROR
immediately without attempting the operation.
Add __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS) after each
HAL_FLASH_Unlock() in both _internal_flash_prog and
_internal_flash_erase to ensure a clean state before each operation.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* stm32wl: reject flash prog writes not aligned to 8-byte doubleword
The STM32WL HAL minimum write unit is one 64-bit doubleword (8 bytes).
_internal_flash_prog silently truncated any trailing bytes when size % 8
!= 0 because dw_count = size / 8 drops the remainder. Return LFS_ERR_INVAL
early so LittleFS sees the error rather than a silent short write.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(nrf52,fs): use atomic SafeFile rename instead of direct write
NRF52 was bypassing the .tmp/readback/rename path entirely — openFile()
deleted the target file and wrote directly to it, and close() returned
true without verifying the write or renaming anything.
Adafruit_LittleFS::rename() calls lfs_rename() directly (confirmed at
Adafruit_LittleFS.cpp:205). Remove both ARCH_NRF52 guards so NRF52
follows the same write-to-.tmp → readback-hash → rename path used by
all other platforms.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(admin): skip uiconfig.proto save on devices without a screen
handleStoreDeviceUIConfig() was writing /prefs/uiconfig.proto
unconditionally. MenuHandler.cpp is already gated behind #if HAS_SCREEN,
so there is no path that populates UI config on screen-less platforms.
Guard the save with #if HAS_SCREEN to avoid wasting a flash block on
devices that will never use it.
The read path (handleGetDeviceUIConfig) does not touch the filesystem
and needs no change.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fs: enable format-on-retry for all platforms in saveToDisk
The FSCom.format() call on save failure was guarded to ARCH_NRF52 with
a comment that other platforms were not ready (bug #4184). STM32WL was
added to the guard in a prior commit. All platforms now expose format
semantics and the retry logic is identical — remove the guard.
To keep NodeDB.cpp platform-agnostic and fix a CI failure on native-tft
(portduino's fs::FS has no format() method), introduce fsFormat() in
FSCommon as the single call-site for all callers:
- Embedded (ESP32, NRF52, STM32WL, RP2040): delegates to FSCom.format()
- Portduino: rmDir("/prefs") + FSBegin() (a no-op on portduino).
rmDir("/prefs") is already called unconditionally by factoryReset()
(NodeDB.cpp:504), so both primitives are proven on portduino.
Replace both direct FSCom.format() calls in NodeDB.cpp with fsFormat().
Note: we do not run portduino locally — portduino/native build testers
please verify the format-on-retry path.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* DO NOT MERGE: nrf52(fs): add File() default constructor bound to InternalFS
Adds File() to the Adafruit LittleFS File class (in the Meshtastic
Adafruit_nRF52_Arduino fork), delegating to File(InternalFS). This
matches the default-constructible File API on all other platforms.
The constructor is implemented in Adafruit_LittleFS_File.cpp rather
than inline in the header to avoid a circular include between
Adafruit_LittleFS_File.h and InternalFileSystem.h.
FOLLOW-UP REQUIRED: nrf52.ini points to a commit SHA on the
mesh-malaysia/Adafruit_nRF52_Arduino fork instead of the upstream
meshtastic framework. Once meshtastic/Adafruit_nRF52_Arduino#5 is
merged, revert nrf52.ini to point back to the upstream meshtastic
framework URL.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* stm32wl(fs): add File() default constructor and document LFS tunables
Adds File() to STM32_LittleFS_Namespace::File, delegating to
File(InternalFS). Implemented in the .cpp to avoid a circular include
between STM32_LittleFS_File.h (which cannot include LittleFS.h) and
the InternalFS extern declaration.
This matches the File API on ESP32/RP2040/Portduino and is a
prerequisite for removing the ARCH_STM32WL guard in xmodem.h.
No behavior change — the constructor leaves the file in the same
closed/unattached state as File(InternalFS) would.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fs: remove arch-specific ifdefs from FSCommon, SafeFile, xmodem
Now that NRF52 and STM32WL have File() default constructors and NRF52
has working atomic SafeFile rename, the capability gaps are closed.
Remove all per-arch guards across the shared FS layer:
FSCommon.cpp — renameFile():
Use FSCom.rename() on all platforms. Adafruit_LittleFS::rename()
calls lfs_rename() directly (Adafruit_LittleFS.cpp:205). The
copy+delete fallback on NRF52/RP2040 was never necessary.
FSCommon.cpp — getFiles():
Replace four ARCH_ESP32 guards with a single filepath pointer at
the top of the loop (file.path() on ESP32, file.name() elsewhere).
Fix strcpy(fileInfo.file_name, filepath): bounded to
sizeof(fileInfo.file_name)-1 with explicit NUL termination to prevent
overflow of the 228-byte meshtastic_FileInfo::file_name array.
FSCommon.cpp — listDir():
Same filepath pointer approach. NRF52/STM32WL were in an else-branch
that only logged but never deleted — now all platforms follow the
unified del path. 12 guards → 2.
Fix three strncpy(buffer, ..., sizeof(buffer)) calls that did not
NUL-terminate when source length >= sizeof(buffer) (255 bytes).
Add explicit buffer[sizeof(buffer)-1] = '\0' after each.
FSCommon.cpp — rmDir():
Use listDir(del=true) everywhere. The ARCH_NRF52 rmdir_r() path and
the ARCH_ESP32|RP2040|PORTDUINO listDir() path collapse to one line.
SafeFile.cpp:
ARCH_NRF52 bypass removed (handled in preceding commit).
xmodem.h:
File file; now works on all platforms via default constructors
added in the two preceding commits.
Remaining #ifdef ARCH_ESP32 in FSCommon.cpp: exactly 4, all for the
file.path() vs file.name() API difference (ESP32 Arduino LittleFS
returns the full path; all others return only the name). That
difference lives in the framework and cannot be closed here.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* stm32wl(fs): add write-behind page cache, reduce virtual block size and FS reservation (FORMAT BREAK)
Adds a write-behind (RMW) page cache to the STM32WL LittleFS driver,
modelled after the NRF52 Adafruit approach (flash_cache.c). This allows
LFS to use 256-byte virtual blocks backed by 2048-byte physical pages:
the erase/prog callbacks accumulate changes in a 2 KB RAM buffer; the
sync callback (and page eviction on page-change) flushes with a single
HAL physical-erase + doubleword-program pass.
LFS tunables changed (FORMAT BREAK — superblock parameters):
block_size: 2048 B → 256 B (8 virtual blocks per physical page)
read_size: 2048 B → 256 B (= block_size)
prog_size: 2048 B → 256 B (= block_size; hardware min is 8 B)
block_count: 112 → 80 (14 phys pages → 10 phys pages = 20 KiB)
Benefits:
- Internal fragmentation: max 2047 B/file → max 255 B/file
- Heap per open LFS file: ~4 KB → 512 B (prog + read buffers)
- Code flash headroom: 6.7 KB → ~14.1 KB (+7.4 KB)
- Block budget: 80 virtual blocks, worst-case peak ~20, ~60 free
Updates board_upload.maximum_size in wio-e5/platformio.ini from 233472
(256 KB − 28 KB) to 241664 (256 KB − 20 KB) to match the reduced FS
reservation.
Justification for the format break: the prior STM32WL firmware had
several flash write bugs fixed earlier in this series (missing error
flag clearing, no abort on first write failure, unaligned write
acceptance). These bugs very likely caused silent config corruption on
deployed devices. The format break should be treated as an enhancement:
it provides a clean, reliably-written starting point. Users will need
to reconfigure their device once after this update.
Correctness fixes applied to the cache implementation:
- alignas(8) on _page_cache: the buffer was uint8_t[] (alignment 1)
but _flash_cache_flush casts it to const uint64_t* — undefined
behaviour per C++ standard, potential Cortex-M hardfault. alignas(8)
guarantees the required alignment for the doubleword cast.
- HAL_FLASH_Lock() return value: was discarded. Now assigned to
lock_rc and propagated into rc if prior writes succeeded, so LFS
sees the error rather than a false success.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* stm32wl(fs): reduce FS reservation from 10 pages to 7 pages (FORMAT BREAK)
Reduces LFS_FLASH_TOTAL_SIZE from 10 × 2 KiB pages (20 KiB) to
7 × 2 KiB pages (14 KiB), freeing 6 KiB for firmware.
board_upload.maximum_size updated accordingly across all STM32WL variants:
241664 (256 KiB - 20 KiB) → 247808 (256 KiB - 14 KiB)
This is a FORMAT BREAK: existing filesystems must be erased before use.
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <me@ndoo.sg>
* fix(fs): return false in renameFile() when FSCom is not defined
Avoids undefined behavior and -Wreturn-type warnings in configurations
that compile FSCommon.cpp without a filesystem backend.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Mirror the EXT_PWR_DETECT pattern: replace runtime static variables
(ext_chrg_detect_mode, ext_chrg_detect_value) with compile-time macros.
Auto-infer EXT_CHRG_DETECT_VALUE from EXT_CHRG_DETECT_MODE when the mode
is INPUT_PULLUP (→ LOW) or INPUT_PULLDOWN (→ HIGH); default to HIGH.
This fixes inverted polarity on variants that define EXT_CHRG_DETECT_MODE
INPUT_PULLUP without an explicit EXT_CHRG_DETECT_VALUE (e.g. russell):
previously the runtime default of HIGH caused isCharging() to return the
opposite of the correct value. With auto-inference the correct LOW active
level is now derived at compile time.
Remove the now-redundant EXT_CHRG_DETECT_VALUE HIGH from ELECROW-ThinkNode-M4
variant.h since HIGH is the inferred default.
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Andrew Yong <noreply@example.com>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
* fix(stm32/russell): define ENABLE_HWSERIAL2 and Serial2 pins
The Russell board variant was missed during Initial serialModule cleanup
(PR #9465), which began requiring Serial2 to be explicitly defined via
ENABLE_HWSERIAL2 and PIN_SERIAL2_TX/RX rather than relying on implicit
defaults, causing a build error.
Signed-off-by: Andrew Yong <me@ndoo.sg>
* feat(stm32/russell): add BME680 support, exclude modules to fit flash
The BME680 is hardware footprint compatible with the BME280 already
present on the Russell board, so add it as an additional lib dep to
enable environment sensing (temperature, humidity, pressure,
gas resistance).
The STM32 target has very limited flash. Even traceroute alone causes
overflow, so the following modules are excluded to stay within budget:
- RANGETEST
- DETECTIONSENSOR
- EXTERNALNOTIFICATION
- POWERSTRESS
- NEIGHBORINFO
- TRACEROUTE
- WAYPOINT
AIR_QUALITY_SENSOR is also excluded as it requires the BSEC2 library
for real IAQ output, which alone overflows flash by ~44KB on this
target. The Adafruit BME680 library is used instead for raw sensor
readings.
Signed-off-by: Andrew Yong <me@ndoo.sg>
---------
Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
mallinfo().fordblks counts only free bytes within the committed arena.
On STM32WL (newlib sbrk heap) the arena grows lazily from _end toward SP,
so fordblks reads near-zero at early boot even when ~48 KB of addressable
space remains. This caused NodeDB::isFull() to fire prematurely and evict
nodes on a freshly booted device.
Fix getFreeHeap() to include uncommitted sbrk headroom (SP - sbrk(0)) so
the returned value reflects true available memory throughout the boot
lifecycle.
Introduce MESHTASTIC_DYNAMIC_SBRK_HEAP as an opt-in build flag (set in
stm32.ini) so the fix is gated to platforms with a dynamic sbrk heap
rather than a static heap. Future platforms with the same heap model can
opt in by adding this flag.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Integrate STM32 battery monitoring into AnalogBatteryLevel, supporting
external GPIO ADC pins as well as internal VBAT channel.
Features:
- ADC reading using STM32 LL (Lower Layer) macros supporting external
ADC channels and internal VBAT channel (AVBAT)
- ADC compensation using STM32 LL macros with factory-calibrated VREFINT
(AVREF) for accurate voltage measurement
- LFP battery OCV curve for STM32WL using AVBAT (STM32 VDD absolute
maximum supply voltage 3.9V, direct connection of Li-Po batteries is
not supported)
Internal VBAT channel implemented in:
- Russell
- RAK3172
In these variants, ADC_MULTIPLIER = (1.01f * 3) = 3.30 as there is a
3:1 internal divider (DS13105 Rev 12 §5.3.21), and a bit of tolerance
as the actual 10% spec leads to readings much too high.
Assisted-by: Claude:sonnet-4-5
Signed-off-by: Andrew Yong <me@ndoo.sg>
Problem:
- Inserting a µSD card causes RadioLib to hit a critical error and reboot
- Device enters a boot loop as the SD card remains inserted
Reproduction:
- Insert a µSD card and power on
- RadioLib reports a critical error on boot
- Device reboots, repeating indefinitely
Root cause:
- On T-Lora Pager, SX1262 and the µSD slot share the same physical SPI bus
(same SCK/MOSI/MISO pins, differentiated only by CS)
- SDCARD_USE_SPI1 is intended for boards where SD is on a separate SPI bus;
it initializes a second ESP32 SPI peripheral (SPI3) for SD
- SPI2 is already driving those same pins for LoRa, so both controllers
simultaneously drive the same GPIO lines, causing bus contention
Fix:
- Remove SDCARD_USE_SPI1 so both devices share a single SPI peripheral (SPI2),
with CS pins providing device selection as intended
- Tested on a custom fork of device-ui; LoRa and SD card map tiles both work
correctly with an SD card inserted
Signed-off-by: Andrew Yong <me@ndoo.sg>
Problem:
- Inserting a µSD card causes RadioLib to hit a critical error and reboot
- Device enters a boot loop as the SD card remains inserted
Reproduction:
- Insert a µSD card and power on
- RadioLib reports a critical error on boot
- Device reboots, repeating indefinitely
Root cause:
- On T-Lora Pager, SX1262 and the µSD slot share the same physical SPI bus
(same SCK/MOSI/MISO pins, differentiated only by CS)
- SDCARD_USE_SPI1 is intended for boards where SD is on a separate SPI bus;
it initializes a second ESP32 SPI peripheral (SPI3) for SD
- SPI2 is already driving those same pins for LoRa, so both controllers
simultaneously drive the same GPIO lines, causing bus contention
Fix:
- Remove SDCARD_USE_SPI1 so both devices share a single SPI peripheral (SPI2),
with CS pins providing device selection as intended
- Tested on a custom fork of device-ui; LoRa and SD card map tiles both work
correctly with an SD card inserted
Signed-off-by: Andrew Yong <me@ndoo.sg>
* fix(MQTT): First MapReport does not get sent
Throttle::isWithinTimespanMs(last_report_to_map, map_publish_interval_msecs) is
used to maintain the map reporting interval, but because last_report_to_map has
an initial value of 0, the map report routine does not start until the system
millis() time has passed map_publish_interval_msecs.
Fix this by adding a check that last_report_to_map is not 0.
Signed-off-by: Andrew Yong <me@ndoo.sg>
* feat(MQTT): Send MapReporting immediately upon location fix
Do not update last_report_to_map when Map Report is attempted without a valid
location, as this results in waiting up to an hour (or configured Map Report
interval).
That usually happens because most nodes do not keep GPS warm, so GPS usually
locks after the first attempt at Map Report.
This change also results in the log WARNing message getting spammed until a
location is obtained, so remove the message for now.
Signed-off-by: Andrew Yong <me@ndoo.sg>
* feat(MQTT): Throttled warning when position is not available for MapReport
Signed-off-by: Andrew Yong <me@ndoo.sg>
---------
Signed-off-by: Andrew Yong <me@ndoo.sg>
- STM32WLE5CCU6
- NFC (unsupported): NXP NT3H2211W0FTTJ (NTAG I2C plus: NFC Forum T2T with I2C interface, password protection and energy harvesting)
- Sensor (unsupported): Analog ADuCM355 (SHTC3 is connected to ADuCM355 and not directly accessible)
- Bicolor LED
- User button (presently not functional in STM32 variants)
The definitions for sensor voltage control are present but commented out to save power, due to lack of sensor support.
Powered by 4x 4000mAh RAMWAY ER18505 Li-SOCl2 batteries.
Flashing:
1. Power down device (remove batteries)
2. Connect USB-UART to J1 (USART2), pinout is below, do not connect +3V3 pin yet
3. Short BOOT pins next to J1
4. Connect +3V3 pin or insert batteries while BOOT pins are shorted
5. Use STM32CubeProgrammer, connect by UART mode
6. Load firmware .hex and download
J1 (USART2); Molex Picoblade (P=1.25mm * 4)
1. +3V3
2. PA3_USART2_RX_J1
3. PA2_USART2_TX_J1
4. GND
Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Support softsleep by defining PIN_GPS_STANDBY on CDTop CD-PA1010D.
This differs from existing MTK GPS e.g. L76K, where pulling PIN_GPS_STANDBY (WAKE-UP pin) low is not sufficient to put the GPS module in standby.
An additional `$PMTK225,4*2F` must be sent to enter "Backup Mode", which is exited by bringing PIN_GPS_STANDBY (WAKE-UP pin) high.
Refer to datasheet[0] §1.9.3 "Backup Mode".
0: https://cdn-learn.adafruit.com/assets/assets/000/084/295/original/CD_PA1010D_Datasheet_v.03.pdf
Signed-off-by: Andrew Yong <me@ndoo.sg>
* Force coverage tests to run in simulation mode
* Revert "Force coverage tests to run in simulation mode"
This reverts commit e4ec719e6f.
* fix: Move `#include "variant.h"` to top of file (fixes#8276)
The original line being further down the file causes any #ifdef/defined() checks for definitions in variant.h to silently skip.
This was noticed when `USE_GC1109_PA` in Heltec v4 and Heltec Wireless Tracker failed to correct program TX_GAIN_LORA, but will also affect any variant.h-dependent configurations in this file, if they would have been defined above where the `#include` previously was.
---------
Co-authored-by: Austin Lane <vidplace7@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* Force coverage tests to run in simulation mode
* Revert "Force coverage tests to run in simulation mode"
This reverts commit e4ec719e6f.
* fix: Move `#include "variant.h"` to top of file (fixes#8276)
The original line being further down the file causes any #ifdef/defined() checks for definitions in variant.h to silently skip.
This was noticed when `USE_GC1109_PA` in Heltec v4 and Heltec Wireless Tracker failed to correct program TX_GAIN_LORA, but will also affect any variant.h-dependent configurations in this file, if they would have been defined above where the `#include` previously was.
---------
Co-authored-by: Austin Lane <vidplace7@gmail.com>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
* docs(xiao_ble): Simplify building and flashing instructions
- **Update Bootloader** - deleted this section, as Meshtastic now builds-in a compatible SoftDevice
- **PlatformIO Environment Preparation** - deleted this section, as Meshtastic now builds-in a compatible SoftDevice
- **Build Meshtastic** - simplified it greatly by referring to Meshtastic documentation
- **Flash the firmware to the Xiao BLE** - simplified it greatly as Meshtastic now builds firmware.uf2; added some observations for a succesful flash
Light cleanup of Markdown and renumbering of sections.
Signed-off-by: Andrew Yong <me@ndoo.sg>
* docs(xiao_ble): Replace some HTML with Markdown, cleanup Markdown
Signed-off-by: Andrew Yong <me@ndoo.sg>
* docs(xiao_ble): Update SX126X_TXEN definition location
Signed-off-by: Andrew Yong <me@ndoo.sg>
* docs(xiao_ble): Fresher information about E22 modules
Signed-off-by: Andrew Yong <me@ndoo.sg>
* docs(xiao_ble): Instructions for E22...M33S modules
Also re-order the Build section to come after the Wiring section since the build instructions require special attention if the wiring/modules differ from the variant's expected pins/module.
Signed-off-by: Andrew Yong <me@ndoo.sg>
* docs(xiao_ble): Rename all XIAO BLE to XIAO nRF52840
Signed-off-by: Andrew Yong <me@ndoo.sg>
* docs(xiao_ble): Remove note about Linux since shell script is gone
Signed-off-by: Andrew Yong <me@ndoo.sg>
* docs(xiao_ble): trunk fmt and fix links
Signed-off-by: Andrew Yong <me@ndoo.sg>
---------
Signed-off-by: Andrew Yong <me@ndoo.sg>
These DIY builds are functionally similar to the legacy xiao_ble variant, but use a pinout harmonized with the officially-supported XIAO nRF52840 & Wio-SX1262 Kit for Meshtastic (SKU 102010710).
An additional E22-900M33S variant is provided to ensure SX1262 transmit power is set below the maximum PA input for that module, to avoid damaging it.
- seeed_xiao_nrf52840_e22_900m30s:
- XIAO nRF52840 + EBYTE E22-900M30S
- EBYTE E22 pinout matching Wio-SX1262 (SKU 113010003)
- I2C
- SDA: D6
- SCL: D7
- User button: D0
- Gain is programmed in firmware - user Tx power setting is the desired final output power
- seeed_xiao_nrf52840_e22_900m33s:
- XIAO nRF52840 + EBYTE E22-900M33S
- EBYTE E22 pinout matching Wio-SX1262 (SKU 113010003)
- I2C
- SDA: D6
- SCL: D7
- User button: D0
- Gain is programmed in firmware - user Tx power setting is the desired final output power
Signed-off-by: Andrew Yong <me@ndoo.sg>
* chore(seeed_xiao_nrf52840_kit): Use build flag for L76K GNSS, rename variant.h ifdef
Signed-off-by: Andrew Yong <me@ndoo.sg>
* feat(seeed_xiao_nrf52840_kit): Support multiple SX126x pinouts via build flags
Signed-off-by: Andrew Yong <me@ndoo.sg>
* feat(seeed_xiao_nrf52840_kit): Pin D0 as user button if pin is unused
Signed-off-by: Andrew Yong <me@ndoo.sg>
* feat: EBYTE E22 and NiceRF gain and SX1262 max power defines
Signed-off-by: Andrew Yong <me@ndoo.sg>
* chore(xiao_ble): Move variant to DIY and extend from seeed_xiao_nrf52840_kit
Signed-off-by: Andrew Yong <me@ndoo.sg>
* feat(seeed_xiao_nrf52840_kit): Pin D6, D7 as I2C SDA, SCL if pins are unused
Signed-off-by: Andrew Yong <me@ndoo.sg>
---------
Signed-off-by: Andrew Yong <me@ndoo.sg>
* feat: seeed_xiao_nrf52840_kit improvements
- LEDs:
- Change RGB LED to be active low as it is common anode
- Remove re-definition of LED_PIN
- Use red LED to indicate flash writes
- Use blue LED as user LED (External Notification module)
- GPIO: Re-word unused BUTTON_PIN comment
- Wire: Set I2C pins to match XIAO nRF52840 Sense's LSM6DS3TR IMU
- Battery:
- Use charge LED to detect charging state
- Move voltage divider boilerplate out of src/main.cpp and into initVariant()
- Fix dependencies for above in related XIAO BLE DIY variants
Build tested variants:
- seeed_xiao_nrf52840_kit
- xiao_ble
- seeed-xiao-nrf52840-wio-sx1262
Flashed to and tested on hardware:
- seeed_xiao_nrf52840_kit
Signed-off-by: Andrew Yong <me@ndoo.sg>
* chore(seeed_xiao_nrf52840_kit): Re-order generic GPIO definitions
Signed-off-by: Andrew Yong <me@ndoo.sg>
* chore: Use ADC_CTRL for XIAO nRF52840
Signed-off-by: Andrew Yong <me@ndoo.sg>
---------
Signed-off-by: Andrew Yong <me@ndoo.sg>
- Rename REGULATORY_GAIN_LORA to TX_GAIN_LORA
- Move gain-based Tx power clamping from RadioInterface::applyModemConfig() to RadioInterface::limitPower()
- User-configured Tx power now matches the Tx power out of the device connector
- Re-order [LoRa Chip]Interface.cpp limitPower() to take place before the final Tx power clamping so we clamp based on the pre-PA Tx power rather than user-configured Tx power
Tested on XIAO BLE variant.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Unset BUTTON_PIN and BUTTON_NEED_PULLUP as the board ships without a user button.
Devices and users expecting a button on GPIO12 have to set [GPIO for user button](https://meshtastic.org/docs/configuration/radio/device/#gpio-for-user-button) to 12 (or any GPIO pin the momentary switch was connected to) to restore functionality.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Specify REGULATORY_GAIN and SX126X_MAX_POWER to prevent exceeding regulatory and hardware limits (i.e. overloading the PA input) respectively.
Also update the build flag to define EBYTE_E22_900M30S instead of just EBYTE_E22, since all the builds on the Discourse topic [New 1W DIY variant: Xiao nRF52840 + Ebyte E22-900M30S](https://meshtastic.discourse.group/t/new-1w-diy-variant-xiao-nrf52840-ebyte-e22-900m30s/7904) are using this module.
That should make it clearer as well that the variant header file should be tweaked if DIY builds are using stronger (E22-900M33S, not commonly available at this time) or weaker (E22-900M22S, not popular for DIY builds due to lack of differentiation from ordinary SX1262 modules).
Retain EBYTE_E22 flag alongside EBYTE_E22_900M30S build flag to prevent possible regressions in code paths generally intended for EBYTE E22 modules.
Signed-off-by: Andrew Yong <me@ndoo.sg>
REGULATORY_GAIN is the total system gain in dBm to subtract from the configured Tx power, to remain within regulatory ERP limit for non-licensed operators.
This value should be set in variant.h and is PA gain + antenna gain (if system ships with an antenna).
This is similar to antenna_gain/NL80211_ATTR_WIPHY_ANTENNA_GAIN/NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN setting in Linux Regulatory/OpenWrt/mac80211/nl80211/iw.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Add REGULATORY_ prefix to LORA_REGIONCODE to prepare for more regulatory configuration options, and update comment block accordingly too.
Signed-off-by: Andrew Yong <me@ndoo.sg>
* AdminModule: Publish fixed position updates
Enabled GPS thread when fixed position is updated, to let the GPS thread run once and publish the new fixed position.
Signed-off-by: Andrew Yong <me@ndoo.sg>
* GPS: Consider changes in only altitude as an updated point
Signed-off-by: Andrew Yong <me@ndoo.sg>
---------
Signed-off-by: Andrew Yong <me@ndoo.sg>
* I2C is not defined in the reference schematic nor HT-DEV-ESP board, remove definition accordingly
* There is no screen in the the reference schematic nor HT-DEV-ESP board, change definition accordingly
* BUTTON_PIN has a 10 kOhm pullup resistor on HT-DEV-ESP, turning off redundant internal pullup should save some power
* LED is connected to GPIO2 on HT-DEV-ESP and is not inverted, update definition accordingly
* Remove redundant undef lines for LoRa pins
Above changes were built and flashed to my [HT-DEV-ESP_V2 board purchased from Heltec's Taobao store](https://item.taobao.com/item.htm?id=521590063077).
Signed-off-by: Andrew Yong <me@ndoo.sg>
Define BATTERY_SENSE_RESOLUTION_BITS based on [amoroz's snippet on the Meshtastic forum](https://meshtastic.discourse.group/t/new-1w-diy-variant-xiao-nrf52840-ebyte-e22-900m30s/7904/10).
Fixes following build error:
```
src/Power.cpp: In member function 'virtual uint16_t AnalogBatteryLevel::getBattVoltage()':
src/Power.cpp:224:79: error: 'BATTERY_SENSE_RESOLUTION_BITS' was not declared in this scope
scaled = operativeAdcMultiplier * ((1000 * AREF_VOLTAGE) / pow(2, BATTERY_SENSE_RESOLUTION_BITS)) * raw;
```
Signed-off-by: Andrew Yong <me@ndoo.sg>
The main use case for this will be to create a custom Heltec WiFi LoRa 32 V3 SG_923 variant, which will be pre-flashed and sent for regulatory approval for retail sale.
Signed-off-by: Andrew Yong <me@ndoo.sg>
Co-authored-by: Ben Meadors <benmmeadors@gmail.com>