CHANGE/manipulate data format from an analytical scale (several lines output)

J-M-L:
wow - i had not seen those as well..

// variables to hold the parsed data

char date_month[bufferSize] = {0};
char date_day[bufferSize] = {0};
char date_year[bufferSize] = {0};
char time_hour[bufferSize] = {0};
char time_min[bufferSize] = {0};
char time_sec[bufferSize] = {0};




come on, don't "piss away" :) memory! a month will be 2 digits, add a '\0' at the end and that's 3... no need to save 50 bytes for that, right? and you could probably even read that as a byte for month number...

Yes, I know that! But I thought that I should consider the WHOLE buffer (date, time, reading, various symbols here and there, etc.) :frowning:

This is the output, absolutely not what I expected :frowning:

I will change the code with the min number of bytes needed then, perhaps 5, just to be sure :slight_smile:

:slight_smile:

well read again what the first param of strtok() should be once you've done the initialization. Also, you are searching for commas, do you see a lot of those into your buffer?

// Code for receiving the data from my analytical scale through RS232 with parsing

// Libraries
#include <LiquidCrystal.h>
#include <SPI.h>
#include <SD.h>

// Interface and Objects definitions
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Creates a LCD LiquidCrystal object
const int chipSelect = 10; // Assigns the pin for the SPI-SD chip selection

const byte bufferSize = 50; // enough for your max length of a message
static byte bufferPosition = 0;
char inputBuffer[bufferSize]; // my array of the data from the scale
char tempBuffer[bufferSize]; // temporary array for use when parsing

// variables to hold the parsed data
char date_month[bufferSize] = {0};
char date_day[bufferSize] = {0};
char date_year[bufferSize] = {0};
char time_hour[bufferSize] = {0};
char time_min[bufferSize] = {0};
char time_sec[bufferSize] = {0};
float reading = 0.0;

boolean newData = false;

//============

void setup()
{
  Serial.begin(9600, SERIAL_7N1); // Includes the scale parameters

  pinMode(chipSelect, OUTPUT); // Ensures that the SPI-SD selection pin is an output
  lcd.begin(16, 2); // Initialises the interface to the LCD screen
  lcd.clear(); // // Clears the LCD screen and positions the cursor in the upper-left corner
  if (!SD.begin(chipSelect)) // Checks if the SD card is working
  {
    lcd.println("SD failed!");
    delay(2000);
    return;
  }

  bufferPosition = 0;
  inputBuffer[0] = '\0'; // initialize with empty string

}

//============

void loop() {
    recvWithEndMarker();
    if (newData == true) {
        strcpy(tempBuffer, inputBuffer);
            // this temporary copy is necessary to protect the original data
            //   because strtok() used in parseData() replaces the commas with \0
        parseData();
        saveData();
        newData = false;
    }
}

//============

void recvWithEndMarker() {
    char endMarker = 'OK';
    char receivedData;
   
    while (Serial.available() > 0 && newData == false) {
        receivedData = Serial.read();

        if (receivedData != endMarker) {
            inputBuffer[bufferPosition] = receivedData;
            bufferPosition++;
            if (bufferPosition >= bufferSize) {
                bufferPosition = bufferSize - 1;
            }
        }
        else {
            inputBuffer[bufferPosition] = '\0'; // terminate the string
            bufferPosition = 0;
            newData = true;
        }
    }
}


//============


void parseData() {      // split the data into its parts

    char * strtokIndx; // this is used by strtok() as an index

    strtokIndx = strtok(tempBuffer,","); // get the first bit
    strcpy(date_month, strtokIndx); // copy it to month variable

    strtokIndx = strtok(tempBuffer,","); // carry on...
    strcpy(date_month, strtokIndx); // carry on copy this byte in the same variable

    strtokIndx = strtok(tempBuffer,",");
    strcpy(date_day, strtokIndx); // copy it to month variable
    
    strtokIndx = strtok(tempBuffer,",");
    strcpy(date_day, strtokIndx); // carry on copy this byte in the same variable, etc.

    strtokIndx = strtok(tempBuffer,",");
    strcpy(date_year, strtokIndx);

    strtokIndx = strtok(tempBuffer,",");
    strcpy(date_year, strtokIndx);
    
 
    strtokIndx = strtok(NULL, ","); // I do the same with the time...
    strcpy(time_hour, strtokIndx);

    strtokIndx = strtok(NULL, ",");
    strcpy(time_hour, strtokIndx);

    strtokIndx = strtok(NULL, ",");
    strcpy(time_min, strtokIndx);

    strtokIndx = strtok(NULL, ",");
    strcpy(time_min, strtokIndx);

    strtokIndx = strtok(NULL, ",");
    strcpy(time_sec, strtokIndx);

    strtokIndx = strtok(NULL, ",");
    strcpy(time_sec, strtokIndx);
    

    strtokIndx = strtok(NULL, ",");
    reading = atof(strtokIndx); // convert this part to a float for the reading cell

}

//============

void saveData()
{
   // **********************************************************************
   // here the inputBuffer has a full line, you could parse it for content
   // I'm just saving the full line to the SD card
   // **********************************************************************
  File readings = SD.open("readings.csv", FILE_WRITE); // Opens the file on the SD card
  if (readings) // If the file opened OK, write to it on SD card
  {
    readings.print(date_month);
    readings.print(',');
    readings.print(date_day);
    readings.print(',');
    readings.print(date_year);
    readings.print(',');
    readings.print(time_hour);
    readings.print(',');    
    readings.print(time_min);
    readings.print(',');
    readings.print(time_sec);
    readings.print(',');
    readings.println(reading);
     
    readings.close(); // Closes the file to save the data on the SD card
  } else { // If the file didn't open, print an error
    lcd.println("FILE NOT OPEN");
    delay(2000);
    lcd.clear();
  }
}

I changed the end marker:

char endMarker = 'OK';

So I think it will build the buffer until the "OK".

Anyway, I will have a look again on that strtok() then :slight_smile: but I am quite struggling :frowning:

Thanks for finding the time to troubleshoot/change my code when you have a bit of time. really appreciated mate.

Mario

I changed the end marker:

the code you have only test for a char, not a string...

Notice something here (besides looking for a comma)

    strtokIndx = strtok(tempBuffer,","); // get the first bit
    strtokIndx = strtok([color=red]tempBuffer[/color],","); // carry on...
...
    strtokIndx = strtok([color=red]NULL[/color], ",");

Now I have this:

warning: overflow in implicit constant conversion [-Woverflow]

I believe it is better to wait for your proper troubleshoot instead of me guessing here and there, because it seems there is LOADS of issue there :frowning: In the meanwhile, I will study again that instruction, promised :slight_smile:

One thing per time :wink:

Cheers,

M.

J-M-L:
:slight_smile:

well read again what the first param of strtok() should be once you've done the initialization. Also, you are searching for commas, do you see a lot of those into your buffer?

I should look for ":" and "/" actually right? Those are the separators for date and time :slight_smile:

I will change again the code and understand what param to put in that instruction I am struggling with :frowning:

right, you need to look for the right separators

you will likely see 3 different types of input in your buffer if the separator is '\n'


In the first buffer you'll have the date/time, in the second your value, and in the third your empty line

put the first line in an array and practice with strtok() until you can extract the right elements

try this and see what gets printed in the console (set at 115200 bauds)

char tmpBuffer[] = "17:35:52 03/21/17\r";
const char anyDelimiters[] = " :/";

void setup() {
  char * ptr;
  int i = 0;

  Serial.begin(115200);

  ptr = strtok(tmpBuffer, anyDelimiters);
  Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

  ptr = strtok(NULL, anyDelimiters);
  Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

  ptr = strtok(NULL, anyDelimiters);
  Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

  ptr = strtok(NULL, anyDelimiters);
  Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

  ptr = strtok(NULL, anyDelimiters);
  Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

  ptr = strtok(NULL, anyDelimiters);
  Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

  ptr = strtok(NULL, anyDelimiters); // will be empty
  Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

  ptr = strtok(NULL, anyDelimiters);// will be empty
  Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");
}

void loop() {
}

then once you understand what happens, consider the function [url=http://www.cplusplus.com/reference/cstdlib/atoi/]atoi()[/url]

LordKelvin:
Coding Badly sorry that link is due by the image uploader I am using to post the screenshot here...

Either stop using that site or remove the URL tags before posting.

J-M-L

I started reading your thread while it was still in the project forum. You are ahead of me with arduinos but I have a suggestion for your goal to post the data in the cloud.

I put together some python code on a RasPi that would read a sensor and post in a google spreadsheet. The access to google spreadsheets was in a project named "gspread". I see there is (or maybe) a version for the Arduino. It might be worth your time to look at it when you get to that point.

Regards

JR

Thanks for the note - I'm unsure which project you are talking about

(Also I prefer not using google consumer services for storing data because of their advertising sponsored data collection practice which they then resell. I don't like being the product - I prefer to pay a bit for my own hosting service and domain and keep my privacy and not help them know too much about me - even if it's for storing the temperature of my cellar - of course everyone is free to do what they choose).

J-M-L:
right, you need to look for the right separators

you will likely see 3 different types of input in your buffer if the separator is '\n'


In the first buffer you'll have the date/time, in the second your value, and in the third your empty line

put the first line in an array and practice with strtok() until you can extract the right elements

try this and see what gets printed in the console (set at 115200 bauds)

char tmpBuffer[] = "17:35:52 03/21/17\r";

const char anyDelimiters[] = " :/";

void setup() {
char * ptr;
int i = 0;

Serial.begin(115200);

ptr = strtok(tmpBuffer, anyDelimiters);
Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

ptr = strtok(NULL, anyDelimiters);
Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

ptr = strtok(NULL, anyDelimiters);
Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

ptr = strtok(NULL, anyDelimiters);
Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

ptr = strtok(NULL, anyDelimiters);
Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

ptr = strtok(NULL, anyDelimiters);
Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

ptr = strtok(NULL, anyDelimiters); // will be empty
Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");

ptr = strtok(NULL, anyDelimiters);// will be empty
Serial.print(++i); Serial.print("\t["); Serial.print(ptr); Serial.println("]");
}

void loop() {
}




then once you understand what happens, consider the function `[url=http://www.cplusplus.com/reference/cstdlib/atoi/]atoi()[/url]`

Thanks I used your code and I understood what is doing (hopefully) :slight_smile: It has been very helpful.

OK, let's tidy up the ideas: for what I understood if I need to gather data from a serial device I need to:

  1. build a buffer
  2. parse the data the way I like
  3. Use the parsed data in variables that I can use for my scope.

First insight: Here I have 2 lines, so I should build 2 buffers: inputBuffer1, inputBuffer2 perhaps
Second insight: I am definying the variable for date and time as char...if I use ATOI I can simply save memory and define them as integer
Third insight: in the parse data function I could actually iterate and build an array {hour, min, sec, month, day, year, reading} and use it with the proper index, for example parseddata etc.
Right?
Anyway, all this "thinking is for AFTER.
ATM, here is my new code:
```
*// Code for receiving the data from my analytical scale through RS232 with parsing

// Libraries
#include <LiquidCrystal.h>
#include <SPI.h>
#include <SD.h>

// Interface and Objects definitions
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Creates a LCD LiquidCrystal object
const int chipSelect = 10; // Assigns the pin for the SPI-SD chip selection

const byte bufferSize = 50;
static byte bufferPosition = 0; // Initialises the buffer index
char inputBuffer[bufferSize]; // Array of the data from the scale
char tempBuffer[bufferSize]; // Temporary array for use when parsing

// Variables to hold the parsed data (and initialisation) - the char can become int once I use ATOI
char date_month[2] = {0};
char date_day[2] = {0};
char date_year[2] = {0};
char time_hour[2] = {0};
char time_min[2] = {0};
char time_sec[2] = {0};
float reading = 0.0; // For the samples weight
boolean newData = false; // Our flag

//============

void setup()
{
Serial.begin(9600, SERIAL_7N1); // Includes the scale parameters

pinMode(chipSelect, OUTPUT); // Ensures that the SPI-SD selection pin is an output
lcd.begin(16, 2); // Initialises the interface to the LCD screen
lcd.clear(); // // Clears the LCD screen and positions the cursor in the upper-left corner
if (!SD.begin(chipSelect)) // Checks if the SD card is working
{
lcd.println("SD failed!");
delay(2000);
return;
}

// bufferPosition = 0; // We already initialise it
inputBuffer[0] = '\0'; // Initialises with empty string

}

//============

void loop() {
recvWithEndMarker();
if (newData == true) {
strcpy(tempBuffer, inputBuffer); // Temporary copy to protect the original data
parseData();
saveData();
newData = false; // Resets the flag so the process can start again
}
}

//============

void recvWithEndMarker() {
char endMarker = '\n';
char receivedData;

while (Serial.available() > 0 && newData == false) {
receivedData = Serial.read(); // reads the data from the scale
// Now we copy the data into the buffer until we do not find \n:
if (receivedData != endMarker) {
inputBuffer[bufferPosition] = receivedData;
bufferPosition++;
// Let's avoid overflow:
if (bufferPosition >= bufferSize) {
bufferPosition = bufferSize - 1;
}
}
else {
inputBuffer[bufferPosition] = '\0'; // Terminates the string
bufferPosition = 0; // Resets the buffer index
newData = true; // We have new data read now
}
}
}

//============

void parseData() { // split the data into its parts
char * ptr; // this is used by strtok() as a pointer
const char anyDelim[] = " :/"; // Defines the delimiters fpr strtok()

ptr = strtok(tempBuffer, anyDelim); // our 1st token extracted from the buffer
strcpy(time_hour, ptr); // Copies the token to the appropriate variable

ptr = strtok(NULL, anyDelim); // NULL carries on from previous token's end
strcpy(time_min, ptr);

ptr = strtok(NULL, anyDelim);
strcpy(time_sec, ptr);

ptr = strtok(NULL, anyDelim);
strcpy(date_month, ptr);

ptr = strtok(NULL, anyDelim);
strcpy(date_day, ptr);

ptr = strtok(NULL, "\r");
strcpy(date_year, ptr);

ptr = strtok(NULL, anyDelim);
reading = atof(ptr); // convert this part to a float for the reading cell
}

//============

void saveData()
{
File readings = SD.open("readings.csv", FILE_WRITE); // Opens the file on the SD card
if (readings) // If the file opened OK, write to it on SD card
{
readings.println(inputBuffer); // This is to check the actual buffer
readings.println(tempBuffer); // This is to check the actual temp buffer
readings.print(date_month);
readings.print(',');
readings.print(date_day);
readings.print(',');
readings.print(date_year);
readings.print(',');
readings.print(time_hour);
readings.print(',');
readings.print(time_min);
readings.print(',');
readings.print(time_sec);
readings.print(',');
readings.println(reading);

readings.close(); // Closes the file to save the data on the SD card

} else { // If the file didn't open, print an error
lcd.println("FILE NOT OPEN");
delay(2000);
lcd.clear();
}
}*
```
here is the output:


I cannot see the buffer printed out and also I do not understand why is catching the hour "12", the year "17" and the month "3" and few "0" around....
Hope I am going OK :slight_smile: :slight_smile:

You are making some progress

I think you still don't get that the way you build the buffer is you capture 1 line at a time - and as I tried to depict in the image above, your buffer won't look the same every time you call parseData(). That's a challenge because your parsing function expects both the date and floating point value.

--> your parsing function needs to be smarter. for example you could check if the length of the buffer is < 5 (for example) by using the function strlen() - in which case you can ignore parsing as it's likely not either the time or the value. then you could check if the buffer contains "G OK" (using the strstr() function). if the "G OK" exists, then you know your buffer starts with the floating point and if not you can probably assume it's the time/date.

checking that strtok() does not return a NULL pointer would also help your code be more robust.

last but not least, may be to get started you could ignore the first few input until you got a line with the "G OK" to make sure you are synchronized on date/time/value

I understood the different buffers, but it is getting complicated...that output is not at all what I ecxpected :frowning:

Trying to improve it:

// Code for receiving the data from my analytical scale through RS232 with parsing

// Libraries
#include <LiquidCrystal.h>
#include <SPI.h>
#include <SD.h>

// Interface and Objects definitions
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Creates a LCD LiquidCrystal object
const int chipSelect = 10; // Assigns the pin for the SPI-SD chip selection

const byte bufferSize = 50;
static byte bufferPosition = 0; // Initialises the buffer index
char inputBuffer[bufferSize]; // Array of the data from the scale
char tempBuffer[bufferSize]; // Temporary array for use when parsing

// Variables to hold the parsed data (and initialisation) - the char can become int once I use ATOI
char date_month[2] = {0};
char date_day[2] = {0};
char date_year[2] = {0};
char time_hour[2] = {0};
char time_min[2] = {0};
char time_sec[2] = {0};
float reading = 0.0; // For the samples weight
boolean newData = false; // Our flag

//============

void setup()
{
  Serial.begin(9600, SERIAL_7N1); // Includes the scale parameters

  pinMode(chipSelect, OUTPUT); // Ensures that the SPI-SD selection pin is an output
  lcd.begin(16, 2); // Initialises the interface to the LCD screen
  lcd.clear(); // // Clears the LCD screen and positions the cursor in the upper-left corner
  if (!SD.begin(chipSelect)) // Checks if the SD card is working
  {
    lcd.println("SD failed!");
    delay(2000);
    return;
  }

  // bufferPosition = 0; // We already initialise it
  inputBuffer[0] = '\0'; // Initialises with empty string

}

//============

void loop() {
  recvWithEndMarker();
  if (newData == true) {
    strcpy(tempBuffer, inputBuffer); // Temporary copy to protect the original data
    parseData();
    saveData();
    newData = false; // Resets the flag so the process can start again
  }
}

//============

void recvWithEndMarker() {
  char endMarker = '\n';
  char receivedData;

  while (Serial.available() > 0 && newData == false) {
    receivedData = Serial.read(); // reads the data from the scale
    // Now we copy the data into the buffer until we do not find \n:
    if (receivedData != endMarker) {
      inputBuffer[bufferPosition] = receivedData;
      bufferPosition++;
      // Let's avoid overflow:
      if (bufferPosition >= bufferSize) {
        bufferPosition = bufferSize - 1;
      }
    }
    else {
      inputBuffer[bufferPosition] = '\0'; // Terminates the string
      bufferPosition = 0; // Resets the buffer index
      newData = true; // We have new data read now
    }
  }
}

//============

void parseData() {      // split the data into its parts
  char * ptr; // this is used by strtok() as a pointer
  const char anyDelim[] = " :/"; // Defines the delimiters fpr strtok()

  int sizeBuffer = strlen (tempBuffer);
  if (sizeBuffer > 5) {

    ptr = strtok(tempBuffer, anyDelim); // our 1st token extracted from the buffer
    strcpy(time_hour, ptr); // Copies the token to the appropriate variable

    ptr = strtok(NULL, anyDelim); // NULL carries on from previous token's end
    strcpy(time_min, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(time_sec, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(date_month, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(date_day, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(date_year, ptr);

  }

  else {

    ptr = strtok(tempBuffer, anyDelim);
    reading = atof(ptr); // convert this part to a float for the reading cell

  }

}

//============

void saveData()
{
  File readings = SD.open("readings.csv", FILE_WRITE); // Opens the file on the SD card
  if (readings) // If the file opened OK, write to it on SD card
  {
    readings.println(inputBuffer); // This is to check the actual buffer
    readings.println(tempBuffer); // This is to check the actual temp buffer
    readings.print(date_month);
    readings.print(',');
    readings.print(date_day);
    readings.print(',');
    readings.print(date_year);
    readings.print(',');
    readings.print(time_hour);
    readings.print(',');
    readings.print(time_min);
    readings.print(',');
    readings.print(time_sec);
    readings.print(',');
    readings.println(reading);

    readings.close(); // Closes the file to save the data on the SD card
  } else { // If the file didn't open, print an error
    lcd.println("FILE NOT OPEN");
    delay(2000);
    lcd.clear();
  }
}

New output:

Remember that one of the buffer will be with "G OK" and will be more than 5 characters. so what do you think this will do to the buffer?

if (sizeBuffer > 5) {

    ptr = strtok(tempBuffer, anyDelim); // our 1st token extracted from the buffer
    strcpy(time_hour, ptr); // Copies the token to the appropriate variable

    ptr = strtok(NULL, anyDelim); // NULL carries on from previous token's end
    strcpy(time_min, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(time_sec, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(date_month, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(date_day, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(date_year, ptr);

  }

I got that, that's why in the following code (not posted below) I put > 15 but same output :frowning:

Now I put the condition re the presence or not of "OK". So if we do not have "OK" (buffDevid is NULL), it is the date/time and parse the way I did, if we do, then just consider the buffer with the floating point reading....

// Code for receiving the data from my analytical scale through RS232 with parsing

// Libraries
#include <LiquidCrystal.h>
#include <SPI.h>
#include <SD.h>

// Interface and Objects definitions
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Creates a LCD LiquidCrystal object
const int chipSelect = 10; // Assigns the pin for the SPI-SD chip selection

const byte bufferSize = 50;
static byte bufferPosition = 0; // Initialises the buffer index
char inputBuffer[bufferSize]; // Array of the data from the scale
char tempBuffer[bufferSize]; // Temporary array for use when parsing

// Variables to hold the parsed data (and initialisation) - the char can become int once I use ATOI
char date_month[2] = {0};
char date_day[2] = {0};
char date_year[2] = {0};
char time_hour[2] = {0};
char time_min[2] = {0};
char time_sec[2] = {0};
float reading = 0.0; // For the samples weight
boolean newData = false; // Our flag

//============

void setup()
{
  Serial.begin(9600, SERIAL_7N1); // Includes the scale parameters

  pinMode(chipSelect, OUTPUT); // Ensures that the SPI-SD selection pin is an output
  lcd.begin(16, 2); // Initialises the interface to the LCD screen
  lcd.clear(); // // Clears the LCD screen and positions the cursor in the upper-left corner
  if (!SD.begin(chipSelect)) // Checks if the SD card is working
  {
    lcd.println("SD failed!");
    delay(2000);
    return;
  }

  // bufferPosition = 0; // We already initialise it
  inputBuffer[0] = '\0'; // Initialises with empty string

}

//============

void loop() {
  recvWithEndMarker();
  if (newData == true) {
    strcpy(tempBuffer, inputBuffer); // Temporary copy to protect the original data
    parseData();
    saveData();
    newData = false; // Resets the flag so the process can start again
  }
}

//============

void recvWithEndMarker() {
  char endMarker = '\n';
  char receivedData;

  while (Serial.available() > 0 && newData == false) {
    receivedData = Serial.read(); // reads the data from the scale
    // Now we copy the data into the buffer until we do not find \n:
    if (receivedData != endMarker) {
      inputBuffer[bufferPosition] = receivedData;
      bufferPosition++;
      // Let's avoid overflow:
      if (bufferPosition >= bufferSize) {
        bufferPosition = bufferSize - 1;
      }
    }
    else {
      inputBuffer[bufferPosition] = '\0'; // Terminates the string
      bufferPosition = 0; // Resets the buffer index
      newData = true; // We have new data read now
    }
  }
}

//============

void parseData() {      // split the data into its parts
  char * ptr; // this is used by strtok() as a pointer
  const char anyDelim[] = " :/"; // Defines the delimiters fpr strtok()

  char * bufDivider;
  bufDivider = strstr (tempBuffer, "OK"); // Checks if the string "OK" is in the buffer
  
  if (bufDivider == NULL) { // If not, then it is the date/time buffer...

    ptr = strtok(tempBuffer, anyDelim); // our 1st token extracted from the buffer
    strcpy(time_hour, ptr); // Copies the token to the appropriate variable

    ptr = strtok(NULL, anyDelim); // NULL carries on from previous token's end
    strcpy(time_min, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(time_sec, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(date_month, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(date_day, ptr);

    ptr = strtok(NULL, anyDelim);
    strcpy(date_year, ptr);

  }

  else { // otherwise, it is the samples weight buffer

    ptr = strtok(tempBuffer, anyDelim);
    reading = atof(ptr); // convert this part to a float for the reading cell

  }

}

//============

void saveData()
{
  File readings = SD.open("readings.csv", FILE_WRITE); // Opens the file on the SD card
  if (readings) // If the file opened OK, write to it on SD card
  {
    readings.println(inputBuffer); // This is to check the actual buffer
    readings.println(tempBuffer); // This is to check the actual temp buffer
    readings.print(date_month);
    readings.print(',');
    readings.print(date_day);
    readings.print(',');
    readings.print(date_year);
    readings.print(',');
    readings.print(time_hour);
    readings.print(',');
    readings.print(time_min);
    readings.print(',');
    readings.print(time_sec);
    readings.print(',');
    readings.println(reading);

    readings.close(); // Closes the file to save the data on the SD card
  } else { // If the file didn't open, print an error
    lcd.println("FILE NOT OPEN");
    delay(2000);
    lcd.clear();
  }
}

Output:

I am obtaining always something SO far from what I expect :frowning:

Please help... :confused: :confused: :confused: :confused:

here is another piece of code to run (with console at 115200) and see the output. then go read the code and try to understand how it works (it is what I explained above)

(hopefully that works, just typed it in)

char tmpBuffer1[] = "17:35:52 03/21/17\r";
char tmpBuffer2[] = "15.0091 G OK\r";
char tmpBuffer3[] = "\r";

const char anyDelimiters[] = " :/";

void parseTime(char * buffer)
{
  int i = 0;
  byte  h, m, s, M, D, Y;
  char * ptr;

  ptr = strtok(buffer, anyDelimiters);
  if (ptr) {
    h = atoi(ptr);
    Serial.print(++i); Serial.print("\t["); Serial.print(h); Serial.println("]");
  }

  ptr = strtok(NULL, anyDelimiters);
  if (ptr) {
    m = atoi(ptr);
    Serial.print(++i); Serial.print("\t["); Serial.print(m); Serial.println("]");
  }

  ptr = strtok(NULL, anyDelimiters);
  if (ptr) {
    s = atoi(ptr);
    Serial.print(++i); Serial.print("\t["); Serial.print(s); Serial.println("]");
  }

  ptr = strtok(NULL, anyDelimiters);
  if (ptr) {
    M = atoi(ptr);
    Serial.print(++i); Serial.print("\t["); Serial.print(M); Serial.println("]");
  }

  ptr = strtok(NULL, anyDelimiters);
  if (ptr) {
    D = atoi(ptr);
    Serial.print(++i); Serial.print("\t["); Serial.print(D); Serial.println("]");
  }

  ptr = strtok(NULL, anyDelimiters);
  if (ptr) {
    Y = atoi(ptr);
    Serial.print(++i); Serial.print("\t["); Serial.print(Y); Serial.println("]");
  }

}

void parseVal(char * buffer)
{
  double v = strtod(buffer, NULL); // see http://www.atmel.com/webdoc/avrlibcreferencemanual/group__avr__stdlib_1ga5ee4d110a3bb55d2eadda05e3ebedf8a.html
  Serial.print("Value="); Serial.print("\t["); Serial.print(v, 5); Serial.println("]");
}

void scanBufferType(char * buffer)
{
  Serial.print("\n---------------------\nAnalysing Buffer["); Serial.print(buffer); Serial.println("]\n---------------------");
  if (strlen(buffer) < 5) {
    Serial.println("Not a meaningful entry");
  } else  if (strstr(buffer, "G OK")) { // see http://www.cplusplus.com/reference/cstring/strstr/
    Serial.println("it's a value");
    parseVal(buffer);
  } else {
    Serial.println("it's a time");
    parseTime(buffer);
  }
}

void setup() {
  Serial.begin(115200);

  scanBufferType(tmpBuffer1);
  scanBufferType(tmpBuffer2);
  scanBufferType(tmpBuffer3);
}

void loop() {}

I understood and appreciated all your teaching..this is my code (I adapted from yours):

// Code for receiving the data from my analytical scale through RS232 with parsing

// Libraries
#include <LiquidCrystal.h>
#include <SPI.h>
#include <SD.h>

// Interface and Objects definitions
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Creates a LCD LiquidCrystal object
const int chipSelect = 10; // Assigns the pin for the SPI-SD chip selection

const byte bufferSize = 50;
static byte bufferPosition = 0; // Initialises the buffer index
char inputBuffer[bufferSize]; // Array of the data from the scale
char tempBuffer[bufferSize]; // Temporary array for use when parsing
const char anyDelimiters[] = " :/";
boolean newData = false; // Our flag
byte D, M, Y, h, m, s; // Defines the variable for date/time
double reading; // Defines the variable for the weight reading

//============

void setup()
{
  Serial.begin(9600, SERIAL_7N1); // Includes the scale parameters

  pinMode(chipSelect, OUTPUT); // Ensures that the SPI-SD selection pin is an output
  lcd.begin(16, 2); // Initialises the interface to the LCD screen
  lcd.clear(); // // Clears the LCD screen and positions the cursor in the upper-left corner
  if (!SD.begin(chipSelect)) // Checks if the SD card is working
  {
    lcd.println("SD failed!");
    delay(2000);
    return;
  }

  inputBuffer[0] = '\0'; // Initialises with empty string

}

//============

void loop() {
  recvWithEndMarker();
  if (newData == true) {
    strcpy(tempBuffer, inputBuffer); // Temporary copy to protect the original data
    scanBufferType(tempBuffer);
    saveData();
    newData = false; // Resets the flag so the process can start again
  }
}

//============

void recvWithEndMarker() {
  char endMarker = '\n';
  char receivedData;

  while (Serial.available() > 0 && newData == false) {
    receivedData = Serial.read(); // reads the data from the scale
    // Now we copy the data into the buffer until we do not find \n:
    if (receivedData != endMarker) {
      inputBuffer[bufferPosition] = receivedData;
      bufferPosition++;
      // Let's avoid overflow:
      if (bufferPosition >= bufferSize) {
        bufferPosition = bufferSize - 1;
      }
    }
    else {
      inputBuffer[bufferPosition] = '\0'; // Terminates the string
      bufferPosition = 0; // Resets the buffer index
      newData = true; // We have new data read now
    }
  }
}

//============

void scanBufferType(char * buffer)
{
  if (strlen(buffer) < 5) {
    return;
  } else  if (strstr(buffer, "G OK")) {
       parseVal(buffer);
  } else {
        parseDateTime(buffer);
  }
}

//============

void parseDateTime(char * buffer)
{
  int i = 0;
  byte  h, m, s, M, D, Y;
  char * ptr;

  ptr = strtok(buffer, anyDelimiters);
  if (ptr) {
    h = atoi(ptr);
  }

  ptr = strtok(NULL, anyDelimiters);
  if (ptr) {
    m = atoi(ptr);
  }

  ptr = strtok(NULL, anyDelimiters);
  if (ptr) {
    s = atoi(ptr);
  }

  ptr = strtok(NULL, anyDelimiters);
  if (ptr) {
    M = atoi(ptr);
  }

  ptr = strtok(NULL, anyDelimiters);
  if (ptr) {
    D = atoi(ptr);
  }

  ptr = strtok(NULL, anyDelimiters);
  if (ptr) {
    Y = atoi(ptr);
  }

}

//============

void parseVal(char * buffer)
{
  double v = strtod(buffer, NULL);
  }

//============

void saveData()
{
  File readings = SD.open("readings.csv", FILE_WRITE); // Opens the file on the SD card
  if (readings) // If the file opened OK, write to it on SD card
  {
    readings.println(inputBuffer); // This is to check the actual buffer
    readings.println(tempBuffer); // This is to check the actual temp buffer
    readings.print(D);
    readings.print(',');
    readings.print(M);
    readings.print(',');
    readings.print(Y);
    readings.print(',');
    readings.print(h);
    readings.print(',');
    readings.print(m);
    readings.print(',');
    readings.print(s);
    readings.print(',');
    readings.println(reading);

    readings.close(); // Closes the file to save the data on the SD card
  } else { // If the file didn't open, print an error
    lcd.println("FILE NOT OPEN");
    delay(2000);
    lcd.clear();
  }
}

This is the output:

I know I should not ask but I am very short on time now :frowning: and I really need to finish this :frowning: may you troubleshoot my code, please? :frowning:

I am trying to learn as much as I can but this project looks too complicated for the time I have :frowning: Sorry do not kill me :frowning: :frowning:

Read about variable Scope

You have these as global and local to the parsing functions so global variables are never set (or not the same variable for the weight)

byte D, M, Y, h, m, s; // Defines the variable for date/time
double reading; // Defines the variable for the weight reading

and saveData() again writes everything every time whereas you are receiving time and weight separately.. you need to call saveData() only after you have received the sentence with "G OK". this way you know you've read a time before and a value.

J-M-L:
Read about variable Scope

You have these as global and local to the parsing functions so global variables are never set (or not the same variable for the weight)

"Local variables are not known to functions outside their own" that's why they are all "0" right?

J-M-L:

byte D, M, Y, h, m, s; // Defines the variable for date/time

double reading; // Defines the variable for the weight reading




and `saveData()` again writes everything every time whereas you are receiving time and weight separately.. you need to call `saveData()` only after you have received the sentence with "G OK". this way you know you've read a time before and a value.

So I should call that function straight after the parsing:

void scanBufferType(char * buffer)
{
  if (strlen(buffer) < 5) {
    Serial.println("Not a meaningful entry");
  } else  if (strstr(buffer, "G OK")) {
       parseVal(buffer);
!!!!CALL SAVEDATA HERE!!!!!
  } else {
        parseDateTime(buffer);
!!!!CALL SAVEDATA HERE!!!!!
  }
}

Is it right? Otherwise, in the general loop is printing out whatever available in that moment and not only what I wanted?

You need to "CALL SAVEDATA" only once which is after reading the weight (ie when you have received the "G OK" after parsing the weight), you know that when you get there you had received first the time in the previous buffer and now the weight, so you are good to go for printing out everything.

Yes for variable scope; so don't re-declare them in the parsing functions, just use the globals

You are very close to have this work