Advance Input Output
Advanced I/O functions extend the Arduino’s capabilities beyond basic digital and analog operations. These functions enable sound generation, pulse timing measurements, and efficient data transfer with parallel and serial devices.
tone()
The tone() function generates a square wave of a specified frequency on a pin. It is commonly used to drive piezo buzzers or speakers. One pin can produce a tone at a time; calling tone() on a second pin will stop the tone on the first.
const int buzzerPin = 8;
void setup()
{
pinMode(buzzerPin, OUTPUT);
}
void loop()
{
// Play a 440 Hz (A4) tone for 500 ms
tone(buzzerPin, 440);
delay(500);
// Play a 880 Hz (A5) tone for 500 ms
tone(buzzerPin, 880);
delay(500);
}
tone(pin, frequency) plays a continuous tone until noTone() is called. A third argument sets a duration in milliseconds, after which the tone stops automatically: tone(pin, 440, 1000) plays for 1 second and then stops without needing noTone().
noTone()
The noTone() function stops the square wave currently being generated by tone() on a pin. It takes one argument the pin number.
const int buzzerPin = 8;
void setup()
{
pinMode(buzzerPin, OUTPUT);
}
void loop()
{
// Play a tone for 1 second, then stop
tone(buzzerPin, 523); // C5 note
delay(1000);
noTone(buzzerPin);
delay(500);
}
Using noTone() gives you precise control over when the sound stops, rather than relying on the optional duration parameter of tone().
pulseIn()
pulseIn() measures the duration of a pulse (HIGH or LOW) on a digital pin. It waits for the pin to transition to the expected state and then times the length of that pulse. It is often used with ultrasonic distance sensors (HC-SR04) to measure echo time.
const int trigPin = 9;
const int echoPin = 10;
void setup()
{
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
void loop()
{
// Send a 10-microsecond trigger pulse
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Measure echo pulse duration in microseconds
long duration = pulseIn(echoPin, HIGH);
float distance = duration * 0.034 / 2; // convert to cm
Serial.print("Distance: ");
Serial.print(distance);
Serial.println(" cm");
delay(500);
}
pulseIn(pin, value) measures a pulse of the specified state (HIGH or LOW). An optional timeout (in microseconds) prevents it from waiting forever: pulseIn(pin, HIGH, 10000) times out after 10 ms.
pulseInLong()
pulseInLong() works identically to pulseIn() but uses a more robust measurement method that is less susceptible to interrupt interference. It is preferred for long pulses or when your sketch uses interrupts.
const int echoPin = 10;
void setup()
{
Serial.begin(9600);
pinMode(echoPin, INPUT);
}
void loop()
{
// Measure a long pulse with better accuracy
long duration = pulseInLong(echoPin, HIGH, 50000);
if (duration == 0)
{
Serial.println("Timeout - no pulse detected");
}
else
{
Serial.print("Pulse duration: ");
Serial.print(duration);
Serial.println(" us");
}
delay(500);
}
pulseInLong() disables interrupts during measurement, making it more accurate for longer pulses but slightly blocking other interrupt-driven operations. Use pulseIn() for short, frequent measurements and pulseInLong() when accuracy matters.
shiftOut()
shiftOut() shifts out a byte of data one bit at a time on a specified pin. It is used to communicate with shift registers (e.g., 74HC595) and other serial-interface devices. Data is sent either most-significant-bit (MSBFIRST) or least-significant-bit (LSBFIRST) first.
const int dataPin = 2; // DS (Serial data input)
const int clockPin = 3; // SH_CP (Shift register clock)
const int latchPin = 4; // ST_CP (Storage register clock / latch)
void setup()
{
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(latchPin, OUTPUT);
}
void loop()
{
// Count from 0 to 255 on 8 LEDs connected to a 74HC595
for (int count = 0; count < 256; count++)
{
digitalWrite(latchPin, LOW); // prepare to shift
shiftOut(dataPin, clockPin, MSBFIRST, count); // send data
digitalWrite(latchPin, HIGH); // latch outputs
delay(100);
}
}
shiftOut(dataPin, clockPin, bitOrder, value) sends a byte (value) serially. bitOrder is either MSBFIRST or LSBFIRST. The latchPin toggling is specific to shift registers like the 74HC595 - it updates all outputs simultaneously after the data has been shifted.
shiftIn()
shiftIn() shifts in a byte of data one bit at a time from a pin. It is used to read data from parallel-to-serial shift registers (e.g., 74HC165) or other serial devices.
const int dataPin = 2; // Q7 (Serial data output)
const int clockPin = 3; // CP (Clock)
const int loadPin = 4; // PL (Parallel load / latch)
void setup()
{
Serial.begin(9600);
pinMode(dataPin, INPUT);
pinMode(clockPin, OUTPUT);
pinMode(loadPin, OUTPUT);
}
void loop()
{
// Latch the parallel inputs into the shift register
digitalWrite(loadPin, LOW);
delayMicroseconds(5);
digitalWrite(loadPin, HIGH);
// Read the byte
byte incoming = shiftIn(dataPin, clockPin, MSBFIRST);
Serial.print("Button state: ");
Serial.println(incoming, BIN);
delay(200);
}
shiftIn(dataPin, clockPin, bitOrder) returns a byte containing the data shifted in from the device. The loadPin is specific to the 74HC165 pulsing it low captures the parallel input state into the register.
Complete Example: Ultrasonic Distance Sensor with Buzzer
This example combines pulseIn(), tone(), and noTone() to create a distance-sensing alarm. As an object gets closer to the ultrasonic sensor, the buzzer beeps faster.
const int trigPin = 9;
const int echoPin = 10;
const int buzzerPin = 8;
void setup()
{
Serial.begin(9600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(buzzerPin, OUTPUT);
}
void loop()
{
// Trigger ultrasonic pulse
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// Measure echo
long duration = pulseIn(echoPin, HIGH, 30000);
float distance = duration * 0.034 / 2;
if (distance > 0 && distance < 200)
{
// Map distance to beep interval: closer = faster beeps
int interval = map(distance, 0, 200, 50, 1000);
tone(buzzerPin, 1000);
delay(interval);
noTone(buzzerPin);
delay(interval);
Serial.print("Distance: ");
Serial.print(distance);
Serial.print(" cm Interval: ");
Serial.println(interval);
}
else
{
noTone(buzzerPin);
}
delay(100);
}