microframework.dk

.NET softwired

Distance measurement with ultrasonic sensor

Introduction

It is very easy to use a .NET Micro Framework device to measure physical short range distance using a ultrasound range finder module. This article describes a hardware module and the software to make it work in a .NET microframework program. The module uses only 2 digital IO pins. You can use any MF device for this, but I have used the NetduinoMini in my project.

The module here can measure distance in the range of 3-450cm with a precision of around 4mm.

I have written a .NET class that wraps the module in easy to use C# code. The code is free to use for you.

Ultrasound module

It is of course possible to build your own module from scratch but it is easier and also cheaper to use a ready made module. I looked at Ebay, and found several candidates which are dirt cheap, and chose the "DYP-ME007" module, which I ordered:

All it takes to use this is +5V power and 2 digital IOs from a microcontroller, 1 output and 1 input.

This particular module has a trigger input and 2 types of output. It has a digital echo output for distance measurements and an additional digital output that indicates if an obstacle is within measurement range of the module (kind of an "on/off detection").

You can go find your own module on Ebay for as little as US$3 including world wide transport here Ebay: Ultrasonic module.

Note that not all modules have the additional output pin that indicates "object within range" but I'm not going to use that in this project anyway. Look for modules that have a "Trig" and "Echo" pins.


Theory

Ultrasonic range finder modules uses the principle of echolocation as used by bats. The module sends out soundwaves (a "ping") and waits for a sound reflection to come back. If the module detects a reflected signal, the distance to the object which caused the reflection can be calculated. This is also known as SONAR (SOund Navigation and RAnging).

Ultrasound soundwaves is defined to have a frequency above 20KHz. This is for most people the upper level of the human audible region. This and other modules similar to the one I use her, use a frequency at 40kHz.

Sound travels at a constant speed, depending on the media (air), pressure and temperature. In theory this means that in order to get very precise measurements, you will need to recalibrate the distance calculation if any of the parameters change after the initial calibration. You can also build in this calibration if you can measure these parameters.

The formula for calculating the distance, keeping airpressure and temperature constant, is:

Distance = 340m/s * Time / 2

Distance is in meters and Time is in seconds. The "divide by 2" part is because the soundwaves will travel from the transducer to the target and then back to the transducer again (2 x distance). 

I will keep it simple in this project and only perform a basic calibration and not care about the other parameters. I'm not going to use this for precise distance measurements anyway.

From the above equation it is easy to see that all we need to measure to be able to calculate the distance, is the "Time" parameter. This is the time it takes from we send out the "Ping" and until we receive the "Echo".


Module description

This module has 2 ultrasound transducers on the board. One for sending out the ping and one for receiving the echo. To get the module up and running, you only have to supply it with 5 volt power and then activate the "Trig" signal. If an obstacle is detected, then the echo pin will be activated and the time between the trigger and receiving the echo signals can be measured. Knowing the time for the echo signal it is a simple calculation to get the distance.

 

  

Making a ping and detecting the echo

To start the distance measurement you have to activate the "trig" pin and then measure the time it takes until the "echo" pin goes active.

Use a "OutputPort" for the trigger and an "InterruptPort" for the echo detection.

// HW IO definitions
private OutputPort trig;
private InterruptPort echo;

// Initialize IO ports and interrupthandler
trig = new OutputPort(TriggerPin, true);
echo = new InterruptPort(EchoPin, true, 
                         Port.ResistorMode.Disabled, 
                         Port.InterruptMode.InterruptEdgeLow);
Echo.OnInterrupt += new NativeEventHandler(echo_OnInterrupt);


Rangefinder code

The rangefinder is implemented as a class, using 2 digital IO pins.

using System;
using Microsoft.SPOT.Hardware;
using System.Threading;

namespace PFJ.NETMF.Hardware.UltrasoundRangefinder
{
    public delegate void RangefinderDistance(double Distance);
    public delegate void RangefinderTicks(long Ticks);

    public class UltrasoundRangefinder : IDisposable
    {
        // HW IO definitions
        private OutputPort trig;
        private InterruptPort echo;

        // Time measurement stuff
        private DateTime pingStart;
        private long timeTicks;

        // Calibration stuff
        private double slope;
        private double offset;

        // Thread stuff
        Thread ScanningThread;
        private object lockObject = new object();
        private bool running;

        // Properties
        private double distance;
        public double Distance
        {
            get 
            {
                lock (lockObject)
                {
                    return distance;
                }
            }
        }

        private bool calibrate;
        public bool Calibrate
        {
            get { return calibrate; }
            set { calibrate = value; }
        }

        #region Event definitions
        public event RangefinderDistance DistanceUpdated;
        private void onDistanceUpdated(double Distance)
        {
            if (DistanceUpdated != null)
                DistanceUpdated(Distance);
        }

        public event RangefinderTicks TicksUpdated;
        private void onTicksUpdated(long Ticks)
        {
            if (TicksUpdated != null)
                TicksUpdated(Ticks);
        }
        #endregion

        #region Construction and Destruction
        public UltrasoundRangefinder(Cpu.Pin TriggerPin, Cpu.Pin EchoPin)
        {
            try
            {
                // Initialize IO ports and interrupthandler
                trig = new OutputPort(TriggerPin, true);
                echo = new InterruptPort(EchoPin, true,
                                         Port.ResistorMode.Disabled,
                                         Port.InterruptMode.InterruptEdgeLow);

                echo.OnInterrupt += new NativeEventHandler(echo_OnInterrupt);

                // Initialize program variables
                timeTicks = 0;
                distance = 0;

                // Set default convertion parameters for ticks to centimeter convertion.
                slope = 0.00173;
                offset = -10.872;
            }
            catch
            {
                throw new Exception("Error initializing UltrasoundRangefinder.");
            }
        }

        public void Dispose()
        {
            // Dispose the IO ports
            trig.Dispose();
            echo.DisableInterrupt();
            echo.Dispose();
        }
        #endregion

        #region Public methods
        // Use a linear function to convert timeticks into centimeters
        public void SetCalibrationData(double SlopeFactor, double Offset)
        {
            this.slope = SlopeFactor;
            this.offset = Offset;
        }

        // Send one ping 
        public void Ping()
        {
            // Send ping and await echo. Echo activates interrupt port.
            lock (lockObject)
            {
                pingStart = DateTime.Now; // Log the starttime of the ping
            }
            // Trig the ultrasound module
            trig.Write(true);
            trig.Write(false);
        }

        // Initialize rangefinder thread and start it.
        public void StartScanning()
        {
            if (ScanningThread == null)
            {
                ScanningThread = new Thread(new ThreadStart(RangeScanning));
                ScanningThread.Start();
            }
        }

        // Set the variable used in the thread method to false to stop the thread.
        public void StopScanning()
        {
            running = false;
        }
        #endregion

        #region Private methods. Thread and interrupthandler.
        // Thread method which activates the Ultrasound module and calculates the distance
        // in centimeters
        // Scanning every 100 milli second
        private void RangeScanning()
        {
            running = true;
            while (running)
            {
                Ping();
                Thread.Sleep(100);
            }
            ScanningThread = null;
        }

        // Echo interrupt handler method
        private void echo_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            // Calculate time from pingstart to echo. 
            // Time calculated in ticks (1 tick = 100 ns)
            lock (lockObject)
            {
                timeTicks = time.Subtract(pingStart).Ticks;

                // Convert timeticks to centimeters
                distance = (double)timeTicks * slope + offset; 
                if (distance < 3 || distance > 500)
                    distance = -1;
            }

            // If calibration is active then raise event with updated ticks value
            if (this.calibrate)
                onTicksUpdated(timeTicks);
            else
                // Raise event with updated distance value, but only if distance > 0
                if (distance > 0)
                    onDistanceUpdated(distance);
        }
        #endregion
    }
}


Links

Netduino - New boards in town!

Just in case you haven't already seen it, there is a number of new .NET MicroFramework 4.1 devices in town, 3 to be exact, - the "Netduino".

The company Secret Labs have made these nice boards of which 2 are Arduino hardware compatible (it can use many of the same "Arduino shields" (plugin interface boards) - some may need minor modifications to the signal levels!) and one is "Basic Stamp 2" compatible..

The Netduino comes in several flavours - all based on the same Atmel 32-bit ARM7 chip AT91SAM7X512 - Standard, Plus and Mini.

I have grabbed some product pictures from the Secret Labs homepage (left to right: Standard, Plus):

 

Netduino standard Netduino Plus

 

The Mini is only 0.6"x1.2" (~15mmx30mm) in size. It plugs directly into a breadboard, as the pins are spaced on a 0.1" standard grid:

Netduino Mini

 

As of the date of writing this, only the Netduino standard board is available for purchase, but the other 2 will also be available soon, according to Secret Labs.

Like the Arduino concept, the Netduino is also Open Source. This means that you can get hardware schematics, board design files and software and reuse it for your own purpose :-) 

Standard and Plus are Arduino pin compatible development boards, where the Mini is like a "24-pin DIP chip" you can use in your own hardware designs. The Mini is "Basic Stamp 2" pin compatible (from the company Parallax).

The Netduino Plus, has built-in ethernet connector.

I personally find the Netduino Mini to be the most exiting of them all.

I bought one of the "standard" editions. It is priced nicely at US$34.95.  My experience with it is that it just worked when plugged in to the PC. It has good performance and enough of memory too.

One thing I noticed which I especially like, is that the USB interface is fast and reliable, unlike some of the other boards from other vendors I also have experience with!

Technical specifications for the Netduinos can be found here:

Netduino Standard specifications
Netduino Plus specifications
Netduino Mini specifications

 

Thumbs up and welcome to the new inhabitants in the .NET MicroFramework world.

Peter

Controlling Servo Motors with .NET MicroFramework

This post illustrates how you can control Servo Motors from .NET MicroFramework using only simple output pins and C# code.

I have this idea of making a walking robot, so in order to test the basic servo control from .NET MicroFramework, I have used 2 servo motors and built a leg. Each Servo motor controls a joint in a leg.


You can start out with a look at the short video on YouTube that I made to show the leg moving.


In my first test I have used the GHI USBizi development board, but no special hardware has been used, only simple GPIO pins. I wanted to use the built-in PWM feature of the GHI board, but the program locked up when I initialized the PWM feature! I don't know if this is an error in the GHI firmware or what, but instead of investigating this further I made my own Servo control class in C#, which implements the PWM control.


Update 2015.

The code running in the video, is as follows:

using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using System.Threading;

namespace PFJ.NETMF.Hardware.Motor
{
    public class Servo
    {
        OutputPort servoPort;
        int pulseWidth;

        int max;
        int min;
        int mid;

        object lockitem;

        public Servo(Cpu.Pin Pin)
        {
            servoPort = new OutputPort(Pin, false);

            max = 1450;
            min = 300;
            mid = 575; // (max - min) / 2;
            pulseWidth = mid;

            lockitem = new object();

            Thread ServoThread = new Thread(ServoProcessor);
            ServoThread.Start();
        }

        public void Left()
        {
            lock (lockitem)
            {
                pulseWidth = min;
            }
        }

        public void Middle()
        {
            lock (lockitem)
            {
                pulseWidth = mid;
            }
        }

        public void Right()
        {
            lock (lockitem)
            {
                pulseWidth = max;
            }
        }

        public void Position(double Percent)
        {
            double d = System.Math.Round((Percent / 100D) * (max - min) + min);
            lock (lockitem)
            {
                pulseWidth = Convert.ToInt32(d.ToString());
            }
        }

        public void Wait(int Delay)
        {
            Thread.Sleep(Delay);
        }

        protected virtual void ServoProcessor()
        {
            while (true)
            {
                lock (lockitem)
                {
                    ServoHighPulse();
                    ServoLowPulse();
                }
                Thread.Sleep(2);
            }
        }


        private void ServoHighPulse()
        {
            servoPort.Write(true);
            DelayMicroSec(pulseWidth);
        }

        private void ServoLowPulse()
        {
            servoPort.Write(false);
            DelayMicroSec(max - pulseWidth);
        }

        /// <summary>
        /// Blocks thread for given number of microseconds
        /// </summary>
        /// <param name="microSeconds">Delay in microseconds</param>
        private void DelayMicroSec(int microSeconds)
        {
            DateTime startTime = DateTime.Now;

            int stopTicks = microSeconds * 10;

            TimeSpan divTime = DateTime.Now - startTime;
            while (divTime.Ticks < stopTicks)
            {
                divTime = DateTime.Now - startTime;
            }
        }
    }
}

Peter

 

Controlling a Stepper Motor with .NET MicroFramework

This post illustrates how you can control a unipolar stepper motor from .NET MicroFramework using very simple hardware.

The motor which I have scavenged from an old 5 1/4" floppy disk drive (do you remember those?) is controlled by a dedicated Stepper Motor control class written in C#.



You can start out with a look at the short video on YouTube that I made to show the motor running:

 


Update 2015.

The code running in the video, is as follows.

using System;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using System.Threading;

namespace PFJ.NETMF.Hardware.Motor
{
    public class StepperMotor2003
    {
        private byte[] pattern1 = { 1, 8, 4, 2};

        OutputPort port1;
        OutputPort port2;
        OutputPort port3;
        OutputPort port4;

        private int delay;
        public int Delay
        {
            get { return delay; }
            set { delay = value; }
        }

        private int index;

        public StepperMotor2003(Cpu.Pin Pin1, Cpu.Pin Pin2, Cpu.Pin Pin3, Cpu.Pin Pin4)
        {
            port1 = new OutputPort(Pin1, true);
            port2 = new OutputPort(Pin2, false);
            port3 = new OutputPort(Pin3, false);
            port4 = new OutputPort(Pin4, false);

            index = 0;
            SetPattern(pattern1[index]);

            this.delay = 2;
        }

        private void SetPattern(byte p)
        {
            switch (p)
            {
                case 1:
                    port1.Write(true);
                    port2.Write(false);
                    port3.Write(false);
                    port4.Write(false);
                    break;
                case 2:
                    port1.Write(false);
                    port2.Write(true);
                    port3.Write(false);
                    port4.Write(false);
                    break;
                case 4:
                    port1.Write(false);
                    port2.Write(false);
                    port3.Write(true);
                    port4.Write(false);
                    break;
                case 8:
                    port1.Write(false);
                    port2.Write(false);
                    port3.Write(false);
                    port4.Write(true);
                    break;
            }
        }

        public void StepRight()
        {
            index = index == 3 ? 0 : index += 1;

            SetPattern(pattern1[index]);
            Thread.Sleep(this.delay);
        }

        public void StepRight(int Steps)
        {
            int i = 0;
            for (int r = 0; r < Steps; r++)
            {
                SetPattern(pattern1[System.Math.Abs(i)]);
                i += 1;
                i = i % 4;
                Thread.Sleep(this.delay);
            }
        }

        public void StepLeft()
        {
            index = index == 0 ? 3 : index -= 1;

            SetPattern(pattern1[index]);
            Thread.Sleep(this.delay);
        }

        public void StepLeft(int Steps)
        {
            int i = 3;
            for (int r = 0; r < Steps; r++)
            {
                SetPattern(pattern1[System.Math.Abs(i)]);
                i = i == 0 ? 3 : i -= 1;
                Thread.Sleep(this.delay);
            }
        }

        public void Wait(int Delay)
        {
            Thread.Sleep(Delay);
        }
    }
}

Peter

 

Happy 1 Year Birthday to microframework.dk

Today (31th December 2009) it is exactly 1 year ago I registered the domain name microframework.dk

The reason why I did that was to have a place to blog about the world of my .NET MicroFramework discoverings especially in relation to the Microsoft Dare to Dream Different contest, which I at that time hoped to be part of. Luckily my entry was accepted as one to move on to round 2, but that was as far as it went. I had a great time developing my project (HACS - House Access Control System with fun doorbell, which you can read about in another article).

Time has shown that I did not find all the time to blog as much as I wanted to do, so I have not released as many articles as I had hoped. But that does not mean that I have not been working with the .NET MicroFramework!

In order to let you see some of what I have been doing (and maybe what will come) I will today on the last day of the year, release 2 videos illustrating that the MF can really control motors. One video shows the control of a Stepper Motor and the other video shows the control of 2 Servo Motors. For now the articles are meant as a teaser, so stay tuned to see more.

Happy Birthday to microframework.dk and Happy New Year to everyone else

Peter

 

Dare To Dream Different - Contest videos released on YouTube

Hey! If you have been waiting to see the videos of the projects submitted to the Microsoft Dare To Dream Different Contest, then wait no more.

The .NET Micro Framework Team at Microsoft has now released all the submitted contest videos on YouTube. I have not yet counted the number of videos, so I can't tell you for sure that they are all there, but you can go have a look for yourself. Funny enough, Microsoft has not yet made any announcement about this, so maybe they are not finished uploading...

All videos are named like "Dare To Dream Different Contest - <project name>", so it should be easy to make a search for the videos. You can also use the link shown above, where I have setup a search criteria for you. Click and try!

I now know what I'm going to do tonight - it's video time!

Peter

A few words about my blog

I regularly receive a number of comments, questions and requests for my blog, and instead of answering each of them one at a time I will try to cover some of the recurring issues you ask or comment.

When you comment

Some comments are regular SPAM and I try to keep that out of the system, but sometimes it can be a little hard to see if a comment is really a comment to something I have written or just a waste of time. Please make your comments as clear as possible and add a real link to a website and email of yours! I welcome statements about projects and articles, but if you don't like the looks of my blog, then I don't need to hear about it. I will change it if I feel for it - not because you tell me to!

If you comment - please add your real name at the end of your post. That way is is more personal and I can see there is a real human at the other end.

The language 

The language on this site is English and there will not be a translated version into any other language. I try my best to write in a way so that it is understandable, but as my native language is Danish there is no guarantee for my English. If I write something that can offend somebody, then please accept my appologies as that is never intentional.

Why am I doing this? 

I maintain this blog because I want to give back something to the .NET Micro Framework community. I don't make money on this, it cost me money and quite a lot of time, but I personally learn a lot from other peoples web sites, so this is my way of paying back.

Sponsors

In case there are any companies who would like to sponsor some hardware then I will receive this with open arms. There are quite a number of nice development boards and other hardware stuff out there. If I receive something from you, I will try to make use of it in a project and write and article for this blog. I will be happy to mention your company name in the article. This way I hope it can be a win-win situation for both of us. If you are interested, then please write me an email.

Blog engine

For those interested in knowing which blog engine I use then I use "BlogEngine.NET" and I have modified the standard "StableStart" theme. It had to be easy to install and maintain and I don't need too much fancy stuff you see on some blog engines. It stores data in XML files, so it can be run on any server that support windows and .NET.

Site Statistics

My site seems to be more popular than I expected it would be as I receive around 50 unique guests each day. Some days it even goes above 100 - this is great :-) I know that many of you are returning guests and that I don't get 50 new visitors each day - if that was the case, then I'm sure we would see a lot more web sites about the micro framework. That would be cool though.

Guest posts

I do not accept guest posts because I simply do not have time to manage this.

Writing for others

Some of you ask me if I want to write articles for you, but the answer is no. I thank you for your request but I just don't have time to write for others!
Time is a scarce ressource and I have trouble even to find time to write articles for this blog. I have a pipeline of articles to write for this blog but as long as there is only 24 hours per day and I must share this between my family, my job, my blog, my programming interests and all my other interests, then I run out of time. Oh, and I also need to sleep...

Take care. Keep sending comments to my posts.

Peter

 

Dare To Dream Different - Competition exit...

My project: HACS - House Access Control System with fun doorbell, didn't make it to the final in the Microsoft Dare To Dream Different competition :-(

But hey - I'm a winner anyway :-). I did get a Device Solutions Tahoo-II development board for free - that is, if I don't count all the hours spent into developing this project. I learned a lot about the Micro Framework for embedded programming in C# directly on hardware,  and this is where the potential is and the real fun lies.

Furthermore, GHI Electronics LLC, who makes a lot of nice Micro Framework products, decided to donate 100 coupons for shopping in their webshop to all round 2 contestants in the competition - this is really generous of them - and I can now claim my 100 USD coupon and go shopping in the GHI shop. Wow, I'm not sure if I can limit my shopping to these 100 USD. They have so many nice boards that I think I have to spend some more money... Time will tell. Their new ChipworkX development system looks like a killer, but also the USBizi and the Embedded master boards are very attractive. A big thanks to GHI for this donation.

Round 2 Finalists

I must congratulate the authors of the 10 projects which made it to the finals :-). You can see a list of their names on the Dream Different contest page. I'm really looking forward to see the videos of these projects. I hope that Microsoft will make all the project videos publicly available, as they have promised earlier. It is always very educational to see what other people create and how they choose to implement it.

Peter

 

Dare To Dream Different - Video List

I have made a list of links to the Dare To Dream Different competition videos people have made public on the net. You can find it in my page list or click here.

If you are a competitor in this competition and have a video of your project, you are welcome to write me where I can find your video. Then I will add it to the list.

Peter