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.

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.


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.