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

 

 

Bash Bar Charts for Text and Web Pages

This blog documents my notes on creating Bash text bar charts. The key topics are:

  • Create a simple horizontal bars (3 lines)
  • Create dynamic bars (~25 lines for an array of data values)
  • Add support for piped and command line data
  • Include options for: help, resizing, titles
  • Add colour and HTML support

A Horizontal Bar

To create a static bar an printf statements can be used. The seq {0..10} can be used to repeat an ASCII █ fill character 10 times.

$ printf 'LABEL: ' ; \
  printf '█%.0s' {1..10} ; \
  printf ' 10\n'

LABEL: ████████████████████ 10

Unfortunately the printf statement has some limitations on variable substitution. A simple workaround is to create a string and then eval it:

$ label="temp"; val=20;
$ bar="printf '█%.0s' {1..$val}" ; 
$ printf '\n%-5s ' $label; eval $bar ; printf ' %d\n' $val

temp  ████████████████████ 20

Coloured Bars

The tput setaf command can change the foreground, and tput setab is used for background colours. Colour codes are:

tput setab [1-7] # Set the background colour using ANSI escape
tput setaf [1-7] # Set the foreground colour using ANSI escape

Num  Colour    #define         R G B

0    black     COLOR_BLACK     0,0,0
1    red       COLOR_RED       1,0,0
2    green     COLOR_GREEN     0,1,0
3    yellow    COLOR_YELLOW    1,1,0
4    blue      COLOR_BLUE      0,0,1
5    magenta   COLOR_MAGENTA   1,0,1
6    cyan      COLOR_CYAN      0,1,1
7    white     COLOR_WHITE     1,1,1

To reset colours back to the defaults use: tput sgr0

An example to print a red bar and a stack of bars:

$ printf '\nLABEL: ' ; \
   tput setaf 1 ;\
   printf '█%.0s' {1..10} ; \
   printf ' 10\n'

LABEL: ██████████ 10
$ printf '\n 3 Stacked Bars: ' ; \
   tput setaf 1 ;\
   printf '█%.0s' {1..10} ; \
   tput setaf 2 ;\
   printf '█%.0s' {1..8} ; \
   tput setaf 4 ;\
   printf '█%.0s' {1..3} ; \
   printf ' 10+8+3=21\n'

 3 Stacked Bars: █████████████████████ 10+8+3=21

Dynamic Bars

The next step is to create a script that dynamically updates the bars. The tput clear command will clear the terminal screen keep the data and bars in the same location. The script below will dynamically show the CPU temperature, idle time and 2 random values with a 10 second update time.

#!/bin/bash
# 
# cpu_bars.sh - Show new data every 10 seconds
#
while :; do
    # Get data values
    CPUtemp=$(sensors | grep CPU | awk '{print substr($2,2,4)}')
    CPUidle=$(iostat | awk '{if (NR==4) print $6}')
    Random1=$((1+ $RANDOM % 100))
    Random2=$((1+ $RANDOM % 100))

    labels=( CPUtemp CPUidle Random1 Random2)
    values=( $CPUtemp $CPUidle $Random1 $Random2)
    units=( degC % psi mm)

    # Show a title
    tput clear
    printf " %10s " "" 
    tput setaf 7; tput smul;
    printf "%s\n\n" "Show CPU Data ($(date +%T'))"
    tput rmul;

    # cycle thru data and show a label, 
    for index in "${!labels[@]}"
    do
          tput setaf $(expr $index + 1); # don't use 0 (black) 
          printf " %10s " "${labels[index]}"
          eval "printf '█%.0s' {1..${values[index]%.*}}"
          printf " %s %s\n\n" ${values[index]} ${units[index]}
    done
    sleep 10
done

This script is run by: bash cpu_bars.sh .Typical output is below.

Bars in HTML

The ANSI colours are not supported in HTML, so instead HTML tags and style properties can be used.

It is important to use <pre> tags for Bash text output. Code to create two static bars in HTML would be:

$ (printf "<h1>A Bar from Bash</h1>\n" 
 printf "<pre><span style='font-size:24px;color:red'}>\n"
 printf 'LABEL1: ' ; printf '█%.0s' {1..10} ; printf ' 10\n'
 printf "</pre></span>\n") > bar1.htm

$ cat bar1.htm
<h1>A Bar from Bash</h1>
<pre><span style='font-size:24px;color:red'}>
LABEL1: ██████████ 10
</pre></span>

The script cpu_webbars.sh creates HTML output for an array of calculated values:

#!/bin/bash
# 
# cpu_webbars.sh - Show bars in HTML
#

# Get data values
CPUtemp=$(sensors | grep CPU | awk '{print substr($2,2,4)}')
CPUidle=$(iostat | awk '{if (NR==4) print $6}')
Random1=$((1+ $RANDOM % 100))
Random2=$((1+ $RANDOM % 100))

labels=( CPUtemp CPUidle Random1 Random2)
values=( $CPUtemp $CPUidle $Random1 $Random2)
units=( degC % psi mm)
colors=(red blue green magenta)

# Show a title
printf "<h1><center>Show CPU Data ($(date '+%T'))</center></h1>\n"
# cycle thru data and show a label, 
for index in "${!labels[@]}"
  do
  printf "<pre><span style='font-size:18px;color: ${colors[index]} '}>\n"
  printf " %10s " "${labels[index]}"
  eval "printf '█%.0s' {1..${values[index]%.*}}"
  printf " %s %s\n\n" ${values[index]} ${units[index]}
  printf "</pre></span>\n"
done

This script can be run and outputted to an file: bash cpu_webbars.sh > test.htm

Once the basic HTML output is working its possible to add headers and footers to make a more complete page:

header.htm > test.htm ; \
cat cpu_webbars.sh >> test.htm ; \
cat footer >> test.htm 

Piping Data to Bars

A script (hbar0.sh) will read the piped data and then create an array (data) of labels and values. The data array is cycled through and labels and bars are shown with a randomized colour:

#!/bin/bash
# hbar0.sh - Read in piped data  and plot bars
#     format: label,value;label2,value2;  and plot bars
#
input=$(< /dev/stdin) ; # read piped data
# remove new lines in files, and check for ";" after data pair 
input=$(echo $input | tr -d '\n')
IFS=';' read -r -a data <<< $input
printf "\n" 
for element in "${data[@]}"pete@lubuntu:~/Writing/Blog/text_bars
do 
  # make at array of each data element
  IFS=',' read -r -a datapt <<< $element
  # add a random color
  tput setaf $((1+ $RANDOM % 7))
  # print the label, bar and value
  printf " %10s " "${datapt##*[0]}"
  bar="printf '█%.0s' {1..${datapt[1]}}"
  eval $bar
  printf " %s\n\n" ${datapt[1]} 
  tput rmso ; # exit color mode   
done

The script can be tested with piped data:

$ echo "temp,33;pressure,44" | bash hbar0.sh

 temp       █████████████████████████████████ 33 

 pressure   ████████████████████████████████████████████ 44 

A data file can also be passed in using the cat command:

$ cat data0.txt 
temp,44;
humidity,33;
pressure,15;
wind spd,33;
wave ht,3;
$ cat data0.txt | bash hbar0.sh

       temp ████████████████████████████████████████████ 44

   humidity █████████████████████████████████ 33

   pressure ███████████████ 15

   wind spd █████████████████████████████████ 33

    wave ht ███ 3

Removing ANSI Colors

Terminal applications use ANSI color codes which unfortunately is not support on Web pages.

ANSI color codes can be removed from files and strings by:

# Strip out ANSI color codes:
cat infile | sed 's/\x1b\[[0-9;]*m//g' > outfile
$ echo "temp,33;pressure,44" | bash hbar0.sh > hbar0.txt
$ cat hbar0.txt

       temp █████████████████████████████████ 33

   pressure ████████████████████████████████████████████ 44

$ cat hbar0.txt | sed 's/\x1b\[[0-9;]*m//g'

       temp █████████████████████████████████ 33

   pressure ████████████████████████████████████████████ 44

A Final App

With the basics in place I was able to create an app that would support scaling, titles, units, custom colour and web output:

$ ./hbars
usage: hbars [data] [option] 
  -h --help     print this usage and exit
  -c --color    set color to all bars (default 7=white)
                 (0-grey,1-red,2=green,3=yellow,4=blue,5=magenta,6=cyan,7=white)
  -p --pretty   add different colors to bars (-c overrides)
  -t --title    top title
  -w --width    max width of bar (default 50)
  -W --Web      make output HTML formatted
  -f --fontsize fontsize for web output (default 24)

 examples:
   echo 'temp,33,C;pressure,14,psi' | ./hbars -t Weather -p -w 40 
   ./hbars -t Weather -p -w 40  'temp,33;pressure,14' 
   cat data.txt | ./hbars -W -f 24 -t 'Raspi Data' > data.htm

The code:

#!/bin/bash
#
# hbars.sh - show some text bars
#   pass data as:  label1,value1,unit1,color1; label2,value2,unit2,colour2; ....  
#
width=50
title=""
color=7
pretty=False
web=False
font=24

usage() { 
  echo "usage: hbars [data] [option] "
  echo "  -h --help     print this usage and exit"
  echo "  -c --color    set color to all bars (default 7=white)"
  echo "                 (0-grey,1-red,2=green,3=yellow,4=blue,5=magenta,6=cyan,7=white)"
  echo "  -p --pretty   add different colors to bars (-c overrides)"
  echo "  -t --title    top title"
  echo "  -w --width    max width of bar (default 50)"
  echo "  -W --Web      make output HTML formatted"
  echo "  -f --fontsize fontsize for web output (default 24)"
  echo ""
  echo " examples:"
  echo "   echo 'temp,33,C;pressure,14,psi' | ./hbars -t Weather -p -w 40 "
  echo "   ./hbars -t Weather -p -w 40  'temp,33;pressure,14' "
  echo "   cat data.txt | ./hbars -W -f 24 -t 'Raspi Data' > data.htm"
  echo ""

  exit 0
}
# Show help usage if no pipe data and no cmd line data
if [ -t 0 ]  && [ $# -eq 0 ] ; then
  usage
fi
# Check for command line options
while getopts "hpc:t:w:Wf:" arg; do
  case "$arg" in
    h) usage ;;
    c)  color=$OPTARG ;;
    p)  pretty=True; icolor=0 ;;
    t)  title=$OPTARG ;;
    w)  width=$OPTARG ;;
    W)  web=True;;
    f)  font=$OPTARG ;;
  esac
done
#------------------------------------------------
# Setup formatting for text, Web and color
# -----------------------------------------------
if [[ ${color} != 7 && ${pretty} = True ]]; then
  pretty=False
fi
colidx=0

setcolors() {
if [ $web = True ]; then
  colors=(gray red green yellow blue magenta cyan white)
  titlebold="echo '<h1>'"
  titlereset="echo '</h1>'"
  #color_set='echo "<span style=font-size:$(font)px  >" ' 
  #color_set="printf '<span  style=\"font-size:$(font)px;color:${colors[colidx]}\" >'" 
  color_set="printf '<pre><span style=\"color:${colors[colidx]} ; font-size:${font}px \" >'" 
  color_rs="echo '</span></pre>'"
else
  colors=(0 1 2 3 4 5 6 7 )
  titlebold="tput bold; tput smul"
  titlereset="tput rmul; tput rmso"
  color_set="tput setaf ${colors[colidx]}"
  color_rs="tput rmso"
fi
}
setcolors
#----------------------------------------------
# Get data, check if stdlin, file, if not assume string
#----------------------------------------------
if [ -p /dev/stdin ]; then
        lastarg=$(< /dev/stdin)
else
	lastarg=$(echo "${@: -1}")
	if test -f "$lastarg"; then
	  lastarg=$(<$lastarg)
	fi
fi
# Cleanup the input data
lastarg=$(echo $lastarg | sed 's/\n/;/g; s/  / /g; s/\t//g; s/;;/;/g')
IFS=';' read -r -a array <<< $lastarg

# ensure that there is some data
if [[ ${array} == 0 ]]; then
  echo "No data found"
  exit 0
fi
echo "input:$lastarg"
#exit 0
#------------------------------------
# Get max value and max label length
#------------------------------------
maxval=0
maxlbl=10
#echo "array:${array[@]}"
for element in "${array[@]}"
do 
  IFS=',' read -r -a datapt <<< $element
  if (( $(echo "$maxval < ${datapt[1]}" |bc -l) )); then
	maxval=${datapt[1]}
  fi
  if (( $(echo "$maxlbl < ${#datapt[0]}" |bc -l) )); then
	maxlbl=${#datapt[0]}
  fi
done
#---------------------------------
# Print Title - use bold/underline
#---------------------------------
if [[ ! -z $title ]]; then
  printf "\n %${maxlbl}s " " "
  eval $titlebold
  printf "%s" "${title}" ; printf "\n\n"
  eval $titlereset
fi
#------------------------------------
# Cycle thru data and build bar chart
#------------------------------------
for element in "${array[@]}"
do
# check data values
  IFS=',' read -r -a datapt <<< $element
  # check for empty records
  if [ ${#datapt[0]} = 0 ]; then 
    break
  fi
  label=${datapt[0]}
  if [[ ${label} != "-t*" ]]; then 
	  val=${datapt[1]}
	  sval=$(bc <<< "$width * $val / $maxval")

	  # add color, use 4th item if available
	  if [[ ${#datapt[@]} > 3 && $pretty = False ]]; then
		icolor=${datapt[3]}
	  fi
	  if [[ $pretty = True ]] ; then
		let colidx++
		if [ $colidx -gt 7 ]; then
		  let colidx=1
		fi
	  elif [[ ${#datapt[@]} > 3 ]]; then
		colidx=${datapt[3]}
	  else
	  	colidx=$color
	  fi
	  setcolors
          eval $color_set
          printf " %${maxlbl}s " "$label"
	  bar="printf '█%.0s' {1..$sval}"
	  eval $bar; 
	  # add value and units if available
	  units=""
	  if [[ ${#datapt[@]} > 2 ]]; then
		units=${datapt[2]}
	  fi
	  printf " %d %s\n\n" $val "$units"
	  eval $color_rs 
  fi
done

SQL Output to Bars

With the base code I was able to start doing some more complicated actions, like piping SQL SELECT output. Below is an example from Sqlite3. The first step is to format the SQL output to: label,value;

pete@lubuntu:$ sqlite3 $HOME/dbs/someuser.db "select fname,age from users limit 4"
Brooke|18
Leah|18
Pete|100
Fred|77
pete@lubuntu:$ sqlite3 $HOME/dbs/someuser.db "select fname,age from users limit 4" \
>  | tr  '|' ',' | tr '\n' ';'
Brooke,18;Leah,18;Pete,100;Fred,77;
pete@lubuntu:$ sqlite3 $HOME/dbs/someuser.db "select fname,age from users limit 4" \ > | tr '|' ',' | tr '\n' ';' | ./hbars -t "Sqlite3 Users" -p Sqlite3 Users Brooke █████████ 18 Leah █████████ 18 Pete ██████████████████████████████████████████████████ 100 Fred ██████████████████████████████████████ 77

Web Page with Bars

Support was added for fixed chart width, engineering units and custom line colours. HTML <center> tags were used on the title.

$ cat data.txt
temp,44,degC,1;
humidity,33,%,4;
air pressure,88,mm Hg,5;
rain/precipation,6,mm,6;

$ cat data.txt | ./hbars -W -f 24 -w 50 -t '<center>Raspi Data</center>' > data.htm

Final Comments

Horizontal bars are relatives easy to create in Bash. Unfortunately showing vertical bars and Line charts will require a different approach

SQLite/Bottle Todo List

I wanted to do a Todo List web application that I could pass on to my kids to try. My goal was to give them an introduction to SQL, Web interfaces and Web templating.

For the Todo List application the Python Bottle Web Framework will be used. The Bottle library is a lightweight standalone micro web framework.

To store the Todo list items an SQLite database is used. SQLite is a file based server-less SQL (Structured Query Language) database which is ideal for small standalone applications.

Finally to build Web page, Python web templates will be used. The advantage of using web templates is that is reduces the amount of code written and it separates the presentation component from the backend logic.

 

Getting Started with Bottle

To install the Python bottle library:

pip install bottle

As a test we can make a program that has a home page (“/”) and a second page, then links can be put on each of the page to move back and forward.

todo_code_1

The @route is a decorator that links a URL call, like “/” the home page to a function. In this example a call to the home page “/” will call the home_page() function.

In the home_page() and pages2() functions the return call is used to pass HTML text to the web browser. The anchor tag (<a) is used to define page links.

The run() function will start the Bottle micro web server on: http://127.0.0.1:8080/

The output is below. 

SQLite

The Python SQLite library is one of the base libraries that is installed with Python.

SQLite has a number of tools and utilities that help manage your databases. One useful  light weight application is: DB Browser for SQLite. It is important to note that SQLite data can be view by multiple applications, but for edits/deletes only 1 application can be accessing SQLite.

For the Todo list we’ll start with a simple database structure using three fields:

  • Category – this a grouping such as: shopping, projects, activities etc.
  • theItem – this is the actual to do item
  • id – an unique index for each item. (This will used later for deleting rows)

The Todo database can be created with the SQLite Brower, or in Python code. The code below creates that database, adds a todo table and then inserts some records.


import sqlite3

print("Create a todo list database...")

conn = sqlite3.connect('todo.db') # Warning: This file is created in the current directory
conn.execute("CREATE TABLE todo (category char(50), theitem char(100),id INTEGER PRIMARY KEY )")
conn.execute("INSERT INTO todo (category, theitem) VALUES ('Shopping','eggs')")
conn.execute("INSERT INTO todo (category, theitem) VALUES ('Shopping','milk')")
conn.execute("INSERT INTO todo (category, theitem) VALUES ('Shopping','bread')")
conn.execute("INSERT INTO todo (category, theitem) VALUES ('Activities','snow tires')")
conn.execute("INSERT INTO todo (category, theitem) VALUES ('Activities','rack lawn')")
conn.commit()

print("Database todo.db created")

The DB Browser can be used to view the newly created database.

make_table

Viewing the Data in Python

An SQL SELECT command is used to get all the records in the todo database.  A fetchall() method is will return all the database rows in a Python tuple variable. Below is the code to write the raw returned data to a browser page.

# Send to raw SQL result to a Web Page
#
import sqlite3
from bottle import route, run

@route('/')
def todo_list():
    conn = sqlite3.connect('todo.db')
    c = conn.cursor()
    
    c.execute("SELECT * FROM todo")
    result = c.fetchall()
    c.close()
# note: the SQL results are an array of data (tuple)
# send results as a string
    return str(result)

run()

todo_raw

The output formatting can be improved by adding a sort to the SQL SELECT statement and then HTML code are be used to show category heading.

todo_code_2

web_rbr

For small applications putting HTML code in the Python code is fine, however for larger web projects it is recommended that the HTML be separated out from the database or backend code.

Web Templates

Web templates allow you to separate the database and back end logic from the web presentation. Bottle has a built-in template engine called Simple Template Engine. I found it did everything that I needed to do. It’s possible to use other Python template libraries, such as Jinja, Mako or Cheetah, if you feel you need some added functionality.

The earlier Python code is simplified by removing the HTML formatting logic. A template object is created with a template name of  sometime.tpl). An example would be:

output = template(make_table0, rows=result, headings = sqlheadings)

Where rows and headings are variable names that are used in the template file. The template file make_table0.tpl is in the working directory.

#
# Build a To Do List with a Web Template
#
import sqlite3
from bottle import route, run, template


@route('/')
def todo_list():
    # Send the output from a select query to a template
    conn = sqlite3.connect('todo.db')
    c = conn.cursor()    
    c.execute("SELECT * FROM todo order by category, theitem")
    result = c.fetchall()
    # define a template to be used, and pass the SQL results
    output = template('make_table0', rows=result)
    return output
        
run()

Templates are HTML pages with inline Python code. Python code can either be in blocks with a <% to start the block and a %> to end the block, or each line can start with %.

Two of the major differences of inline template Python code are:

  • indenting the line of Python is not required or used
  • control statements like : if and for need an end statement

A template that takes the SQL results and writes each row in a table would look like:

tpl_make_table

todo_tmp_table

A template that take the SQL results and writes a category heading and lists the items would look like:

tpl_make_list

web_tpl_rbr

Include ADD and DELETE to the Templates

The next step is to include ADD and DELETE functionality.

For the DELETE functionality, a form is added to the existing template. A ‘Delete’ button is placed beside all the Todo items and the button passes the unique index id (row[2]) of the item. The form has a POST request  with the action going to the page ‘/delete‘ on the Bottle Web Server.

For the ADD functionality, a new template is created and a %include call is put at the bottom of the main template. (You could also put everything in one file).

The main template now looks like:

tpl_show_todo

The new item template uses a dropdown HTML element with some predefined category options (Activities, Projects and Shopping). The item text will displace 25 characters but more can be entered. Pressing the save button will generate a POST request to the “/new” URL on the Bottle server.

The new_todo.tpl file is:

tpl_newtask

Bottle Python Code with /add and /delete Routes

The final step is to include routes for the /add and /delete URL references.

The new_item() function gets the category and Todo item from the form in the new_todo template. If the request passes a non-blank item (request.forms.get(“theitem”) then an SQL INSERT command will add a row. The unique item ID is automatically included because this field is defined as an index key.

The delete_item() function reads the unique item id that is passed from the button to issue an SQL DELETE statement.

At the end of the new_item() and delete_item() function the user is redirected back to the home (“/”) page.

#
# Build a Todo List 
#
import sqlite3
from bottle import route, run, template, request, redirect, post  

# The main page shows the Todo list, /new and /delete references are called from this page
@route('/')
def todo_list():
    conn = sqlite3.connect('todo.db')
    c = conn.cursor()
    
    c.execute("SELECT * FROM todo order by category,theitem ")
    result = c.fetchall()
    # in case column names are required
    colnames = [description[0] for description in c.description]
    numcol = len(colnames)
    # for now only the rows=result variables are used
    output = template('show_todo', rows=result, headings=colnames, numcol = numcol)
    return output

# Add new items into the database
@route('/new', method='POST')
def new_item():

    print("New Post:", request.body.read())
    theitem = request.forms.get("theitem")
    newcategory = request.forms.get("newcategory")

    if theitem != "":        
        
        conn = sqlite3.connect('todo.db')
        c = conn.cursor()
        c.execute("INSERT INTO todo (category,theitem) VALUES (?,?)", (newcategory,theitem))
        conn.commit()
        c.close()

    redirect("/") # go back to the main page   

# Delete an item in the database
@route('/delete', method='POST')
def delete_item():

    print("Delete:", request.body.read() )
    theid = request.forms.get("delitem").strip()
    print("theid: ", theid)
               
    conn = sqlite3.connect('todo.db')
    c = conn.cursor()
    sqlstr = "DELETE FROM todo WHERE id=" + str(theid)
    print(sqlstr)
    c.execute(sqlstr)
    conn.commit()
    c.close()

    redirect("/") # go back to the main page   
        
run()

The application will look something like:

todo_final

Final Clean-up

Some of the final clean-up could include:

  • enlarge the database to include fields like: status, due date, who is responsible etc.
  • add an “Are you sure?” prompt before doing adds and deletes
  • verify double entries aren’t included
  • include an edit feature
  • make the interface slicker

If you want to speed up the performance PyPy can be used instead of the Python interpreter. To use Pypy (after you’ve installed it), you will need to install the pip and bottle:

pypy3 -m ensurepip --user
pypy3 -mpip install bottle --user

Final Comments

As I was working on this I found a good BottleTutorial: Todo-List Application. This tutorial approaches the Todo list project differently but it is still a worthwhile reference.

SVG Web Animation

There are a number of good Web animation techniques that are available. Flash animation support is being discontinued in 2020, but luckily there are some good alternatives, such as HTML 5 Canvas, Scalar Vector Graphics (SVG),  and even CSS (Cascading Style Sheets) can be used .

HTML 5 Canvas draws 2D graphics, on the fly with a JavaScript. The canvas is rendered pixel by pixel. In canvas, once the graphic is drawn, it is forgotten by the browser. If its position should be changed, the entire scene needs to be redrawn, including any objects that might have been covered by the graphic.

SVG is a language for describing 2D graphics in XML, which means that every element is available within the SVG DOM. In SVG, each drawn shape is remembered as an object. If attributes of an SVG object are changed, the browser can automatically re-render the shape.

In this blog I wanted to document my SVG testing.

Using SVG

SVG can be used inline within an HTML document. Below is an example with a rectangle, circle and some text.

<!DOCTYPE html>
<html>
<body>

<svg width="400" height="400">
  <rect width="100" height="100" fill= "blue" />
  <circle cx="150" cy="50" r="40" fill="green" />
  <text x="10" y="120" fill="red">SOME SVG TEXT</text>
</svg>

</body>
</html>

This will generate a web like:

svg1

An SVG element has a width and height property and all the drawing elements are positioned within this area.

Dynamic SVG Bar

A bar can be created using a rectangle () element. Javascript is used to dynamically adjust the top and height of the rectangle.

<!DOCTYPE html>
<html>
<head>
<script type="text/JavaScript">

setTimeout(updatebar, 2000);

function updatebar() {
// 
  var newvalue = (Math.random() * 300); // get a random integer 0-100
  if (newvalue > 250) {
    document.getElementById("bar1").style.fill = "red";
  } else {
    document.getElementById("bar1").style.fill = "green";
  }
  newvalue = Math.round(newvalue);
  var newheight = newvalue.toString();
  var num = 300 - newvalue - 5;
  var newy = num.toString();  

  document.getElementById("bar1").setAttribute("height",newheight);
  document.getElementById("bar1").setAttribute("y",newy);
  document.getElementById("bartext").innerHTML = "value: " + newheight ;
  setTimeout(updatebar, 2000);
}
</script>
</head>
<body>
<h2> SVG Bar Example</h2>
<svg width="400" height="400" >
  <rect x="0" y="0" width="160" height="300" style="fill:white;stroke:black;stroke-width:4;" />
  <rect id="bar1" x="5" y="145" width="150" height="150" style="fill:green;stroke:black;stroke-width:2;" /> 
  <text id="bartext" x=30 y = 320 style="font-family:verdana;font-weight:bold">value: 50 </text>
</svg>

</body>
</html>

svg_bar

Animate Graphics

There some SVG drawing packages such as: https://www.drawsvg.org/drawsvg.html

There are also some free libraries, I used: https://www.opto22.com/support/resources-tools/demos/svg-image-library to animate a flow valve.

I needed to do a little bit of trial and error but I was able to add a text element to the bottom of the SVG that could be animated with Javascript. Below is the SVG file:

<svg x="0px" y="0px" viewBox="0 0 100 100" xml:space="preserve"><svg><g>
<g id="shape">
  <circle fill="#999999" cx="50.093" cy="32.75" r="32.75"></circle>
  <rect x="0.176" y="71.667" fill="#999999" width="99.833" height="26.833"></rect>
  <rect x="44.926" y="63" fill="#999999" width="10.333" height="10.5"></rect>
  <rect y="70" fill="#999999" width="4.5" height="30"></rect> 
  <rect x="95.509" y="70" fill="#999999" width="4.5" height="30"></rect>
</g>
<g id="linear">
  <linearGradient class="linear" id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="50.0926" y1="94.5113" x2="50.0926" y2="75.6554">
    <stop offset="0" style="stop-color:#E6E6E6"></stop>
    <stop offset="1" style="stop-color:#000000"></stop>
  </linearGradient>
  <polygon class="linear" fill="url(#SVGID_1_)" points="70.031,75.655 60.041,79.079 40.144,79.079 30.154,75.655 0.176,75.655 0.176,94.511 
    29.289,94.511 40.144,90.791 60.041,90.791 70.896,94.511 100.009,94.511 100.009,75.655   ">
</g>
<g id="light">
  <circle fill="#E6E6E6" cx="50.093" cy="32.75" r="29.074"></circle>
  <path fill="#E6E6E6" d="M44.833,71.5v-6.409c0.606,0.096,1.217,0.179,1.833,0.241V71.5H44.833z"></path>
</g>
<g id="hlight">
  <path fill="#FFFFFF" d="M18.343,33.75C18.343,15.663,33.005,1,51.093,1c8.789,0,16.763,3.469,22.646,9.104
    C67.777,3.881,59.391,0,50.093,0c-18.087,0-32.75,14.663-32.75,32.75c0,9.298,3.881,17.685,10.104,23.646
    C21.812,50.514,18.343,42.539,18.343,33.75z">
  <rect x="26.093" y="21.474" fill="#FFFFFF" width="48" height="23.053"></rect>
</g>
<g id="shadow">
  <path fill="#000000" d="M72.738,9.104c5.635,5.882,9.104,13.857,9.104,22.646c0,18.087-14.663,32.75-32.75,32.75
    c-8.789,0-16.763-3.469-22.646-9.104C32.408,61.619,40.795,65.5,50.093,65.5c18.087,0,32.75-14.663,32.75-32.75
    C82.843,23.452,78.961,15.066,72.738,9.104z">
  <path d="M55.259,71.5v-6.409c-0.606,0.096-1.217,0.179-1.833,0.241V71.5H55.259z"></path>
  <polygon points="27.235,22.589 74.093,22.589 74.093,21.474 26.093,21.474 26.093,44.526 27.235,44.526  "></polygon>
  <path d="M22.018,33.75c0-16.057,13.017-29.074,29.074-29.074c7.774,0,14.83,3.057,20.047,8.028
    c-5.296-5.558-12.764-9.028-21.047-9.028c-16.057,0-29.074,13.017-29.074,29.074c0,8.283,3.469,15.751,9.028,21.047
    C25.076,48.58,22.018,41.524,22.018,33.75z">
</g>
</g>
<text id="flowval" name="flowval" x = "40" y = "40" fill= "green" style="font-family:verdana;font-weight:bold"> 00 </text></svg>
</svg>

External SVG files can be used in a Web page by:

  •  <img – as an image file. (Only good for static or self-contained SVG files)
  • <frame – as a frame.
  • <object – as a object reference within an existing web document
  • <embed – as an embedded element

Unfortunately I found that Chrome only supports inline SVG with Javascript. Microsoft Edge and Firefox worked with both the object and embed tags.

Below is the code that I used to animate the text on the flow valve:

<!DOCTYPE html>
<html>
<head>
<script type="text/JavaScript">

setTimeout(updatebar, 2000);

function updatebar() {
// 
  var randvalue = (Math.random() * 100); // get a random integer 0-100
  var flowvol = Math.round(randvalue.toString());
  // set the text in the svg object   
  var svgobj = document.getElementById("flowmeter");
  var svgdoc =  svgobj.getSVGDocument();
  svgdoc.getElementById("flowval").textContent =  flowvol ;
  
  setTimeout(updatebar, 2000);
}
</script>
</head>
<body>
<h2> SVG Flow Meter</h2>
<embed id="flowmeter" height= '50%' width='100%' src="flowmeter.svg" >

</body>
</html>

flowmeter_1

Final Comments

I found that it can be a little challenging to tweek existing SVG library examples. The browser “development tools” can be used to find and test SVG elements. Below is an example where I found the solar panel element that I need to animate.

svg_devtools

For 100% cross-browser capability it would be recommended to put all the SVG code in one document.

The next step will be to use a Raspberry Pi Web app and some AJAX calls to animate the data.

Streaming Video Server

There are some reasonably priced video surveillance products available on the market. If however you have a USB Web Cam and a Raspberry Pi (or equivalent or an old PC) you can make your own.

If you are working in the Unix world I would recommend motion , it’s super easy to setup and it’s got lots of added features if you want to turn them on. To install:

sudo apt-get install motion

Once you have motion installed you’ll need to tweek some of it’s parameters by:

sudo nano /etc/motion/motion.conf

The /etc/motion/motion.conf file contains a lot of cool parameters to tweek, some of the more important ones are:

# Image width (pixels). Valid range: Camera dependent, default: 352
width 800

# Image height (pixels). Valid range: Camera dependent, default: 288
height 600

# Maximum number of frames to be captured per second.
framerate 1

# Maximum framerate for stream streams (default: 1)
stream_maxrate 1

# Restrict stream connections to localhost only (default: on)
stream_localhost off

The speed of your hardware and network will determine how many frames per second you can use.

To run the video server enter:

sudo motion

The motion package has a built in web server that is accessed by: http://your_ip:8081

livevideo

Hardware Setup

There are lots of options for the mounting of your video server. I like to use Lego Pi cases about ($8) and then use some Lego to secure the rest of the components. Below is an example where I used a PIR (Pyroelectric Infrared) module ($3) to manually turn the motion software on/off, and I added a USB card reader to enable/disable the system.

OLYMPUS DIGITAL CAMERA
Homemade Security System

Performance and Tuning

Ideally it would be nice to run at 24 frames per second (or better) to get smooth video action. Unfortunately I found that the Raspberry Pi 3 would often freeze up at this refresh rate with a USB camera. The linux command line tool vmstat can be useful to show your CPU and I/O status. Below is some sample vmstat output:

$ vmstat # Pi 3 video server at 24 fps
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r b swpd free buff cache si so bi bo in cs us sy id wa st
 1 0 0 593552 17808 269856 0 0 41 57 803 180 7 1 74 18 0

The id is the idle time (74%), which isn’t bad. The problem however id wa, waiting for I/O.

You will have to do some trial and error to get a refresh rate that works best for you.

Port Forwarding

With the basic setup you will be able to access the video server from your local LAN/WAN. If however you want to access your video server from the Internet then you will need to allow a connection through your router to your video server, this is called port forwarding.

Unfortunately the configuration of port forwarding will vary from manufacturer to manufacture, so please see the documentation for your specific devices.

 

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

 

 

 

littleBits/Pi Temperature Monitor

By using littleBits Proto modules it is possible to create Raspberry Pi projects with littleBits components. On this project we created a temperature monitor with a local indication and a remote Web page using Node-Red.

The parts used were:

  • 1 Raspberry Pi 3
  • 1 Explorer Hat (used for Analog Inputs)
  • 2 littleBits Proto bits
  • 1 littleBits Temperature bit
  • 1 littleBits Number bit
  • 1 littleBits Wire bit (optional, used for easier wiring)

The Wiring

To complete the littleBits circuit the first Proto bit was wired with 5V to the VCC and the data connectors. Pi GND was wired to the module’s GND. The second Proto bit had the data pin with to the Explorer HAT analog input 1.

tempcircuit

Node-Red Logic

On the Pi, the following Node-Red nodes were used:

  • Explorer Hat Node – reads the analog input
  • Function Node – only pass on analog input 1
  • Smooth Node – removes noise and jitter
  • Function Node – converts 0-5V to 0-100 C

temp_logic

The logic for the first function node, (to pass only analog input 1), was:

 if (msg.topic == "explorerhat/analog.1"){
return msg;
} 

We added a smooth node to remove noise in the analog input signal. The configuration that we used for the smooth node was:

temp_smooth

The logic for the second function, (to convert the voltage to a temperature), was:

msg.payload= (100/5)*msg.payload;
return msg;

Finally we used a Chart Dashboard node to show the results on a Web page. Our configuration for this was:

temp_gauge

To view the Web page use the URL of : http://the-pi-address:1880/ui

temp_pic_full

Remote Controlled Rocket Launcher

We’ve made a number of different versions of the mobile rocket launcher, and all of them have been fun. The version above is using the a PI 1 with a PiFace Digital module for the motor controls and a wireless keyboard for the controls.

The version shown below is using a PI 3, with an ExploreHAT Pro for the motor controls and a Python Web Server program for the controls. By using some simplified HTML tagging we were able to use an old PSP as a remote interface console.

launcher

Rocket Launcher Sample Code

We found the rocket launcher in a sale bin, but they call be purchased at: http://dreamcheeky.com/thunder-missile-launcher. The rocket launcher comes with it’s own Windows based program.

Below is some Python code that will control the rockets launchers turret and fire a missile. Based on this sample code you should be able to make some cool projects. A couple of notes from our testing:

  • you need to manually stop the turret motion once you start it. So you need to set your own wait time
  • you need 3-4 seconds between each missile firing. It might be a coincident but we damaged our first rocket launcher trying to issue fast firing commands.
 import usb
import sys
import time

device = usb.core.find(idVendor=0x2123, idProduct=0x1010)

# On Linux we need to detach usb HID first
try:
device.detach_kernel_driver(0)
# except Exception, e:
except Exception:

pass # already unregistered

device.set_configuration()

endpoint = device[0][(0,0)][0]

down = 1 # down
up = 2 # up
left = 4 # rotate left
right = 8 # rotate right
fire = 16 # fire
stop = 32 # stop

#device.ctrl_transfer(0x21, 0x09, 0x0200, 0, [signal])

while True:

print('r = right, l = left, u = up, d = down, f = fire ')
key = raw_input ('enter key:')
if (key == 'l'):
device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, left, 0x00,0x00,0x00,0x00,0x00,0x00])
if (key == 'u'):
device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, up, 0x00,0x00,0x00,0x00,0x00,0x00])
if (key == 'r'):
device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, right, 0x00,0x00,0x00,0x00,0x00,0x00])
if (key == 'd'):
device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, down, 0x00,0x00,0x00,0x00,0x00,0x00])
if (key == 'f'):
device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, fire, 0x00,0x00,0x00,0x00,0x00,0x00])
time.sleep(4)
time.sleep(0.1)
device.ctrl_transfer(0x21, 0x09, 0, 0, [0x02, stop, 0x00,0x00,0x00,0x00,0x00,0x00]) 

 

 

 

Android Controlled Robot Arm

Create a mobile robot arm that you can control from an Android smart phone.

We found a used robot arm (OWI-535) on Kijiji, but you could buy a new one for $30-$60.  We then mounted it on an Arduino car chassis ($20) with some duct tape. Luckily the OW-535 robot arm has all its wiring exposed and it comes with a good wiring document.

Parts

For this project we used:

  • 1- OWI-535 robot arm
  • 1- Car chassis ($17)
  • 1- Four motor Arduino shield ($10)
  • 1- Two motor Arduino shield ($10)
  • 1- JY-MCU Bluetooth module ($7)
  • 1- Arduino Mega
  • Duct tape, jumpers and 4 alligator clips
  • 1- Small USB charger
  • 1- Small Box (to house all the components)

OLYMPUS DIGITAL CAMERA

Wiring

The biggest challenge is to control the 5 robot motors and 2 wheel motors. For this we used an Arduino Mega and 2 motors shields. Depending on the motor shields that you use you probably won’t be able stack the motor shields. We stacked one motor shield and we had the second motor shield floating.

robot-arm-circuit

Some of the key wiring connections were:

  • Mega Pin 44 -> Floating Motor Shield pin 4
  • Mega Pin 45 -> Floating Motor Shield pin 5
  • Mega Pin 46 ->Floating Motor Shield pin 6
  • Mega Pin 47 -> Floating Motor Shield pin 7
  • Mega Pin 5V -> Floating Motor Shield pin 5V and VIN
  • Mega Pin 19 RX1 -> Bluetooth module TX
  • Mega Pin 18 TX1 -> Bluetooth module RX

To power this project we found that a portable USB charger was enough. If however you find that a USB charger does not deliver enough power the OWI-535 robot arm has a built in power pack with exposed power connections.

The motor terminals on the car chassis are a little fragile, so rather than soldering wires we used alligator clips. We also duct taped the wires under the car chassis to keep them secure.

The Arduino wiring was pretty ugly so we stuffed all the loose components into a small box. The box was duct taped to the back of the robot arm, and 2 cuts were made to feed the wires through.

inside_box.png

Code

For the Arduino code you’ll need to add the appropriate motor libraries. For our motor shields  we used the Adafruit V1 motor shield (https://github.com/adafruit/Adafruit-Motor-Shield-library).

Because the first motor shield blocked the RX0/TX0 pins, we used RX1 and TX1 on pins 19 and 18. For the Bluetooth communications it meant the Serial1 was used in the Anduino code instead of Serial.

For our commands we used:

  • 0 = Hand open (motor 1 forward)
  • 1 = Hand closed (motor 1 backward)
  • 2 = Wrist down (motor 2 forward)
  • 3 = Wrist up (motor 2 backward)
  • 4 = Elbow down (motor 3 forward)
  • 5 = Elbow up (motor 3 backward)
  • 6 = Shoulder up (motor 4 forward)
  • 7 = Shoulder down (motor 4 backward)
  • g = Go forward (pins: 44/46=HIGH, 45/47=255)
  • s = Stop (pins: 44/46=HIGH, 45/47 =0)
  • r = Right turn (pins: 44/46=HIGH, 45=0, 47 =255)
  • l = Left turn (pins: 44/46=HIGH, 45=255, 47 =0)
  • b = Go backward (pins 44/46= LOW, pins 45/47 =255)
  • o = Light ON, (pin 50 = HIGH)
  • f = Light OFF, (pin 50 = LOW)

Our final Arduino code is:

//
// Bluetooth control of a mobile robot arm
//
#include 

char inByte;
// Define remapped pins for 'floating' motor shield
int E1 = 45; 
int M1 = 44; 
int E2 = 46; 
int M2 = 47; 
int LIGHTpin = 50;

// DC motor on M1
AF_DCMotor motor1(1); // hand
AF_DCMotor motor2(2); // wrist
AF_DCMotor motor3(3); // elbow
AF_DCMotor motor4(4); // shoulder


void setup() {
 pinMode(M1, OUTPUT); 
 pinMode(M2, OUTPUT);

pinMode( 19, INPUT_PULLUP ); // For better Bluetooth stability
 Serial1.begin(9600); 
 Serial1.println("Robot Commands");
 Serial1.println("Menu...");
 Serial1.println("Enter: 0-7 for robot arm, g/s/l/r/b for wheels ");
}

void loop() {
 
 if (Serial1.available() > 0) {

// read the incoming byte:
 inByte = Serial1.read();
 if (inByte == '0') { runmotor(motor1,FORWARD); }
 if (inByte == '1') { runmotor(motor1,BACKWARD); }
 if (inByte == '2') { runmotor(motor2,FORWARD); }
 if (inByte == '3') { runmotor(motor2,BACKWARD); }
 if (inByte == '4') { runmotor(motor3,FORWARD); }
 if (inByte == '5') { runmotor(motor3,BACKWARD); }
 if (inByte == '6') { runmotor(motor4,FORWARD); }
 if (inByte == '7') { runmotor(motor4,BACKWARD); }
 if (inByte == 'g') { runwheels(HIGH,HIGH,255,255); } 
 if (inByte == 's') { runwheels(HIGH,HIGH,0,0); } 
 if (inByte == 'r') { runwheels(HIGH,HIGH,0,255); } 
 if (inByte == 'l') { runwheels(HIGH,HIGH,255,0); } 
 if (inByte == 'b') { runwheels(LOW,LOW,255,255); } 
 if (inByte == 'o') { digitalWrite(LIGHTpin, HIGH); }
 if (inByte == 'f') { digitalWrite(LIGHTpin, LOW); } 
 }
}
//--------------------------------------------------
void runwheels (int M1dir,int M2dir,int E1speed, int E2speed) {
 // For control of wheels direction and speed
 digitalWrite(M1, M1dir); 
 digitalWrite(M2, M2dir); 
 analogWrite(E1, E1speed);
 analogWrite(E2, E2speed); 
}
//--------------------------------------------------
void runmotor(AF_DCMotor themotor, int direction) {
 // Robot Arm motor control
 themotor.setSpeed(250);
 themotor.run(direction);
 delay(250);
 themotor.run(RELEASE); 
}

Android App

MIT’s App Inventor is free and it’s a great way to quickly make Android apps: http://ai2.appinventor.mit.edu/.

For this app we used:

  • 1 TableArrangement
  • 1 Listpicker
  • 15 Buttons
  • 7 Labels
  • 1 BluetoothClient (non-visible)

app1.PNG

A TableArrangement component with 3 columns and 11 rows is used to position all of the buttons and labels. After a component is positioned into the table, select the component and then use the Properties window to change its text, color, or sizing. The Components window is used to rename the component.

In the Blocks screen, blocks are dragged from the left Blocks section to the main viewer section. The following key blocks were used:

  • when Screen1.Initialize. This is called when the app is opened and it will show a list of all the phone’s paired Bluetooth device. This list is generated by connecting the blocks: BluetoothClient1.AddressesAndNames to set ListPicker1.Elements
  • when ListPicker1.BeforePicking. This block is called after the JY-MCU Bluetooth module (typically HC-06) is selected. Inside this block ListPicker1.Selection is an input to the Call BluetoothClient1.Connect block.
  • when .Click. This block is called when a “command” button is clicked. This block will send the required Bluetooth text command, so for example “g” is sent to go forward, and “s” is sent to stop.

app3

The full AppInventor code is:

full_logic

Once all the logic is created, you can download the app (APK file) or use a QR quick code to put it on your phone.

app2qr

The final step is to power up your Arduino project. When you do this the JY-MCU Bluetooth module will start to blink. This is telling you the module is ready to pair with your phone. On your phone’s SETUP->BLUETOOTH SETTINGS scan for new devices and you should see the JY-MCU Bluetooth module (it’ll probably be called HC-06). The pairing code is: 1234.

Now you are ready to open your new Android app. A dialog will ask you which device to connect to. Select your HC-06 device.

select

For this point on you will be able to control the robot with your phone.

screenshot