# Buttons

The six buttons on a Kode Dot: a four-way pad and two singles, how five of them reach the P4 through an expander, and the code to read them.

## What it is

Six push-buttons: a four-way pad on the front, and one single button above
it and one below. Each is a switch to ground with a pull-up, so a line reads
high at rest and low while pressed. That is all a button is: one bit, pressed
or not, no pressure and no analogue level.

They do not all reach the P4 the same way. Five of them, the pad and the
bottom single, hang off a **TCA6408** I2C port expander and come back as
five bits of one byte. The sixth, the top single, is wired straight to a P4
pin. Reading the buttons is one I2C read plus one pin read, which the Kode
driver combines for you.

## How it is connected

```mermaid
flowchart LR
  p4["ESP32-P4"] -- "I2C, 0x20" --> exp["TCA6408"]
  exp -- "P0" --> bot["bottom"]
  exp -- "P1..P4" --> pad["pad: up, left, down, right"]
  top["top"] -- "GPIO35" --> p4
```

*Five buttons through the expander on the I2C bus, one straight to a pin*

The expander answers at `0x20` on the shared I2C bus and has 10 kΩ pull-ups
on the board for its five button lines. The top button is on `GPIO35` with
the P4's internal pull-up. The expander's interrupt line reaches `GPIO6`, but
Kode's examples poll on a timer instead.

## Specifications

| Thing | Value |
| --- | --- |
| Buttons | Six, all active low: pressed reads 0 |
| Expander | TCA6408, U24, at `0x20` on the shared I2C bus |
| Bottom single | Expander `P0` |
| Pad up, left, down, right | Expander `P1`, `P2`, `P3`, `P4` |
| Top single | `GPIO35` on the P4 |
| Pull-ups | 10 kΩ on the board for the five; the P4's internal one for the top |
| Interrupt | Expander interrupt on `GPIO6`, unused by any Kode example |
| Kode driver | `kode_buttons_init`, then `kode_buttons_pressed(btn)` for one or `kode_buttons_mask()` for all six in one read |
| Validated | `Kode-Dot-Val/11-INPUT`: all six on a board |

## Gotchas

> **DANGER**
>
> **The screen went black when your button code started.** Bit `P7` of the same
> expander powers the display. Code that configures the expander must reuse the
> handle `kode_bsp_init` created and keep its direction mask on `P0` to `P4`;
> put `P7` in it and you switch the panel off. `kode_buttons_init` does this
> right; do not make a second expander.

> **NOTE**
>
> **Pressed is 0.** Every line reads high at rest. If your code sees all six
> buttons "pressed" at boot, the polarity is inverted.

## Apps that use it

On the marketplace, apps that put this part to work. Install one from
`start/first-app` and see it before you write a line.

- https://apps.kode.diy/app/358599b5-9ee1-4d91-b1c5-47c93da98e0a
- https://apps.kode.diy/app/e22664f5-9a5a-4c6f-8fd6-89ef68ab20a3
- https://apps.kode.diy/app/963762ed-8005-4342-94ec-4e8253e48bf3
- https://apps.kode.diy/app/5d4012e8-74c6-4542-ab47-4b42a1927ef4
- https://apps.kode.diy/app/dbbf2b68-3e2c-4bba-9ae3-81122e839b00
- https://apps.kode.diy/app/f81b17f6-8598-41f0-8aa3-a8080e103d5b
- https://apps.kode.diy/app/b20a29cd-7353-4bdb-86a0-6b844cf98e1a
- https://apps.kode.diy/app/04c1ec90-c7c1-48fb-ac53-c8822aef31ad

## Example code

Log every button as it goes down. Press the pad and the two singles and each
one prints its name once per press.

```c
/*
 * buttons_log.c - 2026-09-18 - from Kode with love
 *
 * Watches the Kode Dot's six buttons twenty times a second and logs each one
 * the moment it goes down. Brings the board up, configures the buttons through
 * the Kode driver, then polls the six-bit mask and reports the edges.
 */
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "kode.h"

static const char *NAMES[] = { "bottom", "pad up", "pad left", "pad down", "pad right", "top" };

/* Board, buttons, then compare each poll with the last one. */
void app_main(void)
{
    ESP_ERROR_CHECK(kode_bsp_init());
    ESP_ERROR_CHECK(kode_buttons_init());

    uint8_t last = 0;
    while (1) {
        uint8_t now = kode_buttons_mask();          /* bit N set while button N is down */
        uint8_t went_down = now & ~last;
        for (int i = 0; i < 6; i++) {
            if (went_down & (1 << i)) ESP_LOGI("buttons", "%s", NAMES[i]);
        }
        last = now;
        vTaskDelay(pdMS_TO_TICKS(50));
    }
}
```

Compiled on the date in the header against the `kode` component with
ESP-IDF v6.1. **TBD**: a run on a V1 board.

## In the SDK

- [`kdrv_buttons.c`](https://github.com/kodediy/kode-sdk/blob/main/components/kdrv/kdrv_buttons.c): the buttons in `kode-sdk`.
- [`kdrv_tca6408a.c`](https://github.com/kodediy/kode-sdk/blob/main/components/kdrv/kdrv_tca6408a.c): the expander they share with the display.
