Grafana 5.x Running on Raspberry Pi

Got around to upgrading my Raspberry Pi 3 Model B weather station with a newer version of Grafana, the Debian packages for ARM are hosted on Grafana’s download page under ARMv7.

Moving from a 2.x word to a 5.x has been impressive, most notably the drag, drop, and resize functionality.

I’m still using InfluxDB and Telegraf to store and populate my time data points.

Latest Raspbian’s (stretch) comes with Influx 1.0.2 and works nicely:

# dpkg -l | grep influxdb
ii  influxdb     1.0.2+dfsg1-1 armhf

While I needed to grab the Telegraf as a Linux Binary (ARM) from Influx’s download page.

Enjoy!

Remote Controlled Car using Raspberry Pi and Webcam

Demo

Setup

First thing I tackled was setting up the L293D H-Bridge on the Bread Board.

I found myself referencing the following Diagram a couple times.

Step one is connecting your chip down the center of your board:

From here I connected the 3 power pins to my board’s power rail using a few Jumpers:

A few more Jumpers connect each side of the chip to ground:

Finally I use a couple Wires to connect both sides of my power and ground rails:

Using a little bit of double sided tape I stick my board onto the Car Chassis:

I also wired up each DC motor, and the battery pack to the board:

Next I wired up my Raspberry Pi‘s GPIO pins, connecting them to the L293D.

Once I’ve verified the GPIOs were connected properly, I used a couple rubber bands to strap the Pi, Portable USB Charger (I used a Vans Shoe Charger) and Web Cam to the cassis:

Code

On the Raspberry Pi I’m using Raspbian as the operating system, and installed a couple pieces of software:

* nginx
* Flask
mjpg-streamer
* Rpi.GPIO

My nginx configuration is really basic and looks like this:

server {
 listen 80 default_server;
 listen [::]:80 default_server;

 root /var/www/html;

 location /stream {
   proxy_pass http://localhost:8080/?action=stream;
   proxy_set_header Content-Type "image/jpeg";
 }

 location / {
   proxy_pass http://localhost:8000;
 }

}

The Flask application is a bit janky, but gets the job done:

app.py

import RPi.GPIO as GPIO
from flask import Flask, render_template
from flask import request

GPIO.setmode(GPIO.BCM)

GPIO.setup(14, GPIO.OUT)
GPIO.setup(15, GPIO.OUT)

GPIO.setup(23, GPIO.OUT)
GPIO.setup(24, GPIO.OUT)


app = Flask(__name__)

@app.route("/")
def index():
    return render_template('index.html')

@app.route("/left")
def left():
    method = request.args.get('method')
    if method == 'stop':
        sig = GPIO.LOW
    else:
        sig = GPIO.HIGH

    GPIO.output(23, sig)
    return "OK"

@app.route("/forward")
def forward():
    method = request.args.get('method')
    if method == 'stop':
        sig = GPIO.LOW
    else:
        sig = GPIO.HIGH

    GPIO.output(15, sig)
    GPIO.output(24, sig)
    return "OK"

@app.route("/backward")
def backward():
    method = request.args.get('method')
    if method == 'stop':
        sig = GPIO.LOW
    else:
        sig = GPIO.HIGH

    GPIO.output(14, sig)
    GPIO.output(23, sig)
    return "OK"

@app.route("/right")
def right():
    method = request.args.get('method')
    if method == 'stop':
        sig = GPIO.LOW
    else:
        sig = GPIO.HIGH

    GPIO.output(14, sig)
    return "OK"

if __name__ == "__main__":
    app.run(host='0.0.0.0', port=8000, debug=True)

While the template contains a bit of Javascript to handle button presses:

templates/index.html

<html>

<head>

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

<style>
  img {
      margin-top: 25px;
      margin-bottom: 25px;
    }
</style>

<script
  src="https://code.jquery.com/jquery-3.2.1.min.js"
  integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
  crossorigin="anonymous"></script>

<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>


<script>


// define our fired states to false
var forward_fired = false;
var backward_fired = false;
var left_fired = false;
var right_fired = false;

// keydown event will start motor
document.onkeydown = function() {

    if(event.keyCode == 87) {

        if(!forward_fired && !backward_fired) {
            forward_fired = true;

            button = document.getElementById('up');
            button.className = 'btn btn-success btn-lg disabled';

            console.log('start forward');
            $.get("/forward")
        }
    }

    if(event.keyCode == 83) {
        if(!backward_fired && !forward_fired) {
            backward_fired = true;

            button = document.getElementById('down');
            button.className = 'btn btn-success btn-lg disabled';

            console.log('start backward');
            $.get("/backward")
        }
    }

    if(event.keyCode == 65) {
        if(!left_fired && !right_fired && !backward_fired) {
            left_fired = true;

            button = document.getElementById('left');
            button.className = 'btn btn-success btn-lg disabled';

            console.log('start left');
            $.get("/left")
        }
    }

    if(event.keyCode == 68) {
        if(!right_fired && !left_fired && !backward_fired) {
            right_fired = true;

            button = document.getElementById('right');
            button.className = 'btn btn-success btn-lg disabled';

            console.log('start right');
            $.get("/right")
        }
    }

};

// keyup event will stop motor
document.onkeyup = function() {


    if(event.keyCode == 32) {
        console.log('beep');
        $.get("/beep")
    }


    if(event.keyCode == 87) {
      if(forward_fired) {
        forward_fired = false;

        button = document.getElementById('up');
        button.className = 'btn btn-default btn-lg disabled';

        console.log('stop forward');
        $.get("/forward?method=stop")
      }
    }

    if(event.keyCode == 83) {
      if(backward_fired) {
        backward_fired = false;

        button = document.getElementById('down');
        button.className = 'btn btn-default btn-lg disabled';

        console.log('stop backward');
        $.get("/backward?method=stop")
      }
    }

    if(event.keyCode == 65) {
      if(left_fired) {
        left_fired = false;

        button = document.getElementById('left');
        button.className = 'btn btn-default btn-lg disabled';

        console.log('stop left');
        $.get("/left?method=stop")
      }
    }

    if(event.keyCode == 68) {
      if(right_fired) {
        right_fired = false;

        button = document.getElementById('right');
        button.className = 'btn btn-default btn-lg disabled';

        console.log('stop right');
        $.get("/right?method=stop")
      }
    }

};

</script>

</head>

</body>


<div class="container">
  <div class="row">
    <center>

      <img src="/stream" class="img-thumbnail">

    </center>
  </div>
</div>

<!-- control buttons -->
<div class="container">
  <div class="row">

    <center>

      <button id="left" type="button" class="btn btn-default btn-lg disabled">
        <span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span>
      </button>

      <button id="up" type="button" class="btn btn-default btn-lg disabled">
        <span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
      </button>

      <button id="down" type="button" class="btn btn-default btn-lg disabled">
        <span class="glyphicon glyphicon-arrow-down" aria-hidden="true"></span>
      </button>

      <button id="right" type="button" class="btn btn-default btn-lg disabled">
        <span class="glyphicon glyphicon-arrow-right" aria-hidden="true"></span>
      </button>

    </center>

  </div>
</div>

</body>

</html>

I haven’t yet configured the Flask and mjpg-streamer processes to startup automatically, so I connect via a shell and start each in a screen session.

Once that is done connect to the running nginx server using your browser, you should be presented with a user interface similar to this:

Use your keyboard to control the car, just like a video game the key W moves forward, S backwards, A left, and D is right.

Component List

Here is a list of each of the components (or comparable) I used during the setup:

Raspberry Pi 3 Model B Motherboard

Official Raspberry Pi 3 Case – Red/White

uxcell White 8.5 x 5.5cm 400 Tie Points 400 Holes Solderless Breadboard

Microsoft LifeCam HD-3000 Webcam – Black (T3H-00011), 720p HD 16:9 Video Chat, Skype Certified

INSMA Motor Smart Robot Car Chassis Kit Speed Encoder Battery Box For Arduino DIY

Adafruit Dual H-Bridge Motor Driver for DC or Steppers – 600mA – L293D [ADA807]

Kuman 120pcs Breadboard Jumper Wires for Arduino Raspberry Pi 3 40pin Male to Female, 40pin Male to Male, 40pin Female to Female Ribbon Cables Kit Multicolored Pack K45

Makerfocus 140pcs Breadboard Board Jumper Cable Wire Kit w Box

Happy Will 200 PCS Breadboard Jumper Wires/Jump Wire Mix Long and Short M/M for Circuit Board

Anker Astro E1 5200mAh Candy bar-Sized Ultra Compact Portable Charger (External Battery Power Bank) with High-Speed Charging PowerIQ Technology (Black)