As part of the Winter Logger project, I got myself some DIP EEPROMs from eBay which were ostensibly AT24C256’s, mostly because I didn’t feel like buying yet more stuff that I probably would never use just to pad out Mouser’s £33 for free shipping minimum order.

This is what I got:

EEPROM from eBay with suspicious markings

Suspicious…

With all the counterfeit semiconductors floating out there, I wanted to verify if I at least had functional EEPROM that wasn’t literally just plastic with some legs tacked onto it - I wanted to test if all the memory cells actually worked.

CircuitPython

Python is always the first choice to hack together these sorts of one off scripts without having to go through the headache of C-like languages. Having a Pi Pico W laying around waiting to be tested, I decided to give this new CircuitPython stuff a go.

It wasn’t the ultra-smooth-switch-off-half-my-brain experience that I was used to with Python, especially with the I2C driver, and Googling didn’t immediately reveal answers, so here is a little trail for first timers out there to follow.

Code (that worked)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import time
import board
import random

# To use default I2C bus (most boards)
# i2c = board.I2C()

# To create I2C bus on specific pins
import busio
# i2c = busio.I2C(board.SCL1, board.SDA1)  # QT Py RP2040 STEMMA connector
i2c = busio.I2C(board.GP1, board.GP0)    # Pi Pico RP2040

size = 32768

# n = size/2048
currentAddress = 0

data = [random.randint(0,255) for t in range(size)]

while not i2c.try_lock():
    pass

try:
    rounds = len(data)/64
    
    currentIndex = 0
    
    print("Writing...")
    for j in range(rounds):
        strToWrite = data[currentIndex: currentIndex+64]
        dataToWrite = currentIndex.to_bytes(2, 'big')
        dataToWrite += bytearray(strToWrite)
        # print("Written round ", j)
        currentIndex += 64
        i2c.writeto(0x50, dataToWrite)
        time.sleep(0.1)
    print("Write done.")

    currentIndex = 0
    
    print("Verifying...")
    for k in range(len(data)/2048): #preveent timeout
        readback = bytearray(2048)
        outBuff = currentIndex.to_bytes(2, 'big')
        i2c.writeto_then_readfrom(0x50, outBuff, readback)
        if readback == bytearray(data[currentIndex: currentIndex+2048]):
            print("Successfully verified block starting at ", currentIndex)
        else:
            print("Failed to verify block starting at", currentIndex)
        currentIndex += 2048


finally:  # unlock the i2c bus when ctrl-c'ing out of the loop
    i2c.unlock()

If a quick EEPROM tester script is all you’re after, there you go.

This code was adapted from the I2C bus scanner script here

Gotchas

In order to save space, some of the niceties of the classic Python interpreter aren’t available, and the error messages aren’t as helpful either.

Here are some of the issues that I encountered (with Adafruit CircuitPython 7.3.3 on 2022-08-29):

  1. If you try to read too many bytes you get a timeout, so I chose to read 2048 bytes to solve this problem
  2. The to_bytes() function throws TypeError: function missing 1 required positional arguments if you specify the names of arguments because natively, MicroPython (of which CircuitPython is a fork) does not implement named arguments to save space:
    • dataToWrite = currentIndex.to_bytes(2, byteorder='big') throws TypeError: function missing 1 required positional arguments
    • dataToWrite = currentIndex.to_bytes(2, 'big') works
    • Even more confusingly, it seems like some of CircuitPython’s own libraries do support keyword arguments (arguments with the names specified)… Specifically, during testing, the i2c functions worked with keyword arguments.
  3. bytearray.extend() does not work, instead, to concatenate byte arrays, use byte_array1 += byte_array2
  4. i2c.writeto(int("0x50", base=16), dataToWrite) throws TypeError: function doesn't take keyword arguments because of the reason specified above. But I was extremely confused because in my mind the function throwing this error is i2c.writeto(), not the native int() casting. You don’t need to cast the string to int by the way, just literally typing 0x50 will work.

I’d recommend that you nest your code as little as possible by the way, since the error messages given in the terminal only tell you which line the error message occurs at, unlike the full blown desktop Python which has nice tracebacks and indicators.

Coil Whine?

As a quick aside, I noticed that the Pi Pico W was whistling when the program was running, and that the pitch would vary over time.

Interesting… Perhaps an investigation for another time.

Conclusion

Well, I’m happy to report that my EEPROM’s passed this data integrity check, time to move on with the Winter Logger project then.

Happy hacking!