send/rcv array via Serial help

I’ve been struggling with trying to send and receive an array over serial to a receiving board, and while I have finally gotten it to display all 3 numbers on the serial monitor, I am thinking I am making it unusable to parse out, meaning it is just sending out 3 ints and the receiving board is just printing out without any ability to determine what is what.

This is just a base sketch, and all I am looking for is 2 bytes (x/y pot values). I have looked at Robin2’s Serial thread, but I’m having a difficult time connecting the dots. The majority of the examples are for receiving while entering data through the serial monitor. I am using analog pot values sent over serial through XBee transmitters, and I have been trying to send/receive numbers. Perhaps it would be better to use ASCII values, but I’m not following some of the terminology used, so it makes it difficult to understand what it is doing. For eg-“strtok”. I’m guessing “str” is string, but what is “tok”? “atoi”? I figured out the “i” part-integer, but the rest is muddy.

Here is the transmitter side, which is a Teensy LC.

byte potX = A0;
byte potY = A1;
byte potXval;
byte potYval;
int myPotVals[3] = {0, 0, 255};
byte n;



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


}

void loop() {
  potXval = map(analogRead(potX), 0, 1023, 0, 200);
  potYval = map(analogRead(potY), 0, 1023, 0, 200);
  myPotVals[0] = potXval;
  myPotVals[1] = potYval;
  myPotVals[2] = 255;

  for (n = 0; n < 3; n++) {
    Serial.println(myPotVals[n], BIN);
    Serial1.write(myPotVals[n]);
  }


  delay(100);


}

The 255 value is not needed, but was put there to try and determine and end of string. While the pot values are limited to 0-200, later in the project I’m sure I will have a 255 value being sent, which could be problematic.

This is the receiving side, which has me confused right now. It compiled fine before, and printed out the x/y pot values and 255, all on new lines, and changed along with the pot values changing on the transmit side. I believe the Parsedata function is not doing anything, as serial is just being read and printed to the serial monitor

This is on a Teensy 3.5 (Sorry Robin2 for mucking your code up so bad…)

const int numChars = 8 ;
char receivedChars[numChars];
char tempChars[numChars];
boolean newData = false;
byte AIN1 = 2;
byte AIN2 = 3;
byte BIN1 = 4;
byte BIN2 = 5;
byte EN = 24;
byte potXval  ;
byte potYval ;
int data;

void setup() {
  pinMode(AIN1, OUTPUT);
  pinMode(AIN2, OUTPUT);
  pinMode(BIN2, OUTPUT);
  pinMode(BIN1, OUTPUT);
  pinMode(EN, OUTPUT);
  Serial.begin(9600);
  Serial2.begin(9600);


}

void loop() {
  if (Serial2.available()>0) {
   strcpy(tempChars, receivedChars);
     parseData();
     data = Serial2.read();
    Serial.println(data);
  
    }
   
   
    if (potXval < 97) {
      digitalWrite(EN, HIGH);
      digitalWrite(AIN1, LOW);
      analogWrite(AIN2, potXval);

    }
    if (potXval > 101) {
      digitalWrite(EN, HIGH);
      digitalWrite(AIN2, LOW);
      analogWrite(AIN1, potXval);

    }
    if (potYval < 97) {
      digitalWrite(EN, HIGH);
      digitalWrite(BIN1, LOW);
      analogWrite(BIN2, potYval);
    }
    if (potYval > 101) {
      digitalWrite(EN, HIGH);
      digitalWrite(BIN2, LOW);
      analogWrite(BIN1, potYval);
    }
  }
  
  delay(10);
}
  void parseData() {
    char * strtokIndx;
    strtokIndx = strtok (tempChars, ",");
    potXval = atoi(strtokIndx);
   // strcpy(potXval, strtokIndx);

    strtokIndx = strtok(NULL, ",");
    potYval = atoi(strtokIndx);

  }

In the end, there may be 20 or more bytes being sent from the transmitter side, and an undetermined number from the receive side back to the transmitter. This is a tank type robot, and this base is for the tracks. Next up is adding an arm, then multiple sensors and wifi video. The latter means integrating a pi and wifi capable of sending the video. I think I’ve got that part on it’s way, but it may be some time before I put it all together. The machine camera has an output to connect to the arduino, allowing tracking with a pan/tilt head. It looks simple enough with the example code and USB from a computer, but losing the wires should be interesting.

Any pointers on what road I should be taking would be welcome, along with some meanings for some of the terms, OR a link to that effect that was written by someone who knows what they are talking about would be awesome. I am leery of just learning from an “expert” anonymous site and learning later it was wrong, like the many sites that say no CL resistor is needed for leds, just plug them into the Uno…
Bad habits are hard to break, and I have enough of them as it is.

What happens if this:

Serial1.write(myPotVals[n]);

only writes one byte to the Serial object? Likewise, what happens if

data = Serial2.read();

only reads one byte?

Finally, what happens if you did this in loop():

   char buffer[5];
   int charsRead;

   if (Serial2.available() > 0) {
      charsRead = Serial2.readBytesUntil('\n', buffer, sizeof(buffer) - 1);
      buffer[charsRead] = '\0';
      data = atoi(buffer);
   }
   // rest of code...

As a sidenote, these are not XBee "modules", but rather just the chip themselves with an external antenna and mounted on a homemade pcb. This means SPI is readily available. Is SPI more advantageous? I'm sure serial will be fast enough for the moment, but as things get more complicated and more data is sent, speed might become an issue. The Delay in the code is slated to go away as soon as I know stability won't be affected. I will be using BWD (yes, I've figured that one out already) to control timing on multiple functions as they require.

Right now, these XBees are just set up as passthrough , but frames can be used up to 64bytes IIRC. It's just a whole new more complicated learning curve to try and understand. I'd like to get basic serial understood first.

** I don't mean to insult anyone's intelligence with trivial statements that are probably basic statements to those who know what's going on, it's more for my own benefit..

Tom

What happens if this:

Serial1.write(myPotVals[n]);

only writes one byte to the Serial object?

I believe that is EXACTLY what is going on, which makes parsing data impossible

Likewise, what happens if

data = Serial2.read();

only reads one byte?

Again, I believe this is what is going on, but on the receive side. It is just printing out a byte, but has no way of determining what var is what. The const 255 was going to be used as a endmarker, but I’m thinking that while it ‘might’ work, it is not correct. I’m trying to learn the ‘correct’ way at this point.

Finally, what happens if you did this in loop():

   char buffer[5];

int charsRead;

if (Serial2.available() > 0) {
      charsRead = Serial2.readBytesUntil(’\n’, buffer, sizeof(buffer) - 1);
      buffer[charsRead] = ‘\0’;
      data = atoi(buffer);
  }
  // rest of code…

That (in general) is what I have envisioned for a couple days now, BUT, I am unsure if I’m getting the ‘\n’ at the end of the array, and I have been unsuccessful in trying to add it. That ‘255’ is where I was trying to add it. The compiler disagreed.

I have come to understand that an array doesn’t include the null at the end from the learning page. It states that the array is ‘0’ indexed, and that for a 10 int array, [9] is the last, while [10] is reading outside of the array and gives invalid data. If it were a null, it wouldn’t be ‘invalid’ but a known value of the null. Am I understanding this somewhat right??

What if:

  • strtokIndx = strtok (tempChars, ",");*

doesn't find a comma and returns NULL? The questions I've asked are designed to help you discover the mistakes you have in your code.

I have come to understand that an array doesn't include the null at the end from the learning page. It states that the array is '0' indexed, and that for a 10 int array, [9] is the last, while [10] is reading outside of the array and gives invalid data. If it were a null, it wouldn't be 'invalid' but a known value of the null. Am I understanding this somewhat right??

It is true that trying to access array[10] would be outside the array, which is why I used:

charsRead = Serial2.readBytesUntil('\n', buffer, sizeof(buffer) - 1);

That ensures that there is room to append the NULL at the end of the input stream in the line following the one above. Therefore, if you have the array defined for 10 elements, the data can be 9 of those elements (0 through 8 ) and the last element is still available for the NULL (array[9]).

tinman13kup: This is on a Teensy 3.5 (Sorry Robin2 for mucking your code up so bad...)

It is not just mucked up. It is completely broken. You have left out the function that actually receives the data.

Make things easy for yourself. Start with one of my examples (the 3rd would be best) without any changes and get it working. If you want to send data in both directions then put the same code on both Teensies.

When that simple example is working make a safe copy of it and then start adding the extra stuff that you need - checking at every stage that it still works.

...R

What if:

strtokIndx = strtok (tempChars, “,”);

doesn’t find a comma and returns NULL? The questions I’ve asked are designed to help you discover the mistakes you have in your code.

I really appreciate it. It’s better to understand than to just ‘know’.
I’m not really following that line though. I guess part of it is not knowing what “strtok” stands for.
I have put your snippet of code in the receiving side, but it just comes up ‘0’

modified code

const int numChars = 8 ;
char receivedChars[numChars];
char tempChars[numChars];
boolean newData = false;
byte AIN1 = 2;
byte AIN2 = 3;
byte BIN1 = 4;
byte BIN2 = 5;
byte EN = 24;
byte potXval  ;
byte potYval ;
int data;
char buffer[5];
int charsRead;

void setup() {
  pinMode(AIN1, OUTPUT);
  pinMode(AIN2, OUTPUT);
  pinMode(BIN2, OUTPUT);
  pinMode(BIN1, OUTPUT);
  pinMode(EN, OUTPUT);
  Serial.begin(9600);
  Serial2.begin(9600);


}

void loop() {



  if (Serial2.available() > 0) {
    charsRead = Serial2.readBytesUntil('\n', buffer, sizeof(buffer) - 1);
    buffer[charsRead] = '\n';
    data = atoi(buffer);
    Serial.println(data);
    parseData();
  }
  // rest of code...

  /*if (Serial2.available() > 0) {
    strcpy(tempChars, receivedChars);
    parseData();
    data = Serial2.read();
    Serial.println(data);

  }*/


  if (potXval < 97) {
    digitalWrite(EN, HIGH);
    digitalWrite(AIN1, LOW);
    analogWrite(AIN2, potXval);

  }
  if (potXval > 101) {
    digitalWrite(EN, HIGH);
    digitalWrite(AIN2, LOW);
    analogWrite(AIN1, potXval);

  }
  if (potYval < 97) {
    digitalWrite(EN, HIGH);
    digitalWrite(BIN1, LOW);
    analogWrite(BIN2, potYval);
  }
  if (potYval > 101) {
    digitalWrite(EN, HIGH);
    digitalWrite(BIN2, LOW);
    analogWrite(BIN1, potYval);
  }


  delay(10);
}
void parseData() {
  char * strtokIndx;
  strtokIndx = strtok (tempChars, ",");
  potXval = atoi(strtokIndx);
  // strcpy(potXval, strtokIndx);

  strtokIndx = strtok(NULL, ",");
  potYval = atoi(strtokIndx);

}

At first, I commented out everything in loop() with the exception of the snippet along with serial print to the monitor. Just 0’s, then I went with the above and get the same.

I have connected the motors up to the driver, and a “0” on the x/y potvalues should have set the motors ON . They don’t do anything, so I don’t think this is getting past the initial if statement.

tinman13kup: At first, I commented out everything in loop() with the exception of the snippet along with serial print to the monitor. Just 0's, then I went with the above and get the same.

I have connected the motors up to the driver, and a "0" on the x/y potvalues should have set the motors ON . They don't do anything, so I don't think this is getting past the initial if statement.

Google is your friend. I just googled "Arduino strtok example" and got almost 5000 hits. Try typing in the code from the here and experiment with it until it makes sense. You'll get a lot more help from this Forum if we see you trying on your own to learn, not just copying code.

Robin2:
It is not just mucked up. It is completely broken. You have left out the function that actually receives the data.

Make things easy for yourself. Start with one of my examples (the 3rd would be best) without any changes and get it working. If you want to send data in both directions then put the same code on both Teensies.

When that simple example is working make a safe copy of it and then start adding the extra stuff that you need - checking at every stage that it still works.

…R

I did try your examples, and they worked fine when entering into the serial monitor. I’m not understanding the “meaning” of the terms used, and that’s where I think I’m going astray. All of the examples use serial monitor input, which also adds that “<,>, \n” in. The learn page for “char str” describes how it adds the /n at the end. Array has no such description, which leads me to believe it is not added. Econjack was trying to explain it, but I’m just a bit dense without understanding the terms associated with it.

All the data I’m looking to send/rcv is numeric. I understand ASCII places a value on a character, and I have the full range of my values available in ASCII.

Do I need to ditch trying to send an array, and instead work with the ASCII values, which seems more inline with your examples?

IIRC, I butchered example 5, trying to eliminate the float and the character message while also trying to use the analog inputs for the data instead of the monitor.

You can send < and > using Serial.print. Send the <, send the actual data comma separated and send the >.

Stay away from Serial.println as it adds ‘\r\n’ (as far as I remember).

Google is your friend. I just googled "Arduino strtok example" and got almost 5000 hits. Try typing in the code from the here and experiment with it until it makes sense. You'll get a lot more help from this Forum if we see you trying on your own to learn, not just copying code.[/]

I'm sorry if I mislead anyone. I don't necessarily want the answers, just looking for nudges in the right direction. I have been experimenting with this part of the code for a couple weeks with as much time as I could spare. If Robin2s serial basics example 3 is the way it should be done, and I should experiment with that to get char values from the inputs and use that on the other end as well, I will go back and spend another month if needed to do it. Just looking for a "yes" that is what needs to be done. How else will I learn?

The code written (without trying to transmit) is of my own, however basic it may be. It is also using terms I can understand the meaning of, which are defined right on the arduino reference page. When I said I don't understand the strtok statement, it is literal. My search defaults to Yahoo search, which is pointless, but going to Google and doing a strtok arduino search got me to a page that told me 'tok' is TOKEN, which is what I was looking for. They also pushed strtok_r, which another page pushes to NOT use it for arduino, to use strtok itself. That is part of what I want to avoid, learning a way that isn't correct for the platforms I'm using, or a way that is just plain wrong.

sterretje:
You can send < and > using Serial.print. Send the <, send the actual data comma separated and send the >.

Stay away from Serial.println as it adds ‘\r\n’ (as far as I remember).

Ugggh! I should have stepped back for awhile and thought about that. I was trying to include it in the array as an int…

I have a picture in my head how to put that in, which, along with Robin2’s examples, might get something through my think skull…maybe.

The serial,println is just to see if something was sent and/or received. No data is entered in the monitor, but I understand what you are saying. If I remove the “ln”, the monitor is likely to stream a neverending stream on numbers.

Alternatively you can use sprintf.

  char txBuffer[32];
  sprintf(txBuffer, "<%d,%d,%d>", myPotVals[0], myPotVals[1], myPotVals[2]);
  Serial.print(txBuffer);

This wastes a bit of RAM compared to individual prints.

tinman13kup:
I’m not understanding the “meaning” of the terms used, and that’s where I think I’m going astray.

If you don’t tell me what you have problems with I can’t help.

To start simple put my 3rd example on one of your Teensies and write a very short program on the other one to send “”
and tell us how you get on. Post BOTH programs as YOU have uploaded them.

It is a complete waste of time worrying about anything else if you cannot get that to work. And the next step will be to get the receiving Teensy to send “” after it receives “”. But one step at a time.

…R

Sorry for the delay, I've been neck deep in strtok.... I have a light grasp on it at the moment as far as what is going on. Never found what "ato*" actually stands for though.

I will work on your reply Robin2. It might take me some time though. Priorities are the terminal wife and 90yr old mother who lives with me, along with dealing with all the associated items taking care of the day to day items.

I'll try not to waste anyone's time, since you seem to know the road I must follow.

Robin2,
I’ve done as you asked, hopefully as basic as you were looking for.
I did opt for 2 teensy LCs instead of the Teensy 3.5.
Rcv Teensy has example 3 uploaded, with the only changes being reading Serial1 and printing to the monitor.

RCV

// Example 3 - Receive with start- and end-markers

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

boolean newData = false;

void setup() {
    Serial.begin(9600);
    Serial1.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 = '>';
    char rc;
 
    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;
    }
}

Look, I didn’t hack it up!!

As for the TX side, you said a short program to sent “”
I’m not sure if this is what you were looking for

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

void loop() {
  Serial1.print("<hello>");
  
}

Yes, I did get "This just in " on the rcv side, in which I would expect it, as it is your code. Other than just adding in the Serial1, it showed the XBee working as well.

If you were looking for something else in the TX side, let me know. Otherwise, I will work on the other portion of what you suggested.

Tom

tinman13kup:
Yes, I did get "This just in " on the rcv side,

Great. That sounds like progress. But it should be saying “This just in Hello” - without the angle brackets. Is it?

Now make safe copies of those working programs and put them to one side so you can go back to them if you run into problems.

For simplicity I will call the sending Teensy A and the receiving Teensy B. At the moment A sends “” to B.

Now add the code that is currently in A to B (and vice versa) so that both Teensies have the same code - both the sending and receiving stuff and post the updated code. It may not work straight away, but don’t worry about that.

…R

You are correct- it only prints "hello"

The next step seems pretty straight forward, and what I expect is both monitors to read the same. Still, I'm going to try and go line by line and type it in instead of copy/paste so I can get a better feel for what each line is doing.

This reminds me of typing in endless lines of code to a commodore vic20 in the early 80's. Wish I would have stuck with it..

Tom

That was moderately difficult to pull off, mostly with my computer. Seems it doesn’t like 2 iterations of the IDE going with the same boards. I might have to pull out a second computer to avoid non-problems.

Here is the code for the RX side, which is now in duplex. I initially had a line in loop to just send the char array(?), but then went into overachiever mode and made it into a response instead in the showNewData function

RX code

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

boolean newData = false;

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

void loop() {
  recvWithStartEndMarkers();
  showNewData();
  // Serial1.print("<hello back>");//simple method

}

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

  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);
    if (receivedChars == "hello") {
      Serial1.print("<hello back>");
    }
    newData = false;
  }
}

The TX side only got the simple line in loop, just to get the response.

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

boolean newData = false;

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

void loop() {
  recvWithStartEndMarkers();
  showNewData();
  Serial1.print("<hello>"); // to initiate response from RX over and over
}

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

  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;
  }
}

I will say this, there are multiple errors in the RX side, getting “h<hello” or other variations. This occurs perhaps 70% of the time. On the TX side, I see an error less than 1% of the time, but since it is only sent if the RX side is correct and actually responds, it’s hard to do a good comparison of the two

This won't work

if (receivedChars == "hello") {

You need to use strcmp() like this

if (strcmp("hello", receivedChars)) {

(hope I have the syntax correct)

...R