Help - TeensyLC custom USB Device pre-build script

Hello all,

Requesting a little help/guidance, as I am unfamiliar with Python scripting. I am wanting to make a script (or multiple scripts) to overwrite/replace and add files to the Teensy core files - I believe with regards to PlatformIO conventions it would be the adding to and modifying the framework-teensyarduino package.

I am adding 2 files, and modifying 4 or 5 files that would be found in .platformio\packages\framework-arduinoteensy\cores\teensy3. (as well as possibly modifying the boards.txt file found in .platformio\packages\framework-arduinoteensy).

Files being modified include: usb_desc.h,usb_desc.cpp,usb_dev.h,usb_dev.cpp.

I am wanting to do this with a pre-build script so that the files being modified/added are visible as part of the project, instead of being in a seperate github repo (as referenced in this post). I’ve been able to modify the core files and build my code using the Arduino IDE, but I think this method would be easier to share and replicate so that other users don’t need to dig through and manually copy and paste files.

I appreciate any help/guidance people are willing to offer!

Why not do it the PlatformIO way and create and reference your custom framework-arduinoteensy package via platform_packages? If you write a script to modify the framework-internal files (and don’t change them back afterwards), it affects the users beyond the usage of your project, namely other projects using framework-teensyarduino will have your changes too.

That is a good point - I guess I assumed that the internal files would be static, and only overwritten/added to during compilation. If that is not the case, then it would indeed make more sense to fork framework-teensyarduino.

My main reason in wanting to use a script was to have the changed files be more visible to someone viewing the project, since making a new class of USB device (i.e. new type of joystick with no serial output, keyboard and mouse with no serial, mouse only, etc) isn’t well documented, and I wanted it to be an easily visible example - though I guess having a public repo of my framework fork would be documentation enough for now.

When referencing a custom framework-teensyarduino, I should be able to set custom build flags, correct?

I.E., instead of using -D USB_RAWHID, use -D USB_CUSTOM_OPTION by adding another USB type to the boards.txt file (that’s where I had to add options to compile with the ArduinoIDE), along with the associated files located in .platformio\packages\framework-arduinoteensy\cores\teensy3.

The boards.txt is not read by PlatformIO and completely ignored. The builder logic and board-specific settings is done via the board manifest and the builder script. The easiest way to add a global macro into the build system is to use build_flags, e.g., build_flags = -DUSB_CUSTOM_OPTION in the platformio.ini. That can not be automated by using a custom framework-arduinoteensy project since there is no build logic (that is used by PlatformIO) in there.

Ok, so the boards.txt file is not used. Got it.

To add a custom USB device build option, how would that be done?

The current documentation explains that setting a build flag such as build_flag = -DUSB_MIDI will cause PlatformIO to compile the program using the associated files that are located in .platformio\packages\framework-arduinoteensy\cores\teensy3

Would I reference my custom framework, and edit the builder script with my custom build flag? (just add to the list of already present build flags)

No, the PlatformIO will compile the cores\teensy3 because the board definition for the Teensy LC says that it belongs to the “teensy3” sub-core.

What USB devices are enabled is not directly controlled by PlatformIO (by e.g. selectively compilating or not compiling certain files), the arduino core will do that by itself because of the way the code is written.

Take a close look at the boards.txt. E.g., it has

menu.usb=USB Type
[..]
teensyLC.menu.usb.serial=Serial
teensyLC.menu.usb.serial.build.usbtype=USB_SERIAL
teensyLC.menu.usb.serial2=Dual Serial
teensyLC.menu.usb.serial2.build.usbtype=USB_DUAL_SERIAL

This defines the menu items (“Serial”, “Dual Serial”) as well as a usbtype subinformation. Now the platform.txt takes that and in the section that describes how the C/C++ files are compiled it does

## Compile c++ files
recipe.cpp.o.pattern="{compiler.path}" [..] -D{build.usbtype} [...]

And so when the user selects “Serial” in the “USB Type” sub menu, all it causes is that the sketch and Teensy Arduino core is compiled with the -DUSB_SERIAL option, which in turn globally defines the macro USB_SERIAL (-D = define).

C/C++ code can react to that by checking whether it’s defined or not with e.g.

#ifdef USB_SERIAL 
// code that is only active when USB_SERIAL macro is defined..
#endif
// equivalent is #if defined(USB_SERIAL)

and so what the Teensy core does is check for the possible USB type macros and based on that, define further macros that finally make up the USB descriptor

Etc.

The USB implementation files (usb_joystick.c, usb_keyboard.c, usb_mouse.c etc.) then react to the further defined macros at the very top of the C file before the function implementation start.

and at the bottom

this means that the entire .c below the #ifdef MOUSE_INTERFACE declaration will be empty if the macro MOUSE_INTERFACE is not defined. And what defines MOUSE_INTERFACE? As you can see in usb_dev.h, only USB_HID, USB_SERIAL_HID, USB_HID_TOUCHSCREEN and USB_EVERYTHING do that. Meaning that when the user e.g. defines USB_SERIAL, the code is inactive.

That means all USB implementation files are passed to the compiler for compilation, but due to them surrounding the entire implementation code with #if defined(usb type..) they will be internally blank after the C-preprocessor is done processing the #if defined.

And hence you do not have to adapt the PlatformIO builder scripts, but you have to add your USB device in the exact same style as the normal core already does (which I presume you already have). Then “selecting a USB Type in the Arduino IDE menu” is equivalent to “add the macro definition through build_flags in the platformio.ini”, e.g., build_flags = -DUSB_MY_CUSTOM_TYPE. In the Arduino IDE, I would then equivalently expect to see a

teensyLC.menu.usb.my_custom=My custom type
teensyLC.menu.usb.my_custom.build.usbtype=USB_MY_CUSTOM_TYPE

declaration to keep compatibility with the Arduino IDE.

Or, thought in reverse: If you develop your custom USB type in the Arduino IDE and that works by just adding another menu entry (e.g., the two lines above), then that exact same code (packaged as framework-arduinoteensy and used via e.g. platform_packages) will work in PlatformIO and the exact same USB device will be activated when you add build_flags = -DUSB_MY_CUSTOM_TYPE in the platformio.ini of the project.

I have attempted what you suggested using platform_packages, but it is not working (at least so far). When compiling, I either get build errors or my custom build flag is not recognized, so the build script defaults to going with the default USB Serial option.

After looking at arduino.py, it includes the built-in USB flags - it would seem like I would also need to fork the default platformio Teensy framework as well to support my changes to the core files. A working example of this can be seen here and here, where the reference project added an additional USB device to the build options.

This doesn’t sound right. Can you upload your complete currently modified framework-arduinoteensy package in a repro so that I can cross-examine this?

I uploaded an unmodified framework-arduinoteensy to check - I was able to compile using that, no issues (as in, used platform_packages to use that framework).

My framework with the changes is located here, it’s the test branch.

The changes made to the teensy core files can be viewed here.

Below is my platform.ini configuration.

platform = teensy
platform_packages = framework_arduinoteensy @ https://github.com/red-sand-robot/framework-arduinoteensy-rsr.git#56bab57edde09da07485eea7523b0a3d628bf06d
board = teensylc
framework = arduino
lib_deps = 
    ; stripped down version of jrowbergs's I2Dev library
    ; - required for ISM330DHCX library to function
    I2CDev-ArduinoCore
    ; library for ISM330DHCX
    ISM330DHCX
build_flags = 
    ; allows Teensy-specific I2C settings to be used
    -DI2CDEV_IMPLEMENTATION=I2CDEV_TEENSY_3X_WIRE
    ; builds file with custom device definition
    -DUSB_GAMEPAD_IMU_ADDON
lib_ignore =
    ; causes issues with I2Cdev for some reason, so ignore during compilation
    Wire

I ended up forking platform-teensy to add USB_GAMEPAD_IMU_ADDON to arduino.py, as well as some other minor changes. In my platform.ini, I put platform = https://github.com/red-sand-robot/platform-teensy-rsr. This has now worked, and let me compile my project, which I have verified works the same as if it were compiled with the ArduinoIDE.