30% off your first 3 months

Get the deal

Reading a sensor with a microcontroller

by DuckyHelperNov 12, 20265 min read
Robotics

TL;DR:

  • Many sensors change their resistance. A voltage divider turns that change into a voltage the microcontroller can read.

  • An analog-to-digital converter (ADC) turns the voltage into a number, such as 0 to 1023 for 10 bits.

  • A digital pin only sees high or low, and an input with nothing driving it needs a pull-up or pull-down resistor.

A light-dependent resistor is a part whose resistance falls as more light hits it. A microcontroller cannot measure resistance directly. It can measure voltage. So the first step in reading almost any sensor is turning what it changes into a voltage.

Voltage dividers

Two resistors in series across a supply split its voltage. With the supply Vcc across R1 on top and R2 on the bottom, the voltage at the middle is Vout = Vcc × R2 / (R1 + R2). If one of the two is a sensor whose resistance changes, Vout changes with it. This arrangement is a voltage divider.

The same divider rule shows up everywhere in electronics, from reading a battery level to setting a reference. Always work out the current as well: a total of 11 kΩ on 5 V draws about 0.45 mA, which is tiny and safe for these parts.

The ADC

An analog-to-digital converter (ADC) measures a voltage and returns a whole number. A 10-bit ADC has 2¹⁰ = 1024 levels, numbered 0 to 1023. If its reference voltage is 5 V, then 0 V reads 0 and 5 V reads 1023, and each step is 5 / 1023 ≈ 4.89 mV. The conversion is count = Vin / Vref × 1023.

The ADC has limits you must respect. Never feed it more than the board's supply voltage, since a 3.3 V board can be damaged by 5 V on a pin. And the last bit or two of a reading flicker with noise, so an average of several readings is steadier.

Worked example: analog vs digital readings from a light sensor

Build a divider on a 5 V board: a 10 kΩ fixed resistor from the middle node to ground, and a light-dependent resistor from 5 V to the middle node. The middle node goes to an analog input. Say the sensor measures 1 kΩ in bright light and 100 kΩ in the dark (typical orders of magnitude, so measure your own part with a multimeter).

  1. Bright: Vout = 5 × 10 / (10 + 1) = 5 × 0.909 = 4.545 V. ADC count: 4.545 / 5 × 1023 ≈ 930.

  2. Dark: Vout = 5 × 10 / (10 + 100) = 5 × 0.0909 = 0.4545 V. ADC count: 0.4545 / 5 × 1023 ≈ 93.

  3. So the analog reading runs from about 93 in the dark to about 930 in bright light, with plenty of values in between for dim and normal rooms.

Check the direction: more light, lower sensor resistance, more of the 5 V drops across the fixed resistor, so the middle voltage rises. If you swap the two parts, the readings invert. This is a common source of confusion when a robot seems to love the dark.

Now the same signal on a digital pin. A digital input only reports high or low. On a typical 5 V microcontroller, a voltage above about 0.6 × 5 = 3 V reads as high, and a voltage below about 0.3 × 5 = 1.5 V reads as low. In between is undefined. Our bright reading, 4.545 V, is clearly high. The dark reading, 0.4545 V, is clearly low. A dim room that gives 2.2 V falls in the forbidden middle, and the pin may flicker. That is why analog readings with your own threshold work better for gradual signals: for instance, if (reading > 500).

const int LIGHT_PIN = A0;

void setup() {
  Serial.begin(115200);
}

void loop() {
  int count = analogRead(LIGHT_PIN);        // 0..1023
  float volts = count * 5.0 / 1023.0;       // for a 5 V reference
  Serial.print(count);
  Serial.print(" ");
  Serial.println(volts);
  delay(100);
}

Pull-up resistors

A digital input wired to nothing is floating: it picks up stray electric fields and reads random highs and lows. Take a push button. If you connect the button between the pin and ground, then when it is not pressed the pin is connected to nothing. A pull-up resistor from the pin to the supply gives it a default high. Pressing the button connects the pin to ground, and it reads low.

The resistor must be large enough not to waste current when the button is pressed: with a 10 kΩ pull-up on 5 V, pressing the button draws 5 / 10000 = 0.5 mA. Most microcontrollers have built-in pull-ups you can turn on in code, with pinMode(pin, INPUT_PULLUP), so no external part is needed. Then the logic is inverted: pressed reads low. A pull-down resistor does the opposite and defaults to low.

Safety: Keep sensor experiments at low voltage from a battery or USB supply, and never connect a pin to mains power. Check that the sensor's voltage matches your board's logic level before wiring.

Where it goes wrong

  • Wrong reference. The count-to-volts formula needs the real reference voltage, which may be 3.3 V, not 5 V.

  • Loose or long wires. Noise rises and readings jump. Keep analog wires short.

  • Divider loading. If the divider resistors are very large, the ADC input itself disturbs the voltage. Moderate values like 10 kΩ are a good start.

  • Floating inputs. Forgetting the pull-up gives random button presses.

  • Sharing a reference. A sensor powered from a noisy motor supply gives noisy readings.

How real robots use it

Line sensors, battery monitors, force sensors, joint potentiometers and many distance sensors use an analog input. Switches, bump sensors and encoders use digital ones. The next step is reacting to a digital edge as it happens, which is the topic of interrupts and timing on a microcontroller. The board choice itself is discussed in microcontrollers vs single-board computers.

Try it yourself

Wire the divider above with a light-dependent resistor and read it over the serial monitor. Cover the sensor with your hand and note the counts, then shine a phone torch on it and note them again. Calculate the voltage for each and check it against a multimeter reading at the middle node.

Frequently asked questions

Why 1023 and not 1024?

There are 1024 levels, numbered 0 to 1023. The top value is 1023, so the full-scale formula uses 1023.

Can I read a digital sensor with the ADC?

You can, but you would just see two values. Digital sensors that send data, such as over I2C, need their own communication code instead.

Get started

DuckyHelper can
do more

It sees your screen and works in any app you use. Try it for 10 minutes, no card.

Get it for Mac, Windows or Linux

Share

Questions and jobs

Do anything

Ask it a question or hand it a job, in any app.

Doing jobs for you is in early access on Mac. Asking works on Mac, Windows and Linux.