Purpose and Memory Technology
Logs are critical for monitoring LoRaTube, confirming real-world performance over time, and, if necessary, identifying failures or design flaws.
This document records the reasoning and choices that led to the selected memory technology. The aim is to ensure reliable long-term logging with minimal power consumption and maximum resilience.
SD cards, though common in consumer projects, were ruled out. Their fragility is well known: rapid cell wear, poor reliability over long periods without activity, unpredictable wear-leveling, and a tendency to become corrupted or unwritable after power loss. Random corruption, freezes, access slowness, and inrush current are all deal-breakers for an ultra-low-power, resilient system.
Serial EEPROMs were also considered. They are inexpensive and widely available. However, their endurance is limited (typically one million write cycles per cell, much less in harsh temperature conditions), and write operations are page-based, which is not well-suited for the intended logging patterns. Write times can be significant, increasing the risk of data corruption during resets or brownouts. Data retention is often not guaranteed beyond a few years, especially with wide temperature variations.
SPI Flash offers large storage but shares many drawbacks: limited endurance, mandatory page erase before writing, complex software wear-leveling, and a real risk of “bricking” the chip if power is lost during write operations. Frequent updates to a few bytes always force a compromise between wear and software complexity. Additionally, available pin count on the C3 is limited, with no other active SPI devices in the design.
FRAM (Ferroelectric RAM) emerged as the solution. Key features:
- Very fast access (read/write in a few microseconds)
- No write latency
- Virtually unlimited endurance (ideal for frequently updated counters)
- Very low operating power
FRAM requires no pre-erasure or software wear-leveling, never blocks on sudden power loss, and provides full-speed access regardless of the frequency or distribution of writes. Data can be written or read byte by byte. It is also known for its resistance to EMI, and data retention exceeds ten years, making it well suited for embedded data loggers requiring high reliability.
There are two minor trade-offs: the chosen device (one of the few available on Aliexpress) draws about 9 µA in idle. While this is low, it is not entirely negligible over several years.
The FRAM is mounted on a large breakout board and is installed via female headers, allowing easy extraction for content analysis without soldering.
Daily Log Format (16 bytes, little-endian)
Available storage is 32 kB, so a daily log format with counters was selected. Counters are stored directly in FRAM to take advantage of its resilience to repeated writes and absence of paging.
Assuming a five-year lifespan:
32 kB / (5 years × 365 days/year) ≈ 17 bytes per day.
The chosen log format is 16 bytes per day:
| Field | Bytes | Description |
|---|---|---|
| timestamp | 4 | Unix UTC (seconds) |
| midnight_temp | 1 | Temperature at midnight (−15…+35 °C normal, −50…+70 °C if TEMP_EXT=1) |
| midnight_voltage | 1 | Main voltage (Vbat/Text, 10–32 V, LSB ≈ 86 mV) |
| noon_temp | 1 | Temperature at noon (RTC or nearest sample) |
| noon_voltage | 1 | Voltage at noon (same rule) |
| wake_rx | 2 | Number of radio wake-ups, saturates at 0xFFFF |
| ratio_rxtrue_per_wake_q8 | 1 | Q8 ratio: (REVEIL_RADIO == 0) ? 0 : clamp(round(255 × TRUE_RX / REVEIL_RADIO)) |
| ratio_tx_per_rxtrue_q8 | 1 | Q8 ratio: (TRUE_RX == 0) ? 0 : clamp(round(255 × TX / TRUE_RX)) |
| flags | 1 | See below |
| noise_min_dbm | 1 | Min radio noise, −130…0 dBm (LSB ≈ 0.51 dB) |
| noise_max_dbm | 1 | Max radio noise |
| crc8 | 1 | CRC-8 with first 15 bytes |
Ratio Calculation (bytes 7 & 8)
- Byte 7: (REVEIL_RADIO == 0) ? 0 : clamp(round(255 × TRUE_RX / REVEIL_RADIO))
- Byte 8: (TRUE_RX == 0) ? 0 : clamp(round(255 × TX / TRUE_RX))
Calculation is performed at log write time (integer division, clamped to 255).
This format is compact but contains all relevant information: battery voltage trends, effect of temperature, presence of electromagnetic noise, and radio efficiency (repeater performance). A CRC8 allows verification of log integrity (1/256 chance for a single undetected error per 16-byte log).
Flags (1 byte)
Flags in byte 13 provide information about the system status and faults:
| Bit | Name | Function |
|---|---|---|
| 0 | TEMP_EXT | Extended temperature range active (modifies min/noon temp range) |
| 1 | E22_RESET | Radio module reset requested |
| 2 | C3_MISSED_TIME | MCU reset detected (time/logs mismatch) |
| 3 | LOW_VOLT | Low voltage detected |
| 4 | WAKE_UART | Wake by UART (should stay zero in normal use; monitors AUX and IRQ) |
| 5 | CORRUPTED_FRAM | Invalid CRC on index or counter slot |
| 6 | C3_RESET | 3.3 V rail reset requested by firmware |
| 7 | C3_BROWNOUT | Brownout detected on C3 |
Circular Buffer Logic
FRAM is used as a circular buffer:
- Each write increments the index.
- When index reaches maximum, it wraps to zero.
- Each log has its own timestamp for strict chronological ordering.
- No magic pointers or implicit dependencies—structure is fully autonomous.
Index and Counter Storage
Log index and daily counters (for RX, system status, etc.) are also stored in FRAM. RTC RAM of the C3 is not used for resilience reasons.
Data is duplicated in two slots (A and B). Slot A is at the start of FRAM, slot B at the end. Both hold identical data (except during update). Each slot includes a magic word, payload, and CRC8 (poly 0x31, reflect in/out; same as for the log).
Purpose of two distant slots:
- Protection against localized corruption: single-bit errors or interrupted writes may damage one slot, but simultaneous corruption is extremely unlikely.
- Protection against external factors: EMI, ionizing radiation, and cosmic rays can cause isolated bit flips; distant slots are less likely to be affected simultaneously.
This structure enables error detection and correction at startup, with no need for an internal journal or sequencer—CRC and magic word are sufficient.
Write Policy
- Always write A, then B, with identical values.
- Write A.
- Verify CRC and readback.
- Retry up to n times if failed.
- Write B.
- Verify CRC and readback.
- Retry up to n times if failed.
- At boot:
- If both A and B are valid: use A, do not set CORRUPTED_FRAM.
- If only one is valid: use it and set CORRUPTED_FRAM.
- If neither is valid: reset all to zero and set CORRUPTED_FRAM.
FRAM is extremely robust; irreversible hardware failure rate is about 10 FIT (failures per billion hours), corresponding to one failure every 11,000 years per device.
Bertrand Selva
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.