File Coverage

blib/lib/HiPi/Interface/HopeRF69.pm
Criterion Covered Total %
statement 15 130 11.5
branch 0 40 0.0
condition 0 10 0.0
subroutine 5 21 23.8
pod 0 16 0.0
total 20 217 9.2


line stmt bran cond sub pod time code
1             #########################################################################################
2             # Package HiPi::Interface::HopeRF69
3             # Description : Control Hope RF69 Transceivers
4             # Copyright : Copyright (c) 2013-2020 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::HopeRF69;
10              
11             #########################################################################################
12              
13 1     1   7 use strict;
  1         2  
  1         30  
14 1     1   6 use warnings;
  1         2  
  1         28  
15 1     1   98 use parent qw( HiPi::Interface );
  1         4  
  1         5  
16 1     1   61 use Carp;
  1         2  
  1         66  
17 1     1   14 use HiPi qw( :rpi :spi :hrf69 );
  1         2  
  1         2321  
18              
19             __PACKAGE__->create_accessors( qw( devicename reset_gpio update_default_on_reset
20             fsk_config ook_config ook_repeat
21             high_power_module max_power_on ) );
22              
23             our $VERSION ='0.82';
24              
25             # Hope recommended updated reset defaults
26             my $reset_defaults = [
27             [ RF69_REG_LNA, 0x88 ],
28             [ RF69_REG_RXBW, 0x55 ],
29             [ RF69_REG_AFCBW, 0x8B ],
30             [ RF69_REG_DIOMAPPING2, 0x07 ],
31             [ RF69_REG_RSSITHRESH, 0xE4 ],
32             [ RF69_REG_SYNCVALUE1, 0x01 ],
33             [ RF69_REG_FIFOTHRESH, 0x8F ],
34             [ RF69_REG_TESTDAGC, 0x30 ],
35             ];
36              
37             sub new {
38 0     0 0   my( $class, %userparams ) = @_;
39            
40 0           my %params = (
41             devicename => '/dev/spidev0.1',
42             speed => 9600000, # 9.6 mhz
43             bitsperword => 8,
44             delay => 0,
45             device => undef,
46             reset_gpio => undef,
47             update_default_on_reset => 1,
48             ook_repeat => 15,
49             fsk_config => [
50             [ RF69_REG_REGDATAMODUL, 0x00 ], # modulation scheme FSK
51             [ RF69_REG_FDEVMSB, 0x01 ], # frequency deviation 5kHz 0x0052 -> 30kHz 0x01EC
52             [ RF69_REG_FDEVLSB, 0xEC ], # frequency deviation 5kHz 0x0052 -> 30kHz 0x01EC
53             [ RF69_REG_FRMSB, 0x6C ], # carrier freq -> 434.3MHz 0x6C9333
54             [ RF69_REG_FRMID, 0x93 ], # carrier freq -> 434.3MHz 0x6C9333
55             [ RF69_REG_FRLSB, 0x33 ], # carrier freq -> 434.3MHz 0x6C9333
56             [ RF69_REG_AFCCTRL, 0x00 ], # standard AFC routine
57             [ RF69_REG_PREAMBLEMSB, 0x00 ], # 3 byte preamble
58             [ RF69_REG_PREAMBLELSB, 0x03 ], # 3 byte preamble
59             [ RF69_REG_LNA, 0x08 ], # 200ohms, gain by AGC loop -> 50ohms
60             [ RF69_REG_RXBW, 0x43 ], # channel filter bandwidth 10kHz -> 60kHz page:26
61             [ RF69_REG_BITRATEMSB, 0x1A ], # 4800b/s
62             [ RF69_REG_BITRATELSB, 0x0B ], # 4800b/s
63             [ RF69_REG_SYNCCONFIG, 0x88 ], # Size of the Synch word = 2 (SyncSize + 1)
64             [ RF69_REG_SYNCVALUE1, 0x2D ], # 1st byte of Sync word
65             [ RF69_REG_SYNCVALUE2, 0xD4 ], # 2nd byte of Sync word
66             [ RF69_REG_PACKETCONFIG1, 0xA0 ], # Variable length, Manchester coding
67             [ RF69_REG_PAYLOADLEN, 0x42 ], # max Length in RX, not used in Tx
68             [ RF69_REG_NODEADDRESS, 0x06 ], # Node address used in address filtering ( not used in this config )
69             [ RF69_REG_FIFOTHRESH, 0x81 ], # Condition to start packet transmission: at least one byte in FIFO
70             [ RF69_REG_OPMODE, RF69_MASK_OPMODE_RX ], # Operating mode to Receive
71             ],
72            
73             ook_config => [
74             [ RF69_REG_REGDATAMODUL, 0x08 ], # modulation scheme OOK
75             [ RF69_REG_FDEVMSB, 0 ], # frequency deviation -> 0kHz
76             [ RF69_REG_FDEVLSB, 0 ], # frequency deviation -> 0kHz
77             [ RF69_REG_FRMSB, 0x6C ], # carrier freq -> 433.92MHz 0x6C7AE1
78             [ RF69_REG_FRMID, 0x7A ], # carrier freq -> 433.92MHz 0x6C7AE1
79             [ RF69_REG_FRLSB, 0xE1 ], # carrier freq -> 433.92MHz 0x6C7AE1
80             [ RF69_REG_RXBW, 0x41 ], # channel filter bandwidth 120kHz
81             [ RF69_REG_BITRATEMSB, 0x40 ], # 1938b/s
82             [ RF69_REG_BITRATELSB, 0x80 ], # 1938b/s
83             [ RF69_REG_PREAMBLEMSB, 0 ], # no preamble
84             [ RF69_REG_PREAMBLELSB, 0 ], # no preamble
85             [ RF69_REG_SYNCCONFIG, 0x98 ], # Size of the Synch word = 4 (SyncSize + 1)
86             [ RF69_REG_SYNCVALUE1, 0x80 ], # sync value 1
87             [ RF69_REG_SYNCVALUE2, 0 ], # sync value 2
88             [ RF69_REG_SYNCVALUE3, 0 ], # sync value 3
89             [ RF69_REG_SYNCVALUE4, 0 ], # sync value 4
90             [ RF69_REG_PACKETCONFIG1, 0 ], # Fixed length, no Manchester coding, OOK
91             [ RF69_REG_PAYLOADLEN, 13 + 8 * 17 ], # Fixed OOK Payload Length
92             [ RF69_REG_FIFOTHRESH, 0x1E ], # Condition to start packet transmission: wait for 30 bytes in FIFO
93             [ RF69_REG_OPMODE, RF69_MASK_OPMODE_TX ], # Transmitter mode
94             ],
95             );
96            
97 0           foreach my $key (sort keys(%userparams)) {
98 0           $params{$key} = $userparams{$key};
99             }
100            
101 0 0         unless( defined($params{device}) ) {
102            
103 0           require HiPi::Device::SPI;
104             $params{device} = HiPi::Device::SPI->new(
105             speed => $params{speed},
106             bitsperword => $params{bitsperword},
107             delay => $params{delay},
108             devicename => $params{devicename},
109 0           );
110             }
111            
112 0           my $self = $class->SUPER::new(%params);
113            
114             # setup defaults
115 0           $self->reset();
116            
117 0           $self->configure($self->fsk_config);
118            
119 0           return $self;
120             }
121              
122             sub configure {
123 0     0 0   my( $self, $config ) = @_;
124 0           for my $msgref ( @$config ) {
125 0           $self->write_register(@$msgref);
126             }
127            
128 0 0         if( $self->high_power_module ) {
129 0           $self->write_register( RF69_REG_OCP, RF69_VAL_OCP_OFF );
130 0           $self->write_register( RF69_REG_PALEVEL, ( $self->read_register(RF69_REG_PALEVEL) & 0x1F) | RF69_PALEVEL_PA1_ON | RF69_PALEVEL_PA2_ON );
131             }
132            
133 0           $self->wait_for(RF69_REG_IRQFLAGS1, RF69_MASK_MODEREADY, RF69_TRUE);
134             }
135              
136             sub change_mode {
137 0     0 0   my($self, $mode, $waitmask) = @_;
138 0   0       $waitmask //= RF69_MASK_MODEREADY;
139 0           $self->write_register(RF69_REG_OPMODE, $mode);
140 0 0 0       if( $self->high_power_module && $self->max_power_on ) {
141 0 0         if( $mode == RF69_MASK_OPMODE_RX ) {
    0          
142 0           $self->write_register( RF69_REG_TESTPA1, 0x55 );
143 0           $self->write_register( RF69_REG_TESTPA2, 0x70 );
144             } elsif( $mode == RF69_MASK_OPMODE_TX ) {
145 0           $self->write_register( RF69_REG_TESTPA1, 0x5D );
146 0           $self->write_register( RF69_REG_TESTPA2, 0x7C );
147             }
148             }
149 0           $self->wait_for(RF69_REG_IRQFLAGS1, $waitmask, RF69_TRUE);
150             }
151              
152             sub set_mode_receiver {
153 0     0 0   my $self = shift;
154 0           $self->change_mode(RF69_MASK_OPMODE_RX, RF69_MASK_MODEREADY );
155             }
156              
157             sub set_mode_transmitter {
158 0     0 0   my $self = shift;
159 0           $self->change_mode(RF69_MASK_OPMODE_TX, RF69_MASK_MODEREADY | RF69_MASK_TXREADY );
160             }
161              
162             sub write_register {
163 0     0 0   my( $self, @data ) = @_;
164             # address is first byte
165 0           $data[0] |= RF69_MASK_REG_WRITE;
166 0           $self->device->transfer_byte_array( @data );
167             }
168              
169             sub read_register {
170 0     0 0   my( $self, $addr, $numbytes ) = @_;
171 0   0       $numbytes ||= 1;
172 0           my @data = ( 0 ) x ( $numbytes + 1 );
173 0           $data[0] = $addr;
174 0           my ($retaddr, @rvals ) = $self->device->transfer_byte_array( @data );
175 0 0         return ( wantarray ) ? @rvals : $rvals[0];
176             }
177              
178 0     0 0   sub write_fifo { shift->write_register( 0x0, @_ ); }
179              
180             sub read_fifo {
181 0     0 0   my $self = shift;
182 0           my( $rval ) = $self->read_register( 0x0, 1 );
183 0           return $rval;
184             }
185              
186             sub clear_fifo {
187 0     0 0   my $self = shift;
188            
189 0           my $state = $self->read_register( RF69_REG_IRQFLAGS2 );
190            
191 0           while ($state & RF69_MASK_FIFONOTEMPTY) {
192 0           my $discard = $self->read_fifo;
193 0           $state = $self->read_register(RF69_REG_IRQFLAGS2);
194             }
195            
196 0           return;
197             }
198              
199             sub reset {
200 0     0 0   my $self = shift;
201 0           my $pin = $self->reset_gpio;
202 0 0         return unless defined($pin);
203 0           require HiPi::GPIO;
204 0           my $gpio = HiPi::GPIO->new;
205 0 0         $gpio->set_pin_mode( $pin, RPI_MODE_OUTPUT ) if( $gpio->get_pin_mode($pin) != RPI_MODE_OUTPUT );
206 0           $gpio->pin_write($pin, RPI_HIGH);
207 0           $self->delay( 100 ); # 0.1 secs
208 0           $gpio->pin_write($pin, RPI_LOW);
209            
210 0 0         if ($self->update_default_on_reset) {
211 0           $self->configure($reset_defaults);
212             }
213 0           return;
214             }
215              
216             sub wait_for {
217 0     0 0   my( $self, $addr, $mask, $true) = @_;
218 0           my $counter = 0;
219 0           my $maxcount = 4000000;
220 0           while ( $counter < $maxcount ) {
221 0           my $ret = $self->read_register( $addr );
222 0 0         last if( ( $ret & $mask ) == ( $true ? $mask : 0 ) );
    0          
223 0           $counter ++;
224             }
225 0 0         if ( $counter >= $maxcount ) {
226 0           croak qq(timeout inside wait loop with addr $addr);
227             }
228 0           return;
229             }
230              
231              
232             sub assert_register_value {
233 0     0 0   my($self, $addr, $mask, $true, $desc) = @_;
234 0           my $val = $self->read_register( $addr );
235 0 0         if ($true){
236 0 0         if (($val & $mask) != $mask) {
237 0           croak sprintf("ASSERTION FAILED: addr:%02x, expVal:%02x(mask:%02x) != val:%02x, desc: %s", $addr, $true, $mask, $val, $desc);
238             }
239             } else {
240 0 0         if (($val & $mask) != 0) {
241 0           croak sprintf("ASSERTION FAILED: addr:%02x, expVal:%02x(mask:%02x) != val:%02x, desc: %s", $addr, $true, $mask, $val, $desc);
242             }
243             }
244 0           return;
245             }
246              
247             sub send_message {
248 0     0 0   my($self, $bytes) = @_;
249              
250 0 0         return unless(scalar( @$bytes ));
251              
252 0           $self->set_mode_transmitter;
253             # write to fifo
254 0           $self->write_fifo( @$bytes );
255             # wait for Packet sent
256 0           $self->wait_for (RF69_REG_IRQFLAGS2, RF69_MASK_PACKETSENT, RF69_TRUE);
257             # assert that all bytes sent
258 0           $self->assert_register_value(RF69_REG_IRQFLAGS2, RF69_MASK_FIFONOTEMPTY | RF69_MASK_FIFOOVERRUN, RF69_FALSE, q(are all bytes sent?));
259             # set back to receive mode
260 0           $self->set_mode_receiver;
261            
262 0           return;
263             }
264              
265             sub send_ook_message {
266 0     0 0   my($self, $bytes, $repeat ) = @_;
267            
268 0 0         return unless scalar @$bytes;
269            
270 0   0       $repeat ||= $self->ook_repeat;
271 0 0         $repeat = 100 if $repeat > 100;
272 0 0         $repeat = 8 if $repeat < 8;
273            
274             # switch to OOK mode
275 0           $self->configure($self->ook_config);
276 0           $self->set_mode_transmitter();
277            
278             # wait for mode ready for transmit after config
279 0           $self->wait_for(RF69_REG_IRQFLAGS1, RF69_MASK_MODEREADY | RF69_MASK_TXREADY, RF69_TRUE);
280            
281             # send first without preamble
282 0           $self->write_fifo( @$bytes[4..15] );
283            
284             # repeated resend with sync bytes
285 0           for (my $i = 0; $i < $repeat; $i++) {
286             # wait while bytes in FIFO exceed FifoThreshold,
287 0           $self->wait_for(RF69_REG_IRQFLAGS2, RF69_MASK_FIFOLEVEL, RF69_FALSE);
288 0           $self->write_fifo( @$bytes );
289             }
290            
291             # wait for Packet sent
292 0           $self->wait_for (RF69_REG_IRQFLAGS2, RF69_MASK_PACKETSENT, RF69_TRUE);
293            
294             # assert that FIFO is empty and there were no overruns
295 0           $self->assert_register_value(RF69_REG_IRQFLAGS2, RF69_MASK_FIFONOTEMPTY | RF69_MASK_FIFOOVERRUN, RF69_FALSE, q(are all bytes sent?));
296            
297             # return to default mode
298 0           $self->configure($self->fsk_config);
299 0           $self->set_mode_receiver();
300            
301 0           return;
302             }
303              
304             sub receive_message {
305 0     0 0   my ( $self ) = @_;
306            
307 0           my $fifostate = $self->read_register( RF69_REG_IRQFLAGS2 );
308            
309 0 0         if ( ( $fifostate & RF69_MASK_PAYLOADRDY ) == RF69_MASK_PAYLOADRDY ) {
310 0           my @databuffer = ();
311 0           while ( $fifostate & RF69_MASK_FIFONOTEMPTY ) {
312 0           push @databuffer, $self->read_fifo;
313 0           $fifostate = $self->read_register(RF69_REG_IRQFLAGS2);
314             }
315            
316 0           return \@databuffer;
317             }
318            
319 0           return undef;
320             }
321              
322              
323             1;
324              
325             __END__