Posts
  • Eric Gunnerson's Compendium

    Being greener

    • 8 Comments

    For most of my life, I've been a "small e" environmentalist. I'm not outspoken on my views, but I really hate to see needless waste. For me, that's meant keeping cars for a long time rather than replacing them, spending a little more on stuff that will last so I don't need to throw as much stuff away, and getting books at the library. Stuff like that.

    I haven't, however, put as much effort into applying the same principles to my profession, and over the past few months, I've been working to be more reasonable in that area as well.

    I've come up with a few practices that I thought I'd pass along, as part of what I'm calling "Green Programming - Simple practices to make the world a better place..."

    Practice #1:

    Consider the following code:

        if (x > 5)
        {
            i++;
        }

    Now, if I need to change it so that 2 is added to the value of i rather than one, I would probably do the following:

    First, I'd select the line with the increment:

        if (x > 5)
        {
            i++;
        }

    hit "delete":

        if (x > 5)
        {
        }

    and then type in the new line:

        if (x > 5)
        {
            i = i + 2;
        }

    I've done that sort of thing millions of times in my career. But not any more. Here's what I do now:

    First, delete the characters that I no longer need. In this case, it's just the second "+" sign:

        if (x > 5)
        {
            i+;
        }

     then, insert " = i "

        if (x > 5)
        {
            i = i +;
        }

     and finally, " 2":

        if (x > 5)
        {
            i = i + 2;
        }

    In my first version, I deleted 12 characters (plus a carriage return and linefeed), and then added 20 new characters. With my new approach, I deleted 1 character and added 7 characters. That small change reduced my deletion percentage by 91.67%, and my new character percentage by a less impressive but still impactful 65.00%.

    Practice #2:

    The first practice made some great improvements, but I thought there was more to be had. I found that I could keep open a session of notepad, and rather than deleting the characters from my code, I would copy them to notepad, for possible reuse later. Unfortunately, since I write a lot of new code, I tended to exhaust this resource fairly quickly, and was forced to fall back on typing new characters.

    Practice #3:

    To give me a better source of recycled characters, I came up with a new approach. Not only do I copy characters to notepad before deleting them, when I need to delete a source file, I copy *all* of it's contents to my notepad buffer before deleting the file.

    I am proud to report that I now am justified in believing that I am "character neutral" in all of my coding. That made me quite happy, until I realized that I still had a problem.

    It was my blogging. I tend to write a fair bit on my blogs (3-5 depending on how you count them), but because of my natural eloquence and low quality standards, I rarely delete stuff, and therefore keeping a buffer for my blog writing was problematic. I tried using my programming one - it certainly is sufficiently full that it won't run out quickly - but I found that I tended to have some problems with expression ("I regret I have but one life to give foreach my country"), not to mention developing having to write lines like this:

    (){=();="");(<,>){=.;=..;=..;<>=<>();(());(..()){=;}}

    to use up some of the excess punctuation.

    So, that clearly wasn't workable, but just yesterday I came up with a great idea. I'm now recycling all the contact spam that is *already generated* by my blog into my blog buffer, and I'm considering re-using the splog links I keep getting, which would be especially convenient as they already have the words that I like to use.

    So, that's my little contribution, because as I like to say, if you're not part of the solution, you're part of the problem. Or the precipitate.

    Future Ideas:

    I haven't thought all of these through fully, but here's a list of other ways you might "recycle and reuse":

    • Don't throw away that command window you're using and create a new one. Reuse the existing one.
    • Keep old classes around to use as the starting point for new ones.
    • As some believe in "Peak Oil", I fear that we've already reached "Peak Guid", and that the days of $4 guids are in sight. I therefore recommend that you save all of the guids that you generate. Sure, it may be 15 years before the software that you used them in is gone and you're free to reuse them, but if you're the guy with 500 guids in your pocket during the Guid crisis of 2025, you're going to be sitting pretty.

    What practices can you add?

    (composed of 100% post-consumer characters)

  • Eric Gunnerson's Compendium

    Uncanny valley

    • 0 Comments

    Try this on your second monitor...

    (yes, I know it's not fully rendered, but "Ew")

  • Eric Gunnerson's Compendium

    International Dance Party

    • 1 Comments

    What a nice build. Make sure to watch the video:

    International Dance Party

     and you might want to take a look at the manual...

  • Eric Gunnerson's Compendium

    Project managers for agile teams...

    • 1 Comments

    A recent question about skill requirements for project managers of agile teams led me to write this:

    In traditional project management, “project manager” means “person who is in charge”. In other words, that person makes the decisions, with varying degrees of depth (sometimes it’s high-level, sometimes it’s micro-management).

     

    Agile doesn’t have somebody in change – inherent in the concept of agile is that the group is responsible and the group decides.

     

    There *is* somebody who facilates (scrummaster in scrum, coach in XP), but their job is very explicitly not to make decisions. That means that the person who takes that role needs to be mindful of that and willing to push any decisions that rise up back down to the time. If the person has previous experience (and a preference) for “being in charge”, they are unlikely to do a good job in that role. If they aren’t thoughful about group dynamics in general, they may not do a good job in that role.

     

    Double that comment if the group is not experienced with making decisions together. If they tend to defer upwards and the facilitator is used to making decisions, you probably won’t get a good result.

     

  • Eric Gunnerson's Compendium

    Job openings in the HealthVault team...

    • 1 Comments

    Long-time readers (who I like to refer to as "those three guys") know that I rarely blog about job openings, but I've decided to make an exception.

    The HealthVault team is building a platform to solve "real important" HealthCare problems that affect the lives of "real people". We're a small but growing group that still has that "new group" smell to it, and we're looking for people who would do well in that sort of environment.

    If you want to find out more information, you can find it at the Microsoft Career site (choose "Health Solutions Group" as the product). If you don't want to find out more information, you can find it at the bottom of a locked filing cabinet stuck in a discused lavatory with a sign on the door saying "Beware of the Leopard".

    Or so I'm told.

     

  • Eric Gunnerson's Compendium

    Scheming for Schema

    • 3 Comments

    As many of you know, we already provide access to the schema for the healthvault types through the Type Schema Browser and the Raw XML API reference page.

    If those pages don't do exactly what you want - say, you want to look at all the schemas together in a tool - you can access the schemas programmatically. The schema for a data type is built on some lower-level schemas (or types, if you wish):

    The first one can be fetched programmatically, and the second and third you will need to download directly from the links I provided.

    Access to the schema is provided through the ItemTypeManager class. You can call GetBaseHealthRecordItemTypeDefinition() to get the base schema (base.xsd), and then use GetHealthRecordItemTypeDefinition() to get the xsd for a specific type.

    And, you can get a list of all the types from the "thing-types" vocabulary, thusly:

    Vocabulary thingTypes = ApplicationConnection.GetVocabulary("thing-types");

    Here's a bit of code that I wrote to fetch all of the schemas and store them in separate files:

        protected void Page_Load(object sender, EventArgs e)
        {
            HealthRecordItemTypeDefinition baseTypes = ItemTypeManager.GetBaseHealthRecordItemTypeDefinition(ApplicationConnection);
            SaveTypeXSD(baseTypes, "base");
            Vocabulary thingTypes = ApplicationConnection.GetVocabulary("thing-types");
            foreach (KeyValuePair<string, VocabularyItem> item in thingTypes)
            {
                Guid thingType = new Guid(item.Key);
                HealthRecordItemTypeDefinition definition = ItemTypeManager.GetHealthRecordItemTypeDefinition(thingType, ApplicationConnection);
                SaveTypeXSD(definition, definition.Name);
            }
            int k = 12;
        }
        void SaveTypeXSD(HealthRecordItemTypeDefinition definition, string name)
        {
            string directoryName = MapPath("platform");
            Directory.CreateDirectory(directoryName).CreateSubdirectory("web").CreateSubdirectory("xsd");
            string filename = MapPath(@"platform\web\xsd\" + name + ".xsd");
            using (StreamWriter writer = System.IO.File.CreateText(filename))
            {
                string schema = definition.XmlSchemaDefinition;
                writer.Write(schema);
            }
        }
    Note that it puts the schema definitions in a directory named platform\web\xsd. This is the same structure that the schemas use in our source tree, and this makes sure things work correctly
  • Eric Gunnerson's Compendium

    Work on your Vocabulary. The word for today is "CodableValue"

    • 1 Comments

    Vocabulary and CodableValue are two of the somewhat interesting types in the HealthVault platform, and I thought I'd spend a little time discussing them.

    So, here's the problem they're designed to solve.

    In medicine, there are sometimes long lists. The kinds of lab tests you can order, the kinds of medication that can be prescribed, or the specialties of physicians. Unfortunately, the actual names can be a bit unwieldy. For the last example, you see things like:

    • Medical Toxicology - Emergency Medicine
    • Neurodevelopmental Disabilities (Pediatrics)
    • Adult Reconstructive Orthopedics

    So, to make things simpler, there are a set of codes - using one to three letters in this case - that are the "standard" ways of encoding the longer string (what I'll call the "Display Text"). Take that code, put it together with the display text (and sometimes an abbreviation), and you get a Vocabulary item. Create a list of a bunch of VocabularyItems (along with some other information I'll talk about in a minute), and you get a Vocabulary, which defines both a list of possible options and the accepted way of encoding them (the key).

    To be a bit more concrete, here's how we can fetch a vocabulary describing the different kinds of aerobic activities:

    Vocabulary vocabulary = ApplicationConnection.GetVocabulary("aerobic-activites");

    foreach (KeyValuePair<string, VocabularyItem> item in vocabulary)
    {
        string key = item.Key;
        string displayText = item.Value.DisplayText;
        string abbreviation = item.Value.AbbreviationText;
    }

    If you looked closely at the foreach part, you noticed that item.Value is really a VocabularyItem, and that's where DisplayText and AbbreviationText came from.

    Here's what I got back from executing that code:

     

    Key DisplayText AbbreviationText
    run running run
    jog jogging jog
    hike hiking hike
    bike bicycling bike
    spin spinning spin
    swim swimming swim
    walk walking walk
    row rowing row

     

    In addition to the information on a specific vocabulary item, there is some general information on the Vocabulary type.

    Culture

    The culture tells you the culture of the vocabulary. When you fetch a vocabulary, it comes back for the current Culture, and you can control whether there is a fallback to other culture's if there's not one specific to the current culture. Note that the key is culture-invariant, and therefore may not make sense to users in your current culture.

    Family

    Family defines the overall source for the vocabulary. If it's from HL7, this would presumably be for that. Or perhaps it might say "AMA", or something else. The family lets you understand where the vocabulary came from.  If it's something the HealthVault team defined, the family is "wc".

    Name

    The name of the vocabulary. Together with the family, that should fully identify the source

    Version

    A string defining a version of the vocabulary.  

     

    Coded and Codable Values

    How is a reference to a specific vocabulary item stored in HealthVault?

    It is done through the CodedValue type. A quick look at the type shows the following properties:

    • Family
    • Value
    • Version
    • VocabularyName

    As you probably figured out, Family, Version, and VocabularyName are used to indicate what vocabulary we are using, and Value is used to store the key for the specific vocabulary item. An application can figure out exactly you mean by looking at the CodedValue.

    That works well when we have a vocabulary and we know exactly what code to use. But there are two more scenarios we need to support.

    First, we might have an item to store, and it's either not in the vocabulary or we don't know what the appropriate vocabulary is. I might want to store my juggling exercise data, but that's not part of aerobic-activities vocabulary. Or, I'm adding the over-the-counter allergy medicine I take, and all I know is the name on the box.

    In other words, sometimes we have very precise information, and sometimes it's less precise, but in both cases the platform and the applications that use the platform need to handle it well.

    The second scenario - and this one's rarer - is that there are times where a single entry might need to store multiple values.

    Both of these scenarios are supported through the CodableValue type, and that's the type that you'll see used in the data types.  It provides a Text property and a way of having multiple coded values.

    CodableValue.Text is always set for all codable values, and that's the one that you display when you're showing that value to a user. Any underlying CodedValue instances are of a "more information" category - the application can use them to find out more information about that Text.

    All Vocabularies

    You can fetch the list of all vocabularies with the following:

    ReadOnlyCollection<VocabularyKey> keys = ApplicationConnection.GetVocabularyKeys();

    Why would you want to do this? Well, it's useful for browsing to see if the vocabularly that you want is currently defined in the system. If there's one that's not in the system, you can request a new vocabulary using the same process you'd use to request a new data type.

    Best Practices

    1. Always set the Text property on the Codable value, even if there are CodedValues underneath.
    2. Always display the Text property as the high-level summary of the CodableValue.
    3. Be thoughtful about displaying the key to a vocabulary item to the user. It might be a number - in which case it is probably okay - but it also might be a string that only works well in one locale.  

    Translation

    One of the nice things about vocabularies is that there is are so many to choose from. There are several different coding systems - and therefore possible vocabularies - for medication. We've talked about providing translation services between different vocabularies - so that an application could easily get hte information in the vocabulary it understands best.

    Would you find that to be a useful feature?

  • Eric Gunnerson's Compendium

    Dealing with PersonalImage

    • 2 Comments

    PersonalImage is a type that encapsulates the picture that shows up for a person in the health record.

    My current image is this:

    With the power to... Melt!

    (those interested into why that is my current image should post the results of their research in the comments)

    PersonalImage is what is called a Singleton type, which - as you've probably already figured out - means that there can only be one of them. But, there might be none of them, so code needs to handle this case as well.

    Here's some code you might find useful.

            using (Stream imageStream = System.IO.File.OpenRead(@"C:\Documents and Settings\ericgu\My Documents\My Pictures\213.jpg"))
            {
                HealthRecordItemCollection collection = PersonInfo.SelectedRecord.GetItemsByType(PersonalImage.TypeId, HealthRecordItemSections.All);
    
                PersonalImage image = null;
    
                if (collection.Count != 0)
                {
                    image = collection[0] as PersonalImage;
    
                    using (Stream currentImageStream = image.ReadImage())
                    {
                        byte[] imageBytes = new byte[currentImageStream.Length];
                        currentImageStream.Read(imageBytes, 0, (int)currentImageStream.Length);
    
                        using (FileStream outputImage = System.IO.File.OpenWrite(@"g:\213.jpg"))
                        {
                            outputImage.Write(imageBytes, 0, imageBytes.Length);
                        }
                    }
                }
    
    
                if (image == null)
                {
                    image = new PersonalImage();
                    image.WriteImage(imageStream, "image/jpg");
                    PersonInfo.SelectedRecord.NewItem(image);
                }
                else
                {
                    image.WriteImage(imageStream, "image/jpg");
                    PersonInfo.SelectedRecord.UpdateItem(image);
                }
            }
    
    
  • Eric Gunnerson's Compendium

    HealthVault and openness

    • 1 Comments

    Yesterday - while I was taking a day off skiing with my wife and daughter - we announced our plans around openness and interoperabiity for HealthVault.

    I'm pretty excited about this. I'm especially excited about making the current .NET client library available under the Microsoft reference license, as I remember how useful it was to step through the MFC or ATL source when I was trying to figure out how things worked (or, more usually, what I had done wrong).

  • Eric Gunnerson's Compendium

    Uploading a file to HealthVault

    • 1 Comments

    I came across a question asking how to upload a file to HealthVault, and decided to write a quick example.

    Files, like everything else in HealthVault, are stored in XML, and in fact, the File class is really a pretty thin wrapper over the underlying type. The File type stores the name of the file, the size, and the type of the content in the file.  The contents of the file is then stored in the "OtherData" member as a base-64 encoded string.

    Here's some code:

    FileInfo fileInfo = new FileInfo(@"C:\Documents and Settings\ericgu\My Documents\My Pictures\lame.jpg");
    using (FileStream stream = System.IO.File.OpenRead(fileInfo.FullName))
    {
       
    BinaryReader reader = new BinaryReader(stream);
        byte[] fileContents = reader.ReadBytes((int) fileInfo.Length);
        string encodedString = Convert.ToBase64String(fileContents);

        Microsoft.Health.ItemTypes.File file = new Microsoft.Health.ItemTypes.File();
        file.Name = fileInfo.Name;
        file.Size = fileInfo.Length;
        file.ContentType =
    new CodableValue("image/jpg");
        file.OtherData =
    new OtherItemData();
        file.OtherData.ContentEncoding =
    "base-64";
       
    file.OtherData.ContentType = @"image/jpg";

        file.OtherData.Data = encodedString;

        PersonInfo.SelectedRecord.NewItem(file);
    }

    That will get the data up into HealthVault. Remember that the OtherData section doesn't come down by default, so if you want to get the contents of the file back, you'll need to specify:

    filter.View.Sections = HealthRecordItemSections.All;

    (or another version of the Sections mask that includes HealthRecordItems.OtherData).

  • Eric Gunnerson's Compendium

    Build a 3D XNA game from scratch - webcast series

    • 0 Comments

    This came highly recommended...

    Build a 3D XNA game from scratch - webcast series

  • Eric Gunnerson's Compendium

    The correct answer is "snowblower"

    • 2 Comments

    As some guessed, a snowblower.

    More specifically, an Ariens 824E snowblower, from SnowblowersDirect.com.

    Some of you know that I ski in the winter, and keep a ski cabin to make ski mornings much nicer. The ski cabin is at about 1000', and traditionally, there really isn't that much snow at that elevation around here - it tends to snow a bit, and then warm up.

    But - as we've found over the last few years - if you have a house that sits on the north side of a hill, you don't get any solar radiation at all, and any snow that you do get is amazingly resilient - it will stick around for months. Though I can usually push the outback into our driveway with the new snow, it doesn't really work on deep slop, unless you're fond of severe drivetrain abuse and the smell of burning clutch(es).

    So, we park at a small spot on the road to our lot. If it's snowed, I have to either move the plowed snow to the side before we park, or just muscle over it. And I need to cut a path through the snow to the cabin so we don't slip and fall over.

    We've had this tentative plan to get an ATV and put a small plow on it, but I pretty much just lazed on that one. And then it started snowing, and snowing, and snowing around here, and I started thinking in terms of snowpack. We've probably had a good 4-5 feet of snow fall, and the snowpack is around 24-30" right now (it was a bit higher a week or so ago). I'm tired of digging.

    Hence the snowblower. I couldn't find any stock here or close on the east side of cascades, so I ended up buying online, which was surprisingly easy. The blower showed up on Friday, we took it up Saturday, and found that it's a nice machine. And, amazingly, it started on the first pull in 20 degree weather, which shows that small engines have improved a bit since the lawn-mowing days of my youth.

    It made quick work of the top 8" of snow, which was still sort of powderly. I had little luck at all with what was underneath, but given the fact that the blower doesn't sink into the snowpack, I'm not surprised. It would have been fine if I could have attacked it incrementally, and I'm hopeful that when it warms up a bit, the pack will soften and I'll be able to get back into the driveway.

    Or, I could try to borrow one of these...

  • Eric Gunnerson's Compendium

    Ninja Warrior

    • 8 Comments

    My daughter found some broadcasts of a show named "Ninja Warrior" (originally named Sasuke in its Japanese broadcast), which is currently running on G4TV. It's an elaborate obstacle course show, with an English voiceover on top of the Japanese commentary.

    Not only do you get to see some really talented athletes, you also get to see ordinary people crash and burn on the first stage, falling into the muddy water.

    Recommended.

  • Eric Gunnerson's Compendium

    What did Eric buy?

    • 5 Comments
    • 228 pound
    • 6 speeds
    • 318cc
  • Eric Gunnerson's Compendium

    Users, records, and multi-record applications

    • 2 Comments

    A topic came up yesterday in the forums, and I thought it was of general interest.

    HealthVault is different from a lot of information systems - at least the ones I've worked with - in that there is a break between the concept of "user" - the person who authenticates into the application - and "record" - the thing that holds information about specific individual.

    This is to support two scenarios.

    The first one is for parents, who want to track the health information of their children. They can create a separate record (that they have access to) for the child.

    The second one is when an adult is managing the health of another adult - that adult can be granted access to another adult's record.

    If you are writing an application, this is something that you need to deal with. If I'm using an application and then I come back to it after I've gotten access to another record, I need to provide the following:

    1. A way to authorize the application to access the new record. This is done through the shell and a redirect, similar to how normal authorization is done.
    2. Some UI to allow the user to switch between records.

    For an example of how to do this, take a look at the HealthAndFitness application.

    The existence of multiple records explains why you need to use PersonInfo.SelectedRecord to access the data - it belongs to the current record, not to the current user.

    That's the overview. The specific point that came up yesterday was around the ids for users and records. An application can access PersonInfo.PersonId and SelectedRecord.Id, and use those to uniquely identify that user and record.

    But, to make it harder to correlate information between applications, the same person using two separate applications is going to have two different values of PersonInfo.PersonId and SelectedRecord.Id, and, in fact, the record ID will change if the application is de-authorized and then re-authorized.

    So, you may need to keep this in mind if you are doing anything with those IDs...

  • Eric Gunnerson's Compendium

    Family Health Guy

    • 1 Comments
    Sean Nolan, my second-level (aka my manager's manager) and chief HealthVault architect, has started a blog named FamilyHealthGuy (no relation, AFAIK, to Family Guy), where his initial post, after dealing with matters mursine, talks a bit about why he's excited about HealthVault.
  • Eric Gunnerson's Compendium

    HealthVault: Batching up queries

    • 1 Comments

    When I first started using the HealthVault SDK, I wrote some code like this, based on what I had seen before:

    HealthRecordSearcher searcher = PersonInfo.SelectedRecord.CreateSearcher();
    HealthRecordFilter filter = new HealthRecordFilter(Height.TypeID);
    searcher.Filters.Add(filter);

    HealthRecordItemCollection items = searcher.GetMatchingItems()[0];

    So, what's up with indexing into the result from GetMatchingItems()? Why isn't it simpler?

    The answer is that queries can be batched up into a single filter, so that you can execute them all at once. So, if we want to, we can write the following:

    HealthRecordSearcher searcher = PersonInfo.SelectedRecord.CreateSearcher();

    HealthRecordFilter
    filterHeight = new HealthRecordFilter(Height.TypeId);
    searcher.Filters.Add(filterHeight);

    HealthRecordFilter filterWeight = new HealthRecordFilter(Weight.TypeId);
    searcher.Filters.Add(filterWeight);
     

    ReadOnlyCollection<HealthRecordItemCollection> results = searcher.GetMatchingItems();

    HealthRecordItemCollection heightItems = results[0];
    HealthRecordItemCollection weightItems = results[1];

    Based on a partner question today, I got a bit interested in what the performance advantages were of batching queries up. So, I wrote a short test application that compared fetching 32 single Height values either serially or batched together.

    Here's what I saw:

    Batch Size Time in seconds
    1 0.98
    2 0.51
    4 0.28
    8 0.16
    16 0.10
    32 0.08

    This is a pretty impressive result - if you need to fetch 4 different items, it's nearly 4 times faster to batch up the fetch compared to doing them independently. Why is this so big?

    Well, to do a fetch, the following thing has to happen:

    1. The request is created on the web server
    2. It is transmitted across the net to HealthVault servers
    3. The request is decoded, executed, and a response is created
    4. It is transmitted back to the web server
    5. The web server unpackages it

    When a filter returns small amounts of data, steps 1, 3, and 5 are pretty fast, but steps 2 and 4 involve network latency, which dominates the elapsed time. So, the batching eliminates those chunks of time, and we get a nice speedup.

    We would therefore expect that as we fetch more data in each request, batching would be less useful. Here is some data for fetching 16 items:

    Batch Size Time in seconds
    1 1.40
    2 0.91
    4 0.66
    8 0.49
    16 0.42
    32 0.39

    Which is pretty much what you would expect.

  • Eric Gunnerson's Compendium

    Miscellanea, and how to get a Wii

    • 2 Comments

    I'm back at work today, after spending a record (for me) 23 days away from the office. The last time I spent that much time off was one of those involuntary vacations that happens sometimes ("the investors decided that they aren't going to fund us any more").

    Of that time, I spent a few days shopping, the week of Christmas in San Diego (beach/zoo/wild animal park/sea world/lego land/aquarium/balboa park), and then a few days skiing. Going from 65 and sunny to 20 and snowing was a bit hard, but we all have our crosses to bear.

    We also bought a wii, which has been a fair bit of fun. I got mine through the services of wiialerts.com, which will send you both an email and a text message on your phone when stock shows up at one of the online retailers. You may be saying, "great, why didn't he tell me that before the holidays?", but I couldn't because a) my massive number of readers might have overloaded the site and b) my wife reads this blog, and she didn't know about the Wii (a risky move, but I got away with it). Probably more of "b" than "a". Anyway, a great site, and I'm sure I would not have gotten my Wii without it.

    Back at work, my group has moved to building 113, which is one of the old MS research buildings. It's pretty nice, though it isn't as nice as the brand new research building, which is both very pretty and has lots of innovative spaces (team rooms, working lounges, etc.) Plus, instead of glass doors between the lobby and the 4-story atrium in the middle of the building (I said it was pretty, right), it has some high-tech turnstiles that are operated with your badge.

  • Eric Gunnerson's Compendium

    Ring of Fire - Summary

    • 1 Comments

    Ring of Fire is a tree ornament I built this year. A big, high-powered, custom-animated tree ornament.

    First, there was the idea and a parts list.

    Then I wrote a simulator to create animations against.

    When that was done, I built the ring and started on the firmware

    And then I did a whole bunch of wiring to hook up the microcontroller to the LEDs on the ring, finished the code, and put it up in the tree in my front yard.

    Finally, I took a low-quality video of it.

     

  • Eric Gunnerson's Compendium

    Santa Animation

    • 1 Comments

    The oldest of my displays. The "landing lights" are blue lights in canning jars dyed blue. All the santas and the reindeer are made of plywood. Some of the lights on the mid and upper santa are burnt out, but I'm not sure I'm going to get around to fixing it before next year.

     Animated with an 8-channel custom Motorola HC11-based microcontroller system driving solid-state relays, all coded in assembler

    Santa Animation
    Santa Animation
  • Eric Gunnerson's Compendium

    Ring of Fire - Video

    • 1 Comments

    Here's a video of the completed project. The video is from my Canon G3, so the quality isn't very good. For sake of comparison, the blue and red globes in the video are 50 watts each, while each LED is only 350mW. The LEDs a bit brighter than they appear because the video is about 20 degrees off-axis.

    Ring of Fire ornament
    Ring of Fire ornament
  • Eric Gunnerson's Compendium

    Tree of Lights Video

    • 1 Comments

    My second animated display. This one has 16 channels and runs using a Motorola 68HC11 microcontroller. In the background you can see the house outline lights, which are a four-color dimming system also based on a 68HC11

     

    Tree of Lights
    Tree of Lights
  • Eric Gunnerson's Compendium

    Ring of Fire - Up in a tree

    • 2 Comments

    The last few days have been fairly hectic. I wanted to get "Ring of Fire" into a demo-able state (to take into work on Friday), so that it could go up today.

    First up was building the project board. As I mentioned before, the LEDs pull 70mA, so I need a separate transistor to drive them, as the AVR can't handle the current requirements. The switch uses a 2n3904 NPN transistor, a 10K base resistor, and a 100K resistor between the emitter and base. And finally, an 42 ohm current-limiting resistor in line with the LED, in between the collector and 5V. That's 16 transitors and 48 resistors, and after about 5 minutes, it was clear that that wasn't going to fit on the board. I decided to put the current-limiting resistor next to the LEDs, and could barely fit the other components on the board. Here's a picture of the board:

     

    Yep, that's a whole bunch of wires.

    Next up was finishing the LED wiring. One of my bike wheels is built with 16 spokes, so I used it to mark the location the LEDs. Then I spent about 4 hours mounting the LEDs onto the ring. Here's a picture of a mounted LED:

    The black wire is the 5V bus, and continues all the way around the ring. The yellow part is a 42 ohm resistor attached to the ground side of the LED, which is then attached to a red wire that runs all the way back to the controller. The LED is then attached to the ring with hot glue. Here's what the completed ring looks like.

    That completed, I turned back to the software.

    Problems that I hit along the way:

    1. I hadn't set the avr to use the internal clock correctly. This meant that it used the clock on the STK500 board, which I changed now and then, so the animation timing made little sense. This also mean that the chip didn't run when plugged into the board.
    2. Reset needs to be pulled low for the avr to run until reset is disabled. This means that until you are ready to burn the final version, you need to pull it low in the final circuit or you can't test it. This also means that that LED is always on. When you are finally ready, you set a fuse, and reset is disabled, so you can't program it using the in-circuit programming approach, so it's a lot harder to do.

    Originally, I was going to put ping-pong balls on the LEDs to diffuse the light, but it turned out that the LEDs have a fairly wide viewing angle, so the diffuser wasn't needed.

    So, the project is done, up and the tree looks pretty nice.

     

  • Eric Gunnerson's Compendium

    Stay at Home Server

    • 1 Comments

    The Windows team has launched Stay at Home Server, and they've produced a short book that introduces it.

    You should read it.

  • Eric Gunnerson's Compendium

    Ring of Fire - Ring and Firmware

    • 4 Comments

    After firming up the animations that I wanted to do, I got two things accomplished in the last few days.

    First, I build the "ring" part. I had bought a 5' piece of pvc electrical conduit to use as a form, and my plan was to bend it into a circle by heating it with a heat gun.

    Initial results were disappointing. The circle that I got was about as circular as the ones I drew when I was in kindergarten, so I decided I needed a form.

    And there, sitting in a box because of my laziness at not putting them away, was a set of spare wheels for my bicycle. A little experimentation showed me three things:

    1. Using the wheel as a form was workable
    2. The piece of conduit I had wasn't long enough
    3. It was sort of bunged up for my first attempt

    A quick trip to Lowes and another 30 minutes with the heat gun, and I had a nice hoop (size 700c) completed.

    Firmware

    After digging out my development board (I use the Atmel STK500) and buying a copy of the CodeWarrior AVR C compiler, I was ready to get started. Well, not quite - I had to set up my work area since we're getting ready to gut and redo our office. My main tools are:

    • A nice 20MHz dual-trace Tektronix oscilloscope that I bought on the internets a few years ago.
    • A benchtop power supply (+5V, +12V, -12V, and 0-20V)
    • A Fluke multimeter
    • A Weller temperature-controlled soldering station
    • My 8 fingers and 2 thumbs

    I don't really need the power supply as I'm using a wall wart to power the development board, but it looks nice under the scope. The multimeter is useful to look at LED currents, and because I'm using PWM to control the brightness of the LEDs, the scope is invaluable for debugging. If you don't want a full-sized scope, there are some decent PC-based solutions, but keep in mind that the precision electronics required for 'scopes are never cheap. I like having the real buttons and knobs rather than having to use a UI on the laptop to do things, though I would really like a TDS1000B.

    <aside>

    I've often advocated that developers should learn multiple languages because knowing languages effects how you approach problems. If you are fluent in Perl, you're much more likely to know when to use a nice regex in C#.

    Similarly, it's good for your skills to spend some time working on a seriously constrained machine like a microcontroller. The 861 I'm using has a whopping 512 *bytes* of memory, which sounds pretty tiny until you note that the 261 only has 128, and some of the 8-pin versions only have 64 bytes.  Trying to structure your algorithms and code to use that amount of memory and the 8K of flash for the program memory will stretch your mind in ways that writing C# code won't.

    I'm undecided on the benefits of writing in assembler. I can be a learning experience, but I'm not sure if the pain of remembering how indexed address mode works transfers over to more rational languages.

    </aside>

    My first order of business was to get the chip in the board, make sure I could burn it, and get a skeleton program running. Getting the chip on the board was trivial, but I had real problems getting it to program (which is done with a PC program that sends the code over a serial cable to the board). After a lot of trial and error - mostly error - a kind soul on avrfreaks.net pointed out that the connections on the STK500 were wrong for my chip, and things started working correctly then.

    Codewarrior is a nice product. Microcontrollers require a lot of configuration up front - you need to set how you are using each of the pins, what interrupts you want, and 35 other things. You do this by writing code that stuffs particular values in specific registers on the chips. Something like:

    TCCRO0A=0x09

    Where you found that by careful reading of the 227 page user's guide for the chip. It's pretty tedious, but CodeWarrior provides a "get started wizard" where you specify what chip you're using and then go through a dialog and choose what options you want and then it generates all the initialization code that is needed for your chip. It's a huge timesaver.

    After tracking down one problem that was keeping my interrupts from firing (traced to missing a bit in one of the interrupt configuration registers), my main loop woke up and started operating. I did a bit more debugging and then ported one of my animations over from C# to C.

    It looked okay on the scope (minus a few glitches), but it seemed slow. Plugging in the leds on the STK500 to 8 of the output bits I'm using showed that a) the animation was working, but very slowly and ii) the LEDs were flickering like nobody's business.

    As I suspectected, the 861 running at 8 MHz doesn't have enough horsepower to handle 256 levels of dimming. I'm not getting the 100 Hz that I need to keep the leds from flickering, and there's not enough free time after the interrupts to do the animation at any reasonable speed.

    I'll spend a little time seeing if I can't speed up the interrupt routine, but the real fix is going to be to reduce the number of dimming levels down to 16, which should give me enough time for everything to run smoothly.

    Then, it will be time to start attaching components to the ring 

Page 5 of 46 (1,128 items) «34567»