How to scale to 8 bits

In the tutorials:

I found the sample code:

This function will read a sensor five times with analogRead() and calculate the average of five readings. It then scales the data to 8 bits (0-255), and inverts it, returning the inverted result.

int ReadSens_and_Condition(){
int i;
int sval = 0;

for (i = 0; i < 5; i++){
sval = sval + analogRead(0); // sensor on analog pin 0
}

sval = sval / 5; // average
sval = sval / 4; // scale to 8 bits (0 - 255)
sval = 255 - sval; // invert output
return sval;
}

What's the mathematical explanation to the lines:
sval = sval / 4; // scale to 8 bits (0 - 255)
sval = 255 - sval; // invert output

Does it matter what range of voltage the sensor senses? it's not written
What's the explanation to the divison by 4?
Is there a site to learn?
Thanks

analogRead() delivers an integer between 0 and 1023. (10 bit)
Divide it by 4 to get an integer between 0 and 255. (8 bit)
Subtract the result from 255 to get an integer in the range 255 to 0. (Inverted)

KarolR:
What's the mathematical explanation to the lines:

KarolR:
sval = sval / 4; // scale to 8 bits (0 - 255)

Does it matter what range of voltage the sensor senses?

the sensor range does matter for the above equation. 'analogRead(0)' is uses a 10bit ADC on most Arduinos (0-1023)

1024/256 = 4 <--- this is why it is dividing by 4 to scale to 8bits

hope that helps

ps 6v6gt beat me to it! :slight_smile:

KarolR:
What's the mathematical explanation to the lines:
sval = sval / 4; // scale to 8 bits (0 - 255)
sval = 255 - sval; // invert output

Does it matter what range of voltage the sensor senses? it's not written
What's the explanation to the divison by 4?

1. Assume sval = 656 = 0x0290 = 0000 0010 1001 0000 = 1010010000 (10-bit active)
2. x = sval/4 = 164 = 0x00A4 = 10100100 (information in sval is not lost; sval is just compressed)
3. invert of x = not(x) = y = 01011011 = 0x5B = 91
4. y = 255 - sval = 255 - 164 = 91 (you got it).

The issue of concern is: analogRead(A0) has the range: 0 to 1023. We want to compress this value over the scale of 0 to 255; so, divide by 4 should be alright. It can be verified by the following map() function.

x (compressed value = sval/4) can also be obtained by using the following map() function:

byte z = map(x, 0, 1023, 0, 255);
Serial.print(z, HEX); //shows: A3 or A4 (163 or 164)

Or, you could just right shift sval by 2 (same as dividing by 4 with integers):

sval >>= 2;

KarolR:
Does it matter what range of voltage the sensor senses? it's not written

If you are using an UNO, or Mega the voltage must NEVER exceed 5v or be negative (i.e. less than 0v).

...R

6v6gt:
analogRead() delivers an integer between 0 and 1023. (10 bit)
Divide it by 4 to get an integer between 0 and 255. (8 bit)
Subtract the result from 255 to get an integer in the range 255 to 0. (Inverted)

What does it mean inverted?
If, for example, i measure 3v, the range is 0-5V (always?), does the function not(x) mean 5-3=2V?
Is the range always assumed 0-5V, not, for example, 0-3V? there are many kinds of sensors, though, no? probably not all return 0-5V, right?

KarolR:
What does it mean inverted?
If, for example, i measure 3v, the range is 0-5V (always?), does the function not(x) mean 5-3=2V?
Is the range always assumed 0-5V, not, for example, 0-3V? there are many kinds of sensors, though, no? probably not all return 0-5V, right?

Inverted in this context means that a high voltage on an analog pin is converted to a low value for a calculation.

Example for a 5 volt arduino (e.g. Uno )

5 volts on pin A0 would normally give an analog value of 1023 (the maximum which could be represented by a 10bit number)
3 volts on pin A0 would normally give an analog reading of 614.
2 volts on pin A0 would normally give an analog reading of 408.

However, you may want the result inverted. Say you had a light which should be bright during darkness and you measured the ambient light value with a photo cell. During darkness lets say the photocell causes a very low voltage on the analog input pin. You want to convert that low value to a high value to make the light bright (using PWM in this case).

If you took the 408 analog reading for 2 volts and inverted it you would get (with rounding errors) the analog reading for 3 volts.

The function not() does a logical inversion. That is it just changes true to false and false to true. It cannot operate on numbers.

A lot of sensors, say a light dependent resistor, are wired with a resistor across the arduino's power rails and divide the Arduino's voltage producing between say 0v and 5v on the analog pin depending on the light level.

A sensor which produced a maximum of 3 volts would show a maximum analog reading of 614 on an analog pin of a 5 volt arduino.

Depending on the application, it may be desirable to round the averaged result and/or the scaled result:

  uint16_t rawValue = 0;
  uint8_t scaledValue;

  for (uint8_t i = 0; i < 5; i++) {
    rawValue = rawValue + analogRead(0);    // sensor on analog pin 0
  }

  rawValue = float(rawValue) / 5.0 + 0.5;    // average and round
  scaledValue = (rawValue + 0x02) >> 2;  // round and scale
  scaledValue = ~scaledValue; // invert

6v6gt:
The function not() does a logical inversion. That is it just changes true to false and false to true. It cannot operate on numbers.

For example:

byte z = map(656, 0, 1023, 0, 255);
  Serial.println(z, BIN);  //z = 10100011
  byte x1 = not(z);        //x1 = 0 = LOW; z is non-zero = HIGH
  byte x2 = ~z;
  Serial.println(x2, BIN); //x2 = bit wise inversion of Z = 1011100

sp.byte z = map(656, 0, 1024, 0, 256);

I think I'd take 8 samples instead of 5. Then, average, round, and scale all in one shot:

 uint16_t rawValue = 0;
  uint8_t scaledValue;

  for (uint8_t i = 0; i < 8; i++) {
    rawValue = rawValue + analogRead(0);    // sensor on analog pin 0
  }

  scaledValue = (rawValue + 0x10) >> 5;  // average, round, & scale
  scaledValue = ~scaledValue; // invert