Skip to content

A state machine code generation tool suitable for bare metal, embedded and more.

License

Notifications You must be signed in to change notification settings

adamfk/StateSmith

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

StateSmith

StateSmith is a cross platform, free/open source tool for generating state machines. The generated code is human readable, has zero dependencies and is suitable for use with tiny bare metal microcontrollers, kernels, or servers. It avoids dynamic memory allocations for the safety or performance inclined.

StateSmith will one day generate code for multiple different languages, but for now, the focus is on good old C (or C++) with zero dependencies.

picture 5

What's new?

See main changelog or dev changelog.

Pinned issues and feature branches show what's in the works.

Demo

Here's a demo youtube video showing a Laser Tag menu implemented with StateSmith and running in an Arduino simulation.

picture 1

Quick Start 🚀

Want to jump right in and just try it!? See quick-start.md

You'll be controlling a console light bulb's brightness in no time!

See also the examples.

Need help? Suggestion? Brainstorm?

Feel free to open a github issue.

Or you can use the project's discussion space.

Why StateSmith?

I couldn't find a state machine code generator that was suitable for low level embedded application use, had an attractive license, and was enjoyable to use.

Many options were painfully slow to use. You couldn't just start designing/writing code. You had to spend a bunch of time creating object oriented mappings of every little thing in an awkward GUI.

One of the reasons why I like StateSmith so much is that you can just get started using it and write code how you want. You can even start with pseudo code and gradually shape it into real code. Even though StateSmith uses a GUI for drawing state machines, all of a state's functionality is written in text - no annoying awkward GUIs that slow you down.

Human Readable Code

To make the generated code as enjoyable to look at as possible, there are a fair number of customizations possible. You can easily create a custom name mangling for pretty much everything.

The default naming strategy looks like this:

// State machine constructor...
void LaserTagMenu1Sm_ctor(LaserTagMenu1Sm* self);

But, you can easily customize to output another common naming convention like this:

// State machine constructor...
void blinky1_printf_sm_ctor(blinky1_printf_sm* self);

Show me the code!

In the future, StateSmith will support numerous state machine code generators. You'll be able to choose from super fast, balanced, and tiny size. You'll also be able to implement your own. The current code generation strategy implements a balanced approach that will work well for various targets.

For the below diagram, here's the generated .h file and .c file.

picture 6

The above state machine isn't very interesting. It just transitions between one state and another state when the do event is dispatched to it.

A more interesting example: Laser Tag Menu! Pew pew! 🔫

There are a bunch of examples. Below is one of them.

picture 7

You can interact with the generated laser tag menu code using the wokwi simulation.

Here's the menu layout:

APP
├── HOME
│   ├── HOME1
│   ├── HOME2
│   └── HOME3
└── MAIN MENU
        ├── SELECT CLASS
        │   ├── ENGINEER
        │   ├── HEAVY
        │   ├── ARCHER
        │   ├── WIZARD
        │   └── SPY
        │── SHOW INFO
        │   ├── INFO 1
        │   ├── INFO 2
        │   └── INFO 3
        └── EAT BACK PRESSES
            └── <stuff to show event handling>

Here's the design diagram. To view inside collapsed states like MM_SELECT_CLASS, you'll need to open the diagram using the UI (see below).

User Interface

StateSmith doesn't actually provide a UI - it currently relies on the free to use, but closed source yEd editor.

picture 9

yEd is great because it is free to use (even for commercial use), cross platform, and meets our needs pretty well once you get used to it.

StateSmith parses the yEd xml file, builds some intermediate graph representations (which will allow user modification soon), then passes the graph to the code generator.

One major future goal is to create a draw.io vscode extension that we can use for the front end. StateSmith was written with this goal in mind so adding a different compiler front end will be relatively easy. The hard part is customizing draw.io to behave the way we need (entering collapsed groups). I did a quick proof of concept and it looks very doable, just needs time and hopefully an excited contributor that is good with web UIs :)

Why use yEd? I like that we just type in text. Some of the other strict UML State Machine tools I tried drove me crazy. It was just so slow to do anything.

How to use yEd for StateSmith

See ./docs/yed-training.md

Is StateSmith ready for use? 🧪

StateSmith is generating working code, and has decent test (188+) and behavior specification coverage. There are 33+ specification integration tests that read a diagram file, generate executable .c state machine code, then compile and execute that .c code in another process and ensure that the state machine behavior is exactly what was expected. This strong test base gives me confidence. It also allows us to refactor and optimize StateSmith without fear of accidentally breaking specified behavior.

picture 1

Every aspect of state behavior is printed during specification testing with the help of the TracingModder class which modifies the state machine design before the code generation step.

Dispatch event EV1
===================================================
State TEST6_S1: check behavior `1. EV1 / { count++; }`. Behavior running.
State TEST6_S1: check behavior `2. EV1 [count >= 2] TransitionTo(TEST6_S2)`. Behavior skipped.

Dispatch event EV1
===================================================
State TEST6_S1: check behavior `1. EV1 / { count++; }`. Behavior running.
State TEST6_S1: check behavior `2. EV1 [count >= 2] TransitionTo(TEST6_S2)`. Behavior running.
Exit TEST6_S1.
Transition action `` for TEST6_S1 to TEST6_S2.
Enter TEST6_S2.

Note that there isn't a stable API yet for user modification of state machine designs, but it is coming (super useful).

Release 0.5.4-alpha added a lot of helpful error messages for when the input design is incorrect in some way:

Failed parsing diagram edge
from: Statemachine{LaserTagMenu1Sm}.State{MENUS_GROUP}.State{MM_SELECT_CLASS}
to:   Statemachine{LaserTagMenu1Sm}.State{MENUS_GROUP}.State{CLASS_SAVED}
Edge label: `via exitt saved`
Reason(s): mismatched input 'exitt' expecting {'entry', 'exit'} at line 1 column 4. Offending symbol: `exitt`.
           extraneous input 'saved' expecting <EOF> at line 1 column 10. Offending symbol: `saved`.
Edge diagram id: n0::n3::e3

The main thing I'd like to caution at this point is that there will be some refactoring in the near future which may affect user code generation scripts. Breaking changes and migration steps will be documented in ./CHANGELOG.md however and should be pretty minimal.

Features

See ./docs/diagram-features.md

How To Run StateSmith

StateSmith is still in alpha development phase so please be patient :)

There will eventually be a standalone CLI tool that has no installation dependencies and works cross platform. See issue 10.

For now, we actually just run StateSmith as a regular C# project from the command line, vscode, or visual studio.

Tutorial

See quick-start.md

About

A state machine code generation tool suitable for bare metal, embedded and more.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • C# 74.5%
  • C 24.2%
  • ANTLR 1.2%
  • Shell 0.1%