0

I am writing a code to capture serial readings from the Arduino to C++

Is there a way to capture the readings line by line and then store it into an array? I have read another post similar to mine, but I am still unable to apply it.

Any help is greatly appreciated, thank you.

Environment setup:

  • Arduino UNO
  • ADXL 335 accelerometer
  • Ubuntu 16.04
  • C++

[Updated] applied solution from Bart

Cpp file

The reason why I added the "for-loop with print and break" is to analyze the array contents.

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <unistd.h>

using namespace std;

char serialPortFilename[] = "/dev/ttyACM0";

int main()
{
    char readBuffer[1024];

    FILE *serPort = fopen(serialPortFilename, "r");

    if (serPort == NULL)
    {
        printf("ERROR");    
        return 0;
    }   

    while(1)
    {                           
        usleep(1000); //sync up Linux and Arduino       
        
        memset(readBuffer, 0, 1024);
        fread(readBuffer, sizeof(char),1024,serPort);       
    
        for(int i=0; i<1024; i++){
            printf("%c",readBuffer[i]);     
        }
        break;
    }
    
    return 0;
}

Ino file

Fetching data from the Accelerometer

#include <stdio.h>

const int xPin = A0;
const int yPin = A1;
const int zPin = A2;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int x = 0, y = 0, z = 0;
  
  x = analogRead(xPin);
  y = analogRead(yPin);
  z = analogRead(zPin);
  
  char buffer[16];
  int n;
  n = sprintf(buffer,"<%d,%d,%d>",x,y,z);
  
  Serial.write(buffer);
}

Results

Running the code for three times Click Here

The ideal outputs should be

<a,b,c><a,b,c><a,b,c>...

but right now, some of the outputs has the values inside "corrupted" (please see the fourth line from the top).

Even if use the start and end markers to determine a correct dataset, the data within the set is still wrong. I suspect the issue lies with the char array from C++, due to it being unsynchronized with Arduino. Else I need to send by Bytes from Arduino (not really sure how)

Community
  • 1
  • 1
  • Besides the loop condition, there's one big difference between the two loops you show. For proper direct comparison you should make the two loops as equal as possible. – Some programmer dude Jun 04 '19 at 07:57
  • Oops. I didn't made it clear. These two loops are ran in separate files. I would want the readings to be collected in the first loop. The second loop was used for me (as a test) to see the display of one iteration only. – onezerofive Jun 04 '19 at 08:06
  • Please take some time to read [the help pages](http://stackoverflow.com/help), take [the SO tour](http://stackoverflow.com/tour), read about [how to ask good questions](http://stackoverflow.com/help/how-to-ask), as well as [this question checklist](https://codeblog.jonskeet.uk/2012/11/24/stack-overflow-question-checklist/). Lastly please learn how to create a [minimal, **complete**, and **verifiable** example](https://stackoverflow.com/help/minimal-reproducible-example). – Some programmer dude Jun 04 '19 at 08:08
  • I would have thought you'd be better off using line-orientated transmission. On the Arduino, you append a linefeed to your `Serial.write()` after the sequence of 3 values. Then, on the C++ reading end, you read by lines (looking for linefeeds) to delimit your 3 readings. – Mark Setchell Jun 06 '19 at 06:18
  • @onezerofive With a buffer of 1024 they would only be out of sync when you are printing out the array and then starting to read again at a random point. I however see your problem more often. Just for clarity could you print a white line or any other marker after the 1024 characters? – Bart Jun 06 '19 at 07:18
  • @Bart May I know what you mean by "after the 1024 characters" ? I tried the[implementations](https://imgur.com/a/X6CaPwR) here. And yes I can see the reading to be started again, at a random point. May I know the issue with 1024 too? – onezerofive Jun 06 '19 at 09:49
  • @onezerofive I now see that your buffer is never filled to the max. You never receive the full 1024 characters. I did not note that you where not looping but executing it just once and exiting the program. Is there a disturbance on the line? Can you use a bus analyser to see which data is coming over the line. – Bart Jun 06 '19 at 10:14

1 Answers1

1

When dealing with two programs running on different processors they will never start sending/receiving at the same time. What you likely see is not that the results are merged wrong it is more likely the reading program started and stopped half way through the data.

When sending data over a line it is best that you:

On the Arduino:

  1. First frame the data.
  2. Send the frame.

On Linux:

  1. Read in data in a buffer.
  2. Search the buffer for a complete frame and deframe.

1. Framing the data

With framing the data I mean that you need a structure which you can recognize and validate on the receiving side. For example you could add the characters STX and ETX as control characters around your data. When the length of your data varies it is also required to send this.

In the following example we take that the data array is never longer than 255 bytes. This means that you can store the length in a single byte. Below you see pseudo code of how a frame could look like:

STX LENGTH DATA_ARRAY ETX

The total length of the bytes which will be send are thus the length of the data plus three.

2. Sending

Next you do not use println but Serial.write(buf, len) instead.

3. Receiving

On the receiving side you have a buffer in which all data received will be appended.

4. Deframing Next each time new data has been added search for an STX character, assume the next character is the length. Using the length +1 you should find a ETX. If so you have found a valid frame and you can use the data. Next remove it from the buffer.

for(uint32_t i = 0; i < (buffer.size() - 2); ++i)
{
  if(STX == buffer[i])
  {
    uint8_t length = buffer[i+2];
    if(buffer.size() > (i + length + 3) && (ETX == buffer[i + length + 2]))  
    {
      // Do something with the data.

      // Clear the buffer from every thing before i + length + 3
      buffer.clear(0, i + length + 3);

      // Break the loop as by clearing the data the current index becomes invalid.
      break; 
    }
  }
}

For an example also using a Cyclic Redundancy Check (CRC) see here

Bart
  • 1,405
  • 6
  • 32
  • Thank you Bart. I definitely agree with your explanation about Linux and Arduino "not being timed together". And your pseudo code solution to resolve it makes. But right now, I'm having issues with what is contain within the markers (I've updated my post) – onezerofive Jun 06 '19 at 04:58