← Advanced Instrumentation

PHYS 351 · Advanced Instrumentation

Lecture 06 · Digital temperature sensing

PHYS 351 — LECTURE 06 NOTES
Serial Communication Protocols, I²C, and the MCP9808 Temperature Sensor
Covers Lab 6

© Ran Yang, Ph.D.
yangran.org/teaching/phys351/

1Where Lab 6 Sits

Lab 4 and Lab 5 were about moving power around: a pin commands, a transistor switches, a motor turns. The MCP9808 is the opposite kind of device. It draws so little current that its VDD pin connects directly to the Pi's 3.3 V supply — no driver, no separate supply, no flyback diode.

What is difficult this week is not the power, it is the conversation. Until now every pin meant one thing: high or low, on or off. From this week a pin carries a protocol — a shared agreement about when a bit is valid, how a device is addressed, and how it says yes. Lab 6 is also, as the handout says plainly, a practical exercise in reading a datasheet, and that is a skill this course is deliberately building.

The one idea to carry out of this lecture

A digital sensor does not send you a temperature. It sends you bits from a numbered register, in a format the datasheet defines. Your job is to know which register to ask for, and how to turn what comes back into a physical quantity with units. The protocol delivers the bits; the datasheet gives them meaning.

1.1  Task map

Lab 6 taskWhat you doConcept & section
1Make a lab directory; enable I²C with raspi-config; rebootBringing the bus up (§12)
2Wire the sensor with the Pi off; run i2cdetect -y 1 and find address 18; use the provided module; note its limitationBus topology, addressing, resolution (§4, §6, §11)
3Write read_temp_high_res() for exactly 0.125 °C, from the datasheet, with correct sign handlingRegister bit layout and decoding (§9, §10)
4°C and °F; a critical-temperature warning; 100+ logged measurements; a plot; a CSV fileFrom a reading to a dataset (§13)
5Optional: a live scrolling animated plot§13.4

2Parallel and Serial Communication

2.1  Two ways to move a byte

To move eight bits from one chip to another there are two obvious schemes.

Parallel: use eight wires, one per bit, and send D7 through D0 all at once. One clock tick moves a whole byte.

Serial: use one wire and send the bits one after another, most significant first. Eight ticks move a byte.

2.2  So which is faster?

Intuitively parallel should win by a factor of eight. The history says otherwise, and the comparison is worth remembering:

InterfaceIntroducedTypeThroughput
PATA
Parallel AT Attachment
1986parallel66 / 100 / 133 MB per second
SATA
Serial ATA
2003serial150 / 300 / 600 MB per second

The serial interface replaced the parallel one and is several times faster. The same story repeats everywhere: USB replaced the parallel printer port, PCI Express replaced PCI, serial Ethernet replaced parallel backplanes.

Why serial wins — three reasons
  1. Skew. Eight parallel bits must arrive simultaneously. Traces differ slightly in length and loading, so bits drift apart in time as the clock rises. The fastest usable clock is set by the worst-case spread, not by any one wire. A single serial line has nothing to be skewed against, so it can be clocked far faster.
  2. Crosstalk. Eight wires switching together in a ribbon couple into one another capacitively and inductively. The noise grows with edge rate, so going faster makes it worse — a self-limiting problem.
  3. Cost and size. Eight wires, eight connector pins and eight driver circuits cost more than one. On a chip with limited pins — exactly the Raspberry Pi's 40-pin header — pins are the scarcest resource of all.

The lesson generalises beyond wires: a narrow, fast, well-disciplined channel usually beats a wide, slow, hard-to-synchronise one.

This matters for the rest of the course. Every sensor from here on — the MCP9808 this week, the LIS3DH in Lab 7, the MCP3008 in Lab 8 — uses two or four wires instead of eight or sixteen, and that is why several of them can share the same header.

3Asynchronous and Synchronous

Serial links divide into two families by how the receiver knows when to sample.

FamilyHow timing is agreedExamples and consequences
Asynchronous No clock is transmitted. Both ends are configured in advance for the same bit rate, and each byte carries start and stop bits so the receiver can re-align. UART (Universal Asynchronous Receiver Transmitter) and USB. Fewer wires, but both ends must agree on the baud rate beforehand — which is why a wrong baud setting gives you a screen of garbage characters.
Synchronous A clock line is transmitted alongside the data. The transmitter says explicitly when each bit is valid. I²C and SPI. One extra wire, but no baud-rate agreement is needed and the clock can even pause mid-transfer. This is what the lab sensors use.

UART is how your laptop talks to a microcontroller over USB, and it is worth knowing that a “USB serial” connection is a UART with a protocol converter in the cable or on the board. On Windows it appears as a COM port in Device Manager; on macOS and Linux it appears as a device file. On the Pi you will use a terminal or VS Code rather than a separate terminal emulator.

Lab 6 uses I²C; Lab 8 uses SPI. Both are synchronous, and comparing them is a natural report discussion.

4The I²C Bus

I²C — Inter-Integrated Circuit — needs exactly two signal wires no matter how many devices are attached:

  • SDA — serial data
  • SCL — serial clock

Every device connects to the same two lines, in parallel. A controller (the Raspberry Pi) starts every transaction and generates the clock; each peripheral has an address and answers only when called. A temperature sensor, an accelerometer, a display and a real-time clock can all share those two wires — which is exactly what happens in Lab 7, where the MCP9808 joins the LIS3DH on the same bus.

VDD = 3.3 V SDA SCL pull-up resistors Raspberry Pi controller MCP9808 0x18 LIS3DH Lab 7 display, RTC, ...
Figure 1. Every device hangs off the same two wires. Adding a sensor costs no extra pins on the Pi — only a distinct address.

4.1  Open drain, pull-ups, and why the bus is active low

This is the piece of I²C that explains the rest of it, so it is worth getting straight.

Every device connects to SDA and SCL through an open-drain output: a transistor that can pull the line down to ground, but has no ability to drive it up. Pulling the line high is the job of the pull-up resistors — one on SDA, one on SCL, both to VDD = 3.3 V.

Why it must be built this way

If two devices could actively drive the line, one driving high while another drove low would short the supply to ground through two transistors — a destructive bus contention. With open-drain outputs that cannot happen: the worst case is several devices pulling low at once, which is harmless.

The consequences follow directly:

  • The bus is active low. A device asserts a signal by pulling the line down. Idle — nobody talking — is both lines high.
  • Low wins. Any device holding the line low overrides everyone else, which is precisely what makes the acknowledge bit (§5.3) work.
  • The pull-up resistors are not optional. Without them the lines never reach a valid high level and nothing works at all.

The pull-up value is a compromise. Too large, and the rise time is slow because the resistor must charge the bus capacitance, rounding the edges and limiting the clock rate. Too small, and the current when a device pulls low becomes excessive. A few kilohms is typical.

Good news for this lab

The Adafruit MCP9808 breakout board already has the pull-up resistors fitted, and the Raspberry Pi has its own on the I²C pins. You do not need to add any. This is one of the reasons a breakout board is worth using: it carries the sensor, its decoupling and its pull-ups, and it brings everything to a 0.1 inch header that fits a breadboard.

It is still worth knowing they are there. If you later put several breakout boards on one bus, their pull-ups sit in parallel and the effective resistance falls — occasionally enough to matter.

5I²C Timing

Four events define the protocol. All of them are about the relationship between SDA and SCL, never about either line alone.

SDA SCL START ACK STOP 8 data bits, MSB first SDA falls while SCL high receiver pulls SDA low = ACK SCL rises, then SDA rises
Figure 2. One I²C byte. START and STOP are the only times SDA is allowed to change while SCL is high — which is exactly what makes them unmistakable.

5.1  START and STOP

ConditionWhat happens on the wiresMeaning
STARTSDA is pulled low while SCL stays highThe controller claims the bus. Everything that follows belongs to this transaction.
STOPSCL rises first, then SDA risesThe bus is released and returns to idle, both lines high.
Repeated STARTA second START with no intervening STOPThe controller keeps the bus and begins a new phase — typically switching from writing a register address to reading that register's contents (§5.4).

5.2  When data is valid

The rule that makes START and STOP unambiguous

During data transfer, SDA must hold steady for the whole time SCL is high. SDA is only permitted to change while SCL is low. The receiver samples on the rising edge of the clock, when the data is guaranteed stable.

This is why a transition on SDA while SCL is high is reserved: it cannot be data, so it can only be a START or a STOP. One rule gives the protocol both its sampling discipline and its framing.

5.3  The ninth bit: ACK and NAK

Every byte on an I²C bus is nine clock pulses, not eight. After the eight data bits, the transmitter releases SDA and issues one more clock, during which the receiver answers:

Bit 9SDANameMeaning
0pulled lowACKReceived. Continue — more to come, do not stop.
1left highNAKNo acknowledgement. Either nobody is at that address, or the receiver has had enough: stop, done.

Notice that a low means yes. That is the active-low convention of §4.1 again: acknowledging takes deliberate effort — a device must pull the line down — whereas silence leaves the pull-up resistor to produce a high. An absent, misaddressed or dead device therefore returns NAK automatically, simply by doing nothing.

This is your first diagnostic

When i2cdetect shows an address, it is reporting that something at that address pulled SDA low. When your Python raises an I/O error on a read, the usual cause is a NAK — nothing answered. So a failed read is almost always a wiring, address or power problem rather than a code problem. Check the wires before you debug the program.

5.4  A complete register read

Reading a register from an I²C sensor takes two phases in one transaction, which is exactly what the repeated START is for:

  1. START
  2. Send the 7-bit device address with the write bit, so the sensor listens — then ACK
  3. Send the register address you want (this is the “pointer”) — then ACK
  4. Repeated START
  5. Send the same device address with the read bit — then ACK
  6. Read the first data byte — the controller sends ACK to say “another, please”
  7. Read the second data byte — the controller sends NAK to say “that is enough”
  8. STOP

Step 6 and 7 are worth pausing on: the ACK after the first byte is what tells the sensor to keep going, and the NAK after the second is what ends the read. The MCP9808's registers are 16 bits wide, so a temperature read is exactly two bytes — a high byte and a low byte.

In your program a library performs all of this in one call, but knowing the sequence is what lets you interpret a logic-analyser trace, and it is why the transaction can fail in several distinct ways.

6Device Addressing

The first byte after START carries a 7-bit device address in bits 7–1, with bit 0 as the direction flag:

bit 0 = 1 → read       bit 0 = 0 → write (1)
7-bit and 8-bit addresses are the same address

This is the most common confusion in I²C, and it produces a device that seems to be at two different places at once.

The MCP9808's default address is 0x18 as a 7-bit address. Shift it left by one to make room for the direction bit and you get 0x30, the “8-bit write address”; adding the read bit gives 0x31. All three numbers describe the same chip.

Linux tools and Python libraries on the Pi use the 7-bit convention, so i2cdetect shows 18 and that is what you pass to the library. Datasheets often tabulate the 8-bit form. If a device appears to be at double the expected address, this is why.

6.1  The address pins

The MCP9808's three address pins A2, A1 and A0 occupy bits 3, 2 and 1 of the address byte, and are pulled low by default — giving 000 and therefore the base address 0x18. Tying them high in the eight possible combinations gives eight addresses, 0x18 through 0x1F, so eight of these sensors can share one bus.

Leave them unconnected for this lab. Their existence is worth understanding, though: it is the standard answer to “what if I need two of the same chip?”, and it is why an address collision between two different chip types is a real design constraint.

Practice 6

(a) Write out the eight bits of the first byte for a write to the MCP9808 at its default address, and give the byte in hexadecimal.

(b) Do the same for a read.

(c) A2, A1 and A0 are tied to 0, 1, 1. What 7-bit address does the device now have?

(d) i2cdetect shows a device at 0x30 where you expected 0x18. What has most likely happened?

7The MCP9808

Inside the package are four blocks, and the datasheet describes each one:

  1. An analog temperature sensor — a bandgap circuit whose output voltage depends on absolute temperature.
  2. An ADC that digitises it.
  3. A set of registers holding the result, the configuration, and the alert limits.
  4. An I²C interface giving access to those registers.

This is the general shape of every modern digital sensor, and recognising it is more useful than memorising any one part. In Lab 7 the LIS3DH has exactly the same architecture with an accelerometer at the front; in Lab 8 the MCP3008 is essentially block 2 on its own, exposed over SPI.

7.1  The breakout board and its four wires

Only four connections are needed:

Sensor pinRaspberry PiNote
VDD3.3 V — physical pin 1 or 17Direct connection is fine; the sensor draws very little. Not 5 V — that would put 5 V logic on the Pi's I²C lines.
GNDany ground — e.g. physical pin 6 or 9The usual shared reference.
SDABCM 2 — physical pin 3The Pi's hardware I²C data line.
SCLBCM 3 — physical pin 5The Pi's hardware I²C clock line.

The remaining breakout pins — Alert, A0, A1, A2 — are left unconnected. Alert is an open-drain output the sensor can assert when a temperature limit is crossed, which is a hardware alternative to the software threshold you will implement in Task 4.

Wire it with the Pi powered off

Lab 6 Task 2 says this explicitly, and it is worth following. Connecting a sensor to a live bus can momentarily short a signal line to a supply pin as the header makes contact.

Check the wiring twice before applying power. Swapping VDD and GND on the breakout is the one error that will destroy the sensor immediately, and the pin order on the board is not the same as the order in the table above.

8The Register Map

All communication with the MCP9808 is reading from, or writing to, a numbered 16-bit register. The register address is a single byte — the “pointer” of §5.4 — and the data is two bytes.

RegisterPointerAccessContents / power-up value
Configuration0x01read/write0x0000 — alert behaviour, hysteresis, shutdown, locks
T upper limit0x02read/write0x0000
T lower limit0x03read/write0x0000
T critical0x04read/write0x0000
Ambient temperature TA0x05read onlyThe measurement — this is the one Task 3 is about
Manufacturer ID0x06read only0x0054 — a fixed, known value
Device ID / revision0x07read only0x0400 — also fixed
Resolution0x08read/write0x03 at power-up — see §11
The best debugging trick in this lab

Registers 0x06 and 0x07 always contain 0x0054 and 0x0400. They never change and do not depend on temperature, wiring or configuration.

So read one of them first. If you get the right constant back, then your wiring, your address, your library and your byte handling are all proven correct in one step, and any remaining problem is in your temperature decoding. If you get something else — or an error — the fault is upstream and there is no point studying your bit manipulation yet.

This split-the-problem-in-half move is worth remembering for Labs 7 and 8 as well; most sensors have an equivalent ID register.

8.1  The configuration register

Register 0x01 is 16 bits of behaviour settings. Lab 6 does not require you to change it, but reading the bit map is good datasheet practice and explains what the sensor can do:

BitsFunctionBitsFunction
15–11unused5Clear alert
10–9Hysteresis4Alert status
8Shutdown mode3Alert output control
7Critical trip lock2Critical alert only
6Alarm window lock1Alert polarity
  0Alert comparator / interrupt mode

9The Ambient Temperature Register

Register 0x05 is where Task 3 lives. Its sixteen bits are not a plain integer — the top four are flags and the bottom twelve are a fixed-point number.

Bit15141312111098
MeaningTA ≥ TcritTA > TupperTA < TlowerSIGN
TA < 0°C
27262524
Bit76543210
Meaning232221202−1
0.5
2−2
0.25
2−3
0.125
2−4
0.0625
Read this layout carefully — Task 3 depends on it
  • Bits 15, 14, 13 are comparison flags against the limit registers. They are not part of the number and must be removed before any arithmetic.
  • Bit 12 is the sign: set means the temperature is below 0 °C. It is also not part of the magnitude.
  • Bits 11–4 are the integer part, weights 27 down to 20 — eight bits, so 0 to 255.
  • Bits 3–0 are the fraction, weights 0.5, 0.25, 0.125 and 0.0625.

The fractional weights are the crux of Task 3. Each bit you keep halves the size of the smallest step you can represent; each bit you discard doubles it. Work out from this table which fractional bits you must retain so that the smallest representable increment is exactly 0.125 °C, and which you must throw away — and justify the choice in your report. The lab is explicit that using the wrong number of fractional bits scores zero, so derive it from the table rather than guessing.

10Decoding a Temperature

The register arrives as two bytes: an upper byte (bits 15–8) and a lower byte (bits 7–0). The decoding differs above and below zero, and Task 3 requires both to be right.

10.1  Positive temperatures

Worked example — +17.75 °C

The register reads upper = 0xC1, lower = 0x1C, i.e. 1100 0001 0001 1100.

  1. Strip the flags. The top four bits of the upper byte are 1100 — comparison flags, plus a sign bit that is 0 here. Mask them off, leaving 0001 as the meaningful part of the upper byte.
  2. Shift the upper part up. Those four bits carry weights 27–24, so shifting left by 4 gives 0001 0000 = 16.
  3. Shift the lower byte down. The lower byte is 0001 1100; its top nibble holds the integer weights 23–20, so shifting right by 4 gives 0001 = 1.
  4. Integer part: 16 + 1 = 17 °C.
  5. Fraction. The bottom nibble of the lower byte is 1100: the 0.5 bit and the 0.25 bit are set, the 0.125 and 0.0625 bits are not. So 0.5 + 0.25 + 0 + 0 = 0.75 °C.
  6. Result: 17 + 0.75 = 17.75 °C. ✓

10.2  Negative temperatures

When bit 12 is set, the twelve magnitude bits hold a two's complement value, and simply reading them as a positive number gives nonsense. Two equivalent routes work:

  • Subtract from the range. Assemble the raw value as though it were positive, then subtract it from the full range — the “256 − value” step in the example below — and negate.
  • Test the sign bit. If bit 12 is set, subtract the full scale from the assembled value directly. This is the more idiomatic approach in Python and generalises to other sensors, including the LIS3DH in Lab 7.
Worked example — −17.75 °C

The register reads upper = 0x3E, lower = 0xFC, i.e. 0011 1110 1111 1100.

  1. Note the sign. The upper nibble is 0011: bit 12 is set, so this is below 0 °C.
  2. Strip the flags and shift. The remaining upper bits are 1110; shifted left by 4 that is 1110 0000 = 224.
  3. Shift the lower byte down. 1111 1100 >> 4 gives 1111 = 15.
  4. Assemble: 224 + 15 = 239 — which is clearly not a temperature. That is the two's complement representation.
  5. Convert: 256 − 239 = 17.
  6. Fraction: the bottom nibble is 1100 again, giving 0.5 + 0.25 = 0.75.
  7. Apply the sign: −(17 + 0.75) = −17.75 °C. ✓
You must test the negative path

Room temperature is positive, so a sign-handling bug is completely invisible during normal testing — and then produces a wildly wrong number the one time it matters. This is a classic, and it is exactly why the lab asks you to handle the sign bit correctly.

Test it deliberately. The honest way is to feed your decoding function the raw byte pairs from the two worked examples above and check that it returns +17.75 and −17.75. Since the register values are known, you do not need a cold sensor to prove the code is right — and that argument itself is worth a sentence in your report.

Practice 10

(a) Decode upper = 0x01, lower = 0x91. Show every step.

(b) Decode upper = 0x19, lower = 0x1C. Is it above or below zero, and how do you know before doing any arithmetic?

(c) What register bytes correspond to exactly +25.0 °C with all flag bits clear?

(d) A student's code reports 239.75 °C on a cold morning. Which step did they omit?

(e) Using the §9 table, list the fractional weights and state the smallest increment representable if you keep only the 0.5 and 0.25 bits. Then work out what you need for exactly 0.125.

11Resolution and Conversion Time

Register 0x08 selects how finely the ADC resolves, and there is a direct cost in speed:

Bits 1–0ResolutionTypical conversion timeNote
00+0.5 °C30 msFastest, coarsest
01+0.25 °C65 ms
10+0.125 °C130 ms
11+0.0625 °C250 msPower-up default (register value 0x03); finest, slowest
Two different meanings of “resolution” — keep them separate

The sensor's resolution is how finely the hardware measures, set by register 0x08. The reported resolution is how many fractional bits your code chooses to use when decoding register 0x05. They are independent, and Task 3 is about the second.

This is what Task 2 is steering you towards when it asks what you notice about the values from the provided module and what its limitation is: the module is not reading a coarser sensor, it is discarding fractional bits that are already there. Your Task 3 function reads the same register and keeps more of them.

Note the direction of the constraint: your decoding can never be finer than the hardware setting. If the sensor were configured for 0.5 °C steps, asking your code for 0.125 would just give you zeros in the low bits.

The conversion time also sets a floor on sampling rate. Task 4 asks for readings every 0.2 s, which is 200 ms — comfortably longer than the 130 ms conversion but not enormously so. Poll much faster than the conversion time and you simply re-read the same value, producing a plot with visible flat steps. If you see stair-stepping in your data, this is why, and saying so is a good observation.

Practice 11

(a) At the power-up setting, what is the fastest meaningful sampling rate in samples per second?

(b) You sample every 50 ms at that setting. What will the data look like, and why?

(c) A 100-point run at 0.2 s intervals takes how long? Does the conversion time change that materially?

12Bringing the Bus Up

Task 1 and the start of Task 2 are pure system setup. None of it needs an internet connection.

# 1. Enable the I2C interface: Interface Options -> I2C -> <Yes>, then reboot
sudo raspi-config

# 2. After the reboot, confirm the bus device exists
ls /dev/i2c-*

# 3. Scan bus 1 for devices. The MCP9808 appears as 18.
i2cdetect -y 1

# 4. Read a register directly from the command line -- no Python needed.
#    Device 0x18, register 0x06 (manufacturer ID), word read.
#    Note: i2cget returns a word byte-swapped; 0x5400 here means 0x0054.
i2cget -y 1 0x18 0x06 w
What the i2cdetect grid is telling you

It walks every address on the bus, issues a START and the address byte, and reports which addresses returned an ACK — the §5.3 mechanism, used as a survey. So:

  • 18 appears. Wiring, power and address are all correct. Proceed.
  • The grid is empty. Nothing acknowledged. Check VDD and GND first, then SDA and SCL, then that they are not swapped.
  • The command errors or there is no /dev/i2c-1. I²C is not enabled — go back to raspi-config and confirm you rebooted.
  • A different address appears. Either an address pin is tied high (§6.1), or you are looking at a 7-bit/8-bit mix-up.

Run i2cdetect before you write a single line of Python. It separates every hardware question from every software question, and it takes two seconds.

13From a Reading to a Dataset

Task 4 turns a working sensor into an experiment. Each part is small; together they are the standard shape of a data-acquisition program.

13.1  Unit conversion

T°F = 95T°C + 32 (2)

Label both values with units in the output. An unlabelled column of numbers is the most common presentation fault in lab reports, and it costs marks for no good reason.

13.2  The threshold warning

Task 4 asks for a critical temperature — 28 °C is suggested — above which the program prints a distinct warning. Put the threshold in a named constant at the top of the file rather than burying the number in a comparison, so you can demonstrate it by touching the sensor and, if the room is warm, retune it in one place.

Worth noticing: the MCP9808 can do this in hardware. Registers 0x02–0x04 hold the limits, bits 15–13 of the temperature register report the comparisons, and the Alert pin can be asserted without the Pi involved at all. Your software check and the sensor's hardware alert are two solutions to the same problem — and contrasting them is a genuinely good discussion point.

13.3  Logging, plotting and the CSV

The requirements are 100+ measurements, elapsed time in seconds since the program started, a plot with a smooth line and individual markers, a title and labelled axes with units, a horizontal dashed line at the threshold, and a temp_data.csv with the headers “Time (s)” and “Temperature (C)”.

Four things that will improve the result
  1. Use time.perf_counter() for elapsed time, taking a reference once before the loop and subtracting. This is the monotonic high-resolution clock from Lecture 03 — the same reasoning applies here.
  2. Collect first, plot afterwards. Appending to a list inside the loop and plotting once at the end keeps the sampling interval clean. Drawing inside the loop adds large, variable delays — the print() problem of Lecture 03 §5.6, in a more expensive form.
  3. Write the CSV exactly as specified, headers included. Then open it in a spreadsheet and look at it. A file that exists but has the columns swapped is worse than no file.
  4. Give the experiment something to show. A flat line at room temperature is a correct but dull result. Warm the sensor with a finger partway through the run and let it cool: you get a rise, a peak and an exponential-looking decay, which crosses your threshold line and makes every feature of the plot meaningful.

13.4  The optional live plot

For extra credit, the plot updates in real time and scrolls to show the most recent N seconds. The idea is to update the data behind an existing plot rather than redrawing the figure each time — redrawing from scratch is what makes naive live plots slow and jerky. Keep a fixed-length buffer of the most recent points, and set the x-limits from the newest timestamp so the window scrolls. The repository's existing animated temperature-plotting scripts are a reasonable place to look for the pattern.

14Common Mistakes and Debugging

14.1  Things that damage hardware

  • VDD and GND swapped. Destroys the sensor immediately. Check twice before powering on.
  • 5 V to the sensor's VDD. Puts 5 V logic on the Pi's I²C lines, which are 3.3 V only and not tolerant.
  • Wiring a live bus. Power the Pi down first, as Task 2 instructs.

14.2  A debugging order that works

  1. Does /dev/i2c-1 exist? No → enable I²C and reboot.
  2. Does i2cdetect -y 1 show 18? No → it is a wiring or power fault, not a code fault.
  3. Does the manufacturer ID read back as 0x0054? No → byte order or library usage. Yes → the whole chain works and the problem is in your decoding.
  4. Does the raw register look plausible? Print the two bytes in hex before converting. Compare against §10 by hand.
  5. Does the decoding handle both signs? Feed it the two known byte pairs from §10.

14.3  Things that waste your afternoon

  • SDA and SCL swapped. Nothing is damaged, and nothing is detected.
  • 7-bit versus 8-bit address confusion (§6).
  • Byte order reversed when combining the two bytes into a 16-bit value — the number moves wildly for tiny temperature changes.
  • Forgetting to mask off bits 15–13 before arithmetic. The flags are usually zero at room temperature, so this bug hides until you set a limit register or the temperature crosses one.
  • Sampling faster than the conversion time and wondering why the data is stair-stepped (§11).
  • Warming the sensor with a finger and expecting instant response — the package has thermal mass and takes seconds.
  • Loose breadboard jumpers. Intermittent I²C failures are very often mechanical.

14.4  Before you leave

Copy your scripts, the CSV and the plots to your own computer; verify they open there; then remove your directory from the shared Pi and shut down cleanly.

15Self-Check

  1. Give three reasons a serial link can outperform a parallel one, and cite the PATA/SATA numbers.
  2. What distinguishes an asynchronous link from a synchronous one? Which family does I²C belong to, and what does that spare you from configuring?
  3. Why are the pull-up resistors mandatory on an I²C bus, and what would happen without them?
  4. Describe the START and STOP conditions in terms of SDA and SCL. Why can neither be mistaken for data?
  5. During data transfer, when is SDA permitted to change, and when does the receiver sample?
  6. On the ninth clock, what does a low SDA mean? Why does a missing device automatically produce the opposite?
  7. The MCP9808's address is 0x18. Write the full first byte for a read and for a write, in binary and hex.
  8. List the bit fields of the ambient temperature register: which bits are flags, which is the sign, which are integer, which are fractional?
  9. Decode upper = 0xC1, lower = 0x1C. Then decode upper = 0x3E, lower = 0xFC.
  10. Which two registers hold fixed known constants, what are they, and how does that help you debug?
  11. Distinguish the sensor's configured resolution from the resolution your decoding reports. Which does Task 3 concern?
  12. Why does sampling every 50 ms at the finest resolution setting not give you finer time detail?
Lab takeaway

Two wires, shared by every device, with pull-ups making the bus active low so that nobody can ever fight for it. A transaction is START, address plus direction, register pointer, data, acknowledge at every byte, STOP. What comes back is not a temperature but sixteen bits whose meaning the datasheet defines — flags, a sign, an integer part and a fraction. Reading that layout correctly, and proving it against known values, is the whole of Task 3.

16Practical Engineering Connections

  1. I²C everywhere. Laptop battery gauges, monitor identification (EDID over I²C), phone sensor hubs, server temperature monitoring and camera modules all use this bus. It is roughly forty years old and completely unavoidable.
  2. Serial beat parallel. SATA over PATA, USB over the parallel port, PCI Express over PCI, HDMI over VGA — the same engineering argument each time.
  3. Register-based devices. Almost every modern peripheral — sensors, radios, power controllers, displays — is a set of numbered registers behind a serial interface. Learn the pattern once and every new datasheet gets easier.
  4. Fixed-point arithmetic. The MCP9808's integer-plus-fraction format is how embedded systems represent non-integers without floating-point hardware, and it appears in audio codecs, motor controllers and DSP throughout.
  5. Two's complement. The negative-temperature handling is the same representation every processor uses for signed integers, and the same trap appears with every signed sensor — including the accelerometer in Lab 7.
  6. Resolution versus speed. The trade in register 0x08 is universal to ADCs: more bits take longer. You will meet it again directly in Lab 8.
  7. Hardware alerts. The Alert pin lets a sensor interrupt a processor instead of being polled — the basis of low-power design, where a device sleeps until something happens.
  8. Known-value debugging. Reading an ID register to prove the channel works before trusting the data is the same discipline as a calibration standard, a checksum or a test pattern. It is how experienced engineers avoid debugging two things at once.

PHYS 351 · Lecture 06 Notes · © Ran Yang, Ph.D. · yangran.org/teaching/phys351/

← All course materials