#!/usr/bin/perl

# e-Badge 1.1 (C)2005 Veit Wahlich <cru@zodia.de>
# Released under the terms of the GNU Public License, version 2 (GPLv2)

use strict;
use warnings;
use IO::File;
use POSIX;

# Default configuration
my $conf={
		version    => '1.1',
		port       => '/dev/ttyS0', # i.e. '/dev/ttyS0'
		baudrate   => 1200,         # 1200 baud is required for the e-Badge
		stty_bin   => '/bin/stty',  # path to the stty utility
		speed      => 2,            # range is 0..4
		repeat     => 0,            # range is 0..255, 0 means forever
		maxlen     => 150,          # fixed to 150 characters
		send_delay => 0             # whether to wait after sending every single character
	};

sub main{
	my $text;
	parseArgs()
		|| do{
			showUsage();
			exit(1);
		};
	$text=constructText($ARGV[0],$conf->{speed},$conf->{repeat});
	if(-c $conf->{port}){
		if(defined($text)){
			sendString($text);
		}
		else{
			showUsage();
			exit(1);
		}
	}
	else{
		die($conf->{port}.' is no character device!'."\n");
	}
}

# parseArgs()
# Parse the command line arguments and set config settings
#
sub parseArgs{
	my $arg;
	if(@ARGV == 0){
		return(0);
	}
	while(@ARGV > 1){
		$arg=shift(@ARGV);
		if($arg eq '-d'){
			$conf->{port}=shift(@ARGV) || return(0);
		}
		elsif($arg eq '-s'){
			$arg=shift(@ARGV);
			if(defined($arg) && $arg=~/^[0-4]$/){
				$conf->{speed}=$arg;
			}
			else{
				return(0);
			}
		}
		elsif($arg eq '-b'){
			$arg=shift(@ARGV);
			if(defined($arg) && $arg=~/^300|600|1200|2400|4800|9600|19200|38400|57600|115200$/){
				$conf->{baudrate}=$arg;
			}
			else{
				return(0);
			}
		}
		elsif($arg eq '-r'){
			$arg=shift(@ARGV);
			if(defined($arg) && $arg=~/^[0-9]+$/ && $arg >= 0 && $arg <= 255){
				$conf->{repeat}=int($arg);
			}
			else{
				return(0);
			}
		}
		elsif($arg eq '-t'){
			$conf->{send_delay}=1;
		}
		else{
			print(STDERR 'Unknown option: '.$arg."\n");
			return(0);
		}
	}
	return(1);
}

# sendString($string)
# Sends a control string to the LED ticker device
#
# Parameters:
#   $string     : string to send to the device
#
sub sendString{
	my($string)=@_;
	my $fd;
	my $i;
	return(undef) unless(defined($string));
	$fd=new IO::File($conf->{port},'w')
		|| die('Error opening port '.$conf->{port}.': '.$!."\n");
	system($conf->{stty_bin},'-F',$conf->{port},'-parenb','-istrip','-cstopb','cs8',$conf->{baudrate})
		&& die('Error setting serial port rate using "'.$conf->{baudrate}.'": '.$!."\n");
	$fd->autoflush(1);
	if($conf->{send_delay}){
		require Time::HiRes;
		for($i=0; $i<length($string); $i++){
			Time::HiRes::usleep(50000);
			print($fd substr($string,$i,1));
		}
	}
	else{
		print($fd $string);
	}
	$fd->close;
	return(1);
}

# $string=constructText($text,$speed,$sign_id,$page)
# Builds a text transfer control string for the LED ticker
#
# Parameters:
#   $text       : a text string that shall be displayed, max. 420 characters
#                 for page B, 80 chars for page A (reserved for logos)
#   $speed      : scroll speed, integer 0..4
#   $sign_id    : the LED ticker's ID, integer 0..255
#   $page       : page to programm, either 'A' or 'B'
#
# Returns:
#   $string     : the control string ready to be sent to device
#
sub constructText{
	my($text,$speed,$repeat)=@_;
	my $string;
	return(undef) unless(defined($text) && defined($speed) && defined($repeat));
	$text=substr(convertTextIso8859_15($text),0,$conf->{maxlen});
	$string=sprintf('01%02x%02x%02x',$repeat,(((4-$speed)*16)+1),length($text)).$text;
	return($string);
}

# $out=convertTextIso8859_15($in)
# Converts a ISO8859-15 string to the device's equivalent charset.
# Also replaces newline/return by spaces.
# 
# Parameters:
#   $in         : ISO8859-15 input string
#
# Returns:
# $out          : string in device's charset
#
sub convertTextIso8859_15{
	my($in)=@_;
	my $out='';
	my $i;
	my $char;
	my $table={
			0xC4 => 0x7F, # 
			0xE4 => 0x80, # 
			0xD6 => 0x81, # 
			0xF6 => 0x82, # 
			0xDC => 0x83, # 
			0xFC => 0x84, # 
			0xDF => 0x85, # 
			0xA4 => 0x86  # 
		};
	return(undef) unless(defined($in));
	for($i=0; $i < length($in); $i++){
		$char=substr($in,$i,1);
		if(exists($table->{ord($char)})){
			$out.=chr($table->{ord($char)});
		}
		else{
			$out.=$char;
		}
	}
	return($out);
}

# showUsage()
# Print usage info to STDOUT
#
sub showUsage{
	print(<<__END);

E-Badge Ticker $conf->{version} (C)2005-2006 Veit Wahlich <cru\@zodia.de>

Syntax: $0 [OPTIONS] <text>

Options [defaults]:
  -d <device>      Device the transmitter is connected to [$conf->{port}]
  -b <baudrate>    The serial device baud rate; 300..115200 [$conf->{baudrate}]
  -s <speed>       Ticker scroll speed; 0 slow, 4 fast; 0..4 [$conf->{speed}]
  -r <iterations>  Iterations of the text; 0 means forever; 0..255 [$conf->{repeat}]
  -t               Activates short delay before sending every single characters,
                   solving problems with some slower E-Badges (req. Time::HiRes)

Notes:
  Maximum text length is 150 characters. Oversized text will be truncated.

__END
}

main();
1;
