How to Print uint64_t in Arduino

The Arduino programming language looks like C++, but it is weird in its own way: there's no C++ standard library. In C++, you can print a variable to the debug console with std::cerr << x;. In Arduino, you have to write a function call: Serial.print(x);. I love that the Streaming library brings back the familiar syntax Serial << x; through some template and macro magic. But when it comes to a uint64_t variable, nothing works!

error: call of overloaded 'print(uint64_t&)' is ambiguous
   Serial.print(x);
                 ^

note: candidates are:
note: size_t Print::print(const __FlashStringHelper*) <near match>
note:   no known conversion for argument 1 from 'uint64_t {aka long long unsigned int}' to 'const __FlashStringHelper*'
note: size_t Print::print(const String&) <near match>
note:   no known conversion for argument 1 from 'uint64_t {aka long long unsigned int}' to 'const String&'

The cause of this error is: Arduino does not know how to print a uint64_t!

How Arduino Prints Stuff

Arduino contains a subset of C++ standard library, plus some additional headers known as the "Arduino Core". Each microcontroller has its own Arduino Core: AVR, SAMD, ESP8266, ESP32. They follow roughly the same API, one of which is the Print class that knows how to print stuff.

class Print
{
public:
  virtual size_t write(uint8_t) = 0;

  size_t print(const __FlashStringHelper*);
  size_t print(const String&);
  size_t print(long, int base = DEC);
  size_t print(unsigned long, int base = DEC);
  size_t print(double);
  size_t print(const Printable&);
  // + many more overloads

  size_t println(const __FlashStringHelper*);
  // + many more overloads
};

Every object with print and println functions derives from the Print class. The subclass (such as serial port or network socket) is expected to implement the write method that prints one character, and then the Print class will convert whatever variable into a series of characters and invoke write once for every character.

print function has many overloads for different variable types. It supports String, int, long, double, and so on. However, there isn't a print overload for uint64_t, and that's why trying to print a uint64_t causes a compile error.

Luckily, Print::print accepts a Printable object. The Printable interface allows new classes to tell Print how to print themselves. The IPAddress class prints nicely by implementing the Printable interface.

Make uint64_t a Printable

uint64_t is not a class. There's no way to make uint64_t a subclass of Printable. However, we can encapsulate it: we create a new class that wraps a uint64_t value and implements the Printable interface. Then, we could print a uint64_t like this:

uint64_t x = 0;
Serial.print(PriUint64<DEC>(x));

I made a little PriUint64 library for this purpose. The library offers a PriUint64 class template that wraps a uint64_t value, and can print it in different radices.

Integrate with Streaming Library

I love the << syntax from Streaming library so much, that I added integration with it. If you #include <Streaming.h> before including <PriUint64.h>, you'll be able to print a uint64_t value directly without wrapping it with PriUint64. This is achieved, of course, by overloading operator<<.

uint64_t x = 0;
Serial << x << endl;

Supporting different number bases, i.e. _HEX, _DEC, _OCT, and _BIN macros, is trickier. It requires quite a bit of template magic, and I had to use <type_traits>. AVR does not have this header (not in their subset of C++ standard library), so this feature only works on ESP8266 and ESP32.

uint64_t x = 0;
Serial << _HEX(x) << _DEC(x) << _OCT(x) << _BIN(x) << endl;

Code Link

View PriUint64.h library on GitHub