Random wanderings through Microsoft Azure esp. the IoT bits, AI on Micro controllers, .NET nanoFramework, .NET Core on *nix, and GHI Electronics TinyCLR
Every so often I print my code out (landscape for notes in margin, double sided to save paper, and colour so it looks like Visual Studio 2022) and within 100 lines noticed the first of no doubt many issues. The SX127X RegLNA enumeration was wrong.
The LnaGain value is bits 5-7 rather than rather than bits 0-2 which could be a problem if the specified lnaGain and lnaBoost values are not the default values.
// Set RegLna if any of the settings not defaults
if ((lnaGain != Configuration.LnaGainDefault) || (lnaBoost != Configuration.LnaBoostDefault))
{
byte regLnaValue = (byte)lnaGain;
regLnaValue |= Configuration.RegLnaLnaBoostLfDefault;
regLnaValue |= Configuration.RegLnaLnaBoostHfDefault;
if (lnaBoost)
{
if (_frequency > Configuration.SX127XMidBandThreshold)
{
regLnaValue |= Configuration.RegLnaLnaBoostHfOn;
}
else
{
regLnaValue |= Configuration.RegLnaLnaBoostLfOn;
}
}
_registerManager.WriteByte((byte)Configuration.Registers.RegLna, regLnaValue);
}
The default lnaGain is G1 and the default lnaBoost is false so if the gain was set to G3(011) then LnaBoostHf current would be 150% and LnaGain would be 000 which is a reserved value.
One afternoon the issue occurred several times in a row, the application wouldn’t startup because the SX127X device detection failed and message transmission was also not being confirmed.(TX Done).
public SX127XDevice(SpiDevice spiDevice, GpioController gpioController, int interruptPin, int resetPin)
{
_gpioController = gpioController;
// Factory reset pin configuration
_resetPin = resetPin;
_gpioController.OpenPin(resetPin, PinMode.Output);
_gpioController.Write(resetPin, PinValue.Low);
Thread.Sleep(20);
_gpioController.Write(resetPin, PinValue.High);
Thread.Sleep(100);
_registerManager = new RegisterManager(spiDevice, RegisterAddressReadMask, RegisterAddressWriteMask);
// Once the pins setup check that SX127X chip is present
Byte regVersionValue = _registerManager.ReadByte((byte)Configuration.Registers.RegVersion);
if (regVersionValue != Configuration.RegVersionValueExpected)
{
throw new ApplicationException("Semtech SX127X not found");
}
// Interrupt pin for RX message & TX done notification
_gpioController.OpenPin(interruptPin, PinMode.InputPullDown);
_gpioController.RegisterCallbackForPinValueChangedEvent(interruptPin, PinEventTypes.Rising, InterruptGpioPin_ValueChanged);
}
I could single step through the code and inspect variables with the debugger and it looks like a timing issue with order of the strobing of the reset pin and the initialisation of the RegisterManager. I’ll spend and hour starting and stopping the application, then smoke test the code for 24 hours with a couple of other devices generating traffic just to check.
namespace devMobile.IoT.SX127xLoRaDevice
{
using System;
using System.Text;
using System.Threading;
class Program
{
private const double Frequency = 915000000.0;
#if ESP32_WROOM_32_LORA_1_CHANNEL
private const int SpiBusId = 1;
#endif
#if NETDUINO3_WIFI
private const int SpiBusId = 2;
#endif
#if ST_STM32F769I_DISCOVERY
private const int SpiBusId = 2;
#endif
private static SX127XDevice sx127XDevice;
static void Main(string[] args)
{
int SendCount = 0;
#if ESP32_WROOM_32_LORA_1_CHANNEL // No reset line for this device as it isn't connected on SX127X
int chipSelectLine = Gpio.IO16;
int interruptPinNumber = Gpio.IO26;
#endif
#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
#if ST_STM32F769I_DISCOVERY
// Arduino D10->PA11
int chipSelectLine = PinNumber('A', 11);
// Arduino D9->PH6
int resetPinNumber = PinNumber('H', 6);
// Arduino D2->PA4
int interruptPinNumber = PinNumber('J', 1);
#endif
Console.WriteLine("devMobile.IoT.SX127xLoRaDevice Client starting");
try
{
#if ESP32_WROOM_32_LORA_1_CHANNEL
Configuration.SetPinFunction(Gpio.IO12, DeviceFunction.SPI1_MISO);
Configuration.SetPinFunction(Gpio.IO13, DeviceFunction.SPI1_MOSI);
Configuration.SetPinFunction(Gpio.IO14, DeviceFunction.SPI1_CLOCK);
sx127XDevice = new SX127XDevice(SpiBusId, chipSelectLine, interruptPinNumber);
#endif
#if NETDUINO3_WIFI || ST_STM32F769I_DISCOVERY
sx127XDevice = new SX127XDevice(SpiBusId, chipSelectLine, interruptPinNumber, resetPinNumber);
#endif
sx127XDevice.Initialise(SX127XDevice.RegOpModeMode.ReceiveContinuous,
Frequency,
lnaGain: SX127XDevice.RegLnaLnaGain.G3,
lnaBoost:true,
powerAmplifier: SX127XDevice.PowerAmplifier.PABoost,
rxPayloadCrcOn: true,
rxDoneignoreIfCrcMissing: false
);
#if DEBUG
sx127XDevice.RegisterDump();
#endif
sx127XDevice.OnReceive += SX127XDevice_OnReceive;
sx127XDevice.Receive();
sx127XDevice.OnTransmit += SX127XDevice_OnTransmit;
Thread.Sleep(500);
while (true)
{
string messageText = $"Hello LoRa from .NET nanoFramework {SendCount += 1}!";
byte[] messageBytes = UTF8Encoding.UTF8.GetBytes(messageText);
//Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss}-TX {messageBytes.Length} byte message {messageText}");
//sx127XDevice.Send(messageBytes);
Thread.Sleep(50000);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static void SX127XDevice_OnReceive(object sender, SX127XDevice.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] = 0x7C;
}
}
string messageText = UTF8Encoding.UTF8.GetString(e.Data, 0, e.Data.Length);
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss}-RX PacketSnr {e.PacketSnr:0.0} Packet RSSI {e.PacketRssi}dBm RSSI {e.Rssi}dBm = {e.Data.Length} byte message {messageText}");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
private static void SX127XDevice_OnTransmit(object sender, SX127XDevice.OnDataTransmitedEventArgs e)
{
sx127XDevice.SetMode(SX127XDevice.RegOpModeMode.ReceiveContinuous);
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss}-TX Done");
}
#if NETDUINO3_WIFI || ST_STM32F769I_DISCOVERY
static int PinNumber(char port, byte pin)
{
if (port < 'A' || port > 'J')
throw new ArgumentException();
return ((port - 'A') * 16) + pin;
}
#endif
}
}
The sample application shows how to configure the library for different devices (SPI port, interrupt pin and optional reset pin) then send/receive payloads. The library is intended to be initialised then run for long periods of time (I’m looking at a month long soak test next) rather than changing configuration while running. The initialise method has many parameters which have “reasonable” default values. (Posts coming about optimising power consumption and range).
I then fixed all the breaking changes (For the initial versions I have not updated the code to use SpanByte etc.).
public Rfm9XDevice(int spiBusId, int chipSelectPin, int resetPin, int interruptPin)
{
//...
// Interrupt pin for RX message & TX done notification
InterruptGpioPin = gpioController.OpenPin(interruptPin);
InterruptGpioPin.SetPinMode(PinMode.Input);
InterruptGpioPin.ValueChanged += InterruptGpioPin_ValueChanged;
}
private void InterruptGpioPin_ValueChanged(object sender, PinValueChangedEventArgs e)
{
if (e.ChangeType != PinEventTypes.Rising)
{
return;
}
byte irqFlags = this.RegisterReadByte(0x12); // RegIrqFlags
//...
}
While “soak testing” the ReceiveInterrupt application I noticed that sometimes when I started the application interrupts were not processed or processing stopped after a while.
Need to be careful not to push the Dragino shield in too far as a couple of the pins (one is not connected and the other is IOREF) will contact the Micro SD card slot. (I have put a strip of Duct tape on the top of the Micro SD card socket)
The first step was to get basic connectivity sorted. I opened the RFM9XLoRa-TinyCLR repository and modified the Serial Peripheral Interface(SPI) and chip select(CS) settings of the ShieldSPI project, then updated the NuGet packages (public feed rather than my local preview files).
I have left the TinyCLR V1 configuration in for backward compatibility
When I ran the application in Visual Studio I could reliably read the RegVersion register.
The thread '<No Name>' (0x2) has exited with code 0 (0x0).
Value = 0x00-12
Value = 0x00-12
Value = 0x00-12
Value = 0x00-12
Value = 0x00-12
Value = 0x00-12
The program '[5] TinyCLR application: Managed' has exited with code 0 (0x0).
The next step was to modify the RegisterScan project to check I could read all the SX127X configuration registers.
class Program
{
static void Main()
{
#if TINYCLR_V2_SC20100DEV
Rfm9XDevice rfm9XDevice = new Rfm9XDevice(SC20100.SpiBus.Spi3, SC20100.GpioPin.PA13);
#endif
#if TINYCLR_V2_FEZDUINO
Rfm9XDevice rfm9XDevice = new Rfm9XDevice(SC20100.SpiBus.Spi6, SC20100.GpioPin.PB1);
#endif
while (true)
{
for (byte registerIndex = 0; registerIndex <= 0x42; registerIndex++)
{
byte registerValue = rfm9XDevice.RegisterReadByte(registerIndex);
Debug.WriteLine($"Register 0x{registerIndex:x2} - Value 0X{registerValue:x2}");
}
Debug.WriteLine("");
Thread.Sleep(10000);
}
}
}
When I ran the application in Visual Studio I could reliably read the registers 0x00 through 0x42.
The thread '<No Name>' (0x2) has exited with code 0 (0x0).
Register 0x00 - Value 0X00
Register 0x01 - Value 0X09
Register 0x02 - Value 0X1a
Register 0x03 - Value 0X0b
Register 0x04 - Value 0X00
Register 0x05 - Value 0X52
Register 0x06 - Value 0X6c
Register 0x07 - Value 0X80
Register 0x08 - Value 0X00
Register 0x09 - Value 0X4f
Register 0x0a - Value 0X09
Register 0x0b - Value 0X2b
Register 0x0c - Value 0X20
Register 0x0d - Value 0X08
Register 0x0e - Value 0X02
Register 0x0f - Value 0X0a
Register 0x10 - Value 0Xff
Register 0x11 - Value 0X71
Register 0x12 - Value 0X15
Register 0x13 - Value 0X0b
Register 0x14 - Value 0X28
Register 0x15 - Value 0X0c
Register 0x16 - Value 0X12
Register 0x17 - Value 0X47
Register 0x18 - Value 0X32
Register 0x19 - Value 0X3e
Register 0x1a - Value 0X00
Register 0x1b - Value 0X00
Register 0x1c - Value 0X00
Register 0x1d - Value 0X00
Register 0x1e - Value 0X00
Register 0x1f - Value 0X40
Register 0x20 - Value 0X00
Register 0x21 - Value 0X00
Register 0x22 - Value 0X00
Register 0x23 - Value 0X00
Register 0x24 - Value 0X05
Register 0x25 - Value 0X00
Register 0x26 - Value 0X03
Register 0x27 - Value 0X93
Register 0x28 - Value 0X55
Register 0x29 - Value 0X55
Register 0x2a - Value 0X55
Register 0x2b - Value 0X55
Register 0x2c - Value 0X55
Register 0x2d - Value 0X55
Register 0x2e - Value 0X55
Register 0x2f - Value 0X55
Register 0x30 - Value 0X90
Register 0x31 - Value 0X40
Register 0x32 - Value 0X40
Register 0x33 - Value 0X00
Register 0x34 - Value 0X00
Register 0x35 - Value 0X0f
Register 0x36 - Value 0X00
Register 0x37 - Value 0X00
Register 0x38 - Value 0X00
Register 0x39 - Value 0Xf5
Register 0x3a - Value 0X20
Register 0x3b - Value 0X82
Register 0x3c - Value 0Xfb
Register 0x3d - Value 0X02
Register 0x3e - Value 0X80
Register 0x3f - Value 0X40
Register 0x40 - Value 0X00
Register 0x41 - Value 0X00
Register 0x42 - Value 0X12
The next step was to modify the RegisterReadAndWrite project to check I could read and write the SX127X configuration registers.
class Program
{
static void Main()
{
#if TINYCLR_V2_SC20100DEV
Rfm9XDevice rfm9XDevice = new Rfm9XDevice(SC20100.SpiBus.Spi3, SC20100.GpioPin.PA13, SC20100.GpioPin.PA14);
#endif
#if TINYCLR_V2_FEZDUINO
Rfm9XDevice rfm9XDevice = new Rfm9XDevice(SC20100.SpiBus.Spi6, SC20100.GpioPin.PB1, SC20100.GpioPin.PA15);
#endif
rfm9XDevice.RegisterDump();
while (true)
{
Debug.WriteLine("Read RegOpMode (read byte)");
Byte regOpMode1 = rfm9XDevice.RegisterReadByte(0x1);
Debug.WriteLine($"RegOpMode 0x{regOpMode1:x2}");
Debug.WriteLine("Set LoRa mode and sleep mode (write byte)");
rfm9XDevice.RegisterWriteByte(0x01, 0b10000000);
Debug.WriteLine("Read RegOpMode (read byte)");
Byte regOpMode2 = rfm9XDevice.RegisterReadByte(0x1);
Debug.WriteLine($"RegOpMode 0x{regOpMode2:x2}");
Debug.WriteLine("Read the preamble (read word)");
ushort preamble = rfm9XDevice.RegisterReadWord(0x20);
Debug.WriteLine($"Preamble 0x{preamble:x2}");
Debug.WriteLine("Set the preamble to 0x80 (write word)");
rfm9XDevice.RegisterWriteWord(0x20, 0x80);
Debug.WriteLine("Read the center frequency (read byte array)");
byte[] frequencyReadBytes = rfm9XDevice.RegisterRead(0x06, 3);
Debug.WriteLine($"Frequency Msb 0x{frequencyReadBytes[0]:x2} Mid 0x{frequencyReadBytes[1]:x2} Lsb 0x{frequencyReadBytes[2]:x2}");
Debug.WriteLine("Set the center frequency to 915MHz (write byte array)");
byte[] frequencyWriteBytes = { 0xE4, 0xC0, 0x00 };
rfm9XDevice.RegisterWrite(0x06, frequencyWriteBytes);
rfm9XDevice.RegisterDump();
Thread.Sleep(30000);
}
}
When I ran the application in Visual Studio I could read and write register values.
The thread '<No Name>' (0x2) has exited with code 0 (0x0).
Register dump
Register 0x00 - Value 0X00
Register 0x01 - Value 0X09
Register 0x02 - Value 0X1a
Register 0x03 - Value 0X0b
Register 0x04 - Value 0X00
Register 0x05 - Value 0X52
Register 0x06 - Value 0X6c
Register 0x07 - Value 0X80
Register 0x08 - Value 0X00
Register 0x09 - Value 0X4f
Register 0x0a - Value 0X09
Register 0x0b - Value 0X2b
Register 0x0c - Value 0X20
Register 0x0d - Value 0X08
Register 0x0e - Value 0X02
Register 0x0f - Value 0X0a
Register 0x10 - Value 0Xff
Register 0x11 - Value 0X71
Register 0x12 - Value 0X15
Register 0x13 - Value 0X0b
Register 0x14 - Value 0X28
Register 0x15 - Value 0X0c
Register 0x16 - Value 0X12
Register 0x17 - Value 0X47
Register 0x18 - Value 0X32
Register 0x19 - Value 0X3e
Register 0x1a - Value 0X00
Register 0x1b - Value 0X00
Register 0x1c - Value 0X00
Register 0x1d - Value 0X00
Register 0x1e - Value 0X00
Register 0x1f - Value 0X40
Register 0x20 - Value 0X00
Register 0x21 - Value 0X00
Register 0x22 - Value 0X00
Register 0x23 - Value 0X00
Register 0x24 - Value 0X05
Register 0x25 - Value 0X00
Register 0x26 - Value 0X03
Register 0x27 - Value 0X93
Register 0x28 - Value 0X55
Register 0x29 - Value 0X55
Register 0x2a - Value 0X55
Register 0x2b - Value 0X55
Register 0x2c - Value 0X55
Register 0x2d - Value 0X55
Register 0x2e - Value 0X55
Register 0x2f - Value 0X55
Register 0x30 - Value 0X90
Register 0x31 - Value 0X40
Register 0x32 - Value 0X40
Register 0x33 - Value 0X00
Register 0x34 - Value 0X00
Register 0x35 - Value 0X0f
Register 0x36 - Value 0X00
Register 0x37 - Value 0X00
Register 0x38 - Value 0X00
Register 0x39 - Value 0Xf5
Register 0x3a - Value 0X20
Register 0x3b - Value 0X82
Register 0x3c - Value 0Xfa
Register 0x3d - Value 0X02
Register 0x3e - Value 0X80
Register 0x3f - Value 0X40
Register 0x40 - Value 0X00
Register 0x41 - Value 0X00
Register 0x42 - Value 0X12
Read RegOpMode (read byte)
RegOpMode 0x09
Set LoRa mode and sleep mode (write byte)
Read RegOpMode (read byte)
RegOpMode 0x80
Read the preamble (read word)
Preamble 0x08
Set the preamble to 0x80 (write word)
Read the center frequency (read byte array)
Frequency Msb 0x6c Mid 0x80 Lsb 0x00
Set the center frequency to 915MHz (write byte array)
Register dump
Register 0x00 - Value 0Xc3
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
At this point I was confident that I could hardware reset the shield and read/modify registers on the SX127X.
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.