Receive Basic
The receive code worked reliably after a couple of attempts. Initially, I was setting the RxTimeout bit on the RegIrqFlags register after retrieving the message, rather than the RxDone bit, and then found I was setting the receive single rather than receive continuous bit of RegOpMode.
I did some basic stress testing with a number of Arduino devices running a slightly modified version of the LoRaSetSyncWord example and my C# code didn’t appear to be dropping messages.
//---------------------------------------------------------------------------------
// Copyright (c) August 2018, devMobile Software
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//---------------------------------------------------------------------------------
namespace devMobile.IoT.Rfm9x.ReceiveBasic
{
using System;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
using Windows.Devices.Spi;
using Windows.Devices.Gpio;
public sealed class Rfm9XDevice
{
private SpiDevice Rfm9XLoraModem = null;
private GpioPin ChipSelectGpioPin = null;
private const byte RegisterAddressReadMask = 0X7f;
private const byte RegisterAddressWriteMask = 0x80;
public Rfm9XDevice(byte chipSelectPin, byte resetPin)
{
SpiController spiController = SpiController.GetDefaultAsync().AsTask().GetAwaiter().GetResult();
var settings = new SpiConnectionSettings(0)
{
ClockFrequency = 500000,
Mode = SpiMode.Mode0,
};
// Chip select pin configuration
GpioController gpioController = GpioController.GetDefault();
ChipSelectGpioPin = gpioController.OpenPin(chipSelectPin);
ChipSelectGpioPin.SetDriveMode(GpioPinDriveMode.Output);
ChipSelectGpioPin.Write(GpioPinValue.High);
// Reset pin configuration to do factory reset
GpioPin resetGpioPin = gpioController.OpenPin(resetPin);
resetGpioPin.SetDriveMode(GpioPinDriveMode.Output);
resetGpioPin.Write(GpioPinValue.Low);
Task.Delay(10);
resetGpioPin.Write(GpioPinValue.High);
Task.Delay(10);
Rfm9XLoraModem = spiController.GetDevice(settings);
}
public Byte RegisterReadByte(byte address)
{
byte[] writeBuffer = new byte[] { address &= RegisterAddressReadMask };
byte[] readBuffer = new byte[1];
Debug.Assert(Rfm9XLoraModem != null);
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
Rfm9XLoraModem.Read(readBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
return readBuffer[0];
}
public ushort RegisterReadWord(byte address)
{
byte[] writeBuffer = new byte[] { address &= RegisterAddressReadMask };
byte[] readBuffer = new byte[2];
Debug.Assert(Rfm9XLoraModem != null);
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
Rfm9XLoraModem.Read(readBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
return (ushort)(readBuffer[1] + (readBuffer[0] << 8));
}
public byte[] RegisterRead(byte address, int length)
{
byte[] writeBuffer = new byte[] { address &= RegisterAddressReadMask };
byte[] readBuffer = new byte[length];
Debug.Assert(Rfm9XLoraModem != null);
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
Rfm9XLoraModem.Read(readBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
return readBuffer;
}
public void RegisterWriteByte(byte address, byte value)
{
byte[] writeBuffer = new byte[] { address |= RegisterAddressWriteMask, value };
Debug.Assert(Rfm9XLoraModem != null);
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
}
public void RegisterWriteWord(byte address, ushort value)
{
byte[] valueBytes = BitConverter.GetBytes(value);
byte[] writeBuffer = new byte[] { address |= RegisterAddressWriteMask, valueBytes[0], valueBytes[1] };
Debug.Assert(Rfm9XLoraModem != null);
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
}
public void RegisterWrite(byte address, [ReadOnlyArray()] byte[] bytes)
{
byte[] writeBuffer = new byte[1 + bytes.Length];
Debug.Assert(Rfm9XLoraModem != null);
Array.Copy(bytes, 0, writeBuffer, 1, bytes.Length);
writeBuffer[0] = address |= RegisterAddressWriteMask;
ChipSelectGpioPin.Write(GpioPinValue.Low);
Rfm9XLoraModem.Write(writeBuffer);
ChipSelectGpioPin.Write(GpioPinValue.High);
}
public void RegisterDump()
{
Debug.WriteLine("Register dump");
for (byte registerIndex = 0; registerIndex <= 0x42; registerIndex++)
{
byte registerValue = this.RegisterReadByte(registerIndex);
Debug.WriteLine("Register 0x{0:x2} - Value 0X{1:x2} - Bits {2}", registerIndex, registerValue, Convert.ToString(registerValue, 2).PadLeft(8, '0'));
}
}
}
public sealed class StartupTask : IBackgroundTask
{
private const int ChipSelectLine = 25;
private const int ResetLine = 17;
private Rfm9XDevice rfm9XDevice = new Rfm9XDevice(ChipSelectLine, ResetLine);
public void Run(IBackgroundTaskInstance taskInstance)
{
// Put device into LoRa + Sleep mode
rfm9XDevice.RegisterWriteByte(0x01, 0b10000000); // RegOpMode
// Set the frequency to 915MHz
byte[] frequencyWriteBytes = { 0xE4, 0xC0, 0x00 }; // RegFrMsb, RegFrMid, RegFrLsb
rfm9XDevice.RegisterWrite(0x06, frequencyWriteBytes);
rfm9XDevice.RegisterWriteByte(0x0F, 0x0); // RegFifoRxBaseAddress
rfm9XDevice.RegisterWriteByte(0x01, 0b10000101); // RegOpMode set LoRa & RxContinuous
while (true)
{
// Wait until a packet is received, no timeouts in PoC
Debug.WriteLine("Receive-Wait");
byte IrqFlags = rfm9XDevice.RegisterReadByte(0x12); // RegIrqFlags
while ((IrqFlags & 0b01000000) == 0) // wait until RxDone cleared
{
Task.Delay(20).Wait();
IrqFlags = rfm9XDevice.RegisterReadByte(0x12); // RegIrqFlags
Debug.Write(".");
}
Debug.WriteLine("");
Debug.WriteLine("Receive-Message");
byte currentFifoAddress = rfm9XDevice.RegisterReadByte(0x10); // RegFifiRxCurrent
rfm9XDevice.RegisterWriteByte( 0x0d, currentFifoAddress); // RegFifoAddrPtr
byte numberOfBytes = rfm9XDevice.RegisterReadByte(0x13); // RegRxNbBytes
// Allocate buffer for message
byte[] messageBytes = new byte[numberOfBytes];
for (int i = 0; I < numberOfBytes; i++)
{
messageBytes[i] = rfm9XDevice.RegisterReadByte(0x00); // RegFifo
}
string messageText = UTF8Encoding.UTF8.GetString(messageBytes);
Debug.WriteLine("Received {0} byte message {1}", messageBytes.Length, messageText);
rfm9XDevice.RegisterWriteByte(0x12, 0b01000000); // RegIrqFlags clear RxDone bit
Debug.WriteLine("Receive-Done");
}
}
}
}
With 3 client devices transmitting the debug output looked like this
Receive-Wait
…
Receive-Message
Received 16 byte message HeLoRa World! 14
Receive-Done
Receive-Wait
……………………………………………………
Receive-Message
Received 16 byte message HeLoRa World! 16
Receive-Done
Receive-Wait
………………………………………………………………………………….
Receive-Message
Received 16 byte message HeLoRa World! 18
Receive-Done
Receive-Wait
………………………………………………………………………….
Receive-Message
Received 16 byte message HeLoRa World! 20
Receive-Done
Receive-Wait
………………
Receive-Message
Received 16 byte message HelloRa World! 0
Receive-Done
Receive-Wait
………………………
Receive-Message
Received 16 byte message HeLoRa World! 22
Receive-Done
Receive-Wait
Most LoRa libraries include the Received Signal Strength Indication(RSSI) & Signal To noise ratio (SNR) information with the received packet. The RSSI needs to be “adjusted” by a constant depending on the frequency so that can wait until after configuration approach has been decided.
Transmitting/receiving with interrupts or design goals next.