views:

386

answers:

2

Hi,

I am having a problem with a Serial Port communication between Arduino Nano and C++, even though the problem is in C++ side. Basically I want to send integers (or long,...) from the Arduino to a C++ program to be processed.

First I did a test sending information from the Arduino to the computer using Matlab. The Arduino code is pretty simple:

int i = 0;

void setup() {

   // start serial port at 9600 bps:
   Serial.begin(9600);
   establishContact(); 
}

void loop() {
  Serial.println(i);
  i=i+1;  
  delay(10);
}

void establishContact() {
   while (Serial.available() <= 0) {
     Serial.println('A', BYTE);
     delay(10);
   }
}

The Matlab side is also simple:

clc;
clear all;
numSec=2;
t=[];
v=[];

s1 = serial('COM3');    % define serial port
s1.BaudRate=9600;               % define baud rate
set(s1, 'terminator', 'LF');    % define the terminator for println
fopen(s1);

try                             % use try catch to ensure fclose
                                % signal the arduino to start collection
    w=fscanf(s1,'%s');              % must define the input % d or %s, etc.
    if (w=='A')
        display(['Collecting data']);
        fprintf(s1,'%s\n','A');     % establishContact just wants 
                                    % something in the buffer
    end

    i=0;
    t0=tic;
    while (toc(t0)<=numSec)
        i=i+1;
        t(i)=toc(t0);
        t(i)=t(i)-t(1);
        v(i)=fscanf(s1,'%d');     
    end

    fclose(s1);
    plot(t,v,'*r')   

catch me
    fclose(s1);                
end       

My goal is, with C++, do the same that is done in Matlab using fscanf(s1, '%d').

Here is the current code that I am using (C++ code):

void main()
{
 HANDLE hSerial;
 hSerial = CreateFile(TEXT("COM3"), 
   GENERIC_READ | GENERIC_WRITE, 
   0,
   NULL, 
   OPEN_EXISTING,
   FILE_ATTRIBUTE_NORMAL,//FILE_FLAG_OVERLAPPED, 
   NULL);



if ( hSerial == INVALID_HANDLE_VALUE)
 {
  printf("Error initializing handler");
 } 
 else 
 {

  // Set the parameters of the handler to the serial port.
  DCB dcb = {0};

  dcb.DCBlength = sizeof(dcb);

  if ( !GetCommState(hSerial, &dcb) )
  {
   printf("Error setting parameters");
  }

  FillMemory(&dcb, sizeof(dcb), 0);
  dcb.BaudRate = CBR_9600;
  dcb.ByteSize = 8;
  dcb.StopBits = ONESTOPBIT;
  dcb.Parity = NOPARITY;

  if ( !SetCommState(hSerial, &dcb) )
  {
   // error setting serial port state.
  }

  // Tell the program not to wait for data to show up
  COMMTIMEOUTS timeouts = {0};

  timeouts.ReadIntervalTimeout = 0;//20;
  timeouts.ReadTotalTimeoutConstant = 0;//20;
  timeouts.ReadTotalTimeoutMultiplier = 0;//50;
  timeouts.WriteTotalTimeoutConstant = 0;//100;
  timeouts.WriteTotalTimeoutMultiplier = 0;//100;

  if ( !SetCommTimeouts(hSerial, &timeouts) )
  {
   printf("Error setting the timeouts");

  }

  char szBuff[5] = "";
  DWORD dwBytesRead = 0;
  int i = 0;
  char test[] = "B\n";
  int maxSamples = 10;
  DWORD dwCommStatus;

  WriteFile(hSerial, test, 2, &dwBytesRead, NULL);

  SetCommMask(hSerial,EV_RXCHAR);

  while (i < maxSamples)
  {
   WaitCommEvent (hSerial, &dwCommStatus, 0);

   if (dwCommStatus & EV_RXCHAR) 
   {
    memset(szBuff,0,sizeof(szBuff));
    ReadFile(hSerial, LPVOID(szBuff), 4, &dwBytesRead, NULL);

    cout<<szBuff;
    printf(" - %d - \n", atoi(szBuff));
   }
   i++;     
  }

  scanf("%d", &i);

  CloseHandle(hSerial);
 }
    }

The goal of my code would be something like num = ReadSerialCOM(hSerial, "%d");

My current C++ code reads the information from the buffer, but there is not an accepted end of line, which implies that my numbers (integers) are received cut.

Eg:

I send 8889 from the Arduino, which places it in the COM port. And the command ReadFile saves '88' into szBuff. At the next iteration '89\n' is saved into sZBuff. Basically I want to avoid to post-process sZBuff to concat '88' and '89\n'.

Anyone? Thanks!

A: 

If I understand your question correctly, one way to avoid having to 'post-process' is to move the pointer passed to ReadFile to the end of the available data, so the ReadFile call is appending to the buffer, instead of overwriting.

Essentially, you would have two pointers. One to the buffer, the other to the end of the data in the buffer. So when your program starts, both pointers will be the same. Now, you read the first 2 bytes. You increment the end-of-data pointer by 2. You do another read, but instead of szBuff, you pass a pointer to the end of the previously read data. You read the next three bytes and you have the complete entry in szBuff.

If you need to wait until some delimiter to mark the end of an entry is received, you could just search the received data for it. If it's not there, you keep reading until you find it. If it is there, you can just return.

// Fill the buffer with 0
char szBuff[256] = {0};
// We have no data in the buffer, so the end of data points to the beginning 
// of the buffer.
char* szEndOfData = szBuff; 
while (i < maxSamples)
{
    WaitCommEvent (hSerial, &dwCommStatus, 0);

    if (dwCommStatus & EV_RXCHAR) 
    {
        // Append up to 4 bytes from the serial port to the buffer
        ReadFile(hSerial, LPVOID(szEndOfData), 4, &dwBytesRead, NULL);
        // Increment the end of data pointer, so it points to the end of the
        // data available in the buffer.
        szEndOfData += dwBytesRead;

        cout<<szBuff;
        printf(" - %d - \n", atoi(szBuff));
    }
    i++;     
}

// Output, assuming what you mentioned happens:
// - 88 -
// - 8889 -

If this approach is acceptable to you, it will require a bit more work. For example, you would have to ensure you don't overflow your buffer. When you remove data from the buffer, you'll have to move all of the data after the removed segment to the beginning, and fix the end of data pointer. Alternatively, you could use a circular buffer.

dauphic
Thank you for your answer. The problem with both systems (the one that I am using right now and your solution) is that in both cases I would have to keep reading `char`'s until I read a end of line. Then put all the char's together to recreate the initial `int`. I was sure, though it looks that I am mistaken, that there was a solution similar to the Matlab `fscanf(h, '%d")` already done for this problem, even though I haven't found anything similar.In the Matlab code this is done using a terminator character and I was basically wondering if I missed some property of the functions used.
sergi
Oh, I see. This is basically the way something like fscanf would be implemented, but Windows doesn't provide any function that can do that, as far as I know.
dauphic
A: 

As Hans Passant and dauphic pointed, it doesn't seem to be a general solution for my question. I am writing, though, the code that I was trying to avoid, just in case somebody finds it useful or face the same problem that I had:

int i = 0;  
DWORD dwBytesRead = 0;
DWORD dwCommStatus = 0;
char szBuff[2] = "";                
int maxRead = 20;   
int sizeNum = 1;    
int *num    = (int*)malloc(maxRead*sizeof(int)); 
char *currNum;
char *pastNum;

// Write something into the Serial Port to start receive 
// information from the Arduino
WriteFile(hSerial, (LPCVOID)"A\0", 1, &dwBytesRead, NULL);    
SetCommMask(hSerial, EV_RXCHAR);

// Start reading from the Serial Port
while ( i < maxRead )
{
    WaitCommEvent (hSerial, &dwCommStatus, 0);

    if (dwCommStatus & EV_RXCHAR) // if a char is received in the serial port
    {
        ReadFile(hSerial, LPVOID(szBuff), 1, &dwBytesRead, NULL);

        if ( szBuff[0] > 47 && szBuff[0] < 58 )
        {
            sizeNum++;
            if (sizeNum ==2)
            {
                currNum = (char*)malloc(sizeNum*sizeof(char));
                strcpy(currNum, szBuff);
            } else
            {
                if (pastNum != NULL)
                    free(pastNum);
                pastNum = currNum;
                currNum = (char*)malloc(sizeNum*sizeof(char));
                strcpy(currNum, pastNum);
                strcpy(currNum+(sizeNum-2)*sizeof(char), szBuff);
            }

            cout << szBuff<<endl;   
        } else if (szBuff[0] == '\n' && sizeNum > 1) // end of number
        {
            num[i] = atoi(currNum);
            i++;                    

            sizeNum = 1;
            if (currNum!=NULL)
                free(currNum);
        }
    }
}
sergi