Setting up Yocto/Poky for Beagleboard-xM/Beagleboard/Beaglebone

I have used Buildroot before for setting up environment for Beagleboard, but OpenEmbedded or Yocto gives much more power with regard to the number of packages you can build or do your own customizations. From here on i am assuming that you have a separate beagle directory in which you are doing this. A knowledge of git and your smartness ūüėČ is assumed.

Clone the poky repository with git

git clone git://git.yoctoproject.org/poky

Enter the poky directory and clone the “meta-ti” layer. This layer will be required for Beagle specific builds.

git clone git://git.yoctoproject.org/meta-ti meta-ti

Clone the meta-openembedded, openembedded-core and meta-qt5 layers, while in the poky directory

git clone git://git.openembedded.org/openembedded-core openembedded-core

git clone git://git.openembedded.org/meta-openembedded meta-openembedded

git clone git://github.com/meta-qt5/meta-qt5 meta-qt5

In each of the git cloned repositories, select the branch you want to work with. If you do not select a branch, all of them will be with the default master branch. For example, you can select the dora or daisy branch.

While in the beagle directory, run source poky/oe-init-build-env poky-build . The poky-build directory is where all your build will take place, downloads will happen and all the packages and images will reside.

So, now your beagle directory will have two directories inside poky, poky-build. The poky directory will have the various meta layers.

After running the source command above, do not exit the terminal or switch to a different terminal or directory. The script which was run did the necessary task of setting up the environment variables required for the build.

Open the conf/bblayers.conf file with an editor like nano or gedit. Add the required entries to have this file exactly as below.

# LAYER_CONF_VERSION is increased each time build/conf/bblayers.conf
# changes incompatibly
LCONF_VERSION = "6"

BBPATH = "${TOPDIR}"
BBFILES ?= ""

BBLAYERS ?= " \
  /home/sanchayan/beagle/poky/meta \
  /home/sanchayan/beagle/poky/meta-yocto \
  /home/sanchayan/beagle/poky/meta-yocto-bsp \
  /home/sanchayan/beagle/poky/meta-ti \
  /home/sanchayan/beagle/poky/meta-qt5 \
  /home/sanchayan/beagle/poky/openembedded-core \
  /home/sanchayan/beagle/poky/meta-openembedded/meta-ruby \
  /home/sanchayan/beagle/poky/meta-openembedded/meta-oe \
  "
BBLAYERS_NON_REMOVABLE ?= " \
  /home/sanchayan/beagle/poky/meta \
  /home/sanchayan/beagle/poky/meta-yocto \
  "

Open a different terminal and go to the openembedded-core in poky directory.  Make a conf directory and add a layer.conf file as below.

# We have a conf and classes directory, append to BBPATH
BBPATH .= ":${LAYERDIR}"

# We have a recipes directory, add to BBFILES
BBFILES += "${LAYERDIR}/recipes*/*/*.bb ${LAYERDIR}/recipes*/*/*.bbappend"

BBFILE_COLLECTIONS += "openembedded-core"
BBFILE_PATTERN_openembedded-core := "^${LAYERDIR}/"
BBFILE_PRIORITY_openembedded-core = "4"

Now go to the meta-openembedded directory in poky. Make a conf directory and add a layer.conf file as below.

# We have a conf and classes directory, append to BBPATH
BBPATH .= ":${LAYERDIR}"

# We have a recipes directory, add to BBFILES
BBFILES += "${LAYERDIR}/recipes*/*/*.bb ${LAYERDIR}/recipes*/*/*.bbappend"

BBFILE_COLLECTIONS += "meta-openembedded"
BBFILE_PATTERN_meta-openembedded := "^${LAYERDIR}/"
BBFILE_PRIORITY_meta-openembedded = "5"

Go to the terminal in which you got into the poky-build directory after running source/oe-init-build-env. Add the following to the conf/local.conf.

BB_NUMBER_THREADS = "4"
PARALLEL_MAKE = "-j 4"
INHERIT += "rm_work"
IMAGE_INSTALL_append = " \
            packagegroup-core-x11 \
            libx11 \            
            qtbase \
            qt3d \
            qtconnectivity \
            qtmultimedia \             
            qtserialport \            
            qtwebsockets \
            qtsvg \
            qtx11extras \
              "

The BB_NUMBER_THREADS and PARALLEL_MAKE in my local.conf is as per the fact that i have a quad core machine. Set it as per your machine configuration. Also, set the MACHINE variable in the file. I set it to MACHINE ?= “beagleboard”. Just add this line below the default. The IMAGE_INSTALL_append will add the packages specified to any image we build and we are going to do a minimal build. You can add the packages you like.

First look for a specific package you like at the below link. Do select the relevant branch as per your branch selection in the start of the tutorial.

http://layers.openembedded.org/layerindex/branch/master/recipes/

After this, check the layer in which that package recipe resides. Clone the layer in the same way we added the meta-ti or meta-qt5 layers and add them to the bblayers.conf file. If the layer has a dependency you need to clone and add the relevant dependency layer too. I wanted to build qt5, so i added the meta-qt5 layer. If you want to build cherokee, you need to add the meta-webserver layer in which the cherokee recipe resides.

Some packages fail due to a fetch failure. This is because a particular define for a url is not there in Yocto which Openembedded uses.

Add the following to meta/classes/mirrors.bbclass and meta/conf/bitbake.conf in the poky source tree respectively. Make sure you add it at the right place.

${SAVANNAH_GNU_MIRROR} http://download-mirror.savannah.gnu.org/releases \n \
${SAVANNAH_NONGNU_MIRROR} http://download-mirror.savannah.nongnu.org/releases \n \

SAVANNAH_GNU_MIRROR = “http://download-mirror.savannah.gnu.org/releases”
SAVANNAH_NONGNU_MIRROR = “http://download-mirror.savannah.nongnu.org/releases”

A patch for the poky tree to do the above is below, which you can apply with git.

diff --git a/meta/classes/mirrors.bbclass b/meta/classes/mirrors.bbclass
index 1fd7cd8..1dd6cd6 100644
--- a/meta/classes/mirrors.bbclass
+++ b/meta/classes/mirrors.bbclass
@@ -19,8 +19,10 @@ ${DEBIAN_MIRROR}    ftp://ftp.si.debian.org/debian/pool \n \
 ${DEBIAN_MIRROR}    ftp://ftp.es.debian.org/debian/pool \n \
 ${DEBIAN_MIRROR}    ftp://ftp.se.debian.org/debian/pool \n \
 ${DEBIAN_MIRROR}    ftp://ftp.tr.debian.org/debian/pool \n \
-${GNU_MIRROR}    ftp://mirrors.kernel.org/gnu \n \
+${GNU_MIRROR}        ftp://mirrors.kernel.org/gnu \n \
 ${KERNELORG_MIRROR}    http://www.kernel.org/pub \n \
+${SAVANNAH_GNU_MIRROR} http://download-mirror.savannah.gnu.org/releases \n \
+${SAVANNAH_NONGNU_MIRROR} http://download-mirror.savannah.nongnu.org/releases \n \
 ftp://ftp.gnupg.org/gcrypt/     ftp://ftp.franken.de/pub/crypt/mirror/ftp.gnupg.org/gcrypt/ \n \
 ftp://ftp.gnupg.org/gcrypt/     ftp://ftp.surfnet.nl/pub/security/gnupg/ \n \
 ftp://ftp.gnupg.org/gcrypt/     http://gulus.USherbrooke.ca/pub/appl/GnuPG/ \n \
diff --git a/meta/conf/bitbake.conf b/meta/conf/bitbake.conf
index b3786a7..29ed3d3 100644
--- a/meta/conf/bitbake.conf
+++ b/meta/conf/bitbake.conf
@@ -568,6 +568,8 @@ KERNELORG_MIRROR = "http://kernel.org/pub"
 SOURCEFORGE_MIRROR = "http://downloads.sourceforge.net"
 XLIBS_MIRROR = "http://xlibs.freedesktop.org/release"
 XORG_MIRROR = "http://xorg.freedesktop.org/releases"
+SAVANNAH_GNU_MIRROR = "http://download-mirror.savannah.gnu.org/releases"
+SAVANNAH_NONGNU_MIRROR = "http://download-mirror.savannah.nongnu.org/releases"
 
 # You can use the mirror of your country to get faster downloads by putting
 #  export DEBIAN_MIRROR = "ftp://ftp.de.debian.org/debian/pool"
diff --git a/meta/recipes-core/images/core-image-minimal.bb b/meta/recipes-core/images/core-image-minimal.bb
index 9716274..13f9127 100644
--- a/meta/recipes-core/images/core-image-minimal.bb
+++ b/meta/recipes-core/images/core-image-minimal.bb
@@ -8,5 +8,5 @@ LICENSE = "MIT"
 
 inherit core-image
 
-IMAGE_ROOTFS_SIZE ?= "8192"
+#IMAGE_ROOTFS_SIZE ?= "8192"
 

Now, you can build an image for your board by doing bitbake core-image-minimal. The generated files and images will be in poky-build/tmp/deploy/images/beagleboard.

Follow this link and transfer the files to the SD card. I don’t know why but putting the uImage in boot directory doesn’t work. Put the uImage in the boot directory which is in the root filesystem.

https://www.yoctoproject.org/downloads/bsps/dora15/beagleboard

Now, plug in the SD card and boot. It boots very quickly. You are supposed to be connected to the debug serial port. For some reason Ethernet and all USB ports don’t work. I am trying to figure out why, will update as soon as i do. X also doesn’t seem to work on running startx.

If you would like to setup qtcreator and use qt5, build meta-toolchain-qt5 and follow the below link. The link is not exactly for qt5, but, can be used for qt5 setup for beagle. No need to follow the relocation related stuff on the link.

http://developer.toradex.com/how-to/how-to-set-up-qt-creator-to-cross-compile-for-embedded-linux

Playing .wav/mp3 file using gstreamer in code

You can also clone this with

git clone https://github.com/SanchayanMaity/gstreamer-audio-playback.git

Though i used this on a Toradex Colibri Vybrid module, you can use the same on a Beagleboard or desktop with the correct setup.

/*
Notes for compilation:
1. For compiling the code along with the Makefile given, a OE setup is mandatory.
2. Before compiling, change the paths as per the setup of your environment.

Please refer the Gstreamer Application Development Manual at the below link before proceeding further
http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/index.html

Comprehensive documentation for Gstreamer
http://gstreamer.freedesktop.org/documentation/

The following elements/plugins/packages are expected to be in the module image for this to work
gstreamer
gst-plugins-base
gst-plugins-good-wavparse
gst-plugins-good-alsa
gst-plugins-good-audioconvert
gst-plugins-ugly-mad

Pipeline to play .wav audio file from command line
gst-launch filesrc location="location of file" ! wavparse ! alsasink 

Pipeline to play .mp3 audio file from command line
gst-launch filesrc location="location of file" ! mad ! audioconvert ! alsasink 

It is also assumed that the USB to Audio device is the only audio device being used on the system, if not the
"device" parameter for alsasink will change and the parameter to be used needs to be checked with cat /proc/asound/cards,
which then needs to be set as follows

In gstreamer pipeline 

Pipeline to play .wav audio file from command line
gst-launch filesrc location="location of file" ! wavparse ! alsasink device=hw:1,0

Pipeline to play .mp3 audio file from command line
gst-launch filesrc location="location of file" ! mad ! audioconvert ! alsasink device=hw:1,0

In code initialisation in init_audio_playback_pipeline
g_object_set (G_OBJECT (data->alsasink), "device", "hw:0,0", NULL);
                            OR
g_object_set (G_OBJECT (data->alsasink), "device", "hw:1,0", NULL);

The pipeline will ideally remain the same for a different audio device, only the device parameter for alsasink will change
*/

#include <gstreamer-0.10/gst/gst.h>
#include <gstreamer-0.10/gst/gstelement.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>

#define NUMBER_OF_BYTES_FOR_FILE_LOCATION    256

volatile gboolean exit_flag = FALSE;

typedef struct  
{
    GstElement *file_source;
    GstElement *pipeline;
    GstElement *audio_decoder;    
    GstElement *audioconvert;
    GstElement *alsasink;    
    GstElement *bin_playback;    
    GstBus *bus;
    GstMessage *message;        
    gchar filelocation[NUMBER_OF_BYTES_FOR_FILE_LOCATION];
}gstData;

gstData gstreamerData;

// Create the pipeline element
gboolean create_pipeline(gstData *data)
{        
    data->pipeline = gst_pipeline_new("audio_pipeline");    
    if (data->pipeline == NULL)
    {            
        return FALSE;
    }
    gst_element_set_state (data->pipeline, GST_STATE_NULL);
    return TRUE;
}

// Callback function for dynamically linking the "wavparse" element and "alsasink" element
void on_pad_added (GstElement *src_element, GstPad *src_pad, gpointer data)
{
    g_print ("\nLinking dynamic pad between wavparse and alsasink\n");

    GstElement *sink_element = (GstElement *) data;     // Is alsasink
    GstPad *sink_pad = gst_element_get_static_pad (sink_element, "sink");
    gst_pad_link (src_pad, sink_pad);

    gst_object_unref (sink_pad);
    src_element = NULL;     // Prevent "unused" warning here
}

// Setup the pipeline
gboolean init_audio_playback_pipeline(gstData *data)
{
    if (data == NULL)
        return FALSE;
        
    data->file_source = gst_element_factory_make("filesrc", "filesource");    
    
    if (strstr(data->filelocation, ".mp3"))
    {
        g_print ("\nMP3 Audio decoder selected\n");
        data->audio_decoder = gst_element_factory_make("mad", "audiomp3decoder");
    }
    
    if (strstr(data->filelocation, ".wav"))
    {
        g_print ("\nWAV Audio decoder selected\n");
        data->audio_decoder = gst_element_factory_make("wavparse", "audiowavdecoder");
    }
        
    data->audioconvert = gst_element_factory_make("audioconvert", "audioconverter");    
    
    data->alsasink = gst_element_factory_make("alsasink", "audiosink");
    
    if ( !data->file_source || !data->audio_decoder || !data->audioconvert || !data->alsasink )
    {
        g_printerr ("\nNot all elements for audio pipeline were created\n");
        return FALSE;
    }    
    
    // Uncomment this if you want to see some debugging info
    //g_signal_connect( data->pipeline, "deep-notify", G_CALLBACK( gst_object_default_deep_notify ), NULL );    
    
    g_print("\nFile location: %s\n", data->filelocation);
    g_object_set (G_OBJECT (data->file_source), "location", data->filelocation, NULL);            
    
    data->bin_playback = gst_bin_new ("bin_playback");    
    
    if (strstr(data->filelocation, ".mp3"))
    {
        gst_bin_add_many(GST_BIN(data->bin_playback), data->file_source, data->audio_decoder, data->audioconvert, data->alsasink, NULL);
    
        if (gst_element_link_many (data->file_source, data->audio_decoder, NULL) != TRUE)
        {
            g_printerr("\nFile source and audio decoder element could not link\n");
            return FALSE;
        }
    
        if (gst_element_link_many (data->audio_decoder, data->audioconvert, NULL) != TRUE)
        {
            g_printerr("\nAudio decoder and audio converter element could not link\n");
            return FALSE;
        }
    
        if (gst_element_link_many (data->audioconvert, data->alsasink, NULL) != TRUE)
        {
            g_printerr("\nAudio converter and audio sink element could not link\n");
            return FALSE;
        }
    }
    
    if (strstr(data->filelocation, ".wav"))
    {
        gst_bin_add_many(GST_BIN(data->bin_playback), data->file_source, data->audio_decoder, data->alsasink, NULL);
    
        if (gst_element_link_many (data->file_source, data->audio_decoder, NULL) != TRUE)
        {
            g_printerr("\nFile source and audio decoder element could not link\n");
            return FALSE;
        }
    
        // Avoid checking of return value for linking of "wavparse" element and "alsasink" element
        // Refer http://stackoverflow.com/questions/3656051/unable-to-play-wav-file-using-gstreamer-apis
        
        gst_element_link_many (data->audio_decoder, data->alsasink, NULL);
        
        g_signal_connect(data->audio_decoder, "pad-added", G_CALLBACK(on_pad_added), data->alsasink);    
    }    
    
    return TRUE;
}

// Starts the pipeline
gboolean start_playback_pipe(gstData *data)
{
    // http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstElement.html#gst-element-set-state
    gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
    while(gst_element_get_state(data->pipeline, NULL, NULL, GST_CLOCK_TIME_NONE) != GST_STATE_CHANGE_SUCCESS);    
    return TRUE;
}

// Add the pipeline to the bin
gboolean add_bin_playback_to_pipe(gstData *data)
{
    if((gst_bin_add(GST_BIN (data->pipeline), data->bin_playback)) != TRUE)
    {
        g_print("\nbin_playback not added to pipeline\n");
        return FALSE;    
    }
    
    if(gst_element_set_state (data->pipeline, GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS)
    {        
        return TRUE;
    }
    else
    {
        g_print("\nFailed to set pipeline state to NULL\n");
        return FALSE;        
    }
}

// Disconnect the pipeline and the bin
void remove_bin_playback_from_pipe(gstData *data)
{
    gst_element_set_state (data->pipeline, GST_STATE_NULL);
    gst_element_set_state (data->bin_playback, GST_STATE_NULL);
    if((gst_bin_remove(GST_BIN (data->pipeline), data->bin_playback)) != TRUE)
    {
        g_print("\nbin_playback not removed from pipeline\n");
    }    
}

// Cleanup
void delete_pipeline(gstData *data)
{
    if (data->pipeline)
        gst_element_set_state (data->pipeline, GST_STATE_NULL);    
    if (data->bus)
        gst_object_unref (data->bus);
    if (data->pipeline)
        gst_object_unref (data->pipeline);    
}

// Function for checking the specific message on bus
// We look for EOS or Error messages
gboolean check_bus_cb(gstData *data)
{
    GError *err = NULL;                
    gchar *dbg = NULL;   
          
    g_print("\nGot message: %s\n", GST_MESSAGE_TYPE_NAME(data->message));
    switch(GST_MESSAGE_TYPE (data->message))
    {
        case GST_MESSAGE_EOS:       
            g_print ("\nEnd of stream... \n\n");
            exit_flag = TRUE;
            break;

        case GST_MESSAGE_ERROR:
            gst_message_parse_error (data->message, &err, &dbg);
            if (err)
            {
                g_printerr ("\nERROR: %s\n", err->message);
                g_error_free (err);
            }
            if (dbg)
            {
                g_printerr ("\nDebug details: %s\n", dbg);
                g_free (dbg);
            }
            exit_flag = TRUE;
            break;

        default:
            g_printerr ("\nUnexpected message of type %d\n", GST_MESSAGE_TYPE (data->message));
            break;
    }
    return TRUE;
}

int main(int argc, char *argv[])
{    
    if (argc != 2)
    {
        g_print("\nUsage: ./audiovf /home/root/filename.mp3\n");
        g_print("Usage: ./audiovf /home/root/filename.wav\n");
        g_print("Note: Number of bytes for file location: %d\n\n", NUMBER_OF_BYTES_FOR_FILE_LOCATION);
        return FALSE;
    }
    
    if ((!strstr(argv[1], ".mp3")) && (!strstr(argv[1], ".wav")))
    {
        g_print("\nOnly mp3 & wav files can be played\n");
        g_print("Specify the mp3 or wav file to be played\n");
        g_print("Usage: ./audiovf /home/root/filename.mp3\n");
        g_print("Usage: ./audiovf /home/root/filename.wav\n");
        g_print("Note: Number of bytes for file location: %d\n\n", NUMBER_OF_BYTES_FOR_FILE_LOCATION);
        return FALSE;
    }    
    
    // Initialise gstreamer. Mandatory first call before using any other gstreamer functionality
    gst_init (&argc, &argv);
    
    memset(gstreamerData.filelocation, 0, sizeof(gstreamerData.filelocation));
    strcpy(gstreamerData.filelocation, argv[1]);        
    
    if (!create_pipeline(&gstreamerData))
        goto err;        
    
    if(init_audio_playback_pipeline(&gstreamerData))
    {    
        if(!add_bin_playback_to_pipe(&gstreamerData))
            goto err;        
        
        if(start_playback_pipe(&gstreamerData))
        {
            gstreamerData.bus = gst_element_get_bus (gstreamerData.pipeline);
            
            while (TRUE)
            {
                if (gstreamerData.bus)
                {    
                    // Check for End Of Stream or error messages on bus
                    // The global exit_flag will be set in case of EOS or error. Exit if the flag is set
                    gstreamerData.message = gst_bus_poll (gstreamerData.bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
                    if(GST_MESSAGE_TYPE (gstreamerData.message))
                    {
                        check_bus_cb(&gstreamerData);
                    }
                    gst_message_unref (gstreamerData.message);            
                }            
                
                if (exit_flag)
                    break;            
                
                sleep(1);                
            }                    
        }    
        remove_bin_playback_from_pipe(&gstreamerData);                    
    }    

err:    
    delete_pipeline(&gstreamerData);
    
    return TRUE;
}

A simple Makefile for compiling the code. You need to change the path as per your OE setup.

#Notes for compilation:
#1. For compiling the code with this Makefile, a OE setup is mandatory.
#2. Before compiling, change the paths as per the setup of your environment.

CC = ${HOME}/oe-core/build/out-eglibc/sysroots/x86_64-linux/usr/bin/armv7ahf-vfp-neon-angstrom-linux-gnueabi/arm-angstrom-linux-gnueabi-gcc
INCLUDES = "-I${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/include" "-I${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/include/glib-2.0" "-I${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/lib/glib-2.0/include" "-I${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/include/gstreamer-0.10" "-I${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/include/libxml2"
LIB_PATH = "-L${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf/usr/lib"
LDFLAGS = -lpthread -lgobject-2.0 -lglib-2.0 -lgstreamer-0.10 -lgstapp-0.10
CFLAGS = -O3 -g --sysroot=${HOME}/oe-core/build/out-eglibc/sysroots/colibri-vf 

all:
    ${CC} ${CFLAGS} ${INCLUDES} ${LIB_PATH} ${LDFLAGS} -o audiovf audiovf.c

clean:
    rm -rf audiovf

Multithreaded Facial Recognition with OpenCV

It has been quiet a while since i have been maintaining this blog and giving some information and codes to work with. Lately i started noticing that this can become tedious. So from now on i will try to give access to the projects or work i do using git. I had a git account since September of 2013, but, never got around to using it.

This project is a modification of the facial recognition project which is given with Mastering OpenCV with Practical Computer Vision. The book is available with Packtpub and Amazon. The code base is here https://github.com/MasteringOpenCV and is maintained by Shervin Emami.

I was trying to do the same on a Toradex NVidia Tegra3 based Colibri T30 module which has four CPU cores. The code is single threaded and as such doesn’t detect faces if the training process is going on. I made changes to this, so that even while the training process is on going, it can still detect faces. And mind you, the training process can go on for quiet a while if there are more than 3-4 faces. So, this is basically a two threaded version of the main code along with a few more changes as per my personal requirement. You can actually go one step further to utilize three cores, though right now i can’t recall what was suppose to be the job of the third core.

I do apologize for the code not being very clean. At first i was trying to use the threading facility available with C++, but since i am no C++ expert i ran into problems which i wasn’t able to fix quickly. Decided to use pthreads, which i am much more familiar and comfortable with. You will find the C++ threading part which i was trying to do, commented out. Once i get some C++ mastery using Bruce Eckel’s Thinking in C++, i will try to do it cleanly in just C++ and clean it or clean it anyways when i get time.

You can clone the project with:

git clone https://github.com/SanchayanMaity/MultithreadedFaceRecognition.git

You need to modify the Makefile to compile the project and use it on your platform which can be a PC or an embedded board. Please do note that this project will be useful only if you are running this on a platform which has two cores or more.

Hope you guys find it useful. Cheers! And git and Linus are awesome.

Face detection with OpenCV


#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include <opencv/highgui.h>
#include "stdio.h"

int main()
{
IplImage* frame = NULL;
const char* cascade_path = "C:\\OpenCV\\opencv\\data\\haarcascades\\haarcascade_frontalface_alt2.xml";
CvHaarClassifierCascade* hc = NULL;
CvMemStorage* storage = cvCreateMemStorage(0);
CvSize minSize = cvSize(100, 100);
CvSize maxSize = cvSize(640, 480);
CvSeq* faces = NULL;
CvCapture* input_camera = NULL;

int key = 0;
int loopCounter = 0;

hc = (CvHaarClassifierCascade*)cvLoad(cascade_path, NULL, NULL, NULL);
if (hc == NULL)
{
printf("\nLoading of classifier failed\n");
return -1;
}

input_camera = cvCaptureFromCAM(-1);
if (input_camera == NULL)
{
printf("\nCould not open camera\n");
return -1;
}

//Grabs and returns a frame from camera
frame = cvQueryFrame(input_camera);
if (frame == NULL)
{
printf("\nCould not capture frame\n");
return -1;
}

cvNamedWindow("Capturing Image ...", 0);

cvResizeWindow("Capturing Image ...",
(int) cvGetCaptureProperty(input_camera, CV_CAP_PROP_FRAME_HEIGHT),
(int) cvGetCaptureProperty(input_camera, CV_CAP_PROP_FRAME_WIDTH));

while(frame != NULL)
{
faces = cvHaarDetectObjects(frame, hc, storage, 1.2, 3, CV_HAAR_DO_CANNY_PRUNING, minSize, maxSize);

for (loopCounter = 0; loopCounter < (faces ? faces->total : 0); loopCounter++)
{
CvRect *r = (CvRect*)cvGetSeqElem(faces, loopCounter);
CvPoint pt1 = { r->x, r->y };
CvPoint pt2 = { r->x + r->width, r->y + r->height };
cvRectangle(frame, pt1, pt2, CV_RGB(0, 255, 0), 3, 4, 0);
}

//Shows a frame
cvShowImage("Capturing Image ...", frame);

// Checks if ESC is pressed and gives a delay
// so that the frame can be displayed properly
key = cvWaitKey(1);
if (key == 27)        // ESC key
{
break;
}
//Grabs and returns the next frame
frame = cvQueryFrame(input_camera);
}

//cvReleaseImage( &frame );
cvReleaseCapture(&input_camera);
cvReleaseMemStorage( &storage );
cvReleaseHaarClassifierCascade( &hc );

//Destroy the window
cvDestroyWindow("Capturing Image ...");

return 0;
}

I have tested the above code on a NVidia Tegra2 with Toradex Colibri T20 module and also on my laptop. Works, but there is a bug it seems. The program crashes if i enable the cvReleaseImage() line. Not an OpenCV expert, will have to really study the functions properly before playing around i guess. Will update this as soon as i found out.

I have not explained the OpenCV installation procedure for windows as it is already documented on the OpenCV website. For using with Beagleboard you can refer to a previous article of mine https://coherentmusings.wordpress.com/2012/06/24/getting-started-with-opencv-on-beagleboard-xm/ .  You can also use OpenCV with embedded Linux if you use Buildroot or OpenEmbedded for building your image.

BB-xM – Issues in development environment set up with Buildroot on Arch Linux

After joining my new company in January, the weekdays have become very hectic. I have still not fully adjusted to this hectic lifestyle, food habits and staying away from my beautiful home in Bombay. Been damn long since i worked on Beagle board and today i finally decided to resume my activities of studying and developing in full swing.

My previous development environment for the beagle was on my own laptop which i left back at home for my parents to use. I was running Ubuntu 12.04 in VM. I have switched to Arch Linux now and use it on my company provided laptop. So, while re-installing the development environment i came across errors. I am going to mention the errors below and show how to fix it.

Error No. 1

In file included from clean-temp.h:22:0,
from clean-temp.c:23:
./stdio.h:477:1: error: ‘gets’ undeclared here (not in a function)
make[4]: *** [clean-temp.o] Error 1
make[4]: Leaving directory `/home/vm/Beagle/buildroot/output/build/m4-1.4.16/lib’

The error exists in this file:
lib/stdio.in.h

This can be fixed with the patch below


--- a/lib/stdio.in.h 2011-03-01 17:39:29.000000000 +0100
+++ b/lib/stdio.in.h 2012-07-05 22:35:32.348982786 +0200
@@ -162,7 +162,9 @@
 so any use of gets warrants an unconditional warning. Assume it is
 always declared, since it is required by C89. */
 #undef gets
+#if HAVE_RAW_DECL_GETS
 _GL_WARN_ON_USE (gets, "gets is a security hole - use fgets instead");
+#endif

 #if @GNULIB_FOPEN@
 # if @REPLACE_FOPEN@

Make a patch file in the directory where the stdio.in.h file is present and apply it as:

patch stdio.in.h < (patch_file_name)

Error No. 2

conftest.c:14625: must be after `@defmac’ to use `@defmacx’

This error occurs during building of autoconf and can be fixed with the below patch


--- a/doc/autoconf.texi
+++ b/doc/autoconf.texi
@@ -15,7 +15,7 @@
 @c The ARG is an optional argument. To be used for macro arguments in
 @c their documentation (@defmac).
 @macro ovar{varname}
-@r{[}@var{\varname\}@r{]}@c
+@r{[}@var{\varname\}@r{]}
 @end macro

 @c @dvar(ARG, DEFAULT)
@@ -23,7 +23,7 @@
 @c The ARG is an optional argument, defaulting to DEFAULT. To be used
 @c for macro arguments in their documentation (@defmac).
 @macro dvar{varname, default}
-@r{[}@var{\varname\} = @samp{\default\}@r{]}@c
+@r{[}@var{\varname\} = @samp{\default\}@r{]}
 @end macro

 @c Handling the indexes with Texinfo yields several different problems.
@@ -8014,10 +8014,10 @@ variables, respectively. The computed linker flags are cached in
 @code{ac_cv_f77_libs} or @code{ac_cv_fc_libs}, respectively.
 @end defmac

-@defmac AC_F77_DUMMY_MAIN (@ovar{action-if-found}, @dvar{action-if-not-found, @
- AC_MSG_FAILURE})
-@defmacx AC_FC_DUMMY_MAIN (@ovar{action-if-found}, @dvar{action-if-not-found, @
- AC_MSG_FAILURE})
+@defmac AC_F77_DUMMY_MAIN (@ovar{action-if-found}, @
+ @dvar{action-if-not-found, AC_MSG_FAILURE})
+@defmacx AC_FC_DUMMY_MAIN (@ovar{action-if-found}, @
+ @dvar{action-if-not-found, AC_MSG_FAILURE})
 @acindex{F77_DUMMY_MAIN}
 @cvindex F77_DUMMY_MAIN
 @acindex{FC_DUMMY_MAIN}
@@ -8268,8 +8268,8 @@ results in @code{ac_cv_fc_srcext_@var{ext}} and
 @code{ac_cv_fc_pp_srcext_@var{ext}} variables, respectively.
 @end defmac

-@defmac AC_FC_PP_DEFINE (@ovar{action-if-success}, @dvar{action-if-failure, @
- AC_MSG_FAILURE})
+@defmac AC_FC_PP_DEFINE (@ovar{action-if-success}, @
+ @dvar{action-if-failure, AC_MSG_FAILURE})
 @acindex{FC_PP_DEFINE}
 @caindex fc_pp_define

@@ -8287,8 +8287,8 @@ The result of this test is cached in the @code{ac_cv_fc_pp_define}
 variable.
 @end defmac

-@defmac AC_FC_FREEFORM (@ovar{action-if-success}, @dvar{action-if-failure, @
- AC_MSG_FAILURE})
+@defmac AC_FC_FREEFORM (@ovar{action-if-success}, @
+ @dvar{action-if-failure, AC_MSG_FAILURE})
 @acindex{FC_FREEFORM}
 @caindex fc_freeform

@@ -8314,8 +8314,8 @@ The result of this test, or @samp{none} or @samp{unknown}, is cached in
 the @code{ac_cv_fc_freeform} variable.
 @end defmac

-@defmac AC_FC_FIXEDFORM (@ovar{action-if-success}, @dvar{action-if-failure, @
- AC_MSG_FAILURE})
+@defmac AC_FC_FIXEDFORM (@ovar{action-if-success}, @
+ @dvar{action-if-failure, AC_MSG_FAILURE})
 @acindex{FC_FIXEDFORM}
 @caindex fc_fixedform

Make a patch file and the save the above in it. Apply it as

patch autoconf.texi < (patch_file_name)

The application of this patch failed in places but after applying the patch the build process went ahead.

Error No. 3

The first error also occurs during the building phase of bison. The first patch solves the issue for this as well. The patch needs to be applied to the stdio.h file in the /lib directory.

N.B. The patch files are not written by me. I have dug them up from forums like gmane.

Device node creation without using “mknod”

In my last post, where i showed how to write a character gpio driver, i had used mknod for device node creation. Without mknod the device files would not have been created under /dev. So, we had to manually create the device node under /dev using mknod. Now, cannot this manual work be done away with? Of course, it can be done!!.

The automatic creation of device files can be handled with udev. One has to ensure that the major and minor numbers assigned to a device controlled by the driver are exported to user space through the sysfs interface. To know more about this, read “The Linux Device Module” chapter from the Linux Device Drivers book.

Below i am posting the source code for the driver module, the user space application and the Makefile. The user space application and Makefile remain the same. I have only changed the name of the device node under /dev from gpio to gpio_drv. So, the user space application code accordingly reflects this.

The Driver Module:


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
#include <linux/version.h>
#include <linux/types.h>
#include <linux/kdev_t.h>
#include <linux/device.h>
#include <linux/cdev.h>

#define GPIO_NUMBER    149     //User LED 0. GPIO number 149. Page 71 of BB-xM Sys Ref Manual.

static dev_t first;         // Global variable for the first device number
static struct cdev c_dev;     // Global variable for the character device structure
static struct class *cl;     // Global variable for the device class

static int init_result;

static ssize_t gpio_read( struct file* F, char *buf, size_t count, loff_t *f_pos )
{
    char buffer[10];

    int temp = gpio_get_value(GPIO_NUMBER);

    sprintf( buffer, "%1d" , temp );

    count = sizeof( buffer );

    if( copy_to_user( buf, buffer, count ) )
    {
        return -EFAULT;
    }

    if( *f_pos == 0 )
    {
        *f_pos += 1;
        return 1;
    }
    else
    {
        return 0;
    }
}

static ssize_t gpio_write( struct file* F, const char *buf, size_t count, loff_t *f_pos )
{

    printk(KERN_INFO "Executing WRITE.\n");

    switch( buf[0] )
    {
        case '0':
        gpio_set_value(GPIO_NUMBER, 0);
        break;

        case '1':
        gpio_set_value(GPIO_NUMBER, 1);
        break;

        default:
        printk("Wrong option.\n");
        break;
    }
    return count;
}

static int gpio_open( struct inode *inode, struct file *file )
{
    return 0;
}

static int gpio_close( struct inode *inode, struct file *file )
{
    return 0;
}

static struct file_operations FileOps =
{
    .owner        = THIS_MODULE,
    .open         = gpio_open,
    .read         = gpio_read,
    .write        = gpio_write,
    .release      = gpio_close,
};

static int init_gpio(void)
{
    //init_result = register_chrdev( 0, "gpio", &FileOps );

    init_result = alloc_chrdev_region( &first, 0, 1, "gpio_drv" );

    if( 0 > init_result )
    {
        printk( KERN_ALERT "Device Registration failed\n" );
        return -1;
    }
    //else
    //{
    //    printk( KERN_ALERT "Major number is: %d\n",init_result );
    //    return 0;
    //}

    if ( (cl = class_create( THIS_MODULE, "chardev" ) ) == NULL )
    {
        printk( KERN_ALERT "Class creation failed\n" );
        unregister_chrdev_region( first, 1 );
        return -1;
    }

    if( device_create( cl, NULL, first, NULL, "gpio_drv" ) == NULL )
    {
        printk( KERN_ALERT "Device creation failed\n" );
        class_destroy(cl);
        unregister_chrdev_region( first, 1 );
        return -1;
    }

    cdev_init( &c_dev, &FileOps );

    if( cdev_add( &c_dev, first, 1 ) == -1)
    {
        printk( KERN_ALERT "Device addition failed\n" );
        device_destroy( cl, first );
        class_destroy( cl );
        unregister_chrdev_region( first, 1 );
        return -1;
    }

    return 0;
}

void cleanup_gpio(void)
{
    //unregister_chrdev( init_result, "gpio" );

    cdev_del( &c_dev );
    device_destroy( cl, first );
    class_destroy( cl );
    unregister_chrdev_region( first, 1 );

    printk(KERN_ALERT "Device unregistered\n");
}

module_init(init_gpio);
module_exit(cleanup_gpio);

MODULE_AUTHOR("Sanchayan");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Beagleboard-xM GPIO Driver");

The User Space Application:


#include <stdio.h>
#include <fcntl.h>

int main(void)
{
    int fd;
    char gpio_buffer[10];
    char choice[10];

    fd = open( "/dev/gpio_drv", O_RDWR );

    printf( "Value of fd is: %d", fd );

    if( fd < 0 )
    {
        printf("Cannot open device \t");
        printf(" fd = %d \n",fd);
        return 0;
     }

     printf("\nPlease enter choice: \t");
     scanf( "%s", choice );
     printf("Your choice is: %s \n", choice );
     write( fd, choice, 1 );
     read( fd, gpio_buffer, 1);
     printf("GPIO value is: %s \n", gpio_buffer );

     if( 0 != close(fd) )
     {
         printf("Could not close device\n");
     }

     return 0;
}

Makefile:

# Cross compilation Makefile for ARM

KERN_SRC=/home/vm/buildroot/output/build/linux-3.2.8
KERN_COMPILER=/opt/CodeSourcery/Sourcery_CodeBench_Lite_for_ARM_GNU_Linux/bin

obj-m := gpio.o

all:
make -C $(KERN_SRC) ARCH=arm CROSS_COMPILE=$(KERN_COMPILER)/arm-none-linux-gnueabi- M=`pwd` modules

clean:
make -C $(KERN_SRC) ARCH=arm CROSS_COMPILE=$(KERN_COMPILER)/arm-none-linux-gnueabi- M=`pwd` clean

The path for your kernel source and path for the CodeSourcery toolchain can and mostly will be different. So change them, as per your environment.

After this, compile your kernel driver module by typing ‚Äúsudo make‚ÄĚ at the command line prompt. After this, cross compile your user space application by typing

arm-none-linux-gnueabi-gcc   gpio_app.c   -o   gpio_app

I have assumed that the cross compiler path has been added to your path environment variable.

You will now have a gpio.ko file and a gpio_app executable. Transfer these files to your beagleboard. I use the ‚Äúscp‚ÄĚ command to transfer files to my board over the ssh connection.

On the command prompt of your beagleboard, do ‚Äúinsmod gpio.ko‚ÄĚ.

This time, you won’t have to do the mknod for device node creation under /dev. You can cd to /sys/class and you will find the chardev entry there. Do ls /dev and you will find gpio_drv already present without having run mknod.

By default, the user led’s 0 and 1 which are connected on GPIO 149 and 150, are used for indicating mmc card access activity and heartbeat.

You can turn these dafault behaviour off by first entering the /sys/class/gpio/beagleboard::usr0 and /sys/class/gpio/beagleboard::usr1 directory. After this, do ‚Äúcat trigger‚ÄĚ. You should see the default behaviour marked.

Now, change the default behaviour for both by doing ‚Äúecho none > trigger‚ÄĚ in their respective directories.

Now, run the user space application by typing ./gpio_app on the command line prompt. You will be prompted to enter the value “0″ or “1″ to turn off or turn on the led. You can observe the state of the led pin on your board after this.

Refer the Linux Device Drivers book by Corbet, Rubini and Greg Kroah Hartman. The second, third and fourteenth chapters are important for this tutorial.

Refer this link http://www.linuxforu.com/2011/04/character-device-files-creation-operations/.  as well. Thanks to Anil Pugalia for his wonderful series on Linux Device Drivers available on http://www.linuxforu.com/tag/linux-device-drivers-series/.

Beagleboard-xM GPIO Kernel Module Driver

Whenever i search for a particular implementation, of let’s say GPIO, only user space implementation comes up in search results which use the sysfs interface. Being more interested in hardware and the kernel side of things, i thought i did start practicing writing some kernel module drivers. So, here is my first simple GPIO kernel module driver for the Beagleboard-xM.

I have set up my environment using Buildroot from the below link. I used this, because at some point i intend to use the camera module and Max Galemin has integrated those packages in his buildroot version. OpenCV 2.3.1 is supported in this buildroot, so that’s a very good plus point. Set up your environment using the instructions given on the below link or i assume you can use the standard buildroot available as well.

http://blog.galemin.com/2012/03/buildroot-2012-02-for-beagleboard-xm-with-li-5m03-mt9p031-camera-support/

You can do the default build or you might want to add support for php, python, lighthttpd, nfs, thttpd and a few other things as well.

Below i am posting the source code for the driver module, the user space application and the Makefile.

The Driver Module:  gpio.ko


#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <asm/uaccess.h>

#define GPIO_NUMBER    149 //User LED 0. GPIO number 149. Page 71 of BB-xM Sys Ref Manual.

static int init_result;

static ssize_t gpio_read( struct file* F, char *buf, size_t count, loff_t *f_pos )
{
char buffer[10];

int temp = gpio_get_value(GPIO_NUMBER);

sprintf( buffer, "%1d" , temp );

count = sizeof( buffer );

if( copy_to_user( buf, buffer, count ) )
{
return -EFAULT;
}

if( *f_pos == 0 )
{
*f_pos += 1;
return 1;
}
else
{
return 0;
}

}

static ssize_t gpio_write( struct file* F, const char *buf, size_t count, loff_t *f_pos )
{

printk(KERN_INFO "Executing WRITE.\n");

switch( buf[0] )
{
case '0':
gpio_set_value(GPIO_NUMBER, 0);
break;

case '1':
gpio_set_value(GPIO_NUMBER, 1);
break;

default:
printk("Wrong option.\n");
break;
}

return count;
}

static int gpio_open( struct inode *inode, struct file *file )
{
return 0;
}

static int gpio_close( struct inode *inode, struct file *file )
{
return 0;
}

static struct file_operations FileOps =
{
.open         = gpio_open,
.read         = gpio_read,
.write         = gpio_write,
.release     = gpio_close,
};

static int init_gpio(void)
{
init_result = register_chrdev( 0, "gpio", &FileOps );

if( 0 > init_result )
{
printk(KERN_ALERT "Device Registration failed\n");
return -1;
}
else
{
printk(KERN_ALERT "Major number is: %d\n",init_result);
return 0;
}
}

void cleanup_gpio(void)
{
unregister_chrdev( init_result, "gpio" );
printk(KERN_ALERT "Device unregistered\n");

}

module_init(init_gpio);
module_exit(cleanup_gpio);

MODULE_AUTHOR("Sanchayan");
MODULE_LICENSE("GPL");

The User Space Application: gpio_app.c


#include<stdio.h>
#include <fcntl.h>

int main(void)
{
int fd;
char gpio_buffer[10];
char choice[10];

fd = open( "/dev/gpio", O_RDWR );

printf( "Value of fd is: %d", fd );

if( fd < 0 )
{
printf("Cannot open device \t");
printf(" fd = %d \n",fd);
return 0;
}

printf("\nPlease enter choice: \t");
scanf( "%s", choice );
printf("Your choice is: %s \n", choice );
write( fd, choice, 1 );
read( fd, gpio_buffer, 1);
printf("GPIO value is: %s \n", gpio_buffer );

if( 0 != close(fd) )
{
printf("Could not close device\n");
}

return 0;
}

Makefile


KERN_SRC=/home/vm/buildroot/output/build/linux-3.2.8
KERN_COMPILER=/opt/CodeSourcery/Sourcery_CodeBench_Lite_for_ARM_GNU_Linux/bin

obj-m := gpio.o

all:
make -C $(KERN_SRC) ARCH=arm CROSS_COMPILE=$(KERN_COMPILER)/arm-none-linux-gnueabi- M=`pwd` modules

clean:
make -C $(KERN_SRC) ARCH=arm CROSS_COMPILE=$(KERN_COMPILER)/arm-none-linux-gnueabi- M=`pwd` clean

The path for your kernel source and path for the CodeSourcery toolchain can and mostly will be different. So change them, as per your environment.

After this, compile your kernel driver module by typing “sudo make” at the command line prompt. After this, cross compile your user space application by typing

arm-none-linux-gnueabi-gcc   gpio_app.c   -o   gpio_app

I have assumed that the cross compiler path has been added to your path environment variable.

You will now have a gpio.ko file and a gpio_app executable. Transfer these files to your beagleboard. I use the “scp” command to transfer files to my board over the ssh connection.

On the command prompt of your beagleboard, do “insmod gpio.ko”. Find out the major number by checking the dmesg output or /proc/devices.

Assuming for example, that the major number is 248, do “mknod /dev/gpio c 248 0”.

The mknod command has to be run for creation of the device node. If mknod is not run, you can see your module loaded using lsmod, but, you won’t notice any entry in the /dev directory with which to access it through your user space application. After running mknod you can see the gpio driver entry in the /dev directory with ls /dev.

By default, the user led’s 0 and 1 which are connected on GPIO 149 and 150, are used for indicating mmc card access activity and heartbeat.

You can turn these dafault behaviour off by first entering the /sys/class/gpio/beagleboard::usr0 and /sys/class/gpio/beagleboard::usr1 directory. After this, do “cat trigger”. You should see the default behaviour marked.

Now, change the default behaviour for both by doing “echo none > trigger” in their respective directories.

Now, run the user space application by typing ./gpio_app on the command line prompt. You will be prompted to enter the value “0” or “1” to turn off or turn on the led. You can observe the state of the led pin on your board after this.

Refer the Linux Device Drivers book by Corbet, Rubini and Greg Kroah Hartman. The second and third chapters are important for this tutorial.

What is MLO file?

I have had the Beagle-xM for a while now. If you have worked on it, you must have noticed the MLO file, which is required to be present in the boot partition of the Micro SD card. The file is necessary for booting the system. So ever wondered, what is this MLO file? No? What are you doing with the Beagle-xM anyways? Yes? Well, then read on.

As far as i know, all TI SOC’s like DM368 and DM3730 (on which i have worked) have a ROM bootloader. On sytem start up or reset, the ROM bootloader (RBL) starts running. The job of this RBL, is to do some very bare minimum initialisation and then find and boot from a device such as MMC card or flash. The RBL has got the capability to boot from a variety of devices like NOR flash, NAND flash, MMC, USB or UART. To know everything in detail, read the 26th chapter of the DM3730 Technical Reference Manual having the TI document number SPRUGN4N.

Now, how does the chip know which device to boot from you ask?. The chip has got a set of pins, sys_boot[4:0] pins, whose state decides what memory or peripheral to boot from. Also, there is a predefined booting order in the case of both memory booting or peripheral booting. For example, let’s say the state of sys_boot[4:0] pins is 0b01110. Then, the booting order will be XIPwait, DOC, USB, UART3 and finally MMC1. XIPwait is Execute in Place memory with wait monitoring and DOC is DiskOnChip memory. You can find this information on Page 3549 of the DM3730 TRM.

In the case of Beagle-xM, the state of the sys_boot pins cause it to boot from the MMC card at the start. As a result, on power on, the ROM will initialise the SD/MMC device, detect a card and look for a boot image. The ROM is capable of reading from the card with a filesystem. It will search for a file named MLO which should be located on the boot partition, on a partition of type FAT16/32. So, this explains the name MLO.

Now, let’s talk about the flow of control. The sys_boot pins specify where the first external boot loader MLO can be found. After the MLO is located and executed, it passes the control to the second external boot loader viz. uboot. Have you seen the uboot.bin file in the boot partition of the micro SD card? No? Jeez, have you been sleeping? U-boot is the application which passes control to the Linux system.¬† The main goal of u-boot is to retrieve the Linux kernel and provide the kernel with information about the location of the Linux filesystem.¬† U-boot can be configured to retrieve the kernel from a variety of options like NFS or flash. U-boot then boots the Linux kernel.¬† The Linux kernel mounts the Linux root filesystem.

In case, you are interested further in knowing the boot sequence, below is a link, which describes the boot sequence. http://blog.techveda.org/ . It uses AT91RM9200 as the reference, but, still should be very informative enough. Also, you can use this link http://lxr.free-electrons.com/ to browse the source.

Hope you found this “What is MLO file?” post informative.

Addendum: Mikkel Nielsen shared quite a few important links and had this to quote. You can also find this in the comments.

Think it could also be Memory LOader, as it is a file containing (apart from metadata) binary code that is loaded directly into internel SoC memory.

TI also refers to it as X-loader; http://processors.wiki.ti.com/index.php/Boot_Sequence.

This suggests it is short for Memory LOcator; https://groups.google.com/forum/#!msg/beagleboard/M-ew_WGjE_A/W2OerOFV3yYJ

Some history about why it is like that here; http://lists.denx.de/pipermail/u-boot/2013-December/168481.html.

OpenCV on Beagleboard-xM | Getting started

Before i start, let me mention a few things which i am assuming.

1. You know how to use the vi Editor.

2. You are connected over a serial link to the BB – xM and are doing all operations from the terminal.

Get the Angstrom Image from the below link and set up the SD card as per  the instructions given.

http://elinux.org/Getting_a_Workshop_SD_Image

After following the set up procedure, you will have a boot partition and root filesystem partition on the SD card.

After setting up the SD card, transfer an image in which you want faces to be detected. This image you can transfer to the /home/root directory in the root filesystem on the SD card.

Plug the SD card and boot up. The board will boot up and you will get the login prompt. Log in using root as the password, after which, you will be placed in the /home/root directory. This is the reason i told you to copy the image in this particular directory.

Use vi and open up a file faces.c by typing “vi faces.c” on the command line. Write the below code in it.

#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include "stdio.h"
#include "string.h"
int main(void)
{
    const char * cascade_path = "/usr/share/opencv/haarcascades/haarcascade_frontalface_default.xml";
    IplImage * img;
    CvHaarClassifierCascade * hc;
    CvSeq * faces;
    CvMemStorage * storage = cvCreateMemStorage(0);
    CvSize minSize = cvSize(20, 20);
    CvSize maxSize = cvSize(60, 60);

   img = cvLoadImage("faces.jpg",0);  //Assuming faces is in the directory the code is in.
   if(img)
   {
       printf("Success");
       printf("\n");
   }
   else
   {
       printf("Failed");
       printf("\n");
   }

   hc = (CvHaarClassifierCascade*)cvLoad(cascade_path,NULL,NULL,NULL);
   faces = cvHaarDetectObjects(img, hc, storage, 1.2, 2, CV_HAAR_DO_CANNY_PRUNING, minSize, maxSize);

   if(faces)
   {
       printf("faces detected %d\n",faces->total);
   }
   else
   {
       printf("cvHaarDetectObjects returned null\n");
   }

   cvReleaseHaarClassifierCascade( &hc );
   cvReleaseImage( &img );
   cvReleaseMemStorage( &storage );

   return 0;

}

After saving the above program, compile it using the command:

arm-angstrom-linux-gnueabi-gcc   faces.c   /usr/lib/libopencv_*.so   -o   faces

I have mentioned /usr/lib/libopencv_*.so on the command line, as the compiler would then dynamically link all the OpenCV library files against this code.

This will create a executable called faces. Run it from the command line by typing “./faces”. The code would run and print the number of faces detected in the image on the command line.

I didn’t have a HDMI to DVI-D cable to connect my monitor to the board, so i printed the output on the command line. If you can connect a monitor, use the functions from the highgui library, and you can display your output on the monitor. You can draw rectangles around the faces detected.

I am currently working on face detection in video and facing a few glitches, but, i will post it when i am successful.

To learn what the functions actually do, refer to the OpenCV 2.2 C reference on the below link.

http://opencv.jp/opencv-2.2_org/c/.

Disclaimer: I have modified the example code given in the reference documentation to suit my own needs.