Arduino data acquisition using ANSI C in VS2005

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:
“xyzce”

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:

#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:

#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:

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 :).

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

serialRead(&b);

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

while (!serialRead(&b)) ; // do nothing until serialRead returns TRUE

Mikal

Mikalhart, thank you for your reply.

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

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:

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.

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

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:

#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
}

Matlab C S-function code (Note - This code produces a block with four outputs: 3 accelerations and 1 compass heading):

#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

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

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.

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

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