{"id":3630,"date":"2020-04-07T02:49:30","date_gmt":"2020-04-07T09:49:30","guid":{"rendered":"http:\/\/www.sheer.us\/weblogs\/?p=3630"},"modified":"2020-04-21T14:51:48","modified_gmt":"2020-04-21T21:51:48","slug":"perl-module-for-decoding-tekpower-tp4000zc","status":"publish","type":"post","link":"https:\/\/www.sheer.us\/weblogs\/uncategorized\/perl-module-for-decoding-tekpower-tp4000zc","title":{"rendered":"Perl module for decoding tekpower TP4000ZC"},"content":{"rendered":"<p>I didn&#8217;t see one of these lying around  so I banged this together in a few minutes to be able to use the TP4000ZC with a raspberry pi.. the intention is to use this to monitor the main bus voltage of my solar array.<\/p>\n<p>This is provided, obviously, with no warranty, and is based on information found at <A HREF=https:\/\/tekpower.us\/downloadable\/download\/sample\/sample_id\/3\/>tek&#8217;s web site<\/A>. It seems to match the display.<\/p>\n<p><code><br \/>\npackage tekpower;<\/p>\n<p>use Device::SerialPort;<br \/>\nuse Data::Dumper;<br \/>\nuse strict;<\/p>\n<p>sub new {<br \/>\n        my $self = {};<br \/>\n        my $type = shift;<br \/>\n        my $port = shift;<\/p>\n<p>        $self->{'debug'} = 0;<br \/>\n        $self->{'port'} = $port;<br \/>\n        $self->{'dev'} = Device::SerialPort->new($port, 1) || die \"Failed to open $port\";<br \/>\n        $self->{'dev'}->baudrate(2400);<br \/>\n        $self->{'dev'}->parity('none');<br \/>\n        $self->{'dev'}->databits(8);<br \/>\n        $self->{'dev'}->stopbits(1);<br \/>\n        $self->{'dev'}->dtr_active(1);<\/p>\n<p>        $self->{'dev'}->debug($self->{'debug'}) if($self->{'debug'});<br \/>\n        $self->{'dev'}->write_settings;<br \/>\n        $self->{'dev'}->read_const_time(3000);<br \/>\n        my $r = bless($self, $type);<\/p>\n<p>        return $r;<br \/>\n}<\/p>\n<p>sub read {<br \/>\n        my $self = shift;<br \/>\n        $self->{'dev'}->reset_error;<br \/>\n        my ($count,$buf) = $self->{'dev'}->read(64);<br \/>\n        print \"count: $count\\n\" if($self->{'debug'});<br \/>\n        my @array = split(\/\/,$buf);<br \/>\n        my $notOk = 1;<br \/>\n        while($notOk && @array) {<br \/>\n                my $z = shift(@array);<\/p>\n<p>                print \"z: \" . ord($z) if($self->{'debug'});<\/p>\n<p>                my $checksum = (ord($z) & 0xF0) >> 4;<br \/>\n                print \"checksum: $checksum\\n\" if($self->{'debug'});<br \/>\n                if($checksum == 1) {<br \/>\n                        unshift(@array,$z);<br \/>\n                        $notOk = 0;<br \/>\n                }<br \/>\n        }<br \/>\n        my @v_array;<br \/>\n        # first check high order bits<br \/>\n        my ($i, $checksum);<br \/>\n        for($i=0;$i<14;$i++) {\n                $checksum = ord($array[$i]) &#038; 0xF0;\n                $v_array[$i] = ord($array[$i]) &#038; 0x0F;\n                $checksum = $checksum >> 4;<br \/>\n                if($checksum != ($i+1)) {<br \/>\n                        print \"Checksum mismatch at $i ($checksum)\\n\" if($self->{'debug'});<br \/>\n                        return undef;<br \/>\n                }<\/p>\n<p>        }<\/p>\n<p>        # second decode reading;<br \/>\n        my $mode;<\/p>\n<p>        $self->{'smode'} = \"AC\" if($v_array[0] & 8);<br \/>\n        $self->{'smode'} = \"DC\" if($v_array[0] & 4);<br \/>\n        # bit 2 is auto ranging, do we care?<br \/>\n        # bit 1 is RS232, um, if you don't know that, what are you doing here?<br \/>\n        $self->{'sign'} = \"+\";<br \/>\n        $self->{'sign'} = \"-\" if($v_array[1] & 8);<br \/>\n        $self->{'digit1'} = $self->convert_digit($v_array[1], $v_array[2], 0 );<br \/>\n        $self->{'digit2'} = $self->convert_digit($v_array[3], $v_array[4], 1 );<br \/>\n        $self->{'digit3'} = $self->convert_digit($v_array[5], $v_array[6], 1 );<br \/>\n        $self->{'digit4'} = $self->convert_digit($v_array[7], $v_array[8], 1 );<\/p>\n<p>        $self->{'number'} = $self->{'sign'} . $self->{'digit1'} . $self->{'digit2'} . $self->{'digit3'} . $self->{'digit4'};<\/p>\n<p>        $self->{'range'} = 'u' if($v_array[9] & 8);<br \/>\n        $self->{'number'} *= 0.000001 if($v_array[9] & 8);<\/p>\n<p>        $self->{'range'} = 'n' if($v_array[9] & 4);<br \/>\n        $self->{'number'} *= 0.000000001 if($v_array[9] & 4);<br \/>\n        $self->{'range'} = 'k' if($v_array[9] & 2);<br \/>\n        $self->{'number'} *= 1000 if($v_array[9] & 2);<\/p>\n<p>        # 1 is diode, do we care?<br \/>\n        $self->{'range'} = 'm' if($v_array[10] & 8);<br \/>\n        $self->{'number'} *= 0.001 if($v_array[10] & 8);<\/p>\n<p>        $self->{'range'} = '%' if($v_array[10] & 4);<br \/>\n        $self->{'range'} = 'M' if($v_array[10] & 2);<br \/>\n        $self->{'number'} *= 1000000 if($v_array[10] & 2);<\/p>\n<p>        $self->{'mode'} = \"farad\" if($v_array[11] & 8);<br \/>\n        $self->{'mode'} = \"ohm\" if($v_array[11] & 4);<br \/>\n        $self->{'mode'} = \"delta\" if($v_array[11] & 2);<\/p>\n<p>        # bit 1 is hold, do we care?<\/p>\n<p>        $self->{'mode'} = \"amps\" if($v_array[12] & 8);<br \/>\n        $self->{'mode'} = \"volts\" if($v_array[12] & 4);<br \/>\n        $self->{'mode'} = \"hz\" if($v_array[12] & 2);<\/p>\n<p>        return $self->{'smode'} . ' ' . $self->{'mode'} . \" \" . $self->{'sign'} . \" \" . $self->{'digit1'} . $self->{'digit2'} . $self->{'digit3'} . $self->{'digit4'} . ' ' . $self->{'range'};<\/p>\n<p>}<\/p>\n<p>sub convert_digit {<br \/>\n        my $self = shift;<br \/>\n        my $lhs = shift;<br \/>\n        my $rhs = shift;<br \/>\n        my $include_decimal = shift;<br \/>\n        my $decimal;<\/p>\n<p>        if($include_decimal) {<br \/>\n                if($lhs & 8) {<br \/>\n                        $decimal = \".\";<br \/>\n                } else {<br \/>\n                        $decimal = \"\";<br \/>\n                }<br \/>\n        }<\/p>\n<p>        $lhs = $lhs & 7;<\/p>\n<p>        my $d;<\/p>\n<p>        # 000 0101 = 1<br \/>\n        if($lhs == 0 && $rhs == 5) {<br \/>\n                $d = 1;<br \/>\n        # 101 1011 = 2<br \/>\n        } elsif($lhs == 5 && $rhs == 11) {<br \/>\n                $d = 2;<br \/>\n        # 001 1111 = 3<br \/>\n        } elsif($lhs == 1 && $rhs == 15) {<br \/>\n                $d = 3;<br \/>\n        # 010 0111 = 4<br \/>\n        } elsif($lhs == 2 && $rhs == 7) {<br \/>\n                $d = 4;<br \/>\n        # 011 1110 = 5<br \/>\n        } elsif($lhs == 3 && $rhs == 14) {<br \/>\n                $d = 5;<br \/>\n        # 111 1110 = 6<br \/>\n        } elsif( $lhs == 7 && $rhs == 14) {<br \/>\n                $d = 6;<br \/>\n        # 001 0101 = 7<br \/>\n        } elsif($lhs == 1 && $rhs == 5) {<br \/>\n                $d = 7;<br \/>\n        # 111 1111 = 8<br \/>\n        } elsif($lhs == 7 && $rhs == 15) {<br \/>\n                $d = 8;<br \/>\n        # 011 1111 = 9<br \/>\n        } elsif($lhs == 3 && $rhs == 15 ) {<br \/>\n                $d = 9;<br \/>\n        # 111 1101 = 0<br \/>\n        } elsif($lhs == 7 && $rhs == 13 ) {<br \/>\n                $d = 0;<br \/>\n        } elsif($lhs == 6 && $rhs == 8) {<br \/>\n                $d = \"L\";<br \/>\n        } else {<br \/>\n                return undef;<br \/>\n        }<\/p>\n<p>        my $v = $decimal . $d;<br \/>\n        print \"V: $v\\n\" if($self->{'debug'});<br \/>\n        return $v;<br \/>\n}<\/p>\n<p>1;<br \/>\n<\/code><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I didn&#8217;t see one of these lying around so I banged this together in a few minutes to be able to use the TP4000ZC with a raspberry pi.. the intention is to use this to monitor the main bus voltage of my solar array. This is provided, obviously, with no warranty, and is based on [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/posts\/3630"}],"collection":[{"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/comments?post=3630"}],"version-history":[{"count":3,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/posts\/3630\/revisions"}],"predecessor-version":[{"id":3636,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/posts\/3630\/revisions\/3636"}],"wp:attachment":[{"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/media?parent=3630"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/categories?post=3630"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.sheer.us\/weblogs\/wp-json\/wp\/v2\/tags?post=3630"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}