A simple yet versatile GDI library for Netduino.

Posted by Mario Vernari Sunday, February 10, 2013 7:49:27 AM
Rate this Content 0 Votes

This is my second attempt of making an hardware abstraction against a limited UI which can be connected to a Netduino. Most of the times, you can drive a character LCD module or something like that, but no complex displays unless some native library or hardware helper will come in rescue. That's because they require a lot of resources, such as RAM as well as CPU speed. However, until you write apps in managed C# on a Netduino, both of them are very limited.

That's the way I considered only very simple displays, and -why not?- the nice led matrix presented in my previous article.

I don't think to have discovered anything new: I took "inspiration" by the old Windows GDI (Win 98 and earlier), mixed with some elegancy of the newer GDI+ (XP). By the way, GDI+ is the one exposed by the .Net Winforms APIs. Instead, the challenge was to create the very minimum to be able to (literally) play with, without forgetting a decent performance.

 

Step-by-step usage of library with the Sure Electronics led-matrix.

The library usage should be pretty straightforward. However, a basic knowledge of the Windows GDI/GDI+ and/or Winforms Graphics will help a lot. I don’t teach here anything about it, because it is very well explained in many places, and there are thousands of good examples.

Instead, here I just want to show you how to proceed, step-by-step, for a very basic app. As a minimal example, this app won’t have any animation, but creates only static images on the display. Animations aren’t particularly different, but it require a bit of ability on manage the graphics at best, due the limited performance.

Here we go!

 

Setting up the project.

NOTE: I will assume that the hardware is already wired and working fine, as explained in my previous article.

The very first thing to do is to start your favorite Visual Studio IDE for creating a Netduino (Plus) 2 project. In my case, I started VS Express 2010 for my Netduino Plus 2.

Just type “NetduinoSureGDI1” as project name (or whatever you want).

tutorial1

 

Once the project has been defined, right going to add a reference for an “Existing Project” to the solution. To do that, simply right-click the root “Solution” node in the “Solution Explorer” pane, then choose “Add” and “Existing Project…”.

tutorial2_cr

 

On doing, you’ll be asked to actually save the solution somewhere on the disk. Choose your own location, or simply confirm what the dialog suggests.

tutorial3

 

At this point, you should browse for the “GDI” library for Netduino (which we’re talink about): specifically you should point to the project descriptor file (.csproj).

tutorial4

 

Once the project has been selected, it will join our experimental solution, together as the contained files.

However, you have to add the GDI project as “Reference” for the target application. To do that, right-click the NetduinoSureGDI1 “Reference” node in the “Solution Explorer”.

tutorial5_cr

 

In the “Projects” tab just select the GDI project, then press “OK”.

tutorial6

 

Now repeat the steps for including another project to the solution, which is the “real” driver for the led-matrix module.

When everything has been done, your experimental solution should look like the below picture.

tutorial7_cr

The above steps are a common task for any project using the led-matrix and leveraging the GDI library. Now will follow some specific code for getting practice with the libraries.

 

Taking practice with the graphics primitives.

It’s time to put our hands on the real code, and having fun with the colored dots of the matrix.

Your “Program” module should look as almost empty. In order to resolve the types being used in this app, the very first thing to do is adding a couple of “using” clauses, which refer to the linked libraries.

 

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;
 
using Cet.HW.GDI;
using Cet.HW.Drivers;
 
namespace NetduinoSureGDI1
{
    public class Program
    {
 
        public static void Main()
        {
 
            //type your code here ...
            
        }
 
    }
}

Afterward, you should add the minimal “infrastructure” needed for the rendering.

 

    public class Program
    {
 
        private static ICompositionRenderer _renderer;
 
 
        public static void Main()
        {
            //create a led-matrix driver instance
            _renderer = new SureHT1632(
                cspin: Pins.GPIO_PIN_D10,
                clkpin: Pins.GPIO_PIN_D9);
 
            //creates the target composition instance
            var composition = _renderer.CreateTarget();
 
 
 
            //dumps the composition to the physical device
            _renderer.Dump(composition);
 
            //keep alive
            Thread.Sleep(Timeout.Infinite);
        }
 
    }

You will notice there are two components involved in this “drawing machine”. One is the real driver (SureHT1632), but there’s a strange “Composition” component as well. As said, one of the goals is creating some “abstraction” wall against the actual hardware used. Our main application shouldn’t be aware at the kind of display we are using. This is also what happens in our PCs: the code we use in an app has to be decoupled from a particular graphics device, or the features of our display.

So, the graphics primitives will be exposed to a composition cache. This pattern allows to use the graphics in any of two flavors: the imperative way and/or the declarative one. Even mixing them is allowed, though.

 

A short note about the colors.

The GDI library offers any RGB combination, as it were a classic 16M colors, plus the alpha-channel. The abstraction of the GDI can’t rely on the limitations of a certain display, but should expose all the capabilities to the logic layer. It’s a driver/display problem circumventing its limitations by a certain rule.

The led-matrix used for the demo offers just two basic colors (red and green), with no dim. That yields a total of three color by mixing both of them: four, if you consider also the “black” color, which implies both the colors off.

So, the driver of the led-matrix will consider a color-component “active” when its value is above the half of the full scale, that is when the corresponding byte is greater or equal than 0x80 (=128), whereas 0xFF (=255) is the maximum.

The blue component is simply ignored, thus many “different” combinations will match in the reality. Also the perfect “white” is impossible to realize, because there are no blue leds.

Even worse is the case of a LCD character module, which is only monochromatic. We’ll see the same GDI library applied to a normal monochrome LCD, because the abstraction allows that. However, talking about “colors” in this case is clearly a misuse: simply does exist only “colored” and “not-colored”.

 

Drawing straight lines.

Yes, you can draw even a pixel, but I believe that’s a trivial task: a line should give us a bit more of satisfaction!

To draw a line you’ll need a “Pen”. When no thickness is specified, it defaults to one.

 

    public class Program
    {
 
        private static ICompositionRenderer _renderer;
 
 
        public static void Main()
        {
            //create a led-matrix driver instance
            _renderer = new SureHT1632(
                cspin: Pins.GPIO_PIN_D10,
                clkpin: Pins.GPIO_PIN_D9);
 
            //creates the target composition instance
            var composition = _renderer.CreateTarget();
 
            //draw a simple line
            composition.DrawLine(
                Pens.Lime,
                new Point(2, 13),
                new Point(13, 2)
                );
 
            //dumps the composition to the physical device
            _renderer.Dump(composition);
 
            //keep alive
            Thread.Sleep(Timeout.Infinite);
        }
 
    }

Here is the result.

WP_000291

 

Now we’ll going to add another straight line, but a bit thicker than the first one.

 

    public class Program
    {
 
        private static ICompositionRenderer _renderer;
 
 
        public static void Main()
        {
            //create a led-matrix driver instance
            _renderer = new SureHT1632(
                cspin: Pins.GPIO_PIN_D10,
                clkpin: Pins.GPIO_PIN_D9);
 
            //creates the target composition instance
            var composition = _renderer.CreateTarget();
 
            //draw a simple line
            composition.DrawLine(
                Pens.Lime,
                new Point(2, 13),
                new Point(13, 2)
                );
 
            //draw a thicker line
            composition.DrawLine(
                new Pen(Colors.Red, 2),
                new Point(29, 10),
                new Point(14, 3)
                );
 
            //dumps the composition to the physical device
            _renderer.Dump(composition);
 
            //keep alive
            Thread.Sleep(Timeout.Infinite);
        }
 
    }

Below is the resulting “composition”.

WP_000292

At the moment of writing there’s no curved lines support, nor special pen’s patterns.

 

Drawing rectangles.

NOTE: the size of a rectangle (width, height) is meant as the distance from the center of the edge lines, thus equals the actual number of pixels drawn minus one.

Just add an empty frame to the drawing (or cut off the previous lines if you like more).

 

    public class Program
    {
 
        private static ICompositionRenderer _renderer;
 
 
        public static void Main()
        {
            //create a led-matrix driver instance
            _renderer = new SureHT1632(
                cspin: Pins.GPIO_PIN_D10,
                clkpin: Pins.GPIO_PIN_D9);
 
            //creates the target composition instance
            var composition = _renderer.CreateTarget();
 
            //draw a simple line
            composition.DrawLine(
                Pens.Lime,
                new Point(2, 13),
                new Point(13, 2)
                );
 
            //draw a thicker line
            composition.DrawLine(
                new Pen(Colors.Red, 2),
                new Point(29, 10),
                new Point(14, 3)
                );
 
            //draw a rectangle frame
            composition.DrawRectangle(
                Pens.Yellow,
                new Rectangle(2, 4, 11, 7)
                );
 
            //dumps the composition to the physical device
            _renderer.Dump(composition);
 
            //keep alive
            Thread.Sleep(Timeout.Infinite);
        }
 
    }

Here is the result.

WP_000293

 

Now, in order to add a filled rectangle, you should consider a “Brush” instead.

 

    public class Program
    {
 
        private static ICompositionRenderer _renderer;
 
 
        public static void Main()
        {
            //create a led-matrix driver instance
            _renderer = new SureHT1632(
                cspin: Pins.GPIO_PIN_D10,
                clkpin: Pins.GPIO_PIN_D9);
 
            //creates the target composition instance
            var composition = _renderer.CreateTarget();
 
            //draw a simple line
            composition.DrawLine(
                Pens.Lime,
                new Point(2, 13),
                new Point(13, 2)
                );
 
            //draw a thicker line
            composition.DrawLine(
                new Pen(Colors.Red, 2),
                new Point(29, 10),
                new Point(14, 3)
                );
 
            //draw a rectangle frame
            composition.DrawRectangle(
                Pens.Yellow,
                new Rectangle(2, 4, 11, 7)
                );
 
            //draw a filled rectangle
            composition.FillRectangle(
                Brushes.Lime,
                new Rectangle(22, 8, 7, 5)
                );
 
            //dumps the composition to the physical device
            _renderer.Dump(composition);
 
            //keep alive
            Thread.Sleep(Timeout.Infinite);
        }
 
    }

Here is the result.

WP_000294

If you wish to draw a filled-framed rectangle, you have to apply both the above functions.

Again, no fancy effects are available at the moment.

 

Drawing text.

Text is something useful: no doubt. The problem is there’s no enough space, even for a normal word.

In the GDI library there’s a fixed-pitch 5x7 font (very similar to the monochrome LCD displays), which is very basic, but also takes a lot of pixels for even a short word. You could also create your own font set, even tighter than the default one.

The DrawString function needs a “Brush” for painting text: that’s because the original GDI+ library would consider a character as an area mostly filled with a certain brush, but also sometime outlined with a pen. This feature is not supported here, thus a brush is required for mimic the original reference.

    public class Program
    {
 
        private static ICompositionRenderer _renderer;
 
 
        public static void Main()
        {
            //create a led-matrix driver instance
            _renderer = new SureHT1632(
                cspin: Pins.GPIO_PIN_D10,
                clkpin: Pins.GPIO_PIN_D9);
 
            //creates the target composition instance
            var composition = _renderer.CreateTarget();
 
            //draw a simple line
            composition.DrawLine(
                Pens.Lime,
                new Point(2, 13),
                new Point(13, 2)
                );
 
            //draw a thicker line
            composition.DrawLine(
                new Pen(Colors.Red, 2),
                new Point(29, 10),
                new Point(14, 3)
                );
 
            //draw a rectangle frame
            composition.DrawRectangle(
                Pens.Yellow,
                new Rectangle(2, 4, 11, 7)
                );
 
            //draw a filled rectangle
            composition.FillRectangle(
                Brushes.Lime,
                new Rectangle(22, 8, 7, 5)
                );
 
            //draw some text
            composition.DrawString(
                "Ciao!",
                Fonts.Fixed5x7,
                Brushes.Red,
                new Point(0, 8)
                );
 
            //dumps the composition to the physical device
            _renderer.Dump(composition);
 
            //keep alive
            Thread.Sleep(Timeout.Infinite);
        }
 
    }

Below is the final picture which should be exposed in some museum.

WP_000295

 

Conclusions.

I really hope you’ll enjoy this library along this led-matrix. It’s far from being perfect, but a lot of fun is guaranteed!

Next time I’ll show you how to animate a drawing.

Comments are closed on this post.