Development skill for building a Raspberry Pi Pico-based RC submarine controller with ballast control, depth hold, pitch stabilization, and failsafe protection. Use when working on embedded C firmware for RP2040, implementing PID controllers, writing sensor drivers (pressure, IMU), PWM capture/output, dual-core safety-critical systems, or any task related to the RC submarine project. Also triggers for Power of 10 coding standards, Pico SDK usage, and underwater vehicle control systems.
Firmware development for a Raspberry Pi Pico-based RC submarine control system with active ballast, depth hold, pitch stabilization, and hardware failsafe protection.
See references/pinout.md for complete GPIO mapping.
# Configure
mkdir build && cd build
cmake -DPICO_SDK_PATH=~/pico-sdk ..
# Build
make -j4
# Flash (with Pico in BOOTSEL mode)
cp rc_sub_controller.uf2 /media/RPI-RP2
# Static analysis
cppcheck --enable=all --error-exitcode=1 src/
rc-sub-controller/
├── CMakeLists.txt
├── src/
│ ├── main.c # Entry, dual-core setup
│ ├── config.h # Pin assignments, constants
│ ├── types.h # Shared data types
│ ├── drivers/ # Hardware abstraction
│ ├── control/ # PID, state machines
│ ├── safety/ # Core 0 safety monitor
│ └── util/ # Logging, CRC, assertions
├── pio/ # PIO programs
└── test/ # Unit tests
Apply these mandatory rules to all code:
-Wall -Werror - All warnings are errors#define P10_ASSERT(cond) do { \
if (!(cond)) { \
log_fatal("ASSERT: %s:%d: %s", __FILE__, __LINE__, #cond); \
trigger_emergency_blow(EVT_ASSERT_FAIL); \
} \
} while(0)
Assert failures trigger emergency surface—never hang.
┌─────────────────────────────────────────────────────────────────┐
│ CORE 0 (Safety) │ CORE 1 (Control) │
│ • Watchdog feeding │ • RC input capture │
│ • Fault detection │ • Sensor polling │
│ • Emergency blow │ • PID loops │
│ • Battery/leak monitor │ • State machine │
│ • 100 Hz loop │ • 50 Hz loop │
│ • CANNOT be blocked │ • Can be complex │
└─────────────────────────────────────────────────────────────────┘
Shared State (lock-free, atomic flags)
Core 0 runs independently—if Core 1 crashes, Core 0 still triggers emergency surface.
Use PIO for accurate PWM measurement. See references/drivers.md for complete implementation.
// Output structure
typedef struct {
uint16_t channels[6]; // 1000-2000 µs
uint32_t timestamp_ms;
bool valid;
} RcFrame_t;
// Convert to signed percent
int8_t pwm_to_percent(uint16_t pulse_us) {
int16_t centered = (int16_t)pulse_us - 1500;
int16_t scaled = (centered * 100) / 500;
return (int8_t)CLAMP(scaled, -100, 100);
}
I2C driver with temperature compensation. See references/drivers.md.
typedef struct {
int32_t depth_cm; // Depth in centimeters
int32_t temp_c_x10; // Temperature × 10
uint32_t timestamp_ms;
bool valid;
} DepthReading_t;
Complementary filter for pitch/roll. See references/drivers.md.
typedef struct {
int16_t pitch_deg_x10; // Pitch × 10 (0.1° resolution)
int16_t roll_deg_x10; // Roll × 10
uint32_t timestamp_ms;
bool valid;
} AttitudeReading_t;
Generic PID with anti-windup. See references/control.md.
typedef struct {
float kp, ki, kd;
float integral;
float prev_error;
float integral_limit;
float output_min, output_max;
} PidController_t;
float pid_update(PidController_t* pid, float setpoint, float measurement, float dt);
void pid_reset(PidController_t* pid);
See references/control.md for complete state machine specification.
States: INIT → SURFACE → DIVING → SUBMERGED_MANUAL
→ SUBMERGED_DEPTH_HOLD
↓
SURFACING → SURFACE
↓
Any state → EMERGENCY (on fault)
Any of these conditions trigger emergency surface:
void trigger_emergency_blow(uint8_t reason) {
// Atomic flag—cannot be undone except by power cycle
g_emergency_active = true;
// 1. Open vent valve (release air from ballast)
valve_open();
// 2. Run pump in reverse (expel water)
pump_set_speed(-100);
// 3. Full up on control surfaces
servo_set_position(SERVO_BOWPLANE, +100);
servo_set_position(SERVO_STERNPLANE, +100);
// 4. Log the event
log_event(EVT_EMERGENCY_BLOW, reason);
}
Execute scripts/task_status.py to see current progress. Tasks are independent—complete in any order.
When generating code files for this project:
Always include the standard header:
/**
* RC Submarine Controller
* [filename] - [brief description]
*
* Power of 10 compliant
*/
Use the type definitions from types.h - See references/types.md
Follow the naming conventions:
module_action() (e.g., pump_set_speed())CamelCase_t (e.g., RcFrame_t)UPPER_CASE (e.g., MAX_DEPTH_CM)lowercase.c/.hRun the validation script after generating code:
scripts/validate_code.py path/to/file.c
If using Claude CLI (Claude Code), see CLAUDE.md for full instructions.
# One-time setup
./scripts/setup.sh # Install all dependencies
python3 scripts/init_project.py . # Initialize project structure
make install-hooks # Setup git hooks
# Daily workflow
make ci-quick # Quick checks
make test # Run unit tests
make build # Build firmware
The CI system enforces Power of 10 rules automatically on commit.
CLAUDE.md - Claude CLI onboarding guidereferences/pinout.md - Complete GPIO pin assignmentsreferences/drivers.md - Driver implementation detailsreferences/control.md - PID and state machine specificationsreferences/types.md - All type definitionsreferences/safety.md - Safety system specificationsreferences/hardware.md - BOM and wiring diagramsci/run_ci.sh - Main CI runner (all checks)ci/p10_check.py - Power of 10 compliance checkerci/install_hooks.sh - Git hook installertest/run_tests.sh - Unit test runnertest/test_harness.h - Minimal test frameworkscripts/init_project.py - Initialize project directory structurescripts/validate_code.py - Check Power of 10 compliancescripts/task_status.py - Show task completion statusscripts/generate_driver.py - Generate driver boilerplateThis skill supports development based on: