.NET Gadgeteer-powered RoombaCam Prototype Complete

A while ago, I got the .NET Gadgeteer FEZ Starter Kit from GHI Electronics. For some reason I never got around to doing anything with it aside from that very basic first project. The other day, though, I got my copy of Getting Started with .NET Gadgeteer, and after reading through a couple of the projects in the book, I was intrigued to do one of my own. So I came up with the idea to build a camera connected to a web server that I could attach to my Roomba. The camera would take pictures periodically and make them available on a simple website so I could check on the Roomba working online.

The prototype

Below are the components from the started kit I used to build the prototype. Of course there is the camera that is triggered by a timer. The current picture is kept in memory, so when a page request comes in through the web server on the Ethernet module, it can be included in the page served. Each picture is also saved under the current timestamp to an SD card, so eventually I could have a page showing a history of everything my Roomba has seen. For testing purposes, I have also included a button that, just like the timer, triggers the picture taking code. For good measure, I also threw in the multicolor LED that displays a simple status (starting, ready, error) even though it won’t be needed in the final version as that is supposed to run unattended.

I build and wrote the code (see below) for all of this within a few hours lat one night last week. Knowing C# this was super easy. The components are really simple to use, and even though some .NET classes are not as full featured in the Micro Framework as they are in the full framework (e.g. the TimeSpan class is missing a couple of convenient factory methods), these limitations are very easy to work around.

RoombaCam Prototype Components

Next step: going mobile

There is one obvious problem with the prototype above: it uses Ethernet and relies on my development PC for power. A post on the .NET Gadgeteer on MSDN helped me solve the latter problem: a battery pack with a Mini-USB connector that plugs into the USB Client module. I couldn’t find any of the battery packs mentioned in that article here in Germany, but I did find a different one on Amazon that seems to work just as well. I don’t know yet what kind of battery life I will get with it, though. I hope it will be a couple of hours, because its probably 3 hours between the time a leave for work (turning the device on) and the Roomba finishing its sweep of my apartment.

For wireless network connectivity, I think I will get the WiFi RS21 Module. It shares the NetworkModule base class with the Ethernet module, so code-changes will be minimal when I swap the two. Unfortunately, the module is currently out-of-stock at GHI Electronics’ German distributor, so I’ll have to wait a while. I guess I could order it directly from GHI in the U.S., but last time I did this, it took the post office forever to the ship the things and factoring in shipping&handling, taxes and import duties it was kind of pricey as well.

Another module I want to get is the compass module, because I think it would be really cool to know the Roomba’s orientation as well. I think I could also use this to detect when Roomba starts and stops moving, so I can start and stop the picture taking accordingly. I don’t know, maybe a gyroscope would better for this, but the gyro module is kind of expensive as well.

I also need a board or something to mount all theses modules to. Right now, everything is spread out on my desk, but for the final version I will need something I can attach safely to the Roomba so things stay in place as it moves around and bumps into things (which it seems to do a lot recently, I don’t know why the obstacle detection seems to have deteriorated).

Where the magic happens

Below is the main program code (Program.cs) to power my RoombaCam. I know still need to refine and refactor some things, which I will do when I get the missing modules. Once that is done, I think I will post the full project source to GitHub or something. After reading Joel Spolsky’s Mercurial tutorial HgInit a wile ago, I really wanted to check out distributed version control, so maybe this is a good opportunity to do so. So stay tuned…

[03-July-2016 Update] Here is the source code.

using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Controls;
using Microsoft.SPOT.Presentation.Media;

using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;
using Gadgeteer.Modules.GHIElectronics;

namespace RoombaCam
{
    public partial class Program
    {
        void ProgramStarted()
        {
            _sdCard.SDCardMounted += (sender, e) => CheckStatus();
            _sdCard.SDCardUnmounted += (sender) => CheckStatus();
            if ((_sdCard.IsCardInserted) && (!_sdCard.IsCardMounted))
            {
                _sdCard.MountSDCard();
            }

            var pictureTakingInterval = new TimeSpan(0, 0, 30);
            _pictureTakingIntervalInSeconds = pictureTakingInterval.Seconds.ToString();
            new System.Threading.Timer(TakePicture, null, new TimeSpan(0, 0, 1), pictureTakingInterval);
            _camera.PictureCaptured += PictureCaptured;

            _ethernet.NetworkUp += StartWebServer;
            _ethernet.NetworkDown += StopWebServer;
            _ethernet.UseDHCP();

            _button.ButtonPressed += (sender, e) => TakePicture(null);

            CheckStatus();
        }

        //
        // General
        //

        private void Log(string message)
        {
            Debug.Print(DateTime.Now.ToString("hh:MM:ss: ") + message);
        }

        private void CheckStatus()
        {
            if ((_sdCard.IsCardMounted) && (_ethernet.IsNetworkUp))
            {
                _led.TurnGreen();
            }
            else
            {
                _led.TurnRed();
            }
        }

        //
        // Camera
        //

        private string _pictureTakingIntervalInSeconds;
        private GT.Picture _picture;
        private bool _captureInProgress;
        private DateTime _pictureTaken;

        private void TakePicture(object state)
        {
            if (_captureInProgress)
            {
                Log("Picture is already being taken, not taking another one right now");
            }
            else
            {
                if (!_camera.CameraReady)
                {
                    Log("Camera not ready, no picture was taken");
                }
                else
                {
                    Log("Taking a picture...");
                    _pictureTaken = DateTime.Now;
                    _captureInProgress = true;
                    _camera.TakePicture();
                }
            }
        }

        void PictureCaptured(Camera sender, GT.Picture picture)
        {
            _picture = picture;
            _captureInProgress = false;
            if (_sdCard.IsCardMounted)
            {
                string fileName = _sdCard.GetStorageDevice().RootDirectory + "\\RoombaCam_" + _pictureTaken.ToString("yyyy-MM-dd_hh-mm-ss") + ".bmp";
                Log("Saving picture as " + fileName + "...");
                System.IO.File.WriteAllBytes(fileName, picture.PictureData);
                Log("Picture saved");
            }
        }

        //
        // Web server responses
        //

        private bool _isWebServerRunning;

        private void StartWebServer(object sender, GTM.Module.NetworkModule.NetworkState networkState)
        {
            Log("Network is up, starting web server...");
            _ethernet.StartLocalServer(80);
            _ethernet.SetupWebEvent("RoombaCam.htm").WebEventReceived += ShowPage;
            _ethernet.SetupWebEvent("CurrentPicture.bmp").WebEventReceived += ShowImage;
            CheckStatus();
            Log("Web server started");
            _isWebServerRunning = true;
        }

        private void StopWebServer(object sender, GTM.Module.NetworkModule.NetworkState networkState)
        {
            if (_isWebServerRunning)
            {
                Log("Network is down, stopping web server...");
                _ethernet.StopLocalServer();
                CheckStatus();
                Log("Web server stopped");
            }
        }

        private void ShowImage(string path, GTM.Module.NetworkModule.HttpMethod method, GTM.Module.NetworkModule.Responder responder)
        {
            Log("HTTP request for \"" + path + "\"");
            responder.Respond(_picture);
        }

        private void ShowPage(string path, GTM.Module.NetworkModule.HttpMethod method, GTM.Module.NetworkModule.Responder responder)
        {
            Log("HTTP request for \"" + path + "\"");
            string page =
@"
<!DOCTYPE html>
<html>
  <head>
    <title>RoombaCam</title>
    <meta http-equiv=""refresh"" content=""" + _pictureTakingIntervalInSeconds + @""">
  </head>
  <body>
    <h1>RoombaCam</h1>
    <p>This is what Roomba was looking at at " + _pictureTaken.ToString("dd MMM yyyy, hh:mm:ss") + @". Page refreshes every " + _pictureTakingIntervalInSeconds + @" seconds.</p>
    <p><img src=""CurrentPicture.bmp"" alt=""The world as Roomba sees it""/></p>
  </body>
</html>";

            responder.Respond(System.Text.UTF8Encoding.UTF8.GetBytes(page), "text/html");
        }
    }
}
Advertisements

Not a gamer, but…

I have been watching SSoHPKC play Halo: Combat Evolved, Halo 2, Halo 3 and Halo: Reach for a while now and started to really like the Halo series (note that Halo 3: ODST is missing from that list, because it is terrible). After limiting myself to watching others play, I figured it was time for me to see for myself. I have had an Xbox 360 for a while now, and while I would describe myself more as a casual gamer, I have also been playing shooters on it. So when I saw Halo: Combat Evolved Anniversary on sale on Amazon the other day, I went for it.

Books in Black and WhiteAnd after playing it for the better part of this weekend, I must say that was a good choice. For one, the Anniversary edition is really cool, because while in normal mode, the graphics are exactly what you would expect from a current game, you can also switch into Classic mode, so the maps will look just like they did when Halo: Combat Evolved was originally released in 2001. So it’s basically two games for the price of one. Sort of. They also added in a bunch of new features, like skulls hid throughout the campaign. I am very proud that I found the first one on my own while exploring the vast map of the Halo level. This exploring is really fun, actually. For instance, did you know, that on Silent Cartographer you can walk out into the ocean from where Echo 419 landed toward that other island. While water is initially shallow, at some point the Master Chief will be walking under water and even if you keep going for a while, you will not actually reach that other island. It seemed to me like one was instead walking under it. You can also fire your gun under water and it will give of a muffled sound and bubbles. While the exploring is fun, I have been using this handy collectible guide to make sure I don’t miss any skulls or terminals.

As I said before, I am more of a casual gamer, so I have to admit that I have been playing on Easy so far. I am usually terrible at doing several things at the same time or in rapid succession. So when engaging enemies, I often just stand there and when they move I might be so busy with trying to follow them that my shots miss them completely. My aim is also kind of poor in general, so I have to resort to  spray and pray tactics most of the time. This obviously wastes a lot of ammo, but thankfully ammo and health are plentiful on Easy. And best of all, you can get many of the achievements while playing on Easy. Others require playing on Heroic or Legendary, but I just know I will never get those. In fact, of the 17 Xbox LIVE titles I have played so far there was just one that I have gotten all the achievements for. That was Voice Studio which isn’t even a real game.

I am happy to report, though, that I have been getting better since I started playing on Friday. Several times now, I have been able to (1) run towards Elites that are themselves running so (2) I have to keep tracking them with the other stick while (3) firing and then (4) using melee to finish them off. Furthermore, I have kind of mastered driving the Warthog without running into obstacles, getting stuck between rocks or flipping over every two seconds and I no longer get lost as much as I used to in the beginning. For teaching me such valuable life skills, I like to think of Halo as an educational video game rather than a first-person shooter. I kind of wish, though, there was a map or something in the game for me to keep track of where I am. Or maybe get the AI to drive for once, that would make things a whole lot easier, too.

Anyway, I now have five out of ten levels finished, so maybe by the time Halo 4 is released this fall, I will be a Halo pro. Or maybe not. Actually, make that probably not. I think I will be watching that one, rather than playing it.

Fun fact: The original release of Halo almost coincided with my 18th birthday.

Reading up on file caching in Windows

C# (the programming language I use most in my day-to-day coding) and other high-level languages like it are great, because they abstract away a lot of the ugly details of programming. Every once in a while though, there is a problem that forces one to peel away theses layers of abstraction and understand the inner workings of what’s beneath to fully analyze the problem.

The most recent instance of this was a situation where a database server would randomly hang for a couple of seconds every now and then (see my Server Fault question about this). This article is a collection of the things I learned and helpful resources I came across while trying to solve this problem.

Analysis and reproduction

The first thing I did was fire up Process Monitor to record what the process in question was doing during the hang as well as the seconds leading up to it. Process Monitor is a great tool for situations like this, as it provides a very detailed picture what API calls a process makes, what flags it passes et cetera. The first step in understanding the problem was obviously to dig into the documentation of the APIs I saw the process was calling (most notably CreateFile, WriteFile and FlushFileBuffers, which confusingly enough is referred to as FlushBuffersFile in Process Monitor). By the way, Windows Sysinternals Administrator’s Reference is a great book to get the most out of Process Monitor and the various other Sysinternals tools.

I then wrote a tool that parsed the Process Monitor log file and replayed the same API calls, so I could reproduce the problem without having to fiddle with the production system. While I am obviously spoiled by the nice APIs in .NET, it was actually kind of fun to be coding at a lower level again, working with handles, pointers, byte arrays and the like.

What Process Monitor and Process Explorer did not tell me, however, was what mode was used to open the file (which had happened long before I had begun my analysis). Luckily, I found a blog post explaining how to determine the share mode of opened file handles. In involves using WinDbg to look at the kernel structures for the file object in question. Using WinDbg is always fun, since it has such an intuitive user interface (not). And as is usually the case, because I use WinDbg so rarely, I hadn’t set up symbols correctly on the machine I was using, so WinDbg didn’t want to do anything at first. I am not exactly sure, though, what it needed symbols for in this situation, but anyway.

Understanding the cache manager

Another great book I always rely on in situations like this is Windows Internals, which provides detailed insights into the inner workings of Windows, in this case, Windows’ cache manager. There is also this presentation on the cache manager providing similar detail. It’s quite interesting, actually, how the cache manager is implemented and how it relies on the memory manager to do the actual caching. Then there is a two part in-depth video ([1], [2]) on Channel 9 with Molly Brown who at the time was (and maybe still is, I don’t know) responsible for the cache manager. She mentions a few additional interesting points about the cache manager, for instance, how it is monitoring an application, trying to figure out the pattern in which it accesses a file so it can provide intelligent read-ahead to improve IO performance.

Finally, there is a bunch of documentation on MSDN, such as this intro to file caching, an article on how to evaluate memory and cache usage and a couple of support documents ([1], [2]) and a blog post, about what problems the file cache can cause under certain circumstances. While this article on file cache performance tuning was written for Windows 2000, a lot of its recommendations should still be applicable as according to Molly Brown in one of the Channel 9 videos mentioned above, the cache manager has changed little since it was first created during the NT days (side note: because of this, I have ordered Inside Windows NT, the first book in the Windows Internals series, to learn about the origins of NT and see how much of it is still there).

Conclusion

As my tests are still underway, I haven’t yet been able to determine the root cause of the performance issues or find a definitive solution for it. If you have any additional insights, tips or things I should look at, please leave a comment or post an answer to my Server Fault question.

I am also curious to see what changes have been made in Windows Server 2008 R2, but unfortunately, Windows Internals 6th Edition Part 2 covering the cache manager, hasn’t been released yet.