SparkFun Forums 

Where electronics enthusiasts find answers.

General project discussion / help
Did you make a robotic coffee pot which implements HTCPCP and decafs unauthorized users? Show it off here!
By sgraber
#173941
I'm working on a project where I want to use the Sparkfun UV Breakout Board, a Redboard, and a 16x2 LCD to report ambient UV radiation. I have everything prototyped up and it's functioning. However, the UV readings are fluctuating a bit and I'd like to smooth out the readings to achieve a +/- 0.01 mW/cm^2 precision.

The first way I smoothed the data was to increase the averaging rate from 8 samples to 75 samples for the sample code (https://learn.sparkfun.com/tutorials/ml ... okup-guide). This yielded an increase of precision from ~0.5-0.7 mW/cm^2 down to +/- 0.1 mW/cm^2. Ideally I'd just keep increasing my averaging sample size, but anything higher than 95 samples in the averaging routine spits out highly erroneous data.

Thus, I believe I need to add a RC circuit to smooth out the readings between the OUT pin of the sensor and A0 on the Redboard. However, I am still learning and am uncertain how to size the resistor and capacitor to smooth the voltage readings further.

Here is the datasheet for the UV Sensor: https://cdn.sparkfun.com/datasheets/Sen ... 3-8-13.pdf

And here is the Sparkfun page on that sensor: https://www.sparkfun.com/products/12705

Could someone provide assistance on how I should proceed to smooth out the voltage fluctuations? Thank you in advance! :)
By dschlic1
#173947
Have you added the decoupling capacitors as called for in the data sheet? Also what range of output voltage are you working with? If the range is limited (spec sheet calls for 1 to 3 volts) perhaps some signal conditioning to change the range to cover more of what your ADC uses. Another way to increase the resolution of an ADC is to use over sampling which is different from averaging. So:
1. Add all called for decoupling capacitors. Might even look at a separate supply for the sensor and ADC part of the microcontroller
2. Match the expected output of the sensor to the dynamic range of the ADC
3. Use over sampling techniques to increase resolution.
By sgraber
#173953
Thank you for your response! Here is how I have it setup currently:

http://imgur.com/iOHGTez,bySSshW

I have it wired up exactly as shown in the below photograph. Correct me if I'm wrong, but I believe the capacitors are already on the breakout board:

Image

Also, could you suggest a starting point for over sampling? I am very new to Arudino, and am trying to wrap my head around it. :)

Image

And here is my code and the averaging routine is at the bottom:
Code: Select all
/* 
 UV Sensor / LCD Display 
 By: Shane Graber
 Date: August 19, 2014
 License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
 
*/

// include the LCD library code:
#include <LiquidCrystal.h>

// initialize the LCD library with the numbers of the interface pins
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

//Hardware pin definitions for UV breakout board
int UVOUT = A0; //Output from the sensor
int REF_3V3 = A1; //3.3V power on the Arduino board

void setup() {
  // set up the LCD's number of columns and rows: 
  lcd.begin(16, 2);
  
  // set cursor position and print to the LCD
  lcd.setCursor(2, 0);
  lcd.print("UV Intensity");
  lcd.setCursor(7, 1);
  lcd.print("mW/cm^2");
  
  // set pins for the UV sensor
  pinMode(UVOUT, INPUT);
  pinMode(REF_3V3, INPUT);
}

void loop()
{
  int uvLevel = averageAnalogRead(UVOUT);
  int refLevel = averageAnalogRead(REF_3V3);
  
  //Use the 3.3V power pin as a reference to get a very accurate output value from sensor
  float outputVoltage = 3.3 / refLevel * uvLevel;
  
  //Convert the voltage to a UV intensity level
  float uvIntensity = mapfloat(outputVoltage, 0.99, 2.8, 0.0, 15.0); 

  // clear the old LCD value; display new value
  lcd.setCursor(0, 1);
  lcd.print("    ");
  lcd.setCursor(2, 1);
  lcd.print(uvIntensity, 1);
 
  // wait a second to update value
  delay(1000);
}

//Takes an average of readings on a given pin
//Returns the average
int averageAnalogRead(int pinToRead)
{
  byte numberOfReadings = 75;
  unsigned int runningValue = 0; 

  for(int x = 0 ; x < numberOfReadings ; x++)
    runningValue += analogRead(pinToRead);
  runningValue /= numberOfReadings;

  return(runningValue);  
}

//The Arduino Map function but for floats
//From: http://forum.arduino.cc/index.php?topic=3922.0
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
Thank you in advance!
By Mee_n_Mac
#173960
There are a couple of things I don't like in your code. First the sampling routine runs waaaaay too fast. It takes a sample, adds it to a subtotal. Then it computes an average after all the samples are taken. The time between samples in the for() loop might be 140 usec. That doesn't sound correct to me as it doesn't allow much time for the noise in the output signal to change. Thus you don't get a true averaging until you collect a lot more samples. You'd be better of waiting a longer time between samples and taking fewer samples. Or use a true running average as shown in this tutorial.
http://arduino.cc/en/Tutorial/Smoothing
Note that each time the code in loop() runs, it takes 1 new sample, discards the oldest sample and computes a running average using the remaining samples. There's a 1 msec delay in the code so samples are taken at slightly longer than 1 msec intervals. You may want to wait longer, I can't tell from the datasheet.

I don't know what kind of values you're seeing but I suspect there's another problem with the averaging code you're using. The runningValue is a type unsigned int, meaning it'll hold numbers from 0 to 65,535 but no fractions. I don't know what kind of values you're getting from the A/D but to hold 95 samples the average number must be < 691 (65,535/95). I suspect your problem with 95 samples is related to this. The code in the tutorial above bypasses that problem.

If you need the LCD to be updated with new data but the loop() now runs too fast, you can put a counter in the loop code and update the LCD only every "N" iterations of the loop().
http://arduino.cc/en/Reference/Modulo
By sgraber
#174054
Thank you! Both of those links were very informative and have really helped move me along! :)