The fastest way to schedule an automated task on a single-board computer is using the Linux crontab. A cron job Raspberry Pi setup allows you to trigger Python scripts at exact intervals without keeping a terminal window open or writing complex background daemons. To log sensor data every 5 minutes, you write a Python script and add */5 * * * * /usr/bin/python3 /home/pi/sensor.py to your user crontab.
However, Raspberry Pi OS Bookworm introduced strict Python environment rules (PEP 668) and altered GPIO permissions, breaking many legacy cron tutorials. This guide provides a bulletproof, modern approach to scheduling hardware-interacting scripts, complete with exact error resolutions.
Time to Complete: 25 Minutes
Project Overview & Hardware Requirements
This build targets the Raspberry Pi 4 Model B (4GB variant) running Raspberry Pi OS Bookworm (64-bit). We will log the internal CPU temperature and read the state of a physical push button to simulate a hardware sensor trigger. By using the built-in gpiozero library and the sysfs thermal zone, we bypass the PEP 668 pip install restrictions that cause 90% of cron job failures on modern Pi OS.
Parts List
- Board: Raspberry Pi 4 Model B (4GB RAM) or Raspberry Pi 5
- OS: Raspberry Pi OS Bookworm (64-bit, Lite or Desktop)
- Component: Momentary push button (normally open)
- Wiring: 2x female-to-female Dupont jumper wires
- Storage: 16GB+ Class 10 MicroSD card
Pin Mapping Table
| Component | Component Pin | Raspberry Pi GPIO | Physical Pin # |
|---|---|---|---|
| Push Button | Leg 1 | GPIO 17 | Pin 11 |
| Push Button | Leg 2 | GND | Pin 9 |
Note: We use the internal pull-up resistor via software, so no external 10kΩ pull-up resistor is required.
Writing the Python Sensor Script
The most critical rule for cron scripts is using absolute paths for every file read, file write, and binary execution. Cron runs with a severely restricted $PATH environment variable. If you use relative paths like data.csv, the file will be written to /root/ or / instead of your home folder, and the script will silently fail.
Create the script using nano /home/pi/pi_status_log.py and paste the following compilable code:
#!/usr/bin/env python3
import csv
import os
from datetime import datetime
from gpiozero import Button
# Pin Definition: Physical Pin 11 (GPIO 17) wired to a push button
BUTTON_PIN = 17
# ABSOLUTE PATH: Critical for cron job compatibility
LOG_FILE = '/home/pi/pi_status_log.csv'
def get_cpu_temp():
try:
# Read from the standard Linux thermal zone sysfs
with open('/sys/class/thermal/thermal_zone0/temp', 'r') as f:
return round(int(f.read()) / 1000.0, 2)
except Exception:
return -1.0
def log_status():
try:
# Initialize GPIO with internal pull-up
btn = Button(BUTTON_PIN, pull_up=True, bounce_time=0.1)
btn_state = 'PRESSED' if btn.is_pressed else 'RELEASED'
temp = get_cpu_temp()
timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
file_exists = os.path.isfile(LOG_FILE)
with open(LOG_FILE, 'a', newline='') as f:
writer = csv.writer(f)
if not file_exists:
writer.writerow(['Timestamp', 'CPU_Temp_C', 'GPIO17_State'])
writer.writerow([timestamp, temp, btn_state])
except PermissionError as e:
print(f'Cron permission error: {e}')
except Exception as e:
print(f'Logging failed: {e}')
if __name__ == '__main__':
log_status()
Test the script manually first by running python3 /home/pi/pi_status_log.py. Verify that /home/pi/pi_status_log.csv is created and contains a row of data.
Configuring the Cron Job Raspberry Pi Schedule
Now we schedule the script. We will use the user-level crontab, which runs as the pi user. This avoids the GPIO permission issues that occur when running hardware scripts as root.
- Open the crontab editor:
crontab -e - If prompted, select
1for nano. - Scroll to the bottom of the file and add the following line to run the script every 5 minutes:
*/5 * * * * /usr/bin/python3 /home/pi/pi_status_log.py >> /home/pi/cron_debug.log 2>&1 - Save and exit (
Ctrl+O,Enter,Ctrl+X). - Verify the cron service is active:
sudo systemctl status cron
>> /home/pi/cron_debug.log 2>&1 appendage is mandatory for debugging. It redirects both standard output and standard error to a text file. Without this, cron errors vanish into the void. Use crontab.guru to verify complex timing strings.
Debugging: When Your Cron Job Fails to Run
If your CSV file isn't updating, do not guess. Check /home/pi/cron_debug.log immediately. Here are the first three things to check, followed by the exact error strings you will encounter.
The First Three Things to Check
- Absolute Paths: Did you use
/usr/bin/python3instead of justpython3? Did you use/home/pi/script.pyinstead ofscript.py? - Execution Permissions: Run
chmod +x /home/pi/pi_status_log.pyto ensure the file is executable. - User Context: Did you accidentally use
sudo crontab -e? Hardware GPIO scripts should usually run in the user crontab (crontab -e) to inherit the correctgpiogroup permissions.
Exact Error Strings and Ranked Causes
Error 1: /bin/sh: 1: python3: not found
- Cause: Cron's default
$PATHis usually just/usr/bin:/bin. It cannot find the python alias. - Fix: Change
python3to/usr/bin/python3in your crontab entry. Find your exact path by runningwhich python3in your normal terminal.
Error 2: PermissionError: [Errno 13] Permission denied: '/var/log/status.csv'
- Cause: You attempted to write the CSV to a system directory like
/var/log/or/root/, but the cron job is running as the standardpiuser. - Fix: Change your
LOG_FILEvariable to an absolute path inside your home directory, e.g.,/home/pi/pi_status_log.csv.
Error 3: gpiozero.exc.BadPinFactory: Unable to load any default pin factory!
- Cause: You edited the root crontab (
sudo crontab -e). The root user in Bookworm lacks the defaultlgpioenvironment mappings thatgpiozerorelies on to talk to the hardware. - Fix: Remove the line from
sudo crontab -e, and add it to the standard usercrontab -einstead. Alternatively, ensure the root user is part of thegpioanddialoutgroups, though user-level execution is vastly preferred for security.
Extending and Simplifying the Build
While cron is excellent for simple time-based triggers, it has limitations. Here is how to adapt this architecture based on your project's scaling needs.
How to Simplify: Systemd Timers
If your script relies on network connectivity (e.g., pushing data to an MQTT broker) and fails because the Wi-Fi hasn't connected yet on boot, cron's @reboot tag will fail silently. Simplify your reliability by switching to a systemd timer. Systemd allows you to declare dependencies, such as After=network-online.target, ensuring your script only runs when the Pi is actually online. Refer to the official Raspberry Pi configuration documentation for systemd service templates.
How to Extend: InfluxDB and Grafana
Logging to a local CSV is fine for a weekend project, but the file will eventually corrupt if power is lost during a write. Extend this build by replacing the csv module with the influxdb-client Python library. You can run InfluxDB in a Docker container on the same Pi 4, allowing you to build real-time Grafana dashboards without worrying about CSV file locks or SD card write-wear from constant appends.
FAQ: Cron Job Raspberry Pi Questions
Why is my cron job raspberry pi script not running on reboot?
If you are using the @reboot directive, the script executes the exact millisecond the cron daemon starts. At this point, the Pi's network interfaces, USB buses, and I2C/SPI kernel modules may not be fully initialized. If your script requires hardware or network access, it will crash instantly. To fix this, add a sleep 30 command before your Python execution in the crontab: @reboot sleep 30 && /usr/bin/python3 /home/pi/script.py.
How do I run a cron job raspberry pi task every second?
You cannot. The cron daemon's minimum resolution is strictly one minute. If you need to poll a GPIO pin or sensor every 500 milliseconds, cron is the wrong tool. Instead, write a continuous while True: loop in Python with a time.sleep(0.5) delay, and run that script as a background systemd service so it survives reboots.
Where does cron job raspberry pi output go if I don't specify a log file?
If you omit the >> /path/to/log.log 2>&1 redirect, cron captures the standard output and standard error, and attempts to email it to the local user via the postfix or exim4 mail daemon. Because most Pi setups do not have a local mail reader configured, this data is effectively lost. You can sometimes find it buried in /var/mail/pi, but explicitly defining a log file redirect is mandatory for reliable debugging.






