For anyone not quite sure on the difference, it may help to note
char c;
Serial.print(c);
Go to Definition on print:
size_t Print::print(char c)
{
return write(c);
}
passes the argument directly to write. Hovering over write, the bottom of the tooltip says
// In Print
public: virtual size_t write(uint8_t) = 0
Printing a character sends the byte over the wire -- no difference.
Printing a byte prints its numeric value with one or more digits.
uint8_t b;
Serial.print(b);
The fact that it takes an optional second argument for the base
// In Print
public: size_t print(unsigned char, int = DEC)
which defaults to decimal, is a clue.
And also note, three kinds of char
unsigned char: the smallest unsigned number range: 0 to 255
signed char: a number from -128 to 127
- just
char: same size, one byte, but intended for a character. Might actually be either signed or unsigned when converted to an int for example.
Extra credit
Looking at the overloads for write in Print.h
virtual size_t write(uint8_t) = 0;
size_t write(const char *str) {
if (str == NULL) return 0;
return write((const uint8_t *)str, strlen(str));
}
virtual size_t write(const uint8_t *buffer, size_t size);
size_t write(const char *buffer, size_t size) {
return write((const uint8_t *)buffer, size);
}
- send a byte: pure virtual, implemented by the subclass. This does all the work.
- send a C-string: for
nullptr, do nothing; but otherwise get its length, then cast it to treat it like a byte buffer
- send a
char buffer: cast it to treat it like a byte buffer
- send a byte buffer: the implementation is at the top of
Print.cpp; it just loops
/* default implementation: may be overridden */
size_t Print::write(const uint8_t *buffer, size_t size)
{
size_t n = 0;
while (size--) {
if (write(*buffer++)) n++;
else break;
}
return n;
}
HardwareSerial muddies the situation by adding overloads to write that take wider "numbers". But all they do
virtual size_t write(uint8_t);
inline size_t write(unsigned long n) { return write((uint8_t)n); }
inline size_t write(long n) { return write((uint8_t)n); }
inline size_t write(unsigned int n) { return write((uint8_t)n); }
inline size_t write(int n) { return write((uint8_t)n); }
is cast to truncate to byte. They don't take a second base argument.
So to send a byte, write is always safer. But you really can just print a char to do the same thing.
What to do on the receiving end....