TinyUSB + FeatherWing nRF52840 incompatibility

I try to use the TinyUSB library with the nRF52840 to create a mass storage device.

I fail to compile a simple example, and after some digging around I suspect that it is a bug inside the TinyUSB library or its library core.

When compiling, there are unexpected errors about classes being purely virtual that shouldn’t be.

I am completely out of ideas on how to tackle this problem and would be happy for any kind of feedback.

Steps to reproduce

The following steps should reproduce the problem.

Dependencies

  • Create a new project for the adafruit_feather_nrf52840 board
  • Install Dependencies:
    • adafruit/Adafruit SPIFlash
    • adafruit/Adafruit TinyUSB Library
    • adafruit/SdFat - Adafruit Fork

Final platformio.ini should look like this:

[env:adafruit_feather_nrf52840]
platform = nordicnrf52
board = adafruit_feather_nrf52840
framework = arduino
lib_deps = 
	adafruit/Adafruit SPIFlash@^3.5.2
	adafruit/Adafruit TinyUSB Library@^1.3.2
	adafruit/SdFat - Adafruit Fork@^1.2.4

Program code

Copy the following code to main.cpp.
It’s a simplified version of a USB flash driver.

#include "SPI.h"
#include "Adafruit_SPIFlash.h"
#include "Adafruit_TinyUSB.h"

int32_t msc_read_cb(uint32_t lba, void *buffer, uint32_t bufsize);
int32_t msc_write_cb(uint32_t lba, uint8_t *buffer, uint32_t bufsize);
void msc_flush_cb(void);

Adafruit_FlashTransport_QSPI flashTransport;
Adafruit_SPIFlash flash(&flashTransport);

// USB Mass Storage object
Adafruit_USBD_MSC usb_msc;

// the setup function runs once when you press reset or power the board
void setup()
{
    flash.begin();

    // Set disk vendor id, product id and revision with string up to 8, 16, 4 characters respectively
    usb_msc.setID("Adafruit", "External Flash", "1.0");

    // Set callback
    usb_msc.setReadWriteCallback(msc_read_cb, msc_write_cb, msc_flush_cb);

    // Set disk size, block size should be 512 regardless of spi flash page size
    usb_msc.setCapacity(flash.size() / 512, 512);

    // MSC is ready for read/write
    usb_msc.setUnitReady(true);

    usb_msc.begin();
}

void loop()
{
}

int32_t msc_read_cb(uint32_t lba, void *buffer, uint32_t bufsize)
{
    return flash.readBlocks(lba, (uint8_t *)buffer, bufsize / 512) ? bufsize : -1;
}

int32_t msc_write_cb(uint32_t lba, uint8_t *buffer, uint32_t bufsize)
{
    return flash.writeBlocks(lba, buffer, bufsize / 512) ? bufsize : -1;
}

void msc_flush_cb(void)
{
    flash.syncBlocks();
}

Expected outcome

It should compile. This example is absolutely the barebone minimum to create a USB Mass Storage Device for the built-in 2MB flash chip of the nRF52840 FeatherWing.

Further, when flashed, it should create a 2MB external drive. But that part is not important for this post as I don’t get it compiled in the first place.

Error Messages

The following errors appear during the build process:

Processing adafruit_feather_nrf52840 (platform: nordicnrf52; board: adafruit_feather_nrf52840; framework: arduino)
---------------------------------------------------------------------------------------------------------------------------------------------------------Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/nordicnrf52/adafruit_feather_nrf52840.html
PLATFORM: Nordic nRF52 (8.1.0) > Adafruit Feather nRF52840 Express
HARDWARE: NRF52840 64MHz, 243KB RAM, 796KB Flash
DEBUG: Current (blackmagic) External (blackmagic, cmsis-dap, jlink, stlink)
PACKAGES:
 - framework-arduinoadafruitnrf52 1.2100.201028 (21.0)
 - tool-sreccat 1.164.0 (1.64)
 - toolchain-gccarmnoneeabi 1.70201.0 (7.2.1)
LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 35 compatible libraries
Scanning dependencies...
Dependency Graph
|-- <Adafruit SPIFlash> 3.5.2
|   |-- <SPI> 1.0
|   |-- <SdFat - Adafruit Fork> 1.2.4
|   |   |-- <SPI> 1.0
|-- <Adafruit TinyUSB Library> 1.3.2
|-- <SdFat - Adafruit Fork> 1.2.4
|   |-- <SPI> 1.0
|-- <SPI> 1.0
Building in release mode
Compiling .pio\build\adafruit_feather_nrf52840\src\main.cpp.o
Compiling .pio\build\adafruit_feather_nrf52840\lib83f\SPI\SPI.cpp.o
Compiling .pio\build\adafruit_feather_nrf52840\lib83f\SPI\SPI_nrf52832.cpp.o
Compiling .pio\build\adafruit_feather_nrf52840\lib49a\SdFat - Adafruit Fork\FatLib\FatFile.cpp.o
Compiling .pio\build\adafruit_feather_nrf52840\lib49a\SdFat - Adafruit Fork\FatLib\FatFileLFN.cpp.o
src\main.cpp:13:19: error: cannot declare variable 'usb_msc' to be of abstract type 'Adafruit_USBD_MSC'
 Adafruit_USBD_MSC usb_msc;
                   ^~~~~~~
In file included from .pio\libdeps\adafruit_feather_nrf52840\Adafruit TinyUSB Library\src/Adafruit_TinyUSB.h:44:0,
                 from src\main.cpp:3:
.pio\libdeps\adafruit_feather_nrf52840\Adafruit TinyUSB Library\src/arduino/msc/Adafruit_USBD_MSC.h:30:7: note:   because the following virtual functions are pure within 'Adafruit_USBD_MSC':
 class Adafruit_USBD_MSC : public Adafruit_USBD_Interface {
       ^~~~~~~~~~~~~~~~~
In file included from C:\Users\Martin\.platformio\packages\framework-arduinoadafruitnrf52\cores\nRF5\TinyUSB\Adafruit_TinyUSB_ArduinoCore/Adafruit_TinyUSB_Core.h:35:0,
                 from C:\Users\Martin\.platformio\packages\framework-arduinoadafruitnrf52\cores\nRF5/Arduino.h:72,
                 from C:\Users\Martin\.platformio\packages\framework-arduinoadafruitnrf52\libraries\SPI/SPI.h:24,
                 from src\main.cpp:1:
C:\Users\Martin\.platformio\packages\framework-arduinoadafruitnrf52\cores\nRF5\TinyUSB\Adafruit_TinyUSB_ArduinoCore/Adafruit_USBD_Device.h:33:22: note: 
virtual uint16_t Adafruit_USBD_Interface::getDescriptor(uint8_t, uint8_t*, uint16_t)
     virtual uint16_t getDescriptor(uint8_t itfnum, uint8_t* buf, uint16_t bufsize) = 0;
                      ^~~~~~~~~~~~~
In file included from C:\Users\Martin\.platformio\packages\framework-arduinoadafruitnrf52\libraries\SPI\SPI.h:24:0,
                 from C:\Users\Martin\.platformio\packages\framework-arduinoadafruitnrf52\libraries\SPI\SPI.cpp:26:
C:\Users\Martin\.platformio\packages\framework-arduinoadafruitnrf52\cores\nRF5/Arduino.h: In instantiation of 'decltype (((b < a) ? b :  a)) min(const T&, const L&) [with T = unsigned int; L = int; decltype (((b < a) ? b :  a)) = unsigned int]':
C:\Users\Martin\.platformio\packages\framework-arduinoadafruitnrf52\libraries\SPI\SPI.cpp:202:50:   required from here
C:\Users\Martin\.platformio\packages\framework-arduinoadafruitnrf52\cores\nRF5/Arduino.h:93:15: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
     return (b < a) ? b : a;
            ~~~^~~~
*** [.pio\build\adafruit_feather_nrf52840\src\main.cpp.o] Error 1
============================================================== [FAILED] Took 6.66 seconds ==============================================================

Cross-Checking with Arduino IDE

The given sample code compiles and runs on the Arduino IDE.
It required to install the Adafruit TinyUSB Library and dependencies.

1 Like

Sure it does, but in the Arduino IDE you’re probably also using the latest core version. PlatformIO lags behind – per releases it’s using the core version v0.21.0 from August 2020, whereas the latest one is v0.24.0.

You must match the library’s version with what core version PlatformIO is using. Specifying the latest Adafruit TinyUSB library with

will not work. On the other hand,

[env:adafruit_feather_nrf52840]
platform = nordicnrf52
board = adafruit_feather_nrf52840
framework = arduino
lib_deps = 
	adafruit/Adafruit SPIFlash@^3.5.2
	adafruit/Adafruit TinyUSB Library@^0.9.1

will lead to a successful compilation of the code you have shown (releases per here).

An issue to have the Arduino core updated is already open per Adafruit nRF52 Arduino BSP 0.22.1 is out. · Issue #116 · platformio/platform-nordicnrf52 · GitHub.

Awesome! Greatly appreciated. Thanks :slight_smile:

Per linked issue above, you can test out PlatformIO upstream support for the latest v0.24 Arduino core release, where you shouldn’t have to use older versions of the Adafruit TinyUSB library anymore. In fact, TinyUSB is built into the core, so you shouldn’t specify a different version in lib_deps and delete the .pio folder of the project to avoid duplicates.

I’ll expect a new stable release of the platform soon.

1 Like

A new version was released per Releases · platformio/platform-nordicnrf52 · GitHub, so normal platformio.ini

[env:adafruit_feather_nrf52840]
platform = nordicnrf52
board = adafruit_feather_nrf52840
framework = arduino

will work after updating (e.g., via the CLI and doing pio platform update nordicnrf52).