Raspi Car

This is a project to make an internet controlled car from an old RC car and a Raspberry Pi. It consists of a web server running on a Raspberry Pi Zero W, a webcam, and an old RC car. The components may be purchased for about $50.00 making this an affordable and fun project. Some soldering is required so you will need a soldering iron.

Jump To:

Component List

  • A Raspberry Pi Zero W
      A Raspberry Pi Zero W is a small single board computer made by the Raspbery Pi Foundation. For more information visit https://www.raspberrypi.org/ . It is perfect because of it's small size and built in Wifi.
  • Micro SD Card
      The Raspberry Pi will need one on which to install the OS. An 8GB or even a 4GB SD card for th OS is sufficient for this project.
  • USB Web Cam
      I recommend a Creative - Live! Cam which runs about $20.00. You may be able to find a less expensive one on ebay, just avoid shipments from China.
  • OTG micro USB-to-USB
      The Raspberry Pi USB input is a micro type so you will needed this to connect the camera to the Pi.
  • RC Car
      Maybe the most difficult component to get. Newer cars in the stores use LiPo batteries and have smaller electronics making them difficult to use. Thrift stores like Salvation Army or Goodwill are the best places. The transmitter won't be needed, so buy it even if it doesn't have one. Having said that, the cars there are sort of hit or miss. Often times they may not work, but for a price of 4-6 dollars a couple of duds won't break the bank. If you have bought more duds than you wanted and are feeling ambitious you could hobble together a car from working parts. If you are unable to find one with a battery you will need to purchase one which will increase the cost. When buying a car note the voltage required. Some cars use standard double A batteries and only run on 6 volts. Those will be difficult to use. For this guide I will use a 9.6 volt battery.
  • NiCad Battery
      If you are unable to find an RC car with one you will need to get a seperate battery. You may decide in the future to get a higher capacity battery for more run time. A custom battery from Battery giant is what I used. A custom battery needs to have the Tamiya style connectors.
  • A USB Charging Adapter.
      One that would be used in a car outlet. This will drop the voltage down from 9.6v to 5v, The operating voltage for the Raspberry Pi.
  • Jumper wires
      Male to female.
  • Wire
      26 or 24 gage; If you can, get both black and red to keep it color coded.
  • Tamiya Big Style Connector Plugs
      Unwired. Connecter that RC car batteries use. If you get a custom battery make sure they use this style connector. Additional ones will be used to connect the car charger to the battery.
  • Mini HDMI Adapter or Cable
      The Raspberry Pi Zero uses a mini HDMI port. This will only be used in the intial stages of setup, leaving the adapter available for other projects.


Install Raspbian OS

Download Raspbian

You can get this image via direct download or torrent here. The regular version comes with a graphical user interface and will occupy more disk space. Get the lite version because there will be no need for the user interface. Every thing in this guide can be done with the command line interface.

Download Win32 Disk Imager

The executable may be found here. Extract the .img file from your Raspbian download and run the disk imager. Select the file you extracted and your SD Card. Click Write to install the OS. After that is done it is time to boot the Pi. Insert the SD card and connect it to a TV or monitor via HDMI. Connect a keybord then provide power. After it boots, login with the default user name "pi" and password "raspberry". Before anything we need to get the device onto the WiFi. (For a more detailed guide check here.)

Set up WiFi

To edit the supplicant file and enter your WiFi credentials, run:

sudo nano /etc/wpa_supplicant/wpa_supplicant.conf

Add these lines to the bottom.

network={ ssid="name_of_wifi" psk="passkey_of_wifi" }

Press ctrl-X, save and exit.

Enable SSH

By default on Raspbian SSH is disabled. To enable it run:

sudo systemctl enable ssh sudo systemctl start ssh

Reboot by running

sudo reboot

Once it has rebooted login and type

ifconfig

Note the IP address. If there is not ip address listed then check the file for WiFi. The Pi may now be disconnected from the TV/monitor and keyboard; they are no longer needed. Further interfacing with the Pi can be done remotely with an SSH client. WinSCP or MobaXterm will work. Select your favorite and log into the IP address from before with the default username and password.

Before we enter any commands we we want to update the Pi to the latest. Run:

sudo apt-get update sudo apt-get upgrade


Node Red

Node Red will hadle the web pages and controlling the GPIOs. For a detailed setup guide visit Node-RED : Running on Raspberry Pi

To get the latest node red for Raspberry Pi run:

bash <(curl -sL https://raw.githubusercontent.com/node-red/raspbian-deb-package/master/resources/update-nodejs-and-nodered)

To create the settings file for Node-Red it needs to be started once. Run:

sudo systemctl start nodered

We need to tell Node-Red where to look for the web page assets. This will become the root directory for any web pages served by http nodes. Run:

nano /home/pi/.node-red/settings.js

And change the line:
//httpStatic: '/home/nol/node-red-static/',
to:
httpStatic: '/home/pi/.picar/www/,

Press ctrl-X, save and exit.

Now we need to copy the web asstets into the folder that was specified in the settings. Create the directories by running:

mkdir /home/pi/.picar mkdir /home/pi/.picar/www

Download the web assets here picar-nodered-www.zip. Unzip and copy the www directory into /home/pi/.picar with your ssh client.

Restart Node-Red:

sudo systemctl restart nodered

Node-Red uses graphical "nodes" to layout a program which it will execute. By connecting these nodes different programs can be written without much actual code. These layouts are called workflows.

To set up the program for this project I have provided three flows to import: Web Server, Control, and Credentials. You can access Node-Red by opening a browser to the Pi's ip address on port 1880 in the node-red directory e.g. ( http://ip_address:1880).

Web Server

Open the menu in the top right of the screen and select Import > Clipboard Paste the following into a new flow.

[ { "id": "acc22612.1a8258", "type": "file in", "z": "1fee60dc.191fff", "name": "auth.html", "filename": "/home/pi/.picar/www/auth.html", "format": "utf8", "sendError": true, "x": 520, "y": 80, "wires": [ [ "aff0c571.204548" ] ] }, { "id": "9d08714e.9b236", "type": "function", "z": "1fee60dc.191fff", "name": "Get Submitted", "func": "var driver = msg.payload.driver.toLowerCase();\nvar key = msg.payload.key;\n\nmsg.driver = driver;\n\nvar keyObject = {};\nkeyObject[driver] = key;\n\nmsg.payload = JSON.stringify(keyObject);\nreturn msg;", "outputs": 1, "noerr": 0, "x": 160, "y": 100, "wires": [ [ "32ae2dc5.59e942" ] ] }, { "id": "ab57849c.aa3bc8", "type": "function", "z": "1fee60dc.191fff", "name": "Store Hash", "func": "msg.key = msg.payload;\nreturn msg;", "outputs": 1, "noerr": 0, "x": 150, "y": 180, "wires": [ [ "b8ae7304.33a36" ] ] }, { "id": "b8ae7304.33a36", "type": "file in", "z": "1fee60dc.191fff", "name": "keys", "filename": "/home/pi/.picar/www/keys", "format": "utf8", "sendError": true, "x": 130, "y": 220, "wires": [ [ "812830b9.9fe19" ] ] }, { "id": "812830b9.9fe19", "type": "json", "z": "1fee60dc.191fff", "name": "", "x": 130, "y": 260, "wires": [ [ "b6755af4.76fc78" ] ] }, { "id": "b6755af4.76fc78", "type": "function", "z": "1fee60dc.191fff", "name": "Validate", "func": "var keyRing = msg.payload;\nvar driver = msg.driver.toLowerCase();\nvar key = msg.key;\n\nif (keyRing[driver] && keyRing[driver] === key) {\n keyobject = {};\n keyobject[driver] = key;\n msg.cookies = {\n carkey: {\n value: JSON.stringify(keyobject),\n maxAge: 10*365*24*60*60*1000\n }\n }\n msg.payload = 'succeeded';\n return msg\n}\nmsg.payload = 'failed';\nreturn msg;", "outputs": "1", "noerr": 0, "x": 140, "y": 300, "wires": [ [ "3b5b91b6.7d3a4e" ] ] }, { "id": "3b5b91b6.7d3a4e", "type": "http response", "z": "1fee60dc.191fff", "name": "", "x": 270, "y": 300, "wires": [] }, { "id": "aff0c571.204548", "type": "http response", "z": "1fee60dc.191fff", "name": "", "x": 650, "y": 80, "wires": [] }, { "id": "d82fa1aa.d92c7", "type": "http in", "z": "1fee60dc.191fff", "name": "", "url": "/picar", "method": "get", "upload": false, "swaggerDoc": "", "x": 360, "y": 60, "wires": [ [ "3bd7d0a7.acf3a" ] ] }, { "id": "a6603349.42ce1", "type": "file in", "z": "1fee60dc.191fff", "name": "keys", "filename": "/home/pi/.picar/www/keys", "format": "utf8", "sendError": true, "x": 350, "y": 140, "wires": [ [ "92e3b880.587308" ] ] }, { "id": "92e3b880.587308", "type": "json", "z": "1fee60dc.191fff", "name": "", "x": 350, "y": 180, "wires": [ [ "4fa6a0c2.68daa" ] ] }, { "id": "4fa6a0c2.68daa", "type": "function", "z": "1fee60dc.191fff", "name": "Validate", "func": "var keyring = msg.payload;\nvar submitted = msg.submitted;\nvar driver = Object.keys(submitted)[0].toLowerCase();\n\nvar key = submitted[driver];\n\nif (keyring[driver] && keyring[driver] === key) {\n return [null, msg];\n}\n\nreturn [msg, null];\n", "outputs": "2", "noerr": 0, "x": 360, "y": 220, "wires": [ [ "eba92dae.3e76e" ], [ "ea78a51.6065e58" ] ] }, { "id": "eba92dae.3e76e", "type": "file in", "z": "1fee60dc.191fff", "name": "auth.html", "filename": "/home/pi/.picar/www/auth.html", "format": "utf8", "sendError": true, "x": 500, "y": 200, "wires": [ [ "7a0630ad.57f1d" ] ] }, { "id": "7a0630ad.57f1d", "type": "http response", "z": "1fee60dc.191fff", "name": "", "x": 630, "y": 220, "wires": [] }, { "id": "ea78a51.6065e58", "type": "file in", "z": "1fee60dc.191fff", "name": "main.html", "filename": "/home/pi/.picar/www/main.html", "format": "utf8", "sendError": true, "x": 500, "y": 240, "wires": [ [ "7a0630ad.57f1d" ] ] }, { "id": "2da48ba9.6645b4", "type": "http in", "z": "1fee60dc.191fff", "name": "", "url": "/picar", "method": "post", "upload": false, "swaggerDoc": "", "x": 150, "y": 60, "wires": [ [ "9d08714e.9b236" ] ] }, { "id": "3bd7d0a7.acf3a", "type": "function", "z": "1fee60dc.191fff", "name": "Get Cookie", "func": "var c = msg.req.cookies;\nif (c.carkey){\n msg.payload = c.carkey;\n msg.submitted = JSON.parse(c.carkey);\n return [null, msg];\n}\nreturn [msg, null]", "outputs": "2", "noerr": 0, "x": 370, "y": 100, "wires": [ [ "acc22612.1a8258" ], [ "a6603349.42ce1" ] ] }, { "id": "32ae2dc5.59e942", "type": "hmac", "z": "1fee60dc.191fff",S "name": "", "algorithm": "HmacSHA512", "key": "Enter-HMAC-Key-Here", "x": 130, "y": 140, "wires": [ [ "ab57849c.aa3bc8" ] ] } ]

Open the hmac node by double clicking it. Enter a secret key to encrypt passwords. This key should be the same as the one in the Credentials flow (more on that).

Control

For the flow that will control the GPIOs, paste the following into a new flow.

[ { "id": "df1c684d.9cb458", "type": "websocket in", "z": "ce96cab0.f0ae38", "name": "", "server": "ded96095.9ca83", "client": "", "x": 130, "y": 160, "wires": [ [ "b0e47c94.e6633" ] ] }, { "id": "b0e47c94.e6633", "type": "json", "z": "ce96cab0.f0ae38", "name": "", "x": 290, "y": 160, "wires": [ [ "c6623a05.7892f8" ] ] }, { "id": "c6623a05.7892f8", "type": "function", "z": "ce96cab0.f0ae38", "name": "", "func": "var m = msg.payload;\n\nvar dmv = context.global.get('dmv')||{};\nvar license = m.License;\nvar now = (new Date()).getTime();\nif(dmv[license] + 300000 < now){\n msg.payload = \"Connection Timeout\";\n return [null, null, null, null, msg];\n}\n\nvar thrust = 'Idle';\nvar yaw = 'Straight';\n\nif(m.Forward == 1 && m.Reverse == 1){\n m.Forward = 0;\n m.reverse = 0;\n}\nif(m.Left == 1 && m.Right == 1){\n m.Right = 0;\n m.Left = 0;\n}\nif(m.Forward == 1){thrust = 'Forward';}\nif(m.Reverse == 1){thrust = 'Reverse';}\nif(m.Left == 1){yaw = 'Left';}\nif(m.Right == 1){yaw = 'Right';}\n\nvar text = thrust + ' ' + yaw;\n\nreturn [{payload: m.Forward},\n {payload: m.Reverse},\n {payload: m.Left},\n {payload: m.Right},\n {payload: text}];", "outputs": "5", "noerr": 0, "x": 410, "y": 160, "wires": [ [ "c7ca048b.847218" ], [ "eea3a4ae.fa6728" ], [ "5ae351ad.5a538" ], [ "575f8ccf.7c4274" ], [ "45ffa258.57e9cc" ] ] }, { "id": "45ffa258.57e9cc", "type": "websocket out", "z": "ce96cab0.f0ae38", "name": "", "server": "ded96095.9ca83", "client": "", "x": 560, "y": 280, "wires": [] }, { "id": "c7ca048b.847218", "type": "rpi-gpio out", "z": "ce96cab0.f0ae38", "name": "PIN: 35 - Forward", "pin": "35", "set": true, "level": "0", "freq": "", "out": "out", "x": 570, "y": 40, "wires": [] }, { "id": "5ae351ad.5a538", "type": "rpi-gpio out", "z": "ce96cab0.f0ae38", "name": "PIN: 38 - Left", "pin": "38", "set": true, "level": "0", "freq": "", "out": "out", "x": 570, "y": 160, "wires": [] }, { "id": "eea3a4ae.fa6728", "type": "rpi-gpio out", "z": "ce96cab0.f0ae38", "name": "PIN: 37 - Reverse", "pin": "37", "set": true, "level": "0", "freq": "", "out": "out", "x": 610, "y": 100, "wires": [] }, { "id": "575f8ccf.7c4274", "type": "rpi-gpio out", "z": "ce96cab0.f0ae38", "name": "PIN: 36 - Right", "pin": "36", "set": true, "level": "0", "freq": "", "out": "out", "x": 600, "y": 220, "wires": [] }, { "id": "ded96095.9ca83", "type": "websocket-listener", "z": "ce96cab0.f0ae38", "path": "/ws/picar", "wholemsg": "false" } ]

Note the pins use for controling the car. Top to bottom they are: 35-forward, 37-reverse, 38-left, 36-right. Use these pins when connecting the Pi to the RC car receiver (more on that).

Credentials

Paste the following into a new flow. This flow will allow the creation of username & password combinations with the secret key.

[ { "id": "4f0d436a.ec82ac", "type": "function", "z": "c737272f.d855b8", "name": "Get Submitted", "func": "msg.driver= msg.payload.driver;\nvar keyobject = {};\nkeyobject[msg.driver] = msg.payload.key;\nmsg.payload = JSON.stringify(keyobject);\nreturn msg;", "outputs": 1, "noerr": 0, "x": 160, "y": 100, "wires": [ [ "8c74f440.f638a8" ] ] }, { "id": "b33337a3.31d4d8", "type": "function", "z": "c737272f.d855b8", "name": "Save Cred", "func": "var driver = msg.driver;\nvar key = msg.key;\n\nvar keyring = msg.payload || {};\nkeyring[driver] = key;\n\nmsg.payload = JSON.stringify(keyring);\nreturn msg;", "outputs": "1", "noerr": 0, "x": 410, "y": 180, "wires": [ [ "d07d1ae3.8f6b68" ] ] }, { "id": "d07d1ae3.8f6b68", "type": "file", "z": "c737272f.d855b8", "name": "Put keys", "filename": "/home/pi/.picar/www/keys", "appendNewline": true, "createDir": false, "overwriteFile": "true", "x": 400, "y": 220, "wires": [] }, { "id": "be908f37.6b4ce", "type": "file in", "z": "c737272f.d855b8", "name": "Get keys", "filename": "/home/pi/.picar/www/keys", "format": "utf8", "sendError": true, "x": 400, "y": 100, "wires": [ [ "d2df6932.934d08" ] ] }, { "id": "d2df6932.934d08", "type": "json", "z": "c737272f.d855b8", "name": "", "x": 390, "y": 140, "wires": [ [ "b33337a3.31d4d8" ] ] }, { "id": "8a376d0d.b18bf", "type": "inject", "z": "c737272f.d855b8", "name": "New Driver", "topic": "", "payload": "{\"driver\":\"\", \"key\":\"\"}", "payloadType": "json", "repeat": "", "crontab": "", "once": false, "x": 140, "y": 60, "wires": [ [ "4f0d436a.ec82ac" ] ] }, { "id": "f0dd55f9.38ace8", "type": "function", "z": "c737272f.d855b8", "name": "Get Cred", "func": "msg.key = msg.payload;\nreturn msg;", "outputs": 1, "noerr": 0, "x": 140, "y": 180, "wires": [ [ "c2927ec8.d2996" ] ] }, { "id": "c2927ec8.d2996", "type": "function", "z": "c737272f.d855b8", "name": "keys Exists", "func": "if(!msg.payload){\n return [{payload:'{}'}, null]\n}\nreturn [null, msg];", "outputs": "2", "noerr": 0, "x": 150, "y": 220, "wires": [ [ "761b678e.1e1158" ], [ "be908f37.6b4ce" ] ] }, { "id": "761b678e.1e1158", "type": "file", "z": "c737272f.d855b8", "name": "Make keys", "filename": "/home/pi/.picar/www/keys", "appendNewline": true, "createDir": false, "overwriteFile": "false", "x": 390, "y": 60, "wires": [] }, { "id": "8c74f440.f638a8", "type": "hmac", "z": "c737272f.d855b8", "name": "", "algorithm": "HmacSHA512", "key": "The-same-secret-key-from-the-webserver-flow", "x": 130, "y": 140, "wires": [ [ "f0dd55f9.38ace8" ] ] } ]

Again open the hmac node by double clicking it. Enter the same secret key you used in the Web Server flow.

To create a new driver from this screen open the New Driver node by double clicking it. Edit the payload with the desired credentials.

{"driver":"enter-user-name", "key":"enter-password"}

Click Done and then Deploy. Once the flow is deployed click the box on the left of the New Driver node to inject the credentials. The username and a hash of the password will be added to the "keys" file. You can now log into the page with those credentials. Any additional times this is done adds a new user. If a username is reused it should update that user. To delete a user open the "keys" file in /home/pi/.picar/www/ and delete that entry. I hope to come up with a better solution for that in the future.

View the results

Point a browser to the root of the Pi's webserver on port 1880. http://ip_address:1880/picar . If everything has gone according to the plan the PiCar webpage should be visible. There will be no video for the webcam because that has not been setup. If the direction buttons are pressed the Pi should respond by updating the direction text.


Install Mjpg-Streamer

This section is adapted from Jacob Salmela's excellent guide. You can check it out his site here.

Two Methods: Camera Module or USB Webcam

You can use the Raspberry Pi camera module, or any compatible USB Webcam. I found that that the USB Webcam method is easier and better quality, but there are instructions for both. In the walkthrough, I’ll be using a USB Webcam.

Check If the Pi Recognizes the Webcam

Plug in the USB Webcam and run the command:

lsusb

Look for an entry relating to your Webcam. This is a good indicator it has been recognized.

First Install some of the dependencies of MJPG-Streamer.

sudo apt-get install libjpeg8-dev imagemagick libv4l-dev

An adjustment needs to be made: the videodev.h file has been replaced with videodev2.h but this change needs to be made manually. To do this, just create a symbolic link.

sudo ln -s /usr/include/linux/videodev2.h /usr/include/linux/videodev.h

Download and Compile MJPG-Streamer

Unfortunately, MJPG-Streamer isn’t available via apt-get, so we need to compile it from source. This may be more complex than setting up a Webcam using Motion, but I found MJPG-Streamer to be less resource-intensive so the additional complexity of setting it up is worth it.

There are a few different ways to install MJPG-Streamer. The easiest is via subversion, which is what I will walkthrough.

Download via Subversion (svn)

Install subversion (if necessary).

sudo apt-get install subversion

Then, download a copy of the MJPG-Streamer source code to your home folder and move into the directory when the download is complete:

cd ~ svn co https://svn.code.sf.net/p/mjpg-streamer/code/mjpg-streamer/ mjpg-streamer cd mjpg-streamer

There are a lot of plugins included with MJPG-Stream, but for this walkthrough, we will just compile three. If you only plan to use a USB Webcam, you can modify the command below to suit your needs. Just leave off the items you do not want.

  • input_uvc.so (for USB Webcams): copies JPGs from a single input (the Webcam in this case) to one or more output plugins. This is good for streaming larger images at a higher framerate with lower CPU usage.
  • input_file.so (for camera module): similar to the above, but copies them from a directory.
  • output_http.so: streams the files to a Webserver.

make mjpg_streamer input_file.so input_uvc.so output_http.so

Alternatively, running make, you can compile everything that comes with MJPG-Streamer. I chose to just install what was necessary to save on storage space and resources.

Move MJPG-Streamer to an Alternate Location.

You could run MJPG-Streamer right from the folder after it is compiled (./mjpg_streamer), but it might be better to give it a permanent home.

Run the following commands to copy it to a more globally-accessible area:

sudo cp mjpg_streamer /usr/local/bin sudo cp output_http.so input_file.so input_uvc.so /usr/local/lib/ sudo cp -R www /usr/local/www

/usr/local/ is a common place for third-party items or things added to a system by an admin. But you could put them wherever you like, or just leave it where it is. You will just need to modify the paths for the rest of the walkthrough.

Export Paths

If you did try to run Mjpg-Streamer now, it probably returned an error about search paths. This is because right now, the system doesn’t know where to find the files. This is an easy fix. Just append the following line to ~/.bashrc (assuming you ran the commands above to copy the files to /usr/local/ . This will make it a permanent change so that you don’t need to do this every time you log in:

export LD_LIBRARY_PATH=/usr/local/lib/

Now you can either log out and back in again, but it is easier to run the source command to apply the change (this has the same effect as logging in):

source ~/.bashrc

Now, you can simply call Mjpg-Streamer no matter what directory you are in. We are almost ready to start capturing images.

Install A Patch

Many users reported a blank white screen when trying to use it. Apparently, this patch fixes it.

cd mjpg-streamer-code/mjpg-streamer patch -p0 < input_uvc_patch.txt make USE_LIBV4L2=true clean all sudo make DESTDIR=/usr/local install

Using input_uvc.so to Capture Images

Start running Mjpg-Streamer with the command:

/usr/local/bin/mjpg_streamer -i "/usr/local/lib/input_uvc.so" -o "/usr/local/lib/output_http.so -w /usr/local/www"

These command options are as follows:

  • -i: uses input_uvc.so (the USB Webcam) as input
  • -o: output_http.so for the output (sending the images to a Web server)
  • -w: the directory, which has the HTML, CSS, and JS files: /usr/local/www

You can cancel the stream by pressing Ctrl+C. If you want to experiment with some other options, some are listed below:

  • -b: runs in the background.
  • -p: set a default port instead of the default 8080.
  • -c: prompts for username:password.
  • -y YUYV: enables YUYV format and disables MJPEG mode.
  • -fps: framerate (in seconds.)

If you used the -b option, you will get your prompt back. So how do you stop it from running? After running the command, you will see a line like:

forked to background (4979)

If you want to stop the stream, just run:

kill 4979

which just kills the process ID (PID) of MJPG-Streamer.

View the Webcam Live-stream From a Browser

(while connected to your local network)

Even if you have already set up a Webserver, you can still run this without problem because it is accessed on a different (default) port: 8080. So when you navigate to it in a browser, just append :8080 after the IP address:

  • locally: http://localhost:8080
  • from another device on the network: http://ip_address:8080

I will be skipping the section "Accessing Your Pi Over the Internet" from the original guide. That will be covered later. Now continue on to "Run MJPG-Streamer as a Daemon (Background Service)"

Run MJPG-Streamer as a Daemon (Background Service)

Until now, we have just been launching MJPG-Streamer on an as-needed basis by running a command with our options. If you want to make this more permanent and have the Raspberry Pi start the Webcam stream when it boots up, we need to tell it to do so.

There are two scripts below: a very basic one that just runs the command, and an advanced script that will allow you to use the service command to control it:

sudo service livestream.sh start sudo service livestream.sh stop sudo service livestream.sh restart

Simple Script

A very simple script to do this is below:

* I have made some changes to the command from the original guide to optimize the camera for the Pi Car.

  • -r 320x240: set the resolution. This lower resolution is faster and good enough for smaller screens like iPhones and Android devices
  • -fps 15: sets the frame rate to 15 frames per second. That keeps the stream fast enough to see where the car is going.
  • -q 50: Lowers the image quality for a faster feed. 50 is fast and an acceptable quality.

sudo nano /etc/init.d/livestream.sh

Paste or type the following.

#!/bin/bash usr/local/bin/mjpg_streamer -b -i "/usr/local/lib/input_uvc.so -y -r 320x240 -fps 15 -q 50" -o "/usr/local/lib/output_http.so -w /usr/local/www"

Save the script. Make it executable:

sudo chmod 755 /etc/init.d/livestream.sh

This next command makes sure it is executed during boot:

sudo update-rc.d livestream.sh defaults

Advanced Script

Below is a script that should load MJPG-Streamer at boot and allow use of the service command:

#!/bin/sh # /etc/init.d/livestream.sh ### BEGIN INIT INFO # Provides: livestream.sh # Required-Start: $network # Required-Stop: $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: mjpg_streamer for webcam # Description: Streams /dev/video0 to http://IP/?action=stream ### END INIT INFO f_message(){ echo "[+] $1" } # Carry out specific functions when asked to by the system case "$1" in start) f_message "Starting mjpg_streamer" usr/local/bin/mjpg_streamer -b -i "/usr/local/lib/input_uvc.so -y -r 320x240 -fps 15 -q 50" -o "/usr/local/lib/output_http.so -w /usr/local/www" sleep 2 f_message "mjpg_streamer started" ;; stop) f_message "Stopping mjpg_streamer…" killall mjpg_streamer f_message "mjpg_streamer stopped" ;; restart) f_message "Restarting daemon: mjpg_streamer" killall mjpg_streamer usr/local/bin/mjpg_streamer -b -i "/usr/local/lib/input_uvc.so -y -r 320x240 -fps 15 -q 50" -o "/usr/local/lib/output_http.so -w /usr/local/www" sleep 2 f_message "Restarted daemon: mjpg_streamer" ;; status) pid=`ps -A | grep mjpg_streamer | grep -v "grep" | grep -v mjpg_streamer. | awk ‘{print $1}’ | head -n 1` if [ -n "$pid" ]; then f_message "mjpg_streamer is running with pid ${pid}" f_message "mjpg_streamer was started with the following command line" cat /proc/${pid}/cmdline ; echo "" else f_message "Could not find mjpg_streamer running" fi ;; *) f_message "Usage: $0 {start|stop|status|restart}" exit 1 ;; esac exit 0


Once saved, run the same commands as above:

sudo chmod 755 /etc/init.d/livestream.sh sudo update-rc.d livestream.sh defaults

Success!

Viewing Mjpg-Stream on the Pi Car

Now if you go to the Pi Car webserver address http://ip_address:1880/picar You should see the video. Sometimes Mjpg-Streamer will not start if there is no camera connected. To ensure video works, connect the camera before booting the Pi.


Assemble the Car

Open Car Up

Remove the car cover and expose the receiver board. On that board there is a chip, it should be the only or largest chip. Here is a data sheet for a transmitter and receiver that is common to all RC cars. RX2_TX2_Datasheet.pdf On page 5 the second chart lists the pins for RX2. Note the pin numbers.

  • Pin 6 (Right)
  • Pin 7 (Left)
  • Pin 10 (Backward)
  • Pin 11 (Forward)

These pins are the same across most cars.

There are two types of chips you may find.

  • Surface mount
     More and more cars are using surface mount chips. These make things a little harder to solder as the connectors are very small. In this case you may want to leave the chip on and just solder to the appropiate leg. Leaving the chip on shouldn't matter.

  • Through Hole
     Older cars and cheaper cars will have larger through hole chips. It is sometimes easier to remove the chip and solder directly to the board. In this case use the male end of a jumper wire to go through the hole.

Solder four jumper wires at least 5 inches long to the four direction pins. Some cars have the chip face down so try to color code your wires for easy reference. Solder one more wire to the ground pin of the chip. I prefer a black wire for ground.

Connect the Battery

Also on the board there should be wires to the battery. Cut cut those wires as far from the board as you can. If the battery was connected via a pcb mount battery holder, solder some wires at least 6 inches from the board to those.

Open the USB car charger and expose the circuits within. Solder two wires to the positive and ground connections at least 6 inches from the board.

With the ends of the power wires from both the receiver and the USB charger, crimp them into a Tamiya connector together, positive with positive and ground with ground. Use a female connector so it can connected to the battery.

Test the Receiver

Connect your battery to your new connector. Make sure the battery is charged, and if the car has one, the power switch is in the on position. Connect the ground pin or wire to the ground of the power. Apply 5 volts to the wires or pins you have on the receiver. Set the car up on a block before forward or backward. If the car responds correctly, congratulations you have not bought a dud. If not, check your soldering.

Connect the Raspberry Pi

Plug the web cam into the Pi and either attach the Pi to external power or the battery. Connect the female end of the jumper to the pins that correspond to Node-Red. Connect the ground pin from the receiver to the ground pin on the Raspberry Pi.

  • 35 - Forward
  • 37 - Reverse
  • 38 - Left
  • 36 - Right
  • 34 - Ground

Go to the Pi's web page http://ip_address:1880/picar

and test the controls. If there is no response check the flow at http://ip_address:1880
If every thing works, celebrate.

Button it Up

TBC...