# Serielle Kommunikation



## freez (3. Feb 2012)

Hallo,

ich möchte 10 bytes immer wieder über eine serielle Leitung übertragen. Das ist ansich kein Problem, dies habe ich bereits realisiert (10 x 0xff  testweise). Meine Frage bezieht sich eher auf die Protokoll Ebene. Ich möchte natürlich sicherstellen, dass die 10 bytes bei der Übertragung nicht verfälscht werden und dass ich auch immer das richtige erste Byte erwische, wenn ich meine Daten daraus lese. Das heist Start und Ende muss ich erkennen können.

Bestimmt gibt es jede Menge Content darüber im Netz, aber ich weiss nicht, wie ich danach suchen soll. Hat jemand links, wie man eine sichere serielle Kommunikation hin bekommt?

In meinem Fall ist es übrigens eine RS232 Schnittstelle, welche an der eine Seite ein Arduino Board hängt und an der anderen Seite ein PC mit JAVA Programm. Beide Seiten kann ich frei programmieren. Wie gesagt ... 10 bytes sind es die ich sicher übertragen will ... nur wie definiere ich das Protokoll dafür sauber?


----------



## HoaX (3. Feb 2012)

HDLC verwenden? Keine Ahnung obs dafür schon eine Implementierung in Java gibt.


----------



## freez (3. Feb 2012)

HoaX hat gesagt.:


> HDLC verwenden? Keine Ahnung obs dafür schon eine Implementierung in Java gibt.



Ja, vielleicht in etwas abgespeckter art ist wohl ein interessanter Ansatz (ohne Adresse z.B.). Ich habe auch schon an Begrenzer gedacht. Aber mal ehrlich, wie stelle ich sicher, dass 01111110 nicht auch irgendwo in den 10Byte vorkommen. Oder stelle ich mir die Implementierung zu kompliziert vor? Gibt es Codebeispiele für die saubere Erkennung von Begrenzern?


----------



## freez (3. Feb 2012)

Was haltet ihr davon:
- 0xFF Als Anfangsbegrenzer
- 10 bytes
- 1 byte Checksumme
- 0xFF als Endbegrenzer

Beim Decodieren muss ich ein 0xFF finden, welches an 12. Stelle auch ein 0xFF hat und das 11. byte als checksumme mit den 10byte übereinstimmt. Ist das ein gangbarer weg? Gäbe es zu viele Kollisionen? Wie erkenne ich Kollisionen? Gibt es bereits Beispiele für so ein vorgehen?


----------



## freez (3. Feb 2012)

Mir helfen auch lmgtfy tipps, wenn sie die richtigen Begriffe enthalten


----------



## HoaX (3. Feb 2012)

freez hat gesagt.:


> Aber mal ehrlich, wie stelle ich sicher, dass 01111110 nicht auch irgendwo in den 10Byte vorkommen. Oder stelle ich mir die Implementierung zu kompliziert vor? Gibt es Codebeispiele für die saubere Erkennung von Begrenzern?


Indem du beim Senden die Bytes zuvor auf dieses Vorkommen testst und ggf. ein Bit einfügst, nennt sich Bit-Stuffing. Allerdings verschiebt das dann natürlich deine nachfolgenden Daten um ein Bit... also evtl recht blöd zu handhaben, sofern man keinen Bit-Zugriff auf die serielle Schnittstelle hat.

Wenn du wirklich nur zehn Bytes hast dann ist ein einfacheres Protokoll natürlich besser. Evtl kannst du irgendwelche Zeichen ausschließen, dass diese in deinen 10 Bytes vorkommen? Dann brauchst du keine Endemarkierung. Dein Ansatz ist an und für sich ok.


----------



## California (4. Feb 2012)

Schick ein Prüfbyte am Ende (dann werden aus den 10 halt 11), das im ersten Ansatz die Summe der 10 Vorgänger enthält (Byte- Summe natürlich, d.h. ohne Überlauf)

Jedesmal wenn Du 11 Byte empfangen hast vergleichst Du die Prüfsummen. Das senkt die Wahrscheinlichkeit einer Fehlübertragung schon mal ganz gewaltig. (Natürlich kann ein Übertragungsfehler einen Bitsalat erzeugen, der 11 Bytes mit der Summe der 10 ersten im elften enthält, ist aber 1:256)

Wenn Du mehr tun willst, klammerst Du die Übertragung in Anfang- Ende Marker (die guten alten ASCII-Konstanten STX und ETX eigenen sich z-B- dafür American Standard Code for Information Interchange ? Wikipedia)

Dein Telegramm sieht dann so aus:

STX - 10 Byte Daten - Prüfbyte -ETX 

jetzt fällt die Wahrscheinlichkeit dafür, dass eine Folge von 13 Byte mit STX am Anfang und ETX am Ende und passender Prüfsumme erzeugt wird, schon gewaltig in den Keller.

Allerdings sind aus den 10 Byte schon 13 geworden, aber Protokolle kosten halt auch was.


----------



## freez (4. Feb 2012)

HoaX hat gesagt.:


> Indem du beim Senden die Bytes zuvor auf dieses Vorkommen testst und ggf. ein Bit einfügst, nennt sich Bit-Stuffing.



Habe ich im Wiki zu HDLC gelesen, aber das stelle ich mir in Java etwas schwierig vor


----------



## freez (4. Feb 2012)

California hat gesagt.:


> Allerdings sind aus den 10 Byte schon 13 geworden, aber Protokolle kosten halt auch was.



Das ist mir durchaus bewusst und auch gar nicht schlimm. Ohne diese Informationen bekomme ich ab und zu richtigen Salat raus. Und das macht keinen Sinn.

Ich werde es wohl so machen wie in deinem Beispiel. Wie sieht es mit der Checksumme aus ... was für Arten der Erzeugung gibt es? In deinem Beispiel hast du von Addition mit Überlauf gesprochen. Wäre auch eine XOR Verknüpfung der 10Bytes möglich? Oder die Addition der 10Bytes in 2Bytes Checksumme?


----------



## HoaX (4. Feb 2012)

Och das geht schon, is für den Anwendungsfall allerdings wohl etwas übertrieben. Machs so wie du gesagt hast, ist auch viel einfacher implementierbar.



California hat gesagt.:


> Jedesmal wenn Du 11 Byte empfangen hast vergleichst Du die Prüfsummen. Das senkt die Wahrscheinlichkeit einer Fehlübertragung schon mal ganz gewaltig. (Natürlich kann ein Übertragungsfehler einen Bitsalat erzeugen, der 11 Bytes mit der Summe der 10 ersten im elften enthält, ist aber 1:256)


Nö, du erhöhst die Wahrscheinlichkeit einer Fehlübertragung, da ja jetzt mehr Daten übertragen werden die verfälscht werden können. Du kannst es aber mit ziemlich hoher Wahrscheinlichkeit erkennen, ob was falsch ist.


----------



## freez (4. Feb 2012)

California hat gesagt.:


> Byte- Summe natürlich, d.h. ohne Überlauf



Wie meinst du das?


----------



## HoaX (4. Feb 2012)

freez hat gesagt.:


> Wie meinst du das?


Hab ich auch nicht verstanden. Nimm einfach nen CRC oder Parität, das sollte ausreichen. Da dein Kabel zum Controller nicht soooo lang ist (davon gehe ich aus) dürfte das locker ausreichen.


----------



## freez (6. Feb 2012)

OK. Danke für die Antworten:

ich habe nun die Prüfung so gelöst (Empfänger):

```
byte h1 = dataList.get(i);		//HeaderByte1
byte h2 = dataList.get(i+1);	//HeaderByte2
			
byte cka = dataList.get(i+12);	//CheckSumme1
byte ckb = dataList.get(i+13);	//CheckSumme2
			
byte t1 = dataList.get(i+14);	//TrailerByte1
byte t2 = dataList.get(i+15);	//TrailerByte2
			
byte ck_a = 0;
byte ck_b = 0;
for (int j=HEADERSIZE; j<(DATASIZE+HEADERSIZE); j++) {
	ck_a+=dataList.get(i+j);  //Calculates checksums
	ck_b+=ck_a;       
}
			
//Prüfe Begrenzer und Checksummen
if(h1 == 0x02 && h2 ==0x02 && t1 == 0x03 && t2 == 0x03 && cka == ck_a && ckb == ck_b){
	checkOK = true;
}
```
i+2 bis i+11 sind die eigentlichen Daten, die ich übertragen will. Somit habe ich 6byte Protokolldaten. 'i' ist die Position in der List, an der die Daten anfangen sollten.


----------



## HoaX (6. Feb 2012)

Schaut ziemlich komisch aus wie du das machst. Wenn du mal die ganze Methode zeigst kann man da sicherlich noch was optimieren. Am Besten zusammen mit dem Teil wo die Daten eingelesen werden.


----------



## freez (7. Feb 2012)

Daten einlesen ist eher trivial. Byte(s) werden gelesen und der dataList angefügt. Sind ausreichend Daten in der Liste, wird geprüft (hier der richtige Code ... der vorherige Post war etwas vereinfacht zum besseren Verständnis):


```
private int check() {
		int pos = -1;
		
		for(int i = 0; i < (dataList.size() -SUMBYTES); i++){
			byte h1 = dataList.get(i);		//HeaderByte1
			byte h2 = dataList.get(i+1);	//HeaderByte2
			
			byte cka = dataList.get(i+12);	//CheckSumme1
			byte ckb = dataList.get(i+13);	//CheckSumme2
			
			byte t1 = dataList.get(i+14);	//TrailerByte1
			byte t2 = dataList.get(i+15);	//TrailerByte2
			
			byte ck_a = 0;
			byte ck_b = 0;
			for (int j=HEADERSIZE; j<(DATASIZE+HEADERSIZE); j++) {
				ck_a+=dataList.get(i+j);  //Calculates checksums
				ck_b+=ck_a;       
			}
			
//			Prüfe Begrenzer und Checksummen
			if(h1 == 0x02 && h2 ==0x02 && t1 == 0x03 && t2 == 0x03 && cka == ck_a && ckb == ck_b){
				pos = i;				
				break;
			}
			
		}
		
		return pos;
	}
```

Also hier die Daten aus dem InputStream lesen und ist pos >= 0 werden die Daten aus der datalist extrahiert (das ganze läuft in einer while schleife):


```
try {
			byte[] data = new byte[1];
			if(inputStream.available() > 0) {
				
				int num = inputStream.read(data);
				
				if(num > 0){
					byte b = data[0];
					dataList.add(b);
					if(dataList.size()>=SUMBYTES){
						int position = check();
						
						if(position >= 0){
							handleData(position+HEADERSIZE);	//nur die reinen Daten
							muellTempCounter += position;
							for(int i = 0; i < position+SUMBYTES; i++)
								dataList.remove(0);
							
						}
					}
				}
			}
		} catch (IOException e) {}
```

muellTempCounter ist hier nur ein Hinweis, um zu prüfen, wie viel "Müll" Bytes weggeworfen werden (d.h. wenn check() aufgerufen wurde, aber die Prüfung fehl schlug, liegen beim nächsten Check am Anfang der DataList Daten, die ich dann einfach wegwerfe).

HandleData extrahiert dann die Daten:

```
private void handleData(int pos) {

		if(dataList.size() > pos+DATASIZE){
			dataa= 0;
			datab = 0;
			datac = 0;
			datad = 0;
			
			dataa+= byte2UnSignedInt(dataList.get(pos+0).byteValue())*POW0;
			dataa+= byte2SignedInt(dataList.get(pos+1).byteValue())*POW1;
			
			datab += byte2UnSignedInt(dataList.get(pos+2).byteValue())*POW0;
			datab += byte2SignedInt(dataList.get(pos+3).byteValue())*POW1;
			
			datac += byte2UnSignedInt(dataList.get(pos+4).byteValue())*POW0;
			datac += byte2SignedInt(dataList.get(pos+5).byteValue())*POW1;
			
			datad += byte2UnSignedInt(dataList.get(pos+6).byteValue())*POW0;
			datad += byte2UnSignedInt(dataList.get(pos+7).byteValue())*POW1;
			datad += byte2UnSignedInt(dataList.get(pos+8).byteValue())*POW2;
			datad += byte2SignedInt(dataList.get(pos+9).byteValue())*POW3;
		}
	}
```


Zur Verdeutlichung hier noch die definierten Konstanten und Members:

```
private static final int DATASIZE = 10;			//Anzahl Bytes, welche Daten haben
	private static final int HEADERSIZE = 2;			//Anzahl Bytes, welche der Header hat
	private static final int TRAILERSIZE = 2;			//Anzahl Bytes, welche Trailer hat
	private static final int CHECKSUMSIZE = 2;			//Anzahl Bytes, welche die Checksumme enthält
	private static final int PROTOKOLLBYTES = HEADERSIZE+TRAILERSIZE+CHECKSUMSIZE;		//Anzahl Bytes für das Protokoll
	private static final int SUMBYTES = DATASIZE+PROTOKOLLBYTES;
	private static final int POW0 = 1;				// POW(256,0)
	private static final int POW1 = 256;			// POW(256,1)
	private static final int POW2 = 256*256;		// POW(256,2)
	private static final int POW3 = 256*256*256;	// POW(256,3)
	InputStream inputStream;
	List <Byte> dataList = new ArrayList<Byte>();
```


----------



## freez (7. Feb 2012)

Eigentlich finde ich das Ganze schön übersichtlich und es funktioniert auch. Aber wenn jemand das noch besser, übersichtlicher, sicherer und kürzer kann, wäre es schön dafür ein Beispiel zu sehen.

Hier der Aufbau des Protokolls (H=Header; C=Checksumme; T=TRAILER)
-------------------------------------------------------------
| *H* | *H* | Daten (Anzahl beliebig => hier 10bytes)| *C* | *C* | *T* | *T* |
-------------------------------------------------------------


----------



## HoaX (7. Feb 2012)

Evtl nicht kürzer, aber definitiv schöner:

```
package freez;

import java.io.ByteArrayOutputStream;
import java.util.LinkedList;
import java.util.Queue;

/**
 * Automat zum Erkennen von Datentelegrammen.
 */
public class Automat {
	enum State {
		FIND_START, HEADER, DATA, TRAILER;
	}
	
	State s = State.FIND_START;
	ByteArrayOutputStream baos;
	// Queue welche immer nur die letzten zwei Zeichen enthölt. Siehe auch shiftQ().
	Queue<Integer> crcQ = new LinkedList<>();
	private byte[] blockData;
	private boolean blockOk;
	
	/**
	 * Erzeugt einen neuen Automaten
	 */
	public Automat() {
		baos = new ByteArrayOutputStream();
	}
	
	/**
	 * Automat zum Erkennen der Daten
	 * @param b nächstes Zeichen aus dem Datenstream
	 * @return <code>true</code> falls ein Block vollständig erkannt wurde, sonst <code>false</code>
	 */
	public boolean consume(int b) {
		switch(s) {
		case FIND_START:
			s = (b == 0x2) ? State.HEADER : State.FIND_START;
			break;
			
		case HEADER:
			s = (b == 0x2) ? State.DATA : State.FIND_START;
			break;
			
		case DATA:
			if (b == 0x3) {
				s = State.TRAILER;
			} else {
				crcQ.add(b);
				shiftQ();
			}
			break;
			
		case TRAILER:
			if (b == 0x3) {
				blockFinished();
				s = State.FIND_START;
				return true;
			} else {
				crcQ.add(0x3);
				crcQ.add(b);
				shiftQ();
				s = State.DATA;
			}
		}
		return false;
	}
		
	/**
	 * Ein Block wurde vollständig erkannt.
	 * Nun werden die Daten geprüft und die Attribute aktualisert.
	 */
	private void blockFinished() {
		blockData = baos.toByteArray();
		baos.reset();
		short checksum = calcChecksum(blockData);
		blockOk = (checksum == (short) ((0xff & crcQ.poll()) << 8 | (0xff & crcQ.poll())));
	}
	
	/**
	 * Prüfsumme für Daten berechnen.
	 * @param d Daten, deren Prüfsumme berechnet werden soll.
	 * @return Errechnete Prüfsumme.
	 */
	public static short calcChecksum(byte[] d) {
		int a=0;
		int b=0;
		for(byte x : d) {
			a+=x;
			b+=a;
		}
		return (short) ((a & 0xff) << 8 | b & 0xff);
	}
	
	/**
	 * Letzten zwei Zeichen sind die Prüfsumme. Alles was davor kam in die
	 * Nutzdaten weiterschieben.
	 */
	private void shiftQ() {
		while(crcQ.size() > 2) {
			int b = crcQ.poll();
			baos.write(b);
		}
	}
	
	/**
	 * Gibt an, ob die Prüfsumme des zuletzt erkannten
	 * Blocks ok war.
	 * @return <code>true</code> falls Prüfsumme ok war, sonst <code>false</code>
	 */
	public boolean isBlockOk() {
		return blockOk;
	}
	
	/**
	 * Nutzdaten des zuletzt vollständig erkannten Blocks
	 * @return byte[] mit den Daten.
	 */
	public byte[] getLastBlock() {
		return blockData;
	}
}
```

Zum Testen:

```
package freez;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class Main {
	public static void main(String... args) throws UnsupportedEncodingException {
		byte[] data = { 0x2, 0x2, 0x30, 0x31, 0x32, 0x33, 65, 66, 67, (byte)0x5c, (byte)0x06, 0x3, 0x3, 
				-1, -1, 
				0x2, 0x2, 67, 65, 0x1, 0x3, 0x3
		}; 		
		Automat a = new Automat();
		
		for (byte b : data) {
			if (a.consume(b)) {
				System.out.println("blockOk: " + a.isBlockOk() + " / " + Integer.toHexString(a.calcChecksum(a.getLastBlock())));
				System.out.println("Data: " + Arrays.toString(a.getLastBlock()));
				System.out.println("As String: " + new String(a.getLastBlock(), "latin1"));
			}
		}
	}
}
```


----------



## freez (7. Feb 2012)

Danke für das Beispiel ... ist echt interessant auch andere herangehensweisen zu sehen. Auch dass die Package Deklaration nach mir benannt ist, finde ich super :toll: :applaus:. 

Vor allem finde ich elegant, wie du den Trailer detektierst. Hat nur einen Nachteil: 2x 0x03 ist durchaus ein valider Wert, den ich übertragen möchte. Wenn ich in deinem Beispiel data in 
	
	
	
	





```
byte[] data = { 0x2, 0x2, 0x30, 0x31, 0x3, 0x3, 65, 66, 67, (byte)0x5c, (byte)0x06, 0x3, 0x3, 
                -1, -1, 
                0x2, 0x2, 67, 65, 0x1, 0x3, 0x3
        };
```
 ändere, funktioniert dein Beispiel nicht. Hier wäre es eigentlich sinnvoll den Datenbereich in Anzahl von Bytes festzulegen und erst dann auf den Begrenzer abzufragen. Oder eine andere intelligente Erfassung.

Übrigens, an deinem Beispiel kommt beides mal blockOK = false. Sollte da nicht beides mal true kommen?


----------



## HoaX (7. Feb 2012)

Beim Ersten sollte eigentlich true kommen, den zweiten hab ich einfach so eingetipselt. 

Oben schreibst du "Daten (Anzahl beliebig => hier 10bytes)" ... Dass der Trailer nicht in den Nutzdaten vorkommen darf ist klar. Wenn man die Länge der Daten vorher schon weiß, dann braucht man ja auch keinen Trailer. HDLC verwendet aus diesem Grund Bitstuffing um zu verhindern dass die Marker in den Nutzdaten vorkommen.

Du kannst bei meinem Beispiel ja Header und Trailer auf eine Folge anpassen, von der du dir sicher bist dass diese nie in den Nutzdaten auftaucht. Oder Byte-Stuffing verwenden, wenn du eine 0x3 sendest, schickst du hinterher noch eine 0x0. Im Empfangsteil musst du dann die 0x0 dann wieder rausfiltern. Ist mit meinem Code oben mit 2-3 Zeilen schnell angepasst:


```
case TRAILER:
            if (b == 0x3) {
                blockFinished();
                s = State.FIND_START;
                return true;
            } else {
                crcQ.add(0x3);
                if (b != 0x0) {
                    crcQ.add(b);
                }
                shiftQ();
                s = State.DATA;
            }
        }
```


----------



## freez (9. Feb 2012)

HoaX hat gesagt.:


> Oben schreibst du "Daten (Anzahl beliebig => hier 10bytes)" ... Dass der Trailer nicht in den Nutzdaten vorkommen darf ist klar. Wenn man die Länge der Daten vorher schon weiß, dann braucht man ja auch keinen Trailer.


Da hast du auch wieder recht. Ist dann eine zusätzlicher Schutz, wenn man noch einen Trailer hat.



HoaX hat gesagt.:


> Du kannst bei meinem Beispiel ja Header und Trailer auf eine Folge anpassen, von der du dir sicher bist dass diese nie in den Nutzdaten auftaucht. Oder Byte-Stuffing verwenden, wenn du eine 0x3 sendest, schickst du hinterher noch eine 0x0. Im Empfangsteil musst du dann die 0x0 dann wieder rausfiltern.



Wenn ich Bytes hätte, die nie vorkommen, hätte ich die schon genommen. Ich übertrage aber 2byte Integers, wo in dem Fall jeder möglicher Wert übertragen werden könnte.

Byte Stuffing wäre ne Möglichkeit. Aber ist das nicht zu viel Aufwand, wenn ich ganz genau weiss, dass es nur 10Bytes sind, die an Daten da sind? 

Aber lange Rede kurzer Sinn ... dein Code gefällt mir. Werde auf alle Fälle meinen Code nach deinem Vorbild anpassen.


----------



## HoaX (10. Feb 2012)

Das einfügen der 0 nach einer 3 ist auch nur 2-3 Zeilen, jedenfalls definitiv nicht viel Aufwand.
Klar ist es gut wenn man weiß wie lang die Daten sind. Was jetzt das bessere ist, vor allem im Hinblick auf evtl. zukünftige Erweiterungen, musst du wissen. Pauschal falsch ist keine von beiden.


----------

