一键导入
tuyaopen-peripheral-led
TDL LED indicator control for TuyaOpen: find, open, set on/off/toggle, flash, and blink with configurable patterns. 指示灯、LED、开关灯、闪烁、呼吸灯。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
TDL LED indicator control for TuyaOpen: find, open, set on/off/toggle, flash, and blink with configurable patterns. 指示灯、LED、开关灯、闪烁、呼吸灯。
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
End-to-end IoT product development orchestration for TuyaOpen projects. Guides from requirements gathering → Tuya Platform product/DP creation → complete embedded firmware generation. State-machine: detects project state and picks up from wherever development currently stands.
TDL audio codec usage for TuyaOpen: discover the audio device, capture microphone PCM frames via callback, play PCM back to the speaker, and set volume. 音频、麦克风、录音、播放、喇叭、扬声器、speaker、mic、声音、语音、codec。
TDL button input handling for TuyaOpen: create button, register event callbacks for single click, double click, long press, and multi-click. 按键、单击、双击、长按、多击、按键事件。
TDL camera control for TuyaOpen: find device, configure resolution/fps/format, open with frame callbacks for YUV422 and JPEG/H264 encoded output. 摄像头、相机、拍照、视频流、YUV、JPEG、H264、人脸识别、图像采集。
TDL display control for TuyaOpen: driver registration, open, brightness, and framebuffer flush via tdl_disp_* APIs. 显示屏、LCD、屏幕、显示、绘制、亮度、刷屏。
TDL thermal printer usage for TuyaOpen: discover the printer, print UTF-8 text (auto GBK conversion on ESC/POS), print 1-bit bitmaps, feed paper, and monitor paper/temperature status. 打印机、热敏打印、打印、小票、票据、出纸、ESC/POS、printer、DP48A。
| name | tuyaopen/peripheral-led |
| description | TDL LED indicator control for TuyaOpen: find, open, set on/off/toggle, flash, and blink with configurable patterns. 指示灯、LED、开关灯、闪烁、呼吸灯。 |
| when_to_use | Use when the user wants to control an indicator LED: turn on/off, toggle, flash, or blink with a pattern. |
| id | peripheral-led |
| surfaces | ["embedded"] |
| tags | ["led","gpio","indicator"] |
tdl_led componentAn LED — onboard or an extra GPIO LED you wire — is always driven via the
tdl_led component: register it (TDD) with tdd_led_gpio_register(LED_NAME_n, …),
then control it with tdl_led_find_dev / tdl_led_open / tdl_led_set_status /
tdl_led_flash / tdl_led_blink.
Do NOT toggle an LED with raw tkl_gpio_write / tal_gpio_* — even if the user
says "GPIO47 high = on" and a one-line write looks easier. Raw GPIO loses flash /
blink / toggle patterns and the software-timer management, and breaks the
TDD→TDL→App layering the project relies on. "It lights up" is not the bar.
// ❌ BAD — raw GPIO, no component
tkl_gpio_init(TUYA_GPIO_NUM_47, &out_cfg);
tkl_gpio_write(TUYA_GPIO_NUM_47, TUYA_GPIO_LEVEL_HIGH);
// ✅ GOOD — register on the next free slot, drive via tdl_led (see Steps below)
tdd_led_gpio_register(LED_NAME_2, &led_cfg); // usr_board (TDD)
TDL_LED_HANDLE_T h = tdl_led_find_dev(LED_NAME_2); // app (TDL)
tdl_led_open(h);
tdl_led_set_status(h, TDL_LED_ON);
onchip-gpio is for non-LED digital pins only. If the load is an LED, you are in
the right skill — keep going; do not fall back to onchip-gpio.
Each LED is a named slot turned on by its own enable flag, chained 1→4.
There is no LED count integer — you do not set a total. To make an extra slot
available, enable that slot's CONFIG_ENABLE_LED_N flag (see chaining rule below).
| Slot | Enable flag | NAME macro (default) | Typical owner |
|---|---|---|---|
| 1 | ENABLE_LED | LED_NAME ("led") | Board BSP (tuya_*_board.c) |
| 2 | ENABLE_LED_2 | LED_NAME_2 ("led2") | Board BSP or usr_board/ |
| 3 | ENABLE_LED_3 | LED_NAME_3 ("led3") | Board BSP or usr_board/ |
| 4 | ENABLE_LED_4 | LED_NAME_4 ("led4") | Board BSP or usr_board/ |
Chaining rule (SDK Kconfig): ENABLE_LED_2 requires ENABLE_LED,
ENABLE_LED_3 requires ENABLE_LED_2, ENABLE_LED_4 requires ENABLE_LED_3.
Slots must be enabled contiguously — you cannot enable slot 4 without 2 and 3.
The board already enables its own slots, so to add one extra you enable the
next flag. A LED_NAME_N macro is #defined only when its ENABLE_LED_N
is set, so #if defined(LED_NAME_N) guards still gate your code.
Before adding an extra LED, count how many the board already registers:
# How many tdd_led_gpio_register() calls in the active board file?
grep -c tdd_led_gpio_register boards/T5AI/TUYA_T5AI_BOARD/tuya_t5ai_board.c
# Or count led entries in .tuyaopen/board-context.md
Then, with N = board slot count and your extra at slot N+1:
enable flag = CONFIG_ENABLE_LED_<N+1>=y /* makes the next slot available */
next free macro = LED_NAME_<N+1> /* register / look up under this */
Examples:
| Board already registers | You add | Enable in app_default.config | Register in usr_board as | App lookup |
|---|---|---|---|---|
1 (LED_NAME) | 1 extra | CONFIG_ENABLE_LED_2=y | LED_NAME_2 | tdl_led_find_dev(LED_NAME_2) |
3 (LED_NAME … LED_NAME_3) | 1 extra | CONFIG_ENABLE_LED_4=y | LED_NAME_4 | tdl_led_find_dev(LED_NAME_4) |
0 (no ENABLE_LED / no board LED) | 1 extra | CONFIG_ENABLE_LED=y | LED_NAME | tdl_led_find_dev(LED_NAME) |
On TUYA_T5AI_BOARD today the BSP registers one onboard LED (slot 1,
LED_NAME) → one extra wire → enable CONFIG_ENABLE_LED_2=y, register LED_NAME_2.
That is a special case, not a universal rule.
| Scenario | Board BSP | Config (app_default.config) | TDD registration | TDL lookup |
|---|---|---|---|---|
| Use onboard LED only | Already registered | Usually nothing extra | board_register_hardware() only | tdl_led_find_dev(LED_NAME) |
| Extra GPIO LED(s) | Owns slots 1…N | CONFIG_ENABLE_LED_<N+1>=y (per extra) | usr_board/ → next free LED_NAME_<N+1> | tdl_led_find_dev(LED_NAME_<N+1>) |
Do not edit SDK src/peripherals/led/Kconfig for normal app work. Set options in app_default.config.
Do not register under a name the board already uses — pick the next free LED_NAME_N slot.
app_default.configMost T5AI boards (e.g. TUYA_T5AI_BOARD) already have:
# boards/T5AI/TUYA_T5AI_BOARD/Kconfig
select ENABLE_LED
No line needed in app_default.config unless ENABLE_LED was disabled elsewhere. Default: slot 1 enabled, LED_NAME → "led".
N).ENABLE_LED_2 up to the one you need). In source/embedded/app_default.config:CONFIG_ENABLE_LED_2=y # makes slot 2 (LED_NAME_2) available; one extra on a 1-LED board
# for a second extra also add CONFIG_ENABLE_LED_3=y, etc. (max slot 4)
# optional rename of the slot you use, e.g. for slot 2:
# CONFIG_LED_NAME_2="led_ext"
In usr_board, register each extra LED on the next free slot:
tdd_led_gpio_register(LED_NAME_2, …)tdd_led_gpio_register(LED_NAME_4, …)In app code, use #if defined(LED_NAME_N) / tdl_led_find_dev(LED_NAME_N) for that same slot macro.
T5AI-Board example (board N=1, one extra): CONFIG_ENABLE_LED_2=y, register LED_NAME_2.
After any hand-edit to app_default.config, refresh before build (see skill tuyaopen/build, section After editing app_default.config manually):
cd source/embedded
tos.py clean -f && tos.py build
Verify both:
.build/cache/using.config → CONFIG_ENABLE_LED_2=y.build/include/tuya_kconfig.h → #define ENABLE_LED_2 1 and #define LED_NAME_2 "led2"A successful build with stale tuya_kconfig.h still compiles without LED_NAME_2 — features silently disappear at compile time.
board_register_hardware() registers the onboard LED — no app TDD code.
Read GPIO and active level from the led device in .tuyaopen/ide/board.json (looked up by its ID:):
## led — Indicator LED
Pins: led=GPIO1(high)
usr_boardRead skill hardware-vibe-coding/peripheral-drivers/usr-board/SKILL.md for CMake wiring.
Register the second device name (LED_NAME_2), not LED_NAME:
/* usr_board/usr_board.c */
#include "usr_board.h"
#include "tdd_led_gpio.h"
#include "tal_api.h"
#define USR_EXT_LED_GPIO TUYA_GPIO_NUM_28 /* from user / ide/board.json */
#define USR_EXT_LED_ACTIVE_LEVEL TUYA_GPIO_LEVEL_HIGH
#if defined(LED_NAME_2)
static OPERATE_RET __usr_register_ext_led(void)
{
TDD_LED_GPIO_CFG_T led_cfg = {
.pin = USR_EXT_LED_GPIO,
.level = USR_EXT_LED_ACTIVE_LEVEL,
.mode = TUYA_GPIO_PUSH_PULL,
};
return tdd_led_gpio_register(LED_NAME_2, &led_cfg);
}
#endif
OPERATE_RET usr_register_hardware(void)
{
OPERATE_RET rt = OPRT_OK;
#if defined(LED_NAME_2)
TUYA_CALL_ERR_RETURN(__usr_register_ext_led());
#endif
return rt;
}
Call order in user_main():
tal_sw_timer_init(); /* required before tdl_led_open() */
board_register_hardware();
usr_register_hardware(); /* after board, before TDL open */
Check the device note and other devices' pins in .tuyaopen/ide/board.json for reserved pins — e.g. on T5AI-Board, GPIO28 is also spk_en (speaker enable). Pick a free GPIO or accept the conflict.
If the LED uses an I2C/SPI controller IC not in the SDK, create
tdd_led_<ic>.h/.c and implement tdl_led_driver_register() with the required callbacks.
#include "tdl_led_manage.h"
tal_sw_timer_init()must run beforetdl_led_open().
Flash/blink uses software timers; without init the device can crash on first timer use.
#include "tal_api.h"
#include "tdl_led_manage.h"
void user_main(void)
{
tal_log_init(TAL_LOG_LEVEL_DEBUG, 4096, (TAL_LOG_OUTPUT_CB)tkl_log_output);
tal_sw_timer_init(); // call early — required before any TDL open
tal_workq_init();
/* Prerequisite: board_register_hardware() already called */
/* Find LED device */
TDL_LED_HANDLE_T led_hdl = tdl_led_find_dev(LED_NAME);
if (NULL == led_hdl) {
PR_ERR("LED device '%s' not found", LED_NAME);
return;
}
/* Open */
tdl_led_open(led_hdl);
/* Control */
tdl_led_set_status(led_hdl, TDL_LED_ON); // turn on
tdl_led_set_status(led_hdl, TDL_LED_OFF); // turn off
tdl_led_set_status(led_hdl, TDL_LED_TOGGLE); // toggle
/* Flash at 1 Hz (500 ms half-cycle) */
tdl_led_flash(led_hdl, 500);
/* Close */
tdl_led_close(led_hdl);
}
| Function | Description |
|---|---|
tdl_led_find_dev(name) | Find registered LED; LED_NAME, LED_NAME_2, … |
tdl_led_open(hdl) | Open device and init GPIO |
tdl_led_set_status(hdl, status) | TDL_LED_ON / TDL_LED_OFF / TDL_LED_TOGGLE |
tdl_led_flash(hdl, half_cycle_ms) | Symmetric flash; half-cycle in ms |
tdl_led_blink(hdl, &cfg) | Custom blink pattern |
tdl_led_close(hdl) | Close and release |
TDL_LED_BLINK_CFG_T blink_cfg = {
.cnt = 3, // blink 3 times (TDL_BLINK_FOREVER = loop)
.start_stat = TDL_LED_ON,
.end_stat = TDL_LED_OFF,
.first_half_cycle_time = 200, // on for 200 ms
.latter_half_cycle_time = 800, // off for 800 ms
};
tdl_led_blink(led_hdl, &blink_cfg);
app_default.config)app_default.config | Effect |
|---|---|
(board selects) ENABLE_LED | Enables LED subsystem + slot 1 → LED_NAME |
CONFIG_ENABLE_LED_2=y | Enables slot 2 → defines LED_NAME_2 |
CONFIG_ENABLE_LED_3=y | Enables slot 3 → defines LED_NAME_3 (requires ENABLE_LED_2) |
CONFIG_ENABLE_LED_4=y | Enables slot 4 → defines LED_NAME_4 (requires ENABLE_LED_3) |
CONFIG_LED_NAME_K="…" | Optional rename for slot K (string device name) |
Slots are 1–4 in SDK Kconfig and chained — enable every flag from
ENABLE_LED_2 up to the slot you need. Enable one flag per extra device (board + usr_board), not a single total count.
.tuyaopen/board-context.md and board source — count N = LEDs already registered by BSP.CONFIG_ENABLE_LED_<N+1>=y (add every chained flag up to it; max slot 4).tos.py clean -f && tos.py build — verify .build/include/tuya_kconfig.h has #define ENABLE_LED_<N+1> 1 and the LED_NAME_<N+1> macro you need.usr_board/ — register each extra on LED_NAME_(N+1), LED_NAME_(N+2), … (do not reuse board slots).usr_board into CMakeLists.txt` (see usr-board skill).tal_sw_timer_init() before any tdl_led_open().board_register_hardware() then usr_register_hardware().#if defined(LED_NAME_N) + tdl_led_find_dev(LED_NAME_N) for the same slot(s).| Symptom | Likely cause | Fix |
|---|---|---|
LED 'led2' not found | Slot flag not enabled or wrong slot name | Enable CONFIG_ENABLE_LED_<N+1>=y; register/find matching LED_NAME_N |
| Log shows compile-time path skipped | Stale tuya_kconfig.h or slot flag not enabled | tos.py clean -f && tos.py build; grep tuya_kconfig.h for ENABLE_LED_N |
| Onboard LED works, external does not | Wrong GPIO, polarity, or pin conflict | Check wiring / polarity; pick free GPIO |
| Crash on first flash/blink | tal_sw_timer_init() not called | Call before tdl_led_open() |
| Registered name collides with board | Used LED_NAME or a slot BSP already owns | Count board LEDs; use next free LED_NAME_N |
LED_NAME_4 not defined though ENABLE_LED_4=y | Chain broken — ENABLE_LED_2/_3 not enabled | Enable every flag from slot 2 up to your slot (contiguous) |
SDK: examples/peripherals/led/ (onboard LED only, single LED_NAME).