How to add a delay to an analog read?

Status
Not open for further replies.

frankzappa

Well-known member
Hello, I'm reading analog sensors and want to make delayed versions of it in a set number of milliseconds or samples.

I'm thinking something like this:

Code:
    out = delayBuffer[i];
    delayBuffer[i++] = in;
    if(i >= delaySetting) i = 0;

However, how do I make that for multiple sensors? I'm new with coding and just starting to learn about OOP. Is that something that could be done in a class?
 
Actually it is quite simple to use. Here a quick implementation using this ring buffer.


Code:
#include "Arduino.h"
#include "circular_buffer.h"

Circular_Buffer<int, 1024> buffer;  // use 1kB of int variables for the buffer

constexpr unsigned dt = 1000;       // time between two measurements in microseconds
constexpr unsigned delta = 10;      // delay line. e.g. delay by 10 measurements

void setup()
{
    while (!Serial) {}

    // prepare the buffer with dummy data to have the read pointer delta steps before the write pointer
    for (unsigned i = 0; i < delta; i++)
    {
        buffer.push_front(0);
    }
}

int newValue = 0;

void loop()
{
    static elapsedMicros stopwatch = 0;

    if (stopwatch > dt)   // do work at a fixed interval 
    {
        stopwatch -= dt;

        newValue++; // simulate a new value, replace with a real measurement

        buffer.push_front(newValue);           // add one value at the front of the buffer
        int oldValue = buffer.pop_back();     // read one value from the back.  Since you always do a read and a write the distance (delay) will stay constant.

        Serial.printf("old: %d  new %d \n", oldValue, newValue);
    }
}

Output:
Code:
old: 0  new 1 
old: 0  new 2 
old: 0  new 3 
old: 0  new 4 
old: 0  new 5 
old: 0  new 6 
old: 0  new 7 
old: 0  new 8 
old: 0  new 9 
old: 0  new 10 
old: 1  new 11 
old: 2  new 12 
old: 3  new 13 
old: 4  new 14 
old: 5  new 15 
old: 6  new 16 
old: 7  new 17 
old: 8  new 18 
old: 9  new 19 
old: 10  new 20 
old: 11  new 21 
old: 12  new 22 
old: 13  new 23 
old: 14  new 24 
old: 15  new 25 
old: 16  new 26 
old: 17  new 27 
old: 18  new 28 
old: 19  new 29 
old: 20  new 30 
old: 21  new 31 
old: 22  new 32 
old: 23  new 33 
old: 24  new 34 
old: 25  new 35 
old: 26  new 36 
old: 27  new 37 
old: 28  new 38 
old: 29  new 39 
old: 30  new 40 
old: 31  new 41 
old: 32  new 42 
old: 33  new 43 
old: 34  new 44 
old: 35  new 45 
old: 36  new 46 
old: 37  new 47 
old: 38  new 48 
old: 39  new 49 
old: 40  new 50 
old: 41  new 51 
old: 42  new 52


Hope this gives you a head start...
 
There are probably a 1000 different ways to do things like this, depending on the complexity of your devices and needs..
For example if you have 4 devices and you only want to read one per cycle you could do as simple as:

Again warning typing in on fly so could be syntax issues...

Code:
uint8_t loop_count = 0;
void loop() {
    switch (loop_count) {
        case0: value0= analogRead(PIN_0); break;
        case1: value1= analogRead(PIN_1); break;
        case2: value2= analogRead(PIN_2); break;
        case3: value3= analogRead(PIN_3); break;
    }
    loop_count = (loop_count+1)&0x3; // increment and constrain to 0-3...
...

You could Table drive it, example only wish to read every N milliseconds different per sensor?
Code:
typedef struct {
    uint8_t    pin;
    uint16_t  delay_ms;    
    uint16_t  value;
    uint32_t  next_ms;
} ANALOG_LIST_t;


ANALOG_LIST_t analog_list[] = {   {14, 50, 0, 0}, {15, 100, 0, 0}, {16, 75, 0, 0}};
#define COUNT_ANALOG_LIST (sizeof(analog_list)/sizeof(analog_list[0])

void loop() {
  uint32_t  loop_time = millis();
  for (uint8_t i = 0; i < COUNT_ANALOG_LIST; i++) {
      if (loop_time >= analog_list[i].next_ms) {
          analog_list[i].value = analogRead(analog_list[i].pin);
          analog_list[i].next_ms += analog_list[i].delay_ms;
          break;  // optional? do you only want one per loop or could put counter of how many and limit or???
       }
    }
...

There are many options in the above. Example I have it only doing one sensor (first one) per loop, but you could remove break and it would do as many as meet the criteria.
Or you could add a counter into loop and limit to only N per loop or ...

Also the updating of the next_ms just adds delay_ms... Could instead set it to: millis() + delay_ma;

Table could also be changed to have iteration counts instead of delays and/or addition to...

And of course you could instead use a class and objects to do this as well..

Hope this helps some and not too confusing.

Good luck
 
Actually it is quite simple to use. Here a quick implementation using this ring buffer.

Thanks for the code, the question is how to do this for multiple sensors, also I need it to run as fast as possible. I have 10 sensors, all updated 600 times per millisecond and I need to run calculations between readings.
 
I think if you need to do measurements and calculations of multiple sensors at a rate of 600kHz the delay line will be your smallest problem. Anyway, here an example how you can measure -> calculate -> store and read for 3 sensors at once.

Code:
#include "Arduino.h"
#include "circular_buffer.h"

constexpr unsigned dt = 10;    // time between two measurements in microseconds
constexpr unsigned delta = 10; // delay line. e.g. delay by 10 measurments

Circular_Buffer<int, 1024> buffers[3];

void setup()
{
    while (!Serial) {}

    // prepare the buffers with dummy data to have the read pointer delta steps before the write pointer
    for (unsigned i = 0; i < delta; i++)
    {
        for (int i = 0; i < 3; i++)
        {
            buffers[i].write(0);
        }
    }
}

int newValues[3];
int oldValues[3];

void loop()
{

    static elapsedMicros stopwatch = 0; // wait for the next time point or remove this 3 lines if you don't care about time between measurements
    while (stopwatch < dt) {}
    stopwatch -= dt;   

    //measure your values and do all calculations you need to do here
    newValues[0]++;
    newValues[1] = newValues[0] + 17;
    newValues[2] = newValues[1] * 2;
 
    // write results to buffers
    for (int i = 0; i < 3; i++)
    {
        buffers[i].write(newValues[i]);
        oldValues[i] = buffers[i].read();
    }

    // do something with the delayed values...
    Serial.printf("old1: %d old2 %d old2: %d  |  new1 %d  new2 %d new3 %d\n", oldValues[0], oldValues[1], oldValues[2], newValues[0], newValues[1], newValues[2]);
}
 
I think if you need to do measurements and calculations of multiple sensors at a rate of 600kHz the delay line will be your smallest problem. Anyway, here an example how you can measure -> calculate -> store and read for 3 sensors at once.

Thanks, I'll implement this into my code :)

I have the 600kHz readings sorted and I've already made it work great, was not easy to figure everything out though, got help here on this forum :)
 
I'm not really getting this part:

Code:
// prepare the buffers with dummy data to have the read pointer delta steps before the write pointer
    for (unsigned i = 0; i < delta; i++)
    {
        for (int i = 0; i < 3; i++)
        {
            buffers[i].write(0);
        }
    }

What exactly is happening here? Looks like you are rewriting the buffers three times. I'm guessing buffers[] is not like a regular array though.
 
No, in this line

Code:
Circular_Buffer<int, 1024> buffers[3];

I declared an array of 3 separate circular buffers, one buffer for each value. In the shown nested for-loop I write 'delta' times (outer loop) the value `0` into each of the 3 buffers (inner loop). This is done to advance the write pointer of each buffer 'delta' positions ahead its read pointer. This determines your delay.

I don't know how many cycles you want to delay your measurement. Just make sure that the buffer size (given in the declaration) is larger than delta. IIRC, the buffer size must be a power of 2. Actually 'delay' woud be a better name for the variable 'delta'...
 
No, in this line

Code:
Circular_Buffer<int, 1024> buffers[3];

I declared an array of 3 separate circular buffers, one buffer for each value. In the shown nested for-loop I write 'delta' times (outer loop) the value `0` into each of the 3 buffers (inner loop). This is done to advance the write pointer of each buffer 'delta' positions ahead its read pointer. This determines your delay.

I don't know how many cycles you want to delay your measurement. Just make sure that the buffer size (given in the declaration) is larger than delta. IIRC, the buffer size must be a power of 2. Actually 'delay' woud be a better name for the variable 'delta'...

Well since I'm doing all the code between sensor readings (I start a new reading and then do calculations on the previous sensor pair while the new readings are converting) I don't actually need to use loops. I have to send data to a buffer for each sensor separately as soon as I have a new sensor reading. Spreading the calculations out between the sensor readings makes it run much quicker.

So what I really need is just learn how to declare a buffer, store the data and read the delayed data. I'd like to do it in a function that I send data to and it returns a delayed signal. The function needs to keep track of which sensor that sent the data.
 
Ok,
you'd still need one buffer for each value you want to delay. Alternatively you could store structs in the buffer but that will complicate stuff unnecessarily. It is easy to pack the stuff into a function. Here an example. Won't get much simpler I'm afraid :)

Code:
#include "circular_buffer.h"

constexpr unsigned delta = 10; // delay line. e.g. delay by 10 measurements

Circular_Buffer<int, 1024> buffers[3];

int getDelayedValue(int newValue, int index)
{
    buffers[index].write(newValue);
    return buffers[index].read();
}

void setup()
{
    while (!Serial) {}

    // prepare the buffers with dummy data to have the read pointer delta steps before the write pointer
    for (unsigned i = 0; i < delta; i++)
    {
        for (int i = 0; i < 3; i++)
        {
            buffers[i].write(0);
        }
    }
}

int idx = 0;

void loop()
{
    // do reading & calcualting for one sensor here or call a corresponding function from here
    int value = random(200);

    // store new value in buffer idx and return delayed value
    int delayed = getDelayedValue(value, idx);

    // do domething with the delayed value
    Serial.printf("value_%d new %d delayed: %d\n", idx, value, delayed);

    // housekeeping
    idx++;
    if (idx >= 3) idx = 0;

    delay(1); // needed here to not overrun sermon, not needed in your real code of course
}
 
Ok,
you'd still need one buffer for each value you want to delay. Alternatively you could store structs in the buffer but that will complicate stuff unnecessarily. It is easy to pack the stuff into a function. Here an example. Won't get much simpler I'm afraid :)

Code:
#include "circular_buffer.h"

constexpr unsigned delta = 10; // delay line. e.g. delay by 10 measurements

Circular_Buffer<int, 1024> buffers[3];

int getDelayedValue(int newValue, int index)
{
    buffers[index].write(newValue);
    return buffers[index].read();
}

void setup()
{
    while (!Serial) {}

    // prepare the buffers with dummy data to have the read pointer delta steps before the write pointer
    for (unsigned i = 0; i < delta; i++)
    {
        for (int i = 0; i < 3; i++)
        {
            buffers[i].write(0);
        }
    }
}

int idx = 0;

void loop()
{
    // do reading & calcualting for one sensor here or call a corresponding function from here
    int value = random(200);

    // store value in buffer idx
    int delayed = getDelayedValue(value, idx);

    // do domething with the delayed value
    Serial.printf("value_%d new %d delayed: %d\n", idx, value, delayed);

    // housekeeping
    idx++;
    if (idx >= 3) idx = 0;

    delay(1); // needed here to not overrun sermon, not needed in your real code of course
}

Excellent, I think this will work.
 
Great! Thank @tonton81 for his useful library the rest is just boiler plate code... Have fun with it :)
 
Great! Thank @tonton81 for his useful library the rest is just boiler plate code... Have fun with it :)

I'm sure it's simple but for me I really needed a simple example because the examples on github are not really for beginners. They require a lot of C++ knowledge to understand. I only know some C :p

I can't sort out the relevant code if you know what I mean.
 
BTW:

Code:
    buffers[index].write(newValue);
    return buffers[index].read();

.write() writes to the first index of the buffer and pushes back all the other values and .read() reads the last value in the buffer right?
 
.write() writes to the first index of the buffer and pushes back all the other values and .read() reads the last value in the buffer right?

Not really (but the effect would be the same).
Don't get confused by this index variable. It defines which of the three buffers you are writing / reading to. It has nothing to do with the position inside the buffer. Just look at the wikipedia article I posted above and try to understand the clock like gif on the right. It shows how the internal read and write pointers work. Every time you write something to a ring buffer it advances the write pointer by one. If you read something it advances the read pointer by one position. If you are always writing and reading together (like in the posted code) the difference between both pointers stays the same and defines your delay. It therefore is important to initially establish this difference in setup() where I do some dummy writes to advance the write pointer.
 
Not really (but the effect would be the same).
Don't get confused by this index variable. It defines which of the three buffers you are writing / reading to. It has nothing to do with the position inside the buffer. Just look at the wikipedia article I posted above and try to understand the clock like gif on the right. It shows how the internal read and write pointers work. Every time you write something to a ring buffer it advances the write pointer by one. If you read something it advances the read pointer by one position. If you are always writing and reading together (like in the posted code) the difference between both pointers stays the same and defines your delay. It therefore is important to establish this delay defining difference in setup() where I do some dummy writes to advance the write pointer.

Yes I know how a ring buffer works but now I realise what the setup code does, thanks.

Another question, is the ring buffer library inside the arduino IDE or do I have to download it?

I can't seem to find it in the library manager.
 
Don't know if tonton had his library added to the library manager. You can always download it from github (link in #2) and either zip install it with the library manager or simply unzip it to your library folder. You might need to remove the master- prefix of the folder name. And you might need to restart the IDE to detect the new lib.
 
Don't know if tonton had his library added to the library manager. You can always download it from github (link in #2) and either zip install it with the library manager or simply unzip it to your library folder. You might need to remove the master- prefix of the folder name. And you might need to restart the IDE to detect the new lib.

Great, thanks :)
 
Circular_Buffer<float, 1024> buffers[10];

Is "1024" the number of slots in the buffer or the total size in bytes?
 
Status
Not open for further replies.
Back
Top