- Where Lab 6 Sits
- Parallel and Serial Communication
- Asynchronous and Synchronous
- The I²C Bus
- I²C Timing
- Device Addressing
- The MCP9808
- The Register Map
- The Ambient Temperature Register
- Decoding a Temperature
- Resolution and Conversion Time
- Bringing the Bus Up
- From a Reading to a Dataset
- Common Mistakes and Debugging
- Self-Check
- Practical Engineering Connections
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.
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 task | What you do | Concept & section |
|---|---|---|
| 1 | Make a lab directory; enable I²C with raspi-config; reboot | Bringing the bus up (§12) |
| 2 | Wire the sensor with the Pi off; run i2cdetect -y 1 and find address 18; use the provided module; note its limitation | Bus topology, addressing, resolution (§4, §6, §11) |
| 3 | Write read_temp_high_res() for exactly 0.125 °C, from the datasheet, with correct sign handling | Register bit layout and decoding (§9, §10) |
| 4 | °C and °F; a critical-temperature warning; 100+ logged measurements; a plot; a CSV file | From a reading to a dataset (§13) |
| 5 | Optional: 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:
| Interface | Introduced | Type | Throughput |
|---|---|---|---|
| PATA Parallel AT Attachment | 1986 | parallel | 66 / 100 / 133 MB per second |
| SATA Serial ATA | 2003 | serial | 150 / 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.
- 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.
- 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.
- 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.
| Family | How timing is agreed | Examples 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.
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.
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.
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.
5.1 START and STOP
| Condition | What happens on the wires | Meaning |
|---|---|---|
| START | SDA is pulled low while SCL stays high | The controller claims the bus. Everything that follows belongs to this transaction. |
| STOP | SCL rises first, then SDA rises | The bus is released and returns to idle, both lines high. |
| Repeated START | A second START with no intervening STOP | The 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
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 9 | SDA | Name | Meaning |
|---|---|---|---|
| 0 | pulled low | ACK | Received. Continue — more to come, do not stop. |
| 1 | left high | NAK | No 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.
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:
- START
- Send the 7-bit device address with the write bit, so the sensor listens — then ACK
- Send the register address you want (this is the “pointer”) — then ACK
- Repeated START
- Send the same device address with the read bit — then ACK
- Read the first data byte — the controller sends ACK to say “another, please”
- Read the second data byte — the controller sends NAK to say “that is enough”
- 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:
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.
(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:
- An analog temperature sensor — a bandgap circuit whose output voltage depends on absolute temperature.
- An ADC that digitises it.
- A set of registers holding the result, the configuration, and the alert limits.
- 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 pin | Raspberry Pi | Note |
|---|---|---|
| VDD | 3.3 V — physical pin 1 or 17 | Direct connection is fine; the sensor draws very little. Not 5 V — that would put 5 V logic on the Pi's I²C lines. |
| GND | any ground — e.g. physical pin 6 or 9 | The usual shared reference. |
| SDA | BCM 2 — physical pin 3 | The Pi's hardware I²C data line. |
| SCL | BCM 3 — physical pin 5 | The 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.
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.
| Register | Pointer | Access | Contents / power-up value |
|---|---|---|---|
| Configuration | 0x01 | read/write | 0x0000 — alert behaviour, hysteresis, shutdown, locks |
| T upper limit | 0x02 | read/write | 0x0000 |
| T lower limit | 0x03 | read/write | 0x0000 |
| T critical | 0x04 | read/write | 0x0000 |
| Ambient temperature TA | 0x05 | read only | The measurement — this is the one Task 3 is about |
| Manufacturer ID | 0x06 | read only | 0x0054 — a fixed, known value |
| Device ID / revision | 0x07 | read only | 0x0400 — also fixed |
| Resolution | 0x08 | read/write | 0x03 at power-up — see §11 |
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:
| Bits | Function | Bits | Function |
|---|---|---|---|
| 15–11 | unused | 5 | Clear alert |
| 10–9 | Hysteresis | 4 | Alert status |
| 8 | Shutdown mode | 3 | Alert output control |
| 7 | Critical trip lock | 2 | Critical alert only |
| 6 | Alarm window lock | 1 | Alert polarity |
| 0 | Alert 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.
| Bit | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
|---|---|---|---|---|---|---|---|---|
| Meaning | TA ≥ Tcrit | TA > Tupper | TA < Tlower | SIGN TA < 0°C | 27 | 26 | 25 | 24 |
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Meaning | 23 | 22 | 21 | 20 | 2−1 0.5 | 2−2 0.25 | 2−3 0.125 | 2−4 0.0625 |
- 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
The register reads upper = 0xC1, lower = 0x1C, i.e. 1100 0001 0001 1100.
- 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, leaving0001as the meaningful part of the upper byte. - Shift the upper part up. Those four bits carry weights 27–24, so shifting left by 4 gives
0001 0000= 16. - 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 gives0001= 1. - Integer part: 16 + 1 = 17 °C.
- 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. - 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.
The register reads upper = 0x3E, lower = 0xFC, i.e. 0011 1110 1111 1100.
- Note the sign. The upper nibble is
0011: bit 12 is set, so this is below 0 °C. - Strip the flags and shift. The remaining upper bits are
1110; shifted left by 4 that is1110 0000= 224. - Shift the lower byte down.
1111 1100>> 4 gives1111= 15. - Assemble: 224 + 15 = 239 — which is clearly not a temperature. That is the two's complement representation.
- Convert: 256 − 239 = 17.
- Fraction: the bottom nibble is
1100again, giving 0.5 + 0.25 = 0.75. - Apply the sign: −(17 + 0.75) = −17.75 °C. ✓
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.
(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–0 | Resolution | Typical conversion time | Note |
|---|---|---|---|
| 00 | +0.5 °C | 30 ms | Fastest, coarsest |
| 01 | +0.25 °C | 65 ms | |
| 10 | +0.125 °C | 130 ms | |
| 11 | +0.0625 °C | 250 ms | Power-up default (register value 0x03); finest, slowest |
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.
(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
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 toraspi-configand 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
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)”.
- 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. - 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. - 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.
- 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
- Does
/dev/i2c-1exist? No → enable I²C and reboot. - Does
i2cdetect -y 1show 18? No → it is a wiring or power fault, not a code fault. - 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.
- Does the raw register look plausible? Print the two bytes in hex before converting. Compare against §10 by hand.
- 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
- Give three reasons a serial link can outperform a parallel one, and cite the PATA/SATA numbers.
- What distinguishes an asynchronous link from a synchronous one? Which family does I²C belong to, and what does that spare you from configuring?
- Why are the pull-up resistors mandatory on an I²C bus, and what would happen without them?
- Describe the START and STOP conditions in terms of SDA and SCL. Why can neither be mistaken for data?
- During data transfer, when is SDA permitted to change, and when does the receiver sample?
- On the ninth clock, what does a low SDA mean? Why does a missing device automatically produce the opposite?
- The MCP9808's address is 0x18. Write the full first byte for a read and for a write, in binary and hex.
- List the bit fields of the ambient temperature register: which bits are flags, which is the sign, which are integer, which are fractional?
- Decode upper = 0xC1, lower = 0x1C. Then decode upper = 0x3E, lower = 0xFC.
- Which two registers hold fixed known constants, what are they, and how does that help you debug?
- Distinguish the sensor's configured resolution from the resolution your decoding reports. Which does Task 3 concern?
- Why does sampling every 50 ms at the finest resolution setting not give you finer time detail?
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
- 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.
- Serial beat parallel. SATA over PATA, USB over the parallel port, PCI Express over PCI, HDMI over VGA — the same engineering argument each time.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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/