From Hentschel
Revision as of 22:44, 14 December 2016 by Thomas (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

So far so good, the cam can be seen on the Webcams page.

Ingredients

The Elbex ECX97-9 PTZ camera

weatherproof enclosure

The camera sittting in the enclosure

Raspbery Pi as controller

USB to RS422 converter

EasyCap frame grabber

This grabber comes in multiple flavors, the one I received is based on the USBTV007. I was not able to get this to work on the RPi, hence this item is replaced by a IP-based video grabber, with the manual at this link.

IP Video 9100A

This required a firmware update to yoics firmware, as described in this thread. Not everything was correct, the installed web interface had no way to upload new firmware (as indicated in the thread). http://<cam.ip.add.ress>/System.htm had the necessary controls and allowed the upload to start. However no progress was indicated. After about an hour or more I disconnected the device (fully expecting that it was going to be bricked), and --big surprise-- the new yoics firmware was installed. A detailed listing of camera comands is at this link.

Ethernet usb pigtail

powered USB hub

stepdown converter to supply power for Raspi and USB devices

24V power supply for the camera and the 5V converter

Hardware build

after drilling the hole for the camera dome

overall schematics

After it became clear that the RPi has trouble with USB video capture (not enough USB bandwidth), the re-design now uses a BNC to IP converter. The RPi will proxy the created MJPEG stream to a common web interface.

overall schematics

Software implementation

Operating system setup and configuration

memeruiz usbtv007 userland driver

This driver is implemented in python and uses a v4l loopback to connect to userland programs

  • install v4l2-loopback [1]
    • this creates a video device under /dev
  • install python dependancies
    • python-libusb1 [2]
    • python-libv4l2 [3]
    • numpy [4]
  • install userland driver
    • install from here, follow the instructions there
  • install mplayer
    • start with mplayer tv:// -tv device=/dev/videoX, where videoX is the v4l loopback device

This works with high CPU load on the Lenovo laptop in Ubuntu 12.04. Probably won't work on the Pi.

building the native driver

Video capture

  • some hints building ambi-tv at this link
  • a ton of good info: [5]
  • getting the driver to work: [6]
  • testing and userland stuff [7]

IP Video 9100A

  • by default, the new firmware only displayed black and white images or mjpeg. Only after setting this to a single channel (
    http://<ip>/SetChannel.cgi?Channel=257
    for the 1st channel ) did the images and the mjpeg stream display in color.
  • installed lighttpd as per these instructions. Basically:
sudo apt-get install lighttpd
sudo apt-get install mysql-server
sudo apt-get install php5-common php5-cgi php5
sudo apt-get install php5-mysql
sudo lighty-enable-mod fastcgi-php
sudo service lighttpd force-reload</pre>
  • added proxy directive to config file in /etc/lighttp
$HTTP["host"] =~ "cam03.home.hentschel.net" { # hostname you want to create
 proxy.server = ( "" =>
   (
     (
       "host" => "192.168.2.10", # where you want to proxy to
       "port" => 80,             # port you want to proxy to
     )
   )
 )
}
  • the URL
    http://<ip>/Jpeg/CamImg.jpg
    gets a current snapshot
  • the URL
    http://<ip>//GetData.cgi
    is supposed to get a mjpeg stream. However, the current proxy settings of lighttp swallow that URL (somehow disable the .cgi extension?) Correction: Works on FF (Chrome only supports mjpeg in a <img> tag)

Camera motion control

Elbex RS422 protocol

Protocol to control Elbex cameras through RS422.  As you can see, all the codes are in hex form:

PAN LEFT: 	F8 1F 00 30
PAN RIGHT: 	F8 1F 00 28
TILT DOWN: 	F8 1F 00 24
TILT UP: 	F8 1F 00 22
ZOOM TELE: 	F8 1F 00 21
ZOOM WIDE: 	F8 1F 80 20
FOCUS NEAR: 	F8 1F 40 20
FOCUS FAR: 	F8 1F 20 20

IRIS AUTO	F8 1F 44 40
IRIS MANUAL	F8 1F 54 40
IRIS CLOSE 	F8 1F 10 20
IRIS OPEN 	F8 1F 08 20

RECALL PRESET 1		F8 1F 01 41
RECALL PRESET 2		F8 1F 02 41
RECALL PRESET 3		F8 1F 03 41
RECALL PRESET 4		F8 1F 04 41
RECALL PRESET 5		F8 1F 05 41
RECALL PRESET 6		F8 1F 06 41
RECALL PRESET 7		F8 1F 07 41
RECALL PRESET 8		F8 1F 08 41
RECALL LAST POSITION		F8 1F 00 41

SET PRESET 1			F8 1F 01 42
SET PRESET 2			F8 1F 02 42
SET PRESET 3			F8 1F 03 42
SET PRESET 4			F8 1F 04 42
SET PRESET 5			F8 1F 05 42
SET PRESET 6			F8 1F 06 42
SET PRESET 7 			F8 1F 07 42
SET PRESET 8			F8 1F 08 42

PAN SPEED 1			F8 1F 01 40		
PAN SPEED 2			F8 1F 02 40
PAN SPEED 3			F8 1F 03 40
PAN SPEED 4			F8 1F 04 40
PAN SPEED 5			F8 1F 05 40
PAN SPEED 6			F8 1F 06 40
PAN SPEED 7			F8 1F 07 40
PAN SPEED 8			F8 1F 08 40

TILT SPEED 1			F8 1F 11 40
TILT SPEED 2			F8 1F 12 40
TILT SPEED 3			F8 1F 13 40
TILT SPEED 4			F8 1F 14 40
TILT SPEED 5			F8 1F 15 40
TILT SPEED 6			F8 1F 16 40
TILT SPEED 7			F8 1F 17 40
TILT SPEED 8			F8 1F 18 40

POWER ON RESET		F8 1F F0 40 FD 40
ALL CLEAR			F8 1F F0 40 FE 40

Transmission format:

Method: all dual pace synchronization
Speed: 4800 bps
Data length: 8 bit
Setting: no parity, start 1 bit, stop 1 bit, no flow control.

Sending the command once will move the camera only one step; to move continuously send the command repeatedly.

Example using command line:

set up comm:

stty -F /dev/ttyUSB0 speed 4800 cs8 -cstopb -parenb

send a PAN LEFT command:

echo -en '\xF0\x1F\x00\x30' > /dev/ttyUSB0
  • -e interprets hex string
  • -n does not add newline at end

initial script

#!/bin/bash
#/bin/stty -F /dev/ttyUSB0 speed 4800 cs8 -cstopb -parenb
#echo -en '\xF8\x1F\x00\x30' > /dev/ttyUSB0; sleep 0.1;

left=  '\xF8\x1F\x00\x30'
right= '\xF8\x1F\x00\x28'
down=  '\xF8\x1F\x00\24'
up=    '\xF8\x1F\x00\22'
tele=  '\xF8\x1F\x00\21'
wide=  '\xF8\x1F\x80\20'
near=  '\xF8\x1F\x40\20'
far=   '\xF8\x1F\x20\20'


move ()
{
COUNTER=0
while [  $COUNTER -lt  $2  ]; do
  echo -en $1 > /dev/ttyUSB0; sleep 0.1;
  let COUNTER+=1;
done
}


case "$1" in
        left)
            move $left $2 
            ;;
         
        right)
            move $right $2
            ;;
        down)
            move $down $2 
            ;;
         
        up)
            move $up $2
            ;;
        tele)
            move $tele $2 
            ;;
         
        wide)
            move $wide $2
            ;;
        near)
            move $near $2 
            ;;
         
        far)
            move $far $2
            ;;
        *)
            echo $"Usage: $0 {left|right|} times"
            exit 1
 
esac

It turns out that the shitty USB support of the RPi - well known by now - puts a dent into the project: After a few move commands, the RPi just freezes and needs a power-cycle reboot. Looks as even for the simplest tasks, the RPi isn't of much use. There are rumors in some of the forums that FreeBSD as well as ArchLinux may behave a tad better. FreeBSD is what I'll try next.

FreeBSD implementation

  • install FreeBSD as per this page
  • before doing anything else, reboot twice. This will expand the root partition to the disk size, which enables installing ports.
  • if you see random boot errors with error 19 (esp. if not power cycling), add hw.bcm2835.sdhci.hs=0 to /boot/loader.conf [8]
  • install lighttp as per these instructions - this will take quite awhile
  • I wound up using sqlite and php5
  • some helpful hints are also at this link
  • after installing bash, setting up lighttp as proxy, and porting the control scripts to FreeBSD, we're in business. The FreeBSD serial driver works like a charm
  • however, driving the serial commands via bash, and starting external commands to talk to the serial port makes things very slow
  • re-implement the control scripts for the camera in Perl, here the script
use strict;
#use Time::HiRes;
# Define command map
my %commandMap =
(
left   =>	"\xF8\x1F\x00\x30",
right  =>	"\xF8\x1F\x00\x28",
down   =>	"\xF8\x1F\x00\x24",
up     =>	"\xF8\x1F\x00\x22",
tele   =>	"\xF8\x1F\x00\x21",
wide   =>	"\xF8\x1F\x80\x20",
near   =>	"\xF8\x1F\x40\x20",
far    =>	"\xF8\x1F\x20\x20",
tspeed =>	"\xF8\x1F\x17\x40",
pspeed =>	"\xF8\x1F\x07\x40",

sp01	=>	"\xF8\x1F\x01\x42",
sp02	=>	"\xF8\x1F\x02\x42",
sp03	=>	"\xF8\x1F\x03\x42",
sp04	=>	"\xF8\x1F\x04\x42",
sp05	=>	"\xF8\x1F\x05\x42",
sp06	=>	"\xF8\x1F\x06\x42",
sp07	=>	"\xF8\x1F\x07\x42",
sp08	=>	"\xF8\x1F\x08\x42",

rp01	=>	"\xF8\x1F\x01\x41",
rp02	=>	"\xF8\x1F\x02\x41",
rp03	=>	"\xF8\x1F\x03\x41",
rp04	=>	"\xF8\x1F\x04\x41",
rp05	=>	"\xF8\x1F\x05\x41",
rp06	=>	"\xF8\x1F\x06\x41",
rp07	=>	"\xF8\x1F\x07\x41",
rp08	=>	"\xF8\x1F\x08\x41"
);

# Get command line arguments
my ($command, $repeat) = @ARGV;
$repeat = 1 if $repeat == '';

# Do we recognize the command?
if (!exists $commandMap{$command}) {
	print "Usage: {left|right|down|up|tele|wide|near|far|tspeed|pspeed} times";
	exit;
}

# Open camera serial port
open(CAMERA, ">/dev/ttyU0")
# open(CAMERA, ">camera.log")
	or die "cannot open camera serial port: $!";

# Send command to camera
for (my $count = 1; $count <= $repeat; $count++) {
	print CAMERA $commandMap{$command};
	# Sleep for 0.1 seconds
#	Time::HiRes::sleep(0.01);
}

# Close camera serial port
close(CAMERA);
  • glue script between perl and php (not really needed)
#!/bin/tcsh
/usr/bin/perl /usr/local/bin/cam.pl $1 $2
  • adding php scripts to control via web interface, example script, one each for each command
<?php
system('/usr/local/bin/cam left 10');
?>
  • implementing simple web interface
<html>
    <head>
        <title>Camera</title>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.1/jquery.min.js"></script>
        <script type="text/javascript">
            // send the input to the cgi script
            // note: sending 3x the input for every keypress, to make the movement smoother
            var submitInput = function(input) {
                $.post("/" + input + ".php");
            };
            
            // submit input when someone presses a key down
            $(document).keydown(function(event) {
                console.log(event.keyCode);
                switch(event.keyCode) {
                   case 82:
                    submitInput('RP01');
                    break;
                  case 83:
                    submitInput('left');
                    break;
                  case 68:
                    submitInput('right');
                    break;
                  case 69:
                    submitInput('up');
                    break;
                  case 88:
                    submitInput('down');
                    break;
                  case 37:
                    submitInput('left');
                    break;
                  case 39:
                    submitInput('right');
                    break;
                  case 38:
                    submitInput('up');
                    break;
                  case 40:
                    submitInput('down');
                    break;
                  default:
                }
            });
        </script>
        <style type="text/css">
        table tr, table td {
            border: 1px solid black;
            padding: 5px;
            width: 25px;
            height: 35px;
            text-align: center;
        }
        table {
            float: left;
            margin: 5px;
        }
        </style>
    </head>
    <body>
        <div>
	<table><tr><td>
	<img src="/GetData.cgi"/>
        </td></tr></table>
	<br clear="both">
        <table>
          <tr>
            <td></td>
            <td><a href="#" onClick="submitInput('uleft');">◤</a></td>
            <td><a href="#" onClick="submitInput('up');">▲</a></td>
            <td><a href="#" onClick="submitInput('uright');">◥</a></td>
            <td></td>
          </tr>
          <tr>
            <td><a href="#" onClick="submitInput('leftfast');">◀◀</a></td>
            <td><a href="#" onClick="submitInput('left');">◀</a></td>
            <td><a href="#" onClick="submitInput('rp01');">⌂</a></td>
            <td><a href="#" onClick="submitInput('right');">▶</a></td>
            <td><a href="#" onClick="submitInput('rightfast');">▶▶</a></td>
          </tr>
          <tr>
            <td></td>
            <td><a href="#" onClick="submitInput('dleft');">◣</a></td>
            <td><a href="#" onClick="submitInput('down');">▼</a></td>
            <td><a href="#" onClick="submitInput('dright');">◢</a></td>
            <td></td>
          </tr>
        </table>
       <table>
          <tr>
            <td><a href="#" onClick="submitInput('telefast');">10x</a></td>
            <td><a href="#" onClick="submitInput('tele');">In</a></td>
            <td><a href="#" onClick="submitInput('fnear');">Near</a></td>
            <td><a href="#" onClick="submitInput('fnearfast');">10x</a></td>
          </tr>
          <tr>
            <td></td>
            <td>Zoom</td>
            <td>Focus</td>
            <td></td>
          </tr>
          <tr>
            <td><a href="#" onClick="submitInput('widefast');">10x</a></td>
            <td><a href="#" onClick="submitInput('wide');">Out</a></td>
            <td><a href="#" onClick="submitInput('ffar');">Far</a></td>
            <td><a href="#" onClick="submitInput('ffarfast');">10x</a></td>
          </tr>
        </table>
        <table>
          <tr>
            <td><a href="#" onClick="submitInput('rp01');">1</a></td>
            <td><a href="#" onClick="submitInput('rp02');">2</a></td>
            <td><a href="#" onClick="submitInput('rp03');">3</a></td>
          </tr>
          <tr>
            <td><a href="#" onClick="submitInput('rp04');">4</a></td>
            <td><a href="#" onClick="submitInput('rp05');">5</a></td>
            <td><a href="#" onClick="submitInput('rp06');">6</a></td>
          </tr>
          <tr>
            <td><a href="#" onClick="submitInput('rp07');">7</a></td>
            <td><a href="#" onClick="submitInput('rp08');">8</a></td>
            <td></td>
          </tr>
        </table>
        </div>
    </body>
</html>


Automated daily reboot

The cam tends to hang every three or so days, and I have been unable to find out why. All the indications point towards the IP Cam server, but even the RPi stops responding after a few days to ping. As a stop gap measure, a automated daily restart needs to be implemented, which can be started via the FreeBSD cron daemon. In order to also reboot the video server, the following command was found in the html source of its web interface:

 http://<cam.host.name.or.ip>/Reboot.cgi?RebootNow=1

This restarts the video server. Rebooting the entire camera also disconnects any clients that have been "hanging on" for longer than necessary.