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
17 changes: 17 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,23 @@ jobs:
target: esp32s3
- path: 'components/state_machine/example'
target: esp32
# this example GENERATES its state machine from Complex.json
# at configure time, and the esp-idf image has no node
command: >-
node_major=$(node -v 2>/dev/null | sed -E "s/^v([0-9]+).*/\1/");
if [ "${node_major:-0}" -lt 18 ]; then
apt-get update &&
apt-get install -y ca-certificates curl gnupg &&
install -d -m 0755 /etc/apt/keyrings &&
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key |
gpg --dearmor --yes -o /etc/apt/keyrings/nodesource.gpg &&
chmod a+r /etc/apt/keyrings/nodesource.gpg &&
printf "%s\n" "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" |
tee /etc/apt/sources.list.d/nodesource.list >/dev/null &&
apt-get update &&
apt-get install -y nodejs;
fi &&
IDF_COMPONENT_MANAGER=0 idf.py build
Comment thread
finger563 marked this conversation as resolved.
Comment thread
Copilot marked this conversation as resolved.
- path: 'components/stream_frame/example'
target: esp32
- path: 'components/sx126x/example'
Expand Down
52 changes: 52 additions & 0 deletions components/state_machine/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,58 @@ Note: This is a generic HFSM implementation - it should be used with generated
code or a manually written state machine, as it provides no functionality on its
own.

## Generating a state machine

`state_machine` is the runtime; the machines themselves are generated
from a model by [webgme-hfsm][repo]. You can model and generate one
entirely in the browser — no install, no server — with the
[**HFSM Playground**][playground], or from the command line:

Comment thread
finger563 marked this conversation as resolved.
```sh
npx -y -p webgme-hfsm@^1.8.0 hfsm-gen my_machine.json -o generated
```

(`-y` skips npx's install prompt for non-interactive/CI use; the `@^1.8.0`
pin matches the example's CMake so generation is reproducible.)

### Generating from your own CMake

This component provides `espp_generate_hfsm()`, so any component can turn a
model into C++ at configure time:

```cmake
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
espp_generate_hfsm(
MODEL "${CMAKE_CURRENT_LIST_DIR}/my_machine.json"
OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/hfsm"
SOURCES_VAR hfsm_srcs
INCLUDE_DIR_VAR hfsm_inc)
endif()

idf_component_register(SRCS "main.cpp" ${hfsm_srcs}
INCLUDE_DIRS "." ${hfsm_inc})
Comment on lines +42 to +43
```

The guard is needed because ESP-IDF does not load a component's
`project_include.cmake` during its early requirements pass — and that pass
does not read `SRCS` either, so the empty variables are what it expects.

It regenerates whenever the model changes, finds the generated file names
rather than making you name them, and leaves out the shared runtime this
component already provides (pass `WITH_SUPPORT` if you want the generator's
copies instead). `NAMESPACE` overrides the model's own.

Requires **node >= 18**; `npx` fetches the generator. Set
`-DESPP_HFSM_GEN_COMMAND="node;/path/to/webgme-hfsm/bin/hfsm-gen.js"` to use a
local checkout, which is also how to build offline.

The example uses exactly this, so its C++ is generated at build time from
[`Complex.json`](example/main/Complex.json) rather than checked in — see
[the example README](example/README.md).

[repo]: https://github.com/finger563/webgme-hfsm
[playground]: https://finger563.github.io/webgme-hfsm/

## Example

This example shows an example of running the below HFSM on an ESP32 in a
Expand Down
45 changes: 45 additions & 0 deletions components/state_machine/example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,51 @@ CLI to manually spawn events and trace the execution). For more information, see

![hfsm](https://user-images.githubusercontent.com/213467/230950083-d4d8a483-31a7-43ac-8822-b1e28d552984.png)

## The state machine is generated, not checked in

The HFSM's C++ is generated from [`main/Complex.json`](main/Complex.json)
every time the example is configured. The model is the source; the C++ is
a build product, like an object file.

The generation is one call to `espp_generate_hfsm()`, provided by the
`state_machine` component — see its README to use it from your own
component.

This needs **node (>= 18)** on your PATH — nothing else, and nothing to
install by hand. `npx` fetches the generator on demand:

```
idf.py build # generates main/Complex.json -> build/.../hfsm/, then builds
```

Editing the model regenerates on the next build; you do not need to clean.

Only the machine itself is generated. The shared runtime it builds on
— `state_base.hpp`, the history states, `magic_enum.hpp` — comes from
this component, via the generator's `--no-support` flag; espp's copies
are the ones the rest of the codebase is built against.

To edit the machine, open it in the **[HFSM Playground][playground]** — a
browser-based editor and code generator, no install and no server. Load
`main/Complex.json` with **Open file…**, or jump straight to this
example's machine:

[**Open this example's HFSM in the playground**][this-model]

Save the edited model back over `main/Complex.json` and rebuild.

If you are working on the generator itself, point the build at your
checkout instead of npx:

```
idf.py -DESPP_HFSM_GEN_COMMAND="node;/path/to/webgme-hfsm/bin/hfsm-gen.js" build
```

The same flag is the way to build without network access.

[playground]: https://finger563.github.io/webgme-hfsm/
[this-model]: https://finger563.github.io/webgme-hfsm/?example=Complex&view=diagram

## How to use example

### Build and Flash
Expand Down
21 changes: 19 additions & 2 deletions components/state_machine/example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,19 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS ".")
# The HFSM's C++ is GENERATED from Complex.json, not checked in: the
# model is the source, the C++ is a build product.
#
# espp_generate_hfsm comes from the state_machine component's
# project_include.cmake. ESP-IDF does not load those during its early
# requirements pass, so the call is guarded -- SRCS and INCLUDE_DIRS
# are not read in that pass either, and the empty variables below are
# what it expects to see.
if(NOT CMAKE_BUILD_EARLY_EXPANSION)
espp_generate_hfsm(
MODEL "${CMAKE_CURRENT_LIST_DIR}/Complex.json"
OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/hfsm"
SOURCES_VAR hfsm_srcs
INCLUDE_DIR_VAR hfsm_inc)
endif()

idf_component_register(
SRCS "hfsm_example.cpp" ${hfsm_srcs}
INCLUDE_DIRS "." ${hfsm_inc})
Loading
Loading