Data sent through serial monitor is only temporary

I have encountered puzzling behavior with sketch 06-08 in Simon Monk's book "Programming Arduino: Getting Started with Sketches". I used the serial monitor to send a number 0 to 5 from the monitor to the sketch, which then sends a calculated pwm value to pin 3. I used a voltmeter to measure the voltage at the pin. It should be 0 to 5 volts (depending on what is sent from the serial monitor), and it is, but only for a second or so. The voltage then goes back to 0.

I have tried all sorts of programming changes to try to understand why the requested voltage is not permanent, and how to make the voltage permanent. No success! Any ideas? My understanding is clearly deficient! :slight_smile:

Here is a slightly modified version of the sketch from Monk's book:

// 6-08A.ino
// analog output example

const int outputPin = 3;
int pwmValue = 2;

void setup()
{
  pinMode(outputPin, OUTPUT);
  Serial.begin(9600);
  Serial.setTimeout(1000);  // can be used to increase persistance
  Serial.println("Enter volts, 0 to 5:");
}

void loop()
{
  if (Serial.available() > 0)
  {
    float volts = Serial.parseFloat();
    pwmValue = volts * 255.0 / 5.0;
  }
  analogWrite(outputPin, pwmValue);
}

javaman2015:
It should be 0 to 5 volts (depending on what is sent from the serial monitor)

No it should not! It's PWM, only a true RMS meter that's fast enough will read sensible a voltage.

And the code is quite terrible. Even has an error in the calculation. ::slight_smile: But uhm, let me guess, line ending in the IDE is set to "Both NL & CR"?

If you really want to know what's going on / learn to program:

  • When will (the crappy) Serial.parseFloat() end?
  • What will that mean when you send a New Line followed by a Carrage Return?

It's PWM, only a true RMS meter that's fast enough will read sensible a voltage.

Or an old-fashioned mechanical multimeter.

Doesn't that classify as a "true RMS meter that's fast enough"? :wink:

It's odd, but I don't think of "damped" as "fast"

No it should not! It's PWM, only a true RMS meter that's fast enough will read sensible a voltage.

My $17.999 multi-meter from Amazon in fact displayed 2 volts then I entered 2 into the serial monitor, displayed 3 volts when I entered 3 into the serial monitor, and so on. So, it seemed to work. However, the display on the multi-meter lasted only a second or so, then went back to zero. My question is: why did it back to zero?

And the code is quite terrible. Even has an error in the calculation.

This is the code from the Simon Monk book. Also, would you be kind enough to point out the error in the calculation?

But uhm, let me guess, line ending in the IDE is set to "Both NL & CR"?

No, the serial monitor was always set to newline.

When will (the crappy) Serial.parseFloat()end?

Timeout defaults to 1000 ms. If I add Serial.setTimeout(5000); then my serial monitor request for, say, 3 volts will display on the multi-meter for 5000 ms, or 5 seconds, before reverting back to zero. Again, I do not understand why the value of pwmValue does not "stick" but instead changes back to zero. That is why I asked the question in the first place.

What will that mean when you send a New Line followed by a Carrage Return?

I do not know, since I never tried this, so I doubt that it could be the cause of the problem.

javaman2015:
My $17.999 multi-meter from Amazon in fact displayed 2 volts then I entered 2 into the serial monitor, displayed 3 volts when I entered 3 into the serial monitor, and so on. So, it seemed to work. However, the display on the multi-meter lasted only a second or so, then went back to zero. My question is: why did it back to zero?

Serial.parseFloat() stops when it encounters first non-numerical character after parsing a valid float representation, or when timeout occurs.

When you first called Serial.parseFloat() you successfully retrieved your float value from the stream. However, when Serial.parseFloat()stopped parsing there was still some leftover characters left in the input stream: end of line markers that followed your float representation.

On the next iteration of loop(), because of those extra characters still sitting in the input stream, Serial.available() was still greater than zero. So, your code made another attempt to call Serial.parseFloat(). This time no valid float representation was found in the stream. This forced Serial.parseFloat();to terminate by timeout and return 0. And your code used this 0 as the new PWM value.


When Serial.parseFloat() returns 0 there's no way to tell whether it was really 0 in the input stream, or that 0 was caused by a timeout. It is a poorly designed function. Same design error as in the standard atoi and its company.

A better idea is to use Serial.readBytes and parse the received data manually afterwards.

Montmorency - your reply and explanation were helpful. I will try Serial.readBytes. Thank you!

javaman2015:
I will try Serial.readBytes.

It requires some effort though. You will have to come up with some "marker" characters that will split your input stream into manageable and individually parseable "chunks"/"lines"/"tokens"/whatever. The previously mentioned new line characters are good potential candidates.

Then you will have to do Serial.readBytes() or Serial.read() and accumulate the data in an internal buffer until you encounter that "marker". This will mean that you've read a complete "chunk" and now can parse it.

This is a rather standard technique, but it is not as easy as just calling Serial.parseFloat() (basically, this is quite similar to what Serial.parseFloat() does internally).

Maybe for starters reading the stream byte-by-byte through Serial.read() will prove to be easier.

What the OP in this thread is doing is exactly the technique I am talking about. Their data arrives enclosed in triangular brackets <...>, which is what they are using as "markers".

You can also get ideas from the Serial Input Basics thread. Instead of the atoi in one of the examples, you can use atof to convert text to a float.

sterretje:
You can also get ideas from the Serial Input Basics thread. Instead of the atoi in one of the examples, you can use atof to convert text to a float.

This is the correct approach, except that one should avoid using functions from ato... group.

The proper C standard library functions for string-to-number conversions are functions from strto... group: strtof, strtod, strtol etc.

one should avoid using functions from ato... group.

Can you please expand on the reason for this ?

javaman2015:
My $17.999 multi-meter from Amazon in fact displayed 2 volts then I entered 2 into the serial monitor,

Then you're lucky :wink: Don't get to used to threat analogWrite() as a voltage, it will bite you :wink:

javaman2015:
This is the code from the Simon Monk book. Also, would you be kind enough to point out the error in the calculation?

I would burn it ::slight_smile: And the error? A byte has 256 levels, not 255.

javaman2015:
No, the serial monitor was always set to newline.

Ah, yeah, my bad. .parseFloat() leaves any char in the buffer that it does not use in the buffer. So even a single NL will retrigger the parseFloat() again. Multiple options:

  • Set to no line ending (but gives slow response)
  • Empty buffer after a parse
  • Don't use the ugly parse function. Going to a float for a decimal point is ugly anyway.

UKHeliBob:
Can you please expand on the reason for this ?

Functions from ato... group suffer from pretty much the same problem as Serial.parseFloat(), albeit to a less terminal degree: these functions provide no means for detecting bad input.

Firstly, if parsing error occurred, these functions simply return 0. No extra feedback is provided. So, when they return 0, there's no way to tell whether an error occurred or the input was actually just 0. Secondly, if the input value overflows, these functions trigger undefined behavior.

To deal with the first problem people often implement their own parsers, trying either to pre-parse the input (to make sure the format is correct before calling an ato... function) or post-parse the input (to figure out what that returned 0 means). They are essentially trying to re-implement the parser that is already implemented inside the ato... function. This is, of course, extremely bad programming technique.

The second problem - the danger of undefined behavior on overflow - can't be meaningfully defended against at all.

Specifically for these reasons these functions were basically "abandoned" by standard C language. Nobody dared to officially deprecate ato... functions back then (they were probably used in a lot of old code by 1990). Instead the C Rationale (PDF link) uses the word "subsumed" for them: "atof, atoi, and atol are subsumed by strtod and strtol, but were retained because they are used extensively in existing code."

New functions in strto... group were introduced to provide conversion facilities. These functions supply full feedback to recognize parsing errors and they also detect and report overflow conditions.

Basically, ato... functions is something one can see in "back of a napkin" sketching/prototyping code. ato... in production code is an instant red flag.

Thanks for the explanation.

I note that you say with reference to the ato... functions

Firstly, if parsing error occurred, these functions simply return 0. No extra feedback is provided. So, when they return 0, there's no way to tell whether an error occurred or the input was actually just 0. Secondly, if the input value overflows, these functions trigger undefined behavior.

Reading up on the strto... functions I find that

If no valid conversion could be performed, a zero value is returned.

and

If str does not point to a valid C-string, or if endptr does not point to a valid pointer object, it causes undefined behavior.

Quotes taken from http://www.cplusplus.com/reference/cstdlib/strtoul/

So, are the strto... functions any better than the ato... functions in these respects ?

The strto functions have an endptr argument that can be used. Never sure how it works so I always have to look it up; I think it is set NULL if input is valid or points to first invalid character if not valid.

UKHeliBob:
Reading up on the strto... functions I find thatand
Quotes taken from http://www.cplusplus.com/reference/cstdlib/strtoul/

So, are the strto... functions any better than the ato... functions in these respects ?

Yes, of course (see std::strtol, std::strtoll - cppreference.com)

In addition to their result,strto... functions have an extra parameter - endptr - which they use as an output parameter. Through this parameter they tell you where exactly they stopped their parsing of the input string. If the input string is syntactically invalid, the function returns 0 and also makes *endptr to point to the very beginning of the original string that you passed in. The function basically tells you "I couldn't parse anything". This is how you detect invalid inputs: returned endptr value is the same as str value.

And the fact that they set errno to ERANGE in case of overflow (instead of falling into undefined behavior) is their another obvious benefit over ato....


As a rather representative use-case:

In C and C++ the valid numeric input format for ato.../strto.../sscanf and other conversion functions has the following structure

[spaces] [sign] number [any garbage]

Note that trailing [any garbage] part. E.g. as far as atoi is concerned, " 123v#daljkfc" is a valid input and the conversion result is 123. atoi does not provide you with any means to detect that trailing "garbage" part.

In some cases you might want to. You might want stricter validity checks in your program, i.e. you might want to detect and reject (or at least warn about) such inputs as " 123v#daljkfc".

Note that strtol will also consider this input as valid and will also return 123. However, it will also give you that endptr pointer pointing to the v character. By analyzing that endptr you can always easily detect the fact that there was something extra attached after the actual number.

Thanks for the extra detail.

A couple of small test programs have extended my understanding.

As to whether I will bother with the extra complexity of testing for errors in my simple programs is another matter :slight_smile: