The circuit in the previous activity only works over a limited light range. You might get the Activity #1 circuit all nice and calibrated in one room, then take it to a brighter room and find that all the voltage measurements will sit at the maximum value. Or, maybe you’ll take it into a darker room, and the voltages will end up never making it past 0.1 V.
This activity introduces a different phototransistor circuit that the Arduino can use to measure a much wider range of light levels. This circuit and sketch can return values ranging from 0 to over 75,000. Be aware: this time the smaller values indicate bright light, and large values indicate low light.
A capacitor is a device that stores charge, and it is a fundamental building block of many circuits. Batteries are also devices that store charge, and for these activities it will be convenient to think of capacitors as tiny batteries that can be charged, discharged, and recharged.
How much charge a capacitor can store is measured in farads (F). A farad is a very large value that’s not practical for use with these BOE Shield-Bot circuits. The capacitors in your kit store fractions of millionths of farads. A millionth of a farad is called a microfarad, and it is abbreviated μF. This one stores one tenth of one millionth of a farad: 0.1 μF.
microfarads: (millionths of a farad), abbreviated μF 1 μF = 1×10-6 F
nanofarads: (billionths of a farad), abbreviated nF 1 nF = 1×10-9 F
picofarads: (trillionths of a farad), abbreviated pF 1 pF = 1×10-12 F
The 104 on the 0.1 μF capacitor’s case is a measurement in picofarads or (pF). In this labeling system, 104 is the number 10 with four zeros added, so the capacitor is 100,000 pF, which is 0.1 μF.
(100,000) × (1 × 10-12) F = (100 × 103) × (1 × 10-12) F
= 100 × 10-9 F = 0.1 × 10-6 F
= 0.1 μF.
These circuits can respond independently to the light level reaching each phototransistor. They will be pointing upward at about 45°, one forward-left and the other forward-right. This way, a sketch monitoring the values of both phototransistors can determine which side of the BOE Shield-Bot sees brighter light. Then, this information can be used for navigation decisions.
(2) phototransistors
(2) capacitors, 0.1 μF (104)
(2) resistors, 1 kΩ (brown-black-red)
(2) jumper wires
The roaming examples in this chapter will depend on the phototransistors being pointed upward and outward to detect differences in light levels from different directions.
Think of each capacitor in this circuit as a tiny rechargeable battery, and think of each phototransistor as a light-controlled current valve. Each capacitor can be charged to 5 V and then allowed to drain through its phototransistor. The rate that the capacitor loses its charge depends on how much current the phototransistor (current valve) allows to pass, which in turn depends on the brightness of the light shining on the phototransistor’s base. Again, brighter light results in more current passing, shadows result in less current.
This kind of phototransistor/capacitor circuit is called a charge transfer circuit. The Arduino will determine the rate at which each capacitor loses its charge through its phototransistor by measuring how long it takes the capacitor’s voltage to decay, that is, to drop below a certain voltage value. The decay time corresponds to how wide open that current valve is, which is controlled by the brightness of the light reaching the phototransistor’s base. More light means faster decay, less light means slower decay.
QT Circuit: A common abbreviation for charge transfer is QT. The letter Q refers to electrical charge (an accumulation of electrons), and T is for transfer.
Connected in Parallel: The phototransistor and capacitor shown in Figure 6‑11 are connected in parallel; each of their leads are connected to common terminals (also called nodes). The phototransistor and the capacitor each have one lead connected to GND, and they also each have one lead connected to the same 1 kΩ resistor lead.
The sketch LeftLightSensor charges the capacitor in the pin 8 QT circuit, measures the voltage decay time, and displays it in the Serial Monitor. Remember, with this circuit and sketch, lower numbers mean brighter light.
We’ll be using this light-sensing technique for the rest of the chapter, so you can take the BOE Shield-Bot from one room to another without having to worry about finding the right resistors for different ambient light levels.
/* * Robotics with the BOE Shield - LeftLightSensor * Measures and displays microsecond decay time for left light sensor. */ void setup() // Built-in initialization block { tone(4, 3000, 1000); // Play tone for 1 second delay(1000); // Delay to finish tone Serial.begin(9600); // Set data rate to 9600 bps } void loop() // Main loop auto-repeats { long tLeft = rcTime(8); // Left rcTime -> tLeft Serial.print("tLeft = "); // Display tLeft label Serial.print(tLeft); // Display tLeft value Serial.println(" us"); // Display tLeft units + newline delay(1000); // 1 second delay } // rcTime function at pin long rcTime(int pin) // ..returns decay time { pinMode(pin, OUTPUT); // Charge capacitor digitalWrite(pin, HIGH); // ..by setting pin ouput-high delay(1); // ..for 5 ms pinMode(pin, INPUT); // Set pin to input digitalWrite(pin, LOW); // ..with no pullup long time = micros(); // Mark the time while(digitalRead(pin)); // Wait for voltage < threshold time = micros() - time; // Calculate decay time return time; // Return decay time }
Before moving on to navigation, you’ll need to run the same test on the right (pin 6) light sensor circuit. Both circuits have to be working well before you can move on to using them for navigation—there’s that subsystem testing again!
It would also be nice to have a third sketch that tests both phototransistor circuits.
void loop() // Main loop auto-repeats { long tLeft = rcTime(8); // Left rcTime -> tLeft Serial.print("tLeft = "); // Display tLeft label Serial.print(tLeft); // Display tLeft value Serial.print(" us "); // Display tLeft units long tRight = rcTime(6); // Left rcTime -> tRight Serial.print("tRight = "); // Display tRight label Serial.print(tRight); // Display tRight value Serial.println(" us"); // Display tRight units + newline delay(1000); // 1 second delay }
When light levels are low, the rcTime function might take time measurements too large for int or even word variables to store. The next step up in storage capacity is a long variable, which can store values from -2,147,483,648 to 2,147,483,647. So, the function definition long rcTime(int pin) is set up to make the function return a long value when it’s done. It also needs to know which pin to measure.
long rcTime(int pin)
A charge transfer measurement takes seven steps: (1) Set the I/O pin high to charge the capacitor. (2) Wait long enough for the capacitor to charge. (3) Change the I/O pin to input. (4) Check the time. (5) Wait for the voltage to decay and pass below the Arduino’s 2.1 V threshold. (6) Check the time again. (7) Subtract the step-3 time from the step-6 time. That’s the amount of time the decay took.
{ pinMode(pin, OUTPUT); // Step 1, part 1 digitalWrite(pin, HIGH); // Step 1, part 2 delay(1); // Step 2 pinMode(pin, INPUT); // Step 3 part 1 digitalWrite(pin, LOW); // Step 3, part 2 long time = micros(); // Step 4 while(digitalRead(pin)); // Step 5 time = micros() - time; // Step 6 & 7 return time; }
In this sketch, Step 1 has two sub-steps. First, pinMode(pin, OUPUT) sets the I/O pin to an output, then digitalWrite(pin, HIGH) makes it supply 5 V to the circuit. Step 3 also has two sub-steps, because the I/O pin is sending a high signal. When the sketch changes the I/O pin’s direction from output-high to input, it adds 10 kΩ of resistance to the circuit, which must be removed. Adding digitalWrite(pin, LOW) after pinMode(pin, INPUT) removes that resistance and allows the capacitor to drain its charge normally through the phototransistor.
The graph below shows the BOE Shield-Bot’s left and right QT circuit voltage responses while the BothLightSensors sketch is running. The device that measures and graphs these voltage responses over time is called an oscilloscope. The two lines that graph the two voltage signals are called traces. The voltage scale for the upper trace is along the left, and the voltage scale for the lower trace is along the right. The time scale for both traces is along the bottom. Labels above each trace show when each command in BothLightSensors executes, so that you can see how the voltage signals respond.
The upper trace in the graph plots the capacitor’s voltage in the pin 8 QT circuit; that’s the left light sensor. In response to digitalWrite(8, HIGH), the voltage quickly rises from 0 V to almost 5 V at about the 1 ms mark. The signal stays at around 5 V for the duration of delay(1). Then, at the 2 ms mark, the rcTime call causes the decay to start. The rcTime function measures the time it takes the voltage to decay to about 2.1 V and stores it in the tLeft variable. In the plot, it looks like that decay took about 1 ms, so the tLeft variable should store a value close to 1000.
The lower trace in the graph plots the pin 6 QT circuit’s capacitor voltage—the right light sensor. This measurement starts after the left sensor measurement is done. The voltage varies in a manner similar to the upper trace, except the decay time takes about 2 ms. We would expect to see tRight store a value in the 2000 neighborhood. This larger value corresponds to a slower decay, which in turn corresponds to a lower light level.