2

I need to integrate an analog signal using my Arduino Uno. I am sampling the input signal at 1 KHz using a delay command in my void loop() and adding up the values at the analog read at that specific time. Then I multiply my added output with 0.001 s to get my integration.

Since, I am new to Arduino Uno, I want some review on the code to check whether I am correct or not. Kindly help, I'll be indebted.

const int a=13; //my output pin
const int c=A0; //my input pin
int d=0; //my summation variable
int e=0; //my integration variable(integration as a summation)
void setup()
{
  pinMode(a, OUTPUT);
}
void loop()
{
  int b=analogRead(c);
  d=d+b; //my summation
  e=d*10^-3; //my integration as a summation, by sampling my signal at 1ms
  analogWrite(a,e); //writing my integration into my output pin

  //print the results to the serial monitor
  Serial.print("input=");
  Serial.print(b);
  Serial.print("\t output=");
  Serial.println(e);

  delay(1); //wait for 1ms as I want it sampled at that rate
}
  • Since you are using an int , d could overflow in as little as 32 measurements. Also analogRead does take some time, as does the code around it, so the time between measurements will be a bit more that 1 ms. I also don't see how you call dividing by 1000 integration. – Gerben Mar 18 '15 at 11:14
  • Could you explain why you want to integrate in the first place? Integrating a positive signal will yield a diverging integral, meaning that, as your program runs, the result will increase without bounds. This is problematic. If your store the integral in an integer variable, even a long int, it will eventually overflow. If you store it in a float, it will loose precision as it grows and, eventually, the summation will have no effect, because d+b is exactly equal to d if d is a big enough float. – Edgar Bonet Mar 19 '15 at 12:22
3

There are a couple of issues with your code.

First of all, 10^-3 is not a valid constant in C/C++ code. The correct syntax would be 1e-3, which means 1 × 10-3. But you have the further problem that you're working with integer variables, and 0.001 rounded to the nearest integer is just zero. You'll probably want to use float variables for this math.

Also, the values you get from AnalogRead() are all non-negative integers, so your summation will quickly overflow unless you subtract a constant from each reading in order to get both positive and negative values.

The second issue is about the delay() function, and trying to use it to determine the sample rate of your system. The delay() function is not all that precise. It really is just guaranteed to produce a delay of at least the number of milliseconds specified, but it can be more — sometimes much more.

Furthermore, the other operations in your loop take much longer than 1 ms anyway. For example, you're printing on the order of 25 to 30 characters to a serial port on every iteration, which is an overall rate of 25,000 to 30,000 characters per second. Unless you're running your serial port at an insane baud rate (430.8 kbaud or more), it simply won't be able to keep up, and it will end up increasing the period of your loop to whatever time it needs.

All of this means that the sample period of your loop is NOT 1 ms as you want, and in fact, it can vary quite a lot based on other things that are going on in the system, such as interrupts. For doing this kind of DSP, you will want to learn how to use a timer interrupt to control the sampling of the analog input, and you'll need to trim the amount of data transmitted for each sample to an amount your serial link can handle.

  • Thank you for your reply. It's valuable really, kindly help me with correcting my errors 1> Regarding the integer data types of d, b and e, I completely understand and hence I'll change them to float variables. 2> Regarding the AnalogRead being non negetive integers, is there any way to read floating value out of analog inputs? 3> Regarding the delay timer, could you direct me to some literature on using timer interrupts to sample my signal? 4> Is there any way around the problem, I mean some built in library functions to help with integration etc... – ubuntu_noob Mar 18 '15 at 11:46
1

Here is an edited version of your code:

/*
 * Integrate a signal read on analog input 0.
 * Print the result every 2 seconds on the serial port.
 */

const int input_pin = A0;
unsigned long last_time_sampled = 0;
unsigned long last_time_printed = 0;

// The integral is in units of DN*us, 1 DN being the ADC step.
float integral = 0;

// Convert from DN*us to V*s, assuming a 5 V reference.
const float conversion = 5.0 / 1024 * 1e-6;

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

void loop()
{
    int sample = analogRead(input_pin);
    unsigned long now = micros();

    // Approximate the signal as a constant equal to the current sample
    // from the time of the previous sample until now.
    integral += sample * (now - last_time_sampled);
    last_time_sampled = now;

    // Print the results every 2 seconds.
    if (now - last_time_printed >= 2000000) {
        Serial.print("input = ");
        Serial.print(sample);
        Serial.print(", output = ");
        Serial.println(integral * conversion);
        last_time_printed = now;
    }

    // Limit the sampling rate to no more than 1000/s.
    delay(1);
}

The main changes I did are:

  1. I renamed the variables to give them more meaningful names. This single change makes the code a lot more readable.
  2. I removed those comments made redundant by the better variable names.
  3. I removed analogWrite(), since I couldn't make any sense out of it.
  4. I compute the time between samples as time differences. Unless you take your samples on some kind of timer interrupt, the delay between samples will never be 1 ms.
  5. I stored the integral as a float to avoid overflows.
  6. I removed the summation variable, as it was redundant with the integration variable. Instead, there is a conversion factor for converting the integral from its “natural” unit (DN⋅µs) into a more human-friendly unit (V⋅s).
  7. I print the results only every 2 seconds, as nobody will read something printed every millisecond.
  8. I added some comments, most importantly for making explicit the approximation used in computing the integral.

There is still one caveat with this code: once the integral is roughly 107 times larger than the samples, which should happen after only a few hours, the integral is not incremented any more, due to loss of precision.

0

I'm not entirely clear on what you're trying to achieve mathematically. However, I can see a number of issues in your code which I'll try to address briefly.

Arithmetic
I suspect this line isn't doing what you intended:

e=d*10^-3

The ^ operator in C/C++ is actually a bitwise XOR (exclusive-or). There is no arithmetic raise-to-the-power operator.

There is a function called pow() for calculating exponents. However, there's no point using it for a fixed value. It would be better to explicitly write the number, i.e. 0.001.

Precision
The power issue notwithstanding, it's important to note that you're working with integers here, and the result of analogRead() is only in the range 0 to 1023. Dividing that by 1000 and storing the result as an integer will destroy your data -- most readings will effectively be 0. (That's not to mention the fact that an int has a limited range and will overflow quite quickly if you don't reset it.)

The simple solution would be to store the data using the float data type (floating point). You would need to declare variables b, d and e as float instead of int. That should let you divide down by 1000 without losing significant precision.

Output range
The analogWrite() function has a very specific output range. It will only accept values 0 to 255 (inclusive). I'm not entirely sure what you're hoping to do with it, but I suspect you'll need some way to map your integration calculation onto that output range, or possibly reset your summation periodically.

If you don't do that, you'll probably find that e ends up over 255 very quickly (within a couple of seconds). The result is that you won't see any change on the output PWM.

  • I'm trying to arrive at integration as a sum of discrete samples, dividing my 1000 I did because my time period was 0.001 seconds. Is there any way around this problem for analog integration, I'll be grateful for your help... – ubuntu_noob Mar 18 '15 at 11:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.