.Net MicroFramework LoRa library Part8

Receive Interrupt

After getting interrupts to work for outbound messages I changed the interrupt pin D10 mapping and the interrupt mask.

Getting this working with my RPI meant the process went relatively smoothly.

//---------------------------------------------------------------------------------
// 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.NetMF.Rfm9X.ReceiveInterrupt
{
   using System;
   using System.Text;
   using System.Threading;
   using Microsoft.SPOT;
   using Microsoft.SPOT.Hardware;
   using SecretLabs.NETMF.Hardware.Netduino;

   public sealed class Rfm9XDevice
   {
      private const byte RegisterAddressReadMask = 0X7f;
      private const byte RegisterAddressWriteMask = 0x80;

      private SPI Rfm9XLoraModem = null;
      private OutputPort ResetGpioPin = null;
      private InterruptPort InterruptPin = null;

      public Rfm9XDevice(Cpu.Pin chipSelect, Cpu.Pin resetPin, Cpu.Pin interruptPin)
      {
         // Factory reset pin configuration
         ResetGpioPin = new OutputPort(Pins.GPIO_PIN_D9, true);
         ResetGpioPin.Write(false);
         Thread.Sleep(10);
         ResetGpioPin.Write(true);
         Thread.Sleep(10);

         this.Rfm9XLoraModem = new SPI(new SPI.Configuration(chipSelect, false, 0, 0, false, false, 2000, SPI.SPI_module.SPI1));

         InterruptPin = new InterruptPort(interruptPin, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeHigh);

         InterruptPin.OnInterrupt += InterruptPin_OnInterrupt;

         Thread.Sleep(100);
      }

      public Rfm9XDevice(Cpu.Pin chipSelect, Cpu.Pin reset)
      {
         // Factory reset pin configuration
         ResetGpioPin = new OutputPort(Pins.GPIO_PIN_D9, true);
         ResetGpioPin.Write(false);
         Thread.Sleep(10);
         ResetGpioPin.Write(true);
         Thread.Sleep(10);

         this.Rfm9XLoraModem = new SPI(new SPI.Configuration(chipSelect, false, 0, 0, false, false, 2000, SPI.SPI_module.SPI1));

         Thread.Sleep(100);
      }

      public Byte RegisterReadByte(byte registerAddress)
      {
         byte[] writeBuffer = new byte[] { registerAddress };
         byte[] readBuffer = new byte[1];
         Debug.Assert(Rfm9XLoraModem != null);

         Rfm9XLoraModem.WriteRead(writeBuffer, readBuffer, 1);

         return readBuffer[0];
      }

      public ushort RegisterReadWord(byte address)
      {
         byte[] writeBuffer = new byte[] { address &= RegisterAddressReadMask };
         byte[] readBuffer = new byte[2];
         Debug.Assert(Rfm9XLoraModem != null);

         readBuffer[0] = RegisterReadByte(address);
         readBuffer[1] = RegisterReadByte(address += 1);

         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);

         for (byte index = 0; index < length; index++)
         {
            readBuffer[index] = RegisterReadByte(address += 1);
         }

         return readBuffer;
      }

      public void RegisterWriteByte(byte address, byte value)
      {
         byte[] writeBuffer = new byte[] { address |= RegisterAddressWriteMask, value };
         Debug.Assert(Rfm9XLoraModem != null);

         Rfm9XLoraModem.Write(writeBuffer);
      }

      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);

         Rfm9XLoraModem.Write(writeBuffer);
      }

      public void RegisterWrite(byte address, 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;

         Rfm9XLoraModem.Write(writeBuffer);
      }

      public void RegisterDump()
      {
         Debug.Print("---Registers 0x00 thru 0x42---");
         for (byte registerIndex = 0; registerIndex  4];

         // Mask off the upper 4 bits to get the rest of it.
         hexString += hexChars[singlebyte & 0x0F];

         return hexString;
      }

      private static string WordToHexString(ushort singleword)
      {
         string hexString = string.Empty;

         byte[] bytes = BitConverter.GetBytes(singleword);

         hexString += ByteToHexString(bytes[1]);

         hexString += ByteToHexString(bytes[0]);

         return hexString;
      }

      public class Program
      {
         public static void Main()
         {
            Rfm9XDevice rfm9XDevice = new Rfm9XDevice(Pins.GPIO_PIN_D10, Pins.GPIO_PIN_D9, Pins.GPIO_PIN_D2);
            byte MessageCount = byte.MinValue;

            // Put device into LoRa + Sleep mode
            rfm9XDevice.RegisterWriteByte(0x01, 0x80); // 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(0x40, 0x0); // RegDioMapping1 0b00000000 DIO0 RxReady & TxReady

            rfm9XDevice.RegisterWriteByte(0x01, 0x85); // RegOpMode set LoRa & RxContinuous

            Thread.Sleep(Timeout.Infinite);
         }
      }
   }
}

In the Visual Studio debug output window I could see received packets

'Microsoft.SPOT.Debugger.CorDebug.dll' (Managed): Loaded 'C:\Program Files (x86)\Secret Labs\Netduino SDK\Assemblies\v4.3\le\SecretLabs.NETMF.Hardware.dll', Symbols loaded.
The thread '' (0x2) has exited with code 0 (0x0).
RegIrqFlags 50
Receive-Message
Received 28 byte message Hello W10 IoT Core LoRa! 247
RegIrqFlags 50
Receive-Message
Received 28 byte message Hello W10 IoT Core LoRa! 246
RegIrqFlags 50
Receive-Message

Next, I’ll integrate the .NetMF receive and transmit interrupt examples, and then refactor the code to extract the RFM9X code into a reusable module.

 

.Net MicroFramework LoRa library Part7

Transmit Interrupt

Starting with the TransmitBasic sample application I modified the code so that a hardware interrupt (specified by SX1276 RegDioMapping1) was generated on TxDone (FIFO Payload Transmission completed).

The application inserts a message into the SX1276 transmit FIFO every 10 seconds with confirmation of transmission displayed shortly afterwards

//---------------------------------------------------------------------------------
// 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.NetMF.Rfm9X.TransmitInterrupt
{
	using System;
	using System.Text;
	using System.Threading;
	using Microsoft.SPOT;
	using Microsoft.SPOT.Hardware;
	using SecretLabs.NETMF.Hardware.Netduino;

	public sealed class Rfm9XDevice
	{
		private const byte RegisterAddressReadMask = 0X7f;
		private const byte RegisterAddressWriteMask = 0x80;

		private SPI Rfm9XLoraModem = null;
		private OutputPort ResetGpioPin = null;
		private InterruptPort InterruptPin = null;

		public Rfm9XDevice(Cpu.Pin chipSelect, Cpu.Pin resetPin, Cpu.Pin interruptPin)
		{
			// Factory reset pin configuration
			ResetGpioPin = new OutputPort(Pins.GPIO_PIN_D9, true);
			ResetGpioPin.Write(false);
			Thread.Sleep(10);
			ResetGpioPin.Write(true);
			Thread.Sleep(10);

			this.Rfm9XLoraModem = new SPI(new SPI.Configuration(chipSelect, false, 0, 0, false, false, 2000, SPI.SPI_module.SPI1));

			InterruptPin = new InterruptPort(interruptPin, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeHigh);

			InterruptPin.OnInterrupt += InterruptPin_OnInterrupt;

			Thread.Sleep(100);
		}

		public Byte RegisterReadByte(byte registerAddress)
		{
			byte[] writeBuffer = new byte[] { registerAddress };
			byte[] readBuffer = new byte[1];
			Debug.Assert(Rfm9XLoraModem != null);

			Rfm9XLoraModem.WriteRead(writeBuffer, readBuffer, 1);

			return readBuffer[0];
		}

		public ushort RegisterReadWord(byte address)
		{
			byte[] writeBuffer = new byte[] { address &= RegisterAddressReadMask };
			byte[] readBuffer = new byte[2];
			Debug.Assert(Rfm9XLoraModem != null);

			readBuffer[0] = RegisterReadByte(address);
			readBuffer[1] = RegisterReadByte(address += 1);

			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);

			for (byte index = 0; index  4];

			// Mask off the upper 4 bits to get the rest of it.
			hexString += hexChars[singlebyte & 0x0F];

			return hexString;
		}

		private static string WordToHexString(ushort singleword)
		{
			string hexString = string.Empty;

			byte[] bytes = BitConverter.GetBytes(singleword);

			hexString += ByteToHexString(bytes[1]);

			hexString += ByteToHexString(bytes[0]);

			return hexString;
		}

		void InterruptPin_OnInterrupt(uint data1, uint data2, DateTime time)
		{
			byte IrqFlags = this.RegisterReadByte(0x12); // RegIrqFlags
			Debug.Print("RegIrqFlags " + ByteToHexString(IrqFlags));

			if ((IrqFlags & 0x08) == 0x08)  // TxDone
			{
				Debug.Print("Transmit-Done");
			}

			this.RegisterWriteByte(0x12, 0xff);// RegIrqFlags
		}

		public class Program
		{
			public static void Main()
			{
				Rfm9XDevice rfm9XDevice = new Rfm9XDevice(Pins.GPIO_PIN_D10, Pins.GPIO_PIN_D9, Pins.GPIO_PIN_D2);
				byte MessageCount = byte.MinValue;

				while (true)
				{
					// Put device into LoRa + Sleep mode
					rfm9XDevice.RegisterWriteByte(0x01, 0x80); // RegOpMode 

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

					// More power - PA_BOOST
					rfm9XDevice.RegisterWriteByte(0x09, 0x80); // RegPaConfig

					// Interrupt on TxDone
					rfm9XDevice.RegisterWriteByte(0x40, 0x40); // RegDioMapping1 0b00000000 DI0 TxDone

					while (true)
					{
						rfm9XDevice.RegisterWriteByte(0x0E, 0x0); // RegFifoTxBaseAddress 

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

						string messageText = "Hello NetMF LoRa! ";
						if (MessageCount != 0)
						{
							messageText += "-" + MessageCount.ToString();
						}
						else
						{
							messageText += MessageCount.ToString();
						}
						MessageCount += 1;

						// load the message into the fifo
						byte[] messageBytes = UTF8Encoding.UTF8.GetBytes(messageText);
						foreach (byte b in messageBytes)
						{
							rfm9XDevice.RegisterWriteByte(0x0, b); // RegFifo
						}

						// Set the length of the message in the fifo
						rfm9XDevice.RegisterWriteByte(0x22, (byte)messageBytes.Length); // RegPayloadLength

						Debug.Print("Sending " + messageBytes.Length + " bytes message " + messageText);
						/// Set the mode to LoRa + Transmit
						rfm9XDevice.RegisterWriteByte(0x01, 0x83); // RegOpMode 

						Thread.Sleep(10000);
					}
				}
			}
		}

	}
}

Unlike the Windows 10 IoT core version I can configure the interrupt to only trigger on the leading edge.

InterruptPin = new InterruptPort(interruptPin, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeHigh);

In the Debug output window of VS2K12 I could see

The thread '' (0x2) has exited with code 0 (0x0).
Sending 19 bytes message Hello NetMF LoRa! 0
RegIrqFlags 08
Transmit-Done
Sending 20 bytes message Hello NetMF LoRa! -1
RegIrqFlags 08
Transmit-Done
Sending 20 bytes message Hello NetMF LoRa! -2
RegIrqFlags 08
Transmit-Done

On my Arduino device the message arrived

LoRa Receiver Callback
Received packet 'Hello NetMF LoRa! 0' with RSSI -29
Received packet 'Hello NetMF LoRa! -1' with RSSI -29
Received packet 'Hello NetMF LoRa! -2' with RSSI -29
Received packet 'Hello NetMF LoRa! -3' with RSSI -29
Received packet 'Hello NetMF LoRa! -4' with RSSI -29

Next step interrupts for processing inbound messages

.Net MicroFramework LoRa library Part6

Receive Basic

I can configure the device to send messages now I need to configure the SX1276 device to receive them. For my first attempt just simple polling for a message to arrive nothing flash.

//---------------------------------------------------------------------------------
// 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.NetMF.Rfm9X.ReceiveBasic
{
   using System;
   using System.Text;
   using System.Threading;
   using Microsoft.SPOT;
   using Microsoft.SPOT.Hardware;
   using SecretLabs.NETMF.Hardware.Netduino;

   public sealed class Rfm9XDevice
   {
      private const byte RegisterAddressReadMask = 0X7f;
      private const byte RegisterAddressWriteMask = 0x80;

      private SPI Rfm9XLoraModem = null;
      private OutputPort ResetGpioPin = null;

      public Rfm9XDevice(Cpu.Pin chipSelect, Cpu.Pin reset)
      {
         // Factory reset pin configuration
         ResetGpioPin = new OutputPort(Pins.GPIO_PIN_D9, true);
         ResetGpioPin.Write(false);
         Thread.Sleep(10);
         ResetGpioPin.Write(true);
         Thread.Sleep(10);

         this.Rfm9XLoraModem = new SPI(new SPI.Configuration(chipSelect, false, 0, 0, false, false, 2000, SPI.SPI_module.SPI1));

         Thread.Sleep(100);
      }

      public Byte RegisterReadByte(byte registerAddress)
      {
         byte[] writeBuffer = new byte[] { registerAddress };
         byte[] readBuffer = new byte[1];
         Debug.Assert(Rfm9XLoraModem != null);

         Rfm9XLoraModem.WriteRead(writeBuffer, readBuffer, 1);

         return readBuffer[0];
      }

      public ushort RegisterReadWord(byte address)
      {
         byte[] writeBuffer = new byte[] { address &= RegisterAddressReadMask };
         byte[] readBuffer = new byte[2];
         Debug.Assert(Rfm9XLoraModem != null);

         readBuffer[0] = RegisterReadByte(address);
         readBuffer[1] = RegisterReadByte(address += 1);

         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);

         for (byte index = 0; index < length; index++)
         {
            readBuffer[index] = RegisterReadByte(address += 1);
         }

         return readBuffer;
      }

      public void RegisterWriteByte(byte address, byte value)
      {
         byte[] writeBuffer = new byte[] { address |= RegisterAddressWriteMask, value };
         Debug.Assert(Rfm9XLoraModem != null);

         Rfm9XLoraModem.Write(writeBuffer);
      }

      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);

         Rfm9XLoraModem.Write(writeBuffer);
      }

      public void RegisterWrite(byte address, 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;

         Rfm9XLoraModem.Write(writeBuffer);
      }

      public void RegisterDump()
      {
         Debug.Print("---Registers 0x00 thru 0x42---");
         for (byte registerIndex = 0; registerIndex  4];

         // Mask off the upper 4 bits to get the rest of it.
         hexString += hexChars[singlebyte & 0x0F];

         return hexString;
      }

      private static string WordToHexString(ushort singleword)
      {
         string hexString = string.Empty;

         byte[] bytes = BitConverter.GetBytes(singleword);

         hexString += ByteToHexString(bytes[1]);

         hexString += ByteToHexString(bytes[0]);

         return hexString;
      }
   }
}

In the debug window I could see messages from one of my arduino devices

The thread '' (0x2) has exited with code 0 (0x0).
Receive-Wait
.
.
.
.
.
.
.

.
.
.
.

Receive-Message
Received 15 byte message HeLoRa World! 0
Receive-Done
Receive-Wait
.
.
.
.
.
.
.
.
.
.
.

Receive-Message
Received 15 byte message HeLoRa World! 2
Receive-Done
Receive-Wait
.
.

Next steps handling inbound & outbound messages with interrupts, then integrating all the samples into a useful functional V1 library.

.Net MicroFramework LoRa library Part5

Transmit Basic

Finally at the point where I can send a message to one of my Windows 10 IoT Core devices. Not going to use interrupts, just putting the bytes to send into the SX1276 Fifo and looping until they are sent.

//---------------------------------------------------------------------------------
// 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.NetMF.Rfm9X.TransmitBasic
{
   using System;
   using System.Text;
   using System.Threading;
   using Microsoft.SPOT;
   using Microsoft.SPOT.Hardware;
   using SecretLabs.NETMF.Hardware.Netduino;

   public sealed class Rfm9XDevice
   {
      private const byte RegisterAddressReadMask = 0X7f;
      private const byte RegisterAddressWriteMask = 0x80;

      private SPI Rfm9XLoraModem = null;
      private OutputPort ResetGpioPin = null;

      public Rfm9XDevice(Cpu.Pin chipSelect, Cpu.Pin reset)
      {
         // Factory reset pin configuration
         ResetGpioPin = new OutputPort(Pins.GPIO_PIN_D9, true);
         ResetGpioPin.Write(false);
         Thread.Sleep(10);
         ResetGpioPin.Write(true);
         Thread.Sleep(10);

         //this.Rfm9XLoraModem = new SPI(new SPI.Configuration(chipSelect, false, 0, 0, false, true, 2000, SPI.SPI_module.SPI1));
         this.Rfm9XLoraModem = new SPI(new SPI.Configuration(chipSelect, false, 0, 0, false, false, 2000, SPI.SPI_module.SPI1));

         Thread.Sleep(100);
      }

      public Byte RegisterReadByte(byte registerAddress)
      {
         byte[] writeBuffer = new byte[] { registerAddress };
         byte[] readBuffer = new byte[1];
         Debug.Assert(Rfm9XLoraModem != null);

         //Rfm9XLoraModem.WriteRead(writeBuffer, readBuffer, 1);
         Rfm9XLoraModem.WriteRead(writeBuffer, readBuffer, 1);

         return readBuffer[0];
      }

      public ushort RegisterReadWord(byte address)
      {
         byte[] writeBuffer = new byte[] { address &amp;= RegisterAddressReadMask };
         byte[] readBuffer = new byte[2];
         Debug.Assert(Rfm9XLoraModem != null);

         //Rfm9XLoraModem.WriteRead(readBuffer, writeBuffer, 1 ); // Check this

         readBuffer[0] = RegisterReadByte(address);
         readBuffer[1] = RegisterReadByte(address += 1);

         return (ushort)(readBuffer[1] + (readBuffer[0] &lt;<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>&lt; 8));
      }

      public byte[] RegisterRead(byte address, int length)
      {
         byte[] writeBuffer = new byte[] { address &amp;= RegisterAddressReadMask };
         byte[] readBuffer = new byte[length];
         Debug.Assert(Rfm9XLoraModem != null);

         //Rfm9XLoraModem.WriteRead(readBuffer, writeBuffer, 1); // Check this

         for (byte index = 0; index  4];

         // Mask off the upper 4 bits to get the rest of it.
         hexString += hexChars[singlebyte &amp; 0x0F];

         return hexString;
      }

      private static string WordToHexString(ushort singleword)
      {
         string hexString = string.Empty;

         byte[] bytes = BitConverter.GetBytes(singleword);

         hexString += ByteToHexString(bytes[1]);

         hexString += ByteToHexString(bytes[0]);

         return hexString;
      }

   }
}

The debugging output of the device shows the message being transmitted

The thread '' (0x2) has exited with code 0 (0x0).
Sending 17 bytes message Hello NetMF LoRa!
Send-wait
.
.
.
.
.
Send-Done
Sending 17 bytes message Hello NetMF LoRa!
Send-wait
.
.
.
.
.
Send-Done

On my Windows 10 IoT core device I could see the messages arriving

RegIrqFlags 01010000
Receive-Message
Received 17 byte message Hello NetMF LoRa!
The thread 0x6b4 has exited with code 0 (0x0).
Sending 22 bytes message W10 IoT Core LoRa! 252
RegIrqFlags 00001000
Transmit-Done
RegIrqFlags 01010000
Receive-Message
Received 17 byte message Hello NetMF LoRa!
The program '[2932] backgroundTaskHost.exe' has exited with code -1 (0xffffffff).

All default settings for not a lot of range etc. but it works

.Net MicroFramework LoRa library Part4

Register Read and Write

For configuration and operation I extended the RegisterManager class with methods for reading/writing bytes, words and byte arrays.

//---------------------------------------------------------------------------------
// 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.NetMF.Rfm9X.RegisterReadandWrite
{
   using System;
   using System.Threading;
   using Microsoft.SPOT;
   using Microsoft.SPOT.Hardware;
   using SecretLabs.NETMF.Hardware.Netduino;

   public sealed class Rfm9XDevice
   {
      private const byte RegisterAddressReadMask = 0X7f;
      private const byte RegisterAddressWriteMask = 0x80;

      private SPI Rfm9XLoraModem = null;
      private OutputPort ResetGpioPin = null;

      public Rfm9XDevice(Cpu.Pin chipSelect, Cpu.Pin reset)
      {
         // Factory reset pin configuration
         ResetGpioPin = new OutputPort(Pins.GPIO_PIN_D9, true);
         ResetGpioPin.Write(false);
         Thread.Sleep(10);
         ResetGpioPin.Write(true);
         Thread.Sleep(10);

         //this.Rfm9XLoraModem = new SPI(new SPI.Configuration(chipSelect, false, 0, 0, false, true, 2000, SPI.SPI_module.SPI1));
         this.Rfm9XLoraModem = new SPI(new SPI.Configuration(chipSelect, false, 0, 0, false, false, 2000, SPI.SPI_module.SPI1));

         Thread.Sleep(100);
      }

      public Byte RegisterReadByte(byte registerAddress)
      {
         byte[] writeBuffer = new byte[] { registerAddress };
         byte[] readBuffer = new byte[1];
         Debug.Assert(Rfm9XLoraModem != null);

         //Rfm9XLoraModem.WriteRead(writeBuffer, readBuffer, 1);
         Rfm9XLoraModem.WriteRead(writeBuffer, readBuffer, 1 );

         return readBuffer[0];
      }

      public ushort RegisterReadWord(byte address)
      {
         byte[] writeBuffer = new byte[] { address &= RegisterAddressReadMask };
         byte[] readBuffer = new byte[2];
         Debug.Assert(Rfm9XLoraModem != null);

         //Rfm9XLoraModem.WriteRead(readBuffer, writeBuffer, 4 ); // Check this

         readBuffer[0] = RegisterReadByte( address ) ;
         readBuffer[1] = RegisterReadByte(address+=1);

         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);

         //Rfm9XLoraModem.WriteRead(readBuffer, writeBuffer, 1); // Check this

         for (byte index = 0; index  4];

         // Mask off the upper 4 bits to get the rest of it.
         hexString += hexChars[singlebyte & 0x0F];

         return hexString;
      }

      private static string WordToHexString(ushort singleword)
      {
         string hexString = string.Empty;

         byte[] bytes = BitConverter.GetBytes(singleword);

         hexString += ByteToHexString(bytes[1]);

         hexString += ByteToHexString(bytes[0]);

         return hexString;
      }

   }
}

I had to add an extra "ToHexString" method so I could display the returned byte and word values

Read RegOpMode (read byte)
RegOpMode 0x09
Set LoRa mode and sleep mode (write byte)
Read the preamble (read word)
Preamble 0x0008
Set the preamble to 0x80 (write word)
Read the centre frequency (read byte array)
Frequency Msb 0x80 Mid 0x00 Lsb 0x4F
Set the centre frequency to 916MHz (write byte array)
—Registers 0x00 thru 0x42—
Register 0x00 – Value 0XF4
Register 0x01 – Value 0X80
Register 0x02 – Value 0X1A
Register 0x03 – Value 0X0B
Register 0x04 – Value 0X00
Register 0x05 – Value 0X52
Register 0x06 – Value 0XE4
Register 0x07 – Value 0XC0
Register 0x08 – Value 0X00
Register 0x09 – Value 0X4F
Register 0x0A – Value 0X09
Register 0x0B – Value 0X2B
Register 0x0C – Value 0X20
Register 0x0D – Value 0X01
Register 0x0E – Value 0X80
Register 0x0F – Value 0X00
Register 0x10 – Value 0X00
Register 0x11 – Value 0X00
Register 0x12 – Value 0X00
Register 0x13 – Value 0X00
Register 0x14 – Value 0X00
Register 0x15 – Value 0X00
Register 0x16 – Value 0X00
Register 0x17 – Value 0X00
Register 0x18 – Value 0X10
Register 0x19 – Value 0X00
Register 0x1A – Value 0X00
Register 0x1B – Value 0X00
Register 0x1C – Value 0X00
Register 0x1D – Value 0X72
Register 0x1E – Value 0X70
Register 0x1F – Value 0X64
Register 0x20 – Value 0X80
Register 0x21 – Value 0X00
Register 0x22 – Value 0X01
Register 0x23 – Value 0XFF
Register 0x24 – Value 0X00
Register 0x25 – Value 0X00
Register 0x26 – Value 0X04
Register 0x27 – Value 0X00
Register 0x28 – Value 0X00
Register 0x29 – Value 0X00
Register 0x2A – Value 0X00
Register 0x2B – Value 0X00
Register 0x2C – Value 0X00
Register 0x2D – Value 0X50
Register 0x2E – Value 0X14
Register 0x2F – Value 0X45
Register 0x30 – Value 0X55
Register 0x31 – Value 0XC3
Register 0x32 – Value 0X05
Register 0x33 – Value 0X27
Register 0x34 – Value 0X1C
Register 0x35 – Value 0X0A
Register 0x36 – Value 0X03
Register 0x37 – Value 0X0A
Register 0x38 – Value 0X42
Register 0x39 – Value 0X12
Register 0x3A – Value 0X49
Register 0x3B – Value 0X1D
Register 0x3C – Value 0X00
Register 0x3D – Value 0XAF
Register 0x3E – Value 0X00
Register 0x3F – Value 0X00
Register 0x40 – Value 0X00
Register 0x41 – Value 0X00
Register 0x42 – Value 0X12

The register dump shows the SX1276 is in LoRa+Sleep mode and the preamble is set to 0x80 etc.

The way I read words and arrays of bytes isn’t very efficient will need to revisit when I have more time and/or access to a digital storage scope.

Wireless field gateway protocol V2

I have now built a couple of nRF2L01P field gateways (for AdaFriut.IO & Azure IoT Hubs) which run as a background tasks on Windows 10 IoT Core on RaspberyPI). I have also written several clients which run on Arduino, devDuino, Netduino, and Seeeduino devices.

I have tried to keep the protocol simple (telemetry only) to deploy and it will be used in high school student projects in the next couple of weeks.

To make the payload smaller the first byte of the message now specifies the message type in the top nibble and the length of the device unique identifier in the bottom nibble.

0 = Echo

The message is displayed by the field gateway as text & hexadecimal.

1 = Device identifier + Comma separated values (CSV) payload

[0] – Set to 0001, XXXX   Device identifier length

[1]..[1+Device identifier length] – Unique device identifier bytes e.g. Mac address

[1+Device identifier length+1 ]..[31] – CSV payload e.g.  SensorID value, SensorID value

 

Wireless field gateway protocol V1

I’m going to build a number of nRF2L01P field gateways (Netduino Ethernet & Wifi running .NetMF, Raspberry PI running Windows 10 IoT Core, RedBearLab 3200  etc.), clients which run on a variety of hardware (Arduino, devDuino, Netduino, Seeeduino etc.) which, then upload data to a selection of IoT Cloud services (AdaFruit.IO, ThingSpeak, Microsoft IoT Central etc.)

The nRF24L01P is widely supported with messages up to 32 bytes long, low power consumption and 250kbps, 1Mbps and 2Mbps data rates.

The aim is to keep the protocol simple (telemetry only initially) to implement and debug as the client side code will be utilised by high school student projects.

The first byte of the message specifies the message type

0 = Echo

The message is displayed by the field gateway as text & hexadecimal.

1 = Device identifier + Comma separated values (CSV) payload

[0] – Set to 1

[1] – Device identifier length

[2]..[2+Device identifier length] – Unique device identifier bytes e.g. Mac address

[2+Device identifier length+1 ]..[31] – CSV payload e.g.  SensorID value, SensorID value

Overtime I will support more message types and wireless protocols.