[I2C] Wire.endTransmission() randomly returns NACK (errcode 2)

Hi everyone, I’m fairly new in the Arduino world, I have a small project which requires to store amounts of data that are too high to be stored on the internal Arduino’s EEPROM.

So I bought an EEPROM chip, the 24LC256, and implemented an abstraction class that stores data on it regardless of the length, so the class can handle as much page writes as required on the chip.

Here’s the code for the write method (I’ve cleaned up debug messages):

bool E2PRM24LC256::write(unsigned short address, byte* data, unsigned short length) {
            byte ACK;
            byte* page = data;
            unsigned short written, writeLength = 0;
            unsigned short pageAddress = address;

            while (written < length) {
                //Initiate IC2 channel with device of given address, this will send start condition byte
                Wire.beginTransmission(this->deviceAddress);
                //Send the first byte of the page start address
                Wire.write((byte)(pageAddress >> 8));
                //Send the last byte of the page start address
                Wire.write((byte)pageAddress);

                //Because of page boundaries we can't always write as much as we want
                //for instance if the write address is 155
                //then the page boundaries are 128-192, if we try to write
                //64 bytes the device will roll over and overwrite data stored
                //at the beginning of the page (from the address 128)
                //Hence we need to find out how much data we can write on this page,
                //to do that we compute the mod of address divided by the page buffer size (64)
                //and substract the result to the page buffer size: 64 - (pageAddress % 64) = available writing length
                writeLength = PAGE_BUFFER_SIZE - (pageAddress % PAGE_BUFFER_SIZE);
                if (writeLength > WIRE_BUFFER_LENGTH) {
                    writeLength = WIRE_BUFFER_LENGTH;
                }
                if (writeLength > length - written) {
                    writeLength = length - written;
                }
                //Prevent overflow
                if (written >= 0) {
                    //Add written bytes to the counter
                    written += Wire.write(&data[written], writeLength);
                } else {
                    return written > 0;
                }
                pageAddress += writeLength;
                ACK = Wire.endTransmission();
                if (ACK > 0) {
                    Serial.print("ACK problem ");
                    Serial.println(ACK);
                    return false;
                }
                delay(5);//Wait 5ms for the page to be fully written on the device
            }

            return true;
        }

It works quite fine, and to be sure I’ve designed a small test:

  1. Generate a byte sequence of random length (between 8 and 128 bytes actually)
  2. Store the bytes at a random address on the device
  3. Read the data from the same address
  4. Compare both and return false if there is a difference
  5. Repeat

So, I’ve runned the test 5 times with 10 or 20 repetitions, it helped me detect flaws in the allocation algorithm that I have fixed, but after a while the endTransmission method returns the error code 2, here’s the test output :

Free memory 1041 B
Writing 85 bytes -> (0) (1) (2) (3) (4) (5) (6) a(7) (8) (9) 
(10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) e(27) (28) (29) (30) (31)  (32) !(33) "(34) #(35) $(36) %(37) &(38) '(39) ((40) )(41) *(42) +(43) ,(44) -(45) .(46) /(47) 0(48) 1(49) 2(50) 3(51) 4(52) 5(53) 6(54) 7(55) 8(56) 9(57) :(58) ;(59) <(60) =(61) >(62) ?(63) @(64) A(65) B(66) C(67) D(68) E(69) F(70) G(71) H(72) I(73) J(74) K(75) L(76) M(77) N(78) O(79) P(80) Q(81) R(82) S(83) T(84) 
To address 25161

Page address 25161 written 0 writeLength 30 pointer address 2101
(0) (1) (2) (3) (4) (5) (6) a(7) (8) (9) 
(10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) e(27) (28) (29) 
Page address 25191 written 30 writeLength 25 pointer address 2131
(30) (31)  (32) !(33) "(34) #(35) $(36) %(37) &(38) '(39) ((40) )(41) *(42) +(43) ,(44) -(45) .(46) /(47) 0(48) 1(49) 2(50) 3(51) 4(52) 5(53) 6(54) 
Page address 25216 written 55 writeLength 30 pointer address 2156
7(55) 8(56) 9(57) :(58) ;(59) <(60) =(61) >(62) ?(63) @(64) A(65) B(66) C(67) D(68) E(69) F(70) G(71) H(72) I(73) J(74) K(75) L(76) M(77) N(78) O(79) P(80) Q(81) R(82) S(83) T(84) 
Read 85 bytes -> (0) (1) (2) (3) (4) (5) (6) a(7) (8) (9) 
(10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) e(27) (28) (29) (30) (31)  (32) !(33) "(34) #(35) $(36) %(37) &(38) '(39) ((40) )(41) *(42) +(43) ,(44) -(45) .(46) /(47) 0(48) 1(49) 2(50) 3(51) 4(52) 5(53) 6(54) 7(55) 8(56) 9(57) :(58) ;(59) <(60) =(61) >(62) ?(63) @(64) A(65) B(66) C(67) D(68) E(69) F(70) G(71) H(72) I(73) J(74) K(75) L(76) M(77) N(78) O(79) P(80) Q(81) R(82) S(83) T(84) 
From address 25161
[......................................]


Test number 1

Free memory -8431 B
Writing 79 bytes -> (0) (1) (2) (3) (4) (5) (6) a(7) (8) (9) 
(10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) e(27) (28) (29) (30) (31)  (32) !(33) "(34) #(35) $(36) %(37) &(38) '(39) ((40) )(41) *(42) +(43) ,(44) -(45) .(46) /(47) 0(48) 1(49) 2(50) 3(51) 4(52) 5(53) 6(54) 7(55) 8(56) 9(57) :(58) ;(59) <(60) =(61) >(62) ?(63) @(64) A(65) B(66) C(67) D(68) E(69) F(70) G(71) H(72) I(73) J(74) K(75) L(76) M(77) N(78) 
To address 5539

Page address 5539 written 0 writeLength 29 pointer address 2101
(0) (1) (2) (3) (4) (5) (6) a(7) (8) (9) 
(10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) e(27) (28) 
Page address 5568 written 29 writeLength 30 pointer address 2130
(29) (30) (31)  (32) !(33) "(34) #(35) $(36) %(37) &(38) '(39) ((40) )(41) *(42) +(43) ,(44) -(45) .(46) /(47) 0(48) 1(49) 2(50) 3(51) 4(52) 5(53) 6(54) 7(55) 8(56) 9(57) :(58) 
Page address 5598 written 59 writeLength 20 pointer address 2160
;(59) <(60) =(61) >(62) ?(63) @(64) A(65) B(66) C(67) D(68) E(69) F(70) G(71) H(72) I(73) J(74) K(75) L(76) M(77) N(78) 
Read 79 bytes -> (0) (1) (2) (3) (4) (5) (6) a(7) (8) (9) 
(10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) e(27) (28) (29) (30) (31)  (32) !(33) "(34) #(35) $(36) %(37) &(38) '(39) ((40) )(41) *(42) +(43) ,(44) -(45) .(46) /(47) 0(48) 1(49) 2(50) 3(51) 4(52) 5(53) 6(54) 7(55) 8(56) 9(57) :(58) ;(59) <(60) =(61) >(62) ?(63) @(64) A(65) B(66) C(67) D(68) E(69) F(70) G(71) H(72) I(73) J(74) K(75) L(76) M(77) N(78) 
From address 5539


Free ram -8431
Test number 10

Free memory -8431 B
Writing 125 bytes -> (0) (1) (2) (3) (4) (5) (6) a(7) (8) (9) 
(10) (11) (12) (13) (14) (15) (16) (17) (18) (19) (20) (21) (22) (23) (24) (25) (26) e(27) (28) (29) (30) (31)  (32) !(33) "(34) #(35) $(36) %(37) &(38) '(39) ((40) )(41) *(42) +(43) ,(44) -(45) .(46) /(47) 0(48) 1(49) 2(50) 3(51) 4(52) 5(53) 6(54) 7(55) 8(56) 9(57) :(58) ;(59) <(60) =(61) >(62) ?(63) @(64) A(65) B(66) C(67) D(68) E(69) F(70) G(71) H(72) I(73) J(74) K(75) L(76) M(77) N(78) O(79) P(80) Q(81) R(82) S(83) T(84) U(85) V(86) W(87) X(88) Y(89) Z(90) [(91) \(92) ](93) ^(94) _(95) `(96) a(97) b(98) c(99) d(100) e(101) f(102) g(103) h(104) i(105) j(106) k(107) l(108) m(109) n(110) o(111) p(112) q(113) r(114) s(115) t(116) u(117) v(118) w(119) x(120) y(121) z(122) {(123) |(124) 
To address 29689

Page address 29689 written 0 writeLength 7 pointer address 2101
(0) (1) (2) (3) (4) (5) (6) ACK problem 2

And the arduino freezes after that

I’ve tried tweaking the options passed to the test methods (run count and executions count) but nothing solved the problem, and I can’t figure out where it comes from…

Any help would be greatly appreciated, thanks !

What Arduino board are you using? Are your i2c lines using proper pullup resistors?

bperrybap: What Arduino board are you using? Are your i2c lines using proper pullup resistors?

Hello, I'm using the Arduino Nano.

No I didn't put any pull up resitors on I2C lines for I've read it wasn't necessary since Arduino nano already have internal pull up resistors for I2C (but I might be wrong, or at least people saying that mght be wrong).

Are you using a raw chip, or did you buy a module? Some modules have the pullups on them. The internal AVR pullups are too weak. While they often can "work" depending on conditions, using only the internal pullups is way outside of what the I2C spec requires and can cause strange issues. I'd recommend adding some external pullups to your signals to make sure it isn't a simple case of using improper pullups. --- bill

bperrybap: Are you using a raw chip, or did you buy a module? Some modules have the pullups on them. The internal AVR pullups are too weak. While they often can "work" depending on conditions, using only the internal pullups is way outside of what the I2C spec requires and can cause strange issues. I'd recommend adding some external pullups to your signals to make sure it isn't a simple case of using improper pullups. --- bill

For the EEPROM? Yes I've bought a raw chip, not a module.

Ok I'll give it a try, this is strange because as per the ATMega328P datasheet IO pullup internal resistors have a minimal value of 20Kohms and the 24LC256 datasheet recommends no more than 10Kohm for I2C pullup resistors value.

But I'll try anyway, thanks !

I believe that the AVR internal pullup is larger than 20k. And if your data sheet says "no more than 10k" then even 20k is larger than the recommend 10k maximum so the internal AVR pullup is too large.

10k is a typical maximum recommended maximum pullup value, which means you want pullups that are equal to or less than that 10k value.

Keep in mind that the pullup is what creates the 1's or the highs on the bus signals. A larger resistor value means less pullup strength so it takes longer for the signal to rise backup to to high. You can find lots of information about this if you Google around or see the I2C spec for a description of what is required and how to calculate the needed value, but basically, the internal AVR pullup is too weak to function reliably in certain situations.

--- bill

bperrybap: I believe that the AVR internal pullup is larger than 20k. And if your data sheet says "no more than 10k" then even 20k is larger than the recommend 10k maximum so the internal AVR pullup is too large.

10k is a typical maximum recommended maximum pullup value, which means you want pullups that are equal to or less than that 10k value.

Keep in mind that the pullup is what creates the 1's or the highs on the bus signals. A larger resistor value means less pullup strength so it takes longer for the signal to rise backup to to high. You can find lots of information about this if you Google around or see the I2C spec for a description of what is required and how to calculate the needed value, but basically, the internal AVR pullup is too weak to function reliably in certain situations.

--- bill

Hi again, thanks a lot for the advice and explanations.

I've tried with external pull up resistors but that didn't solved the problem.

I've disabled the internal pull up resistors from the twi.c library and put 2 10k pullup resistors (which is the value recommend by the datasheet for 100khz which is the SCL frequency used by the twi.c library), to be sure I've also tried with 2k2 resistors the other value recommended for 400khz and 1mhz, still no luck.

Interresting thing is that when I power the project the test doesn't fails, il fails only when I reboot the arduino or re-upload the sketch whithout unplugging it first.

And you hooked up the pullups between the signal and VCC? not between the AVR and the device.

This is where having a logic analyzer would be extremely helpful to see what is actually happening on the i2c signals.

A NAK of 2 means that the slave didn't respond to its address. It almost sounds like the device is not responding during a write cycle (which is normal), but the 5ms delay you have should be enough time to allow it complete.Maybe 5ms is sometimes not enough?

How big is WIRE_BUFFER_LENGTH? The stock Wire library doesn't support writing more than 32 bytes at a time. It defines the symbol BUFFER_LENGTH as 32

You might try bumping your 5ms delay to 10 or 20ms to see if the issue goes away. If it goes away, then the worst case timing is more than 5ms. To make things faster, it might be useful to create a polling loop that polls the part for completion rather than use a blind delay. See section 7 ACKNOWLEDGE POLLING of the datasheet. When polling you may also want to check the millis() time to ensure that you don't poll beyond a certain maximum threshold that is known to be too long like say 50ms.

--- bill

bperrybap: And you hooked up the pullups between the signal and VCC? not between the AVR and the device.

This is where having a logic analyzer would be extremely helpful to see what is actually happening on the i2c signals.

A NAK of 2 means that the slave didn't respond to its address. It almost sounds like the device is not responding during a write cycle (which is normal), but the 5ms delay you have should be enough time to allow it complete.Maybe 5ms is sometimes not enough?

How big is WIRE_BUFFER_LENGTH? The stock Wire library doesn't support writing more than 32 bytes at a time. It defines the symbol BUFFER_LENGTH as 32

You might try bumping your 5ms delay to 10 or 20ms to see if the issue goes away. If it goes away, then the worst case timing is more than 5ms. To make things faster, it might be useful to create a polling loop that polls the part for completion rather than use a blind delay. See section 7 ACKNOWLEDGE POLLING of the datasheet. When polling you may also want to check the millis() time to ensure that you don't poll beyond a certain maximum threshold that is known to be too long like say 50ms.

--- bill

Here's a picture http://imgur.com/a/mX1ki the SDA and SCL pins of the EEPROM are tied to the arduino and to vcc throught the resistors. Unfortunately I don't have a logic analyzer.

5 ms is the maximum guaranteed write cycle time as per datasheet, I've tried with 10, 20 and even 50 ms but that didn't worked either :/

WIRE_BUFFER_LENGTH's value is 30, I'm well aware of the 32 bytes buffer size in the Wire library, so I can send a maximum of 32 bytes to the EEPROM at once, 2 bytes for the address, and 30 bytes of payload.

Yes I've read about ACK polling in the datasheet and I also find the blind delay an ugly solution, but I could not figure out how to implement ACK polling with the Wire library !

Can't it be that I do too much writes in a row and the I2C bus gets flooded or something like that? Even if I added 10ms delay between each write test and 5 betwen each tests row.

I'll try with another EEPROM of the same model, maybe this one is broken somehow.

In the meantime if you have any idea, thanks again for your help !

Serybva:
Here’s a picture http://imgur.com/a/mX1ki the SDA and SCL pins of the EEPROM are tied to the arduino and to vcc throught the resistors.

Pullups look ok.

Yes I’ve read about ACK polling in the datasheet and I also find the blind delay an ugly solution, but I could not figure out how to implement ACK polling with the Wire library !

You do it the same way you probe for devices on the bus.
Do a beginTransmission(addr) followed by a endTransmission() and then look at the status returned by endTransmission()
0 - device responded ok
1 - data length too large (can’t happen)
2 - NACK on address send (you will see this when it is busy)
3 - Nack on data send (can’t happen)
4 - twi error (this is something bad/odd happening on bus - shouldn’t happen if there is a single master)

If you are in a loop you will need to do some additional things:

  • Monitor millis() to make sure you don’t get stuck in a loop. i.e. give up and break out after say 50ms
  • have small delay between endTransmission() and the next beginTransmission()
    This is critical on pic32 (chipKit) devices as the i2c h/w will hang if you don’t have at least 20us delay.
    When looping on ESP8266 devices you must strobe the watchdog. You can do this either by using yield() or using delay() which calls yield(). Some of this will depend on the version of IDE you are using or intend to support.
    (old IDEs don’t have a yield() function)
    If you are only targeting newer IDEs that support yield() you can use delayMicroseconds() with say 50 or 100us and then yield() to give a fairly rapid poll.
    If wanting to support older IDEs simply use delay(1)

Can’t it be that I do too much writes in a row and the I2C bus gets flooded or something like that?
Even if I added 10ms delay between each write test and 5 betwen each tests row.

I wouldn’t think so.
You may want to have a look at Jack’s extEEPROM library.
It has all the polling stuff already in it.

I’ve used on a couple of different parts and it seems to work.
— bill

bperrybap:
Pullups look ok.
You do it the same way you probe for devices on the bus.
Do a beginTransmission(addr) followed by a endTransmission() and then look at the status returned by endTransmission()
0 - device responded ok
1 - data length too large (can’t happen)
2 - NACK on address send (you will see this when it is busy)
3 - Nack on data send (can’t happen)
4 - twi error (this is something bad/odd happening on bus - shouldn’t happen if there is a single master)

Ho ok, I tried to implement it that way before but I was sending data to the device with the write method and it messed up the data write, now it works, here’s the code :

 bool E2PRM24LC256::ackPoll() {
            byte ACK = 0;

            for (byte time = 0;time < ACK_POLL_TIMEOUT;time++) {
                Wire.beginTransmission(this->deviceAddress);
                ACK = Wire.endTransmission();
                Serial.print("ACK ");
                Serial.println(ACK);
                if (ACK == 0) {

                    return true;
                }
                delay(1);
            }

            return false;
        }

Using ACK poll instead of blind delay after a write seem to have dramatically improved the error rate, most of the time the test is successful, and fails 1 every 3 or 4 which is clearly better, but it still fails at some point so the blind delay wasn’t the only problem.

I’ve also replaced the EEPROM chip with another one of the same model with no improvement.

Here’s an output :

Writing 52 bytes
To address 21979

Page address 21979 written 0 writeLength 30 pointer address 2101
ACK 2
ACK 2
ACK 2
ACK 2
ACK 0

Page address 22009 written 30 writeLength 7 pointer address 2131
ACK 2
ACK 2
ACK 2
ACK 2
ACK 0

Page address 22016 written 37 writeLength 15 pointer address 2138
ACK 2
ACK 2
ACK 2
ACK 2
ACK 0

Read 52 bytes 
From address 21979


Test number 10

Writing 34 bytes
To address 24279

Page address 24279 written 0 writeLength 30 pointer address 2101
ACK problem 2

So the problem here is clearly that after a successfull ack poll resulting in a device ack (endTransmission returned 0) the device nack at the first next write.

You are not showing all the code so it is not possible for us to review what the code is doing.

Did you have any issues with Jack's code?

--- bill

I didn’t put all the code because of the post size limitation :wink:

Nope, I didn’t reproduce the issue with the code you linked above, which is why after carreful examination of both implementations I fixed the issue !

Turns out it was a side effect of the read method, indeed here’s the former read method :

        byte* E2PRM24LC256::read(unsigned short address, unsigned short length) {
            unsigned short read = 0;
            unsigned short i = 0;
            byte data[READ_BUFFER_SIZE];

            if (length > READ_BUFFER_SIZE) {
                length = READ_BUFFER_SIZE;
            }

                //Initiate IC2 channel with device whose address was given to the constructor, this will send start condition byte
                Wire.beginTransmission(this->deviceAddress);

                //Queue the first byte of the read start address
                Wire.write((byte)(address >> 8));

                //Queue the last byte of the read start address
                Wire.write((byte)address);

                //Send stop condition to EEPROM to actually transmit read address
                Wire.endTransmission();

                //Requests X bytes from the device, this corresponds to a read start condition with R/W bit to 1
                read += Wire.requestFrom((int)this->deviceAddress, (int)length);


            //The wire library won't allow us to read more than 32 bytes at once, so we loop until
            //all the byte we want are read
            while (i < length) {

                for (;i < read; i++) {
                    data[i] = Wire.read();
                }

                read += Wire.requestFrom((int)this->deviceAddress, (int)(length - read));
            }

            return data;
        }

As you can see I wasn’t sending the read address to the device before further calls to the requestFrom method, actually I was misguided by the datasheet :

To provide sequential reads, the 24XX256 contains an internal Address Pointer which is incremented by one at the completion of each operation. This Address Pointer allows the entire memory contents to be serially read during one operation.

I did not realize that it was required to send a read address again for further read operations because of the wire library !

I fixed the method according to this implementation, ran a few tests and everything seems to be ok now, I’ll run a bunch of other tests to make sure it is stabilized and will come back to post the whole class, if it can help someone else.

Again thanks a lot for your time and help, regards.