Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,26 @@ while (true) {
}
```

### Active High / active low

By default the library assumes that your buttons will short the pin to the ground, and initialize them with the pin pulled up. If the buttons in your schematic use "active High" topology (so when pressed they connect the pin to a logical "high" voltage level), you need your button pin pulled down. Use these functions instead:

```c
button_t *simple_button_active_high = create_button_active_high(14, simple_callback);
button_t *complex_button_active_high = create_button_queued_active_high(15, complex_callback);

// You can mix active-high and active-low buttons in your project:
button_t *simple_button_active_low = create_button(16, simple_callback);
button_t *complex_button_active_low = create_button(17, simple_callback);
```

## Projects using this library
- [Dodepan](https://github.com/TuriSc/Dodepan)
- [Jukephone](https://github.com/TuriSc/Jukephone)

## Version History

- 2026-02-25 - Add active-high button support for buttons that pull to VCC instead of ground
- 2026-01-04 - Add per-button callback modes (immediate and queued). Restores backwards compatibility
- 2025-11-22 - Add queued events polling to invoke callbacks outside interrupt context
- 2023-02-14 - Initial release
36 changes: 30 additions & 6 deletions button.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,13 +207,17 @@ void button_destroy(button_t *button) {
* @param pin The GPIO pin number
* @param onchange The onchange callback function
* @param use_queue If true, use event queue; if false, execute callbacks immediately
* @param active_high If true, pin is pulled down; if false, pin is pulled up
* @return The new button structure, or NULL on failure
*/
static button_t * create_button_internal(int pin, void (*onchange)(button_t *), bool use_queue) {
static button_t * create_button_internal(int pin, void (*onchange)(button_t *), bool use_queue, bool active_high) {
if (pin >= 28 || !onchange) return NULL;

gpio_init(pin);
gpio_pull_up(pin);
if (active_high)
gpio_pull_down(pin);
else
gpio_pull_up(pin);
button_t *b = (button_t *)(malloc(sizeof(button_t)));
if (!b) return NULL;

Expand All @@ -226,21 +230,41 @@ static button_t * create_button_internal(int pin, void (*onchange)(button_t *),
}

/**
* @brief Creates a new button structure with immediate callback execution
* @brief Creates a new button structure with immediate callback execution, pin pulled up
* @param pin The GPIO pin number
* @param onchange The onchange callback function
* @return The new button structure, or NULL on failure
*/
button_t * create_button(int pin, void (*onchange)(button_t *)) {
return create_button_internal(pin, onchange, false);
return create_button_internal(pin, onchange, false, false);
}

/**
* @brief Creates a new button structure with queued callback execution
* @brief Creates a new button structure with queued callback execution, pin pulled up
* @param pin The GPIO pin number
* @param onchange The onchange callback function
* @return The new button structure, or NULL on failure
*/
button_t * create_button_queued(int pin, void (*onchange)(button_t *)) {
return create_button_internal(pin, onchange, true);
return create_button_internal(pin, onchange, true, false);
}

/**
* @brief Creates a new button structure with immediate callback execution, pin pulled down
* @param pin The GPIO pin number
* @param onchange The onchange callback function
* @return The new button structure, or NULL on failure
*/
button_t * create_button_active_high(int pin, void (*onchange)(button_t *)) {
return create_button_internal(pin, onchange, false, true);
}

/**
* @brief Creates a new button structure with queued callback execution, pin pulled down
* @param pin The GPIO pin number
* @param onchange The onchange callback function
* @return The new button structure, or NULL on failure
*/
button_t * create_button_queued_active_high(int pin, void (*onchange)(button_t *)) {
return create_button_internal(pin, onchange, true, true);
}
22 changes: 20 additions & 2 deletions button.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ void listen(uint pin, int condition, handler fn, void *arg);
void button_system_init(void);

/**
* @brief Creates a new button structure with immediate callback execution
* @brief Creates a new button structure with immediate callback execution, pin pulled up
* @param pin The GPIO pin number
* @param onchange The onchange callback function
* @return The new button structure, or NULL on failure
Expand All @@ -145,14 +145,32 @@ void button_system_init(void);
button_t * create_button(int pin, void (*onchange)(button_t *));

/**
* @brief Creates a new button structure with queued callback execution
* @brief Creates a new button structure with queued callback execution, pin pulled up
* @param pin The GPIO pin number
* @param onchange The onchange callback function
* @return The new button structure, or NULL on failure
* @note Callbacks are queued and require button_poll_events() to be called from main loop
*/
button_t * create_button_queued(int pin, void (*onchange)(button_t *));

/**
* @brief Creates a new button structure with immediate callback execution, pin pulled down
* @param pin The GPIO pin number
* @param onchange The onchange callback function
* @return The new button structure, or NULL on failure
* @note Callbacks execute immediately in alarm context (no polling required)
*/
button_t * create_button_active_high(int pin, void (*onchange)(button_t *));

/**
* @brief Creates a new button structure with queued callback execution, pin pulled down
* @param pin The GPIO pin number
* @param onchange The onchange callback function
* @return The new button structure, or NULL on failure
* @note Callbacks are queued and require button_poll_events() to be called from main loop
*/
button_t * create_button_queued_active_high(int pin, void (*onchange)(button_t *));

/**
* @brief Poll for button events and process callbacks (call from main loop)
* @return Number of events processed
Expand Down