File Coverage

blib/lib/Device/Chip/CCS811.pm
Criterion Covered Total %
statement 66 72 91.6
branch 1 2 50.0
condition 4 6 66.6
subroutine 17 18 94.4
pod 6 8 75.0
total 94 106 88.6


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