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?


Log in to reply
 

Pycom on Twitter

Looks like your connection to Pycom Forum was lost, please wait while we try to reconnect.