How to get a library to hook into the Arduino's main loop?

First, my background: I am an "old hand" at software development and microcontroller-chip programming, but I am BRAND NEW to AVR chip, and Arduino programming, as of this past weekend. This is my first Arduino forum post.

Note: If these questions have been discussed before, or if there is documentation showing how to do what I ask, could you please provide URLs that link specifically to the answers?

My two questions, in a "nut-shell":

  • Does the Arduino's internal "Operating Sytem" (or "OS") provide a "hook" or API to add a library method/function call to the internal, hidden main loop (i.e. to call a method/function from the same entity that makes the call to a sketch's loop() function)?
  • Is there a software API or system call to reset the Arduino board?

Further Details:

I have in mind to write a software Watchdog library that does two things:

  • Set up a watchdog timer and reset it every time through the main loop (and reset the Arduino board if the watchdog timer times out);
  • Flash the (usually) provided Arduino on-board LED, as a "heartbeat" monitor.

The watchdog timeout value (0 for off, to disable the watchdog) and the LED flash on/off times, are to be provided as parameters to the class constructor, something like this (e.g. before the void setup() definition):

// Setup a watchdog timer of 5 seconds, and flash a heartbeat
// with on/off times of 50 ms and 950 ms, respectively
Watchdog wd(5000, 50, 950);

And that is ALL that should be required to set it up and to start it running when the main loop starts.

Both the LED flashing and watchdog reset should be managed as part of the main loop, and NOT from any timer interrupt service routine (ISR), since, in many cases, interrupts can continue to function even if the main loop has "died" or "gone off to la-la land", for any reason

It would be nice to have the class library automatically add the required function calls to the main loop, without requiring the sketch programmer to add any libary calls in the sketch's loop() function.

Is there any way to do this?

Thanks and best regards
The "Duinonator"

First thing, the Arduino has no OS, and you don't want to be recursively, either directly or indirectly, calling the loop function.

The best way I've found to reset the processor is to enable the watchdog, and then simply forget to kick it.

  1. In the source code provided with the Arduino there is this folder

C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino

it contains the file main.cpp which you can edit to provide your "secret call"

  1. Not a 100% cold reset but a jmp to address 0x0000 does quite a good job.
    typically a function pointer is set to 0 and that function is called.

@CtrlAltElite and @robtillaart

Thank you for your very quick replies! This is the first time I have used this forum and I am pleasantly surprised. :slight_smile:

I did not mean to imply that I wanted to make a recursive call to loop(). What I meant was that there must be some internal, hidden entity that periodically calls the sketch's loop() method. I was wondering if there was any way to add a function call to that same hidden entity through some system API, which could be set up from my library class. As an example (which very likely does is NOT do-able), in my constructor I would like to be able to do something like:

AddToSystemMainLoop(myLibaryLoopFunction);

Also, I would like this to be available to any sketch writer using the library, so any custom modifications to the Arudino main.cpp would be out of the question.

If there is no way to do this, then I would need to add explicit instructions in the library documentation to have sketch-writers add a library method call inside the loop() method. :frowning:

Thanks for the idea of using the AVR hardware watchdog timer, or else a function pointer to 0x0000, to reset the board.

I could probably use the H/W WD timer instead of doing it in software (so long as there is a periodic s/w reset of the wd timer).

"Duinonator"

There is a simple way to get almost the behavior you want

#include "secretFunction.h"

void setup()
{
  Serial.begin(115200);
}

void loop()
{
   if (secretFunction) secretFunction();
  ...

}

"secretFunction.h" has a function pointer called secretFunction that defaults to zero == false

The user can assign another function to it and it will be called.

NO this is not automatic but at least it shows a way to get it done. (sort of)

If there is no way to do this, then I would need to add explicit instructions in the library documentation to have sketch-writers add a library method call inside the loop() method. :frowning:

That is the usual way of doing things. Breaking that pattern is not a good idea, even if it were feasible.

Two things:

As said, reset can be archived pointing to address 0. To do so,s imply define a function pointing to 0 as global:

void(* resetFunctionName) (void) = 0;//declare reset function at address 0

And call it where you want:

resetFunctionName();

For the watchdog, you may use Timers.

If you want a library to simplify things and make it portable, I've just created one: GitHub - Naguissa/uTimerLib: Arduino tiny and cross-device compatible timer library

It's available at Arduino IDE Library Manager.

You can:

  1. Attach watchdog functionality at desired interval

  2. Attach watchdog with a timeout and reattach it (with same or different timeout) when executions goes OK.

This way you can track previous milis() and compare to actual ones to decide if you want to perform the reset.

Timers are not difficult, but one thing about that library is that I'm porting it to all architectures, so you may want to program watchdog as library and be able to reuse on any project you want.

I suggest adding a loop() function in a separate (library) file. Or in your main program if you wish.
This loop() (below) has a list of functions that should be called in the main loop.

Initially the list is empty. Add your own loop_sub() functions to the list by passing their addresses as parameter to add_loop(your_function_name).

#include "loop_functions.h"

struct loop_element {
  void    (*function)();
  struct  loop_element *next;  
};
static struct loop_element *loop_list = 0;
unsigned long now;                                    // global variable containing millisec since boot

void loop(void)
{
  now = millis();                                     // Update now once a loop
  struct loop_element *loop_ptr = loop_list;
  while (loop_ptr)  {
    (*loop_ptr->function)();                          // Call added user function
    loop_ptr = loop_ptr->next;
  }
}

void add_loop (void(*function)(void)) {
  if (function == 0)           Serial.println ("Illegal call add_loop(0) !");
  struct loop_element *new_element = (loop_element *) malloc (sizeof(struct loop_element));
  if (new_element == 0)       Serial.println ("add_loop: malloc() returns 0 !");
  new_element->next = loop_list;
  new_element->function = function;
  loop_list = new_element;
}

Acompanying header file (to be included in your progams using the loop_list):

#ifndef _LOOP_FUNCTIONS
#define _LOOP_FUNCTIONS

extern unsigned long now;               // =  millis(); updated every loop
void add_loop (void(*function)(void));  // How to add a function in loop()

#endif _LOOP_FUNCTIONS

Example main program that contains 2 parts, both containing their own setup() and loop():

  • blink a led
  • print some serial

The loop() functions are added to the main loop() in their respective setup() functions !

In this example, each loop() function contain 3 lines to make them active only once when needed.
The global now is filled with millis() only once in the main loop, so every sub-loop() doesn't need to get it from millis().

#include "loop_functions.h"

void setup(void) {
  Serial.begin(115200);
  led_setup();
  serial_setup();
}

//--------------------------------------------------------------
// Led blink module containing setup() and loop()

void led_setup(void)
{
  pinMode(LED_BUILTIN, OUTPUT);          // initialize digital pin LED_BUILTIN as an output.
  digitalWrite(LED_BUILTIN, 0);
  add_loop(led_loop);
}

static void led_loop(void) {
  static unsigned long next_time = 0;
  if ((long)(now - next_time) < 0)  return;
  next_time = now + 1000; // Repeat this function every 1 seconds

  static int flip = 0 ;
  flip = 1 - flip;
  digitalWrite(LED_BUILTIN, flip); // Blink LED
}

//--------------------------------------------------------------
// Hello module containing setup() and loop()


void serial_setup(void)
{
  pinMode(LED_BUILTIN, OUTPUT); // initialize digital pin as output.
  add_loop(serial_loop);
}

static void serial_loop(void) {
  static unsigned long next_time = 0;
  if ((long)(now - next_time) < 0)  return;
  next_time = now + 3000; // Repeat this function every 3 seconds

  Serial.println("Hello again");
}

You can add this loop() code to your own main loop.
But you can also just add this file into your library, rename your own loop() to my_loop(), and in your setup() you add my_loop() to the list with add_loop(my_loop));.

Update: added example in ZIP file

Update: added TimeSlicer as library with 1 example. Uncompress it in your Arduino/libraries folder, (re)start the Arduino IDE, and try the example.

test_loop.zip (2.72 KB)

TimeSlicer.zip (3.8 KB)

Jumping to 0 is NOT the same as a reset. In particular, none of the peripherals will be returned to their reset state.

There is no “OS”, and there is no way to add a function to loop() (or to be run for every call to loop()) at runtime.

PS: the code that calls loop() on an avr arduino is at ArduinoCore-avr/main.cpp at master · arduino/ArduinoCore-avr · GitHub
It’s quite trivial.

Attached a library "TimeSlicer" with example to my previous post.

Install the library and use the example.

In you program, you can add your own loop_functions to be called by the system loop without modification of the Arduino core code.