You need a \n at the end of any string you send over serial TO the Arduino:
Notice this part of the serialEvent() function:
// if the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n') {
stringComplete = true;
}
As you can see, it is looking for the \n (newline) character to indicate the full string has been received. So, you must include a newline (\n) at the end of the string you are sending to the Arduino.
The tutorial (https://www.arduino.cc/en/Tutorial/BuiltInExamples/SerialEvent) also states (emphasis added):
This example demonstrates use of the serialEvent() function. This function is automatically called at the end of loop() when there is serial data available in the buffer. In this case, each character found is added to a string until a newline is found. Then the string is printed and set back to null.
If using the Arduino Serial Monitor, be sure to choose the "Newline" option from the dropdown at the bottom. Then, whenever you type something in the bar at the top and press Enter, it will automatically append a \n to the end of your string to the Arduino when you send it. 
If sending data from a C, C++, Python, Java, or other program to the Arduino over serial, again, be sure to include a \n at the end of your string TO the Arduino.
How does serialEvent() get called anyway?
To answer this:
Also, I don't see this example code calling the serialEvent() function anywhere. Where do I have to call serialEvent()? Or, how does it otherwise ever run and get called?
In short, the fact that you defined the serialEvent() function at all makes it get called automatically by the Arduino core code. If you don't define it, it won't get called. If you do define it, it will. This is because your definition of serialEvent() is strong and overrides the weak one declared here in HardwareSerial.cpp:
// SerialEvent functions are weak, so when the user doesn't define them,
// the linker just sets their address to 0 (which is checked below).
// The Serialx_available is just a wrapper around Serialx.available(),
// but we can refer to it weakly so we don't pull in the entire
// HardwareSerial instance if the user doesn't also refer to it.
#if defined(HAVE_HWSERIAL0)
void serialEvent() __attribute__((weak)); // <======
bool Serial0_available() __attribute__((weak));
#endif
So, if you define a serialEvent() function at all, that makes the address to that function non-zero (whereas if it was simply the gcc weak declaration, it would remain zero), making this if statement true, so serialEvent() gets called inside the serialEventRun() function:
void serialEventRun(void)
{
#if defined(HAVE_HWSERIAL0)
if (Serial0_available && serialEvent && Serial0_available()) serialEvent();
// ...
#endif
}
And, that in-turn gets called in the Arduino main() function here:
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
So, let's go over that again. If you do NOT define serialEvent(), then its only declaration is this:
void serialEvent() __attribute__((weak));
That's a weak declaration, so it will simply give the function a program space memory address of 0 (zero) in the event it is never defined. That means that this if statement is false, so serialEvent() is never called inside serialEventRun():
if (Serial0_available && serialEvent && Serial0_available()) serialEvent();
If that's the case, then serialEventRun() contains nothing and apparently is also given a program space address of 0 (zero) since it has nothing to do. That makes this if statement in the main() function false, and serialEventRun() never gets called either:
for (;;) {
loop();
if (serialEventRun) serialEventRun();
}
Don't use serialEvent() at all
Personally, I don't think you should use serialEvent at all. Just add the logic to the loop() function directly, like this. You get the exact same effect and get to control things manually yourself instead. Notice that the only changes I made were:
- I renamed the
serialEvent() function to readSerial().
- I added the
readSerial() call to the top of your loop() function.
That's it! Here is the code with those changes. I tested it on an ATmega328-based Pro Mini just now and it works fine, properly printing out any newline (\n)-terminated string I send to it, same as the unmodified code for this example as well.
String inputString = ""; // a String to hold incoming data
bool stringComplete = false; // whether the string is complete
void setup() {
// initialize serial:
Serial.begin(9600);
// reserve 200 bytes for the inputString:
inputString.reserve(200);
}
void loop() {
readSerial();
// print the string when a newline arrives:
if (stringComplete) {
Serial.println(inputString);
// clear the string:
inputString = "";
stringComplete = false;
}
}
void readSerial() {
while (Serial.available()) {
// get the new byte:
char inChar = (char)Serial.read();
// add it to the inputString:
inputString += inChar;
// if the incoming character is a newline, set a flag so the main loop can
// do something about it:
if (inChar == '\n') {
stringComplete = true;
}
}
}
Why did they make the weak serialEvent() function at all when that's all you need to do to control this manually as it is? I don't know. It's probably a convenience thing: it saves you a few lines of code if you are doing this (overriding weak function calls) for multiple serial event calls, such as serialEvent(), serialEvent1(), serialEvent2(), and serialEvent3(). For more-obvious control, however, I'd just manually make my own functions and call them inside the loop() function instead, as I do just above.
Other References:
- gcc
weak function attribute: https://gcc.gnu.org/onlinedocs/gcc-3.2/gcc/Function-Attributes.html
weak
The weak attribute causes the declaration to be emitted as a weak symbol rather than a global. This is primarily useful in defining library functions which can be overridden in user code, though it can also be used with non-function declarations. Weak symbols are supported for ELF targets, and also for a.out targets when using the GNU assembler and linker.
- From my eRCaGuy_hello_world repo, here is my check_addr_of_weak_undefined_funcs.cpp file where I prove to myself that
weak functions which are declared but not defined really do have addresses equal to nil (ie: zero, or 0).