Go Down

Topic: Interface C++ to Arduino (Read 337 times) previous topic - next topic

XZ001

I've been working on a large project to build an automated system in my lab.  I'm using the library from this post and going off their examples for making an Arduino talk to the PC through the serial port:

https://playground.arduino.cc/Interfacing/CPPWindows

The Arduino code reads characters from the serial port until it reaches a carriage return and then parses the string into a command, which tells it to move various actuators in the system.  This part of the code seems to be working fine, but the .ino file is attached for reference.  The important part of the code is in the serialEvent() function.

The C++ code uses the library from the link above.  I created a small test loop before the rest of the code for debugging which looks like this:

Code: [Select]
while (SP->IsConnected())
{
string outputData = "";
cin >> outputData;
outputData.append(pcr);
writeResult = SP->WriteData((char*)outputData.c_str(), outputData.length() + 1);
//writeResult = writeResult & SP->WriteData(pcr, 1);

while (true) {
Sleep(500);
char incomingData[256] = "";
readResult = SP->ReadData(incomingData, dataLength);
//printf("Bytes read: (0 means no data available) %i\n", readResult);
incomingData[readResult] = 0;

input.append(incomingData);

check_ok = input.find(pcr);
check_error = input.find(error);

if (check_ok != string::npos || check_error != string::npos || readResult == 0) {
cout << "Receiving: " << input << '\n';
input.clear();
break;
}
}
}


To clarify, the variable pcr is defined before this and is a pointer to the carriage return character.  When I attempt to run this code I get the output in the .jpg attached to the post. First I sent the command "getp" which causes the the arduino to send back a string with with the x and y position of two actuators in the system.  I also put in some other print statements while I was debugging earlier, which is why it also prints back the input each iteration as it reads it off the serial and a string "Valid Command." when it confirms that there was a carriage return at the end.  Finally it sends the string that it should followed by "ok" which it prints after every command.  All of this is fine, but then it starts sending back a bunch of gibberish as if the serials had mismatched baudrates.  I then sent another command "setpx0y0"; however it only repeated this gibberish and didn't print back any of the debug statements.  This doesn't happen when I use the serial monitor.  Does anyone know what the problem could be?

horace

#1
Jul 07, 2017, 01:56 pm Last Edit: Jul 07, 2017, 02:02 pm by horace
if I run code using the serial monitor or teraterm it appears towork OK, e.g. teraterm
Code: [Select]
g
ge
get
getp
getp
Valid Command.
X: -1.38, Y: -1.00
ok
s
se
set
setp
setpx
setpx0
setpx0y
setpx0y0
setpx0y0
Valid Command.
ok
g
ge
get
getp
getp
Valid Command.
X: 0.00, Y: 0.00
ok

it looks as though the C++ code is the problem
which C++ compiler are you using?

what happens in
Code: [Select]

readResult = SP->ReadData(incomingData, dataLength);
//printf("Bytes read: (0 means no data available) %i\n", readResult);
incomingData[readResult] = 0;

if there is no data available and ReadData() returns -1 ?
you will access array index  -1 which is before the start of the array hence corrupting memory

XZ001

I'm compiling in Visual Studio 2017 with the default compiler. 

Looking at the library file it appears the the ReadData function returns 0 if there is no data.

horace

looking at the code in the link
https://playground.arduino.cc/Interfacing/CPPWindows

ReadData() is specified so
Code: [Select]
//Read data in a buffer, if nbChar is greater than the
        //maximum number of bytes available, it will return only the
        //bytes available. The function return -1 when nothing could
        //be read, the number of bytes actually read.
        int ReadData(char *buffer, unsigned int nbChar);


could you do something along the lines of
Code: [Select]
while((readResult = SP->ReadData(incomingData, dataLength)) <= 0) ;
//printf("Bytes read: (0 means no data available) %i\n", readResult);
incomingData[readResult] = 0;

i.e. loop until data is available

using Visual Studio I tend to use the SerialPort component from the toolbox


XZ001

I didn't see that comment in the header file; however, when I looked at the .cpp code itself the Read data function ends with this:

Code: [Select]
//If nothing has been read, or that an error was detected return 0
    return 0;


So it should be returning 0 if nothing is read. 

XZ001

Update:

I'm thinking this error might have something to do with the RX buffer on the Arduino becoming corrupted.  My reasoning for this is because I set the Arduino's code to print out "Event" whenever SerialEvent was called and print out each character as it read it.  When I ran it again I got the attached result.  The program seems to run SerialEvent until it gets to the carriage return, prints what it's supposed to, but the SerialEvent is trigger several more times as it seems to parse data that was not meant to be reached.  Subsequent commands don't have as much effect.  I made some slight changes to the C++ code so here it is again for reference:

Code: [Select]
while (SP->IsConnected())
{
string outputData = "";
cin >> outputData;
outputData.append(pcr);
//outputData.append(pnc);
writeResult = SP->WriteData((char*)outputData.c_str(), outputData.length() + 1);
//writeResult = writeResult && SP->WriteData(pnc, 1);
//writeResult = writeResult & SP->WriteData(pcr, 1);

while (true) {
Sleep(500);
char incomingData[256] = "";
readResult = SP->ReadData(incomingData, dataLength);
//printf("Bytes read: (0 means no data available) %i\n", readResult);
//incomingData[readResult] = 0;
input.append(incomingData, 0, readResult);

check_ok = input.find(pcr);
check_error = input.find(error);

if (check_ok != string::npos || check_error != string::npos || readResult == 0) {
cout << "Receiving: " << input << '\n';
input.clear();
break;
}
}
}


My confusion is as to what could cause the Arduino to continue parsing characters in the buffer after reaching the end.  Any ideas?

horace

looking at the C++ statement
Code: [Select]
writeResult = SP->WriteData((char*)outputData.c_str(), outputData.length() + 1);
why is the number of bytes to write outputData.length() + 1
you will be transmitting an extra byte after the carriage return ?

XZ001

Yes, I noticed that and changed it after I posted, but it didn't seem to change anything.  I don't know why I had it set to that in the first place, but it doesn't seem to be the root of the problem. 

horace

how have you defined pcr? e.g.
Code: [Select]
string  pcr="\n";

XZ001

Code: [Select]
char cr = '\r';
char* pcr = &cr;


It's a carriage return rather than a newline character. 

horace

#10
Jul 08, 2017, 07:18 am Last Edit: Jul 08, 2017, 07:47 am by horace
if I run this code using GNU C++
Code: [Select]
string outputData = "";
char cr = '\r';
                char* pcr = &cr;
cin >> outputData;
outputData.append(pcr);
cout << "length = " << outputData.length() << endl;
cout << outputData << endl;
 

I get
Code: [Select]
hello
length = 9
Ï■mlo

Process returned 0 (0x0)   execution time : 2.900 s

I enter "hello" and append a  carriage retrun which should be six characters but it displays the outputData string length as 9 characters and the display of outputData shows none printable characters

the problem is you have defined cr as a single character '\r' not as a C string with a terminating '\0' so when it is appended to outputData you append '\r' and whatever is in memory until a 0 is found

you could define pcr as a char array
Code: [Select]
string outputData = "";
char pcr[] = "\r";
cin >> outputData;
outputData.append(pcr);
cout << "length = " << outputData.length() << endl;
cout << outputData << endl;

when executed I get
Code: [Select]
hello
length = 6
hello

the string length is correct and it displays the contents Ok

alternatively define pcr as a string
Code: [Select]
string outputData = "", pcr="\r";;

XZ001

I edited the code with that in mind and it works now.  Thanks!

Metaconta

#12
Jul 14, 2017, 04:25 pm Last Edit: Jul 14, 2017, 04:27 pm by Metaconta
Hello:

Here it explains very well a tutorial in Spanish on Arduino C ++ and serial port.

Https://www.slideshare.net/Metaconta2/arduino-c-y-puerto-serie

View video.

Arduino:
Code: [Select]
const byte Led = 13;   // Declaramos la variable pin del Led.
char caracter;
String comando;

void setup()
{
  pinMode(Led, OUTPUT);  // Inicializa el pin del LED como salida:
  Serial.begin(115200);     // Puerto serie 115200 baudios.
}

void loop()
{
  /*
    Voy leyendo carácter a carácter lo que se recibe por el canal serie
    (mientras llegue algún dato allí), y los voy concatenando uno tras otro
    en una cadena. En la práctica, si usamos el "Serial monitor" el bucle while
    acabará cuando pulsamos Enter. El delay es conveniente para no saturar el
    canal serie y que la concatenación se haga de forma ordenada.
  */
  while (Serial.available() > 0)
  {
    caracter = Serial.read();
    comando.concat(caracter);
    delay(10);
  }

  /*
    Una vez ya tengo la cadena "acabada", compruebo su valor y hago que
    la placa Arduino reacciones según sea este. Aquí podríamos hacer lo
    que quisiéramos: si el comando es "tal", enciende un Led, si es cual,
    mueve un motor... y así.
  */

  // Si le llega el mensaje Luz_ON.
  if (comando.equals("Luz_ON") == true)
  {
    digitalWrite(Led, HIGH); // Enciende el Led 13.
    Serial.write("ON - Led encendido.");    // Envía este mensaje a C++.
  }

  // Si le llega el mensaje Luz_ON.
  if (comando.equals("Luz_OFF") == true)
  {
    digitalWrite(Led, LOW); // Apaga el Led 13.
    Serial.write("OFF - Led apagado. ");  // Envía este mensaje a C++.
  }

  // Limpiamos la cadena para volver a recibir el siguiente comando.
  comando = "";
}




C++:
Code: [Select]
#include
#include
#include
#include "SerialClass.h"
using namespace std;

void main()
{
    // Título de la ventana
    SetConsoleTitle("Control Led Arduino - Visual Studio C++ 2017");

    // Puerto serie.
    Serial* Puerto = new Serial("COM4");

    // Comandos para Arduino.
    char Luz_ON[] = "Luz_ON"; // Envía "Luz_ON" al puerto serie.
    char Luz_OFF[] = "Luz_OFF";
    char lectura[50] = "\0"; // Guardan datos de entrada del puerto.

    int opc; // Guarda un 1 o 2 tipo entero queintroduces desde la consola.

    while (Puerto->IsConnected())
    {
        cout << endl; // Retorno.
        cout << "Introduzca la opcion deseada: " << endl;
        cout << "Pulse 1 para encender el Led, pulse 2 para apagar." << endl << endl; // Muestra texto en pantalla.

        cin >> opc; // Aquí introduces un número, el 1 o el 2.

        switch (opc) // Espera recibir un 1 o un 2.
        {
        case 1:
            // Encener luz.
            cout << "Enviando: " << Luz_ON << endl; // Muestra en pantalla textos.
            Puerto->WriteData(Luz_ON, sizeof(Luz_ON) - 1); // Envía al puerto el texto "Luz_ON".
            break;

        case 2:
            // Apagar luz.
            cout << "Enviando: " << Luz_OFF << endl;
            Puerto->WriteData(Luz_OFF, sizeof(Luz_OFF) - 1);
            break;

        default: // Si haz pulsado otro número distinto del 1 y 2, muestra
            cout << "Puse del 1 al 2."; // este mensaje.
        }


        Sleep(500);
        int n = Puerto->ReadData(lectura, 49); // Recibe datos del puerto serie.
        if (n > 0)
        {
            lectura[n + 1] = '\0'; // Limpia de basura la variable.
            cout << "Recibido: " << lectura << endl; // Muestra en pantalla dato recibido.
            cout << "-------------------" << endl;
        }

        cin.ignore(256, '\n'); // Limpiar buffer del teclado.
    }
}





Greetings.

Go Up