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.