Go Down

Topic: Arduino data acquisition using ANSI C in VS2005 (Read 1 time) previous topic - next topic

Jael

Feb 12, 2009, 07:04 pm Last Edit: Feb 13, 2009, 03:47 pm by henrique21 Reason: 1
Hello everybody. I'm new to the Arduino forum and let me start by thanking you all for your contributions to the forum. It has been a huge help resource for me.

I am working on a Quadrotor that will use Arduino to do all the sensor processing, control and handle all telemetry.

The sensors i am using are an ADXL330 accelerometer and a HMC6352 compass.

Currently, i am testing data acquisition, using a serial port, to a computer. The program that reads the data was coded in ANSI C using Visual Studio 2005. The purpose of the code is for a later use in MatLab to produce a Simulink Block for data acquisition.

The message i am sending is something like:
"x2y1z99c350e" (where "e" stands for "message end").
in other words:
"x<xvalue>y<yvalue>z<yvalue>c<compass angle>e"

The problem is that the C program keeps crashing. I have tested and modified the program in several ocasions, googled and researched a lot, but i was not able to solve it so far. There are occasions when the program runs well, but as soon as i toutch the sensors it crashes. Bellow i show the code for the Arduino code, the ANSI C program, and an example of the program execution (as a console application).

Arduino code:
Code: [Select]

#include <Wire.h> //Library for I2C implementation using analog pins 4 (SDA) and 5 (SCL)

//Accelerometer variables
float ax,ay,az; //Acceleration values in m/s^2 for all three axis
float as=0.0049; //Arduino analog resolution (V/unit)
float zg=1.65; //Sensor zero g = Input Voltage/2 (V)
float sR=0.329; //Sensor resolution (V/g)
int fact=100; //Multiplicatin factor for sending data through the serial port

//Compass variables
int compassAddress = 0x42 >> 1;  // compass I2C slave adress: 0x42
                                // because the wire.h library only uses the 7 bits most significant bits
                                // a shift necessary is to get the most significant bits.
int reading = 0;                 // Compass heading in degrees

void setup()
{
 Wire.begin();
 Serial.begin(9600); // Initialize serial communications with setup
}

void loop()
{
 // get the most recent acceleration values for all three axis
 ax = (analogRead(0)*as-zg)/sR;
 ay = (analogRead(1)*as-zg)/sR;
 az = (analogRead(2)*as-zg)/sR;  
 
 // Compass task 1: connect to the HMC6352 sensor
 Wire.beginTransmission(compassAddress);
 Wire.send('A');             // The ascii character "A" tells the compass sensor to send data
 Wire.endTransmission();
 
 // Compass task 2: wait for data processing
 delay(10);   // Compass datasheet says we need to wait at least 6000 microsegundos (0.006s) for data processing the sensor
 
 // Compass task 3: Request heading
 Wire.requestFrom(compassAddress, 2);    // request 2 bytes of data
 
 // Compass task 4: Get heading data
 if(2 <= Wire.available())    // if 2 bytes are available
 {
   //16bit numbers can be broken in two 8 bit chunks. The first is called high byte and the second low byte
   reading = Wire.receive();  // get high byte
   reading = reading << 8;    // shift high byte
   reading += Wire.receive(); // get low byte
   reading /= 10;           // comment this line if you wish to get the heading in decidegrees instead of degrees
 }

 // output sensor data
 Serial.print("x");
 Serial.print(ax*fact,DEC);  //Serial.print can not send floats, we can however multiply a decimal number by 100 and convert it into an integer
 Serial.print("y");
 Serial.print(ay*fact,DEC);
 Serial.print("z");
 Serial.print(az*fact,DEC);
 Serial.print("c");
 Serial.print(reading,DEC);
 Serial.print("e");
 
 Serial.flush();
}


ANSI C code:
Code: [Select]

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

HANDLE hSerial;

int serialOpen(char *name);
BOOL serialRead(unsigned char *byte);
int serialWrite(unsigned char *data, int size);
void getSensorData(int *x,int *y,int *z,int *c);

//open Serial port; pass "COM1"/"COM2"/... for file name
int serialOpen(char *name)
{
     DCB dSerial;

     hSerial = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
     if(hSerial == INVALID_HANDLE_VALUE)
           return -1;

   if(!GetCommState(hSerial, &dSerial))
           return -2;

   dSerial.BaudRate=CBR_9600;
   dSerial.ByteSize=8;
   dSerial.StopBits=ONESTOPBIT;
   dSerial.Parity=NOPARITY;

   if(!SetCommState(hSerial, &dSerial))
           return -3;

     return 0;
}

//read a single byte from the serial port
BOOL serialRead(unsigned char *byte)
{
     int readSize;

     if((ReadFile(hSerial, byte, 1, &readSize, NULL)) && (readSize>0))
           if(readSize)
                 return TRUE;

     return FALSE;
}

void getSensorData(int *x, int *y, int *z, int *c)
{
     unsigned char b[1]={0}; //serial 1 char buffer
     unsigned char valx[5]={0}; //buffer for accelerometer x value string
     unsigned char valy[5]={0}; //buffer for accelerometer y value string
     unsigned char valz[5]={0}; //buffer for accelerometer z value string
     unsigned char valc[4]={0}; //buffer for compass angle string
     unsigned char rbuffer[16]={0}; //buffer for Arduino message
     int isx=0; //check for 'x' char in message
     int isy=0; //check for 'y' char in message
     int isz=0; //check for 'z' char in message
     int isc=0; //check for 'c' char in message
     int ise=0; //check for 'e' char in message
     int i=0; //counter

     i=0;
     do{
           serialRead(&b);
           rbuffer[i]=b[0];
           if(rbuffer[i]=='x') isx=1;
           if(rbuffer[i]=='y') isy=1;
           if(rbuffer[i]=='z') isz=1;
           if(rbuffer[i]=='c') isc=1;
           if(rbuffer[i]=='e') ise=1;
           i++;
           if(b[0]=='e' && (isx == 0 || isy == 0 || isz ==0 || isc == 0 || ise == 0)) //check for message integrity
           {
                 i=0;
                 isx=0;
                 isy=0;
                 isz=0;
                 isc=0;
           }
     }while(b[0]!='e' && i<16 && (isx == 0 || isy == 0 || isz == 0 || isc == 0 || ise == 0));
     
     printf("%s, %d\n",rbuffer, i); //print received message

           if(rbuffer[0]=='x') //Extract accelerometer x value from message string
           {
                 i=1;
                       do{
                             strncat(valx,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='y');
                 *x=atoi(valx);
           }

           if(rbuffer[i]=='y') //Extract accelerometer y value from message string
           {
                 i++;
                       do{
                             strncat(valy,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='z');
                 *y=atoi(valy);
           }

           if(rbuffer[i]=='z') //Extract accelerometer z value from message string
           {
                 i++;
                       do{
                             strncat(valz,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='c');
                 *z=atoi(valz);
           }

           if(rbuffer[i]=='c') //Extract compass angle from message string
           {
                 i++;
                       do{
                             strncat(valc,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='e');
                 *c=atoi(valc);
           }
}

void main(void)
{
     int xx=0; //accelerometer x axis value
     int yy=0; //accelerometer y axis value
     int zz=0; //accelerometer z axis value
     int cc=0; //compass angle value
     int i=0; //counter

serialOpen("COM3"); //Open serial port
Sleep(2500);

     while(!kbhit())
     {
           getSensorData(&xx,&yy,&zz,&cc);
           printf("i:%d x:%d y:%d z:%d c:%d\n",i,xx,yy,zz,cc);
           Sleep(10);
           i++;
     }

CloseHandle(hSerial); //Close serial port
}


Example run of the ANSI C program:
Code: [Select]

i:600 x:4 y:1 z:89 c:19
x4y3z89c19e, 11
i:601 x:4 y:3 z:89 c:19
x3y3z88c19e, 11
i:602 x:3 y:3 z:88 c:19
x3y6z88c22e, 11
i:603 x:3 y:6 z:88 c:22
x4y4z88c22e, 11
i:604 x:4 y:4 z:88 c:22
x6y3z91c22e, 11
i:605 x:6 y:3 z:91 c:22
x6y3z88c22e, 11
i:606 x:6 y:3 z:88 c:22
x7y6z92c26e, 11
i:607 x:7 y:6 z:92 c:26
x6y4z92c28e, 11
i:608 x:6 y:4 z:92 c:28
x7y6z91c28e, 11
i:609 x:7 y:6 z:91 c:28
x7y6z91c26e, 11
i:610 x:7 y:6 z:91 c:26
x7y4z92222222222[ch9568][ch9568][ch9568][ch9568][ch9568][ch9568][ch9568][ch9568], 16


As you can see from the example above, i was able to do 609 readings until i decided to move the sensors and the program crashed (again).

Thanks in advance to all of you :).

mikalhart

#1
Feb 12, 2009, 07:25 pm Last Edit: Feb 12, 2009, 07:26 pm by mikalhart Reason: 1
Jael,

Just at first glance it looks like your C program doesn't handle the case correctly where no Serial data has yet arrived.  When you call

Code: [Select]
serialRead(&b);

you are not counting on the possibility that serialRead() might return FALSE;  I would try replacing that line with

Code: [Select]
while (!serialRead(&b)) ; // do nothing until serialRead returns TRUE

Mikal

Jael

Mikalhart, thank you for your reply.

I have implemented your suggestions and although the program seemed to be more stable, it eventually crashed again:

Quote

i:242 x:4 y:3 z:88 c:330
x4y3z89c330e, 12
i:243 x:4 y:3 z:89 c:330
x4y0z86c331e, 12
i:244 x:4 y:0 z:86 c:331
x3y1z88c329e, 12
i:245 x:3 y:1 z:88 c:329
x4y1z89c330e, 12
i:246 x:4 y:1 z:89 c:330
x4y3z89c330e, 12
i:247 x:4 y:3 z:89 c:330
x4y3z88c330e, 12
i:248 x:4 y:3 z:88 c:330
x6y3z89c329e, 12
i:249 x:6 y:3 z:89 c:329
x7y6z89c330e, 12
i:250 x:7 y:6 z:89 c:330
x4y3z89c330e, 12
i:251 x:4 y:3 z:89 c:330
x3y0z89c329e, 12
i:252 x:3 y:0 z:89 c:329
x-51y-62z37c329e[ch9568][ch9568][ch9568][ch9568][ch9568][ch9568][ch9568][ch9568], 16
i:253 x:-51 y:-62 z:37 c:329
x-340y-340z-342c[ch9568][ch9568][ch9568][ch9568][ch9568][ch9568][ch9568][ch9568], 16


I also noticed that the message buffer was too small, so i decided to increase it:

Quote

unsigned char rbuffer[19]={0}; //buffer for Arduino message


After i made this change, the program executed wonderfully for some time, and when it crashed, i got no more garbage chars (i.e. "[ch9568][ch9568][ch9568][ch9568][ch9568][ch9568][ch9568][ch9568]"). Tomorrow i will investigate further and post the answer if i am able to find any until then.

mikalhart

I notice that your big do/while loop terminates when i is not less than 16.  (Presumably you've changed this to 19 in the revised version.)  But this means that it potentially executes with i==16 (or 19) and then sets rbuffer[19] = b[0].  This is a buffer overflow, because rbuffer[18] is the last available slot.

Judging by the latest output, I would say that 19 is still too small.  There is no need to be stingy with RAM here.  Set rbuffer to, say, 128, and see if the problem resolves itself.  I think your problems are mostly related to buffer overflow.

Mikal

Jael

#4
Feb 13, 2009, 05:03 pm Last Edit: Feb 13, 2009, 05:04 pm by henrique21 Reason: 1
Mikal, i increased the buffer size to 128 as you suggested and it works! I ran the program for 10 minutes and collected thousands of samples without any problem. I will post both the working ANSI C code and the Matlab C S-Function code for data acquisition in Simulink.

Once again, i thank you for your assistance.

ANSI-C Code:
Code: [Select]

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

HANDLE hSerial;

int serialOpen(char *name);
BOOL serialRead(unsigned char *byte);
int serialWrite(unsigned char *data, int size);
void getSensorData(int *x,int *y,int *z,int *c);

//open Serial port; pass "COM1"/"COM2"/... for file name
int serialOpen(char *name)
{
     DCB dSerial;

     hSerial = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
     if(hSerial == INVALID_HANDLE_VALUE)
           return -1;

   if(!GetCommState(hSerial, &dSerial))
           return -2;

   dSerial.BaudRate=CBR_9600;
   dSerial.ByteSize=8;
   dSerial.StopBits=ONESTOPBIT;
   dSerial.Parity=NOPARITY;

   if(!SetCommState(hSerial, &dSerial))
           return -3;

     return 0;
}

//read a single byte from the serial port
BOOL serialRead(unsigned char *byte)
{
     int readSize;

     if((ReadFile(hSerial, byte, 1, &readSize, NULL)) && (readSize>0))
           if(readSize)
                 return TRUE;

     return FALSE;
}

void getSensorData(int *x, int *y, int *z, int *c)
{
     unsigned char b[1]={0}; //serial 1 char buffer
     unsigned char valx[5]={0}; //buffer for accelerometer x value string
     unsigned char valy[5]={0}; //buffer for accelerometer y value string
     unsigned char valz[5]={0}; //buffer for accelerometer z value string
     unsigned char valc[4]={0}; //buffer for compass angle string
     unsigned char rbuffer[128]={0}; //buffer for Arduino message
     int isx=0; //check for 'x' char in message
     int isy=0; //check for 'y' char in message
     int isz=0; //check for 'z' char in message
     int isc=0; //check for 'c' char in message
     int ise=0; //check for 'e' char in message
     int i=0; //counter

     i=0;
     do{
           while (!serialRead(&b)) ; // do nothing until serialRead returns TRUE //serialRead(&b);
           rbuffer[i]=b[0];
           if(rbuffer[i]=='x') isx=1;
           if(rbuffer[i]=='y') isy=1;
           if(rbuffer[i]=='z') isz=1;
           if(rbuffer[i]=='c') isc=1;
           if(rbuffer[i]=='e') ise=1;
           i++;
           if(b[0]=='e' && (isx == 0 || isy == 0 || isz ==0 || isc == 0 || ise == 0)) //check for message integrity
           {
                 i=0;
                 isx=0;
                 isy=0;
                 isz=0;
                 isc=0;
           }
     }while(b[0]!='e' && i<128 && (isx == 0 || isy == 0 || isz == 0 || isc == 0 || ise == 0));
     

           if(rbuffer[0]=='x') //Extract accelerometer x value from message string
           {
                 i=1;
                       do{
                             strncat(valx,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='y');
                 *x=atoi(valx);
           }

           if(rbuffer[i]=='y') //Extract accelerometer y value from message string
           {
                 i++;
                       do{
                             strncat(valy,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='z');
                 *y=atoi(valy);
           }

           if(rbuffer[i]=='z') //Extract accelerometer z value from message string
           {
                 i++;
                       do{
                             strncat(valz,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='c');
                 *z=atoi(valz);
           }

           if(rbuffer[i]=='c') //Extract compass angle from message string
           {
                 i++;
                       do{
                             strncat(valc,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='e');
                 *c=atoi(valc);
           }
}

void main(void)
{
     int xx=0; //accelerometer x axis value
     int yy=0; //accelerometer y axis value
     int zz=0; //accelerometer z axis value
     int cc=0; //compass angle value
     int i=0; //counter

serialOpen("COM3"); //Open serial port
Sleep(2500);

     while(!kbhit())
     {
           getSensorData(&xx,&yy,&zz,&cc);
           printf("i:%d x:%d y:%d z:%d c:%d\n",i,xx,yy,zz,cc);
           Sleep(10);
           i++;
     }

CloseHandle(hSerial); //Close serial port
}

Jael

#5
Feb 13, 2009, 05:08 pm Last Edit: Feb 13, 2009, 05:09 pm by henrique21 Reason: 1
Matlab C S-function code (Note - This code produces a block with four outputs: 3 accelerations and 1 compass heading):
Code: [Select]

#define S_FUNCTION_NAME  rs232
#define S_FUNCTION_LEVEL 2

#include "simstruc.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

HANDLE hSerial;

int serialOpen(char *name);
BOOL serialRead(unsigned char *byte);
void getSensorData(int *x,int *y,int *z,int *c);

//open Serial port; pass "COM1"/"COM2"/... for file name
int serialOpen(char *name)
{
     DCB dSerial;

     hSerial = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
     if(hSerial == INVALID_HANDLE_VALUE)
           return -1;

   if(!GetCommState(hSerial, &dSerial))
           return -2;

   dSerial.BaudRate=CBR_9600;
   dSerial.ByteSize=8;
   dSerial.StopBits=ONESTOPBIT;
   dSerial.Parity=NOPARITY;

   if(!SetCommState(hSerial, &dSerial))
           return -3;

     return 0;
}

//read a single byte from the serial port
BOOL serialRead(unsigned char *byte)
{
     int readSize;

     if((ReadFile(hSerial, byte, 1, &readSize, NULL)) && (readSize>0))
           if(readSize)
                 return TRUE;

     return FALSE;
}

void getSensorData(int *x, int *y, int *z, int *c)
{
     unsigned char b[1]={0}; //serial 1 char buffer
     unsigned char valx[5]={0}; //buffer for accelerometer x value string
     unsigned char valy[5]={0}; //buffer for accelerometer y value string
     unsigned char valz[5]={0}; //buffer for accelerometer z value string
     unsigned char valc[4]={0}; //buffer for compass angle string
     unsigned char rbuffer[128]={0}; //buffer for Arduino message
     int isx=0; //check for 'x' char in message
     int isy=0; //check for 'y' char in message
     int isz=0; //check for 'z' char in message
     int isc=0; //check for 'c' char in message
     int ise=0; //check for 'e' char in message
     int i=0; //counter

     i=0;
     do{
           while (!serialRead(&b)) ; // do nothing until serialRead returns TRUE //serialRead(&b);
           rbuffer[i]=b[0];
           if(rbuffer[i]=='x') isx=1;
           if(rbuffer[i]=='y') isy=1;
           if(rbuffer[i]=='z') isz=1;
           if(rbuffer[i]=='c') isc=1;
           if(rbuffer[i]=='e') ise=1;
           i++;
           if(b[0]=='e' && (isx == 0 || isy == 0 || isz ==0 || isc == 0 || ise == 0)) //check for message integrity
           {
                 i=0;
                 isx=0;
                 isy=0;
                 isz=0;
                 isc=0;
           }
     }while(b[0]!='e' && i<128 && (isx == 0 || isy == 0 || isz == 0 || isc == 0 || ise == 0));
   

           if(rbuffer[0]=='x') //Extract accelerometer x value from message string
           {
                 i=1;
                       do{
                             strncat(valx,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='y');
                 *x=atoi(valx);
           }

           if(rbuffer[i]=='y') //Extract accelerometer y value from message string
           {
                 i++;
                       do{
                             strncat(valy,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='z');
                 *y=atoi(valy);
           }

           if(rbuffer[i]=='z') //Extract accelerometer z value from message string
           {
                 i++;
                       do{
                             strncat(valz,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='c');
                 *z=atoi(valz);
           }

           if(rbuffer[i]=='c') //Extract compass angle from message string
           {
                 i++;
                       do{
                             strncat(valc,&rbuffer[i],1);
                             i++;
                       }while(rbuffer[i]!='e');
                 *c=atoi(valc);
           }
}

/*================*
* Build checking *
*================*/

/* Function: mdlInitializeSizes ===============================================
* Abstract:
*   Setup sizes of the various vectors.
*/
static void mdlInitializeSizes(SimStruct *S)
{
   ssSetNumSFcnParams(S, 0);
   if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
       return; /* Parameter mismatch will be reported by Simulink */
   }

   if (!ssSetNumInputPorts(S, 0)) return;


   if (!ssSetNumOutputPorts(S,4)) return;
   ssSetOutputPortWidth(S, 0, 1);  //Port 1 width
   ssSetOutputPortWidth(S, 1, 1);  //Port 2 width
   ssSetOutputPortWidth(S, 2, 1);  //Port 3 width
   ssSetOutputPortWidth(S, 3, 1);  //Port 4 width

   ssSetNumSampleTimes(S, 1);   /* number of sample times                */

   /* Take care when specifying exception free code - see sfuntmpl_doc.c */
   ssSetOptions(S,
                SS_OPTION_WORKS_WITH_CODE_REUSE |
                SS_OPTION_EXCEPTION_FREE_CODE |
                SS_OPTION_USE_TLC_WITH_ACCELERATOR);
   

}


/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
*    Specifiy that we inherit our sample time from the driving block.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
   ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
   ssSetOffsetTime(S, 0, 0.0);
   ssSetModelReferenceSampleTimeDefaultInheritance(S);
 
}

#define MDL_START  /* Change to #undef to remove function */
#if defined(MDL_START)
 /* Function: mdlStart =======================================================
  * Abstract:
  *    This function is called once at start of model execution. If you
  *    have states that should be initialized once, this is the place
  *    to do it.
  */
 static void mdlStart(SimStruct *S)
 {
   serialOpen("COM3");
   Sleep(2500);
 }
#endif /*  MDL_START */

/* Function: mdlOutputs =======================================================*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
   real_T            *x    = ssGetOutputPortRealSignal(S,0);
   real_T            *y    = ssGetOutputPortRealSignal(S,1);
   real_T            *z    = ssGetOutputPortRealSignal(S,2);
   real_T            *c    = ssGetOutputPortRealSignal(S,3);
   int_T  xx=0;
     int_T  yy=0;
     int_T  zz=0;
     int_T  cc=0;
   
     getSensorData(&xx,&yy,&zz,&cc);
       *x=xx;
       *y=yy;
       *z=zz;
       *c=cc;
}


/* Function: mdlTerminate =====================================================
* Abstract:
*    No termination needed, but we are required to have this routine.
*/
static void mdlTerminate(SimStruct *S)
{
               CloseHandle(hSerial); //Fecha a porta COM
}



#ifdef  MATLAB_MEX_FILE    /* Is this file being compiled as a MEX-file? */
#include "simulink.c"      /* MEX-file interface mechanism */
#else
#include "cg_sfun.h"       /* Code generation registration function */
#endif



pown

Maybe a stupid question, but how can this be ansi C when windows.h is not a part of the ansi libraries?

mikalhart

I think because ANSI makes no prohibition about what kinds of headers or libraries you incorporate into your program -- as long as they too are ANSI-compliant.

pown

Are you sure?
When I look for example at: http://en.wikipedia.org/wiki/C_standard_library
Then there must be a standard library.

When the meaning of the ansi standard is to make code more portable then I can't see the portability from windows.h.
If I want to run this program on a Linux or UNIX I can't see portability
at all....

Regards,

Marc

mikalhart

http://en.wikipedia.org/wiki/Ansi_C

Hmm... you might be right.  I know that Visual C++ 2005 is ANSI compliant, but I guess that doesn't mean that all programs written in it are.

M

Go Up