# Arduino Language Learning: millis() Function
## **What is millis()?**
The **millis()** function is a built-in Arduino function that returns the number of milliseconds since the Arduino board began running the current program. It's like a stopwatch that starts when you upload your code and keeps running until you reset the board.
### **Basic Concept**
Think of `millis()` as a digital clock that:
- **Starts at 0** when your program begins
- **Counts up** continuously in milliseconds
- **Never stops** unless you reset the Arduino
- **Overflows** after about 49.7 days (4,294,967,295 milliseconds)
### **Why Use millis() Instead of delay()?**
- **delay()**: Pauses the entire program (blocking)
- **millis()**: Non-blocking timing (program continues running)
- **Better Performance**: Allows multiple tasks to run simultaneously
- **Responsive**: Keeps your Arduino responsive to inputs
## **Basic millis() Usage**
### **Syntax**
```cpp
unsigned long currentTime = millis();
```
### **Return Type**
- **Type**: `unsigned long`
- **Range**: 0 to 4,294,967,295 (about 49.7 days)
- **Unit**: Milliseconds (1/1000th of a second)
### **Simple Example**
```cpp
void setup() {
Serial.begin(9600);
Serial.println("Program started!");
}
void loop() {
unsigned long currentTime = millis();
Serial.print("Time since start: ");
Serial.print(currentTime);
Serial.println(" milliseconds");
delay(1000); // Wait 1 second
}
```
**Serial Monitor Output:**
```
Program started!
Time since start: 0 milliseconds
Time since start: 1000 milliseconds
Time since start: 2000 milliseconds
Time since start: 3000 milliseconds
```
## **Converting millis() to Different Units**
### **Seconds**
```cpp
unsigned long seconds = millis() / 1000;
```
### **Minutes**
```cpp
unsigned long minutes = millis() / 60000; // 60,000 ms = 1 minute
```
### **Hours**
```cpp
unsigned long hours = millis() / 3600000; // 3,600,000 ms = 1 hour
```
### **Complete Time Display**
```cpp
void setup() {
Serial.begin(9600);
}
void loop() {
unsigned long currentMillis = millis();
unsigned long seconds = currentMillis / 1000;
unsigned long minutes = seconds / 60;
unsigned long hours = minutes / 60;
// Remove completed units
seconds = seconds % 60;
minutes = minutes % 60;
Serial.print("Uptime: ");
Serial.print(hours);
Serial.print("h ");
Serial.print(minutes);
Serial.print("m ");
Serial.print(seconds);
Serial.println("s");
delay(1000);
}
```
**Serial Monitor Output:**
```
Uptime: 0h 0m 1s
Uptime: 0h 0m 2s
Uptime: 0h 0m 3s
...
Uptime: 0h 1m 0s
Uptime: 0h 1m 1s
```
## **Non-blocking Timing with millis()**
### **The Problem with delay()**
```cpp
void loop() {
// Task 1
digitalWrite(13, HIGH);
delay(1000); // Program stops here for 1 second
// Task 2
digitalWrite(13, LOW);
delay(1000); // Program stops here for 1 second
// Task 3 - This won't run until 2 seconds later!
Serial.println("Hello");
}
```
### **The Solution with millis()**
```cpp
unsigned long previousMillis = 0;
const long interval = 1000; // 1 second
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Task 1: Toggle LED
static boolean ledState = false;
ledState = !ledState;
digitalWrite(13, ledState);
// Task 2: Print message
Serial.println("Hello");
}
// Other tasks can run here without delay!
}
```
## **Timing Patterns with millis()**
### **Pattern 1: Simple Interval**
```cpp
unsigned long previousMillis = 0;
const long interval = 2000; // 2 seconds
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Code to run every 2 seconds
Serial.println("2 seconds have passed!");
}
}
```
### **Pattern 2: Multiple Intervals**
```cpp
unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;
const long interval1 = 1000; // 1 second
const long interval2 = 3000; // 3 seconds
void loop() {
unsigned long currentMillis = millis();
// Task 1: Every 1 second
if (currentMillis - previousMillis1 >= interval1) {
previousMillis1 = currentMillis;
Serial.println("Task 1: Every second");
}
// Task 2: Every 3 seconds
if (currentMillis - previousMillis2 >= interval2) {
previousMillis2 = currentMillis;
Serial.println("Task 2: Every 3 seconds");
}
}
```
### **Pattern 3: One-shot Timer**
```cpp
unsigned long startTime = 0;
const long duration = 5000; // 5 seconds
boolean timerActive = false;
void loop() {
unsigned long currentMillis = millis();
// Start timer
if (!timerActive) {
startTime = currentMillis;
timerActive = true;
Serial.println("Timer started!");
}
// Check if timer finished
if (timerActive && (currentMillis - startTime >= duration)) {
timerActive = false;
Serial.println("Timer finished! 5 seconds have passed.");
}
}
```
### **Pattern 4: Countdown Timer**
```cpp
unsigned long startTime = 0;
const long countdownDuration = 10000; // 10 seconds
boolean countdownActive = false;
void loop() {
unsigned long currentMillis = millis();
// Start countdown
if (!countdownActive) {
startTime = currentMillis;
countdownActive = true;
Serial.println("Countdown started!");
}
// Show remaining time
if (countdownActive) {
unsigned long elapsed = currentMillis - startTime;
unsigned long remaining = countdownDuration - elapsed;
if (remaining > 0) {
Serial.print("Time remaining: ");
Serial.print(remaining / 1000);
Serial.println(" seconds");
} else {
countdownActive = false;
Serial.println("Countdown finished!");
}
}
delay(1000); // Update every second
}
```
## **Timing Statistics with millis()**
### **Average Time Calculation**
```cpp
unsigned long startTime = 0;
unsigned long totalTime = 0;
int loopCount = 0;
void loop() {
unsigned long loopStart = millis();
// Simulate some work
delay(random(100, 500)); // Random delay 100-500ms
// Calculate loop time
unsigned long loopTime = millis() - loopStart;
totalTime += loopTime;
loopCount++;
// Calculate and display average
unsigned long averageTime = totalTime / loopCount;
Serial.print("Loop #");
Serial.print(loopCount);
Serial.print(" took ");
Serial.print(loopTime);
Serial.print("ms, Average: ");
Serial.print(averageTime);
Serial.println("ms");
}
```
### **Performance Monitoring**
```cpp
unsigned long lastReportTime = 0;
const long reportInterval = 5000; // Report every 5 seconds
int operationsCount = 0;
void loop() {
// Simulate operations
for (int i = 0; i < 100; i++) {
// Some work here
operationsCount++;
}
// Report performance every 5 seconds
unsigned long currentMillis = millis();
if (currentMillis - lastReportTime >= reportInterval) {
float operationsPerSecond = (float)operationsCount / 5.0;
Serial.print("Performance: ");
Serial.print(operationsPerSecond);
Serial.println(" operations/second");
operationsCount = 0;
lastReportTime = currentMillis;
}
}
```
## **Best Practices**
### **1. Use Constants for Intervals**
```cpp
// Good
const long BLINK_INTERVAL = 1000;
const long REPORT_INTERVAL = 5000;
// Avoid
unsigned long previousMillis = 0;
// ... later in code ...
if (currentMillis - previousMillis >= 1000) {
```
### **2. Handle Overflow Safely**
```cpp
// Good - Safe overflow handling
if ((unsigned long)(currentMillis - previousMillis) >= interval) {
previousMillis = currentMillis;
}
// Avoid - Can fail after overflow
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
}
```
### **3. Use Descriptive Variable Names**
```cpp
// Good
unsigned long lastBlinkTime = 0;
unsigned long lastReportTime = 0;
// Avoid
unsigned long time1 = 0;
unsigned long time2 = 0;
```
### **4. Group Related Timing Variables**
```cpp
// Good - Grouped timing variables
unsigned long lastBlinkTime = 0;
unsigned long lastReportTime = 0;
const long BLINK_INTERVAL = 500;
const long REPORT_INTERVAL = 2000;
// Avoid - Scattered variables
unsigned long time1 = 0;
const long interval1 = 500;
unsigned long time2 = 0;
const long interval2 = 2000;
```
### **5. Use millis() for Non-blocking Delays**
```cpp
// Good - Non-blocking
unsigned long previousMillis = 0;
const long interval = 1000;
void loop() {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
// Do something
}
// Other code can run here
}
// Avoid - Blocking
void loop() {
// Do something
delay(1000); // Blocks everything
// Other code waits
}
```
## **Common Use Cases**
### **1. Non-blocking Delays**
- Replace `delay()` with `millis()` for responsive programs
- Allow multiple tasks to run simultaneously
- Keep Arduino responsive to inputs
### **2. Timing Events**
- Schedule tasks at specific intervals
- Create time-based sequences
- Implement countdown timers
### **3. Performance Monitoring**
- Measure execution time
- Calculate averages and statistics
- Monitor system performance
### **4. State Machines**
- Time-based state transitions
- Create complex timing patterns
- Implement animations and sequences
### **5. Data Logging**
- Timestamp events and data
- Create time-based data records
- Implement periodic reporting
## **Summary**
### **Key Points:**
1. **millis()** returns milliseconds since program start
2. **Non-blocking**: Unlike `delay()`, doesn't pause the program
3. **Overflow**: Occurs after ~49.7 days (4,294,967,295 ms)
4. **Safe Handling**: Use `(unsigned long)` casting for overflow safety
5. **Multiple Tasks**: Enable simultaneous timing of different events
### **When to Use millis():**
- **Non-blocking Delays**: Replace `delay()` for responsive programs
- **Multiple Timers**: Run several timed events simultaneously
- **State Machines**: Time-based state transitions
- **Performance Monitoring**: Measure execution times
- **Data Logging**: Add timestamps to events
### **Next Steps:**
- **Practice**: Try the timing patterns with your Arduino
- **Combine**: Use millis() with other Arduino functions
- **Experiment**: Create your own timing-based projects
- **Explore**: Learn about micros() for microsecond timing
**millis() is essential for creating responsive, multi-tasking Arduino programs. Master it, and you'll be able to create sophisticated timing-based projects!**
TinkerBlock UNO R3 Starter Kit
Dive into the world of electronics, Arduino programming, and STEM projects with the Lonely Binary TinkerBlock Series Starter Kit.
- Choosing a selection results in a full page refresh.