Serial read problem

Hi Guys,

i have a problem reading data from serial.
Small Datasets aren´t the problem, but when it comes to bigger sets like 1024 bytes it gets a little tricky.

The Problem is the there is always a shift of 256 bytes in my array and i´m not really sure why.

Below is my code, i hope somebody can help me.

I have a startmarker, so i now every answer must start with 65 0 1 83, but there is no endmarker, i just know that the telegram must have 1208 bytes. So normally it shoudnt be that hard,

void setup() {
  Serial2.begin(38400, SERIAL_8N1, RXD2, TXD2); // Start Regulator Serial
}

void loop() { 
  readRegulator();
}

char TXBuffer[1000], RXBuffer[2000];
int PARAMS[1000];
unsigned int txp, rxp, ChecksumModBus;
int switchTelegram;
void readRegulator() {
  unsigned int Crc;
  txp = 0, rxp = 0;
  delay(10000);
  if(switchTelegram == 2) {
    TXBuffer[txp++] = 80; // Adresse 0
    
    TXBuffer[txp++] = 0; // Function 3
    TXBuffer[txp++] = 1; // Function 3
    
    TXBuffer[txp++] = 83; // StartAddr HB
    TXBuffer[txp++] = 01; // StartAddr LB
  
    TXBuffer[txp++] = 01; // Anzahl HB
    TXBuffer[txp++] = 00; // Anzahl LB
    TXBuffer[txp++] = 00; // Anzahl LB
  }

  
   ChecksumModBus = 0;
   for (int i = 0; i < txp; i++) {
     ChecksumModBus ^= TXBuffer[i];
   }
   Crc = ~ChecksumModBus;
   TXBuffer[txp++] = ChecksumModBus;
   TXBuffer[txp++] = Crc;
  
  
  Serial.println("Send");
  for (int i = 0; i < txp; i++) Serial2.write(TXBuffer[i]);

  delay(100); // Added this line
  //Serial.println("Data Received:");
  while(Serial2.available()) {
    char inChar;
    /*
    inChar = (char)Serial2.read();
    RXBuffer[rxp++] = inChar;
    delayMicroseconds(750); // inter character time out */

    Serial2.readBytes(RXBuffer, 1208);
    
    if(switchTelegram == 2) {
      if(RXBuffer[0] == 65 && RXBuffer[1] == 0 && RXBuffer[2] == 1 && RXBuffer[3] == 83 && (((int)RXBuffer[1206] + (int)RXBuffer[1207]) == 255)){ 
        for (int rxCnt = 0; rxCnt < 600; rxCnt++) {
          unsigned char Hbyte = RXBuffer[6 + (rxCnt * 2)];
          unsigned char LByte = RXBuffer[7 + (rxCnt * 2)];
            
          int value = (int)(Hbyte + (LByte * 256));
          PARAMS[rxCnt] = value;
        }
        for(int i = 1204; i < 1208; i++) {
          Serial.println((int)RXBuffer[i]);
        }
      }
      //memset(RXBuffer, 0, sizeof(RXBuffer)); // Removed this line
    }
  }
  
  switchTelegram = 2;
}

This question is better suite here....

But couple of things I notice:

  • I think Serial.readBytes() can handle large blocks like that but do know it's a blocking way of doing so. Did you look at [Robin2's excellent tutorial]Serial Input Basics - updated - Introductory Tutorials - Arduino Forum]Robin2's excellent tutorial](Serial Input Basics - updated - Introductory Tutorials - Arduino Forum)?

  • Note Serial.readBytes() also stops reading after a time out! So you should use at least the return value to check if it indeed read 1208 bytes.

  • Next up, you expect a start marker after which you want to read 1204 more bytes. Why do you do excellently the opposite? You start by reading 1208 bytes, having NO CLUE where you actually start reading the data. After which you expect the start marker to be at the start of the stream...

  • Do you really need to set every every entry to 0 again?

int value = (int)(Hbyte + (LByte * 256));

You do know you make Hbyte the Low byte and the Lbyte the High byte here? Also shifting is considered to be more readable then multiplying :slight_smile:

Tip:

You need to post a complete program that demonstrates the problem.

What timeout have you set for readBytes() ?

What is sending the data? IMHO it would be much better to receive the data in smaller chunks. Have a look at the examples in Serial Input Basics - simple reliable ways to receive data.

...R

Hi, thanks for the fast reply, i will change me code above.

septillion:

  • Note Serial.readBytes() also stops reading after a time out! So you should use at least the return value to check if it indeed read 1208 bytes.

I´ve done that and i´m always getting 256 as return value.

septillion:

  • Next up, you expect a start marker after which you want to read 1204 more bytes. Why do you do excellently the opposite? You start by reading 1208 bytes, having NO CLUE where you actually start reading the data. After which you expect the start marker to be at the start of the stream...

I know, but i´ve no clue how to change this. Is is possible to Read only 4 Bytes and then the last 1204 bytes?

septillion:

  • Do you really need to set every every entry to 0 again?

No, but its easier to detect if there is an error in my array.

septillion:

int value = (int)(Hbyte + (LByte * 256));

You do know you make Hbyte the Low byte and the Lbyte the High byte here? Also shifting is considered to be more readable then multiplying :slight_smile:

I´m also getting data via modbus thats the reason why it is in the wrong order.

Robin2:
What timeout have you set for readBytes() ?

I dont have set any timeout

Robin2:
What is sending the data? IMHO it would be much better to receive the data in smaller chunks. Have a look at the examples in Serial Input Basics - simple reliable ways to receive data.

I also can get the data via modbus telegram but i need 1200 values, with modbus i have to make 6 different telegrams, so i would prefer getting everything a once.

UPDATE:

Now i´m receiving the data, what i have done is, i added a delay of 100 milliseconds before the while(serial2.available) and removed the memset. Even the checksum is correct, but i´m not understanding why it is working. I have read that the serial is very slow, so i thought maybe put a small delay so that the data can transfer.

MarcelW:
I´ve done that and i´m always getting 256 as return value.

First though was that readBytes() may only do up to 256 bytes but the fact it works now points to something else. But as I said, I really think using Serial.readBytes() is a terrible approach because of it's blocking nature.

MarcelW:
I know, but i´ve no clue how to change this. Is is possible to Read only 4 Bytes and then the last 1204 bytes?

Then you clearly didn't read the article Robin2 and I linked to :wink:

MarcelW:
No, but its easier to detect if there is an error in my array.

Fair enough. But I would remove it after debugging.

MarcelW:
I´m also getting data via modbus thats the reason why it is in the wrong order.

So? Is that a reason to use confusing variable names?

MarcelW:
I dont have set any timeout

Then it defaults to 1000ms.

MarcelW:
but i´m not understanding why it is working. I have read that the serial is very slow, so i thought maybe put a small delay so that the data can transfer.

Yeah, you probably timed out readBytes(). And depending on the speed (but again, can't say from a crappy snippet :wink: ) Serial can be very very very slow compared to the Arduino. And that's why it's best to NOT lock it into receiving data. Just read the data byte by byte when it becomes available. Heck, that way you don't even need a hellish 1208 bytes large array. Because you can just set PARAM[] every two bytes read (after receiving a start signal).

So yeah, we know you guys don't like to read long articles or being told the approach you have now isn't good. But if you want to have good/reliable/clear code you better do :wink:

septillion:
Yeah, you probably timed out readBytes(). And depending on the speed (but again, can't say from a crappy snippet :wink: )

I´ve updated my snippet, do you miss something? baud is 38k.

septillion:
So yeah, we know you guys don't like to read long articles or being told the approach you have now isn't good. But if you want to have good/reliable/clear code you better do :wink:

I have no problem with reading long articles and i know my approach is not the best, i even said that i want to understand why it is working, but its also not easy to work on a problem for hours and beeing a little bit helpless :wink:

septillion:
Just read the data byte by byte when it becomes available. Heck, that way you don't even need a hellish 1208 bytes large array. Because you can just set PARAM[] every two bytes read (after receiving a start signal).

I´m understanding what u want me to say but i´m not really sure how to do this.
Do you have a small example for me? Would me help a lot. And yes i have overflown the article of robin but its so much input at once.

Ahh, updating posts is very very confusing. Just make a new post if you want to add info.

And the example is almost 99% in the article of Robin2 :wink: Only he uses a single digit start marker and a stop marker. Not to hard to think of how to extend it to have multiple start markers and just count instead of a stop marker.

But, how do you know the stream never has the values (aka, PARAM's) 65 followed by 21249 in it? Aka, the start marker as data? You don't seem to write to code for the sending bit but just curious :slight_smile:

Also, which board are you using?

septillion:
Ahh, updating posts is very very confusing. Just make a new post if you want to add info.

And the example is almost 99% in the article of Robin2 :wink: Only he uses a single digit start marker and a stop marker. Not to hard to think of how to extend it to have multiple start markers and just count instead of a stop marker.

But, how do you know the stream never has the values (aka, PARAM's) 65 followed by 21249 in it? Aka, the start marker as data? You don't seem to write to code for the sending bit but just curious :slight_smile:

Also, which board are you using?

Hi, sorry for the long absence, i was on holiday.

I´m still trying to fix this and i think i found the problem.

When i´m displaying the Serial.available() it starts by 0 and then goes to 256, and at the end i´m missing some chars. So i think i´m getting an overflow and thats the reason why Serial.read() is not working but Serial.readByte is.

Changing RX_BUFFER is not really working, do you have an idea whats wrong?

EDIT :

I have changed the baud rate from 38k to 9600 and no everything is working like it should, the Serial.available never overflows. So the only problem now is to change the buffer size, but change it in HardwareSerial.h is not working.

MarcelW:
Hi, sorry for the long absence, i was on holiday.

I´m still trying to fix this and i think i found the problem.

Please post the latest version of your program in your next Reply. Also please describe in detail what it does and what you want it to do that is different.

...R

void setup() {
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // Start Regulator Serial
}
void loop(){
  readSerial();
}
void readSerial() {
  rxp = 0;
  
    while(Serial2.available()) {
      Serial.print(Serial2.available());
      RXBuffer[rxp++] = Serial2.read();
      Serial.println(rxp);
    }
}

I´m just waiting for Serial2 to be available, if this is the case, i´m reading byte by byte.
As mentioned before the "Serial.print(Serial2.available())" reaches 255, so there must be some kind of overflow. This appears with a baud of 38400, with 9600 baud the byte buffer of Serial2 never goes that high.
So now i´m looking for a way to extend the buffer.

What is RXBuffer[]? It is not defined in your program - maybe you have not posted the complete program?

...R

The Serial buffer is big enough as it is. You just have to read it more often if it overflows. Making it larger will just delay the problem.

Robin2:
What is RXBuffer[]? It is not defined in your program - maybe you have not posted the complete program?

...R

char TXBuffer[1000], RXBuffer[2000];

its the complete program, i have just ignored the includes and defines.

septillion:
The Serial buffer is big enough as it is. You just have to read it more often if it overflows. Making it larger will just delay the problem.

How can i read it faster? Making it larger would not delay the problem, if i could set it to 512 byte it would never overflows because i´m not sending that much at once.

MarcelW:
char TXBuffer[1000], RXBuffer[2000];

its the complete program, i have just ignored the includes and defines.

Please just post the complete complete program. It makes life so very much easier when we can see the whole thing.

And tell us what Arduino you are using.

...R

I´m using an esp32

#define RXD2 16
#define TXD2 17

#include <Arduino_JSON.h>
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiAP.h>
#include <HTTPClient.h>
#include "lang.h"
#include "ESPAsyncWebServer.h"
#include "esp_task_wdt.h"

#define LED_BUILTIN 2   // Set the GPIO pin where you connected your test LED or comment this line out if your dev board has a built-in LED
#define _SS_MAX_RX_BUFF 512


const char *ssid = "";
const char *password = "";

AsyncWebServer server(80);

char TXBuffer[1000], RXBuffer[2000];
int PARAMS[1000];
int CircuitNames[8][20];
unsigned int txp, rxp, ChecksumModBus;
int switchTelegram;
unsigned int Crc;

int deviceID = 0;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(115200);
  WiFi.softAP(ssid, password, 5, 3);
  IPAddress myIP = WiFi.softAPIP();
  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); // Start Regulator Serial
  
 
  server.on("/getDP/", HTTP_GET, [](AsyncWebServerRequest *request){
    readRegulator();
    request->send(200, "text/plain", "message received");
  });
 
  server.begin();
}
bool waitForResponse = false;
void readRegulator() {
  txp = 0, rxp = 0;

  TXBuffer[txp++] = 80; // Adresse 0
      
  TXBuffer[txp++] = 1>>8; // Function 3
  TXBuffer[txp++] = 1; // Function 3
      
  TXBuffer[txp++] = 83; // StartAddr HB
  TXBuffer[txp++] = 01; // StartAddr LB
    
  TXBuffer[txp++] = 01; // Anzahl HB
  TXBuffer[txp++] = 00; // Anzahl LB
  TXBuffer[txp++] = 00; // Anzahl LB
  switchTelegram = 0;
    
  ChecksumModBus = 0;
  for (int i = 0; i < txp; i++) {
    ChecksumModBus ^= TXBuffer[i];
  }
  Crc = ~ChecksumModBus;
  TXBuffer[txp++] = ChecksumModBus;
  TXBuffer[txp++] = Crc;
  
  for (int i = 0; i < txp; i++) Serial2.write(TXBuffer[i]);
  for (int i = 0; i < txp; i++) Serial.write(TXBuffer[i]);
  memset(TXBuffer, 0, sizeof(TXBuffer));
}

void loop(){
  readSerial();
}
void readSerial() {
  rxp = 0;
  
    while(Serial2.available()) {
      char inChar;
      inChar = (char)Serial2.read();
      RXBuffer[rxp++] = inChar;
      Serial.println(inChar);


    if(rxp > 3) {
      if(rxp >= 1207 && RXBuffer[0] == 65 && (RXBuffer[3] == 83 || RXBuffer[3] == 86) && (((int)RXBuffer[1206] + (int)RXBuffer[1207]) == 255)){
        for (int rxCnt = 0; rxCnt < 600; rxCnt++) {
          unsigned char Hbyte = RXBuffer[6 + (rxCnt * 2)];
          unsigned char LByte = RXBuffer[7 + (rxCnt * 2)];
          int value = (int)(Hbyte + (LByte * 256));
          PARAMS[rxCnt] = value;
        }
        waitForResponse = true;
        deviceID = RXBuffer[1]*256 + RXBuffer[2];
        Serial.print("Parameter erhalten");
      } 
    }
  } 
}

I would not have a complex IF statement like this in any of my programs. I would have no confidence in what it does. I would at least make it a series of cascaded IF statements as that would allow me to Serial.print() statements in different parts to check what is happening.

if(rxp >= 1207 && RXBuffer[0] == 65 && (RXBuffer[3] == 83 || RXBuffer[3] == 86) && (((int)RXBuffer[1206] + (int)RXBuffer[1207]) == 255)){

Are you trying to analyse the data as it arrives or have you enough space in your buffer to save the whole incoming message. If the latter then I would receive all the data before trying to analyse any of it.

...R

Robin2:
I would not have a complex IF statement like this in any of my programs. I would have no confidence in what it does. I would at least make it a series of cascaded IF statements as that would allow me to Serial.print() statements in different parts to check what is happening.

if(rxp >= 1207 && RXBuffer[0] == 65 && (RXBuffer[3] == 83 || RXBuffer[3] == 86) && (((int)RXBuffer[1206] + (int)RXBuffer[1207]) == 255)){

Are you trying to analyse the data as it arrives or have you enough space in your buffer to save the whole incoming message. If the latter then I would receive all the data before trying to analyse any of it.

...R

I have enough space to save the complete message, the message that i´m receiving is exactly 1207 bytes long, byte 1206 and 1207 is the checksum of the message.

The problem is that the serial.read() is to slow when i have a baudrate of 56k, so it is only reading around 800 bytes because there is an overflow in der serial.available buffer

MarcelW:
The problem is that the serial.read() is to slow when i have a baudrate of 56k, so it is only reading around 800 bytes because there is an overflow in der serial.available buffer

Can you write a short program that does nothing else than check the serial input buffer and save the data to your array?

Oh, wait, the second example in the link I gave you in Reply #2 should do that. Have you tried it?

...R

Robin2:
Can you write a short program that does nothing else than check the serial input buffer and save the data to your array?

Oh, wait, the second example in the link I gave you in Reply #2 should do that. Have you tried it?

...R

That's exactly that what I´m already doing.

MarcelW:

void loop(){

readSerial();
}
void readSerial() {
  rxp = 0;
 
    while(Serial2.available()) {
      char inChar;
      inChar = (char)Serial2.read();
      RXBuffer[rxp++] = inChar;
      Serial.println(inChar);
  }
}

MarcelW:
That's exactly that what I´m already doing.

The code in Reply #18 is not the same as the example in my Tutorial. Why not try the example with no changes except those needed to deal with the larger char array?

...R