So there is another interesting project in the make.Maybe you checked out my Raspberry cyOBD-II project earlier.
I built a OBD-II KW1281-protocol based car computer which delivers a lot of data, produced by the engine control unit. After solving some issues it seemed that the Raspberry is not able to hold timings when the engine was running.
I got so mad that I decided to rip the whole Raspberry construction out of my car and went over to my motorcycle, which hasn’t even got an analog clock in it.
- 1x Raspberry Pi B 512MB RAM
- 1x Ducati ECU
- 1x HD44780 display from OBD project
- 2x DS18B20 w1-temperature sensors
- 1x WinTec GPS-Receiver
- 1x ALFA AWUS036NH 2W WLAN
The whole project can be summarized in:Build a motorcycle computer which delivers useful and useless information to the driver.
Also you can get a GPS-calculated speed, trip length/time, georeferenced statistics, odometer, temperature statistics, gear number, oil pressure, oil temperature.
The bonus on this is the Raspberry Scan-Bot which I also built earlier – so I can scan while I’m driving and even see the results in my display. For all of you who contacted me: I will distribute the source files in this article, so you can build your own scan bot.
This will be an evolution and fusion of my earlier projects, hopefully it will merge to something better.
First things first: HD44780 Soldering
After I accidently decided to rip the wires off my HD44780 panel, I had to re-solder them.
This was quite a mess, but nothing special.
Check out these wire graphics, so you can build it on your own with ease!
Temperature Sensors DS18B20
These little guys are awesome. All you need is 1 wire, 3 phases and a 4.7kOhm resistor.
It’s possible to connect them all in a row. In case that they are digital, they address theirselves on the bus.
These sensors are incredibly cheap at 1.30 EUR each – what a decent deal!
Okay this could be a little bit embarrassing for me, I’m not very experienced at soldering.
I can even see some of you facepalming right now. The whole (temporarily) construction looks like this:
Software / Data
Raspberry PiOn my Raspberry Pi I’m using Debian or ArchLinux. Both of them are capable of the drivers we’re using and got the required libraries in kernel. We just have to load them and start using our peripheral devices without recompiling the whole kernel.
Controlling the HD44780 Display via PythonPython is a wonderful programming language which allows you to control this display with ease.
To get something on your display, you can try this code.
The cool thing about this script is, that it should be multi-purpose with other types of this display.
In my case I’m using a 20×2 display, but you can set your own rows and columns in code.
#!/usr/bin/python import RPi.GPIO as GPIO import os # Basic HD44780 python script # Definition of GPIO pins (customize if necessary) DISPLAY_RS = 7 DISPLAY_E = 8 DISPLAY_DATA4 = 25 DISPLAY_DATA5 = 24 DISPLAY_DATA6 = 23 DISPLAY_DATA7 = 18 GPIO.setwarnings(False)
Temperature Sensor GPIOIf your display works, you may now ask yourself how to access the DS18B20 sensor.
This is quite simple.
No matter if you got ArchLinux or Debian (Raspbian):
sudo modprobe w1-gpio sudo modprobe w1-thermThis code enables your w1-bus driver.
I wrote a little init.d script under Raspbian which loads it at startup.
To get some measuring data, you have to do the following:
cd /sys/bus/w1/devices/If everything is wired correctly you should see a folder named 28-000*** and a w1_master_bus folder.
If you don’t see, you probably forgot to enable drivers, miss-wired your sensor or you screwed it completely.
In my case the folder is named 28-000003adbf29. If you navigate into it, you will see a bunch of files.
To get a temperature, you need to read the w1_slave file.
cat /sys/bus/w1/devices/28-000003adbf29/w1_slaveThe output should be the following:
[root@cyOBD-II DUC]$ cat /sys/bus/w1/devices/28-000003adbf29/w1_slave A49 01 4b 46 7f ff 07 10 f6 : crc=f6 YES 49 01 4b 46 7f ff 07 10 f6 t=20562Okay, we have to transform it to get the temperature.
The real temperature is behind “t=20562“. You have to divide it with 1.000 and got your degree in celsius.
In this scenario it’s 20.56°C in my room. If you want you can touch it and see the temps raise.
I’m wondering how this is made in countries with imperial system – other sensors?
This script should return the actual temperature instead of the other information.
// In PHP $query = shell_exec("cat /sys/bus/w1/devices/28-000003adbf29/w1_slave"); $query = substr($query,69,0); $temp = round($query / 1000,1);
# In Python temp = os.popen("cat /sys/bus/w1/devices/28-000003adbf29/w1_slave").read() # cut the first 69 chars temp = temp[69:] temp = decimal.Decimal(temp) temp = temp/1000 temp = round(temp,1)Now our temperature should be read out.
Let’s see what we got next!
Display current temperature on HD44780This step is straight forward right now.
You can use the python script above to display it really easy.
The code above should work for you. It loops every second.
You can’t speed up it, the sensor will only deliver each second – trust me.
def main(): GPIO.setmode(GPIO.BCM) GPIO.setup(DISPLAY_E, GPIO.OUT) GPIO.setup(DISPLAY_RS, GPIO.OUT) GPIO.setup(DISPLAY_DATA4, GPIO.OUT) GPIO.setup(DISPLAY_DATA5, GPIO.OUT) GPIO.setup(DISPLAY_DATA6, GPIO.OUT) GPIO.setup(DISPLAY_DATA7, GPIO.OUT) display_init() # Boot Screen lcd_byte(LCD_LINE_1, DISPLAY_CMD) lcd_string(" cyOBD-II") lcd_byte(LCD_LINE_2, DISPLAY_CMD) lcd_string(" DUCATI Edition") lcd_byte(LCD_LINE_3, DISPLAY_CMD) lcd_string(" (c) cyantific.de") time.sleep(3) while(1): temp = os.popen("cat /sys/bus/w1/devices/28-000003adbf29/w1_slave").read() temp= temp[69:] temp = decimal.Decimal(temp) temp = temp/1000 temp = round(temp,1) lcd_byte(LCD_LINE_1, DISPLAY_CMD) lcd_string("Air "XX.X* Env " + str(temp) +"*") time.sleep(1)If everything works fine you should get this result.
Please don’t mind that the display is a little bit buggy at this time.
If you don’t solder it well, it could cause a data line on GND.
Uh Oh, Data of Ducati ECU?
Okay there it is, the Ducati engine control unit. Unfortunately there is nothing special on/in it to get data of.
I think it only manages engine specific controlling – nothing special.
The good thing about this is, that I won’t get into trouble with timings – like in my car.
So I let the ECU do what it is supposed to do.
Enhancing DisplayTo display one single line is very boring, so I decided to add a few more parameters on display.
I added Trip length, which basically filters out the uptime response from Linux.
Also there is now a current kph, average kph, max kph and odometer function, which I hope I can calculate via the GPS module later.
Enhancing codeNow the display is prepared to display a few more parameters.
Let’s see how I got the trip time calculated.
The Raspberry will be powered by the Ducati itself, so why not check the uptime and filter the response.
uptime = os.popen("uptime").read() uptime = uptime[13:15]This piece of code basically starts after the 13th character of the response and stops reading it after the 15th.
Okay, this is kind of stupid because I’m only regarding the minutes, yet – but I will correct this piece of code later on.
To display it, simply do this:
lcd_byte(LCD_LINE_2, DISPLAY_CMD) lcd_string("Oil XX.X* Trip " + str(uptime) +"m")The whole display-part of my python script looks like this:
lcd_byte(LCD_LINE_1, DISPLAY_CMD) lcd_string("Air XX.X* Env " + str(temp1) +"*") lcd_byte(LCD_LINE_2, DISPLAY_CMD) lcd_string("Oil XX.X* Trip " + str(uptime) +"m") lcd_byte(LCD_LINE_3, DISPLAY_CMD) lcd_string("kph XX.X -> 123km") lcd_byte(LCD_LINE_4, DISPLAY_CMD) lcd_string("Max XX.X AVG 86kmh") time.sleep(0.2)
Attaching GPS to Raspberry
This should not be a hard thing in regular – but I learned in the past that the Raspberrys USB serial drivers are buggy.
Connect your receiver and check out what kind of USB bridge you have (most receivers are serial devices)
Before we do anything we have to install the GPS-daemon called gpsd and some cool dependencies.
pacman -S gpsd gpsd-clients python-gpsNow we check if our receiver is recognized:
[root@alarmpi ~]$ lsusb Bus 001 Device 004: ID 10c4:ea60 Cygnal Integrated Products, Inc. CP210x UART BridgeIt appears that my USB GPS-Receiver got a CP210x UART bridge – in my case it isn’t working!
If you think you are lucky:
Connect the GPS receiver to the Raspberry and reboot it, you should not get something like this:
If you got this – this is how you fix this mess!
Turn off your Raspberry and disconnect the GPS receiver.
Turn it back on and connect via ssh or whatever you did before to access your Rasp.
Now type in
nano /boot/cmdline.txtand type in at the very end of the line the following:
dwc_otg.speed=1Now hit CTRL+X and Y to save.
Turn off your Raspberry – now you got USB 1.1 mode enabled.
If you believe that you are over it – forget it.
I tried a lot of commands and this seems to work for me.
Basically we are enabling our gpsd and connect it to our serial port which is our GPS receiver.
The important “-n” parameter means, that the GPS device doesn’t wait for an application to activate.
[root@alarmpi ~]$ gpsd -n /dev/ttyUSB0 -F /var/run/gpsd.sock [root@alarmpi ~]$ cgps
If everything is set up well, the screen output should be something like this:
Time via GPS and PythonThe Raspberry lacks of a realtime clock, so it’s not possible to save time/date settings.
One of the awesome features of a GPS receiver is, that it can get the current date and time over the air.
There are several methods to get your date right, but in this case I will use a very handy Python script.
You can grab it here: http://code.google.com/p/gpstime/
The script will connect to your GPS receiver and sets the time correctly.
If you are using ArchLinux, you have to keep in mind to remove the “sudo” of gpstime.py.
os.system('sudo date --set="%s"' % gpstime)to
os.system('date --set="%s"' % gpstime)You can also install sudo with pacman, just in case.
To set your date, simply do this:
[root@alarmpi gpstime]$ python2 gpstime.py Attempting to access GPS time... Setting system time to GPS time... Mon Jun 3 15:09:59 BST 2013 System time set.
GPS and PythonOne cool thing about python is the built-in GPS support.
For using a bunch of helpful functions, you add the following library in your program:
from gps import *Now we can access our GPS device directly in python!
Let’s do a little test.
print ' GPS reading' print '----------------------------------------' print 'latitude ' , gpsd.fix.latitude print 'longitude ' , gpsd.fix.longitude print 'time utc ' , gpsd.utc,' + ', gpsd.fix.time print 'altitude (m)' , gpsd.fix.altitude print 'eps ' , gpsd.fix.eps print 'epx ' , gpsd.fix.epx print 'epv ' , gpsd.fix.epv print 'ept ' , gpsd.fix.ept print 'speed (m/s) ' , gpsd.fix.speed print 'climb ' , gpsd.fix.climb print 'track ' , gpsd.fix.track print 'mode ' , gpsd.fix.mode print print 'sats ' , gpsd.satellitesIf everything works fine, you should get information about your position. You can guess what you can do now. Just keep it in mind for display purposes later on.
GPS Distance and HaversineMaybe you remember that I wanted to add an odometer function to get my mileage on trip.
You can calculate the distance between latitudes and longitudes with the haversine formula.
I just set up a formula for you to test it quickly.
def haversine(lon1, lat1, lon2, lat2): """ Calculate the great circle distance between two points on the earth (specified in decimal degrees) """ # convert decimal degrees to radians lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2]) # haversine formula dlon = lon2 - lon1 dlat = lat2 - lat1 a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2 c = 2*atan2(sqrt(a),sqrt(1-a)) # 6367 is avg radius of earth in km km = 6367 * c return km; # Add this to your main! def main(): # initial reading of coordinates for start point start_lat = gpsd.fix.latitude start_lon = gpsd.fix.longitude time.sleep(5) # if you moved your position, this is the new one now_lat=gpsd.fix.latitude now_lon=gpsd.fix.longitude print haversine(start_lon, start_lat, now_lon, now_lat)
Congratulations, you just measured your distance between 2 points.
The overall mileage is a trivial one-liner:
# Place it into a while-loop for refreshing distance_overall+=haversine(start_lon,start_lat,gpsd.fix.longitude,gpsd.fix.latitude)
Maybe you will notice that you are constantly moving, because the distance will raise incredibly fast.
In my case this is caused by the inaccuracy of my GPS.
It logs constantly different values, so I recommend to add some kind of threshold to it.
You may add some kind of this code at the end of our haversine function:
if km >=0.05: return km else: return 0
If the distance between 2 points is below than 0.04 (or 40 meters), it won’t return the new value.
The value “0” is added to my overall mileage.
If you are still experiencing movement, you can adjust it higher.
I don’t want to log each meter I moved, so I decided to set it relatively high for 10 meters.
GPS related Data on DisplayIf you can remember, you read out GPS data in section GPS and Python.
Why not adding a few of cool features?
In your main loop you can add this for example:
# Main or function, what you prefer altitude = gpsd.fix.altitude satellites = len(gpsd.satellites) act_speed = gpsd.fix.speed # variables without values are NaN # Value is in meters per second # Converting m/s into kph: if math.isnan(act_speed)==1: speed="---" last_speed="---" else: act_speed=act_speed*3.6 act_speed = round(act_speed,1) # Max Speed if act_speed>last_speed: last_speed=act_speed # Altitude if math.isnan(gpsd.fix.altitude)==1: altitude="!!!" lcd_byte(LCD_LINE_1, DISPLAY_CMD) lcd_string("Air "+str(temp2)+"* Env " + str(temp1) +"*") lcd_byte(LCD_LINE_2, DISPLAY_CMD) lcd_string("Oil XX.X* Trp "+str(uptime)) lcd_byte(LCD_LINE_3, DISPLAY_CMD) lcd_string("kph "+str(act_speed)+" -> "+str(distance_overall)+"km") lcd_byte(LCD_LINE_4, DISPLAY_CMD) lcd_string("Max "+str(last_speed)+" AVG XXXkph") time.sleep(0.5)
You see I played a little bit and made another page on the display which rotates every 5 seconds.
I added the scan bot feature not yet, but the display is now capable to display the data.
You can guess that WPA, WEP and OPN and ALL are wireless related!
You may notice that my GPS receiver thinks I’m moving.
I have to adjust the threshold on haversine again and get some threshold on my speed.
I’m definately not moving with ~4 km/h.
Adding DS18B20 sensorThis is a short one: I added another DS18B20 sensor for oil temperature.
My overall temperature reading is the following code:
temp1 = os.popen("cat /sys/bus/w1/devices/28-000003ad9bc0/w1_slave").read() temp1= temp1[69:] temp1 = decimal.Decimal(temp1) temp1 = temp1/1000 temp1 = round(temp1,1) temp2 = os.popen("cat /sys/bus/w1/devices/28-000003ada84f/w1_slave").read() temp2= temp2[69:] temp2 = decimal.Decimal(temp2) temp2 = temp2/1000 temp2 = round(temp2,1) temp3 = os.popen("cat /sys/bus/w1/devices/28-000003ad927d/w1_slave").read() temp3= temp3[69:] temp3 = decimal.Decimal(temp3) temp3 = temp3/1000 temp3 = round(temp3,1)
I know, AVG speed is mispositioned, but anyway:
What I will do nextOkay at this point I don’t know what to write anymore.
I now got 3 temperature sensors on my desk and solving some issues.
The altitude is ridiculously high.
The speed is inaccurate (bouncing from 0.0 to 9.0 and higher).
The sensors need to be soldered on the target wire.
I need to measure the distance between air-intake and seat (cable work).
*This is a reminder for me to remember that I have to add autostart events if everything is installed correctly.
Coming soon …