Go Down

Topic: Serial call & response between two Unos (Read 562 times) previous topic - next topic

imchipwood

I'm having trouble getting two Arduino Unos to correctly respond to each other. The code is for displaying flight information of a quadcopter running AeroQuad software v3.0.1.

Arduino Uno 1 - receive call for data, send data. Ignore the softwareSerial declarations, I was using those for a bit and just didn't remove them. This works over Serial Monitor - if I type 'a', it responds with data in the format of 'Z(voltage)Z(state)' where voltage is a 4 digit number (mV) and state is either 0 or 1, corresponding to acrobatic and stable modes.
Code: [Select]
#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // rx, tx

int i = 980;
char buffer = 0;

void setup() {
  Serial.begin(115200);
  mySerial.begin(115200);  // opens serial port, sets data rate
}

void loop() {
  if (Serial.available() > 0) {
    buffer = Serial.read();
    if (buffer == 'a') {
      //for (int i = 980; i < 1300; ++i) {
        Serial.print('Z');  // send checkbit
        if (i < 1000) {
          Serial.print('0'); }
        Serial.print(i);    // send valid battery level
        Serial.print('Z');  // send checkbit again
        if (i < 1150) {
          Serial.println('0');  // send rate mode
        } else {
          Serial.println(1);  // send stable mode
        }
        ++i;
        delay(50);
        if (i > 1300) {
          i = 980; }
      //}
    }
  }
}


Arduino Uno 2 - connected to a GLCD to display data. I'm guessing there's a better way to encode/decode the data but I was getting tons of errors on my GLCD without multiple checks on each code, no errors with the checks. This code works when the first Uno is simply spitting out data, not waiting for a request for it. If I remove the Serial.available() and Serial.read() from the code on the first Uno, it works, constantly sending data to the GLCD Uno and the GLCD correctly displays it. It stops working when set up for call and response. I can confirm in Serial Monitor that this code is sending an 'a' approximately every second.
Code: [Select]
void loop() {
  currentTime = millis();
  counter++;
  // check incoming bits for correctly formatted data, fill appropriate buffers
  if (Serial.available() > 0) {
    for (int i = 0; i < 6; ++i) {
      if (i == 1) {        // first, fill check buffer and confirm correct format
        checkBuff[0] = Serial.read();
        delay(2);          // if first bit is 'Z', continue on, else, don't update buffers
      } if (checkBuff[0] == 'Z') {
        if ((i > 0) && (i < 5)) {    // fill battery buffer (bits 1-4)
          battBuff[i - 1] = Serial.read();
          delay(2);
        } else if (i == 5) {          // fill second check buffer
          checkBuff[1] = Serial.read();
          delay(2);
        } else if (checkBuff[1] == 'Z') {
          flightMode = Serial.read();
          delay(2);
          Serial.flush();            // flush serial buffer to make room for more data
          checkBuff[0] = '0';        // reset check buffer
          checkBuff[1] = '0';
        }
      }
    }
  }
  // display information
  if (counter > 20) {
    GLCD.ClearScreen();
    counter = 0; }
  displayBattery(battBuff, 0, 0);
  displayMode(flightMode, 0, 1);
  if ((currentTime - previousTime) > 1000) {
    Serial.print('a');
    previousTime = currentTime;
    delay(20);
  }
}


I didn't bother including the setup() and display functions as they aren't relevant to the problem. So, both Unos work independently through Serial Monitor, but when connected to each other, nothing happens. If Uno 1 just spits out data constantly, the GLCD will display it fine. Nothing changes on the GLCD with the call and response code.

Any help would be appreciated, I'm guessing the two Arduinos aren't syncing up correctly but I don't know how to fix it...

imchipwood

#1
Mar 13, 2012, 01:31 am Last Edit: Mar 13, 2012, 01:37 am by imchipwood Reason: 1
I've set them up to display whatever they're receiving over serial, turns out the one receiving a call for data is not receiving the same character sent to it. What's strange is that it sends the data out fine still if I force the buffer to be the correct character even though it's receiving the wrong character. They seem to be synced up as the GLCD is printing the same data I'm receiving on Serial Monitor, but the byte sent from the GLCD Uno isn't arriving correctly on the other one.

Nick Gammon

This is wrong, basically:

Code: [Select]
  if (Serial.available() > 0) {
...
        checkBuff[0] = Serial.read();
...
          battBuff[i - 1] = Serial.read();
...
          checkBuff[1] = Serial.read();


You only know you have one byte available, but are reading 3 or more. You have to do things a bit differently, for example as I describe here:

http://gammon.com.au/serial

imchipwood

#3
Mar 13, 2012, 09:30 am Last Edit: Mar 13, 2012, 09:50 am by imchipwood Reason: 1
As I understand it the Serial buffer can fit 128 bytes, if the bytes are all sent, calling Serial.read() just pops the next one off.

Regardless, I changed it use a switch statement as it cleaned things up and made my debugging a lot easier, but now I'm running into a very strange problem. Here's the code for the receiving of data:
Code: [Select]
// mode variables
char flightMode = 0;

// buffer variables
char battLevelBuff[4];
byte battCellBuff = 0;

(omitted code for brevity)

void loop() {
if (Serial.available() > 0)
   {
     queryType = Serial.read();
     delay(2);
     switch (queryType)
     {
       case 'Z': // read battery voltage level in mV
         for (int i = 0; i < 4; ++i)
         {
           battLevelBuff[i] = Serial.read();
           delay(2);
         }
         queryType = '0';
         break;
       
       case 'Y':  // read battery cell count
         battCellBuff = Serial.read() - '0';
         queryType = '0';
         delay(2);
         break;
         
       case 'X':  // read flight mode, rate or stable
         flightMode = Serial.read();
         queryType = '0';
         delay(2);
         break;
         
       case '0':  // default case
         break;
     }
}


If I send Z1234 over Serial Monitor, it correct reads this and my display reads 12.34V. If I send X0 or X1, the mode switches on the display correctly. However, sending Y1, Y2, etc. causes the battCellBuff variable to change correctly but it appends '0R' to the end of my voltage buffer. I haven't the slightest idea how to fix this other than chop off the last two bytes of the voltage buffer every time Yx is read (x being an int between 1 and 9). I've changed the case to 'G' and a number of other letters, happens regardless. Sending Y0 removes the appended characters from battCellBuff...

It all works when connected to my quadcopter except the battery cell count part... It's nice being able to flip switches on a remote and see the changes on the GLCD!

Nick Gammon


As I understand it the Serial buffer can fit 128 bytes, if the bytes are all sent, calling Serial.read() just pops the next one off.


Yes but you tested for > 0, not > (the number you expect). Serial transmission takes time. Just testing if something is available doesn't guarantee it is all available.

Nick Gammon

What are the delays for? To make it "work"? Try reading the link I posted.

imchipwood

I did read your link but I don't fully understand either of your methods so I didn't implement them. I'm not much of a coder, I prefer hardware, so it's all still foreign to me. Your images show a 1/980s delay between byte TX, so I threw in small delays to get around it. It's not vital that this display show data at the absolute fastest rate it can, as the data on the quad can only be sent at 100Hz anyway. I realize a delay isn't the most effective method for doing this but it works. I'll fix it tomorrow or some other time, I don't know why I'm still up at 4AM...

Anyway, it works again. I removed the polling for cell count as it can be determined from the voltage level easily if the battery is assumed to be operating in normal conditions - a safe assumption for a flying object.

Nick Gammon

Yes, the delays sort-of work. But if the sending end is interrupted (by an interrupt, heh) then the delay may be longer between bytes. Reworking without delays is more reliable. Basically you just collect into a buffer as fast as you can (which in fact won't be that fast) but without delays. At some "end of packet" indicator (such as newline) you go back to the buffer and extract everything out of it.

Go Up