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.