3B-X

Einführung

Was soll das den nun sein: 3B-X? Klingt fast wie ein Roboter. Aber in Wirklichkeit, ist es ein kleines Protokoll zum senden und empfangen von Daten über serielle Verbindungen, mit recht hoher Zuverlässigkeit aber hohem Protokollaufwand. Ins leben gerufen wurde es von Jochen Hebeler im Rahmen eines AVR Projektes.

Aufbau

Wie sieht nun 3B-X aus? Wie schon beschrieben, soll es einfach sein. Und so ist es auch.
Das X steht für die Breite der verwendeten Variablen, ob 8-Bit, 16 oder 32. Hier wird exemplarisch 3B-8 behandelt.
3B-8 sieht für jedes Datenpaket eine führende 0, danach der Spezifizierer der ungleich 0 sein muss und dann die Daten vor. Exemplarisch an 3B-8 könnte das so aussehen(Hexadezimal):
0x00 0x01 0x54
Den Typspezifizierer nutzt man dazu, unterschiedliche Daten zu kennzeichnen. Auch erkennt man, dass 2/3 aller Daten eigentlich nur Packet-Header sind. Damit ist es recht Ineffizient, aber wenn es sowieso nicht auf jede Microsekunde ankommt und man keine großen(relativ) Datenmengen verschicken will, ist es akzeptabel. Der Vorteil dieses Protokolls wird in der Implementation ersichtlich. Ein 3B-8 Sender braucht nur eine serielle Verbindung wie RS-232 und muss dann nur 0-Byte, Typ-Byte und die Daten senden.
Ein Empfänger ist ähnlich einfach zu konstruieren. Man wartet auf den Übergang von 0-Byte auf Nicht-0-Byte, der nur am Anfang eines Paketes auftritt, wertet dann das Typ-Byte aus, z.B in einem switch-Block und verwertet anschließend die Daten.
Damit ist das Protokoll sehr ausfallsicher, denn sollte der Empfänger mal den Anschluss verlieren, muss er nur auf den nächsten Übergang von 0-Byte auf Nicht-0-Byte warten und weiß wieder wo er ist. Aber dieses Protokoll funktioniert nur richtig, wenn man konstant Daten sendet. Um einmal Werte zu ändern, eignet es sich nicht, da hierfür keine Garantie für korrektem Empfang gibt. Auch sollte man sich überlegen, ob eventuell falsche Datensätze schaden anrichten können und muss sich dagegen absichern, da dieses Protokoll keine Überprüfung der Daten vorsieht.

Anwendungsbeispiele

Sender auf einem AVR:

//Initialise UART!
void SendByte(uint8_t data)
{
	while(!(UCSRA & (1 << UDRE)));
	UDR = data;
}
 
void SendData(uint8_t type, uint8_t data)
{
	SendByte(0);
	SendByte(type);
	SendByte(data);
}

Empfänger auf einem AVR:

//Execute a command recived
void ExecuteCmd(uint8_t cmd, uint8_t data)
{
	switch(cmd)
	{
	case 0:	//Nothing
		return;
		break;
	case 1:	//self-test
		SelfTest();
		break;
	case 2:		//forward
		Motor1(data, direction = vorwaerts);
		Motor2(data, direction = vorwaerts);
		break;
	case 3:		//backward
		Motor1(data, direction = rueckwaerts);
		Motor2(data, direction = rueckwaerts);
		break;
	case 4:		//servo angle
		SetServo(data);
		break;
	case 5:		//Motor 1 forward
		Motor1(data, direction = vorwaerts);
		break;
	case 6:		//Motor 1 backward
		Motor1(data, direction = rueckwaerts);
		break;
	case 7:		//Motor 2 forward
		Motor2(data, direction = vorwaerts);
		break;
	case 8:		//Motor 2 backward
		Motor2(data, direction = rueckwaerts);
		break;
	case 9:		//halt
		Motor1(data, direction = halt);
		Motor2(data, direction = halt);
		break;
	case 10:	//stopp
		Motor1(data, direction = stopp);
		Motor2(data, direction = stopp);
		break;
	case 11:
		irDistRef = data;
		break;
	case 12:
		usDistRef = data;
		break;
	}
}
[...]
unsigned char data[32];
for(int i = 0; i < 10; i++)
		{
			if(!(data[i] == 0 && data[i+1] != 0)) continue;
			ExecuteCmd(data[i+1], data[i+2]);
		}