#! /usr/bin/perl

use strict;
use utf8;
#use Device::SerialPort qw( :PARAM :STAT 0.07 );
use IO::Termios;
use Time::HiRes qw(usleep);

# B4 C0 A8 01 01 00 1E  set address
# B0 C0 A8 01 01 00 1A  read voltage
# B2 C0 A8 01 01 00 1C  read power
# B3 C0 A8 01 01 00 1D  read energy
$| = 1;

=pod
my $port = Device::SerialPort->new("/dev/ttyS7") || die "$!\n";

$port->baudrate(9600);
$port->databits(8);
$port->parity("none");
$port->stopbits(1);
$port->handshake("none");

$port->write_settings or die "no settings\n";

#print "handshake: ", $port->handshake, "\n";
#print "baud: ", $port->baudrate, "\n";
=cut

my $port = IO::Termios->open( "/dev/ttyS7", "9600,8,n,1" )
   or die "Cannot open ttyS8 - $!";
$port->autoflush(1);

my @incdata = ();

my $v 		= 220.0;
my $v_dir	= 'inc';

my $p		= 1000;
my $p_dir   = 'inc';

my $e		= 300;
my $e_cnt	= 0;

my $pkgs_sent = 0;
my $chars_out = 0;

my $prev_req = 0;

my $c;

print "starting PZEM emulation\n";

while (1) {
	#my $char = $port->lookfor();
	#my ($count, $char) = $port->read(1);
	my $cn = $port->sysread($c, 1);
	if ($cn){
		#printf "\rReceived character: %02x                   ", ord($c);
		push(@incdata, ord($c));		
	}
  
	if( @incdata >= 7 ){
		if( $incdata[1] == 0xC0 and $incdata[2] == 0xA8 and $incdata[3] == 0x01 and $incdata[4] == 0x01 and $incdata[5] == 0x00){
			if($incdata[0] == 0xB4 and $incdata[6] == 0x1E){ # setaddr command
				$pkgs_sent+++;
				print "\rsetaddr command detected  $pkgs_sent                 ";
				$chars_out = $port->syswrite("\xA4\x00\x00\x00\x00\x00\xA4", 7);
				print "\nincomplete! \n" if $chars_out != 7;
				$prev_req = 3;
				splice( @incdata, 0, 7);	  
	  		}elsif($incdata[0] == 0xB0 and $incdata[6] == 0x1A){ # read voltage command
				$pkgs_sent++;
				print"\rread voltage command detected  $pkgs_sent  $v                  ";
				$chars_out = $port->syswrite(get_voltage(), 7);
				print "\nincomplete! \n" if $chars_out != 7;
				print "\nsequence broken! \n" if $prev_req != 3 ;
				$prev_req = 1;
				splice( @incdata, 0, 7);
	  		}elsif($incdata[0] == 0xB2 and $incdata[6] == 0x1C){ # read power command
				$pkgs_sent++;
				print"\rread power command detected  $pkgs_sent  $p                 ";
				$chars_out = $port->syswrite(get_power(), 7);
				print "\nincomplete! \n" if $chars_out != 7;
				print "\nsequence broken! \n" if $prev_req != 1;
				$prev_req = 2;
				splice( @incdata, 0, 7);
	  		}elsif($incdata[0] == 0xB3 and $incdata[6] == 0x1D){ # read energy command
				$pkgs_sent++;
				print"\rread energy command detected $pkgs_sent   $e                 ";
				$chars_out = $port->syswrite(get_energy(), 7);
				print "\nincomplete! \n" if $chars_out != 7;
				print "\nsequence broken! \n" if $prev_req != 2;
				$prev_req = 3;
				splice( @incdata, 0, 7);
	  		}else{
				shift @incdata;
	  		}
		}else{
	  		shift @incdata;
		}
  	}
  	usleep(5*1000);  
}




sub get_voltage{
	my @resp = (0,0,0,0,0,0,0);
  
  	if( $v > 235 ){
		$v_dir = 'dec';
  	}elsif( $v < 205){
		$v_dir = 'inc';
  	}
  
  	if( $v_dir eq 'inc'){
		$v += 0.1;
  	}else{
		$v -= 0.1;
  	}
  
  	my $tmp = sprintf("%.1f", $v);
  	my($vi, $vf) = split(/\./, $tmp);
  
  	$resp[0] = 0xA0;

  	($resp[1], $resp[2]) = (sprintf("%04X",$vi) =~ m/^(\w{2})(\w{2})$/);
  	($resp[1], $resp[2]) = (hex($resp[1]), hex($resp[2]));
  	$resp[3] = $vf + 0;
  	$resp[6] = $resp[0]+$resp[1]+$resp[2]+$resp[3]+$resp[4]+$resp[5]+0;
  
  	return pack("C7", @resp);
}

sub get_power{
  	my @resp = (0,0,0,0,0,0,0);
  
  	if( $p > 3000 ){
		$p_dir = 'dec';
  	}elsif( $p < 20){
		$p_dir = 'inc';
  	}
  
  	if( $p_dir eq 'inc'){
		$p += 5;
  	}else{
		$p -= 5;
  	}
  

  	$resp[0] = 0xA2;
 	($resp[1], $resp[2]) = (sprintf("%04X",$p) =~ m/^(\w{2})(\w{2})$/);
  	($resp[1], $resp[2]) = (hex($resp[1]), hex($resp[2]));
  	$resp[6] = $resp[0]+$resp[1]+$resp[2]+$resp[3]+$resp[4]+$resp[5]+0;
  
  	return pack("C7", @resp);

}

sub get_energy{
  	my @resp = (0,0,0,0,0,0,0);
  
  	$e++, $e_cnt=0 if( ++$e_cnt > 400);

  	$resp[0] = 0xA3;
  	($resp[1], $resp[2], $resp[3]) = (sprintf("%06X",$e) =~ m/^(\w{2})(\w{2})(\w{2})$/);
  	($resp[1], $resp[2], $resp[3]) = (hex($resp[1]), hex($resp[2]), hex($resp[3]));
  	$resp[6] = $resp[0]+$resp[1]+$resp[2]+$resp[3]+$resp[4]+$resp[5]+0;
  
  	return pack("C7", @resp);
}
