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:
- 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); }
- 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.
-
Were you able to resolve this issue and achieve communication between the LoPy and the RF95?
-
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".
-
Hi @joseperez,
A good place to start would be here: https://github.com/pycom/pycom-micropython-sigfox/blob/master/esp32/mods/modlora.c
-
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?