Difference between revisions of "RPi-Cam-Web-Interface"

From eLinux.org
Jump to: navigation, search
m (url correction)
(33 intermediate revisions by 3 users not shown)
Line 1: Line 1:
A space for user contributed ideas and code for the excellent RPi Cam Web Interface found here: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=43&t=63276
+
This is the wiki for user contributed ideas and code for the excellent RPi Cam Web Interface found here: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=43&t=63276
  
== Startup Commands ==
+
Source code also available on GitHub:
Assuming the camera starts on boot, to have the camera image automatically rotate after rebooting the Pi, enter the following command in '''/etc/rc.local''' after the '''raspimjpeg''' command:
+
* https://github.com/silvanmelchior/RPi_Cam_Web_Interface
 +
* https://github.com/silvanmelchior/userland/tree/master/host_applications/linux/apps/raspicam
  
<code>echo 'ro 270' > /var/www/FIFO</code>
+
'''Remember, anyone can create an account on here and add to this wiki.'''
  
This startup command will change the stream to Image Mode:
+
== Issues with latest Camera Firmware ==
 +
(Still on-going May 2014 - Silvan as been unable to find the issue so far as no errors are being reported)
  
<code>echo 'pm' > /var/www/FIFO</code>
+
If you find that videos or photos are freezing, or perhaps motion isn't recording video properly, try going back to an older firmware using this command:
  
If you wish to issue more than one command at startup, you'll need to put the sleep command in-between them:
+
<syntaxhighlight>sudo rpi-update 8660fe5152f6353dec61422808835dbcb49fc8b2</syntaxhighlight>
  
<code>echo 'pm' > /var/www/FIFO
+
== Updating to the latest version ==
  
sleep 1
+
First, update your firmware because RaspiMJPEG will utilise the latest camera firmware:
 +
<syntaxhighlight>
 +
sudo apt-get update
 +
sudo apt-get dist-upgrade
 +
sudo rpi-update
 +
sudo reboot
 +
</syntaxhighlight>
 +
Then update the code (git pull and stop raspimjpeg are now run when using the install option):
  
echo 'ro 270' > /var/www/FIFO</code>
+
<syntaxhighlight>
 +
cd /home/pi/RPi_Cam_Web_Interface
 +
git pull origin master
 +
./RPi_Cam_Web_Interface_Installer.sh install
 +
./RPi_Cam_Web_Interface_Installer.sh start
 +
</syntaxhighlight>
 +
 
 +
== The Config File ==
 +
Thanks to the amazing hard work by Silvan Melchior, RaspiMJPEG now uses a config file to set various parameters on starting the RPi Can Web Interface.
 +
 
 +
The release update which includes an example of the config file can be found here:
 +
http://www.raspberrypi.org/phpBB3/viewtopic.php?p=524707#p524707
 +
 
 +
A few examples on how to set variables can be found below.
 +
 
 +
=== Changing the Field of View ===
 +
Follow this tutorial to start a different view automatically during boot:
 +
 
 +
There are three different preview modes available which are set by changing the resolution of the '''video_width''' and '''video_height''' options in the '''# Video Options''' section.
 +
 
 +
The view along with resolution is shown below:
 +
<syntaxhighlight>
 +
<view name> - <video_width> x <video_height>
 +
Std FOV - 1920 x 1080
 +
16:9 wide FOV - 1296 x 730
 +
4:3 full FOV - 1296 x 976
 +
</syntaxhighlight>
 +
e.g. to set 4:3 full FOV:
 +
<syntaxhighlight>
 +
#
 +
# Video Options
 +
#
 +
video_width 1296
 +
video_height 976
 +
</syntaxhighlight>
  
 
== Remote access to website with User/Pass and changing port==
 
== Remote access to website with User/Pass and changing port==
  
http://www.raspberrypi.org/phpBB3/viewtopic.php?p=500460#p500460
+
http://www.raspberrypi.org/forums/viewtopic.php?p=500460#p500460
 +
 
 +
If using motion you need to edit motion.conf adding the directive:
 +
 
 +
<syntaxhighlight>netcam_userpass username:password</syntaxhighlight>
 +
 
 +
(source http://www.raspberrypi.org/forums/viewtopic.php?p=530057#p530057)
 +
 
 +
== Central Motion Detection from Multiple Cams using iSpy Connect==
 +
http://www.raspberrypi.org/forums/viewtopic.php?p=518500#p518500
  
 
== Configure Timelapse ==
 
== Configure Timelapse ==
  
'''Please note''' - a time-lapse feature will soon be built into RaspiMJPEG itself so the web-interface doesn't need to be running while capturing images. Therefore use the instructions in this link with caution.
+
Timelapse is now built into RaspiMJPEG
 
 
http://www.raspberrypi.org/phpBB3/viewtopic.php?p=514289#p514289
 
  
 
== Use nginx web server instead of Apache ==
 
== Use nginx web server instead of Apache ==
  
This could cause difficulties when silvanmelchior releases an update because it would try to install Apache again
+
This could cause difficulties when silvanmelchior releases an update because it would try to install Apache again
  
 
http://www.raspberrypi.org/phpBB3/viewtopic.php?p=515259#p515259
 
http://www.raspberrypi.org/phpBB3/viewtopic.php?p=515259#p515259
 +
  
 
== View video stream on an iDevice / Smartphone ==
 
== View video stream on an iDevice / Smartphone ==
Line 40: Line 91:
 
* On the Pi, run:
 
* On the Pi, run:
  
<code>ln -s /run/shm/mjpeg/cam.jpg /var/www/cam.jpg</code>
+
<syntaxhighlight>sudo ln -s /run/shm/mjpeg/cam.jpg /var/www/cam.jpg</syntaxhighlight>
  
 
* Download "IP Cam View Pro" (I used IP Cam View Lite on the iPhone - can upgrade to Pro)
 
* Download "IP Cam View Pro" (I used IP Cam View Lite on the iPhone - can upgrade to Pro)
Line 51: Line 102:
 
* Press Test
 
* Press Test
 
* If it works, press Save
 
* If it works, press Save
 +
 +
== Embed live-preview in own homepage ==
 +
 +
* Navigate to /var/www
 +
* Remove all files except cam_pic.php, script_min.js and FIFO
 +
* Copy your own homepage into /var/www
 +
* Change your index.html/php: Add script_min.js (<script src="script_min.js"></script> in header)
 +
* Change your index.html/php: Add [i]onload="setTimeout('init();', 100);"[/i] to body
 +
* Change your index.html/php: Add <img id="mjpeg_dest"> at the place where you want the live-preview.
 +
 +
The size can be changed either as parameter in /etc/rc.local or with CSS. To add further features (change settings, record images/videos), study the existing homepage.
 +
 +
 +
== Save an image upon motion detection ==
 +
 +
http://www.raspberrypi.org/forum/viewtopic.php?f=43&t=63276&start=175#p491013
 +
 +
 +
== Move saved images and videos to another folder ==
 +
 +
Detailed instructions on how to map a network NFS share to /var/www/media - http://www.raspberrypi.org/forums/viewtopic.php?p=531344#p531344
 +
 +
When to do the move - http://www.raspberrypi.org/forum/viewtopic.php?p=515967#p515967
 +
 +
Mounting a share - http://www.raspberrypi.org/phpBB3/viewtopic.php?p=513781#p513781
 +
 +
Mounting a Windows Share - http://www.stuffaboutcode.com/2012/05/raspberry-pi-connect-nas-windows-share.html
 +
 +
If you've mounted a network share to something other than /var/www/media, such as /mnt/myshare, you can bind the two together using this command:
 +
 +
<syntaxhighlight>sudo mount --bind /mnt/myshare /var/www/media</syntaxhighlight>
 +
 +
== Scale camera window correctly in Chrome on Android ==
 +
 +
http://www.raspberrypi.org/phpBB3/viewtopic.php?p=515786#p515786
 +
 +
 +
== Button on web interface to reboot the Pi ==
 +
 +
http://www.raspberrypi.org/phpBB3/viewtopic.php?p=515504#p515504
 +
 +
 +
== Rotation not working? ==
 +
 +
Try a different browser - http://www.raspberrypi.org/forum/viewtopic.php?p=513705#p513705
 +
 +
Internet Explorer isn't supported, Opera and Firefox <21 can't show the preview of recorded videos.
 +
 +
 +
== Slow video stream? ==
 +
 +
http://www.raspberrypi.org/forum/viewtopic.php?p=513162#p513162
 +
 +
== Pi-Pan and Pi-Light ==
 +
 +
Information about Pi-Pan and Pi-Light: http://www.openelectrons.com/Pi-Pan‎
 +
 +
Needed Hardware for this addition:
 +
* Pi-Pan (OpenElectrons)
 +
* Pi-Light (OpenElectrons)
 +
* Pi-Case to mount Pi-Pan recommended (e.g. OpenElectrons)
 +
 +
With these code-changes and additions, it's possible to control Pi-Pan and Pi-Light from the RPi Cam Web Interface. Just follow these steps:
 +
 +
* Assembly and install Pi-Pan: http://www.openelectrons.com/index.php?module=pagemaster&PAGE_user_op=view_page&PAGE_id=20
 +
* Add the file "pipan_pipe.py" to the pipan-files with the following content:
 +
<syntaxhighlight>
 +
#!/usr/bin/env python
 +
 +
import time
 +
import os, sys
 +
import pipan
 +
import pilight
 +
 +
p_servo = pipan.PiPan()
 +
p_led = pilight.PILIGHT()
 +
 +
while True:
 +
  pipein = open("/var/www/FIFO_pipan", 'r')
 +
  line = pipein.readline()
 +
  line_array = line.split(' ')
 +
  if line_array[0] == "servo":
 +
    p_servo.do_pan(int(line_array[1]))
 +
    p_servo.do_tilt(int(line_array[2]))
 +
  elif line_array[0] == "led":
 +
    p_led.createPiLight(int(line_array[1]),int(line_array[2]),int(line_array[3]))
 +
  pipein.close()
 +
</syntaxhighlight>
 +
 +
* Navigate to "/var/www" and add a named pipe with the following commands:
 +
<syntaxhighlight>
 +
sudo mknod FIFO_pipan p
 +
sudo chmod 666 FIFO_pipan
 +
</syntaxhighlight>
 +
 +
* Edit "/etc/rc.local": add the following line below the raspimjpeg-command (change the path to the directory where you extracted the pipan-files:
 +
<syntaxhighlight>
 +
python /home/pi/pipan_pipe.py &
 +
</syntaxhighlight>
 +
 +
* Add the file "pipan.php" to "/var/www":
 +
<syntaxhighlight>
 +
<?php
 +
 +
  //
 +
  // settings
 +
  //
 +
  $min_pan = 60;
 +
  $max_pan = 190;
 +
  $min_tilt = 120;
 +
  $max_tilt = 220;
 +
 +
 
 +
  //
 +
  // code
 +
  //
 +
  if(isset($_GET["pan"])) {
 +
    if(is_numeric($_GET["pan"])) {
 +
      if(is_numeric($_GET["tilt"])) {
 +
        $pan = round($min_pan + (($max_pan - $min_pan)/200*$_GET["pan"]));
 +
        $tilt = round($min_tilt + (($max_tilt - $min_tilt)/200*$_GET["tilt"]));
 +
        $pipe = fopen("FIFO_pipan","w");
 +
        fwrite($pipe, "servo $pan $tilt ");
 +
        fclose($pipe);
 +
      }
 +
    }
 +
  }
 +
 +
  if(isset($_GET["red"])) {
 +
    if(is_numeric($_GET["red"])) {
 +
      if(is_numeric($_GET["green"])) {
 +
        if(is_numeric($_GET["blue"])) {
 +
          $pipe = fopen("FIFO_pipan","w");
 +
          fwrite($pipe, "led " . $_GET["red"] . " " . $_GET["green"] . " " . $_GET["blue"] . " ");
 +
          fclose($pipe);
 +
        }
 +
      }
 +
    }
 +
  }
 +
 +
?>
 +
</syntaxhighlight>
 +
 +
* Add the file "pipan.js" to "/var/www":
 +
<syntaxhighlight>
 +
var pan = 100;
 +
var tilt = 100;
 +
var pan_bak = 100;
 +
var tilt_bak = 100;
 +
var pan_start;
 +
var tilt_start;
 +
var touch = false;
 +
var led_stat = false;
 +
var ajax_pipan;
 +
var pipan_mouse_x;
 +
var pipan_mouse_y;
 +
 +
document.onkeypress = pipan_onkeypress;
 +
 +
if(window.XMLHttpRequest) {
 +
  ajax_pipan = new XMLHttpRequest();
 +
}
 +
else {
 +
  ajax_pipan = new ActiveXObject("Microsoft.XMLHTTP");
 +
}
 +
ajax_pipan.onreadystatechange = ajax_pipan_done;
 +
 +
function ajax_pipan_done() {
 +
  if(ajax_pipan.readyState == 4) {
 +
    if(touch) {
 +
      if((pan_bak != pan) || (tilt_bak != tilt)) {
 +
        ajax_pipan_start();
 +
      }
 +
      else {
 +
        setTimeout("ajax_pipan_done()", 100);
 +
      }
 +
    }
 +
  }
 +
}
 +
 +
function ajax_pipan_start () {
 +
  ajax_pipan.open("GET","pipan.php?pan=" + pan + "&tilt=" + tilt, true);
 +
  ajax_pipan.send();
 +
  pan_bak = pan;
 +
  tilt_bak = tilt;
 +
}
 +
 +
function servo_left () {
 +
  if(pan <= 190) pan += 10;
 +
  ajax_pipan_start();
 +
}
 +
 +
function servo_right () {
 +
  if(pan >= 10) pan -= 10;
 +
  ajax_pipan_start();
 +
}
 +
 +
function servo_up () {
 +
  if(tilt >= 10) tilt -= 10;
 +
  ajax_pipan_start();
 +
}
 +
 +
function servo_down () {
 +
  if(tilt <= 190) tilt += 10;
 +
  ajax_pipan_start();
 +
}
 +
 +
function led_switch () {
 +
 +
  if(!led_stat) {
 +
    led_stat = true;
 +
    ajax_pipan.open("GET","pipan.php?red=" + document.getElementById("pilight_r").value + "&green=" + document.getElementById("pilight_g").value + "&blue=" + document.getElementById("pilight_b").value, true);
 +
    ajax_pipan.send();
 +
  }
 +
  else {
 +
    led_stat = false;
 +
    ajax_pipan.open("GET","pipan.php?red=0&green=0&blue=0", true);
 +
    ajax_pipan.send();
 +
  }
 +
 +
}
 +
 +
function pipan_onkeypress (e) {
 +
 +
  if(e.keyCode == 97) servo_left();
 +
  else if(e.keyCode == 119) servo_up();
 +
  else if(e.keyCode == 100) servo_right();
 +
  else if(e.keyCode == 115) servo_down();
 +
  else if(e.keyCode == 102) led_switch();
 +
 +
}
 +
 +
function pipan_start () {
 +
 +
  pipan_mouse_x = null;
 +
  pipan_mouse_y = null;
 +
  pan_start = pan;
 +
  tilt_start = tilt;
 +
  document.body.addEventListener('touchmove', pipan_move, false)
 +
  document.body.addEventListener('touchend', pipan_stop, false)
 +
  touch = true;
 +
  ajax_pipan_start();
 +
 +
}
 +
 +
function pipan_move (e) {
 +
 +
  var ev = e || window.event;
 +
 +
  if(pipan_mouse_x == null) {
 +
    pipan_mouse_x = e.changedTouches[0].pageX;
 +
    pipan_mouse_y = e.changedTouches[0].pageY;
 +
  }
 +
  mouse_x = e.changedTouches[0].pageX;
 +
  mouse_y = e.changedTouches[0].pageY;
 +
  e.preventDefault()
 +
 +
  var pan_temp = pan_start + Math.round((mouse_x-pipan_mouse_x)/5);
 +
  var tilt_temp = tilt_start + Math.round((pipan_mouse_y-mouse_y)/5);
 +
  if(pan_temp > 200) pan_temp = 200;
 +
  if(pan_temp < 0) pan_temp = 0;
 +
  if(tilt_temp > 200) tilt_temp = 200;
 +
  if(tilt_temp < 0) tilt_temp = 0;
 +
 
 +
  pan = pan_temp;
 +
  tilt = tilt_temp;
 +
 
 +
}
 +
 +
 +
function pipan_stop () {
 +
 +
  document.body.removeEventListener('touchmove', pipan_move, false)
 +
  document.body.removeEventListener('touchend', pipan_stop, false)
 +
  touch = false;
 +
 +
  if(pipan_mouse_x == null) led_switch();
 +
}
 +
</syntaxhighlight>
 +
 +
* Add the following line to "index.html" below "<script src="script.js"></script>":
 +
<syntaxhighlight>
 +
<script src="pipan.js"></script>
 +
</syntaxhighlight>
 +
 +
* Add the following lines to "index.html" below "<input id="halt_button" type="button">":
 +
<syntaxhighlight>
 +
<br>
 +
<input type="button" value="up" onclick="servo_up();"><br>
 +
<input type="button" value="left" onclick="servo_left();">
 +
<input type="button" value="down" onclick="servo_down();">
 +
<input type="button" value="right" onclick="servo_right();">
 +
</syntaxhighlight>
 +
 +
* Add the following lines to "index.html" below "<input id="halt_button" type="button">":
 +
<syntaxhighlight>
 +
<tr>
 +
  <td>Pi-Light:</td>
 +
  <td>
 +
    R: <input type="text" size=4 id="pilight_r" value="255">
 +
    G: <input type="text" size=4 id="pilight_g" value="255">
 +
    B: <input type="text" size=4 id="pilight_b" value="255"><br>
 +
    <input type="button" value="ON/OFF" onclick="led_switch();">
 +
  </td>
 +
</tr>
 +
</syntaxhighlight>
 +
 +
That's it. After rebooting your Pi, you should be able to control Pi-Pan with the new Buttons "Up", "Down", "Left" and "Right" or on the keyboard with "W", "S", "A" and "D". The Pi-Light can be controled in the settings-table or on the keyboard with "F". If you have a touch-device (Android or iOS), you can pan/tilt by dragging the preview-image around and change the Pi-Light by clicking on the preview. To change the minimum/maximum pan/tilt angles, edit the settings in /var/www/pipan.php.
 +
 +
== Overlay/Composite two images together ==
 +
One way to add an image over the top of another is with ImageMagick.  I've constructed a crude example below, and it works!  But it bogs down the refresh rate, drives the load level to 1.5 and is barely acceptable.  I'd like to toggle it on/off with a new button, and improve it's performance, so please edit this article better.
 +
 +
<syntaxhighlight>
 +
<?php
 +
/// derived from http://www.php.net/manual/en/imagick.compositeimage.php
 +
/// requires 'sudo apt-get install php5-imagick' on RPi
 +
/// backup /var/www/cam_pic.php, then replace it with this
 +
/// goal is to put backup lines on reverse truck/trailer camera(s)
 +
 +
    $tlines = new Imagick('/var/www/lines/TrailerLines0deg.png');
 +
    /// TrailerLines0deg.png is a image of the guidelines with a transparent background
 +
    /// TODO: use steering wheel to select correct line graphic from library
 +
 +
    $camimg = new Imagick('/dev/shm/mjpeg/cam.jpg');
 +
    $camimg->compositeImage($tlines, Imagick::COMPOSITE_DEFAULT, 70, 20);
 +
    header("Content-Type: image/png");
 +
    echo $camimg;
 +
?>
 +
</syntaxhighlight>

Revision as of 05:24, 1 August 2014

This is the wiki for user contributed ideas and code for the excellent RPi Cam Web Interface found here: http://www.raspberrypi.org/phpBB3/viewtopic.php?f=43&t=63276

Source code also available on GitHub:

Remember, anyone can create an account on here and add to this wiki.

Issues with latest Camera Firmware

(Still on-going May 2014 - Silvan as been unable to find the issue so far as no errors are being reported)

If you find that videos or photos are freezing, or perhaps motion isn't recording video properly, try going back to an older firmware using this command:

sudo rpi-update 8660fe5152f6353dec61422808835dbcb49fc8b2

Updating to the latest version

First, update your firmware because RaspiMJPEG will utilise the latest camera firmware:

sudo apt-get update
sudo apt-get dist-upgrade
sudo rpi-update
sudo reboot

Then update the code (git pull and stop raspimjpeg are now run when using the install option):

cd /home/pi/RPi_Cam_Web_Interface
git pull origin master
./RPi_Cam_Web_Interface_Installer.sh install
./RPi_Cam_Web_Interface_Installer.sh start

The Config File

Thanks to the amazing hard work by Silvan Melchior, RaspiMJPEG now uses a config file to set various parameters on starting the RPi Can Web Interface.

The release update which includes an example of the config file can be found here: http://www.raspberrypi.org/phpBB3/viewtopic.php?p=524707#p524707

A few examples on how to set variables can be found below.

Changing the Field of View

Follow this tutorial to start a different view automatically during boot:

There are three different preview modes available which are set by changing the resolution of the video_width and video_height options in the # Video Options section.

The view along with resolution is shown below:

<view name> - <video_width> x <video_height>
Std FOV - 1920 x 1080
16:9 wide FOV - 1296 x 730
4:3 full FOV - 1296 x 976

e.g. to set 4:3 full FOV:

#
# Video Options
#
video_width 1296
video_height 976

Remote access to website with User/Pass and changing port

http://www.raspberrypi.org/forums/viewtopic.php?p=500460#p500460

If using motion you need to edit motion.conf adding the directive:

netcam_userpass username:password

(source http://www.raspberrypi.org/forums/viewtopic.php?p=530057#p530057)

Central Motion Detection from Multiple Cams using iSpy Connect

http://www.raspberrypi.org/forums/viewtopic.php?p=518500#p518500

Configure Timelapse

Timelapse is now built into RaspiMJPEG

Use nginx web server instead of Apache

This could cause difficulties when silvanmelchior releases an update because it would try to install Apache again

http://www.raspberrypi.org/phpBB3/viewtopic.php?p=515259#p515259


View video stream on an iDevice / Smartphone

Credit goes to Oke for the original post. A few more tweaks added for iPhone app. http://www.raspberrypi.org/phpBB3/viewtopic.php?p=507756#p507756

  • On the Pi, run:
sudo ln -s /run/shm/mjpeg/cam.jpg /var/www/cam.jpg
  • Download "IP Cam View Pro" (I used IP Cam View Lite on the iPhone - can upgrade to Pro)
  • Select the menu icon
  • Press Manage Cameras
  • Select Add Camera then Generic URL
  • Give your cam a name
  • For Type choose Generic Video URL
  • Enter your URL, e.g. http://192.168.0.1:80/cam.jpg
  • Press Test
  • If it works, press Save

Embed live-preview in own homepage

  • Navigate to /var/www
  • Remove all files except cam_pic.php, script_min.js and FIFO
  • Copy your own homepage into /var/www
  • Change your index.html/php: Add script_min.js (<script src="script_min.js"></script> in header)
  • Change your index.html/php: Add [i]onload="setTimeout('init();', 100);"[/i] to body
  • Change your index.html/php: Add <img id="mjpeg_dest"> at the place where you want the live-preview.

The size can be changed either as parameter in /etc/rc.local or with CSS. To add further features (change settings, record images/videos), study the existing homepage.


Save an image upon motion detection

http://www.raspberrypi.org/forum/viewtopic.php?f=43&t=63276&start=175#p491013


Move saved images and videos to another folder

Detailed instructions on how to map a network NFS share to /var/www/media - http://www.raspberrypi.org/forums/viewtopic.php?p=531344#p531344

When to do the move - http://www.raspberrypi.org/forum/viewtopic.php?p=515967#p515967

Mounting a share - http://www.raspberrypi.org/phpBB3/viewtopic.php?p=513781#p513781

Mounting a Windows Share - http://www.stuffaboutcode.com/2012/05/raspberry-pi-connect-nas-windows-share.html

If you've mounted a network share to something other than /var/www/media, such as /mnt/myshare, you can bind the two together using this command:

sudo mount --bind /mnt/myshare /var/www/media

Scale camera window correctly in Chrome on Android

http://www.raspberrypi.org/phpBB3/viewtopic.php?p=515786#p515786


Button on web interface to reboot the Pi

http://www.raspberrypi.org/phpBB3/viewtopic.php?p=515504#p515504


Rotation not working?

Try a different browser - http://www.raspberrypi.org/forum/viewtopic.php?p=513705#p513705

Internet Explorer isn't supported, Opera and Firefox <21 can't show the preview of recorded videos.


Slow video stream?

http://www.raspberrypi.org/forum/viewtopic.php?p=513162#p513162

Pi-Pan and Pi-Light

Information about Pi-Pan and Pi-Light: http://www.openelectrons.com/Pi-Pan‎

Needed Hardware for this addition:

  • Pi-Pan (OpenElectrons)
  • Pi-Light (OpenElectrons)
  • Pi-Case to mount Pi-Pan recommended (e.g. OpenElectrons)

With these code-changes and additions, it's possible to control Pi-Pan and Pi-Light from the RPi Cam Web Interface. Just follow these steps:

#!/usr/bin/env python

import time
import os, sys
import pipan
import pilight

p_servo = pipan.PiPan()
p_led = pilight.PILIGHT()

while True:
  pipein = open("/var/www/FIFO_pipan", 'r')
  line = pipein.readline()
  line_array = line.split(' ')
  if line_array[0] == "servo":
    p_servo.do_pan(int(line_array[1]))
    p_servo.do_tilt(int(line_array[2]))
  elif line_array[0] == "led":
    p_led.createPiLight(int(line_array[1]),int(line_array[2]),int(line_array[3]))
  pipein.close()
  • Navigate to "/var/www" and add a named pipe with the following commands:
sudo mknod FIFO_pipan p
sudo chmod 666 FIFO_pipan
  • Edit "/etc/rc.local": add the following line below the raspimjpeg-command (change the path to the directory where you extracted the pipan-files:
python /home/pi/pipan_pipe.py &
  • Add the file "pipan.php" to "/var/www":
<?php

  //
  // settings
  //
  $min_pan = 60;
  $max_pan = 190;
  $min_tilt = 120;
  $max_tilt = 220;

  
  //
  // code
  //
  if(isset($_GET["pan"])) {
    if(is_numeric($_GET["pan"])) {
      if(is_numeric($_GET["tilt"])) {
        $pan = round($min_pan + (($max_pan - $min_pan)/200*$_GET["pan"]));
        $tilt = round($min_tilt + (($max_tilt - $min_tilt)/200*$_GET["tilt"]));
        $pipe = fopen("FIFO_pipan","w");
        fwrite($pipe, "servo $pan $tilt ");
        fclose($pipe);
      }
    }
  }

  if(isset($_GET["red"])) {
    if(is_numeric($_GET["red"])) {
      if(is_numeric($_GET["green"])) {
        if(is_numeric($_GET["blue"])) {
          $pipe = fopen("FIFO_pipan","w");
          fwrite($pipe, "led " . $_GET["red"] . " " . $_GET["green"] . " " . $_GET["blue"] . " ");
          fclose($pipe);
        }
      }
    }
  }

?>
  • Add the file "pipan.js" to "/var/www":
var pan = 100;
var tilt = 100;
var pan_bak = 100;
var tilt_bak = 100;
var pan_start;
var tilt_start;
var touch = false;
var led_stat = false;
var ajax_pipan;
var pipan_mouse_x;
var pipan_mouse_y;

document.onkeypress = pipan_onkeypress;

if(window.XMLHttpRequest) {
  ajax_pipan = new XMLHttpRequest();
}
else {
  ajax_pipan = new ActiveXObject("Microsoft.XMLHTTP");
}
ajax_pipan.onreadystatechange = ajax_pipan_done;

function ajax_pipan_done() {
  if(ajax_pipan.readyState == 4) {
    if(touch) {
      if((pan_bak != pan) || (tilt_bak != tilt)) {
        ajax_pipan_start();
      }
      else {
        setTimeout("ajax_pipan_done()", 100);
      }
    }
  }
}

function ajax_pipan_start () {
  ajax_pipan.open("GET","pipan.php?pan=" + pan + "&tilt=" + tilt, true);
  ajax_pipan.send();
  pan_bak = pan;
  tilt_bak = tilt;
}

function servo_left () {
  if(pan <= 190) pan += 10;
  ajax_pipan_start();
}

function servo_right () {
  if(pan >= 10) pan -= 10;
  ajax_pipan_start();
}

function servo_up () {
  if(tilt >= 10) tilt -= 10;
  ajax_pipan_start();
}

function servo_down () {
  if(tilt <= 190) tilt += 10;
  ajax_pipan_start();
}

function led_switch () {

  if(!led_stat) {
    led_stat = true;
    ajax_pipan.open("GET","pipan.php?red=" + document.getElementById("pilight_r").value + "&green=" + document.getElementById("pilight_g").value + "&blue=" + document.getElementById("pilight_b").value, true);
    ajax_pipan.send();
  }
  else {
    led_stat = false;
    ajax_pipan.open("GET","pipan.php?red=0&green=0&blue=0", true);
    ajax_pipan.send();
  }

}

function pipan_onkeypress (e) {

  if(e.keyCode == 97) servo_left();
  else if(e.keyCode == 119) servo_up();
  else if(e.keyCode == 100) servo_right();
  else if(e.keyCode == 115) servo_down();
  else if(e.keyCode == 102) led_switch();

}

function pipan_start () {

  pipan_mouse_x = null;
  pipan_mouse_y = null;
  pan_start = pan;
  tilt_start = tilt;
  document.body.addEventListener('touchmove', pipan_move, false)
  document.body.addEventListener('touchend', pipan_stop, false)
  touch = true;
  ajax_pipan_start();

}

function pipan_move (e) {

  var ev = e || window.event;

  if(pipan_mouse_x == null) {
    pipan_mouse_x = e.changedTouches[0].pageX;
    pipan_mouse_y = e.changedTouches[0].pageY;
  }
  mouse_x = e.changedTouches[0].pageX;
  mouse_y = e.changedTouches[0].pageY;
  e.preventDefault()

  var pan_temp = pan_start + Math.round((mouse_x-pipan_mouse_x)/5);
  var tilt_temp = tilt_start + Math.round((pipan_mouse_y-mouse_y)/5);
  if(pan_temp > 200) pan_temp = 200;
  if(pan_temp < 0) pan_temp = 0;
  if(tilt_temp > 200) tilt_temp = 200;
  if(tilt_temp < 0) tilt_temp = 0;
  
  pan = pan_temp;
  tilt = tilt_temp;
  
}


function pipan_stop () {

  document.body.removeEventListener('touchmove', pipan_move, false)
  document.body.removeEventListener('touchend', pipan_stop, false)
  touch = false;

  if(pipan_mouse_x == null) led_switch();
}
  • Add the following line to "index.html" below "<script src="script.js"></script>":
<script src="pipan.js"></script>
  • Add the following lines to "index.html" below "<input id="halt_button" type="button">":
<br>
<input type="button" value="up" onclick="servo_up();"><br>
<input type="button" value="left" onclick="servo_left();">
<input type="button" value="down" onclick="servo_down();">
<input type="button" value="right" onclick="servo_right();">
  • Add the following lines to "index.html" below "<input id="halt_button" type="button">":
<tr>
  <td>Pi-Light:</td>
  <td>
    R: <input type="text" size=4 id="pilight_r" value="255">
    G: <input type="text" size=4 id="pilight_g" value="255">
    B: <input type="text" size=4 id="pilight_b" value="255"><br>
    <input type="button" value="ON/OFF" onclick="led_switch();">
  </td>
</tr>

That's it. After rebooting your Pi, you should be able to control Pi-Pan with the new Buttons "Up", "Down", "Left" and "Right" or on the keyboard with "W", "S", "A" and "D". The Pi-Light can be controled in the settings-table or on the keyboard with "F". If you have a touch-device (Android or iOS), you can pan/tilt by dragging the preview-image around and change the Pi-Light by clicking on the preview. To change the minimum/maximum pan/tilt angles, edit the settings in /var/www/pipan.php.

Overlay/Composite two images together

One way to add an image over the top of another is with ImageMagick. I've constructed a crude example below, and it works! But it bogs down the refresh rate, drives the load level to 1.5 and is barely acceptable. I'd like to toggle it on/off with a new button, and improve it's performance, so please edit this article better.

<?php
/// derived from http://www.php.net/manual/en/imagick.compositeimage.php
/// requires 'sudo apt-get install php5-imagick' on RPi
/// backup /var/www/cam_pic.php, then replace it with this
/// goal is to put backup lines on reverse truck/trailer camera(s)

    $tlines = new Imagick('/var/www/lines/TrailerLines0deg.png');
    /// TrailerLines0deg.png is a image of the guidelines with a transparent background
    /// TODO: use steering wheel to select correct line graphic from library

    $camimg = new Imagick('/dev/shm/mjpeg/cam.jpg');
    $camimg->compositeImage($tlines, Imagick::COMPOSITE_DEFAULT, 70, 20);
    header("Content-Type: image/png");
    echo $camimg;
?>