Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

Getting Started with .NET Gadgeteer Part 2: A Larson Scanner with Button, Potentiometer and Progress Display

Pete Brown - 11 November 2011

Happy 11/11/11!

For those of us who grew up in the 80s, the back-and-forth red lights on the front of KITT and the Cylons was considered a really awesome effect. It's still popular today, and is even named after Gary Larson, the person responsible for those shows.

image

image

Now, a good Larson scanner uses some PWM to modify the intensity of the LEDs trailing the leading light. With the understanding that this is a beginner-focused article, I'm going to pass on that and simply have an LED that moves back and forth, more like the Cylon scanner, less like the Knight Rider scanner.

The Circuit

The circuit is a simple pin-hogging parallel approach. I use pins 3 through 9 on an extender module, each with a resistor and an LED.

image

Here's a photo of the actual circuit. Note that I used 120R resistors here because that's what I had handy. Use the correct resistors for your particular LEDs.

image

Gadgeteer Program

Here's the source code for the first version. When you run the program, it immediately starts scanning back and forth at a set speed.

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;
using Gadgeteer.Interfaces;

namespace GadgeteerLarsonScanner
{
public partial class Program
{
private GT.Timer _ledTimer;

private DigitalOutput[] _scannerOutput;
private int _currentOutput = 0;

void ProgramStarted()
{
const int lowPin = 3;
const int highPin = 9;

_scannerOutput = new DigitalOutput[highPin - lowPin + 1];

for (int pin = lowPin, i=0; pin <= highPin; pin++,i++)
{
_scannerOutput[i] = ScannerLedExtender.SetupDigitalOutput((GT.Socket.Pin)pin, false);
}

_ledTimer = new GT.Timer(100, GT.Timer.BehaviorType.RunContinuously);
_ledTimer.Tick += new GT.Timer.TickEventHandler(OnTimerTick);
_ledTimer.Start();

}

private bool _movingLowToHigh = true;

private void OnTimerTick(GT.Timer timer)
{
// turn off old LED
_scannerOutput[_currentOutput].Write(false);

if (_movingLowToHigh)
{
_currentOutput += 1;
if (_currentOutput > _scannerOutput.Length - 1)
{
// we were at the end, switch direction
_currentOutput = _scannerOutput.Length - 1;
_movingLowToHigh = false;
}
}
else
{
_currentOutput -= 1;
if (_currentOutput < 0)
{
// we were at the end, switch direction
_currentOutput = 0;
_movingLowToHigh = true;
}
}

// turn on new LED
_scannerOutput[_currentOutput].Write(true);
}
}
}

Difference in Brightness

You may have noticed a difference in brightness between some of the LEDs. In some cases, this is because of different types of LEDs or values of resistors. You can easily test this by swapping them around and seeing if the brightness level follows the resistor or LED.

You may also see some brightness values that are the same no matter what you swap around. I think the different pins on some of the ports can source different levels of voltage or current, but I haven't metered them to be sure. It seems, for example, that Pin 6 on a few ports is relatively high voltage or current, while pin 9 in some cases was very low.

The next step is to do a little modification to allow us to control the speed.

Adding in a Potentiometer to Control Speed

When I ordered my Gadgeteer kit, I ordered a potentiometer module at the same time. These have been out of stock for a bit, but should be back soon. I connected it to one of the "A" ports, port 10 in this case, and named it "SpeedPot"

image

Then add the following code to the program. This polls the potentiometer value every time the timer ticks. Because the potentiometer is an analog device, you won't get an event when the value changes; voltage changes are just too fiddly for something like that. Instead, when call ReadPotentiometerPercentage, you get back a double value between 0.0 and 1.0 which represents where the pot is positioned.

private void OnTimerTick(GT.Timer timer)
{
...

// turn on new LED (this is existing code)
_scannerOutput[_currentOutput].Write(true);

// adjust the interval based on the potentiometer
timer.Interval = new TimeSpan(0, 0, 0, 0, GetAdjustedSpeed());

}

private const int _minTimer = 10;
private const int _maxTimer = 210;
private int GetAdjustedSpeed()
{
double percentage = SpeedPot.ReadPotentiometerPercentage();

return (int)(percentage * (_maxTimer - _minTimer) + _minTimer);
}

Now run it. Cool!

Adding a Button

Here's one last cool thing you can add. The pot was an example of polling for a value. In this next bit, we're going to use an event to get a value. Add a button module and connect it up to Socket 5. Name it StartStopButton. As the name suggests, we'll use this to start and stop the scanner.

image

First, we need to wire up the event handler, so in the ProgramStarted method, add this code to the bottom. Note that I removed the _ledTimer.Start() code.

void ProgramStarted()
{
...
// Be sure to comment out the Start() as shown here
//_ledTimer.Start();

StartStopButton.ButtonPressed += StartStopButton_ButtonPressed;
}

Did you know (or is it "The More than you Know"?)

Did you know that you can leave out the explicit delegate creation when wiring up an event handler? The IDE automatically puts it in there for you so it's not a time-saver, but it does save space when listing out code in blog posts and books. These two lines of code are equivalent:

StartStopButton.ButtonPressed += StartStopButton_ButtonPressed;

StartStopButton.ButtonPressed += new Button.ButtonEventHandler(StartStopButton_ButtonPressed);

There are, of course lots of other ways to define event handlers. I'll have another post on that soon.

Next, we need the event handler code itself. This is a simple toggle of the timer when you press the button.

private void StartStopButton_ButtonPressed(Button sender, Button.ButtonState state)
{
if (_ledTimer.IsRunning)
_ledTimer.Stop();
else
_ledTimer.Start();
}

Now when you run the program, the scanner doesn't automatically start. Instead, you need to press the button to get it to start. Once it is running, you can press the button again to pause it, leaving the last LED lit.

The button example is a great case of event-driven programming on the gadgeteer. Rather than having to poll the value of a pin every X milliseconds, you simple wait for the event handler to be fired off.

Adding in the Screen

Ok, if you have the Gadgeteer starter kit, you have this ginormous touch screen just begging you to play with it. Let's use it to display a little information. First thing you need to do is connect up the screen. Also, if you don't already have one attached, plug a 9v adapter into the power supply port on your dual power module. Chances are, USB is not going to keep up with powering the LEDs, the potentiometer, and the LCD screen. You are welcome to try, of course, especially if you're plugged into a powered hub.

image

Note that I had to move the potentiometer to socket 9, as it was using the only "T" socket on the board. None of the code has to change, the designer takes care of passing the appropriate socket number into the constructor for the pot. Why did I need to connect the touch screen? I've seen reports that the touch screen sometimes acts up if you don't wire up the T port, even if you're not using touch. In this case, simply better safe than sorry.

There are a few ways to program for the screen. The first is a subset of WPF. The second, in the case of GHI hardware, is a library known as Glide. The third is to use the SimpleGraphics API. As that is the shortest and meets what I want to do here, I'll do that. WPF and Glide will come in a future post.

Embedding the Font

The Gadgeteer doesn't include any fonts loaded in the firmware. This makes sense from a space-saving perspective, but means a little more work on your part. If you've done Silverlight development, you'll likely already be familiar with having to embed fonts. Whatever font you embed, make sure you have an appropriate license. Don't assume I've done that check myself, here. One nice thing is that the default project template comes with two fonts already embedded, so you can use those directly.

image

We'll use the NinaB font here. To use that, we need to load the resource. Add a new function named SetupDisplay which will be called from the ProgramStarted method.

private Font _largeFont;
private Font _smallFont;

private void SetupDisplay()
{
_largeFont = Resources.GetFont(Resources.FontResources.NinaB);
_smallFont = Resources.GetFont(Resources.FontResources.small);

Display.SimpleGraphics.BackgroundColor = GT.Color.Black;
Display.SimpleGraphics.DisplayText("My Gadgeteer Demo", _smallFont, GT.Color.White, 10, 10);
}

Make sure you add a call to that to the ProgramStarted method. It doesn't really matter where, but I added it as the last line.

Displaying Text and Graphics

There are two lines of text to display for this application. The top line just says "My Gadgeteer Demo". The second line is the current speed of the scanner. In addition to the text, there are two rectangles which make up a progress bar.

Add a new method named UpdateDisplay. This method will handle the update of the information on-screen. It calls out to DisplayProgressBar to create an on-screen progress bar showing the value of the potentiometer.

private const uint _maxProgressWidth = 280;
private const uint _progressLeft = 20;
private const uint _progressTop = 40;
private const uint _progressHeight = 40;
private void DisplayProgressBar()
{
uint progressWidth = (uint)(_maxProgressWidth * _currentSpeedPercentDouble);
uint nonProgressWidth = _maxProgressWidth - progressWidth;
uint nonProgressLeft = _progressLeft + progressWidth;

Display.SimpleGraphics.DisplayRectangle(
GT.Color.Blue,
0,
GT.Color.FromRGB(0x00, 0x00, 0xBA),
nonProgressLeft, _progressTop, nonProgressWidth, _progressHeight);

GT.Color progressColor;
if (_ledTimer.IsRunning)
progressColor = GT.Color.Green;
else
progressColor = GT.Color.Red;

Display.SimpleGraphics.DisplayRectangle(
GT.Color.Red,
0,
progressColor,
_progressLeft, _progressTop, progressWidth, _progressHeight);

}

private void UpdateDisplay()
{
DisplayProgressBar();

Display.SimpleGraphics.DisplayText(
_currentSpeedPercent.ToString() + "%", _largeFont, GT.Color.White, 60, 60);
}

This method needs to be called from a couple places. It also requires an update to GetAdjustedSpeed to update some new variables. For those reasons, it's best just to look at the final source code in total.

Final Source Code

The final source code with everything in place looks like this:

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;
using Gadgeteer.Interfaces;

namespace GadgeteerLarsonScanner
{
public partial class Program
{
private GT.Timer _ledTimer;

private DigitalOutput[] _scannerOutput;
private int _currentOutput = 0;

void ProgramStarted()
{
Debug.Print("Program Started");

const int lowPin = 3;
const int highPin = 9;

_scannerOutput = new DigitalOutput[highPin - lowPin + 1];

for (int pin = lowPin, i=0; pin <= highPin; pin++,i++)
{
_scannerOutput[i] = ScannerLedExtender.SetupDigitalOutput((GT.Socket.Pin)pin, false);
}


_ledTimer = new GT.Timer(100, GT.Timer.BehaviorType.RunContinuously);
_ledTimer.Tick += new GT.Timer.TickEventHandler(OnTimerTick);
//_ledTimer.Start();

StartStopButton.ButtonPressed += StartStopButton_ButtonPressed;

SetupDisplay();

}

private void StartStopButton_ButtonPressed(Button sender, Button.ButtonState state)
{
if (_ledTimer.IsRunning)
_ledTimer.Stop();
else
_ledTimer.Start();

UpdateDisplay();
}

private bool _movingLowToHigh = true;

private void OnTimerTick(GT.Timer timer)
{
// turn off old LED
_scannerOutput[_currentOutput].Write(false);

if (_movingLowToHigh)
{
_currentOutput += 1;
if (_currentOutput > _scannerOutput.Length - 1)
{
// we were at the end, switch direction
_currentOutput = _scannerOutput.Length - 1;
_movingLowToHigh = false;
}
}
else
{
_currentOutput -= 1;
if (_currentOutput < 0)
{
// we were at the end, switch direction
_currentOutput = 0;
_movingLowToHigh = true;
}
}

// turn on new LED
_scannerOutput[_currentOutput].Write(true);

timer.Interval = new TimeSpan(0, 0, 0, 0, GetAdjustedSpeed());
}

private const int _minTimer = 10;
private const int _maxTimer = 210;
private int _currentSpeedPercent = 0;
private double _currentSpeedPercentDouble = 0;

private int GetAdjustedSpeed()
{
_currentSpeedPercentDouble = SpeedPot.ReadPotentiometerPercentage();

_currentSpeedPercent = (int)(_currentSpeedPercentDouble * 100);

UpdateDisplay();

return (int)(_currentSpeedPercentDouble * (_maxTimer - _minTimer) + _minTimer);
}

private Font _largeFont;
private Font _smallFont;

private void SetupDisplay()
{
_largeFont = Resources.GetFont(Resources.FontResources.NinaB);
_smallFont = Resources.GetFont(Resources.FontResources.small);

Display.SimpleGraphics.BackgroundColor = GT.Color.Black;
Display.SimpleGraphics.DisplayText("My Gadgeteer Demo", _smallFont, GT.Color.White, 10, 10);
}

private const uint _maxProgressWidth = 280;
private const uint _progressLeft = 20;
private const uint _progressTop = 40;
private const uint _progressHeight = 40;
private void DisplayProgressBar()
{
uint progressWidth = (uint)(_maxProgressWidth * _currentSpeedPercentDouble);
uint nonProgressWidth = _maxProgressWidth - progressWidth;
uint nonProgressLeft = _progressLeft + progressWidth;

Display.SimpleGraphics.DisplayRectangle(
GT.Color.Blue,
0,
GT.Color.FromRGB(0x00, 0x00, 0xBA),
nonProgressLeft, _progressTop, nonProgressWidth, _progressHeight);

GT.Color progressColor;
if (_ledTimer.IsRunning)
progressColor = GT.Color.Green;
else
progressColor = GT.Color.Red;

Display.SimpleGraphics.DisplayRectangle(
GT.Color.Red,
0,
progressColor,
_progressLeft, _progressTop, progressWidth, _progressHeight);

}

private void UpdateDisplay()
{
DisplayProgressBar();

Display.SimpleGraphics.DisplayText(
_currentSpeedPercent.ToString() + "%", _largeFont, GT.Color.White, 60, 60);
}
}
}

In that, we see polling, event-driven sensing, digital outputs, timers, and using the screen. Not too shabby for microcontroller work :)

In this example, I didn't break anything out into classes, as I normally would. In a real application, the progress bar, the scanner, and the display functionality would all be pulled out into separate classes which better encapsulate the functionality. In addition, the painting is not efficient, but is instead just for illustration. Enough disclaimers? :)

Photos

Here are some photos of the finished product showing the Larson scanner complete with progress bar.

image 

image

image

image

Where to Learn More

Be sure to visit the Gadgeteer forums for your hardware vendor. In the case of the GHI boards, the FEZ Spider and Hydra forums can be found here. There are tons of people there, both from the community and from GHI, all willing to help.

 

Update 11/11/11: Updated version of the circuit and some additional information here.

 

       
posted by Pete Brown on Friday, November 11, 2011
filed under:        

1 comment for “Getting Started with .NET Gadgeteer Part 2: A Larson Scanner with Button, Potentiometer and Progress Display”

  1. M Molinasays:
    Pete,

    Thanks for posting your Getting Started with .NET Gadgeteer Part 2: A Larson Scanner with Button, Potentiometer and Progress Display, it is certainly the type of information and details that I want to read before jumping into the .NET Gadgetteer bandwagon. Is Part 1 of the same available somehwere? I just joined tweeer and was not able to find it if it's poted there.
    Thanks again for sharing your project experiences with the Gagetteer!

Comment on this Post

Remember me