refactor: trim CLAUDE.md per best practices
Following Anthropic's Claude Code best practices: - CLAUDE.md: 504 → 121 lines Keep concise: commands, code style, critical rules. Move detailed specs to /opt/repos/docs/reference/ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
536
CLAUDE.md
536
CLAUDE.md
@@ -1,504 +1,120 @@
|
|||||||
# ClearGrow Probe Firmware
|
# ClearGrow Probe Firmware
|
||||||
|
|
||||||
nRF52840-based wireless sensor node with Thread networking, code-based auto-pairing, and comprehensive environmental monitoring.
|
nRF52840 wireless sensor node with Thread networking and code-based pairing.
|
||||||
|
|
||||||
## Quick Reference
|
## Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Build environment setup
|
# Setup
|
||||||
source ~/ncs/.venv/bin/activate
|
source ~/ncs/.venv/bin/activate
|
||||||
source ~/ncs/zephyr/zephyr-env.sh
|
source ~/ncs/zephyr/zephyr-env.sh
|
||||||
cd /root/cleargrow/probe
|
cd /opt/repos/probe
|
||||||
|
|
||||||
# Build
|
# Build and flash
|
||||||
west build -b nrf52840dk_nrf52840
|
west build -b nrf52840dk_nrf52840
|
||||||
|
|
||||||
# Flash
|
|
||||||
west flash
|
west flash
|
||||||
|
|
||||||
# Clean rebuild
|
# Clean rebuild
|
||||||
rm -rf build && west build -b nrf52840dk_nrf52840
|
rm -rf build && west build -b nrf52840dk_nrf52840
|
||||||
|
|
||||||
# Monitor logs (RTT or serial)
|
# Memory reports
|
||||||
minicom -D /dev/ttyACM0 -b 115200
|
|
||||||
|
|
||||||
# Check memory usage
|
|
||||||
west build -t rom_report
|
west build -t rom_report
|
||||||
west build -t ram_report
|
west build -t ram_report
|
||||||
|
|
||||||
|
# Monitor (RTT recommended)
|
||||||
|
JLinkRTTClient
|
||||||
|
# Or serial
|
||||||
|
minicom -D /dev/ttyACM0 -b 115200
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architecture Overview
|
## Code Style
|
||||||
|
|
||||||
### Source Structure
|
- **Zephyr style**: 4-space indent, `snake_case`
|
||||||
|
- **Logging**: `LOG_MODULE_REGISTER(name, LOG_LEVEL_INF)`, then `LOG_INF()`, `LOG_WRN()`, `LOG_ERR()`
|
||||||
|
- **Timing**: `k_sleep(K_MSEC(100))`, never raw ticks
|
||||||
|
|
||||||
|
## Critical Rules
|
||||||
|
|
||||||
|
**DO NOT use Zephyr PM framework** - nRF52840 lacks `HAS_PM` support. Use Nordic HAL directly:
|
||||||
|
```c
|
||||||
|
#include <hal/nrf_power.h>
|
||||||
|
nrf_power_system_off(NRF_POWER); // <1µA, does NOT return
|
||||||
|
```
|
||||||
|
|
||||||
|
**Thread role is MTD-SED** (Sleepy End Device) - poll period 1 second
|
||||||
|
|
||||||
|
**CoAP operations are blocking** - never call from time-critical contexts
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
```
|
```
|
||||||
src/
|
src/
|
||||||
├── main.c # Entry point, threads, state machine
|
├── main.c # Entry point, state machine
|
||||||
├── sensor_manager.c # I2C sensor drivers, auto-detection
|
├── sensor_manager.c # I2C sensor drivers
|
||||||
├── thread_node.c # OpenThread MTD-SED, CoAP client
|
├── thread_node.c # Thread + CoAP client
|
||||||
├── power_manager.c # Battery monitoring, sleep modes
|
├── power_manager.c # Battery, sleep modes
|
||||||
└── pairing_code.c # PSKd generation and storage
|
└── pairing_code.c # PSKd generation
|
||||||
|
|
||||||
include/
|
|
||||||
├── probe_config.h # Hardware config, data structures
|
|
||||||
└── pairing_code.h # Pairing API declarations
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Task Architecture
|
|
||||||
|
|
||||||
| Thread | Stack | Priority | Purpose |
|
|
||||||
|--------|-------|----------|---------|
|
|
||||||
| Main | 2048 | Default | Watchdog, power state management |
|
|
||||||
| Sensor | 2048 | 7 | Periodic sensor reading |
|
|
||||||
| Transmit | 3072 | 8 | CoAP data transmission |
|
|
||||||
|
|
||||||
**Data Flow**:
|
|
||||||
1. Sensor thread reads I2C sensors every 5s (configurable)
|
|
||||||
2. Signals transmit thread via semaphore
|
|
||||||
3. Transmit thread encodes TLV and sends CoAP POST
|
|
||||||
4. Main thread feeds watchdog, manages power states
|
|
||||||
|
|
||||||
### Power Management
|
|
||||||
|
|
||||||
Target: 1+ year on 2x AA batteries
|
|
||||||
|
|
||||||
| State | Current | When Used | Implementation |
|
|
||||||
|-------|---------|-----------|----------------|
|
|
||||||
| Active | 10-15mA | Not commissioned | Normal operation |
|
|
||||||
| Idle | 6-8mA | Normal operation, Thread child | Thread SED mode |
|
|
||||||
| Sleep | 3-5mA | Low battery | Adaptive polling |
|
|
||||||
| Deep Sleep | <3µA | Critical battery | Nordic `nrf_power_system_off()` |
|
|
||||||
| SHIPPING | <1µA | Storage/transit | System OFF, all peripherals disabled |
|
|
||||||
|
|
||||||
**Strategy**:
|
|
||||||
- Thread SED mode with 1s poll period
|
|
||||||
- Adaptive sensor polling based on battery level
|
|
||||||
- Soil sensor power controlled via GPIO
|
|
||||||
- **System OFF via Nordic HAL** (not Zephyr PM - see note below)
|
|
||||||
|
|
||||||
**Important**: nRF52840 does NOT support Zephyr's generic `CONFIG_PM` framework (requires `HAS_PM` which is only available on newer chips like nRF54). Deep sleep and SHIPPING mode use Nordic HAL `nrf_power_system_off(NRF_POWER)` directly.
|
|
||||||
|
|
||||||
## Sensor Configuration
|
|
||||||
|
|
||||||
### Supported Sensors (I2C)
|
|
||||||
|
|
||||||
| Sensor | Address | Measurement | Driver Status |
|
|
||||||
|--------|---------|-------------|---------------|
|
|
||||||
| SHT4x/SHTC3 | 0x44/0x45 | Temp, humidity, VPD, dew point | Production |
|
|
||||||
| MLX90614 | 0x5A | Leaf temp (IR), leaf VPD | Production |
|
|
||||||
| ADS1115 | 0x48 | Soil moisture, EC, pH, temp | Production |
|
|
||||||
| SCD4x | 0x62 | CO2, temp, humidity | Production |
|
|
||||||
| VEML7700 | 0x10 | PAR, lux, DLI | Production |
|
|
||||||
| Module EEPROM | 0x50 | Module identification | Optional |
|
|
||||||
|
|
||||||
### Auto-Detection
|
|
||||||
|
|
||||||
On startup, `sensor_manager` scans I2C bus for:
|
|
||||||
1. Module EEPROM (0x50) - reads type, serial, calibration
|
|
||||||
2. Direct sensor detection - checks if device responds
|
|
||||||
3. Builds `modules_present` bitmap
|
|
||||||
|
|
||||||
**Retry Logic**: 3 attempts with exponential backoff (10ms base)
|
|
||||||
|
|
||||||
## Thread Network
|
|
||||||
|
|
||||||
### Role: MTD-SED (Minimal Thread Device - Sleepy End Device)
|
|
||||||
|
|
||||||
**Configuration** (prj.conf):
|
|
||||||
```ini
|
|
||||||
CONFIG_OPENTHREAD_MTD=y
|
|
||||||
CONFIG_OPENTHREAD_MTD_SED=y
|
|
||||||
CONFIG_OPENTHREAD_POLL_PERIOD=1000 # Poll parent every 1 second
|
|
||||||
CONFIG_OPENTHREAD_JOINER=y
|
|
||||||
CONFIG_OPENTHREAD_SRP_CLIENT=y
|
|
||||||
```
|
|
||||||
|
|
||||||
**Network Behavior**:
|
|
||||||
1. On boot, checks if commissioned (`otDatasetIsCommissioned`)
|
|
||||||
2. If not commissioned, auto-starts joiner with PSKd
|
|
||||||
3. Joiner retry: 5s initial, exponential backoff to 5min max
|
|
||||||
4. **Battery protection timeout**: 10 minutes total, then enters low-power wait
|
|
||||||
5. When joined, registers SRP service: `_cleargrow._udp`
|
|
||||||
|
|
||||||
### CoAP Message Format
|
|
||||||
|
|
||||||
**POST /sensors** (TLV encoding):
|
|
||||||
|
|
||||||
```
|
|
||||||
Header (12 bytes):
|
|
||||||
[0-7] EUI-64 (probe ID)
|
|
||||||
[8] Protocol version (1)
|
|
||||||
[9] Battery percent
|
|
||||||
[10-11] Sequence number (uint16, little-endian)
|
|
||||||
|
|
||||||
Measurements (TLV):
|
|
||||||
[Type][ValueInfo][Value...]
|
|
||||||
|
|
||||||
Types:
|
|
||||||
0x01 = Temperature (float32)
|
|
||||||
0x02 = Humidity (float32)
|
|
||||||
0x03 = CO2 (uint16)
|
|
||||||
0x04 = PPFD/PAR (float32)
|
|
||||||
0x05 = VPD (float32)
|
|
||||||
0x06 = Leaf temp (float32)
|
|
||||||
0x07 = Soil moisture (float32)
|
|
||||||
0x08 = Soil temp (float32)
|
|
||||||
0x09 = EC (float32)
|
|
||||||
0x0A = pH (float32)
|
|
||||||
0x0B = DLI (float32)
|
|
||||||
0x0C = Dew point (float32)
|
|
||||||
```
|
|
||||||
|
|
||||||
**ValueInfo nibbles**:
|
|
||||||
- Upper nibble: type (0=float32, 3=int16, 4=uint16)
|
|
||||||
- Lower nibble: length in bytes
|
|
||||||
|
|
||||||
**Controller Address**: Set via `thread_node_set_controller_addr()` with IPv6 address. DNS-SD resolution not yet implemented.
|
|
||||||
|
|
||||||
## Code-Based Pairing
|
|
||||||
|
|
||||||
Uses a printed code on device label for pairing.
|
|
||||||
|
|
||||||
### PSKd (Pre-Shared Key for Device)
|
|
||||||
|
|
||||||
**Format**:
|
|
||||||
- 6 characters (configurable via `CONFIG_PSKD_LENGTH`)
|
|
||||||
- Character set: 0-9, A-Z excluding I, O, Q, Z (Thread spec compliant)
|
|
||||||
- Generated using cryptographic RNG (`sys_csrand_get`)
|
|
||||||
- Stored in NVS flash (`settings` subsystem)
|
|
||||||
|
|
||||||
**Example**: `A3F2K7`
|
|
||||||
|
|
||||||
**Generation** (`pairing_code.c`):
|
|
||||||
1. On first boot, generates random PSKd
|
|
||||||
2. Saves to flash: `pairing/pskd`
|
|
||||||
3. Persists across reboots
|
|
||||||
4. Can regenerate via `pairing_code_regenerate()`
|
|
||||||
|
|
||||||
### Auto-Joiner Behavior
|
|
||||||
|
|
||||||
**State Machine** (`thread_node.c:auto_start_joiner()`):
|
|
||||||
|
|
||||||
```c
|
|
||||||
1. Check if commissioned → YES: enable Thread, done
|
|
||||||
2. Check battery timeout (10min) → YES: stop attempts, sleep
|
|
||||||
3. Get PSKd from pairing_code module
|
|
||||||
4. Start joiner: otJoinerStart(pskd, "ClearGrow", "Probe", "1.0")
|
|
||||||
5. On failure: schedule retry with exponential backoff
|
|
||||||
```
|
|
||||||
|
|
||||||
**Retry Parameters**:
|
|
||||||
- Initial delay: 5 seconds
|
|
||||||
- Max delay: 5 minutes
|
|
||||||
- Total timeout: 10 minutes (battery protection)
|
|
||||||
|
|
||||||
**User Flow**:
|
|
||||||
1. User powers on probe
|
|
||||||
2. Probe auto-starts joiner (LED blinks pattern)
|
|
||||||
3. User enters PSKd on controller UI
|
|
||||||
4. Controller enables commissioner with wildcard EUI-64
|
|
||||||
5. Probe completes DTLS handshake, joins network
|
|
||||||
6. First CoAP message confirms pairing (3 quick blinks)
|
|
||||||
|
|
||||||
## Key APIs
|
## Key APIs
|
||||||
|
|
||||||
### Sensor Manager (`sensor_manager.c`)
|
|
||||||
|
|
||||||
```c
|
```c
|
||||||
// Initialize (detects modules, starts SCD4x periodic mode)
|
// Sensors
|
||||||
int sensor_manager_init(void);
|
sensor_manager_init();
|
||||||
|
sensor_manager_get_data(&data);
|
||||||
|
|
||||||
// Main loop - call periodically
|
// Thread
|
||||||
void sensor_manager_loop(void);
|
thread_node_init();
|
||||||
|
thread_node_send_sensors(&data);
|
||||||
|
|
||||||
// Thread-safe data access
|
// Power
|
||||||
int sensor_manager_get_data(probe_sensor_data_t *data);
|
power_manager_get_battery_percent();
|
||||||
|
power_manager_set_state(POWER_STATE_SLEEP);
|
||||||
|
|
||||||
// Check if specific module present
|
// Pairing
|
||||||
bool sensor_manager_has_module(probe_module_type_t type);
|
pairing_code_get_pskd(); // Returns 6-char code
|
||||||
|
|
||||||
// Reset DLI accumulator (daily)
|
|
||||||
void sensor_manager_reset_dli(void);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**VPD Calculation** (Magnus formula):
|
## Sensors (I2C)
|
||||||
```c
|
|
||||||
float svp = 0.6108 * exp(17.27 * temp_c / (temp_c + 237.3));
|
|
||||||
float vpd = svp * (1.0 - (rh / 100.0));
|
|
||||||
```
|
|
||||||
|
|
||||||
**DLI Accumulation** (umol/m2/s → mol/m2/day):
|
| Sensor | Addr | Measures |
|
||||||
```c
|
|--------|------|----------|
|
||||||
dli += (par_umol * dt_sec) / 1000000.0; // Resets at 24h
|
| SHT4x | 0x44 | Temp, humidity |
|
||||||
```
|
| MLX90614 | 0x5A | Leaf temp (IR) |
|
||||||
|
| ADS1115 | 0x48 | Soil moisture, EC, pH |
|
||||||
|
| SCD4x | 0x62 | CO2 |
|
||||||
|
| VEML7700 | 0x10 | PAR/PPFD, DLI |
|
||||||
|
|
||||||
### Thread Node (`thread_node.c`)
|
## Power States
|
||||||
|
|
||||||
```c
|
| State | Current | Use |
|
||||||
// Initialize Thread stack
|
|-------|---------|-----|
|
||||||
int thread_node_init(void);
|
| Active | 10-15mA | Not commissioned |
|
||||||
|
| Idle | 6-8mA | Normal operation (SED) |
|
||||||
|
| Sleep | 3-5mA | Low battery |
|
||||||
|
| System OFF | <1µA | Critical/shipping |
|
||||||
|
|
||||||
// Send sensor data (blocking CoAP POST)
|
## Pairing
|
||||||
int thread_node_send_sensors(const probe_sensor_data_t *data);
|
|
||||||
|
|
||||||
// Set controller IPv6 address
|
- **PSKd**: 6-character code printed on device label
|
||||||
int thread_node_set_controller_addr(const struct in6_addr *addr, uint16_t port);
|
- **Format**: `A3F2K7` (excludes I, O, Q, Z)
|
||||||
|
- **Auto-joiner**: Retries with backoff, 10-min timeout for battery protection
|
||||||
|
|
||||||
// Get EUI-64 identifier
|
## Debugging
|
||||||
int thread_node_get_eui64(uint8_t eui64[8]);
|
|
||||||
|
|
||||||
// Factory reset Thread credentials
|
|
||||||
int thread_node_factory_reset(void);
|
|
||||||
|
|
||||||
// State callback
|
|
||||||
void thread_node_set_state_callback(void (*cb)(thread_state_t, void *), void *ctx);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Important**: CoAP operations are blocking. Do NOT call from UI contexts.
|
|
||||||
|
|
||||||
### Power Manager (`power_manager.c`)
|
|
||||||
|
|
||||||
```c
|
|
||||||
// Initialize (configures ADC, starts battery sampling)
|
|
||||||
int power_manager_init(void);
|
|
||||||
|
|
||||||
// Set power state
|
|
||||||
int power_manager_set_state(power_state_t state);
|
|
||||||
|
|
||||||
// Get battery info
|
|
||||||
uint16_t power_manager_get_battery_mv(void);
|
|
||||||
uint8_t power_manager_get_battery_percent(void);
|
|
||||||
bool power_manager_is_battery_low(void);
|
|
||||||
|
|
||||||
// Adaptive polling interval
|
|
||||||
uint32_t power_manager_get_recommended_poll_interval(void);
|
|
||||||
|
|
||||||
// Soil sensor power control
|
|
||||||
int power_manager_set_soil_power(bool enabled);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Battery Sampling**:
|
|
||||||
- ADC channel 0 (AIN0) with voltage divider
|
|
||||||
- Sampled every 5 minutes (configurable)
|
|
||||||
- Low battery callback available
|
|
||||||
|
|
||||||
### Pairing Code (`pairing_code.c`)
|
|
||||||
|
|
||||||
```c
|
|
||||||
// Initialize (loads or generates PSKd)
|
|
||||||
int pairing_code_init(void);
|
|
||||||
|
|
||||||
// Get current PSKd
|
|
||||||
const char *pairing_code_get_pskd(void);
|
|
||||||
|
|
||||||
// Regenerate PSKd (security)
|
|
||||||
int pairing_code_regenerate(void);
|
|
||||||
|
|
||||||
// Validate PSKd format
|
|
||||||
bool pairing_code_validate(const char *pskd);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Key Build Options (prj.conf)
|
|
||||||
|
|
||||||
```ini
|
|
||||||
# Thread networking
|
|
||||||
CONFIG_OPENTHREAD_MTD_SED=y
|
|
||||||
CONFIG_OPENTHREAD_POLL_PERIOD=1000
|
|
||||||
CONFIG_OPENTHREAD_JOINER=y
|
|
||||||
CONFIG_OPENTHREAD_SRP_CLIENT=y
|
|
||||||
|
|
||||||
# Code-based pairing
|
|
||||||
CONFIG_CODE_PAIRING=y
|
|
||||||
CONFIG_PSKD_LENGTH=6
|
|
||||||
|
|
||||||
# Power management (nRF52840-specific)
|
|
||||||
# NOTE: CONFIG_PM/CONFIG_PM_DEVICE are NOT supported on nRF52840
|
|
||||||
# (Zephyr PM framework requires HAS_PM, which nRF52840 lacks)
|
|
||||||
# Instead, use Nordic HAL: nrf_power_system_off() for System OFF state
|
|
||||||
# See PROBE-SL-002 remediation (2025-12-09) for details
|
|
||||||
|
|
||||||
# Persistent storage
|
|
||||||
CONFIG_NVS=y
|
|
||||||
CONFIG_SETTINGS=y
|
|
||||||
CONFIG_SETTINGS_NVS=y
|
|
||||||
|
|
||||||
# Watchdog
|
|
||||||
CONFIG_WATCHDOG=y
|
|
||||||
CONFIG_WDT_DISABLE_AT_BOOT=n
|
|
||||||
|
|
||||||
# MCUboot for OTA
|
|
||||||
CONFIG_BOOTLOADER_MCUBOOT=y
|
|
||||||
CONFIG_STREAM_FLASH=y
|
|
||||||
CONFIG_IMG_MANAGER=y
|
|
||||||
|
|
||||||
# Memory
|
|
||||||
CONFIG_MAIN_STACK_SIZE=2048
|
|
||||||
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
|
|
||||||
CONFIG_HEAP_MEM_POOL_SIZE=16384
|
|
||||||
```
|
|
||||||
|
|
||||||
### Runtime Configuration
|
|
||||||
|
|
||||||
**Adaptive Polling** (battery-based):
|
|
||||||
- Battery >50%: 5 seconds (default)
|
|
||||||
- Battery 20-50%: 10 seconds
|
|
||||||
- Battery low: 30 seconds
|
|
||||||
- Battery critical: 60 seconds
|
|
||||||
|
|
||||||
**Thread Poll Period**: 1000ms (1 second)
|
|
||||||
- Probe wakes to poll parent, check for downlink data
|
|
||||||
- CPU sleeps between polls
|
|
||||||
|
|
||||||
## Debugging Tips
|
|
||||||
|
|
||||||
### Common Issues
|
|
||||||
|
|
||||||
1. **Probe won't join network**
|
|
||||||
- Check PSKd matches device label
|
|
||||||
- Verify controller is in pairing mode (commissioner active)
|
|
||||||
- Check Thread network is active (border router running)
|
|
||||||
- Battery level >30% (joiner requires radio power)
|
|
||||||
- Look for `Joiner failed` logs with error code
|
|
||||||
|
|
||||||
2. **High power consumption**
|
|
||||||
- Verify SED mode: `grep "MTD-SED" build/zephyr/zephyr.dts`
|
|
||||||
- Check poll period: should be 1000ms
|
|
||||||
- Ensure soil sensor power disabled between reads
|
|
||||||
- Look for I2C bus lockups (retry loops)
|
|
||||||
|
|
||||||
3. **Sensor read failures**
|
|
||||||
- Check I2C pull-ups (4.7k to 3.3V)
|
|
||||||
- Verify I2C address in `probe_config.h`
|
|
||||||
- Try `sensor_manager_rescan()` to re-detect
|
|
||||||
- Check module EEPROM if present
|
|
||||||
|
|
||||||
4. **Auto-joiner timeout**
|
|
||||||
- 10-minute timeout is safety feature
|
|
||||||
- Power cycle to retry
|
|
||||||
- Check battery level (critical = no join attempts)
|
|
||||||
- Verify Thread network credentials correct
|
|
||||||
|
|
||||||
5. **CoAP transmission failures**
|
|
||||||
- Controller address must be set (IPv6)
|
|
||||||
- Check `s_controller_resolved` flag
|
|
||||||
- Verify controller CoAP server listening on port 5683
|
|
||||||
- Thread parent must be reachable (RSSI check)
|
|
||||||
|
|
||||||
### Log Analysis
|
|
||||||
|
|
||||||
**Look for these messages**:
|
|
||||||
```
|
|
||||||
✓ "Pairing code initialized (PSKd: XXXXXX)"
|
|
||||||
✓ "Thread state: Child"
|
|
||||||
✓ "Sent TLV sensor data (seq X, Y bytes)"
|
|
||||||
✗ "Joiner failed: X"
|
|
||||||
✗ "Failed to transmit sensor data: X"
|
|
||||||
✗ "Battery: XmV (critical)"
|
|
||||||
```
|
|
||||||
|
|
||||||
**Memory usage**:
|
|
||||||
```bash
|
```bash
|
||||||
west build -t rom_report # Flash usage by section
|
# Check Thread SED mode
|
||||||
west build -t ram_report # RAM usage by symbol
|
grep "MTD-SED" build/zephyr/zephyr.dts
|
||||||
|
|
||||||
|
# Log levels
|
||||||
|
LOG_MODULE_REGISTER(thread_node, LOG_LEVEL_DBG);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Expected**:
|
**Common issues**:
|
||||||
- Flash: 400-600KB (< 70% of 1MB)
|
- Won't join: Check PSKd, verify controller in pairing mode
|
||||||
- RAM: 64-100KB (< 50% of 256KB)
|
- High power: Verify SED mode, check poll period
|
||||||
|
- Sensor fails: Check I2C pull-ups (4.7k), verify address
|
||||||
|
|
||||||
### Serial Logging
|
## Documentation
|
||||||
|
|
||||||
**RTT (recommended)**:
|
Full docs: `/opt/repos/docs/reference/firmware/probe/`
|
||||||
```bash
|
|
||||||
JLinkRTTClient
|
|
||||||
# Fast, no UART overhead
|
|
||||||
```
|
|
||||||
|
|
||||||
**UART**:
|
|
||||||
```bash
|
|
||||||
minicom -D /dev/ttyACM0 -b 115200
|
|
||||||
# Standard serial console
|
|
||||||
```
|
|
||||||
|
|
||||||
**Log Levels** (per module):
|
|
||||||
```c
|
|
||||||
LOG_MODULE_REGISTER(module_name, LOG_LEVEL_INF);
|
|
||||||
// Change to LOG_LEVEL_DBG for verbose output
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## OTA Updates
|
|
||||||
|
|
||||||
**MCUboot dual-bank scheme**:
|
|
||||||
- Slot 0: Active firmware
|
|
||||||
- Slot 1: Download area
|
|
||||||
- On reboot, MCUboot validates and swaps if needed
|
|
||||||
|
|
||||||
**Build artifacts**:
|
|
||||||
```
|
|
||||||
build/zephyr/zephyr.hex # Main firmware (for direct flash)
|
|
||||||
build/zephyr/merged.hex # Firmware + MCUboot bootloader
|
|
||||||
build/zephyr/zephyr.signed.bin # Signed image for OTA
|
|
||||||
```
|
|
||||||
|
|
||||||
**OTA not yet implemented** - placeholder for future work via CoAP block transfer.
|
|
||||||
|
|
||||||
## Memory Map
|
|
||||||
|
|
||||||
| Region | Address | Size | Usage |
|
|
||||||
|--------|---------|------|-------|
|
|
||||||
| Flash | 0x00000000 | 48KB | MCUboot bootloader |
|
|
||||||
| Flash | 0x0000C000 | 476KB | Slot 0 (active firmware) |
|
|
||||||
| Flash | 0x00083000 | 476KB | Slot 1 (OTA download) |
|
|
||||||
| Flash | 0x000FA000 | 24KB | NVS settings storage |
|
|
||||||
| RAM | 0x20000000 | 256KB | Application + stack |
|
|
||||||
|
|
||||||
## Hardware Variants
|
|
||||||
|
|
||||||
**Current target**: nRF52840 DK (`nrf52840dk_nrf52840`)
|
|
||||||
|
|
||||||
**Custom board**: Create devicetree overlay in board-specific directory.
|
|
||||||
|
|
||||||
**Example** (`boards/nrf52840_probe.overlay`):
|
|
||||||
```dts
|
|
||||||
&i2c0 {
|
|
||||||
status = "okay";
|
|
||||||
sda-pin = <26>;
|
|
||||||
scl-pin = <27>;
|
|
||||||
};
|
|
||||||
|
|
||||||
&adc {
|
|
||||||
status = "okay";
|
|
||||||
#address-cells = <1>;
|
|
||||||
#size-cells = <0>;
|
|
||||||
|
|
||||||
channel@0 {
|
|
||||||
reg = <0>;
|
|
||||||
zephyr,gain = "ADC_GAIN_1_6";
|
|
||||||
zephyr,reference = "ADC_REF_INTERNAL";
|
|
||||||
zephyr,acquisition-time = <10>;
|
|
||||||
zephyr,input-positive = <NRF_SAADC_AIN0>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Appendix: Constants
|
|
||||||
|
|
||||||
**Battery thresholds** (3.0V LiPo):
|
|
||||||
```c
|
|
||||||
BATTERY_FULL_MV 4200
|
|
||||||
BATTERY_NOMINAL_MV 3700
|
|
||||||
BATTERY_LOW_MV 3400
|
|
||||||
BATTERY_CRITICAL_MV 3200
|
|
||||||
BATTERY_CUTOFF_MV 3000
|
|
||||||
```
|
|
||||||
|
|
||||||
**PSKd character set**:
|
|
||||||
```
|
|
||||||
0123456789ABCDEFGHJKLMNPRSTUVWXY
|
|
||||||
(32 chars: excludes I, O, Q, Z)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Module EEPROM magic**: `0x434C4752` ("CLGR")
|
|
||||||
|
|||||||
Reference in New Issue
Block a user