.NET nanoFramework SX127X LoRa library with Interrupts

To test the nanoFramework transmit and receive with interrupts implementation I used three Dragino LoRa Shields, a Seeeduino V4.2 and a pair of Netduino 3 Wifi devices.

Seeeduino and nanoFramework

I started with transmit as I was confident my Netduino 3 Wifi & Seeeduino + Dragino LoRa Shields could receive messages.

Interrupt pin configuration
SX127X ReqIrqFlags options

The TransmitInterrupt application loads the message to be sent into the First In First Out(FIFO) buffer, RegDioMapping1 is set to interrupt onTxDone(PacketSent-00), then RegRegOpMode-Mode is set to Transmit. When the message has been sent InterruptGpioPin_ValueChanged is called, and the TxDone(0b00001000) flag is set in the RegIrqFlags register.

The ReceiveInterrupt application sets the RegDioMapping1 to interrupt on RxDone(PacketReady-00), then the RegRegOpMode-Mode is set to Receive(TX-101). When a message is received InterruptGpioPin_ValueChanged is called, with the RxDone(0b00001000) flag set in the RegIrqFlags register, and then the message is read from First In First Out(FIFO) buffer.

namespace devMobile.IoT.SX127x.ReceiveTransmitInterrupt
{
...
   public sealed class SX127XDevice
   {
...
      public SX127XDevice(int busId, int chipSelectLine, int interruptPin, int resetPin)
      {
         var settings = new SpiConnectionSettings(busId, chipSelectLine)
         {
            ClockFrequency = 1000000,
            Mode = SpiMode.Mode0,// From SemTech docs pg 80 CPOL=0, CPHA=0
            SharingMode = SpiSharingMode.Shared
         };

         SX127XTransceiver = new SpiDevice(settings);

         GpioController gpioController = new GpioController();


         // Factory reset pin configuration
         gpioController.OpenPin(resetPin, PinMode.Output);

         gpioController.Write(resetPin, PinValue.Low);
         Thread.Sleep(20);
         gpioController.Write(resetPin, PinValue.High);
         Thread.Sleep(20);

         // Interrupt pin for RX message & TX done notification 
         gpioController.OpenPin(interruptPin, PinMode.InputPullDown);

         gpioController.RegisterCallbackForPinValueChangedEvent(interruptPin, PinEventTypes.Rising, InterruptGpioPin_ValueChanged);
      }
...
   }

      private void InterruptGpioPin_ValueChanged(object sender, PinValueChangedEventArgs e)
      {
         byte irqFlags = this.ReadByte(0x12); // RegIrqFlags
         Debug.WriteLine($"RegIrqFlags 0X{irqFlags:x2}");

         if ((irqFlags & 0b01000000) == 0b01000000)  // RxDone 
         {
            Debug.WriteLine("Receive-Message");
            byte currentFifoAddress = this.ReadByte(0x10); // RegFifiRxCurrent
            this.WriteByte(0x0d, currentFifoAddress); // RegFifoAddrPtr

            byte numberOfBytes = this.ReadByte(0x13); // RegRxNbBytes

            // Allocate buffer for message
            byte[] messageBytes = this.ReadBytes(0X0, numberOfBytes);

            // Remove unprintable characters from messages
            for (int index = 0; index < messageBytes.Length; index++)
            {
               if ((messageBytes[index] < 0x20) || (messageBytes[index] > 0x7E))
               {
                  messageBytes[index] = 0x20;
               }
            }

            string messageText = UTF8Encoding.UTF8.GetString(messageBytes, 0, messageBytes.Length);
            Debug.WriteLine($"Received {messageBytes.Length} byte message {messageText}");
         }

         if ((irqFlags & 0b00001000) == 0b00001000)  // TxDone
         {
            this.WriteByte(0x01, 0b10000101); // RegOpMode set LoRa & RxContinuous
            Debug.WriteLine("Transmit-Done");
         }

         this.WriteByte(0x40, 0b00000000); // RegDioMapping1 0b00000000 DI0 RxReady & TxReady
         this.WriteByte(0x12, 0xff);// RegIrqFlags
      }

   public class Program
   {
...
   #if NETDUINO3_WIFI
      private const int SpiBusId = 2;
#endif
...

      public static void Main()
      {
         int SendCount = 0;
...
#if NETDUINO3_WIFI
         // Arduino D10->PB10
         int chipSelectLine = PinNumber('B', 10);
         // Arduino D9->PE5
         int resetPinNumber = PinNumber('E', 5);
         // Arduino D2 -PA3
         int interruptPinNumber = PinNumber('A', 3);
#endif
...
  
       Debug.WriteLine("devMobile.IoT.SX127x.ReceiveTransmitInterrupt starting");

         try
         {
...
#if NETDUINO3_WIFI || ST_STM32F769I_DISCOVERY
            SX127XDevice sx127XDevice = new SX127XDevice(SpiBusId, chipSelectLine, interruptPinNumber, resetPinNumber);
#endif
            Thread.Sleep(500);

            // Put device into LoRa + Sleep mode
            sx127XDevice.WriteByte(0x01, 0b10000000); // RegOpMode 

            // Set the frequency to 915MHz
            byte[] frequencyWriteBytes = { 0xE4, 0xC0, 0x00 }; // RegFrMsb, RegFrMid, RegFrLsb
            sx127XDevice.WriteBytes(0x06, frequencyWriteBytes);

            // More power PA Boost
            sx127XDevice.WriteByte(0x09, 0b10000000); // RegPaConfig

            sx127XDevice.WriteByte(0x01, 0b10000101); // RegOpMode set LoRa & RxContinuous

            while (true)
            {
               // Set the Register Fifo address pointer
               sx127XDevice.WriteByte(0x0E, 0x00); // RegFifoTxBaseAddress 

               // Set the Register Fifo address pointer
               sx127XDevice.WriteByte(0x0D, 0x0); // RegFifoAddrPtr 

               string messageText = $"Hello LoRa {SendCount += 1}!";

               // load the message into the fifo
               byte[] messageBytes = UTF8Encoding.UTF8.GetBytes(messageText);
               sx127XDevice.WriteBytes(0x0, messageBytes); // RegFifo 

               // Set the length of the message in the fifo
               sx127XDevice.WriteByte(0x22, (byte)messageBytes.Length); // RegPayloadLength
               sx127XDevice.WriteByte(0x40, 0b01000000); // RegDioMapping1 0b00000000 DI0 RxReady & TxReady
               sx127XDevice.WriteByte(0x01, 0b10000011); // RegOpMode 

               Debug.WriteLine($"Sending {messageBytes.Length} bytes message {messageText}");

               Thread.Sleep(10000);
            }
         }
         catch (Exception ex)
         {
            Debug.WriteLine(ex.Message);
         }
      }
...
   }
}
ReceiveTransmitInterrupt application output

The ReceiveTransmitInterrupt application combines the functionality TransmitInterrupt and ReceiveInterrupt programs. The key differences are the RegDioMapping1 setup and in InterruptGpioPin_ValueChanged where the TxDone & RxDone flags in the RegIrqFlags register specify how the interrupt is handled.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.