Many arduino functions return -1 if something 'fails'. Serial.read for example returns -1 or the infamous ÿ when this function is called while the serial buffer is empty.

As embedded C progammer, I am personally a fan of letting functions return data in the form of modifying pointers which points to passed on addresses and return states with the actual return functionality of a function. I am used to return a 0 if a function is 'not done' or 'failed'.

I see no logic in why many arduino functions return an int type which contains -1 instead of just a 0.

Would it not be better to let functions return a boolean type or an unsigned char type in which false or '0' would indicate a 'failed' process and true or '1' would indicate a 'succesful' process?

Also: if I type: if(Serial.read()) {/* body */ } and Serial.read() returns -1 because the buffer is empty. Will the If's body be executed or not?

  • how would you return a byte with value 0 from read() if 0 would indicate error?
    – Juraj
    Sep 3, 2019 at 13:19
  • @Juraj I realized that Serial.read() was perhaps a very poor example as this function returns both return values as a state (-1). Perhaps that Serial.begin() would be a better example Sep 4, 2019 at 6:40

4 Answers 4


I cannot assert anything for sure, as I was not involved in the design of the Arduino APIs. However, just like Elliot Alderson, I suspect this was inspired by the traditional C APIs. The manual page for getchar(), which is a very old function from the standard C library, states:

fgetc(), getc() and getchar() return the character read as an unsigned char cast to an int or EOF on end of file or error.

Given that EOF is usually defined as -1, this is consistent with the behavior of Serial.read().

Being beginner-friendly is also a major goal of the API. Pointers are known to be a huge difficulty for beginners.

There may also be some efficiency considerations involved. Arduino started on the AVR platform. This is a RISC architecture, with a large number of generic CPU registers (namely 32). The compiler usually manages to allocate most local variables to registers. The calling convention ensures that in most cases function parameters and return values also go through CPU registers. This means we can avoid many RAM accesses, which are inherently much slower than register access. Using a return-by-pointer convention defeats these optimizations.

Lastly, it is a good thing that simple things are easy to write. For example, an interpreter for single-character commands can be very easily written as:

switch (Serial.read()) {
case 'a':
case 'b':
// etc...

with no need to explicitly test for the serial buffer not being empty. Obviously, a more complex command language will need a full-blown interpreter, but the focus of Arduino has always been on simple programs being easy to write.


Arduino API is port of Processing. Processing is Java API to communicate with a MCU running Firmata. Arduino enabled sketches with the same API to run directly on the MCU.

So for example stream functions from Java stream classes were ported to Arduino API. This includes the read() function, to read one byte. Of course Java and Java basic libraries have inspiration in C++.

For other read and write functions, if the return value is count of bytes written or read then 0 is a valid return value and -1 indicates error.

Processing, Wiring and Arduino were created by interactive arts teachers and students, not by embedded systems developers. So you can't wonder about not ideal data types used. Later some things were improved by software developers.

as embedded C developer you should know that:

  • 0 is a valid value to read, so it can't be a value indicating error
  • 'if' executes if the evaluated value is not 0
  • How does this answer the question? You didn't say anything about return values. Sep 3, 2019 at 13:58
  • sorry, you are not familiar with the Java read function.. I added a link to JavaDoc of Java read() function. and I addressed the thought error of OP about the error return value of read() in a comment
    – Juraj
    Sep 3, 2019 at 14:19
  • In an Arduino forum I think you should assume that readers are not familiar with Java. Also, could you include the important information directly in your answer, just in case the link breaks some day? Sep 3, 2019 at 14:39
  • So to get to the heart of the matter, can you explain why (if in fact it is true) Java functions return -1 instead of 1 (or True) for an error condition? I mean even those functions that are not returning a byte value, but only a pass/fail indication. Sep 3, 2019 at 15:28
  • @ElliotAlderson, that is explained in your answer :-). I have "inspired in C++", which is inspired in C...
    – Juraj
    Sep 3, 2019 at 16:11

-1 is returned when "0" would be a perfectly valid response.

To take your example of Serial.read(): 0 is a valid byte or ASCII character. -1 isn't. If you returned 0 for "failure" then there would be no way of differentiating between that and the valid byte value 0.

  • Are you sure that the Serial.read() can not return binary values rather than just ASCII? I can't find that in the documentation. Sep 3, 2019 at 13:59
  • What is ASCII? It is just shapes that humans have decided to associate with numbers. Everything is numbers. The numbers 0-255 - and we have decided that certain ones of those numbers will represent letters in our minds. Serial.read() returns the byte that is next in the receive buffer. That byte is 0-255, and if we want 65 to mean "A" then we can. But if there's no byte to return from the receive buffer then something "not a byte" must be returned to mean "there's nuffink there, bruv!", and -1 is not a byte.
    – Majenko
    Sep 3, 2019 at 14:05
  • Ah, so the missing bit of information is that we are assuming that an int is defined to be bigger than 8 bits, and the Serial.read() returns an int rather than a char. A char value of 0xFF could be a valid return value, but an int of 0xFFFF is not. Sep 3, 2019 at 14:22
  • That's not an assumption. It's a cold hard fact. An int will always be bigger than a byte.
    – Majenko
    Sep 3, 2019 at 14:27
  • Agreed, given an ANSI compiler. I shouldn't have said "assuming" I should have said "recognizing". Sep 3, 2019 at 15:05

I suspect that this a legacy from Unix command programming, which was first done in C. For commands that only needed to return status and not data, a return value of zero meant success and any other return value indicated some kind of failure. Individual programmers could designate specific non-zero values for specific error types, if desired.

Remember that the return value comes back to the calling function in a register. And these days the term "Arduino" encompasses a broad range of architectures, including 32-bit ARM Cortex-M. Specifying that the return value is a char offers no advantage in this case.

The boolean datatype is a fairly recent addition to the C language, so it's not surprising that you don't see it used much in existing, portable libraries.

Returning an address from a simple function that just needs to indicate success or failure seems like overkill.

If you really want control over the datatypes, include stdint.h and use the types defined therein. If you want an unsigned 8-bit value use uint8_t and not unsigned char. If you want to allow the compiler to choose the best datatype that is at least 8 bits, use uint_fast8_tf.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.