> ## Documentation Index
> Fetch the complete documentation index at: https://launchpad.anxlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ANX SDK GPIO API: Digital I/O Control

> Configure GPIO pins as input or output, read and write digital values, enable pull resistors, and attach interrupt handlers using the ANX SDK GPIO API.

The ANX SDK GPIO API (`anx/gpio.h`) lets you configure, read, and write any of the 20 available GPIO pins on the ANX dev module. Whether you are toggling an LED, sampling a button, or bit-banging a custom protocol, every digital I/O operation goes through this API. All functions return `anx_err_t` so you can handle configuration errors consistently alongside the rest of your application code.

## Include

Add the GPIO header to any source file that performs digital I/O operations:

```c theme={null}
#include "anx/gpio.h"
```

<Note>
  You still need `#include "anx/anx.h"` as the first include in your file. `anx/gpio.h` depends on the common types defined there.
</Note>

## Direction Configuration

Configure a pin as either an input or an output before performing any read or write operations on it. Calling `anx_gpio_read()` on a pin configured as output, or `anx_gpio_write()` on a pin configured as input, returns `ANX_ERR_INVALID_ARG`.

### `anx_gpio_set_direction()`

Sets the direction of a GPIO pin.

<ParamField path="pin" type="uint8_t" required>
  GPIO number in the range 0–33. Refer to the ANX dev module pinout diagram for the physical pin mapping.
</ParamField>

<ParamField path="dir" type="anx_gpio_dir_t" required>
  Pin direction. Use `ANX_GPIO_INPUT` to configure the pin as a high-impedance input, or `ANX_GPIO_OUTPUT` to drive it as a push-pull output.
</ParamField>

<ResponseField name="return" type="anx_err_t">
  `ANX_OK` on success. `ANX_ERR_INVALID_ARG` if `pin` is out of range or `dir` is not a valid `anx_gpio_dir_t` value.
</ResponseField>

```c gpio_direction.c theme={null}
#include "anx/anx.h"
#include "anx/gpio.h"

#define LED_PIN    2
#define BUTTON_PIN 8

void app_main(void) {
    anx_gpio_set_direction(LED_PIN,    ANX_GPIO_OUTPUT);
    anx_gpio_set_direction(BUTTON_PIN, ANX_GPIO_INPUT);
}
```

## Pull Resistor Configuration

Enable an internal pull-up or pull-down resistor on any input pin to avoid leaving it floating. Always call `anx_gpio_set_direction()` before calling `anx_gpio_set_pull()`.

### `anx_gpio_set_pull()`

Configures the internal pull resistor for a GPIO pin.

<ParamField path="pin" type="uint8_t" required>
  GPIO number in the range 0–33.
</ParamField>

<ParamField path="pull" type="anx_gpio_pull_t" required>
  Pull resistor mode. Choose from:

  * `ANX_GPIO_PULL_NONE` — no pull resistor (pin floats when disconnected)
  * `ANX_GPIO_PULL_UP` — internal pull-up to VCC
  * `ANX_GPIO_PULL_DOWN` — internal pull-down to GND
</ParamField>

<ResponseField name="return" type="anx_err_t">
  `ANX_OK` on success. `ANX_ERR_INVALID_ARG` if `pin` is out of range or `pull` is not a valid value.
</ResponseField>

<Tip>
  Use `ANX_GPIO_PULL_UP` for active-low buttons and switches. This way the pin reads `1` at rest and transitions to `0` when the button is pressed, which is the expected polarity for falling-edge interrupts.
</Tip>

## Reading and Writing

### `anx_gpio_write()`

Drives a GPIO output pin high or low.

<ParamField path="pin" type="uint8_t" required>
  GPIO number in the range 0–33. The pin must already be configured as `ANX_GPIO_OUTPUT`.
</ParamField>

<ParamField path="level" type="uint8_t" required>
  Output level: `1` to drive the pin high (VCC), `0` to drive it low (GND).
</ParamField>

<ResponseField name="return" type="anx_err_t">
  `ANX_OK` on success. `ANX_ERR_INVALID_ARG` if the pin is not configured as an output or the pin number is out of range.
</ResponseField>

***

### `anx_gpio_read()`

Samples the current logic level of a GPIO input pin.

<ParamField path="pin" type="uint8_t" required>
  GPIO number in the range 0–33. The pin must already be configured as `ANX_GPIO_INPUT`.
</ParamField>

<ResponseField name="return" type="uint8_t">
  `1` if the pin is high, `0` if the pin is low. Returns `0` on error (check `anx_gpio_set_direction()` was called first).
</ResponseField>

```c gpio_blink.c theme={null}
#include "anx/anx.h"
#include "anx/gpio.h"

#define LED_PIN 2

void app_main(void) {
    anx_gpio_set_direction(LED_PIN, ANX_GPIO_OUTPUT);

    while (1) {
        anx_gpio_write(LED_PIN, 1);
        anx_delay_ms(500);
        anx_gpio_write(LED_PIN, 0);
        anx_delay_ms(500);
    }
}
```

## Interrupts

Attach an interrupt handler to a GPIO input pin to react to level changes without polling. ANX OS calls the handler from an interrupt context, so keep the function as short as possible.

### `anx_gpio_attach_interrupt()`

Attaches an ISR callback to a GPIO pin.

<ParamField path="pin" type="uint8_t" required>
  GPIO number in the range 0–33. The pin must already be configured as `ANX_GPIO_INPUT`.
</ParamField>

<ParamField path="trigger" type="anx_gpio_trigger_t" required>
  The edge or edges that fire the interrupt:

  * `ANX_GPIO_TRIGGER_RISING` — fires when the pin transitions from low to high
  * `ANX_GPIO_TRIGGER_FALLING` — fires when the pin transitions from high to low
  * `ANX_GPIO_TRIGGER_BOTH` — fires on both rising and falling edges
</ParamField>

<ParamField path="handler" type="anx_gpio_isr_t" required>
  A pointer to your ISR callback function. The function signature must be `void handler(void *arg)`.
</ParamField>

<ResponseField name="return" type="anx_err_t">
  `ANX_OK` on success. `ANX_ERR_INVALID_ARG` if the pin is not configured as input, the pin number is out of range, or `handler` is NULL.
</ResponseField>

```c gpio_interrupt.c theme={null}
#include "anx/anx.h"
#include "anx/gpio.h"

#define BUTTON_PIN 8

void IRAM_ATTR button_isr(void *arg) {
    // Keep ISR short — just set a flag
    static volatile bool pressed = false;
    pressed = true;
}

void app_main(void) {
    anx_gpio_set_direction(BUTTON_PIN, ANX_GPIO_INPUT);
    anx_gpio_set_pull(BUTTON_PIN, ANX_GPIO_PULL_UP);
    anx_gpio_attach_interrupt(BUTTON_PIN, ANX_GPIO_TRIGGER_FALLING, button_isr);
}
```

<Warning>
  Keep ISR handlers extremely short. Never call `anx_delay_ms()`, allocate heap memory with `malloc()`, or invoke any ANX SDK function that is not explicitly marked as ISR-safe inside an interrupt handler. Doing so can corrupt the scheduler state or trigger a watchdog reset. Set a flag in the ISR and handle the work in `app_main()` or a dedicated task instead.
</Warning>

<Accordion title="Debouncing hardware buttons">
  Mechanical switches produce multiple rapid transitions when pressed or released, causing your ISR to fire several times per button press. The most reliable approach is to record `anx_uptime_ms()` at the start of the ISR and ignore subsequent interrupts that arrive within 50 ms of the last one. Implement this debounce check in your main-loop handler rather than inside the ISR itself.
</Accordion>
