Skip to content

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


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 configs

Board: xiao_nrf54l15/nrf54l15/cpuapp

Key Pins:

FunctionPinConfig
I2C SDAP1.10i2c22, 400 kHz
I2C SCLP1.11i2c22, 400 kHz
PWM servoP1.06pwm20 channel 0
Battery ADCP1.05AIN1 (SAADC)

I2C Devices:

  • Notecard: addr 0x17
  • DS3231 RTC: addr 0x68

1. k_sleep(10 SEC) — stabilize supply
2. pwm_servo_init() — PWM, servo to closed
3. ble_init() — start BLE advertising
4. adc_init() — battery measurement setup
5. notecard_configure() — send hub.set, card.voltage, note.template
6. k_sleep(15 SEC) — wait for Notecard network attach
7. ds3231_get_datetime() — check RTC (resync if year < 2020)
8. threads_start() — launch 4 worker threads
9. main loop: k_sleep(10 SEC) forever
ThreadPriorityStackPeriodFunction
sat_init3204860s poll, 30min timeoutDetect Notecard satellite session
collector4204830sGather voltage, GPS, timestamp
gsm5204860sSend event via cellular
sat52048300sSend event via satellite

🔴 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:

  1. Make repo Private immediately
  2. Rotate unlock tokens (per-device or OTP)
  3. Change WiFi password
  4. Use public-key crypto for BLE unlock (app signs challenge with private key, device verifies)
  5. Store tokens in environment variables / Kconfig secrets, not hardcoded

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.


#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 Claro

Problem: 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).


File: adc.c, collector thread

// Every 30 seconds, single sample, no averaging
int 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).


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.


ComponentImpactSolution
AccelerometerNo wake-on-motion → GPS always onAdd LIS2DH12 driver
Fuel gaugeBattery ± 5% SOC onlyAdd MAX17048 I2C driver
MOSFET power gate for servoServo draws 40 mA idleAdd GPIO control + hardware MOSFET
Notch filter (Iridium)If Iridium used, blocks GPSAdd SAW filter in hardware

  1. Make repo Private + rotate BLE tokens + change WiFi password (15 min)
  2. Fix Movistar APN (1 line code)
  3. Fix default values 14.1 → 0.0 (5 min)
  4. Switch to periodic modes (2 hours firmware refactor)
  5. Implement firmware security for BLE unlock (1–2 days)
  1. Battery SOC lookup table instead of linear formula
  2. ADC averaging (64 samples + median filter)
  3. Dynamic APN detection
  4. Proper error handling if Notecard fails at boot
  1. OTA firmware update via Notecard
  2. Secure boot (nRF54L15 has TrustZone)
  3. Tamper detection (accelerometer + magnetic switch)

DocumentUpdate Needed
/how-it-worksConfirm nRF54L15 (not nRF9160), add BLE section
/firmware/low-powerConfirm continuous → periodic modes switch plan
/hardware/schematic-reviewConfirm one servo (code proves it)