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.

TCP echo server with lwip

LWIP stands for Lightweight Internet Protocol stack and was developed by Adum Dunkels at the Swedish Institute Of Computer Science. It is open source TCP/IP stack designed mainly for embedded systems and written in C.

Today, i will give you guys a sample code for implementing TCP/IP echo server. The assumption is that, you have lwip already ported to your controller. So, here is the code.

char tcp_buffer[1024];
static void close_conn (struct tcp_pcb *pcb )
{
    tcp_arg(pcb, NULL);
    tcp_sent(pcb, NULL);
    tcp_recv(pcb, NULL);
    tcp_close(pcb);
}
static err_t echo_recv( void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err )
{
    int i;
    int len;
    char *pc;

    if ( err == ERR_OK && p != NULL )
    {
        tcp_recved( pcb, p->tot_len );
        pc = (char *)p->payload;
        len =p->tot_len;

        for( i=0; i<len; i++ )
        {
            tcp_buffer[i] = pc[i];
        }

        if( tcp_buffer[0] == 'X' )
            close_conn( pcb );

        pbuf_free( p );

        if( len > tcp_sndbuf( pcb ) )
            len= tcp_sndbuf( pcb );

        tcp_write( pcb, tcp_buffer, len, 0 );
        tcp_sent( pcb, NULL );
    }
    else
    {
        pbuf_free( p );
    }

    if( err == ERR_OK && p == NULL )
    {
        close_conn( pcb );
    }

    return ERR_OK;
}
static err_t echo_accept(void *arg, struct tcp_pcb *pcb, err_t err )
{
    LWIP_UNUSED_ARG( arg );
    LWIP_UNUSED_ARG( err );
    tcp_setprio( pcb, TCP_PRIO_MIN );
    tcp_recv( pcb, echo_recv );
    tcp_err( pcb, NULL );
    tcp_poll( pcb, NULL, 4 );
    return ERR_OK;
}
void tcp_init( void )
{
    struct tcp_pcb *tcp_pcb;
    tcp_pcb = tcp_new();
    tcp_bind(tcp_pcb, IP_ADDR_ANY, 23);

    tcp_pcb = tcp_listen( tcp_pcb );
    tcp_accept( tcp_pcb, echo_accept );
}

Instructions for use:

1. Set up lwip stack with a call to lwipInit function.

2. Call tcp_init() before entering while(1). Everything will be running in an interrupt context, so if you want to test this application alone, your while(1) can be empty.

Let me explain how the code works.

First, on call to the tcp_init function, a pointer to tcp_pcb structure is created. pcb is the short form of Protocol Control Buffer. Then a new tcp_pcb structure is created, with a call to tcp_new function, which returns a pointer, which is assingned to the pointer we just declared. This is then bound to the pcb structure, by call to tcp_bind and passing the pointer to tcp_pcb structure as the first argument, binding it to any ip address, which the board is assigned or gets through dhcp, as the second argument and finally the tcp port number on which communication will take place as the third argument. A call to tcp_listen puts the connection in a listening state for any incoming connections. The tcp_accept function is passed the pointer we have been using till now, and the name of the function as the second argument, which will be called, when an incoming connection request arrives on the listening connection. This is an implementation of callback functions. Google it. Here is a link http://www.newty.de/fpt/callback.html.

When an incoming connection request arrives, the echo_accept function is called. The main line here is the fourth line. It passes the relevant pointer as the first argument and the function to be called when new data arrives.

On data arrival, the echo_recv function gets called. tcp_recved must be called when the application has received the data. The len argument indicates the length of the received data. After this, the data is taken (should be self explanatory on how the data is extracted and what happens when an “X” is sent) and send back with a call to tcp_write and tcp_sent.

In case, you are using it with Stellaris, to which it has already been ported to by Texas Instruments, go the StellarisWare folder. In it, go to third_party, lwip-1.3.1 (your lwip version may be different) and then docs. Open and read the rawapi document in it, to understand the use of these functions.

Hope, that helps.

N.B. Every function in lwip returns error codes. I have ignored the return codes in most of the cases above. To make a more robust application, perform your operation based on what return codes you get.

 

Cinnamon with Ubuntu

For haters of unity, despair not, we have Cinnamon.

The setup is very simple.Open up a terminal and type the following one by one.

sudo add-apt-repository ppa:gwendal-lebihan-dev/cinnamon-stable
sudo apt-get update
sudo apt-get install cinnamon

Once this stage is complete, log out, change the environment and log in.And you have the good old desktop interface. Thanks to the developers of Cinnamon.

The side panel interface may be good for touch screen smartphones, but, sucks when it comes to desktop. May be the idiots at Canonical and others will realise this some time.

Alternatively, you can just use the superb Linux Mint distro.

Update: I recently installed the Ubuntu 12.04 LTS, previous to which i was using Ubuntu 11.10. The above did not work in one shot in 12.04. Even APT package manager, didn’t work at start. Had to upgrade the system for APT to work. I haven’t found a proper fix for it yet which worked, will post as soon as i find one.

CGI and SSI with lwip on Stellaris

CGI stands for Common Gateway Interface and SSI stands for Server Side Include. The below post is going to be about, how to use them with lwip for employing web page interface in your embedded system.

First, i would like to give some clarification. My work on the above was build up over and above the enet_io example code provided by Texas Instruments with their Stellaris LM3S6965 boards. My main aim of this post is to help you in understanding the code flow and is very specific to the example provided. I have not done any work in porting lwip nor written this application from ground up.

So let’s get started.

You will need to have the html code and the program code side by side to understand what’s going on.

With web pages, you would like to display some data and send data entered on the web page to your embedded system.

1. SSI Server Side Includes (Displaying data on web page)

When a web page request is sent from a browser to your embedded system, which has got lwip configured with CGI and SSI, if any SSI tag is present on the web page being served, the SSI handler is called. These handlers are registered with a call to the function http_set_ssi_handler(SSIHandler, g_pcConfigSSITags, NUM_CONFIG_SSI_TAGS), where SSIHandler is the function which gets called, when the web page is served containing SSI tags. g_pcConfigSSITags is an array containing the list of SSI tags. These names you will find in the html code of the web page and in the program code file  enet_io.c.  NUM_CONFIG_SSI_TAGS is the number of individual SSI tags, the http server can expect to find.

To understand what i said, let me take you through the enet_io code. Open the folder fs in the code directory, and open the htm web page with a web brower.  In the web page, the SSI tag corresponding to this, can be found in I/O Control Demo2 link. Use view source, by right clicking on the web page. The SSI tag will be like <!–#PWMfreq–>. In the g_pcConfigSSITags array, you will see “PWMfreq”. So, whenever the system is serving this web page, as there is an occurrence of an SSI tag, the SSI handler is called, and the value of PWMfreq is substituted by the actual value. In the SSI handler, you will find something like below,

case SSI_INDEX_PWMFREQ:
ulVal = io_get_pwmfreq();
usnprintf(pcInsert, iInsertLen, “%d”, ulVal);
break;

When the user requests the web page, on seeing the occurence of the SSI tag, the SSI handler gets called, the case corresponding to PWMFreq, is selected and executed, the io_get_pwmfreq() returns the value of pwm frequency on the system, and this is inserted in the web page by usnprintf function. This gets done for each SSI tag in the web page. In case, you have defined a tag on the web page, but, not written a corresponding entry in the tags array and the handler, you will get an error on the web page, complaining no tag found.

To summarise, register your SSI handler, include the SSI tag in the tags array, include a corresponding entry in the handler, which will return its value. And viola!. You can now display values on web pages.

2. CGI Common Gateway Interface (Sending data to the system)

Lets say you enter a new value of PWM frequency and click on Update Settings. On clicking, just check what url is displayed on the address bar of your browser. I entered a value of 50 for PWM frequency and the url changed to “file:///C:/StellarisWare/boards/ek-lm3s6965/enet_io/fs/iocontrol.cgi?LEDOn=1&PWMFrequency=50&PWMDutyCycle=100&Update=Update+Settings”. ( N.B. I was not connected to the board while writing this and executed it from my local drive, so the file url). You can see, how the value of 50 was appended and send to the system. Also, notice the iocontrol.cgi in the url. This is defined along with the corresponding CGI handler in the g_psConfigCGIURIs array. When your board, receives this web page request, the lwip stack knows which function to call. The handlers are registered with a call to http_set_cgi_handlers(g_psConfigCGIURIs, NUM_CONFIG_CGI_URIS).  In the corresponding CGI handler, the parameters are extracted from the URL, by FindCGIParameter function and GetCGIParam function, and the functions are called for setting these values.

This is how data gets passed to the system using CGI through web pages.

I hope, i was able to at least clear a little bit for you to start off on your own and make changes you want. In case, i missed something, do post a comment and i will try to help you out.

Debugging a hard fault in ARM Cortex M3

I have been working on the Stellaris LM3S6965 for quiet a while now. So i thought, may be i will put my learning’s in one place, so others can use them and not get stuck, where i did.

The Stellaris LM3S6965 is based on the ARM Cortex M3 architecture. What i am going to mention in my post, is how to track down the source of a hard fault. So let’s get straight to the point.

On getting a hard fault, check the value of MSP and PSP viz. the stack pointers. It’s likely the MSP, if you are not using an RTOS.

Go to the address contained in the MSP. The following registers would be on the stack in increasing order,

Registers ro – r3, r12, lr, pc, xpsr.

One thing i must mention is, if your fault handler does something other than spinning in a loop, a couple of other registers might be pushed on to the stack by the compiler. You need to take care of this. I am assuming here, that the default fault handler does nothing else besides spinning in a loop.

So take a look at the stack. The pushed PC should help you in debugging, as it usually points to the instruction that caused the fault.

The next place to look is in the NVIC viz. the Nested Vector Interrupt Controller. The Configurable Fault Status Register is at address 0xE000ED28 and contains bits that identify the cause of the fault and can also tell if the stacked PC actually points to the faulting instruction. There are a couple of other registers, the Memory Manage Fault Address register and the Bus Fault Address register which provide the address of the fault for those kinds of faults. But, you need to look at the status register first to identify the cause of the fault and that will tell you if the BFAR or MMFAR contains a useful address.

To learn more, refer to the ARM Cortex M3 technical reference manual. If you would like to learn about ARM architecture in general, the ARM System Developer’s Guide by Andrew Sloss, Dominic Symes and Chris Wright is a very good place to start.