Setting a pace

What can i use in C++ to set a pace ?

if i draw something on screen now it is displayed as fast as possible, there is no limit besides the hardware.
What if i want do draw something every second ?(lets say a digital clock)
Can i use the RTC ? or do i need some kind of function ?

1 Like

Try this :stuck_out_tongue:

1 Like

Thereā€™s four problems with that code:

  1. Itā€™s static, so you can only have one timer, otherwise the timers start interfering with each other.
  2. It returns int instead of bool.
  3. Some of the other types arenā€™t quite right (uint64_t and unsigned long should be uint32_t because getTime() returns uint32_t)
  4. It breaks when getTime overflows.

The first three problems are easy to solve:

// One timer per event
class Timer
{
public:
	// Not completely necessary, but it future-proofs
	// the code in case `getTime` is changed
	// to return a different (i.e. larger) type
	using TimeType = decltype(Pokitto::Core::getTime());
	using DurationType = decltype(TimeType(0) - TimeType(0));
private:
	bool firstRun = true;
	TimeType previousTime = 0;
	DurationType targetDuration = 0;
public:
	Timer(DurationType targetDuration)
		: targetDuration(targetDuration)
	{
	}
	
	DurationType getTargetDuration(void) const
	{
		return this->targetDuration;
	}
	
	void reset(void)
	{
		this->firstRun = true;
	}

	bool hasDurationElapsed(void)
	{
		if(this->firstRun)
		{
			this->previousTime = Pokitto::Core::getTime();
			this->firstRun = false;
			return false;
		}
				
		const auto now = Pokitto::Core::getTime();
		const auto duration = now - this->previousTime;
		if (duration >= this->targetDuration)
		{
			this->previousTime = Pokitto::Core::getTime();
			return true;
		}
		
		return false;
	}
};

The last issue is complicated to solve, but I think this might do the trick:

// One timer per event
class Timer
{
public:
	// Not completely necessary, but it future-proofs
	// the code in case `getTime` is changed
	// to return a different (i.e. larger) type
	using TimeType = decltype(Pokitto::Core::getTime());
	using DurationType = decltype(TimeType(0) - TimeType(0));
private:
	bool firstRun = true;
	TimeType previousTime = 0;
	DurationType targetDuration = 0;
public:
	Timer(DurationType targetDuration)
		: targetDuration(targetDuration)
	{
	}
	
	DurationType getTargetDuration(void) const
	{
		return this->targetDuration;
	}
	
	void reset(void)
	{
		this->firstRun = true;
	}

	bool hasDurationElapsed(void)
	{
		if(this->firstRun)
		{
			this->previousTime = Pokitto::Core::getTime();
			this->firstRun = false;
			return false;
		}
		
		const auto now = Pokitto::Core::getTime();		
		if(now < this->previousTime)
		{
			const auto timeUntilOverflow = TimeType(~0) - this->previousTime;
			if((now + timeUntilOverflow) > targetDuration)
			{
				this->previousTime = now;
				return true;
			}
		}
		else
		{		
			const auto duration = now - this->previousTime;
			if (duration >= this->targetDuration)
			{
				this->previousTime = Pokitto::Core::getTime();
				return true;
			}
		}
		
		return false;
	}
};
2 Likes

Thereā€™s also a way to make it so multiple events can share the same timer, but it involves an extra function.

By offloading the work into an update function thatā€™s called at the start of every loop and then storing the result internally, the timer can be used to synchronise multiple actions.
(Anything that needs to use the timer will need a reference or a pointer to the timer though, copying/passing by value wonā€™t work.)

e.g.

#include "Pokitto.h"
#include "Timer.h"

Pokitto::Core pokitto;

int main ()
{
	Timer timer1 = Timer(500);
	Timer timer2 = Timer(1000);
	pokitto.begin();
	while (pokitto.isRunning())
	{
		if (pokitto.update())
		{
			timer1.update();
			timer2.update();
		
			pokitto.display.print("Hello World!");
			
			if(timer1.hasDurationElapsed())
				pokitto.display.print("Half second");
				
			if(timer2.hasDurationElapsed())
				pokitto.display.print("Full second");
		}
	}
}

Hereā€™s the modified code samples.

(Bear in mind I havenā€™t tested either of these, or the code I posted before. If they donā€™t compile or donā€™t work, let me know and Iā€™ll look into it. Time is a hard thing to get right.)

The three-point fix:

// One timer per event
class Timer
{
public:
	// Not completely necessary, but it future-proofs
	// the code in case `getTime` is changed
	// to return a different (i.e. larger) type
	using TimeType = decltype(Pokitto::Core::getTime());
	using DurationType = decltype(TimeType(0) - TimeType(0));
private:
	bool firstRun = true;
	bool hasElapsed = false;
	TimeType previousTime = 0;
	DurationType targetDuration = 0;
public:
	Timer(DurationType targetDuration)
		: targetDuration(targetDuration)
	{
	}
	
	DurationType getTargetDuration(void) const
	{
		return this->targetDuration;
	}
	
	void reset(void)
	{
		this->firstRun = true;
	}

	void update(void)
	{
		if(this->firstRun)
		{
			this->previousTime = Pokitto::Core::getTime();
			this->firstRun = false;
			this->hasElapsed = false;	
		}
				
		const auto now = Pokitto::Core::getTime();
		const auto duration = now - this->previousTime;
		if (duration >= this->targetDuration)
		{
			this->previousTime = Pokitto::Core::getTime();
			this->hasElapsed = true;
		}
		
		this->hasElapsed = false;
	}

	bool hasDurationElapsed(void) const
	{
		return this->hasElapsed;
	}
};

The four-point fix.

// One timer per event
class Timer
{
public:
	// Not completely necessary, but it future-proofs
	// the code in case `getTime` is changed
	// to return a different (i.e. larger) type
	using TimeType = decltype(Pokitto::Core::getTime());
	using DurationType = decltype(TimeType(0) - TimeType(0));
private:
	bool firstRun = true;
	bool hasElapsed = false;
	TimeType previousTime = 0;
	DurationType targetDuration = 0;
public:
	Timer(DurationType targetDuration)
		: targetDuration(targetDuration)
	{
	}
	
	DurationType getTargetDuration(void) const
	{
		return this->targetDuration;
	}
	
	void reset(void)
	{
		this->firstRun = true;
	}

	void update(void)
	{		
		if(this->firstRun)
		{
			this->previousTime = Pokitto::Core::getTime();
			this->firstRun = false;
			this->hasElapsed = false;
		}
		
		const auto now = Pokitto::Core::getTime();		
		if(now < this->previousTime)
		{
			const auto timeUntilOverflow = TimeType(~0) - this->previousTime;
			if((now + timeUntilOverflow) > targetDuration)
			{
				this->previousTime = now;
				this->hasElapsed = true;
			}
		}
		else
		{		
			const auto duration = now - this->previousTime;
			if (duration >= this->targetDuration)
			{
				this->previousTime = Pokitto::Core::getTime();
				this->hasElapsed = true;
			}
		}
		
		this->hasElapsed = false;
	}

	bool hasDurationElapsed(void) const
	{
		return this->hasElapsed;
	}
};

Itā€™s also possible to get rid of the firstRun logic by moving it into a begin function, but it would make reset behave differently and begin would have to be called at the start as well.

There are other ways to handle it as well, like a delta time based timer, but thatā€™s going to start getting more complicated than providing a single class. (If start discussing delta timing my comments will end up being 2-4 times larger.)

Thank you Pharap, i am working on a simple racer as a way to learn the code.
But i must say this looks quite daunting.

So lets see if i got this right :

  • i can add timer1.update(); to the main.cpp file and trigger code on that using the hasDurationElapsed
  • i add a Timer.h that includes either your 3-point fix or the 4-point fix.

I will try it out, and see if i can get it working. :smiley:

Time can be a pretty hard thing to deal with in code.

Though thereā€™s a lot of much scarier out there (at least some of which was written by me).

Pretty much.

If youā€™re using the second version then just call update near the start of your if(pokitto.update()) block.

Iā€™m more confident in the 3-point fix than the 4-point fix but the likelihood of the timer overflowing is pretty low (youā€™d need to have the Pokitto running for about 49.71 days before the timer rolls over) so you probably wonā€™t need the 4-point version.

Try my example code first to make sure it works before adding it to anything complicated.
Or maybe something like:

#include "Pokitto.h"
#include "Timer.h"

Pokitto::Core pokitto;

int main ()
{
	Timer timer1 = Timer(500);
	Timer timer2 = Timer(1000);
	
	int counter1 =0;
	int counter2 = 0;
	
	pokitto.begin();
	while (pokitto.isRunning())
	{
		if (pokitto.update())
		{
			timer1.update();
			timer2.update();
		
			pokitto.display.print("Hello World!");
			pokitto.display.print(counter1);
			pokitto.display.print(counter2);
			
			if(timer1.hasDurationElapsed())
				++counter1;
				
			if(timer2.hasDurationElapsed())
				++counter2;
		}
	}
}

Since it might be a bit easier to see.

One more thing, in case you donā€™t already know this - if you put it in a .h file, make sure to put #pragma once at the top.

ok thanks, i will let you know if it works out :grin:

1 Like

Well, it did not work. But i do understand what your intention was and the Gettime does give me a steady number to work with. So i am working from that point and see where it gets me. :smile:

How exactly did it not work?

  • Did it not compile properly?
  • Did it compile but not do what it was supposed to do?
  • Which version were you using?
  • How were you using it (i.e. what was your code like)?

Iā€™ll probably be able to either fix it or come up with something more suitable to your needs if I have more details.

I am using the mbed online compiler, i added a .h file for the timer and placed the #pragma on top.
Then i tried your example code to see if that would compile(inside the main.cpp). This caused a buch of errors, i couldnt say what all those errors meant as i dont fully understand the code.

i got :
Error: Data member initializer is not allowed in ā€œTimer.hā€, Line: 11, Col: 20
Error: Identifier ā€œ_runningā€ is undefined in ā€œPokittoLib/mbed-pokitto/common/Timer.cppā€, Line: 31, Col: 11
and a bunch more like that.

But it could easily be that i build the structure incorrect, i am still new to C++ (and the compiler)

You did gave me a idea about setting the pace using the gettime() so i am working that out for myself, just to learn and understand the code.

It looks like the code is getting confused between that Timer.h and a Timer.h thatā€™s already in the Pokitto library.

I just tried compiling on Embitz and ran into the same issue, so it looks like thereā€™s probably a using namespace mbed; somewhere in the library thatā€™s upsetting things, among other possible issues.

The easiest solution to the name collision is to rename the file and the class. Maybe something like SimpleTimer.h and class SimpleTimer.

When testing I realised I missed an else anyway so Iā€™ll add that back in.

If the ā€œdata member initialiser is not allowedā€ error comes up again, I think I might know whatā€™s wrong. (Especially if youā€™re also getting an error about using or decltype, in which case Iā€™ll definitely know whatā€™s wrong and Iā€™ll have to rewrite the code again.)
If itā€™s the problem I think it is, itā€™s something that wouldnā€™t be an issue with Embitz, just the online compiler.
(In case youā€™re curious, I think the online compiler doesnā€™t support C++11, and Iā€™m using some C++11 techniques in the code.)


I didnā€™t notice the mbed::Timer class already included with the Pokitto lib. That might also be suitable, but it looks like itā€™s a bit heavier than the one I wrote, and has more features.


3-point fix version 2 (actually tested - it compiles and works)

// One timer per event
class SimpleTimer
{
public:
	// Not completely necessary, but it future-proofs
	// the code in case `getTime` is changed
	// to return a different (i.e. larger) type
	using TimeType = decltype(Pokitto::Core::getTime());
	using DurationType = decltype(TimeType(0) - TimeType(0));
private:
	bool firstRun = true;
	bool hasElapsed = false;
	TimeType previousTime = 0;
	DurationType targetDuration = 0;
public:
	SimpleTimer(DurationType targetDuration)
		: targetDuration(targetDuration)
	{
	}

	DurationType getTargetDuration(void) const
	{
		return this->targetDuration;
	}

	void reset(void)
	{
		this->firstRun = true;
	}

	void update(void)
	{
		if(this->firstRun)
		{
			this->previousTime = Pokitto::Core::getTime();
			this->firstRun = false;
			this->hasElapsed = false;
		}

		const auto now = Pokitto::Core::getTime();
		const auto duration = now - this->previousTime;
		if (duration >= this->targetDuration)
		{
			this->previousTime = Pokitto::Core::getTime();
			this->hasElapsed = true;
		}
		else
		{
			this->hasElapsed = false;
		}
	}

	bool hasDurationElapsed(void) const
	{
		return this->hasElapsed;
	}
};

Sample code:

#include "Pokitto.h"
#include "SimpleTimer.h"

Pokitto::Core pokitto;

int main ()
{
	SimpleTimer timer1 = SimpleTimer(500);
	SimpleTimer timer2 = SimpleTimer(1000);

	int counter1 = 0;
	int counter2 = 0;

	pokitto.begin();
	while (pokitto.isRunning())
	{
		if (pokitto.update())
		{
			timer1.update();
			timer2.update();

			pokitto.display.println("Hello World!");
			pokitto.display.println(counter1);
			pokitto.display.println(counter2);

			if(timer1.hasDurationElapsed())
				++counter1;

			if(timer2.hasDurationElapsed())
				++counter2;
		}
	}
}
1 Like

Nice, i already had the feeling that timer was used somewhere so its good to know that was the case.

I will see if i can use it as it is now. :smile:

1 Like