Update (7/18/08): this driver and instructions work for ZoneMinder 1.22.x only, see my update for ZM 1.23.x support.

I can’t even remember when was the last time that I had to reverse engineer something.. it may have been as long ago as my college days. But last week I had a couple of evenings of fun figuring out the inner workings of my Trendnet TV-IP400W. This is a cool little internet camera with built in wifi and a built in web server. You can control it from a browser and you can actually pan and tilt this camera around the room, so even though the picture quality isn’t great (probably comparable to your average webcam), it can be very useful if you want to keep an eye on your house, your pets, your baby, etc from anywhere in the world. The best part: you can pick one up online for less than $200, which is a steal for a pan/tilt IP camera.

The built in web server is useful for checking up on the live situation, but it does not have any features to automatically notify something or someone if motion is detected in front of the camera. The windows control application does do motion triggered recording, but if you want to monitor your camera from outside your home you’ll have to set it up to email all the videos to yourself, as the windows app can’t function as a web interface.

All of this makes this camera more of a toy, with limited remote monitoring features. But, I wouldn’t be me if I didn’t figure out a way to get what I wanted from this thing. Since I already have 2 Linux boxes running Misterhouse, MythTV and various databases and developer tools, I figured one of these boxes could easily function as a surveillance system as well.

So I set out to install ZoneMinder on my Ubuntu system. ZoneMinder is an extremely full featured (open source) video camera security and surveillance system. It supports multiple CCTV and IP cameras, has a web interface, it can control PTZ (Pan Tilt Zoom) cameras, and supports custom triggers, multiple motion zones per camera, etc.

After a little bit of tinkering I got ZoneMinder running, and it turned out that even getting it to capture video from my Trendnet cam turned out to be trivial… All I needed to do was to configure the camera stream to http://192.168.0.140/video.cgi on my home LAN!

Unfortunately, that’s as much as I’d be able to do with ZoneMinder and this cam. The IP400W uses a proprietary control protocol to move the camera and there wasn’t any ZoneMinder driver for it yet.

But how hard could it be to write one?


The camera already came with a web interface with simple form buttons to control movement, so I figured these commands should be easy to fake by a script. That wasn’t the case though, since there were no direct http requests from the camera control page: the page loads a Java applet and this applet is responsible for all TCP/IP communication with the camera.

After that first little hump I figured I should probably first email Trendnet and ask them nicely for the protocol specs, but as I more or less expected they weren’t able to give me that info (booo). So my only chance would be to reverse engineer the protocol and write my control script with what I learned from that. Here’s what I did.. and if you’re not interested in how I did it you can scroll straight down to find out how to get your own Trendnet camera working in ZoneMinder. If you just want to know more about the details of the protocol so you can write your own driver, check out the specs here.

decompiling the java applet of the Trendnet’s web interface.

So as it turned out, the web interface wasn’t sending control commands back to the camera using HTTP GET or POST from the control page, but it was sending commands through a Java applet instead, by calling functions like movePanTiltDegree() on the applet. That wasn’t going to tell me much about the command protocol, so I installed Jad, saved the applets from my cam on my harddisk and started decompiling.
I had never actually decompiled a java class before and it was pretty interesting to see proprietary binary-only software turn back into source code with Jad! Of course all original names of non-public methods and variables had been lost, but there were only a couple of private methods in the class, and many of the member variables I could give real names again by looking at how they were used in public getter and setter methods. Unfortunately Jad had problems decompiling part of the class file, so the resulting source file had some garbage in there and I wasn’t going to be able to run the code in a debugger to really see what commands it would generate. But in any case it was clear what urls it uses for control, for administration features and for the video stream, and what command parameters it knows.

running a network sniffer to reverse engineer the protocol

Decompiling the camera’s java applet was very useful, but after seeing that the applet uses simple HTTP POST requests to send commands to the camera, I realized I may have been able to skip all of that and figure out the (text based) protocol with a simple network sniffer. So I installed Eavesdrop on my Mac, a handy litle tool that will tell you exactly what data is coming into and leaving your system. (before all of this I had tried Firebug, since that should also tell you about all requests leaving Firefox, but as it turns out Firebug does not track any requests sent by java applets, only by html and javascript code).
As soon as I fired up Eavesdrop and started playing with the IP400W’s control pages, the answers to my questions started rolling in.
In less than an hour I had all the commands written down and I was able to send basic commands to the camera with a simple perl script.
While playing with the parameter values, i even found some commands that aren’t possible with the web interface. It turns out the camera can be moved diagonally as well (which is not possible with the Trendnet’s web interface, although I think it is with the PC app) and there is also a swing mode that moves slowly from the extreme left to the extreme right position and back, stop for a second at every 10 degrees or so.

Another interesting thing I learned was that the zoom feature isn’t really supported on the camera server side at all. When you change the view mode to 2x or 4x, it is the applet in your browser that simply magnifies the image. It was clear from the picture degradation that the camera didn’t have an optical zoom, but it’s too bad that the digital zoom is done on the client side, since that means that ZoneMinder won’t have access to a zoom feature, unless I’d write some server side script that can manipulate the incoming video stream.

For people who want to write their own control applications, the protocol descriptions are here, and you can read about my ruby driver here. My ruby driver actually includes support for storing presets, which the ZM driver can’t do (yet).

writing the zoneminder control script and configure ZoneMinder for this cam

With the IP400W’s control protocol (mostly) decyphered, it was now time to write the ZoneMinder control script. Fortunately, that was the easy part. ZoneMinder has done an excellent job of creating a camera control interface that should be able to support controllable cameras with almost any set of features, by calling a script or external program with certain predefined parameters. Even better news, an existing control script for a panasonic IP camera showed me how that is sending HTTP GET commands to a Panasonic camera with a similar set of features. I’m not a Perl pro at all, but I could handle this one. Another hour later, I was controlling my Trendnet cam through ZoneMinder!

I’ll get to the point now, because if you’re still reading here, you’re probably also an IP400W or IP400 owner who wants to use it with ZoneMinder. Here’s what to do:

  1. Download my zmcontrol-trendnet-ip400.pl script and install it in a directory that’s in your path. On my Ubuntu system the other control scripts were installed in /usr/local/bin so that would be a good spot to put this one as well. Make sure the script has execute permissions so you may want to run chmod 755 zmcontrol-trendnet-ip400.pl
  2. That’s really all that should be required on the installation end. Now all that’s left is to configure ZoneMinder to Control your Trendnet. First, make sure your ZoneMinder configuration is configured to control PTZ cameras. In the System tab of the Options page, make sure the option ZM_OPT_CONTROL is checked.
  3. If you haven’t done so yet, it’s time to add your new monitor now. In the General tab, give your cam a name (mine is called kitchen_cam) and set the source type to ‘remote’. In most of the tabs I didn’t change much from the default settings. Here’s what my tabs look like:

General Tabmonitor - source tab

The settings in the ‘buffers’ and ‘timestamp’ tabs do not need to be changed. My ‘control’ tab looks as follows:

monitor - control tab

Make sure the control IP address or hostname is correct, it should be the same as what you entered in the ’source’ tab.

The Control Type input dropdown in your ZoneMinder installation likely won’t have the Trendnet IP-400W option, so let’s enter that. Click on ‘edit’.

Control capabilities screen

Click on ‘Add New Control’. This should pop up the Control Capability window with a 9 different tabs. These screens are used to specify what kind of features our camera supports. I’ve added screenshots of my tabs below. Some of these settings may be irrelevant or not optimal, but as they say: if it ain’t broke, don’t fix it.

No zoom, focus, white and iris features are supported by this camera so you can leave all the checkboxes unchecked in the corresponding tabs. Finally, my presets tab looks like this:

You probably want to change the ‘Num Presets’ value to the actual number of presets that you’re using (the max is 25), so you don’t end up with a bunch of buttons in the control page that don’t do anything.

Setting presets is not supported yet by my ZoneMinder driver. I’ve figured out how to do it now and it’s supported in my new Ruby driver, but I probably won’t add it to the ZoneMinder driver unless I get a lot of requests for it. (it’s a little trickier to implement than the other commands, since it requires pulling in and parsing part of an mjpeg stream).

When you’re done setting values in all these tabs, click on ’save’ and you should see the control capabilities window again, with your new Trendnet control added to the list. Make sure this new control is selected in the Monitor - Control tab above.

The one thing that I do need to add at some point is security control. Right now, this driver will only work if you disable user access control in the Configuration -> User screen of the Trendnet administration interface. So make sure your cam is behind a firewall!

It’s time to test your new camera control. Click on your camera in the main ZoneMinder console page, and when your camera window opens you should see a ‘Control’ link in the top. (if you don’t see it, double check that the ZM_OPT_CONTROL option is set as described in step 2). Click on the Control link and you should see something like this:

All the buttons for movement and presets should work now. If they don’t, check your ZoneMinder logs and take it from there.

I hope this page is helpful to the TV-IP400 users out there! Please let me know if you have any suggestions or feedback or if you’ve improved the code or configuration.

I’ve also been working on a Ruby driver for this camera, and a small and simple rails application for viewing and controlling it from the web. It’s got a separate interface for handhelds such as smart phones or pocket PCs so you can easily check on your house/company/baby/dog when you’re out and about. It could also make a good software/hardware combo for setting up a cheap controllable outdoor or indoor webcam for public access. I’ll post more about this soon.