Excessive wire.requestFrom() warnings on Nano ESP32

I think this should be in the Github for the Nano ESP32 repository but I don't see a place to actually submit issues so I'm doing it here. On the Nano ESP32 this code generates about a dozen error messages that seem to come from ambiguous overloads of the Wire.requestFrom() function. I've only looked at a few other boards as reference but the Nano ESP32 seems to have a lot more overloads of this function than others which I assume is why it's uniquely having this issues.

Example Code

#include "Wire.h"

void setup() { Wire.begin(); }

void loop() { uint8_t bytesReceived = Wire.requestFrom(0x55, 1, true); }

Wire.h (Nano ESP32)

  size_t requestFrom(uint16_t address, size_t size, bool sendStop);
  uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop);
  uint8_t requestFrom(uint16_t address, uint8_t size, uint8_t sendStop);
  size_t requestFrom(uint8_t address, size_t len, bool stopBit);
  uint8_t requestFrom(uint16_t address, uint8_t size);
  uint8_t requestFrom(uint8_t address, uint8_t size, uint8_t sendStop);
  uint8_t requestFrom(uint8_t address, uint8_t size);
  uint8_t requestFrom(int address, int size, int sendStop);
  uint8_t requestFrom(int address, int size);

Warnings

C:\Users\Test\Test.ino: In function 'void loop()':
C:\Users\Test\Test.ino:9:57: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
   uint8_t bytesReceived = Wire.requestFrom(0x55, 1, true);
                                                         ^
In file included from C:\Users\Test\Test.ino:1:
C:\Users\User\AppData\Local\Arduino15\packages\arduino\hardware\esp32\2.0.13\libraries\Wire\src/Wire.h:126:13: note: candidate 1: 'uint8_t TwoWire::requestFrom(int, int, int)'
     uint8_t requestFrom(int address, int size, int sendStop);
             ^~~~~~~~~~~
C:\Users\User\AppData\Local\Arduino15\packages\arduino\hardware\esp32\2.0.13\libraries\Wire\src/Wire.h:122:12: note: candidate 2: 'size_t TwoWire::requestFrom(uint8_t, size_t, bool)'
     size_t requestFrom(uint8_t address, size_t len, bool stopBit);
            ^~~~~~~~~~~
C:\Users\Test\Test.ino:9:57: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
   uint8_t bytesReceived = Wire.requestFrom(0x55, 1, true);
                                                         ^
In file included from C:\Users\Test\Test.ino:1:
C:\Users\User\AppData\Local\Arduino15\packages\arduino\hardware\esp32\2.0.13\libraries\Wire\src/Wire.h:126:13: note: candidate 1: 'uint8_t TwoWire::requestFrom(int, int, int)'
     uint8_t requestFrom(int address, int size, int sendStop);
             ^~~~~~~~~~~
C:\Users\User\AppData\Local\Arduino15\packages\arduino\hardware\esp32\2.0.13\libraries\Wire\src/Wire.h:120:13: note: candidate 2: 'uint8_t TwoWire::requestFrom(uint16_t, uint8_t, bool)'
     uint8_t requestFrom(uint16_t address, uint8_t size, bool sendStop);
             ^~~~~~~~~~~
C:\Users\Test\Test.ino:9:57: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
   uint8_t bytesReceived = Wire.requestFrom(0x55, 1, true);
                                                         ^
In file included from C:\Users\Test\Test.ino:1:
C:\Users\User\AppData\Local\Arduino15\packages\arduino\hardware\esp32\2.0.13\libraries\Wire\src/Wire.h:126:13: note: candidate 1: 'uint8_t TwoWire::requestFrom(int, int, int)'
     uint8_t requestFrom(int address, int size, int sendStop);
             ^~~~~~~~~~~
C:\Users\User\AppData\Local\Arduino15\packages\arduino\hardware\esp32\2.0.13\libraries\Wire\src/Wire.h:119:12: note: candidate 2: 'size_t TwoWire::requestFrom(uint16_t, size_t, bool)'
     size_t requestFrom(uint16_t address, size_t size, bool sendStop);
            ^~~~~~~~~~~
C:\Users\Test\Test.ino:9:57: warning: ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second:
   uint8_t bytesReceived = Wire.requestFrom(0x55, 1, true);
                                                         ^
In file included from C:\Users\Test\Test.ino:1:
C:\Users\User\AppData\Local\Arduino15\packages\arduino\hardware\esp32\2.0.13\libraries\Wire\src/Wire.h:126:13: note: candidate 1: 'uint8_t TwoWire::requestFrom(int, int, int)'
     uint8_t requestFrom(int address, int size, int sendStop);
             ^~~~~~~~~~~
C:\Users\User\AppData\Local\Arduino15\packages\arduino\hardware\esp32\2.0.13\libraries\Wire\src/Wire.h:122:12: note: candidate 2: 'size_t TwoWire::requestFrom(uint8_t, size_t, bool)'
     size_t requestFrom(uint8_t address, size_t len, bool stopBit);
            ^~~~~~~~~~~

Wire.h (SAMD)

  size_t requestFrom(uint8_t address, size_t quantity, bool stopBit);
  size_t requestFrom(uint8_t address, size_t quantity);

(No warnings)

It is not unique to the Nano ESP32, the Wire library in the AVR core will give similar warnings if the IDE is set to show all compiler warnings. The default compiler options differ on the ESP32, which can cause more warnings to show, or can cause the compiler to flag an error for something that will only show as a warning on another platform.

The basic cause is that the parameters you are passing do not exactly match the function definitions, and the compiler cannot determine which function definition is a better match. As an example, an overloaded function that takes either two uint8_t or two uint16_t, when called with arguments that are a uint8_t and a uint16_t, will work as well on either version of the function (presuming the uint16_t holds a value that fits in a uint8_t). If you look at the actual library, often the overloads of the function serve to change the argument type to what is needed by the base function, as an example (from the AVR library):

uint8_t TwoWire::requestFrom(int address, int quantity)
{
  return requestFrom((uint8_t)address, (uint8_t)quantity, (uint8_t)true);
}

Things are better if you use normal code:

#include <Wire.h>

void setup() 
{ 
  Wire.begin(); 
}

void loop() 
{ 
  int bytesReceived = Wire.requestFrom(0x55, 1);
}

No one uses the third parameter for Wire.requestFrom().

So I think what I'm getting out of this is that the SAMD library "should" have more overloads to technically support the same inputs as the other boards, but since it doesn't it just relies on the warnings being turned off to not be super annoying?

Is manually "casting" all of the inputs really the only way to get around this while making the compilers happy? Seems a bit annoying.

Point taken but its a supported function so this shouldn't be relevant.

Having the inputs be the correct type in the first place would be a better solution. The library only supports 7-bit addresses, and can handle a maximum of 32 bytes, so there is no need to use anything larger than a uint8_t for either.

I think this actually answers what my next question was going to be. I removed the third argument from my example code and it compiled with no warnings. But when I did the same in my real code it had the same issue. But I think its because my real code was something like:

uint8_t adr1 = 0x55;
Wire.requestFrom(adr1, 2);

The address is obviously a uint8_t but I'm guessing it still doesn't know how it should interpret the "2".

Looks that way, I get the same warning compiling for an UNO. The compiler defaults to using int, and there is an overload of the function that takes integers as arguments, so either make both arguments uint8_t, or both int.

It really does not matter in the end which overload of the function is selected, they all feed into the same function in the end. Not sure if the compiler will ultimately optimize away the intermediate function or not.

Why did you use "uint8_t addr1" ? It is a normal variable and a "int" will be fine.
You don't have to worry about those 8 bits that might or might not get lost.
The "uint8_t" is to force a variable of 8 bits when it absolutely must be 8 bits. There is no reason for that.

It starts with a good description. The documentation of the Wire library should tell how to use it, so each Wire library can be compatible with that. I'm afraid that will never happen.

The SAMD core (Arduino Zero) has up to 255 bytes length. Some software implementations have unlimited length. There are 10-bit I2C adresses (they are very rare).

I mean you have to pick something and if you know the number will always be less than 255 you should pick a uint8. I follow that rule generally but in this case the address is a device constant that's know at compile time.


Sorry, not following what you're getting at here.

I hope that you don't mind that I disagree with that. I think that source code should be readable in the first place. Showing off that you know more than beginners with a unnecessary specific type is not needed.
I think this is perfectly fine:

void setup() 
{ 
  pinMode(13,OUTPUT); 
  for(int i=0; i<3; i++)
  {
    digitalWrite(13,HIGH);
    delay(200);
    digitalWrite(13,LOW);
    delay(200);
  }
}

void loop() 
{ 
}

If you try the sketch above for a "Arduino Uno" and change the 'int' to 'uint8_t', then you gain nothing. If you do that for a "ESP32 Dev Module" or a "Raspberry Pi Pico" then it takes more code with a 'uint8_t'. So you get the opposite of what you want.

The KISS rule is there for a reason: https://en.wikipedia.org/wiki/KISS_principle
If you want small code, then 'int' can be better than 'uint8_t'.

Have I convinced you, or do you deliberately want larger binary code and less readable source code ?
Note that the sketch is handpicked to get the result that I want :wink:

It is not a big deal, but it can be a lot worse:

Wire.requestFrom(0x3C, 3);    // good
Wire.requestFrom((int)reinterpret_const_cast<uint8_t>0x3C, (uint8_t) 3, static_cast<boolean>true);  // terribly bad

Those were my thoughts about the types for the parameters of the functions of the Wire library.

The length is not just 32 bytes. See also my Github Wiki: Buffer size of the Wire library · Koepel/How-to-use-the-Arduino-Wire-library Wiki · GitHub
Why exclude a larger length with a 8-bit variable ? If someone wants a length of 1024 then it should be possible.
The right type for the length would be 'size_t' in my opinion, and 'int' as second best.

When the I2C address is a 'uint8_t', then a future update for 10-bit addresses are excluded. A 'int' for the I2C address would be my first choice.

However, any type is better than no good documentation which types can be used.

Can anyone explain why the library has so many overloads of the requestFrom() function? If I create overloads of a function in a sketch, with the various combinations of argument similar to requestFrom(), then I will get the ambiguous warnings. If I only create a single function that takes the desired type (in the case of the avr processors uint8_t), then I can use uint8_t, uint16_t, int, or even float for a parameter with no warnings or errors as long as I don't explicitly use a number that will not fit within an uint8_t.

1 Like

Because it is a big mess. I'm sorry, but there is no better explanation.
You could make an Issue on Github, that someone should not get that trouble. But that does not change the fact that it is different for different boards.

I don't agree that uint8 is any less readable than int. If it matters at all I would think that using the same data type for everything removes a layer of understanding. I don't know what you mean about "showing off", I did it the way that I did because that's what I thought was appropriate here.

I wasn't aware that unit8_t is larger than int on some platforms though, could you elaborate that? I'm totally willing to learn from you here but a link to the KISS wiki page isn't exactly helpful.

Are you talking about the buffer length or the address? The device address is permanently 0x55, I don't understand why I you're saying to protect for future changes. And this particular line of code specifically wants two bytes so I don't understand why do you think I should allow for more?

For your situation, there is not much else to say then that the parameters for the functions of the Wire library are different for each platform. Try to work around it.
That's all. The rest of this post is just me babbling about other things.


Sorry that I gave the wrong impression. I was no longer talking about your code. I was using your topic to write down my thoughts about the Wire library, and what it could be in the future. To avoid the problems that you and many others have. I think that the all the Wire libraries for all platforms have to be changed. I others would have joined then we could perhaps propose a better way with a Issue on Github.

I call this "showing off":

Wire.requestFrom((int)reinterpret_const_cast<uint8_t>0x3C, (uint8_t) 3, static_cast<boolean>true);  // terribly bad

If someone writes such code, then someone is trying to say: "look at me how good I am, I know all the fancy things that you don't know".
I meant to say: be carefull for being not too specific when writing code.

There is another reason why I prefer 'int' in a simple for-loop. When beginner sees your code and uses 'uint8_t' in a for-loop, then they could do this:

for(uint8_t i = 10; i >= 0; i--)   // bug

The ESP32 and the Raspberry Pi Pico are 32-bit processors. They need extra code to select the 8 bits from the 32-bits.

In the electronics world, they have the Magic Smoke. In the code world, there is the KISS rule. I hope it was not offensive.

Gotcha, all good! Thanks for the help!