Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

How do I pause a while loop using the onboard button?


Best Answer ShVerni, 17 May 2014 - 01:14 AM

Hello,
 
If you'd like to do everything in one loop, this seems like the easiest solution:

public class Program
    {
        private static bool _blink = true;
        private static OutputPort _led = new OutputPort(Pins.ONBOARD_LED, false);
        private static InputPort _button = new InputPort(Pins.ONBOARD_BTN, false, Port.ResistorMode.Disabled);
        public static void Main()
        {
            while(true)
            {
                if (_button.Read())
                    _blink = !_blink;
                if (_blink)
                    _led.Write(!_led.Read()); //Sets the LED to the opposite of its current state
                Thread.Sleep(250);
            }
        }
    }

Now obviously there are some downsides to this approach, mainly that the button is only polled every 250 ms, so the button may not be very responsive and could be "missed" as you noticed.
 
Now, the idea you had of using two loops in different threads could look something like this:

public class Program
    {
        private static bool _blink = true;
        private static OutputPort _led = new OutputPort(Pins.ONBOARD_LED, false);
        private static InputPort _button = new InputPort(Pins.ONBOARD_BTN, false, Port.ResistorMode.Disabled);
        public static void Main()
        {
            //Start button polling thread
            new Thread(buttonLoop).Start();

            while(true)
            {
                if (_blink)
                    _led.Write(!_led.Read());
                Thread.Sleep(250);
            }
        }

        private static void buttonLoop()
        {
            while (true)
            {
                if (_button.Read())
                    _blink = !_blink;
                Thread.Sleep(50);
            }
        }
    }

You could try tuning the Thread.Sleep call in the buttonLoop to get the responsiveness you'd like, but there's still a lot of wasted overhead looping through things and polling the button. Plus, there's still a chance of "missing" the button press or reading too many presses.
 
This brings us to the event driven method, which is probably the most elegant solution, and uses an interrupt port for the button:

public class Program
    {
        private static bool _blink = true;
        private static OutputPort _led = new OutputPort(Pins.ONBOARD_LED, false);
        private static InterruptPort _button = new InterruptPort(Pins.ONBOARD_BTN, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeHigh);
        public static void Main()
        {
            //Attach the interrupt event
            _button.OnInterrupt += _button_OnInterrupt;

            while(true)
            {
                if (_blink)
                    _led.Write(!_led.Read());
                Thread.Sleep(250);
            }
        }

        static void _button_OnInterrupt(uint port, uint status, DateTime time)
        {
            Debug.Print("Button pushed");
            _blink = !_blink;
        }
    }

I should make a note to notice this parameter in the InterruptPort: Port.InterruptMode.InterruptEdgeHigh. That sets the interrupt to trigger whenever it sees a "high" logic value, essentially when the button would be read as True. I actually forget if the onboard_btn when pressed gives a "high" or "low" logic signal. If that doesn't work, try changing that parameter to Port.InterruptMode.InterruptEdgeLow. This also applies to all the times I called _button.Read() in my other examples, if things aren't working right you might need to change them to !_button.Read().

 

Also, if you use the interrupt method, you'll probably notice a lot of "bounce", which is the button quickly triggering multiple times as you press it, so you'll have to lean a bit about debouncing, and this post is a good place to start:
 
http://forums.netdui...upt/#entry55487

 

That thread also has some good infomation on buttons and interrupts in general. There are definitely other ways to do this sort of thing, but this should be a good start.

 

Good luck!

Go to the full post


  • Please log in to reply
7 replies to this topic

#1 Basset

Basset

    New Member

  • Members
  • Pip
  • 7 posts
  • LocationCanada

Posted 16 May 2014 - 08:45 PM

Hello

 

After not using my netduino for almost a year I want to get back into it now that I am home from university for the summer.  I've been trying to brush up on what I learned, so bear with me if I mess something simple up.

 

So about a year ago I was pretty much just playing with the onboard led, learning about pwm and making it fade in and out as well as using the button to turn the led on and off as seen in one of the example videos.  However I am stuck at how to 'pause' the flashing or pulsing led with the button. Last year I was able to do it but if I pressed the button at the wrong time it would 'miss' the input in the while loop.

 

To summarize what I want to do is to be able to press the button to stop the blinking and then press it again to start the blinking again.

 

ex. controlling this code:

public static void Main()
{
 // write your code here
 OutputPort led = new OutputPort(Pins.ONBOARD_LED, false);
 while (true)
 {
 led.Write(true); // turn on the LED
 Thread.Sleep(250); // sleep for 250ms
 led.Write(false); // turn off the LED
 Thread.Sleep(250); // sleep for 250ms
 }
}

with the button.  I know I can put the button.Read() command in the while loop condition and then put all of that in another while loop, but this starts to fall apart when I go to more complex fading or other patterns that may take a second or too to get through so if I press the button when its not looking for it then the blinking continues on.

 

I believe the answer would have something to do with threading or event handlers but I can't seem to wrap my head around them, or been able to find an example for the netduino that does a similar thing.  If anyone has a good tutorial on how to build event handlers and making events (is this the right terminology?) as well as using threads it would be greatly appreciated.

 

I assume a similar method with threading would be used if I wanted to maybe blink an led while doing other things which I would also like to eventually learn how to do.

 

Thanks for your help!

 

 



#2 ShVerni

ShVerni

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationNew York, New York

Posted 17 May 2014 - 01:14 AM   Best Answer

Hello,
 
If you'd like to do everything in one loop, this seems like the easiest solution:

public class Program
    {
        private static bool _blink = true;
        private static OutputPort _led = new OutputPort(Pins.ONBOARD_LED, false);
        private static InputPort _button = new InputPort(Pins.ONBOARD_BTN, false, Port.ResistorMode.Disabled);
        public static void Main()
        {
            while(true)
            {
                if (_button.Read())
                    _blink = !_blink;
                if (_blink)
                    _led.Write(!_led.Read()); //Sets the LED to the opposite of its current state
                Thread.Sleep(250);
            }
        }
    }

Now obviously there are some downsides to this approach, mainly that the button is only polled every 250 ms, so the button may not be very responsive and could be "missed" as you noticed.
 
Now, the idea you had of using two loops in different threads could look something like this:

public class Program
    {
        private static bool _blink = true;
        private static OutputPort _led = new OutputPort(Pins.ONBOARD_LED, false);
        private static InputPort _button = new InputPort(Pins.ONBOARD_BTN, false, Port.ResistorMode.Disabled);
        public static void Main()
        {
            //Start button polling thread
            new Thread(buttonLoop).Start();

            while(true)
            {
                if (_blink)
                    _led.Write(!_led.Read());
                Thread.Sleep(250);
            }
        }

        private static void buttonLoop()
        {
            while (true)
            {
                if (_button.Read())
                    _blink = !_blink;
                Thread.Sleep(50);
            }
        }
    }

You could try tuning the Thread.Sleep call in the buttonLoop to get the responsiveness you'd like, but there's still a lot of wasted overhead looping through things and polling the button. Plus, there's still a chance of "missing" the button press or reading too many presses.
 
This brings us to the event driven method, which is probably the most elegant solution, and uses an interrupt port for the button:

public class Program
    {
        private static bool _blink = true;
        private static OutputPort _led = new OutputPort(Pins.ONBOARD_LED, false);
        private static InterruptPort _button = new InterruptPort(Pins.ONBOARD_BTN, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeHigh);
        public static void Main()
        {
            //Attach the interrupt event
            _button.OnInterrupt += _button_OnInterrupt;

            while(true)
            {
                if (_blink)
                    _led.Write(!_led.Read());
                Thread.Sleep(250);
            }
        }

        static void _button_OnInterrupt(uint port, uint status, DateTime time)
        {
            Debug.Print("Button pushed");
            _blink = !_blink;
        }
    }

I should make a note to notice this parameter in the InterruptPort: Port.InterruptMode.InterruptEdgeHigh. That sets the interrupt to trigger whenever it sees a "high" logic value, essentially when the button would be read as True. I actually forget if the onboard_btn when pressed gives a "high" or "low" logic signal. If that doesn't work, try changing that parameter to Port.InterruptMode.InterruptEdgeLow. This also applies to all the times I called _button.Read() in my other examples, if things aren't working right you might need to change them to !_button.Read().

 

Also, if you use the interrupt method, you'll probably notice a lot of "bounce", which is the button quickly triggering multiple times as you press it, so you'll have to lean a bit about debouncing, and this post is a good place to start:
 
http://forums.netdui...upt/#entry55487

 

That thread also has some good infomation on buttons and interrupts in general. There are definitely other ways to do this sort of thing, but this should be a good start.

 

Good luck!



#3 Basset

Basset

    New Member

  • Members
  • Pip
  • 7 posts
  • LocationCanada

Posted 17 May 2014 - 04:25 AM

Thank you for your help!  

 

I tried the two examples you listed earlier in the year and as you said it wasn't all that reliable.  I have tried the last example you posted and it works perfectly!  Seeing how it's written up gives me some ideas on how I can expand it now and adapt it to other projects I am working on.

 

One more question: in your variables and some other peoples they lead with an underscore and I was wondering what it was for?  I have seen some people in tutorials use them inside methods and I assume they are to keep things more structured when the program gets more advanced but I am not sure.



#4 ShVerni

ShVerni

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationNew York, New York

Posted 17 May 2014 - 02:15 PM

I'm glad everything worked for you!
 
The question of the underscore is one of convention, and of course open to debate. The convention is that the underscore is used to differentiate private variables from public ones, especially if you want to have a private variable and a public facing version of that variable:

    public class Foo
    {
        //Private variable
        private int _bar;
        //Public read only version of variable
        public int bar
        {
            get
            {
                return _bar;
            }
        }

        //Another private variable
        private int _baz;
        //Public version that returns twice _baz
        public int baz
        {
            get
            {
                return 2 * _baz;
            }
            set
            {
                _baz = value;
            }
        }
    }

However, this convention is no longer recommended instead using the keyword 'this':
 
http://stackoverflow...arp-underscores
 
However, as this is a convention, and not a hard and fast rule, it's always up to the individual developer to choose which convention to use, and arguments can, and have, been made for both sides.
 
I, personally, prefer the underscore since it's the convention I learned and it seems cleaner than 'this', but I can definitely see the advantage to 'this' being an explicit reference to private member; ultimately the choice is up to you.

 

There was another reason I used _button: .NETMF already has a class called Button--with a capital B--so then having a variable with the name button would be very confusing (and VisualStudio kept trying to autocomplete to Button), the underscore fixed that problem as well.

 

Hope that helps!



#5 jrlyman3

jrlyman3

    Advanced Member

  • Members
  • PipPipPip
  • 67 posts
  • LocationNorth St Paul. MN

Posted 18 May 2014 - 12:49 AM

Of course, there are many (many) conventions ... I prefer the use of m_Button instead of _button.  This was promoted by Microsoft not sure if they still do.  You're never going to make everyone happy.  The important thing to me is to be consistent.

 

And have fun ....

 

John



#6 Paul Newton

Paul Newton

    Advanced Member

  • Members
  • PipPipPip
  • 724 posts
  • LocationBerkshire, UK

Posted 18 May 2014 - 06:29 AM

A project I worked on used "m_" to signify "member" - e.g. all the members of the classes had m_ whilst local variables in functions had names names with no prefix.

 

The great thing about standards and procedures is that there are so many to chose from.



#7 ShVerni

ShVerni

    Advanced Member

  • Members
  • PipPipPip
  • 138 posts
  • LocationNew York, New York

Posted 19 May 2014 - 02:21 AM

The great thing about standards and procedures is that there are so many to chose from.

Hilarious and true! Just for the record, I believe the "m_" prefix is taken from C++, though the Microsoft guidelines say to forgo any prefixes at all for member variables:

 

http://blogs.msdn.co.../26/361363.aspx

 

But, as mentioned many times before, you can use whatever convention you want, and it's too late for me to stop using prefixes at this point.



#8 Basset

Basset

    New Member

  • Members
  • Pip
  • 7 posts
  • LocationCanada

Posted 19 May 2014 - 02:46 AM

Thanks for the insight on the naming conventions. Kinda reminds me of this xkcd: http://xkcd.com/927/






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.