📖 Scenario: You are working with an embedded system that communicates with a sensor using the I2C protocol. The sensor sends data bytes, and your microcontroller must acknowledge (ACK) each byte received to confirm successful reception. If the microcontroller does not want more data, it sends a Not Acknowledge (NACK) signal to stop the communication.
🎯 Goal: Build a simple C program that simulates receiving 5 bytes from an I2C sensor. Your program will send an ACK after each byte except the last one, where it will send a NACK to indicate the end of data reception.
📋 What You'll Learn
Create an array called received_bytes with exactly 5 bytes: 0x10, 0x20, 0x30, 0x40, 0x50
Create a variable called byte_count and set it to 5
Use a for loop with variable i to iterate over received_bytes
Inside the loop, print the received byte in hexadecimal format
Send an ACK after each byte except the last one, where you send a NACK
Print ACK sent or NACK sent accordingly after each byte
💡 Why This Matters
🌍 Real World
I2C communication is common in embedded systems to read sensors or communicate with other chips. Correctly sending ACK and NACK signals ensures reliable data transfer.
💼 Career
Understanding I2C acknowledge behavior is essential for embedded software engineers working with microcontrollers and hardware interfaces.
Progress0 / 4 steps
1
Create the data array for received bytes
Create an array called received_bytes with these exact 5 bytes: 0x10, 0x20, 0x30, 0x40, 0x50.
Embedded C
Hint
Use unsigned char received_bytes[5] = {0x10, 0x20, 0x30, 0x40, 0x50}; to create the array.
2
Create a variable for the number of bytes
Create an integer variable called byte_count and set it to 5.
Embedded C
Hint
Use int byte_count = 5; to store the number of bytes.
3
Loop through the bytes and simulate ACK/NACK
Use a for loop with variable i from 0 to byte_count - 1 to iterate over received_bytes. Inside the loop, print the byte in hexadecimal format using printf("Received byte: 0x%02X\n", received_bytes[i]). Then, if i is less than byte_count - 1, print ACK sent. Otherwise, print NACK sent.
Embedded C
Hint
Use a for loop and if condition to print ACK or NACK after each byte.
4
Print the final output
Run the program to print each received byte and the corresponding ACK or NACK message.
Embedded C
Hint
Run the program and check the console output matches the expected ACK and NACK messages.
Practice
(1/5)
1. What does an ACK (Acknowledge) signal mean in I2C communication?
easy
A. The sender is requesting to resend the data
B. The receiver wants to stop communication immediately
C. The communication line is idle
D. The receiver successfully received data and is ready for more
Solution
Step 1: Understand ACK meaning in I2C
ACK means the receiver signals it has received the data correctly and is ready to continue.
Step 2: Compare options with ACK meaning
Only The receiver successfully received data and is ready for more matches this meaning; others describe stop or errors.
Final Answer:
The receiver successfully received data and is ready for more -> Option D
Quick Check:
ACK = Ready for more data [OK]
Hint: ACK means receiver says 'send more' [OK]
Common Mistakes:
Confusing ACK with NACK meaning
Thinking ACK means stop communication
Assuming ACK means error occurred
2. Which of the following code snippets correctly sends a NACK after reading the last byte in I2C using embedded C?
easy
A. i2c_read_byte(I2C1, ACK);
B. i2c_read_byte(I2C1, NACK);
C. i2c_write_byte(I2C1, NACK);
D. i2c_send_ack(I2C1);
Solution
Step 1: Identify correct function to read with NACK
To signal no more data is needed, the read function must be called with NACK.
Step 2: Check options for correct usage
i2c_read_byte(I2C1, NACK); uses i2c_read_byte with NACK, which is correct. Others either send ACK or write instead of read.
Final Answer:
i2c_read_byte(I2C1, NACK); -> Option B
Quick Check:
Read last byte with NACK to stop [OK]
Hint: Use read with NACK to stop reading last byte [OK]
Common Mistakes:
Using ACK instead of NACK after last byte
Calling write instead of read for NACK
Confusing function names for ACK/NACK
3. Consider this code snippet reading 3 bytes from an I2C device:
for (int i = 0; i < 3; i++) {
data[i] = (i < 2) ? i2c_read_byte(I2C1, ACK) : i2c_read_byte(I2C1, NACK);
}
What is the purpose of sending NACK on the last byte?
medium
A. To request the device to resend the last byte
B. To acknowledge the last byte and continue reading
C. To indicate the master will stop reading after this byte
D. To reset the I2C bus immediately
Solution
Step 1: Analyze the loop and ACK/NACK usage
The loop reads 3 bytes; for first two bytes it sends ACK, for last byte it sends NACK.
Step 2: Understand NACK meaning on last byte
NACK signals the master will stop reading after this byte, telling the slave no more data is needed.
Final Answer:
To indicate the master will stop reading after this byte -> Option C
Quick Check:
NACK on last byte = stop reading [OK]
Hint: NACK on last byte means stop reading [OK]
Common Mistakes:
Thinking NACK requests resend
Assuming NACK resets bus
Confusing ACK and NACK roles
4. You wrote this code to read bytes from I2C but the slave device never stops sending data:
for (int i = 0; i < 4; i++) {
data[i] = i2c_read_byte(I2C1, ACK);
}
What is the likely problem?
medium
A. You never sent NACK after the last byte to stop reading
B. You should use i2c_write_byte instead of i2c_read_byte
C. The loop should run 5 times instead of 4
D. You need to send a STOP condition before reading
Solution
Step 1: Check ACK/NACK usage in the loop
The code sends ACK for every byte read, never sending NACK to signal stop.
Step 2: Understand effect of missing NACK
Without NACK after last byte, slave keeps sending data, causing endless transmission.
Final Answer:
You never sent NACK after the last byte to stop reading -> Option A
Quick Check:
Missing NACK means no stop signal [OK]
Hint: Send NACK after last byte to stop slave [OK]
Common Mistakes:
Using ACK for all bytes including last
Confusing read and write functions
Ignoring need for STOP condition after read
5. You want to write a function that reads a variable number of bytes from an I2C device and properly sends ACK for all bytes except the last one, which should send NACK. Which code snippet correctly implements this behavior?
hard
A. for (int i = 0; i < length - 1; i++) {
data[i] = i2c_read_byte(I2C1, ACK);
}
data[length - 1] = i2c_read_byte(I2C1, NACK);
B. for (int i = 0; i < length; i++) {
data[i] = i2c_read_byte(I2C1, ACK);
}
C. for (int i = 0; i < length; i++) {
data[i] = i2c_read_byte(I2C1, NACK);
}
D. data[0] = i2c_read_byte(I2C1, NACK);
for (int i = 1; i < length; i++) {
data[i] = i2c_read_byte(I2C1, ACK);
}
Solution
Step 1: Understand ACK/NACK pattern for multiple bytes
All bytes except last must be read with ACK; last byte with NACK to stop.
Step 2: Analyze each option
for (int i = 0; i < length - 1; i++) {
data[i] = i2c_read_byte(I2C1, ACK);
}
data[length - 1] = i2c_read_byte(I2C1, NACK); reads all but last byte with ACK, then last byte with NACK, correctly signaling stop. Others send wrong signals or in wrong order.
Final Answer:
for (int i = 0; i < length - 1; i++) {
data[i] = i2c_read_byte(I2C1, ACK);
}
data[length - 1] = i2c_read_byte(I2C1, NACK); -> Option A
Quick Check:
ACK all but last, NACK last byte [OK]
Hint: Send NACK only on last byte read [OK]
Common Mistakes:
Sending NACK on all bytes
Sending ACK on last byte
Reading last byte first with NACK
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.