├── vcan-up.sh ├── canup.sh ├── showevic.sh ├── 3honk.sh ├── ridscan.sh ├── README.md ├── overheadflash.sh ├── batvolt.sh ├── 2k.sh ├── dimmer.sh ├── cabintemp.sh ├── evic.sh ├── gettime.sh ├── showrpm.sh ├── autoheat.sh ├── autocool.sh ├── obd.sh ├── mute.sh ├── remote.sh ├── autocollect.sh ├── ventmode.sh ├── fanspeed.sh ├── rid.sh ├── pycan2.py ├── tkcan2.py ├── tkcan.py ├── tkcan1.py ├── logreader.sh ├── pycan.py ├── tkcan3.py └── tkcan4.py /vcan-up.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ip link add dev vcan0 type vcan 4 | ip link add dev vcan1 type vcan 5 | ip link set vcan0 up 6 | ip link set vcan1 up 7 | -------------------------------------------------------------------------------- /canup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Sets up the can interfaces 3 | 4 | ip link set can1 up type can bitrate 500000 5 | ip link set can0 up type can bitrate 125000 6 | ifconfig can0 txqueuelen 65536 7 | ifconfig can1 txqueuelen 65536 8 | -------------------------------------------------------------------------------- /showevic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | candump can0,328:0fff | ( while read a b c d e f g h i j k 3 | do 4 | [ "$e" -gt 39 ] && echo -n "Line ${e:1:1}: " 5 | [ "$f$g$h$i$j$k" == "000000000000" ] && echo "" 6 | printf "%b" "\x${f}\x${g}\x${h}\x${i}\x${j}\x${k}" 7 | done 8 | ) 9 | -------------------------------------------------------------------------------- /3honk.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "10 03" | isotpsend -s 620 -d 504 -p 00: -P a can1 4 | for i in 1 2 3 5 | do 6 | echo HONK 7 | echo "2F D0 AD 03 01" | isotpsend -s 620 -d 504 -p 00:00 -P a can1 8 | sleep 0.05 9 | echo UNHONK 10 | echo "2F D0 AD 03 00" | isotpsend -s 620 -d 504 -p 00:00 -P a can1 11 | sleep 0.1 12 | done 13 | -------------------------------------------------------------------------------- /ridscan.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | module=$1 3 | shift 1 4 | a=$(printf "%d" 0x$1) 5 | if [ $? -ne 0 ] ; then a=0 ; fi 6 | if [ "$a" == "" ] ; then a=0 ; fi 7 | b=$(printf "%d" 0x$2) 8 | if [ $? -ne 0 ] ; then b=65535 ; fi 9 | if [ "$b" == "" ] ; then b=65535; fi 10 | 11 | echo START: $a END: $b 12 | 13 | for i in `seq $a $b`; do printf "%4x ------" $i ; rid $module $( printf %4x $i ) 14 | while [ $? == 99 ] ; do 15 | echo "REDO:" ; rid $module $( printf %4x $i ) 16 | done 17 | done 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jeep-jl-powernet-scripts 2 | 3 | This is a repository of knowledge: 4 | Based around the 2018+ Jeep Wrangler JL platform canbus 5 | gathered by a number of people on the jlwranglerforums.com forum 6 | For more details, see the following link: 7 | 8 | https://www.jlwranglerforums.com/forum/threads/reverse-engineering-the-wranglers-can-bus-powernet.82139/ 9 | 10 | We have a spreadsheet of identified Can ID's and data, maintained on Google Docs: 11 | 12 | https://docs.google.com/spreadsheets/d/16ypMADKinBBnH1pOY4-gMmVRjeR85fYplpV12aCHJC4 13 | 14 | Please use this information at your own risk! 15 | -------------------------------------------------------------------------------- /overheadflash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Wake up the CAN bus. 4 | cansend can0 2D3#0700000000000000 5 | sleep 2 6 | 7 | # 2 byte message: Enter Diagnostic Session Control 8 | # Subtype: Extended Diagnostic Session 9 | cansend can1 620#0210030000000000 10 | 11 | sleep 1 12 | 13 | # Perform an infinite loop 14 | 15 | while [ 1 ] 16 | do 17 | 18 | # 5 byte message: Input/Output Control by Identifier 19 | # Identifier $D1BE, option $0301 (overhead courtesy light on) 20 | echo Third brake light on 21 | cansend can1 620#052FD1BE03010000 22 | sleep 0.5 23 | 24 | # 5 byte message: Input/Output Control by Identifier 25 | # Identifier $D1BE, option $0300 (overhead courtesy light off) 26 | echo Third brake light off 27 | cansend can1 620#052FD1BE03000000 28 | sleep 0.5 29 | 30 | done 31 | -------------------------------------------------------------------------------- /batvolt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | error() { 4 | echo "ERROR: Is the vehicle off? Battery voltage data not found." 5 | echo "DEBUG: A valid ID 2C2 message was not seen on CAN-IHS within 2 seconds." 6 | echo " " 7 | # Exit with a failure result code 8 | exit 1 9 | } 10 | 11 | initialize () { 12 | COMMAND="timeout -s 9 2 /usr/bin/candump -L can0,02C2:0fff" 13 | can2C2=$( $COMMAND | tail -1 ) 14 | } 15 | 16 | initialize 17 | if [ "$can2C2" == "" ] ; then 18 | # Wake the CAN bus and try again. 19 | cansend can0 2D3#0700000000000000 ; sleep 1.1 20 | initialize 21 | # Display an error and exit if no messages were received. 22 | [ "$can2C2" == "" ] && error 23 | fi 24 | 25 | if [ "$( echo "$can2C2" | cut -c30-43 )" == "FFFFFFFFFFFFFF" ] ; then error; fi 26 | temp="$( echo "$can2C2" | cut -c34-35 )" 27 | temp="$( printf "%d" 0x$temp )" 28 | temp="$( echo "1 k $temp 10 / p" | dc )" 29 | 30 | echo $temp vdc 31 | -------------------------------------------------------------------------------- /2k.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # No wakeup script necessary 4 | # If the engine isn't running, this whole thing is useless anyhow. 5 | 6 | # Diagnostic session (requesting more rights) 7 | echo "10 92" | isotpsend -s 7E0 -d 7E8 -p 00:00 -P a can1 8 | sleep 0.25 9 | 10 | # Tester is present - remain in diagnostic session 11 | # This is probably redundant, but I'm leaving it here for now. 12 | echo "3E 00" | isotpsend -s 7E0 -d 7E8 -p 00:00 -P a can1 13 | sleep 0.25 14 | 15 | # Set engine to 2K RPMs 16 | echo "31 05 07 D0" | isotpsend -s 7E0 -d 7E8 -p 00:00 -P a can1 17 | 18 | # Maintain current setting by continuing to tell the Wrangler 19 | # that a testing device is still connected to it. As soon as we 20 | # exit this script (CONTROL-C or a kill command), our 21 | # messages to the module will stop, and when that happens, 22 | # we'll exit out of diagnostic mode and the engine RPM 23 | # command will automatically be cancelled. 24 | 25 | while [ 1 ] 26 | do 27 | sleep 0.25 28 | # Tester is present - remain in diagnostic session 29 | echo "3E 00" | isotpsend -s 7E0 -d 7E8 -p 00:00 -P a can1 30 | done 31 | -------------------------------------------------------------------------------- /dimmer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Raspberry Pi Backlight control via 3 | # Jeep wrangler JL canbus 4 | 5 | LAST=77777 6 | LASTDIM=199 7 | 8 | # Watch the canbus for packets from the light switch 9 | COMMAND="/usr/bin/candump -L can1,0291:0FFF" 10 | $COMMAND | while read TIME BUS MESSAGE 11 | do 12 | NOW=$SECONDS 13 | # Run just once per second to save CPU 14 | [ "$NOW" != "$LAST" ] && { 15 | #Pick out just the data bits that we need 16 | DATA=${MESSAGE:4} 17 | RUNL=${DATA:5:1} 18 | DIML=${DATA:12:2} 19 | # Only change the dimmer if the running lights are on 20 | if [[ "$RUNL" == "3" || "$RUNL" == "1" ]] 21 | then 22 | # change hex into decimal 23 | DIMRAW="$(printf "%d" 0x$DIML)" 24 | # scale the 22-200 input to 0-53 25 | DIMCALC="`bc <<< $((DIMRAW - 22))*.3`" 26 | # drop the floating point and add 7 to meet the screen minimum 27 | DIMLEVEL="$(($(printf %.0f $DIMCALC)+7))" 28 | else 29 | # full brightness if lights are off 30 | DIMLEVEL=199 31 | fi 32 | # only change the screen brightness if there is a change 33 | if [ "$DIMLEVEL" != "$LASTDIM" ] 34 | then 35 | echo "Dimming to $DIMLEVEL / 200" 36 | # update the screen brightness 37 | echo $DIMLEVEL > /sys/class/backlight/rpi_backlight/brightness 38 | LASTDIM=$DIMLEVEL 39 | fi 40 | LAST=$NOW 41 | } 42 | done 43 | 44 | -------------------------------------------------------------------------------- /cabintemp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | error() { 4 | echo "N/A" 5 | # Exit with a failure result code 6 | exit 1 7 | } 8 | 9 | # Just in case the CAN bus is asleep, wake it up with a fake button press. 10 | 11 | cansend can0 2D3#0700000000000000 ; sleep 1 12 | 13 | # 1/10th of a second from now, send the UDS query for Cabin Temperature 14 | # NOTE: It currently responds with cabin temperature in Fahrenheit. It is 15 | # not known if there is a switch which might also make it output 16 | # instead of Celsius. 17 | 18 | ( sleep 0.1 ; cansend can0 783#0322D01E00000000 ) & 19 | 20 | # Collect any messages on CAN-IHS which have an ID of 0503. 21 | # After 0.5 seconds, stop collecting. 22 | 23 | COMMAND="timeout -s 1 0.6 /usr/bin/candump -L can0,0503:0FFF" 24 | 25 | # Only look for messages which are a successful response to our 26 | # UDS query. If we received more than one response, only use 27 | # the last one. And isolate the output so it only contains the 28 | # byte that we're looking for. 29 | 30 | RESPONSE=$( $COMMAND | tail -1 | grep \#100962D01E | cut -d# -f2 | cut -c15-16) 31 | 32 | # If no response was found, exit gracefully with an error. 33 | 34 | if [ "$RESPONSE" == "" ] ; then error; fi 35 | 36 | # Convert out byte from hexadecimal to decimal. 37 | 38 | RESPONSE=$( printf "%d" 0x$RESPONSE ) 39 | 40 | # Subtract 54 (an arbitrary magic number) from the response so that we have 41 | # an accurate reading which can range from -54 to 201 degrees Fahrenheit. 42 | 43 | RESPONSE=$( expr $RESPONSE - 54 ) 44 | 45 | # Print the temperature to standard output. 46 | 47 | echo ${RESPONSE}F 48 | -------------------------------------------------------------------------------- /evic.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | #!/bin/bash 4 | 5 | # This script turns text into an EVIC dash message 6 | # STRING TIPS: https://linuxhandbook.com/bash-string-length/ 7 | # ARRAY TIPS: https://www.shell-tips.com/bash/arrays/ 8 | 9 | [ "$2" == "" ] && { 10 | echo "USAGE: $(basename $0) [line] [text]" 11 | echo " Displays [text] on the EVIC music information page" 12 | echo " Valid line numbers are: 2 (artist), 3 (title) 1 (input name)" 13 | echo "" 14 | exit 1 15 | } 16 | 17 | FUNCTION=$1 18 | shift 19 | declare -a message 20 | string="$@" 21 | length=${#string} 22 | remainder=$(( ( 3 - ( $length % 3 ) ) % 3 )) 23 | POSITION=1 24 | for i in $( seq 1 $length) 25 | do 26 | message[$POSITION]='00' 27 | POSITION=$(( $POSITION + 1 )) 28 | message[$POSITION]=$(printf "%02X" "'${string:$(( i - 1 )):1}'") 29 | POSITION=$(( $POSITION + 1 )) 30 | done 31 | for i in $( seq 1 $remainder ) 32 | do 33 | message[$POSITION]=00 34 | POSITION=$(( $POSITION + 1 )) 35 | message[$POSITION]=00 36 | POSITION=$(( $POSITION + 1 )) 37 | length=$(( $length + 1 )) 38 | done 39 | lines=$(( $length / 3 )) 40 | [ "$DEBUG" == "true" ] && echo "FUNCTION: $function" 41 | [ "$DEBUG" == "true" ] && echo "STRING : $string" 42 | [ "$DEBUG" == "true" ] && echo "LENGTH : $length characters ($lines lines)" 43 | [ "$DEBUG" == "true" ] && echo "ENCODED : ${message[*]}" 44 | 45 | POSITION=1;INIT=4 46 | for i in $( seq $(( $lines -1 )) -1 0 ) 47 | do 48 | LINE=$(printf "%x" $i) 49 | COMMAND="cansend can0 328#${LINE}0.${INIT}${FUNCTION}" 50 | for j in 1 2 3 4 5 6 51 | do 52 | COMMAND="$COMMAND.${message[$POSITION]}" 53 | POSITION=$(( $POSITION + 1 )) 54 | done 55 | $COMMAND ; sleep 0.002 56 | INIT=0 57 | done 58 | cansend can0 328#00.00.00.00.00.00.00.00 59 | sleep 0.005 60 | 61 | 62 | -------------------------------------------------------------------------------- /gettime.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | #!/bin/bash 4 | 5 | # This script reads the current date and time from the CAN-IHS bus 6 | # and returns the output. 7 | 8 | # If we run into an error, this fuction displays a generic error message and exits. 9 | error() { 10 | echo "ERROR: Is the vehicle off? Current time and date not found." 11 | echo "DEBUG: A valid ID 350 message was not seen on CAN-IHS within 3 seconds." 12 | echo " " 13 | # Exit with a failure result code 14 | exit 1 15 | } 16 | 17 | initialize () { 18 | # The method we use to read any CAN-IHS message with an ID of 350 is stored in $COMMAND. 19 | # The "timeout" command limits how long we're willing to wait to only one second. 20 | COMMAND="timeout -s 9 1.1 /usr/bin/candump -L can0,0350:0fff" 21 | 22 | # We begin by grabbing out message. 23 | # If we got lucky and received two, we only keep the last one. 24 | can350=$( $COMMAND | tail -1 ) 25 | } 26 | 27 | initialize 28 | # Retry if no message received. 29 | if [ "$can350" == "" ] ; then 30 | # Wake the CAN bus and try again. 31 | cansend can0 2D3#0700000000000000 ; sleep 1.1 32 | initialize 33 | # Display an error and exit if no messages were received. 34 | [ "$can350" == "" ] && error 35 | fi 36 | 37 | # Display an error and exit if the clock has not been initialized. 38 | if [ "$( echo "$can350" | cut -c30-43 )" == "FFFFFFFFFFFFFF" ] ; then error; fi 39 | 40 | # Extract the hexadecimal numbers from the message. 41 | rawtime="$( echo "$can350" | cut -d# -f2 )" 42 | 43 | # Reformat the numbers as hexadecimal bytes with spaces in between them. 44 | rawtime="$( echo "$rawtime" | fold -w2 | paste -sd' ' )" 45 | 46 | # Change year so that it is represented as one word instead of two bytes. 47 | rawtime="$( echo "$rawtime" | cut -c1-11,13-20 )" 48 | 49 | # Read the six hexadecimal fields as: seconds minutes hours year month day 50 | # Convert to decimal, Reformat as: day/month/year hours:minutes:seconds 51 | time="$( echo $rawtime | { read seconds minutes hours year month day ; \ 52 | printf "%d/%d/%d %02d:%02d:%02d\n" \ 53 | 0x$month 0x$day 0x$year 0x$hours 0x$minutes 0x$seconds ; } )" 54 | 55 | # Print the result to the screen 56 | echo "$time" 57 | 58 | # Exit with a successful result code 59 | exit 0 60 | 61 | -------------------------------------------------------------------------------- /showrpm.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | #!/bin/bash 4 | 5 | # The message ID that we're listening for. 6 | id=can0,0322:0FFF 7 | 8 | # The starting position inside the message (1=the first hexadecimal digit) 9 | pos=1 10 | 11 | # How many hexadecimal digits we want to work with 12 | # (1=nibble, 2=byte, 4=word, etc) 13 | size=4 14 | 15 | # How often we update the name of the page. It counts the number of times 16 | # a new message (of the type selected above) has to appear on the CAN bus 17 | # before we refresh the page title. (20=we update the title every 18 | # time 20 new messages are received) 19 | # MAKING THIS VALUE TOO LOW MAY INTRODUCE REFRESH LAG. 20 | trate=20 21 | 22 | # This is the name that we print on the bottom of the music info page. 23 | tname="RPM" 24 | 25 | # This controls how often we refresh the value that we're monitoring. 26 | # It determines how many messages must appear appear on the CAN bus 27 | # before we display the latest value. (1=always display the latest value, 28 | # 2=display every other new value, 10=display every 10th new value) 29 | # BE CAREFUL OF MESSAGES WITH QUICK UPDATES (like 1/100th of a second) 30 | # YOU WILL CREATE LAG IF YOU REFRESH THOSE WITH A REFRESH RATE OF 1. 31 | rate=1 32 | 33 | # MAIN SCRIPT BEGINS HERE ------------------------------------------- 34 | 35 | # Adjusts our human-readable number into an actual offset. Do not modify. 36 | pos=$(($pos+3)) 37 | 38 | # Read only our selected message from the CAN bus. 39 | # Continually repeat this loop unless we haven't received any messages in 40 | # a 10 second period (10000ms). 41 | candump -T 10000 -L $id | \ 42 | ( while read a b c 43 | do 44 | # Increment the message counter. 45 | n=$((n+1)) 46 | 47 | # If the time is right, refresh the title. 48 | [ $(( $n % $trate )) -eq 0 ] && ./evic.sh 1 "$tname" 49 | 50 | # Extract the value we selected. 51 | value=${c:$pos:$size} 52 | 53 | # Create the text we're going to display. 54 | # We convert our hexadecimal value into a decimal number. 55 | text="$(printf "%d" "0x${value}")" 56 | 57 | # If the text is 65535 ($FFFF), print the word OFF instead. 58 | [ "$text" == "65535" ] && text=OFF 59 | 60 | # If the time is right, refresh with the latest text. 61 | [ $(( $n % $rate )) -eq 0 ] && ./evic.sh 3 "$text" 62 | done 63 | ) 64 | 65 | 66 | -------------------------------------------------------------------------------- /autoheat.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | #!/bin/bash 4 | 5 | STARTED=$( date +%s ) 6 | 7 | # Sets HVAC preferences when vehicle has been remotely started. 8 | echo "$(date) AUTOHEAT: Initializing." 9 | 10 | procs="$( ps -eaf | grep autoheat | grep -v grep | grep -v " $$ " | wc -l )" 11 | if [ $procs -gt 0 ] 12 | then 13 | echo "$(date) AUTOHEAT: ERROR - more than one process running! Exiting." 14 | exit 1 15 | fi 16 | 17 | # If the blower motor is off, turn on the HVAC system. 18 | FANSPEED=$( /home/pi/bin/fanspeed ) 19 | [ "$FANSPEED" == "0" ] && { 20 | echo "$(date) AUTOHEAT: turning on HVAC system" 21 | cansend can0 2D3#0700000000010000 22 | sleep 4.1 23 | } 24 | 25 | # Store settings 26 | VENTMODE=$(/home/pi/bin/ventmode) 27 | FANSPEED=$(/home/pi/bin/fanspeed) 28 | echo "$(date) AUTOHEAT: Current VENT: $VENTMODE and SPEED: $FANSPEED" 29 | 30 | # Mute the stereo 31 | echo "$(date) AUTOHEAT: Muting the radio" 32 | /home/pi/bin/mute ON ; sleep 0.31 33 | 34 | # Turn the fan all the way up 35 | echo "$(date) AUTOHEAT: setting fan to MAX" 36 | /home/pi/bin/fanspeed 7 37 | 38 | # Turn on the HVAC front defroster 39 | # This also turns OFF the air conditioner and turns OFF air recirculation. 40 | echo "$(date) AUTOHEAT: clearing modes via the front defroster" 41 | /home/pi/bin/ventmode 00 ; sleep 1.12 42 | 43 | # Select a vent combination of windshield and floor 44 | echo "$(date) AUTOHEAT: directing air to windshield and floor" 45 | ventmode 02 ; sleep 1.12 46 | 47 | # Turn on air recirculation. 48 | # This automatically causes the A/C to engage, so we'll need to undo that. 49 | echo "$(date) AUTOHEAT: recirculating air" 50 | cansend can0 2D3#0700000000000200 51 | sleep 1.1 52 | 53 | # Start setting the driver side to maximum heat setting 54 | # Just 8 steps at first. 55 | for i in `seq 1 8` 56 | do 57 | cansend can0 2D3#0700000000040000 58 | sleep 0.36 59 | done 60 | sleep 0.6 61 | 62 | echo "$(date) AUTOHEAT: turning OFF the AC system" 63 | # Turn off the A/C cooling (was turned on with recirculate mode) 64 | cansend can0 2D3#0700000000000100 65 | sleep 0.6 66 | 67 | echo "$(date) AUTOHEAT: setting temperature to MAX" 68 | # Finish setting the driver side to maximum heat setting 69 | # Now 18 more steps. 70 | for i in `seq 1 18` 71 | do 72 | cansend can0 2D3#0700000000040000 73 | sleep 0.36 74 | done 75 | sleep 0.6 76 | 77 | # Alter the passenger temperature up and down to break any temperature sync 78 | echo "$(date) AUTOHEAT: Syncing driver/passenger settings" 79 | cansend can0 2D3#0700000000100000 80 | sleep 0.36 81 | cansend can0 2D3#0700000000200000 82 | sleep 1.93 83 | 84 | # Engage the Driver / Passenger temperature Sync 85 | cansend can0 342#0000000400 86 | sleep 1.61 87 | 88 | echo "$(date) AUTOHEAT: exiting normally." 89 | 90 | 91 | -------------------------------------------------------------------------------- /autocool.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | #!/bin/bash 4 | 5 | STARTED=$( date +%s ) 6 | 7 | # Sets HVAC preferences when vehicle has been remotely started. 8 | echo "$(date) AUTOCOOL: Initializing." 9 | 10 | procs="$( ps -eaf | grep autoheat | grep -v grep | grep -v " $$ " | wc -l )" 11 | if [ $procs -gt 0 ] 12 | then 13 | echo "$(date) AUTOCOOL: ERROR - more than one process running! Exiting." 14 | exit 1 15 | fi 16 | 17 | # ALL PREFLIGHT CHECKS HAVE ALREADY BEEN DONE. 18 | # WE CAN BEGIN OUR WORK IMMEDIATELY. 19 | 20 | # If the blower motor is off, turn on the HVAC system. 21 | FANSPEED=$( /home/pi/bin/fanspeed ) 22 | [ "$FANSPEED" == "0" ] && { 23 | echo "$(date) AUTOCOOL: turning on HVAC system" 24 | cansend can0 2D3#0700000000010000 25 | sleep 4.1 26 | } 27 | 28 | # Store settings 29 | VENTMODE=$(/home/pi/bin/ventmode) 30 | FANSPEED=$(/home/pi/bin/fanspeed) 31 | echo "$(date) AUTOCOOL: Active VENT $VENTMODE and SPEED $FANSPEED" 32 | 33 | # Mute the stereo 34 | echo "$(date) AUTOCOOL: Muting the radio" 35 | /home/pi/bin/mute ON ; sleep 0.31 36 | 37 | # Set the fan speed to maximum 38 | echo "$(date) AUTOCOOL: setting fan to MAX" 39 | /home/pi/bin/fanspeed 7 40 | 41 | # Set the front defroster (which deactivates recirculation mode and AC mode) 42 | echo "$(date) AUTOCOOL: clearing modes with front defroster" 43 | /home/pi/bin/ventmode 00 44 | sleep 1.12 45 | 46 | # Now, select the exact blower we want (the main vents) 47 | echo "$(date) AUTOCOOL: directing air to panel vents" 48 | ventmode 08 49 | 50 | # Start setting the driver side to maximum cool setting 51 | # Just 8 steps at first. 52 | for i in `seq 1 8` 53 | do 54 | cansend can0 2D3#0700000000080000 55 | sleep 0.36 56 | done 57 | sleep 0.6 58 | 59 | # Turn on the A/C cooling (turns on with recirculate mode) 60 | echo "$(date) AUTOCOOL: Turning on AC system" 61 | cansend can0 2D3#0700000000000100 62 | sleep 0.6 63 | 64 | echo "$(date) AUTOCOOL: setting temperature to LOW" 65 | # Finish setting the driver side to maximum cool setting 66 | # Now 18 more steps. 67 | for i in `seq 1 18` 68 | do 69 | cansend can0 2D3#0700000000080000 70 | sleep 0.36 71 | done 72 | sleep 0.6 73 | 74 | # Turn OFF air recirculation (which came on with the AC) 75 | echo "$(date) AUTOCOOL: Turning OFF air recirculation" 76 | cansend can0 2D3#0700000000000200 77 | 78 | echo "$(date) AUTOCOOL: Syncing driver/passenger settings" 79 | # Alter the passenger temperature down and up to break any 80 | # existing temperature sync 81 | cansend can0 2D3#0700000000200000 82 | sleep 0.36 83 | cansend can0 2D3#0700000000100000 84 | sleep 0.63 85 | 86 | sleep 1.3 87 | 88 | ## Sync the driver/passenger HVAC controls 89 | cansend can0 342#0000000400 90 | sleep 0.61 91 | 92 | echo "$(date) AUTOCOOL: exiting normally." 93 | 94 | 95 | -------------------------------------------------------------------------------- /obd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # NOTE: This script sends an OBD-II command and returns 4 | # with the response string. 5 | 6 | error() { 7 | # Exit with a message and a failure result code 8 | echo "NO RESPONSE" 9 | exit 1 10 | } 11 | 12 | initialize () { 13 | # 1/10th of a second from now, send the OBD-II query with the 14 | # user-provided PID. 15 | 16 | ( sleep 0.1 ; cansend can1 7E0#0201${PID}0000000000 ) & 17 | 18 | # Collect any messages on CAN-C which have an ID of $7E8. 19 | # After a predefined number of seconds, stop collecting. 20 | 21 | COMMAND="timeout -s 1 $WAIT /usr/bin/candump -L can1,7E8:0FFF" 22 | 23 | # Only look for messages which are a successful response to our 24 | # particular OBD-II query. 25 | # UDS query. If we received more than one response, only use 26 | # the last one. And isolate the output so it only contains the 27 | # byte that we're looking for. 28 | 29 | RESPONSE=$( $COMMAND | egrep "\#..41${PID}|\#10..41${PID}" | cut -d# -f2 | head -1) 30 | DATA=${RESPONSE:6} 31 | # As long as we receive data, continue evaluating the output. 32 | [ "$DATA" != "" ] && { 33 | LENGTH=${RESPONSE:0:2} 34 | # If the length is 10 (HEX) then we have a response longer than 35 | # we can process, and in a different format. We'll have to adjust 36 | # things slightly so that we can read the first four characters. 37 | [ $LENGTH == "10" ] && { 38 | LENGTH=${RESPONSE:2:2} 39 | DATA=${DATA:2} 40 | } 41 | # Extract the message length and message data. 42 | LENGTH=$( printf "%d" 0x$LENGTH ) 2>/dev/null 43 | LENGTH=$(( $LENGTH - 2 )) >> /dev/null 2>/dev/null 44 | DATA=${DATA:0:$(( LENGTH * 2 ))} 2>/dev/null 45 | } 46 | } 47 | 48 | # MAIN CODE BEGINS HERE --------------------------------------------------- 49 | 50 | # Take the user-provded OBD-II PID and convert it to uppercase. 51 | # If they did not provide a pid, print a help message and exit. 52 | 53 | PID=$( echo $1 | tr '[:lower:]' '[:upper:]' ) 54 | if [ "$PID" == "" ] ; then 55 | echo "USAGE: `basename $0` [XX]" 56 | echo "" 57 | exit 1 58 | fi 59 | 60 | # If they provided a single hex digit, then prepend it with a 0. 61 | if [ $(echo ${#PID}) -eq 1 ] ; then 62 | PID="0$PID" 63 | fi 64 | 65 | # Test to see if it is a hex number. If not, print an error and exit. 66 | printf "%d" 0x$PID > /dev/null 2>&1 67 | if [ $? -ne 0 ] ; then 68 | echo "ERROR: $PID is not a valid hexadecimal value" 69 | echo "" 70 | exit 2 71 | fi 72 | 73 | # Send the OBD-II query to the BCM. 74 | # NOTE: This REQUIIRES the vehicle to be running. 75 | WAIT=0.2 76 | initialize 77 | 78 | # If there was no response, we could have missed the message. Or it 79 | # could have taken too long to reply. Try again, this time waiting 80 | # up to 0.5 seconds for a reply. 81 | if [ "$LENGTH" == "" ] ; then 82 | WAIT=0.5 83 | initialize 84 | if [ "$LENGTH" == "" ] ; then error; fi 85 | fi 86 | 87 | # Print the response as individual decimal numbers. 88 | # They will need to be reassembled via the published OBD-II formulas. 89 | for i in $(echo $DATA | fold -w2 | paste -sd' ') 90 | do 91 | printf "%d " 0x$i 92 | done 93 | echo "" 94 | -------------------------------------------------------------------------------- /mute.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # The script either detects or modifies the radio's mute setting. 4 | 5 | usage() { 6 | echo "" 7 | echo "USAGE: $(basename $0) [on/off]" 8 | echo "" 9 | exit 1 10 | } 11 | 12 | TRIES=0 13 | MAXTRIES=3 14 | DEBUG=false 15 | DELAY=1.1 16 | MUTEID=99 17 | MUTEWANTED=$(echo $1 | tr '[:lower:]' '[:upper:]') 18 | [ "$MUTEWANTED" == "" ] && MUTEWANTED=XX 19 | [ "$MUTEWANTED" == "ON" ] && MUTEWANTED=03 20 | [ "$MUTEWANTED" == "OFF" ] && MUTEWANTED=00 21 | [ "$MUTEWANTED" != "00" ] && [ "$MUTEWANTED" != "03" ] && [ "$MUTEWANTED" != "XX" ] && usage 22 | 23 | read_error() { 24 | echo "FAILURE: Could not read the mute status." 25 | echo "" 26 | # Exit with a failure result code 27 | exit 1 28 | } 29 | 30 | write_error() { 31 | echo "FAILURE: Could not change the mute status." 32 | echo "" 33 | # Exit with a failure result code 34 | exit 1 35 | } 36 | 37 | request_mute () { 38 | 39 | # Collect fourth byte of message ID $25D on CAN-C. 40 | # After $DELAY seconds, stop collecting. 41 | 42 | COMMAND="timeout -s 1 $DELAY /usr/bin/candump -L can1,025D:0FFF" 43 | 44 | # Only collect messages which are a direct response to our own query. 45 | # All other messages will be ignored. 46 | 47 | RESPONSE=$( $COMMAND | cut -d# -f2 | cut -c7-8 | tail -1) 48 | } 49 | 50 | # MAIN PROGRAM LOOP BEGINS HERE ------------------------------------------ 51 | # Read the mute status. If it isn't what we want, 52 | # hit the mute button! 53 | 54 | while [ $MUTEID != $MUTEWANTED ] 55 | do 56 | 57 | # See what our mute status is (no actual request, just listening) 58 | request_mute 59 | 60 | # If no response was found, try again. 61 | if [ "$RESPONSE" == "" ] ; then 62 | DELAY=1.6 63 | /home/pi/bin/wake 64 | request_mute 65 | fi 66 | 67 | # If no response was found, try again. 68 | if [ "$RESPONSE" == "" ] ; then 69 | DELAY=2.2 70 | request_mute 71 | # If no response AGAIN, then exit with an error. 72 | if [ "$RESPONSE" == "" ] ; then read_error; fi 73 | fi 74 | 75 | MUTEID=$RESPONSE 76 | 77 | [ "$MUTEWANTED" == "XX" ] && { 78 | [ "$MUTEID" == "03" ] && { echo ON; exit 0; } 79 | [ "$MUTEID" == "00" ] && { echo OFF; exit 0; } 80 | echo UNKNOWN ; exit 1 81 | } 82 | 83 | 84 | # If the mute status isn't what we want, hit the mute toggle button. 85 | 86 | [ "$DEBUG" == "true" ] && echo MUTEID: $MUTEID MUTEWANTED: $MUTEWANTED 87 | [ "$MUTEID" != "$MUTEWANTED" ] && { 88 | # It isn't what we want. We're hitting the MUTE toggle button. 89 | [ "$DEBUG" == "true" ] && echo TOGGLING MUTE 90 | cansend can0 2D3#0700010000000000 ; sleep 0.21 91 | } 92 | 93 | # We're going to try up to FIVE times to change the mute status. 94 | # After that, exit with an error. 95 | 96 | TRIES=$(( $TRIES + 1 )) 97 | [ "$TRIES" -gt $MAXTRIES ] && write_error 98 | 99 | done 100 | 101 | # A clean exit. Either we reported a value, made a change, or was requested 102 | # to make a change but no change was necessary. 103 | 104 | exit 0 105 | -------------------------------------------------------------------------------- /remote.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | touch /tmp/remote 4 | 5 | # Do some quick WiFi Tuning (might produce errors, that's okay) 6 | iwconfig wlan0 rate auto 7 | iwconfig wlan0 rts 2304 8 | iwconfig wlan0 frag 2304 9 | 10 | # Monitors for *remote* door lock/unlock commands and uses 11 | # those to disable/enable the Raspberry Pi's WiFi transmitter. 12 | # 13 | # Locked = WiFi Off (secure) 14 | # Unlocked = WiFi On (less secure) 15 | 16 | # Tunable variables: 17 | 18 | # Name for the CAN-IHS interface (can0, can1, vcan0, etc) 19 | CANIHS=can0 20 | WIFIDEV=wlan0 21 | DEBUG=false # Values: true,false,raw 22 | LOCKED=0 23 | 24 | LASTREMOTE="1C0#FFFFFFFFFFFF" 25 | sleep 5 26 | 27 | candump -L $CANIHS,01C0:0FFFF | while read time bus REMOTE 28 | do 29 | 30 | [ $DEBUG == "raw" ] && echo CAN-IHS data: $REMOTE 31 | HEADER="TIME : $time $( date )\nDATA : $REMOTE" 32 | 33 | # LOCK COMMAND: WIFI TURNED OFF (SECURED) 34 | if [ "$REMOTE" == "1C0#210000900000" ] ; then 35 | if [ "$REMOTE" != "$LASTREMOTE" ] ; then 36 | [ $DEBUG == "true" ] && echo -e "$HEADER" 37 | [ $DEBUG == "true" ] && echo "EVENT : KEYFOB LOCK COMMAND RECEIVED" 38 | [ $DEBUG == "true" ] && echo "ACTION: NONE" 39 | [ $DEBUG == "true" ] && echo "" 40 | fi 41 | fi 42 | 43 | # 1ST UNLOCK COMMAND: NO ACTION TAKEN 44 | if [ $REMOTE == "1C0#230000900000" ] ; then 45 | if [ "$REMOTE" != "$LASTREMOTE" ] ; then 46 | [ $DEBUG == "true" ] && echo -e "$HEADER" 47 | [ $DEBUG == "true" ] && echo "EVENT : KEYFOB 1ST UNLOCK COMMAND RECEIVED" 48 | [ $DEBUG == "true" ] && echo "ACTION: NONE" 49 | [ $DEBUG == "true" ] && echo "" 50 | fi 51 | fi 52 | 53 | # 2ND UNLOCK COMMAND: WIFI TURNED ON (LESS SECURED) 54 | if [ $REMOTE == "1C0#240000900000" ] ; then 55 | if [ "$REMOTE" != "$LASTREMOTE" ] ; then 56 | [ $DEBUG == "true" ] && echo -e "$HEADER" 57 | [ $DEBUG == "true" ] && echo "EVENT : KEYFOB 2ND UNLOCK COMMAND RECEIVED" 58 | 59 | # Flip between locked (LOCKED=1) and unlocked (LOCKED=0) 60 | LOCKED=$(( ! $LOCKED )) 61 | 62 | [ $LOCKED -eq 0 ] && { 63 | # ENABLING WIFI TRANSMITTER 64 | # All commands appear to be necessary even if they return errors. 65 | # Use extreme care when editing the following section. 66 | echo $(date) REMOTE: Turning on Wi-Fi Device 67 | iwconfig $WIFIDEV txpower on 68 | sleep 5 69 | iwconfig $WIFIDEV txpower auto 70 | sleep 2 71 | ifconfig $WIFIDEV up 72 | } 73 | 74 | [ $LOCKED -eq 1 ] && { 75 | # DISABLING WIFI TRANSMITTER 76 | echo $(date) REMOTE: Turning off Wi-Fi Device 77 | ifconfig $WIFIDEV down 78 | sleep 2 79 | iwconfig $WIFIDEV txpower off 80 | } 81 | 82 | [ $DEBUG == "true" ] && echo "" 83 | fi 84 | fi 85 | 86 | # IDLE (NO COMMANDS): NO ACTION 87 | if [ $REMOTE == "1C0#000000800000" ] ; then 88 | if [ "$REMOTE" != "$LASTREMOTE" ] ; then 89 | [ $DEBUG == "true" ] && echo -e "$HEADER" 90 | [ $DEBUG == "true" ] && echo "EVENT : IDLE STATE (DEFAULT) HAS RESUMED" 91 | [ $DEBUG == "true" ] && echo "ACTION: NONE" 92 | [ $DEBUG == "true" ] && echo "" 93 | fi 94 | fi 95 | 96 | # KEEP TRACK OF WHAT THE PREVIOUS COMMAND WAS. 97 | # THIS MAY BE USEFUL TO TRACK WHEN MONITORING OTHER CAN BUS IDs. 98 | LASTREMOTE=$REMOTE 99 | 100 | done 101 | -------------------------------------------------------------------------------- /autocollect.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # AUTOCOLLECT: This script monitors the state of the engine, launching 4 | # independent functions when vehicle is powered on, the engine is running, 5 | # every minute the engine is running, every 10 minutes the engine is 6 | # running, every hour the engine is running, and when the engine has been 7 | # shut down. 8 | 9 | # HERE ARE THE FUNCTIONS YOU MAY POPULATE AS NEEDED ------------------ 10 | 11 | # Called when the vehicle has been powered up (TIP button or remote start) 12 | 13 | vehiclepoweredon () { 14 | echo "$(date) AUTOCOLLECT: Vehicle power-on items go here." 15 | } 16 | 17 | # Called when the engine was just started and is stable 18 | 19 | enginestarted () { 20 | echo "$(date) AUTOCOLLECT: Post engine-start items go here." 21 | } 22 | 23 | # Called in one minute intervals after the engine is running. 24 | 25 | oneminute() { 26 | echo "$(date) AUTOCOLLECT: One minute items go here." 27 | } 28 | 29 | # Called in ten minute intervals after the engine is running. 30 | 31 | tenminute() { 32 | echo "$(date) AUTOCOLLECT: Ten minute items go here." 33 | } 34 | 35 | # Called in one hour intervals after the engine is running. 36 | 37 | onehour() { 38 | echo "$(date) AUTOCOLLECT: One hour items go here." 39 | } 40 | 41 | # Called when the engine is shutting down or has been shut down. 42 | 43 | engineshutdown () { 44 | echo "$(date) AUTOCOLLECT: Engine shutdown items go here." 45 | 46 | # Flush out any cached/pending IO activity (like to the SD card). 47 | sync ; sync ; sleep 5 ; sync ; sync 48 | 49 | } 50 | 51 | # THE MAIN SCRIPT BEGINS HERE --------------------------------------- 52 | 53 | echo "$(date) AUTOCOLLECT: Script started, waiting for events." 54 | 55 | # Set some flags that we'll use later. 56 | 57 | VEHICLEON=0 58 | STARTED=0 59 | LAST=77777 60 | 61 | # THE ENGINE MONITORING LOOP BEGINS HERE ---------------------------- 62 | 63 | COMMAND="/usr/bin/candump -T 1000 -L can1,0077:0FFF" 64 | $COMMAND | while read TIME BUS MESSAGE 65 | do 66 | 67 | # Regardless of what rate we receive messages at, we will analyze 68 | # them only once per second. (Conserves CPU.) 69 | 70 | NOW=$SECONDS 71 | [ "$NOW" != "$LAST" ] && { 72 | DATA=${MESSAGE:4} 73 | STATE=${DATA:0:4} 74 | 75 | # A work-around for hexadecimal digits during remote start. 76 | 77 | [ "$STATE" == "5D21" ] && STATE=5555 78 | 79 | # Determine when the engine is running. 80 | 81 | if [ "$STATE" == "0422" -o "$STATE" == "4421" ] 82 | then 83 | 84 | [ $STARTED -eq 0 ] && enginestarted & 85 | STARTED=1 86 | 87 | # Call the oneminute function once per minute. 88 | [ $(( $SECONDS % 60 )) -eq 45 ] && oneminute & 89 | 90 | # Call the tenminute function once per ten minutes. 91 | [ $(( $SECONDS % 600 )) -eq 599 ] && tenminute & 92 | 93 | # Call the onehour function once per hour. 94 | [ $(( $SECONDS % 36000 )) -eq 35970 ] && onehour & 95 | fi 96 | 97 | # Determine when the vehicle has been powered up. 98 | 99 | if [ "$STATE" -gt "0399" ] 100 | then 101 | # Vehicle is on. Engine may or may not be running. 102 | [ $VEHICLEON -eq 0 ] && vehiclepoweredon & 103 | VEHICLEON=1 104 | fi 105 | 106 | # Determine when the engine is no longer running. 107 | 108 | if [ "$STATE" -lt "0400" ] 109 | then 110 | 111 | # Engine has just shut down or is shutting down right now. 112 | 113 | [ $STARTED -eq 1 ] && { 114 | engineshutdown & 115 | break 2 116 | break 117 | exit 0 118 | } 119 | STARTED=0 120 | VEHICLEON=0 121 | fi 122 | } 123 | LAST=$NOW 124 | done 125 | 126 | # THIS LAST PART HAPPENS WHEN ALL BUS TRAFFIC HAS DIED DOWN --------- 127 | 128 | echo $(date) AUTOCOLLECT: No bus traffic, script ending. 129 | -------------------------------------------------------------------------------- /ventmode.sh: -------------------------------------------------------------------------------- 1 | 2 | 3 | #!/bin/bash 4 | 5 | # This script reads or modifies the currently selected HVAC mode. 6 | 7 | usage() { 8 | echo "" 9 | echo "USAGE: $(basename $0) [vent id]" 10 | echo " Possible IDs: 00=Defrost, 02=Defrost+Feet, 04=Feet," 11 | echo " 06=Panel+Floor, 08=Panel, 0F=Auto" 12 | echo " " 13 | exit 1 14 | } 15 | 16 | TRIES=0 17 | MAXTRIES=6 18 | DEBUG=false 19 | DELAY=0.6 20 | VENTID=99 21 | VENTWANTED=$(echo $1 | tr '[:lower:]' '[:upper:]') 22 | [ "$VENTWANTED" == "-H" ] && usage 23 | [ "$VENTWANTED" == "HELP" ] && usage 24 | [ ${#VENTWANTED} -ne 2 ] && VENTWANTED="XX" 25 | 26 | read_error() { 27 | echo "FAILURE: Could not read the vent mode from the HVAC Module." 28 | echo "" 29 | # Exit with a failure result code 30 | exit 1 31 | } 32 | 33 | write_error() { 34 | echo "FAILURE: Could not change the vent mode in the HVAC Module." 35 | echo "" 36 | # Exit with a failure result code 37 | exit 1 38 | } 39 | 40 | request_hvac_vent () { 41 | # In 0.1 second from now... 42 | # ...send a request to the HVAC Module asking for the vent mode status. 43 | 44 | ( sleep 0.1 ; cansend can0 783#0322029800000000 ) & 45 | 46 | # Collect all responses from the HVAC Module at ID $503 on CAN-IHS. 47 | # After $DELAY seconds, stop collecting. 48 | 49 | COMMAND="timeout -s 1 $DELAY /usr/bin/candump -L can0,0503:0FFF" 50 | 51 | # Only collect messages which are a direct response to our own query. 52 | # All other messages will be ignored. 53 | 54 | RESPONSE=$( $COMMAND | grep \#04620298 | cut -d# -f2 | cut -c9-10 | tail -1) 55 | } 56 | 57 | # MAIN PROGRAM LOOP BEGINS HERE ------------------------------------------ 58 | # Read the HVAC Vent Mode. If it isn't what we want, 59 | # hit the HVAC Mode button. Lather, rinse, repeat. 60 | 61 | while [ $VENTID != $VENTWANTED ] 62 | do 63 | 64 | # Ask the HVAC module for which vents have been selected. 65 | request_hvac_vent 66 | 67 | # If no response was found, try again. 68 | if [ "$RESPONSE" == "" ] ; then 69 | DELAY=1.2 70 | request_hvac_vent 71 | fi 72 | 73 | # If no response was found, try again. 74 | if [ "$RESPONSE" == "" ] ; then 75 | DELAY=1.8 76 | request_hvac_vent 77 | # If no response AGAIN, then exit with an error. 78 | if [ "$RESPONSE" == "" ] ; then read_error; fi 79 | fi 80 | 81 | VENTID=$RESPONSE 82 | 83 | [ "$VENTWANTED" == "XX" ] && { 84 | echo $VENTID 85 | exit 0 86 | } 87 | 88 | # If the current HVAC vent mode isn't the one we want, 89 | # then hit the appropriate HVAC control button. 90 | 91 | [ "$DEBUG" == "true" ] && echo VENTID: $VENTID VENTWANTED: $VENTWANTED 92 | [ "$VENTID" != "$VENTWANTED" ] && { 93 | # If they wanted auto vents, let's just hit the auto button and exit. 94 | [ "$VENTWANTED" == "0F" ] && { 95 | # Turns on the automatic FAN and VENT control 96 | cansend can0 2D3#0700000000020000; sleep $DELAY 97 | exit 0 98 | } 99 | # If they wanted the front defroster, then hit the front 100 | # defroster button and exit. 101 | [ "$VENTWANTED" == "00" ] && { 102 | # Turns on the HVAC front defroster 103 | cansend can0 2D3#0700000000800000; sleep $DELAY 104 | exit 0 105 | } 106 | # Otherwise, hit HVAC Mode button to cycle the setting 107 | cansend can0 2D3#0700000000000800 ; sleep $DELAY 108 | } 109 | 110 | # We're going to try up to FIVE times to change the HVAC mode 111 | # to our desired selection. After that, exit with an error. 112 | 113 | TRIES=$(( $TRIES + 1 )) 114 | [ "$TRIES" -gt $MAXTRIES ] && write_error 115 | 116 | done 117 | 118 | # A clean exit. Either we reported a value, made a change, or was requested 119 | # to make a change but no change was necessary. 120 | 121 | exit 0 122 | 123 | -------------------------------------------------------------------------------- /fanspeed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This script reads or sets the HVAC fan speed. 4 | 5 | usage() { 6 | echo "" 7 | echo "USAGE: $(basename $0) [speed]" 8 | echo " When setting values: 01 (low) through 07 (max)" 9 | echo " When reading values: 00 (off), 01 (low) through 07 (max)" 10 | echo " " 11 | exit 1 12 | } 13 | 14 | TRIES=0 15 | MAXTRIES=5 16 | DEBUG=false 17 | DELAY=1.1 18 | SPEEDID=99 19 | SPEEDWANTED="$1" 20 | 21 | SHOWUSAGE=1 22 | [ $SPEEDWANTED -gt 0 ] 2>/dev/null && [ $SPEEDWANTED -lt 8 ] 2>/dev/null && SHOWUSAGE=0 23 | [ "$SPEEDWANTED" == "" ] && { SPEEDWANTED="XX"; SHOWUSAGE=0; } 24 | 25 | [ $SHOWUSAGE -eq 1 ] && usage 26 | 27 | read_error() { 28 | echo "FAILURE: Could not read a valid fan speed message." 29 | echo "" 30 | # Exit with a failure result code 31 | exit 1 32 | } 33 | 34 | write_error() { 35 | echo "FAILURE: Could not change the fan speed to the desired value." 36 | echo "" 37 | # Exit with a failure result code 38 | exit 1 39 | } 40 | 41 | request_fan_speed () { 42 | 43 | # Collect fan speed from message ID $2B1 on CAN-C 44 | # This message repeats one time per second. 45 | # After $DELAY seconds, stop collecting. 46 | 47 | COMMAND="timeout -s 1 $DELAY /usr/bin/candump -L can1,02B1:0FFF" 48 | 49 | # Only collect messages which are a direct response to our own query. 50 | # All other messages will be ignored. 51 | 52 | RESPONSE=$( $COMMAND | cut -d# -f2 | cut -c1-2 | tail -1) 53 | } 54 | 55 | # MAIN PROGRAM LOOP BEGINS HERE ------------------------------------------ 56 | # Read the HVAC fan speed. If it isn't what we want, hit the "FAN +" or 57 | # the "FAN -" as many times as neeeded. Lather, rinse, repeat. Delays are 58 | # introduced into each of these loops in order to avoid hysteresis. 59 | 60 | while [ $SPEEDID != $SPEEDWANTED ] 61 | do 62 | 63 | # Read the HVAC fan speed from the CAN bus 64 | request_fan_speed 65 | 66 | # If no response was found, send a wakeup packet. Try again. 67 | if [ "$RESPONSE" == "" ] ; then 68 | DELAY=1.1 69 | /home/pi/bin/wake ; sleep 0.5 70 | request_fan_speed 71 | fi 72 | 73 | # If no response was found, send a wakeup packet. Try again. 74 | if [ "$RESPONSE" == "" ] ; then 75 | DELAY=1.8 76 | /home/pi/bin/wake 77 | request_fan_speed 78 | 79 | # If no response AGAIN, then exit with an error. 80 | if [ "$RESPONSE" == "" ] ; then read_error; fi 81 | fi 82 | 83 | SPEEDID=$( printf "%d" 0x$RESPONSE ) 84 | # A speed of "127" means offline, so we'll treat it as fan speed 0. 85 | [ $SPEEDID -eq 127 ] && SPEEDID=0 86 | [ $SPEEDID -gt 7 ] && read_error 87 | 88 | # If the user supplied no arguments, print the fan speed and exit. 89 | [ "$SPEEDWANTED" == "XX" ] && { 90 | echo $SPEEDID 91 | exit 0 92 | } 93 | 94 | # If the current HVAC fan speed isn't what we want, then hit the 95 | # appropriate fan speed buttons to increase or decrease it. 96 | 97 | [ "$DEBUG" == "true" ] && echo SPEED: $SPEEDID DESIRED: $SPEEDWANTED 98 | 99 | [ "$SPEEDID" -lt "$SPEEDWANTED" ] && { 100 | for i in $( seq $SPEEDID $(( $SPEEDWANTED -1 )) ) 101 | do 102 | # Increase the fan speed 103 | # Use a larger delay to compensate for fan spin-up time 104 | [ "$DEBUG" == "true" ] && echo REQUESTING FAN SPEED INCREASE 105 | cansend can0 273#0000050000000000; sleep 0.73 106 | done 107 | # exit 0 108 | } 109 | [ "$SPEEDID" -gt "$SPEEDWANTED" ] && { 110 | for i in $( seq $SPEEDWANTED $(( $SPEEDID -1 )) ) 111 | do 112 | # Decrease the fan speed 113 | # Use a short delay because fans wind-down quickly. 114 | [ "$DEBUG" == "true" ] && echo REQUESTING FAN SPEED DECREASE 115 | cansend can0 273#00000A0000000000; sleep 0.23 116 | done 117 | # exit 0 118 | } 119 | 120 | [ "$SPEEDID" -eq "$SPEEDWANTED" ] && exit 0 121 | 122 | # We're going to try up to FIVE times to change the HVAC fan speed. 123 | # After that, exit with an error. 124 | 125 | TRIES=$(( $TRIES + 1 )) 126 | [ "$TRIES" -gt $MAXTRIES ] && write_error 127 | 128 | done 129 | 130 | # A clean exit. Either we reported a value, made a change, or was requested 131 | # to make a change but no change was necessary. 132 | 133 | exit 0 134 | -------------------------------------------------------------------------------- /rid.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | error() { 4 | echo "N/A" 5 | # Exit with a failure result code 6 | exit 1 7 | } 8 | 9 | initialize () { 10 | # Issue a "Get Data By Identifier" command to the specified module 11 | # and wait (with timeout) for a response. 12 | 13 | ( sleep 0.04 ; \ 14 | echo "22 $ID" | /usr/bin/isotpsend -s $SOURCE -d $DEST -p 00:00 -P l $BUS ) & 15 | 16 | # Collect the response. If no response in 8 seconds, abort. 17 | # NOTE: Most of the time, 8 seconds is excessive. But some Identifiers 18 | # will actually take this much time, if not more. 19 | 20 | COMMAND="timeout -s 1 8 /usr/bin/isotprecv -s $SOURCE -d $DEST -p 00:00 -P l $BUS" 21 | 22 | RESPONSE="$( $COMMAND )" 23 | 24 | # Get rid of the last character (trailing space) 25 | RESPONSE=${RESPONSE} 26 | 27 | # We could get rid of ALL SPACING between hexadecimal numbers, 28 | # but let's not do that. Code left here in case it is useful elsewhere. 29 | # DATA=$( echo ${RESPONSE:9} | tr -d "[:blank:]" ) 30 | 31 | # POPULATE A FEW VARIABLES WE'LL USE BELOW 32 | 33 | DATA=$( echo ${RESPONSE:9} ) 34 | RID="${RESPONSE:0:2}" 35 | SID="22" 36 | SUCCESS="$( printf "%X" $(( 0x$SID + 0x40 )) )" 37 | OK=99 38 | 39 | if [ "$RID" == "$SUCCESS" ] ; then 40 | OK=0 41 | echo "SUCCESS" 42 | echo "${RESPONSE:9}" 43 | 44 | # We need a better solution, but for now, we're storing data as 45 | # FILENAMES under the /home/pi/modules/ directory. This works 46 | # up until the point we have a lot of data to store, then the 47 | # filename becomes too big. In that case, instead of storing the 48 | # data as a filename, it creates a file called FILE and stores 49 | # the data in there. Yuck, yuck, yuck! 50 | 51 | if [ ! -d /home/pi/modules/$MODULE/$OPERATION/${ID:0:2}${ID:3:2} ] ; then 52 | mkdir -p /home/pi/modules/$MODULE/$OPERATION/${ID:0:2}${ID:3:2} 53 | fi 54 | touch /home/pi/modules/$MODULE/$OPERATION/${ID:0:2}${ID:3:2}/"${DATA}" 55 | if [ $? -ne 0 ] ; then 56 | echo $DATA | cat > /home/pi/modules/$MODULE/$OPERATION/${ID:0:2}${ID:3:2}/FILE 57 | fi 58 | fi 59 | 60 | if [ "$RID" == "7F" ] ; then 61 | OK=1 62 | echo "NEGATIVE RESPONSE" 63 | echo "$RESPONSE" 64 | fi 65 | 66 | if [ "$OK" == "99" ] 67 | then 68 | echo "UNKNOWN RESPONSE" 69 | echo ${RESPONSE} 70 | fi 71 | 72 | exit $OK 73 | 74 | } 75 | 76 | # MAIN CODE BEGINS HERE 77 | 78 | MODULE=$( echo $1 | tr '[:upper:]' '[:lower:]' ) 79 | OPERATION=rid 80 | OK=0 81 | 82 | # In this section, we translate module names into the concrete variables 83 | # we need to use. We're doing these as if...then statements when we should 84 | # probably be working with arrays. But this is a product of figuring things 85 | # out when you go along and quickly putting a solution in place because you 86 | # need something here and now. This needs to be revisited. 87 | 88 | if [ "$MODULE" == "bcm" ] ; then 89 | OK=1 90 | SOURCE=620 91 | DEST=504 92 | BUS=can1 # CAN-C 93 | fi 94 | 95 | if [ "$MODULE" == "sccm" ] ; then 96 | OK=1 97 | # INVALID INFORMATION - NEEDS CORRECTED 98 | SOURCE=763 99 | DEST=4E3 100 | BUS=can1 # CAN-C 101 | fi 102 | 103 | if [ "$MODULE" == "radio" ] ; then 104 | OK=1 105 | SOURCE=7BF 106 | DEST=53F 107 | BUS=can0 # CAN-IHS 108 | fi 109 | 110 | [ "$MODULE" == "ipc" ] && MODULE="ipcm" 111 | if [ "$MODULE" == "ipcm" -o "$MODULE" == "evic" ] ; then 112 | OK=1 113 | MODULE="ipcm" # Standardizing on Instrument Panel Cluster Module 114 | SOURCE=742 115 | DEST=4C2 116 | BUS=can1 # CAN-C 117 | fi 118 | 119 | if [ "$MODULE" == "sccm" ] ; then 120 | OK=1 121 | SOURCE=123 122 | DEST=321 123 | BUS=can0 # CAN-IHS 124 | fi 125 | 126 | if [ "$MODULE" == "hvac" ] ; then 127 | OK=1 128 | SOURCE=783 129 | DEST=503 130 | BUS=can0 # CAN-IHS 131 | fi 132 | 133 | # echo "MODULE: $MODULE BUS: $BUS SOURCE: $SOURCE DEST: $DEST" 134 | 135 | if [ $OK -ne 1 ] ; then 136 | echo "INVALID MODULE SPECIFIED: $MODULE" 137 | exit 99 138 | fi 139 | 140 | shift 1 141 | 142 | INPUT=$( printf "%04X" 0x$@ 2>/dev/null ) 143 | if [ $? -ne 0 ] 144 | then 145 | echo "INVALID IDENTIFIER SPECIFIED: $@" 146 | exit 1 147 | fi 148 | 149 | ID="${INPUT:0:2} ${INPUT:2:2}" 150 | # echo "INPUT: $INPUT ID: $ID" 151 | 152 | # Send our UDS request and store the response. 153 | initialize 154 | -------------------------------------------------------------------------------- /pycan2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # jeep canbus live data display 3 | # v2 4 | 5 | # import libraries 6 | import can 7 | import curses 8 | 9 | canFilter = list() 10 | 11 | # can bus variables, change these for vcan or can 12 | canIHS = "can0" 13 | canC = "can1" 14 | 15 | # defined types to process the data. x = can message , a = byte 1 , b = byte 2 16 | def raw8(x,a): 17 | return(x[a]) 18 | 19 | def raw16(x,a,b): 20 | return((x[a]<<8) + x[b]) 21 | 22 | def volt(x,a): 23 | return(x[a] / 10) 24 | 25 | def temp(x,a): 26 | return(round((((x[a] - 40) * (9 / 5)) + 32))) 27 | 28 | def tilt(x,a,b): 29 | return(round(((x[a]<<8) + x[b] - 2048) / 10)) 30 | 31 | def rpm(x,a,b): 32 | if x[a] == 0xFF: 33 | return(0) 34 | return((x[a]<<8) + x[b]) 35 | 36 | def mph(x,a,b): 37 | return(round(((x[a]<<8) + x[b]) / 200,1)) 38 | 39 | def psi(x,a): 40 | return(round(((x[a] * 4) * 0.145038))) 41 | 42 | def gear(x,a): 43 | if x[a] == 0x4E: 44 | return('N') 45 | elif x[a] == 0x52: 46 | return('R') 47 | elif x[a] == 0x31: 48 | return('1') 49 | elif x[a] == 0x32: 50 | return('2') 51 | elif x[a] == 0x33: 52 | return('3') 53 | elif x[a] == 0x34: 54 | return('4') 55 | elif x[a] == 0x35: 56 | return('5') 57 | elif x[a] == 0x36: 58 | return('6') 59 | elif x[a] == 0x37: 60 | return('7') 61 | elif x[a] == 0x38: 62 | return('8') 63 | elif x[a] == 0x50: 64 | return('P') 65 | elif x[a] == 0x44: 66 | return('D') 67 | 68 | def xfer(x,a): 69 | if x[a] == 0x00: 70 | return('4x2H') 71 | elif x[a] == 0x02: 72 | return('N') 73 | elif x[a] == 0x10: 74 | return('4x4H') 75 | elif x[a] == 0x20: 76 | return('N') 77 | elif x[a] == 0x40: 78 | return('4x4L') 79 | elif x[a] == 0x80: 80 | return('Shifting') 81 | 82 | def steer(x,a,b): 83 | return(((x[a]<<8) + x[b]) - 0x1000) 84 | 85 | def wrapper(msg,name,func,output,colum,*args): 86 | return(func(msg,*args)) 87 | 88 | def pstemp(x,a): 89 | return(round(((x[a] * (9 / 5)) + 32))) 90 | 91 | # list of can ID's and details to monitor in this order: 92 | # (ID, Channel, [("name", process, type, colum, byte1, byte2)]) 93 | monitorlist=[(0x2C2, 94 | canIHS, 95 | [("Batt V",volt,str,0,2), 96 | ("Batt ?",raw8,hex,0,0)]), 97 | (0x02B, 98 | canC, 99 | [("Roll",tilt,str,0,0,1), 100 | ("Tilt",tilt,str,0,2,3), 101 | ("Yaw",tilt,str,0,4,5)]), 102 | (0x322, 103 | canIHS, 104 | [("RPM",rpm,str,0,0,1), 105 | ("MPH",mph,str,0,2,3)]), 106 | (0x127, 107 | canC, 108 | [("IAT",temp,str,0,0), 109 | ("Coolant",temp,str,0,1)]), 110 | (0x13D, 111 | canC, 112 | [("Oil Temp",temp,str,0,3), 113 | ("Oil Pres",psi,str,0,2)]), 114 | (0x093, 115 | canC, 116 | [("Gear",gear,str,0,2)]), 117 | (0x277, 118 | canC, 119 | [("Transfer",xfer,str,0,0)]), 120 | (0x023, 121 | canC, 122 | [("Steer Angl",steer,str,0,0,1), 123 | ("Rate",steer,str,0,2,3)]), 124 | (0x128, 125 | canC, 126 | [("PS Temp",pstemp,str,0,1), 127 | ("PS PSI",raw8,str,0,2), 128 | ("PS UNK 3",raw8,str,0,3)]), 129 | (0x13F, 130 | canC, 131 | [("0x13F 2",raw8,str,0,1), 132 | ("0x13F 2 3",raw16,str,0,2,3), 133 | ("0x13F 4 5",raw16,str,0,4,5)]) 134 | ] 135 | 136 | stdscr = curses.initscr() 137 | 138 | # setup 139 | for monitor in monitorlist: 140 | # build out the can bus filtering list. only receive messages that we care about. 141 | canFilter.append({"can_id": monitor[0], "can_mask": 0xFFF, "can_channel": monitor[1]}) 142 | for detail in monitor[2]: 143 | # place item titles, according to how they appear in the list 144 | stdscr.addstr(monitorlist.index(monitor),(monitor[2].index(detail) * 30),detail[0]) 145 | 146 | stdscr.refresh() 147 | 148 | # define the can bus 149 | bus = can.interface.Bus('', bustype='socketcan', filter=canFilter) 150 | 151 | # Process every single message received from the canbus 152 | try: 153 | for msg in bus: 154 | for monitor in monitorlist: 155 | if msg.arbitration_id == monitor[0] and msg.channel == monitor[1]: 156 | for detail in monitor[2]: 157 | stdscr.addstr(monitorlist.index(monitor),((monitor[2].index(detail) * 30) + 15), '%-5s' % detail[2](wrapper((msg.data),*detail))) 158 | stdscr.refresh() 159 | 160 | except: 161 | bus.shutdown() 162 | curses.nocbreak() 163 | stdscr.keypad(0) 164 | curses.echo() 165 | curses.endwin() 166 | raise 167 | 168 | -------------------------------------------------------------------------------- /tkcan2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # jeep canbus live data display 3 | # v2 4 | 5 | # import libraries 6 | import can 7 | import time 8 | from tkinter import * 9 | try: 10 | camavail = True 11 | import cv2 12 | cap = cv2.VideoCapture(0) 13 | if cap is None or not cap.isOpened(): 14 | camavail = False 15 | except ImportError: 16 | camavail = False 17 | 18 | canFilter = list() 19 | 20 | # can bus variables, change these for vcan or can 21 | canIHS = "vcan0" 22 | canC = "vcan1" 23 | 24 | gaugetotal = 8 25 | gaugerow = 2 26 | gauge = 0 27 | 28 | # defined types to process the data. x = can message , a = byte 1 , b = byte 2 29 | def raw8(x,a): 30 | return(x[a]) 31 | 32 | def raw16(x,a,b): 33 | return((x[a]<<8) + x[b]) 34 | 35 | def volt(x,a): 36 | return(x[a] / 10) 37 | 38 | def temp(x,a): 39 | return(round((((x[a] - 40) * (9 / 5)) + 32))) 40 | 41 | def tilt(x,a,b): 42 | return(round(((x[a]<<8) + x[b] - 2048) / 10)) 43 | 44 | def rpm(x,a,b): 45 | if x[a] == 0xFF: 46 | return(0) 47 | return((x[a]<<8) + x[b]) 48 | 49 | def mph(x,a,b): 50 | return(round(((x[a]<<8) + x[b]) / 200,1)) 51 | 52 | def psi(x,a): 53 | return(round(((x[a] * 4) * 0.145038))) 54 | 55 | def gear(x,a): 56 | if x[a] == 0x4E: 57 | return('N') 58 | elif x[a] == 0x52: 59 | return('R') 60 | elif x[a] == 0x31: 61 | return('1') 62 | elif x[a] == 0x32: 63 | return('2') 64 | elif x[a] == 0x33: 65 | return('3') 66 | elif x[a] == 0x34: 67 | return('4') 68 | elif x[a] == 0x35: 69 | return('5') 70 | elif x[a] == 0x36: 71 | return('6') 72 | elif x[a] == 0x37: 73 | return('7') 74 | elif x[a] == 0x38: 75 | return('8') 76 | elif x[a] == 0x50: 77 | return('P') 78 | elif x[a] == 0x44: 79 | return('D') 80 | 81 | def xfer(x,a): 82 | if x[a] == 0x00: 83 | return('4x2H') 84 | elif x[a] == 0x02: 85 | return('N') 86 | elif x[a] == 0x10: 87 | return('4x4H') 88 | elif x[a] == 0x20: 89 | return('N') 90 | elif x[a] == 0x40: 91 | return('4x4L') 92 | elif x[a] == 0x80: 93 | return('Shifting') 94 | 95 | def steer(x,a,b): 96 | return(((x[a]<<8) + x[b]) - 0x1000) 97 | 98 | def pstemp(x,a): 99 | return(round(((x[a] * (9 / 5)) + 32))) 100 | 101 | # End of types 102 | 103 | # Cleanup on exit 104 | def callback(): 105 | bus.shutdown() 106 | print(bus.state) 107 | print("exit") 108 | raise 109 | 110 | # setup gauges 111 | def gaugesetup() 112 | gcount = 0 113 | maxline = 4 114 | for monitor in monitorlist: 115 | for detail in monitor[2]: 116 | rpmfr = Frame(frame) 117 | rpmdsc = Label(rpmfr, text="RPM", font=("Helvetica", "16")) 118 | rpmdsc.pack(side=TOP) 119 | rpmlabel = Label(rpmfr, font=("Helvetica", "16")) 120 | rpmlabel.pack() 121 | rpmgauge = Canvas(rpmfr, width=200, height=100) 122 | rpmgauge.pack() 123 | coord = 0, 0, 100, 200 #define the size of the gauge 124 | rpmgauge.create_arc(coord, start=30, extent=120, fill="black", width=2) 125 | rpmneedle = rpmgauge.create_arc(coord, start= 119, extent=1, width=7) 126 | 127 | 128 | 129 | # Process messages as they are received 130 | def wrapper(msg,name,func,output,gmin,gmax,dtype,*args): 131 | return(func(msg,*args)) 132 | 133 | def newmsg(msg): 134 | for monitor in monitorlist: 135 | if msg.arbitration_id == monitor[0] and msg.channel == monitor[1]: 136 | for detail in monitor[2]: 137 | detail[2]["text"] = wrapper(msg.data,*detail) 138 | detail[2].pack 139 | 140 | 141 | # list of can ID's and details to monitor in this order: 142 | # (ID, Channel, [("name", process, entity, min, max, type, byte1, byte2)]) 143 | monitorlist=[(0x2C2, 144 | canIHS, 145 | [("Batt V",volt,gauge0,11,15,str,2)]), 146 | (0x02B, 147 | canC, 148 | [("Roll",tilt,rollg,-30,30,str,0,1), 149 | ("Tilt",tilt,tiltg,-30,30,str,2,3), 150 | ("Yaw",tilt,yawg,-30,30,str,4,5)]), 151 | (0x127, 152 | canC, 153 | [("IAT",temp,gauge1,30,200,str,0), 154 | ("Coolant",temp,gauge2,150,300,str,1)]), 155 | (0x13D, 156 | canC, 157 | [("Oil Temp",temp,gauge3,150,300,str,3), 158 | ("Oil Pres",psi,gauge4,0,80,str,2)]), 159 | (0x093, 160 | canC, 161 | [("Gear",gear,text0,,,str,2)]), 162 | (0x277, 163 | canC, 164 | [("Transfer",xfer,text1,,,str,0)]), 165 | (0x023, 166 | canC, 167 | [("Steer Angl",steer,text2,,,str,0,1)]), 168 | (0x128, 169 | canC, 170 | [("PS Temp",pstemp,gauge5,50,300,str,1)]) 171 | ] 172 | 173 | # setup 174 | while gauge < gaugetotal: 175 | 176 | 177 | 178 | # define the can bus 179 | bus = can.interface.Bus('', bustype='socketcan', filter=canFilter) 180 | Notifier = can.Notifier(bus, [newmsg], loop=None) 181 | 182 | # Process every single message received from the canbus 183 | try: 184 | 185 | 186 | except: 187 | bus.shutdown() 188 | curses.nocbreak() 189 | stdscr.keypad(0) 190 | curses.echo() 191 | curses.endwin() 192 | raise 193 | 194 | -------------------------------------------------------------------------------- /tkcan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # GUI Can data display using tkinter 3 | 4 | from tkinter import * 5 | import time 6 | import can 7 | 8 | # If using vcan for log playback, change the values in the quotes below 9 | can0 = "can0" 10 | can1 = "can1" 11 | 12 | battv = None 13 | rpm = None 14 | mph = None 15 | fsstate = True 16 | 17 | def full(): 18 | print("full screen") 19 | global fsstate 20 | fsstate = not fsstate 21 | root.attributes("-fullscreen", fsstate) 22 | if fsstate == True: 23 | fullbutton.config(relief=SUNKEN, text="Small") 24 | fullbutton.pack() 25 | else: 26 | fullbutton.config(relief=RAISED, text="Full") 27 | fullbutton.pack() 28 | 29 | def blchange(bllevels): 30 | print("backlight changed") 31 | print(str(bllevels)) 32 | 33 | def tick(): 34 | s = time.strftime('%I:%M:%S%p') 35 | if s != clock["text"]: 36 | clock["text"] = s 37 | clock.after(200, tick) 38 | 39 | def goaway(): 40 | bigbutton.config(text="Come Back", command=comeback) 41 | bigbutton.pack() 42 | frame.pack_forget() 43 | 44 | def comeback(): 45 | bigbutton.config(text="Go Away", command=goaway) 46 | bigbutton.pack() 47 | frame.pack(side=TOP) 48 | return "break" 49 | 50 | def newrpm(rpm): 51 | low_r = 0 # chart low range 52 | hi_r = 7000 # chart hi range 53 | if rpm == 65535: 54 | rpm = 0 55 | if str(rpm) != rpmlabel["text"]: 56 | rpmlabel["text"] = str(rpm) 57 | rpmlabel.pack() 58 | rpmangle = (120 * (hi_r - rpm) / (hi_r - low_r) + 30) 59 | rpmgauge.itemconfig(rpmneedle,start = rpmangle) 60 | rpmgauge.pack() 61 | 62 | def newmph(mph): 63 | if str(mph) != mphlabel["text"]: 64 | mphlabel["text"] = str(mph) 65 | mphlabel.pack() 66 | 67 | def newbattv(battv): 68 | if str(battv) != battvlabel["text"]: 69 | battvlabel["text"] = str(battv) 70 | battvlabel.pack() 71 | 72 | def newpstemp(pstemp): 73 | low_r = 50 # chart low range 74 | hi_r = 250 # chart hi range 75 | if str(pstemp) != pstemplabel["text"]: 76 | pstemplabel["text"] = str(pstemp) 77 | pstemplabel.pack() 78 | pstempangle = (120 * (hi_r - pstemp) / (hi_r - low_r) + 30) 79 | pstempgauge.itemconfig(pstempneedle,start = pstempangle) 80 | pstempgauge.pack() 81 | 82 | def newmsg(msg): 83 | if msg.arbitration_id == 0x2C2 and msg.channel == can0: 84 | newbattv(msg.data[2] / 10) 85 | if msg.arbitration_id == 0x322 and msg.channel == can0: 86 | newrpm((msg.data[0]<<8) + msg.data[1]) 87 | newmph(round(((msg.data[2]<<8) + msg.data[3]) / 200, 1)) 88 | if msg.arbitration_id == 0x128 and msg.channel == 'can1': 89 | newpstemp(round(((msg.data[1]) * (9 / 5)) + 32)) 90 | 91 | def canwakeup(): 92 | wakeup = can.Message(data=[0x07, 0, 0, 0, 0, 0, 0, 0], is_extended_id=False, arbitration_id=0x2D3, channel=can0) 93 | print(wakeup) 94 | bus.send(wakeup, timeout=1) 95 | 96 | def callback(): 97 | bus.shutdown() 98 | print(bus.state) 99 | print("exit") 100 | raise 101 | 102 | root = Tk() 103 | root.geometry("800x400+0+40") 104 | root.title("This is Root") 105 | root.protocol("WM_DELETE_WINDOW", callback) 106 | root.attributes("-fullscreen", fsstate) 107 | root.configure(bg='gray') 108 | 109 | topframe=Frame(root) 110 | topframe.pack(side=BOTTOM) 111 | 112 | quitbutton = Button( 113 | topframe, text="QUIT", fg="red", font=("Helvetica", "16"), height=2, width=8, command=topframe.quit) 114 | quitbutton.pack(side=LEFT) 115 | bigbutton = Button( 116 | topframe, text="GO AWAY", fg="red", font=("Helvetica", "16"), height=2, width=8, command=goaway) 117 | bigbutton.pack(side=LEFT) 118 | bigbutton2 = Button( 119 | topframe, text="Wake Up", fg="red", font=("Helvetica", "16"), height=2, width=8, command=canwakeup) 120 | bigbutton2.pack(side=LEFT) 121 | fullbutton = Button( 122 | topframe, text="Small", relief=SUNKEN, fg="red", font=("Helvetica", "16"), height=2, width=8, command=full) 123 | fullbutton.pack(side=LEFT) 124 | clock = Label(topframe, font=("Helvetica", "16")) 125 | clock.pack(side=RIGHT, fill=BOTH, expand=1) 126 | tick() 127 | 128 | 129 | frame = Frame(root) 130 | frame.pack(side=TOP) 131 | 132 | blfr = Frame(frame) 133 | bldsc = Label(blfr, text="Backlight", font=("Helvetica", "16")) 134 | bldsc.pack(side=LEFT) 135 | backlight = Scale(blfr, command=blchange, orient=HORIZONTAL, length = 400, to = 100) 136 | backlight.pack(side=RIGHT) 137 | blfr.pack() 138 | 139 | battfr = Frame(frame) 140 | battvdsc = Label(battfr, text="Batt V", font=("Helvetica", "16")) 141 | battvdsc.pack(side=LEFT) 142 | battvlabel = Label(battfr, font=("Helvetica", "16")) 143 | battvlabel.pack(side=RIGHT) 144 | battfr.pack() 145 | 146 | rpmfr = Frame(frame) 147 | rpmdsc = Label(rpmfr, text="RPM", font=("Helvetica", "16")) 148 | rpmdsc.pack(side=LEFT) 149 | rpmlabel = Label(rpmfr, font=("Helvetica", "16")) 150 | rpmlabel.pack(side=LEFT) 151 | 152 | rpmgauge = Canvas(rpmfr, width=200, height=150) 153 | rpmgauge.pack(side=LEFT) 154 | coord = 0, 0, 150, 150 #define the size of the gauge 155 | rpmgauge.create_arc(coord, start=30, extent=120, fill="white", width=2) 156 | rpmneedle = rpmgauge.create_arc(coord, start= 119, extent=1, width=7) 157 | 158 | pstempdsc = Label(rpmfr, text="PSTemp", font=("Helvetica", "16")) 159 | pstempdsc.pack(side=LEFT) 160 | pstemplabel = Label(rpmfr, font=("Helvetica", "16")) 161 | pstemplabel.pack(side=LEFT) 162 | 163 | pstempgauge = Canvas(rpmfr, width=200, height=150) 164 | pstempgauge.pack(side=LEFT) 165 | pstempgauge.create_arc(coord, start=30, extent=120, fill="white", width=2) 166 | pstempneedle = pstempgauge.create_arc(coord, start= 119, extent=1, width=7) 167 | 168 | rpmfr.pack() 169 | 170 | mphfr = Frame(frame) 171 | mphdr = Label(mphfr, text="MPH", font=("Helvetica", "16")) 172 | mphdr.pack(side=LEFT) 173 | mphlabel = Label(mphfr, font=("Helvetica", "16")) 174 | mphlabel.pack(side=RIGHT) 175 | mphfr.pack() 176 | 177 | 178 | bus = can.interface.Bus('', bustype='socketcan',filter=[{"can_id": 0x2C2, "can_mask": 0xFFF},{"can_id": 0x322, "can_mask": 0xFFF}]) 179 | Notifier = can.Notifier(bus, [newmsg], loop=None) 180 | 181 | 182 | root.mainloop() 183 | bus.shutdown() 184 | 185 | -------------------------------------------------------------------------------- /tkcan1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # GUI Can data display using tkinter 3 | 4 | from tkinter import * 5 | import time 6 | import can 7 | try: 8 | camavail = True 9 | import cv2 10 | cap = cv2.VideoCapture(0) 11 | if cap is None or not cap.isOpened(): 12 | camavail = False 13 | except: 14 | camavail = False 15 | 16 | # If using vcan for log playback, change the values in the quotes below 17 | can0 = "can0" 18 | can1 = "can1" 19 | 20 | battv = None 21 | rpm = None 22 | mph = None 23 | fsstate = True 24 | 25 | def full(): 26 | print("full screen") 27 | global fsstate 28 | fsstate = not fsstate 29 | root.attributes("-fullscreen", fsstate) 30 | if fsstate == True: 31 | fullbutton.config(relief=SUNKEN, text="Small") 32 | fullbutton.pack() 33 | else: 34 | fullbutton.config(relief=RAISED, text="Full") 35 | fullbutton.pack() 36 | 37 | def blchange(bllevels): 38 | print("backlight changed") 39 | print(str(bllevels)) 40 | 41 | def tick(): 42 | s = time.strftime('%I:%M:%S%p') 43 | if s != clock["text"]: 44 | clock["text"] = s 45 | clock.after(200, tick) 46 | 47 | def goaway(): 48 | bigbutton.config(text="Come Back", command=comeback) 49 | bigbutton.pack() 50 | frame.pack_forget() 51 | 52 | def comeback(): 53 | bigbutton.config(text="Go Away", command=goaway) 54 | bigbutton.pack() 55 | frame.pack(side=TOP) 56 | return "break" 57 | 58 | def newrpm(rpm): 59 | low_r = 0 # chart low range 60 | hi_r = 7000 # chart hi range 61 | if rpm == 65535: 62 | rpm = 0 63 | if str(rpm) != rpmlabel["text"]: 64 | rpmlabel["text"] = str(rpm) 65 | rpmlabel.pack() 66 | rpmangle = (120 * (hi_r - rpm) / (hi_r - low_r) + 30) 67 | rpmgauge.itemconfig(rpmneedle,start = rpmangle) 68 | rpmgauge.pack() 69 | 70 | def newmph(mph): 71 | if str(mph) != mphlabel["text"]: 72 | mphlabel["text"] = str(mph) 73 | mphlabel.pack() 74 | 75 | def newbattv(battv): 76 | if str(battv) != battvlabel["text"]: 77 | battvlabel["text"] = str(battv) 78 | battvlabel.pack() 79 | 80 | def newpstemp(pstemp): 81 | low_r = 50 # chart low range 82 | hi_r = 250 # chart hi range 83 | if str(pstemp) != pstemplabel["text"]: 84 | pstemplabel["text"] = str(pstemp) 85 | pstemplabel.pack() 86 | pstempangle = (120 * (hi_r - pstemp) / (hi_r - low_r) + 30) 87 | pstempgauge.itemconfig(pstempneedle,start = pstempangle) 88 | pstempgauge.pack() 89 | 90 | def newmsg(msg): 91 | if msg.arbitration_id == 0x2C2 and msg.channel == can0: 92 | newbattv(msg.data[2] / 10) 93 | if msg.arbitration_id == 0x322 and msg.channel == can0: 94 | newrpm((msg.data[0]<<8) + msg.data[1]) 95 | newmph(round(((msg.data[2]<<8) + msg.data[3]) / 200, 1)) 96 | if msg.arbitration_id == 0x128 and msg.channel == can1: 97 | newpstemp(round(((msg.data[1]) * (9 / 5)) + 32)) 98 | 99 | def canwakeup(): 100 | wakeup = can.Message(data=[0x07, 0, 0, 0, 0, 0, 0, 0], is_extended_id=False, arbitration_id=0x2D3, channel=can0) 101 | print(wakeup) 102 | bus.send(wakeup, timeout=1) 103 | 104 | def radioreboot(): 105 | radiorebootcmd = can.Message(data=[0x02, 0x11, 0x01, 0, 0, 0, 0, 0], is_extended_id=False, arbitration_id=0x7BF, channel=can0) 106 | bus.send(radiorebootcmd, timeout=1) 107 | 108 | def maxac(): 109 | maxaccmd = can.Message(data=[0x80, 0, 0, 0, 0, 0], is_extended_id=False, arbitration_id=0x342, channel=can0) 110 | bus.send(maxaccmd, timeout=1) 111 | 112 | def synchvac(): 113 | synchvaccmd = can.Message(data=[0, 0, 0, 0x04, 0], is_extended_id=False, arbitration_id=0x342, channel=can0) 114 | bus.send(synchvaccmd, timeout=1) 115 | 116 | def callback(): 117 | cap.release() 118 | bus.shutdown() 119 | print(bus.state) 120 | print("exit") 121 | raise 122 | 123 | def camera(): 124 | while(cap.isOpened()): 125 | ret, frame = cap.read() 126 | if ret == True: 127 | cv2.imshow('Frame',frame) 128 | else: 129 | break 130 | cap.release() 131 | 132 | root = Tk() 133 | root.geometry("800x400+0+40") 134 | root.title("This is Root") 135 | root.protocol("WM_DELETE_WINDOW", callback) 136 | root.attributes("-fullscreen", fsstate) 137 | root.configure(bg='gray') 138 | 139 | topframe=Frame(root) 140 | topframe.pack(side=BOTTOM) 141 | 142 | quitbutton = Button( 143 | topframe, text="QUIT", fg="red", font=("Helvetica", "16"), height=2, width=7, command=topframe.quit) 144 | quitbutton.pack(side=LEFT) 145 | if camavail: 146 | bigbutton = Button( 147 | topframe, text="CAMERA", fg="red", font=("Helvetica", "16"), height=2, width=7, command=camera) 148 | bigbutton.pack(side=LEFT) 149 | bigbutton2 = Button( 150 | topframe, text="Wake Up", fg="red", font=("Helvetica", "16"), height=2, width=7, command=canwakeup) 151 | bigbutton2.pack(side=LEFT) 152 | radiorebootbutton = Button( 153 | topframe, text="Reboot", fg="red", font=("Helvetica", "16"), height=2, width=7, command=radioreboot) 154 | radiorebootbutton.pack(side=LEFT) 155 | maxacbutton = Button( 156 | topframe, text="MAX AC", fg="red", font=("Helvetica", "16"), height=2, width=7, command=maxac) 157 | maxacbutton.pack(side=LEFT) 158 | synchvacbutton = Button( 159 | topframe, text="Sync", fg="red", font=("Helvetica", "16"), height=2, width=7, command=synchvac) 160 | synchvacbutton.pack(side=LEFT) 161 | clock = Label(topframe, font=("Helvetica", "16")) 162 | clock.pack(side=RIGHT, fill=BOTH, expand=1) 163 | tick() 164 | 165 | 166 | frame = Frame(root) 167 | frame.pack(side=TOP) 168 | 169 | blfr = Frame(frame) 170 | bldsc = Label(blfr, text="Backlight", font=("Helvetica", "16")) 171 | bldsc.pack(side=LEFT) 172 | backlight = Scale(blfr, command=blchange, orient=HORIZONTAL, length = 400, to = 100) 173 | backlight.pack(side=RIGHT) 174 | blfr.pack() 175 | 176 | battfr = Frame(frame) 177 | battvdsc = Label(battfr, text="Batt V", font=("Helvetica", "16")) 178 | battvdsc.pack(side=LEFT) 179 | battvlabel = Label(battfr, font=("Helvetica", "16")) 180 | battvlabel.pack(side=RIGHT) 181 | battfr.pack() 182 | 183 | rpmfr = Frame(frame) 184 | rpmdsc = Label(rpmfr, text="RPM", font=("Helvetica", "16")) 185 | rpmdsc.pack(side=LEFT) 186 | rpmlabel = Label(rpmfr, font=("Helvetica", "16")) 187 | rpmlabel.pack(side=LEFT) 188 | 189 | rpmgauge = Canvas(rpmfr, width=200, height=150) 190 | rpmgauge.pack(side=LEFT) 191 | coord = 0, 0, 150, 150 #define the size of the gauge 192 | rpmgauge.create_arc(coord, start=30, extent=120, fill="white", width=2) 193 | rpmneedle = rpmgauge.create_arc(coord, start= 119, extent=1, width=7) 194 | 195 | pstempdsc = Label(rpmfr, text="PSTemp", font=("Helvetica", "16")) 196 | pstempdsc.pack(side=LEFT) 197 | pstemplabel = Label(rpmfr, font=("Helvetica", "16")) 198 | pstemplabel.pack(side=LEFT) 199 | 200 | pstempgauge = Canvas(rpmfr, width=200, height=150) 201 | pstempgauge.pack(side=LEFT) 202 | pstempgauge.create_arc(coord, start=30, extent=120, fill="white", width=2) 203 | pstempneedle = pstempgauge.create_arc(coord, start= 119, extent=1, width=7) 204 | 205 | rpmfr.pack() 206 | 207 | mphfr = Frame(frame) 208 | mphdr = Label(mphfr, text="MPH", font=("Helvetica", "16")) 209 | mphdr.pack(side=LEFT) 210 | mphlabel = Label(mphfr, font=("Helvetica", "16")) 211 | mphlabel.pack(side=RIGHT) 212 | mphfr.pack() 213 | 214 | 215 | bus = can.interface.Bus('', bustype='socketcan',filter=[{"can_id": 0x2C2, "can_mask": 0xFFF},{"can_id": 0x322, "can_mask": 0xFFF}]) 216 | Notifier = can.Notifier(bus, [newmsg], loop=None) 217 | 218 | 219 | root.mainloop() 220 | bus.shutdown() 221 | 222 | -------------------------------------------------------------------------------- /logreader.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | TMPDIR=/run/tmpfiles.d 3 | 4 | # Set only one of these to 1, the rest to 0 5 | UPDATE_SECONDS=1 6 | UPDATE_TENTHS=0 7 | UPDATE_HUNDREDTHS=0 8 | 9 | # Enable if you want some alternate values displayed (where available) 10 | SHOW_ALTERNATES=0 11 | 12 | flag="02B" 13 | flag2="08B" 14 | flag3="027" 15 | 16 | echo "00000000" > /$TMPDIR/0AB 17 | echo "023#FFFF" > /$TMPDIR/023 18 | echo "F" > /$TMPDIR/358 19 | echo "000001" > /$TMPDIR/3D2 20 | echo "00" > /$TMPDIR/232 21 | echo "0000000000000000" > /$TMPDIR/$flag 22 | echo "FFFFFFFFFFFFFFFF" > /$TMPDIR/$flag2 23 | echo "000000000" > /$TMPDIR/07B 24 | echo "000" > /$TMPDIR/079 25 | echo "0000000000000000" > /$TMPDIR/$flag3 26 | echo "000000000000" > /$TMPDIR/322 27 | 28 | GO=0 29 | cat $1 | egrep " 023#| 079#| 07B#| $flag3#| 232#| 340#| 358#| 3D2#| 322#| 122#| 0AB#| $flag#| $flag2#" | while read a b c 30 | do 31 | 32 | case "$c" in 33 | 34 | $flag3#*) echo "${c:4:16}" > /$TMPDIR/$flag3 ;; 35 | $flag2#*) echo "${c:4:16}" > /$TMPDIR/$flag2 ;; 36 | $flag#*) echo "${c:4:16}" > /$TMPDIR/$flag ;; 37 | 079#*) echo "${c:5:9}" > /$TMPDIR/079 ;; 38 | 07B#*) echo "${c:5:9}" > /$TMPDIR/07B ;; 39 | 232#*) echo "${c:6:2}" > /$TMPDIR/232 ;; 40 | 358#*) echo "${c:5:1}" > /$TMPDIR/358 ;; 41 | 3D2#*) echo "${c:4:6}" > /$TMPDIR/3D2 ;; 42 | 0AB#*) echo "${c:8:4}" > /$TMPDIR/0AB ;; 43 | 122#*) echo "${c:4:4}" > /$TMPDIR/122 ;; 44 | 45 | 46 | 023#*) 47 | [ "$UPDATE_HUNDREDTHS" == "1" ] && GO=1 48 | echo "$c" > /$TMPDIR/023 49 | ;; 50 | 51 | 322#*) 52 | echo "${c:4:12}" > /$TMPDIR/322 53 | [ "$UPDATE_TENTHS" == "1" ] && GO=1 54 | ;; 55 | 56 | # CASE 340 57 | 340#*) 58 | echo "$c" | cut -c9-10,19,20 > /$TMPDIR/340 59 | [ "$UPDATE_SECONDS" == "1" ] && GO=1 60 | ;; 61 | esac 62 | 63 | if [ "$GO" == "1" ] 64 | then 65 | 66 | trans=`cat /$TMPDIR/340` 67 | speed="${trans:2:2}" 68 | 69 | # The date is done here 70 | time="${a:6:13}" 71 | echo -n "$time " 72 | 73 | rawkey=`cat /$TMPDIR/122` 74 | key="Unk${rawkey} " 75 | case "$rawkey" in 76 | 0301) key="Kill" ;; 77 | 0302) key="Kill" ;; 78 | 0502) key="Acc " ;; 79 | 1502) key="Acc " ;; 80 | 0000) key="Off " ;; 81 | 0001) key="Off " ;; 82 | 4501) key="Strt" ;; 83 | 5D01) key="Crnk" ;; 84 | 4401) 85 | key="RRun" 86 | [ "$rpm1" == "0.0k" ] && key="RAcc" 87 | ;; 88 | 0402) key="Run " ;; 89 | esac 90 | echo -n "KEY: $key " 91 | 92 | BRAKE=`cat /$TMPDIR/079 | cut -c1-3` 93 | BRAKE=$(printf "%d" 0x$BRAKE) 94 | BRAKE=`echo "0k $BRAKE 22.5 / p" | dc` 95 | [ "$BRAKE" -gt 100 ] && BRAKE=100 96 | [ "$BRAKE" -lt 100 ] && BRAKE=" $BRAKE" 97 | [ "$BRAKE" -lt 10 ] && BRAKE=" $BRAKE" 98 | echo -n "BRK:$BRAKE%" 99 | 100 | ACC="$(cat /$TMPDIR/07B)" 101 | ACCEL="$(printf "%d" 0x${ACC:0:3})" 102 | ACCEL2="$(printf "%d" 0x${ACC:6:3})" 103 | [ $ACCEL2 -lt 2000 ] && ACCEL2=2000 104 | ACCEL2=`echo "0k $ACCEL2 2000 - 18 / p" | dc` 105 | ACCEL2=`printf "%2d" $ACCEL2` 106 | [ "$ACCEL" -lt 1900 ] && ACCEL=1900 107 | ACCEL=`echo "0k $ACCEL 1990 - 100 * 2000 / p" | dc` 108 | [ "$ACCEL" -lt 0 ] && ACCEL=0 109 | [ "$ACCEL" -lt 100 ] && ACCEL=" $ACCEL" 110 | [ "$ACCEL" -lt 10 ] && ACCEL=" $ACCEL" 111 | echo -n " ACCL:$ACCEL% (VALVE $ACCEL2%) " 112 | 113 | rpm1=`cat /$TMPDIR/322 | cut -c1-4` 114 | #rpm2=`cat /$TMPDIR/322 | cut -c5-8` 115 | rpm1="$(printf "%d" 0x$rpm1)" 116 | [ $rpm1 = 65535 ] && rpm1="0" 117 | #rpm1=`echo "1 k $rpm1 1000 / p" | dc` 118 | #rpm1=`printf "%.1f" $rpm1`"k" 119 | printf "RPM: %4d " $rpm1 120 | #echo -n "RPM: $rpm1 " 121 | 122 | STEER=`cat /$TMPDIR/023 | cut -c5-8` 123 | STEER="$(printf "%d" 0x$STEER)" 124 | STEERSIGN="R " 125 | [[ $STEER -gt 4096 ]] && STEERSIGN="L " 126 | STEER=`echo "4096 $STEER -p" | dc | cut -d- -f2` 127 | STEER=`echo "2k $STEER 200 / 100 * p" | dc | cut -d. -f1` 128 | STEER=`printf "%3d\n" ${STEER}` 129 | SYMBOL="°" 130 | [ "$STEER" -lt 2 ] && STEERSIGN=" " && STEER=" 0" 131 | [ "$STEER" -gt 1000 ] && STEERSIGN="IN" && STEER="VAL" && SYMBOL="D" 132 | echo -n "WHEEL: ${STEERSIGN}${STEER}${SYMBOL} " 133 | 134 | [ "$speed" == "FF" ] && speed="00" 135 | speed="$(printf "%d" 0x$speed)" 136 | 137 | gear=`echo $trans | cut -c2` 138 | [ "$gear" == "F" ] && gear="NA" 139 | [ "$gear" == "B" ] && gear="R " && speed="-$speed" 140 | [ "$gear" == "D" ] && gear="P " 141 | if [[ "$gear" =~ ^[1-9]+$ ]] 142 | then gear="D$gear" 143 | fi 144 | [ "$gear" == "0" ] && gear="N " 145 | 146 | mph=`cat /$TMPDIR/322 | cut -c5-8` 147 | mph="$( printf "%d" 0x$mph)" 148 | [ $mph == 65535 ] && mph="0" 149 | [ $gear == "R " ] && mph="-$mph" 150 | mph=`echo "2 k $mph 200 / p" | dc` 151 | mph=`printf "%2.2f" $mph` 152 | 153 | compass=`cat /$TMPDIR/358` 154 | case "$compass" in 155 | F) compass="??" ;; 156 | 0) compass="N " ;; 157 | 1) compass="NE" ;; 158 | 2) compass=" E" ;; 159 | 3) compass="SE" ;; 160 | 4) compass="S " ;; 161 | 5) compass="SW" ;; 162 | 6) compass=" W" ;; 163 | 7) compass="NW" ;; 164 | esac 165 | echo -n "DIR: $compass " 166 | 167 | echo -n " GEAR: $gear " 168 | 169 | echo -n "(`cat /$TMPDIR/0AB`) " 170 | echo -n "ODOM: " 171 | odometer=`cat /$TMPDIR/3D2` 172 | odometer="$(printf "%d" 0x$odometer)" 173 | odometer=`echo " $odometer * 50 / 8 " | bc` 174 | if [ "$odometer" == "6" ] 175 | then 176 | odometer="0" 177 | echo -n "??????.?mi " 178 | else 179 | printf '%8.1f' `echo "$odometer / 100" | bc -l` 180 | echo -n "mi " 181 | fi 182 | 183 | printf "MPH: %5s " $mph 184 | 185 | value=`cat /$TMPDIR/$flag2` 186 | value1=$(( 0x${value:0:4} )) 187 | value2=$(( 0x${value:4:4} )) 188 | value3=$(( 0x${value:8:4} )) 189 | value4=$(( 0x${value:12:4} )) 190 | [ $value1 -eq 49152 ] && value1=0; # Initialization values 191 | [ $value2 -eq 49152 ] && value2=0; # Initialization values 192 | [ $value3 -eq 49152 ] && value3=0; # Initialization values 193 | [ $value4 -eq 49152 ] && value4=0; # Initialization values 194 | [ $value1 -gt 32767 ] && value1="$(( - $value1 + 0x7FFF ))" 195 | [ $value2 -gt 32767 ] && value2="$(( - $value2 + 0x7FFF ))" 196 | [ $value3 -gt 32767 ] && value3="$(( - $value3 + 0x7FFF ))" 197 | [ $value4 -gt 32767 ] && value4="$(( - $value4 + 0x7FFF ))" 198 | [ $value1 -gt 16383 ] && value1="$(( $value1 - 0x3FFF ))" 199 | [ $value2 -gt 16383 ] && value2="$(( $value2 - 0x3FFF ))" 200 | [ $value3 -gt 16383 ] && value3="$(( $value3 - 0x3FFF ))" 201 | [ $value4 -gt 16383 ] && value4="$(( $value4 - 0x3FFF ))" 202 | value1=$(echo "scale=2 ; $value1 / 20" | bc) 203 | value2=$(echo "scale=2 ; $value2 / 20" | bc) 204 | value3=$(echo "scale=2 ; $value3 / 20" | bc) 205 | value4=$(echo "scale=2 ; $value4 / 20" | bc) 206 | 207 | # By uncommenting this section, we will display RELATIVE tire speed 208 | # instead of individual tire speed. Useful for watching the balance 209 | # between individual tires throughout a drive. 210 | [ $value1 == "0" ] && value1="0.001" 211 | [ $value2 == "0" ] && value2="0.001" 212 | [ $value3 == "0" ] && value3="0.001" 213 | [ $value4 == "0" ] && value4="0.001" 214 | tirea=$(echo "scale=3 ; ($value2 + $value3 + $value4) / 3" | bc) 215 | tireb=$(echo "scale=3 ; ($value1 + $value3 + $value4) / 3" | bc) 216 | tirec=$(echo "scale=3 ; ($value1 + $value2 + $value4) / 3" | bc) 217 | tired=$(echo "scale=3 ; ($value1 + $value2 + $value3) / 3" | bc) 218 | value1=$(echo "scale=2 ; $value1 / $tirea" | bc) 219 | value2=$(echo "scale=2 ; $value2 / $tireb" | bc) 220 | value3=$(echo "scale=2 ; $value3 / $tirec" | bc) 221 | value4=$(echo "scale=2 ; $value4 / $tired" | bc) 222 | printf "Wheels: [%4s %4s] [%4s %4s] " $value1 $value2 $value3 $value4 223 | 224 | # If the section above is commented, uncomment this. 225 | # If the section above is uncommented, comment this. 226 | # This adjusts the scale used to print tire speeds. 227 | # printf "MPHx4: [%1.2f %1.2f] [%1.2f %1.2f] " $value1 $value2 $value3 $value4 228 | 229 | temp=`cat /$TMPDIR/232` 230 | temp="$(printf "%d" 0x$temp)" 231 | temp=`echo $temp | cut -d" " -f2` 232 | #temp=`echo " $temp * 1.8 + 32 " | bc` 233 | [ "$temp" -lt 100 ] && temp=" $temp" 234 | [ "$temp" -lt 10 ] && temp=" $temp" 235 | [ "$temp" == "255" ] && temp=" ??" 236 | echo -n " RADIO: ${temp} F " 237 | 238 | echo "" 239 | GO=0 240 | fi 241 | 242 | done 243 | -------------------------------------------------------------------------------- /pycan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # jeep canbus live data display 3 | 4 | # import libraries 5 | import can 6 | import curses 7 | 8 | canIHS = "vcan0" 9 | canC = "vcan1" 10 | 11 | # start curses screen management 12 | stdscr = curses.initscr() 13 | 14 | # lay out our plain text 15 | stdscr.addstr(1,1,'Batt') 16 | stdscr.addstr(1,11,'Vdc') 17 | stdscr.addstr(2,1,'Roll') 18 | stdscr.addstr(2,14,'Tilt') 19 | stdscr.addstr(2,26,'Yaw') 20 | stdscr.addstr(4,1,'RPM') 21 | stdscr.addstr(4,14,'MPH') 22 | stdscr.addstr(4,27,'Gear') 23 | stdscr.addstr(4,40,'4x4') 24 | stdscr.addstr(6,1,'Steering Angle') 25 | stdscr.addstr(6,25,'Turn') 26 | stdscr.addstr(6,40,'Temp') 27 | stdscr.addstr(6,50,'Pres') 28 | stdscr.addstr(8,1,'Tire psi') 29 | stdscr.addstr(10,1,'Brake pedal') 30 | stdscr.addstr(10,18,'pressure') 31 | stdscr.addstr(10,30,'Motion') 32 | stdscr.addstr(12,1,'Temps IAT') 33 | stdscr.addstr(12,20,'Coolant') 34 | stdscr.addstr(14,1,'Lights') 35 | stdscr.addstr(16,1,'MPG') 36 | stdscr.addstr(16,30,'Cabin F') 37 | stdscr.addstr(18,1,'Oil PSI') 38 | 39 | # update the screen with the text 40 | stdscr.refresh() 41 | 42 | # startup the canbus interface and filter only the ids that we want 43 | bus = can.interface.Bus('', bustype='socketcan') 44 | 45 | # wrap everything in a try to catch exceptions cleanly 46 | try: 47 | # iterate through all messages received 48 | for msg in bus: 49 | # match a can id and can bus 50 | if msg.arbitration_id == 0x2C2 and msg.channel == canIHS: 51 | # place data using str to convert float data to a string 52 | # this is using the 3rd hex word, deviding by 10 53 | stdscr.addstr(1,6,'%-5s' % str(msg.data[2] / 10)) 54 | # once the message has been processes, refresh the screen with the data 55 | stdscr.addstr(1,20,'%-6s' % str(((msg.data[0]<<8) + msg.data[1]) / 100)) 56 | stdscr.refresh() 57 | # match another message 58 | if msg.arbitration_id == 0x02B and msg.channel == canC: 59 | # place data, multiple words each in its own spot 60 | stdscr.addstr(2,6,'%-6s' % str(((msg.data[0]<<8) + msg.data[1] - 2048) / 10)) 61 | stdscr.addstr(2,20,'%-6s' % str(((msg.data[2]<<8) + msg.data[3] - 2032) / 10)) 62 | stdscr.addstr(2,30,'%-6s' % str(((msg.data[4]<<8) + msg.data[5] - 2048) / 10)) 63 | # once the message has been processes, refresh the screen with the data 64 | stdscr.refresh() 65 | if msg.arbitration_id == 0x322 and msg.channel == canIHS: 66 | rpmstr = str(((msg.data[0]<<8) + msg.data[1])) 67 | if rpmstr == "65535": 68 | rpmstr = str(0) 69 | stdscr.addstr(4,6,'%-6s' % rpmstr) 70 | stdscr.addstr(4,20,'%-4s' % str(round(((msg.data[2]<<8) + msg.data[3]) / 200,1))) 71 | # once the message has been processes, refresh the screen with the data 72 | stdscr.refresh() 73 | if msg.arbitration_id == 0x023 and msg.channel == canC: 74 | stdscr.addstr(6,16,'%-6s' % str(((msg.data[0]<<8) + msg.data[1]) - 0x1000)) 75 | stdscr.addstr(6,32,'%-6s' % str(((msg.data[2]<<8) + msg.data[3]) - 0x1000)) 76 | stdscr.refresh() 77 | if msg.arbitration_id == 0x079 and msg.channel == canC: 78 | stdscr.addstr(10,12,'%-5s' % str(((msg.data[0]<<8) + msg.data[1]) & 0x0FFF)) 79 | stdscr.addstr(10,25,'%-5s' % str((msg.data[2]<<8) + msg.data[3])) 80 | if (msg.data[0] & 0xF0) == 0x80: 81 | movingstat = 'Stopped' 82 | elif (msg.data[0] & 0xF0) == 0x00: 83 | movingstat = 'Moving' 84 | else: 85 | movingstat = str(msg.data[0] & 0xF0) 86 | stdscr.addstr(10,40,'%-10s' % movingstat) 87 | stdscr.refresh() 88 | if msg.arbitration_id == 0x127 and msg.channel == canC: 89 | stdscr.addstr(12,15,'%-3s' % str(round((((msg.data[0] - 40) * (9 / 5)) + 32)))) 90 | stdscr.addstr(12,30,'%-3s' % str(round((((msg.data[1] - 40) * (9 / 5)) + 32)))) 91 | stdscr.refresh() 92 | if msg.arbitration_id == 0x291 and msg.channel == canC: 93 | stdscr.addstr(14,10,'%-4s' % hex(msg.data[0])) 94 | stdscr.addstr(14,15,'%-4s' % hex(msg.data[1])) 95 | stdscr.addstr(14,20,'%-4s' % hex(msg.data[2])) 96 | stdscr.addstr(14,25,'%-4s' % hex(msg.data[3])) 97 | stdscr.addstr(14,30,'%-4s' % hex(msg.data[4])) 98 | stdscr.addstr(14,35,'%-4s' % hex(msg.data[5])) 99 | stdscr.addstr(14,40,'%-4s' % hex(msg.data[6])) 100 | stdscr.addstr(14,45,'%-4s' % hex(msg.data[7])) 101 | stdscr.refresh() 102 | if msg.arbitration_id == 0x071 and msg.channel == canC: 103 | stdscr.addstr(12,50,'%-6s' % str((msg.data[0]<<8) + msg.data[1])) 104 | stdscr.addstr(12,60,'%-6s' % str(msg.data[4])) 105 | stdscr.refresh() 106 | if msg.arbitration_id == 0x093 and msg.channel == canC: 107 | if msg.data[2] == 0x4E: 108 | mtstatus = 'N' 109 | elif msg.data[2] == 0x52: 110 | mtstatus = 'R' 111 | elif msg.data[2] == 0x31: 112 | mtstatus = '1' 113 | elif msg.data[2] == 0x32: 114 | mtstatus = '2' 115 | elif msg.data[2] == 0x33: 116 | mtstatus = '3' 117 | elif msg.data[2] == 0x34: 118 | mtstatus = '4' 119 | elif msg.data[2] == 0x35: 120 | mtstatus = '5' 121 | elif msg.data[2] == 0x36: 122 | mtstatus = '6' 123 | elif msg.data[2] == 0x37: 124 | mtstatus = '7' 125 | elif msg.data[2] == 0x38: 126 | mtstatus = '8' 127 | elif msg.data[2] == 0x50: 128 | mtstatus = 'P' 129 | elif msg.data[2] == 0x44: 130 | mtstatus = 'D' 131 | else: 132 | mtstatus = hex(msg.data[2]) 133 | stdscr.addstr(4,33,'%-4s' % mtstatus) 134 | stdscr.refresh() 135 | if msg.arbitration_id == 0x277 and msg.channel == canC: 136 | if msg.data[1] == 0x02: 137 | mtstatus = 'N' 138 | elif msg.data[0] == 0x00: 139 | mtstatus = '2HI' 140 | elif msg.data[0] == 0x10: 141 | mtstatus = '4HI' 142 | elif msg.data[0] == 0x20: 143 | mtstatus = 'N' 144 | elif msg.data[0] == 0x40: 145 | mtstatus = '4LO' 146 | elif msg.data[0] == 0x80: 147 | mtstatus = 'SHIFT' 148 | else: 149 | mtstatus = msg.data[0] 150 | stdscr.addstr(4,45,'%-5s' % mtstatus) 151 | stdscr.refresh() 152 | if msg.arbitration_id == 0x128 and msg.channel == canC: 153 | stdscr.addstr(6,45,'%-5s' % str(round((((msg.data[1]) * (9 / 5)) + 32)))) 154 | stdscr.addstr(6,55,'%-5s' % str(msg.data[2])) 155 | stdscr.addstr(6,60,'%-5s' % str(msg.data[5])) 156 | stdscr.refresh() 157 | if msg.arbitration_id == 0x296 and msg.channel == canC: 158 | stdscr.addstr(8,15,'%-3s' % str(msg.data[3])) 159 | stdscr.addstr(8,20,'%-3s' % str(msg.data[4])) 160 | stdscr.addstr(8,25,'%-3s' % str(msg.data[5])) 161 | stdscr.addstr(8,30,'%-3s' % str(msg.data[6])) 162 | if msg.arbitration_id == 0x2F2 and msg.channel == canC: 163 | stdscr.addstr(16,20,'%-5s' % str(((msg.data[6]<<8) + msg.data[7]) / 10)) 164 | if msg.arbitration_id == 0x33A and msg.channel == canIHS: 165 | stdscr.addstr(16,40,'%-6s' % str(round(((((((msg.data[0]<<8) + msg.data[1]) / 100) - 40) * (9 / 5)) + 32),1))) 166 | stdscr.addstr(16,50,'%-4s' % hex(msg.data[2])) 167 | if msg.arbitration_id == 0x13D and msg.channel == canC: 168 | stdscr.addstr(18,10,'%-5s' % str(msg.data[2])) 169 | stdscr.addstr(18,20,'%-5s' % str(msg.data[3])) 170 | stdscr.addstr(18,40,'%-4s' % str(msg.data[4])) 171 | stdscr.addstr(18,50,'%-4s' % str(msg.data[5])) 172 | if msg.arbitration_id == 0x30A and msg.channel == canIHS: 173 | stdscr.addstr(20,10,'%-5s' % str(round((((msg.data[3] - 40) * (9 / 5)) + 32)))) 174 | if msg.arbitration_id == 0x230 and msg.channel == canIHS: 175 | stdscr.addstr(20,20,'%-5s' % str(round((((msg.data[1] - 40) * (9 / 5)) + 32)))) 176 | if msg.arbitration_id == 0x332 and msg.channel == canIHS: 177 | stdscr.addstr(20,30,'%-5s' % str(round((((msg.data[4] - 40) * (9 / 5)) + 32)))) 178 | if msg.arbitration_id == 0x3B2 and msg.channel == canIHS: 179 | stdscr.addstr(20,40,'%-5s' % str(round((((msg.data[2] - 40) * (9 / 5)) + 32)))) 180 | if msg.arbitration_id == 0x13D and msg.channel == canC: 181 | stdscr.addstr(12,35,'%-3s' % str(round((((msg.data[3] - 40) * (9 / 5)) + 32)))) 182 | stdscr.addstr(12,40,'%-3s' % str(round((msg.data[2] * 4) * 0.145038))) 183 | 184 | 185 | 186 | 187 | # catch errors, display them, and exit cleanly 188 | except: 189 | bus.shutdown() 190 | curses.nocbreak() 191 | stdscr.keypad(0) 192 | curses.echo() 193 | curses.endwin() 194 | raise 195 | -------------------------------------------------------------------------------- /tkcan3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # GUI Can data display using tkinter 3 | 4 | from tkinter import * 5 | import time 6 | import can 7 | import subprocess 8 | import signal 9 | 10 | # If using vcan for log playback, change the values in the quotes below 11 | can0 = "vcan0" 12 | can1 = "vcan1" 13 | 14 | battv = None 15 | rpm = None 16 | mph = None 17 | fsstate = True 18 | cam = None 19 | oldpstemp = None 20 | oldrpm = None 21 | oldtilt = None 22 | oldroll = None 23 | 24 | def full(): 25 | print("full screen") 26 | global fsstate 27 | fsstate = not fsstate 28 | root.attributes("-fullscreen", fsstate) 29 | if fsstate == True: 30 | fullbutton.config(relief=SUNKEN, text="Small") 31 | fullbutton.pack() 32 | else: 33 | fullbutton.config(relief=RAISED, text="Full") 34 | fullbutton.pack() 35 | 36 | def blchange(bllevels): 37 | print("backlight changed") 38 | print(str(bllevels)) 39 | 40 | def tick(): 41 | s = time.strftime('%I:%M:%S%p') 42 | if s != clock["text"]: 43 | clock["text"] = s 44 | clock.after(200, tick) 45 | 46 | def goaway(): 47 | bigbutton.config(text="Come Back", command=comeback) 48 | bigbutton.pack() 49 | frame.pack_forget() 50 | 51 | def comeback(): 52 | bigbutton.config(text="Go Away", command=goaway) 53 | bigbutton.pack() 54 | frame.pack(side=TOP) 55 | return "break" 56 | 57 | def newrpm(rpm): 58 | global oldrpm 59 | low_r = 0 # chart low range 60 | hi_r = 7000 # chart hi range 61 | if rpm == 65535: 62 | rpm = 0 63 | if rpm != oldrpm: 64 | gauge1.itemconfig(gauge1label, text=str(rpm)) 65 | rpmangle = (120 * (hi_r - rpm) / (hi_r - low_r) + 30) 66 | gauge1.itemconfig(gauge1needle,start = rpmangle) 67 | gauge1.grid() 68 | oldrpm = rpm 69 | 70 | def newmph(mph): 71 | if str(mph) != mphlabel["text"]: 72 | mphlabel["text"] = str(mph) 73 | mphlabel.pack() 74 | 75 | def newbattv(battv): 76 | if str(battv) != battvlabel["text"]: 77 | battvlabel["text"] = str(battv) 78 | battvlabel.pack() 79 | 80 | def newpstemp(pstemp): 81 | global oldpstemp 82 | low_r = 50 # chart low range 83 | hi_r = 250 # chart hi range 84 | if pstemp != oldpstemp: 85 | gauge2.itemconfig(gauge2label, text=str(pstemp)) 86 | pstempangle = (120 * (hi_r - pstemp) / (hi_r - low_r) + 30) 87 | gauge2.itemconfig(gauge2needle,start = pstempangle) 88 | gauge2.grid() 89 | oldpstemp = pstemp 90 | 91 | def newtilt(tilt, roll): 92 | global oldtilt 93 | global oldroll 94 | if tilt != oldtilt: 95 | gauge7.itemconfig(gauge7label, text=str(tilt)) 96 | gauge7.itemconfig(gauge7needle, start=tilt) 97 | gauge7.grid() 98 | oldtilt = tilt 99 | if tilt > 15: 100 | gauge7.itemconfig(gauge7needle, fill="yellow") 101 | if tilt > 25: 102 | gauge7.itemconfig(gauge7needle, fill="red") 103 | else: 104 | gauge7.itemconfig(gauge7needle, fill="green") 105 | if roll != oldroll: 106 | gauge8.itemconfig(gauge8label, text=str(roll)) 107 | gauge8.itemconfig(gauge8needle, start=roll) 108 | gauge8.grid() 109 | oldroll = roll 110 | if roll > 15: 111 | gauge8.itemconfig(gauge8needle, fill="yellow") 112 | if roll > 25: 113 | gauge8.itemconfig(gauge8needle, fill="red") 114 | else: 115 | gauge8.itemconfig(gauge8needle, fill="green") 116 | 117 | def newmsg(msg): 118 | if msg.arbitration_id == 0x2C2 and msg.channel == can0: 119 | newbattv(msg.data[2] / 10) 120 | if msg.arbitration_id == 0x322 and msg.channel == can0: 121 | newrpm((msg.data[0]<<8) + msg.data[1]) 122 | newmph(round(((msg.data[2]<<8) + msg.data[3]) / 200, 1)) 123 | if msg.arbitration_id == 0x128 and msg.channel == can1: 124 | newpstemp(round(((msg.data[1]) * (9 / 5)) + 32)) 125 | if msg.arbitration_id == 0x02B and msg.channel == can1: 126 | newtilt(round(((msg.data[0]<<8) + msg.data[1] - 2048) / 20)*2, round(((msg.data[2]<<8) + msg.data[3] - 2048) / 20)*2) 127 | 128 | def canwakeup(): 129 | wakeup = can.Message(data=[0x07, 0, 0, 0, 0, 0, 0, 0], is_extended_id=False, arbitration_id=0x2D3, channel=can0) 130 | print(wakeup) 131 | bus.send(wakeup, timeout=1) 132 | 133 | def radioreboot(): 134 | radiorebootcmd = can.Message(data=[0x02, 0x11, 0x01, 0, 0, 0, 0, 0], is_extended_id=False, arbitration_id=0x7BF, channel=can0) 135 | bus.send(radiorebootcmd, timeout=1) 136 | 137 | def maxac(): 138 | maxaccmd = can.Message(data=[0x80, 0, 0, 0, 0, 0], is_extended_id=False, arbitration_id=0x342, channel=can0) 139 | bus.send(maxaccmd, timeout=1) 140 | 141 | def synchvac(): 142 | synchvaccmd = can.Message(data=[0, 0, 0, 0x04, 0], is_extended_id=False, arbitration_id=0x342, channel=can0) 143 | bus.send(synchvaccmd, timeout=1) 144 | 145 | def blankscreen(): 146 | subprocess.call(['xscreensaver-command', '-activate']) 147 | 148 | def callback(): 149 | topframe.quit() 150 | 151 | def camera(): 152 | global cam 153 | if cam: 154 | cam.terminate() 155 | cam = None 156 | frame.pack(side=TOP, fill="x") 157 | else: 158 | cam = subprocess.Popen(["raspivid", "-t", "0", "-v", "-w", "800", "-h", "480", "-op", "200", "-rot", "180"]) 159 | camstatus = cam.poll() 160 | if camstatus is None: 161 | frame.pack_forget() 162 | 163 | def button1(): 164 | bigbutton1 = Button( 165 | topframe, text="CAMERA", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=camera) 166 | bigbutton1.pack(side=LEFT) 167 | def button2(): 168 | bigbutton2 = Button( 169 | topframe, text="Wake Up", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=canwakeup) 170 | bigbutton2.pack(side=LEFT) 171 | def button3(): 172 | maxacbutton = Button( 173 | topframe, text="MAX AC", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=maxac) 174 | maxacbutton.pack(side=LEFT) 175 | def button4(): 176 | synchvacbutton = Button( 177 | topframe, text="Sync", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=synchvac) 178 | synchvacbutton.pack(side=LEFT) 179 | def button5(): 180 | quitbutton = Button( 181 | topframe, text="QUIT", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=topframe.quit) 182 | quitbutton.pack(side=LEFT) 183 | def button6(): 184 | screenoffbutton = Button( 185 | topframe, text="Screen OFF", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=blankscreen) 186 | screenoffbutton.pack(side=LEFT) 187 | def button7(): 188 | radiorebootbutton = Button( 189 | topframe, text="Reboot", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=radioreboot) 190 | radiorebootbutton.pack(side=LEFT) 191 | 192 | 193 | root = Tk() 194 | root.geometry("800x480+0+0") 195 | root.title("This is Root") 196 | root.protocol("WM_DELETE_WINDOW", callback) 197 | root.attributes("-fullscreen", fsstate) 198 | root.configure(bg='black') 199 | 200 | topframe=Frame(root) 201 | topframe.configure(bg='black') 202 | topframe.pack(side=BOTTOM, fill="x") 203 | button1() 204 | button2() 205 | button3() 206 | button4() 207 | button5() 208 | button6() 209 | 210 | textframe=Frame(root) 211 | textframe.pack(side=BOTTOM, fill="x") 212 | 213 | mphdr = Label(textframe, text="MPH", font=("Helvetica", "16")) 214 | mphdr.pack(side=LEFT) 215 | mphlabel = Label(textframe, font=("Helvetica", "16")) 216 | mphlabel.pack(side=LEFT) 217 | 218 | battvdsc = Label(textframe, text="Batt V", font=("Helvetica", "16")) 219 | battvdsc.pack(side=LEFT) 220 | battvlabel = Label(textframe, font=("Helvetica", "16")) 221 | battvlabel.pack(side=LEFT) 222 | 223 | frame = Frame(root) 224 | frame.pack(side=TOP, fill="x") 225 | frame.configure(bg='black') 226 | 227 | coord = 0, 0, 200, 350 #define the size of the gaug 228 | fullcoord = 0, 0, 175, 175 229 | 230 | gauge1 = Canvas(frame, width=200, height=175) 231 | gauge1.grid(row=1, column=1) 232 | gauge1.create_arc(coord, start=30, extent=120, fill="white", width=2) 233 | gauge1desc = gauge1.create_text(100,120, text="RPM", font=("Helvetica", "16")) 234 | gauge1label = gauge1.create_text(100,80, text="", font=("Helvetica", "16")) 235 | gauge1needle = gauge1.create_arc(coord, start= 150, extent=1, width=7) 236 | 237 | gauge2 = Canvas(frame, width=200, height=175) 238 | gauge2.grid(row=1, column=2) 239 | gauge2.create_arc(coord, start=30, extent=120, fill="white", width=2) 240 | gauge2desc = gauge2.create_text(100,120, text="PSTEMP", font=("Helvetica", "16")) 241 | gauge2label = gauge2.create_text(100,80, text="", font=("Helvetica", "16")) 242 | gauge2needle = gauge2.create_arc(coord, start= 150, extent=1, width=7) 243 | 244 | gauge3 = Canvas(frame, width=200, height=175) 245 | gauge3.grid(row=1, column=3) 246 | gauge3.create_arc(coord, start=30, extent=120, fill="white", width=2) 247 | gauge3desc = gauge3.create_text(100,120, text="GAUGE3", font=("Helvetica", "16")) 248 | gauge3label = gauge3.create_text(100,80, text="", font=("Helvetica", "16")) 249 | gauge3needle = gauge3.create_arc(coord, start= 150, extent=1, width=7) 250 | 251 | gauge4 = Canvas(frame, width=200, height=175) 252 | gauge4.grid(row=1, column=4) 253 | gauge4.create_arc(coord, start=30, extent=120, fill="white", width=2) 254 | gauge4desc = gauge4.create_text(100,120, text="GAUGE4", font=("Helvetica", "16")) 255 | gauge4label = gauge4.create_text(100,80, text="", font=("Helvetica", "16")) 256 | gauge4needle = gauge4.create_arc(coord, start= 150, extent=1, width=7) 257 | 258 | gauge5 = Canvas(frame, width=200, height=175) 259 | gauge5.grid(row=2, column=1) 260 | gauge5.create_arc(coord, start=30, extent=120, fill="white", width=2) 261 | gauge5desc = gauge5.create_text(100,120, text="GAUGE5", font=("Helvetica", "16")) 262 | gauge5label = gauge5.create_text(100,80, text="", font=("Helvetica", "16")) 263 | gauge5needle = gauge5.create_arc(coord, start= 150, extent=1, width=7) 264 | 265 | gauge6 = Canvas(frame, width=200, height=175) 266 | gauge6.grid(row=2, column=2) 267 | gauge6.create_arc(coord, start=30, extent=120, fill="white", width=2) 268 | gauge6desc = gauge6.create_text(100,120, text="GAUGE6", font=("Helvetica", "16")) 269 | gauge6label = gauge6.create_text(100,80, text="", font=("Helvetica", "16")) 270 | gauge6needle = gauge6.create_arc(coord, start= 150, extent=1, width=7) 271 | 272 | gauge7 = Canvas(frame, width=200, height=175) 273 | gauge7.grid(row=2, column=3) 274 | gauge7.create_oval(fullcoord, fill="white", width=2) 275 | gauge7desc = gauge7.create_text(100,120, text="TILT", font=("Helvetica", "16")) 276 | gauge7label = gauge7.create_text(100,140, text="", font=("Helvetica", "16")) 277 | gauge7needle = gauge7.create_arc(fullcoord, start= 0, extent=180, width=7, fill="green") 278 | 279 | gauge8 = Canvas(frame, width=200, height=175) 280 | gauge8.grid(row=2, column=4) 281 | gauge8.create_oval(fullcoord, fill="white", width=2) 282 | gauge8desc = gauge8.create_text(100,120, text="ROLL", font=("Helvetica", "16")) 283 | gauge8label = gauge8.create_text(100,140, text="", font=("Helvetica", "16")) 284 | gauge8needle = gauge8.create_arc(fullcoord, start= 0, extent=180, width=7, fill="green") 285 | 286 | bus = can.interface.Bus('', bustype='socketcan',filter=[{"can_id": 0x2C2, "can_mask": 0xFFF},{"can_id": 0x322, "can_mask": 0xFFF}]) 287 | Notifier = can.Notifier(bus, [newmsg], loop=None) 288 | 289 | 290 | root.mainloop() 291 | bus.shutdown() 292 | if cam: 293 | cam.terminate() 294 | -------------------------------------------------------------------------------- /tkcan4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # GUI Can data display using tkinter 3 | 4 | from tkinter import * 5 | import time 6 | import can 7 | import subprocess 8 | import signal 9 | 10 | # If using vcan for log playback, change the values in the quotes below 11 | canIHS = "can0" 12 | canC = "can1" 13 | 14 | canFilter = list() 15 | 16 | battv = None 17 | rpm = None 18 | mph = None 19 | fsstate = True 20 | cam = None 21 | oldpstemp = None 22 | oldrpm = None 23 | oldtilt = None 24 | oldroll = None 25 | oldiat = None 26 | oldcoolant = None 27 | oldoiltemp = None 28 | oldoilpres = None 29 | 30 | # defined types to process the data. x = can message , a = byte 1 , b = byte 2 31 | def raw8(x,a): 32 | return(x[a]) 33 | 34 | def raw16(x,a,b): 35 | return((x[a]<<8) + x[b]) 36 | 37 | def volt(x,a): 38 | return(x[a] / 10) 39 | 40 | def temp(x,a): 41 | return(round((((x[a] - 40) * (9 / 5)) + 32))) 42 | 43 | def tilt(x,a,b): 44 | return(round(((x[a]<<8) + x[b] - 2048) / 10)) 45 | 46 | def rpm(x,a,b): 47 | if x[a] == 0xFF: 48 | return(0) 49 | return((x[a]<<8) + x[b]) 50 | 51 | def mph(x,a,b): 52 | return(round(((x[a]<<8) + x[b]) / 200,1)) 53 | 54 | def psi(x,a): 55 | return(round(((x[a] * 4) * 0.145038))) 56 | 57 | def gear(x,a): 58 | if x[a] == 0x4E: 59 | return('N') 60 | elif x[a] == 0x52: 61 | return('R') 62 | elif x[a] == 0x31: 63 | return('1') 64 | elif x[a] == 0x32: 65 | return('2') 66 | elif x[a] == 0x33: 67 | return('3') 68 | elif x[a] == 0x34: 69 | return('4') 70 | elif x[a] == 0x35: 71 | return('5') 72 | elif x[a] == 0x36: 73 | return('6') 74 | elif x[a] == 0x37: 75 | return('7') 76 | elif x[a] == 0x38: 77 | return('8') 78 | elif x[a] == 0x50: 79 | return('P') 80 | elif x[a] == 0x44: 81 | return('D') 82 | 83 | def xfer(x,a): 84 | if x[a] == 0x00: 85 | return('2H') 86 | elif x[a] == 0x02: 87 | return('N') 88 | elif x[a] == 0x10: 89 | return('4H') 90 | elif x[a] == 0x20: 91 | return('N') 92 | elif x[a] == 0x40: 93 | return('4L') 94 | elif x[a] == 0x80: 95 | return('XX') 96 | else: 97 | return('??') 98 | 99 | def steer(x,a,b): 100 | return(((x[a]<<8) + x[b]) - 0x1000) 101 | 102 | def pstemp(x,a): 103 | return(round(((x[a] * (9 / 5)) + 32))) 104 | 105 | 106 | # Display Functions 107 | def full(): 108 | print("full screen") 109 | global fsstate 110 | fsstate = not fsstate 111 | root.attributes("-fullscreen", fsstate) 112 | if fsstate == True: 113 | fullbutton.config(relief=SUNKEN, text="Small") 114 | fullbutton.pack() 115 | else: 116 | fullbutton.config(relief=RAISED, text="Full") 117 | fullbutton.pack() 118 | 119 | def newrpm(lrpm): 120 | global oldrpm 121 | low_r = 0 # chart low range 122 | hi_r = 7000 # chart hi range 123 | if lrpm == 65535: 124 | lrpm = 0 125 | if lrpm != oldrpm: 126 | oldrpm = lrpm 127 | 128 | def newmph(lmph): 129 | if str(lmph) != text1label["text"]: 130 | text1label["text"] = str(lmph) 131 | text1label.pack() 132 | 133 | def newbattv(lbattv): 134 | if str(lbattv) != text2label["text"]: 135 | text2label["text"] = str(lbattv) 136 | text2label.pack() 137 | 138 | def newgear(lgear): 139 | if str(lgear) != text3label["text"]: 140 | text3label["text"] = str(lgear) 141 | text3label.pack() 142 | 143 | def newxfer(lxfer): 144 | if str(lxfer) != text4label["text"]: 145 | text4label["text"] = str(lxfer) 146 | text4label.pack() 147 | 148 | def newpstemp(lpstemp): 149 | global oldpstemp 150 | low_r = 50 # chart low range 151 | hi_r = 250 # chart hi range 152 | if lpstemp != oldpstemp: 153 | text8label["text"] = str(lpstemp) 154 | pstempangle = (120 * (hi_r - lpstemp) / (hi_r - low_r) + 30) 155 | gauge2.itemconfig(gauge2needle,start = pstempangle) 156 | gauge2.grid() 157 | oldpstemp = lpstemp 158 | 159 | def newiat(liat): 160 | global oldiat 161 | low_r = 50 # chart low range 162 | hi_r = 250 # chart hi range 163 | if liat != oldiat: 164 | text9label["text"] = str(liat) 165 | iattempangle = (120 * (hi_r - liat) / (hi_r - low_r) + 30) 166 | gauge3.itemconfig(gauge3needle,start = iattempangle) 167 | gauge3.grid() 168 | oldiat = liat 169 | 170 | def newcoolant(lcoolant): 171 | global oldcoolant 172 | low_r = 100 # chart low range 173 | hi_r = 300 # chart hi range 174 | if str(lcoolant) != text7label["text"]: 175 | text7label["text"] = str(lcoolant) 176 | text7label.pack() 177 | coolanttempangle = (120 * (hi_r - lcoolant) / (hi_r - low_r) + 30) 178 | gauge1.itemconfig(gauge1needle,start = coolanttempangle) 179 | gauge1.grid() 180 | oldcoolant = lcoolant 181 | 182 | def newoiltemp(loiltemp): 183 | global oldoiltemp 184 | low_r = 100 # chart low range 185 | hi_r = 300 # chart hi range 186 | if loiltemp != oldoiltemp: 187 | text11label["text"] = str(loiltemp) 188 | oiltemptempangle = (120 * (hi_r - loiltemp) / (hi_r - low_r) + 30) 189 | gauge5.itemconfig(gauge5needle,start = oiltemptempangle) 190 | gauge5.grid() 191 | oldoiltemp = loiltemp 192 | 193 | def newoilpres(loilpres): 194 | global oldoilpres 195 | low_r = 0 # chart low range 196 | hi_r = 80 # chart hi range 197 | if loilpres != oldoilpres: 198 | text12label["text"] = str(loilpres) 199 | oilprestempangle = (120 * (hi_r - loilpres) / (hi_r - low_r) + 30) 200 | gauge6.itemconfig(gauge6needle,start = oilprestempangle) 201 | gauge6.grid() 202 | oldoilpres = loilpres 203 | 204 | def newtilt(ltilt): 205 | global oldtilt 206 | if ltilt != oldtilt: 207 | gauge7.itemconfig(gauge7label, text=str(ltilt)) 208 | gauge7.itemconfig(gauge7needle, start=ltilt) 209 | gauge7.grid() 210 | oldtilt = ltilt 211 | if ltilt > 15: 212 | gauge7.itemconfig(gauge7needle, fill="yellow") 213 | if ltilt > 25: 214 | gauge7.itemconfig(gauge7needle, fill="red") 215 | else: 216 | gauge7.itemconfig(gauge7needle, fill="green") 217 | 218 | def newroll(lroll): 219 | global oldroll 220 | if lroll != oldroll: 221 | gauge8.itemconfig(gauge8label, text=str(lroll)) 222 | gauge8.itemconfig(gauge8needle, start=lroll) 223 | gauge8.grid() 224 | oldroll = lroll 225 | if lroll > 15: 226 | gauge8.itemconfig(gauge8needle, fill="yellow") 227 | if lroll > 25: 228 | gauge8.itemconfig(gauge8needle, fill="red") 229 | else: 230 | gauge8.itemconfig(gauge8needle, fill="green") 231 | 232 | 233 | # list of can ID's and details to monitor in this order: 234 | # (ID, Channel, [("name", process, type, function, byte1, byte2)]) 235 | monitorlist=[(0x2C2, 236 | canIHS, 237 | [("Batt V",volt,newbattv,2)]), 238 | (0x02B, 239 | canC, 240 | [("Roll",tilt,newroll,0,1), 241 | ("Tilt",tilt,newtilt,2,3)]), 242 | (0x322, 243 | canIHS, 244 | [("RPM",rpm,newrpm,0,1), 245 | ("MPH",mph,newmph,2,3)]), 246 | (0x127, 247 | canC, 248 | [("IAT",temp,newiat,0), 249 | ("Coolant",temp,newcoolant,1)]), 250 | (0x13D, 251 | canC, 252 | [("Oil Temp",temp,newoiltemp,3), 253 | ("Oil Pres",psi,newoilpres,2)]), 254 | (0x093, 255 | canC, 256 | [("Gear",gear,newgear,2)]), 257 | (0x277, 258 | canC, 259 | [("Transfer",xfer,newxfer,0)]), 260 | (0x128, 261 | canC, 262 | [("PS Temp",pstemp,newpstemp,1)]) 263 | ] 264 | 265 | 266 | # Buttons 267 | def canwakeup(): 268 | wakeup = can.Message(data=[0x07, 0, 0, 0, 0, 0, 0, 0], is_extended_id=False, arbitration_id=0x2D3, channel=CanIHS) 269 | print(wakeup) 270 | bus.send(wakeup, timeout=1) 271 | 272 | def radioreboot(): 273 | radiorebootcmd = can.Message(data=[0x02, 0x11, 0x01, 0, 0, 0, 0, 0], is_extended_id=False, arbitration_id=0x7BF, channel=canIHS) 274 | bus.send(radiorebootcmd, timeout=1) 275 | 276 | def maxac(): 277 | maxaccmd = can.Message(data=[0x80, 0, 0, 0, 0, 0], is_extended_id=False, arbitration_id=0x342, channel=canIHS) 278 | bus.send(maxaccmd, timeout=1) 279 | 280 | def synchvac(): 281 | synchvaccmd = can.Message(data=[0, 0, 0, 0x04, 0], is_extended_id=False, arbitration_id=0x342, channel=canIHS) 282 | bus.send(synchvaccmd, timeout=1) 283 | 284 | def blankscreen(): 285 | subprocess.call(['xscreensaver-command', '-activate']) 286 | 287 | def callback(): 288 | topframe.quit() 289 | 290 | def camera(): 291 | global cam 292 | if cam: 293 | cam.terminate() 294 | cam = None 295 | frame.pack(side=TOP, fill="x") 296 | else: 297 | cam = subprocess.Popen(["raspivid", "-t", "0", "-v", "-w", "800", "-h", "480", "-op", "200", "-rot", "180"]) 298 | camstatus = cam.poll() 299 | if camstatus is None: 300 | frame.pack_forget() 301 | 302 | def button1(): 303 | bigbutton1 = Button( 304 | topframe, text="CAMERA", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=camera) 305 | bigbutton1.pack(side=LEFT) 306 | def button2(): 307 | bigbutton2 = Button( 308 | topframe, text="Wake Up", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=canwakeup) 309 | bigbutton2.pack(side=LEFT) 310 | def button3(): 311 | maxacbutton = Button( 312 | topframe, text="MAX AC", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=maxac) 313 | maxacbutton.pack(side=LEFT) 314 | def button4(): 315 | synchvacbutton = Button( 316 | topframe, text="Sync", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=synchvac) 317 | synchvacbutton.pack(side=LEFT) 318 | def button5(): 319 | quitbutton = Button( 320 | topframe, text="QUIT", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=topframe.quit) 321 | quitbutton.pack(side=LEFT) 322 | def button6(): 323 | screenoffbutton = Button( 324 | topframe, text="Screen OFF", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=blankscreen) 325 | screenoffbutton.pack(side=LEFT) 326 | def button7(): 327 | radiorebootbutton = Button( 328 | topframe, text="Reboot", fg="red", activeforeground="red", bg="black", activebackground="black", font=("Helvetica", "16"), height=2, width=7, command=radioreboot) 329 | radiorebootbutton.pack(side=LEFT) 330 | 331 | 332 | root = Tk() 333 | root.geometry("800x480+0+0") 334 | root.title("This is Root") 335 | root.protocol("WM_DELETE_WINDOW", callback) 336 | root.attributes("-fullscreen", fsstate) 337 | root.configure(bg='black') 338 | 339 | topframe=Frame(root) 340 | topframe.configure(bg='black') 341 | topframe.pack(side=BOTTOM, fill="x") 342 | button1() 343 | button2() 344 | button3() 345 | button4() 346 | button5() 347 | button6() 348 | 349 | textframe=Frame(root) 350 | textframe.pack(side=BOTTOM, fill="x") 351 | 352 | text1dsc = Label(textframe, text="MPH", font=("Helvetica", "16")) 353 | text1dsc.pack(side=LEFT) 354 | text1label = Label(textframe, font=("Helvetica", "16"), width=5) 355 | text1label.pack(side=LEFT) 356 | 357 | text2dsc = Label(textframe, text="Batt V", font=("Helvetica", "16")) 358 | text2dsc.pack(side=LEFT) 359 | text2label = Label(textframe, font=("Helvetica", "16"), width=5) 360 | text2label.pack(side=LEFT) 361 | 362 | text3dsc = Label(textframe, text="Gear", font=("Helvetica", "16")) 363 | text3dsc.pack(side=LEFT) 364 | text3label = Label(textframe, font=("Helvetica", "16"), width=5) 365 | text3label.pack(side=LEFT) 366 | 367 | text4dsc = Label(textframe, text="Xfer", font=("Helvetica", "16")) 368 | text4dsc.pack(side=LEFT) 369 | text4label = Label(textframe, font=("Helvetica", "16"), width=5) 370 | text4label.pack(side=LEFT) 371 | 372 | text5dsc = Label(textframe, text="", font=("Helvetica", "16")) 373 | text5dsc.pack(side=LEFT) 374 | text5label = Label(textframe, font=("Helvetica", "16"), width=5) 375 | text5label.pack(side=LEFT) 376 | 377 | text6dsc = Label(textframe, text="", font=("Helvetica", "16")) 378 | text6dsc.pack(side=LEFT) 379 | text6label = Label(textframe, font=("Helvetica", "16"), width=5) 380 | text6label.pack(side=LEFT) 381 | 382 | textframe2=Frame(root) 383 | textframe2.pack(side=BOTTOM, fill="x") 384 | 385 | text7dsc = Label(textframe2, text="CoolT", font=("Helvetica", "16")) 386 | text7dsc.pack(side=LEFT) 387 | text7label = Label(textframe2, font=("Helvetica", "16"), width=5) 388 | text7label.pack(side=LEFT) 389 | 390 | text8dsc = Label(textframe2, text="PsTemp", font=("Helvetica", "16")) 391 | text8dsc.pack(side=LEFT) 392 | text8label = Label(textframe2, font=("Helvetica", "16"), width=5) 393 | text8label.pack(side=LEFT) 394 | 395 | text9dsc = Label(textframe2, text="IAT", font=("Helvetica", "16")) 396 | text9dsc.pack(side=LEFT) 397 | text9label = Label(textframe2, font=("Helvetica", "16"), width=5) 398 | text9label.pack(side=LEFT) 399 | 400 | text10dsc = Label(textframe2, text="", font=("Helvetica", "16")) 401 | text10dsc.pack(side=LEFT) 402 | text10label = Label(textframe2, font=("Helvetica", "16"), width=5) 403 | text10label.pack(side=LEFT) 404 | 405 | text11dsc = Label(textframe2, text="Oil", font=("Helvetica", "16")) 406 | text11dsc.pack(side=LEFT) 407 | text11label = Label(textframe2, font=("Helvetica", "16"), width=5) 408 | text11label.pack(side=LEFT) 409 | 410 | text12dsc = Label(textframe2, text="OilPres", font=("Helvetica", "16")) 411 | text12dsc.pack(side=LEFT) 412 | text12label = Label(textframe2, font=("Helvetica", "16"), width=5) 413 | text12label.pack(side=LEFT) 414 | 415 | 416 | frame = Frame(root) 417 | frame.pack(side=TOP, fill="x") 418 | frame.configure(bg='black') 419 | 420 | coord = 0, 0, 200, 350 #define the size of the gaug 421 | fullcoord = 0, 0, 175, 175 422 | 423 | gauge1 = Canvas(frame, width=200, height=175) 424 | gauge1.grid(row=1, column=1) 425 | gauge1.create_arc(coord, start=30, extent=120, fill="white", width=2) 426 | gauge1desc = gauge1.create_text(100,120, text="CoolT", font=("Helvetica", "16")) 427 | gauge1needle = gauge1.create_arc(coord, start= 150, extent=1, width=7) 428 | 429 | gauge2 = Canvas(frame, width=200, height=175) 430 | gauge2.grid(row=1, column=2) 431 | gauge2.create_arc(coord, start=30, extent=120, fill="white", width=2) 432 | gauge2desc = gauge2.create_text(100,120, text="PSTEMP", font=("Helvetica", "16")) 433 | gauge2label = gauge2.create_text(100,80, text="", font=("Helvetica", "16")) 434 | gauge2needle = gauge2.create_arc(coord, start= 150, extent=1, width=7) 435 | 436 | gauge3 = Canvas(frame, width=200, height=175) 437 | gauge3.grid(row=1, column=3) 438 | gauge3.create_arc(coord, start=30, extent=120, fill="white", width=2) 439 | gauge3desc = gauge3.create_text(100,120, text="IAT", font=("Helvetica", "16")) 440 | gauge3label = gauge3.create_text(100,80, text="", font=("Helvetica", "16")) 441 | gauge3needle = gauge3.create_arc(coord, start= 150, extent=1, width=7) 442 | 443 | gauge4 = Canvas(frame, width=200, height=175) 444 | gauge4.grid(row=1, column=4) 445 | gauge4.create_arc(coord, start=30, extent=120, fill="white", width=2) 446 | gauge4desc = gauge4.create_text(100,120, text="", font=("Helvetica", "16")) 447 | gauge4needle = gauge4.create_arc(coord, start= 150, extent=1, width=7) 448 | 449 | gauge5 = Canvas(frame, width=200, height=175) 450 | gauge5.grid(row=2, column=1) 451 | gauge5.create_arc(coord, start=30, extent=120, fill="white", width=2) 452 | gauge5desc = gauge5.create_text(100,120, text="OilTemp", font=("Helvetica", "16")) 453 | gauge5needle = gauge5.create_arc(coord, start= 150, extent=1, width=7) 454 | 455 | gauge6 = Canvas(frame, width=200, height=175) 456 | gauge6.grid(row=2, column=2) 457 | gauge6.create_arc(coord, start=30, extent=120, fill="white", width=2) 458 | gauge6desc = gauge6.create_text(100,120, text="OilPres", font=("Helvetica", "16")) 459 | gauge6label = gauge6.create_text(100,80, text="", font=("Helvetica", "16")) 460 | gauge6needle = gauge6.create_arc(coord, start= 150, extent=1, width=7) 461 | 462 | gauge7 = Canvas(frame, width=200, height=175) 463 | gauge7.grid(row=2, column=3) 464 | gauge7.create_oval(fullcoord, fill="white", width=2) 465 | gauge7desc = gauge7.create_text(100,120, text="TILT", font=("Helvetica", "16")) 466 | gauge7label = gauge7.create_text(100,140, text="", font=("Helvetica", "16")) 467 | gauge7needle = gauge7.create_arc(fullcoord, start= 0, extent=180, width=7, fill="green") 468 | 469 | gauge8 = Canvas(frame, width=200, height=175) 470 | gauge8.grid(row=2, column=4) 471 | gauge8.create_oval(fullcoord, fill="white", width=2) 472 | gauge8desc = gauge8.create_text(100,120, text="ROLL", font=("Helvetica", "16")) 473 | gauge8label = gauge8.create_text(100,140, text="", font=("Helvetica", "16")) 474 | gauge8needle = gauge8.create_arc(fullcoord, start= 0, extent=180, width=7, fill="green") 475 | 476 | 477 | # cheat 478 | def wrapper(msg,name,func,output,*args): 479 | output(func(msg,*args)) 480 | 481 | 482 | # Process every single message received from the canbus 483 | def newmsg(msg): 484 | for monitor in monitorlist: 485 | if msg.arbitration_id == monitor[0] and msg.channel == monitor[1]: 486 | for detail in monitor[2]: 487 | wrapper((msg.data),*detail) 488 | 489 | # Build the can filter list 490 | for monitor in monitorlist: 491 | # build out the can bus filtering list. only receive messages that we care about. 492 | canFilter.append({"can_id": monitor[0], "can_mask": 0xFFF, "can_channel": monitor[1]}) 493 | 494 | 495 | # define the can bus 496 | bus = can.interface.Bus('', bustype='socketcan', filter=canFilter) 497 | Notifier = can.Notifier(bus, [newmsg], loop=None) 498 | 499 | 500 | root.mainloop() 501 | bus.shutdown() 502 | if cam: 503 | cam.terminate() 504 | 505 | 506 | root.mainloop() 507 | bus.shutdown() 508 | if cam: 509 | cam.terminate() 510 | --------------------------------------------------------------------------------