What happens when the TTimer interval is < than it takes to run its own code? lazarus / 62-General ---------------------------------------------------------------------- Alzibiff · 2015-08-30 12:32 I am returning to visual Pascal some 20 years since I used it last so things are a wee bit rusty... I am writing a program to control a solenoid - when the solenoid is ON, it allows water to flow. When the solenoid is OFF, the water stops flowing. Why? I want to set up a sort of dripping tap and control the ON and OFF times of the solenoid to vary the size and frequency of the drips. I have sorted out the code to switch the solenoid on and off from my RPi using one of its GPIO pins so I am making progress but the rest is making my brain hurt. (I have three solenoids to control and make three dripping taps). I have a button which will toggle the dripping on and off and was thinking that a TTimer object would do the job with the OnTimer code going something like this ... Switch solenoid ON delay to generate a drip turn solenoid OFF delay to generate an interval between drips (end of OnTimer code, back to the start) That way I get a drip every time that the timer ticks ... yes? I want to vary both delay periods by taking values from a couple of edit boxes - that is, the time that it takes the TTimer to tick is going to change. What value do I set for the TTimer.interval? Can I simply have a value less than the total of the combined delays - say 1ms -  so that I get a basic repeat..until false loop for my ONTimer code? In short, what I want to do is enable a timer so that it tick tick ticks away continuously repeating code till I tell it to stop by clicking a button which will disable it. All assistance gratefully received, Alan ---------------------------------------------------------------------- derek.john.evans · 2015-08-30 13:26 TTimer is somewhat inaccurate and the call back uses the main thread. TFPTimer also synchronizes with the main thread. What about running time critical threads? ---------------------------------------------------------------------- derek.john.evans · 2015-08-30 13:51 QuoteWhat happens when the TTimer interval is less time than it takes to run its own I just had a look. From what I can tell, a timer with interval 500ms, will still tick away at 500ms even if the timer event takes 400ms.  If the event takes 1000ms, then the timer will slow to 1000ms. ie: No timer events will "build up", and no-multiple timer calls will happen, since they are sent in the main thread. So, yes, you can have a short timer, and then check LclIntf.GetTickCount64 to get the actual time. Which is what a lot of games do. I guess, the issue is, TTimer runs in the main thread, which is effected by all GUI processing. So, how critical are these events? ---------------------------------------------------------------------- Alzibiff · 2015-08-30 18:28 link=topic=29504.msg186515#msg186515 date=1440942709] QuoteI guess, the issue is, TTimer runs in the main thread, which is effected by all GUI processing. So, how critical are these events? That is good news (for me). The accuracy of the timer is not so critical in my application but it needs to be as repeatable as possible from one call to the next. I understand the point which you are making about the rest of the GUI processing affecting timer accuracy and although this seems that it COULD be a problem, my program will be doing the same tasks from call to call. That is, the GUI processing time should be more or less constant throughout the run of my program. Of course, to confirm the above, I will just have to  suck it and see I guess! I'm grateful for your response though as I can continue to develop my application with a wee bit more confidence before the rest of my electronic components arrive and I get a chance to try things out experimentally. One last question - what is "LclIntf.GetTickCount64" exactly and how do I check it? Thank you, ALan ---------------------------------------------------------------------- derek.john.evans · 2015-08-30 18:37 The function GetTickCount64 is in the unit LclIntf. Basically it maps to the Win32 GetTickCount function: https://msdn.microsoft.com/en- us/library/windows/desktop/ms724408%28v=vs.85%29.aspx It returns milliseconds and is 10-16ms accurate. (according to Microsoft). It just means, you use a timer to monitor your taps, but you should use GetTickCount to determine if they should switch on/off since it is the real time. TTimer is an event interval which it tries to make good on. ---------------------------------------------------------------------- User137 · 2015-08-30 21:20 Here is a little test program. I have noticed that there is a minimum interval that TTimer is able to run, and that seems to be about 15. Anything less than interval 15 is redundant. Code: [Select]type   { TForm1 }   TForm1 = class(TForm)     Timer1: TTimer;     procedure FormCreate(Sender: TObject);     procedure Timer1Timer(Sender: TObject);   private     delay, second: TTime;   public   end; var Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject); begin   second:=encodetime(0, 0, 1, 0);   delay:=Now+second; end; procedure TForm1.Timer1Timer(Sender: TObject); begin   tag:=tag+1;   if now>delay then begin     delay:=delay+second;     caption:=inttostr(tag);     tag:=0;   end; end;It keeps printing 64 ticks per second, when i have TTimer interval set to 1. 1000/64 = 15,625. Anyway, for higher precision you need another timer (like some suggest Epictimer), or TThread with sleep(1). Without it a thread will max a cpu core, which is something TTimer will never do. Also for TTimer a good strategy is to keep it running at the minimum interval at all times, never stopping it. Just handle all the timings with same code, and use boolean states determining if they need to do something or not. ---------------------------------------------------------------------- Alzibiff · 2015-09-01 07:02 Quote from: User137 on August 30, 2015, 11:20:10 pmAnyway, for higher precision you need another timer (like some suggest Epictimer), or TThread with sleep(1). Without it a thread will max a cpu core, which is something TTimer will never do. Also for TTimer a good strategy is to keep it running at the minimum interval at all times, never stopping it. Just handle all the timings with same code, and use boolean states determining if they need to do something or not. Hmmm - that makes a lot of sense but sheesh - things have changed since my days of programming with the first version of Delphi! I like the TThread idea and although I have had a quick scan at the Wiki to try and find out how to implement a thread which I can just use exclusively for my timer code, first impressions are that I am going to need more help to implement it. Being totally honest I am also confused as to where  sleep(1)  comes into the equation - can I simply not start a thread and stop a thread when I want to use one? Alan ---------------------------------------------------------------------- balazsszekely · 2015-09-01 07:49 @Alzibiff Here you go: Code: [Select]unit uThreadTimer; {$mode objfpc}{$H+} interface uses   Classes, SysUtils, lclintf; type   TOnTimer = procedure(Sender: TObject) of object;   { TThreadTimer }   TThreadTimer = class(TThread)   private     FTime: QWORD;     FInterval: Cardinal;     FOnTimer: TOnTimer;     FEnabled: Boolean;     procedure DoOnTimer;   protected     procedure Execute; override;   public     property OnTimer: TOnTimer read FOnTimer write FOnTimer;     property Interval: Cardinal read FInterval write FInterval;     property Enabled: Boolean read FEnabled write FEnabled;     procedure StopTimer;     procedure StartTimer;     constructor Create(CreateSuspended: Boolean);     destructor Destroy; override;   end; implementation constructor TThreadTimer.Create(CreateSuspended: Boolean); begin   inherited Create(CreateSuspended);   FInterval := 1000;   FreeOnTerminate := True;   FEnabled := False; end; destructor TThreadTimer.Destroy; begin   //   inherited Destroy; end; procedure TThreadTimer.DoOnTimer; begin   if Assigned(FOnTimer) then     FOnTimer(Self); end; procedure TThreadTimer.Execute; begin   while not Terminated do   begin     Sleep(1);     if (GetTickCount64 - FTime > FInterval) and (FEnabled) then     begin       FTime := GetTickCount64;       Synchronize(@DoOnTimer);     end;   end; end; procedure TThreadTimer.StopTimer; begin   FEnabled := False; end; procedure TThreadTimer.StartTimer; begin   FTime := GetTickCount64;   FEnabled := True;   if Self.Suspended then     Start; end; end. Use it this way: Code: [Select]//...  TForm1 = class(TForm)     Button1: TButton;     procedure Button1Click(Sender: TObject);   private     procedure OnTimer(Sender: TObject);     { private declarations }   public     { public declarations }   end; var   Form1: TForm1; implementation uses uThreadTimer; {$R *.lfm} { TForm1 } var   ThreadTimer: TThreadTimer; procedure TForm1.Button1Click(Sender: TObject); begin   ThreadTimer := TThreadTimer.Create(True);   ThreadTimer.Interval := 1000;   ThreadTimer.OnTimer := @OnTimer;   ThreadTimer.StartTimer; end; procedure TForm1.OnTimer(Sender: TObject); begin   ShowMessage('ok'); end;              procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction); begin   ThreadTimer.StopTimer;   ThreadTimer.Terminate; end; //...  Please note: 1. The minimal interval is 15-20 ms. Under windows you can replace GetTickCount64 with timeGetTime(you should import this function, see microsoft documentation). This way you can get down to 5 ms. For even smaller intervals use QueryPerformanceCounter ~ 1 ms. Under linux, osx  see the clock_gettime function. 2. Synchronize will block your worker thread. If the code under Synchronize/OnTimer is time intensive, you should use queues or other asynchronous updating methods(fpc 3.1.1 needed for queues). ---------------------------------------------------------------------- derek.john.evans · 2015-09-01 07:56 That looks like the same thread style implementation as fpTimer.TFPTimer *EDIT* Actually, dont use fpTimer. It seems to use Now() for time. Code: [Select]function _GetTickCount: Cardinal; begin   Result := Cardinal(Trunc(Now * 24 * 60 * 60 * 1000)); end;  Maybe Now() is better than what I thought? ---------------------------------------------------------------------- balazsszekely · 2015-09-01 08:00 Quote@derek.john.evans That looks like the same thread style implementation as fpTimer.TFPTimer It's similar but not the same. Please read this: http://forum.lazarus.freepascal.org/index.php/topic,27245.0.html for more info. ---------------------------------------------------------------------- balazsszekely · 2015-09-01 08:05 Quote@derek.john.evans Maybe Now() is better than what I thought?No it's not. Definitely not suitable for threads. That's why fpTimer is a no go. ---------------------------------------------------------------------- Thaddy · 2015-09-01 08:25 Why don't you combine the solenoid with a floater? But anyway, for your purpose a simple basic timer would suffice, just check for greater or equal. A small floater can of course be made with two contacts, a piece of cork, a piece of rope  and a paperclip or two (basic boyscout stuff) BTW: A floater scales very well, from microscopic to industrial scale. Don't rely on timing alone.