Go Down

Topic: Sending multiple sensor readings between two Arduinos (Read 1 time) previous topic - next topic

mrbollero

Hello all-

First, thanks for reading. I'm working on an automotive dash display that will read data from a number of sensors (approx 15) and output data for 23 variables. I'm trying to send all of this data from one Arduino to the other via two HC-05 modules. I've been using this example (http://www.esologic.com/parsing-serial-data-sent-to-arduino/),which works with 4 or 5 sensor outputs but adding more and I'm (understandably) getting errors. The total message size is about 160 bytes. Any suggestions on a better approach?

I've thought of maybe converting each sensor reading into a single byte (since almost all are <255), which would reduce the number of bytes to be transferred to about 30, but I'm not sure where to start.

funman1

Why not just split the message into several snipets?
Instead of sending it all at once,
send each sensor 1 at a time with a leading data "header" of which sensor the following data sent corresponds to?

Also what speed are you having them connect and "Talk" at?
I would crank down the speed to the slowest settings possible.
It seems in my projects "slow data" can plow through almost anything, and end up correct on the other side.


 

mrbollero

I was thinking about that- I'm just not sure where to get started coding. I'm still learning about how Serial data is transferred. For example, the coolant temp sensor outputs 100 degrees which is stored as Ct (an int). I could send "<Ct100>" to the second arduino, but I'm not sure how to code it so the second arduino will parse the string it received and recognize that 100 can be assigned to the variable Ct.

Right now I'm transferring at the default 9600 but could easy change to 4800.

funman1

I'm no expert but what I have done is added a special character to the end of my data sends,
IE 100* so the serial keeps getting "next" bytes until the byte == your special character "*" then takes what it has, and dumps it into a variable.

Robin2

Have a look at the examples in Serial Input Basics - simple reliable ways to receive data.

If you have a good receiving system the sending system should be very straightforward.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

mrbollero

#5
Feb 21, 2018, 04:33 pm Last Edit: Feb 21, 2018, 04:41 pm by mrbollero
Thanks all. Robin2- I've seen your serial thread and looked it over multiple times and finally figured out how I can apply it. Thanks for posting that in the first place.

I've tried to adapt your code so that the sending device will send a message like <Ct,100>. The Ct would denote which sensor and the integer the value of the reading. Most of it is working but I'm hung up- I'm trying to have it assign the value of 100 to the Ct variable but the if statement is not activating because it's not seeing messageFromPC as "Ct". Using != works fine and assigns the value to Ct. What am I missing?


Code: [Select]
void parseData() {      // split the data into its parts

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

    strtokIndx = strtok(tempChars,",");      // get the first part - the string
    strcpy(messageFromPC, strtokIndx);// copy it to messageFromPC
   
  strtokIndx = strtok(NULL, ">"); // this continues where the previous call left off
    integerFromPC = atoi(strtokIndx);     // convert this part to an integer

  Serial.print(messageFromPC);  
  if (messageFromPC == "Ct") {   
    Ct = integerFromPC; }
}

mrbollero

Nevermind- realized that messageFromPC is an array so I needed to check it value by value.

Code: [Select]
if (messageFromPC[0] == 'C')

Etc...

Robin2

You can use the strcmp() function to test for a multi-character match.

However, even better would be to use single character codes. Between the upper- and lower-case characters you have 52 options.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

mrbollero

Yeah, I plan on doing that- will clean up the code a little. Thanks again!

mrbollero

#9
Feb 23, 2018, 12:00 am Last Edit: Feb 23, 2018, 12:24 am by mrbollero
Robin- I'm using your code (with modifications of course, but the idea is the same). About 1 in 10 readings or so have errors, however. Here are a couple snippets of printing off the tempChars.

TempChars:M<C,17
25TempChars:fv,4.0
26TempChars:O,17
25TempChars:P,0
24TempChars:R,
26TempChars:H,
25TempChars:S,0
25TempChars:A,383
26TempChars:D,B
25TempChars:U,13
25TempChars:F,5
25TempChars:E,-197
25TempChars:I,,197
26TempChars:B,-197
25TempChars:2
25TempChars:G,,0.008.8

26TempChars:D,
24TempChars:fp,23.8
26TempChars:G,20
25TempChars:ff,0.00
26TempChars:fu,0.00
25TempChars:E,-197
25TempChars:I,-197
26TempChars:B,-197
24TempChars:H,
26TempChars:M,
25TempChars:fx,-58.4
242624252525TempChars:8T,0

The numbers in the 20s are the number of milliseconds it took to run the for loop. You can in the first selection some issues for some variables: I has two commas, and so does G along with additional .8 tagged on the end. Sometimes extra random characters (@, periods, commas, etc) get added into the character array. Any idea what could be causing this? The other arduino is sending data in the following format:

Code: [Select]
Serial32.print("<C,"); Serial32.print(Ct,0); Serial32.print(">");

Here is the code for the functions I'm using:

Code: [Select]

void recvWithStartEndMarkers() {
static boolean recvInProgress = false;
static byte ndx = 0;
char startMarker = '<';
char endMarker = '>';
char rc;

while (Serial3.available() > 0 && newData == false) {
rc = Serial3.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 parseData() {      // split the data into its parts

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


Serial.print("TempChars:"); Serial.println(tempChars);
strtokIndx = strtok(tempChars,",");      // get the first part - the string
strcpy(messageFromPC, strtokIndx);// copy it to messageFromPC

if(messageFromPC[0] == 'f') {
strtokIndx = strtok(NULL, ",");
floatFromPC = atof(strtokIndx);
} else {
strtokIndx = strtok(NULL, ">"); // this continues where the previous call left off
integerFromPC = atoi(strtokIndx); // convert this part to an integer
}
if (messageFromPC[0] == 'C') {
Ct = integerFromPC;
//Serial.print("Coolant:"); Serial.print(Ct); Serial.print("F;");
}

if (messageFromPC[0] == 'O') {
Ot = integerFromPC;
//Serial.print("Oil:"); Serial.print(Ot); Serial.print("F;");
}
if (messageFromPC[0] == 'P') {
Op = integerFromPC;
//Serial.print(Op); Serial.println("PSI");
}
if (messageFromPC[0] == 'R') {
rpm = integerFromPC;
//Serial.print("RPM: "); Serial.println(rpm);
}
if (messageFromPC[0] == 'S') {
Sp = integerFromPC;
//Serial.print("Speed:"); Serial.println(Sp);
}
if (messageFromPC[0] == 'U') {
Va = integerFromPC;
//Serial.print("Vac:"); Serial.print(Va); Serial.println("inHg;");
}
if (messageFromPC[0] == 'F') {
Fl = integerFromPC;
//Serial.print("Fuel:"); Serial.print(Fl);Serial.println("%");
}
if (messageFromPC[0] == 'E') {
Et = integerFromPC;
//Serial.print("Outside Temp:"); Serial.print(Et); Serial.println(" F");
}
if (messageFromPC[0] == 'I') {
It = integerFromPC;
//Serial.print("Internal Temp:"); Serial.print(It); Serial.println(" F");
}
if (messageFromPC[0] == 'B') {
Vt = integerFromPC;
//Serial.print("Vent Temp:"); Serial.print(Vt); Serial.println(" F");
}
if (messageFromPC[0] == 'T') {
Tt = integerFromPC;
////Serial.print("Trans Temp:"); //Serial.print(Tt); //Serial.println(" F")
}
if (messageFromPC[0] == 'Z') {
Z = integerFromPC;
//Serial.print("Heading: "); //Serial.println(Z);
}
if (messageFromPC[0] == 'H') {
Hr = integerFromPC;
//Serial.print("Time:"); //Serial.print(Hr); //Serial.print(":"); //Serial.println(Mn);
}
if (messageFromPC[0] == 'M') {
Mn = integerFromPC;
//Serial.print("Time:"); //Serial.print(Hr); //Serial.print(":"); //Serial.println(Mn);
}
if (messageFromPC[0] == 'A') {
Alt = integerFromPC;
//Serial.print("Altitude:"); //Serial.print(Alt); //Serial.println("m");
}
if (messageFromPC[0] == 'D') {
Cr = integerFromPC;
//Serial.print("Course:"); //Serial.print(Cr); //Serial.println("degrees");
}
if (messageFromPC[0] == 'G') {
Ft = integerFromPC;
//Serial.print("Fuel Temp:"); //Serial.print(Ft); //Serial.println("F");
}
if (messageFromPC[0] == 'f' && messageFromPC[1] == 'v') {
Vav = floatFromPC;
//Serial.print("Voltage:"); //Serial.print(Vav); //Serial.print(" volts");
}
if (messageFromPC[0] == 'f' && messageFromPC[1] == 'u') {
Fu = floatFromPC;
//Serial.print("Trip:"); //Serial.println(Fu); //Serial.print("ml");
}
if (messageFromPC[0] == 'f' && messageFromPC[1] == 'f') {
Ff = floatFromPC;
//Serial.print("Rate:"); //Serial.print(Ff); //Serial.println("L/Min; ");
}
if (messageFromPC[0] == 'f' && messageFromPC[1] == 'p') {
Fp = floatFromPC;
//Serial.print("Fuel Pressure:"); //Serial.print(Fp); //Serial.println("PSI;");
}
if (messageFromPC[0] == 'f' && messageFromPC[1] == 'x') {
X = floatFromPC;
//Serial.print("Left/Right: "); //Serial.println(X);
}
if (messageFromPC[0] == 'f' && messageFromPC[1] == 'y') {
Y = floatFromPC;
//Serial.print("Up/Down: "); //Serial.println(Y);

}
}



Robin2

Please post the complete program.

And before doing so use the AutoFormat tool to make your code easier to read.

It may help to use ELSE IF

And rather than a whole bunch of if (messageFromPC[0] == 'f' && messageFromPC[1] == 'v') { i would do
Code: [Select]
if (messageFromPC[0] == 'f' ) {
    if ( messageFromPC[1] == 'v') {
    
    }
    else if ( messageFromPC[1] == 'u') {

    }
    // etc
}

You will be much less likely to make a mistake

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

mrbollero

Thanks. One issue, I believe, is that the other arduino is taking over 1 second to run it's loop. I have a lot of work to do to trim it, but some of the sensor readings just take 300-600 milliseconds.

The receiving arduino code is below. It was too long to post in it's entirety so I removed much of the lcd printing instructions (left the first few lines in as an example).  I haven't had a chance to go through and add the 'else's but will do that soon.

Code: [Select]

#include <LiquidCrystal.h>
const byte numChars = 12;
char receivedChars[numChars];
char tempChars[numChars];
char messageFromPC[numChars] = {};
int integerFromPC = 0;
float floatFromPC = 0.0;
int Ct, Ot, Et, It, Vt, Tt, Ft, rpm, Cr, Alt, Z;
byte  Fl, Va, Op, Sp, Hr, Mn;
float Vav, Fp, Y, X, Ff, Fu;
boolean newData = false;
unsigned long start;
//Pages
int page_counter = 1;
int calc_page = page_counter;
int up = 45;
boolean current_up = LOW;
boolean last_up = LOW;
boolean current_down = LOW;
boolean last_down = LOW;
int down = 10;           //Down button
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

void setup() {
  Serial.begin(9600);
  Serial3.begin(9600);
  Serial.println("To test, enter data <SensorName,Value>");
  lcd.begin(20, 4);
}

//---- De-bouncing function for all buttons----//
boolean debounce(boolean last, int pin)
{
  boolean current = digitalRead(pin);
  if (last != current)
  {
    delay(5);
    current = digitalRead(pin);
  }
  return current;
}


void loop() {
  start = millis();
  recvWithStartEndMarkers();
  if (newData == true) {
    strcpy(tempChars, receivedChars);
    // this temporary copy is necessary to protect the original data
    //   because strtok() used in parseData() replaces the commas with \0
    parseData();
    //showParsedData();
    newData = false;
  }
  //Pages
  current_up = debounce(last_up, up);         //Debounce for Up button
  current_down = debounce(last_down, down);   //Debounce for Down button
  if (last_up == LOW && current_up == HIGH) { //When up button is pressed
    lcd.clear();                     //When page is changed, lcd clear to print new page
    if (page_counter < 4) {           //Page counter never higher than 3(total of pages)
      page_counter = page_counter + 1; //Page up
    }
    else {
      page_counter = 1;
    }
  }
  last_up = current_up;
  //Page Down
  if (last_down == LOW && current_down == HIGH) { //When down button is pressed
    lcd.clear();                     //When page is changed, lcd clear to print new page
    if (page_counter > 1) {           //Page counter never lower than 1 (total of pages)
      page_counter = page_counter - 1; //Page down
    }
    else {
      page_counter = 1;
    }
  }
  last_down = current_down;
  //Screen 1:
  switch (page_counter) {
    case 1: {
        lcd.setCursor(0, 0);
        lcd.print("Clnt:");
        lcd.print(Ct);
        lcd.setCursor(9, 0);
        lcd.print("F;");
        lcd.setCursor(11, 0);
        lcd.print("Volt:");
        lcd.print(Vav, 1);
      //...
      }
      break;
///3 more pages
  
  }
  Serial.print(millis() - start);
}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (Serial3.available() > 0 && newData == false) {
    rc = Serial3.read();

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

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

void parseData() {      // split the data into its parts
  char * strtokIndx; // this is used by strtok() as an index
  strtokIndx = strtok(tempChars, ",");     // get the first part - the string
  strcpy(messageFromPC, strtokIndx);// copy it to messageFromPC
  Serial.print("TempChars:"); Serial.println(strtokIndx);

  if (messageFromPC[0] == 'f') {
    strtokIndx = strtok(NULL, ",");
    floatFromPC = atof(strtokIndx);
  } else {
    strtokIndx = strtok(NULL, ">"); // this continues where the previous call left off
    integerFromPC = atoi(strtokIndx); // convert this part to an integer
  }
  Serial.print("TempChars:"); Serial.println(strtokIndx); Serial.print(floatFromPC); Serial.print(integerFromPC);

  if (messageFromPC[0] == 'C') {
    Ct = integerFromPC;
    //Serial.print("Coolant:"); Serial.print(Ct); Serial.print("F;");
  }

  if (messageFromPC[0] == 'O') {
    Ot = integerFromPC;
    //Serial.print("Oil:"); Serial.print(Ot); Serial.print("F;");
  }
  if (messageFromPC[0] == 'P') {
    Op = integerFromPC;
    //Serial.print(Op); Serial.println("PSI");
  }
  if (messageFromPC[0] == 'R') {
    rpm = integerFromPC;
    //Serial.print("RPM: "); Serial.println(rpm);
  }
  if (messageFromPC[0] == 'S') {
    Sp = integerFromPC;
    //Serial.print("Speed:"); Serial.println(Sp);
  }
  if (messageFromPC[0] == 'U') {
    Va = integerFromPC;
    //Serial.print("Vac:"); Serial.print(Va); Serial.println("inHg;");
  }
  if (messageFromPC[0] == 'F') {
    Fl = integerFromPC;
    //Serial.print("Fuel:"); Serial.print(Fl);Serial.println("%");
  }
  if (messageFromPC[0] == 'E') {
    Et = integerFromPC;
    //Serial.print("Outside Temp:"); Serial.print(Et); Serial.println(" F");
  }
  if (messageFromPC[0] == 'I') {
    It = integerFromPC;
    //Serial.print("Internal Temp:"); Serial.print(It); Serial.println(" F");
  }
  if (messageFromPC[0] == 'B') {
    Vt = integerFromPC;
    //Serial.print("Vent Temp:"); Serial.print(Vt); Serial.println(" F");
  }
  if (messageFromPC[0] == 'T') {
    Tt = integerFromPC;
    ////Serial.print("Trans Temp:"); //Serial.print(Tt); //Serial.println(" F")
  }
  if (messageFromPC[0] == 'Z') {
    Z = integerFromPC;
    //Serial.print("Heading: "); //Serial.println(Z);
  }
  if (messageFromPC[0] == 'H') {
    Hr = integerFromPC;
    //Serial.print("Time:"); //Serial.print(Hr); //Serial.print(":"); //Serial.println(Mn);
  }
  if (messageFromPC[0] == 'M') {
    Mn = integerFromPC;
    //Serial.print("Time:"); //Serial.print(Hr); //Serial.print(":"); //Serial.println(Mn);
  }
  if (messageFromPC[0] == 'A') {
    Alt = integerFromPC;
    //Serial.print("Altitude:"); //Serial.print(Alt); //Serial.println("m");
  }
  if (messageFromPC[0] == 'D') {
    Cr = integerFromPC;
    //Serial.print("Course:"); //Serial.print(Cr); //Serial.println("degrees");
  }
  if (messageFromPC[0] == 'G') {
    Ft = integerFromPC;
    //Serial.print("Fuel Temp:"); //Serial.print(Ft); //Serial.println("F");
  }
  if (messageFromPC[0] == 'f' && messageFromPC[1] == 'v') {
    Vav = floatFromPC;
    //Serial.print("Voltage:"); //Serial.print(Vav); //Serial.print(" volts");
  }
  if (messageFromPC[0] == 'f' && messageFromPC[1] == 'u') {
    Fu = floatFromPC;
    //Serial.print("Trip:"); //Serial.println(Fu); //Serial.print("ml");
  }
  if (messageFromPC[0] == 'f' && messageFromPC[1] == 'f') {
    Ff = floatFromPC;
    //Serial.print("Rate:"); //Serial.print(Ff); //Serial.println("L/Min; ");
  }
  if (messageFromPC[0] == 'f' && messageFromPC[1] == 'p') {
    Fp = floatFromPC;
    //Serial.print("Fuel Pressure:"); //Serial.print(Fp); //Serial.println("PSI;");
  }
  if (messageFromPC[0] == 'f' && messageFromPC[1] == 'x') {
    X = floatFromPC;
    //Serial.print("Left/Right: "); //Serial.println(X);
  }
  if (messageFromPC[0] == 'f' && messageFromPC[1] == 'y') {
    Y = floatFromPC;
    //Serial.print("Up/Down: "); //Serial.println(Y);

  }
}

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

Robin2

One issue, I believe, is that the other arduino is taking over 1 second to run it's loop.
Then you need to fix that. Aim for AT MOST 100 millisecs. And 10 millisecs would be better.

If printing to the LCD is causing the problem then only do a little bit of the printing in each iteration of loop. The human eye won't notice.

If (which I doubt) it is completely impossible to reduce the loop() time for the receiver then you will have to slow down the transmitter so it sends less than 64 bytes in the time it takes the receiver to loop().

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

mrbollero

I was able to reduce from over 2 seconds down to about 600 milliseconds but I don't see it getting much faster. There are just way too many sensors to get it below the 100 mark. Another issue is that I'm having to use Software Serial to send the data due to some wiring issues, but I have a pcb coming that will allow me to resolve that. I would guess many of the errors come from that. Thanks for the suggestions.

Robin2

I was able to reduce from over 2 seconds down to about 600 milliseconds but I don't see it getting much faster. There are just way too many sensors to get it below the 100 mark.
Why would it take more than 1 millisec to get data from a sensor? Indeed why would it take more than 10 microsecs?

But without seeing your complete program and the datasheets for the sensors I can't say more than that.

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

Go Up