Skip to content

Instantly share code, notes, and snippets.

@dglaude
Created December 20, 2021 00:55
Show Gist options
  • Save dglaude/6e1fbe1e2c2d1222f99872a506a48a2b to your computer and use it in GitHub Desktop.
Save dglaude/6e1fbe1e2c2d1222f99872a506a48a2b to your computer and use it in GitHub Desktop.
Writing URI into st25dv16 from CircuitPython
# SPDX-FileCopyrightText: Copyright (c) 2021 Tim Cocks for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_st25dv16`
================================================================================
CircuitPython driver for the I2C EEPROM of the Adafruit ST25DV16 Breakout
* Author(s): Tim Cocks
Implementation Notes
--------------------
**Hardware:**
* `Adafruit ST25DV16K I2C RFID EEPROM Breakout - STEMMA QT / Qwiic <https://www.adafruit.com/product/4701>`_
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
https://github.com/adafruit/circuitpython/releases
# * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
# * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
"""
# imports
import time
from micropython import const
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_24LC32.git" ### fix me
_MAX_SIZE_I2C = const(0x800) # 16kbits
class EEPROM:
"""
Driver base for the EEPROM Breakout.
"""
def __init__(self, max_size, write_protect=False, wp_pin=None):
self._max_size = max_size
self._wp = write_protect
self._wraparound = False
if not wp_pin is None:
self._wp_pin = wp_pin
# Make sure write_prot is set to output
self._wp_pin.switch_to_output()
self._wp_pin.value = self._wp
else:
self._wp_pin = wp_pin
@property
def write_wraparound(self):
"""Determines if sequential writes will wrapaound highest memory address
(``len(EEPROM) - 1``) address. If ``False``, and a requested write will
extend beyond the maximum size, an exception is raised.
"""
return self._wraparound
@write_wraparound.setter
def write_wraparound(self, value):
if not value in (True, False):
raise ValueError("Write wraparound must be 'True' or 'False'.")
self._wraparound = value
@property
def write_protected(self):
"""The status of write protection. Default value on initialization is
``False``.
When a ``WP`` pin is supplied during initialization, or using
``write_protect_pin``, the status is tied to that pin and enables
hardware-level protection.
When no ``WP`` pin is supplied, protection is only at the software
level in this library.
"""
return self._wp if self._wp_pin is None else self._wp_pin.value
def __len__(self):
"""The size of the current EEPROM chip. This is one more than the highest
address location that can be read or written to.
.. code-block:: python
fram = adafruit_fram.FRAM_xxx() # xxx = 'I2C' or 'SPI'
# size returned by len()
len(fram)
# can be used with range
for i in range(0, len(fram))
"""
return self._max_size
def __getitem__(self, address):
"""Read the value at the given index, or values in a slice.
.. code-block:: python
# read single index
fram[0]
# read values 0 thru 9 with a slice
fram[0:9]
"""
if isinstance(address, int):
if not 0 <= address < self._max_size:
raise ValueError(
"Address '{0}' out of range. It must be 0 <= address < {1}.".format(
address, self._max_size
)
)
buffer = bytearray(1)
read_buffer = self._read_address(address, buffer)
elif isinstance(address, slice):
if address.step is not None:
raise ValueError("Slice stepping is not currently available.")
regs = list(
range(
address.start if address.start is not None else 0,
address.stop + 1 if address.stop is not None else self._max_size,
)
)
if regs[0] < 0 or (regs[0] + len(regs)) > self._max_size:
raise ValueError(
"Address slice out of range. It must be 0 <= [starting address"
":stopping address] < {0}.".format(self._max_size)
)
buffer = bytearray(len(regs))
read_buffer = self._read_address(regs[0], buffer)
return read_buffer
def __setitem__(self, address, value):
"""Write the value at the given starting index.
.. code-block:: python
# write single index
fram[0] = 1
# write values 0 thru 4 with a list
fram[0:4] = [0,1,2,3]
"""
if self.write_protected:
raise RuntimeError("FRAM currently write protected.")
if isinstance(address, int):
if not isinstance(value, int):
raise ValueError("Data must be a single integer for single addresses")
if not 0 <= address < self._max_size:
raise ValueError(
"Address '{0}' out of range. It must be 0 <= address < {1}.".format(
address, self._max_size
)
)
self._write(address, value, self._wraparound)
elif isinstance(address, slice):
if not isinstance(value, (bytes, bytearray, list, tuple)):
raise ValueError(
"Data must be bytes, bytearray, list, "
"or tuple for multiple addresses"
)
if (address.start is None) or (address.stop is None):
raise ValueError("Boundless slices are not supported")
if (address.step is not None) and (address.step != 1):
raise ValueError("Slice stepping is not currently available.")
if (address.start < 0) or (address.stop > self._max_size):
raise ValueError(
"Slice '{0}:{1}' out of range. All addresses must be 0 <= address < {2}.".format( # pylint: disable=line-too-long
address.start, address.stop, self._max_size
)
)
if len(value) < (len(range(address.start, address.stop))):
raise ValueError(
"Cannot set values with a list smaller than the number of indexes"
)
self._write(address.start, value, self._wraparound)
def _read_address(self, address, read_buffer):
# Implemented by subclass
raise NotImplementedError
def _write(self, start_address, data, wraparound):
# Implemened by subclass
raise NotImplementedError
class EEPROM_I2C(EEPROM):
"""I2C class for EEPROM.
:param: ~busio.I2C i2c_bus: The I2C bus the EEPROM is connected to.
:param: int address: I2C address of EEPROM. Default address is ``0x50``.
:param: bool write_protect: Turns on/off initial write protection.
Default is ``False``.
:param: wp_pin: (Optional) Physical pin connected to the ``WP`` breakout pin.
Must be a ``digitalio.DigitalInOut`` object.
"""
# pylint: disable=too-many-arguments
def __init__(self, i2c_bus, address=0x53, write_protect=False, wp_pin=None):
from adafruit_bus_device.i2c_device import ( # pylint: disable=import-outside-toplevel
I2CDevice as i2cdev,
)
self._i2c = i2cdev(i2c_bus, address)
super().__init__(_MAX_SIZE_I2C, write_protect, wp_pin)
def _read_address(self, address, read_buffer):
write_buffer = bytearray(2)
write_buffer[0] = address >> 8
write_buffer[1] = address & 0xFF
with self._i2c as i2c:
i2c.write_then_readinto(write_buffer, read_buffer)
return read_buffer
def _write(self, start_address, data, wraparound=False):
# Decided against using the chip's "Page Write", since that would require
# doubling the memory usage by creating a buffer that includes the passed
# in data so that it can be sent all in one `i2c.write`. The single-write
# method is slower, and forces us to handle wraparound, but I feel this
# is a better tradeoff for limiting the memory required for large writes.
buffer = bytearray(3)
if not isinstance(data, int):
data_length = len(data)
else:
data_length = 1
data = [data]
if (start_address + data_length) > self._max_size:
if wraparound:
pass
else:
raise ValueError(
"Starting address + data length extends beyond"
" FRAM maximum address. Use ``write_wraparound`` to"
" override this warning."
)
with self._i2c as i2c:
for i in range(0, data_length):
if not (start_address + i) > self._max_size - 1:
buffer[0] = (start_address + i) >> 8
buffer[1] = (start_address + i) & 0xFF
else:
buffer[0] = ((start_address + i) - self._max_size + 1) >> 8
buffer[1] = ((start_address + i) - self._max_size + 1) & 0xFF
buffer[2] = data[i]
i2c.write(buffer)
time.sleep(0.005)
# pylint: disable=no-member
@EEPROM.write_protected.setter
def write_protected(self, value):
if value not in (True, False):
raise ValueError("Write protected value must be 'True' or 'False'.")
self._wp = value
if not self._wp_pin is None:
self._wp_pin.value = value
# SPDX-FileCopyrightText: Copyright (c) 2021 David Glaude
#
# SPDX-License-Identifier: Unlicense
import board
import adafruit_st25dv16
i2c = board.I2C()
eeprom = adafruit_st25dv16.EEPROM_I2C(i2c)
# Value Protocol
# ----- --------
# 0x00 No prepending is done ... the entire URI is contained in the URI Field
# 0x01 http://www.
# 0x02 https://www.
# 0x03 http://
# 0x04 https://
#Goal: https://circuitpython.org/
#head=0x04
#str="circuitpython.org/"
#Goal: https://learn.adafruit.com/adafruit-st25dv16k-i2c-rfic-eeprom-breakout
head=0x04
str="learn.adafruit.com/adafruit-st25dv16k-i2c-rfic-eeprom-breakout"
#Goal: https://www.poureva.be/spip.php?article997
#head=0x02
#str="poureva.be/spip.php?article997"
l=len(str)
buf = bytearray ([0xe1, 0x40, 0x40, 0x05, 0x03, 0x00, 0xd1, 0x01, 0x00, 0x55])
buf[5] = (l+5)
buf[8] = (l+1)
eeprom[0:len(buf)]=buf
eeprom[len(buf)]=head
k=len(buf)+1
eeprom[k:k+l]=bytearray(str)
eeprom[k+l]=0xfe
print("length: {}".format(len(eeprom)))
for i in range(0, 5):
j = i * 16
hex_string = ":".join("%02x" % b for b in eeprom[j:j+15])
print(j, "> ", hex_string, "> ", eeprom[j:j+15])
while True:
pass
@racer161
Copy link

you might like to have this wrapper as well

class ST25DV16:

    header_to_string = {
        0x00: "",
        0x01: "http://www.",
        0x02: "https://www.",
        0x03: "http://",
        0x04: "https://",
    }

    def __init__(self, i2c_bus):
        self._eeprom = EEPROM_I2C(i2c_bus, 0x53)

    #prepend data values
    # Value    Protocol
    # -----    --------
    # 0x00     No prepending is done ... the entire URI is contained in the URI Field
    # 0x01     http://www.
    # 0x02     https://www.
    # 0x03     http://
    # 0x04     https://
    #eeprom[0:len(buf)]=buf
    #eeprom[len(buf)]=head
    def read_data(self):
        #read all the data from the user memory and pretty print it
        data = self._eeprom[0:0x100]

        head = data[10]
        string_data = data[10:]

        print("Header: ", self.header_to_string[head])
        
        decoded_string_data = ""
        for char in string_data:
            #decode the hex value as a character
            decoded_string_data += chr(char)

        print(decoded_string_data)

    def write_string(self, string_data, header=0x04):
        boiler_plate = bytearray([0xe1, 0x40, 0x40, 0x05, 0x03, 0x00, 0xd1, 0x01, 0x00, 0x55])

        l=len(string_data)

        #set the length of the data
        boiler_plate[5] = l + 5
        #set the length of the header
        boiler_plate[8] = l + 1

        #write the boiler plate to the eeprom
        self._eeprom[0:len(boiler_plate)] = boiler_plate
        #write the header to the eeprom
        self._eeprom[len(boiler_plate)] = header

        k=len(boiler_plate)+1
        #write the data to the eeprom
        self._eeprom[k:k+l]=bytearray(string_data, 'utf-8')
        self._eeprom[k+l]=0xfe

        #set the rest of the eeprom to 0x00
        self._eeprom[k+l+1:0x100] = bytearray(0x100 - (k+l+1))

@dglaude
Copy link
Author

dglaude commented Sep 29, 2024

Thanks @racer161

This piece of code is kind of abandon (from my side), I hope it helped or I hope your piece will help other, so it is very unlikely if you don't find the parent library and contribute an example to it.

I am pretty sure my code is a steal from one example in Adafruit Circuit Python Library.
Or could it be this: https://github.com/markmcgookin/pico-i2c-rfid
So far I don't trace the origin, something adafruit circuitpython i2c eeprom on github?
Like this: https://github.com/adafruit/Adafruit_CircuitPython_24LC32

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment