# Tuyaopen/peripheral Button

> TDL button input handling for TuyaOpen: create button, register event callbacks for single click, double click, long press, and multi-click. 按键、单击、双击、长按、多击、按键事件。

- Skill: `tuya/tuyaopen-peripheral-button` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tuya/tuyaopen-peripheral-button`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tuya/tuyaopen-peripheral-button/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tuya (https://skillmd.com/u/tuya)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tuya/tuyaopen-peripheral-button

---


# TuyaOpen TDL Button

## Driver Registration (TDD)

Decide by **adaptation, not by whether the SDK has the driver** — `tdd_gpio_button`
ships in the SDK either way:

- **Board-adapted button** (listed in `board-context.md`) → `board_register_hardware()`
  registers it automatically; write no TDD code.
- **Externally-attached button** (the user wired their own button — NOT in
  `board-context.md`, e.g. "extra button on GPIO7") → register it yourself in
  **`usr_board`** (see `usr-board/SKILL.md`). You still reuse the existing
  `tdd_gpio_button_register` — `board_register_hardware()` does **not** wire a
  button it never adapted. Put the `__usr_register_button()` below into
  `usr_board.c` and call it from `usr_register_hardware()`:

```c
#include "tdd_button_gpio.h"

static OPERATE_RET __usr_register_button(void)
{
    BUTTON_GPIO_CFG_T btn_cfg = {
        .pin              = TUYA_GPIO_NUM_17,      // Pins: btn_0=GPIO17
        .level            = TUYA_GPIO_LEVEL_LOW,   // Pins: btn_0=GPIO17(low)
        .mode             = BUTTON_IRQ_MODE,
        .pin_type.irq_edge = TUYA_GPIO_IRQ_FALL,
    };
    return tdd_gpio_button_register(BUTTON_NAME, &btn_cfg);
}
```

Pin and active level: for a **board-adapted** button, read its `pins` from
`.tuyaopen/ide/board.json` (by `ID:`); for an **externally-attached** button,
use the GPIO/level the user gave (must be a `gpio` from `board.json.expansionPins`).

### New button IC with no SDK driver (still in `usr_board`)

Reusing `tdd_gpio_button_register` covers ordinary GPIO buttons. Only when the
button needs a TDD driver the SDK does **not** have (e.g. capacitive touch key,
I2C keypad) do you also write `tdd_button_<ic>.h/.c` inside `usr_board/` and
implement `tdl_button_register()` with the callbacks. This is an addition to the
`usr_board` flow, not an alternative to it.

---

## Headers

```c
#include "tdl_button_manage.h"
```

## Usage Template

```c
#include "tal_api.h"
#include "tkl_output.h"
#include "tdl_button_manage.h"

/* Event callback */
static void button_event_cb(char *name, TDL_BUTTON_TOUCH_EVENT_E event, void *argc)
{
    switch (event) {
    case TDL_BUTTON_PRESS_DOWN:         PR_NOTICE("%s: press down", name);    break;
    case TDL_BUTTON_PRESS_UP:           PR_NOTICE("%s: press up", name);      break;
    case TDL_BUTTON_PRESS_SINGLE_CLICK: PR_NOTICE("%s: single click", name);  break;
    case TDL_BUTTON_PRESS_DOUBLE_CLICK: PR_NOTICE("%s: double click", name);  break;
    case TDL_BUTTON_LONG_PRESS_START:   PR_NOTICE("%s: long press", name);    break;
    default: break;
    }
}

void user_main(void)
{
    tal_log_init(TAL_LOG_LEVEL_DEBUG, 4096, (TAL_LOG_OUTPUT_CB)tkl_log_output);

    /* Prerequisite: board_register_hardware() already called */

    TDL_BUTTON_CFG_T button_cfg = {
        .long_start_valid_time     = 3000, // long-press threshold (ms)
        .long_keep_timer           = 1000, // hold repeat interval (ms)
        .button_debounce_time      = 50,   // debounce (ms)
        .button_repeat_valid_count = 2,    // clicks >= 2 trigger REPEAT event
        .button_repeat_valid_time  = 500,  // multi-click window (ms)
    };

    TDL_BUTTON_HANDLE button_hdl = NULL;
    TUYA_CALL_ERR_GOTO(tdl_button_create(BUTTON_NAME, &button_cfg, &button_hdl), EXIT);

    /* Register only the events you need */
    tdl_button_event_register(button_hdl, TDL_BUTTON_PRESS_SINGLE_CLICK, button_event_cb);
    tdl_button_event_register(button_hdl, TDL_BUTTON_LONG_PRESS_START,   button_event_cb);

EXIT:
    return;
}
```

## API Reference

| Function | Description |
|----------|-------------|
| `tdl_button_create(name, &cfg, &hdl)` | Create button with timing config |
| `tdl_button_event_register(hdl, event, cb)` | Register callback for one event type |
| `tdl_button_delete(hdl)` | Delete button and release hardware |
| `tdl_button_read_status(hdl, &status)` | Read current state (0=released, 1=pressed) |
| `tdl_button_set_scan_time(ms)` | Set scan interval (default 10 ms) |

## Event Types

| Event | Description |
|-------|-------------|
| `TDL_BUTTON_PRESS_DOWN` | Pressed |
| `TDL_BUTTON_PRESS_UP` | Released |
| `TDL_BUTTON_PRESS_SINGLE_CLICK` | Single click |
| `TDL_BUTTON_PRESS_DOUBLE_CLICK` | Double click |
| `TDL_BUTTON_PRESS_REPEAT` | Multi-click (≥ `button_repeat_valid_count` times) |
| `TDL_BUTTON_LONG_PRESS_START` | Long press start (held ≥ `long_start_valid_time` ms) |
| `TDL_BUTTON_LONG_PRESS_HOLD` | Long press hold (fires every `long_keep_timer` ms) |

## Multiple Buttons

```c
#if defined(BUTTON_NAME_2)
TDL_BUTTON_HANDLE button_hdl_2 = NULL;
tdl_button_create(BUTTON_NAME_2, &button_cfg, &button_hdl_2);
tdl_button_event_register(button_hdl_2, TDL_BUTTON_PRESS_SINGLE_CLICK, button_event_cb);
#endif
```

## Enable Macro and NAME Macro

```
CONFIG_ENABLE_BUTTON=y        # enables button and defines BUTTON_NAME
CONFIG_BUTTON_NAME="button1"  # device name (default "button1")
```

`BUTTON_NAME` is passed to `tdl_button_create()`.
Additional buttons use `BUTTON_NAME_2`, `BUTTON_NAME_3` (requires enabling `ENABLE_BUTTON_2`, etc.).

## Reference Example

`examples/peripherals/button/`

