The state machine design pattern is very powerful for easily controlling embedded systems. In this project, I've used the DIY Otto - in my case the Zowi by BQ - and programmed the little robot with YAKINDU Statechart Tools, a tool for creating state machines in a visual way and generating C/C++ code with it.
If you want to, you can 3D print your own Otto by following these instructions or you get a builder kit.
Serial.println("Hello Otto!")Otto is a 3D printable, Arduino based, open-source robot. In the video, you'll see a version developed by BQ. They call him Zowi, who's got the goal to bring technology to kids. BQ provides a full library on Github for the Zowi, which can also be used by Otto.
So, with this library, it's simply possible to program Otto, let him walk, dance, sing, swing and so on... He also can detect and avoid obstacles, play sounds and wake up by some noise. The API (Application Programming Interface) is quite simple and written in C++.
A simple application could be implemented like this:
#include <Arduino.h>
#include "Zowi.h"
Otto otto;
void setup() {
otto.init(2, 3, 4, 5);
}
void loop() {
if(otto.getDistance() < 100) {
otto.walk();
} else {
otto.turn();
}
}
Otto will be initialized and starts running forward, as long as possible. If he detects an obstacle, he will turn (left).
Define Otto's BehaviourHowever, the above example is too simple. He can do much more! Let's start defining his behaviour. For this project, I'm using Otto as a round counter. I'll shortly sum things up:
- Initialize Otto
- Let him walk in a circle three times
- Waiting for the next start by changing Otto's mood every five seconds
- Wake up by knocking on his head
- Display the current count on his mouth
- Walk back and forth until an obstacle has been removed
- Sing & Smile while waiting
These are already a bunch of specifications. To control Otto like this, you would need to write a lot of lines of code. As I mentioned before, I've implemented this solution with state machines. This makes it possible to specify the behaviour graphically and automatically generate code instead of writing it by hand.
Otto's Brain - The State MachineWith YAKINDU Statechart Tools it is possible to describe the behaviour in a visual way using state machines. The logical execution can be seen in the model below. It would be a waste of time to explain everything when it's already described self-explanatory as a model.
One thing left is the definition section. This is the textual part of the model where operations (functions), variables and events can be defined. They can be used within the model and will be generated as C++ code.
Wire things up! Serving The APIFrom now on I will call Otto by its original name Zowi, as I've done the complete implementation by using the BQ library.
One thing that must be first implemented are the operations, which are declared in the definition section:
operation init()
operation home()
operation walk(steps : integer, T: integer, dir : integer)
operation turn()
operation getDistance() : real
operation getNoise() : real
operation putMouth(mouth : integer)
operation sing(songName : integer)
They are redirecting between the API of the state machine, by implementing the interface and the API of the Zowi.
class ZowiCallbacks : public ZowiSCT::SCI_Zowi_OCB{
public:
ZowiCallbacks();
virtual ~ZowiCallbacks();
void init();
void home();
void walk(sc_integer steps, sc_integer T, sc_integer dir);
void turn();
sc_real getDistance();
sc_real getNoise();
void putMouth(sc_integer mouth);
void sing(sc_integer songName);
};
Zowi zowi;
ZowiCallbacks::ZowiCallbacks() {
}
ZowiCallbacks::~ZowiCallbacks() {
}
void ZowiCallbacks::init() {
zowi.init(PIN_YL, PIN_YR, PIN_RL, PIN_RR);
}
void ZowiCallbacks::home() {
zowi.home();
}
void ZowiCallbacks::walk(sc_integer steps, sc_integer T, sc_integer dir) {
zowi.walk(steps, T, dir);
}
void ZowiCallbacks::turn() {
zowi.turn();
}
sc_real ZowiCallbacks::getDistance() {
return zowi.getDistance();
}
sc_real ZowiCallbacks::getNoise() {
return zowi.getNoise();
}
void ZowiCallbacks::putMouth(sc_integer mouth) {
zowi.putMouth(mouth);
}
void ZowiCallbacks::sing(sc_integer songName) {
zowi.sing(songName);
}
The main.ino file consists of three parts:
- Defining the needed objects
- Initialization
- While loop
At first, the needed objects must be defined:
ZowiSCT* zowi_sct = new ZowiSCT();
ZowiCallbacks* zowi_callbacks = new ZowiCallbacks();
CPPTimerInterface* timer_sct = new CPPTimerInterface();
After this, everything must be initialized:
void setup() {
zowi_sct->setSCI_Zowi_OCB(zowi_callbacks);
zowi_sct->setTimer(timer_sct);
zowi_sct->init();
zowi_sct->enter();
}
At last, the state machine will be called periodically:
long current_time = 0;
long last_cycle_time = 0;
void loop() {
last_cycle_time = current_time;
current_time = millis();
timer_sct->updateActiveTimer(zowi_sct, current_time - last_cycle_time);
zowi_sct->runCycle();
}
Using time events in the state machine requires using a timer, which must be updated correctly.
That's it!
Get The Otto Example!Add the example to a running IDE with:
File -> New -> Example -> YAKINDU Statechart Examples -> Next -> Zowi (C++)
>> You can download the IDE here <<
You can start with a 30 days trial. Afterwards, you must get a license, which is free for non-commercial use!
Comments