Lopy and RFM95 communication



  • Hi,

    I am trying to communicate an Arduino Pro Mini with RFM95 module (acting as a transmiter) and a Lopy (acting as a receiver).

    Inspired by this thread I've come to these two pieces of code:

    1. Arduino
    #include <SPI.h>
    #include <RH_RF95.h>
    
    #define FREQUENCY  868.0
    
    struct tpayload {
       uint32_t deviceid;   //4
       byte seq;            //1
       byte indicator1;     //1
       byte indicator2;     //1
       byte stype;          //1
       long svalue;         //4
       byte check;          //1
    };
    
    tpayload payload;
    RH_RF95 rf95;    // Chip Select=D10, INT Pin = D2
    
    void setup() {
      Serial.begin(115200);
      if (!rf95.init()) {
         Serial.println("init failed");
      }
      else { 
        Serial.print("init OK - "); Serial.print(FREQUENCY); Serial.println("mhz"); 
      }
    
      payload.seq = 0;
      payload.deviceid = 8512;
      payload.indicator1 = 1;
      payload.indicator1 = 2;
      payload.stype = 2;
      payload.svalue = -10;
    
     
      rf95.setModemConfig(RH_RF95::Bw125Cr45Sf128);  
      rf95.setTxPower(23, false);
      rf95.setPreambleLength(8);
      rf95.setFrequency(868.0);
    }
    
    void loop() {
      payload.seq++;
      if (payload.seq == 0) payload.seq++;  // to avoid division by zero at the next line
      payload.check = payload.deviceid % payload.seq;  // you need this to discard corrupted messages
      uint16_t now = micros();
      rf95.send( (const uint8_t*)&payload, sizeof(payload));
      Serial.print("Sent ");
      Serial.println(payload.seq);
      delay(5000);
    }
    
    1. Lopy
    import struct
    import socket
    from network import LoRa
    import time
    import gc
    
    LORA_MSG_FORMAT = "LLBBBBlB" 
    
    
    class incoming_message:
        def __init__(self, data):
           self.control, self.deviceID, self.Seq, self.indicator1,  self.indicator2,  self.stype,  self.svalue,  self.check = struct.unpack(LORA_MSG_FORMAT, data)
           #self.control is something added from RFM95. Just discard it. Your message starts from the 5th byte
    
    class server:
        def __init__(self):
            cfg_mode = LoRa.LORA
            cfg_freq = 868000000                # accepts values between 863000000 and 870000000 in the 868 band
            cfg_bandwidth=LoRa.BW_125KHZ        # LoRa.BW_125KHZ or LoRa.BW_250KHZ
            cfg_sf = 7                          # Accepts values between 6 and 12. Ver Datasheet semtech SX1272
                                                #  6 =   64  Chips/Symbol
                                                #  7 =  128  Chips/Symbol
                                                #  8 =  256  Chips/Symbol
                                                #  9 =  512  Chips/Symbol
                                                # 10 = 1024  Chips/Symbol
                                                # 11 = 2048  Chips/Symbol
                                                # 12 = 4096  Chips/Symbol
                                                
            cfg_preamble=8                      #configures the number of pre-amble symbols. The default value is 8
            cfg_coding_rate=LoRa.CODING_4_5     #LoRa.CODING_4_5, LoRa.CODING_4_6, LoRa.CODING_4_7 or LoRa.CODING_4_8
            cfg_power_mode=LoRa.ALWAYS_ON       #LoRa.ALWAYS_ON, LoRa.TX_ONLY or LoRa.SLEEP
            
            #medium range: bandwidth=LoRa.BW_250KHZ,coding_rate=LoRa.CODING_4_5,sf=7
            #fast short range: bandwidth=LoRa.BW_500KHZ,coding_rate=LoRa.CODING_4_5,sf=7
            #slow long range: bandwidth=LoRa.BW_125KHZ,coding_rate=LoRa.CODING_4_8,sf=9
            #very slow long range: bandwidth=LoRa.BW_125KHZ,coding_rate=LoRa.CODING_4_8,sf=12
            
            self.lora = LoRa(mode=cfg_mode, 
                tx_power=14,  # It accepts between 2 and 14 for the 868 band
                frequency=cfg_freq, 
                bandwidth=cfg_bandwidth, 
                sf=cfg_sf, 
                preamble=cfg_preamble, 
                coding_rate=cfg_coding_rate,  
                rx_iq=True, 
                tx_iq=False, # disables TX IQ inversion
                power_mode=cfg_power_mode,
                public=False)
           
            
            self.lora.init(mode=cfg_mode,
              frequency=cfg_freq,
              tx_power=14,  # It accepts between 2 and 14 for the 868 band
              bandwidth=cfg_bandwidth,
              sf=cfg_sf,
              preamble=cfg_preamble,
              coding_rate=cfg_coding_rate,
              power_mode=cfg_power_mode,
              tx_iq=False, # disables TX IQ inversion
              rx_iq=True, # enables RX IQ inversion
              public=False)
              
            self.lora_sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
            self.lora_sock.setblocking(False)
            
        def update(self):
            raw_data = self.lora_sock.recv(61)
            
            if raw_data:
                try:
                    msg = incoming_message(raw_data)
                    if msg.deviceID % msg.Seq == msg.check:
                        print("msg received. devid:", msg.deviceID,  " seq:", msg.Seq)
                    else:
                        print("msg check failed")    
                except:
                    print("msg failed to parse")
            else:
     #           print("pass")
                pass
    
    def test_server():
        srv = server()
        cnt=0
        while True:
            cnt=cnt+1
            if (cnt%1000==0):
                print(cnt)
            srv.update()
            
            
    test_server()
        
    

    But, unfortunately, it is not working. I mean, Lopy is not apparently receiving anything. Any hints?

    The project will be sensor on the Arduino side that will send data periodically to the Lopy that will act as a gateway to my local network. But now I'm in the early stage, meaning trying to communicate both devices.

    Thanks a lot.



  • After some investigation, no results. I choose to investigate what are the register values the RFM95 has by default and then configure Lopy to align to those values.

    I mean, for the RH_RF95_REG_39_SYNC_WORD in Lopy library we have ...

    if (init_data->public) {
        Radio.Write(REG_LR_SYNCWORD, LORA_MAC_PUBLIC_SYNCWORD);
    } else {
        Radio.Write(REG_LR_SYNCWORD, LORA_MAC_PRIVATE_SYNCWORD);
    }
    

    So I printed what is the value of that register and found that the default value is 0x12 which corresponds to LORA_MAC_PRIVATE_SYNCWORD so in the Lopy Lora we must initialize with Public=False.

    For the IQ_Inversion we have:

    // In SX1272Send
        case MODEM_LORA:
            {
                if( SX1272.Settings.LoRa.TxIqInverted == true )
                {
                    SX1272Write( REG_LR_INVERTIQ, ( ( SX1272Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_ON ) );
                    SX1272Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON );
                }
                else
                {
                    SX1272Write( REG_LR_INVERTIQ, ( ( SX1272Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF ) );
                    SX1272Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF );
                }
    
    // In SX1272SetRx:
             
            case MODEM_LORA:
            {
                if( SX1272.Settings.LoRa.RxIqInverted == true )
                {
                    SX1272Write( REG_LR_INVERTIQ, ( ( SX1272Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_ON | RFLR_INVERTIQ_TX_OFF ) );
                    SX1272Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_ON );
                }
                else
                {
                    SX1272Write( REG_LR_INVERTIQ, ( ( SX1272Read( REG_LR_INVERTIQ ) & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF ) );
                    SX1272Write( REG_LR_INVERTIQ2, RFLR_INVERTIQ2_OFF );
                }
    

    And after getting the register values it turns out that Tx_invertIQ and Rx_InvertIQ should be set as False.

    This is the code for Arduino....

    #include <SPI.h>
    #include <RH_RF95.h>
    
    #define FREQUENCY  868.0
    
    #define REG_LR_INVERTIQ                             0x33
    #define REG_LR_INVERTIQ2                            0x3B
    #define RFLR_INVERTIQ2_ON                           0x19
    #define RFLR_INVERTIQ2_OFF                          0x1D
    // From sx1272Regs-LoRa.h
    #define RFLR_INVERTIQ_RX_ON                         0x40 // Bit 6 ON
    #define RFLR_INVERTIQ_RX_OFF                        0x00 // Bit 0 OFF
    #define RFLR_INVERTIQ_TX_MASK                       0xFE // Selects bit 0
    #define RFLR_INVERTIQ_RX_MASK                       0xBF // Selects bit 6
    #define RFLR_INVERTIQ_TX_OFF                        0x01 // Bit 0 ON
    #define RFLR_INVERTIQ_TX_ON                         0x00 // Bit 0 OFF
    
    
    struct tpayload {
       uint32_t deviceid;   //4
       byte seq;            //1
       byte indicator1;     //1
       byte indicator2;     //1
       byte stype;          //1
       long svalue;         //4
       byte check;          //1
    };
    
    tpayload payload;
    RH_RF95 rf95;    // Chip Select=D10, INT Pin = D2
    
    void setup() {
      Serial.begin(115200);
      if (!rf95.init()) {
         Serial.println("init failed");
      }
      else { 
        Serial.print("init OK - "); Serial.print(FREQUENCY); Serial.println("mhz"); 
      }
    
      payload.seq = 0;
      payload.deviceid = 8512;
      payload.indicator1 = 1;
      payload.indicator1 = 2;
      payload.stype = 2;
      payload.svalue = -10;
    
     
      rf95.setModemConfig(RH_RF95::Bw125Cr45Sf128);  
      rf95.setTxPower(23, false);
      rf95.setPreambleLength(8);
      rf95.setFrequency(868.0);
    
    
      // Prints 0x12 so LORA_MAC_PRIVATE_SYNCWORD=0x12 then Public=False
      Serial.print("REG_LR_SYNCWORD: ");
      Serial.println(rf95.spiRead(0x39),HEX);
      byte iq=rf95.spiRead(REG_LR_INVERTIQ);
      byte iq2=rf95.spiRead(REG_LR_INVERTIQ2);
      
      // TxIqInverted == false, RxIqInverted==false
      byte iq_tx=(( iq & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF );
      byte iq_rx=(( iq & RFLR_INVERTIQ_TX_MASK & RFLR_INVERTIQ_RX_MASK ) | RFLR_INVERTIQ_RX_OFF | RFLR_INVERTIQ_TX_OFF );
    
      // All values are 0x27, so TxIqInverted == false, RxIqInverted==false
      Serial.print("REG_LR_INVERTIQ: ");
      Serial.println(iq,HEX);
      Serial.println(iq_tx,HEX);
      Serial.println(iq_rx,HEX);
    
      // It prints 0x1D so it's RFLR_INVERTIQ2_OFF                          
      Serial.print("REG_LR_INVERTIQ2: ");
      Serial.println(iq2,HEX);
    
    }
    
    void loop() {
      payload.seq++;
      if (payload.seq == 0) payload.seq++;  // to avoid division by zero at the next line
      payload.check = payload.deviceid % payload.seq;  // you need this to discard corrupted messages
      uint16_t now = micros();
      rf95.send( (const uint8_t*)&payload, sizeof(payload));
      Serial.print("Sent ");
      Serial.println(payload.seq);
      delay(5000);
    }
    

    And this is for the Lopy....

    import struct
    import socket
    from network import LoRa
    import time
    import gc
    
    LORA_MSG_FORMAT = "LLBBBBlB" 
    
    
    class incoming_message:
        def __init__(self, data):
           self.control, self.deviceID, self.Seq, self.indicator1,  self.indicator2,  self.stype,  self.svalue,  self.check = struct.unpack(LORA_MSG_FORMAT, data)
           #self.control is something added from RFM95. Just discard it. Your message starts from the 5th byte
    
    class server:
        def __init__(self):
            cfg_mode = LoRa.LORA
            cfg_freq = 868000000                # accepts values between 863000000 and 870000000 in the 868 band
            cfg_bandwidth=LoRa.BW_125KHZ        # LoRa.BW_125KHZ or LoRa.BW_250KHZ
            cfg_sf = 7                          # Accepts values between 6 and 12. Ver Datasheet semtech SX1272
                                                #  6 =   64  Chips/Symbol
                                                #  7 =  128  Chips/Symbol
                                                #  8 =  256  Chips/Symbol
                                                #  9 =  512  Chips/Symbol
                                                # 10 = 1024  Chips/Symbol
                                                # 11 = 2048  Chips/Symbol
                                                # 12 = 4096  Chips/Symbol
                                                
            cfg_preamble=8                      #configures the number of pre-amble symbols. The default value is 8
            cfg_coding_rate=LoRa.CODING_4_5     #LoRa.CODING_4_5, LoRa.CODING_4_6, LoRa.CODING_4_7 or LoRa.CODING_4_8
            cfg_power_mode=LoRa.ALWAYS_ON       #LoRa.ALWAYS_ON, LoRa.TX_ONLY or LoRa.SLEEP
            
            #medium range: bandwidth=LoRa.BW_250KHZ,coding_rate=LoRa.CODING_4_5,sf=7
            #fast short range: bandwidth=LoRa.BW_500KHZ,coding_rate=LoRa.CODING_4_5,sf=7
            #slow long range: bandwidth=LoRa.BW_125KHZ,coding_rate=LoRa.CODING_4_8,sf=9
            #very slow long range: bandwidth=LoRa.BW_125KHZ,coding_rate=LoRa.CODING_4_8,sf=12
            
            self.lora = LoRa(mode=cfg_mode, 
                tx_power=14,  # It accepts between 2 and 14 for the 868 band
                frequency=cfg_freq, 
                bandwidth=cfg_bandwidth, 
                sf=cfg_sf, 
                preamble=cfg_preamble, 
                coding_rate=cfg_coding_rate,  
                rx_iq=False, 
                tx_iq=False, 
                power_mode=cfg_power_mode,
                public=False)
           
            
            self.lora.init(mode=cfg_mode,
              frequency=cfg_freq,
              tx_power=14,  # It accepts between 2 and 14 for the 868 band
              bandwidth=cfg_bandwidth,
              sf=cfg_sf,
              preamble=cfg_preamble,
              coding_rate=cfg_coding_rate,
              power_mode=cfg_power_mode,
              tx_iq=False, 
              rx_iq=False, 
              public=False)
              
            self.lora_sock = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
            self.lora_sock.setblocking(False)
            
        def update(self):
            raw_data = self.lora_sock.recv(61)
            
            if raw_data:
                try:
                    msg = incoming_message(raw_data)
                    if msg.deviceID % msg.Seq == msg.check:
                        print("msg received. devid:", msg.deviceID,  " seq:", msg.Seq)
                    else:
                        print("msg check failed")    
                except:
                    print("msg failed to parse")
            else:
     #           print("pass")
                pass
    
    def test_server():
        srv = server()
        cnt=0
        while True:
            cnt=cnt+1
            if (cnt%1000==0):
                print(cnt)
            srv.update()
            
            
    test_server()
        
    

    But, as I said earlier, no results. So it may be caused by other settings or "way to communicate".





  • OK. Thank you. I'll look into it. One more silly question: Where can I find the source code of the Lora library so that I can see details on the implementation?



  • @joseperez said in Lopy and RFM95 communication:

    I never tried this sort communication because the Radiohead library is very low level and even the "raw" LoRa on the LoPy is slightly more defined but at a minimum I'd expect you need to set the sync word to match your LoPy setting of public=(True|False) via register RH_RF95_REG_39_SYNC_WORD and also your rx_iq, tx_iq settings should match what's set via RH_RF95_REG_33_INVERT_IQ.

    I cannot implement/test this at the moment, but maybe you can look into these areas and find suitable values?


 

Hello World?

Pylife on Kickstarter - November 2018








Back Us On Kickstarter >

Pycom on Twitter