Decoding I2C and UART
Objectives
Section titled “Objectives”By the end of this lesson you will:
- Set up the I2C decoder, and read the address, read/write bit and ACK from a bus scan
- Set up the UART decoder at the correct baud rate, and describe the symptoms of setting it wrong
Before you start
Section titled “Before you start”- You can already connect a logic analyzer, set its sample rate, and use a trigger (lesson Capturing a first digital signal)
- A TESAIoT Dev Kit flashed with the QWA309 Header I/O Test example, which has a Scan button that scans the I2C bus and a UART Echo button that sends a short packet out the header’s UART pin
- The board’s pinout diagram, to find the SDA and SCL pins of the I2C bus on the header, and pin P15.1 (UART TX)
- If using the Eva Kit, scan the I2C bus and print text over UART with your own program — the decoding steps are exactly the same
See it work first
Section titled “See it work first”Press the Scan button on the header test program’s screen. It prints a table of addresses 0x08 through 0x77; a cell where a device responds shows a number, and other cells show --.
It then reports “Found N device(s),” followed by a list of addresses.
How does the program know a device is at a given address? It asks each address in turn, 112 times in all, and listens for anyone answering “present.” In this lesson, we will eavesdrop on all 112 of those conversations on the real wires, and prove with our own eyes that the table on screen tells the truth.
Concepts
Section titled “Concepts”1. I2C on the wire: START, address, R/W, ACK, STOP
Section titled “1. I2C on the wire: START, address, R/W, ACK, STOP”I2C uses two wires, SCL (the clock, driven by the master) and SDA (data). Both are open-drain — a device can only pull the line down. A pull-up resistor is what pulls the line back up, the same principle as the active-low button in the lesson Pull-ups, pull-downs and buttons.
- START (S): SDA falls while SCL is still high — the signal that “I am about to speak”
- The 7-bit address: sent MSB first. Data on SDA must stay steady while SCL is high, and may change only while SCL is low
- R/W: the 8th bit; 0 = write, 1 = read
- ACK / NACK: on the 9th clock pulse, the master releases SDA. If the addressed device really exists, it pulls SDA down (ACK); if no one does, SDA stays high via the pull-up (NACK)
- STOP (P): SDA rises while SCL is high
The 7-bit address versus the byte on the wire. The first byte on the wire is the address shifted left by one bit, with the R/W bit appended. So address 0x30 as a write becomes the byte 0x60, and as a read becomes 0x61. Some datasheets write the address in its 8-bit form (0x60), which confuses people very often. sigrok’s decoder shows it in 7-bit form by default (e.g. “Address write: 30”).
What the bus-scan tool does. The header test program sends START + address + W, then STOP, for each address in turn — no other data at all. Whichever address gets an ACK is where a device lives. Per the board’s SDK documentation, the I2C bus on the TESAIoT Dev Kit’s header has a CapSense chip (PSoC 4000T) that answers at 0x08, so you should see an ACK at 0x08 at minimum. Compare any other address that answers against the list the screen prints.
Sizing an I2C pull-up (formula from TI SLVA689). The resistor must not be so small that a device cannot pull the line down to V_OL, and not so large that the rising edge is slower than the spec allows.
R_p(min) = (V_CC − V_OL(max)) / I_OL = (3.3 − 0.4) V / 3 mA = 967 ΩR_p(max) = t_r / (0.8473 × C_b) Fast-mode 400 kHz (t_r ≤ 300 ns), a 100 pF bus → 300 ns / (0.8473 × 100 pF) = 3.54 kΩ Standard-mode 100 kHz (t_r ≤ 1000 ns), a 100 pF bus → 11.8 kΩSo a 400 kHz bus with 100 pF of capacitance can choose anywhere between 967 Ω and 3.54 kΩ — for example, 2.2 kΩ. The constant 0.8473 comes from ln(0.7 / 0.3), the time an RC curve takes to rise from 0.3 × V_CC to 0.7 × V_CC — exactly I2C’s V_IL and V_IH thresholds.
2. UART on the wire: no clock, so both sides must agree on speed beforehand
Section titled “2. UART on the wire: no clock, so both sides must agree on speed beforehand”UART sends data on a single wire per direction (one side’s TX connects to the other side’s RX), with no clock wire at all, so both sides must set the same baud rate in advance. The most common format is 8N1: 8 data bits, no parity bit, 1 stop bit.
- The idle line is 1
- The start bit is 0 for one bit period — the receiver uses this falling edge to set its timing
- The 8 data bits are sent least-significant bit first (LSB first) — the opposite of I2C
- The stop bit is 1. If the receiver reads 0 here instead, it reports a framing error
The numbers at 115200 baud:
1 bit = 1 / 115200 s = 8.68 µs1 byte = 10 bits (start + 8 + stop) = 86.8 µsMaximum throughput = 115200 / 10 = 11,520 bytes per secondExample: byte 0xA5 = 1010 0101, sent LSB first. The bits on the wire after the start bit are therefore 1, 0, 1, 0, 0, 1, 0, 1, followed by a stop bit of 1.
The header test program’s UART Echo. It sends a 4-byte packet out pin P15.1 at 115200 8N1: A5 11 <counter> <checksum>.
The counter starts at 0 and increments by one on every press; the checksum is the XOR of the first three bytes, so the first press gives A5 11 00 B4 (0xA5 XOR 0x11 = 0xB4).
The program also prints the bytes it sent on screen, in a TX: line, so we have an answer key to check the decoder against.
If no ESP32-S3 board answers as the example expects, per the constants in the code, the program waits 250 ms for a reply, pauses 10 ms, and resends — up to 4 times in total. Measure the real spacing from the capture yourself.
3. Reading a capture to spot mistakes
Section titled “3. Reading a capture to spot mistakes”A wrong baud setting. The decoder measures bits at the wrong rhythm, giving garbled bytes and framing errors.
- Setting 9600 against a real 115200 signal: one bit the decoder expects is 104 µs, but the whole 4-byte packet (about 347 µs) only lasts about 3.3 bits at 9600, so the decoder sees a single garbled byte or an error
- Setting 57600 (half speed): each bit the decoder reads spans two real bits, giving wrong byte values, usually with framing errors
- Setting too fast, e.g. 230400: the decoder sees one real bit as two, and the resulting bytes are meaningless
Finding the baud rate from a capture. Zoom in on the narrowest pulse (one bit), then compute baud ≈ 1 / that width. An 8.68 µs pulse is 115200; a 104 µs pulse is 9600. Then choose the nearest standard value — UART tolerates only a small baud error (generally, the combined error of both sides should stay under about 2 to 3%).
Other symptoms a capture can reveal:
| What you see | Possible cause |
|---|---|
| I2C: every address gets a NACK | No device present, the device has no power, SDA and SCL are swapped, or the address is wrong |
| I2C: SDA or SCL stuck low the whole time | A device is stuck mid-transaction, or a wire is shorted to ground |
| I2C: the rising edge is a long, slow curve (needs an oscilloscope to see) | The pull-up is too large, or the bus is long enough that capacitance is high |
| UART: nothing on the RX wire even though the other side is sending | TX is wired to TX (TX and RX must be crossed), or a common GND was forgotten |
Worked example
Section titled “Worked example”Problem: decode the header test program’s I2C bus scan, and confirm the result against the screen.
- Connect the leads. The device’s GND to GND, channel 0 to SCL, channel 1 to SDA of the I2C bus on the header (find their positions from the board’s pinout diagram).
- Sample rate. Not knowing the bus speed yet, use 8 MHz to start, which covers up to 400 kHz (20 samples per SCL period). Capture, then measure SCL’s period. If it turns out to be 100 kHz, you can lower the sample rate to capture for longer.
- Trigger on SDA’s falling edge (which happens at START). Press Run, then press Scan on screen.
- Add a decoder. In PulseView, add a decoder named I2C, and set SCL = channel 0 and SDA = channel 1.
- Read the result. The decoder track shows the sequence Start → Address write: 08 → ACK or NACK → Stop, repeating for each address in turn.
- Check. Address 0x08 should get an ACK (the CapSense chip); addresses with no device get a NACK. Count every address that got an ACK, and compare against “Found N device(s)” on screen — every address must match.
- Look at the raw byte. Switch the decoder’s display to show the address in 8-bit form; address 0x08 as a write must show as 0x10.
If the number of ACKs on the wire does not match the screen, do not blame the instrument yet — first check whether you captured all 112 addresses (was the capture time long enough?), and whether the sample rate was fast enough.
Practice
Section titled “Practice”- A device has 7-bit address 0x44. What is the first byte on the wire for a write, and for a read?
- A decoder shows “Address read: 68” followed by NACK. What does this mean, and what should you check next?
- UART at 9600 8N1: how long is one bit, one byte, and what is the maximum throughput in bytes per second?
- Sending byte 0x55 over UART 8N1, what does the wire’s shape look like, and why is this byte useful for finding the baud rate?
- You measure the narrowest pulse on a UART wire at 26 µs. What is the baud rate likely to be?
- A 3.3 V I2C bus at 100 kHz has 200 pF of total capacitance. Find R_p(min) and R_p(max), then decide whether 4.7 kΩ works.
- Pressing UART Echo a third time (counter = 0x02), what should all 4 bytes of the packet be?
Solution
Section titled “Solution”- Write: 0x44 shifted left one bit = 0x88. Read: 0x89.
- The master asked to read from address 0x68, and no one answered. Check that the device has power, is really on this same bus (a board may have several buses), and that the address matches the datasheet, including any address-select pins on the device.
- 1 bit = 1 / 9600 = 104.2 µs. 1 byte = 10 bits = 1.042 ms. Maximum throughput = 960 bytes per second.
- 0x55 = 0101 0101, sent LSB first gives 1, 0, 1, 0, 1, 0, 1, 0. Combined with the start bit (0) and stop bit (1), the wire alternates between 0 and 1 on every single bit — a square wave where every pulse is exactly one bit wide. Measuring that width gives the baud rate immediately.
- 1 / 26 µs = 38,462 baud. The nearest standard value is 38400.
- R_p(min) = (3.3 − 0.4) / 3 mA = 967 Ω. R_p(max) = 1000 ns / (0.8473 × 200 pF) = 5.90 kΩ. 4.7 kΩ falls within this range, so it works.
A5 11 02 B6, because 0xA5 XOR 0x11 XOR 0x02 = 0xB6.
Check your understanding
Section titled “Check your understanding”Answer at least 4 of the 5 questions in quiz.yaml correctly.
Part A: the I2C bus scan. Follow the worked example, then fill in the table.
| What to check | On the board’s screen | From the decoder |
|---|---|---|
| SCL frequency | not shown | |
| Number of addresses asked | 112 (0x08 to 0x77) | |
| Addresses that got an ACK | ||
| Wire byte of the first ACK’d address (8-bit form) | not shown |
Part B: UART Echo
- Connect channel 0 to P15.1 (TX), and, if you have a spare channel, channel 1 to P15.0 (RX). Set the sample rate to 4 MHz, capture time 2 s, trigger on channel 0’s falling edge.
- Add a decoder named UART. Set baud 115200, 8 data bits, parity none, 1 stop bit, and set the decoder’s RX to read channel 0. (The names RX and TX in the decoder’s settings refer to which wire the decoder reads, not which side of the board they belong to.)
- Press UART Echo on screen. Compare the bytes the decoder reads against the
TX:line on screen — every byte must match. Count how many times the packet was sent, and how far apart. - Measure the start bit’s width, calculate the real baud rate from the capture, and compare it against 115200.
- Deliberately get it wrong. Change the decoder’s baud to 57600 and 9600. Record what the decoder shows, and explain it using section 3.
- Watch channel 1 (RX). If nothing answers, the wire stays idle at 1 the whole time. If you have an ESP32-S3 board as the example’s README describes, you should see a 5-byte reply starting with 0x5A.
| Decoder’s baud | What you see |
|---|---|
| 115200 | |
| 57600 | |
| 9600 |
Going further
Section titled “Going further”A logic analyzer can tell you when a signal is 0 or 1, but not what the real voltage looks like. The next lesson, Oscilloscope basics, will show us signal edges, overshoot, and noise that a logic analyzer cannot see. If you want to go further with I2C on this board, see the lesson Driving an RGB matrix over I2C in the TESAIoT Firmware Stack course.
Reflect
Section titled “Reflect”Next time a program tells you “sent” or “device not found,” how many minutes would it take to prove with a logic analyzer what really happened on the wire — and is that worth more than guessing?
References
Section titled “References”- sigrok protocol decoders
- Texas Instruments: Understanding the I2C Bus (SLVA704)
- SDK: cm33/sensors/01_i2c_bus_scan.c
- Texas Instruments: I2C Bus Pullup Resistor Calculation (SLVA689)
- SDK: the QWA309 base board’s capability map (kit-tesaiot-pse84-ai/README.md)
- Code of QWA309 Header I/O Test (Developer Hub, commit 372d0d8)
Review questions
Answer on your own first, then open the answer.
-
A device has the 7-bit address 0x30. What is the first byte on SDA for a write? (Objective 1)
- 0x30
- 0x60
- 0x61
- 0x18
Show answer
Answer: B. 0x60
ไบต์แรกคือ address เลื่อนซ้ายหนึ่งบิตแล้วต่อด้วยบิต R/W เขียนคือ 0 จึงได้ 0x60 ถ้าอ่านจะเป็น 0x61
-
During a bus scan the decoder shows "Address write: 3C" followed by NACK. What does it mean? (Objective 1)
- อุปกรณ์ที่ 0x3C รับข้อมูลเรียบร้อย
- ไม่มีอุปกรณ์ตอบรับที่ address 0x3C ในจังหวะนาฬิกาที่ 9 SDA จึงค้างสูง
- บัสลัดวงจร
- decoder ตั้งความเร็วผิด
Show answer
Answer: B. ไม่มีอุปกรณ์ตอบรับที่ address 0x3C ในจังหวะนาฬิกาที่ 9 SDA จึงค้างสูง
ในจังหวะที่ 9 มาสเตอร์ปล่อย SDA อุปกรณ์ที่มีอยู่จริงจะดึงลงเป็น ACK ถ้าไม่มีใครดึง pull-up ทำให้ SDA เป็น 1 คือ NACK เครื่องสแกนจึงสรุปว่าที่อยู่นี้ว่าง
-
Which statements about I2C signalling are correct? (choose all that apply) (Objective 1)
- START คือ SDA ตกลงขณะ SCL สูง
- ACK คือฝั่งรับดึง SDA ลงในจังหวะนาฬิกาที่ 9
- ข้อมูลบน SDA ควรเปลี่ยนขณะ SCL สูง
- STOP คือ SDA ขึ้นขณะ SCL สูง
Show answer
Answer: A. START คือ SDA ตกลงขณะ SCL สูง · B. ACK คือฝั่งรับดึง SDA ลงในจังหวะนาฬิกาที่ 9 · D. STOP คือ SDA ขึ้นขณะ SCL สูง
ข้อมูลต้องนิ่งขณะ SCL สูง และเปลี่ยนได้ตอน SCL ต่ำ การเปลี่ยน SDA ขณะ SCL สูงถูกสงวนไว้เป็นสัญญาณ START และ STOP เท่านั้น
-
About how long is one bit on a 115200 baud UART? (Objective 2)
- 8.68 µs
- 86.8 µs
- 115 µs
- 0.87 µs
Show answer
Answer: A. 8.68 µs
1 / 115200 = 8.68 µs ส่วน 86.8 µs คือหนึ่งไบต์แบบ 8N1 (10 บิตรวมบิตเริ่มและบิตหยุด)
-
The line runs at 115200 baud but the decoder is set to 9600. What do you see? (Objective 2)
- ไบต์ถูกต้องแต่แสดงช้าลง
- ไบต์เพี้ยนหรือหายไปจำนวนมาก และมี framing error เพราะ decoder อ่านบิตผิดจังหวะ
- decoder ปรับ baud ให้เองอัตโนมัติ
- สายสัญญาณจะเปลี่ยนเป็น 9600 ตาม
Show answer
Answer: B. ไบต์เพี้ยนหรือหายไปจำนวนมาก และมี framing error เพราะ decoder อ่านบิตผิดจังหวะ
UART ไม่มีสายนาฬิกา ฝั่งอ่านใช้จังหวะที่ตั้งไว้ ที่ 9600 หนึ่งบิตยาว 104 µs ครอบหลายบิตจริง ทั้งแพ็กเก็ตจึงกลายเป็นไบต์เพี้ยนไม่กี่ไบต์และ framing error วิธีแก้คือวัดพัลส์ที่แคบที่สุดแล้วหา baud จริง
Cite this lesson
If you teach from this lesson or reuse it in slides or documents, credit it with the text below. If you changed it, add (adapted) after the title.
"Decoding I2C and UART" from TESA Open Knowledge by the Thai Embedded Systems Association (TESA), https://github.com/tesaiot/tesa-qualification-program, licensed under CC BY-NC 4.0
Thai attribution: "ถอดรหัส I2C และ UART" จาก TESA Open Knowledge โดยสมาคมสมองกลฝังตัวไทย (Thai Embedded Systems Association: TESA) https://github.com/tesaiot/tesa-qualification-program สัญญาอนุญาต CC BY-NC 4.0
TESA Open Knowledge · © 2026 สมาคมสมองกลฝังตัวไทย (TESA) · CC BY-NC 4.0
Content is licensed CC BY-NC 4.0. Reuse it non-commercially and credit the Thai Embedded Systems Association (TESA) every time. · How to cite TESA