user23991: I'm writing a library which needs an ISR to turn off an LED some time after it was turned on. Since it's all about turning an LED on and off it doesn't need to be very precise. On the other hand I would like to use this library in a program where timer1 and timer2 are given out to other (more important) tasks, and I also need the millis() and micros() functions. Last, I'm using an ATmega328P microcontroller, which only has 3 timers (and I can't easily replace it with another one).

So I was wondering if I could attach an ISR to timer0 without affecting the above Arduino functions, and – if that can be done – what would be the restrictions of such an ISR (e.g. I suppose I could not use all the timer/interrupt modes...) and the disadvantages or side effects in using this timer in a non-Arduino library and the Arduino millis() and micros() functions.
Thanks in advance for any answer!

user10794: 
So I was wondering if I could attach an ISR to timer0 without affecting the above Arduino functions ...

No, because those functions use an already-attached interrupt.

it doesn't need to be very precise ...

Just test for the current time (eg. using millis or micros) in your main loop.

the program main loop is sometimes quite long (e.g. sometimes it has to execute other loops) ...

Restructure so that it doesn't do that. You can use the state machine concept to do things from time to time, without getting stuck in inner loops for a long time.

user40024: I think the first solution suggested by dannyf's answer is wrong as ISR in AVR GCC can't be defined as weak function (why).
I think the best solution is to add handler (AKA hook) functions using function pointer to the Timer0 ISR inside the core wiring.c source file shipped with Arduino core, and assign latter (attache) this function pointer to any desired function in the other library/class or in the main application code. 
More about this trick in this article here.

user30005: 
So I was wondering if I could attach an ISR to timer0 without affecting the above Arduino functions,

Yes. A few ways, depending on your level of comfort:

You can declare the stock Arduino Timer0 OVF "weak" and write your own where you can insert your ISR. But you have to handle the interaction between the millis() / micros() related variables.
You can modify the stock Arduino Timer0 OVF to insert your own ISR.
And – if that can be done – what would be the restrictions of such an ISR (e.g. I suppose I could not use all the timer/interrupt modes...)
The stock ISR would look like this:
ISR(TIM0_OVF_vect) {
    ...
    timer0_fract = f;
    timer0_millis = m;
    timer0_overflow_count++;

    //insert your isr here
    _mytmr0_isrptr();
}

Where _mytmr0_isrptr() is a function pointer to your own ISR handler.
The simplest is probably to use the output compare interrupts. You can get it to behave like a virtual timer and can do lots of other stuff.

Obviously, you can think of tons of other ways and this is just a starting point.

What are those interrupts? How can I use them? –

Timer0 has three interrupts associated with it: overflow and compare channel A and channel B. The overflow interrupt is already being used by the timing functions millis() and micros(), as shown earlier.
The compare channel A/B interrupts are unused. And this discussion is about using them for timing purposes.
The general ideal is demonstrated in blog post Using Spare Output Compare Channels as Timers – the Collection.
The task there is more complicated than what you are trying to do here, but the basic ideal is the same. To do what you wanted here it is much simpler than that.
If you still cannot figure it out, I can provide you with code pieces to get you started.

23991: <p>I'm writing a library which needs an ISR to turn off an LED some time after it was turned on. Since it's all about turning an LED on and off it doesn't need to be very precise. On the other hand I would like to use this library in a program where timer1 and timer2 are given out to other (more important) tasks, and I also need the <code>millis()</code> and <code>micros()</code> functions. Last, I'm using an ATmega328P microcontroller, which only has 3 timers (and I can't easily replace it with another one).</p>

<hr>

<p>So I was wondering if I could attach an ISR to timer0 without affecting the above Arduino functions, and – if that can be done – what would be the restrictions of such an ISR (e.g. I suppose I could not use all the timer/interrupt modes...) and the disadvantages or side effects in using this timer in a non-Arduino library and the Arduino <code>millis()</code> and <code>micros()</code> functions.</p>

<p>Thanks in advance for any answer!</p>
 10794: <blockquote>
  <p>So I was wondering if I could attach an ISR to timer0 without affecting the above Arduino functions ...</p>
</blockquote>

<p>No, because those functions use an already-attached interrupt.</p>

<blockquote>
  <p>it doesn't need to be very precise ...</p>
</blockquote>

<p>Just test for the current time (eg. using <code>millis</code> or <code>micros</code>) in your main loop.</p>

<blockquote>
  <p>the program main loop is sometimes quite long (e.g. sometimes it has to execute other loops) ...</p>
</blockquote>

<p>Restructure so that it doesn't do that. You can use the <a href="http://gammon.com.au/statemachine" rel="nofollow noreferrer">state machine</a> concept to do things from time to time, without getting stuck in inner loops for a long time.</p>
 40024: <p>I think the first solution suggested by dannyf's answer is wrong as ISR in AVR GCC can't be defined as weak function (<a href="https://stackoverflow.com/questions/45481305/why-cant-avr-interrupt-vectors-be-attribute-weak">why</a>).</p>

<p>I think the best solution is to add handler (AKA hook) functions using function pointer to the Timer0 ISR inside the core wiring.c source file shipped with Arduino core, and assign latter (attache) this function pointer to any desired function in the other library/class or in the main application code. </p>

<p>More about this trick in this <a href="https://atadiat.com/en/micros/e-arduino-trick-share-interrupt-service-routines-between-libraries-application/" rel="nofollow noreferrer">article here</a>.</p>
 30005: <blockquote>
  <p>So I was wondering if I could attach an ISR to timer0 without affecting the above Arduino functions,</p>
</blockquote>

<p>Yes. A few ways, depending on your level of comfort:</p>

<ol>
<li><p>You can declare the stock Arduino Timer0 OVF "weak" and write your own where you can insert your ISR. But you have to handle the interaction between the millis() / micros() related variables.</p></li>
<li><p>You can modify the stock Arduino Timer0 OVF to insert your own ISR.</p>

<p>And – if that can be done – what would be the restrictions of such an ISR (e.g. I suppose I could not use all the timer/interrupt modes...)</p>

<p>The stock ISR would look like this:</p>

<pre><code>ISR(TIM0_OVF_vect) {
    ...
    timer0_fract = f;
    timer0_millis = m;
    timer0_overflow_count++;

    //insert your isr here
    _mytmr0_isrptr();
}
</code></pre>

<p>Where _mytmr0_isrptr() is a function pointer to your own ISR handler.</p></li>
<li><p>The simplest is probably to use the output compare interrupts. You can get it to behave like a virtual timer and can do lots of other stuff.</p></li>
</ol>

<p>Obviously, you can think of tons of other ways and this is just a starting point.</p>

<blockquote>
  <p>What are those interrupts? How can I use them? –</p>
</blockquote>

<p>Timer0 has three interrupts associated with it: overflow and compare channel A and channel B. The overflow interrupt is already being used by the timing functions millis() and micros(), as shown earlier.</p>

<p>The compare channel A/B interrupts are unused. And this discussion is about using them for timing purposes.</p>

<p>The general ideal is demonstrated in blog post <em><a href="https://dannyelectronics.wordpress.com/2017/06/23/using-spare-output-compare-channels-as-timers-collection/" rel="nofollow noreferrer">Using Spare Output Compare Channels as Timers – the Collection</a></em>.</p>

<p>The task there is more complicated than what you are trying to do here, but the basic ideal is the same. To do what you wanted here it is much simpler than that.</p>

<p>If you still cannot figure it out, I can provide you with code pieces to get you started.</p>
