1 Line PHP Servers for Raspberry Pi Projects

I wanted to introduce my daughters to using PHP on Raspberry Pi Projects. My goal has been to try and keep things super minimalist so that they can quickly pull together a project for school without being overwhelmed with installation or dependency issues.

In this blog I will look at:

  • Running a 1-Line PHP server (no other web server will be used or installed)
  • Show Bash diagnostics tools on a PHP page (3 lines of PHP)
  • Use a PHP calls to read/write Rasp Pi GPIO pins

Loading and Running a PHP Server

Typically PHP is loaded with Web server applications like Apache, but PHP can be run as a standalone application. To install PHP on a Raspberry Pi or Ubuntu system:

sudo apt-get install php -y

The syntax to run PHP as a standalone web server:

php -S <addr>:<port> [-t docroot] startpage

# for example
php -S 192.168.0.111:8080 mypage.htm

# to use Bash to get your IP
php -S $(hostname -I | awk '{print $1 ":8080"}') mypage.htm 

It’s important to note that the standalone PHP server is designed for testing and very small projects. It should not be used for any kind of production environment.

Superquick PHP Primer

PHP is an extremely popular Web programming language.

PHP originally stood for : Personal Home Page, but after a lot of enhancements it now stands for: “PHP: Hypertext Preprocessor”.

A PHP file normally contains HTML tags, and some PHP scripting code. A PHP script starts with “<?php” and ends with “?>“. An example would be (test1.php) :

<!DOCTYPE html>
<html>
<body>

<h1>A PHP Test Page</h1>

<?php
// This is PHP script block
$a = 4;
$b = 6;
echo "Hello World! ";
echo "4 x 6 =" . ($a * $b);
?>
<p>Below is a single line of PHP</p>
<?php echo "This is a single line" ?>

</body>
</html>

To learn about the PHP syntax there are some good references.

To run this page:

php -S $(hostname -I | awk '{print $1 ") test1.php

It’s useful to know that PHP code can be run directly with the PHP interpreter:

# A single line of PHP (note: -r for single line)
$ php -r '$a=4; $b=5; echo  $a * $b . "\n" ; '
20

# Run a PHP script 
$ php test1.php

<!DOCTYPE html>
<html>
<body>

<h1>A PHP Test Page</h1>

Hello World! 4 x 6 =24<p>Below is a single line of PHP</p>
This is a single line
</body>
</html> 

Command line Diagnostics on a Web Page

Linux command line utilities can have their output viewable on a Web page. The example below (vmstats.php) uses the PHP shell_exec call to execute the vmstat command line utility and the results are echoed to the web page. The <pre> tag is used to present the results in fixed-width font.

<?php
// vmstats.php - A PHP test page to some CPU stats 
echo "<pre>";
echo shell_exec('vmstat');
echo "</pre>";
?>

A 1 line PHP web server command to call this page is:

php -S $(hostname -I | awk '{print $1 ":8080") vmstats.php

PHP Interfacing to Pi GPIO

There are a few ways to access the GPIO pins in PHP:
1. use a PHP library
2. shell to the gpio command

Using a PHP library allows for a standard PHP interface, with an object model.

However from testing I found that the PHP libraries were not as flexible as the standard gpio command. For example you could not access extended GPIO pin numbers (i.e. 200).

GPIO Command Line Utility

PHP can shell out to the gpio command line utility. I liked this approach because I could test the actions manually before putting them into a PHP web page.

A simple gpio read example would be:

<html lang="en">
<head>
</head>
<body>
<?php
$ret = shell_exec('gpio read 7');
echo "Pin 7 status = " . $ret;
?>
</body>
</html>

And a gpio write example (with reading back the result) would be:

<html>
<head>
</head>
<body>
<?php
exec("gpio write 7 1");
$ret = shell_exec('gpio read 7');
echo "Pin 7 status = " . $ret;
?>
</body>
</html>

Control A Pi Rover using PHP

The Raspberry Pi rover project is a good example that pulls together:

  • Control of Pi GPIO pins
  • PHP Forms
  • Cascading Style Sheets (CSS) for mobile phone presentations

Motors should not be connected directly to a Raspberry Pi because they could potential damage the Pi hardware. It is recommended that some intermediate equipment or a motor top be used. For this project we used a Pimoroni ExplorerHat Pro.

PHP Forms

This example uses an HTML post method that is defined in the <form> tag. The buttons pass the values of: go, stop, left and right.

The PHP code look for the post value and then does a gpio write for the required action. For example a go will turn on both GPIO motor pins.

The full PHP code is below:

<?php
// rover.php - control Rasp Pi GPIO pins using PHP form with buttons
//

// adjust pins for the specific motor setup 
$leftpin = 7;
$rightpin = 2;

// Get post feedback message
if (isset($_POST['submit'])) {
	switch ($_POST['submit']) {
		case "go":
			exec("gpio write " . $leftpin . " 1");
			exec("gpio write " . $rightpin . " 1");
			break;
		case "stop":
			exec("gpio write " . $leftpin . " 0");
			exec("gpio write " . $rightpin . " 0");
			break;
		case "left":
			exec("gpio write " . $leftpin . " 1");
			exec("gpio write " . $rightpin . " 0");
			break;
		case "right":
			exec("gpio write " . $leftpin . " 0");
			exec("gpio write " . $rightpin . " 1");
			break;
	}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
  <title>PHP/Pi Rover Controls</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">

  <h2>PI Four Button Example</h2>
  <form action="" method="post">
    <div class="form-group">

    <button type="submit" name="submit" class="btn-success btn-lg" style="width:100%" value="go">Forward</button>
    <button type="submit" name="submit" class="btn-info btn-lg" style="width:49%" value="left">Left</button>
    <button type="submit" name="submit" class="btn-info btn-lg" style="width:49%" value="right">Right</button>
    <button type="submit" name="submit" class="btn-danger btn-lg" style="width:100%" value="stop">Stop</button>
  </form>
</div>

</body>
</html>

Mobile CCS Templates

There are quite a few good mobile templates to choose from. Bootstrap (http://getbootstrap.com/) is one of the most popular frameworks, and for Pi applications it seems to be a good fit.

Some of the key items are:

  • Add references in to the bootstrap ccs and js files
  • Add tags with the required class definitions:
    • the btn-lg class will make a large button, instead of standard sized btn 
    • different button colours are possible using btn-info, btn-success. btn-danger
    • Button width is defined with style=”width: xx%” . For multiple buttons the sum of the width needs to <100%

Further Examples

Below are some pictures of a mobile rocket launcher project.  The Web page had two sections. The top section controlled bi-directional motors that were connected to a Explorer HAT Pro shield. The bottom section controlled the rocket launcher turret. The missile launcher was connected via a USB cable to the Pi.

Screenshot
OLYMPUS DIGITAL CAMERA

 

 

NodeJS Raspberry Pi Rover

My typical Raspberry Pi projects are done in Python. I thought that I’d try some Node.js testing because I find the standalone Python Webserver (http.server library) to be a little slow on the Pi hardware.

Getting Started with Node.js on Raspberry Pi

Node.js can be installed on your Pi by:

$ sudo apt-get update
$ sudo apt-get install -y nodejs

There are a few options on how to talk to the GPIO (General Purpose Input/Output) pins on the Pi. I tested a few and I found that pigpio worked well for my setup. It is installed by:

sudo apt-get install pigpio

To set a GPIO pin to be an output and next to turn it on, a simple Node.js program (gpio.js) would be:

// gpio.js - set GPIO pin 4 to ON

const Gpio = require('pigpio').Gpio; 
const led = new Gpio(4, {mode: Gpio.OUTPUT});
led.digitalWrite(1);

To run the program:

sudo node gpio.js

It is important to note that access to the GPIO pins require admin rights so you will need to run your scripts with sudo (super user do).

Node.js Webserver

To make a simple webserver (mywebserver.js) :

// mywebserver.js - a simple websever on port 8080
//
var http = require('http').createServer(handler); //require http server, and create server with function handler()
var fs = require('fs'); //require filesystem module

http.listen(8080); //listen to port 8080

function handler (req, res) { //create server
  fs.readFile(__dirname + '/index.html', function(err, data) { //read file index.html in public folder
    if (err) {
      res.writeHead(404, {'Content-Type': 'text/html'}); //display 404 on error
      return res.end("404 Not Found");
    } 
    res.writeHead(200, {'Content-Type': 'text/html'}); //write HTML
    res.write(data); //write data from index.html
    return res.end();
  });
}

This script references the http and the fs (file system) modules, and this allow us to reference an external index.html page. The handler function is used to manage the HTTP requests.

Next you’ll need to make an index.html page, below is a simple example:

<!DOCTYPE html>
<html>
<body>
<h1>Dummy HTML Test Page</h1><hr>
</html>
</body>
</html>

To test this page run: node mywebserver.js , and from a browser use your Pi’s IP address with port 8080, for example : 

dummy

Make the Web Page Dynamic

To make the Web Page dynamic we can use the socket.io package. It is installed by:

$ npm install socket.io --save

By using socket.io, javascript functions on the web page can communicate to functions running on the Node.js web server.socketio

Once a function is created in the Node.js webserver application the Web page can pass data to that function.

The Raspberry Pi Rover

There are some low cost Arduino car frames that cost under $10. These car frames can be used with a Raspberry Pi but you will need to be careful on how the motors are powered. Depending on the motors you might be able to drive them directly from Pi GPIO pins, however it is recommended that you use some external hardware to protect your Pi. There are some good Pi motor top options available, for my project I used the Pimoroni Explorer Hat Pro

js_rover2

My hardware setup used a Pi 3 with a portable phone charger. I used some duct tape to secure the wiring, charger and Pi together.

The motor pins will vary based on hardware that you use, so my code my need to be tweeked for your setup. The ‘control’ function is what I used to define the motor state. Some key words : forward, left, right, stop or back were passed between the web page and the server app to define the rover’s motor action. 

// ws_2_rover.js - NodeJS WebServer App to control a Rover

var http = require('http').createServer(handler); //require http server, and create server with function handler()
var fs = require('fs'); //require filesystem module
var io = require('socket.io')(http) //require socket.io module and pass the http object (server)

const Gpio = require('pigpio').Gpio;
// modify for your motor pinouts
const MOTOR1 = new Gpio(21, {mode: Gpio.OUTPUT}); 
const MOTOR2 = new Gpio(19, {mode: Gpio.OUTPUT}); 
const MOTOR3 = new Gpio(20, {mode: Gpio.OUTPUT}); 
const MOTOR4 = new Gpio(26, {mode: Gpio.OUTPUT}); 

// Ensure that the rover app starts without the motors running
function rover_stop() {
    MOTOR1.digitalWrite(0);
    MOTOR2.digitalWrite(0);
    MOTOR3.digitalWrite(0);
    MOTOR4.digitalWrite(0);
}

http.listen(8080); //listen to port 8080

function handler (req, res) { //create server

  fs.readFile(__dirname + '/web_2_rover.htm', function(err, data) { //read file index.html in public folder
    if (err) {
      res.writeHead(404, {'Content-Type': 'text/html'}); //display 404 on error
      return res.end("404 Not Found");
    } 
    res.writeHead(200, {'Content-Type': 'text/html'}); //write HTML
    res.write(data); //write data from index.html
    return res.end();
  });
}

rover_stop();

io.sockets.on('connection', function (socket) {// WebSocket Connection
  console.log('A user connected');
  var controlvalue = ""; // variable for current status of the rover

  socket.on('control', function(data) { //get light switch status from client
    controlvalue = data;
    console.log('control input: ' + data);

    rover_stop(); // stop the motors, and then do the required action

    if (controlvalue == "forward") { 
      MOTOR1.digitalWrite(1); 
      MOTOR2.digitalWrite(1); 
    }
    if (controlvalue == "left") { 
      MOTOR2.digitalWrite(1); 
    }
    if (controlvalue == "right") { 
      MOTOR1.digitalWrite(1); 
    }

    if (controlvalue == "backward") { 
      MOTOR3.digitalWrite(1); 
      MOTOR4.digitalWrite(1); 
    }

  });
});

I used the Bootstrap template to offer a mobile friendly web interface. A button onclick function was used to pass the requested motor action (forward, left, right, stop, backward) to the control socket function. Below is my web page (web_2_rover.htm):

<!DOCTYPE html>
<html>
<head>
<title>NodeJS Web Rover Control</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.3/socket.io.js"></script>

<script>
var socket = io(); //load socket.io-client and connect to the host that serves the page
</script>
</head>

<body>
<div class="container">
  <h1>NodeJS Web Rover Control</h1>
  <button onclick="socket.emit('control','forward')" class="btn btn-success" style="width: 100%">Forwards</button>
  <button onclick="socket.emit('control','left')" class="btn btn-primary" style="width: 49%">Left</button>
  <button onclick="socket.emit('control','right')" class="btn btn-primary" style="width: 49%">Right</button>
  <button onclick="socket.emit('control','stop')" class="btn btn-danger" style="width: 100%">Stop</button>
  <button onclick="socket.emit('control','backward')" class="btn btn-warning" style="width: 100%">Backwards</button>     
</div>
</body>
</html>

To run the rover app enter:

sudo node ws_2_rover.js

Final Comments

I have done this project also in Python. The Python code is a little cleaner because the Pimoroni Explorer Hat has a Python library so I could easily adjust the motor speeds. However I found that the Node.js web interface to be a little faster than Python on the Pi.

 

 

Le gocart (Python Tkinter GUI)

For this project we wanted to control a Lego vehicle with a Python Tkinter app. Next we added a short cut to the Pi desktop and then we used VNC to see the Pi desktop and our app on a tablet.

Hardware Setup

Our hardware components were:

  • Raspberry Pi 3
  • Pimoroni ExplorerHat Pro – supports bi-directional DC motors
  • Dexter Connectors – allow 2 wire connections to Lego Mindstorms parts
  • 2 Lego Mindstorms motors
  • Portable USB charger
  • lots of Lego parts
  • 4 jumpers

le_gocart_parts

The Lego Mindstorms parts are little pricey but they allow you to make some pretty funky contraptions. The other thing that we like about the Mindstorms motors is that they have a lot of torque for a 5V DC motor.

There are a few options for the cabling (like cutting the cable and exposing the individual wires) we used the Dexter connectors that are breadboard friendly. ANA and GND connections on the Dexter side go to Motor + and Motor – on the ExplorerHat Pro board.

le_gocart_wiring

 

Python Tkinter

The Tkinter library allows you to create a simple graphic user interface (GUI) with components like: buttons, sliders, lists, text, labels etc.

For our interface we created a grid of 3 rows and 2 columns with 5 buttons. We made a simple motor function where we passed the speed and direction of the wheels. A negative speed is backwards, zero is stop, and a positive speed is forward.

import Tkinter
import explorerhat 

top = Tkinter.Tk()
top.title("Car Control")

explorerhat.motor.one.speed(0)
explorerhat.motor.one.speed(0)

#Define the buttons

def motor(Left,Right):
 explorerhat.motor.one.speed(Right)
 explorerhat.motor.two.speed(Left)

B_Left = Tkinter.Button(top, text ="Left", bg = "green", fg = "white", width= 15, height= 5, command = lambda: motor (50,0)).grid(row=1,column=1)
B_Right = Tkinter.Button(top, text ="Right", bg = "green", fg = "white", width= 15, height= 5, command = lambda: motor (0,50)).grid(row=1,column=2)
B_Forward= Tkinter.Button(top, text ="Forward", bg = "green", fg = "white", width= 15, height= 5, command = lambda: motor (50,50)).grid(row=2,column=1)
B_Backward = Tkinter.Button(top, text ="Backward", bg = "green", fg = "white", width= 15, height= 5, command = lambda: motor (-50,-50)).grid(row=2,column=2)
B_Stop = Tkinter.Button(top, text ="Stop", bg = "red", fg = "white", width= 33, height= 3, command = lambda: motor (0,0)).grid(row=3,column=1,columnspan=2)

top.mainloop()

VNC1

Pi Shortcut

To create a Pi shortcut, create a file:

nano $HOME/desktop/pi.desktop

Inside this file define the name, path, and icon info for your new application:

[Desktop Entry]
Name=Car Controls
Comment=Python Tkinter Car Control Panel
Icon=/home/pi/car1.png
Exec=python /home/pi/mycarapp.py
Type=Application
Terminal=false
Categories=None;

VNC (Virtual Network Computing)

VNC is install on the Raspbian image. To enable VNC run:

sudo raspi-config

Then select the interfacing option, and then select VNC and enable.

raspi-config-vnc

Finally you will need to define a VNC password and load some VNC software on your Tablet. There are a lot of packages to choose from. We have an Android table and we used RemoteToGo without any problems.

Note, when your Pi boots without a HDMI monitor connected the desktop resolution will be at a low setting (probably 800×600) this can be adjusted. For us we simply resized the desktop to fit our tablet screen.

Tow Truck

The goal for our tow truck was to have a 4-axis crane and a movable vehicle that we could remotely control with an Android smart phone.

tow_truck2

The parts we used for this project were:

Hardware Setup

The tow truck project used a camera mount for up/down/left/right crane motion and an Arduino car chassis for mobility. The controls were done using Bluetooth.

Meccano was used to build a box for the main structure. Wire was used to secure everything together. We laid a folded piece of paper under the Arduino Mega to ensure that none of the Arduino solder connections shorted on the metal Meccano base.

tow_truck_inside

The motor and servo shield that we used did not expose any of the extra Arduino pins, so we needed to use the Mega board. We then wired the Bluetooth module to the exposed pins on the end of the Mega.

tow_truck_circuit

Arduino Code

The Arduino code will vary a little based on the motor/servo shield that is used. Our shield was an older version 1 (V1) board that used direct pin connections (no I2C or SDA/SCL connections).

Also because Tx/Rx (Tx0/Rx)) were not available once our motor/servo shield was installed we used Tx1/Rx1 and so our Bluetooth connection was on Serial1 and not Serial.

For the Bluetooth communications we used the following command letters:

  • R = drive right
  • L = drive left
  • f = drive forwards
  • b = drive backwards
  • s = stop driving
  • r = move crane right
  • l = move crane left
  • u= move crane up
  • d = move crane down

Our Arduino code is below:

#include <Servo.h> 

Servo servo1;
Servo servo2;

char thecmd;
int xpos = 90;
int ypos = 90;

AF_DCMotor motor1(1); 
AF_DCMotor motor2(2);

void setup() {
  pinMode( 19, INPUT_PULLUP );
  Serial1.begin(9600);
  Serial1.println("Crane Controls");
  Serial1.println("r = right, l = left, u= up, d = down");
  Serial1.println("Driving Controls");
  Serial1.println("R = right, L = left, f = forwards, b = backwards, s = stop");
  servo1.attach(9); // attaches the servo on pin 9 to the servo object 
  servo2.attach(10); // attaches the servo on pin 9 to the servo object 
  
  servo1.write(xpos);
  servo2.write(ypos);

  motor1.setSpeed(255);
  motor2.setSpeed(255);

}
void loop() {
  
  if (Serial1.available() > 0) {
        // read the incoming byte: 
       thecmd = Serial1.read();
       Serial1.println(thecmd);
       if (thecmd =='l') { move_crane(servo1, 5); }
       if (thecmd =='r') { move_crane(servo1, -5); }
       if (thecmd =='d') { move_crane(servo2, 5); }
       if (thecmd =='u') { move_crane(servo2, -5); }
       if (thecmd =='f') { 
            motor1.run(FORWARD); 
            motor2.run(FORWARD); 
       }
        if (thecmd =='b') { 
            motor1.run(BACKWARD); 
            motor2.run(BACKWARD); 
       }
       if (thecmd =='L') { 
           motor1.run(BACKWARD); 
           motor2.run(FORWARD); 
       }
       if (thecmd =='R') { 
            motor1.run(FORWARD); 
            motor2.run(BACKWARD); 
       }
       if (thecmd =='s') { 
           motor1.run(RELEASE); 
           motor2.run(RELEASE); 
       }
  }
}

void move_crane(Servo theservo, int direction) {
  int minpos = 50;
  int maxpos = 220;
  if (direction < 0) {
    if (ypos > minpos) {
      ypos = ypos + direction;
      theservo.write(ypos);
    }
  }
  else {
    if (ypos < maxpos) {
      ypos = ypos + direction;
      theservo.write(ypos); 
    }   
  }
}

Android Program

To communication to an Android smart phone we used MIT’s App inventor. This is a free Web based Android development tool.

There are many ways to layout a control screen, for us we used a 10×3 table and then populated it with buttons. Our layout is shown below:

ai_layout

The button logic will pass the required letter command to the Bluetooth component:

ai_logic

Our final running App looked like:

Screenshot_tow_truck

Pi/Node-Red Car

The goal of the Pi/Node-Red car project was to create a small vehicle that can be controlled from a smart phone . For the project we used:

  • 1 Car chassis for Arduino ($15)
  • 1 Pimoroni Explorer HAT Pro  ($23)
  • 1 Portable microUSB charger
  • 1 USB WiFi Adapter
  • 4 short alligator clips and 4 connectors
  • Duct tape

The Arduino car chassis may require a small amount of assembly. Rather than soldering connections we like to use short alligator clips. It is not recommended to wire DC motors directly to a Raspberry Pi so the Pimoroni Explorer HAT Pro is used to connect the 2 DC motors.

The Raspberry Pi and the portable microUSB charger are secured to the top of the car chassis with duct tape. The left motor is wired to the motor 1 connectors on the Explorer Hat, and the right motor is wired to motor 2 connectors. Note you may have to do a little trial and error on the Explorer HAT “+” and “-” motor connections to get both wheels spinning in a forward direction.

The Explorer HAT Node-Red library is installed by:

 cd $HOME/.node-red
npm install node-red-dashboard 

The Web dashboard presentation is configured in the “dashboard” tab. For this example we create 2 groups: a control group to drive the vehicle, and a light group to turn on the Explorer Pro lights. Use the “+group” button to add a group, and the “edit” to change an existing group.
dash_conf

To control a motor, an “Explorer HAT” node and a dashboard button node are dropped and connected together. All the configuration is done in the button node . The button configure options are:

  • the group the button will appear in (Controls)
  • the size of the button (3×1 = 50% of width and narrow)
  • Topic, motor.one or motor.twois used for motor control
  • Payload, -100 = reverse, 0=stop, 100 = forward

Control_conf

The Explorer HAT has 4 colored LEDs. To toggle the LEDS, the topic is light.color with 1=ON, and 0=OFF . We thought that it would be fun to also add some Web dashboard button to control the colored lights.

light_conf

The Node-Red dashboard user interface is accessed by: ipaddress:1880/UI, so for example 192.168.1.102:1880/ui. Below is a picture that shows the final vehicle logic and the Web dashboard.

 

final_logic2