Display commute times on a Character LCD using Raspberry Pi

Display real-time estimate of commute time between two locations based on traffic and historic data on a Character LCD using Raspberry Pi, Python 3 and Google Maps Distance Matrix API. If you have a LCD and a Raspberry Pi lying around or want to purchase one or if you are often late for work like me and want to have a traffic monitor then this project might be for you.

LCD showing the commute times
Best Guessed and Pessimistic commute times

Hardware Requirements

  • Raspberry Pi
  • Character LCD
  • Jumper wires
  • Breadboard
  • Potentiometer (If your LCD supports contrast adjustment)

Wiring

Essential wiring for writing data to LCD in four bit mode

Raspberry PiLCD
GNDVSS
5V or 3.3V (Depends on LCD)VDD
GPIO 4RS
GNDR/W
GPIO 17E
GPIO 25DB4
GPIO 24DB5
GPIO 23DB6
GPIO 18DB7

Depending upon LCD, you may have backlight.

Raspberry PiLCD
5V or 3.3V (Depends on LCD)Backlight Anode (+ve)
GNDBacklight Cathode (-ve)

If your LCD supports changing of contrast, you can connect a potentiometer to adjust that.

Raspberry PiPotentiometerLCD
5V or 3.3V (Depends on LCD)+ve pin
GND-ve pin
Data PinContrast Pin (Usually 3rd pin on LCDs using Hitachi HD44780 LCD controller)

Software Requirements

We are going to use Python programming language to make an Google Maps API call and then display the results to LCD.

Installing the dependencies

Adafruit Python CharLCD library

Get the LCD library here and cd to the downloaded folder and run the following command to install it.

    sudo python3 setup.py install
  

Requests

Run the following command in terminal to download and install requests.

    sudo pip3 install requests
  

Google Maps Distance Matric API

Run the following command in terminal to download and install the API.

    sudo pip3 install -U googlemaps
  

Getting an API key to make API calls

Login to your google account and get the API key for your project. If you don't have an existing project, then you might have to create a new one here.

Python Code

Python 3 code for getting the commute time and displaying it on LCD

Importing the dependencies

    import time
    import Adafruit_CharLCD as LCD
    import requests
  

Variables for making API call

Update the api_key variable with the key you got before.

    api_key = 'YOUR_API_KEY'
    base_url = 'https://maps.googleapis.com/maps/api/distancematrix/json?'
  

Origin and destination addresses

    origin = 'Papakura,Auckland,New+Zealand'
    destination = 'Penrose,Auckland,New+Zealand'
  

Variables for driving the LCD

Corresponding Raspberry Pi GPIO pins for register select, instruction and data registers on LCD. We will use these variables later to initialize the LCD.

    lcd_rs = 4
    lcd_en = 17
    lcd_d4 = 25
    lcd_d5 = 24
    lcd_d6 = 23
    lcd_d7 = 18
  

Size of the LCD, If you have a character LCD of 16x2, then modify the lcd_columns variable to 16 and lcd_rows to 2. Since, I used 20x4 LCD, I will use the following values.

    lcd_columns = 20
    lcd_rows = 4
  

Initialize the LCD

Create a lcd object by calling the constructor method defined in the Adafruit LCD library and pass in the required parameters. If your LCD supports backlight, then you could pass in an additional parameter of backlight = 1 or backlight = 0 to turn it on or off.

    lcd = LCD.Adafruit_CharLCD(lcd_rs, lcd_en, lcd_d4, lcd_d5, lcd_d6, lcd_d7, lcd_columns, lcd_rows)
  

Making the API call

We are going to structure the URL and then use python library requests to make a get request to API endpoint. To get a valid response you will also need to include additional parameters namely origins, destinations, departure time, traffic_model and API key in your request.  Response message is then stored in variable and converted to JSON. In this example, I have passed in the result to display message. This method makes two requests - one to get best guessed commute time and other to get pessimistic time. Pessimistic time will be often more than the best guessed time.

    def get_data():
      try:
        # Make request to get best guess duration between specified two points
        r1 = requests.get(base_url+'origins={}&destinations={}&departure_time={}&traffic_model={}&key={}'.format(origin, destination, time_in_seconds_UTC(), 'best_guess', api_key))
        r1= r1.json()['rows'][0]['elements'][0] # Convert the response to JSON
        best_guess_duration = r1['duration_in_traffic']['text']
    
        # Make request to get worst case duration between specified two points
        r2 = requests.get(base_url+'origins={}&destinations={}&departure_time={}&traffic_model={}&key={}'.format(origin, destination, time_in_seconds_UTC(), 'pessimistic', api_key))
        r2 = r2.json()['rows'][0]['elements'][0] # Convert the response to JSON
        pessimistic_duration = r2['duration_in_traffic']['text']
    
        display('Estimated Drive Time', 'BG: ' + best_guess_duration, 'PSMT: ' + pessimistic_duration, human_readable_time()) # Pass the results to display function
      except requests.exceptions.RequestException as e:
        # Print time when exception happened and exception meyyssage
        print(time.strftime("%d-%m-%Y %H:%M:%S", time.localtime()))
        print(e)
        display('Error, check console', 'Trying again...') # Update LCD with error message
  

Also, I have wrapped the code in try and except block to catch if the request fails for whatever reason. If the exception happens, print function will print the time when it happened and reason in console and LCD will show Error, check console', 'Trying again...'. LCD will update with commute data again if the next request is successful.

The display function

Display function takes advantage of the message function provided by Adafruit LCD library. You can pass in upto four strings while calling it and it will display each string on a new line. lcd.clear() will clear any existing text on LCD and lcd.home() will bring the cursor to row 1 and column 1. For best results, you might want to limit the string length to maximum characters your LCD can display on each line. If you want to display multi-line message, then its best to use lcd.message()

    def display(line1 = '', line2 = '', line3 = '', line4 = ''):
      lcd.clear()
      lcd.home()
      lcd.message(line1 + '\n' + line2 + '\n' + line3 + '\n' + line4)
  

Get real time traffic data

So far, we have the code to get distance and duration but but we are not actually making a request. To get a response we have to call get_data() function and keep calling it every minute to get the updated duration based on traffic. I have wrapped this code in main() function

    def main():
      display('Loading...')
      get_data() # Gets and displays data on LCD
      time.sleep(60) # Wait for 1 minute
  

The following code will keep the program running once started and display Stopped on LCD when you stop it.

    # Keep the program running and update LCD every 1 minute
    while True:
      try:
        main()
      finally:
        display('Stopped')
  

Other helper functions

Helper functions are called in get_data() method.

    # This piece of code returns the time in seconds in UTC which is passed to maps API as a value for departure_time
    def time_in_seconds_UTC():
      return int(round(time.time()))

    #Following code returns a human readable date and time, this is printed on fourth line of LCD
    def human_readable_time():
        return time.strftime("%d %b %Y %I:%M %P", time.localtime())
  

Starting the app

Finally, we are at a point to start the program and get the real time traffic duration and distance on the LCD. You can get the source code here.

Download the zip file and extract it. Open up your command prompt or terminal and cd into the folder and run the following command.

    python3 app.py
  

That's it. Thanks for reading.