Blinky application is a simple but self-explanatory example to understand how to represent a state machine, how to use time events, and how to analyze a state machine behaviour by means of the RKH framework. Moreover, since this example is built by CMake, it could be used as a starting point for building applications based on RKH framework using CMake. The Blinky application could be thought as the hello world program of a traditional programming language.
Despite RKH framework is written in C language, it could be used in a C++ application without much effort. This is mainly due to RKH framework was developed from ground up using OOP concepts. However, you have to keep in mind the following things if you want to use it in your C++ application:
- An active class must be derived from the class
RKH_SMA_T
of RKH. - Every state machine's action must be implemented as a C callback function, whose signature must comply with RKH requirements, but the body of these callbacks is written in C++ language.
The behavior of Blinky is defined by the following state diagram.
RKH is a flexible, efficient, highly portable, and freely available open-source state machine framework providing the infrastructure for quickly and safely developing reactive applications for real-time embedded systems.
RKH provides not only an unusual, efficient and straightforward method for implementing and executing state machines, but also the needed infrastructure to build reactive applications in embedded systems. It is composed of modules, procedures, and supporting tools; such as a method for implementing and executing flat state machines and statecharts, asynchronous messaging, cross-platform abstraction, run time tracing, time management, dynamic memory mechanism to deal with fragmentation, unit-test harness, plus others.
RKH allows developers to verify and validate a reactive application’s behaviour at runtime by means of the framework’s built-in tracer. It can utilize any traditional OS/RTOS or work without one. It also encourages the embedded software community to apply best principles and practices of software engineering for building flexible, maintainable and reusable software.
RKH is open source and licensed under the GNU v3.0. You can find the source code on GitHub.
If you want to learn more about the benefits of this flexible, efficient and highly portable state machine framework read on here.
CMake is an open-source, cross-platform family of tools designed to build, test and package software. There are several ways to install CMake, depending on your platform. Follow these instructions to do that.
RKH allows developers to verify and validate a reactive application's behaviour at runtime by means of its built-in tracer. In addition, RKH provides a very simple but powerful console application, called Trazer, to visualize the trace events' output in a legible manner. It can be downloaded and installed as follows.
- Download Trazer for Linux 64-bits from its official repository
- Copy downloaded file to a folder and extract it
- Change the directory to previous folder
- Check it is alright by executing
./trazer
It contains Blinky state machine model
It includes the application code written in C++ language. The most important files and directories are listed below:
- signal.h: defines signals as enumerated constants, which are used as state machine triggers.
- event.h: defines events types, which are derived from RKH framework types.
- priority.h: defines active object priorities as enumerated constants.
- blinky.h/.cpp: specifies and implements respectively the Blinky active object. Please correlate this implementation with the state diagram shown above.
- main.cpp: contains the
main()
function, which initializes both BSP and Blinky active object, then executes the RKH framework in order to orchestrates this reactive application. - rkhcfg.h: adapts and configures RKH in compile-time.
- CMakeLists.txt: to make the executable
- bsp.h: defines the BSP abstraction layer
- rkhfwk_adapter.h: defines utilities for using RKH framework in a C++ application. This file is going to be part of RKH framework in future releases.
- bsp_blinky.cpp: implements a BSP specific part according to this example.
Knowing that C++ is a powerful and versatile language, this project proposes several alternatives, listed below, to use RKH framework in a C++ application.
- Public version (blinky.public.h/blinky.public.cpp/bsp_blinky.non-singleton.cpp/main.non-singleton.cpp):
- Every C callback just calls a specific C++ method of the active class.
- It means that every C callback has its own C++ method, whose implementation represents the action's behavior of a state machine.
- C callbacks are private, static and non-member functions of the active class.
- Before accessing to active class members callbacks must perform a downcast to the active class.
- Using inheritance the behavior of state machine's actions could be dynamically changed.
- In order to test this alternative you have to rename files blinky.public.h/blinky.public.cpp/bsp_blinky.non-singleton.cpp/main.non-singleton.cpp to blinky.h/blanky.cpp/bsp_blinky.cpp/main.cpp respectively.
- Private version (blinky.private.h/blinky.private.cpp/bsp_blinky.non-singleton.cpp/main.non-singleton.cpp):
- C callbacks implement the action's behavior using C++ as usual.
- These callbacks are non-member functions, so they are declared as friends of the active class to access to its members.
- Before accessing to active class members callbacks must perform a downcast to the active class.
- In order to test this alternative you have to rename files blinky.public.h/blinky.public.cpp/bsp_blinky.non-singleton.cpp/main.non-singleton.cpp to blinky.h/blanky.cpp/bsp_blinky.cpp/main.cpp respectively.
- Protected version (blinky.protected.h/blinky.protected.cpp/bsp_blinky.non-singleton.cpp/main.non-singleton.cpp):
- Every C callback just calls a specific C++ method of the active class.
- It means that every C callback has its own C++ method, which implements the dynamic action's behavior.
- C callbacks are private and non-member functions of the active class.
- Having defined C++ methods as protected, C callbacks were declared as friends of the active class.
- Before accessing active class members callbacks must perform a downcast to the active class.
- Using inheritance the behavior of state machine's actions could be dynamically changed.
- In order to test this alternative you have to rename files blinky.pprotected.h/blinky.protected.cpp/bsp_blinky.non-singleton.cpp/main.non-singleton.cpp to blinky.h/blanky.cpp/bsp_blinky.cpp/main.cpp respectively.
- Lazy singleton version (blinky.lazy-singleton.h/blinky.lazy-singleton.cpp/*bsp_blinky.lazy-singleton.cpp_/main.lazy-singleton.cpp):
- This alternative is very similar to previous ones. The main difference is that the active class can only be instantiated once but providing a way for clients to access that class in such a way that the same single object is returned each time. This is commonly referred to as a singleton pattern, or a singleton class.
- In order to test this alternative you have to rename files blinky.lazy-singleton.h/blinky.lazy-singleton.cpp/ bsp_blinky.lazy-singleton.cpp/main.lazy-singleton.cpp to blinky.h/blinky.cpp/bsp_blinky.cpp/main.cpp respectively.
- Hidden Singleton version (blinky.closed-singleton.h/blinky.closed-singleton.cpp/bsp_blinky.closed-singleton.cpp/main.closed-singleton.cpp):
- This alternative is a kind of singleton class in which its specification is entirely hidden. It provides a constant, opaque and global pointer to access the only class instance.
- In order to test this alternative you have to rename files blinky.closed-singleton.h/blinky.closed-singleton.cpp/ bsp_blinky.closed-singleton.cpp/main.closed-singleton.cpp_ to blinky.h/blinky.cpp/bsp_blinky.cpp/main.cpp respectively.
It contains Git submodules almost exclusively.
- RKH: here is located the RKH framework's source code as a Git submodule.
- CMakeLists.txt: to make a static library from RKH's source code
Top level CMakeLists.txt. CMake is controlled by input files that by convention are called CMakeLists.txt, which lists both configuration commands as well as dependencies between source files and targets. A CMake-based build system is organized as a set of high-level logical targets. Each target corresponds to an executable or library, or is a custom target containing custom commands. In this case, this file calls the CMakeLists.txt in the sub-directories src and third-party to create the following:
- blinky: an executable from source code in src directory
- rkh: a static library from RKH framework in third-party/RKH directory
All temporary build and object files are located in this directory keeping the source tree clean.
Set the RKH environment defining an environment variable called RKH_BASE
that
sets the path to the RKH base directory. It will be used by the build system.
RKH_BASE
can explicitly be set by typing:
export RKH_BASE="path/to/rkh-examples/blinky.cmake.cpp/third-party/RKH"
These instructions are part of the classic CMake build procedure:
cd path/to/rkh-examples/blinky.cmake.cpp/build
cmake .. -DRKH_DEV_BUILD=ON -DRKH_PLATFORM="__LNXGNU__" -DGIT_SUBMODULE=ON
make
Alternatively, if you are using a modern CMake, you can instead do this:
cd path/to/rkh-examples/blinky.cmake.cpp
cmake -S . -DRKH_DEV_BUILD=ON -B build -DRKH_PLATFORM="__LNXGNU__" -DGIT_SUBMODULE=ON
cmake --build build
- Open a console, change the directory where you previously downloaded Trazer, and run it by executing the following command line:
./trazer -t 6602
- Open another console, and run the blinky application following these instructions:
cd path/to/rkh-examples/blinky.cmake.cpp/build
./src/blinky
The RKH environment is defined through the environment variable RKH_BASE
that
sets the path to the RKH base directory. It will be used by the build system.
RKH_BASE
can explicitly be set by typing:
export RKH_BASE="path/to/rkh-examples/blinky.cmake.cpp/third-party/RKH"
First of all, run CMake using the Eclipse generator "Eclipse CDT4 - Unix Makefiles".
cd path/to/rkh-examples/
mkdir build
cd build
cmake ../blinky.cmake.cpp -DRKH_DEV_BUILD=ON -DRKH_PLATFORM="__LNXGNU__" -DGIT_SUBMODULE=ON -G"Eclipse CDT4 - Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug
Then, import the previously generated project in Eclipse:
- Select File > Import... to bring up the Import wizard.
- Choose Existing Project into Workspace and click the Next button.
- Select the
path/to/rkh-examples/build
project directory. - Click the Finish button to import the selected project into the workspace.
- Right-click on project blinky-Debug@build in the Project Explorer
- Choose Build Project
- Open a console, change the directory where you previously downloaded Trazer, and run it by executing the following command line:
./trazer -t 6602
- Import CMake project in Eclipse CDT
- Right-click on project blinky-Debug@build in the Eclipse Project Explorer
- Choose Run As > Local C/C++ Application > blinky (with green bug label, located in src/blinky). The embedded Eclipse console shows up and the application starts
- Open a console, change the directory where you previously downloaded Trazer, and run it by executing the following command line:
./trazer -t 6602
- Import CMake project in Eclipse CDT
- Right-click on project blinky-Debug@build in the Eclipse Project Explorer
- Choose Debug As > Local C/C++ Application > blinky (with green bug label, located in src/blinky). You will now see the debug perspective with the Blinky application window open. The C/C++ editor repositions in the perspective.
While the application is running, you can validate and verify its behaviour through the trace events showed on the Trazer output. Each trace event includes a time stamp and additional information associated with it. A capture of Trazer output is shown below.
It shows the trace records when the state machine processes a after 2s
trigger. Since RKH can generate more than 100 different trace events
during its execution, its trace module allow you to filter one or more
of them in runtime, so you can choose the traces that you need.