File Coverage

blib/lib/Device/Chip/CCS811.pm
Criterion Covered Total %
statement 69 75 92.0
branch 1 2 50.0
condition 4 6 66.6
subroutine 18 19 94.7
pod 6 8 75.0
total 98 110 89.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, 2021-2023 -- leonerd@leonerd.org.uk
5              
6 6     6   1330167 use v5.26;
  6         58  
7 6     6   29 use warnings;
  6         11  
  6         163  
8 6     6   537 use Object::Pad 0.800;
  6         8915  
  6         253  
9              
10             package Device::Chip::CCS811 0.03;
11             class Device::Chip::CCS811
12 6     6   3184 :isa(Device::Chip::Base::RegisteredI2C);
  6         32108  
  6         194  
13              
14 6     6   833 use Future::AsyncAwait;
  6         10  
  6         22  
15              
16 6     6   2676 use Data::Bitfield 0.02 qw( bitfield boolfield enumfield );
  6         11094  
  6         433  
17              
18 6     6   2314 use Device::Chip::Sensor -declare;
  6         15257  
  6         26  
19              
20 6     6   637 use utf8;
  6         11  
  6         29  
21              
22             =encoding UTF-8
23              
24             =head1 NAME
25              
26             C - chip driver for F
27              
28             =head1 SYNOPSIS
29              
30             use Device::Chip::CCS811;
31             use Future::AsyncAwait;
32              
33             my $chip = Device::Chip::CCS811->new;
34             await $chip->mount( Device::Chip::Adapter::...->new );
35              
36             await $chip->init;
37              
38             await $chip->change_config( DRIVE_MODE => 1 );
39              
40             sleep 60; # wait for chip to warm up
41              
42             my ( $eCO2, $eTVOC ) = await $chip->read_alg_result_data;
43              
44             printf "eCO2=%dppm\n", $eCO2;
45             printf "eTVOC=%dppb\n", $eTVOC;
46              
47             =head1 DESCRIPTION
48              
49             This L subclass provides specific communication to a
50             F F Digital Gas Sensor attached to a computer via an I²C
51             adapter.
52              
53             The reader is presumed to be familiar with the general operation of this chip;
54             the documentation here will not attempt to explain or define chip-specific
55             concepts or features, only the use of this module to access them.
56              
57             =cut
58              
59             method I2C_options
60 5     5 0 1807 {
61             return (
62 5         26 addr => 0x5A,
63             max_bitrate => 400E3,
64             );
65             }
66              
67             use constant {
68 6         11402 REG_STATUS => 0x00,
69             REG_MEAS_MODE => 0x01, # app
70             REG_ALG_RESULT_DATA => 0x02, # app
71             REG_RAW_DATA => 0x03, # app
72             REG_ENV_DATA => 0x05, # app
73             REG_THRESHOLDS => 0x10, # app
74             REG_BASELINE => 0x11, # app
75             REG_HW_ID => 0x20,
76             REG_HW_VERSION => 0x21,
77             REG_FW_BOOT_VERSION => 0x23,
78             REG_FW_APP_VERSION => 0x24,
79             REG_INTERNAL_STATE => 0xA0, # app
80             REG_ERROR_ID => 0xE0,
81             REG_APP_ERASE => 0xF1, # boot
82             REG_APP_DATA => 0xF2, # boot
83             REG_APP_VERIFY => 0xF3, # boot
84             REG_APP_START => 0xF4, # boot
85             REG_SW_RESET => 0xFF,
86 6     6   936 };
  6         11  
87              
88             =head1 METHODS
89              
90             The following methods documented in an C expression return L
91             instances.
92              
93             =cut
94              
95             =head2 read_status
96              
97             $status = await $chip->read_status;
98              
99             Reads the C register and returns a hash reference containing the
100             following fields:
101              
102             FWMODE => "boot" | "app"
103             APP_ERASE => 0 | 1
104             APP_VERIFY => 0 | 1
105             APP_VALID => 0 | 1
106             DATA_READY => 0 | 1
107             ERROR => 0 | 1
108              
109             =cut
110              
111             bitfield { format => "bytes-LE" }, REG_STATUS =>
112             FWMODE => enumfield( 7, qw( boot app )),
113             APP_ERASE => boolfield( 6 ),
114             APP_VERIFY => boolfield( 5 ),
115             APP_VALID => boolfield( 4 ),
116             DATA_READY => boolfield( 3 ),
117             ERROR => boolfield( 0 );
118              
119 3         4 async method read_status ()
  3         4  
120 3         7 {
121             # Not cached
122 3         13 return { unpack_REG_STATUS( await $self->read_reg( REG_STATUS, 1 ) ) };
123 3     3 1 1539 }
124              
125             bitfield { format => "bytes-LE" }, MEAS_MODE =>
126             DRIVE_MODE => enumfield( 4, qw( 0 1 2 3 4 )),
127             INT_DATARDY => boolfield( 3 ),
128             INT_THRESH => boolfield( 2 );
129              
130             =head2 read_config
131              
132             $config = await $chip->read_config;
133              
134             Reads the C configuration register and reeturns a hash reference
135             containing the following fields:
136              
137             DRIVE_MODE => 0 | 1 | 2 | 3 | 4
138             INT_DATARDY => 0 | 1
139             INT_THRESH => 0 | 1
140              
141             =cut
142              
143             field $_configbyte;
144              
145 3         4 async method read_config ()
  3         4  
146 3         8 {
147 3   66     15 return { unpack_MEAS_MODE( $_configbyte //= await $self->read_reg( REG_MEAS_MODE, 1 ) ) };
148 3     3 1 1671 }
149              
150             =head2 change_config
151              
152             await $chip->change_config( %changes );
153              
154             Writes updates to the C configuration register.
155              
156             =cut
157              
158 1         1 async method change_config ( %changes )
  1         3  
  1         2  
159 1         4 {
160 1         3 my $config = await $self->read_config;
161              
162 1         101 $config->{$_} = $changes{$_} for keys %changes;
163              
164 1         6 await $self->write_reg( REG_MEAS_MODE, $_configbyte = pack_MEAS_MODE( %$config ) );
165 1     1 1 12049 }
166              
167             =head2 read_alg_result_data
168              
169             $data = await $chip->read_alg_result_data;
170              
171             Reads the C register and returns a hash reference containing
172             the following fields, in addition to the C fields.
173              
174             eCO2 => INT (in units of ppm)
175             eTVOC => INT (in unts of ppb)
176             ERROR_ID => INT
177              
178             =cut
179              
180 6         9 async method read_alg_result_data ()
  6         7  
181 6         9 {
182 6         32 my $bytes = await $self->read_reg( REG_ALG_RESULT_DATA, 6 );
183 5         17328 my ( $eCO2, $eTVOC, $status, $err ) = unpack "S> S> a1 C", $bytes;
184              
185             return {
186 5         24 eCO2 => $eCO2,
187             eTVOC => $eTVOC,
188             unpack_REG_STATUS( $status ),
189             ERROR_ID => $err,
190             }
191 6     6 1 279 }
192              
193             =head2 read_id
194              
195             $id = await $chip->read_id;
196              
197             Reads the C register and returns an integer. This should be the value
198             0x81 for the F chip.
199              
200             =cut
201              
202 1         2 async method read_id ()
  1         2  
203 1         3 {
204 1         15 return unpack "C", await $self->read_reg( REG_HW_ID, 1 );
205 1     1 1 259 }
206              
207             =head2 init
208              
209             await $chip->init;
210              
211             Performs the chip startup actions; namely, starting the application if the
212             chip is still in bootloader mode.
213              
214             =cut
215              
216 1         3 async method init ()
  1         1  
217 1         4 {
218 1 50       5 if( ( await $self->read_status )->{FWMODE} ne "app" ) {
219 1         1556 await $self->write_reg( REG_APP_START, "" );
220             }
221 1     1 1 13376 }
222              
223             field $_pending_read_f;
224              
225 8         10 method _next_read ()
  8         12  
226 8     8   20 {
227             return $_pending_read_f //=
228 8   66 5   30 $self->read_alg_result_data->on_ready(sub { undef $_pending_read_f });
  5         800  
229             }
230              
231             declare_sensor eCO2 =>
232             units => "ppm",
233             precision => 0,
234             method => async method () { ( await $self->_next_read )->{eCO2} };
235              
236             declare_sensor eTVOC =>
237             units => "ppb",
238             precision => 0,
239             method => async method () { ( await $self->_next_read )->{eTVOC} };
240              
241 0           async method initialize_sensors ()
  0            
242 0           {
243 0           await $self->init;
244              
245 0           await $self->change_config( DRIVE_MODE => 1 );
246 0     0 0   }
247              
248             =head1 AUTHOR
249              
250             Paul Evans
251              
252             =cut
253              
254             0x55AA;