What Is a BSP?
~5 min slides
The Top of the Stack
A Board Support Package encapsulates board-specific knowledge — pin assignments, peripheral configuration, and hardware defaults.
BSP allows us to skip the configure step. Pattern becomes Instantiate → Control.
Same idea as HAL with PAC — BSP takes PAC singletons as input to create HAL instances and use them.
Without BSP (raw HAL)
#![allow(unused)] fn main() { // Instantiate let peripherals = esp_hal::init(Config::default()); // Configure let led = Output::new( peripherals.GPIO5, Level::Low, OutputConfig::default(), ); // Control led.set_high(); }
With BSP
#![allow(unused)] fn main() { // Instantiate (BSP takes HAL peripherals as input) let peripherals = esp_hal::init(Config::default()); let mut uferris = uferris_init(peripherals).unwrap(); // Control — no configure step needed uferris.led1_on(); }
The BSP still requires instantiation — you pass the HAL peripherals into uferris_init(). But the board-specific configuration (which pin, what drive strength, which pull resistor) is all handled internally. You go straight from Instantiate to Control.
It's Not Magic
The BSP is just Rust code that does exactly what you've been doing — but packages it up with meaningful names. Open the source and you'll see Output::new(), I2c::new(), and all the same patterns.
The value is:
- No pin lookup errors — the board knows its own pin assignments
- Sensible defaults — configurations chosen for the specific board's hardware
- Convenience — less boilerplate for common setups
When to Use a BSP vs. Raw HAL
| Use BSP When | Use Raw HAL When |
|---|---|
| Quick prototyping | You need non-default configurations |
| Your board has a BSP | You're using custom hardware |
| You don't need fine control | You want to learn the lower layers |
In a workshop about ecosystem navigation, understanding the raw HAL is essential. The BSP is the reward: once you understand the layers underneath, you can appreciate what the BSP does for you.