# pot-head Development Guide
## What pot-head Does
Transforms raw ADC values into clean, processed output values:
```
Raw ADC Value → pot-head Processing → Clean Output Value
```
**Core Features (v0.1):**
- Linear and logarithmic response curves
- EMA and moving average noise filters
- Schmitt trigger and change threshold hysteresis
- Snap and dead zones
- Pickup and PassThrough grab modes
## Project Structure
```
pot-head/
├── src/
│ ├── lib.rs # Main library entry point
│ ├── config.rs # Configuration types (Config)
│ ├── state.rs # Runtime state management
│ ├── pothead.rs # Core PotHead implementation
│ ├── filters/ # Noise filtering implementations
│ ├── curves/ # Response curve implementations
│ ├── hysteresis/ # Hysteresis mode implementations
│ ├── snap_zones/ # Snap zone implementations
│ └── grab_mode/ # Grab mode implementations
├── docs/
│ └── FEATURES.md # User-facing feature documentation
├── examples/ # Usage examples and demonstrations
└── tests/ # Integration tests
```
## Key Design Principles
### 1. No I/O, Pure Math
This library does NO hardware interaction. It's a pure transformation function. Users handle ADC reads and apply the library's processing.
### 2. Dual Type Parameters
```rust
PotHead<TIn, TOut = TIn>
```
- `TIn`: Input type (typically ADC integer like `u16`)
- `TOut`: Output type (often normalized `f32` or application-specific)
- Default `TOut = TIn` for same-type cases
### 3. Static ROM Configuration (v0.1)
v0.1 uses static ROM configuration exclusively:
- **Static ROM config** (config in flash, minimal RAM)
- **Builder pattern** deferred to v0.2+ (alongside calibration features)
### 4. Feature-Gated Compilation
Three meaningful features control dependencies and optional functionality:
```toml
default = ["std-math", "grab-mode"]
std-math = ["libm"] # Logarithmic curves
moving-average = ["heapless"] # Moving average filter
grab-mode = [] # Pickup/PassThrough modes (~24-40 bytes)
```
### 5. Error Handling Strategy
- **Const validation**: Catch config errors at compile time
- **Runtime clamping**: Gracefully handle ADC glitches
- **Debug assertions**: Catch issues in development
- **No panics in release**: Embedded-friendly
## Implementation Guidelines
### Code Organization
- Each major feature in its own module
- Feature-gate only dependency-driven code (libm, heapless) and grab-mode
- Keep processing pipeline in `PotHead::process()` clean and linear
### Processing Pipeline
```rust
Input (TIn)
→ Normalize (f32)
→ Filter
→ Curve
→ Snap Zones
→ Grab Mode
→ Denormalize (TOut)
→ Output (TOut)
```
### Performance Requirements
- `process()` called in tight loops (1-10ms intervals)
- Zero allocations (stack only)
- Minimal branching in hot path
- All processing modes compiled in by default (minimal overhead)
### Testing Strategy
- Unit tests for each module
- Integration tests for full pipeline
- Property-based tests for numeric edge cases
- Example programs demonstrating common use cases
## Common Patterns
### Static ROM Config Example (v0.1)
```rust
static VOLUME_CONFIG: Config<u16, f32> = Config {
input_min: 0,
input_max: 4095,
output_min: 0.0,
output_max: 1.0,
curve: ResponseCurve::Logarithmic,
filter: NoiseFilter::ExponentialMovingAverage { alpha: 0.3 },
hysteresis: HysteresisMode::ChangeThreshold { threshold: 0.05 },
snap_zones: &[SnapZone::new(0.0, 0.02, SnapZoneType::Snap)],
grab_mode: GrabMode::Pickup,
};
// Validate at compile time
const _: () = {
match VOLUME_CONFIG.validate() {
Ok(()) => {},
Err(e) => panic!("{}", e),
}
};
// Create instance (only state in RAM)
let mut pot = PotHead::new(&VOLUME_CONFIG);
// In main loop:
let volume: f32 = pot.process(adc_value);
```
## Documentation Requirements
- All public APIs need doc comments
- Feature-gated items should document the required feature (std-math, moving-average, grab-mode)
- Demonstrate common use cases in interactive example
- Safety notes for `unsafe` code (if any)
- Performance characteristics (especially for filters)
## Reference Documentation
**Feature Documentation:** `docs/FEATURES.md`
- Complete feature reference for users
- Usage examples for all features
- Configuration patterns
- Future roadmap
**Key Design Decisions:**
- Numeric type design (separate TIn/TOut) - eliminates wasteful conversions
- Error handling strategy (const validation + runtime clamping) - zero overhead in release
- Feature gating details - compile-time optimization
- Grab mode implementations - Pickup and PassThrough for parameter automation
- Overlapping snap zones behavior - first match wins (priority by order)
- Static ROM configuration (v0.1) - builder API deferred to v0.2+
## Development Workflow
1. **Understand the architecture** - Review `FEATURES.md` and existing implementation
2. **Implement features incrementally** - One feature module at a time
3. **Test as you go** - Unit tests for each module
4. **Update examples** - Demonstrate new features in interactive example
5. **Lean and clean documentation** - Brief API docs. No verbose doc comments
6. **Update FEATURES.md** - Document user-facing changes
## Adding New Features
When adding new features:
1. Consider if the change affects existing design decisions
2. Update both implementation and documentation together
3. Add feature flag if it introduces new dependencies
4. Update `FEATURES.md` with usage examples
5. Add tests demonstrating the feature
---
**Remember:** This library is for embedded systems. Prioritize:
- Zero allocations
- Predictable performance
- Minimal binary size
- Clear, maintainable code