File Coverage

blib/lib/Device/Chip/HTU21D.pm
Criterion Covered Total %
statement 90 90 100.0
branch 4 6 66.6
condition 4 6 66.6
subroutine 21 21 100.0
pod 4 5 80.0
total 123 128 96.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, 2016-2023 -- leonerd@leonerd.org.uk
5              
6 4     4   768782 use v5.26;
  4         38  
7 4     4   19 use warnings;
  4         8  
  4         107  
8 4     4   540 use Object::Pad 0.800;
  4         8991  
  4         175  
9              
10             package Device::Chip::HTU21D 0.10;
11             class Device::Chip::HTU21D
12 1     1   500 :isa(Device::Chip);
  1         13800  
  1         40  
13              
14 4     4   961 use utf8;
  4         7  
  4         28  
15              
16 4     4   95 use Carp;
  4         7  
  4         288  
17              
18 4     4   1677 use Data::Bitfield 0.02 qw( bitfield boolfield );
  4         7438  
  4         286  
19 4     4   27 use List::Util qw( first );
  4         8  
  4         286  
20              
21 4     4   25 use Future::AsyncAwait 0.38; # async method
  4         46  
  4         19  
22 4     4   1719 use Future::Mutex;
  4         1687  
  4         170  
23              
24 4     4   1522 use Device::Chip::Sensor -declare;
  4         9975  
  4         17  
25              
26 4     4   465 use constant PROTOCOL => "I2C";
  4         8  
  4         471  
27              
28             =encoding UTF-8
29              
30             =head1 NAME
31              
32             C - chip driver for F
33              
34             =head1 SYNOPSIS
35              
36             use Device::Chip::HTU21D;
37             use Future::AsyncAwait;
38              
39             my $chip = Device::Chip::HTU21D->new;
40             await $chip->mount( Device::Chip::Adapter::...->new );
41              
42             printf "Current temperature is is %.2f C\n",
43             await $chip->read_temperature;
44              
45             =head1 DESCRIPTION
46              
47             This L subclass provides specific communication to a
48             F F attached to a computer via an I²C adapter.
49              
50             The reader is presumed to be familiar with the general operation of this chip;
51             the documentation here will not attempt to explain or define chip-specific
52             concepts or features, only the use of this module to access them.
53              
54             =cut
55              
56             sub I2C_options
57             {
58 3     3 0 1102 my $self = shift;
59              
60             return (
61 3         18 addr => 0x40,
62             max_bitrate => 400E3,
63             );
64             }
65              
66             =head1 ACCESSORS
67              
68             The following methods documented in an C expression return L
69             instances.
70              
71             =cut
72              
73             use constant {
74             # First-byte commands
75 4         5859 CMD_TRIGGER_TEMP_HOLD => 0xE3,
76             CMD_TRIGGER_HUMID_HOLD => 0xE5,
77             CMD_TRIGGER_TEMP_NOHOLD => 0xF3,
78             CMD_TRIGGER_HUMID_NOHOLD => 0xF5,
79             CMD_WRITE_REG => 0xE6,
80             CMD_READ_REG => 0xE7,
81             CMD_SOFT_RESET => 0xFE,
82 4     4   24 };
  4         6  
83              
84             bitfield { format => "bytes-LE" }, REG_USER =>
85             RES0 => boolfield( 0 ),
86             OTPDISABLE => boolfield( 1 ),
87             HEATER => boolfield( 2 ),
88             ENDOFBATT => boolfield( 6 ),
89             RES1 => boolfield( 7 );
90              
91             =head2 read_config
92              
93             $config = await $chip->read_config;
94              
95             Returns a C reference of the contents of the user register.
96              
97             RES => "12/14" | "11/11" | "10/13" | "8/12"
98             OTPDISABLE => 0 | 1
99             HEATER => 0 | 1
100             ENDOFBATT => 0 | 1
101              
102             =head2 change_config
103              
104             await $chip->change_config( %changes );
105              
106             Writes updates to the user register.
107              
108             =cut
109              
110             my @RES_VALUES = ( "12/14", "8/12", "10/13", "11/11" );
111              
112 3         4 async method read_config ()
  3         4  
113 3         7 {
114 3         11 my %config = unpack_REG_USER(
115             await $self->protocol->write_then_read( pack( "C", CMD_READ_REG ), 1 )
116             );
117              
118 3         10330 my $res = ( delete $config{RES0} ) | ( delete $config{RES1} ) << 1;
119 3         6 $config{RES} = $RES_VALUES[$res];
120              
121 3         13 return \%config;
122 3     3 1 275 }
123              
124 2         4 async method change_config ( %changes )
  2         4  
  2         3  
125 2         5 {
126 2         4 my $config = await $self->read_config;
127              
128 2         127 $config->{$_} = $changes{$_} for keys %changes;
129              
130 2         5 my $res = delete $config->{RES};
131              
132 3     3   7 $res = first { $RES_VALUES[$_] eq $res } 0 .. 3;
  2         11  
133 2 50       9 defined $res or
134             croak "Unrecognised new value for RES - '$changes{RES}'";
135              
136 2         10 my $val = pack_REG_USER(
137             RES0 => $res & ( 1<<0 ),
138             RES1 => $res & ( 1<<1 ),
139             %$config,
140             );
141              
142 2         187 await $self->protocol->write( pack "C a", CMD_WRITE_REG, $val );
143 2     2 1 8891 }
144              
145             field $_mutex;
146              
147 7         10 async method _trigger_nohold ( $cmd )
  7         9  
  7         8  
148 7         12 {
149 7         23 my $protocol = $self->protocol;
150              
151 7   66     53 $_mutex //= Future::Mutex->new;
152              
153 7     7   715 return await $_mutex->enter( async sub {
154 7         18 await $self->protocol->write( pack "C", $cmd );
155              
156 7         20039 my $attempts = 10;
157 7         22 while( $attempts ) {
158 8         1199 my $f = $protocol->read( 2 );
159 8 50       12240 $attempts-- and $f = $f->else_done( undef );
160              
161 8         627 my $bytes = await $f;
162             # TODO: Is this a tinyUSSB bug? We're successfully reading two 0xFF
163             # bytes before it's ready
164 8 100 66     1355 defined $bytes and $bytes ne "\xFF\xFF" and
165             return unpack "S>", $bytes;
166              
167 1         6 await $protocol->sleep( 0.01 );
168             }
169 7         91 });
170 7     7   12 }
171              
172             =head1 METHODS
173              
174             =cut
175              
176             =head2 read_temperature
177              
178             $temperature = await $chip->read_temperature;
179              
180             Triggers a reading of the temperature sensor, returning a number in degrees C.
181              
182             =cut
183              
184             declare_sensor temperature =>
185             units => "°C",
186             precision => 2;
187              
188 4         7 async method read_temperature ()
  4         6  
189 4         10 {
190 4         13 my $val = await $self->_trigger_nohold( CMD_TRIGGER_TEMP_NOHOLD );
191              
192 4         1608 return -46.85 + 175.72 * ( $val / 2**16 );
193 4     4 1 10070 }
194              
195             =head2 read_humidity
196              
197             $humidity = await $chip->read_humidity;
198              
199             Triggers a reading of the humidity sensor, returning a number in % RH.
200              
201             =cut
202              
203             declare_sensor humidity =>
204             units => "%RH",
205             precision => 1;
206              
207 3         6 async method read_humidity ()
  3         5  
208 3         10 {
209 3         8 my $val = await $self->_trigger_nohold( CMD_TRIGGER_HUMID_NOHOLD );
210              
211 3         452 return -6 + 125 * ( $val / 2**16 );
212 3     3 1 10460 }
213              
214             =head1 AUTHOR
215              
216             Paul Evans
217              
218             =cut
219              
220             0x55AA;