.NET nanoFramework Qorvo DW1000 – RAK13801 Device SPI

When developing libraries it’s good to have a selection of different platforms for testing as this can significantly improve the quality and robustness of the implementation. A few months ago I noticed that RAK Wireless have a UWB Module Decawave DW1000 Wisbblock so I added one to an order.

My second Qorvo DW1000 setup is a RAK120000 Wisblock Core module, on a RAK19007 WisBlock Base with a RAK13801 WisBlock Wireless module

RAK12000 + RAK19007 + RAK13801 test platform

The Qorvo DW1000 module has a Serial Peripheral Interface (SPI) so the Master In Slave Out(MISO), Master Out Slave In(MOSI), Serial Clock(SCLK) and Chip Slave Select(CSS) pins of the RAK11200 WisBiock Core Module have to be setup using the Configuration.SetPinFunction method of the nanoFramework.Hardware.Esp32 library.

RAK11200 Schematic with SPI pins highlighted.
RAK13801 Schematic with SPI pins highlighted.

I have added a couple of C# processor directives (MAKERFABS_ESP32UWB & RAK11200_RAK1907_RAK13801) so the platform that the Qorvo DW1000 module is running on can be configured.

public class Program
{
#if MAKERFABS_ESP32UWB
    private const int SpiBusId = 1;
    private const int chipSelectLine = Gpio.IO04;
#endif
#if RAK11200_RAK1907_RAK13801
    private const int SpiBusId = 1;
    private const int chipSelectLine = Gpio.IO32;
#endif

    public static void Main()
    {
        Thread.Sleep(5000);

        Debug.WriteLine("devMobile.IoT.Dw1000.ShieldSPI starting");

        try
        {
#if MAKERFABS_ESP32UWB
            Configuration.SetPinFunction(Gpio.IO19, DeviceFunction.SPI1_MISO);
            Configuration.SetPinFunction(Gpio.IO23, DeviceFunction.SPI1_MOSI);
            Configuration.SetPinFunction(Gpio.IO18, DeviceFunction.SPI1_CLOCK);
#endif
#if RAK11200_RAK1907_RAK13801
            Configuration.SetPinFunction(Gpio.IO35, DeviceFunction.SPI1_MISO);
            Configuration.SetPinFunction(Gpio.IO25, DeviceFunction.SPI1_MOSI);
            Configuration.SetPinFunction(Gpio.IO33, DeviceFunction.SPI1_CLOCK);
#endif
            var settings = new SpiConnectionSettings(SpiBusId, chipSelectLine)
            {
                ClockFrequency = 2000000,
                Mode = SpiMode.Mode0,
            };

            using (SpiDevice device = SpiDevice.Create(settings))
            {
                while (true)
                {
                    byte[] writeBuffer = new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0 }; // 0x0 = DEV_ID
                    byte[] readBuffer = new byte[writeBuffer.Length];

                    device.TransferFullDuplex(writeBuffer, readBuffer); // 15, 48, 1, 202, 222

                    uint ridTag = (uint)(readBuffer[4]<< 8 | readBuffer[3]);
                    byte model = readBuffer[2];
                    byte ver = (byte)(readBuffer[1] >> 4);
                    byte rev = (byte)(readBuffer[1] & 0x0f);

                    Debug.WriteLine(String.Format($"RIDTAG 0x{ridTag:X2} MODEL 0x{model:X2} VER 0X{ver:X2} REV 0x{rev:X2}"));

                   Thread.Sleep(10000);
                }
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}

The alignment of the RAK11200 WisBiock Core Module pins and labels on the circuit diagram tripped me up. My initial configuration caused the device to reboot every time the application started.

Visual Studio 2022 Debug window displaying the decoded value from Register 0x0

At the top of test applications, I usually have a brief delay i.e Thread.Sleep(5000) so I can attach the debugger or erase the flash before the application crashes.

.NET nanoFramework Qorvo DW1000 – Makerfabs Device SPI

The Makerfabs ESP32 UWB(Ultra Wideband) module has a Qorvo DW1000 and Espressif ESP32 module. The Espressif ESP32 module can run the .NET nanoFramework but does not have a Qorvo DW1000 library. (March2023)

Makerfabs ESP32 UWB(Ultra Wide Band) module

Before any coding I used nanoff to “flash” the Espressif ESP32 module with the latest version of .NET nanoFramework

Flashing Makerfabs ESP32 UWB module with nanoff

The Qorvo DW1000 module has a Serial Peripheral Interface (SPI) so the Master In Slave Out(MISO), Master Out Slave In(MOSI), Serial Clock(SCLK) and Chip Slave Select(CSS) pins have to be configured using the Configuration.SetPinFunction method of the nanoFramework.Hadware.Esp32 library

Makerfabs ESP32 UWB module schematic

Even though SPI is an industry standard there are often subtle differences which need to be taken into account when reading from/writing to registers. The DW1000 has a static “Device Identifier” which I used to debug my “proof of concept” code.

DW1000 Datasheet Register Map documentation for Register 0x00

The DeviceSPI program reads register 0x00 and then displays the decoded payload.

public class Program
{
#if MAKERFABS_ESP32UWB
    private const int SpiBusId = 1;
    private const int chipSelectLine = Gpio.IO04;
#endif

    public static void Main()
    {
        Thread.Sleep(5000);

        Debug.WriteLine("devMobile.IoT.Dw1000.ShieldSPI starting");

        try
        {
#if MAKERFABS_ESP32UWB
            Configuration.SetPinFunction(Gpio.IO19, DeviceFunction.SPI1_MISO);
            Configuration.SetPinFunction(Gpio.IO23, DeviceFunction.SPI1_MOSI);
            Configuration.SetPinFunction(Gpio.IO18, DeviceFunction.SPI1_CLOCK);
#endif
            var settings = new SpiConnectionSettings(SpiBusId, chipSelectLine)
            {
                ClockFrequency = 2000000,
                Mode = SpiMode.Mode0,
            };

            using (SpiDevice device = SpiDevice.Create(settings))
            {
                Thread.Sleep(500);

                while (true)
                {
                    /*
                    byte[] writeBuffer = new byte[] { 0x0, 0x0, 0x0, 0x0, 0x0 }; // 0x0 = DEV_ID
                    byte[] readBuffer = new byte[writeBuffer.Length];

                    device.TransferFullDuplex(writeBuffer, readBuffer); // 15, 48, 1, 202, 222
                    */

                    byte[] writeBuffer = new byte[] { 0x0 }; // 0x0 = DEV_ID
                    byte[] readBuffer = new byte[5];

                    device.TransferFullDuplex(writeBuffer, readBuffer); // 15, 48, 1, 202, 222
                       
                    uint ridTag = (uint)(readBuffer[4]<< 8 | readBuffer[3]);
                    byte model = readBuffer[2];
                    byte ver = (byte)(readBuffer[1] >> 4);
                    byte rev = (byte)(readBuffer[1] & 0x0f);

                    Debug.WriteLine(String.Format($"RIDTAG 0x{ridTag:X2} MODEL 0x{model:X2} VER 0X{ver:X2} REV 0x{rev:X2}"));

                    Thread.Sleep(10000);
                }
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message);
        }
    }
}
Visual Studio 2022 Debug window displaying the decoded value from Register 0x0

The DW1000 User Manual is > 240 pages, with roughly 140 pages of detailed documentation about the DW1000 register set so progress will be slow.

.NET nanoFramework Qorvo DW1000 – flashing & flashing

A few months ago one of my customers purchased five Makerfabs ESP32 UWB(Ultra Wideband) devices to see how the range of Qorvo DW1000 modules was impacted by the steel gates and fences in a stock yard. The .NET nanoFramework runs on Espressif ESP32 devices but there is currently (Feb2023) no Qorvo DW1000 module support so I borrowed the devices.

First step was to use nanoff to flash the device with the latest suitable firmware

The Makerfabs board has 5 LEDs, one illuminated when the device has power, and the other four are connected to the Qorvo DW1000 module.

Makerfabs ESP32 UWB schematic

An important milestone for any hardware project is to get a Light Emitting Diode(LED) to flash but there none connected to the Espressif ESP32 module.

I used a Grove-4 pin Male Jumper to Grove 4 pin Conversion Cable to connect a Seeedstudio LED to the device

public class Program
{
    private static GpioController _gpioController;

    public static void Main()
    {
        _gpioController = new GpioController();

        //GpioPin led = _gpioController.OpenPin(Gpio.IO00, PinMode.Output);
        //GpioPin led = _gpioController.OpenPin(Gpio.IO01, PinMode.Output);
        //GpioPin led = _gpioController.OpenPin(Gpio.IO02, PinMode.Output);
        //GpioPin led = _gpioController.OpenPin(Gpio.IO03, PinMode.Output);
        GpioPin led = _gpioController.OpenPin(Gpio.IO04, PinMode.Output);

        led.Write(PinValue.Low);

        while (true)
        {
           led.Toggle();
           Thread.Sleep(125);
            led.Toggle();
            Thread.Sleep(125);
            led.Toggle();
            Thread.Sleep(125);
            led.Toggle();
            Thread.Sleep(525);
        }
    }
}

The test code was “inspired” by the .NET nanoFramework Blinky sample