Some Verto water meter collector units use Lora communication at 433 MHz ISM band.

Arduino Uno
Dragino Lora shield (Semtech SX1278)

 

arduino-lora-rx-433.jpg

 

arduino-lora-rx-433-2.jpg

 

arduino-lora-rx-433-3.jpg

 

#include <SPI.h>
 
#define SERIAL_SPEED        115200
 
#define LORA_FREQ           433.300
#define STATE_SLEEP         0
#define STATE_STANDBY       1
#define STATE_FSRX          4
#define STATE_RX            5
#define S_DELAY             10
#define L_DELAY             100
#define RX_BUFFER_LEN       255
#define SX_FREQ             "Frequency "
#define SX_VERSION          "Chip version "
#define PRINT_MODEM_STAT
#undef PRINT_MODEM_STAT
 
#define RegFifo               0x00
#define RegOpMode             0x01
#define RegFrMsb              0x06
#define RegFrMid              0x07
#define RegFrLsb              0x08
#define RegFifoRxCurrentAddr  0x10
#define RegFifoAddrPtr        0x0D
#define RegIrqFlagsMask       0x11
#define RegIrqFlags           0x12
#define RegRxNbBytes          0x13
#define RegModemStat          0x18
#define RegPktSnrValue        0x19
#define RegPktRssiValue       0x1A
#define RegRssiValue          0x1B
#define RegHopChannel         0x1C
#define RegModemConfig1       0x1D
#define RegModemConfig2       0x1E
#define RegVersion            0x42
 
#define SX_OSC_FREQ         32000000
#define SX_RESET            9
#define SX_RESET_DELAY_H    5
#define SX_RESET_DELAY_L    100
#define SPI_SS              10
#define SPI_RW_DELAY        50
 
uint8_t modemStat;
uint8_t rxBuffer[RX_BUFFER_LEN];
 
void setup() {
  Serial.begin(SERIAL_SPEED);
  if (initSX()) {
    memset(rxBuffer, 0, RX_BUFFER_LEN);
    uint8_t r = readSPI(RegVersion);
    Serial.print(F(SX_VERSION));
    char strVer[4];
    snprintf(strVer, 4, "%1u.%1u", r >> 4 , r & 0x0F);
    Serial.println(strVer);
    Serial.println();
    startLora();
 
    setFreq(LORA_FREQ);
    Serial.print(F(SX_FREQ));
    Serial.println(LORA_FREQ, DEC);
 
// Set SF12, CR 4/5, explicit header mode
    r = readSPI(RegModemConfig2);
    r = r & B11000000;
    writeSPI(RegModemConfig2, r);
// Set BW
    r = readSPI(RegModemConfig1);
    // 62.5 kHz
    //bitClear(r, 7); bitSet(r, 6); bitSet(r, 5); bitClear(r, 4);
    // 125 kHz
    bitClear(r, 7); bitSet(r, 6); bitSet(r, 5); bitSet(r, 4);
    // 250 kHz
    //bitSet(r, 7); bitClear(r, 6); bitClear(r, 5); bitClear(r, 4);
    // 500 kHz
    //bitSet(r, 7); bitClear(r, 6); bitClear(r, 5); bitSet(r, 4);
    writeSPI(RegModemConfig1, r);
   
    setState(STATE_FSRX);
    delay(L_DELAY);
    setState(STATE_RX);
    delay(S_DELAY);
 
#ifdef PRINT_MODEM_STAT
    modemStat = readSPI(RegModemStat);
    Serial.println();
    Serial.print(F("Modem status: "));
    Serial.print("\t");
    Serial.print(modemStat, BIN);
    Serial.println();
#endif
  }
}
 
void loop() {
  uint8_t r;
#ifdef PRINT_MODEM_STAT
  r = readSPI(RegModemStat);
  if (r != modemStat) {
    modemStat = r;
    Serial.println();
    Serial.print(F("Modem status: "));
    Serial.print("\t");
    Serial.print(r, BIN);
    Serial.println();
  }
#endif
  if (getRxState() == 1) {
    r = readFifo(rxBuffer, RX_BUFFER_LEN);
    if (r > 0) {
      Serial.println();
      for (uint8_t i = 0; i < r; i++) {
        Serial.print(rxBuffer[i], HEX);
      }
      Serial.println();
    }
    clearLoraIrqFlags();
    memset(rxBuffer, 0, RX_BUFFER_LEN);
    Serial.println(lastPktSnrValue(), DEC);
  }
}
 
void setFreq(float f)
{
  uint32_t lf = (f * 1000000) / 61.035;
  writeSPI(RegFrMsb, (lf >> 16) & 0xff);
  writeSPI(RegFrMid, (lf >> 8) & 0xff);
  writeSPI(RegFrLsb, lf & 0xff);
}
 
void setState(uint8_t s) {
  uint8_t b = readSPI(RegOpMode);
  bitClear(b, 2);
  bitClear(b, 1);
  bitClear(b, 0);
  switch (s) {
    case STATE_SLEEP:
    {
      break;
    }
    case STATE_STANDBY:
    {
      bitSet(b, 0);
      break;
    }
    case STATE_FSRX:
    {
      bitSet(b, 2);
      break;
    }
    case STATE_RX:
    {
      bitSet(b, 2);
      bitSet(b, 0);
      break;
    }
  }
  writeSPI(RegOpMode, b);
}
 
void startLora()
{
  setState(STATE_SLEEP);
  uint8_t b = readSPI(RegOpMode);
  bitSet(b, 7);
  writeSPI(RegOpMode, b);
}
 
uint8_t getLoraIrqFlag(uint8_t v) {
  return bitRead(readSPI(RegIrqFlags), v);
}
 
void clearLoraIrqFlag(uint8_t v) {
  uint8_t b = readSPI(RegIrqFlags);
  bitSet(b, v);
  writeSPI(RegIrqFlags, b);
}
 
void clearLoraIrqFlags() {
  writeSPI(RegIrqFlags, 0xFF);
}
 
int16_t pktRssiValue() {
  return (int16_t) (-164 + readSPI(RegPktRssiValue));
}
 
int16_t rssiValue() {
  return (int16_t) (-164 + readSPI(RegRssiValue));
}
 
uint8_t lastPktSnrValue()
{
  return (readSPI(RegPktSnrValue) >> 2);
}
 
int8_t getRxState() {
  uint8_t b = readSPI(RegIrqFlags);
  if (bitRead(b, 6)) {
    return 1; // packet rx
  }
  if (bitRead(b, 7)) {
    return -1; // timeout
  }
  return 0;
}
 
void discardFifo()
{
  uint8_t b = readSPI(RegFifoRxCurrentAddr);
  writeSPI(RegFifoAddrPtr, b);  
  uint8_t n = readSPI(RegRxNbBytes);
  writeSPI(RegFifoAddrPtr, 0);
  for (b = 0; b < n; b++) {
    readSPI(RegFifo);
  }
}
 
uint8_t readFifo(uint8_t buf[], uint8_t buf_len)
{
  uint8_t l = 0;
  writeSPI(RegFifoAddrPtr, readSPI(RegFifoRxCurrentAddr));
  uint8_t n = readSPI(RegRxNbBytes);
  if (buf_len < n) {
    l = buf_len;
  }
  else {
    l = n;
  }
  uint8_t i;
  for(i = 0; i < l; i++) {
    buf[i] = readSPI(RegFifo);
  }
  uint8_t cnt = n - buf_len;
  if (cnt > 0) {
    for (i = 0; i < cnt; i++) {
      readSPI(RegFifo);
    }
  }
  return l;
}
 
bool initSX() {
  pinMode(SX_RESET, OUTPUT);
  resetSX();
  initSPI();
  if (readSPI(RegVersion) == 0) {
    return false;
  }
  setState(STATE_STANDBY);
  return true;
}
 
void resetSX() {
  digitalWrite(SX_RESET, LOW);
  delayMicroseconds(SX_RESET_DELAY_L);
  digitalWrite(SX_RESET, HIGH);
  delay(SX_RESET_DELAY_H);
}
 
uint8_t readSPI(uint8_t addr) {
  digitalWrite(SPI_SS, LOW);
  SPI.transfer(addr);
  delayMicroseconds(SPI_RW_DELAY);
  uint8_t v = SPI.transfer(0x00);
  digitalWrite(SPI_SS, HIGH);
  return v;
}
 
void writeSPI(uint8_t addr, uint8_t v) {
  digitalWrite(SPI_SS, LOW);
  SPI.transfer(addr | 0x80);
  delayMicroseconds(SPI_RW_DELAY);
  SPI.transfer(v);
  digitalWrite(SPI_SS, HIGH);
}
 
void initSPI()
{
  digitalWrite(SPI_SS, HIGH);
  pinMode(SPI_SS, OUTPUT);
  SPI.begin();
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);
}
 
// Parse LoRa class B device beacon (RX2) downlink message
// https://www.ncbi.nlm.nih.gov/pmc/articles/PMC5038744/
void parseLoraMsg(uint8_t rxPacket[]) {
    uint16_t i;
    Serial.println(F("PHYPayload MHDR:"));
    Serial.print(F("MType: "));
    Serial.print((rxPacket[0] & B11100000) >> 5);
    Serial.print("\t");
    Serial.print(F("RFU: "));
    Serial.print((rxPacket[0] & B00011100) >> 2);
    Serial.print("\t");
    Serial.print(F("Major: "));
    Serial.println(rxPacket[0] & B00000011);
    Serial.println(F("MACPayload FHDR: "));
    Serial.print(F("DevAddr: "));
    for (i = 1; i < 5; i++) {
      Serial.print(rxPacket[i]);
    }
    Serial.println("");
    Serial.print((rxPacket[1] & B11111110) >> 1);
    Serial.println("");
    Serial.print(F("FCtrl:\tADR: "));
    Serial.print((rxPacket[5] & B10000000) >> 7);
    Serial.print(F(" ADRAckReq: "));
    Serial.print((rxPacket[5] & B01000000) >> 6);
    Serial.print(F(" ACK: "));
    Serial.print((rxPacket[5] & B00100000) >> 5);
    Serial.print(F(" RFU: "));
    Serial.print((rxPacket[5] & B00010000) >> 4);
    Serial.print(F(" FOptsLen: "));
    uint8_t foptslen = rxPacket[5] & B00001111;
    Serial.println(foptslen);
    Serial.print(F("FCnt: "));
    Serial.print(rxPacket[6]);
    Serial.print(rxPacket[7]);
    if (foptslen > 0) {
      Serial.println("");
      Serial.print(F("FOpts: (MACCommand(s) byte 1: CID, bytes 2-4: Args) "));
      for (i = 8; i < (8 + foptslen); i++) {
        Serial.print(rxPacket[i]);
      }
      foptslen = i;
    }
    else {
      foptslen = 8;
    }
    Serial.println("");
    Serial.print(F("FPort: "));
    Serial.print(rxPacket[foptslen]);
}