Chapter 10: Sensing Light and Temperature


We have learned that computers sense their environment and, based on what is sensed, they make decisions and control things in their environment. Computers sense, decide and act. We learned in Chapter 4 how to sense a button press and based on that information decide to act by turning an LED on or off. Your PC senses keyboard button presses using techniques similar to what we've learned and then based on those presses, makes decisions and acts. For instance, if you are using a word processing program, the PC senses that you press the 'a' button, then decides to act by displaying an 'a' on the screen. And, while that is a very simple sequence, the basic principles of sensing, deciding and acting are the same sequence used by the most complex computing and electronics systems. When one of Google's autonomous cars drives itself through San Francisco - a very complex task - it is simply sensing, deciding and acting. This month we will begin expanding what we can sense with the Arduino by learning to sense light and temperature.

You can download the Arduino source code for this chapter from:
a101_ch10_supplemental.zip

 

Chapter 10: Sensing Light and Temperature


We have learned that computers sense their environment and, based on what is sensed, they make decisions and control things in their environment. Computers sense, decide and act. We learned in Chapter 4 how to sense a button press and based on that information decide to act by turning an LED on or off. Your PC senses keyboard button presses using techniques similar to what we've learned and then based on those presses, makes decisions and acts. For instance, if you are using a word processing program, the PC senses that you press the 'a' button, then decides to act by displaying an 'a' on the screen. And, while that is a very simple sequence, the basic principles of sensing, deciding and acting are the same sequence used by the most complex computing and electronics systems. When one of Google's autonomous cars drives itself through San Francisco - a very complex task - it is simply sensing, deciding and acting. This month we will begin expanding what we can sense with the Arduino by learning to sense light and temperature.

You can download the Arduino source code for this chapter from:
a101_ch10_supplemental.zip

Sensing Light with a CdS Light Dependent Resistor

 

Figure 1: CdS light dependent resistor photo

 

Figure 2: CDS sensor breadboard and schematic symbols



One of the early discoveries in electricity was that some materials have a resistance that varies with the amount of light that falls on them. Cadmium Sulfide (CdS) is one such material. This light dependent resistance was used to measure light levels for photography. Today this and similar material are used in a variety of applications including turning on lights at dusk and then turning them off again at dawn. We also see them in sensors in our entertainment devices that detect infrared light and are used to receive commands from hand-held remote controls. Digital cameras have millions of light sensors to detect the red, green and blue light falling on each sensor picture element.

Our CdS light sensor has a resistance that is inversely proportional to the incident light. The higher the light, the lower the resistance. We can use this by applying what we learned in Chapters 6 and 7 about using resistors as voltage dividers.

In Lab 2 of Chapter 7, we learned that if we placed a potentiometer (a variable resistor) in series with a constant resistor so that we can measure the voltage change due to the potentiometer position. When we vary the angle of the potentiometer (thus changing its resistance) then the current flowing into the constant resistor depends on that current variation. This variation in current changes the voltage across the constant resistor which we then measure using ADC and the analogInput() command. The light sensor works much like the potentiometer, so it can be placed in series with a constant resistor and we can measure the voltage across that resistor as an indicator of the changing resistance of the light sensor.

Figure 3: How a light sensor works



As we see in Figure 3, the voltage will vary because the resistance is varying in the sensor thus acting as a sort of valve controlling the current through the 10k ohm resistor. And, as we did in Chapter 8, we measure the voltage across the 10k ohm resistance and find that this value is proportional to the light falling on the sensor.

To further illustrate this, I built the circuit shown in Lab 1 and placed it under a very bright light. I then used my hand to shade the sensor, moving my hand up and down to vary the light falling on the sensor. I got the following voltages:

Covered: 0.415 volts
2 inch: 2.222 volts
4 inches: 2.627 volts
6 inches: 2.964 volts
8 inches: 3.311 volts
10 inches: 3.374 volts
Uncovered: 3.877 volts

We can clearly see that we are getting measurable voltage variations from the varying light falling on the sensor. We can use those voltages to control a device, much like in Chapter 8 where we used the varying voltages from a potentiometer to control the brightness of an LED or the angle of a servomotor. If we had a very bright light such as the sun at noon, I expect we would get significantly different results from those shown here, but there still would be a proportional relation to the voltage and the hand blocking height.

Let's reinforce our learning about Ohm's law to calculate the resistance across the light sensor at each of the readings.

Current = Voltage/Resistance (I = V/R)

Covered: Current = 0.415/10000 = 0.0000415 Amps
2 inch: Current = 2.222/10000 = 0.0002222 Amps
4 inches: Current = 2.627/10000 = 0.0002627 Amps
6 inches: Current = 2.964/10000 = 0.0002964 Amps
8 inches: Current = 3.311/10000 = 0.0003311 Amps
10 inches: Current = 3.374/10000 = 0.0003374 Amps
Uncovered: Current = 3.877/10000 = 0.0003877 Amps

This tells us what portion of the 5 volts is dropped across the 10k ohm resistor. The rest of that 5 volts is dropped across the light sensor so we calculate the voltage across it as:

Covered: Sensor voltage = 5 - 0.415 = 4.585
2 inch: Sensor voltage = 5 - 2.222 = 2.778
4 inches: Sensor voltage = 5 - 2.627 = 2.373
6 inches: Sensor voltage = 5 - 2.964 = 2.036
8 inches: Sensor voltage = 5 - 3.311 = 1.689
10 inches: Sensor voltage = 5 - 3.374 = 1.626
Uncovered: Sensor voltage = 5 - 3.877 = 1.123

And then using Ohm's law to get the light sensor resistance:

Sensor resistance = Voltage/Current (R = V/I)
Covered: Sensor resistance = 4.585/0.0000415 = 110482 ohm
2 inch: Sensor resistance = 2.778/0.0002222 = 12513 ohm
4 inches: Sensor resistance = 2.373/0.0002627 = 9033 ohm
6 inches: Sensor resistance = 2.036/0.0002964 = 6869 ohm
8 inches: Sensor resistance = 1.689/0.0003311 = 5101 ohm
10 inches: Sensor resistance = 1.626/0.0003374 = 4819 ohm
Uncovered: Sensor resistance = 1.123/0.0003877 = 2897 ohm

So what this is telling us is that when the light sensor is exposed to very little light the resistance is over 110000 ohm and that a very little extra light (hand at 2 inches) drops that resistance to just over 12500 ohm. The resistance progressively drops as more light is added (the hand is raised) bringing it down to about 2900 ohm when the light sensor is uncovered. This doesn't really tell us anything about the absolute value of that light as measured in some accepted dimension such as lux, but it does tell us that we are able to get a relative change in voltage as a function of changes in light level due to moving our hand. We will use these facts in our labs.

Sensing Temperature with a Thermistor IC


A thermistor is a resistor whose resistance varies with temperature. This is similar to the light sensor that varies resistance with light levels. The Arduino 101 Projects Kit has a MCP9700A Thermistor IC (Integrated Circuit) that contains signal-conditioning circuitry that outputs a voltage proportional to the change in temperature. This circuitry simplifies our task since we can use the Arduino ADC to measure the voltage and directly translate the reading to a temperature. The IC uses a T0-92 package as shown in Figure 4. Figure 5 shows a drawing from the data-sheet that illustrates the pin locations. Figure 6 has the breadboard and schematic symbols that we will use. This device measures in Celsius and its accuracy is +- 2° C from -40° C to 125° C. For those of us in America, we also translate Celsius to Fahrenheit in our Lab software using the following formula: °F = °C x 9/5 + 32.

Figure 4: Temperature sensor photo

 

Figure 5 - Temperature sensor datasheet drawing

 

Figure 6: Temperature sensor breadboard and schematic symbols



The MCP9700A outputs 10.0 mV/°C (10.0 millivolts [.01 volts] per degree Celsius) scaled for 500mV output at 0°C. Thus if we measure 500mV (0.5V) on the ADC, we know the sensor is detecting 0 °C. We can use this base point: 500mV for 0 °C to calculate the voltage for the maximum accurate temperature: 125 °C, and the minimum accurate temperature: -40 °C. Stop for a moment and think how you would do this. First look at the maximum accurate temperature: 125 °C. We know that we have 500 mV for 0 °C and that the sensor outputs 10 mV per °C. This means we have 10 mV per °C for 125 °C so we will see an additional 10*125 = 1250 mV above the mV level for 0 °C. So we add the mV at 0 °C, 500mv, to the 1250 mV for 125 °C and calculate that we would read 1750 mV as the voltage for our maximum accurate temperature reading. If this isn't clear then please write it out and do the calculations by hand so that you understand how to convert from volts to temperature.

Let's repeat this process to determine the voltage for the minimum accurate temperature, which is -40 °C. See if you can do this before reading further. We have a total of -40 °C, which would equal -400 mV change. Since we know that we would read 500 mV for 0 °C then we can subtract the 400 mV to get a -40 °C reading of 500 - 400 = 100 mV. We now know that if we read 100mV we have -40 °C, that if we read 500 mV we have 0 °C and that if we read 1750 mV we have 125 °C. Clear?

We saw in Chapters 6 and 7 that our ADC looks at voltages from 0 to 5 volts and divides that into 1024 steps with 0 being 0 volts and 1023 being 5 volts. We further saw that each ADC step represents 5/1024 = 0.00488 volts (4.88 mV). We know that the sensor outputs 10mV per °C, so how many steps in the ADC output would indicate 10mV? We see that the 10mV divided by 4.88 mV gives us 2.04918 steps, but the ADC steps are integers so we are going to have to do some math here. Two steps of the ADC (2 * 4.88mV) = 9.77 mV, so two steps are pretty close to a single °C change in temperature (10mV). Notice that the error between 10 and 9.77 is just under 1% and since the error for the sensor itself is 2% we might be safe ignoring the error and saying that two steps in the ADC output is equal to exactly 1 °C. Doing that would allow us to use integers rather than floating point data (data type that has integer and fractional parts divided by a decimal point) which use a lot more of the computer's resources to do calculations that when we use integers. Using the integer simplification would probably be good enough for most applications and would save us some serious computing. However, since we aren't doing a lot of extra work with this program, we will go ahead and write the code using floating-point data types. [To rephrase: in a more constrained program that does a lot of work and takes up a lot of space we probably wouldn't use floating-point, but accept the error and say that 2 ADC steps equals 1 °C.]

Or saying this another way, we know that the temperature sensor will output voltages between 100mV and 1750mV to indicate -40 °C to +125°C. The ADC outputs values from 0 to 1023 in steps representing 4.88mV per step. This tells us that the lowest valid ADC reading will be for 100mV giving us: 100mV/4.88mV/step = 20.49 step. But, of course, these steps are integers so we will say that the lowest valid step value is 21 which equals 21step * 4.88mV/step = 102.48 mV. We know that the temperature sensor outputs 100mV for -40 °C so 102.48 mV is slightly above that. We know that the sensor outputs 10 mV per °C, so we can calculate 2.48 mV / 10 mV/ °C = .248 °C giving us a temperature of - 40 °C + .248 °C = -39.75 °C. Of course our real accuracy isn't quite that good, but for our purposes here, we say that our systems lowest accurate temperature is -39.75 °C.

We can repeat this logic to calculate the highest accurate temperature reading: 1750 mV / 4.88 mV/step = 358.61 steps (359 steps since it is an integer) But when we calculate 359 step * 4.88 mV/step = 1751.92 mV we see that the 359 takes us above the highest accurate temperature output, that is 125 °C at 1750 mV, so lets drop the maximum step to 358 and we calculate 358 step * 4.88 mV/step = 1747.04 mV which is in range. We now see that the range of accurate readings from our ADC will be 21 to 358 out of a possible range for the ADC or 0 to 1023. For our purposes we should consider any ADC readings below 21 or above 358 as invalid and ignore them when we do our software conversions of the ADC readings to voltage and then °C.

Since this discussion is fairly complex, we will have a Lab to use all this in a test program to convert fake ADC readings to voltage and °C.

Lab 1: Sensing relative light levels


This lab implements the light measurement discussed above. This circuit works like the one shown in Figure 3, except we use our hand instead of clouds to vary the light falling on the sensor. We then use the Arduino ADC to measure the changing voltage across the resistor.

Parts Required:
1 Arduino
1 USB cable
1 Arduino Proto Shield and jumper wires
1 CdS light sensor
1 10000 ohm resistor

Estimated time for this lab: 30 minutes

Check off when complete:
Build the circuit shown in Figures 7, 8 and 9.

Figure 7: CdS sensor circuit breadboard

 

Figure 8: CdS sensor circuit schematic

 

Figure 9: CdS sensor circuit photo


Either copy andd paste, or type in the following program into the Arduino IDE. 

// A101_ch10_light_sensor_voltage 7/2/14 Joe Pardue
int sensorPin = A0;  // analog input pin
int sensorValue = 0;  // store the analog input value

>void setup() {
  Serial.begin(57600);
  Serial.println("Measure light sensor voltage rev 1.0");
}

void loop() {
  if(Serial.available())
  {
    char c = Serial.read();
    if(c == 'r')
    { 
     // read the value from the sensor:
      sensorValue = analogRead(sensorPin);      
      Serial.print("LDR voltage: ");
      Serial.print(sensorValue);      
      Serial.print("  Voltage: ");      
      Serial.println(((5.0*(float)sensorValue)/1024.0), 3); 
   }
  }                
}


Notice that this program is identical to the A101_ch7_pot_voltage.ino program from Chapter 7, except that some comments are changed. This powerfully illustrates the value of code reuse.
Load the program to your Arduino and then run the Arduino Serial Monitor.
Repeat the experiment described earlier where you cover the sensor with your hand and then move it roughly 2 inches up for each subsequent measurement.
You should get results that appear somewhat like those in Figure 10.

Figure 10: Measuring light sensor voltage

 

Lab 2: Converting ADC sensor readings to temperature


Converting Arduino ADC readings to voltage and then converting those voltages to temperature readings is somewhat complicated. To reinforce the earlier discussion we will write a program to create a series of faked ADC readings and verify that the techniques we discussed work as described.

Parts Required:
1 Arduino
1 USB cable

Estimated time for this lab: 30 minutes

Check off when complete:
Either copy and paste, or type in the following program into the Arduino IDE.

// A101_ch10_volts_to_temp_test 8/14/14

#define VOLTS_PER_ADC_STEP 5.0/1024.0
#define VOLTS_PER_DEGREE 0.01 
#define temp_BASE -40.0
#define LOW_ADC 20.49
#define HIGH_ADC 358.61
#define LOW_VOLTAGE 0.5

int temperatueSensorPin = A0;  // analog input pin
float tempSensorADC = 0.0; // variable to for the ADC reading
float tempertureSensorVoltage = 0.0; // variable for the converted voltage
float tempSensorVoltage = 0.0; // variable for the sensor voltage
float tempCelcius = 0.0; // variable for degrees Celcius

void setup()
{
  Serial.begin(57600);
  Serial.println("Volts to temp test. rev 1.0");
 
 // Look from lowest ADC reading in +10 steps
 for(float i = LOW_ADC; i < HIGH_ADC; i += 10.0)
 { 
  tempSensorADC = i;
  tempSensorVoltage = (tempSensorADC * VOLTS_PER_ADC_STEP);
  tempCelcius = ((tempSensorVoltage-0.5) / VOLTS_PER_DEGREE);

  showIt(); 
 }
 
  // Look at highest acceptable ADC reading
  tempSensorADC = HIGH_ADC;
  tempSensorVoltage = (tempSensorADC * VOLTS_PER_ADC_STEP);
  tempCelcius = ((tempSensorVoltage - 0.5)/ VOLTS_PER_DEGREE);
  showIt();
}

void loop()
{
  // do nothing 
}

void showIt()
{
  Serial.print("tempSensorADC = ");
  Serial.println(tempSensorADC);
  Serial.print("tempSensorVoltage = ");
  Serial.println(tempSensorVoltage); 
  Serial.print("Temperature in Celcius = ");
  Serial.println(tempCelcius); 
  Serial.print("Temperature in Fahrenheit = ");
  // °F = °C  x  9/5 + 32 
  Serial.println( ((tempCelcius * 9)/5) + 32);   
}


Compile and run the program and verify that you get the output shown in Figure 8.
Make sure you are clear on what is going on in this program and if necessary then review the earlier discussion and the source code.

Figure 11: Volts to temperature test

 

Lab 3: Sensing temperature


For this lab we will reuse the circuit built for the above labs and we will add the temperature sensor to it. We won't use the light sensor in this Lab, but in the next chapter we will use both sensors together.

Parts Required:
1 Arduino
1 USB cable
1 Arduino Proto Shield and jumper wires
1 CdS light sensor
1 10000 ohm resistor
1 MCP9700A temperature sensor

Estimated time for this lab: 30 minutes

Check off when complete:
Build the circuit shown in Figures 9 and 10.

Figure 12: Temperature sensor breadboard

 

Figure 13: Temperature sensor schematic



Either copy and paste, or type in the following program into the Arduino IDE.

// A101_ch10_sensing_temperature 8/15/14

#define VOLTS_PER_ADC_STEP 5.0/1024.0
#define VOLTS_PER_DEGREE 0.01 
#define temp_BASE -40.0
#define LOW_ADC 20.49
#define HIGH_ADC 358.61
#define LOW_VOLTAGE 0.5

int tempSensorPin = A1;  // analog input pin for temperature sensor

float tempSensorADC = 0.0; // variable to for the ADC reading
float tempertureSensorVoltage = 0.0; // variable for the converted voltage
float tempSensorVoltage = 0.0; // variable for the sensor voltage
float tempCelsius = 0.0; // variable for degrees Celsius

void setup()
{
  Serial.begin(57600);
  Serial.println("Sensing Temperature. rev 1.0");
}

void loop()
{
  tempSensorADC = analogRead(tempSensorPin);
  tempSensorVoltage = (tempSensorADC * VOLTS_PER_ADC_STEP);
  tempCelsius = ((tempSensorVoltage - 0.5)/ VOLTS_PER_DEGREE); 
  
  Serial.print("Temperature in Celsius = ");
  Serial.println(tempCelsius); 
  Serial.print("Temperature in Fahrenheit = ");
  // °F = °C  x  9/5 + 32 
  Serial.println( ((tempCelsius * 9)/5) + 32);  
  
  delay(5000);
}


Compile and run the program.
After the first temperature reading, gently squeeze the sensor between your thumb and forefinger. Hold the sensor for several readings then release it. Verify that you get the temperature rise and fall output similar to that shown in Figure 14.

Figure 14: Temperature sensing output