Lesson 76: Understanding Stream Class
Learn how Stream powers read/available/parse functions and how it differs from Print in Arduino.
Progress indicator
Lesson 76 of 76
Learning Objectives
- Understand what the Stream class is and why many Arduino input sources use the same read API.
- Learn the exact relationship between Stream (input) and Print (output) in Serial communication.
- Use available(), read(), parseInt(), and parseFloat() with safer beginner-friendly patterns.
- Parse simple text commands into integer/float runtime settings with validation.
- Avoid blocking behavior, timeout confusion, and partial-command parsing mistakes.
Concept Explanation
What is the Stream Class
Stream is an Arduino base class for reading incoming data from sources like Serial, WiFi clients, files, and software serial ports.
In simple terms, Stream gives you a standard way to consume incoming bytes no matter where they come from. This makes your input logic easier to reuse later.
Stream Class Purpose
It provides a common input API so you can use similar code patterns across different communication channels.
- Read one byte at a time.
- Check if data is available.
- Parse numeric values from text input.
- Apply the same mental model across Serial, network streams, and file streams.
How Stream Works
- Incoming bytes are placed in a receive buffer.
available()tells how many bytes are waiting.read()consumes one byte from that buffer.parseInt()/parseFloat()scan and convert numeric text.- Your application validates and applies parsed values to runtime variables.
Stream vs Print (Comparison)
Printis for output (sending data).Streamis for input (reading/parsing data).Serialsupports both because it combines read and write behavior.- Typical loop pattern: read input with Stream functions, then send feedback using Print functions.
Common Stream Functions
available(),read(),peek()readBytes(),readString(),readStringUntil()parseInt(),parseFloat(),setTimeout()
For beginners, start with available() + read() or readStringUntil() before moving into more advanced buffer handling.
read() and available() Basics
Always call available() before read(). This prevents empty buffer reads and keeps your loop stable.
Think of available() as checking your inbox size. If inbox is zero, do not try to read a message yet.
parseInt() and parseFloat()
These functions scan stream text and convert number tokens automatically. They are beginner-friendly, but they can wait for timeout if expected data does not arrive.
parseInt()returns whole numbers.parseFloat()returns decimal numbers.- When parsing fails, return values may default to
0or0.0.
When to Use Stream
- Serial command interfaces for quick testing.
- Reading structured input from communication modules.
- Simple text protocol parsing in embedded projects.
- Any project where runtime values must be adjusted without recompiling code.
Example Code
This example reads text commands and parses integer/float values using Stream functions.
int setPoint = 25;
float gain = 1.5;
void setup() {
Serial.begin(115200);
Serial.setTimeout(300);
Serial.println("Send command format:");
Serial.println("TEMP 30");
Serial.println("GAIN 2.25");
}
void loop() {
if (Serial.available() > 0) {
String command = Serial.readStringUntil(' ');
command.trim();
if (command == "TEMP") {
int value = Serial.parseInt();
if (value > 0) {
setPoint = value;
Serial.print("Updated setPoint: ");
Serial.println(setPoint);
} else {
Serial.println("Invalid TEMP value");
}
} else if (command == "GAIN") {
float value = Serial.parseFloat();
if (value > 0.0) {
gain = value;
Serial.print("Updated gain: ");
Serial.println(gain, 2);
} else {
Serial.println("Invalid GAIN value");
}
} else {
Serial.print("Unknown command: ");
Serial.println(command);
}
}
delay(50);
}Example Code Explanation
Serial.setTimeout(300)sets parser wait limit so loop is less likely to block for long.readStringUntil(' ')reads command name (TEMPorGAIN) until first space.command.trim()removes extra spaces/newline characters for cleaner comparisons.- If command is
TEMP,parseInt()reads the next integer token from stream. - Integer value is validated before applying to
setPoint. - If command is
GAIN,parseFloat()reads decimal token and updatesgain. - Float value is validated before update, then printed with fixed decimal formatting.
- Unknown command path provides feedback so user sees command mismatch immediately.
- Short delay keeps loop calm and avoids excessive serial traffic.
What Happens Inside
- Bytes enter Serial RX buffer from your terminal input.
- Stream methods scan the buffer to find delimiters and numeric tokens.
- Parser converts token text into int/float values.
- If parsing times out, default fallback values (often 0) may be returned.
- Your code decides whether parsed values are valid before applying them.
- After update, Print methods send confirmation text to Serial Monitor.
- This read-parse-validate-print cycle repeats continuously in
loop().
Common Mistakes with Stream
- Calling
read()without checkingavailable(). - Using parse functions without timeout awareness, causing blocked loops.
- Not validating parsed values before applying them to control logic.
- Assuming one read call always contains a complete command.
- Not clearing spaces/newlines, causing command comparison failures.
- Applying parsed zero values without checking whether parse actually succeeded.
Best Practices for Stream
- Use clear command formats (example:
TEMP 30,GAIN 2.25). - Set a reasonable timeout using
setTimeout(). - Validate parsed values before updating system variables.
- Provide serial feedback messages so users know what the board accepted.
- Use explicit command grammar and document it in startup serial output.
- Keep timeout short enough for responsiveness but long enough for expected user input.
Try it now
Open the simulator workspace and step through Stream parsing flow for TEMP/GAIN commands.