#!/bin/bash # # showevic.sh -- decode and display live EVIC music-info text from # CAN-IHS message $328. Passive read counterpart to # evic.sh (which writes the same display). # # Originally created: jmccorm # Last updated: 05.2026 (polish by magikh0e) # # WHAT IT DOES # Tails `candump` for $328 frames on CAN-IHS and reconstructs the # ASCII text the radio / head unit is currently displaying on the # instrument cluster's EVIC region. Output is line-buffered so each # frame becomes visible immediately. Runs until interrupted # (Ctrl+C clean-exits). # # Typical uses: # - Watch what the radio is writing while testing evic.sh # writes from a second terminal # - Verify that a custom CAN-replay rig is broadcasting the # right bytes # - Debug EVIC text that "should" be displayed but isn't # (compare what the bus is carrying vs what the cluster shows) # # $328 FRAME FORMAT (see bus-message-reference.html#id-328) # # Each frame is 8 bytes: 07 NN cc cc cc cc cc cc # # byte 0 = 0x07 marker / write-cluster command type # byte 1 = line marker if 0x41 / 0x42 / 0x43 # -> "Line 1: " / "Line 2: " / "Line 3: " emitted # sub-position byte for continuation frames otherwise # (0x0F / 0x1F / 0x2F etc.) # bytes 2-7 = 6 ASCII chars (or 0x00 padding) # # An all-zero bytes-2-through-7 frame is the line terminator -- we # emit a newline. A normal 6-char frame just gets printed as raw # ASCII using printf's \x escape. # # USAGE # ./showevic.sh Stream live EVIC text # ./showevic.sh --help Print this header # # OVERRIDABLE ENV VARS # IHS_BUS CAN-IHS interface name (default: can0) # # REQUIRES # - can-utils (candump) apt install can-utils # - CAN-IHS interface up at 125 kbps: # ip link set $IHS_BUS up type can bitrate 125000 # - Radio active / vehicle awake -- $328 only broadcasts when the # head unit has something to display. # # REVISION NOTES (2026-05-18) # - jmccorm's exact decode logic preserved (line-marker detection, # terminator-frame newline, ASCII-byte printf reconstruction). # - FIXED: `[ "$e" -gt 39 ]` was bash arithmetic on a hex byte # string. Works on 0x41/0x42/0x43 (the values it cares about), # but throws "integer expression expected" on 0x0F / 0x1F / 0x2F # (the sub-position bytes in continuation frames). Without # set -e, the legacy silently swallowed the error and kept # going; functional, but loud in stderr and brittle if run # under set -e. New version string-matches against literal # "4[123]" instead. # - Renamed the cryptic single-letter read variables (a..k) to # semantic names (iface, id, dlc, b0..b7) so a reader can # follow the decode without consulting candump's output format # in another window. # - Added shebang doc-block, --help, $IHS_BUS env var override, # SIGINT/SIGTERM trap for clean exit, pre-flight checks for # candump and the CAN interface. # - Original preserved as showevic.legacy.txt. # set -u # --------------------------------------------------------------------- # Configuration # --------------------------------------------------------------------- : "${IHS_BUS:=can0}" # --------------------------------------------------------------------- # CLI # --------------------------------------------------------------------- case "${1:-}" in -h|--help) awk '/^# showevic\.sh/,/^$/ { print }' "$0" | sed 's/^# \{0,1\}//' exit 0 ;; esac # --------------------------------------------------------------------- # Pre-flight # --------------------------------------------------------------------- if ! command -v candump >/dev/null 2>&1; then echo "ERROR: candump not found. apt install can-utils" >&2 exit 2 fi if ! ip link show "$IHS_BUS" >/dev/null 2>&1; then echo "ERROR: CAN interface $IHS_BUS not found" >&2 echo " Bring it up: ip link set $IHS_BUS up type can bitrate 125000" >&2 exit 2 fi # --------------------------------------------------------------------- # SIGINT cleanup # --------------------------------------------------------------------- # Ctrl+C should emit a trailing newline so the prompt comes back on a # fresh line, then exit cleanly. The candump subprocess in the pipe # gets SIGPIPE from the shell teardown automatically. cleanup() { echo exit 0 } trap cleanup INT TERM # --------------------------------------------------------------------- # Main: stream $328 frames, decode each one inline. # --------------------------------------------------------------------- # candump default output for a $328 frame looks like: # can0 328 [8] 07 41 4D 61 67 69 6B 65 # # Read splits into 11 fields: # iface=can0 id=328 dlc=[8] b0..b7 = the 8 payload bytes candump "${IHS_BUS},328:0fff" | while read -r iface id dlc b0 b1 b2 b3 b4 b5 b6 b7; do # Line marker frame: byte 1 is one of 0x41 / 0x42 / 0x43. # Emit "Line N: " using the second nibble (1, 2, or 3) of the # line marker. String match instead of arithmetic so we don't # eat a parse error on continuation-frame bytes like 0x0F. if [[ "$b1" =~ ^4[123]$ ]]; then echo -n "Line ${b1:1:1}: " fi # Terminator frame: bytes 2-7 are all zero. Emit a newline so # the next line of EVIC text starts cleanly. if [[ "$b2$b3$b4$b5$b6$b7" == "000000000000" ]]; then echo "" continue fi # Decode bytes 2-7 as ASCII characters. printf "%b" "\x${b2}\x${b3}\x${b4}\x${b5}\x${b6}\x${b7}" done