# ADC / Arduino Lab

<p class="block-synopsis">Wire a Sharp infrared distance sensor to an Arduino Due, apply the Objective 2.5 ADC math to the Due's own converter, then characterize and calibrate the sensor into a voltage-to-distance conversion good to ±0.5 cm — and prove it on distances you did not use to build it.</p>

:::{admonition} At a glance
:class: key-concept

Run at **Lesson 22**. Work in **pairs** — your partner **must not** be someone from your
Project 1 group. Ideally only one of you has programmed a microcontroller before. You
**demo your working sensor to your instructor** during the period and **individually**
answer the questions in **Gradescope**; together they are worth **1 project point**.

**GenAI is authorized only up to Level 3** — use of GenAI as a feedback tool on
student-generated work, as defined in the AW GenAI Policy. This is **stricter than the
course default.** Excel, MATLAB, or a similar tool is permitted for the curve fit.
:::

## Lab Goal

Block 2 has taken you from the frequency domain through filters, analog-to-digital
conversion, and signal conditioning. Today you build the whole chain yourself and make it
produce a number you can check against a ruler.

By the end of the period you will have a sketch that reads a Sharp infrared distance sensor
and prints **distance in centimeters**, accurate to **±0.5 cm anywhere from 2 to 15 cm** —
and you will have evidence that it is that accurate, measured at distances you deliberately
kept out of the calibration.

That accuracy claim is the whole lab. Getting *a* number out of the sensor takes ten
minutes. Getting a number you can defend is the engineering.

## Equipment

- **Arduino Due** development board and its **mini-USB cable**
- **Sharp GP2Y0A51SK0F** analog infrared distance sensor — rated **2 cm to 15 cm**, with a
  three-wire lead (white, red, black)
- A **ruler** with millimeter marks
- A **flat target** — a book cover, a notebook, or an index card taped to something that
  stands up on its own
- Your laptop, with the **Arduino IDE** installed

## Authorized Resources

The provided Arduino kit, your laptop, a ruler, the Arduino website
([docs.arduino.cc/programming](https://docs.arduino.cc/programming/)), the sensor
specification sheet, the starter sketch, course slides and readings, and EI from
instructors during the period. **Excel, MATLAB, or similar** is permitted for the curve fit.

You may also use GenAI **only up to Level 3** (use of GenAI as a feedback tool on
student-generated work, as defined in the AW GenAI Policy). The Arduino website is usually
more instructive than a chatbot for syntax questions.

You may **not** use any materials from previous semesters.

Document any collaboration or GenAI use in your Gradescope submission.

## Part 1 — What You Are Working With

A **microcontroller** is a low-cost, single-chip computer. The microcontroller on your board
is just the one large chip in the middle; everything else on the development board exists to
make wiring and programming convenient. A product built around a microcontroller gets a
custom board carrying only what that product needs. Microcontrollers are one kind of
**embedded system** — the others you should know by name are **FPGAs** and **ASICs**, which
trade development cost and flexibility against speed, unit cost, and hardware security. The
Lesson 22 deck has the comparison table; we use a microcontroller here because it is cheap,
fast to develop against, and power-efficient.

That is all the background you need to start wiring.

## Part 2 — Wire the Sensor

Connect the sensor's three-wire lead to the Due:

| Wire | Pin on the Due | What it carries |
| --- | --- | --- |
| **White** | `A0` | the sensor's **analog output** — the signal you will measure |
| **Red** | `5V` | **power into the sensor** |
| **Black** | one of the `GND` pins | the ground return |

:::{admonition} The sensor runs on 5 V. The Due's analog pins do not.
:class: warning

The red lead goes to `5V` because that is what the sensor needs to *operate*. Its **output**
is a different signal entirely: the white lead never rises above about **2.46 V**, which is
safely under the Due's **3.3 V** analog input limit. That is the only reason a 5 V-powered
part is safe to read with this board.

**Never wire the red 5 V lead into `A0` or any other analog pin.** The Due's pins are
**not 5 V tolerant** — unlike an Arduino Uno's — and putting 5 V on one destroys the board.
Check the white/red/black assignment against the table above *before* you plug in the USB
cable.
:::

## Part 3 — Set Up the IDE and Read Raw Counts

1. Download and install the Arduino IDE from
   [docs.arduino.cc/software/ide/](https://docs.arduino.cc/software/ide/).
2. Connect the Arduino to your laptop with the provided USB cable. **Plug the mini-USB into
   the port labeled `USB Programming`** — the middle of the three connectors on that edge of
   the board, between the native-USB jack and the barrel jack. That port both powers the
   board and programs it. The *other* mini-USB jack cannot program the board — use it by
   mistake and you will spend the period wondering why nothing uploads.
3. Launch the IDE and open the starter sketch,
   {download}`DistanceSensor.ino <../_static/DistanceSensor.ino>`.
4. In the dropdown at the top of the IDE, select **which board and which COM port** you are
   using. It usually detects them for you; if it does not, ask your instructor.
5. Upload the sketch and open the **Serial Monitor** (the magnifying glass in the top right).
   Set it to **9600 baud**.

**You should see** a new line every **2 seconds** carrying a raw count and a voltage, and
`d = ?` where the distance will go. Move your hand toward and away from the sensor: the
numbers should move smoothly and repeatably. If they do not change at all, re-check the
white lead is in `A0`.

### Where those two numbers come from

The starter sketch runs Objective 2.5's math on real hardware. `analogRead()` on the Due
defaults to **10-bit** conversion over an input range of **0 V to 3.3 V**, so:

$$\text{levels} = 2^{b} = 2^{10} = 1024$$

$$\Delta V = \frac{V_{\max} - V_{\min}}{2^{b}} = \frac{3.3\ \text{V} - 0\ \text{V}}{1024} = 3.22\ \frac{\text{mV}}{\text{level}}$$

**Record that $\Delta V$ — you will be asked for it in Gradescope.**

The raw count `analogRead()` hands you *is* the quantization level from the reading:

$$QL = \left\lfloor \frac{V_{in} - V_{\min}}{\Delta V} \right\rfloor$$

and the sketch's `raw * 3.3 / 1024` simply runs that backward to recover the voltage. It
can only ever recover it to within one $\Delta V$ — 3.22 mV of quantization error is the
noise floor under everything you do today.

## Part 4 — Collect the Calibration Data

The sensor gives you volts. You want centimeters. Nothing in the sketch knows how to get
from one to the other yet, and nothing can until you measure it.

Set the ruler down with its zero at the front face of the sensor, stand your target upright
on the ruler, and record the voltage the Serial Monitor prints at each distance below.

- Use **at least 8 fit distances** spread across the full 2–15 cm range, and
- **at least 3 check distances** that you will **not** use in the fit.

A good split — fit at **2, 3, 4, 5, 7, 9, 11, 13, 15 cm**, check at **2.5, 6, 8, and 12 cm**:

| Role | Distance $d$ (cm) | Measured $V$ (V) | $1/V$ (V⁻¹) |
| --- | --- | --- | --- |
| fit | 2 | | |
| fit | 3 | | |
| fit | 4 | | |
| fit | 5 | | |
| fit | 7 | | |
| fit | 9 | | |
| fit | 11 | | |
| fit | 13 | | |
| fit | 15 | | |
| **check** | **2.5** | | |
| **check** | **6** | | |
| **check** | **8** | | |
| **check** | **12** | | |

The sketch prints every 2 seconds. Wait for two or three prints at each distance and record
a typical value rather than the first one you see — and keep the target square to the sensor,
because a tilted target reflects less light and reads as farther away.

**You should see** the voltage fall as the target moves away, from roughly 2 V at 2 cm to
roughly 0.4 V at 15 cm. It falls **fast** at first and then flattens out. That shape is the
problem you are about to solve.

:::{admonition} Stay inside 2–15 cm
:class: warning

Below about 1.5 cm the sensor's output **turns around and falls again** — the curve on the
specification sheet peaks near 2.46 V and then drops. A reading of 1.7 V could mean about
**0.5 cm or 3.0 cm**, and nothing in your data can tell you which. The 2 cm lower limit is
not conservatism, it is where the sensor stops being a function.
:::

## Part 5 — Fit the Conversion, Then Plug It In

Objective 2.6 gave you one linear map, $v_{out} = K v_{in} + B$, and a method for finding
$K$ and $B$ from two endpoints. You have never yet pointed it at a real transducer. Do that
now — with one preparatory step.

**The problem.** Your $V$-versus-$d$ data is not a straight line, so a straight-line fit
cannot describe it. Fit a line straight through the raw pairs and it slices across the curve:
on the sensor's own published curve, fit at the nine fit distances from Part 4, that line is off by
about **−2.5 cm at 2 cm**, **+2.2 cm at 5 cm** and **−3.3 cm at 15 cm** — four to seven times
the spec, undershooting at the ends and overshooting in between. No choice of slope and
intercept fixes that. It is the wrong shape.

**The fix.** Fit a straight line to *transformed* data instead. Add the $1/V$ column to your
table, then plot **$d$ on the vertical axis against $1/V$ on the horizontal axis**. Those
points fall very nearly on a straight line, because the sensor's output is close to
proportional to the reciprocal of distance.

Fit that line the way Objective 2.6 taught you — from two endpoints — and then, if you have
the tool, check it against a trendline:

$$d = K \cdot \frac{1}{V} + B$$

which is exactly Objective 2.6's $v_{out} = K v_{in} + B$, with $1/V$ as the input and
distance as the output. $K$ is your **gain**, in cm·V; $B$ is your **bias**, in cm. Note
that both now carry units the Lesson 21 versions did not: the same algebra is doing a
different job here, converting in software *behind* the converter instead of conditioning a
voltage *ahead* of it.

:::{admonition} Start with the two-point method you already know
:class: key-concept

Use the Objective 2.6 machinery on your **2 cm and 15 cm** rows: write
$2 = K(1/V_2) + B$ and $15 = K(1/V_{15}) + B$, subtract to eliminate $B$, solve for $K$,
then back-substitute. It is the same procedure as the thermocouple example, and on the
sensor's published curve it lands inside spec.

**Then, second, a trendline over all nine fit points** — Excel, MATLAB, or similar. Two
points can only be fit exactly, so a two-point fit hands the whole job of absorbing
measurement noise to two readings; a trendline fits the same straight line through all nine
at once and averages that noise out. Expect the two-point fit to land near the ±0.5 cm limit
and the trendline fit to beat it comfortably.
:::

**Sanity-check your constants before you type them in.** $K$ should come out somewhere
around **5 to 8 cm·V** and $B$ somewhere between **−2 and 0 cm**. A $K$ that is negative, or
off by a factor of ten, means the axes went in backward — you plotted $1/V$ against $d$
instead of $d$ against $1/V$.

Now open the sketch and find this block:

```cpp
// ====== PLUG IN (Part 5): YOUR CALIBRATION ======
const float CAL_K = 0.0;   // cm * V
const float CAL_B = 0.0;   // cm
// ================================================
```

Type your two constants in, complete the `distanceCm()` function just below it so it returns
`CAL_K / volts + CAL_B`, and upload. The `d = ?` in the Serial Monitor becomes a distance in
centimeters as soon as `CAL_K` is no longer zero.

## Part 6 — Validate Against the Check Distances

You now have a conversion that fits the data you built it from. That is not evidence it
works — it is close to a tautology. **How would you test your conversion?** On distances it
has never seen.

Put the target at each of your **check** distances — the ones you kept out of the fit — and
record what the sketch now prints:

| Check distance (cm) | Sketch reports (cm) | Error (cm) |
| --- | --- | --- |
| 2.5 | | |
| 6 | | |
| 8 | | |
| 12 | | |

**The spec: $|\text{error}| \leq 0.5$ cm at every check point.**

**You should see** errors of a few tenths of a centimeter, scattered in both directions. If
one check point is badly off while the others are fine, re-measure it — a tilted target or a
misread ruler is far more likely than a bad fit. If *all* of them are off in the **same**
direction, your fit has a bias: check that you did not accidentally include a check point in
the fit, and check the arithmetic on $B$.

If you cannot meet spec, go back to Part 4 and re-take the readings that look least
consistent. Do not adjust $K$ and $B$ by hand until the check points pass — a constant tuned
to hit the check points has turned them into fit points, and you are back to having no
evidence at all.

**Record your maximum $|\text{error}|$ across all check points.** Gradescope asks for it.

## Part 7 — Demo and Gradescope

1. **Demo to your instructor.** Show the Serial Monitor printing live distances, and be
   ready to put the target at a distance your instructor picks and have it read correctly.
2. **Individually answer the Gradescope questions.** You will need your $\Delta V$, your $K$
   and $B$, your maximum check-point error, and your explanation of why the fit was done on
   $1/V$.
3. **Both partners keep a copy of the working sketch, with the constants in it.** You need it
   at Lesson 26.

## Submission Checklist

- [ ] Sensor wired white→`A0`, red→`5V`, black→`GND`, checked before power
- [ ] Starter sketch running and streaming raw counts and volts at 9600 baud
- [ ] $\Delta V$ for the Due's ADC computed from $2^b$ and the 0–3.3 V range
- [ ] Calibration table filled in — **at least 8 fit distances** across 2–15 cm and **at least 3 check distances** held out
- [ ] $d$ plotted against $1/V$ and a line fit to it
- [ ] $K$ and $B$ recorded, sanity-checked, and typed into the sketch's PLUG-IN block
- [ ] `distanceCm()` completed and the Serial Monitor printing centimeters
- [ ] Every check distance validated, errors recorded, **max $|\text{error}| \leq 0.5$ cm**
- [ ] Working sensor demonstrated to your instructor
- [ ] Gradescope questions answered **individually**
- [ ] Documentation statement — collaboration and any GenAI use
- [ ] Both partners leave with a copy of the calibrated sketch

## On to Project 2

You leave today with working code that contains your calibration constants $K$ and $B$ and a
function `distanceCm()`. At Lesson 26 that function becomes the **range channel** of a
fire-control system: the Arduino watches the sensor and decides, on its own, when a target
has been detected and when it is inside the engagement envelope.

Those decisions are thresholds on distance — a **detection gate at 12.0 cm** and an
**engagement envelope at 6.0 cm** — so they are only ever as accurate as the calibration you
just built. **A biased seeker declares in-range early.** Half a centimeter of bias in
`distanceCm()` is half a centimeter of bias in every engagement decision Project 2 makes.

## Files

Starter sketch — {download}`DistanceSensor.ino <../_static/DistanceSensor.ino>`
