Arduino serial comms

Trying to understand Arduino serial communication. I am not a professional programmer. I plan to use Serial communication between MCUs and several peripherals, I'd really like to understand how it all works.

Consider these commands:

// Example 1:
int test1 = 123;
Serial.print(test1);   // sermon prints 123.
Serial.write(test1);   // sermon prints {.

// Example 2:
char test2[] = "123";
Serial.print(test2);   // sermon prints 123.
Serial.write(test2);   // sermon prints 123.

// Example 3:
char test3 = 123;
Serial.print(test3);   // sermon prints {.
Serial.write(test3);   // sermon prints {.

I understand that the serial monitor assumes each byte received is an ascii encoded character, correct? For Example 1: Serial.print converts variable values to ascii encoded values and sends them to the ser mon, correct? Serial mon then decodes them from ascii and prints the character, correct? Serial write sends the variable values as they are, but ser mon assumes these are ascii values, decodes them and prints them, correct?

For Example 2, the variable is a char type. That means the values stored in the char variable are the ascii encoded values for the assigned characters, correct? Serial.print recognizes because of the char type that the values are already ascii encoded and just sends these values correct? Serial.write also sends the stored ascii values as is to the ser mon.

Example 3, I'm not sure what happens here. It appears to me that 123 is stored in the char variable as-is, no ascii coding. Serial.print does not ascii encode the value because it assumes it is already ascii encoded due to being a char type, is this correct? The ser mon decode it as if it was ascii and thus prints the { character. For Serial.write, the ser mon has received the ascii code for {, which is 123 therefore Serial.write sent 123, which means it does not interpret char value as ascii values, correct?

I would really appreciate any corrections or clarifications/explanations. Thanks.

Serial data transmission is very general with regard to data types, but the Arduino serial monitor acts as a dumb ASCII terminal program and is mostly used for simple program debugging.

For some useful insights, have a look at the Serial Input Basics tutorial.

no, the variable type is char* - a pointer to char. A char is a single character, it can't contains a string "123"

the same as in Example 1 Serial.write()

Correct on every assumption, I think....

Serial.write(test2);

I wonder if this sends the null-terminator after the '1', '2' and '3'?

Serial.write() will know that the char array has size 4, and so send '1', '2', '3', '\0' ?

Whereas

Serial.print(test2);

will send only '1', '2', '3' ?

Sure. A string initialized like this:

already contains a terminator

print() and write() both return the number of bytes written.

They both returned 3 when I ran test2 ("123")

If you look at the different overloads for print() and write(), there is no method that takes a parameter of type int for write() so example 1 and example 3 are identical for write() in that they both get passed a uint8_t parameter.

For print(), there is a method that takes an int as well as another that takes a char so you get two different behaviors. The first being to turn a number into the ASCII representation of the number and the second one to just print as-is.

All the gory details can be found in the print.h header file that is part of the IDE somewhere on your PC.

Of course, because there are three characters in this line. If you run strlen(), it will also give you 3.
But that doesn't mean there is no terminator in the line :slight_smile:

Ok, so why didn't Serial.write("123") return 4? That array is 4 bytes long.

I get why Serial.print("123") returns 3. It understands that the null terminator is not part of the c-string.

But Serial.write("123") knows only that it had been given 4 bytes of data, doesn't it?

The print() function is only an interface to number of write() functions. So it works the same way for char* data type.

So Serial.write() returns the number of printable characters that have been written? Why would it distinguish between printable and non-printable?

Yes, but, Serial.write() should not return the result of strlen(), it should return the result of size(), which is 4?

This is really confusing!

I don't know exactly what it returns... But the print() method in reality is only write() :

Serial.print(char* c) {
   return Serial.write(c);
}

See the source code of the applicable overload of write() in Print.h:

    size_t write(const char *str) {
      if (str == NULL) return 0;
      return write((const uint8_t *)str, strlen(str));
    }

If I add:

  Serial.println(Serial.write(testX));

to each example I get 3 sent for Ex1, 1 sent for Ex2, 1 sent for Ex3. ?

If I have a Win10 PC with a std installation Where do I find the libraries for Serial, Print, I2C, SPI ? These are all different depending on the board you are using right? I'd really like to know.

On a typical Windows install for AVR boards like an UNO:
C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino\

If you enable verbose reporting in Preferences, you'll get a whole lot more information about what's happening "under the hood" in the real world.
HTH

No, it doesn't.
Serial.write(test2, 4) would send the null, but write() for a char array with no explicitly specified length has it's own strlen():

For IDE 2.x, all board packages are in C:\Users\yourUsername\AppData\Local\Arduino15\packages.

For standard IDE 1.8.x the AVR board package is in the directory mentioned in post #18. All other board packages are in C:\Users\yourUsername\AppData\Local\Arduino15\packages. Once you upgrade the AVR board package it will also be in C:\Users\yourUsername\AppData\Local\Arduino15\packages.

If you have both IDE 1.8.x and IDE 2.x installed,the Arduino15 directory is shared,

If you have a portable installation of IDE 1.x, the board packages are in pathToIDE\portable\packages except for the non-upgraded AVR board package which is in pathToIDE\hardware.