blink 1.05

This sketch introduces new versions of the scheduling functions which have been moved into the subs.ino file/tab, and adds a print() task loop. You'll need to open the serial monitor to see it working. The LED blinks at 2Hz, and the print() function runs at 3Hz. You can see this syncopation by watching the on-board LED blinking at one rate while the transmit LED blinks at the other.

I couldn't find any way to make a gentle transition from blink104 to blink105, and anticipate a lot more questions - but at this point we have a scheduling "infrastructure" capable of supporting a fairly large number of tasks...

blink105.ino (1.89 KB)

blink.h (1.7 KB)

subs.ino (5.56 KB)

This looks great, but of course, I have questions :slight_smile:

  1. It looks like time_t is a 64bit impl of usec, not msec, right? I don't need the accuracy of usec at this point, do you have a similar struct, etc. for msec, or am I just dead-wrong here?

  2. I see you have arrays holding queued tasks. A set of helper functions to somehow "cancel" a task and/or to reset the time on a specific task would be a great addition!
    For example: I would like to use the aftr() to set an "alarm" to occur iif an event doesn't happen in the meantime. If said event were detected, reset the aftr-time to an additional delay.

Awesome stuff.
-AJ

Tasking. It's almost like an OS. And then Micro$oft will want to take over. You know, Windows ain't done till Arduino won't run.

AJ - It's an easy change: just change the call to micros() to call millis() instead. :slight_smile:

I was originally going to use millis(), but then realized that once I'd expanded to 64-bits there'd be no upside to millis() - and since I was paying the price, I might as well have all the benefits to be got. Hmm - in addition to changing the call, you'd probably want to update the conversion macros in the header file (NBD).

I thought about adding a cncl() capability, but don't need it for my application (which may very well be the only one I ever tackle with an Arduino) so decided to leave that for anyone who needs it and/or is willing to spend the time. Where I have a subtask that might need to be cancelled, I just set a flag so that when it's invoked it simply says "I'm done", cleans up its internals, and gives control back.

I meant to add: The task queue only starts out as an array because "array of struct" is supported by C while "singly-linked list of n elements" is not. You probably noticed that I hid a one-time initialization loop to effect that conversion in the schd() function.

GFS - This is a long, long way from an OS, and it's probably safe from that particular portion of the "Dark Side". AFAICT, MS is still struggling to understand interrupt-driven software. :grin:

Edit: added array/list afterthought

I took the weekend off, but thought about this a little more.

I guess I need to be able to do some basic math with the "enhanced time" you created ("time_t"), though the scheduling "feature" you've already built already does a lot of what I might need.

I don't know that I can do it with the struct. I know in C++ you can overload operators if you use a class. Do you know if I can do that in the C/C++ subset available to the Arduino? If so, is the thought that building-out such a class (instead of the straightforward struct you have) will waste precious RAM or something?

EDIT: Maybe I just haven't worked with C structs in such a long time, because you do actually illustrate math in your examples. Are you saying that the struct is "packed" in such a way that the two, 32bit ulongs act like one, 64bit ulong if you just perform math on the struct as a whole? Like would this work...?

time_t x = now();
x += ULONG_MAX; //add roughly 50 days if struct is millis
// This wouldn't overflow or anything, and my variable "x" now is today + 50days (or whatever the actual ULONG_MAX equates to)???

Thanks for the help.
-AJ

Take another look and distinguish carefully between 'struct' and 'union'.

I suggest that you don't do anything with the struct, and instead just work with the 64-bit time_t value returned by now().

The union allows me to tell the compiler to force the time_t value and the struct containing the two longs to occupy the same memory space. That lash-up allows access to the entire 64-bit time as a single unsigned long long variable, or to access either the upper or lower 32-bit value as an unsigned long, which makes adjustment very much easier when whichever time source overflowed.

Whether anything is "wasted" depends on what you need. The minimal code I posted is for people to hack on to suit their own needs. I was fairly well pleased to shrink the executable part as far as I did - and I'll be really pleased if anyone can show me how to shrink it further. :grin:

What is the difference between schd() and aftr()? Is there a function for...

  1. Executing a function X milliseconds after it is called, without conflicting with the loop:
//...
void blink(){
  static boolean output = 1;
  digitalWrite(13, output ^= 1);
}
void loop(){
  callAfter(blink, 250);
}
  1. Executing a function, then "wait" X milliseconds before doing it again.
  2. Being able to terminate functions, or execute it X times, then stop [forever].

Nice and clean. It's like recursion without having to go back.

GoForSmoke:
Nice and clean. It's like recursion without having to go back.

What do you mean?

In recursion each call has a return.

Are you talking about schd() or aftr()? If one of them is

GoForSmoke:
Nice and clean. It's like recursion without having to go back.

then what is the other one?

They are both nice and clean. Have you written any recursive routines before? I don't mean it's exactly like recursion, just allows recursive-like functionality without loading up the stack.

So, they are both the same? Is it possible to stop and continue the functions at will?

I don't know what 'same' you refer to. I look at the code and see ways to use it that remind me of recursion. My explanation comes from what I know, what I have done. Your mileage may vary.

If you make your code edit the event queue you can stop/continue/change events. If you so desire then you have the source and can add functions, part of the power of code. OTOH you can put a flag check in the function(s) you will schedule and on condition not execute the function.

Code language is like an alphabet to write novels with. The story is up to the author.

BTW; RIP Ray Bradbury, your works did inspire many.

By "same", I mean that if I replace aftr() with schd() in my code, there is no difference. I wanted to know how to use each function, and which function is best for what situation.

From Blink.h, but the bolds are mine:

int schd(time_t,fptr_t); /* Call a function at a specific time /
int aftr(time_t,fptr_t); /
Call a function after spec delay */

BTW Morris, I downloaded from your link and while the file name is blink105.ino.. well, the comments are always the last thing to change, lol, right? My copy, the comment says 1.04.

I saw that in the code. I looked there before asking. The problem is that I don't know the difference between "specific time" and "specific delay". I don't know how to use schd() either. For aftr(), I think that the aftr() statement needs to be inside the function with no parameters, and called in the setup. The function idle() must be put in the loop. That is shown in the example. Then, if you want to stop the function somewhere in the loop when some condition is met, how is that done?

  • It's the difference between specific time '3 PM tomorrow' and specific delay 'an hour from now'.
    One is absolute and the other is relative.

  • Specific just means putting a value on it. '3 PM tomorrow' is specific, 'sometime later' is not. When someone asks "can you get more specific" they are asking for more details. In tech docs you will see the word 'Specifications', it's a certain class of details.

Mostly though I'd say go by the code and only use comments and variable names as guides at best, but be prepared to see differences even if only subtle. English is not Logic.

Nice contribution! I am going to try it out this weekend and see if my Novice level can grasp full use of it. :slight_smile:

GoForSmoke:

  • It's the difference between specific time '3 PM tomorrow' and specific delay 'an hour from now'.
    One is absolute and the other is relative.

So aftr() calls the function X milliseconds later. But, Arduino doesn't use hours, minutes, seconds, etc. like we do (e.g. 2012/06/08 3:18 PM EST) as absolute values. Then, how does one use schd()? Is the absolute time at a particular millis() value (e.g. run function when millis() hits 5000), or what?