C++ program sending serial data, Arduino reading different chars each time.

My project is currently nothing more than getting my feet wet in serial communications without using the built-in arduino serial console.

Method: Send serial using POSIX compliant C++ to arduino.
Expected result: Arduino reads sent serial data, and displays it on an 16x2 LCD.
Actual result: When using the arduino IDE serial console the result works exactly as expected. When sending the data from my c++ program the arduino reads different characters every time, usually non-english characters.

Things i've tried to fix it:
I've been trying to fix this for more than a week before asking for help. I've spent more than 24 hours looking up the problem, and can' t seem to make it work.

I've tried the current method, I tried using libserial, I've tried using other people's "working" code. I was able to get it to work when I made a terminal version, the port configuration of which is in the current code, but not working. I've tried introducing delays when opening the port (I have a capacitor in the arduino to prevent it from resetting when the port opens - confirmed working.) I've researched the basics of serial communication, the settings, and different methods of sending it, but nothing is working.

The program does use a wxwidgets interface, which I wont include in the code for brevity's sake. It's worth noting that I've tried closing the port after every send, as well as just closing it when the program terminates. Neither seems to matter.

C++ Program:

#include "wx_pch.h"
#include "ArduinoInterfaceMain.h"
#include <wx/msgdlg.h>

#include <iostream>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <fstream>
#include <string.h>
#include <wx/combobox.h>
//#include "arduino-serial-lib.h"
#define PORT "/dev/ttyACM0"
#define BAUD 9600

using namespace  std;
int fd;

int openPort(){
       int fd = open(PORT, O_RDWR | O_NOCTTY | O_NDELAY);
    if(fd == 1){
    //  printf("failed to open port \n");
    }

    struct termios config;
    if(!isatty(fd)){
    //  cout << "Not  a real terminal";
    }

    if(tcgetattr(fd, &config) < 0){
    //  cout << "tcgetattr: fd < 0";
    }

    config.c_iflag  &= ~(IGNBRK | BRKINT  | ICRNL | INLCR | PARMRK | INPCK | ISTRIP | IXON);
    //~(Turn off processing | Convert break to null byte | No CR->NL | No NL->CR | Ignore Parity Errors | Dont strip high bit | no flow control);
    config.c_oflag = 0;
    config.c_lflag &= ~(ECHO | ECHONL | ICANON | IEXTEN | ISIG);
    config.c_cflag &= ~(CSIZE | PARENB);
    config.c_cflag  |= CS8;
    config.c_cc[VMIN] = 1;
    config.c_cc[VTIME] = 0;
    if(cfsetispeed(&config, B9600)  < 0 || cfsetospeed(&config, B9600) < 0){
    //  cout << "Problem setting Baud to 9600";
    }
    if(tcsetattr(fd, TCSAFLUSH, &config) < 0){
    //  cout << "Problem setting attributes to fd";
    }
    return(fd);
}

int closePort(int fd){
return close(fd);
}

int transmit(int fd, string str){
    int len = str.size();
    int n = write(fd, &str, len);
    if (n!=len){
        perror("transmit():Couldn't write full string");
        return  -1;
    }
}

void ArduinoInterfaceFrame::OnbtnSendTestClick(wxCommandEvent& event)
{
 fd = openPort();
 usleep(10000);
 //char dummystring = 'x';
string dummystring = "he";
int a = transmit(fd, dummystring);// need  function to generate  this string  from options!
  lblResponse->SetLabel(wxT("sent"));
  //closePort(fd);
}

I also tried this, based on the tutorial i found here.

/*    struct termios toptions;
    int fd;

    fd = open(PORT, O_RDWR | O_NOCTTY | O_NDELAY);
    //fd = open(PORT, O_RDWR | O_NONBLOCK );

    if (fd == -1)  {
        perror("serialport_init: Unable to open port ");
        return -1;
    }

    //int iflags = TIOCM_DTR;
    //ioctl(fd, TIOCMBIS, &iflags);     // turn on DTR
    //ioctl(fd, TIOCMBIC, &iflags);    // turn off DTR

    if (tcgetattr(fd, &toptions) < 0) {
        perror("serialport_init: Couldn't get term attributes");
        return -1;
    }
    speed_t brate = BAUD; // let you override switch below if needed
    switch(BAUD) {
    case 4800:   brate=B4800;   break;
    case 9600:   brate=B9600;   break;
    case 19200:  brate=B19200;  break;
    case 38400:  brate=B38400;  break;
    case 57600:  brate=B57600;  break;
    case 115200: brate=B115200; break;
    }
    cfsetispeed(&toptions, B9600);
    cfsetospeed(&toptions, B9600);

    // 8N1
    toptions.c_cflag &= ~PARENB;
    toptions.c_cflag &= ~CSTOPB;
    toptions.c_cflag &= ~CSIZE;
    toptions.c_cflag |= CS8;
    // no flow control
    toptions.c_cflag &= ~CRTSCTS;

    //toptions.c_cflag &= ~HUPCL; // disable hang-up-on-close to avoid reset

    toptions.c_cflag |= CREAD | CLOCAL;  // turn on READ & ignore ctrl lines
    toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl

    toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
    toptions.c_oflag &= ~OPOST; // make raw

    // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
    toptions.c_cc[VMIN]  = 0;
    toptions.c_cc[VTIME] = 0;
    //toptions.c_cc[VTIME] = 20;

    tcsetattr(fd, TCSANOW, &toptions);
    if( tcsetattr(fd, TCSAFLUSH, &toptions) < 0) {
        perror("init_serialport: Couldn't set term attributes");
        return -1;
    }

    return fd;*/

And the code on the Arduino. Remember that when I send data from the IDE console it works 100%.

#include <LiquidCrystal.h>
LiquidCrystal lcd(12,11,5,4,3,2);

//int  intMenu, intSubmenu, intChoice, intEnd = -1;
char buffer[6];

void setup(){
  lcd.begin(16, 2);
  lcd.print("Booted");
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop() {

  if(Serial.available()){
    delay(10);
   // if(Serial.peek() == '<'){
      //Serial.read();             // Erase <
      //delay(10);
      for(int n=0; n<5; n++){    //Get Data
        buffer[n] = Serial.read();
        delay(10);
      }
      lcd.setCursor(0,1);
      for(int i=0;i<5;i++){
        lcd.print(buffer[i]);
        Serial.println(buffer[i]);
      }
    //}

  }
}

A lot of the code has commented-out code. This is just the result of different attempts having different results. I've removed a lot of commented code, but obviously that wont affect the outcome.

I suspect the problem lies in datatypes, but sending a single char type over serial to the Arduino gives the same results as a string or an int.

In your C++ program it is a really bad idea to use the same name 'fd' globally and locally.

Remember that when I send data from the IDE console it works 100%

Doesn't matter. In your Arduino sketch, "if(Serial.available()){" only guarantees you that there is at least one character available but you read 5 - delaying 10ms before reading them is still no guarantee that they are there.

Pete

el_supremo:
In your C++ program it is a really bad idea to use the same name 'fd' globally and locally.

Remember that when I send data from the IDE console it works 100%

Doesn't matter. In your Arduino sketch, "if(Serial.available()){" only guarantees you that there is at least one character available but you read 5 - delaying 10ms before reading them is still no guarantee that they are there.

Pete

Thanks for being the first to reply.

On the IDE serial monitor, if I give less than 5 values it just repeats the values given. The 10ms delay is to give the serial time to catch up. If I read the bytes as fast as the microcontroller was able, it'd give me garbage.

Since it reads the data one byte at a time, then writes it one byte at a time, I should get at least 1 predictable character on the LCD before any trash. I've also tried this with an array size of 1, same thing.

The 10ms delay is to give the serial time to catch up

I know why the delay is there. I've seen it many times before and it is the wrong way to do it.

string dummystring = "he";

Is 'string' exactly equivalent to 'char *' or is it similar to the String class on Arduino?
If it is like Arduino String then in this statement:

    int n = write(fd, &str, len);

the address of 'str' might not point to the string itself, in which case you'll get garbage.

Pete

A string is basically a managed char[].

I've tried sending chars, and char arrays and have had no success with either. I'll look at the string class again and test your theory.

Is your C++ program allowing time for the Arduino to reset after the C++ program opens the serial port? And does the C++ program keep the serial port open until all communication with the Arduino is finished?

You might be interested in this Python example. The principles will be the same in any language.

...R

I finally got it working!

I changed the code from:

void ArduinoInterfaceFrame::OnbtnSendTestClick(wxCommandEvent& event)
{
 fd = openPort();
 usleep(10000);
 //char dummystring = 'x';
string dummystring = "he";
int a = transmit(fd, dummystring);// need  function to generate  this string  from options!
  lblResponse->SetLabel(wxT("sent"));
  //closePort(fd);
}

to:

void ArduinoInterfaceFrame::OnbtnSendTestClick(wxCommandEvent& event)
{
 fd = openPort();
 usleep(10000);
 char dummystring[5] ={'t','e','s','t','1'};
char buf[6];
sprintf(buf, "%s",dummystring);
write(fd, dummystring, 5);
usleep(15000);
 closePort(fd);
}

I'm sure someone will see this and tell me it's poor form and my understanding of c/++ is sloppy, but I'm used to it. It works!
Now to rewrite it with variables.

Do you know that every time you open and close the port you reset your arduino?

Grumpy_Mike:
Do you know that every time you open and close the port you reset your arduino?

I'm glad you're being thorough, it was the first thing that caught me off guard during this learning experience. I have it bypassed with a spare 470uF capacitor I had lying around. In case anyone googles across this, put the capacitor between Reset and Gnd.

SimbaSpirit:
I have it bypassed with a spare 470uF capacitor I had lying around. In case anyone googles across this, put the capacitor between Reset and Gnd.

Wouldn't it be far far easier to write the code properly ?

...R

Robin2:

SimbaSpirit:
I have it bypassed with a spare 470uF capacitor I had lying around. In case anyone googles across this, put the capacitor between Reset and Gnd.

Wouldn't it be far far easier to write the code properly ?

...R

It's replies like this that make me think its time to find a new forum. What is it with people having such a condescending attitude?

As I said, the purpose of this was to learn to code serial. When I'm ready to roll out a project with a purpose then obviously I'll put in the time to program around all the issues. Believe it or not, putting two pieces of metal into two holes is actually faster and easier, and when I'm testing the code I don't have to wait the extra time for it to reboot.

SimbaSpirit:
As I said, the purpose of this was to learn to code serial. When I'm ready to roll out a project with a purpose then obviously I'll put in the time to program around all the issues. Believe it or not, putting two pieces of metal into two holes is actually faster and easier, and when I'm testing the code I don't have to wait the extra time for it to reboot.

This paragraph seems to validate my comment ???

...R

Why cut off your nose spite your face?

 char dummystring[5] ={'t','e','s','t','1'};
char buf[6];
sprintf(buf, "%s",dummystring);
write(fd, dummystring, 5);

What is buf for? Why are you passing a non-NULL terminated array of chars to a function that expects a string? Why are you copying stuff to buf in any case?