require Exporter;
our @ISA = qw(Exporter);
-our @EXPORT = qw( modbus_crc16 $protocol read_parameter_frame write_parameter_frame );
+our @EXPORT = qw( modbus_crc16 $protocol read_parameter_frame write_parameter_frame $function_code_description hex_dump protocol_decode );
use Data::Dump qw(dump);
return $frame . modbus_crc16($frame);
}
+
+our $function_code_description = {
+ up => {
+ 0x07 => 'Upstream Heartbeat Frame',
+ 0x08 => 'Upstream alarm frame',
+ 0x03 => 'Upstream Read Parameter Frame',
+ 0x06 => 'Upstream return write parameter frame',
+ },
+ down => {
+ 0x03 => 'Downstream read parameter frame',
+ 0x06 => 'Downstream write parameter frame',
+ }
+};
+
+sub hex_dump {
+ my $bin = shift;
+ my $hex = unpack('H*', $bin);
+ $hex =~ s{(..)}{$1 }g;
+ return $hex;
+}
+
+sub protocol_decode {
+ my ( $up_down, $bin ) = @_;
+
+ $SIG{__WARN__} = sub {
+ return unless $debug;
+ print STDERR @_;
+ };
+
+ my $hash = {
+ up_down => $up_down,
+ bin => $bin,
+ };
+
+ my $cksum = substr($bin, -2, 2);
+
+ if ( my $crc = modbus_crc16( substr($bin,0,-2) ) ) {
+ if ( $crc ne $cksum ) {
+ $hash->{error}->{crc} = "got " . unpack('H*',$crc) . " expected " . unpack('H*',$cksum);
+ }
+ }
+
+ my ( $header, $ver, $function_code, $len ) = unpack("CCCv", $bin);
+
+ $hash->{function_code} = $function_code;
+ $hash->{ver} = $ver;
+ $hash->{len} = $len;
+
+ my $data = substr($bin,5,-2);
+
+ warn "header = $header 0x", hex_dump($header)," ver = $ver function_code = $function_code len = $len == ",length($data), " data = ",hex_dump($data), " cksum = ", unpack('H*',$cksum);
+
+ if ( $header != 0x5a ) {
+ $hash->{error}->{header} = "$header expected 0x5a";
+ }
+
+ my $length_data = length($data);
+ if ( $length_data != $len ) {
+ $hash->{error}->{length} = "$length_data expected $len";
+ }
+
+
+ while ( $data ) {
+
+ warn "XXX data = ", hex_dump($data);
+
+ my $data_id = unpack( 'C', substr($data,0,1) );
+
+ if ( ! exists( $protocol->{$data_id} ) ) {
+ my $len = unpack('C', substr($data,1,1));
+ push @{ $hash->{error}->{data_id} }, sprintf "data_id %d 0x%2x len %d [%s]", $data_id, $data_id, $len, unpack('H*', substr($data,2,$len));
+ $data = substr($data,2 + $len);
+ next;
+ }
+ my $pack_fmt = $protocol->{$data_id}->{pack_fmt} || die "can't find pack_fmt for data_id $data_id";
+
+ my $data_range;
+ my $data_len;
+
+ if ( $data_id == 0x00 ) {
+ # sequence number
+
+ $data_range = substr($data,2,4);
+ $data = substr($data,6);
+
+ } elsif ( $up_down eq 'down' && $function_code == 0x03 ) {
+ # type A
+ # 0x03 => 'Downstream read parameter frame',
+
+ $data_range = substr($data,0,1);
+ $data = substr($data,1);
+
+ } elsif ( $up_down eq 'up' && ( $function_code == 0x07 || $function_code == 0x08 || $function_code == 0x03 ) ) {
+ # type B
+ # 0x07 => 'Upstream Heartbeat Frame',
+ # 0x08 => 'Upstream alarm frame',
+ # 0x03 => 'Upstream Read Parameter Frame',
+
+ $data_len = unpack('C', substr($data,1,1));
+ $data_range = substr($data,2, $data_len);
+
+ $data = substr($data,2 + $data_len);
+
+ } elsif ( $up_down eq 'down' && $function_code == 0x06 ) {
+ # type B
+ # 0x06 => 'Downstream write parameter frame',
+
+ $data_len = unpack('C', substr($data,1,1));
+ $data_range = substr($data,2, $data_len);
+
+ $data = substr($data,2 + $data_len);
+
+ } elsif ( $up_down eq 'up' && $function_code == 0x06 ) {
+ # type C
+ # 0x06 => 'Upstream return write parameter frame',
+
+ $data_len = unpack('C', substr($data,1,1));
+ $data_range = substr($data,2, $data_len);
+ if ( $data_len == 1 ) {
+ # XXX return is OK/not OK
+ $pack_fmt = 'C';
+ }
+
+ $data = substr($data,2 + $data_len);
+
+ } else {
+ $hash->{error}->{function_code_unknown} = $function_code;
+ print STDERR "ERROR unknown function_code = $function_code\n";
+ return $hash;
+ }
+
+ my @v = unpack($pack_fmt, $data_range);
+
+ my $v = join(' ', @v);
+
+ if ( $data_id == 0x0c ) {
+ $v = $v / 100;
+ } elsif ( $data_id == 0x0d ) {
+ $v = $v / 100;
+ }
+
+ push @{ $hash->{data_id_order} }, $data_id;
+ $hash->{data_id}->{$data_id} = $v;
+ $hash->{data_len}->{$data_id} = $data_len;
+ $hash->{data_range}->{$data_id} = $data_range;
+
+ }
+
+ return $hash;
+
+} # protocol_decode
+
+
1;
# 6. Protocol Format
# DATA ID ID Description Data Type(Data Length) R/W Range Default Remark
__DATA__
-0x00 Seq # l DWord(4) R / 0 The platform can carry the ID when the platform downstream reads and sets the device parameters, and the device returns the same data. Please refer to the example for use.Each seq # refer to one command and its response.
-0x01 PN l DWord(4) R / / /
+0x00 Seq # L DWord(4) R / 0 The platform can carry the ID when the platform downstream reads and sets the device parameters, and the device returns the same data. Please refer to the example for use.Each seq # refer to one command and its response.
+0x01 PN L DWord(4) R / / /
0x02 Model C Byte(1) R 32 32 Inner number:32
0x03 X axis angle f Float(4) R -90-90 / X axis angle
0x04 Y axis angle f Float(4) R -90-90 / Y axis angle
0x19 Alarm axis C Byte(1) R 0~3 / 0: no alarm; 1: X axis alarm 2: Y axis alarm; 3: X/Y axis alarm at the same time
0x1A SIM card ID Q QWord(8) R 0~18446744073709551615 / Take the first 19 digits, the last digit is discarded
0x1B Alarm angle f Float(4) R/W 0.06-30.00 5.0 ZCT330E relative mode, passive upload
-0x1C Alarm trigger time l Dword(4) R/W 2-20 2 ZCT330E relative mode, passive upload
+0x1C Alarm trigger time L Dword(4) R/W 2-20 2 ZCT330E relative mode, passive upload
0x1D Static angle f Float(4) R/W 0.06-30.00 2.5 ZCT330E relative mode, passive upload
-0x1E Static trigger time l Dword(4) R/W 2-20 2 ZCT330E relative mode, passive upload
-0x1F Acquisition interval l Dword(4) R/W 1-2160000 60 ZCT330E absolute mode, passive upload
-0x20 Failure retransmission l Dword(4) R/W 20-2160000 3600 ZCT330E absolute mode, passive upload
-0x21 Heartbeat interval l DWord(4) R/W 60~131071 86400 Interval at which the device periodically uploads data to the server
+0x1E Static trigger time L Dword(4) R/W 2-20 2 ZCT330E relative mode, passive upload
+0x1F Acquisition interval L Dword(4) R/W 1-2160000 60 ZCT330E absolute mode, passive upload
+0x20 Failure retransmission L Dword(4) R/W 20-2160000 3600 ZCT330E absolute mode, passive upload
+0x21 Heartbeat interval L DWord(4) R/W 60~131071 86400 Interval at which the device periodically uploads data to the server
0x22 IMEI number of the device Q QWord(8) R 0~18446744073709551615 / Refers to the IMEI of the NB-IOT network module in the product.
0x23 Backup server IP&port CCCCs 4*Byte(1)+Word(2) R/W / CTIOT:117.60.157.137,5683 MQTT:0.0.0.0,0 / if set, clears 0x3b, if 0x23 and 0x3b are set at same time, 0x23 has priority
0x24 Backup server enable C Byte(1) R/W 0~255 0 0 means off, non-zero means on
0x3B Backup server domain name and port a* 64*Byte(1) R/W / mqtt.zc-sensor.com,1883 Supports CTIOT and MQTT protocols. In the case of backup IP, the IP is preferentially backed up; the domain name and port are distinguished by commas, and the length is <=64. , will be cleared if 0x23 is set
0x3D Protocol type C Byte(1) R/W 0~1 CTIOT 0:CTIOT 1:MQTT Other values are invalid.
0x3E Alarm angle f Float(4) R/W 0.06-30.00 2.0 ZCT330E vibration mode, passive upload
-0x3F Alarm trigger time l Dword(4) R/W 80-20000 240 ZCT330E vibration mode, passive upload (trigger time in ms)
+0x3F Alarm trigger time L Dword(4) R/W 80-20000 240 ZCT330E vibration mode, passive upload (trigger time in ms)
0x40 Static angle f Float(4) R/W 0.06-30.00 1.0 ZCT330E vibration mode, passive upload
-0x41 Static trigger time l Dword(4) R/W 80-20000 240 ZCT330E vibration mode, passive upload (trigger time in ms)
-0x42 Heartbeat interval l DWord(4) R/W 60~2160000 86400 ZCT330E (unit: s) vibration mode, Interval at which the device periodically uploads data to the server
-0x43 Failure retransmission l Dword(4) R/W 20-2160000 3600 ZCT330E (unit: s) vibration mode, passive upload
+0x41 Static trigger time L Dword(4) R/W 80-20000 240 ZCT330E vibration mode, passive upload (trigger time in ms)
+0x42 Heartbeat interval L DWord(4) R/W 60~2160000 86400 ZCT330E (unit: s) vibration mode, Interval at which the device periodically uploads data to the server
+0x43 Failure retransmission L Dword(4) R/W 20-2160000 3600 ZCT330E (unit: s) vibration mode, passive upload
0x44 Alarm angle f Float(4) R/W ZCT330M:‐90°~90°,ZCT330E:0.06-30.00 ZCT330M:3°,ZCT330E:5.0 X/Y axis alarm angle is consistent, ZCT330E in absolue mode, passsive upload
-0x45 Alarm trigger time l Dword(4) R/W 0-3600 0 ZCT330E Sensor waiting time for the server to issue a command (s), passive upload
+0x45 Alarm trigger time L Dword(4) R/W 0-3600 0 ZCT330E Sensor waiting time for the server to issue a command (s), passive upload