Code Notes - UCSD-E4E/Wolf-Tracker-2014 GitHub Wiki
Some important things to note is how the entire system is configured: what ports are used, file structures, scripts that startup, etc. The following topics are covered:
- Arduino Code
- Nginx (web server/web proxy) config
- NodeJS Installation Notes
- NodeJS Server
- mjpg_streamer
Nginx was installed onto the BeagleBone using apt-get, the config file for the project is as follows:
server {
listen 80 default_server;
listen [::]:80 default_server;
root /usr/share/nginx/html;
index index.html index.htm;
server_name localhost;
location /node_eats {
proxy_redirect off;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8124;
}
location / {
try_files $uri $uri/ =404;
}
}
The Nginx config file is located at /usr/share/ngix/sites-available/default
. The first part of the script:
listen 80 default_server;
listen [::]:80 default_server;
root /usr/share/nginx/html;
index index.html index.htm;
server_name localhost;
simply sets up an ordinary web server listening on port 80. The files that the web server shows up are located at /usr/share/nginx/html
. This means that if we go to the website over our connection we would be seeing the webpage located at /usr/share/nginx/html/index.html
which is the default webpage that Nginx starts with. Nginx is a great tool for web development and in this project in particular since AJAX data can be redirected to a server at a different subnet. Here:
location /node_eats {
proxy_redirect off;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:8124;
}
We are setting up a proxy to pass through any requests for /node_eats
to our local server listening on port 8124. The rest of the config file:
location / {
try_files $uri $uri/ =404;
}
just lets us access webpages normally.
NodeJS is a wonderful tool that can be installed on the BeagleBone without much effort. One of the difficulties found was internet connectivity issues when clone large files using git or even using wget. Before starting downloading sudo su
was run to properly configure node globally.
First, the pre-requisites were installed:
sudo apt-get install python
sudo apt-get install build-essential
To get around this NodeJS was downloaded locally and SCP'ed over to the BeagleBone to be properly configured and installed. NodeJS was downloaded using:
wget http://nodejs.org/dist/v0.10.5/node-v0.10.5.tar.gz
scp node-v0.10.5.tar.gz ubuntu@<address of BeagleBone>
After SCP'ing the the Node tarball to the BeagleBone, the file was untarred and configured:
tar xzvf node-v0.10.5.tar.gz
cd node-v0.10.5
./configure --without-snapshot
NodeJS was then compiled. It takes a long time to compile so go take a nap or watch some anime for a bit.
make
Lastly we check to make sure it is installed correctly. Run
./node -e 'console.log("It worked!");'
If this runs correctly then install it.
make install
NodeJS was used as the middle man communicator between the web interface front-end and the arudino microcontroller. The following script runs at boot scheduled by crontab along with the following bash script that enables the serial port to the Arduino on boot:
#!/bin/bash
#MiddleMan.sh
#Enable UART (Serial) Communication on Boot
#wait 5 seconds to make sure everything is started on the Beagle Bone
sleep (5)
#this line enables UART1 on the the Beagle Bone and sets up the device tree automagically
echo BB-UART1 > /sys/devices/bone_capemgr.*/slots
#This sets up the speed of the serial communication to the Arduino
stty -F /dev/ttyO1 57600
//serverWSerial.js
var http = require('http');
var fs = require('fs');
//set up the serial port
var SerialPort = require("serialport").SerialPort;
var serialport = new SerialPort("/dev/ttyO1", {
baudrate: 57600
});
//opens the serial port for us to use
serialport.on('open', function(){
});
function sendToArduino(data){
for(var i = 0; i < data.length; i++){
serialport.write(data.charAt(i));
}
serialport.write('\n');
}
//create the server
http.createServer(function(req,res){
if(req.method == 'POST'){
var body = '';
req.on('data',function(data){
body += data;
});
req.on('end',function(){
var OBJ = JSON.parse(body);
var stringData = "L:" + OBJ.leftStick.toFixed(2) + "|" +
"R:" + OBJ.rightStick.toFixed(2) + ";";
sendToArduino(stringData);
});
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('post recieved');
} else {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello Node.js\n');
}
}).listen(8124, "127.0.0.1");
console.log('Server running at http://127.0.0.1:8124/');
The first part of the code:
var http = require('http');
var fs = require('fs');
//set up the serial port
var SerialPort = require("serialport").SerialPort;
tells Node what modules are required to be installed and used. The modules used in this project were fs
, http
, and serialport
. The serialport
module was installed with the following using npm:
npm install serialport
The next part of the code sets up a new serial port with a baudrate of 57600 and opens it:
var serialport = new SerialPort("/dev/ttyO1", {
baudrate: 57600
});
//opens the serial port for us to use
serialport.on('open', function(){
});
The next part is a helper function which is describe later in this section. The helper function is followed by the nodeJS server code. The node server is setup with using the http.createServer()
function. createServer()
takes in a function that uses req
and res
(you could have them named request and response for greater readibility) where req
is the HTTP request that was sent to the server. The listen(<port>,<ip address>)
at the end of the createServer function specifies the port and IP the server will listen to. Here we are listening to port 8124 on our localhost.
http.createServer(function(req,res){
...
//code here
...
}).listen(8124, "127.0.0.1");
The server first checks if it is a POST request and proccesses the data accordingly. If a POST request was not sent, we still send a response back acknowledging that the server did recieve some kind of request.
if(req.method == 'POST'){
...
//code to send to arduino
...
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('post recieved');
} else {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello Node.js\n');
}
When a POST request is sent to the server the data is parsed. Sometimes when data is sent to the server it is not all sent at once and is sent incrementally which is the reason for the req.on('data', <function>);
.Here we add the string data that was sent to our temporary variable body
until we finish recieving the data which happens in the req.on('end',<function>)
.
var body = '';
req.on('data',function(data){
body += data;
});
req.on('end',function(){
var OBJ = JSON.parse(body);
var stringData = "L:" + OBJ.leftStick.toFixed(2) + "|" +
"R:" + OBJ.rightStick.toFixed(2) + ";";
sendToArduino(stringData);
});
The data is then parsed using JSON.parse
and saved to local variable OBJ
. By using JSON.parse(<data>)
, the data inside the JSON object can be accessed using the variable names sent (leftStick and rightStick). The format that is sent to the Arduino is L:<left stick value>|R:<right stick value>;
where each stick value is preceeded by an 'L' or 'R', representing the individual sticks and further separated with a '|'. Lastly, the send string is finalized with a ';' which acts as the end of string delimeter. A helper function was used called sendToArduino(<data>)
to send the string to the arduino.
function sendToArduino(data){
for(var i = 0; i < data.length; i++){
serialport.write(data.charAt(i));
}
serialport.write('\n');
}
The sendToArduino()
function is necessary because only a single char can be sent to the Arduino for the data to be reliably sent. If the entire string is sent with serialport.write()
, the data recieved is incorrect except for the first character. The sendToArduino()
function simply splits up the data and sends the string char by char and finally writes out a newline character '\n'. The newline character is sent because the arduino sets up a blocking call for a newline-character terminated string before using the data.
mjpg_streamer
was used to stream the camera video to the website.
First, the pre-requisites were installed:
sudo apt-get install g++ curl pkg-config libv4l-dev libjpeg-dev build-essential libssl-dev vim cmake
sudo apt-get install imagemagick
wget https://github.com/shrkey/mjpg-streamer/raw/master/mjpg-streamer.tar.gz
tar -xvf ./mjpg-streamer.tar.gz
cd mjpg-streamer
make
make install
Lastly we check to make sure it is installed correctly. Run
sudo ./mjpg_streamer -i "./input_uvc.so" -o "./output_http.so -w ./www"
MJPG Streamer Version: svn rev:
i: Using V4L2 device.: /dev/video0
i: Desired Resolution: 640 x 480
i: Frames Per Second.: 5
i: Format............: MJPEG
o: www-folder-path...: ./www/
o: HTTP TCP port.....: 8080
o: username:password.: disabled
o: commands..........: enabled
mjpg-streamer sends the jpg images from the camera and broadcasts it at 127.0.0.1:8080
, which if you configure it correctly lets video be seen at that address. (For example, if the Beagle Bone's address is 192.168.7.2 which it is automatically configured to if Network Setup instructions were followed. The video can be seen when the wolftracker page is opened up).