Recieving a seriel string, save character, rewrite and send modified string

How am i able to temporaely save the data from a string that looks something like this?

:SQ±LLLL VVVV AAAA±CCCC±SSS 1111 2222 3333 4444 5555 6666 QQ[CR][LF]

An example of what i need to do is.

The value that S holds i would like to save in a variable
The value that Q holds i would like to save in a variable
The value that LLLL holds i would like to save in a variable
etc.etc.

And in more or less the same scan i need to rewrite it and send it in another modified string.

: T Q+/-000 000 000+/-000+/-00000+/-00000+/-00000+/-00000 QQ[CR][LF]

I hope there is somebody there who can help, I am stuck :frowning:

Br Mogensen

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data. There is also a parse example.

Once you have received and saved the data it should be simple to iterate over the char array and change any values before resending.

...R

Thanks Robin2.

I am still a bit confused.

Am I able to parse the data when there are no commas or < ?? Is there a function where i can split :SQ±LLLL

So that i read the S, Q and ±LLLL seperate?

Br Mogensen

If the characters are always in the same place you can select them by position. Probably use the strncpy() function. Or just start at the appropriate place and iterate over the next N characters. For example

byte n = 0;
for (n = startPos; n < numBytes; n++) {
    tmpArray[n-startPos] = receivedChars[n];
}
tmpArray[n] = '\0'; // terminating char

Make sure to create tmpChar[] longer than the max you will need

...R

So if i start with extracting the S

SQ±LLLL

byte n = 0;
for (n = startPos; n < numBytes; n++1) { // 1 To read the S
tmpArray[n-startPos] = receivedChars[n];
}
tmpArray[n] = '\0'; // terminating char

and the Q

byte n = 1;
for (n = startPos; n < numBytes; n++1) { // 1 To read the Q
tmpArray[n-startPos] = receivedChars[n];
}
tmpArray[n] = '\0'; // terminating char

Is this the way I should understand it? Arduino is a bit new to me, so your help i very appreciated :slight_smile:

Br Mogensen

I thought you wanted "SQ" as a pair? Followed by 5 bytes for +LLLL?

The variable numBytes should have the number of bytes (or characters) that you want to read. numChars might have been a better name.

Otherwise you seem to have the right idea.

You should also get into the habit of using Serial.println() to show you the results of what you are doing. When it works you can delete the print code.

...R

Assuming fixed positions (and hence fixed length for variables).

char data[] = ":SQ±LLLL VVVV AAAA±CCCC±SSS 1111 2222 3333 4444 5555 6666 QQ";

void setup()
{

  char s[2];  // will hold "S"
  char q[2];  // will hold "Q"
  char l[6];  // will hold "±LLLL"
  char v[5];  // will hold "VVVV"
  ...
  ...

  // clear all arrays; also takes care of string terminator so we don't have to worry about it.
  memset(s, 0, sizeof(s));
  memset(q, 0, sizeof(q));
  memset(l, 0, sizeof(l));
  memset(v, 0, sizeof(v));
  ...
  ...

  // copy the data to their respective variables
  memcpy(s, &data[1], sizeof(s) - 1); // copy one character from position 1 in data
  memcpy(q, &data[2], sizeof(q) - 1); // copy one character from position 2 in data
  memcpy(l, &data[3], sizeof(l) - 1); // copy five characters from position 3 in data
  memcpy(v, &data[9], sizeof(v) - 1); // copy four characters from position 9 in data
  ...
  ...
  
  Serial.begin(115200);
  Serial.print("s: "); Serial.println(s);
  Serial.print("q: "); Serial.println(q);
  Serial.print("l: "); Serial.println(l);
  Serial.print("v: "); Serial.println(v);

}

void loop()
{

}

Every array is one larger than the number of characters it has to store so there is space for a terminating nul character. Clearing the arrays fills the array with nul characters and hence we do not have to worry about that.

The memcpy simply copies a gived number of characters from source to destination; the number of characters is one smaller than the size of the target array so the nul character will not be overwritten and we have proper c-style strings.

The result of above

s: S
q: Q
l: ±LLLL
v: VVVV

If you don't have fixed positions, you can't use the above but you probably need to find the spaces in the text and take it from there (see Robin2's parse example in the Serial Input Basics thread).

I'm not quite sure about your second requirement; I don't see a relation between all these zeroes and the LLLL, VVVV etc.

Note
Please use code tags when posting code.

Thanks a million Robin2.

Br Johnny Mogensen

Hmm sorry to bother you again.

It will not let me write a seriel string into the array. Cant determine size of array?

Is there an easy way to do this?

Br Mogensen

#include <Wire.h>  
#include <EEPROM.h>  
#include <PString.h> 
#include <LiquidCrystal.h> //JMO
#include <SoftwareSerial.h>

// Mega Board

///// LCD JMO //////////  

LiquidCrystal lcd(12, 11, 5, 4, 3, 2); //Create liquidCrystal JMO


////////////////////////////////////////////////////////////// software serial connections determine input and output of seriel ports///////////////////////////////////////////////////////////////////////////////////////////////// 

const int RX1 = 19;
const int TX1 = 18;
const int RX2 = 17;
const int TX2 = 16;

// create SoftwareSerial objects
SoftwareSerial SoftSerialOne(RX1,TX1);
SoftwareSerial SoftSerialTwo(RX2,TX2);


int ReadFromSerielPort1 =0;    //JMO data from port 2
int ReadFromSerielPort2 =0;
///////////////////////////////////////////////////////// Constants inputs outputs and registers /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


int TSS440Running = 22;

boolean RTSS440Running; 
boolean RTSS350Running; 

//char data[] = ":SQ±LLLL VVVV AAAA±CCCC±SSS 1111 2222 3333 4444 5555 6666 QQ";


void setup() {

/// LCD BEGIN JMO //
     lcd.begin(16, 2);

//////////////// JMO In / Out Defined ///////////////////////////////////
 
pinMode(TSS440Running,INPUT);

///////////////////////// setup the software serial pins /////////////////////////////////////

pinMode(RX1, INPUT);
pinMode(RX2, INPUT);
pinMode(TX1, OUTPUT);
pinMode(TX2, OUTPUT);

Serial.begin(9600);
SoftSerialOne.begin(9600);
SoftSerialTwo.begin(9600);
(SoftSerialOne.available() > 0) ;
char data[] = SoftSerialOne.read();

char s[2];  // will hold "S"
char q[2];  // will hold "Q"
char llll[6];  // will hold "±LLLL"
char vvvv[5];  // will hold "VVVV"
char aaaa[5];  // will hold "AAAA"
char cccc[6];  // will hold "±CCCC"
char sss[5];  // will hold "±SSS"
char a1111[5];  // will hold "1111"
char a2222[5];  // will hold "2222"
char a3333[5];  // will hold "3333"
char a4444[5];  // will hold "4444"
char a5555[5];  // will hold "5555"
char a6666[5];  // will hold "6666"
char qq[3];  // will hold "QQ"


// clear all arrays; also takes care of string terminator so we don't have to worry about it.
memset(s, 0, sizeof(s));
memset(q, 0, sizeof(q));
memset(llll,  0, sizeof(llll));
memset(vvvv,  0, sizeof(vvvv));
memset(aaaa,  0, sizeof(aaaa));
memset(cccc,  0, sizeof(cccc));
memset(sss,   0, sizeof(sss));
memset(a1111, 0, sizeof(a1111));
memset(a2222, 0, sizeof(a2222));
memset(a3333, 0, sizeof(a3333));
memset(a4444, 0, sizeof(a4444));
memset(a5555, 0, sizeof(a5555));
memset(a6666, 0, sizeof(a6666));
memset(qq,    0, sizeof(qq));

//char data[] = ":<S1><Q2><±LLLL3> <VVVV10> <AAAA15><±CCCC20><±SSS 1111 2222 3333 4444 5555 6666 QQ";
//char data[] = ":SQ±LLLL VVVV AAAA±CCCC±SSS 1111 2222 3333 4444 5555 6666 QQ";
//                1-2-3  - 9  - 14 - 18 - 23 -28-  33  -38 - 43  -48 - 53 -58
// copy the data to their respective variables
memcpy(s,     &data[1],  sizeof(s) - 1);     // copy one character from position 1 in data
memcpy(q,     &data[2],  sizeof(q) - 1);     // copy one character from position 2 in data
memcpy(llll,  &data[3],  sizeof(llll) - 1);  // copy five characters from position 3 in data
memcpy(vvvv,  &data[9],  sizeof(vvvv) - 1);  // copy four characters from position 9 in data
memcpy(aaaa,  &data[14], sizeof(aaaa) - 1);  // copy four characters from position 14 in data
memcpy(cccc,  &data[18], sizeof(cccc) - 1);  // copy five characters from position 18 in data
memcpy(sss,   &data[23], sizeof(sss) - 1);   // copy four characters from position 23 in data
memcpy(a1111, &data[28], sizeof(a1111) - 1); // copy four characters from position 28 in data
memcpy(a2222, &data[33], sizeof(a2222) - 1); // copy four characters from position 33 in data
memcpy(a3333, &data[38], sizeof(a3333) - 1); // copy four characters from position 38 in data
memcpy(a4444, &data[43], sizeof(a4444) - 1); // copy four characters from position 43 in data
memcpy(a5555, &data[48], sizeof(a5555) - 1); // copy four characters from position 48 in data
memcpy(a6666, &data[53], sizeof(a6666) - 1); // copy four characters from position 53 in data
memcpy(qq,    &data[58], sizeof(qq) - 1);    // copy two characters from position 58 in data

Serial.begin(9600);
Serial.print("s: "); Serial.println(s);
Serial.print("q: "); Serial.println(q);
Serial.print("llll: "); Serial.println(llll);
Serial.print("vvvv: "); Serial.println(vvvv);
Serial.print("aaaa: "); Serial.println(aaaa);
Serial.print("cccc: "); Serial.println(cccc);
Serial.print("sss: "); Serial.println(sss);
Serial.print("a1111: "); Serial.println(a1111);
Serial.print("a2222: "); Serial.println(a2222);
Serial.print("a3333: "); Serial.println(a3333);
Serial.print("a4444: "); Serial.println(a4444);
Serial.print("a5555: "); Serial.println(a5555);
Serial.print("a6666: "); Serial.println(a6666);
Serial.print("qq: "); Serial.println(qq);

}

void loop() {
////////////////////////////// TSS 440 ////////////////////////////////////

}

Data was just an example to show how you could get the values. You need to implement the principles of Robin2's Serial Input Basics thread first. That will give you a c-style string (array of characters) that you can parse as I showed above.

And if you use a Mega, don't use software serial unless you run out of serial ports; the Mega has 4 hardware serial ports (Serial, Serial1, Serial2 and Serial3); use those instead.

Ok thanks sterretje I will try to. Is there anyway to adapt your metode? It seamed pretty straight forward to begin with :slight_smile:

Br Mogensen

Base your code on the second example in the Serial Input Basics thread.

You can rename (e.g. extractValues()) and modify the showNewData() function and incorporate my code in there. It depends on what you eventually need what it will exactly look like; I prefer to use local variables (and the suggested code uses that in setup())'; global variables or a struct might suite your needs better.

Thanks I will try to have a look and I promisses I won`t bother you more today :slight_smile:

Br Mogensen

@Mogensen, when you post code please use the code button </> so it looks like Reply #6.

It would be nice if you would update your Reply #8 accordingly.

...R

Sorry for that will do next time.

Br Mogensen

I can`t seem to get my head around on how to split the date.

I would like to use example 3 to extract the seriel date, but how should i split the data when there is no , etc etc.

String :SQ±LLLL VVVV AAAA±CCCC±SSS 1111 2222 3333 4444 5555 6666 QQ[CR][LF]

And the code i would like to use.

I guees I will have the complete sentence in ndx but how to seperate it?

const byte numChars = 68;
char receivedChars[numChars];

boolean newData = false;

void setup() {
    Serial.begin(9600);
    Serial.println("<Arduino is ready>");
}

void loop() {
    recvWithStartEndMarkers();
    showNewData();
}

void recvWithStartEndMarkers() {
    static boolean recvInProgress = false;
    static byte ndx = 0;
    char startMarker = ':';
    char endMarker = 'n';
    char rc;
 
 // if (Serial.available() > 0) {
    while (Serial1.available() > 0 && newData == false) {
        rc = Serial1.read();

        if (recvInProgress == true) {
            if (rc != endMarker) {
                receivedChars[ndx] = rc;
                ndx++;
                if (ndx >= numChars) {
                    ndx = numChars - 1;
                }
            }
            else {
                receivedChars[ndx] = '\0'; // terminate the string
                recvInProgress = false;
                ndx = 0;
                newData = true;
            }
        }

        else if (rc == startMarker) {
            recvInProgress = true;
        }
    }
}

void showNewData() {
    if (newData == true) {
        Serial.print("This just in ... ");
        Serial.println(receivedChars);
        newData = false;
    }
}

The examples you have been showing us are stylized or theorectical. Can you provide a couple of examples with actual data in them.

May I assume that you are receiving the complete string correctly?

...R

Thats a little problem because the actually instrument that should provide the string is not up and running but it could look like this.

This is a different string but the same principle.

: T Q+/-000 000 000+/-000+/-00000+/-00000+/-00000+/-00000 QQ[CR][LF]

:T -004 130 055+075+00170+00324+00223-00000 00

Or

T?+??? ??? 085+???+00008+00012+00012+00000 99

Or

:T?+132 200 085+115+00062+00033+00016-00000 20

etc. etc

Br Mogensen

Your endmarker is not the character 'n' but the character '\n' (linefeed).

Next, if your text does not contain a comma but a space, you can split on that space instead of the comma.

But as you have a fixed width (at least that is the assumption)

void showNewData()
{
  char s[2];  // will hold "S"
  char q[2];  // will hold "Q"
  char l[6];  // will hold "±LLLL"
  char v[5];  // will hold "VVVV"
  ...
  ...

  if (newData == true)
  {
    // Robin2's original
    Serial.print("This just in ... ");
    Serial.println(receivedChars);
    newData = false;

    // my addition from an earlier post.

    // clear all arrays; also takes care of string terminator so we don't have to worry about it.
    memset(s, 0, sizeof(s));
    memset(q, 0, sizeof(q));
    memset(l, 0, sizeof(l));
    memset(v, 0, sizeof(v));
    ...
    ...

    // copy the data to their respective variables
    memcpy(s, &receivedChars[1], sizeof(s) - 1); // copy one character from position 1 in data
    memcpy(q, &receivedChars[2], sizeof(q) - 1); // copy one character from position 2 in data
    memcpy(l, &receivedChars[3], sizeof(l) - 1); // copy five characters from position 3 in data
    memcpy(v, &receivedChars[9], sizeof(v) - 1); // copy four characters from position 9 in data
    ...
    ...

    Serial.begin(115200);
    Serial.print("s: "); Serial.println(s);
    Serial.print("q: "); Serial.println(q);
    Serial.print("l: "); Serial.println(l);
    Serial.print("v: "); Serial.println(v);
  }
}

Please explain the '±' in SQ±LLLL; is that either a plus or a minus. If it's a plus or minus, is the plus always there for positive numbers. Same for '+/-' in : T Q+/-000.

Can you please post an example of the input text and which fields must go where in the output text so we can match it.

:SQ±LLLL VVVV AAAA±CCCC±SSS 1111 2222 3333 4444 5555 6666 QQ

: T Q+/-000 000 000+/-000+/-00000+/-00000+/-00000+/-00000 QQ

Note that the latter does not contain LLLL or VVVV etc so we have no idea.

Ok I will try :-?

If we take this string instead.

_ = space character

+/- can both be positive and negative value.

?+? is 3 question mark if i make 3 it makes a ???

: T Q+/-000 000 000+/-000+/-00000+/-00000+/-00000+/-00000 QQ[CR][LF]

: Start character
T = Packet identtifier
Q=Quality flag can be "blank" or a ?
lat=+/-000 =Lat offset can hold +/-value or a ?+?
ver=+/-000=Ver offset can hold +/-value or a ?+?
alt=+/-000=alt can hold +/-value
dob=+/-000 dob can hold +/-value or a ?+?
Coil1=Can hold +- values
Coil2=Can hold +- values
Coil3=Can hold +- values
Coil4=Can hold +- values
QQ= Holds a value from 00 to 99

The string looks something like this note that the string only is separated with _ = Space

:TQ+/-lat_Ver_+/-alt +/-dob+/-Coil1+/-Coil2+/-Coil3+/-Coil4_QQ[CR][LF]
:T -004 130 055 + 075 +00170 +00324 +00223 -00000 00 //Values like they could be
:T? +??? ?+? 085 + ?+? +00008 +00012 +00012 +00000 99 //Values like they also could look like this etc.etc.

Does it make any sense?

Br Mogensen