MTP Problem

sbfreddie

Well-known member
This is an oddball problem that has just come up, maybe because I have not built this piece of code including MTP for quite some time.
I received this peculiar error from my build:
/Users/freddie/.platformio/packages/framework-arduinoteensy/libraries/USBHost_t36/bluetooth.cpp:35:10: fatal error: EEPROM.h: No such file or directory
And in the process of trying to figure out what the problem was, considering that I did not include "USBHost_t36" in my build because I am not using it for anything, I looked further and discovered that the build engine was building the "SdFat" and "SD" libraries as well, which I also did not include in my build.
In digging into this dilemma, I discover in the MTP part of the build, which I did include in my build this section of MTP_SD_Callbacks.cpp:
// This code should only be build and run if the SD library was included in the sketch
#if defined(__has_include) && __has_include(<SD.h>)
#include <SD.h>
and in this section of MTP_USBFS_Callbacks.cpp:
// This code should only be build and run if the USBHost_t36 library was included in the sketch
#if defined(__has_include) && __has_include(<USBHost_t36.h>)
#include <USBHost_t36.h>
aha! So this is the guilty culprit. The compiler is not responding to the directive "__has_include" at all probably because this version of Teensy:
- framework-arduinoteensy @ 1.159.0 (1.59)
- tool-teensy @ 1.159.0 (1.59)
- toolchain-gccarmnoneeabi-teensy @ 1.110301.0 (11.3.1)
Teensy 1.59 which is the current released version, is using the compiler version 11.3.1, and according to my google search the directive "__has_include" was not included until version 17 of the compiler.
Any ideas on how to get around this problem without hacking up the MTP Library?
Regards,
Ed
 
I don't think that is the cause. If the compiler didn't support __has_include it wouldn't be trying to pull in the USBHost_t36 library, rather that line would trigger a compilation error.

This could be a filename case issue (the file is actually named "eeprom.h" rather than "EEPROM.h") or just platformio not handling include directories the same way as the arduino IDE, in which case it should be <avr/eeprom.h>.
 
I disagree with your guess, it is not the compiler at all it is the pre-compiler directive (or whatever they call it) simply ignoring the directive and going on its merry way including every thing it sees after the __has_include directive that it does not understand.
I have been using PlatformIO for years and this is the first time, anything like this has come up, because these inclusions in MTP are very recent.
Regards,
Ed
 
How could the compiler evaluate __has_include(x) if it had no definition for __has_include ?
 
I simply ignores it, as you can see it is compiling everything after the ignored directive which is why my build is compiling all these files I did not ask it to compile, which finally leads to the error that it could not find EEPROM.h which I have not included in my build.
Regards,
Ed
 
Compilers don't simply ignore errors. If you put #if some_thing_that_isnt_defined anywhere in a source file the compiler will complain when it tries to evaluate it.
 
Last edited:
Here are some quotes from a google search on the matter:
__has_include ( " q-char-sequence " )
__has_include
( < h-char-sequence > )(4)(since C++17)__has_include ( string-literal )
__has_include
( < h-pp-tokens > )(5)(since C++17)

4.2.12 __has_include, __has_include_next​

The special operators __has_include (operand) and __has_include_next (operand) may be used in ‘#if’ and ‘#elif’ expressions to test whether the header referenced by their operand can be included using the ‘#include’ and ‘#include_next’ directive, respectively. Using the operators in other contexts is not valid. The operand takes the same form as the file in the ‘#include’ and ‘#include_next’ directives respectively (see Include Syntax) and the operators evaluate to a nonzero value if the header can be included and to zero otherwise. Note that that the ability to include a header doesn’t imply that the header doesn’t contain invalid constructs or ‘#error’ directives that would cause the preprocessor to fail.

The __has_include and __has_include_next operators by themselves, without any operand or parentheses, act as predefined macros so that support for them can be tested in portable code. Thus, the recommended use of the operators is as follows:

#if defined __has_include
# if __has_include (<stdatomic.h>)
# include <stdatomic.h>
# endif
#endif

The first ‘#if’ test succeeds only when the operator is supported by the version of GCC (or another compiler) being used. Only when that test succeeds is it valid to use __has_include as a preprocessor operator. As a result, combining the two tests into a single expression as shown below would only be valid with a compiler that supports the operator but not with others that don’t.

#if defined __has_include && __has_include ("header.h") /* not portable */

#endif

The same holds for __has_include_next.

All I am saying is that the pre-compiler directive is ignoring the __has_include and including everything after it when it should not. I do not know why, but that is the problem, otherwise it would not be including all these of files that I have not included in my build, which leads to the error of the the missing EEPROM.h file. I will leave this problem up to the compiler experts here on the forum to figure out as it is baffling to me.
Regard,
Ed
 
It's not ignoring it; defined(__has_include) evaluates to true and __has_include(<USBHost_t36.h>) also evaluates to true, hence the following lines are processed by the compiler.

You seem to be hung up on the compiler not supporting __has_include, but it does. Just because the compiler version is only 11.3.1 does not mean it doesn't support C++17, in fact that is what Teensyduino tells it to use by default in boards.txt:
teensy41.build.flags.cpp=-std=gnu++17 -fno-exceptions -fpermissive -fno-rtti -fno-threadsafe-statics -felide-constructors -Wno-error=narrowing
 
Your first statement is incorrect. There is no #include USBHost_t36 anywhere in my entire code base, nor is their any #include SD.h, or for that matter #include EEPROM.h. None of these are anywhere in my codebase. Yet it is compiling these libraries that I have in not included in my code base. Have a look at the output:
Compiling .pio/build/release/lib243/SdFat/FatLib/FatName.cpp.o
Compiling .pio/build/release/lib243/SdFat/FatLib/FatPartition.cpp.o
Compiling .pio/build/release/lib243/SdFat/FatLib/FatVolume.cpp.o
Compiling .pio/build/release/lib243/SdFat/FreeStack.cpp.o
Compiling .pio/build/release/lib243/SdFat/FsLib/FsFile.cpp.o
Compiling .pio/build/release/lib243/SdFat/FsLib/FsNew.cpp.o
Compiling .pio/build/release/lib243/SdFat/FsLib/FsVolume.cpp.o
Compiling .pio/build/release/lib243/SdFat/MinimumSerial.cpp.o
Compiling .pio/build/release/lib243/SdFat/SdCard/SdCardInfo.cpp.o
Compiling .pio/build/release/lib243/SdFat/SdCard/SdSpiCard.cpp.o
Compiling .pio/build/release/lib243/SdFat/SdCard/SdioTeensy.cpp.o
Compiling .pio/build/release/lib243/SdFat/SpiDriver/SdSpiArtemis.cpp.o
Compiling .pio/build/release/lib243/SdFat/SpiDriver/SdSpiChipSelect.cpp.o
Compiling .pio/build/release/lib243/SdFat/SpiDriver/SdSpiDue.cpp.o
Compiling .pio/build/release/lib243/SdFat/SpiDriver/SdSpiESP.cpp.o
Compiling .pio/build/release/lib243/SdFat/SpiDriver/SdSpiParticle.cpp.o
Compiling .pio/build/release/lib243/SdFat/SpiDriver/SdSpiSTM32.cpp.o
Compiling .pio/build/release/lib243/SdFat/SpiDriver/SdSpiSTM32Core.cpp.o
Compiling .pio/build/release/lib243/SdFat/SpiDriver/SdSpiTeensy3.cpp.o
Compiling .pio/build/release/lib243/SdFat/common/FmtNumber.cpp.o
Compiling .pio/build/release/lib243/SdFat/common/FsCache.cpp.o
Compiling .pio/build/release/lib243/SdFat/common/FsDateTime.cpp.o
Compiling .pio/build/release/lib243/SdFat/common/FsName.cpp.o
Compiling .pio/build/release/lib243/SdFat/common/FsStructs.cpp.o
Compiling .pio/build/release/lib243/SdFat/common/FsUtf.cpp.o
Compiling .pio/build/release/lib243/SdFat/common/PrintBasic.cpp.o
Compiling .pio/build/release/lib243/SdFat/common/upcase.cpp.o
Compiling .pio/build/release/lib243/SdFat/iostream/StdioStream.cpp.o
Compiling .pio/build/release/lib243/SdFat/iostream/StreamBaseClass.cpp.o
Compiling .pio/build/release/lib243/SdFat/iostream/istream.cpp.o
Compiling .pio/build/release/lib243/SdFat/iostream/ostream.cpp.o
Compiling .pio/build/release/libeff/SD/SD.cpp.o
Compiling .pio/build/release/libabb/USBHost_t36/BluetoothConnection.cpp.o
Compiling .pio/build/release/libabb/USBHost_t36/MassStorageDriver.cpp.o
Compiling .pio/build/release/libabb/USBHost_t36/SerEMU.cpp.o
Compiling .pio/build/release/libabb/USBHost_t36/USBFilesystemFormatter.cpp.o
Compiling .pio/build/release/libabb/USBHost_t36/adk.cpp.o
Compiling .pio/build/release/libabb/USBHost_t36/antplus.cpp.o
Compiling .pio/build/release/libabb/USBHost_t36/bluetooth.cpp.o
Compiling .pio/build/release/libabb/USBHost_t36/digitizer.cpp.o
Compiling .pio/build/release/libabb/USBHost_t36/ehci.cpp.o
Compiling .pio/build/release/libabb/USBHost_t36/enumeration.cpp.o
/Users/freddie/.platformio/packages/framework-arduinoteensy/libraries/USBHost_t36/bluetooth.cpp:35:10: fatal error: EEPROM.h: No such file or directory
Perhaps you can explain to me why it's building these libs without me asking for them, they are not included in any of my code base. The only explanation is the MTP is doing it, for what ever the reason.
Regards,
Ed
 
Compilers don't simply ignore errors. If you put #if some_thing_that_isnt_defined anywhere in a source file the compiler will complain when it tries to evaluate it.
I'm not sure that is correct. I quite often use the mechanisms shown below to temporarily exclude sections of my code, and no complaints are generated by the compiler:

Code:
#define IGNORE_THIS
#define INCLUDE_THIS

void setup() {
}

void loop() {
#ifndef IGNORE_THIS
   for (int i = 0; i < 10; i++)
   {
      Serial.println("ignoring this code or not");
   }
#endif

#ifdef INCLUDE_THIS
   for (int j = 0; j < 10; j++)
   {
      Serial.println("including this code or not");
   }
#endif
}

If the #define IGNORE THIS is left uncommented, then the code in the first loop is ignored, as desired, & never compiled in. On the other hand, if the #define IGNORE_THIS is commented out, then the code in the first loop remains & is compiled in. Similarly, if the #define INCLUDE THIS is commented out, then the code in the second loop is ignored, as desired, & never compiled in. On the other hand, if the #define INCLUDE_THIS is left uncommented, then the code in the second loop remains & is compiled in. The #else directive can also be used if/as needed to swap between one section of code and another. NOTE the specific use of #ifdef vs. #ifndef.

Hope that helps . . .

Mark J Culross
KD5RXT
 
Your first statement is incorrect. There is no #include USBHost_t36 anywhere in my entire code base, nor is their any #include SD.h, or for that matter #include EEPROM.h. None of these are anywhere in my codebase. Yet it is compiling these libraries that I have in not included in my code base. Have a look at the output:

Perhaps you can explain to me why it's building these libs without me asking for them, they are not included in any of my code base. The only explanation is the MTP is doing it, for what ever the reason.
Regards,
Ed
Because __has_include() doesn't check if a particular file has already been included, it checks if it can be included based on its existence on the host machine. The MTP library is indeed pulling those libraries into the build simply because that's how it is written. It's probably not what was intended judging by the author's comment above it, but the compiler doesn't read comments and it certainly doesn't understand intent.

It's generally not a good idea to use __has_include in projects designed for arduino, due to the weird way it detects which libraries to use / builds the include directory specification for the final compile... this likely has something to do with it detecting that USBHost_t36 is needed but not also detecting that USBHost_t36 has a dependency on EEPROM.

I'm not sure that is correct. I quite often use the mechanisms shown below to temporarily exclude sections of my code, and no complaints are generated by the compiler:

Code:
#define IGNORE_THIS
#define INCLUDE_THIS

void setup() {
}

void loop() {
#ifndef IGNORE_THIS
   for (int i = 0; i < 10; i++)
   {
      Serial.println("ignoring this code or not");
   }
#endif

#ifdef INCLUDE_THIS
   for (int j = 0; j < 10; j++)
   {
      Serial.println("including this code or not");
   }
#endif
}

If the #define IGNORE THIS is left uncommented, then the code in the first loop is ignored, as desired, & never compiled in. On the other hand, if the #define IGNORE_THIS is commented out, then the code in the first loop remains & is compiled in. Similarly, if the #define INCLUDE THIS is commented out, then the code in the second loop is ignored, as desired, & never compiled in. On the other hand, if the #define INCLUDE_THIS is left uncommented, then the code in the second loop remains & is compiled in. The #else directive can also be used if/as needed to swap between one section of code and another. NOTE the specific use of #ifdef vs. #ifndef.

Hope that helps . . .

Mark J Culross
KD5RXT
I made a slight mistake with my example, I should have used #if some_thing_that_isnt_defined(x) to explicitly indicate evaluation.
You're using #ifndef and #ifdef rather than #if with a macro. A more appropriate example would look like this:
Code:
#define INCLUDE_MAYBE(a) a

void setup() {
}

void loop() {
#if INCLUDE_MAYBE(1)
   for (int j = 0; j < 10; j++)
   {
      Serial.println("including this code or not");
   }
#endif
}
and now when you comment out the definition for INCLUDE_MAYBE:
Code:
error: missing binary operator before token "("
    7 | #if INCLUDE_MAYBE(1)
      |                  ^
 
Last edited:
and according to my google search the directive "__has_include" was not included until version 17 of the compiler.

Support for __has_include began with gcc version 5, even though most other C++17 features came with version 7, and version 8 was the first claiming full C++17 support.

This wikipedia page shows when each C++ feature was first supported.

1745968456408.png



Whatever info you found by a google search is clearly wrong. It can't be version 17. The gcc releases page shows the most recent release (just days ago) is version 15.1. There simply is no gcc version 17 yet!
 
As for why you're getting strange results, I'd like to suggest compiling your program (or a small test program) with Arduino IDE.

We regularly hear about problems with PlatformIO which turn out to be caused by using different compiler options. Even if you hate Arduino IDE and never want to really use it, just running Arduino IDE for the sake of comparison testing can be really helpful. The compiler settings and other key details aren't easily changeable in Arduino IDE, which is frustrating if you're into tweaking those things, but that lack of flexibility is really helpful as a "known quantity" for comparison and solving problems these sorts of compiler problems.

In Arduino IDE, use File > Preferences turn turn on verbose output during compile. Then you'll be able to see the exact compiler commands when you click Verify and it compiles your program.
 
I just dumped MTP and all the problems disappeared. It is definitely a MTP Library problem.
Regards,
Ed

My platformio.ini settings are the same as the teensy build flags settings.
 
Not sure what version of MTP stuff you are running. If you are running off of the ones in my github project,
for the most part I have not touched it in a few years now and keep wondering if I should archive it.

About six months ago, Paul was in the process of rewriting a lot of the code. I believe he removed most if not all of
that call back update code. I am not sure what the state of that code is, but I thought at that time he was ready to
include it in the next Teensyduino beta.

I believe his most recent branch is up at: https://github.com/PaulStoffregen/MTP_Teensy up in Pauls_branch.
@PaulStoffregen, should he try that one?
 
Not sure what version of MTP stuff you are running. If you are running off of the ones in my github project,
for the most part I have not touched it in a few years now and keep wondering if I should archive it.

About six months ago, Paul was in the process of rewriting a lot of the code. I believe he removed most if not all of
that call back update code. I am not sure what the state of that code is, but I thought at that time he was ready to
include it in the next Teensyduino beta.

I believe his most recent branch is up at: https://github.com/PaulStoffregen/MTP_Teensy up in Pauls_branch.
@PaulStoffregen, should he try that one?
from paul's github.
MTP Responder for Teensy 3.x and 4.x

Uses SD interface and Bill Greiman's SdFat_V2 as distributed via Teenyduino supporting exFAT and SDIO

code is based on https://github.com//WMXZ-EU/MTP_t4 with modification by WMXZ

code is based on https://github.com/yoonghm/MTP with modification by WMXZ

see also https://forum.pjrc.com/threads/43050-MTP-Responder-Contribution for discussions
 
from paul's github.
From his default branch: Here are all of the includes in the source files.
Code:
C:\Users\kurte\Desktop\MTP_Teensy-pauls_branch\src>grep #include *
MTP_Storage.cpp:#include "core_pins.h"
MTP_Storage.cpp:#include "usb_dev.h"
MTP_Storage.cpp:#include "usb_serial.h"
MTP_Storage.cpp:#include "MTP_Teensy.h"
MTP_Storage.cpp:#include "MTP_Storage.h"
MTP_Storage.cpp:#include <limits.h>
MTP_Storage.cpp:#include <MemoryHexDump.h>
MTP_Storage.h:#include "core_pins.h"
MTP_Storage.h:#include "FS.h"
MTP_Teensy.cpp:#include "MTP_Teensy.h"
MTP_Teensy.cpp:#include "MTP_Const.h"
MTP_Teensy.cpp:#include "usb_desc.h"
MTP_Teensy.cpp:#include "usb_mtp.h"
MTP_Teensy.cpp:#include "usb_names.h"
MTP_Teensy.cpp:#include "usb_mtp.h"
MTP_Teensy.cpp:#include "usb_mtp.h"
MTP_Teensy.h:#include "core_pins.h"
MTP_Teensy.h:#include "usb_dev.h"
MTP_Teensy.h:#include "MTP_Storage.h"
So I don't believe anything directly in the library use SD or SDFat...
And I believe all of the other files included, like: usb_dev.h or usb_mtp.h ... are in teensy core.

Could be wrong.
 
I must have an old version, using PlatformIO, I put all the non standard libraries into the Lib folder and that is what it uses to build the project. I find this useful most of the time so I don't get stuck somewhere down the road with a new version the blows up my build. I have been using the same one (from your github) going back to 2022. Perhaps I should revisit the MTP library, and see if paul's changes make a difference.
Regards,
Ed
 
@sbfreddie - You beat me to it. The latest version I believe is here (Pauls Branch). This may require the latest version of the "cores" library do to changes to "FS.h". I think the "rename()" function was changed to "name()". I had to update to the latest "cores" library to avoid an Arduino compile error indicating that "rename()" was not found ;)
 
With the latest libraries, a simple sketch with just "MTP_Teensy.h" included results in:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:39608, data:7144, headers:8540   free for files:8071172
   RAM1: variables:8800, code:36904, padding:28632   free for local variables:449952
   RAM2: variables:16512  free for malloc/new:507776
/home/wwatson/arduino-1.8.19-1.59/hardware/teensy/../tools/stdout_redirect /tmp/arduino_build_309496/testing.ino.lst /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/../tools/arm/bin/arm-none-eabi-objdump -d -S -C /tmp/arduino_build_309496/testing.ino.elf
Using library MTP_Teensy-pauls_branch at version 1.0.0 in folder: /home/wwatson/Arduino/libraries/MTP_Teensy-pauls_branch

With just "SD.h" included:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:72400, data:8168, headers:8516   free for files:8037380
   RAM1: variables:11040, code:69704, padding:28600   free for local variables:414944
   RAM2: variables:16512  free for malloc/new:507776
/home/wwatson/arduino-1.8.19-1.59/hardware/teensy/../tools/stdout_redirect /tmp/arduino_build_309496/testing.ino.lst /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/../tools/arm/bin/arm-none-eabi-objdump -d -S -C /tmp/arduino_build_309496/testing.ino.elf
Multiple libraries were found for "SD.h"
 Used: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/SD
 Not used: /home/wwatson/arduino-1.8.19-1.59/libraries/SD
Using library SD at version 2.0.0 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/SD
Using library SdFat-Experiment_SDIO2 at version 2.1.2 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/SdFat-Experiment_SDIO2
Using library SPI at version 1.0 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/SPI

and with just "USBHost_t36.h" included:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:36092, data:5096, headers:8984   free for files:8076292
   RAM1: variables:6368, code:33392, padding:32144   free for local variables:452384
   RAM2: variables:16512  free for malloc/new:507776
/home/wwatson/arduino-1.8.19-1.59/hardware/teensy/../tools/stdout_redirect /tmp/arduino_build_309496/testing.ino.lst /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/../tools/arm/bin/arm-none-eabi-objdump -d -S -C /tmp/arduino_build_309496/testing.ino.elf
Using library USBHost_t36 at version 0.2 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/USBHost_t36
Using library SdFat-Experiment_SDIO2 at version 2.1.2 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/SdFat-Experiment_SDIO2
Using library SPI at version 1.0 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/SPI
Using library EEPROM at version 2.0 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/EEPROM

A three included:
Code:
Memory Usage on Teensy 4.1:
  FLASH: code:75236, data:10216, headers:8752   free for files:8032260
   RAM1: variables:13344, code:72520, padding:25784   free for local variables:412640
   RAM2: variables:16512  free for malloc/new:507776
/home/wwatson/arduino-1.8.19-1.59/hardware/teensy/../tools/stdout_redirect /tmp/arduino_build_309496/testing.ino.lst /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/../tools/arm/bin/arm-none-eabi-objdump -d -S -C /tmp/arduino_build_309496/testing.ino.elf
Multiple libraries were found for "SD.h"
 Used: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/SD
 Not used: /home/wwatson/arduino-1.8.19-1.59/libraries/SD
Using library MTP_Teensy-pauls_branch at version 1.0.0 in folder: /home/wwatson/Arduino/libraries/MTP_Teensy-pauls_branch
Using library SD at version 2.0.0 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/SD
Using library SdFat-Experiment_SDIO2 at version 2.1.2 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/SdFat-Experiment_SDIO2
Using library SPI at version 1.0 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/SPI
Using library USBHost_t36 at version 0.2 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/USBHost_t36
Using library EEPROM at version 2.0 in folder: /home/wwatson/arduino-1.8.19-1.59/hardware/teensy/avr/libraries/EEPROM

The simple sketch:
Code:
// testing.ino

//#include <MTP_Teensy.h>
//#include <SD.h>
//#include <USBHost_t36.h>

//**************************************************************************

//**************************************************************************

void setup() {
  while (!Serial && millis() < 5000) {} //wait for Serial Monitor
  Serial.printf("%c testing.ino\n\n",12);

}

void loop() {
  waitforInput(); // Wait for input from serial cconsole.

}

void waitforInput()
{
  Serial.println("Press anykey to continue");
  while (Serial.read() == -1) ;
  while (Serial.read() != -1) ;
}

All seems to be working as intended...
 
I believe his most recent branch is up at: https://github.com/PaulStoffregen/MTP_Teensy up in Pauls_branch.
@PaulStoffregen, should he try that one?

Probably not until I package up 1.60-beta4. Should come within a few days. But even then, it'll only be for Arduino IDE.

While it's theoretically possible to grab the code from github or local files installed for Arduino IDE and put them all into PlatformIO, that's a manual and error-prone process. Probably will only create more problems than it solves. If using PlatformIO only, best to just wait.
 
Back
Top