RFM9X.NetNF on Netduino

After publishing my RFM9X.NetMF library working I noticed a Netduino 3 Wifi sitting at the back of my desk. I have got a few Netduinos and NanoFramework support (as a reference platform) for the Netduino 3 Wifi had caught my attention.

Netduino 3 Wifi LoRa test rig

The first step was to get the chip select, reset and Serial Peripheral interface(SPI) configurations sorted. I’m using a Dragino LoRa shield for Arduino and a Netduino 3 Wifi.

Dragino LoRa Shield Schematic

The first step was to figure out the configuration using the 00.Shield project. After some experimentation I figured out the SPI port connected to D10-D13 was SPI2 (SPI1 is connected to the MicroSD port)

//---------------------------------------------------------------------------------
// Copyright (c) April 2020, 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.
//
//---------------------------------------------------------------------------------
//#define ESP32_WROOM_32_LORA_1_CHANNEL   //nanoff --target ESP32_WROOM_32 --serialport COM4 --update
#define NETDUINO3_WIFI   // nanoff --target NETDUINO3_WIFI --update
//NOTE May 2020 ST_NUCLEO64_F091RC device doesn't work something broken in SPI configuration
//#define ST_NUCLEO64_F091RC // nanoff --target ST_NUCLEO64_F091RC --update
//#define ST_STM32F429I_DISCOVERY       //nanoff --target ST_STM32F429I_DISCOVERY --update
//NOTE May 2020 ST_STM32F769I_DISCOVERY device doesn't work SPI2 mappings broken 
//#define ST_STM32F769I_DISCOVERY      // nanoff --target ST_STM32F769I_DISCOVERY --update 
namespace devMobile.IoT.Rfm9x.ShieldSPI
{
   using System;
   using System.Diagnostics;
   using System.Threading;

   using Windows.Devices.Gpio;
   using Windows.Devices.Spi;

#if ESP32_WROOM_32_LORA_1_CHANNEL
   using nanoFramework.Hardware.Esp32;
#endif

   public class Program
   {
      private const byte RegVersion = 0x42;
#if ESP32_WROOM_32_LORA_1_CHANNEL
      private const string SpiBusId = "SPI1";
#endif
#if NETDUINO3_WIFI
      private const string SpiBusId = "SPI2";
#endif
#if ST_NUCLEO64_F091RC
      private const string SpiBusId = "SPI1";
#endif
#if ST_STM32F429I_DISCOVERY
      private const string SpiBusId = "SPI5";
#endif
#if ST_STM32F769I_DISCOVERY
      private const string SpiBusId = "SPI5";
#endif

      public static void Main()
      {
#if ESP32_WROOM_32_LORA_1_CHANNEL // No reset line for this device as it isn't connected on SX127X
         int ledPinNumber = Gpio.IO17;
         int chipSelectPinNumber = Gpio.IO16;
#endif
#if NETDUINO3_WIFI
         int ledPinNumber  = PinNumber('A', 10);
         // Arduino D10->PB10
         int chipSelectPinNumber = PinNumber('B', 10);
         // Arduino D9->PE5
         int resetPinNumber = PinNumber('E', 5);
#endif
#if ST_NUCLEO64_F091RC // No LED for this device as driven by D13 the SPI CLK line
         // Arduino D10->PB6
         int chipSelectPinNumber = PinNumber('B', 6);
         // Arduino D9->PC7
         int resetPinNumber = PinNumber('C', 7);
#endif
#if ST_STM32F429I_DISCOVERY // No reset line for this device as I didn't bother with jumper to SX127X pin
         int ledPinNumber  = PinNumber('G', 14);
         int chipSelectPinNumber = PinNumber('C', 2);
#endif
#if ST_STM32F769I_DISCOVERY
         int ledPinNumber  = PinNumber('J', 5);
         // Arduino D10->PA11
         int chipSelectPinNumber = PinNumber('A', 11);
         // Arduino D9->PH6
         int resetPinNumber = PinNumber('H', 6);
#endif
         Debug.WriteLine("devMobile.IoT.Rfm9x.ShieldSPI starting");

         try
         {
            GpioController gpioController = GpioController.GetDefault();

#if NETDUINO3_WIFI|| ST_NUCLEO64_F091RC || ST_STM32F769I_DISCOVERY
            // Setup the reset pin
            GpioPin resetGpioPin = gpioController.OpenPin(resetPinNumber);
            resetGpioPin.SetDriveMode(GpioPinDriveMode.Output);
            resetGpioPin.Write(GpioPinValue.High);
#endif

#if ESP32_WROOM_32_LORA_1_CHANNEL || NETDUINO3_WIFI|| ST_STM32F429I_DISCOVERY || ST_STM32F769I_DISCOVERY
            // Setup the onboard LED
            GpioPin led = gpioController.OpenPin(ledPinNumber);
            led.SetDriveMode(GpioPinDriveMode.Output);
#endif

#if ESP32_WROOM_32_LORA_1_CHANNEL
            Configuration.SetPinFunction(nanoFramework.Hardware.Esp32.Gpio.IO12, DeviceFunction.SPI1_MISO);
            Configuration.SetPinFunction(nanoFramework.Hardware.Esp32.Gpio.IO13, DeviceFunction.SPI1_MOSI);
            Configuration.SetPinFunction(nanoFramework.Hardware.Esp32.Gpio.IO14, DeviceFunction.SPI1_CLOCK);
#endif

            var settings = new SpiConnectionSettings(chipSelectPinNumber)
            {
               ClockFrequency = 500000,
               Mode = SpiMode.Mode0,// From SemTech docs pg 80 CPOL=0, CPHA=0
               SharingMode = SpiSharingMode.Shared,
            };

            using (SpiDevice device = SpiDevice.FromId(SpiBusId, settings))
            {
               Thread.Sleep(500);
            
               while (true)
               {
                  byte[] writeBuffer = new byte[] { RegVersion, 0x0 };
                  byte[] readBuffer = new byte[writeBuffer.Length];

                  device.TransferFullDuplex(writeBuffer, readBuffer);

                  Debug.WriteLine(String.Format("Register 0x{0:x2} - Value 0X{1:x2}", RegVersion, readBuffer[1]));

                  #if ESP32_WROOM_32_LORA_1_CHANNEL|| NETDUINO3_WIFI || ST_STM32F429I_DISCOVERY || ST_STM32F769I_DISCOVERY
                     led.Toggle();
                  #endif
                  Thread.Sleep(10000);
               }
            }
         }
         catch (Exception ex)
         {
            Debug.WriteLine(ex.Message);
         }
      }

#if  NETDUINO3_WIFI || ST_NUCLEO64_F091RC || ST_STM32F429I_DISCOVERY || ST_STM32F769I_DISCOVERY
      static int PinNumber(char port, byte pin)
      {
         if (port < 'A' || port > 'J')
            throw new ArgumentException();

         return ((port - 'A') * 16) + pin;
      }
#endif
   }
}

In the Visual Studio output windows I could see the correct version register value

The thread '<No Name>' (0x2) has exited with code 0 (0x0).
devMobile.IoT.Rfm9x.ShieldSPI starting
Register 0x42 - Value 0X12
Register 0x42 - Value 0X12
...

After checking the configuration of the reset (D9) and interrupt (D2) pins in other test harness programs my final configuration for Rfm9xLoRaDevice client was

//---------------------------------------------------------------------------------
// Copyright (c) April/May 2020, 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.
//
//---------------------------------------------------------------------------------
//#define ADDRESSED_MESSAGES_PAYLOAD
//#define ESP32_WROOM_32_LORA_1_CHANNEL   //nanoff --target ESP32_WROOM_32 --serialport COM4 --update
#define NETDUINO3_WIFI   // nanoff --target NETDUINO3_WIFI --update
//#define ST_STM32F429I_DISCOVERY       //nanoff --target ST_STM32F429I_DISCOVERY --update
namespace devMobile.IoT.Rfm9x.LoRaDeviceClient
{
	using System;
	using System.Diagnostics;
	using System.Text;
	using System.Threading;

#if ESP32_WROOM_32_LORA_1_CHANNEL
	using nanoFramework.Hardware.Esp32;
#endif

	using devMobile.IoT.Rfm9x;

	class Program
	{
		private const double Frequency = 915000000.0;
#if ST_STM32F429I_DISCOVERY
		private const string DeviceName = "Disco429";
		private const string SpiBusId = "SPI5";
#endif
#if ESP32_WROOM_32_LORA_1_CHANNEL
		private const string DeviceName = "ESP32";
		private const string SpiBusId = "SPI1";
#endif
#if NETDUINO3_WIFI
		private const string DeviceName = "N3W";
		private const string SpiBusId = "SPI2";
#endif
#if ADDRESSED_MESSAGES_PAYLOAD
		private const string DeviceName = "LoRaIoT1";
#endif

		static void Main()
		{
			byte MessageCount = System.Byte.MaxValue;
#if ST_STM32F429I_DISCOVERY
			int chipSelectPinNumber = PinNumber('C', 2);
			int resetPinNumber = PinNumber('C', 3);
			int interruptPinNumber = PinNumber('A', 4);
#endif
#if ESP32_WROOM_32_LORA_1_CHANNEL
         int chipSelectPinNumber = Gpio.IO16;
         int interruptPinNumber = Gpio.IO26;

			Configuration.SetPinFunction(Gpio.IO12, DeviceFunction.SPI1_MISO);
			Configuration.SetPinFunction(Gpio.IO13, DeviceFunction.SPI1_MOSI);
			Configuration.SetPinFunction(Gpio.IO14, DeviceFunction.SPI1_CLOCK);

			Rfm9XDevice rfm9XDevice = new Rfm9XDevice(SpiBusId, chipSelectPinNumber, interruptPinNumber);
#endif
#if NETDUINO3_WIFI
			// Arduino D10->PB10
			int chipSelectPinNumber = PinNumber('B', 10);
			// Arduino D9->PE5
			int resetPinNumber = PinNumber('E', 5);
			// Arduino D2->PA3
			int interruptPinNumber = PinNumber('A', 3);
#endif

#if ST_STM32F429I_DISCOVERY || NETDUINO3_WIFI
			Rfm9XDevice rfm9XDevice = new Rfm9XDevice(SpiBusId, chipSelectPinNumber, resetPinNumber, interruptPinNumber);
#endif
			rfm9XDevice.Initialise(Frequency, paBoost: true);

#if DEBUG
			rfm9XDevice.RegisterDump();
#endif

			rfm9XDevice.OnReceive += Rfm9XDevice_OnReceive;
#if ADDRESSED_MESSAGES_PAYLOAD
			rfm9XDevice.Receive(UTF8Encoding.UTF8.GetBytes(DeviceName));
#else
			rfm9XDevice.Receive();
#endif
			rfm9XDevice.OnTransmit += Rfm9XDevice_OnTransmit;

			Thread.Sleep(10000);

			while (true)
			{
				string messageText = string.Format("Hello from {0} ! {1}", DeviceName, MessageCount);
				MessageCount -= 1;

				byte[] messageBytes = UTF8Encoding.UTF8.GetBytes(messageText);
				Debug.WriteLine(string.Format("{0}-TX {1} byte message {2}", DateTime.UtcNow.ToString("HH:mm:ss"), messageBytes.Length, messageText));
#if ADDRESSED_MESSAGES_PAYLOAD
				rfm9XDevice.Send(UTF8Encoding.UTF8.GetBytes(HostName), messageBytes);
#else
				rfm9XDevice.Send(messageBytes);
#endif
				Thread.Sleep(10000);
			}
		}

		private static void Rfm9XDevice_OnReceive(object sender, Rfm9XDevice.OnDataReceivedEventArgs e)
		{
			try
			{
				// Remove unprintable characters from messages
				for (int index = 0; index < e.Data.Length; index++)
				{
					if ((e.Data[index] < 0x20) || (e.Data[index] > 0x7E))
					{
						e.Data[index] = 0x20;
					}
				}

				string messageText = UTF8Encoding.UTF8.GetString(e.Data, 0, e.Data.Length);

#if ADDRESSED_MESSAGES_PAYLOAD
				string addressText = UTF8Encoding.UTF8.GetString(e.Address, 0, e.Address.Length);

				Debug.WriteLine(string.Format(@"{0}-RX From {1} PacketSnr {2} Packet RSSI {3}dBm RSSI {4}dBm ={5} ""{6}""", DateTime.UtcNow.ToString("HH:mm:ss"), addressText, e.PacketSnr, e.PacketRssi, e.Rssi, e.Data.Length, messageText));
#else
				Debug.WriteLine(string.Format(@"{0}-RX PacketSnr {1} Packet RSSI {2}dBm RSSI {3}dBm ={4} ""{5}""", DateTime.UtcNow.ToString("HH:mm:ss"), e.PacketSnr, e.PacketRssi, e.Rssi, e.Data.Length, messageText));
#endif
			}
			catch (Exception ex)
			{
				Debug.WriteLine(ex.Message);
			}
		}

		private static void Rfm9XDevice_OnTransmit(object sender, Rfm9XDevice.OnDataTransmitedEventArgs e)
		{
			Debug.WriteLine(string.Format("{0}-TX Done", DateTime.UtcNow.ToString("HH:mm:ss")));
		}

#if ST_STM32F429I_DISCOVERY || NETDUINO3_WIFI
		static int PinNumber(char port, byte pin)
      {
         if (port < 'A' || port > 'J')
            throw new ArgumentException();

         return ((port - 'A') * 16) + pin;
      }
#endif
	}
}


The sample client could reliable send and receive messages.

The thread '<No Name>' (0x2) has exited with code 0 (0x0).
Register dump
Register 0x00 - Value 0X7A
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 0XCF
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 0X00
Register 0x21 - Value 0X08
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
00:00:25-TX 20 byte message Hello from N3W ! 255
00:00:25-TX Done
00:00:35-TX 20 byte message Hello from N3W ! 254
00:00:35-TX Done
00:00:45-TX 20 byte message Hello from N3W ! 253
00:00:45-TX Done
00:00:46-RX PacketSnr 9.50 Packet RSSI -70dBm RSSI -110dBm =59 " LoRaIoT1Maduino2at 43.9,ah 75,wsa 1,wsg 2,wd 36.00,r 0.00,"
00:00:55-TX 20 byte message Hello from N3W ! 252
00:00:55-TX Done
00:01:05-TX 20 byte message Hello from N3W ! 251
00:01:05-TX Done

Overall the process was fairly painless and helped identify a bug in the configuration of the Mode register in one of the test harness applications.

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 )

Google photo

You are commenting using your Google 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.