© Joseph Coburn 2020
J. CoburnBuild Your Own Car Dashboard with a Raspberry Pihttps://doi.org/10.1007/978-1-4842-6080-7_14

14. System Polishing

Chapter goal: Use HTML and CSS to make the dashboard usable and pretty. Fine-tune the system and shape it into a working dashboard.
Joseph Coburn1 
(1)
Alford, UK
 

Up to this point, you’ve been working mainly with Python and Bash. You’ve been writing sensor components and building circuits, with little thought to the style or usability. You’ve been following my mantra of “make it work, then make it pretty.” Well now the time has come to make it pretty. This is one chapter where you have a lot of flexibility. Want to make it bright pink? Fill the screen with cat photos, or a sentient AI able to perceive your every move? That’s all possible, and totally up to your creativity. Don’t worry if you’re not creative – I’ll show you how to make a minimalistic dashboard, which you can use as a basis to expand on into something unique.

For this chapter, you’ll use several libraries to help make this dashboard pretty. You’ll use Bootstrap (https://getbootstrap.com/) for most of the styling and layout. You’ll also use Font Awesome (https://fontawesome.com/) for some fantastic (and open source) icons. Font Awesome has a paid-for, Pro service, but the free tier is more than good enough for this project.

For most websites, a content delivery network (CDN) lets you quickly and easily pull in these external assets. CDNs greatly speed up the user experience on any website, and free public CDNs exist to host the required resources for you. However, because the Pi is going in your car, it may not always have (nor should it need to rely on) an Internet connection. This makes public CDNs a no-go. I’ll show you how to download these resources and serve them up locally from the Pi.

Public-facing websites also need to cater to a variety of users – from very old browsers, small screens, and Internet connections to browsers built-into games consoles, ultrafast modern computers, or even ancient systems such as OS/2. On the Internet, you get it all, and it’s your job as a developer to ensure that most visitors can see your website as you intended. Fortunately, this dashboard is only for you to use. The Pi has a known web browser, with a fixed-size screen. Every time you visit the page, the Pi renders the content in the same way. This makes development and testing slightly easier. At the expense of cutting corners, you can be happy enough if it looks good on the Pi. You don’t need to worry about other users or browser compatibility.

Basic Dashboard Layout

Let’s begin by designing the basic dashboard (ignoring the reversing screen for now). This will start as a “static” page. It won’t update based on the data from the Pi; you’re just getting a feel for the design.

Start by downloading the assets for Bootstrap and Font Awesome. Visit the Bootstrap download page (https://getbootstrap.com/docs/4.4/getting-started/download/). The current version of Bootstrap is v4.4 – but this is likely to change in the future, as new releases come out. Double-check you’re on the latest version by clicking the “v4.4” on the top-right corner of the screen and choosing the “latest” option from the version drop-down menu (Figure 14-1). Scroll down to the “Compiled CSS and JS” section, and choose download source. This will download a compressed file, containing all the files Bootstrap needs to work – which is a lot.
../images/488914_1_En_14_Chapter/488914_1_En_14_Fig1_HTML.jpg
Figure 14-1

Changing Bootstrap version before download

Double-click this file to extract it. You now have a Bootstrap folder called something like bootstrap-4.3.1-dist (your version number may differ). Back in your project terminal, create a folder called static, inside your Pi_Car folder:
mkdir Pi_Car/static
Now move the entire bootstrap folder into this static folder – either through finder/your GUI tool of choice or over the command line:
mv /Users/coburn/Downloads/bootstrap-4.3.1-dist /Users/coburn/Documents/Pi-Car/Pi_Car/static

Modify the paths to suit your folder structure.

Bootstrap is now ready to use in your application, so let’s get Font Awesome ready in much the same way. Create an account at Font Awesome (https://fontawesome.com/) by choosing Start for Free shown in Figure 14-2. Enter your email address and choose Send Kit Code. It’s not possible to download Font Awesome without signing up.
../images/488914_1_En_14_Chapter/488914_1_En_14_Fig2_HTML.jpg
Figure 14-2

Font Awesome getting started screen

After confirming your email address, visit the “Other Methods” page (https://fontawesome.com/start#other-methods) and choose download (Figure 14-3). From the next page, choose Download Font Awesome Free for the Web. Confirm you have a new file downloaded called something like fontawesome-free-5.13.0-web. This version number is subject to change in the future, but Font Awesome will serve you the new files as they become available.
../images/488914_1_En_14_Chapter/488914_1_En_14_Fig3_HTML.jpg
Figure 14-3

Font Awesome download button

Once again, double-click to extract the compressed files, and move them into your static folder:
mv /Users/coburn/Downloads/fontawesome-free-5.13.0-web /Users/coburn/Documents/Pi-Car/Pi_Car/static
Let’s create the core dashboard now. Inside your templates folder, create a new file called main.html:
touch templates/main.html
Here’s the code you need:
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Pi Car</title>
    <meta name="description" content="Raspberry Pi based car system">
    <meta name="author" content="Joe Coburn">
    <link href="static/fontawesome-free-5.13.0-web/css/all.css" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="static/bootstrap-4.3.1-dist/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="static/css/style.css">
</head>
<body>
    <div class="container text-center text-light">
        <h1>Hello.</h1>
        <h2>Wednesday 8th January 2020</h2>
        <div class="row">
            <div class="col text-left">
                <i class="fas fa-clock"></i>15:50
            </div>
            <div class="col text-right">
                <i class="fas fa-snowflake"></i> 20° C
                <br />
                <i class="fas fa-moon"></i>
                <i class="fas fa-sun"></i>Daytime
            </div>
        </div>
        <div class="row">
            <div class="col text-middle">
                <i class="fas fa-car icon-huge"></i>
            </div>
        </div>
        <div class="row">
            <div class="col text-middle">
                <i class="fas fa-exclamation"></i><span class="exclamation">Boot is open</span>
                <br>
                <i class="fas fa-lightbulb"></i>Fog lights are on
            </div>
        </div>
    </div>
</body>
</html>

This may look like a lot, but it’s much simpler than it may first appear. This isn’t code as such, it’s markup. Hypertext Markup Language, or HTML, is the Internet standard markup language for websites. It can’t perform calculations or logic, it’s simply instructions for the browser to interpret. HTML is a markup language. With it, you can define tables, boxes, text, images, and so on. Your browser understands these commands and does its best to draw the elements you specify. Each browser interprets these slightly differently, however, making cross-platform web development more like a game of whack-a-mole if you’re not careful.

HTML does not render pretty colors or define the sizes of things aside from a few browser defaults. Cascading Style Sheets, or CSS, almost always accompanies HTML to make things pretty, but I’ll cover that after breaking down this HTML.

HTML is a simple language, and your page will continue to work even if you make a mistake – it might look a bit odd though, depending on the error. HTML contains tags. A tag is an instruction and can contain other text and other tags inside it. Tags need a matching tag to close them, although there are exceptions, and self-closing tags. Think of HTML as defining a series of boxes for text of images to sit in. This basic tag is a H1 element:
<h1>Hello.</h1>

It has an opening and a closing tag and contains the text “Hello”.

Starting at the top of the file, there are some tags to tell the browser this is HTML and the language to use.
<!doctype html>
<html lang="en">

The doctype is a bit like a shebang in Bash scripts – it doesn’t need closing. This HTML tag is closed at the end of the file. Therefore, everything in this file is enclosed in the HTML tag.

Next is the head tag, with associated nested tags:
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Pi Car</title>
    <meta name="description" content="Raspberry Pi based car system">
    <meta name="author" content="Joe Coburn">
    <link href="/static/fontawesome-free-5.13.0-web/css/all.css" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="/static/bootstrap-4.3.1-dist/css/bootstrap.min.css">
    <link rel="stylesheet" type="text/css" href="/static/css/style.css">
</head>

The head tag is used for page metadata. Things such as the page title, character encoding (charset), and other metadata are defined here. Toward the bottom you can see the link tags. These pull in your Bootstrap and Font Awesome CSS files you downloaded earlier. This makes them accessible on the page. This also references a style.css file. This doesn’t exist yet, but it’s where you’ll write any custom CSS needed to style this page.

Moving on, the body tag is where the bulk of your page content lives. You can see this is full of div tags, which are containers to store other elements. H tags (H1, H2, etc.) are used for titles or larger text. You can see these tags have multiple classes attached:
<div class="col text-right">

Classes are used to point HTML elements to a CSS style. This lets you reuse CSS without copying and pasting it all over the place and lets you target specific elements to style. The classes listed here are defined by Bootstrap, with a little custom CSS.

Finally, there are the i tags:
<i class="fas fa-lightbulb"></i>

These elements use the Font Awesome classes to render the icons you can see on the dashboard.

There are lots of different HTML elements, tags, and techniques, but the premise is mostly the same across them all – different tags, each supporting attributes such as class, each one with specific purpose. Some tags have historical relevance, and some are no longer valid, as they are deprecated due to their age or incompatibility.

Remember, this HTML is static right now – it’s hard-coded to always display the same information – and isn’t tied into your sensor data.

Moving onto the CSS – most of this is handled for you by Bootstrap. As far as CSS files go, this is quite a small file, at 33 lines long. Create a new folder called CSS inside your static folder:
mkdir Pi_Car/static/css
Create a CSS file called style:
touch Pi_Car/static/css/style.css
This file will contain your custom CSS – any changes you need which Bootstrap doesn’t handle for you. Here’s the code you need:
html,
body {
      font-size: 25px;
      background-color: var(--gray-dark);
      text-shadow: 2px 2px #000000;
      cursor: none;
      padding: 0;
}
i {
      margin-right: 10px;
}
.icon-huge {
      font-size: 350px;
      font-weight: bold;
      text-shadow: 4px 4px #000000;
}
.fa-snowflake {
      color: var(--blue);
}
.fa-sun {
      color: var(--yellow);
}
.fa-moon {
      color: var(--cyan);
}
.fa-exclamation,
.exclamation {
      color: var(--red);
}

CSS lets you change a huge number of stylistic and layout elements. You can animate elements; change the size, color, and position of almost anything; hide elements; and more. The changes here mostly impact the size, and color of the text and icons.

Core HTML elements are targeted with their name – such as html. Everything inside the curly braces is applied to elements that meet the criteria. You can target multiple elements, high-level elements, or only elements that exist inside a different element. Some of the colors use the bootstrap variables. This uses the Bootstrap variable called “--blue”:
.fa-snowflake {
      color: var(--blue);
}
The cursor attribute ensures you won’t see the mouse cursor on the screen. Not very useful for a website, but incredibly useful for a Pi project such as this:
cursor: none;
Now that you have your HTML and CSS all ready, modify your application route inside main.py to load the main.html template, using the render_template function from Flask. Import the function:
from flask import Blueprint, jsonify, render_template
Then temporarily modify your route to ignore the data:
@data_blueprint.route("/")
def show():
    app.logger.info("Starting to retrieve core data")
    temperature = Sensors.get_external_temp()
    boot_status = Sensors.get_boot_status()
    light_status = Sensors.get_light_status()
    reverse_light = Sensors.get_reverse_status()
    fog_light = Sensors.get_fog_light_status()
    rear_distance = Sensors.get_rear_distance_sensor()
    result = {
        "temperature": "temperature",
        "boot": "boot_status",
        "light": "light_status",
        "reverse": "reverse_light",
        "rear_distance": "rear_distance",
        "fog": "fog_light",
    }
    Sensors.beep()
    app.logger.info("Finished retrieving core data")
    app.logger.debug(f"Core data: {result}")
    #return jsonify(result)
    return render_template("main.html")
Run the app on your computer and visit it in your web browser of choice, found at http://127.0.0.1:5000/.
../images/488914_1_En_14_Chapter/488914_1_En_14_Fig4_HTML.jpg
Figure 14-4

Static dashboard styling

Observe what it looks like (shown in Figure 14-4). The background has a dark color, the text is white (with a slight shadow), and there are various elements displaying (fake) data. There are icons next to some of the items. There are currently too many icons – not all of these will be visible when it’s hooked into your main dataset, but for now, commit the code and perform a build on the Pi. Visit the Pi’s application at http://pi-car:5000/ and verify the build. Resize your browser window and observe how the layout reflows to fit. It may not always fit at all sizes, but that’s OK – the Pi has a fixed-size screen.

Bootstrap is configured on this page for a responsive layout. That is to say, as the size of your web browser changes, so does the layout of the page. Figure 14-5 shows this dashboard on a different sized screen. This doesn’t really matter for this project, as the Pi will always have the same screen size, but it’s a good practice to get into (and Bootstrap does it all for you).
../images/488914_1_En_14_Chapter/488914_1_En_14_Fig5_HTML.jpg
Figure 14-5

Dashboard resized to the screen

Right now, the Pi boots up. It downloads the latest code, installs the dependencies, and spins up the Flask server (accessible over the network). It doesn’t have a screen, and it doesn’t open the web browser and load the page for you – let’s fix that next.

Autoload the Pi’s Web Browser

For a truly interaction-free startup process, the Pi needs to open a web browser and visit the application it’s already running. Your installation of Raspbian on the Pi comes with a web browser. It’s perhaps not as full featured as your desktop computer browser, but it’s sufficient for this project.

If it’s not currently connected, then plug in your Pi’s mini LCD display using the full-size HDMI to micro-HDMI cable. Connect to the Pi over SSH.

It’s perfectly possible to start a browser from the root user account, perhaps in your rc.local, but that’s an imperfect solution. Right now, your Pi boots to the Lightweight X11 Desktop Environment (LXDE) desktop. This provides your GUI desktop interface, which so far you have ignored in favor of a remote terminal session. This LXDE environment has control over the HDMI display. It’s difficult to launch a browser which uses this display through the existing boot scripts. Enter LXDE-pi/autostart. Commands inside this file run when the desktop loads, for any user. Begin by opening the file:
sudo nano /etc/xdg/lxsession/LXDE-pi/autostart
Under the existing options in here, add the following command:
/usr/bin/chromium-browser --no-first-run --noerrdialogs --start-fullscreen --start-maximized --disable-notifications --disable-infobars --kiosk --incognito http://localhost:5000

Save and exit the file. What does this do? It starts the Chromium browser, located at /usr/bin/chromium-browser. It points it to your localhost address and port of your Flask server, and it passes in several options to the browser. These options start the browser without any distractions. Fullscreen, kiosk mode prevents you from showing the URL address bar or bookmarks and hides lots of alerts and warnings – perfect for an integrated system such as this.

Restart the Pi and observe as the browser starts after booting to the desktop environment. You may notice that the address cannot be found, but after several minutes, it eventually arrives. This is because Flask is still booting when the browser starts. Shortly I’ll show you how to speed this up once you’re no longer actively developing the system.

For me, the Pi is running my display horizontally – and it won’t switch automatically just by rotating the Pi. For my system, I know I want a vertical display, so I need to configure that. If you’re happy with your display as is, then you don’t need to worry about this step. Begin by editing the displayed config file, called dispsetup.sh :
sudo nano /usr/share/dispsetup.sh
Now add this line before the exit 0, which must remain at the bottom of the file:
DISPLAY=:0 xrandr --output HDMI-1 --rotate left
This tells the Pi to configure the primary display (DISPLAY=:0), connected to HDMI 1. It rotates it 90 degrees to the left. To rotate different directions, change left to one of the following:
  • Left

  • Right

  • Inverted

  • Normal

Now restart the Pi to see these changes take effect – shown in Figure 14-6.
../images/488914_1_En_14_Chapter/488914_1_En_14_Fig6_HTML.jpg
Figure 14-6

The almost completed dashboard

Show Sensor Data

Up until now you have two fairly independent modules. The template shows a nice-looking dashboard with hard-coded data, and the back-end Python code reads all the sensors. Let’s link the two together. All the hard work is already done, so it’s almost a simple case of pointing the templates to the data. Modify your main route inside main.py.

Change the return to use Flask’s render_template, and pass the sensor data to the main.html template. I’ve removed the beep and reverse function calls, as these are not needed just yet. Here’s the revised code:
from flask import Blueprint, render_template
from .sensors import Sensors
from flask import current_app as app
from datetime import datetime
main_blueprint = Blueprint("main", __name__)
@main_blueprint.route("/")
def show():
    app.logger.info("Starting to retrieve core data")
    sensors = Sensors()
    temperature = sensors.get_external_temp()
    boot_status = sensors.get_boot_status()
    light_status = sensors.get_light_status()
    fog_light = sensors.get_fog_light_status()
    today = datetime.now()
    time = today.strftime("%H:%M")
    date = today.strftime("%A %d %B %Y")
    result = {
        "temperature": temperature,
        "boot": boot_status,
        "light": light_status,
        "fog": fog_light,
        "time": time,
        "date": date,
    }
    app.logger.info("Finished retrieving core data")
    app.logger.debug(f"Core data: {result}")
    return render_template("main.html", data=result)
Notice how the sensor data is passed to the template through the name data. It’s a dictionary called result , but on the template itself, you’ll access it through its new name of data. I’ve modified this to include the current date and time – a useful change, which is simple with the Python core library:
today = datetime.now()
time = today.strftime("%H:%M")
date = today.strftime("%A %d %B %Y")

The strftime function converts the datetime object into a readable format.

Over inside the main.html template, you can access this data through Jinja2 variables, like this:
{{ data.date }}
By using simple logic, or just spitting out the data, you can control what’s shown to the screen. For example, here’s how to show the snowflake icon only if it’s cold:
{% if data.temperature <=4 %}
    <i class="fas fa-snowflake"></i>
{% endif %}
Here’s the revised code for your body tag:
<div class="container text-center text-light">
    <h2>Hello.</h2>
    <h3>{{ data.date }}</h3>
    <div class="row">
        <div class="col text-left">
            <i class="fas fa-clock"></i>{{ data.time }}
        </div>
        <div class="col text-right">
            {% if data.temperature <=4 %}
                <i class="fas fa-snowflake"></i>
            {% endif %}
            {{ data.temperature }}° C
            <br />
            {% if data.light == 'Daytime' %}
                <i class="fas fa-sun"></i>
            {% else %}
                <i class="fas fa-moon"></i>
            {% endif %}
            {{ data.light }}
        </div>
    </div>
    <div class="row">
        <div class="col text-middle">
            <i class="fas fa-car icon-huge"></i>
        </div>
    </div>
    <div class="row">
        <div class="col text-middle">
            {% if data.boot == 'Open' %}
                <i class="fas fa-exclamation"></i><span class="exclamation">Boot is open</span>
            {% endif %}
            <br>
            {% if data.fog %}
                <i class="fas fa-lightbulb"></i>Fog lights are on
            {% endif %}
        </div>
    </div>
</div>

Remember the golden rule when working with templates – simple logic to show/hide data or minor UI tweaks are fine. Complex logic or calculations should live inside the Python classes!

Finally, let’s get this data to update in near-real time. Inside the head tags, add this new meta tag:
<meta http-equiv="refresh" content="0.25">

This HTML tag instructs the browser to reload the page every quarter of a second. It doesn’t actually do it that quickly, but it’s fairly fast. Now, the page constantly reloads, getting fresh data every time. Commit your code and push to the Pi. Once booted, observe the dashboard. Make some changes such as holding down buttons, covering the light sensor, or warming up the temperature sensor. Notice how the dashboard updates to reflect these changes? Very nice – it’s almost complete now.

Reversing Module Completion

Several of the stand-alone projects included components and sensors for the reversing module. The Pi camera, beeper unit, and rear distance sensor all require integrating into this dashboard.

Let’s begin with the reverse sensor. Check this every time the main page loads – if it’s triggered (the car is in reverse gear), switch to the reverse camera feed page. Starting with the Python code inside main.py, shuffle the code around such that the reverse sensor is checked before any other sensors – no point checking the temperature if the reversing screen will run. Use a simple if to route to the reverse code if the sensor is detected:
reverse_light = sensors.get_reverse_status()
if reverse_light:
    return reverse()
The reverse name needs importing from the reverse.py file:
from .reverse import reverse
Now, whenever the main page detects the reverse sensor, it will redirect and run the reverse route code. If you run this code on the Pi, there’s one big problem. The video feed is loading from http://pi-car:8081. This host does not resolve on the Pi – you only configured it on your computer, so let’s fix that. Using a hosts entry like this means the video feed can work on the Pi, or when accessing the Pi over the network – it’s a far more flexible approach. On the Pi, edit your hosts file:
sudo nano /etc/hosts
Add a new entry to route pi-car to your localhost address:
127.0.0.1 pi-car

Now save and restart. When booted, hold down the reverse sensor button (or otherwise trigger the sensor). You should see the Pi switch to the video feed page – it’s not pretty yet, but it should work (providing you configured the Pi camera and video stream in the previous projects).

Let’s improve this reversing page. It needs to call the beep and distance sensor functions, the camera needs fitting to the page, and it needs some styling. Here’s the revised HTML to go inside reverse.html:
<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
        <title>Pi Car</title>
        <meta name="description" content="Raspberry Pi based car system">
        <meta name="author" content="Joe Coburn">
        <link rel="stylesheet" type="text/css" href="/static/css/style.css">
    </head>
    <body>
        <img src="{{ base_url }}">
    </body>
</html>

You could argue that you don’t need to “follow the rules” as it were. This HTML adds little functionality, and as your dashboard isn’t on the Internet, you don’t need to follow standards. That’s understandable – feel free to skip this, but it’s a good practice to follow the standards. Shortly you’ll modify this to include some essential code to read the sensors and issue the beep.

Notice how most of this HTML in the head section is identical to the main page – with a few stylesheets removed. In any big system, or website with more than three or four pages, it makes sense to centralize this logic to reduce duplication. For example, changing the name of your CSS file requires updating the same tag in two files now. In this project with only two basic templates, that’s an acceptable compromise. If you’re interested in reducing this duplication, then look into Jinja macros (https://jinja.palletsprojects.com/en/2.11.x/templates/#macros) – an excellent way to reuse code.

You may need to rotate your camera again, based on your final screen orientation. As with the first rotation, edit your motion config file to handle this:
sudo nano /etc/motion/motion.conf
rotate 90

Revisit the reversing camera chapter for full motion config breakdown.

When you’re ready, commit your code and restart the Pi. Trigger the reverse light sensor, and watch as your dashboard switches over to the live camera feed – it’s really taking shape now. It’s not there yet, however. The reversing page doesn’t switch back to the main dashboard when the reverse gear is disengaged, and it doesn’t yet issue the beep or IR distance-sensing reverse assist, so let’s fix that.

For this file, constantly restarting the page as was the case with the main index page is too disruptive to the live video feed. It works, but the video feed ends up slightly jittery. To accomplish this, you’ll use JavaScript. JavaScript is a front-end programming language. It’s executed by your web browser, and website visitors can often read the code itself. It’s not the same thing as Java, and it’s perfect for running tasks “in the background” using Asynchronous JavaScript and XML (AJAX) . AJAX is used with JavaScript to create asynchronous client-side applications. You could use it on the previous page to update all the data, but it would require a fair amount of JavaScript code, for little benefit.

Almost all of the modern Internet uses JavaScript in some way. It doesn’t need installing either, as it’s part of the instructions your browser can understand by default. Let’s not get too carried away though, as JavaScript is horribly inconsistent (even with itself) and it’s much slower than Python to execute. Still, it’s more than good enough for this small task.

For this task, JavaScript will essentially load another web page, which will route to Flask, ping the distance sensor, and issue a beep if an object is nearby. JavaScript doesn’t need to change any data, or even look for a successful response – it just hits this page and carries on as normal.

Create a new Flask route inside reverse.py called reverse_beep . This needs a route of /reverse/beep/, which is the URL address your JavaScript will load. Here’s the code:
@reverse_blueprint.route("/reverse/beep/")
def reverse_beep():
    sensors = Sensors()
    rear_distance_item = sensors.get_rear_distance_sensor()
    if rear_distance_item:
        sensors.beep()
    return “Success”

There are no surprises in this code. It calls the get_rear_distance_sensor function from your Sensors class. If that returns True, it issues a beep using the beep function. If you visit this new route from your browser, you can see it in action (http://pi-car:5000/reverse/beep/). There is no logging in this function – and for good reason. There’s enough logging in the sensor code itself to handle this, and as this code could potentially run four times every second, your logs will quickly fill up with too much noise and become almost unusable.

Let’s write JavaScript to call this endpoint. Over in reverse.html, create a script tag, which needs to live inside the head tag:
<script type="text/javascript">
</script>
Any JavaScript your write must be within a script tag. The script type of text/javascript tells your browser that this script contains JavaScript. Here’s your JavaScript AJAX code:
var runSensors = function() {
  var xhttp = new XMLHttpRequest();
  xhttp.open("GET", "/reverse/beep/", true);
  xhttp.send();
}
window.onload = function() {
  setInterval(runSensors, 250);
}

This code performs three core functions. It creates a function called runSensors, following the JavaScript naming convention of CamelCase . This function uses the XMLHttpRequest object to send an AJAX request to your new /reverse/beep/ route. Next, it uses the setInterval function to call your new runSensors function. SetInterval runs code regularly on a schedule, defined by the number – 250 milliseconds in this case. Every quarter of a second, JavaScript pings the /reverse/beep/ route to sense objects and issue beeps. This does not care about the response, or the duration.

The final argument to xhttp.open is set to true. This is the asynchronous parameter – it instructs the browser to run this code in the background while doing other tasks. No matter how slow this endpoint is, it will still issue the next command, and the next, and so on. Remember, this endpoint waits one second while issuing the beep.

Finally, window.onload ensures your code is loaded when the page loads. Without this, your code would never execute at all.

Commit your code and perform a build. Trigger the reverse sensor to load the reverse page. Now cover the distance sensor, and observe the beeps. Now move your hand and listen – the beeps stop. If you open your application logs, you’ll see the beep and distance sensor logs corresponding to the AJAX requests every quarter of a second.

Finally, let’s write some code to check the reverse sensor. If it’s no longer detecting the reverse bulb, JavaScript needs to switch back to the main dashboard, and let that take over.

Back in Python, modify the reverse_beep function to call the main reverse sensor, and return a status of Exit if it’s no longer detected:
@reverse_blueprint.route("/reverse/beep/")
def reverse_beep():
    sensors = Sensors()
    reverse_light = sensors.get_reverse_status()
    if not reverse_light:
        return "Exit"
    rear_distance_item = sensors.get_rear_distance_sensor()
    if rear_distance_item:
        sensors.beep()
    return "Success"
Finally, modify the JavaScript runSensors function to check the result returned from the Python, and redirect to the dashboard if it equals Exit. This uses the onreadystatechange function, and it checks the readyState variables equals four – the operation is complete. Here’s the complete JavaScript code:
var runSensors = function() {
  var xhttp = new XMLHttpRequest();
  xhttp.open("GET", "/reverse/beep/", true);
  xhttp.onreadystatechange = function() {
    if (xhttp.readyState === 4) {
      result = xhttp.response
      if (result === "Exit") {
        window.location.href = "/";
      }
    }
  }
  xhttp.send();
}
window.onload = function() {
  setInterval(runSensors, 250);
}

You can learn more about the different ready state options at https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/readyState.

Load the Pi’s main dashboard. Play around with triggering the reverse sensor, and see it redirect to the reversing camera (with distance sensing and beeps). Now remove power from the sensor and watch it send you back to the dashboard. Excellent – the whole system is nearly finished now.

Final Pi Improvements

Your system is now practically complete. The Python is done, there’s no more code to write, and it’s fully working – pending installation in your car. There are two tasks left to finish this system. You may have noticed your Pi going to sleep to save energy. After five minutes or so, the display turns off and your dashboard disappears. It’s still around – it’s accessible over SSH and it operates just fine, but the screen is off. The Pi has switched off its display output to save energy. This energy saving is a useful feature for occasional use, but it’s a nuisance for a car dashboard project. While the Pi has power and is running the application, it should send data to the display. Let’s fix this.

Modify the Debian display configuration file lightdm.conf:
sudo nano /etc/lightdm/lightdm.conf
This file contains configuration options for all kinds of system-level display features. Scroll down to the [Seat:*] section. Uncomment the xserver-command=X line by removing the hash symbol. Extend this line to prevent the display from sleeping – shown in Figure 14-7:
xserver-command=X -s 0 dpms
Save and exit this file, taking care not to disrupt any other config lines. Now restart your Pi and wait some time – roughly five to ten minutes should be enough. Watch and see that your display now remains on forevermore – if the Pi is on, so is the display.
../images/488914_1_En_14_Chapter/488914_1_En_14_Fig7_HTML.jpg
Figure 14-7

Sample Debian display configuration file

The final change is to prevent the Pi from auto-updating its code base from Git. This is slow – you have to code a delay into the script to ensure the Internet connection exists, and it’s ugly. By waiting, downloading, and then installing the latest dependencies and code, the LXDE has already booted and started the browser. This leads to an ugly few minutes whereby the browser is ready to go, but the server isn’t running yet. Besides, full CI/CD perhaps isn’t the best paradigm for a car – having to wait for an update before you can begin your journey isn’t the best experience – ever been frustrated waiting for video game updates before?

Head back into your rc.local file from previous chapters, and comment out all the slow stuff – the sleep, clone script, and pipenv install. Here’s the final script contents:
#sleep 15
export PATH=/home/pi/.pyenv/shims:$PATH
export FLASK_APP=Pi_Car.app
cd /home/pi/Documents/Pi-Car/
#./clone.sh
#pipenv install
pipenv run flask run --host=0.0.0.0 &
exit 0

By commenting these lines out, you can easily reinstate them in the future. A fun expansion project would be to push updates to the Pi, or integrate your build chain to deploy to a small number of cars, and continue the rollout once it’s stable and there are no reported issues over a given period of time. If there are issues, automatically roll back to the previous version. This is beyond the scope of this book, and it’s straying into devops territory. For those interested, Ansible (www.ansible.com/) is a great tool for this job.

Restart your Pi and bask in your own brilliance. Watch the sheer speed at which the Pi can start now that it’s no longer burdened by auto-updates. It doesn’t need an Internet connection to work – it has everything it needs in a self-contained package. Any sensor failures won’t hold it back, as you programmed it defensively to operate in almost any environment. Naturally, if lots of sensors fail, it may enter into limp mode  – with diminished functionality. That’s still far better than a total failure, and often at times it’s not possible to work normally if there’s a huge system failure.

This chapter cleans up the system and turns it into a functional and pleasant dashboard. By building on the application logic developed in all the previous chapters, there’s a lot of margin for customization here. All the previous chapters develop the core logic, and so much of this chapter is open to your interpretation. You learned how to switch between the rear view camera and the main display and how to speed up the entire application’s startup time. You learned about CDNs, CSS, JavaScript, and HTML, along with AJAX and Chromium.

Throughout this book, you have learned how to write software. It’s my hope that you really understood what’s happening at every level and tediously typed out every line of code, instead of blindly loading up the repository and doing a copy-paste job. I’d love to hear from you if you enjoyed this book. Visit this book’s GitHub repository at https://github.com/CoburnJoe/Pi-Car and say hello. Create a pull request and enhance the code, or join any discussions around topics (either in this repo, or anywhere else on GitHub). While GitHub doesn’t support direct messages anymore, you can see my latest projects and contact details on my profile: https://github.com/CoburnJoe.

Here are some possible expansion projects, all based on the hardware and software you’ve used so far:
  • Door sensors

  • Boot sensor

  • Headlight sensor

  • Engine temperature sensor

  • Random “message of the day” (perhaps based on the time of day)

  • Climate control options

  • Dashboard “debug” overlay/reporting of sensor failure to the dashboard

  • Apple CarPlay/Android Auto support

  • Multiple reversing sensors

  • Report errors over the Internet

Last of all, remember that code is for humans. Computers will understand almost any jumbled mess of spaghetti code, so make your code simple and clear, such that anyone in the future can easily understand its intent. Complex, clever, or otherwise convoluted code makes modifications or troubleshooting more complex for everyone involved.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.23.127.197