{"id":678,"date":"2009-09-09T21:28:03","date_gmt":"2009-09-10T01:28:03","guid":{"rendered":"http:\/\/biosrhythm.com\/?p=678"},"modified":"2009-09-09T21:28:03","modified_gmt":"2009-09-10T01:28:03","slug":"talking-to-an-old-ptz-camera","status":"publish","type":"post","link":"https:\/\/biosrhythm.com\/?p=678","title":{"rendered":"Talking to an old PTZ camera"},"content":{"rendered":"<p><img loading=\"lazy\" class=\"alignleft size-medium wp-image-679\" title=\"arduino-picturetel-ptz1\" src=\"http:\/\/paulrickards.com\/wp-content\/uploads\/2009\/09\/arduino-picturetel-ptz1-225x300.jpg\" alt=\"arduino-picturetel-ptz1\" width=\"225\" height=\"300\" srcset=\"https:\/\/biosrhythm.com\/wp-content\/uploads\/2009\/09\/arduino-picturetel-ptz1-225x300.jpg 225w, https:\/\/biosrhythm.com\/wp-content\/uploads\/2009\/09\/arduino-picturetel-ptz1.jpg 600w\" sizes=\"(max-width: 225px) 100vw, 225px\" \/>I recently obtained an old Polycom PTZ-1 camera.  This camera went to an old video conferencing system, long past it&#8217;s usefulness in today&#8217;s era of instant desktop video conferencing ala Skype.  The camera was inside a large plastic housing to keep it secure&#8211; I removed it from the housing to get a closer look.<\/p>\n<p>I know most cameras are controlled via RS-232 so I searched around and <a href=\"http:\/\/www.hypertony.co.uk\/hardware\/files\/Powercam100miniprotocol.pdf\">found a site that listed the control codes necessary to talk with this camera.<\/a> <a href=\"http:\/\/www.bdlcam.com\/instructions\/picturetel.gif\">I also found how to build the cable to go from a serial DB-9 to a mini-din 8 here.<\/a> That&#8217;s where the Arduino comes in.<\/p>\n<p>Previously, I figured out <a href=\"http:\/\/little-scale.blogspot.com\/2007\/07\/nes-controller-to-arduino.html\">how to interface an NES joy-pad to the Arduino<\/a> (<a href=\"http:\/\/www.parallax.com\/Store\/Components\/Other\/tabid\/157\/CategoryID\/32\/List\/0\/SortField\/0\/Level\/a\/ProductID\/522\/Default.aspx\">even purchasing the proper female jacks<\/a> so I wouldn&#8217;t need to cut the original cables).  So I thought about marrying the camera with the controller.  Behold, the the Picture PTZ-1 being controlled by the Arduino via an NES joypad.<\/p>\n<p>The first problem I encountered was that the PTel PTZ-1 liked to talk serial using ODD parity.  Yikes, I didn&#8217;t think the Arduino could do that.  <a href=\"http:\/\/www.arduino.cc\/cgi-bin\/yabb2\/YaBB.pl?num=1243894373\">Well, it can actually.  I found a forum posting that listed an extension to the serial protocol of the Arduino.<\/a><\/p>\n<p>Although the Arduino has on board serial, it needs help to speak RS-232 serial (because of voltage differences).  That&#8217;s where the <a href=\"http:\/\/www.sparkfun.com\/commerce\/product_info.php?products_id=8394\">MAX232 chip comes in and it&#8217;s cast of supporting capacitors.<\/a><\/p>\n<p>Below is the Arduino sketch that I&#8217;m using.  The code will pan and tilt the camera using the plus-pad.  The A and B buttons control zoom in and out.  Select and Start are for presets.  The camera has 8 hardware presets.  Press select the number of times equal to the preset you want and press start.  To set a preset, press select the number of times equal to the preset, holding on the last preset and press start.<\/p>\n<p>#include &lt;SerialExtension.h&gt;<\/p>\n<p># NES Controller Pin Setup<br \/>\nint latch = 8;  \/\/ set the latch pin<br \/>\nint clock = 9;  \/\/ set the clock pin<br \/>\nint datin = 10; \/\/ set the data in pin<\/p>\n<p># A Status LED To Light When Receiving input from NES<br \/>\nint ledpin = 13;   \/\/ set the led status pin<\/p>\n<p># Zero Counters<br \/>\nint selects = 0;<br \/>\nint starts = 0;<\/p>\n<p>unsigned char report[6];<br \/>\nunsigned char prev_report[6];<\/p>\n<p>void setup() {<br \/>\n\/\/ Setup NES controller<br \/>\npinMode(latch, OUTPUT);<br \/>\npinMode(clock, OUTPUT);<br \/>\npinMode(datin, INPUT);<br \/>\npinMode(ledpin, OUTPUT);<\/p>\n<p>digitalWrite(latch, HIGH);<br \/>\ndigitalWrite(clock, HIGH);<\/p>\n<p>Serial.begin(9600);<br \/>\nSetSerial(9600,&#8217;O&#8217;, 8, 1);<br \/>\ndelay(500);<br \/>\ninit_camera();<br \/>\nreport[0]=0x80;<br \/>\nreport[1]=0x80;<br \/>\nreport[2] = report[3] = report[4] = report[5] = 0;<br \/>\nprev_report[0]=0x80;<br \/>\nprev_report[1]=0x80;<br \/>\nprev_report[2] = prev_report[3] = prev_report[4] = prev_report[5] = 0;<br \/>\n}<\/p>\n<p>void loop () {<br \/>\nNEScontrollerRead();<\/p>\n<p>\/\/ Tilt up begin<br \/>\nif ((report[1] == 0) &amp;&amp; (prev_report[1] == 0x80)) { motion_begin(&#8216;t&#8217;, &#8216;u&#8217;); }<br \/>\n\/\/ Tilt up stop<br \/>\nif ((report[1] == 0x80) &amp;&amp; (prev_report[1] == 0)) { motion_stop(&#8216;t&#8217;, &#8216;u&#8217;); }<br \/>\n\/\/ Tilt up cont<br \/>\nif ((report[1] == 0) &amp;&amp; (prev_report[1] == 0)) { motion_cont(&#8216;t&#8217;, &#8216;u&#8217;); }<\/p>\n<p>\/\/ Tilt down begin<br \/>\nif ((report[1] == 0xFF) &amp;&amp; (prev_report[1] == 0x80)) { motion_begin(&#8216;t&#8217;, &#8216;d&#8217;); }<br \/>\n\/\/ Tilt down stop<br \/>\nif ((report[1] == 0x80) &amp;&amp; (prev_report[1] == 0xFF)) { motion_stop(&#8216;t&#8217;, &#8216;d&#8217;); }<br \/>\n\/\/ Titlt down cont<br \/>\nif ((report[1] == 0xFF) &amp;&amp; (prev_report[1] == 0xFF)) { motion_cont(&#8216;t&#8217;, &#8216;d&#8217;); }<\/p>\n<p>\/\/ Pan left begin<br \/>\nif ((report[0] == 0) &amp;&amp; (prev_report[0] == 0x80)) { motion_begin(&#8216;p&#8217;, &#8216;l&#8217;); }<br \/>\n\/\/ Pan left stop<br \/>\nif ((report[0] == 0x80) &amp;&amp; (prev_report[0] == 0)) { motion_stop(&#8216;p&#8217;, &#8216;l&#8217;); }<br \/>\n\/\/ Pan left cont<br \/>\nif ((report[0] == 0) &amp;&amp; (prev_report[0] == 0)) { motion_cont(&#8216;p&#8217;, &#8216;l&#8217;); }<\/p>\n<p>\/\/ Pan right begin<br \/>\nif ((report[0] == 0xFF) &amp;&amp; (prev_report[0] == 0x80)) { motion_begin(&#8216;p&#8217;, &#8216;r&#8217;); }<br \/>\n\/\/ Pan right stop<br \/>\nif ((report[0] == 0x80) &amp;&amp; (prev_report[0] == 0xFF)) { motion_stop(&#8216;p&#8217;, &#8216;r&#8217;); }<br \/>\n\/\/ Pan right cont<br \/>\nif ((report[0] == 0xFF) &amp;&amp; (prev_report[0] == 0xFF)) { motion_cont(&#8216;p&#8217;, &#8216;r&#8217;); }<\/p>\n<p>\/\/ Zoom in begin<br \/>\nif ((report[2] == 1) &amp;&amp; (prev_report[2] == 0)) { motion_begin(&#8216;z&#8217;, &#8216;i&#8217;); }<br \/>\n\/\/ Zoom in stop<br \/>\nif ((report[2] == 0) &amp;&amp; (prev_report[2] == 1)) { motion_stop(&#8216;z&#8217;, &#8216;i&#8217;); }<br \/>\n\/\/ Zoom in cont<br \/>\nif ((report[2] == 1) &amp;&amp; (prev_report[2] == 1)) { motion_cont(&#8216;z&#8217;, &#8216;i&#8217;); }<\/p>\n<p>\/\/ Zoom out begin<br \/>\nif ((report[3] == 1) &amp;&amp; (prev_report[3] == 0)) { motion_begin(&#8216;z&#8217;, &#8216;o&#8217;); }<br \/>\n\/\/ Zoom out stop<br \/>\nif ((report[3] == 0) &amp;&amp; (prev_report[3] == 1)) { motion_stop(&#8216;z&#8217;, &#8216;o&#8217;); }<br \/>\n\/\/ Zoom out cont<br \/>\nif ((report[3] == 1) &amp;&amp; (prev_report[3] == 1)) { motion_cont(&#8216;z&#8217;, &#8216;o&#8217;); }<\/p>\n<p>\/\/ Select button function<br \/>\nif ((report[4] == 1) &amp;&amp; (prev_report[4] == 0)) {<br \/>\nselects++;<br \/>\nif (selects &gt; 8) { selects = 0; }<br \/>\n}<\/p>\n<p>\/\/ Select and Start functions<br \/>\n\/\/ Goto preset<br \/>\n\/\/ Press &#8220;Select&#8221; times to equal the number of the preset<br \/>\n\/\/ then press &#8220;Start&#8221; to go to that preset.<br \/>\nif ((report[5] == 1) &amp;&amp; (prev_report[5] == 0) &amp;&amp; (report[4] == 0)) {<br \/>\nif (selects &gt; 0) {<br \/>\ncommand_begin();<br \/>\nSerial.print(&#8220;g0&#8221;);<br \/>\nselects&#8211;;<br \/>\nSerial.print(selects);<br \/>\nSerial.print(&#8220;n&#8221;);<br \/>\ncommand_end();<br \/>\nselects = 0;<br \/>\n}<br \/>\n}<\/p>\n<p>\/\/ Store preset<br \/>\n\/\/ Press &#8220;Select&#8221; times MINUS 1 equal to the numer of the preset to SET<br \/>\n\/\/ Press and HOLD &#8220;Select&#8221; on the last number and press &#8220;Start&#8221; to set the preset<br \/>\nif ((report[4] == 1) &amp;&amp; (report[5] == 1) &amp;&amp; (prev_report[5] == 0)) {<br \/>\nif (selects &gt; 0) {<br \/>\ncommand_begin();<br \/>\nSerial.print(&#8220;s00&#8221;);<br \/>\nselects&#8211;;<br \/>\nSerial.print(selects);<br \/>\ncommand_end();<br \/>\nselects = 0;<br \/>\n}<br \/>\n}<\/p>\n<p>prev_report[0] = report[0];<br \/>\nprev_report[1] = report[1];<br \/>\nprev_report[2] = report[2];<br \/>\nprev_report[3] = report[3];<br \/>\nprev_report[4] = report[4];<br \/>\nprev_report[5] = report[5];<\/p>\n<p>delay(100);<br \/>\n}<\/p>\n<p>void NEScontrollerRead() {<\/p>\n<p>int x = 0x80, y = 0x80; \/\/ 0x80 = 128 = axis centered (nothing pressed)<br \/>\nint a, b, select, start, led = 0;<br \/>\nreport[0] = report[1] = report[2] = report[3] = report[4] = report[5] = 0;<br \/>\nunsigned char tmp = 0;<\/p>\n<p>digitalWrite(latch, LOW);<br \/>\ndigitalWrite(clock, LOW);<\/p>\n<p>digitalWrite(latch, HIGH);<br \/>\ndelayMicroseconds(2);<br \/>\ndigitalWrite(latch, LOW);<\/p>\n<p>\/\/ button A<br \/>\na = !digitalRead(datin);<br \/>\nNESCycleClock();<\/p>\n<p>\/\/ button B<br \/>\nb = !digitalRead(datin);<br \/>\nNESCycleClock();<\/p>\n<p>\/\/ button Select<br \/>\nselect = !digitalRead(datin);<br \/>\nNESCycleClock();<\/p>\n<p>\/\/ button Start<br \/>\nstart = !digitalRead(datin);<br \/>\nNESCycleClock();<\/p>\n<p>\/\/ button Up<br \/>\nif (!digitalRead(datin)) { y = 0; led = 1;}<br \/>\nNESCycleClock();<\/p>\n<p>\/\/ button Down<br \/>\nif (!digitalRead(datin)) { y = 0xff; led = 1; }<br \/>\nNESCycleClock();<\/p>\n<p>\/\/ button Left<br \/>\nif (!digitalRead(datin)) { x = 0; led = 1; }<br \/>\nNESCycleClock();<\/p>\n<p>\/\/ button Right<br \/>\nif (!digitalRead(datin)) { x = 0xff; led = 1; }<br \/>\nNESCycleClock();<\/p>\n<p>report[0] = x;<br \/>\nreport[1] = y;<br \/>\nreport[2] = a;<br \/>\nreport[3] = b;<br \/>\nreport[4] = select;<br \/>\nreport[5] = start;<br \/>\n\/\/report[2] = (a &lt;&lt; 0) + (b &lt;&lt; 1) + (select &lt;&lt; 2) + (start &lt;&lt; 3);<br \/>\nif (a || b || select || start || led) { digitalWrite(ledpin, 1); }<br \/>\nelse { digitalWrite(ledpin, 0); }<br \/>\n}<\/p>\n<p>void NESCycleClock() {<br \/>\ndelayMicroseconds(4);<br \/>\ndigitalWrite(clock, LOW);<br \/>\ndigitalWrite(clock, HIGH);<br \/>\ndelayMicroseconds(2);<br \/>\n}<\/p>\n<p>void command_begin() {<br \/>\nSerial.print(0x02, BYTE);<br \/>\nSerial.print(&#8220;ck&#8221;);<br \/>\n}<\/p>\n<p>void command_end() {<br \/>\nSerial.print(0x04, BYTE);<br \/>\n}<\/p>\n<p>void init_camera() {<br \/>\ncommand_begin();<br \/>\nSerial.print(&#8220;k&#8221;);<br \/>\ncommand_end();<\/p>\n<p>command_begin();<br \/>\nSerial.print(&#8220;i0n&#8221;);<br \/>\ncommand_end();<\/p>\n<p>\/\/ Wait for an ACK from camera<br \/>\nwhile(Serial.read() != 0x04) {}<\/p>\n<p>command_begin();<br \/>\nSerial.print(&#8220;d0&#8221;);<br \/>\nSerial.print(0xF2, BYTE);<br \/>\ncommand_end();<br \/>\ndelay(100);<\/p>\n<p>\/\/ Wait for an ACK from camera<br \/>\nwhile(Serial.read() != 0x04) {}<\/p>\n<p>command_begin();<br \/>\nSerial.print(&#8220;z0m&#8221;);<br \/>\ncommand_end();<\/p>\n<p>\/\/ Wait for an ACK from camera<br \/>\nwhile(Serial.read() != 0x04) {}<br \/>\n}<\/p>\n<p>void motion_begin(char axis, char dir) {<br \/>\ncommand_begin();<br \/>\nSerial.print(&#8220;r0k&#8221;);<br \/>\nSerial.print(axis, BYTE);<br \/>\nSerial.print(dir, BYTE);<br \/>\nif (dir == &#8216;u&#8217;) { Serial.print(&#8220;099&#8221;); }<br \/>\nif (dir == &#8216;d&#8217;) { Serial.print(&#8220;097&#8221;); }<br \/>\nif (dir == &#8216;l&#8217;) { Serial.print(&#8220;100&#8221;); }<br \/>\nif (dir == &#8216;r&#8217;) { Serial.print(&#8220;101&#8221;); }<br \/>\ncommand_end();<br \/>\n}<\/p>\n<p>void motion_cont(char axis, char dir) {<br \/>\ncommand_begin();<br \/>\nSerial.print(&#8220;c0k&#8221;);<br \/>\nSerial.print(axis, BYTE);<br \/>\nSerial.print(dir, BYTE);<br \/>\ncommand_end();<br \/>\n}<\/p>\n<p>void motion_stop(char axis, char dir) {<br \/>\ncommand_begin();<br \/>\nSerial.print(&#8220;p0k&#8221;);<br \/>\nSerial.print(axis, BYTE);<br \/>\nSerial.print(dir, BYTE);<br \/>\ncommand_end();<br \/>\n}<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I recently obtained an old Polycom PTZ-1 camera. This camera went to an old video conferencing system, long past it&#8217;s usefulness in today&#8217;s era of instant desktop video conferencing ala Skype. The camera was inside a large plastic housing to keep it secure&#8211; I removed it from the housing to get a closer look. I [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[3,15],"tags":[],"_links":{"self":[{"href":"https:\/\/biosrhythm.com\/index.php?rest_route=\/wp\/v2\/posts\/678"}],"collection":[{"href":"https:\/\/biosrhythm.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/biosrhythm.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/biosrhythm.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/biosrhythm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=678"}],"version-history":[{"count":0,"href":"https:\/\/biosrhythm.com\/index.php?rest_route=\/wp\/v2\/posts\/678\/revisions"}],"wp:attachment":[{"href":"https:\/\/biosrhythm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=678"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/biosrhythm.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=678"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/biosrhythm.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=678"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}