User Tools

Site Tools


private:serialdevice

Table of Contents

SerialDevice

SerialDevice est un protocole de messages courts sur liaison série (herzienne).

Le caractère peu dense et répétitif de l'occupation fait que les collisions ont des conséquences “relatives”.

Les messages sont des trames d'octets émis à 4800 bauds, 1 start, 1 stop, parité paire :

  • Start (STX : 0x02)
  • Adresse expéditeur (1 octet)
  • Données (16 octets)
  • Checksum (1 octet)
  • Stop (ETX : 0x03)

La checksum est une Fletcher-8 portant sur adresse + données.

L'émission doit se faire avec une période partiellement aléatoire de préférence (ex. : 10s +/- 0.5s), une trame met 46ms à être émise.

Emission

Code C pour ATMEGA.

#define SERIALDEVICE_ADDRESS 0xc2
 
void serialDeviceSendByte(uint8_t data) {
	while(!(UCSRA & (1<<UDRE)));
	UDR = data;
}
 
// message must be 16 byte long
void serialDeviceSendMessage(uint8_t *message) {
	uint16_t sum1 = 0x000f, sum2 = 0x000f;
	uint8_t len = 16;
 
	if(!SERIALDEVICE_ADDRESS) return;
 
	serialDeviceSendByte(0x02); // STX
 
	serialDeviceSendByte(SERIALDEVICE_ADDRESS); // Sender address
	sum2 += sum1 += SERIALDEVICE_ADDRESS; // Merge into checksum
 
	// Send data while computing 8-bit fletcher checksum
	while(len--) {
		serialDeviceSendByte(*message);
		sum2 += sum1 += *message;
		data++;
	}
 
	sum1 = (sum1 & 0x0f) + (sum1 >> 4);
	sum1 = (sum1 & 0x0f) + (sum1 >> 4);
	sum2 = (sum2 & 0x0f) + (sum2 >> 4);
	sum2 = (sum2 & 0x0f) + (sum2 >> 4);
 
	serialDeviceSendByte(sum2<<4 | sum1); // Send checksum
 
	serialDeviceSendByte(0x03); // ETX
}

Réception

Démon perl qui écoute le port série.

serialDevice.pl
#!/usr/bin/perl
 
use Device::SerialPort;
 
my $port_path = '/dev/ttyS1';
 
my %map = ();
 
# devices.map file is a <sender address> <message processing module> list
# Message processing module must expose a gotMessage and a terminate method.
# The gotMessage method receives the sender address and the data values as integer as arguments.
open(my $f, '<devices.map') or die('Cannot open device map !');
while(<$f>) {
	if($_ =~ /([0-9]{1, 3})\s+([a-z][a-z0-9_]+)/) {
		if(int($1) && -f $2.'.pm') {
			use $2;
			$map{int($1)} = $2;
		}
	}
}
close $f;
 
our $end = 0;
$SIG{'TERM'} = sub {
	$end = 1;
};
 
my $port = Device::SerialPort->new($port_path) or die('Cannot open port !');
$port->baudrate(4800) or die('Failed to set baudrate !');
$port->parity('even') or die('Failed to set parity !');
$port->databits(8) or die('Failed to set data length !');
$port->stty_icml(1) or die('Failed to set cr as new line !');
$port->handshake('none') or die('Failed to set handshaking !');
$port->write_settings or die('Failed to write settings !');
 
sub readPort {
	my $port = shift;
	my ($n, $c) = $port->read(1);
	return ord($c);
}
 
sub fletcher {
	my $sum1 = 15, $sum2 = 15;
	$sum2 += ($sum1 += shift) for(my $i=0; $i<=17; $i++);
	$sum1 = ($sum1 & 0x0f) + int($sum1 / 16);
	$sum1 = ($sum1 & 0x0f) + int($sum1 / 16);
	$sum2 = ($sum2 & 0x0f) + int($sum2 / 16);
	$sum2 = ($sum2 & 0x0f) + int($sum2 / 16);
	return 16*$sum2 + $sum1;
}
 
my $i, $n, $c, @d, $s;
 
while(!$end) {
	if(readPort($port) == 2) { # STX
		@d = ();
		for($i=0; $i<17; $i++) {
			push(@d, readPort($port));
		}
		$s = readPort($port);
		if(
			(readPort($port) == 3) # ETX
			&& $d[0]
			&& (fletcher(@d) == $s)
		) {
			eval $map{$d[0]}.'::gotMessage('.join(', ', @d).')' if(defined $map{$d[0]});
		}
	}
}
 
my %m = ();
$m{$map{$_}} = 1 foreach(keys %map);
eval $_.'::terminate()' foreach(keys %m);
 
undef $port;
 
0;
private/serialdevice.txt · Last modified: 2022/06/30 21:13 by 127.0.0.1