# compensating for voltage divider computing sensor board analog reading

I'm looking at air quality sensors with the intention of monitoring indoor air quality.

One sensor I'm looking at is the Waveshare Dust Sensor Detector Module with Sharp GP2Y1010AU0F Onboard. See https://www.waveshare.com/wiki/Dust_Sensor for a technical description of the sensor board.

I found this sensor on Amazon and reading over the reviews found one with a fair amount of technical details about how the person is using it.

This review mentions the code used with this sensor and one thing I do not understand is the multiple sampling of the analog signal for 11 times creating a sum of those 11 samples. The reason given for this code is because the line output is routed through a resistor voltage divider that is some 11K ohms (a 10k ohms plus a 1K ohms resistors) on the sensor board before presented to the output connector on the board.

I've also looked at the demo source code from the Waveshare website, a copy of which is posted further below. That code generates an average of 10 samples which is then multiplied by 11.

So it seems that both the Amazon reviewer's code and the demo program are taking the 11 to 1 resister voltage divider into account using two different procedures.

Why are they doing an adjustment rather than just using a reading of the line and taking that value?

Post on Amazon and the code there

The reviewer wrote:

2./ While the Sharp sensor has output Aout on pin 5 in range 0.4-4V the Waweshare board lowers it with the resistor divider R10 10k + R6 1k (11:1);

3./ I simply compensated the voltage divider by adding 11 counts in the measuring loop. In addition the command 'analogReference(INTERNAL);' changes default reference voltage from 5V to 1.1V further increasing counts from the analog to digital converter.

The source code posted is as follows:

long int systemTime;    // replaces delay()
int AoutMin = 360;      // equal to Aout min in mV for clean air
int AoutMax = 3600;     // equal to Aout max in mV (toothpick in measuring window)
float dustConst;        // it will be computed from Aout range and Sharp sensor range 0-500 ug/m3
int dustConc;           // dust concentration in ug/m3/mV
unsigned int sum11;     // sum of 11 counts to compensate 11:1 Aot voltage divider
int AoutAvg;            // direct Sharp Vout in mV computed as average from 11 measurements
int ref1024 = 1100;     // reference voltage for 10-bit ADC; 1023 counts = 1100 mV
int dustQualityIndex;   // it is quite arbitrary
char *dustQualityChar[] = {"1.Excellent", "2.Very good", "3.Good", "4.Fair", "5.Poor"};

void setup() {
Serial.begin(9600);
pinMode(A0, INPUT);  // pin selected for 3.3V, GND, AOUT, and ILED
pinMode(A2, OUTPUT); // in order RED, BLACK, BLUE, and YELLOW as on WaveShare board
analogReference(INTERNAL); // for better resolution use 1100mV instead of default 5000mV
analogRead(A0);       // activate IOref pin, 1.1V will be present
dustConst = 500 / float(AoutMax - AoutMin); // in ug/m3 per millivolt
Serial.print("\n ------------------------------------------------------\n");
Serial.print(" The WaveShare board divides the Sharp Sensor Vout 11:1.\n");
Serial.print(" The sum of 11 counts will compensate for lowered output.\n");
Serial.print(" Then the sum is converted to mV, and from it\n");
Serial.print(" is computed dust concentration and air quality.");
Serial.print("\n-------------------------------------------------------\n");
Serial.print("500/(");
Serial.print(AoutMax);
Serial.print("-");
Serial.print(AoutMin);
Serial.print(")=");
Serial.print(dustConst);
Serial.print(" Dust density constant in ug/m3/mV");
Serial.print("\n-------------------------------------------------------\n");
}

void loop() {
sum11 = 0;
for (int i = 0; i < 11; i++) {
//Sharp datasheet: pulse cycle 10ms, pulse width 0.32ms, sampling at 0,28ms
digitalWrite(A2, HIGH);
delayMicroseconds(280);
delayMicroseconds(40);   // 280+40=320
digitalWrite(A2, LOW);
delayMicroseconds(9680); // 320 + 9680 = 10000
}
if ((millis() - systemTime) > 2000) {
computeAirQuality();
systemTime = millis();
}
}

void computeAirQuality()
{
AoutAvg = float(sum11) * float(ref1024) / 1024; // Sharp sensor direct Aout in mV
int q, i;
q = float(AoutAvg - AoutMin) * dustConst;
if (q < 0) q = 0; // handle non positive values

if (q < 40) i = 0;
else if (q < 80) i = 1;
else if (q < 160) i = 2;
else if (q < 320) i = 3;
else i = 4;

dustConc = q; dustQualityIndex = i;
printAirQuality();
}

void printAirQuality()
{
Serial.print(" Sum_11 ");
Serial.print(sum11);
Serial.print(" \t ");
Serial.print(AoutAvg);
Serial.print(" mV \tdust_C ");
Serial.print(dustConc);
Serial.print(" ug/m3\t\t");
Serial.print(dustQualityChar[dustQualityIndex]);
Serial.print(" air quality\n");
}


Demo code posted on Waveshare site

The demo Arduino program from the Waveshare site is doing a kind of moving average of the last ten values read from the sensor and then multiplying that average by 11.

/*********************************************************************************************************
*
* File                : DustSensor
* Hardware Environment:
* Build Environment   : Arduino
* Version             : V1.0.5-r2
* By                  : WaveShare
*
*                                       http://www.waveshare.net
*                                       http://www.waveshare.com
*
*********************************************************************************************************/
#define        COV_RATIO                       0.2            //ug/mmm / mv
#define        NO_DUST_VOLTAGE                 400            //mv
#define        SYS_VOLTAGE                     5000

/*
I/O define
*/
const int iled = 7;                                            //drive the led of sensor

/*
variable
*/
float density, voltage;

/*
private function
*/
int Filter(int m)
{
static int flag_first = 0, _buff[10], sum;
const int _buff_max = 10;
int i;

if(flag_first == 0)
{
flag_first = 1;
for(i = 0, sum = 0; i < _buff_max; i++)
{
_buff[i] = m;
sum += _buff[i];
}
return m;
}
else
{
sum -= _buff[0];
for(i = 0; i < (_buff_max - 1); i++)
{
_buff[i] = _buff[i + 1];
}
_buff[9] = m;
sum += _buff[9];

i = sum / 10.0;
return i;
}
}

void setup(void)
{
pinMode(iled, OUTPUT);
digitalWrite(iled, LOW);                                     //iled default closed

Serial.begin(9600);                                         //send and receive at 9600 baud
Serial.print("*********************************** WaveShare ***********************************\n");
}

void loop(void)
{
/*
*/
digitalWrite(iled, HIGH);
delayMicroseconds(280);
digitalWrite(iled, LOW);

/*
covert voltage (mv)
*/
voltage = (SYS_VOLTAGE / 1024.0) * adcvalue * 11;

/*
voltage to density
*/
if(voltage >= NO_DUST_VOLTAGE)
{
voltage -= NO_DUST_VOLTAGE;

density = voltage * COV_RATIO;
}
else
density = 0;

/*
display the result
*/
Serial.print("The current dust concentration is: ");
Serial.print(density);
Serial.print(" ug/m3\n");

delay(1000);
}

• I can see possible over-voltage issues of using a 5V boost supplied signal if Arduino shares the same V+ input. A series current limiting R should have been adequate for the signal. No good reason for 1/11. Averaging random noise improves st .dev by sqrt(n) so not much improvement. Dust tends to have a log scale, so a linear scale with a high threshold is pretty coarse. Dec 10, 2021 at 20:57
• @TonyStewartEE75 what do you mean by "Dust tends to have a log scale"? Do you have an article that would explain dust measurement? I think the underlying reason for the voltage divider is to drop the AOUT voltage to within the range of an A/D converter on an MCU. The Arduino code sets the range for the A/D on the board to be 0.0 v to 1.1 v. I'm not sure about other MCU devices such as STM32 however I suspect the value was chosen to have a range compatible with most A/D converters on common MCUs. Dec 11, 2021 at 5:25
• I have experience using a 16 channel Laser particle counter and the particle count increases inversely to the particle size over a several decades in random dust. However smoke particles cover a small range of large particles. I used this to measure office and manufacturing environments for HDD ranging up to 1M particles per cu.ft down to 100kp/cf. HEPA flow booths are 10kp/cf max, clean room rooms & HDD enclosures with recirculating HEPA filters are 1 to 10 p/cf all for >0.1um yet far more particles exist under this size . This range indicates 6 orders of magnitude. Dec 11, 2021 at 14:57
• I measured mass and counts by using air deflection for a calibrated nozzle air flow by deflecting particles with a transverse pitot tube with IR reflection and counts of particles per second then extrapolated to particles per cubic foot. Dec 11, 2021 at 15:01

Why are they doing an adjustment rather than just using a reading of the line and taking that value?

• The output from the device is 0.4V to 4.0V.
• The waveshare board is meant to be powered from a supply of 2.5V to 5.5V. This probably restricted them to using an ADC reference of 2.5V minus some headroom. For whatever reason, they picked 1.1V.
• With a 1.1V reference the A/D converter can only measure signals up to 1.1V max.
• Therefore, they needed to divide the signal by at least a factor or 3.64:1.

For some reason they have chosen to divide by 11.

• From a signal to noise ratio standpoint, 11 is not optimal because you get 1~2 less bits in your reading.
• The resistor values are nice round numbers and are probably cheap and easy to get.
• An 11:1 divider has high input impedance (so it won't load down the sensor, and protects the board from faults), but lower output impedance going to the ADC. See Why is high input impedance good?
• Adding 11 measurements together reduces overall noise compared to taking a single measurement. Also using addition doesn't require that the MCU can multiply numbers, so they can use a really cheap MCU.
• 11 is an integer. If they divided by a fraction, then reconstructing the original reading may have required floating point math, and therefore a more expensive microcontroller.
• Is "output from the device" referring to the VO line from the Sharp GP2Y1010AU0F dust sensor? Most of Waveshare board seems to be power supply/conversion for the GP2Y1010AU0F and a voltage divider for the VO of the GP2Y1010AU0F to produce AOUT with smaller range for the board? The A/D converter you mention is on the Arduino? I don't recognize an A/D in the Waveshare board schematic. GP2Y1010AU0F max AOUT is 4.0 v so divided by 11 is 0.36 v so using Arduino A/D set analogReference(INTERNAL) sets A/D range to 0.0 v to 1.1 v.. Waveshare chose common, cheap resistors to convert AOUT below 1.1v? Dec 11, 2021 at 1:49