#!/bin/bash # # monitor.sh -- top-level ignition orchestrator. # # Watches $122 on CAN-IHS for ignition state changes and uses them to # coordinate several other in-vehicle Pi processes: # # On ignition-on transition: # - CPU governor -> ondemand (more power while the engine runs) # - Spawn autocollect.sh (vehicle-statistics collector) # - Spawn dump (any) (black-box CAN-traffic recorder) # - Spawn autohvac (remote-start HVAC automation; # exits on its own if not remote-started) # # On ignition-off transition: # - Kill the black-box recorder # - CPU governor -> powersave (energy efficient while parked) # # Originally created: 01.2022 by jmccorm # Last updated: 05.2026 (polish by magikh0e) # # RELATION TO OTHER ON-SITE SCRIPTS # This script is a SUPERSET of Blackbox_monitor.sh -- both watch # $122 and gate the dump recorder's lifecycle on engine state, but # monitor.sh additionally manages the CPU governor and spawns # autocollect / autohvac. If you're running monitor.sh, you do NOT # also run Blackbox_monitor.sh -- you'd end up with two competing # dump recorders. See the parent index entry for disposition # guidance. # # REQUIRES # - can-utils (candump) apt install can-utils # - cpufreq policy0 writable echo X > /sys/devices/system/cpu/ # cpufreq/policy0/scaling_governor # (write requires root or appropriate # udev rule; if /sys path is missing # set GOVERNOR=false) # - /home/pi/bin/autocollect, dump, autohvac on PATH # - CAN-IHS up at 125 kbps on can0 in this script # # REVISION NOTES (2026-05-16) # - Fixed `[ ... ]]` syntax error on the sleep-30 line. # - Replaced 64-bit-broken `printf %d 0x$IGNITION` comparison with # a portable top-32-bits hex-to-decimal extraction. Original # code worked on 32-bit Pi (Pi 3 and older) because bash printf # overflowed the value; on 64-bit Pi 4 / 5 the larger int range # would make the comparison always-true and falsely report # "engine running" on every frame. # - Fixed "IGNITIOIN" typo in tail comment. # - Quoted variables passed to candump cut chain. # - Header documents the relation to Blackbox_monitor.sh so a # reader doesn't accidentally run both. PATH=$PATH:/home/pi/bin touch /tmp/monitor # TUNABLE VARIABLES # Name for the CAN-IHS interface (can0, can1, vcan0, etc) CANIHS=can0 # Name of the WiFi device used in ifconfig, iwconfig WIFIDEV=wlan0 # ENABLE OR DISABLE DEBUG STATEMENTS DEBUG=false # Values: true,false,raw # AUTOMATICALLY TUNE THE CPU GOVERNOR? (ONDEMAND VS POWERSAVE?) GOVERNOR=true # LOCAL VARIABLES WHICH STORE STATE INFORMATION dumpid=0 LASTIGNITION=99 # Wait five seconds and then change the CPU governor to powersave mode. sleep 5 echo $(date) MONITOR: Initializing. echo $(date) MONITOR: uptime $(uptime | cut -d, -f1-2) if [ "$GOVERNOR" == "true" ] ; then echo $(date) MONITOR: Changing CPU governor to: POWERSAVE echo "powersave" > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor fi # CONSTANTLY MONITOR THE IGNITION SWITCH FOR ITS LATEST VALUE. candump -L $CANIHS,0122:0FFFF | while read TIME BUS IGNITION do [ "$DEBUG" == "raw" ] && echo CAN-IHS data: $IGNITION HEADER="TIME : $TIME $( date )\nDATA : $IGNITION" # CONVERT $IGNITION TO A *DECIMAL NUMBER* SO WE CAN DO EASY COMPARISONS. # # Original jmccorm 2022 code did: # IGNITION=$( printf %d 0x$IGNITION ) # which relied on 32-bit bash arithmetic overflowing the full 64-bit # payload value down to the top word. On 64-bit bash (Pi 4 / 5) # the printf returns the full 64-bit integer and the comparison # below would always be true -- every frame would look like "engine # running." The fix below explicitly extracts the top 32 bits. # # RUN MODE OR ENGINE STARTING/RUNNING: top word >= 0x04000000 # ACCESSORY MODE OR VEHICLE OFF: top word < 0x04000000 PAYLOAD=$( echo "$IGNITION" | cut -d"#" -f2 ) IGNITION=$(( 16#${PAYLOAD:0:8} )) # DEBUGGING STATEMENT [ "$DEBUG" == "raw" ] && echo IGNITION: $IGNITION LASTIGNITION: $LASTIGNITION # WHEN ENGINE GOES FROM OFF TO ON, LAUNCH OUR ROUTINES. if [ $IGNITION -gt 67108863 ] ; then if [ $LASTIGNITION -lt 67108864 ] ; then [ "$DEBUG" == "true" ] && echo -e "$HEADER" [ "$DEBUG" == "true" ] && echo "EVENT : VEHICLE HAS BEEN TURNED ON" # CHANGING CPU GOVERNOR TO THE ONDEMAND POLICY # Make more CPU power available while the engine is running. echo $(date) MONITOR: Vehicle starting up. if [ "$GOVERNOR" == "true" ] ; then echo $(date) MONITOR: Changing CPU governor to: ONDEMAND echo "ondemand" > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor fi # START THE AUTOMATED COLLECTION OF VEHICLE STATISTICS echo $(date) MONITOR: Starting Autocollect script. /home/pi/bin/autocollect & # STARTING THE BLACK BOX RECORDER, STORING INTO: /home/pi/log echo $(date) MONITOR: Starting Black Box Recorder. nohup /home/pi/bin/dump any > /dev/null & dumpid="$!" sleep 2 dumpid=$( pgrep -P $dumpid ) [ "$DEBUG" == "true" ] && echo "DUMP STARTED, PID: $dumpid" # A SHORT DELAY BEFORE STARTING THE NEXT SECTION sleep 5 # STARTUP ROUTINE FOR HVAC AUTOMATION AFTER IGNITION-START # When the vehicle is started, we want the remote-start HVAC automation # routine to run. The only exemption is if the engine was already running # when this script was launched. If that's the case ($LASTIGNITION == 99), # then we do nothing. # THIS SCRIPT WAS ORIGINALLY DESIGNED FOR A RASPBERRY # PI THAT RECEIVED CONSTANT POWER. SO IF THIS SCRIPT # SAW THAT THE ENGINE WAS ALREADY RUNNING, IT WOULD NOT # LAUNCH THE AUTOHVAC SCRIPT BECAUSE IT THOUGHT IT MAY # HAVE BEEN REBOOTED WHILE THE ENGINE WAS RUNNING. # # NOW THAT I HAVE THE RASPBERRY PI POWERING ON WITH # THE VEHICLE'S IGNITION (AND BOOTING 30 SECONDS AFTER # THAT), THE TEST IS NO LONGER NEEDED. BUT THE LOGIC # IS LEFT IN PLACE, JUST IT LOOKS FOR AN ABSURD VALUE # OF 123456 INSTEAD OF 99 AS IT'S TEST CONDITION. if [ $LASTIGNITION != 123456 ] ; then echo $(date) MONITOR: Starting AutoHVAC # The timing of autohvac expects that it's been called after the # Raspberry Pi had to time booting, which would have been a # good 30 seconds after the car started. So if the engine WASN'T # already running when this script began, this adds a 30 second # delay so that we wait the same amount (and have reproducible # timings) whether or not the Raspberry Pi had been running or if # it had to take time booting from scratch. [ $LASTIGNITION != 99 ] && sleep 30 # The heater routine will exit if the vehicle was not remote started. # Either way, it will shortly terminate after starting and does not # need monitoring. /home/pi/bin/autohvac & fi [ "$DEBUG" == "true" ] && echo "" fi fi # ENGINE IS NO LONGER RUNNING, KILL THE BLACK BOX if [ $IGNITION -lt 67108864 ] ; then if [ $LASTIGNITION -gt 67108863 ] ; then [ "$DEBUG" == "true" ] && echo -e "$HEADER" [ "$DEBUG" == "true" ] && echo "EVENT : VEHICLE HAS BEEN TURNED OFF" [ "$DEBUG" == "true" ] && echo "ACTION: TERMINATING BLACK BOX RECORDER" # KILLING THE BLACK BOX RECORDER (IF ACTIVE) if [ $dumpid -gt 0 ] ; then sleep 2 echo $(date) MONITOR: Black Box Recorder ended. pkill -TERM -P $dumpid kill -TERM $dumpid dumpid=0 fi # CHANGING CPU GOVERNOR TO THE POWERSAVE POLICY # Become more energy efficient while the engine is not running. if [ "$GOVERNOR" == "true" ] ; then echo $(date) MONITOR: Changing CPU governor to: POWERSAVE echo "powersave" > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor fi [ "$DEBUG" == "true" ] && echo "" fi fi # KEEP TRACK OF WHAT THE PREVIOUS STATE OF THE IGNITION SWITCH WAS. # This is how we can tell if it has changed or not, since we want to # take action only when there has been a change in its value. LASTIGNITION=$IGNITION done