File Coverage

blib/lib/HiPi/Interface/MS5611.pm
Criterion Covered Total %
statement 21 108 19.4
branch 0 26 0.0
condition 0 6 0.0
subroutine 7 15 46.6
pod 0 4 0.0
total 28 159 17.6


line stmt bran cond sub pod time code
1             #########################################################################################
2             # Package HiPi::Interface::MS5611
3             # Description : Interface to MS5611_01BA03 barometric pressure sensor
4             # Copyright : Copyright (c) 2013-2017 Mark Dootson
5             # License : This is free software; you can redistribute it and/or modify it under
6             # the same terms as the Perl 5 programming language system itself.
7             #########################################################################################
8              
9             package HiPi::Interface::MS5611;
10              
11             #########################################################################################
12              
13 1     1   1035 use strict;
  1         2  
  1         29  
14 1     1   7 use warnings;
  1         2  
  1         29  
15 1     1   6 use parent qw( HiPi::Interface );
  1         2  
  1         5  
16 1     1   62 use HiPi qw( :i2c :rpi :ms5611);
  1         2  
  1         432  
17 1     1   11 use HiPi::RaspberryPi;
  1         2  
  1         7  
18 1     1   44 use Carp;
  1         9  
  1         108  
19              
20             our $VERSION ='0.81';
21              
22             __PACKAGE__->create_accessors( qw( backend crc) );
23              
24             use constant {
25 1         1271 CMD_RESET => 0x1E,
26             CMD_ADC_READ => 0x00, # // ADC read command
27             CMD_ADC_CONV => 0x40, # // ADC conversion command
28             CMD_ADC_D1 => 0x00, # // ADC D1 conversion
29             CMD_ADC_D2 => 0x10, # // ADC D2 conversion
30             CMD_PROM_RD => 0xA0, # // Prom read command
31 1     1   8 };
  1         1  
32              
33             sub new {
34 0     0 0   my ($class, %userparams) = @_;
35 0           my $pi = HiPi::RaspberryPi->new();
36            
37 0 0         my %params = (
38             devicename => ( $pi->board_type == RPI_BOARD_TYPE_1 ) ? '/dev/i2c-0' : '/dev/i2c-1',
39             address => 0x76,
40             device => undef,
41             backend => 'i2c',
42             );
43            
44             # get user params
45 0           foreach my $key( keys (%userparams) ) {
46 0           $params{$key} = $userparams{$key};
47             }
48            
49 0 0         if( $params{busmode} ) {
50 0           $params{backend} = $params{busmode};
51             }
52            
53 0 0         unless( defined($params{device}) ) {
54 0 0         if ( $params{backend} eq 'bcm2835' ) {
55 0           require HiPi::BCM2835::I2C;
56             $params{device} = HiPi::BCM2835::I2C->new(
57             address => $params{address},
58 0 0         peripheral => ( $params{devicename} eq '/dev/i2c-0' ) ? HiPi::BCM2835::I2C::BB_I2C_PERI_0() : HiPi::BCM2835::I2C::BB_I2C_PERI_1(),
59             );
60             } else {
61 0           require HiPi::Device::I2C;
62             $params{device} = HiPi::Device::I2C->new(
63             devicename => $params{devicename},
64             address => $params{address},
65 0           busmode => 'i2c', # don't smbus
66             );
67             }
68             }
69            
70 0           my $self = $class->SUPER::new(%params);
71            
72 0           $self->_init;
73            
74 0           return $self;
75             }
76              
77             sub reset {
78 0     0 0   my $self = shift;
79 0           $self->device->bus_write( CMD_RESET );
80 0           $self->delay( 3 );
81 0           return;
82             }
83              
84             sub _read_prom {
85 0     0     my($self, $coefnum) = @_;
86 0           $self->device->bus_write( CMD_PROM_RD + $coefnum * 2 );
87 0           my @ret = $self->device->bus_read( undef, 2);
88 0           my $prom = $ret[0] * 256 + $ret[1];
89 0           return $prom;
90             }
91              
92             sub _crc4 {
93 0     0     my( $self ) = @_;
94            
95             # int cnt; // simple counter
96             # unsigned int n_rem; // crc reminder
97             # unsigned int crc_read; // original value of the crc
98             # unsigned char n_bit;
99            
100 0           my $n_rem = 0x00;
101 0           my $crc_read = $self->crc->[7];
102 0           $self->crc->[7] = 0;
103 0           for (my $cnt = 0; $cnt < 16; $cnt ++) { #// operation is performed on bytes
104             # // choose LSB or MSB
105 0 0         if ( $cnt % 2 ==1 ) {
106 0           $n_rem ^= (($self->crc->[$cnt>>1]) & 0x00FF);
107             } else {
108 0           $n_rem ^= ( $self->crc->[$cnt>>1] >> 8);
109             }
110 0           for (my $n_bit = 8; $n_bit > 0; $n_bit--) {
111 0 0         if ($n_rem & 0x8000) {
112 0           $n_rem = ($n_rem << 1) ^ 0x3000;
113             } else {
114 0           $n_rem = $n_rem << 1;
115             }
116             }
117             }
118 0           $n_rem= (0x000F & ($n_rem >> 12)); #// // final 4-bit remainder is CRC code
119 0           $self->crc->[7] = $crc_read; # // restore the crc_read to its original place
120 0           return $n_rem ^ 0x00;
121             }
122              
123             sub _init {
124 0     0     my $self = shift;
125            
126 0           my @promvals = ();
127            
128             # get callibration coeffs
129 0           for ( my $i = 0; $i < 8 ; $i++ ) {
130 0           my $promval = $self->_read_prom( $i );
131 0           push @promvals, $promval;
132             }
133            
134 0           $self->crc( \@promvals );
135 0           my $n_crc = $self->_crc4( @promvals );
136             # is the crc check worth it ?????
137             }
138              
139             sub _adc_cmd {
140 0     0     my( $self, $cmd ) = @_;
141 0           $self->device->bus_write( CMD_ADC_CONV + $cmd );
142            
143 0           my $osr = $cmd & 0x0F;
144 0 0         if( $osr == MS5611_OSR_256 ) {
    0          
    0          
    0          
145 0           $self->delay(1);
146             } elsif($osr == MS5611_OSR_512 ) {
147 0           $self->delay(3);
148             } elsif($osr == MS5611_OSR_1024 ) {
149 0           $self->delay(4);
150             } elsif($osr == MS5611_OSR_2048 ) {
151 0           $self->delay(6);
152             } else {
153 0           $self->delay(10);
154             }
155            
156 0           $self->device->bus_write( CMD_ADC_READ );
157            
158 0           my @ret = $self->device->bus_read( undef, 3);
159            
160 0           my $result = ($ret[0] * 65536 ) + ($ret[1] * 256 ) + $ret[2];
161 0           return $result;
162             }
163              
164             sub read_pressure_temp {
165 0     0 0   my($self, $pres_osr, $temp_osr ) = @_;
166 0   0       $pres_osr //= MS5611_OSR_4096;
167 0   0       $temp_osr //= MS5611_OSR_4096;
168            
169 0           my $D2 = $self->_adc_cmd( CMD_ADC_D2 + $temp_osr);
170 0           my $D1 = $self->_adc_cmd( CMD_ADC_D1 + $pres_osr);
171            
172 0           my $dT = $D2 - $self->crc->[5] * (2**8);
173            
174 0           my $OFF = $self->crc->[2] * (2**16) + $dT * $self->crc->[4] / (2**7);
175 0           my $SENS = $self->crc->[1] * (2**15) + $dT * $self->crc->[3] / (2**8);
176              
177 0           my $TEMP = 2000 + ( $dT * $self->crc->[6]) / ( 2**23 );
178            
179 0 0         if( $TEMP < 2000 ) {
180 0           my $T2 = ($dT**2) / (2**31);
181 0           my $OFF2 = 5 * ($TEMP - 2000)**2 / 2;
182 0           my $SENS2 = 5 * ($TEMP - 2000)**2 / 2**2;
183 0 0         if( $TEMP < -1500 ) {
184 0           $OFF2 = $OFF2 + 7 * ( $TEMP + 1500 )**2;
185 0           $SENS2 = $SENS2 + 11 * ($TEMP + 1500)**2 / 2;
186             }
187 0           $TEMP -= $T2;
188 0           $SENS -= $SENS2;
189 0           $OFF -= $OFF2;
190             }
191            
192 0           my $P = ( ($D1 * $SENS) / (2**21) - $OFF ) / ( 2**15 );
193            
194 0           return ( $P / 100, $TEMP / 100 );
195             }
196              
197             sub sea_level_pressure {
198 0     0 0   my( $class, $pressure, $altitude, $temperature, $gravity) = @_;
199 0   0       $gravity ||= 9.81; # acceleration due to gravity
200 0           my $dgc = 287.0; # dry gas constant
201            
202             # Po = ((P * 1000) * Math.exp((g*Zg)/(Rd * (Tv_avg + 273.15))))/1000;
203            
204 0           my $result = (($pressure * 1000) * exp(($gravity * $altitude)/($dgc * ($temperature + 273.15))))/1000;
205            
206 0           $result = sprintf("%.2f", $result);
207 0           return $result;
208             }
209              
210             1;
211              
212             __END__