The WaterHobo

Ever Diligent Ever Watching

Once upon a time a subdivision swimming pool caused a minor problem, but to a problem solving geek a problem is a problem that needs an answer.  So what were the problem and the solution?  Well for all you legal types it is rabbits eating in my garden.  So for the sake of this discussion we are talking about rabbits, however I must make a disclaimer that in motion detection and autofire if an unwitting person is trespassing on my property and happens to walk into the field of fire... well we will refer to them as collateral damage.  Well the problem was Adult rabbits and little rabbits cutting between two houses instead of just walking an extra few feet to the pathway to the concrete pond. The little rabbits were never really the problem, it was the adults, there is nothing like being on your deck and some adult rabbit cutting between your house and the neighbor's and they just look at you like "What is there something wrong?" Never asking if it was ok, just doing it.  Some how I have to believe they would have an issue if I say ran laps around their house for my exercise program, but I digress... So I have this problem of rabbits cutting in my yard, it does not matter the time and of course it is from Memorial day to Labor day, before and after that time frame the traffic reduces greatly.  Of course the most active time is during swim meet season when I also have to deal with an extra 100 rabbits or so from different subdivisions.  Ok I have a problem, so I must have goals or objectives that have to be met in order for the ROI to be there.  So my defined goals were:

      • Must have automatic fire ability
      • Must be able to detect motion
      • Must be able to record the event
      • Must be able to turn off auto fire mode and take on manual control
      • Must be able to "Water the Lawn"
      • Must be able to cover a 30 foot field of fire
      • Must have a range of about 25 to 35 feet 
      • Must have the ability to work at night with all of the above features
      • Must be able to Play back any recordings and laugh while sitting on the deck drinking a beer.

So what was the solution?  This I pondered and opened up my book of knowledge called the Internet and using the glorious thing called searching, I started to come up with an idea.  The picture above is actually prototype number two.  I will briefly talk about Prototype one, but since media player in a browser has a 5 second buffer(yes I tried to override it, but it ignored my override values) that I can not override anywhere, I had to move on to version two.  To meet the requirements/objectives I decided the first prototype needed the following items.

      • X10 120 volt outlet
      • X10 Transmitter and Receiver
      • X10 Libraries that would work with C# 
      • In ground sprinkler solenoid
      • Phidget single servo board with servo
      • Phidget C# libraries
      • PVC pipe, Copper Ice maker pipe, and a few other plumbing parts
      • Old computer, 1.7 GHz with a gig of ram
      • USB Web camera

Like I said above I ran into a problem with media player in a browser, so if anyone know the answer to this problem please let me know.  Here is some photos and video of prototype one.

 

Video of version 1 from the console, note the delay in the video

**** Video is a 6 meg file, still learning on making the format "Web" friendly  ***

As you can see I have met a bunch of my goals in prototype 1, I can shoot about 30 feet and can cover a field of fire of about 30 feet.  I can also control the movement of the barrel by a servo and a Phidget device.  In this version in the top bunker "Window" I have a web camera mounted to the barrel and it shows the video of what it sees as the barrel moves. I control this all via a web page that was written in C# and using ASP 2.0 with a little bit of Ajax thrown in.  But as I have said before media player force me to version 2.

One issue that I did want to point out is when dealing with controlling your phidget devices from a web page, I ran into a state problem.  the solution was to declare the phidget variable as a static variable.  This allowed me to maintain control over the servos between postbacks.  I do believe a better answer which I am working on is to create all calls to the servo and X10 from a web service.  I have included the code to the web service, it still needs some work on it, namely turning everything into a singleton type call, I do not want multiple instances of the object.

static Phidgets.Servo enc1;

 

So what is version 2 and what lessons did we learn in version 1?  Well in version 1, I found out latency is not your friend; you have about 6 seconds to before target was out of range and also that cheap water hoses will burst under pressure. "Rabbits" are stupid, if they walk to close to either house they feel enclosed, so they walk right down the middle, giving a great field of fire.  They also react to sound, it will either make them stop and look for the sound or at the least keep walking but look for the sound, and this offers great photo shoot opportunities.    Another lesson learnt was that X10 works, but could work better, in time I will just switch to relays.  The in ground sprinkler solenoid worked great, it offered almost instant on, and has only a 2 second wait to turn off (time for solenoid to release). Version 2 needed to do everything version 1could do and then some, so back to the drawing board. 

I decided to go back to a thick client, dump the web camera and moved to an Infrared camera.  The most important upgrade was to an open source library that I found called Aforge.  Andrew Kirillov has done a fantastic job with his library .  It held the foundation of version 2's code base.  It provided me with the motion detection I needed plus had an avi writer sample.  He also had some great topics on "codeproject.com" on uses of his library.  This is what had me replace the web camera with an IP camera. Other thing that I did was replace all hoses with PVC pipe, I liked the easy setup and flexibility of the hose, but they can not hold up to the pressure day after day.  So to recap I still am using the same turret concept in version 1 that was in version 2.  I removed the mobile web camera and now have a fixed infrared camera, replaced all hoses with PVC pipes.  Added Aforge libraries and his Camera control.  Added in sound and ability to record from the point of motion to a fixed time after motion is no longer detected.  And finally switched to a thick windows client.  This is some finished photos:

 

 

 

 

 

Yes I know that the plumbing could be better, but this is only a prototype, in fact some of it will disappear based on lessons/enhancements that will be made to version 2 that is why it is called a prototype so we can learn. 

The code is very simple most of the complexity was already done for me by some of the others who created the libraries that I am using.  I simply put a bunch of parts together, some of the things that I did, plus some of the changes to the libraries; I will go over.  Like I stated before, each of the libraries have good working samples and I would suggest you start there.  From that point you can work any of the code I did into your projects as you like.

This method shows all of the functionality in one call so I am showing it for its simplicity.

         MyGun myGun = new MyGun();  //defined at the form level as I use this object for manual control also.

         public void HolyHandGrenade()
        {
            log += "In Holy Grenade " + DateTime.Now.ToString() + " ,";
            SoundPlayer sp = new SoundPlayer("Daffy04.wav");
            sp.Play();
            myGun.Grenade();
            sp = new SoundPlayer("Sam35.wav");
            sp.Play();

            // leave  on for 10 seconds, servo is moving back and forth at this time
            Thread.Sleep(10000);
            try
            {
                if (thread.IsAlive)
                {
                    thread.Abort();
                }
                // kill the current thread
            }
            catch (Exception e)
            {
                bridgeErrMsg += ",BridgeViewer.HolyGrenade => " + e.Message + " " + myGun.errmsg;
                log += "In Holy Grenade execp " + DateTime.Now.ToString() + " ,";
            }
            finally
            {
                myGun.ReLoadGun();
                log += "In Holy Grenade finally " + DateTime.Now.ToString() + " ,";
            }
        }   

One of the more important things (this is also demostrated in the Aforge samples, it is what I followed ) is that you will need to setup some events in your code that will be triggered by the Camera control.  The events that I am currently using are the "NewFrame" and "Alarm" events. They are setup by the OpenVideoSource and CloseVideoSource method calls.  Needless to say the Alarm event is fired everytime motion is detected.  The key thing to remember here is that the alarm is firing with every new frame that the camera takes that is many in a second.  I only want to fire the gun if it is not currently firing and motion is detected.  In the NewFrame event this is fired every time the camera takes a picture, again remember this is many times a second.  This event is where the code is writing to an AVI file frame by frame, but this too I wanted to control, so if I did not want to capture everything to video I could override the defaults.  My future plan here will be to replace the current AVI writer with one using directx so I will be able to capture video and sound.  Also I plan to add a gun site to the frames here so in manual mode you will be able to see a cross hair and it would move as you would move a joystick.

        // On alarm Event
        private void camera_Alarm(object sender, System.EventArgs e)
        {
            timeLastAlarm = DateTime.Now;
            AlarmOn = true;

             if (AutoFire == true)
            {
               if (gunOn == false)
                {
                    log += "Gun on " + DateTime.Now.ToString() + " ,";
                    gunOn = true;
                    thread = new Thread(new ThreadStart(HolyHandGrenade));
                    thread.Start();
                }

            }

        }

 

        // On new frame
        private void camera_NewFrame(object sender, System.EventArgs e)
        {
            DateTime now = DateTime.Now;

            if(timeLastAlarm.AddMinutes(minsAfterLastAlarm) < now && writer != null)
            {
                writer.Close();
                writer.Dispose();
                writer = null;
                AlarmOn = false;
            }

            if (Rec == true && AlarmOn == true)
            {
                // lets save the frame

                if (writer == null)
                {
                    // create file name
                    DateTime date = DateTime.Now;
                    String fileName = String.Format("{0}-{1}-{2} {3}-{4}-{5}.avi",
                        date.Year, date.Month, date.Day, date.Hour, date.Minute, date.Second);

                    try
                    {
                        OpenWriter(fileName, cameraWindow.Camera.Width, cameraWindow.Camera.Height);
                    }
                    catch
                    {
                        if (writer != null)
                        {
                            writer.Dispose();
                            writer = null;
                        }
                    }
                }

                // save the frame
                try
                {
                    // save the frame
                    MotionDetector.Camera camera = cameraWindow.Camera;

                    camera.Lock();

                    // dispose old frame
                    if (bmpLastFrame != null)
                    {
                        bmpLastFrame.Dispose();
                    }

                    Bitmap bmpSource = camera.LastFrame;
                    bmpLastFrame = (Bitmap)bmpSource.Clone();
                    writer.AddFrame(bmpLastFrame);
  
                  camera.Unlock();
                    timeLastSavedFrame = DateTime.Now;

           
                }
                catch (Exception ex)
                {
                    System.Diagnostics.Trace.WriteLine(ex.ToString());
                }

            }

         }

 


        // Open video source
        private void OpenVideoSource(IVideoSource source)
        {
            // set busy cursor
            this.Cursor = Cursors.WaitCursor;

            // close previous video source
            CloseVideoSource();

            // create camera
            MotionDetector.Camera camera = new MotionDetector.Camera(source, detector);
            // start camera
            camera.Start();

            // attach camera to camera window
            cameraWindow.Camera = camera;

             // set event handlers
            camera.NewFrame += new EventHandler(camera_NewFrame);
            camera.Alarm += new EventHandler(camera_Alarm);
            camera.VideoSourceError += new EventHandler(camera_VideoSourceError);
            // start timer
            timer.Start();

            this.Cursor = Cursors.Default;
        }


        // Close current video source
        private void CloseVideoSource()
        {
            MotionDetector.Camera camera = cameraWindow.Camera;

            if (camera != null)
            {
                // stop timer
                timer.Stop();

                camera.NewFrame -= new EventHandler(camera_NewFrame);
                camera.Alarm -= new EventHandler(camera_Alarm);
                camera.VideoSourceError -= new EventHandler(camera_VideoSourceError);

                // detach camera from camera window
                cameraWindow.Camera = null;
                Application.DoEvents();

                // signal camera to stop
                camera.SignalToStop();
                // wait 2 seconds until camera stops
                for (int i = 0; (i < 20) && (camera.IsRunning); i++)
                {
                    Thread.Sleep(100);
                }
                if (camera.IsRunning)
                    camera.Stop();
                camera = null;

                // reset motion detector
                if (detector != null)
                    detector.Reset();

            }
        }

 

 

Like I have already stated, most all of the samples from AForge are pretty straight forward and easy to use, almost all of the above snipit of code came from one of the samples.  One thing I did do was break the camera control out into a project by itself.  Once this was created I just had to simply drag and drop the control on a form like any other .Net control and do whatever I wanted from with in my application.  One change I did implement in the Camera control was to setup a property to give me more control on the sensitivity for motion detection, I felt the default was way to sensitive for my needs and wanted a way to adjust it. 

       public double AlarmLevel
        {
            get
              {
                 return alarmLevel;
              }

              set
              {
                  alarmLevel = value;
              }
        }

As for my Gun class, it is made up of two other classes.  One of these classes handles the Phidget devices the other handles the X10 devices.  The Gun Class just puts it all together.  The web service I have enclosed demostrates all of this and it is pretty straight forward to understand.  If you still have questions feel fee to send me an email.  web service code is here .  

So with version 2 where are we and what did we learn? 

  • Motion detection works great, sometimes to good. 
  • We now have video and it is meeting the goal of having something to laugh about.
  • Auto fire works and combine it with "Holy Hand Grenade" mode and you cover a lot of territory.
  • You can teach "Rabbits" new tricks.
  • PVC was the right move.
  • Servo controller boards do not like thunder storms, keep it water tight
  • A lot more "Rabbits" cut through than I thought, some even riding bikes on my lawn
  • Do not shoot your wife, even by accident (might not be a version 3 :) ).
  • Your kid will use it like running through a lawn sprinkler
  • Using sound effects from Star Trek, Mars Attacks, Daffy Duck and Yosimini Sam added to the fun
  • Infrared camera's are not all built the same

Ok so what type of enhancements are we talking here (some of these are already in the works)?  Well just brain storming...

  • Create a click once client so I can deploy to friends to have manual fun.
  • Add in Microsoft's new Robot SDK
  • Put functionality of servo and X10 control in a web service (already did this and it opens oh so many ideas. cell phones come to mind...also what waterhobo.com is needed for...)
  • Have motion detection that goes to motion tracking.
  • Switch from only horizontal to both horizontal and vertical (Pan and Tilt) for barrel
  • Switch to directX avi recording, I want to record sound and video
  • Using directX, add in Joystick capability even from remote over the web (using web service)
  • Make it more portable
  • Add in pop up in ground sprinklers
  • Create Infrared Spot light to help cameras at night.

In closing I have learnt a great deal and have a great deal of rework to do, in some cases things were to complex and in others there was just not enough functionality for what I wanted to do, but I do know it can be done, and I do love a challenge, so it will be. And finally a note from the WaterHobo, well I did not catch any rabbits, but man you would not believe all of the collateral damage or people that "Just" happened to trespass on my yard, some as late as 11:30 at night.  They choose to ignore my "Pest" control signs, I guess that is why we call them collateral damage....

Stay tuned for more videos and version 3, its coming...

If you would like to know more email me (Tim Higgins) at tbh726@gmail.com

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

This Web Page Created with PageBreeze Free Website Builder