Go Down

Topic: Only reading one byte at a time through the serial port (Read 2468 times) previous topic - next topic

notorious



I have an issue which is simple but perplexing at the same time:

I am sending a simple command over the serial port on a Mega 2560 over Serial1 to a device which returns a serial response back. For example I am sending:

const unsigned int STATUS_SEND[] = {0x61, 0x20, 0x11, 0x92};

using a button connected to one of the digital i/o pins correctly configured and all that.

I am expeciting to recieve a hex stream that should be 7120A031, however what I find is the Serial monitor only sends back 71. If I continue to press the button I eventually get the rest after a total of 4 button presses which look like this on the serial monitor:

71
20
A0
31

Now if I use Tera Term and send the commands through there I get the whole stream in one go as I should. I should be getting up to 128 characters through the serial buffer but it doesnt seem to be the case. I havent changed anything and my code to read it is quite standard and straight forward:

Serial1.print(STATUS_SEND);

if (Serial1.available() > 0) {

int read_status = Serial1.read();

Serial.print(read_status, HEX);

}

Any ideas?

pYro_65

#1
Jul 12, 2012, 03:11 pm Last Edit: Jul 12, 2012, 03:13 pm by pYro_65 Reason: 1
you have only asked for one character when checking available so your code may start before 128 bytes of data has been received.

Code: [Select]

#define NUMBER_OF_BYTES_REQUIRED  128
if (Serial1.available() >= NUMBER_OF_BYTES_REQUIRED ) {

//Do something with 128 bytes not 1.
int read_status = Serial1.read();

Serial.print(read_status, HEX);

}

PaulS

Quote
Only reading one byte at a time through the serial port

That's the way it works. That should come as no surprise.

Quote
I am sending a simple command over the serial port on a Mega 2560 over Serial1 to a device which returns a serial response back. For example I am sending:

const unsigned int STATUS_SEND[] = {0x61, 0x20, 0x11, 0x92};

We'd need to see proof of that.

Quote
using a button connected to one of the digital i/o pins correctly configured and all that.

For what purpose?

Quote
I am expeciting to recieve a hex stream that should be 7120A031, however what I find is the Serial monitor only sends back 71. If I continue to press the button I eventually get the rest after a total of 4 button presses which look like this on the serial monitor:

71
20
A0
31

What you are expecting, and what actually happens, appear to be two different things. Serial data is sent one byte at a time. You need to read the whole reply, one byte at a time.

Quote
Any ideas?

Don't post code snippets. Post all of your code.

cyclegadget

I added comments to the code to explain what is happening. You need to add a code statement that says... do not send "STATUS_SEND" until I have read "x" bytes from Serial1.

Code: [Select]
Serial1.print(STATUS_SEND);  // step 1

if (Serial1.available() > 0) {  // step 2 check for byte available

int read_status = Serial1.read(); //read 1 byte save to read_status

Serial.print(read_status, HEX);   // print read_status

}

//return to step 1
Good links: Eagle tutorial= http://www.youtube.com/playlist?list=PLDE1858BD83D19C70
General Arduion tutorials = http://tronixstuff.wordpress.com
http://www.gammon.com.au/forum/bbshowpost.php?bbtopic_id=123

PaulS

@pyro_65
With a 64 character serial buffer (1.0+), waiting for the buffer to hold more than 128 characters will require a very long wait. That is not a realistic way of reading large amounts of data.

notorious




Here is my code:

Code: [Select]
#include <Bounce.h>



const unsigned int button = 30;
char STATUS_READ[] = {0x61, 0x21, 0x11, 0x92};


void setup() {
 
  Serial.begin(19200);                     //terminal serial port open
  Serial1.begin(38400);                    //DVR Serial port open
  UCSR1C = (UCSR1C & 0xCF) | (0b11 << 4);  //Change serial 1 to odd parity
 
 
  pinMode(button, INPUT);
}

const unsigned int DEBOUNCE_DELAY = 20;
Bounce button_fire(button, DEBOUNCE_DELAY);


void loop() {
  handle_button_fire();
}

void handle_button_fire() {
  if (button_fire.update()) {
    if (button_fire.read() == HIGH) {
     
      Serial1.print(STATUS_READ);
     
      if (Serial1.available() > 0) {
     
      int readstatus = Serial1.read();
      Serial.println(readstatus, HEX);
      }
    }
  }
}




notorious



I know I can get around the problem by having a simple loop that runs 4 times to return what I am expecting, but is there no way to just read the stream buffer as it is because I'm not expecting the hardware will go crazy and send a load of junk down the port?


Quote
#define NUMBER_OF_BYTES_REQUIRED  128
if (Serial1.available() >= NUMBER_OF_BYTES_REQUIRED ) {

//Do something with 128 bytes not 1.
int read_status = Serial1.read();

Serial.print(read_status, HEX);

}


I have tried the above but with no luck...


AWOL

See the comment above about receive buffer size.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

PaulS

Quote
I know I can get around the problem by having a simple loop that runs 4 times to return what I am expecting, but is there no way to just read the stream buffer as it is because I'm not expecting the hardware will go crazy and send a load of junk down the port?

How do you know to expect 4 bytes?

Are you not aware that serial data can get lost?

The fact that you are not expecting the hardware to go crazy is no guarantee that it won't.

notorious



So what your saying is the only way is to read it all is to iterate the read 4 times to get my stream of data?


PaulS

Quote
So what your saying is the only way is to read it all is to iterate the read 4 times to get my stream of data?

I'm saying that that might not even work. Data can be lost, so you only might get less than bytes. Hardware can generate unexpected results, so you might get more than 4 bytes.

You need to either wait a little while for a response, and then read everything available (not the preferred solution) or read all the data until the end-of-packet marker arrives. Of course, this means that the device needs to send some end of packet marker and that you know what the end of packet marker is (and that it doesn't get lost).

If you don't know what the end of packet marker is, or there isn't one, then, depending on what else the Arduino is doing, perhaps a function that read all the data that arrived in the next 100 milliseconds might be appropriate to call. If 100 milliseconds is too long, make it shorter. You'll need to experimentally determine how long to wait for the data. The millis() function would prove useful for determining how long you have been waiting for data.

pYro_65


@pyro_65
With a 64 character serial buffer (1.0+), waiting for the buffer to hold more than 128 characters will require a very long wait. That is not a realistic way of reading large amounts of data.


And if someone said they "feel like shit" would you poo on a plate? :)

Enlarge the serial buffer.

...If you are performing a calculation on a large amount of data, that cannot be calculated without all data, then you really have no option but waiting. Also if you are stressing that a blocking loop is time consuming, then simply don't block. Copying out into a working data buffer might have no actual benefit over storing the data in an enlarged serial buffer unless you need more than one pass of the data.

The point I was stressing is the code was expected read more than one byte, so therefore a wait for an acceptable amount of data is closer to what is expected.

Quote
I have tried the above but with no luck...


Probably won't as it waits for 128 bytes ( standard serial support 64 ) and only reads one byte into an int.

notorious


I can understand your logic if I had a load of data coming in, but 4 bytes per message sent which will be approximately once every 5 seconds to check status, seems a bit complicated..at the data rate of the dvr which is 38400bps, it roughly means it sends 4.8bytes/ms which means my timer would only have to wait 1ms...unfortunately no terminating character is sent except the last byte is a modulo-256 checksum of 3 bytes before it.

jraskell



I can understand your logic if I had a load of data coming in, but 4 bytes per message sent which will be approximately once every 5 seconds to check status, seems a bit complicated..at the data rate of the dvr which is 38400bps, it roughly means it sends 4.8bytes/ms which means my timer would only have to wait 1ms...unfortunately no terminating character is sent except the last byte is a modulo-256 checksum of 3 bytes before it.


That is completely incongruous to all your previous posts, which talked about a stream of 128 characters coming in.

The quality of the help you receive will almost always be directly proportional to the quality of the information you provide.

That being said, Serial comms should NEVER be coupled to some external activity (in this case, a button press).  Execute your Serial comms completely independently of everything else, store the necessary results somewhere, and then act upon them when necessary (either immediately, or if necessary based on that external activity).

What is this dvr you referred to as well.  Based on your last post, it sounds like the proper means of handling incoming serial data is with a small 4 character ring buffer, constantly checking for a valid checksum with each character read in, until you have a valid checksum, and thus a valid packet of data.  With the absence of any synchronizing  characters, there aren't m/any other options for properly processing the incoming data.

Constantin

My recommendation is to use Bill Porters EasyTransfer to help with this sort of problem. It performs CRC checks, so if the data is good, you can send it back to the Mega and verify that the right data was received. If the data wasn't good then you can signal that too.

Go Up