Arduino Events

Hi all. I just downloaded KurtE's fork of the core libs to give the Serial changes a try and noticed that eventResponder was included. So I figured I would give it a shot and test the example that Paul posted. As expected it worked no issue. So I started to looking at what's its doing and of modifying the code. So if I can ask a few questions:
  1. In the loop the call to myevent.triggerEvent(); is starting the event process, turning it on?
  2. The "doSomething" function has a call to mytimer.begin(550, mytimerevent);, not 100% sure what that does - sets the trigger for every 550 ms or starts it after 550ms?
  3. The mytimer2.beginRepeating(750, mytimerevent2) sets the trigger to go off every 750 ms?

Ok so with that said if I change the example to add another repeating timer (think I did this right):
Code:
#include <EventResponder.h>

EventResponder myevent;
EventResponder mytimerevent;
EventResponder mytimerevent2;
EventResponder mytimerevent3;
MillisTimer mytimer;
MillisTimer mytimer3;
MillisTimer mytimer2;

void setup() {
  while (!Serial) ;
  Serial.println("EventResponder test");
  myevent.attach(dosomething);
  mytimerevent.attach(dotimer);
  mytimerevent2.attach(dotimer2);
  mytimerevent3.attach(dotimer3);
  mytimer2.beginRepeating(750, mytimerevent2);
  mytimer3.beginRepeating(250, mytimerevent3);
}

void loop() {
  Serial.println("loop trigger dosomething");
  myevent.triggerEvent();
  delay(2000);
}

void dosomething(EventResponderRef event)
{
  Serial.println("dosomething event");
  mytimer.begin(550, mytimerevent);
}

void dotimer(EventResponderRef event)
{
  Serial.println("oneshot timer event");
}

void dotimer2(EventResponderRef event)
{
  Serial.println("  repeating timer event");
}

void dotimer3(EventResponderRef event)
{
  Serial.println("    what a wonderful world");
}

It never executes dotimer2. It appears that the earlier trigger resets all timers to zero, I never see "repeating timer event":
Code:
loop trigger dosomething
dosomething event
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
loop trigger dosomething
dosomething event
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world

Also if I noticed if I remove the delay from the loop everything hangs and I have to reset to get the ide/sm working again. Know I have to be missing something. Help.

thanks
Mike
 
Last edited:
my guess: Without that delay the loop is running 10's or 100's of thousands of times per second. The serial.print's are overwhelming the serial output speed and the triggerEvent is acting the same.

Look into elapsedMillis() and only do those things when some time has elapsed - it doesn't do delay() - just doe it to check and reset the elapsedMillis value to limit entry.
 
Got it. Also beginning to understand a little more did a quick test with out the one shot and just used the repeating. So if I have two separate repeating events only the one with the earliest time gets triggered but not the later one. So you can then only have one repeating event.
 
Not sure about "It never executes dotimer2." - KurtE's copy may not be the latest release with it working? Check the Github PJRC source or try again when it comes out in the 1.38b3 if answers don't show up. I scanned the prior posts - that is based on a Paul posted sample - there may be something about overlapping events and priority?
 
Thanks defragster. Think you are right about that. Think I will wait for beta#3 before starting to play with this one.
 
Hi defragster and mjs513 - I believe my code (in this branch) is now only slightly out of date. That is I have not picked up Frank's change Paul merged in a couple of days ago and these had to do with Interval timer... My master branch is in sync... May try to remember how to rebase my change on github, such that the branch only has those changes...

But back to issue. I do think it might be beneficial to figure out your hang. As my SPI update which added the spi transfer buffer SPI.transfer(buf, retbuf, cnt) also added a version in with Events...
SPI.transfer(buf, retbuf, cnt, eventResponder) which has been pulled in for the next beta :D and with it I was running into a hang as well, that was solved by adding in a call to yield();
At first I thought it was timing, as delay(1) solved it, but then delayMicroseconds(1000) did not...

Edit: just did a rebase operation on that branch... hopefully worked correctly - Also should point out that still not sure if these changes will actually make it in to the main line or not... And if so if the allocation of added buffer space for reads and/or writes will be done by separate methods like I have it or if, it gets added to the begin method.
 
Last edited:
I downloaded Paul's updates to the eventresponder (.h, .cpp and elapsedMillis) and also added a couple of yields. When it first starts it looks like it is doing the right thing:
Code:
EventResponder test
loop trigger dosomething
dosomething event
oneshot timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
loop trigger dosomething
dosomething event
    what a wonderful world
oneshot timer event
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
loop trigger dosomething
dosomething event
    what a wonderful world
oneshot timer event
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
loop trigger dosomething
dosomething event
    what a wonderful world
oneshot timer event
    what a wonderful world
    what a wonderful world
    what a wonderful world
    what a wonderful world
Then it losses the longer duration timer event. Here is the code if you want to give it a try:
Code:
#include <EventResponder.h>

EventResponder myevent;
EventResponder mytimerevent;
EventResponder mytimerevent2;
EventResponder mytimerevent3;
MillisTimer mytimer;
MillisTimer mytimer3;
MillisTimer mytimer2;

void setup() {
  while (!Serial) ;
  Serial.println("EventResponder test");
  myevent.attach(dosomething);
  mytimerevent.attach(dotimer);
  mytimerevent2.attach(dotimer2);
  mytimerevent3.attach(dotimer3);
  mytimer2.beginRepeating(750, mytimerevent2);
  mytimer3.beginRepeating(550, mytimerevent3);
}

void loop() {
  Serial.println("loop trigger dosomething");
  myevent.triggerEvent();
  delay(8000);
}

void dosomething(EventResponderRef event)
{
  Serial.println("dosomething event");
  mytimer.begin(550, mytimerevent);
}

void dotimer(EventResponderRef event)
{
  Serial.println("oneshot timer event");
}

void dotimer2(EventResponderRef event)
{
  Serial.println("  repeating timer event");
  yield();
}

void dotimer3(EventResponderRef event)
{
  Serial.println("    what a wonderful world");
  yield();
}
 
@paul, @mjs513 @defragster - I believe I know the first issue, which I will show in current millisTimer code:
Code:
void MillisTimer::beginRepeating(unsigned long milliseconds, EventResponderRef event)
{
	if (_state != TimerOff) end();
	if (!milliseconds) return;
	_event = &event;
	_ms = milliseconds;
	_reload = milliseconds;
	addToWaitingList();
}

void MillisTimer::addToWaitingList()
{
	_prev = nullptr;
	bool irq = disableTimerInterrupt();
	[COLOR="#FF0000"]_next = listWaiting;
	listWaiting = this; // TODO: use STREX to avoid interrupt disable[/COLOR]
	enableTimerInterrupt(irq);
}

void MillisTimer::addToActiveList() // only called by runFromTimer()
{
	if (listActive == nullptr) {
		// list is empty, easy case
...
The problem is that when you call the beginRepeating twice, the code both times just puts the timer object into a static member listWaiting, so last one wins...
This is assuming that the timer interrupt is not triggered in between the two calls. There may be other issues like this, still looking...

Edit: Looks like the runFromTimer will run into similar issue as both will want to trigger and again add them self back in using addToWaitingList

EDIT2: wrong on adding to list as using next pointer as a queue...
 
Last edited:
Actually the more I look at the runFromTimer code, I think it may need some tweaking to properly handle multiple objects.
Code:
void MillisTimer::runFromTimer()
{
	MillisTimer *timer = listActive;
	while (timer) {
		if (timer->_ms > 0) {
			timer->_ms--;
			break;
		} else {
			MillisTimer *next = timer->_next;
			if (next) next->_prev = nullptr;
			listActive = next;
			timer->_state = TimerOff;
			EventResponderRef event = *(timer->_event);
			event.triggerEvent(0, timer);
			if (timer->_reload) {
				timer->_ms = timer->_reload;
				timer->addToActiveList();
			}
			timer = listActive;
		}
	}
	bool irq = disableTimerInterrupt();
	MillisTimer *waiting = listWaiting;
	listWaiting = nullptr; // TODO: use STREX to avoid interrupt disable
	enableTimerInterrupt(irq);
	while (waiting) {
		MillisTimer *next = waiting->_next;
		waiting->addToActiveList();
		waiting = next;
	}
}
That is the first while(timer) loop when you have two objects with in this example _ms will have 750 in it, the first item the value >0 so you decrement it, but do nothing with 2nd item.

I think the 2nd part has to do with adding items to the list... And I was wrong on the first part as you are using the next pointer to hold the added items in a queue... So probably again issue with only first item being decremented...

Edit+1 ;) Actually looks like the code already decremented others in the list based off of the item before it in the list... Still checking...

Edit ++++
I understand more... (I think) - More of issues with the function:
Code:
void MillisTimer::addToActiveList() // only called by runFromTimer()
{
	if (listActive == nullptr) {
		// list is empty, easy case
		_next = nullptr;
		_prev = nullptr;
		listActive = this;
	} else if (_ms < listActive->_ms) {
		// this timer triggers before any on the list
		_next = listActive;
		_prev = nullptr;
		listActive->_prev = this;
		listActive = this;
	} else {
		// add this timer somewhere after the first already on the list
		MillisTimer *timer = listActive;
		while (timer->_next) {
			_ms -= timer->_ms;
			timer = timer->_next;
			if (_ms < timer->_ms) {
				// found the right place in the middle of list
				_next = timer;
				_prev = timer->_prev;
				timer->_prev = this;
				_prev->_next = this;
				_state = TimerActive;
				return;
			}
		}
		// add this time at the end of the list
		_ms -= timer->_ms;
		_next = nullptr;
		_prev = timer;
		timer->_next = this;
	}
	_state = TimerActive;
}
That is the code is trying to setup something like: Suppose you had two timer values one at 500 and another at 750, What you want is the list like:
listActive -> T1(500) -> T2(250) -> nullptr....
So you count t1 down and then it is removed and the T2 counts down so it counts 500+250=750...
This works if you first add the 500 handled by (listActive == nullptr) test and then add the 750 as it will not have timer->next so will add to end and will decrement it's count by the 500...

However suppose you add it in the 750 one first followed by 500. The 750 is added by null ptr, now you call with 500, it is caught by the case:
(_ms < listActive->_ms) which adds the new 500 item to start of the list, but it does not decrement the 750 item so it is still in at 750...

Also same case if you have more than 2 items and the item is added in middle, it does not decrement the items after it by it remaining time.

Got to run, will look into fixing later today if someone does not beat me to it.
 
Last edited:
Hopefully I fixed the problem on my unified-serial branch of my fork.
The fix went in with the commit: https://github.com/KurtE/cores/commit/60b21cbdc76e17fdc2a7ec98e6aaef97eba8c1ce

The issue was, when a repeating event is triggered, it adds the item back onto the active list. It properly finds the correct spot to place it. Suppose right after things were setup, you had the list like:
MT3(250)->MT(300)->MT2(200)

So for the next 250 tics nothing happens, but then it triggers... It then resets to 250 Calls the add again with MT(300)->MT2(200)
It finds that it wants to add the item to start of the list and ends up again like: MT3(250)->MT(300)->MT2(200)

So the ones after it get starved. What it needed to do was to decrement the next items time by it's remaining time, so the list should be: at this next time:
MT3(250)->MT(50)->MT2(200)

Same problem with the next add where MT3(250) it needed to all the add again with MT(50)->MT2(200)
So it needed to update to have MT(50) -> MT3(200) -> mt2(0) (Actually in this case it will probably end up with MT3 at end with 0 and MT2(200), but ....

@mjs513 - maybe see if it works for you .

@paul - I can create a new branch for this if you would like and issue it as it's own Pull Request or can grab from the commit
 
@KurtE, @paul, @defragster

Looks like you broke the code, all timer events are firing with the example sketch above less the yields. Here is a sample output:
Code:
EventResponder test
loop trigger dosomething
dosomething event
oneshot timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
loop trigger dosomething
dosomething event
  repeating timer event
    what a wonderful world
oneshot timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
loop trigger dosomething
dosomething event
  repeating timer event
    what a wonderful world
oneshot timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
loop trigger dosomething
dosomething event
  repeating timer event
    what a wonderful world
oneshot timer event
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
    what a wonderful world
  repeating timer event
    what a wonderful world
loop trigger dosomething
dosomething event
  repeating timer event
    what a wonderful world
....

Changing and reversing the trigger
Code:
mytimer2.beginRepeating(750, mytimerevent2);
  mytimer3.beginRepeating(950, mytimerevent3);
I'm not sure is triggering properly - would have thought the "what a wonderful world" would show first then repeating event timer, but that may be me getting confused - OUTPUT:
Code:
EventResponder test
loop trigger dosomething
dosomething event
oneshot timer event
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
    what a wonderful world
  repeating timer event
  repeating timer event
    what a wonderful world

for the heck of it I add a fourth timer, the devil that I am, and it works:
Code:
EventResponder test
loop trigger dosomething
dosomething event
oneshot timer event
    Teensy 3.5 rocks!
  repeating timer event
    what a wonderful world
    Teensy 3.5 rocks!
  repeating timer event
    what a wonderful world
    Teensy 3.5 rocks!
  repeating timer event
    Teensy 3.5 rocks!
    what a wonderful world
  repeating timer event
    Teensy 3.5 rocks!
    what a wonderful world
  repeating timer event
    Teensy 3.5 rocks!
  repeating timer event
    what a wonderful world
    Teensy 3.5 rocks!
  repeating timer event
    Teensy 3.5 rocks!
    what a wonderful world
    Teensy 3.5 rocks!
  repeating timer event
    what a wonderful world
    Teensy 3.5 rocks!
  repeating timer event
loop trigger dosomething

Heres is that code:
Code:
#include <EventResponder.h>

EventResponder myevent;
EventResponder mytimerevent;
EventResponder mytimerevent2;
EventResponder mytimerevent3;
EventResponder mytimerevent4;
MillisTimer mytimer;
MillisTimer mytimer3;
MillisTimer mytimer2;
MillisTimer mytimer4;

void setup() {
  while (!Serial) ;
  Serial.println("EventResponder test");
  myevent.attach(dosomething);
  mytimerevent.attach(dotimer);
  mytimerevent2.attach(dotimer2);
  mytimerevent3.attach(dotimer3);
  mytimerevent4.attach(dotimer4);
  mytimer2.beginRepeating(850, mytimerevent2);
  mytimer3.beginRepeating(1050, mytimerevent3);
  mytimer4.beginRepeating(750, mytimerevent4);

}

void loop() {
  Serial.println("loop trigger dosomething");
  myevent.triggerEvent();
  delay(8000);
}

void dosomething(EventResponderRef event)
{
  Serial.println("dosomething event");
  mytimer.begin(550, mytimerevent);
}

void dotimer(EventResponderRef event)
{
  Serial.println("oneshot timer event");
}

void dotimer2(EventResponderRef event)
{
  Serial.println("  repeating timer event");
}

void dotimer3(EventResponderRef event)
{
  Serial.println("    what a wonderful world");
}

void dotimer4(EventResponderRef event)
{
  Serial.println("    Teensy 3.5 rocks!");
  yield();
}

Thanks
Mike
 
I know that this is early in the process but I decided to see what would happen if I combined the above example with the TeensyThread library test example. It would not compile with the following error:
Code:
C:\Users\CYBERP~1\AppData\Local\Temp\arduino_build_56842/core\core.a(EventResponder.cpp.o): In function `systick_isr':

C:\Local Programs\arduino-1.8.3-KE\hardware\teensy\avr\cores\teensy3/EventResponder.cpp:324: multiple definition of `systick_isr'

C:\Users\CYBERP~1\AppData\Local\Temp\arduino_build_56842\libraries\TeensyThreads\TeensyThreads.cpp.o:C:\Users\CyberPalin\Documents\Arduino\libraries\TeensyThreads/TeensyThreads.cpp:196: first defined here

c:/local programs/arduino-1.8.3-ke/hardware/tools/arm/bin/../lib/gcc/arm-none-eabi/5.4.1/../../../../arm-none-eabi/bin/ld.exe: Disabling relaxation: it will not work with multiple definitions

collect2.exe: error: ld returned 1 exit status

Error compiling for board Teensy 3.5.

This may be a problem if you use eventresponded in one of the other system libraries like SPI and try using SPI from within a thread. I know @tonton81 uses spi with teensythreads in his applications.

Thanks
Mike
 
Hi Mike,

Not sure what exactly is going on in your example. Would probably have to map out all of the timings. Also as about calling yield(). My guess is it is not a good idea as you are in an interrupt handler at the time...

To debug some of this earlier my version (not the one I uploaded) I added a member: dumpTimerList() which dumped the address of each item in the list plus timer value...

Actually to test this more I would maybe have a tendency to instrument your code more, like maybe something like:
Code:
#define EVENT2_TIME 850
ulong32_t event2_expected_time;
...
mytimer2.beginRepeating( EVENT2_TIME, mytimerevent2);
t event2_expected_time = millis() +  EVENT2_TIME;
void dotimer2(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T2(%d)\n", t, (t >= event2_expected_time)? t - event2_expected_time : event2_expected_time - t);
  event2_expected_time  += EVENT2_TIME;
}

This is typed on the fly so probably issues, but instead of random strings, print out time, which timer and how far it is off from what we expected...

I may do something like this if no one beats me to it,

As for using this with Thread library, does not surprise me as both are needing to get to the low level timer. As for SPI, currently unless you use the new API with event object, all SPI interfaces are Synchronous and wait until the operation is done before completing... Obviously it might be nice for other threads to be able to do stuff as this code is waiting for things like N bytes being fully transferred, but that is independent of this. However as for the new Async version, probably lots to test especially for threads and the like.
 
Hi Kurt.

Adding the timings was on my todo list for tomorrow. Had too many other things to do today and getting tired now :).

As for the teensythread library, SPI and events was just a guess on my part that there may be other conflicts when this gets implemented. Might say early warning - something to think about.

Thanks for taking the time on this Kurt.
Mike

EDIT: Need to take my own advice. Eyes are closing when I did this so take with grain of salt. One thing I noticed is that when I got rid of the serial.println things started behaving nicer :) heres the output:
Code:
EventResponder test
loop trigger dosomething
dosomething event
4117 - T0(2)
4317 - T4(2)
4417 - T2(2)
4617 - T3(2)
5067 - T4(2)
5267 - T2(2)
5667 - T3(2)
5817 - T4(2)
6117 - T2(2)
6567 - T4(2)
6717 - T3(2)
6967 - T2(2)
7317 - T4(2)
7767 - T3(2)
7817 - T2(2)
8067 - T4(2)
8667 - T2(2)
8817 - T3(2)
8817 - T4(2)
9517 - T2(2)
9567 - T4(2)
9867 - T3(2)
10317 - T4(2)
10367 - T2(2)
10917 - T3(2)
11067 - T4(2)
11217 - T2(2)
loop trigger dosomething
dosomething event
11817 - T4(2)
11967 - T3(2)
12067 - T2(2)
12117 - T0(7452)
12567 - T4(2)
12917 - T2(2)
13017 - T3(2)
13317 - T4(2)
13767 - T2(2)
14067 - T3(2)
14067 - T4(2)
14617 - T2(2)
14817 - T4(2)
15117 - T3(2)
15467 - T2(2)
15567 - T4(2)
16167 - T3(2)
16317 - T2(2)
16317 - T4(2)
17067 - T4(2)
17167 - T2(2)
17217 - T3(2)
17817 - T4(2)
18017 - T2(2)
18267 - T3(2)
18567 - T4(2)
18867 - T2(2)
19317 - T3(2)
19317 - T4(2)

Heres the code - give a try:
Code:
#include <EventResponder.h>

#define EVENT2_TIME 850
#define EVENT3_TIME 1050
#define EVENT4_TIME 750
#define EVENT0_TIME 550

uint32_t event0_expected_time;
uint32_t event2_expected_time;
uint32_t event3_expected_time;
uint32_t event4_expected_time;

EventResponder myevent;
EventResponder mytimerevent;
EventResponder mytimerevent2;
EventResponder mytimerevent3;
EventResponder mytimerevent4;
MillisTimer mytimer;
MillisTimer mytimer3;
MillisTimer mytimer2;
MillisTimer mytimer4;


void setup() {
  while (!Serial) ;
  Serial.println("EventResponder test");
  
  myevent.attach(dosomething);
  mytimerevent.attach(dotimer);
  mytimerevent2.attach(dotimer2);
  mytimerevent3.attach(dotimer3);
  mytimerevent4.attach(dotimer4);
  
  event0_expected_time = millis() +  EVENT0_TIME;
  mytimer2.beginRepeating( EVENT2_TIME, mytimerevent2);
  event2_expected_time = millis() +  EVENT2_TIME;
  mytimer3.beginRepeating(EVENT3_TIME, mytimerevent3);
  event3_expected_time = millis() +  EVENT3_TIME;
  mytimer4.beginRepeating(EVENT4_TIME, mytimerevent4);
  event4_expected_time = millis() +  EVENT4_TIME;

}

void loop() {
  Serial.println("loop trigger dosomething");
  myevent.triggerEvent();
  delay(8000);
}

void dosomething(EventResponderRef event)
{
  Serial.println("dosomething event");
  mytimer.begin(EVENT0_TIME, mytimerevent);
}

void dotimer(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T0(%d)\n", t, (t >= event0_expected_time)? t - event0_expected_time : event0_expected_time - t);
  event0_expected_time  += EVENT0_TIME;}

void dotimer3(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T3(%d)\n", t, (t >= event3_expected_time)? t - event3_expected_time : event3_expected_time - t);
  event3_expected_time  += EVENT3_TIME;
}

void dotimer4(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T4(%d)\n", t, (t >= event4_expected_time)? t - event4_expected_time : event4_expected_time - t);
  event4_expected_time  += EVENT4_TIME;
}


void dotimer2(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T2(%d)\n", t, (t >= event2_expected_time)? t - event2_expected_time : event2_expected_time - t);
  event2_expected_time  += EVENT2_TIME;
}
 
Last edited:
Looks good!

The T0 time is probably not set up correctly which is why you see a value like: 12117 - T0(7452)

Since it is a do once type of timer, you probably need to change:
Code:
void dosomething(EventResponderRef event)
{
  Serial.println("dosomething event");
  mytimer.begin(EVENT0_TIME, mytimerevent);
[COLOR="#FF0000"]  event0_expected_time = millis() +  EVENT0_TIME;[/COLOR]
}

As the expected time depends on when we call the begin method.

I created a Pull request: https://github.com/PaulStoffregen/cores/pull/252

I think it did solve most of the issues, that I saw,
 
Last edited:
Fixed the start timer0 as you showed worked fine. Ran a couple more variations including making two timers trigger at the same time and it worked fine no problem. Did try out seeing when the sketch would hang by changing the delay. With no delay it hangs, with a 1 ms delay sketch runs but nothing gets trigger, well actually something gets trigger but the numbers totally crazy, but wasn't really expecting to see anything trigger.

Thanks
Mike
 
Thanks Mike,

@mjs513 - @Paul - Does the explanations like the below help... Or would you prefer to either debug yourself and/or a simple I think I fixed it, try it again...

Now for the other issues with no delay and 1ms delay....

I think they are more a robustness/verification issue, with a couple of different issues. First probably swamping USB Serial port. As the code reduces without thinking about events to something like:
Code:
void loop() {
  Serial.println("loop trigger dosomething");
  Serial.println("dosomething event");
}

----------------

Now if we are thinking about events, we have the issue of the link lists integrity: Again if we boil the code down and have now delays, we end up with something like:
Code:
  mytimer.begin(550, mytimerevent);
  mytimer.begin(550, mytimerevent);
...
The problem here is after the first call: We have the waiting queue: listWaiting -> mytimer -> nullptr
Then we call the 2nd time: our begin function tries to detect this:
Code:
void MillisTimer::begin(unsigned long milliseconds, EventResponderRef event)
{
	if (_state != TimerOff) end();
	if (!milliseconds) return;
	_event = &event;
	_ms = milliseconds;
	_reload = 0;
	addToWaitingList();
}
With the first test... Problem was the addToWaitingList did not update the state so we end up with circular loop: listWaiting -> mytimer -> mytimer ->...

Hopefully easy fix: Set the state in addToWaitingList like:
Code:
void MillisTimer::addToWaitingList()
{
	_prev = nullptr;
	bool irq = disableTimerInterrupt();
	_next = listWaiting;
	listWaiting = this; // TODO: use STREX to avoid interrupt disable
[COLOR="#FF0000"]	_state = TimerWaiting;[/COLOR]
	enableTimerInterrupt(irq);
}

-----

Next issue: With short delay... My guess anything less than about 550...

So again the first call works like before: and you have listwaiting -> myTimer -> nullptr .

So with the delay the millis timer happens and the item is removed from the waiting list and put on the active list. Now if you have your other events that we added with values like T2=850, T3=1050 and T4=750... And for this conversation assume no timers have elapsed, you would have something like:
listActive -> mytimer(550) -> T4(200) -> T2(100) -> T3(200)

So in state != TimerOff so it calls end(), which removes it from the Active list:
Code:
void MillisTimer::end()
{
	bool irq = disableTimerInterrupt();
	TimerStateType s = _state;
	if (s == TimerActive) {
		if (_next) {
			_next->_prev = _prev;
		}
		if (_prev) {
			_prev->_next = _next;
		} else {
			listActive = _next;
		}
		_state = TimerOff;
	} else if (s == TimerWaiting) {
		if (listWaiting == this) {
			listWaiting = _next;
		} else {
			MillisTimer *timer = listWaiting;
			while (timer) {
				if (timer->_next == this) {
					timer->_next = _next;
					break;
				}
				timer = timer->_next;
			}
		}
		_state = TimerOff;
	}
	enableTimerInterrupt(irq);
}
So I think it properly can remove the item from the list, BUT timings are screwed up:
We have something like: listActive -> T4(200) -> T2(100) -> T3(200)
So it will fire too early. As our 550 (minus how many tics happened) was not taken into account. So above code should probably be updated like:
Code:
void MillisTimer::end()
{
	bool irq = disableTimerInterrupt();
	TimerStateType s = _state;
	if (s == TimerActive) {
		if (_next) {
			_next->_prev = _prev;
[COLOR="#FF0000"]                        _next->_ms += _ms;   // add in the rest of our timing to next entry...[/COLOR]
		}
		if (_prev) {
			_prev->_next = _next;
		} else {
			listActive = _next;
		}
		_state = TimerOff;
	} else if (s == TimerWaiting) {
...
I will try these changes out locally and if it does not look like it completely screwed things up, will upload to the patch branch I created.
 
Update: made the above changes and I believe they are working. So update Pull request: https://github.com/PaulStoffregen/cores/pull/252

I tried with no wait and with delay 250... No wait hung as expected due to Serial messages, so I commented them out and it appears to work:
Code:
#include <EventResponder.h>

#define EVENT2_TIME 850
#define EVENT3_TIME 1050
#define EVENT4_TIME 750
#define EVENT0_TIME 550

uint32_t event0_expected_time;
uint32_t event2_expected_time;
uint32_t event3_expected_time;
uint32_t event4_expected_time;

EventResponder myevent;
EventResponder mytimerevent;
EventResponder mytimerevent2;
EventResponder mytimerevent3;
EventResponder mytimerevent4;
MillisTimer mytimer;
MillisTimer mytimer3;
MillisTimer mytimer2;
MillisTimer mytimer4;


void setup() {
  while (!Serial) ;
  Serial.println("EventResponder test");
  
  myevent.attach(dosomething);
  mytimerevent.attach(dotimer);
  mytimerevent2.attach(dotimer2);
  mytimerevent3.attach(dotimer3);
  mytimerevent4.attach(dotimer4);
  
  event0_expected_time = millis() +  EVENT0_TIME;
  mytimer2.beginRepeating( EVENT2_TIME, mytimerevent2);
  event2_expected_time = millis() +  EVENT2_TIME;
  mytimer3.beginRepeating(EVENT3_TIME, mytimerevent3);
  event3_expected_time = millis() +  EVENT3_TIME;
  mytimer4.beginRepeating(EVENT4_TIME, mytimerevent4);
  event4_expected_time = millis() +  EVENT4_TIME;

}

void loop() {
//  Serial.println("loop trigger dosomething");
  myevent.triggerEvent();
//  delay(250);
}

void dosomething(EventResponderRef event)
{
//  Serial.println("dosomething event");
  mytimer.begin(EVENT0_TIME, mytimerevent);
  event0_expected_time = millis() +  EVENT0_TIME;
}

void dotimer(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T0(%d)\n", t, (t >= event0_expected_time)? t - event0_expected_time : event0_expected_time - t);
  event0_expected_time  += EVENT0_TIME;}

void dotimer3(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T3(%d)\n", t, (t >= event3_expected_time)? t - event3_expected_time : event3_expected_time - t);
  event3_expected_time  += EVENT3_TIME;
}

void dotimer4(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T4(%d)\n", t, (t >= event4_expected_time)? t - event4_expected_time : event4_expected_time - t);
  event4_expected_time  += EVENT4_TIME;
}


void dotimer2(EventResponderRef event)
{
  uint32_t t = millis();
  Serial.printf("%u - T2(%d)\n", t, (t >= event2_expected_time)? t - event2_expected_time : event2_expected_time - t);
  event2_expected_time  += EVENT2_TIME;
}

Edit: Side question... Wondering about timings...

If you do code like:
Code:
  mytimer.begin(EVENT0_TIME, mytimerevent);
  event0_expected_time = millis() +  EVENT0_TIME;
And then during the event you compare the millis() with the expected time and we are appear to be always off by 2...

As you can see by some of the output from above program:
Code:
785687 - T4(2)
785837 - T3(2)
785837 - T2(2)
786437 - T4(2)
786687 - T2(2)
786887 - T3(2)
787187 - T4(2)
787537 - T2(2)
787937 - T3(2)
787937 - T4(2)
Why? We wait for the next systick timer interrupt to move it from waiting to active (so here is +1) and then the runFromTimer only runs when _ms == 0 else decrements... So if we called to wait 1 ms. It would get the next systick after added to queue, decrement, wait for next systick and then run... So another +1
Wonder if it maters.
 
Last edited:
@KurtE - just tried your changes myself and looks like you got it. Like you said the Serial.prints kill the timing and it hangs. Was wondering about the delta of 2 all the time. Figured it was overhead, so I never mentioned it.
 
Put in quick update(hack) to handle the off by 2...

Changed the begin and beginRepeating to decrement by 2 wait time > 2 else set to 0... Left the repeat time alone as the issue was on the first firing of the timer after started...
Updated PR...
 
Isn't it possible to use that extended sysick only when the Event Responder is in use ?
With redirecting the interrupt - only if an app uses the Event Responder - for example.
 
Isn't it possible to use that extended sysick only when the Event Responder is in use ?
With redirecting the interrupt - only if an app uses the Event Responder - for example.

I'd love to make patch and a pullrequest, tomorrow - if wanted. Are there any worries?
 
I was going to suggest (and or do), maybe add the event responder code only after the first millis object is used...
 
Back
Top