TuyaOpen Project & Config Management
SDK root: All paths and commands in this skill are relative to the TuyaOpen SDK root (
$OPEN_SDK_ROOTon Linux/macOS/PowerShell,%OPEN_SDK_ROOT%on Windows CMD). Activate the environment first — see skilltuyaopen/env-setup.
Docs: https://tuyaopen.ai/docs/tos-tools/tos-guide
Creating a New Project
All
tos.py newsubcommands are interactive (useinput()/ menu prompts). They require a TTY and cannot be used in non-interactive Agent/CI pipelines.
tos.py new project (interactive)
Creates a new application from a template in the current working directory.
cd apps/my_category # navigate to where you want the project
tos.py new project # defaults to base framework
tos.py new project --framework arduino # Arduino-style project
Flow:
- Prompts for project name (e.g.
my_app). - Copies template from
tools/app_template/<framework>/into<cwd>/<project_name>/. - Fails if the directory already exists.
Templates:
| Framework | Entry file | Entry point |
|---|---|---|
base |
src/tuya_app_main.c |
user_main() — on Linux runs as main(), on MCU spawns a thread via tuya_app_main() |
arduino |
src/tuya_app_main.cpp |
Arduino-style setup() / loop() |
Generated project structure:
my_app/
├── CMakeLists.txt # collects src/, include/, links against tuyaos
└── src/
└── tuya_app_main.c
After creation — next steps:
cd my_app- Select a config:
tos.py config choice(interactive), or manually createapp_default.config(see skilltuyaopen/buildfor Kconfig format). - Build:
tos.py build
A new project has no app_default.config — the build system will copy an empty template on first build, but you must configure a platform/board before a meaningful build succeeds.
tos.py new board (interactive)
Creates a new board BSP directory under boards/<platform>/.
Flow:
- Lists available platforms (T5AI, ESP32, LINUX, etc.) — select one.
- Prompts for new board name (e.g.
MY_CUSTOM_BOARD). - Creates
boards/<platform>/<board_name>/with template files (Kconfig, CMakeLists.txt, board_com_api.h, board source file). - Automatically registers the board in
boards/<platform>/Kconfigso it appears inconfig choice. - For ESP32, chip name defaults to
esp32s3; for other platforms, uses the platform name.
See skill tuyaopen/add-board for the full board adaptation guide.
Configuration Management
For detailed Kconfig editing guidance (dependency mechanisms, defconfig format, config pipeline), see skill tuyaopen/build.
Which config commands does this SDK have? — ask the SDK
tos.py config has two generations. Always ask the installed SDK what it supports; never infer it from a version number.
tos.py config -h # the list of subcommands IS the answer
If -h lists… |
Then |
|---|---|
only choice, menu, save |
older generation — hand-edit app_default.config + tos.py clean -f |
also set, get, list, diff |
newer generation — use them; also implies choice -l and save -n/-f |
Probe once, before planning the change, and commit to that branch. Do not run the new command and parse its failure.
If the environment isn't activated or you aren't inside a project directory (tos.py config requires both), check the source tree instead:
test -f "$OPEN_SDK_ROOT/tools/cli_command/util_kconfig.py" # bash
Test-Path "$env:OPEN_SDK_ROOT/tools/cli_command/util_kconfig.py" # PowerShell
util_kconfig.py ships with the new subcommands, so its presence tracks them exactly.
Do not gate on
tos.py version. It prints agit describestring such asv1.9.0-17-g13a1d0de— the tag is whatever release came before the checkout, so SDKs with and without these subcommands both report the same tag. Feature detection is the only reliable gate.
tos.py config choice (interactive)
tos.py config choice # list configs from project config/ or boards/
tos.py config choice -d # only show boards/ default configs (skip project config/)
tos.py config choice -l # print available config names and exit, no clean (newer SDK — probe first)
Selects a pre-verified config. Writes to app_default.config. Triggers a full clean first.
Config lookup priority:
- Project's own
config/directory (e.g.apps/tuya.ai/your_chat_bot/config/) boards/<platform>/config/global configs (shown when no project configs exist, or with-d)
Note:
-dis an option of thechoicesubcommand (not the global--debugflag).
tos.py config menu (interactive)
tos.py config menu
Opens a terminal-based Kconfig editor. Triggers a full clean first. Best for fine-tuning options with complex dependencies — the editor resolves select / depends on automatically. See skill tuyaopen/build for the Kconfig Dependency Guide.
tos.py config save
tos.py config save # interactive — prompts for a name
tos.py config save -n my_board # non-interactive (newer SDK — probe first)
tos.py config save -n my_board -f # overwrite an existing preset
Copies the current app_default.config to the project's config/ directory as a named preset. Useful after customizing with config menu or config set.
Where -n is supported (check tos.py config save -h): it skips the prompt, an existing file is an error unless -f is given, and running without -n outside a TTY fails with a clear message instead of hanging.
Non-Interactive Config (Agent / CI)
1. Switching to a whole pre-verified config — works on every SDK:
tos.py config choice -c TUYA_T5AI_EVB # from project config/ dir
tos.py config choice -d -c TUYA_T5AI_EVB # from boards/ default configs
This triggers a full clean, which is exactly what a board switch needs.
2. Changing individual options — depends on what tos.py config -h reported (see above).
set/get/list/diff present — use them. The CONFIG_ prefix is optional everywhere:
tos.py config get ENABLE_WIFI # one value
tos.py config get -a ENABLE_LIBLVGL # type, prompt, visibility, deps
tos.py config list -p MBEDTLS # filtered dump (-j for JSON)
tos.py config set ENABLE_LIBLVGL=y ENABLE_MBEDTLS_SSL_MAX_CONTENT_LEN=8192
tos.py config set -u ENABLE_LIBLVGL # revert to Kconfig default
tos.py config diff TUYA_T5AI_EVB # semantic diff vs current config
config set is dependency-aware and all-or-nothing: every token is validated before anything is written, so a failed batch writes nothing. It re-derives using.config and invalidates the generated build artifacts — no manual tos.py clean -f needed for an ordinary option change.
Full semantics, flags, and troubleshooting: references/CONFIG_CLI.md.
Not present — hand-edit app_default.config. See skill tuyaopen/build for format details and Kconfig dependency handling.
After hand-editing
app_default.config, runtos.py clean -fbefore rebuilding. Unlikeconfig choice/config menu/config set(which handle this automatically), a manual edit does not invalidate the build, so the stale.build/cache/using.configmay be reused and your changes ignored. Runtos.py clean -fthentos.py build.Hand-editing also bypasses kconfiglib:
choicesymbols are not made mutually exclusive and derived symbols (CONFIG_PLATFORM_CHOICE,CONFIG_CHIP_CHOICE) are not updated. Set exactly one platform and one board, and never set the derived symbols yourself.
Non-Interactive Project Creation (Agent / CI)
tos.py new is interactive and cannot be used in Agent/CI. Create the project manually by writing three files.
Required Directory Structure
<project_name>/
├── CMakeLists.txt
├── app_default.config
├── include/ # optional — create if you have shared headers
└── src/
└── tuya_app_main.c
The project can live under examples/ or apps/ — both are valid build locations.
Step 1: CMakeLists.txt
##
# @file CMakeLists.txt
# @brief
#/
set(APP_PATH ${CMAKE_CURRENT_LIST_DIR})
get_filename_component(APP_NAME ${APP_PATH} NAME)
aux_source_directory(${APP_PATH}/src APP_SRC)
set(APP_INC ${APP_PATH}/include)
########################################
# Target Configure
########################################
add_library(${EXAMPLE_LIB})
target_sources(${EXAMPLE_LIB}
PRIVATE
${APP_SRC}
)
target_include_directories(${EXAMPLE_LIB}
PRIVATE
${APP_INC}
)
This is the standard template from tools/app_template/base/CMakeLists.txt. It auto-collects all .c files under src/ — no need to list them individually.
Step 2: app_default.config
Select the target platform and board. Both a platform choice and a board choice are required.
Common platform + board pairs:
| Platform | Config lines | Target |
|---|---|---|
| LINUX / Ubuntu | CONFIG_BOARD_CHOICE_LINUX=yCONFIG_BOARD_CHOICE_UBUNTU=y |
Native x86/x64 ELF on Ubuntu/Debian |
| LINUX / Raspberry Pi | CONFIG_BOARD_CHOICE_LINUX=yCONFIG_BOARD_CHOICE_RASPBERRY_PI=y |
Native ARM ELF on RPi |
| T5AI | CONFIG_BOARD_CHOICE_T5AI=y |
Tuya T5AI MCU |
| ESP32 | CONFIG_BOARD_CHOICE_ESP32=y |
Espressif ESP32 series |
Example for LINUX (the only platform that can compile and run natively on the host):
CONFIG_BOARD_CHOICE_LINUX=y
CONFIG_BOARD_CHOICE_UBUNTU=y
Step 3: src/tuya_app_main.c
Entry source file must be named tuya_app_main.c (convention from the official template). It follows a dual-path entry pattern:
#include "tal_api.h"
#include "tkl_output.h"
static void user_main(void)
{
tal_log_init(TAL_LOG_LEVEL_DEBUG, 1024, (TAL_LOG_OUTPUT_CB)tkl_log_output);
PR_DEBUG("hello world\r\n");
while (1) {
tal_system_sleep(1000);
}
}
#if OPERATING_SYSTEM == SYSTEM_LINUX
void main(int argc, char *argv[])
{
user_main();
}
#else
static THREAD_HANDLE ty_app_thread = NULL;
static void tuya_app_thread(void *arg)
{
user_main();
tal_thread_delete(ty_app_thread);
ty_app_thread = NULL;
}
void tuya_app_main(void)
{
THREAD_CFG_T thrd_param = {0};
thrd_param.stackDepth = 1024 * 4;
thrd_param.priority = THREAD_PRIO_1;
thrd_param.thrdname = "tuya_app_main";
tal_thread_create_and_start(&ty_app_thread, NULL, NULL, tuya_app_thread, NULL, &thrd_param);
}
#endif
Key points:
user_main()contains all application logic- On LINUX:
main()callsuser_main()directly (native process) - On MCU:
tuya_app_main()spawns a thread that callsuser_main() OPERATING_SYSTEM == SYSTEM_LINUX(value 100) is set automatically by the LINUX platform Kconfig
Step 4: Build and Run
cd <project_dir>
mkdir -p .cache && touch .cache/.dont_prompt_update_platform
tos.py build
./dist/<project>_<version>/<project>_<version>.elf # LINUX only
Updating Dependencies
tos.py update
Switches each platform submodule to its pinned commit ($OPEN_SDK_ROOT/platform/platform_config.yaml). Run after git pull.
tos.py Command Reference
See references/TOS_COMMANDS.md. For the non-interactive config subcommands in depth, see references/CONFIG_CLI.md.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
tos.py new hangs |
Waiting for input() — interactive only |
Use in a TTY terminal; cannot be automated |
Project exists error on new |
Directory already exists | Choose a different name or delete the existing directory |
config menu arrow keys broken |
Windows terminal compat | Use h/j/k/l; or switch between cmd/powershell |
could not lock config file |
Stale ~/.gitconfig.lock |
rm ~/.gitconfig.lock |
No configs shown in config choice |
No config/ dir and no board configs for current platform |
Create app_default.config manually or check platform setup |
Build fails after tos.py new |
No config selected yet | Run tos.py config choice or create app_default.config |
Error: No such command 'set' |
This SDK does not have the non-interactive config subcommands | Probe with tos.py config -h first; hand-edit app_default.config, then tos.py clean -f |
config set fails with a dependency reason |
depends on / visibility blocks the symbol |
tos.py config get -a NAME; enable the parent, or set both in one config set |
config save hangs or aborts in CI |
Prompting for a name without a TTY | Pass -n NAME if config save -h lists it |
More config-specific troubleshooting: references/CONFIG_CLI.md.