File Coverage

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