Here’s how I think about that function:
There’s an ever-repeating cycle that comes around (say) once per second (60BPM, a typical heartrate).
The output waveform is the sum of two single-pulse waveforms with offset timing.
So let’s define what a single-pulse wave looks like, and then we can add two of them. Here’s one way to do that:
For the first X/2 milliseconds, the otuput value of the function rises from zero to some maximum, let’s say 255.
From X/2 ms to X ms, the output value falls from 255 back to 0.
In all other cases, the output value is zero.
SO, something like this pseudocode:
uint8_t pulsewave8( uint32_t ms, uint16_t cyclelength, uint16_t pulselength)
{
uint16_t T = ms % cyclelength;
if( T > pulselength) return 0;
uint16_t halfpulse = pulselength / 2;
if( T <= halfpulse ) {
// first half = going up
return (T * 255) / halfpulse;
} else {
//second half = coming down
return ((pulselength - T) * 255) / halfpulse;
}
}
So if you call it like this:
Y = pulsewave8( millis(), 1000, 100);
you’ll get one 100ms long pulse every 1000ms.
And if you do this:
Y = pulsewave8( millis() + 200, 1000, 100);
you’ll get one 100ms long pulse every 1000ms, but offset 200ms from the first one.
So then you can:
Y1 = pulsewave8( millis(), 1000, 100);
Y2 = pulsewave8( millis() + 200, 1000, 100);
Y = qadd8( Y1, Y2); // add without overflow.
Something like that should work, more or less. None of this has been compiled or tested or run, but it’s a conceptual start.