File Coverage

blib/lib/Device/Chip/From/Sensirion.pm
Criterion Covered Total %
statement 78 78 100.0
branch 10 10 100.0
condition 2 3 66.6
subroutine 13 13 100.0
pod 1 1 100.0
total 104 105 99.0


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2024 -- leonerd@leonerd.org.uk
5              
6 6     6   453104 use v5.26;
  6         25  
7 6     6   36 use warnings;
  6         19  
  6         392  
8 6     6   860 use Object::Pad 0.800;
  6         11792  
  6         279  
9              
10 6     6   916 use utf8;
  6         18  
  6         42  
11              
12             package Device::Chip::From::Sensirion 0.02;
13             class Device::Chip::From::Sensirion
14 1     1   785 :isa(Device::Chip);
  1         32020  
  1         183  
15              
16 6     6   5845 use Sublike::Extended 0.29 'method';
  6         4947  
  6         39  
17              
18 6     6   420 use Future::AsyncAwait;
  6         14  
  6         47  
19              
20 6     6   435 use constant PROTOCOL => "I2C";
  6         12  
  6         12391  
21              
22             =head1 NAME
23              
24             C - a collection of chip drivers for F sensors
25              
26             =head1 DESCRIPTION
27              
28             This distribution contains a number of L drivers for various
29             sensor chips manufactured by F.
30              
31             It also acts as a base class, providing common functionality for often-used
32             operations.
33              
34             =cut
35              
36             # The CRC algorithm from the data sheet
37             sub _gen_crc8 ( $bytes )
38 32     32   57 {
  32         84  
  32         47  
39 32         56 my $crc = 0xFF;
40 32         95 foreach my $byte ( unpack "C*", $bytes ) {
41 64         101 $crc ^= $byte;
42 64         134 foreach ( 1 .. 8 ) {
43 512 100       1087 $crc = ( $crc << 1 ) ^ ( ( $crc & 0x80 ) ? 0x31 : 0 );
44             }
45 64         116 $crc &= 0xFF;
46             }
47 32         152 return $crc;
48             }
49              
50             sub _pack_with_crc ( $word )
51 4     4   9 {
  4         7  
  4         7  
52 4         11 my $bytes = pack "S>", $word;
53 4         14 return pack "a2 C", $bytes, _gen_crc8( $bytes );
54             }
55              
56             =head1 METHODS
57              
58             =for highlighter language=perl
59              
60             =cut
61              
62 17         54 async method _cmd ( $cmd,
  17         30  
63             :$words_out = undef,
64             :$delay = undef,
65 17     17   59 :$read = 0,
66 17         34 ) {
  17         103  
  17         29  
67 17         85 my $bytes_out = pack( "S>", $cmd );
68              
69 17 100 66     75 if( $words_out and @$words_out ) {
70 2         12 $bytes_out .= _pack_with_crc( $_ ) for @$words_out;
71             }
72              
73 17 100       52 if( !$read ) {
74 1         8 await $self->protocol->write( $bytes_out );
75 1         9945 return;
76             }
77              
78 16         116 my $protocol = $self->protocol;
79              
80 16         75 my $bytes_in;
81 16 100       51 if( !defined $delay ) {
82 14         83 $bytes_in = await $protocol->write_then_read( $bytes_out, $read * 3 );
83             }
84             else {
85 2         14 await $protocol->write( $bytes_out );
86 2         11148 await $protocol->sleep( $delay );
87 2         3461 $bytes_in = await $protocol->read( $read * 3 );
88             }
89              
90             # Bytes contains 3x ( 16bit data, 8bit CRC )
91 16         39583 my @dat = unpack( "(a2 C)*", $bytes_in );
92 16         55 my @words;
93 16         77 while( @dat ) {
94 28         82 my $word = shift @dat;
95 28         53 my $crc = shift @dat;
96 28 100       81 die "CRC mismatch on word " . ( scalar @words ) . "\n" if _gen_crc8( $word ) != $crc;
97 27         135 push @words, unpack "S>", $word;
98             }
99 15         116 return @words;
100             }
101              
102 14     14   6706 async method _read ( $cmd, $words )
  14         39  
  14         22  
  14         43  
  14         26  
103 14         34 {
104 14         62 return await $self->_cmd( $cmd, read => $words );
105             }
106              
107             # Sensirion chips seem to have a common method for reading their serial number
108              
109             =head2 get_serial_number
110              
111             $bytes = await $chip->get_serial_number;
112              
113             Returns a 6-byte encoding of the chip's internal serial number.
114              
115             =cut
116              
117 2     2 1 5483 async method get_serial_number ()
  2         8  
  2         4  
118 2         5 {
119 2         12 my @words = await $self->_read( 0x3682, 3 );
120 1         123 return pack( "(S>)*", @words );
121             }
122              
123             =head1 AUTHOR
124              
125             Paul Evans
126              
127             =cut
128              
129             0x55AA;