Interfacing BeagleBoard with Simulink and Arduino
From Matt Bilsky
Contents
Overview
Clean Installation vs. Virtual Machine
To attempt to build a clean environment for development is a large undertaking. It will take many hours and most likely you will encounter a few issues along the way. To ease development I have created a Virtual Machine image of the development environment I created. Using this will allow you to skip these first steps and only require you to begin at topic 3.1.
All the demonstrations listed in the tutorial are located in the /home/matt/Documents/MATLAB folder on the virtual machine.
The image can be viewed using the free VMware player located here: http://www.vmware.com/products/player/
Software Configuration
This guide is based around Matlab 2010b for Linux running on Ubuntu 11.10 32-bit. Updates have been turned off in the Virtual Machine to help preserve compatibility.
The BeagleBoard support package only works with Matlab 2010b.
Hardware Configuration
Here is a list of hardware used in this tutorial and the accompanying demonstrations:
- USB serial breakout board http://www.sparkfun.com/products/718
- BeagleBoard Classic Revision C5
- Toshiba USB "Dynadock U" laptop dock (for USB Ethernet)
- USB Mini A to OTG female cable
- 5-Volt power cable for BeagleBoard
- Arduino Uno
- Logitech 9000 Pro webcam
You can get everything you need for the BeagleBoard here: https://specialcomp.com/beagleboard/order.htm
Here is a shot of the setup:
Common Sources of Error
- Make sure that you can SSH to root@BeagleBoardIP without having to enter your password.
- Ensure that you have set your Matlab environment variables each time you launch the program.
- If your BeagleBoard's IP address changes, you need to change it both in eclipseidesetup and in the mex-file arguments under configure parameters in your Simulink model.
Project History
At the beginning of this project, the Hopper Space Simulator at Lehigh University has had all of its control algorithms programmed in the Arduino language, and executed by an on-board Arduino. I was tasked to explore the feasibility of interfacing an Arduino with a Gumstix or BeagleBoard board and using Simulink to generate the code for them. The idea is that we can continue to use an Arduino Mega to handle the input/output to the servos, speed controllers, and sensors. The control algorithms will then be executed on the higher speed computer, and the Arduino would just talk to the sensors.
I was given complete freedom, as no one had attempted this before on the team. I then began my research with no background, eventually exploring the abilities of Simulink for code generation, setting up and installing Angstrom Linux on the BeagleBoard, and finally how to use C code in Simulink S-functions for serial communication. I also explored the blocks that are available for the BeagleBoard including the audio input/output blocks and the video capture blocks.
After many issues along the way, I ultimately was successful in this goal. While the demonstrations at the bottom of this page are quite trivial, they show that we have the ability to send and receive data to the Arduino. It is a simple matter of adding more data coming to and from Simulink, and replacing the LEDs with the desired hardware to make the BeagleBoard the full brains behind the Hopper.
More information about the Hopper Project is available here: http://www.youtube.com/watch?v=b3Gdt-OvaKo
Getting Started
Generating GNU Toolchain using BitBake
To install and configure the BitBake program for OpenEmbedded, we are going to follow the guide published on the Gumstix website:
http://gumstix.org/software-development/open-embedded/61-using-the-open-embedded-build-system.html
Follow Steps until you get to the section "Environmental Setup"
I have found the Method B is the easiest for setting up the enviroment, specifically:
source ~/overo-oe/build/profile
The machine type should also be set to BeagleBoard. To do this open the terminal and execute the following:
cd ~/overo-oe/build/conf nano auto.conf
Next change the specified machine to "beagleboard"
To save your changes press "Ctrl" and "X" and at the prompt that appears type "y" followed by enter.
The only package necessary for code generation is the gdbcross recipe. To build it, type the following:
bitbake gdb-cross
Note that this will take some time as it needs to download and extract all the necessary packages.
It is common for the compiling to fail due to an error within a package. The best solution is to first try the following:
bitbake -c clean <failed recipe> bitbake -c rebuild <failed recipe>
If bitbake gdb-cross fails on the same package again the best solution is to google your error. A search such as "<failed recipe> bitbake openembedded" will ofter return numerous forum results from users who have had similar issues.
A good site to get answers from is Old Nabble: http://old.nabble.com. Try different searches and try the parent recipe for the one that is failing.
Often times the fix includes modifying the recipe version or changing a dependency
Once BitBake is finished it will display a summary:
NOTE: Tasks Summary: Attempted 580 tasks of which 580 didn't need to be rerun and 0 failed.
Installing Angstrom Distribution for BeagleBoard
Rather than building an image for the BeagleBoard ourselves, it is much easier to use the Narcissus online image builder located here: http://narcissus.angstrom-distribution.org/
- Set the machine type to BeagleBoard
- Set the name of the image to something easy to type (since you will have to extract it later)
- Under "User environment selection" select X11
- Under "Additional console packages" select GDBServer
- Under "Platform specific packages" select all of the packages from "Texas Instruments OMAP3x/AM3x family" and "Texas Instruments OMAP3x/DaVinci/OMAPL family (using DSP)"
Select "Build me!" and wait as your distribution is assembled. Then save the created archive to your desktop.
Next follow the directions located here: http://treyweaver.blogspot.com/2010/10/installing-angstrom-on-beagleboard-xm.html to setup and install OpenEmbedded to a SD card.
Put the card in your BeagleBoard and fire it up.
Once booted, run the following commands to setup ssh
ipkg remove dropbear ipkg update ipkg install openssh ipkg install openssh-sftp ipkg install openssh-sftp-server
To give your BeagleBoard a static IP address for the entire time it is booted enter:
ifconfig 192.168.X.X netmask 255.255.255.0
To create a Key based authentication system for the BeagleBoard and your host computer, at the Linux terminal type "sudo ssh root@192.168.X.X" where the IP address is that of your BeagleBoard. The system will ask you if you trust the host and if you want to cache the key. Type "yes" and then continue to log in until you see the root@beagleboard prompt. Type "exit" to end the connection.
Now try the ssh command again. This time it should not prompt you for a password, but should just connect automatically. If this is not the case, try following the instructions here to generate a new key pair and transfer it over: https://help.ubuntu.com/community/SSH/OpenSSH/Keys
Installing Eclipse Ganymede
A pre-requisite for Eclipse to work is the JRE version 6.0. It is available here:http://www.oracle.com/technetwork/java/javase/downloads/jre-6u30-download-1377142.html. We want the Java SE 6.0 JRE package for Linux x86
Although it is not the most recent release, the supported version of Eclipse is Ganymede version Sr2 You can download it here: http://www.eclipse.org/downloads/packages/release/ganymede/sr2. We want the package "Eclipse IDE for C/C++ Developers" for 32 bit Linux systems
Extract the archive to a location you can find easily. I chose to place it on the desktop at ~/Desktop/eclipse
Located within this folder is the eclipse executable. Launch this (double click on eclipse). When it launches, a window will appear asking you to specify a workspace location. Set this to a folder where you want eclipse to store your compiled files. I chose ~/matlab_workspace
Once you are in Eclipse, we need to configure the location of the shared files so that they can be included when building the Simulink models.
At top of the Eclipse window select Window -> Preferences
In the Preferences pane, navigate to C/C++ -> Debug -> Common Source Lookup
To add folders select Add -> File System Directory -> OK -> Browse
Navigate to each of the directories posted in the image below in the browse menu. Note the folder name is listed to the left, followed by its path (matlab_workspace-/home/matt). In the browse window enter the directory you are looking for, then select "OK". It will appear correct in the dialog before you click "OK" for a second time. When you are back at the window pictured below it will be displayed in the re-arranged format.
Replace /home/matt with the appropriate path on your system. Also note that the 3rd folder down, MATLAB is the working directory we are using in Matlab, and where all of the models are saved.
Select "Apply" then "OK"
Eclipse is now fully configured.
Configuring Matlab 2010b Code Generation
The first step is to extract and install the BeagleBoard support package from MathWorks here: http://www.mathworks.com/academia/beagleboard/. Follow the ReadMe file in the archive for installation directions.
I was unable to get the package to install correctly, so I made the changes manually. If you analyze the installer, it changes the file name of the files it is going to overwrite to filename_old. It is up to you if you wish to do this for each file that you are going to replace. Inside the support package is a zip file with a directory structure similar to /usr/local/MATLAB/R2010b/. This is because you need to take the files from the same location in this zip file's structure and move them to the same location in the Matlab root. Yes it it tedious, but it gets the job done.
To launch Matlab 2010b first open the terminal then execute the following:
sudo sh /usr/local/MATLAB/R2010b/bin/matlab
Once the Matlab window appears, type the follwing at the command prompt:
eclipseidesetup
The "Embedded IDE Link: Eclipse Adapter Setup" window should appear. Modify the settings to match those in the picture below
- Set "Executable" to the location of your Eclipse installation folder
- Set "Workspace" to the folder you created to serve as your Eclipse workspace
- Set "IP Address" to the IP address of your BeagleBoard
Click "Apply" followed by "OK"
The last step is to set the shared library search path.
This configuration assumes that you also built using BitBake the omap3-console-image or omap3-desktop-image. If you are using the Virtual Machine image, this has been done for you.
Execute the following in the Linux terminal:
sudo nano /usr/local/MATLAB/R2010b/toolbox/idelink/extensions/eclipseide/host/.gdbinit_angstrom
Comment out all the lines except:
handle SIG34 ... and handle SIG35 ...
At the bottom of the file add the following:
Set solib-search-path /home/matt/overo-oe/tmp/rootfs/omap3-console-image/lib
Then type "Ctrl" and "x" followed by "y" to save the changes and exit.
Generating Code Using Simulink for the BeagleBoard
Launching Matlab
To launch Matlab 2010b first open the terminal then execute the following:
sudo sh /usr/local/MATLAB/R2010b/bin/matlab
Wait for the Matlab window to appear
If you have Changed the IP address of your BeagleBoard, you need to update it in the IDE setup, otherwise you can continue to setting up the environment
To update the IDE setup type the follwing at the command prompt: eclipseidesetup *Set "IP Address" to the IP address of your BeagleBoard *Click "Apply" followed by "OK"
Next we need to change the Matlab Current Folder to the one where we have all our model files stored. I normally use /home/matt/Documents/MATLAB (This is where all the file are on the Virtual Machine). If you do not change this, you will get an error when you try to build your model that you are in the MATLAB bin folder.
Setup the Matlab environment
At the Prompt enter the following (where /home/matt/overo-oe is the location of your OpenEmbedded BitBake folder):
setenv('OETREE', '/home/matt/overo-oe/') setenv('PATH', ['/home/matt/overo-oe/tmp/sysroots/i686-linux/usr/armv7a/bin' ':' getenv('PATH')])
Now that the variables have been set, we can being working in Simulink.
Launch Simulink by typing "Simulink" at the command prompt.
Configuring Simulink Parameters
In the library browser go to Embedded IDE Link -> Common
Drag the Target Preferences block to the model
Hit yes on the pop-up dialog, we will configure the parameters ourselves.
Double click on the Target Preferences block, the configuration window will appear.
- Set the IDE/Toolchain drop-down menu to Eclipse
- Set the board to BeagleBoard ARM
- Set the CPU Clock to match your board (leave at 600 Mhz for BeagleBoard)
Select "Apply" then "OK"
Launch the Simulation Parameters menu by navigating to Simulation -> Configure Parameters
First under "Solver" set how long you want the simulation to run. To make it run until you stop it set Stop Time: to "inf"
Scroll down to Real-Time Workshop -> Interface
Set Interface: to "External mode"
Set your parameters to match those in the picture below. Change "Mex-file Arguemnts" to your board's IP address (remember to put it in single quotes)
Now look at the settings in Real-Time Workshop -> Embedded IDE Link
Set the Compiler option string to:
-O2 -g -mfloat-abi=softfp
Set the maximum build time and maximum operation time to 10000
The final item that needs to be configured are the model properties. Launch the dialog from File -> Model Properties
In this dialog, you can specify Matlab functions to be executed in the background during each step of a programs execution.
The only parameters that must be set are the close functions. These are located under CloseFCN. Enter the following commands where "working_video" is replaced by the name of your sketch:
rtwprivate ssgencode ModelCloseRequest working_video
Select "Apply" then "OK"
Finally, set your model to execute in External Mode by going Simulation -> External
Now your model is configured properly.
Generating Code
Once you have created your model, the next step is to build the code. Simulink will automatically launch the Eclipse IDE, which will build then debug your code.
To begin the build process type "Ctrl" and "B" or select Tools -> Real-Time Workshop -> Build Model
Watch you code build, and check for any errors. Matlab will display errors, and so will the console in the Eclipse IDE.
This video shows what you should see in the Eclipse console when the code is compiling. If the Matlab command window seems to be stuck at Downloading Program for a few minutes, look at this console to see if there were any errors when compiling.
Executing Code
The build process should automatically launch your code in the Eclipse IDE and on the BeagleBoard.
If everything is running correctly it should look like this:
To connect your model to the BeagleBoard simulation go Simulation -> Connect to Target. This will allow data to be sent to the scopes and displays connected in your sketch.
It is possible to disconnect from the target without stopping the code execution by selecting Simulation -> Disconnect from Target
To stop the code on the BeagleBoard select Simulation -> Stop Real-Time Code. You can also hit the red stop button in the middle of the Eclipse debug window.
It is also possible to manually copy and execute the compiled files to the BeagleBoard. This is extremely useful if your model starts then exits immediately in the GDB debugger pane in Eclipse.
To copy the file navigate to the following location where "matlab_workspace" is the location of your Eclipse workspace, and "tutorial" is the name of your model.
/home/matt/matlab_workspace/tutorial/CustomMW
Next run the following command to copy the compiled executable to the BeagleBoard. Change the IP address to match that of your BeagleBoard and change "tutorial" to the name of your model:
scp tutorial root@192.168.X.X:/home/root
Finally, to execute the model type the following. The following output will be the diagnostic information from the code's execution (replace "tutorial" with the name of your model):
ssh root@192.168.X.X /home/root/tutorial
Using S-Functions to Communicate Over Serial
Setting Library Locations
Once you have created a S-function builder block, double click on it to configure it.
The first thing we need to do is add the library files we are using for Serial communication and for String manipulation.
Under the Library tab go to Include files and external function decelerations. Enter the following libraries and external function decleration(along with any other files you may want to use in your C code):
If during code generation you get an error stating that a library is missing, or you want to add your own custom files to include, place the files in /usr/include, and call them with their full file paths.
Configuring Inputs and Outputs
We need to create input and output ports on our S-function so that we can pass data to and from the Arduino. To specify your inputs navigate to the Input Ports tab and configure your input variables similar to the example below:
You can receive arrays and vectors, but if you are only receiving a double, which is the case here, specify the number of rows to 1.
It is also necessary to specify the outputs. This is done under the Output Ports tab. See an example below:
Again you can customize the outputs to your specifications.
Adding Communication Functions
Regardless of what data you wish to send and receive from the Arduino, it is necessary to define these functions in the top of the Outputs tab before you begin coding. Copy and paste the following functions: (Based on the Arduino-serial C program located here: http://todbot.com/blog/2006/12/06/arduino-serial-c-code-to-talk-to-arduino/)
int serialport_writebyte( int fd, uint8_t b) { int n = write(fd,&b,1); if( n!=1) return -1; return 0; } int serialport_write(int fd, const char* str) { int len = strlen(str); int n = write(fd, str, len); if( n!=len ) return -1; return 0; } int serialport_read_until(int fd, char* buf, char until) { char b[1]; int i=0; do { int n = read(fd, b, 1); /* read a char at a time*/ if( n==-1) return -1; /* couldn't read*/ if( n==0 ) { usleep( 10 * 1000 ); /* wait 10 msec try again*/ continue; } buf[i] = b[0]; i++; } while( b[0] != until ); buf[i] = 0; /* null terminate the string*/ return 0; } /* takes the string name of the serial port (e.g. "/dev/tty.usbserial","COM1") and a baud rate (bps) and connects to that port at that speed and 8N1. opens the port in fully raw mode so you can send binary data. returns valid fd, or -1 on error*/ int serialport_init(const char* serialport, int baud) { struct termios toptions; int fd; /*fprintf(stderr,"init_serialport: opening port %s @ %d bps\n", serialport,baud);*/ fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY); if (fd == -1) { perror("init_serialport: Unable to open port "); return -1; } if (tcgetattr(fd, &toptions) < 0) { perror("init_serialport: Couldn't get term attributes"); return -1; } speed_t brate = baud; /* let you override switch below if needed*/ switch(baud) { case 4800: brate=B4800; break; case 9600: brate=B9600; break; #ifdef B14400 case 14400: brate=B14400; break; #endif case 19200: brate=B19200; break; #ifdef B28800 case 28800: brate=B28800; break; #endif case 38400: brate=B38400; break; case 57600: brate=B57600; break; case 115200: brate=B115200; break; } cfsetispeed(&toptions, brate); cfsetospeed(&toptions, brate); /* 8N1*/ toptions.c_cflag &= ~PARENB; toptions.c_cflag &= ~CSTOPB; toptions.c_cflag &= ~CSIZE; toptions.c_cflag |= CS8; /*no flow control*/ toptions.c_cflag &= ~CRTSCTS; toptions.c_cflag |= CREAD | CLOCAL; /* turn on READ & ignore ctrl lines*/ toptions.c_iflag &= ~(IXON | IXOFF | IXANY); /* turn off s/w flow ctrl*/ toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); /* make raw*/ toptions.c_oflag &= ~OPOST; /* make raw*/ /* see: http://unixwiz.net/techtips/termios-vmin-vtime.html*/ toptions.c_cc[VMIN] = 0; toptions.c_cc[VTIME] = 20; if( tcsetattr(fd, TCSANOW, &toptions) < 0) { perror("init_serialport: Couldn't set term attributes"); return -1; } return fd; }
Sending and Receiving Data
Let's look at the basics for sending and receiving data in a S-Function.
unsigned char buf[4096]; char buffer [50];
The char array buf is the holding array for the incoming data from the serial port. buffer holds the output string.
int cp = serialport_init("/dev/ttyUSB0",115200); usleep(50000);
These lines initialize the serial port and then pauses 50ms. To figure out what port you need to use unplug and plug back in your USB serial port, then on the BeagleBoard type "dmesg | grep tty". The output will be the name of your serial port. Replace ttyUSB0 with your port.
115200 is the Baud rate specification. This can be changed to any standard Baud, but make sure it matches the one set on your Arduino.
int r = serialport_read_until(cp,buf,"\n"); usleep(50000);
This block of code reads the serial buffer to the array buf, and then pauses.
int outStr = sprintf(buffer,"%f,%f\n",u0[0],u1[0]); int m = serialport_write(cp,buffer); usleep(50000);
This code snippit converts data we wish to send to the Arduino to a string using sprintf. It then prints the data through the port and then pauses again.
close(cp);
This final command closes the serial port so that we can open it again on the next iteration.
2-Way Communication Between Simulink and Arduino
Simulink Side
Below is the entire S-function Outputs code for the video and Arduino demonstration listed at the bottom. It is all of the code discussed above, just in one place so you can easily copy and paste it. Be sure to include the Serial functions in the Outputs tab before pasting in the code below.
unsigned char buf[4096]; char buffer [50]; int cp = serialport_init("/dev/ttyUSB0",115200); usleep(50000); int r = serialport_read_until(cp,buf,"\n"); y0[0]=atof(buf); usleep(50000); int outStr = sprintf(buffer,"%f,%f\n",u0[0],u1[0]); int m = serialport_write(cp,buffer); usleep(50000); close(cp);
Arduino Side
Below is the sketch running on the Arduino in the video and Arduino demonstration.
#include <stdio.h> #include <string.h> #include <Servo.h> char* incoming; int i = 0; double data[5]; void setup(){ Serial.begin(115200); incoming = "000000000000000000000000000000000000000000000000"; pinMode(11,OUTPUT); } void loop(){ int n = Serial.available(); i=0; while(n>0){ n--; incoming[i++]=Serial.read(); } i=0; delay(350); char * pch; pch = strtok (incoming,","); while (pch != NULL) { data[i++]=atof(pch); pch = strtok (NULL, ","); } i=0; int z = map(data[0],0,12,0,255); analogWrite(11,z); Serial.println(analogRead(0)); }
Demonstrations and Code Examples
Sum-Diff Codegen Example
Follow the MathWorks tutorial located within Matlab help entitled "Linux-ARM Code Generation for Texas Instruments OMPA35xx Processors"
Since we already configured the system for code generation, we can skip down to the section "Generate Code for Linux-ARM"
At the command line enter "sumdiff_codegen"
Connect a scope to "Out1"
When the model opens, configure the "Target Preferences" and "Configurations Parameters" to those described above rather than those specified in the tutorial.
Build the code, connect to the target, and double click on the scope. You should see the numbers increasing. This means that your model is successfully working on the BeagleBoard!
Parametric Audio Equalizer
This demonstration is based on the Parametric Audio Equalizer demo. In Matlab help search for "Parametric Audio Equalizer" and select the one for the Target Support Package.
Right click and select Save As to save my version of this demo: Parametric Audio Equalizer
Scroll down to Task 6 - Device Driver Integration. Next to step 1 select the Device Driver Integration Model Template.
To get the ADC and DAC blocks, at the Matlab command prompt type alsalib. This will bring up the audio in and out blocks for the BeagleBoard.
Again we want to follow the configuration settings listed above in this document rather than those in the demo.
For the Audio Capture block, set the parameters as follows:
Here are the settings for Audio Playback:
Double click on "Edit Parameters" in blue and set the Sampling Rate to 22050.
Build the model and connect to the target. After plugging in a music source to the line in port on the BeagleBoard, and speakers to the output, you should be able to hear the sound change as you adjust the parameters on your host computers.
Here is a video of what my setup looked like on the host computer, and the sound that resulted from the BeagleBoard:
Arduino and Video Capture
The following demonstration shows the video capture tools of the BeagleBoard, along with a demonstration of the BeagleBoard <-> Arduino interface.
There are 2 parts to the demonstration. One half counts the number of pennies in front of the camera, then sends the number to the Arduino which adjusts the brightness of the blue LED accordingly. The second half sends the current reading of the optical sensor to the BeagleBoard over serial and then receives the brightness to set the red LED to.
Right-click on the following links and select Save As to save them to your computer
The Arduino with the Serial sketch discussed above (download it HERE).
The Simulink model is called working_video (download it HERE).
The circuit consists of 2 LEDs with 1k pull-up resistors wired to digital outputs 11(blue) and 9(red). A TI OPT101 Photodiode is attached to analog input A0. Between the Arduino and the Rs232 adapter, the Rx <=> Tx and Tx <=> Rx lines are crossed. Be sure to ground the two devices to each other to prevent noise in the data.
If you want to get to the video capture block, type "v4l2lib" at the Matlab command line.
Here is a picture of the actual setup:
And the video capture setup:
Here is a video showing the output on the connected host computer:
This next video shows the LEDs being adjusted and the pennies being placed:
Finally, a test of the optical sensor:
Sources
http://pixhawk.ethz.ch/wiki/tutorials/omap/openembedded_bitbake_installation https://help.ubuntu.com/community/SSH/OpenSSH/Keys http://www.debuntu.org/ssh-key-based-authentication-p2 https://help.ubuntu.com/community/MATLAB/R2010b http://rafaelaroca.wordpress.com/2011/08/24/webspectrum/ http://cboard.cprogramming.com/networking-device-communication/119973-rs232-serial-port-library-linux-windows.html http://www.arduino.cc/playground/Interfacing/LinuxTTY http://www.cplusplus.com/reference/clibrary/cstdio/sprintf/ http://todbot.com/blog/2006/12/06/arduino-serial-c-code-to-talk-to-arduino/ http://www.cplusplus.com/reference/clibrary/cstdlib/atof/ http://www.mathworks.com/academia/beagleboard/ http://www.easysw.com/~mike/serial/serial.html http://blog.integrii.net/?p=115