Main Site Documentation

Misunderstanding Gadgeteer timers


#1

OK, it looks like I do not understand Gadgeteer timers. Here’s v1 of a fragment of some code I’m having trouble with:


        private GT.Timer _DatasetTitleTimer;

        void ProgramStarted()
        {
            _DatasetTitleTimer = new GT.Timer(3000, GT.Timer.BehaviorType.RunOnce);
            _DatasetTitleTimer.Tick += _DatasetTitleTimer_Tick;
        }

        private void Poller_NewDataAvailable(object source, NewDataEventArgs args)
        {
            if (!DisksCalibrating() && !_DatasetTitleTimer.IsRunning)
            {
                if (_LatestData == null || args.DataSetId != _LatestData.DataSetId)
                {
                    SendKeyOverSerial(args);
                    _DatasetTitleTimer.Start();
                }
                else
                {
                    if (!_LatestData.DataEquals(args))
                    {
                        SendDisksToPosition(args.Data);
                    }
                }
                _LatestData = args;
            }
        }

        private void _DatasetTitleTimer_Tick(GT.Timer timer)
        {
            SendDisksToPosition(_LatestData.Data);
        }

Or this rewrite


        private void Poller_NewDataAvailable(object source, NewDataEventArgs args)
        {
            if (!DisksCalibrating())
            {
                if (_LatestData == null || args.DataSetId != _LatestData.DataSetId)
                {
                    SendKeyOverSerial(args);
                    var datasetTitleTimer = new GT.Timer(3000, GT.Timer.BehaviorType.RunOnce);
                    datasetTitleTimer.Tick += _DatasetTitleTimer_Tick;
                    datasetTitleTimer.Start();
                }
                else
                {
                    if (!_LatestData.DataEquals(args))
                    {
                        SendDisksToPosition(args.Data);
                    }
                }
                _LatestData = args;
            }
        }

        private void _DatasetTitleTimer_Tick(GT.Timer timer)
        {
            timer.Tick -= _DatasetTitleTimer_Tick;
            Debug.Print("_DatasetTitleTimer_Tick at " + _Poller.When());
            SendDisksToPosition(_LatestData.Data);
        }

In both cases I would expect that the timer would fire after roughly 3 seconds (3,000 milliseconds). I do not think of netmf as real time so I would not expect the timer to fire after exactly 3 seconds but what I see in practise is the first time it fires after 3 seconds, the second run through it’s about 12 seconds, and thereafter it is anything between 8 and 19 seconds. How can a 3000 millisecond timer routinely take 19 seconds to fire? What have I misunderstood about Gadgeteer timers and their use? How can I put that right?


#2
using System;
using Microsoft.SPOT;
using GT = Gadgeteer;
using GTM = Gadgeteer.Modules;

namespace GadgeteerApp2
{
    public partial class Program
    {
        // This method is run when the mainboard is powered up or reset.   
        void ProgramStarted()
        {
            GT.Timer timer = new GT.Timer(3000);
            timer.Tick += timer_Tick;
            timer.Start();
        }
        void timer_Tick(GT.Timer timer)
        {
            Debug.Print(DateTime.Now.ToString());
        }
    }
}

#3

Sorry Bill, is that an answer? If so then I’m afraid I do not understand it. It looks the same as my v1 code in the question. What’s your point?


#4

That my code fires every 3 seconds…and it’s not quite the same… :wink:


#5

Remove any blocking io calls and try again.


#6

explicitly, Bill’s code starts the timer in the ProgramStarted() method, and reliably fires in that 3sec timeframe.


#7

I cannot start the timer in ProgramStarted because I need to start it in response to an incoming event. Here’s what’s happening:

  1. The main program starts and initializes a class library that opens up the network and repeatedly calls a URL to fetch the latest data to display. When it fetches data it raises an event.

  2. The main program handles the event and parses the data. It first sends some of the data over serial to an mbed. (N.B. The mbed is driving an eInk display and a series of motors. The mbed first displays the title screen for the data and then after a short pause rotates the colour wheels and replaces the title picture with the legend for the data.)

  3. The main program pauses for three seconds.

  4. The main program calls a class library to send the disks on a physical pie chart to the positions required by the latest data.

So I cannot start the timer responsible for stage (3) in ProgramStarted because it needs to start each time I get data. But when I do start it I expect it to take more-or-less three seconds, not between eight and nineteen seconds.

(I’ll add two picture’s of the physical pie chart so you get some idea what I mean)


#8

@ dumbledad - if you dont have any joy i am in the office tomorrow and will come and spin your wheel :whistle:


#9

Justin, sadly I’ll be on a train to Dundee.


#10

Just leave the keys to your office under the mat then ::slight_smile:


#11

You have to be aware that as long as you don’t exit the timer event handler, no other timer, and eventually some other events are not fired.
NETMF (and therefor Gadgeteer) uses a single background thread for all timers.


#12

Thanks Richard, that is interesting and very possibly the source of my problems. Do you have code or advice on how to chain timers together (e.g. kick of one timer as the result of another firing)?

Also, I just found this post on Gadgeteer timers by Nic http://www.netmf.com/gadgeteer/forum/default.aspx?g=posts&m=8583#post8583 :

“Most importantly, using a GT.Timer also ensures that any calls you make to other Gadgeteer methods from the Tick event handler execute on the Dispatcher (for example, periodically updating a display or flashing an LED). This means that calls execute safely without running into cross-thread exceptions. For this same reason, it is often better to use a GT.Timer rather than a Thread in your application code.”

How does the Dispatcher stuff he mentions tally with the single thread model you describe?


#13

I’m not really experienced with Gageteer, I’m using plain NETMF ususally.
But you could use my ThreadPlool class to easily run asynchrony tasks:
https://www.ghielectronics.com/community/codeshare/entry/806