Before starting coding
At first sight the logic is very simple: just put the feet in the right place and at the right time.
But working with so many LED pins and so many different timings and error checks turned out not as easy as we hoped. Also the code had to be highly parametric, in order to be able to track down the timing variables and to improve the interaction by tweaking them.
Tabs organization
We started the coding by dividing the program into several tabs, which makes it much easier to find the things that need to be edited inside a quite long code.
- The first tab shows the version of the program, the authors and some notes.
- In the pins tab we assigned all the pins to arrays divided by typology. We divided them in step pins, LED pins, error pins, motor pins and output pins. Each of them an array that stores inside itself all the pin addresses of that kind.
- In the global_variables tab we declared all the global variables organized by logical purpose inside the code; for example we grouped together all the timing variables to change from here all the timing and delays and tweak the interaction without even looking at the logical part, and a boolean “debug” variable that enables or disables all the serial outputs used to follow variable values in the serial monitor.
- In the setup tab we initialized every pin in the arrays (input or output, HIGH or LOW) depending on its purpose; we also initialized the serial output for debug.
- The loop tab hosts all the logical part of the program.
Main structure: switch case and millis() timers
To organize it in a clear way we implemented a switch case function structure which allowed us to define the interaction according to the different states of the installation: standby, play and output. A simple line of code allows us to move to the next state case if a given condition is satisfied.
For example, when the user is on both the two initial footprints, the program switches to mode 2, and the game starts.
The code devoted to the control of LEDs has been implemented using a relative time control based on the millis() function; which allowed us to keep all the condition control to run during the switching on and off operations. Normally for this kind of operations the delay() function is used but, this it has a big limitation that made it incompatible with our purpose: it pauses the execution of the entire code.
So we found out to use a millis() based time control cycle in order to get the same output without the delay. We set an new interval every cycle, so we can execute operations with different timing while keeping the rest of the code running continuously. This technique also allowed us to make the program to wait a certain amount of time before and after doing an action, as well as defining the action length.
In the game mode (case 2) we keep track of the active step by storing its LED pin number into the stepActive variable. The code works parametrically so all the interactions implemented in the code pass from one step the next once we update the stepActive value. The stepState variable stores the status (on or off) of the stepActive LED pin.
This independent timing system was particularly useful in various tasks:
- In the standby mode to light all the steps LEDs in a given order
- During the game mode to wait 1000 milliseconds before lighting the next step and for the error controls for users who move too fast or too slow.
- To allow the implementation of an automatic reset after a given time (if a user leaves in the middle of a game)
- To implement all the time-based error detectors
Error counting
The user starts the game with 1000 health points. The error system is a function that subtracts 1 point for every cycle in which the error condition is satisfied. So the error system basically counts the overall time in which the user was in a wrong position.
Time-based errors
Every active step passes trough 3 states: once activated it starts by lighting up for a certain amount of time, waiting to be pressed (stepCase 1). If not pressed in that time it automatically switches to a slow blink (stepCase 2) that warns the user. After a certain amount of blinks the active step starts to blink fast and starts lowering the health value (stepCase 3).
So, to summarize, the stepActive stores the LED pin address of the activeStep, stepState his condition (on or off) and stepCase its 3 possible behaviours, which are:
- steady (the right time in order to place the feet)
- blinking (to alert the user and to speed up)
- blinking fast (to show the user and anyone else around that it is an error)
Position based errors
Apart from time-based errors, whenever the user puts his feet on the wrong place, it will be an error:
- Wrong step (too far ahead or too far behind the active one)
- Lateral platforms stepped on
At any given time only 3 steps are allowed to be pressed: the two where the user is, plus the lighted one, which is always ahead of the previous two.
Column: motorized light output
Once the game starts, the column health meter switches on and the servo motor reaches its maximum height (about 73 degrees). The digital servo motor accepts a value for the position between 0 and 360, so we had to convert a 1000 to 0 scale to a 73 to 0 scale. Out first attempt divided the health value by 1.3 but it was not the best solution because the output values followed parabolic curve: what we wanted was a linear range. So we used the map() function to convert proportionally the error value into the angle value linearly.
Telling Flash to play the proverbs
When the user is on the last step, the program switches into the third case. Here, in order to enable the connection between Arduino and Flash, we used another Arduino board. The Arduino Mega is responsible for all the interaction inputs and light outputs, while the Arduino 2009 receives a signal from the Mega and then translate it into a serial signal to communicate with the computer, where a server translates the serial to TCP signal in order to allow the data to arrive at a Flash animation. The signal sends 2 variables, one for the activation state (0 or 1) and one for the proverb number (1 to 7): Flash parses the 2 values and plays the given proverb, then returns to a standby mode.
If you ask if it was necessary to use a second Arduino just to send the serial signal, we will probably say no. All can be done by the Arduino Mega; nonetheless using 2 controllers allowed us to develop the 2 parts individually and test the proverb output at home.
The 2 Arduinos are connected together with 2 cables: one connects the 2 grounds, the other goes from a digital output on the first to a digital input on the second (if a digital pin is input or output can be assigned by the pinMode function). According to the setup of the second Arduino we used a digital pin input to intercept the signal from the first one. When the pin becomes HIGH in the first one, the input pin on the second detects it and send the serial data through the USB interface to the computer.
Gobbo output
In case 3 of the code there’s also a light output on the Gobbo statue. If the health is more than 500 the output will be “good”; if not, “bad”.
- The “good” output is the proverb plus a green light on the Gobbo. This is achieved by switching to HI the green light pin and the second Arduino connection pin.
- The “bad” output is a red light on the Gobbo. This is achieved by switching to HI the red light pin.
Then, after 7 seconds, all the variables and pins are set to the initial state, the case switches to 1 (standby mode) and the installation is ready for a new run.
Equilibria | Context | How it works | Prototyping | Credits | Downloads