Serial Interfacing

Hi community

I got a problem with the Serial Interface...
Quick Description of the Project:
One Way Communication from an another Device to the Arduino
The other Device has a Bit Serial interface with 9600Baud. routed over a Optocoupler to the Arduinos Serial Port guarantees the Communication.
The Telegram is always built the same. Consisting of 8 Bytes, beginning with a Header and ending with a Checksum.
Now the Problem:
It seems that the Communication Sketch does not work properly. I tried several attempts with miscellaneous methods. But nothing seems to work.

Variant 1:

RxByte = 8;
Serial2.readBytes(inBuffer, RxByte);

Here i had problem that the inBuffer was not filled in the right sequence (Header should be at inBuffer[0], it was everywhere else...)

Variant 2:

while(Serial2.available() > 0)
inBuffer1[0] = Serial2.read();

if (inBuffer1[0]==0x5C)
{
  RxByte = 7;
  Serial2.readBytes(inBuffer, RxByte);
}

I tried to force the Telegram into the right Sequence, but now i have the Problem that the Arduino fills the inBuffer with random messages...

Do anybody have another attempt to try out? I'm thankful for every hint...

Greetz Vortex

Description

readBytes() read characters from a stream into a buffer. The function terminates if the determined length has been read, or it times out (see setTimeout()).

readBytes() returns the number of bytes placed in the buffer. A 0 means no valid data was found.

So, it is silly to throw away the number of bytes read. Why are you?

The Telegram i receive from the other Device is always 8Byte long... i don't expect any other Data incoming

The Telegram i receive from the other Device is always 8Byte long... i don't expect any other Data incoming

You should expect, though, when you read some bytes that you were only able to read 3 of them, or 5 of them, or 1 of them...

We need to see ALL of your code. What I would guess, though, is that it is wasting a lot of time, and the serial buffer gets full, resulting in data loss.

Ok here is what i have so far…
but there are 380 rows of code

void setup()
{ 
  lcd.begin(20, 4);
  lcd.clear();
  lcd.setCursor(4, 1);
  lcd.print("INITIALIZING");

  Serial.begin(115200); 
  Serial1.begin(9600, SERIAL_8E2); 
  Serial2.begin(9600, SERIAL_8E2);
  
  OpMode = 0;
  ElNeg = false;
  AzNeg = false;
  A_LED_DOZ = 53;
  A_LED_TLG = 52;
  pinMode(A_LED_DOZ, OUTPUT);
  pinMode(A_LED_TLG, OUTPUT);
  x_TLG_analyse = true;
  x_out_act = true;

/*  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("AZIMUTH:");
  lcd.setCursor(0, 1);
  lcd.print("ELEVATION:");
  lcd.setCursor(17, 0);
  lcd.print("MIL");
  lcd.setCursor(17, 1);
  lcd.print("MIL");*/
}

void loop()
{
  //EINGABE
  if (digitalRead(7))
  {
  //Serial.println("SIMULATIONSBETRIEB");
  OpMode = 1;
  }
  else
  {
  //Serial.println("DATEN-SICHTBETRIEB");
  OpMode = 2;
  }
  
  //VERARBEITUNG
  switch (OpMode)
  {   
    case 1:
    //OpMode 1=Simulationsbetrieb (SU_Tx)
    
    break;
    
    case 2:
    //OpMode 2=Datensichtbetrieb (DOZ_Rx)
    
    //Eingabe von DOZ
    //if (x_TLG_analyse == true && x_out_act == true)
    //{    
    //RxByte = 8;
    //Serial2.readBytes(inBuffer, RxByte);
    //}
    while(Serial2.available() > 0)
    inBuffer1[0] = Serial2.read();
    /*
    inBuffer[0] = Received Byte 1 = HEADER (5C, HEX)
    inBuffer[1] = Received Byte 2 = Azimuth, MsPart
    inBuffer[2] = Received Byte 3 = Azimuth, LsPart
    inBuffer[3] = Received Byte 4 = Elevation, MsPart
    inBuffer[4] = Received Byte 5 = Elevation, LsPart
    inBuffer[5] = Received Byte 6 = N/A (FF, HEX)
    inBuffer[6] = Received Byte 7 = Logikinfo (LSB = on Target)
    inBuffer[7] = Received Byte 8 = EXOR Byte 1-7
    */ 
    
    if (inBuffer1[0]==0x5C)
    {
      RxByte = 7;
      Serial2.readBytes(inBuffer, RxByte);
      //Auswertung starten
      x_TLG_analyse = false;
      x_CRP_TLG = false;
      
      // Azimuth Wert zusammenführen
      AzMSB = inBuffer[1];
      AzLSB = inBuffer[2];
      for (int i=0; i<=7; i++)
      {
        bitTemp = bitRead(AzMSB,i);
        bitWrite(Az,i+8, bitTemp);
      }
      for (int i=0; i<=7; i++)
      {
        bitTemp = bitRead(AzLSB,i);
        bitWrite(Az,i, bitTemp);
      }
      
      //Elevation Wert zusammenführen
      ElMSB = inBuffer[3];
      ElLSB = inBuffer[4];
      for (int i=0; i<=7; i++)
      {
        bitTemp = bitRead(ElMSB,i);
        bitWrite(El,i+8, bitTemp);
      }
      for (int i=0; i<=7; i++)
      {
        bitTemp = bitRead(ElLSB,i);
        bitWrite(El,i, bitTemp);
      }
      El = UINTtoINT(El);
      
      //Logikinfo auswerten
      x_DOZonTg = bitRead(inBuffer[5], 0);
      
      //Auswertung beendet
      x_TLG_analyse = true;
    }
    
    else
    {
      x_TLG_analyse = false;
      x_CRP_TLG = true;
      x_TLG_analyse = true;
    }
    
 Serial.print(inBuffer1[0], HEX);
 Serial.print("\t");
 Serial.print(inBuffer[1], HEX);
 Serial.print("\t");
 Serial.print(inBuffer[2], HEX);
 Serial.print("\t");
 Serial.print(inBuffer[3], HEX);
 Serial.print("\t");
 Serial.print(inBuffer[4], HEX);
 Serial.print("\t");
 Serial.print(inBuffer[5], HEX);
 Serial.print("\t");
 Serial.print(inBuffer[6], HEX);
 Serial.print("\t");
 Serial.print(inBuffer[7], HEX);
 Serial.print("\t");
 Serial.print(inBuffer[8], HEX);
 Serial.print("\t");
 Serial.print(inBuffer[9], HEX);
 Serial.print("\t");
 Serial.println(inBuffer[10], HEX);
    
    break;
        
  }
  
  //AUSGABE
  
  //Ausgabe aktiv
  x_out_act = false;
  
  /*if (x_CRP_TLG)
  {
    lcd.clear();
    lcd.setCursor(0, 1);
    lcd.print("TELEGRAM NOT CORRECT");
  }
  else
  {*/
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("AZIMUTH:");
    lcd.setCursor(0, 1);
    lcd.print("ELEVATION:");
    lcd.setCursor(17, 0);
    lcd.print("MIL");
    lcd.setCursor(17, 1);
    lcd.print("MIL");
    
    digitalWrite(A_LED_TLG, x_CRP_TLG);
    digitalWrite(A_LED_DOZ, x_DOZonTg);
    
    //OpMode
    switch (OpMode)
    {   
      case 1:
        lcd.setCursor(1, 2);
        lcd.print("SIMULATIONSBETRIEB");
      break;
    
      case 2:
        lcd.setCursor(1, 2);
        lcd.print("DATEN-SICHTBETRIEB");
      break;
      
      default:
        lcd.setCursor(2, 1);
        lcd.print("CHECK SWITCH S1");
      break;
    }
    
    //DOZ auf Ziel
    if (x_DOZonTg)
    {
      lcd.setCursor(4, 3);
      lcd.print("DOZ AUF ZIEL");
      //digitalWrite(A_LED_DOZ, true);
    }
    else
    {
      lcd.setCursor(0, 3);
      lcd.print("                    ");
      //digitalWrite(A_LED_DOZ, false);
    }
    
    //AZ-EL Werte
    
    //Serial.print("AZIMUTH =\t");
    //Serial.println(Az);
    //Serial.print("ELEVATION =\t");
    //Serial.println(El);
    
    if (El <= 0)
    {
      lcd.setCursor(11, 1);
      lcd.print("-");
      El = El * -1;
      ElNeg = true;
    }
    else
    {
      lcd.setCursor(11, 1);
      lcd.print(" ");
      ElNeg = false;
    }
    
    SEl = String(El);
    lenEl = SEl.length();
    lcd.setCursor(12, 1);
    lcd.print("     ");
    
    switch (lenEl)
    {
      case 4:                               //Stringlänge 4
        cursEl=12;
      break;    
      case 3:                               //Stringlänge 3
        cursEl=13;
      break;
      case 2:                               //Stringlänge 2
        cursEl=14;
      break;
      case 1:                               //Stringlänge 1
        cursEl=15;
      break;
      default:
        lcd.setCursor(12 ,1);
        lcd.print("     ");
        cursEl=12; 
      break;
    }
    
    // set Cursor Pos in relation to Az-Value (Komma immer an der gleichen Stelle)
  
    SAz = String(Az);
    lenAz = SAz.length();
    lcd.setCursor(12, 0);
    lcd.print("     ");
     
    switch (lenAz)
    {
      case 4:                               //Stringlänge 4
        cursAz=12;
      break;    
      case 3:                               //Stringlänge 3
        cursAz=13;
      break;
      case 2:                               //Stringlänge 2
        cursAz=14;
      break;
      case 1:                               //Stringlänge 1
        cursAz=15;
      break;
      default:
        lcd.setCursor(12 ,0);
        lcd.print("     ");
        cursAz=12; 
      break;
    }
    
    //print Az, El Values to LCD
    lcd.setCursor(cursAz ,0);
    lcd.print(Az);
    lcd.setCursor(cursEl, 1);
    lcd.print(El);
    
    //Ausgabe aktiv
    x_out_act = true;
  //}  
}

/*************************************************************************************
**              Umrechnung UNSIGNED INT to SIGNED INT (2nd Complement)
*************************************************************************************/
int UINTtoINT(unsigned int input)
{
  //Signed INT to Unsigned INT umrechunng
  //2er-Komplement = MSB sign bit, Rest invertiert, +1
  int result;
  byte temp_array[15];
  for (int i=0; i<15; i++)
  {
    temp_array[i] = bitRead(input, i);
  }
  for (int i=0; i<15; i++)
  {
    switch (temp_array[i])
    {
      case 0:
        temp_array[i] = 1;
        break;
      case 1:
        temp_array[i] = 0;
        break;
    }
  }
  for (int i=0; i<15; i++)
  {
    bitWrite(result, i, temp_array[i]);
  }
  bitWrite(result, 15, 1);                      //Mark as negative
  result=result+1;
  return result;
}

You are assuming these functions wait indefinitely, whereas they timeout.
Call available() and read that many is the best course of action, then you always know how many
characters you have got and you never get stuck waiting for input and loop() can service other actions.

while(Serial2.available() > 0)

inBuffer1[0] = Serial2.read();

What do you think that this does? It reads all available characters and dumps them in inBuffer1[0]. So if there were three bytes, inBuffer1[0] will contain the last one.

Have a look at serial input basics how to do it.

thanks to sterretje for the helpful link! i will setup my communication completely new...
just have to figure out how to handle it with a start marker and no end marker...

Vortex86:
just have to figure out how to handle it with a start marker and no end marker...

If there are always N bytes after the start marker you just need to count them as they arrive.

If the number of bytes can vary and if the number is included as part of the message it will be a little more complex because you will have to extract the number while the message is being received.

...R

a big thanks to all of the helpful tips in this forum…

i figured it out and it seems to be working:

void recvWithStartMarker8ByteDOZ() {
	static boolean recvInProgress = false;
	static byte ndx = 0;
	char startMarker = 0x5C;
	byte rc;
	
	// if (Serial.available() > 0) {
	while (Serial2.available() > 0 && newData == false) { // <<== NEW - get all bytes from buffer
		rc = Serial2.read();

		if (recvInProgress == true) {
			if (ndx <= 6) {
                            if (rc != startMarker)  {
                            	receivedChars[ndx] = rc;
				ndx++;
                            }
                            else {
                            ndx = 0;
                            }
			}
			else {
				receivedChars[ndx] = '\0'; // terminate the string
				recvInProgress = false;
				ndx = 0;
				newData = true;
			}
		}

		else if (rc == startMarker) {
			recvInProgress = true;
		}
	}
        while (Serial2.available() == 0)
        {
          timeout();
        }
          
}

but now struggling with the next problem. When there is no data received for more than 2 Seconds i want to print “NO DATA” on the LCD Display. → see function timeout

void timeout() 
{
  int i_test;
  
  i_time_s = millis()/1000;
  i_test = i_time_s % 2;
  if (i_test == 0)
  
  {

    lcd.setCursor(0, 1);
    lcd.print("      NO DATA       ");
  }
}

You should keep ‘things’ separate.

The reading of the data and the displaying of the timeout are totally different things. You don’t show where you call recvWithStartMarker8ByteDOZ().

How about keeping a static variable containing the time that the last byte was received?

/*
  receive data
  returns false if a timeout occures during the receive, else true
*/
bool recvWithStartMarker8ByteDOZ() {
  static unsigned long timeLastbyteReceived = millis();

  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = 0x5C;
  byte rc;

  // if (Serial.available() > 0) {
  while (Serial2.available() > 0 && newData == false) { // <<== NEW - get all bytes from buffer
    rc = Serial2.read();
    timeLastbyteReceived = millis()

    if (recvInProgress == true) {
      if (ndx <= 6) {
        if (rc != startMarker)  {
          receivedChars[ndx] = rc;
          ndx++;
        }
        else {
          ndx = 0;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

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

  // condition for timeout; timeout only occurs if a receive is in progress
  if (recvInProgress == true && millis() - timeLastbyteReceived > 20000)
  {
    // do required cleanup
    ...
    ...
    // indicate timeout
    return false;
  }
  // indicate no timeout
  return true;
}

Note that the return type of the function is no longer a ‘void’ but a ‘bool’ that you can test. The condition at the end of the function needs to be fine-tuned to your needs. I considered it a timeout if a receive is in progress, else not.

Assuming you call the function from loop

void loop()
{
  if (recvWithStartMarker8ByteDOZ() == false)
  {
    // display no data on LCD
  }
}

The only way to know that 2 seconds has elapsed with nothing happening is to record when something last happened, and then compare now to then.

Since you never record when anything happens, or, if you do, you don't use that value in the timeout() function, you will only know if it has been two seconds since the Arduino reset.

Not terribly useful information. Look at the blink without delay example.

Vortex86:
but now struggling with the next problem. When there is no data received for more than 2 Seconds i want to print “NO DATA” on the LCD Display. → see function timeout

At the location in the recv function where it sets newData = true; add a line lastNewDataMillis = millis(); then elsewhere in the program you can test

if (millis() - lastNewDataMillis >= interval) {
   // print NO DATA messag
}

…R