Rivercam with PICAXE pan/tilt control

lbenson

Senior Member
#1
Well, I finally have the webcam set up for the pan/tilt thread I started a year and a half ago, “Pan and tilt control of webcam with PICAXE 08M”: http://www.picaxeforum.co.uk/showthread.php?t=9425&highlight=pan+tilt

Edit: The webcam is viewable here: http://www.lyzby.com/cam.html

Here is a screen shot of the view and control panel.

[EDIT--ignore: You should be able to access these two windows at 24.224.212.167:8080/ and 24.224.212.167:8081 (reduce this window to the right quarter of the screen). The image is viewed and the camera controlled at http://24.224.212.167:8080/cam.html Sorry for the numbered IP addresses, but they are not under my control, and are changeable, tho so far they change only about every 6 months. It might be possible to combine these two into a single window, but I don’t know how.

The Logitech Quickcam Pro 9000 camera is placed in a window in a finished room in the barn, along with the wireless router acting to bridge to the house network (a Linksys WRT54G), an NSLU2 which streams the camera image and passes on pan/tilt commands, and a PICAXE microprocessor board which uses the pan/tilt commands to move servos to pan and tilt the camera.

The network setup was on the complex side—a Netgear WGR614v6 is connected to the cable modem; another Netgear WGR614v6 is wired to that and provides a wireless Access Point on the side of the house closest to the barn; the WRG54G bridges that wireless signal to devices in the barn, including the NSLU2.

Here is a shot of the setup in place.

And another.
 
Last edited:

lbenson

Senior Member
#2
The camera, servos, and picaxe08m control board

The project uses two servos from Trossen Robotics, shown here:

http://www.trossenrobotics.com/store...th-Servos.aspx
The camera and NSLU2 are shown here:

The pan/tilt servos are controlled by a PICAXE 08M microprocessor. The program, circuit schematic, and board images are below.
Code:
' 08Srv_1D test servo
#picaxe 08m

symbol panSpot = b13
symbol tiltSpot = b12
symbol activeServo = b11
symbol activeSpot = b10
symbol lastSpot = b9
symbol panServo = 1
symbol tiltServo = 2
symbol servoMin = 75
symbol servoMax = 225
symbol servoCenter = 150
symbol tiltMin = 135

pause 10000               ' wait 10 seconds
activeSpot = servoCenter 
panSpot = activeSpot       ' center
tiltSpot = activeSpot      ' center
lastSpot = activeSpot - 1
'high 0          'set up for serout
ActiveServo = tiltServo    ' tilt servo is active
gosub servoOut
ActiveServo = panServo     ' pan servo is active
gosub servoOut

main:
  do
    serin 4,N2400,b1
'    serout 0,N2400,(b1, " ",  #b1, " ")
    select b1
     case "p"
      activeServo = panServo
      activeSpot = panSpot
      lastSpot = panSpot   ' make sure we move with new servo
     case "t"
      activeServo = tiltServo
      activeSpot = tiltSpot
      lastSpot = tiltSpot   ' make sure we move with new servo
     case "0"
      activeSpot = servoCenter 
     case "1"
      dec activeSpot
     case "2"
      inc activeSpot
     case "3"
      activeSpot = activeSpot - 5
     case "4"
      activeSpot = activeSpot + 5
     case "5"
      activeSpot = activeSpot - 10
     case "6"
      activeSpot = activeSpot + 10
     case "7"
      activeSpot = servoMin
     case "8"
      activeSpot = servoMax
    endselect

    activeSpot = activeSpot max servoMax 
    activeSpot = activeSpot min servoMin 
    if activeServo = tiltServo then
      activeSpot = activeSpot min tiltMin
    endif
    if activeSpot <> lastspot then
'      serout 0,N2400,(#activeServo, " ",  #activeSpot)
      if activeServo = panServo then
        panSpot = activeSpot
      else
        tiltSpot = activeSpot
      endif
      gosub servoOut
      lastspot = activeSpot
'      pause 50
    endif
  loop

servoOut:  ' smoothly move from lastSpot
  b2 = activeSpot - lastSpot
  b3 = 1
  if activeSpot < lastSpot then
    b2 = lastSpot - activeSpot
    b3 = $FF   ' minus one
  endif
'  serout 0,N2400,(#b2, " ", #b3, " ", #lastSpot, " ", #activeSpot,10,13)
  activeSpot = lastSpot
  for b4 = 1 to b2       ' move smoothly to new activeSpot
    activeSpot = activeSpot + b3
'  serout 0,N2400,(#b4, " ", #b3, " ", #b2, " ", #activeSpot,10,13)
      pulsout activeServo,activeSpot
      pause 20
      pulsout activeServo,activeSpot  ' twice seems to make it work
      pause 20
  next b4
  return


I wrapped the board in electricians tape and was barely able to stuff it inside the case of the NSLU2. It would have been better to do the board with SMD components, but I’ve never done that. The number and size of the connectors is such that the board size could not have been reduced greatly, but it would have been less bulky. The connectors are: power, servo1, servo2, serial control line, programming connector.

I powered the picaxe board off of the NSLU2. The image below shows the power takeoff. Also shown is the DS1233 which I added to make the NSLU2 restart automatically if power has been disrupted and restored (see http://www.nslu2-linux.org/wiki/HowTo/ForcePowerAlwaysOn Alternative one--with DS1233 instead of MPC120).
 

lbenson

Senior Member
#3
The WRT54G

This router is running openWrt. Any recent version using the 2.4 kernel should work (2.4 because wireless is not supported for this router in 2.6 as of this date--tho a fix is said to be close). The crucial wireless configuration file to set up the bridging is as follows (my terminology may not be correct&#8212;I understand this may not strictly be a bridge).
Code:
cat /etc/config/wireless
----------------------------------------
config wifi-device  wl0
        option type     broadcom
        option channel  11

        # REMOVE THIS LINE TO ENABLE WIFI:
#        option disabled 1

config wifi-iface
        option device   wl0
        option network  lan
        option mode     sta
        option ssid     Omnibus4
        option encryption none
 
Last edited:

lbenson

Senior Member
#4
The NSLU2

The NSLU2 also runs openWrt, a custom build with the following enabled in &#8220;make menuconfig&#8221;: nano, lua, mjpg-streamer, kmod-video-core, kmod-video-uvc, kmod-usb-core,kmod-usb-ochi, kmod-usb-serial, kmod-usb-serial-ftdi, and kmod-usb-serial-pl2303.

The webcam plugs into one USB port on the NSLU2, and a USB-to-serial converter into the other. This converter is a PL2303 model. I&#8217;ve also successfully used ftdi models. In theory the serial control could be provided from serial on the NSLU2 board, but after one unsuccessful attempt, I took the easy route.

I drilled 3 1/8th-inch holes in the NSLU2 case, next to the power socket, where there is room inside the case. I cleaned up the opening with my pocket knife so that it was big enough to insert the two servo leads and the serial for the control line.

General information about the NSLU2 is available here: http://www.nslu2-linux.org/wiki/Main/HomePage

Information about openWrt is available here: http://openwrt.org/

The mjpg-streamer program sets up a directory named webcam_www and puts several control programs in there. I used one of them, javascript_simple.html, without changes and renamed it index.html. This is what is running when you go to http://24.224.212.167:8080/
http://24.224.212.167:8081/ takes you to index.html in the standard busybox httpd web server directory, /www. This file looks like this:
Code:
<html>
<head>
<title>Rivercam</title>
</head>

<body>
<form name="form1" method="post" action="/cgi-bin/set-cam">
  <p>
    <input name="Radio_Button" type="radio" value="p" onClick="this.form.submit()"> pan
    <input name="Radio_Button" type="radio" value="t" onClick="this.form.submit()"> tilt
  </p>
  <p>
    Camera movement operates in two modes--pan and tilt.  In "pan" mode, buttons below
      move the camera left or right; in "tilt" mode, up or down.
  </p>
  <p>
    <input name="Radio_Button" type="radio" value="0" onClick="this.form.submit()"> center<br>
    <input name="Radio_Button" type="radio" value="1" onClick="this.form.submit()"> left/down 1<br>
    <input name="Radio_Button" type="radio" value="2" onClick="this.form.submit()"> right/up 1<br>
    <input name="Radio_Button" type="radio" value="3" onClick="this.form.submit()"> left/down 5 steps<br>
    <input name="Radio_Button" type="radio" value="4" onClick="this.form.submit()"> right/up 5 steps<br>
    <input name="Radio_Button" type="radio" value="5" onClick="this.form.submit()"> left/down 10 steps<br>
    <input name="Radio_Button" type="radio" value="6" onClick="this.form.submit()"> right/up 10 steps<br>
    <input name="Radio_Button" type="radio" value="7" onClick="this.form.submit()"> left/down full<br>
    <input name="Radio_Button" type="radio" value="8" onClick="this.form.submit()"> right/up full
  </p>
  <p>
    Daylight hours only--in Nova Scotia (UTC minus 4 hours)
  </p>
</form>
</body>
</html>
Each radio button submits the form immediately when clicked, calling /cgi-bin/set-cam:
Code:
#!/bin/sh
read QUERY_STRING
eval $(echo "$QUERY_STRING"|awk -F'&' '{for(i=1;i<=NF;i++){print $i}}')
tmp=`httpd -d $Radio_Button`
echo "$tmp" > /dev/ttyUSB0
echo "Content-type: text/html"
echo ""
cat /www/index.html
This program simply echoes to the serial port (/dev/ttyUSB0) the value defined in the radio button (p,t,0,1,2,3,4,5,6,7,8), which the picaxe decodes to change the servo settings. Then the cgi program lists the original &#8220;index.html&#8221; program back to standard output, so the radio buttons are available again with no apparent change in the form and no need for a &#8220;submit&#8221; button.
 
Last edited:

lbenson

Senior Member
#6
It's nice to check off an item which has been on my todo list for two years. Now I can look at the river when I am not there.

In fall and winter, eagles regularly come to the near-dead snag across the river at center position. At this resolution, you would not see more than a white dot in the tree--zoom is for another time and a better camera.

Now, what's my next project?
 

lbenson

Senior Member
#7
BB--I think with forms it would be possible, but I don't know how to do it, and I'm not sure that there wouldn't be significant refresh flicker as the page was reloaded. Something to try to figure out at some point.
 

lbenson

Senior Member
#9
Per suggestions above and elsewhere, I've made changes in the NSLU2 code--the controls are now on the same page as the image, and they have been reworked to make the movement easier.

Combining the images was simple with frames--6 lines in /webcam_www/cam.html, as someone showed me:

Code:
<html>
<frameset cols="*,30%">
   <frame src="http://24.224.212.167:8080/">
   <frame src="http://24.224.212.167:8081/index.html">
</frameset>
</html>
In the /www directory, index.html is now as below. The pan and tilt commands are separate radio buttons, and each posts a two-charactor value--the number as before and "p" or "t" to indicate pan or tilt.
Code:
<html>
<head>
<title>Rivercam</title>
</head>

<body>
<form name="form1" method="post" action="/cgi-bin/set-cam">
<!--<form name="form1" method="post" action="/cgi-bin/test-post"> -->
<!--<form name="form1" method="post" action="/cgi-bin/test.lua"> -->
  <p>
    Click a button to pan Camera left or right, or tilt it up or down
  </p>
  <p>
<TABLE BORDER="1" CELLSPACING="0" CELLPADDING="5">
<TR><TD>Pan</TD><TD>Tilt</TD>
<TR><TD>
    <input name="Radio_Button" type="radio" value="p0" onClick="this.form.submit()"> center<br>
</TD><TD>
    <input name="Radio_Button" type="radio" value="t0" onClick="this.form.submit()"> center<br>
</TD></TR>
<TR><TD>
    <input name="Radio_Button" type="radio" value="p3" onClick="this.form.submit()"> left 5 steps<br>
</TD><TD>
    <input name="Radio_Button" type="radio" value="t3" onClick="this.form.submit()"> down 5 steps<br>
</TD></TR>
<TR><TD>
    <input name="Radio_Button" type="radio" value="p4" onClick="this.form.submit()"> right 5 steps<br>
</TD><TD>
    <input name="Radio_Button" type="radio" value="t4" onClick="this.form.submit()"> up 5 steps<br>
</TD></TR>
<TR><TD>
    <input name="Radio_Button" type="radio" value="p5" onClick="this.form.submit()"> left 10 steps<br>
</TD><TD>
    <input name="Radio_Button" type="radio" value="t5" onClick="this.form.submit()"> down 10 steps<br>
</TD></TR>
<TR><TD>
    <input name="Radio_Button" type="radio" value="p6" onClick="this.form.submit()"> right 10 steps<br>
</TD><TD>
    <input name="Radio_Button" type="radio" value="t6" onClick="this.form.submit()"> up 10 steps<br>
</TD></TR>
<TR><TD>
    <input name="Radio_Button" type="radio" value="p7" onClick="this.form.submit()"> left full<br>
</TD><TD>
    <input name="Radio_Button" type="radio" value="t7" onClick="this.form.submit()"> down full<br>
</TD></TR>
<TR><TD>
    <input name="Radio_Button" type="radio" value="p8" onClick="this.form.submit()"> right full
</TD><TD>
    <input name="Radio_Button" type="radio" value="t8" onClick="this.form.submit()"> up full
</TD></TR></TABLE>
  </p>
  <p>
    Daylight hours only--in Nova Scotia (UTC minus 4 hours)
  </p>
</form>
</body>
</html>
The cgi script was modified to send the two characters to the picaxe one at a time, with a one-second pause between to give the picaxe time to get back to the serin. A 100 millisecond pause would probably be fine, but I don't know how to do that in a linux script. As follows, /www/cgi-bin/set-cam:
Code:
#!/bin/sh
read QUERY_STRING
eval $(echo "$QUERY_STRING"|awk -F'&' '{for(i=1;i<=NF;i++){print $i}}')
tmp=`httpd -d $Radio_Button`
#echo "$tmp" > /dev/ttyUSB0
mode=${tmp:0:1}
movement=${tmp:1:1}
echo "$mode" > /dev/ttyUSB0
#next a pause--experimental, based on NSLU2 timing
sleep 1
#echo "                                       " > /dev/null
echo "$movement" > /dev/ttyUSB0
echo "$tmp$movement" >> /var/log/set-cam.log
echo "Content-type: text/html"
echo ""
cat /www/index.html
I've modified the first post to show that the camera can be viewed and controlled at http://24.224.212.167:8080/cam.html
 

hax

New Member
#11
Great work!

I just had a play. Fantastic!

I am sitting on a train with a wireless 3g internet card and I can view and control an image thousands of km away. Amazing.
 

nbw

Senior Member
#14
that's ok - I'll try again in a few days. My 5 yr old will think it's pretty cool to be able to control a camera over a river in the USA from here in NZ!
 
Top