How RPi Pico WAV Player Detects Raspberry Pi Pico vs Waveshare RP2040-LCD-0.96 Boards
The RPi Pico WAV Player distinguishes between Raspberry Pi Pico and Waveshare RP2040-LCD-0.96 boards by sampling the state of GPIO 25 at startup, which is pulled low on the Pico (on-board LED) and pulled high on the Waveshare board (LCD back-light control).
The elehobica/rpi_pico_wav_player project is a lightweight WAV audio player designed for RP2040 and RP2350 microcontrollers. One of its key features is automatic hardware detection, allowing a single firmware binary to run on both the vanilla Raspberry Pi Pico and the Waveshare RP2040-LCD-0.96 display board without manual configuration. This article explains the precise mechanism used to distinguish these platforms at runtime.
How GPIO 25 Enables Automatic Board Detection
The detection mechanism relies on the electrical differences of GPIO 25 (PICO_DEFAULT_LED_PIN) between the two board variants.
Electrical Differences Between Pico and Waveshare
On the Raspberry Pi Pico, GPIO 25 is wired to the on-board LED cathode. The LED is driven low by default, resulting in a logic low state at startup.
On the Waveshare RP2040-LCD-0.96 (and its RP2350-based variant), GPIO 25 is wired to the LCD back-light control (BLK) pin. This pin is pulled up to 3.3 V through a resistor, resulting in a logic high state at startup.
The Detection Algorithm in main.cpp
The firmware executes the detection routine in src/main.cpp immediately after system initialization:
// src/main.cpp (lines 30-40)
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_IN);
#if defined(RASPBERRYPI_PICO2)
board_type_t board_type = gpio_get(PICO_DEFAULT_LED_PIN)
? WAVESHARE_RP2350_LCD_096 : RASPBERRY_PI_PICO_2;
#else
board_type_t board_type = gpio_get(PICO_DEFAULT_LED_PIN)
? WAVESHARE_RP2040_LCD_096 : RASPBERRY_PI_PICO;
#endif
The gpio_get(PICO_DEFAULT_LED_PIN) call returns true when the pin reads high (Waveshare board) and false when low (Pico). The conditional compilation using RASPBERRYPI_PICO2 ensures correct detection for both RP2040 and RP2350 silicon variants.
Board Type Enum and Hardware Abstraction
Once detected, the board type propagates through the system via a strongly-typed enumeration.
Defining Board Variants in common.h
The board_type_t enum in src/common.h defines all supported hardware targets:
// src/common.h (lines 11-16)
typedef enum {
RASPBERRY_PI_PICO = 0,
WAVESHARE_RP2040_LCD_096,
RASPBERRY_PI_PICO_2,
WAVESHARE_RP2350_LCD_096,
} board_type_t;
This enumeration allows subsystems to switch behavior based on the detected hardware without scattering magic numbers throughout the codebase.
Propagating Board Type to Subsystems
The main() function passes the detected board_type to initialization routines:
// Conceptual flow from src/main.cpp
pm_init(board_type); // Power management initialization
ui_init(board_type); // User interface initialization
Each module uses this parameter to configure board-specific pin assignments and calibration values.
Hardware-Specific Configurations
Subsystems adapt their behavior based on the board_type value to account for differing pinouts and electrical characteristics.
LCD Pin Mapping in LcdCanvas.cpp
The LCD controller configuration in src/LcdCanvas.cpp selects SPI pins based on the board type:
// src/LcdCanvas.cpp (lines 74-81)
case WAVESHARE_RP2040_LCD_096: // fallthrough
case WAVESHARE_RP2350_LCD_096:
pin_spi_cs = PIN_LCD_SPI1_CS_WAVESHARE;
pin_spi_sck = PIN_LCD_SPI1_SCK_WAVESHARE;
pin_spi_mosi = PIN_LCD_SPI1_MOSI_WAVESHARE;
pin_dc = PIN_LCD_DC_WAVESHARE;
pin_rst = PIN_LCD_RST_WAVESHARE;
pin_blk = PIN_LCD_BLK_WAVESHARE;
This ensures the ST7735S LCD receives correct SPI signals and back-light control regardless of which board is detected.
Battery Voltage Compensation in power_manage.cpp
The power management module adjusts battery monitoring calibration for Waveshare boards. In src/power_manage.cpp, the firmware adds the forward voltage drop of the back-light diode:
// src/power_manage.cpp (lines 25-27)
if (_board_type == WAVESHARE_RP2040_LCD_096 ||
_board_type == WAVESHARE_RP2350_LCD_096) {
voltage += 0.33; // Forward voltage of D2 (MBR230LSFT1G)
}
This compensation ensures accurate battery level reporting on Waveshare hardware, where the back-light circuit affects the voltage divider network.
Summary
- GPIO 25 sampling provides automatic detection by exploiting electrical differences between the Raspberry Pi Pico (LED cathode, logic low) and Waveshare RP2040-LCD-0.96 (back-light pull-up, logic high).
- Zero configuration is required; the firmware adapts pin assignments, LCD initialization, and battery calibration automatically based on the detected
board_type_tvalue. - Modular design allows subsystems like
LcdCanvasandpower_manageto switch behavior cleanly via the shared enumeration defined insrc/common.h.
Frequently Asked Questions
How does the detection method handle the Raspberry Pi Pico 2 (RP2350)?
The firmware uses conditional compilation (#if defined(RASPBERRYPI_PICO2)) to distinguish between RP2040 and RP2350 silicon during the detection phase. When building for the Pico 2, a high reading on GPIO 25 maps to WAVESHARE_RP2350_LCD_096, while a low reading maps to RASPBERRY_PI_PICO_2. This ensures correct board type assignment across both microcontroller generations.
Can the firmware run on other RP2040 boards without modification?
The detection logic specifically targets GPIO 25 states defined by the Raspberry Pi Pico and Waveshare RP2040-LCD-0.96 schematics. Other boards may wire GPIO 25 differently (e.g., floating or connected to different peripherals), which could cause misidentification. For unsupported boards, you would need to modify the detection logic in src/main.cpp or explicitly set the board_type variable to match your hardware's pinout in src/common.h.
Why does the Waveshare board require battery voltage compensation?
The Waveshare RP2040-LCD-0.96 routes the LCD back-light through a Schottky diode (D2, MBR230LSFT1G) connected to GPIO 25. When measuring battery voltage via the ADC, this diode introduces a forward voltage drop of approximately 0.33 V that affects the voltage divider network. The power_manage.cpp module adds this offset back to the calculated voltage only when a Waveshare board is detected, ensuring accurate battery level reporting across both hardware variants.
Is GPIO 25 safe to sample as an input during startup?
Yes. The detection code configures GPIO 25 as a high-impedance input using gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_IN) before reading it. On the Raspberry Pi Pico, this captures the LED cathode's default low state without driving current. On the Waveshare board, it safely reads the pull-up voltage for the back-light control without interfering with the LCD initialization sequence. This approach avoids electrical conflicts or GPIO damage during the brief detection window.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →