├── gpssync ├── uploadTrips.py ├── README.md ├── DatabasePI.sql ├── main.sh └── tripData.py /gpssync: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LuukEsselbrugge/Dashcam/HEAD/gpssync -------------------------------------------------------------------------------- /uploadTrips.py: -------------------------------------------------------------------------------- 1 | import time 2 | import mysql.connector 3 | import os 4 | import requests 5 | import json 6 | 7 | REMOTE_ADR = 'https://server.com/upload/addTrip' 8 | 9 | mydb = mysql.connector.connect( 10 | host="localhost", 11 | user="root", 12 | passwd="root", 13 | database="Dashcam" 14 | ) 15 | #Wait a bit in case Wifi is not connected yet 16 | time.sleep(15) 17 | #Upload Trip(s) 18 | cursor = mydb.cursor() 19 | cursor.execute("SELECT * FROM Trip", ()) 20 | rows = cursor.fetchall() 21 | 22 | for row in rows: 23 | print (row[0]) 24 | 25 | try: 26 | cursor.execute("SELECT * FROM TripData WHERE TripID=%s", (row[0],)) 27 | TripData = cursor.fetchall() 28 | 29 | multipart_form_data = { 30 | 'video': (row[0]+'.mp4', open('recording/'+row[0]+'.mp4', 'rb')), 31 | } 32 | 33 | response = requests.post(REMOTE_ADR,files=multipart_form_data,data={'Trip': json.dumps(row, indent=4, sort_keys=True, default=str),'TripData': json.dumps(TripData, indent=4, sort_keys=True, default=str)}) 34 | 35 | print(response.status_code) 36 | 37 | if(response.status_code == 200): 38 | print("Trip data and video upload successful!, Deleting local content") 39 | os.system("rm recording/"+str(row[0])+".mp4") 40 | mydb.cursor().execute("DELETE FROM Trip WHERE TripID=%s", (str(row[0]),)) 41 | mydb.cursor().execute("DELETE FROM TripData WHERE TripID=%s", (str(row[0]),)) 42 | mydb.commit() 43 | except Exception: 44 | print("Could not upload this trip file might be missing") 45 | 46 | mydb.commit() 47 | 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Automated OBDII car Dashcam system 2 | 3 | ---- 4 | ## What is Dashcam? 5 | A fully automated Dashcam system written for the Raspberry pi in bash and Python3. It will record trips and upload these to a server when Wifi is available 6 | 7 | ![Preview](https://i.imgur.com/E8bVDzm.jpg) 8 | [MoreImages](https://imgur.com/a/HetpOQ9) 9 | 10 | **Work in progress** 11 | 12 | ## Software 13 | **Python3** 14 | 15 | * mysql connector 16 | * json 17 | * requests 18 | * pySerial 19 | * pynmea2 20 | 21 | **Linux packages (Raspberry pi)** 22 | 23 | * rfcomm 24 | * mysqladmin 25 | * WiringPi 26 | * gpsdate (Pre compiled ARM binary included) [Link](https://github.com/adamheinrich/gpsdate) 27 | * v4l-utils (For disabling autofocus on Logitech C920, optional) 28 | * ffmpeg 29 | * mysql server 30 | 31 | For automatic launch on boot **crontab -e** 32 | 33 | # m h dom mon dow command 34 | @reboot sudo screen -dm -S cd /home/pi/ && sudo bash main.sh 35 | 36 | **Bluetooth Pairing** 37 | 38 | Pair your OBDII device using bluetoothctl before launch 39 | 40 | **Database** 41 | 42 | * Create a new database called Dashcam 43 | * Import the included sql file into Dashcam 44 | 45 | **Extra** 46 | Create a folder called "recording" in the project root 47 | 48 | ## Hardware 49 | **Components** 50 | 51 | * Raspberry PI 52 | * Logitech C920 53 | * NEO-6M GPS module (Serial) 54 | * Relay 55 | * ~12V to 5V converter 56 | * ELM327 OBDII Bluetooth adapter 57 | 58 | **Connections** 59 | 60 | * GPS serial TX/RX to Raspberry pi UART RX/TX 61 | * Camera to any USB port 62 | * Relay connected to auxiliary power of car (on when key in aux position) to Raspberry pi GPIO pin 9 (reset) and ground 63 | 64 | I experienced issues when using the onboard bluetooth of the Raspberry pi 3 (Kernel panics) I could not figure this issue out so for now I use a cheap Bluetooth USB dongle 65 | 66 | -------------------------------------------------------------------------------- /DatabasePI.sql: -------------------------------------------------------------------------------- 1 | -- phpMyAdmin SQL Dump 2 | -- version 4.8.4 3 | -- https://www.phpmyadmin.net/ 4 | -- 5 | -- Host: localhost 6 | -- Generation Time: Jan 18, 2019 at 09:40 PM 7 | -- Server version: 5.7.24-0ubuntu0.18.10.1 8 | -- PHP Version: 7.2.10-0ubuntu1 9 | 10 | SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; 11 | SET AUTOCOMMIT = 0; 12 | START TRANSACTION; 13 | SET time_zone = "+00:00"; 14 | 15 | 16 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 17 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 18 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 19 | /*!40101 SET NAMES utf8mb4 */; 20 | 21 | -- 22 | -- Database: `Dashcam` 23 | -- 24 | 25 | -- -------------------------------------------------------- 26 | 27 | -- 28 | -- Table structure for table `Trip` 29 | -- 30 | 31 | CREATE TABLE `Trip` ( 32 | `TripID` varchar(128) NOT NULL, 33 | `Date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 34 | `Distance` double NOT NULL DEFAULT '0' 35 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 36 | 37 | -- -------------------------------------------------------- 38 | 39 | -- 40 | -- Table structure for table `TripData` 41 | -- 42 | 43 | CREATE TABLE `TripData` ( 44 | `ID` varchar(8) NOT NULL, 45 | `TripID` varchar(128) NOT NULL, 46 | `Timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, 47 | `RPM` int(5) NOT NULL, 48 | `Speed` int(5) NOT NULL, 49 | `Throttle` int(5) NOT NULL, 50 | `CTemp` int(5) NOT NULL, 51 | `ATemp` int(5) NOT NULL, 52 | `Lon` double NOT NULL, 53 | `Lat` double NOT NULL 54 | ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 55 | 56 | -- 57 | -- Indexes for dumped tables 58 | -- 59 | 60 | -- 61 | -- Indexes for table `Trip` 62 | -- 63 | ALTER TABLE `Trip` 64 | ADD PRIMARY KEY (`TripID`); 65 | 66 | -- 67 | -- Indexes for table `TripData` 68 | -- 69 | ALTER TABLE `TripData` 70 | ADD PRIMARY KEY (`ID`); 71 | COMMIT; 72 | 73 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 74 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 75 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 76 | -------------------------------------------------------------------------------- /main.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #MAC adress of OBDII device 3 | rfcomm bind 0 00:1D:A5:68:98:8C 4 | v4l2-ctl -c focus_auto=0 5 | ./gpssync -d 5 /dev/ttyAMA0 6 | sleep 3 7 | #Wait for mysql to start 8 | while ! mysqladmin ping -u root -proot --silent; do 9 | sleep 1 10 | done 11 | ######Handle issue where with this car (Peugeot 206 1.6 2002) auxiliary power (Where reset relay is connected) gets turned on for a split second when unlocking car, which resulted in early boot and instant shutdown 12 | gpio mode 9 in 13 | contact=$(gpio read 9) 14 | conSec=("0") 15 | while [ $contact = "1" ] 16 | do 17 | if [ "$conSecUit" = "300" ]; then #If auxiliary power not on in 5 minutes, turn off car 18 | shutdown -h now 19 | exit 1 20 | fi 21 | ((conSecUit++)) 22 | contact=$(gpio read 9) 23 | sleep 1 24 | done 25 | ###### 26 | while true 27 | do 28 | TripID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1) 29 | 30 | gpio mode 9 in 31 | gpio mode 8 out 32 | gpio write 8 1 33 | contact=$(gpio read 9) 34 | conSecUit=("0") 35 | 36 | #Start OBDII recording 37 | python3 tripData.py $TripID & 38 | OBD_PID=$! 39 | #Start Camera recording 40 | ffmpeg -vsync 1 -async 1 -ar 44100 -ac 2 -f alsa -thread_queue_size 1024 -i hw:1,0 -f v4l2 -codec:v h264 -framerate 30 -video_size 1920x1080 -thread_queue_size 1024 -itsoffset 1 -i /dev/video0 -copyinkf -codec:v copy -codec:a aac -ab 128k -g 10 /recording/$TripID.mp4 & 41 | CAMERA_ID=$! 42 | 43 | #Grace period for Aux power off (In case of stall and need for restart of engine) 44 | while [ "$conSecUit" -le "10" ] 45 | do 46 | contact=$(gpio read 9) 47 | if [ $contact = "1" ]; then 48 | ((conSecUit++)) 49 | echo "Contact is uit voor: $conSecUit" 50 | else 51 | conSecUit=("0") 52 | fi 53 | sleep 1 54 | done 55 | 56 | echo "Aux power off, Upload process starten" 57 | #Aux power is off stop OBDII record & Camera record 58 | kill $OBD_PID 59 | pkill ffmpeg 60 | 61 | #Start uploading all local trips to remote server 62 | python3 uploadTrips.py 63 | #Check if Aux power is still off (In case it came back on while we were uploading) 64 | if [ $(gpio read 9) ]; then 65 | echo "Aux power still off, shutdown started" 66 | shutdown -h now 67 | exit 1 68 | fi 69 | done 70 | -------------------------------------------------------------------------------- /tripData.py: -------------------------------------------------------------------------------- 1 | 2 | import serial 3 | import io 4 | import time 5 | import re 6 | import sys 7 | import mysql.connector 8 | import pynmea2 9 | import threading 10 | import string 11 | import random 12 | import os 13 | 14 | mydb = mysql.connector.connect( 15 | host="localhost", 16 | user="root", 17 | passwd="root", 18 | database="Dashcam" 19 | ) 20 | 21 | TripID = "" 22 | RPM = 0 23 | KMH = 0 24 | CTemp = 0 25 | ATemp = 0 26 | TPos = 0 27 | 28 | Lon = 0.0 29 | Lat = 0.0 30 | 31 | done = 1 32 | gpsdone = 1 33 | 34 | TripID = str(sys.argv[1]) 35 | print("TripID: "+TripID) 36 | 37 | s = 0 38 | 39 | #Add Trip to database 40 | mydb.cursor().execute("INSERT INTO Trip (TripID, Date) VALUES (%s,CURRENT_TIMESTAMP)", (TripID,)) 41 | mydb.commit() 42 | 43 | def _readline(s): 44 | eol = b'\r' 45 | leneol = len(eol) 46 | line = bytearray() 47 | while True: 48 | c = s.read(1) 49 | if c: 50 | line += c 51 | if line[-leneol:] == eol: 52 | break 53 | else: 54 | break 55 | return bytes(line) 56 | 57 | 58 | def updateGPS(GPSser): 59 | global Lat, Lon, gpsdone 60 | try: 61 | raw = GPSser.read(1200) 62 | result = re.search("GPGGA(.*)\r",raw.decode() ) 63 | msg = pynmea2.parse("$GPGGA"+result.group(1) ) 64 | if msg.latitude != 0.0 and msg.longitude != 0.0: 65 | Lon = msg.latitude 66 | Lat = msg.longitude 67 | except Exception as ex: 68 | print("Could not update GPS Loc") 69 | gpsdone = 1 70 | return 71 | 72 | def updateOBD(): 73 | global RPM, KMH, CTemp, ATemp, done, s, TPos 74 | try: 75 | r = sendCommand('0D') 76 | if "error" not in r: 77 | KMH = int(r[0],16) 78 | 79 | r = sendCommand('0C') 80 | if "error" not in r: 81 | RPM = ( 256 * int(r[0],16) + int(r[1],16) ) / 4 82 | 83 | r = sendCommand('05') 84 | if "error" not in r: 85 | CTemp = int(r[0],16) - 40 86 | 87 | r = sendCommand('0F') 88 | if "error" not in r: 89 | ATemp = int(r[0],16) - 40 90 | 91 | r = sendCommand('11') 92 | if "error" not in r: 93 | TPos = (100 / 255) * int(r[0],16) 94 | except Exception as ex: 95 | print("Could not connect to OBDII retrying") 96 | print(ex) 97 | done = 1 98 | return 99 | 100 | 101 | errorCount = 0 102 | def sendCommand( str ): 103 | global s, errorCount 104 | pid = str 105 | str = '01 ' + str + '1' 106 | s.write(str.encode()+'\r\n'.encode()) 107 | s.flushInput() 108 | res = _readline(s) 109 | result = re.search('41 ' + pid+' (.*) ', res.decode()) 110 | s.reset_input_buffer() 111 | try: 112 | out = result.group(1).split(' ') 113 | return out 114 | except AttributeError: 115 | #print("") 116 | return ["00","00","00","error"] 117 | 118 | 119 | while 1: 120 | try: 121 | s = serial.Serial('/dev/rfcomm0', 115200, timeout=1) 122 | GPSser = serial.Serial('/dev/ttyAMA0', 9600,timeout=0.1) 123 | s.write('ATZ\r\n'.encode()) 124 | time.sleep(2) 125 | s.write('ATE0\r\n'.encode()) 126 | s.flushInput() 127 | while 1: 128 | 129 | if done == 1: 130 | o = threading.Thread(target=updateOBD, args=()) 131 | o.start() 132 | done = 0 133 | if gpsdone == 1: 134 | t = threading.Thread(target=updateGPS, args=(GPSser,)) 135 | t.start() 136 | gpsdone = 0 137 | 138 | time.sleep(1) 139 | #print ("RPM="+str(RPM)+" KM/H="+str(KMH)+" Coolant temp="+str(CTemp)+" Air temp="+str(ATemp)+" Throttle="+str(TPos)+" GPS="+str(Lon)) 140 | #Add TripLog to database 141 | ID = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(8)) 142 | mydb.cursor().execute("INSERT INTO TripData (ID, TripID, RPM, Speed, Throttle, CTemp, ATemp, Lon, Lat) VALUES (%s, %s,%s,%s,%s,%s,%s,%s,%s)", (ID, TripID, int(RPM), int(KMH), int(TPos), int(CTemp), int(ATemp), Lon, Lat)) 143 | mydb.commit() 144 | 145 | except Exception as ex: 146 | print("something wong") 147 | print(ex) 148 | --------------------------------------------------------------------------------