TILT!Audio features a lua runtime 5.2 to control additional extensions like shakers, LED-lights and also servos.
To make this possible TILT!Audio firmware embeds a lua runtime (lua virtual machine) and loads a “init.lua” script from the sound directory.
The init.lua script already has access to all core lua language features and also the TILT!Audio bindings see lua reference. The binding allows access to the sound engine, communicate via i2c, logging messages and get information about the current installation.
Lua in its language ecosystem also offers modules and package managers that allows installing and managing modules that extend lua features. One of this package managers is luarocks.
Luarocks
Using luarocks as package manager to install a new module together with TILT!Audio is not so straight forward as you can read it on most websites, because the linux running on a TILT!Audio card is tailored down to a minimum to run as efficient as it should to produce sound and nothing else.
What is missing for example is a c compiler that most lua modules require as they come as a combination of lua code and c code.
Also luarocks requires a certain directory structure that is aware of the different lua versions and different places in the system where the lua runtime should look for “modules” (which is *.so and *.lua files).
The TILT!Audio start script does set the required environment variables to let lua runtime find modules, but still not in that way that a module installed by luarocks would expect.
Using a Arduino mini pro as extension to a TILT!Audio board, you can easily control a shaker motor via pwm (pulse width modulation).
First we need a motor driver module that is capable of driving the high current motor power. e.g. something like this:
The picture is more or less self-explaining the only thing that need to be connected to the Arduino mini pro is the GND / PWM input.
Now looking at the Arduino mini pro we use pin 5 as pwm output and to connect to the TILT!Audio board we use the i2c bus the is controlled by the SCL / SDC pins (together with GND / Vcc of course):
Be sure to choose a 5v type for Arduino mini pro as there are also available in 3.3v but the TILT!Audio connector for the i2c bus provides 5v. Also the 5v type runs on 16Mhz instead of only 8Mhz for 3.3v with is also good.
First thing we do is switch shaker on, off and set speed (within a limited range to not make to pinball machine jump):
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
intcurrentSpeed=1;
intmaxSpeed=20;
voidoff(){
analogWrite(PWM_PIN,0);
}
voidon(){
analogWrite(PWM_PIN,currentSpeed);
}
voidsetSpeed(intspeed){
if(speed>0&&speed<=maxSpeed){
currentSpeed=speed;
}
on();
}
Second we wire these functions to i2c control:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
voidreceiveEvent(intcount)
{
if(count==I2C_MSG_IN_SIZE)
{
bytecmd=Wire.read();
bytevalue=Wire.read();
switch(cmd){
case0x01:
on();
break;
case0x02:
off();
break;
case0x03:
setSpeed(value);
break;
default:
// unknown command
}
}
}
And additionally we can create “sequences”. A sequence would play back some schema of on and off or speed up and speed downs. The idea is to define a sequence as 3 integers: command byte, value, interval in milliseconds. We define these commands:
0x00: stop -> end of the sequence
0x01: switch on, value -> speed
0x02: switch off, value -> don’t matter
0x03: ramp up, value -> target speed at end of interval
We delegate playing those sequences to the Arduino and later just trigger it from the TILT!Audio board by sending some command to the i2c bus:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#define END 0x00
#define ON 0x01
#define OFF 0x02
#define RAMP 0x03
#define SETSPEED 0x04
#define PLAYSEQ 0x05
intsequences[]={ON,5,500,OFF,0,500,ON,5,500,END};
int*seqPtr=NULL;
unsignedlongnextAction=0;// timer marker for next action
floatrampInc;// speed incs for ramps in steps
intrampNo=0;// counts number of step in ramp phase
voidloop(){
unsignedlongnow=millis();
if(now>nextAction){
// ramp active
if(rampNo>0){
nextAction=now+50;
setSpeed(currentSpeed+rampInc);
rampNo--;
}else{
// read next command from sequence
intcmd=*seqPtr++;
if(cmd==END){
seqPtr=NULL;// end of sequence
nextAction=0;
}else{
intval=*seqPtr++;
intdelay=*seqPtr++;
nextAction=now+delay;
switch(cmd){
caseON:setSpeed(val);break;
caseOFF:off();break;
caseRAMP:
rampNo=delay/50;// inc every 50ms
nextAction=now+50;
rampInc=(val-currentSpeed)/(float)rampNo;
}
}
}
}
}
And again create a function that triggers the sequence playback and wire it to i2c control: