What do you think of when you hear the name Laravel? Laravel 4 powers an increasing number of websites, but that’s not the only place it can be used. In this tutorial we’re going to use it to control embedded systems.
This tutorial demonstrates the use of an Arduino and a web cam. There are other smaller parts, but these are the main ones. The Arduino needs firmata installed (explained later) and the webcam needs to be compatible with Linux and Motion (installed later).
We will go over using things like LEDs and resisters, but none of those are particularly important. We will also go over using servos and these are important. You can find the bracket I use at: https://www.sparkfun.com/products/10335 and the servos I use at: https://www.sparkfun.com/products/9065. You can get these parts, and an Arduino Uno, for less than $55.
We’re developing a Laravel 4 application which has lots of server-side aspects; but there’s also an interactive interface. There be scripts!
For this; we’re using Bootstrap and jQuery. Download Bootstrap at: http://getbootstrap.com/ and unpack it into your public folder. Where you put the individual files makes little difference, but I have put the scripts in public/js, the stylesheets in public/css and the fonts in public/fonts. Where you see those paths in my source code; you should substitute them with your own.
Next up, download jQuery at: http://jquery.com/download/ and unpack it into your public folder.
For the server-side portion of dependencies, we need to download a library called Ratchet. I’ll explain it shortly, but in the meantime we need to add it to our composer.json file:
1
"require"
:
{
2
"laravel/framework"
:
"4.0.*"
,
3
"cboden/Ratchet"
:
"0.3.*"
4
},
Follow that up with:
1
composer update
We’ll now have access to the Ratchet library for client-server communication, Bootstrap for styling the interface and jQuery for connecting these two things together.
I love all things Arduino and Raspberry Pi. When I’m not programming; I’m trying to build things (in the real world) which can interact with my programming. Don’t let that fool you though: I am not an expert in electronics. There are bound to be better ways to do all of this stuff.
If you can’t get a specific program installed because apt or permissions or network settings or aliens; I cannot help you. I have neither the time nor the experience to guide you through the many potential problems. Hit me up on Twitter and, if I have not yet already done so, I will connect you to a forum where you can discuss this with other (smarter) people.
The first step to creating an interface is returning static HTML for a web request. We handle this by creating a route, a controller and a view:
1
<?
php
2
3
Route
::
get
(
"/"
,
[
4
"as"
=>
"index/index"
,
5
"uses"
=>
"IndexController@indexAction"
6
]);
1
<?
php
2
3
class
IndexController
4
extends
BaseController
5
{
6
public
function
indexAction
()
7
{
8
return
View
::
make
(
"index/index"
);
9
}
10
}
1
<!DOCTYPE html>
2
<html
lang=
"en"
>
3
<head>
4
<meta
charset=
"UTF-8"
/>
5
<title>
Laravel 4 Embedded Systems</title>
6
<link
rel=
"stylesheet"
href=
"/css/bootstrap.3.0.0.css"
/>
7
<link
rel=
"stylesheet"
href=
"/css/bootstrap.theme.3.0.0.css"
/>
8
<link
rel=
"stylesheet"
href=
"/css/shared.css"
/>
9
</head>
10
<body>
11
<div
class=
"container"
>
12
<div
class=
"row"
>
13
<div
class=
"col-md-12"
>
14
<h1>
Laravel 4 Embedded Systems</h1>
15
<input
type=
"number"
max=
"255"
min=
"0"
step=
"5"
name=
"led"
/>
16
<input
type=
"number"
max=
"255"
min=
"0"
readonly
name=
"sensor"
/>
17
<input
type=
"number"
max=
"180"
min=
"0"
step=
"10"
name=
"x"
/>
18
<input
type=
"number"
max=
"180"
min=
"0"
step=
"10"
name=
"y"
/>
19
</div>
20
</div>
21
</div>
22
<script
src=
"/js/jquery.1.9.1.js"
></script>
23
<script
src=
"/js/shared.js"
></script>
24
</body>
25
</html>
If you’ve created all of these files (and configured your web server to serve the Laravel application) then you should see a set of controls in your browser, when you visit /. Before we move on; let’s just quickly set up a socket server for this interface to talk to the PHP server with….
We’ve covered this topic before; so I won’t go into too much detail. We’re going to set up a Ratchet socket server, to pass messages back and forth between the interface and the PHP server.
1
try
{
2
if
(
!
WebSocket
)
{
3
console
.
log
(
"no websocket support"
);
4
}
else
{
5
6
var
socket
=
new
WebSocket
(
"ws://127.0.0.1:8081/"
);
7
8
socket
.
addEventListener
(
"open"
,
function
(
e
)
{
9
console
.
log
(
"open: "
,
e
);
10
});
11
12
socket
.
addEventListener
(
"error"
,
function
(
e
)
{
13
// console.log("error: ", e);
14
});
15
16
socket
.
addEventListener
(
"message"
,
function
(
e
)
{
17
var
data
=
JSON
.
parse
(
e
.
data
);
18
console
.
log
(
"message: "
,
data
);
19
});
20
21
// console.log("socket:", socket);
22
23
window
.
socket
=
socket
;
24
25
}
26
}
catch
(
e
)
{
27
// console.log("exception: " + e);
28
}
1
<?
php
2
3
namespace
Formativ\Embedded
;
4
5
use
Evenement\EventEmitter
;
6
use
Illuminate\Support\ServiceProvider
;
7
8
class
EmbeddedServiceProvider
9
extends
ServiceProvider
10
{
11
protected
$defer
=
true
;
12
13
public
function
register
()
14
{
15
$this
->
app
->
bind
(
"formativ.embedded.emitter"
,
function
()
16
{
17
return
new
EventEmitter
();
18
});
19
20
$this
->
app
->
bind
(
"formativ.embedded.command.serve"
,
function
()
21
{
22
return
new
Command\Serve
(
23
$this
->
app
->
make
(
"formativ.embedded.socket"
)
24
);
25
});
26
27
$this
->
app
->
bind
(
"formativ.embedded.socket"
,
function
()
28
{
29
return
new
Socket
(
30
$this
->
app
->
make
(
"formativ.embedded.emitter"
)
31
);
32
});
33
34
$this
->
commands
(
35
"formativ.embedded.command.serve"
36
);
37
}
38
39
public
function
provides
()
40
{
41
return
[
42
"formativ.embedded.emitter"
,
43
"formativ.embedded.command.serve"
,
44
"formativ.embedded.socket"
45
];
46
}
47
}
1
<?
php
2
3
namespace
Formativ\Embedded
;
4
5
use
Evenement\EventEmitterInterface
;
6
use
Ratchet\MessageComponentInterface
;
7
8
interface
SocketInterface
9
extends
MessageComponentInterface
10
{
11
public
function
getEmitter
();
12
public
function
setEmitter
(
EventEmitterInterface
$emitter
);
13
}
1
<?
php
2
3
namespace
Formativ\Embedded
;
4
5
use
Evenement\EventEmitterInterface
as
Emitter
;
6
use
Exception
;
7
use
Ratchet\ConnectionInterface
as
Connection
;
8
9
class
Socket
10
implements
SocketInterface
11
{
12
protected
$emitter
;
13
14
protected
$connection
;
15
16
public
function
getEmitter
()
17
{
18
return
$this
->
emitter
;
19
}
20
21
public
function
setEmitter
(
Emitter
$emitter
)
22
{
23
$this
->
emitter
=
$emitter
;
24
}
25
26
public
function
__construct
(
Emitter
$emitter
)
27
{
28
$this
->
emitter
=
$emitter
;
29
}
30
31
public
function
onOpen
(
Connection
$connection
)
32
{
33
$this
->
connection
=
$connection
;
34
$this
->
emitter
->
emit
(
"open"
);
35
}
36
37
public
function
onMessage
(
Connection
$connection
,
$message
)
38
{
39
$this
->
emitter
->
emit
(
"message"
,
[
$message
]);
40
}
41
42
public
function
onClose
(
Connection
$connection
)
43
{
44
$this
->
connection
=
null
;
45
}
46
47
public
function
onError
(
Connection
$connection
,
Exception
$e
)
48
{
49
$this
->
emitter
->
emit
(
"error"
,
[
$e
]);
50
}
51
52
public
function
send
(
$message
)
53
{
54
if
(
$this
->
connection
)
55
{
56
$this
->
connection
->
send
(
$message
);
57
}
58
}
59
}
1
<?
php
2
3
namespace
Formativ\Embedded\Command
;
4
5
use
Formativ\Embedded\SocketInterface
;
6
use
Illuminate\Console\Command
;
7
use
Ratchet\ConnectionInterface
;
8
use
Ratchet\Http\HttpServer
;
9
use
Ratchet\Server\IoServer
;
10
use
Ratchet\WebSocket\WsServer
;
11
use
Symfony\Component\Console\Input\InputOption
;
12
use
Symfony\Component\Console\Input\InputArgument
;
13
14
class
Serve
15
extends
Command
16
{
17
protected
$name
=
"embedded:serve"
;
18
19
protected
$description
=
"Creates a firmata socket server."
;
20
21
public
function
__construct
(
SocketInterface
$socket
)
22
{
23
parent
::
__construct
();
24
25
$this
->
socket
=
$socket
;
26
27
$socket
->
getEmitter
()
->
on
(
"message"
,
function
(
$message
)
{
28
$this
->
info
(
"Message: "
.
$message
.
"."
);
29
});
30
31
$socket
->
getEmitter
()
->
on
(
"error"
,
function
(
$e
)
{
32
$this
->
line
(
"Exception: "
.
$e
->
getMessage
()
.
"."
);
33
});
34
}
35
36
public
function
fire
()
37
{
38
$port
=
(
integer
)
$this
->
option
(
"port"
);
39
40
$server
=
IoServer
::
factory
(
41
new
HttpServer
(
42
new
WsServer
(
43
$this
->
socket
44
)
45
),
46
$port
47
);
48
49
$this
->
info
(
"Listening on port "
.
$port
.
"."
);
50
$server
->
run
();
51
}
52
53
protected
function
getOptions
()
54
{
55
return
[
56
[
57
"port"
,
58
null
,
59
InputOption
::
VALUE_REQUIRED
,
60
"Port to listen on."
,
61
8081
62
]
63
];
64
}
65
}
These four files create a means with which we can start a socket server, listen for messages and respond to them. The shared.js file connects to this server and allows us to send and receive these messages. It’s simpler than last time; but sufficient for our needs!
Real-time communication is typically done through a software layer called Firmata. There are Firmata libraries for many programming languages, though PHP is not one of them. While there is one library listed on the official Firmata website, I could not get it to work.
So we’re going to create our own simple Firmata-like protocol. I managed to put one together that handles analog reads, analog writes and servo control. It does this by parsing strings received over serial connection. This is what it looks like:
1
#include <Servo.h>
2
3
String
buffer
=
""
;
4
String
parts
[
3
];
5
Servo
servos
[
13
];
6
int
index
=
0
;
7
8
void
setup
()
9
{
10
Serial
.
begin
(
9600
);
11
buffer
.
reserve
(
200
);
12
}
13
14
void
loop
()
15
{
16
17
}
18
19
void
handle
()
20
{
21
int
pin
=
parts
[
1
].
toInt
();
22
int
value
=
parts
[
2
].
toInt
();
23
24
if
(
parts
[
0
]
==
"pinMode"
)
25
{
26
if
(
parts
[
2
]
==
"output"
)
27
{
28
pinMode
(
pin
,
OUTPUT
);
29
}
30
31
if
(
parts
[
2
]
==
"servo"
)
32
{
33
servos
[
pin
].
attach
(
pin
);
34
}
35
}
36
37
if
(
parts
[
0
]
==
"digitalWrite"
)
38
{
39
if
(
parts
[
2
]
==
"high"
)
40
{
41
digitalWrite
(
pin
,
HIGH
);
42
}
43
else
44
{
45
digitalWrite
(
pin
,
LOW
);
46
}
47
}
48
49
if
(
parts
[
0
]
==
"analogWrite"
)
50
{
51
analogWrite
(
pin
,
value
);
52
}
53
54
if
(
parts
[
0
]
==
"servoWrite"
)
55
{
56
servos
[
pin
].
write
(
value
);
57
}
58
59
if
(
parts
[
0
]
==
"analogRead"
)
60
{
61
value
=
analogRead
(
pin
);
62
}
63
64
Serial
.
print
(
parts
[
0
]
+
","
+
parts
[
1
]
+
","
+
value
+
".
\n
"
);
65
}
66
67
void
serialEvent
()
68
{
69
while
(
Serial
.
available
())
70
{
71
char
in
=
(
char
)
Serial
.
read
();
72
73
if
(
in
==
'.'
||
in
==
','
)
74
{
75
parts
[
index
]
=
String
(
buffer
);
76
buffer
=
""
;
77
78
index
++
;
79
80
if
(
index
>
2
)
81
{
82
index
=
0
;
83
}
84
}
85
else
86
{
87
buffer
+=
in
;
88
}
89
90
if
(
in
==
'.'
)
91
{
92
index
=
0
;
93
buffer
=
""
;
94
handle
();
95
}
96
}
97
}
The Arduino sketch is only one side of the puzzle. We also need to modify our socket server to communicate with the Arduino:
1
protected
$device
;
2
3
public
function
__construct
(
SocketInterface
$socket
)
4
{
5
parent
::
__construct
();
6
7
$this
->
socket
=
$socket
;
8
9
$socket
->
getEmitter
()
->
on
(
"message"
,
function
(
$message
)
10
{
11
fwrite
(
$this
->
device
,
$message
);
12
13
$data
=
trim
(
stream_get_contents
(
$this
->
device
));
14
$this
->
info
(
$data
);
15
16
$this
->
socket
->
send
(
$data
);
17
});
18
19
$socket
->
getEmitter
()
->
on
(
"error"
,
function
(
$e
)
20
{
21
$this
->
line
(
"exception: "
.
$e
->
getMessage
()
.
"."
);
22
});
23
}
24
25
public
function
fire
()
26
{
27
$this
->
device
=
fopen
(
$this
->
argument
(
"device"
),
"r+"
);
28
stream_set_blocking
(
$this
->
device
,
0
);
29
30
$port
=
(
integer
)
$this
->
option
(
"port"
);
31
32
$server
=
IoServer
::
factory
(
33
new
HttpServer
(
34
new
WsServer
(
35
$this
->
socket
36
)
37
),
38
$port
39
);
40
41
$this
->
info
(
"Listening on port "
.
$port
.
"."
);
42
$server
->
run
();
43
}
44
45
protected
function
getArguments
()
46
{
47
return
[
48
[
49
"device"
,
50
InputArgument
::
REQUIRED
,
51
"Device to use."
52
]
53
];
54
}
55
56
public
function
__destruct
()
57
{
58
if
(
is_resource
(
$this
->
device
))
{
59
fclose
(
$this
->
device
);
60
}
61
}
We’re using PHP file read/write methods to communicate with the Arduino. In our fire() method, we open a connection to the Arduino and store it in $this->device. We also set the stream to non-blocking. This is so that read requests to the Arduino don’t wait until data is returned before allowing the rest of the PHP processing to take place.
In the __construct() method we also modify the message event listener to write the incoming message (from the browser) to the Arduino. We read any info the Arduino has and send it back to the socket. This effectively creates a bridge between the Arduino and the socket.
We’ve also added two additional methods. The getArguments() method stipulates a required device argument. We want the serve command to be able to use any device name, so this argument makes sense. We’ve also added a __destruct() method to close the connection to the Arduino.
Finally, we need to add some JavaScript to read and write from the Arduino:
1
try
{
2
if
(
!
WebSocket
)
{
3
console
.
log
(
"no websocket support"
);
4
}
else
{
5
6
var
socket
=
new
WebSocket
(
"ws://127.0.0.1:8081/"
);
7
var
sensor
=
$
(
"[name='sensor']"
);
8
var
led
=
$
(
"[name='led']"
);
9
var
x
=
$
(
"[name='x']"
);
10
var
y
=
$
(
"[name='y']"
);
11
12
socket
.
addEventListener
(
"open"
,
function
(
e
)
{
13
socket
.
send
(
"pinMode,6,output."
);
14
socket
.
send
(
"pinMode,3,servo."
);
15
socket
.
send
(
"pinMode,5,servo."
);
16
});
17
18
socket
.
addEventListener
(
"error"
,
function
(
e
)
{
19
// console.log("error: ", e);
20
});
21
22
socket
.
addEventListener
(
"message"
,
function
(
e
)
{
23
24
var
data
=
e
.
data
;
25
var
parts
=
data
.
split
(
","
);
26
27
if
(
parts
[
0
]
==
"analogRead"
)
{
28
sensor
.
val
(
parseInt
(
parts
[
2
],
10
));
29
}
30
31
});
32
33
window
.
socket
=
socket
;
34
35
led
.
on
(
"change"
,
function
()
{
36
socket
.
send
(
"analogWrite,6,"
+
led
.
val
()
+
"."
);
37
});
38
39
x
.
on
(
"change"
,
function
()
{
40
socket
.
send
(
"servoWrite,5,"
+
x
.
val
()
+
"."
);
41
});
42
43
y
.
on
(
"change"
,
function
()
{
44
socket
.
send
(
"analogWrite,3,"
+
y
.
val
()
+
"."
);
45
});
46
47
setInterval
(
function
()
{
48
socket
.
send
(
"analogRead,0,void."
);
49
},
1000
);
50
51
}
52
}
catch
(
e
)
{
53
// console.log("exception: " + e);
54
}
This script does a number of cool things. When it loads, we set the pinMode of the LED pin (6) and the two servo pins (3, 5) to the correct modes. We also attach a message event listener to receive and parse messages from the Arduino.
We then attach some event listeners for the input elements in the interface. These will respond to changes in value by sending signals to the Arduino to adjust its various pin values.
Finally, we set a timer to ping the Arduino with analogRead requests on the sensor pin. Thanks to the message event listener, the return values will be received and reflected in the sensor input field.
To connect to the Arduino, you should run the following command:
1
php artisan embedded:serve /dev/cu.usbmodem1411
On my system, the Arduino is reflected as /dev/cu.usbmodem1411 by this may differ on yours. For instance, my Ubuntu machine shows it as /dev/ttyACM0. The easiest way to find out what it is, is to unplug it, then run the following command:
1
ls /dev/tty*
Look over the list to familiarise yourself with the devices you see there. The plug the Arduino in, wait a couple seconds and run that command again. This time you should see a new addition to the list of devices. This is most likely your Arduino.
I usually like to include Vagrant configurations for running these projects. This time I found it confusing to try and communicate with an Arduino through the host machine. So instead I decided to try the server.php script bundled with Laravel applications. Turns out you can run the server with the following command:
1
php -S 127.0.0.1:8080 -t ./public server.php
Now you can go to http://127.0.0.1:8080 and see the interface we created earlier. You need to start the serve command successfully before this interface will work. If everything is running correctly, you should immediately start to see the sensor values being updated in the interface.
You can now adjust the LED input to control the brightness of the LED, and the x and y inputs to control the rotation of the servos.
Streaming video from a web cam can be tricky, so we’re going to take time-lapse photos instead…
The utility we’ll be using on OSX is called ImageSnap. We can install it with homebrew:
brew install imagesnap
Once that’s installed; we can periodically take photos with it by using a command resembling:
1
imagesnap -d "Microsoft LifeCam"
-w 3
The web cam I am using is Microsoft LifeCam but you can find the appropriate one for you with the command:
1
imagesnap -l
I also had to add some warm-up time, with the -w switch.
The utility we’ll be using on Ubuntu/Debian is called Streamer. We can install it with the command:
1
apt-get install -y streamer
You can take a photo with the web cam, using the following command:
1
streamer -o snapshot.jpeg
Depending on whether you are using OSX or Ubuntu/Debian; you will need to set up a shell script to periodically take photos. Save them to a web-accessible location and display them with the following changes:
1
<img
name=
"photo"
/>
1
setInterval
(
function
()
{
2
$
(
"[name='photo']"
).
attr
(
"src"
,
"[path to photo]"
);
3
},
1000
);