Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 72 additions & 2 deletions cmake/zephyrbt-from-behaviourtreecpp-xml.cmake
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Copyright (c) 2024-2025 O.S. Systems Software LTDA.
# Copyright (c) 2024-2025 Freedom Veiculos Eletricos
# Copyright (c) 2024-2026 O.S. Systems Software LTDA.
# Copyright (c) 2024-2026 Freedom Veiculos Eletricos
# SPDX-License-Identifier: Apache-2.0

include(CheckIncludeFile)
Expand All @@ -22,6 +22,7 @@ function(zephyrbt_define_from_behaviourtreecpp_xml
set(zephyrbt_inc_file "${output_inc}/${zephyrbt_name}.h")
set(zephyrbt_data_file "${output_src}/${zephyrbt_name}_data.c")
set(zephyrbt_stub_file "${output_src}/${zephyrbt_name}_stub.c")
set(zephyrbt_lua_file "${output_src}/${zephyrbt_name}_gen.lua")

file(GLOB_RECURSE zephyrbt_user_include ${CMAKE_CURRENT_SOURCE_DIR}/include/zephyrbt_user.h)
string(LENGTH "${zephyrbt_user_include}" zephyrbt_user_include_valid)
Expand All @@ -46,6 +47,7 @@ function(zephyrbt_define_from_behaviourtreecpp_xml
OUTPUT ${zephyrbt_inc_file}
${zephyrbt_data_file}
${zephyrbt_stub_file}
${zephyrbt_lua_file}
DEPENDS ${input_file}
COMMAND ${PYTHON_EXECUTABLE}
${ZEPHYR_ZEPHYRBT_MODULE_DIR}/scripts/generate-zephyrbt-from-behaviourtreecpp-xml
Expand All @@ -59,6 +61,35 @@ function(zephyrbt_define_from_behaviourtreecpp_xml
${zephyrbt_user_include_file}
)

# Embed the generated .lua file as a C string header
# using lua_zephyr's luaz_gen.py script.
if(CONFIG_ZEPHYR_BEHAVIOUR_TREE_LUA_CONDITIONS)
set(zephyrbt_lua_hdr
"${output_inc}/${zephyrbt_name}_gen_lua_script.h")
set(luaz_gen_script
"${ZEPHYR_LUA_MODULE_DIR}/scripts/luaz_gen.py")
set(luaz_template
"${ZEPHYR_LUA_MODULE_DIR}/templates/lua_template.h.in")

add_custom_command(
OUTPUT ${zephyrbt_lua_hdr}
DEPENDS ${zephyrbt_lua_file}
COMMAND ${PYTHON_EXECUTABLE} ${luaz_gen_script}
--mode source
--template ${luaz_template}
--output ${zephyrbt_lua_hdr}
--name "${zephyrbt_name}_gen"
${zephyrbt_lua_file}
COMMENT "Embedding ${zephyrbt_name}_gen.lua"
)

add_custom_target(${zephyrbt_target}_lua
ALL
DEPENDS ${zephyrbt_lua_hdr}
)
add_dependencies(${target} ${zephyrbt_target}_lua)
endif()

add_custom_target(${zephyrbt_target}
ALL
DEPENDS ${input_file} ${zephyrbt_user_include}
Expand All @@ -73,3 +104,42 @@ function(zephyrbt_define_from_behaviourtreecpp_xml
${zephyrbt_stub_file}
)
endfunction()


# zephyrbt_add_lua_file(target, lua_file)
#
# Embed a user .lua file as a C string header for loading
# as a strong override before the generated weak conditions.
# The header provides const char {name}_lua_script[].
function(zephyrbt_add_lua_file target lua_file)
cmake_path(GET lua_file FILENAME _fname)
cmake_path(REMOVE_EXTENSION _fname OUTPUT_VARIABLE _name)

set(_lua_src "${CMAKE_CURRENT_SOURCE_DIR}/${lua_file}")
set(_lua_hdr
"${CMAKE_CURRENT_BINARY_DIR}/include/${_name}_lua_script.h")
set(luaz_gen_script
"${ZEPHYR_LUA_MODULE_DIR}/scripts/luaz_gen.py")
set(luaz_template
"${ZEPHYR_LUA_MODULE_DIR}/templates/lua_template.h.in")

add_custom_command(
OUTPUT ${_lua_hdr}
DEPENDS ${_lua_src}
COMMAND ${PYTHON_EXECUTABLE} ${luaz_gen_script}
--mode source
--template ${luaz_template}
--output ${_lua_hdr}
--name "${_name}"
${_lua_src}
COMMENT "Embedding ${_name}.lua"
)

add_custom_target(${_name}_lua_header ALL
DEPENDS ${_lua_hdr})
add_dependencies(${target} ${_name}_lua_header)

target_include_directories(${target} PRIVATE
${CMAKE_CURRENT_BINARY_DIR}/include
)
endfunction()
71 changes: 69 additions & 2 deletions include/zephyr/zephyrbt/zephyrbt.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2024-2025 O.S. Systems Software LTDA.
* Copyright (c) 2024-2025 Freedom Veiculos Eletricos
* Copyright (c) 2024-2026 O.S. Systems Software LTDA.
* Copyright (c) 2024-2026 Freedom Veiculos Eletricos
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -10,6 +10,11 @@

#include <zephyr/kernel.h>

#ifdef CONFIG_ZEPHYR_BEHAVIOUR_TREE_LUA_CONDITIONS
#include <lua.h>
#include <lauxlib.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -72,6 +77,35 @@ struct zephyrbt_blackboard_item {
const enum zephyrbt_blackboard_item_type type;
};

#ifdef CONFIG_ZEPHYR_BEHAVIOUR_TREE_LUA_CONDITIONS
enum zephyrbt_condition_type {
ZEPHYRBT_COND_FAILURE_IF,
ZEPHYRBT_COND_SUCCESS_IF,
ZEPHYRBT_COND_SKIP_IF,
ZEPHYRBT_COND_WHILE,
ZEPHYRBT_COND_ON_HALTED,
ZEPHYRBT_COND_ON_FAILURE,
ZEPHYRBT_COND_ON_SUCCESS,
ZEPHYRBT_COND_POST,
ZEPHYRBT_COND_COUNT,
};

struct zephyrbt_node_conditions {
int refs[ZEPHYRBT_COND_COUNT];
};

struct zephyrbt_lua_cond_ref {
const int node_idx;
const int cond_type;
const char *func_name;
};

struct zephyrbt_bb_name_entry {
const char *name;
const int bb_idx;
};
#endif /* CONFIG_ZEPHYR_BEHAVIOUR_TREE_LUA_CONDITIONS */

struct zephyrbt_context {
#ifdef CONFIG_ZEPHYR_BEHAVIOUR_TREE_NODE_INFO
const char *name;
Expand All @@ -93,6 +127,18 @@ struct zephyrbt_context {
bool thread_yield;
#endif
#endif
#ifdef CONFIG_ZEPHYR_BEHAVIOUR_TREE_LUA_CONDITIONS
lua_State *lua;
int bb_ref;
struct zephyrbt_node_conditions *conditions;
const struct zephyrbt_lua_cond_ref *lua_cond_refs;
const int lua_cond_ref_count;
const struct zephyrbt_bb_name_entry *bb_names;
const int bb_names_count;
const char *lua_gen_script;
const char *const *lua_user_scripts;
int lua_user_script_count;
#endif
};

// clang-format off
Expand Down Expand Up @@ -129,6 +175,18 @@ struct zephyrbt_context {
.node = _nodes, \
.nodes = ARRAY_SIZE(_nodes), \
.blackboard = _blackboard, \
IF_ENABLED(CONFIG_ZEPHYR_BEHAVIOUR_TREE_LUA_CONDITIONS, ( \
.lua = NULL, \
.bb_ref = LUA_NOREF, \
.conditions = _name##_conditions, \
.lua_cond_refs = _name##_lua_cond_refs, \
.lua_cond_ref_count = ARRAY_SIZE(_name##_lua_cond_refs), \
.bb_names = _name##_bb_names, \
.bb_names_count = ARRAY_SIZE(_name##_bb_names), \
.lua_gen_script = _name##_gen_lua_script, \
.lua_user_scripts = NULL, \
.lua_user_script_count = 0, \
)) \
}

// clang-format on
Expand Down Expand Up @@ -215,6 +273,15 @@ enum zephyrbt_child_status zephyrbt_decorator_timeout_init(struct zephyrbt_conte
struct zephyrbt_node *self);
#endif

#ifdef CONFIG_ZEPHYR_BEHAVIOUR_TREE_LUA_CONDITIONS
int zephyrbt_lua_init(struct zephyrbt_context *ctx);
enum zephyrbt_child_status zephyrbt_lua_pre_check(struct zephyrbt_context *ctx,
struct zephyrbt_node *self);
void zephyrbt_lua_post_check(struct zephyrbt_context *ctx, struct zephyrbt_node *self,
enum zephyrbt_child_status status);
bool zephyrbt_lua_while_check(struct zephyrbt_context *ctx, struct zephyrbt_node *self);
#endif

#ifdef __cplusplus
}
#endif
Expand Down
32 changes: 32 additions & 0 deletions samples/subsys/zephyrbt/lua_conditions/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright (c) 2026 O.S. Systems Software LTDA.
# Copyright (c) 2026 Freedom Veiculos Eletricos
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
include(${ZEPHYR_ZEPHYRBT_MODULE_DIR}/cmake/zephyrbt-from-behaviourtreecpp-xml.cmake)

project(zephyrbt_lua_conditions)

zephyr_include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/include
)

target_sources(app PRIVATE
src/main.c
src/lua_conditions.c
)

if(NOT CONFIG_NATIVE_APPLICATION)
target_sources(app PRIVATE src/posix_stubs.c)
endif()

zephyrbt_define_from_behaviourtreecpp_xml(app
models/lua_conditions.xml
${CMAKE_BINARY_DIR}/include
${CMAKE_BINARY_DIR}/src
4096
0
yes
)
138 changes: 138 additions & 0 deletions samples/subsys/zephyrbt/lua_conditions/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
.. Copyright (c) 2026 O.S. Systems Software LTDA.
.. Copyright (c) 2026 Freedom Veiculos Eletricos
.. SPDX-License-Identifier: Apache-2.0
.. _zephyrbt_lua_conditions:

Zephyr Behaviour Tree - Lua Conditions
#######################################

Overview
********

This sample demonstrates pre/post conditions on behaviour tree nodes
using Lua scripts. Conditions are defined as XML attributes and
evaluated inline during tree evaluation.

The sample exercises these condition types:

- ``_skipIf`` — skip node when counter exceeds a threshold
- ``_successIf`` — short-circuit to SUCCESS on even counter values
- ``_failureIf`` — return FAILURE when counter reaches a specific value
- ``_onSuccess`` / ``_onFailure`` — set blackboard flags after tick
- ``_post`` — increment a counter after every tick

Each condition is generated as a weak Lua function in
``lua_conditions_gen.lua``. Users can override any function by providing
a ``.lua`` file loaded before the generated one (see `Overriding
Conditions`_ below).

Prerequisites
*************

This sample requires the ``lua_zephyr`` module. After cloning ZephyrBT,
fetch all dependencies and initialize the Lua submodule:

.. code-block:: console

west update
cd deps/modules/lib/lua_zephyr
git submodule update --init
cd -

The ``west update`` command fetches ``lua_zephyr`` and other west
projects. The ``git submodule update --init`` step inside ``lua_zephyr``
clones the Lua 5.5.0 core sources which are tracked as a git submodule.

Building and Running
********************

This application can be built and executed on ``native_sim/native/64``
as follows:

.. code-block:: console

west build -p -b native_sim/native/64 samples/subsys/zephyrbt/lua_conditions -t run

For embedded targets with sufficient RAM (e.g. ``mps2/an385``):

.. code-block:: console

west build -p -b mps2/an385 samples/subsys/zephyrbt/lua_conditions -t run

.. note::

The ``qemu_cortex_m3`` board has only 64 KB of RAM which is not
sufficient for Lua. Use ``native_sim/native/64`` or a board with at
least 256 KB of RAM.

Sample Output
=============

.. code-block:: console

*** Booting Zephyr OS build 26d4afe225d0 ***
I: increment: counter=1
I: check_value: ticked (odd counter)
I: guarded_action: executed
I: increment: counter=2
I: guarded_action: executed
I: increment: counter=3
I: check_value: ticked (odd counter)
I: guarded_action: executed
I: increment: counter=4
I: guarded_action: executed
I: guarded_action: executed

Observations:

- ``increment`` stops appearing after ``counter=4`` because ``_skipIf``
triggers (``bb.counter > 3``).
- ``check_value: ticked`` only appears for odd counters (1, 3). Even
values (2, 4) are short-circuited by ``_successIf``
(``bb.counter % 2 == 0``).
- ``guarded_action`` continues running because the counter stays at 4
and ``_failureIf`` checks ``bb.counter == 5``.

Generated Lua File
==================

The code generator produces ``lua_conditions_gen.lua`` from the XML
condition attributes. Each condition becomes a weak-guarded function:

.. code-block:: lua

if not inc_counter_skip_if then
function inc_counter_skip_if(bb)
return bb.counter > 3
end
end

if not inc_counter_post then
function inc_counter_post(bb)
bb.ticks = bb.ticks + 1
end
end

Functions receive the blackboard (``bb``) as an argument. Read values
with ``bb.name`` and write with ``bb.name = value``.

Overriding Conditions
=====================

To override a generated condition, create a ``.lua`` file with the same
function name and register it in ``CMakeLists.txt``:

.. code-block:: lua

-- src/my_overrides.lua
function inc_counter_skip_if(bb)
return bb.counter > 10
end

.. code-block:: cmake

zephyrbt_add_lua_file(app src/my_overrides.lua)

User files are loaded before the generated script. Since generated
functions use ``if not ... then`` guards, user definitions take
precedence.
11 changes: 11 additions & 0 deletions samples/subsys/zephyrbt/lua_conditions/include/zephyrbt_user.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright (c) 2026 O.S. Systems Software LTDA.
* Copyright (c) 2026 Freedom Veiculos Eletricos
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYRBT_USER_H_
#define ZEPHYRBT_USER_H_

#endif /* ZEPHYRBT_USER_H_ */
Loading