Code Analysis — Xiao_Cloudtainer Repository
Code Analysis — Cloudtainer Firmware Codebase
Section titled “Code Analysis — Cloudtainer Firmware Codebase”Repository: https://github.com/Lucosiar/Xiao_Cloudtainer Target: Seeed XIAO nRF54L15 + Blues Notecard + Starnote (Skylo satellite) Language: C (Zephyr RTOS) + Python (Blues note-zephyr driver) Analysis Date: 2026-04-24
Codebase Structure
Section titled “Codebase Structure”Xiao_Cloudtainer/├── CMakeLists.txt — Zephyr build config├── README.md — west build instructions for XIAO nRF54L15├── prj.conf — Kconfig options (BLE, I2C, ADC, PWM, Notecard)├── note-zephyr — Blues Notecard driver submodule├── xiao_nrf54l15.overlay — hardware config for XIAO board├── build/ — build artifacts└── src/ ├── main.c — entry point, init sequence ├── thread.c — 4 threads + BLE peripheral ├── notecard.c — I2C driver ├── pwm_servo.c — servo PWM control ├── adc.c — battery voltage via SAADC ├── ds3231.c — RTC driver ├── led.c — GPIO LED control └── variables.h — event structures, interval configsHardware Configuration (from Overlay)
Section titled “Hardware Configuration (from Overlay)”Board: xiao_nrf54l15/nrf54l15/cpuapp
Key Pins:
| Function | Pin | Config |
|---|---|---|
| I2C SDA | P1.10 | i2c22, 400 kHz |
| I2C SCL | P1.11 | i2c22, 400 kHz |
| PWM servo | P1.06 | pwm20 channel 0 |
| Battery ADC | P1.05 | AIN1 (SAADC) |
I2C Devices:
- Notecard: addr 0x17
- DS3231 RTC: addr 0x68
Software Architecture
Section titled “Software Architecture”Initialization Sequence (main.c)
Section titled “Initialization Sequence (main.c)”1. k_sleep(10 SEC) — stabilize supply2. pwm_servo_init() — PWM, servo to closed3. ble_init() — start BLE advertising4. adc_init() — battery measurement setup5. notecard_configure() — send hub.set, card.voltage, note.template6. k_sleep(15 SEC) — wait for Notecard network attach7. ds3231_get_datetime() — check RTC (resync if year < 2020)8. threads_start() — launch 4 worker threads9. main loop: k_sleep(10 SEC) foreverFour Working Threads
Section titled “Four Working Threads”| Thread | Priority | Stack | Period | Function |
|---|---|---|---|---|
| sat_init | 3 | 2048 | 60s poll, 30min timeout | Detect Notecard satellite session |
| collector | 4 | 2048 | 30s | Gather voltage, GPS, timestamp |
| gsm | 5 | 2048 | 60s | Send event via cellular |
| sat | 5 | 2048 | 300s | Send event via satellite |
Critical Security Findings
Section titled “Critical Security Findings”🔴 1. Hardcoded Credentials in Public Repo
Section titled “🔴 1. Hardcoded Credentials in Public Repo”File: notecard_variables.h, Status: PUBLIC GitHub repo
#define WIFI_NETWORK_NAME "asimetrixIOA"#define WIFI_NETWORK_PASSWORD "4s1m4tr1x" // ⚠️ PLAINTEXT#define BLE_UNLOCK_TOKEN_1 "jsnf9233" // ⚠️ Anyone can unlock#define BLE_UNLOCK_TOKEN_2 "kqtm4816" // ⚠️#define NOTECARD_DEVICE_ID "351077454557425"Risk:
- WiFi network compromised
- BLE tokens public → anyone within Bluetooth range can unlock any Cloudlock in Argentina
- IMEI exposed → spoofing risk
Fix:
- Make repo Private immediately
- Rotate unlock tokens (per-device or OTP)
- Change WiFi password
- Use public-key crypto for BLE unlock (app signs challenge with private key, device verifies)
- Store tokens in environment variables / Kconfig secrets, not hardcoded
🔴 2. All Subsystems in Continuous Mode
Section titled “🔴 2. All Subsystems in Continuous Mode”File: notecard_variables.h
#define MODE_HUB_SET "continuous"#define MODE_GPS "continuous"#define MODE_GSM "continuous"#define MODE_SAT "continuous"Impact:
- GPS 24/7: 432 mAh/day wasted
- Cellular always attached: ~200 mAh/day
- Satellite always on: ~480 mAh/day
- Total: ~1100 mAh/day for connectivity alone
With 3000 mAh battery → ~2.7 days autonomy (customer wants 48–72 hours minimum)
Fix: Switch to periodic mode + voltage-variable sync intervals (see firmware/low-power guide).
🟡 3. Magic Default Values in Event Structure
Section titled “🟡 3. Magic Default Values in Event Structure”static cloudlock_event_body g_event = { .voltage = 14.1f, // invalid placeholder .battery_percentage = 14.1f, .uptime = 14.1f, .lat = 14.1f, // somewhere in Caribbean .lng = 14.1f, ...};Problem: These 14.1 values get sent to Notehub before collector thread fills them properly. Customer sees “strange coordinates” in dashboard.
Fix: Initialize to 0.0 or NaN, only send when collector has valid values.
🟡 4. APN Hardcoded for Argentina Only
Section titled “🟡 4. APN Hardcoded for Argentina Only”#define APN_NAME "igprs.claro.com.ar"#define EXTERNAL_APN_CLARO "igprs.claro.com.ar"#define EXTERNAL_APN_MOVISTAR "igprs.claro.com.ar" // BUG! Also ClaroProblem: Movistar APN copied from Claro (copy-paste error). If device deployed in Europe or uses Movistar SIM → wrong APN.
Fix: Add dynamic APN detection via Notecard card.wireless API.
🟡 5. Linear Battery Math (Noncompliant)
Section titled “🟡 5. Linear Battery Math (Noncompliant)”float pct = (voltage - LIPO_MIN_V) / (LIPO_MAX_V - LIPO_MIN_V) * 100.0f;Problem: LiPo voltage curve is nonlinear. At 3.7–3.9V → 20–80% SOC, voltage barely changes. Linear formula ±5% wrong.
Fix: Use lookup table from datasheet or add MAX17048 fuel gauge (±1% accurate).
🟡 6. No ADC Filtering
Section titled “🟡 6. No ADC Filtering”File: adc.c, collector thread
// Every 30 seconds, single sample, no averagingint raw = adc_read();voltage = raw * scale_factor;Problem: Servo activation causes voltage noise → bogus ADC value. No median filter or time-domain averaging.
Fix: Average 64–128 samples before reporting. Or use Notecard’s card.voltage as fallback (already filtered).
🟢 Good Practices
Section titled “🟢 Good Practices”✅ Mutex-protected I2C: All Notecard calls wrapped in NOTECARD_CALL() macro with mutex lock — correct thread-safety.
✅ Factory reset toggle: #define FACTORY_RESET_NOTECARD_ON_BOOT allows firmware to clear bad templates without reflashing.
✅ Memory allocation: CONFIG_HEAP_MEM_POOL_SIZE=32KB sufficient for nRF54L15’s 256KB RAM.
Missing Components (Not in Code)
Section titled “Missing Components (Not in Code)”| Component | Impact | Solution |
|---|---|---|
| Accelerometer | No wake-on-motion → GPS always on | Add LIS2DH12 driver |
| Fuel gauge | Battery ± 5% SOC only | Add MAX17048 I2C driver |
| MOSFET power gate for servo | Servo draws 40 mA idle | Add GPIO control + hardware MOSFET |
| Notch filter (Iridium) | If Iridium used, blocks GPS | Add SAW filter in hardware |
Recommended Action Items (Priority Order)
Section titled “Recommended Action Items (Priority Order)”Critical (Pre-Production)
Section titled “Critical (Pre-Production)”- Make repo Private + rotate BLE tokens + change WiFi password (15 min)
- Fix Movistar APN (1 line code)
- Fix default values 14.1 → 0.0 (5 min)
- Switch to periodic modes (2 hours firmware refactor)
- Implement firmware security for BLE unlock (1–2 days)
Important (Short-term)
Section titled “Important (Short-term)”- Battery SOC lookup table instead of linear formula
- ADC averaging (64 samples + median filter)
- Dynamic APN detection
- Proper error handling if Notecard fails at boot
Nice to Have (v2)
Section titled “Nice to Have (v2)”- OTA firmware update via Notecard
- Secure boot (nRF54L15 has TrustZone)
- Tamper detection (accelerometer + magnetic switch)
How This Changes Previous Docs
Section titled “How This Changes Previous Docs”| Document | Update Needed |
|---|---|
/how-it-works | Confirm nRF54L15 (not nRF9160), add BLE section |
/firmware/low-power | Confirm continuous → periodic modes switch plan |
/hardware/schematic-review | Confirm one servo (code proves it) |