-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
fd48d2b
commit 8ae9acc
Showing
4 changed files
with
304 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
# Written by FACTS Engineering | ||
# Copyright (c) 2023 FACTS Engineering, LLC | ||
# Licensed under the MIT license. | ||
""" | ||
`AT24MAC_EEPROM` | ||
================================================================================ | ||
AT24MACx02 EEPROM Library | ||
Provides an interface to read and write from EEPROM memory | ||
as well as retrieve the unique MAC Address and serial number | ||
Compatible with AT24MAC402 and AT24MAC602 devices using I2C | ||
* Author(s): Adam Cummick, Tristan Warder | ||
""" | ||
|
||
import time | ||
from adafruit_bus_device.i2c_device import I2CDevice | ||
from micropython import const | ||
|
||
__version__ = "0.0.0+auto.0" | ||
__repo__ = "https://github.com/facts-engineering/CircuitPython_AT24MAC_EEPROM" | ||
|
||
_EEPROM_LENGTH = const(256) # bytes | ||
_PAGE_SIZE = const(16) | ||
_WRITE_TIME = 0.005 | ||
|
||
_EUI_LOCATION = b"\x9A" | ||
_MAC_ADDR_LENGTH = const(6) | ||
|
||
# Uncomment for AT24MAC602 with EUI64 8 byte MAC Address | ||
# _EUI_LOCATION = b'\x98' | ||
# _MAC_ADDR_LENGTH = const(8) | ||
|
||
_SERIAL_NUMBER_LOCATION = b"\x80" | ||
_SERIAL_NUMBER_LENGTH = const(16) | ||
|
||
|
||
class AT24MAC: | ||
"""Class to interface with EEPROM device""" | ||
|
||
def __init__(self, i2c, address_pins=0b100): | ||
self.eeprom_device = I2CDevice(i2c, 0x50 | address_pins, True) | ||
self.eui_device = I2CDevice(i2c, 0x58 | address_pins, True) | ||
self.mac = self.read_mac_address() | ||
self.serial_number = self.read_serial_number() | ||
|
||
def read_serial_number(self): | ||
"""Reads the unique serial number for the Device""" | ||
serial_number = bytearray(_SERIAL_NUMBER_LENGTH) | ||
|
||
with self.eui_device as device: | ||
device.write_then_readinto(_SERIAL_NUMBER_LOCATION, serial_number) | ||
serial_number = int.from_bytes(serial_number, "big") | ||
|
||
return serial_number | ||
|
||
def read_mac_address(self): | ||
"""Reads the 6 byte mac address from the | ||
AT24MAC402 device | ||
""" | ||
mac_address = bytearray(_MAC_ADDR_LENGTH) | ||
|
||
with self.eui_device as device: | ||
device.write_then_readinto(_EUI_LOCATION, mac_address) | ||
|
||
return mac_address | ||
|
||
def __getitem__(self, address): | ||
if isinstance(address, int): | ||
return self._read(address) | ||
if isinstance(address, slice): | ||
if address.start is not None: | ||
start = address.start | ||
if (start < 0) or (start >= _EEPROM_LENGTH): | ||
raise ValueError("Start Address goes beyond memory limit") | ||
else: | ||
start = 0 | ||
if address.stop is not None: | ||
stop = address.stop | ||
if (stop < 0) or (stop >= _EEPROM_LENGTH): | ||
raise ValueError("Stop Address goes beyond memory limit") | ||
else: | ||
stop = _EEPROM_LENGTH | ||
length = stop - start | ||
return self._read(start, length) | ||
|
||
raise ValueError("Item must be integer or slice") | ||
|
||
def __setitem__(self, address, data): | ||
if isinstance(address, int): | ||
self._write(address, data) | ||
elif isinstance(address, slice): | ||
if address.start is not None: | ||
start = address.start | ||
if (start < 0) or (start >= _EEPROM_LENGTH): | ||
raise ValueError("Start Address goes beyond memory limit") | ||
else: | ||
start = 0 | ||
if address.stop is not None: | ||
stop = address.stop | ||
if (stop < 0) or (stop >= _EEPROM_LENGTH): | ||
raise ValueError("Stop Address goes beyond memory limit") | ||
else: | ||
stop = _EEPROM_LENGTH | ||
|
||
self._write(start, data) | ||
else: | ||
raise ValueError("Item must be integer or slice") | ||
|
||
def __len__(self): | ||
return _EEPROM_LENGTH | ||
|
||
def _read(self, data_address, length=1): | ||
"""Reads data back from EEPROM at specified address | ||
length defaults to 1, but can be specified | ||
data is returned as bytearray | ||
""" | ||
if (length + data_address) > _EEPROM_LENGTH: | ||
raise ValueError( | ||
f"Data address {data_address} and length {length} go beyond memory limit" | ||
) | ||
|
||
data_bytes = bytearray(length) | ||
data_address = data_address.to_bytes(1, "big") | ||
|
||
with self.eeprom_device as device: | ||
device.write_then_readinto(data_address, data_bytes) | ||
|
||
if length > 1: | ||
return data_bytes | ||
|
||
return data_bytes[0] | ||
|
||
def _write(self, data_address, data): | ||
"""writes a byte or bytearray to device at specified address | ||
data length is determined by bytearray passed in. Data is compared to | ||
the current page before writing to preserve EEPROM life. | ||
When writing larger than 1 page, memory is only checked page by page | ||
""" | ||
if not isinstance(data, (bytearray, bytes)): | ||
try: | ||
if 0x00 <= data <= 0xFF: | ||
data = data.to_bytes(1, "big") | ||
except TypeError: | ||
data = bytearray(data) | ||
|
||
if (len(data) + data_address) > _EEPROM_LENGTH: | ||
raise ValueError( | ||
f"Data address {data_address} and length {len(data)} go beyond memory limit" | ||
) | ||
|
||
if len(data) > 1: | ||
total_pages = int(len(data) / _PAGE_SIZE) | ||
remaining_bytes = len(data) % _PAGE_SIZE | ||
offset = _PAGE_SIZE - (data_address % _PAGE_SIZE) | ||
if offset == 16: | ||
offset = 0 | ||
remaining_bytes -= offset | ||
else: | ||
offset = 0 | ||
total_pages = 0 | ||
remaining_bytes = 1 | ||
|
||
current_address = data_address | ||
if offset > 0: | ||
self._write_page(data_address, data[0:offset]) | ||
current_address += offset | ||
if total_pages > 0: | ||
for i in range(offset, total_pages * _PAGE_SIZE, _PAGE_SIZE): | ||
this_page = data[i : i + _PAGE_SIZE] | ||
self._write_page(current_address, this_page) | ||
current_address += _PAGE_SIZE | ||
if remaining_bytes > 0: | ||
self._write_page(current_address, data[offset + total_pages * _PAGE_SIZE :]) | ||
|
||
def _write_page(self, data_address, data): | ||
"""Writes a single page to eeprom. Data is compared to current data | ||
before writing to extend EEPROM life. Delay cycle is included for write | ||
latency | ||
""" | ||
|
||
if not self._does_data_match(data_address, data): | ||
data_address = data_address.to_bytes(1, "big") | ||
data_message = data_address + data | ||
|
||
with self.eeprom_device as device: | ||
device.write(data_message) | ||
time.sleep(_WRITE_TIME) | ||
|
||
def _does_data_match(self, data_address, data): | ||
"""Compares data passed in with the data at the specified address""" | ||
|
||
current_values = self._read(data_address, len(data)) | ||
if len(data) == 1: | ||
current_values = bytes([current_values]) | ||
return current_values == data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# circuitpython-at24mac-eeprom | ||
|
||
Driver to interface with AT24MAC402 and AT24MAC602 devices using I2C | ||
|
||
## Usage | ||
|
||
the AT24MACx02 devices are EEPROM devices with a built-in MAC address. | ||
|
||
```python | ||
import at24mac | ||
import board | ||
i2c = busio.I2C(board.SCL, board.SDA) | ||
eeprom = at24mac_eeprom.AT24MAC(i2c) | ||
|
||
print(eeprom.mac) # Format for use with Wiznet5k | ||
print([hex(val) for val in eeprom.mac]) # Readable format | ||
print(eeprom.serial_number) | ||
print() | ||
|
||
# Write and read to address 0 | ||
eeprom[0] = 76 | ||
print(eeprom[0]) | ||
print() | ||
|
||
# Write and read to address 100-104 | ||
eeprom[100] = [6, 7, 8, 9, 10] | ||
print([val for val in eeprom[100:105]]) | ||
print() | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Basic Example to read and write from AT24MACx02 EEPROM Devices | ||
|
||
# Written by FACTS Engineering | ||
# Copyright (c) 2023 FACTS Engineering, LLC | ||
# Licensed under the MIT license. | ||
|
||
# | ||
|
||
import board | ||
import busio | ||
import at24mac_eeprom | ||
|
||
# Create EEPROM Object | ||
i2c = busio.I2C(board.ATMAC_SCL, board.ATMAC_SDA) | ||
# i2c = busio.I2C(board.SCL, board.SDA) # For external I2C devices | ||
eeprom = at24mac_eeprom.AT24MAC(i2c) | ||
|
||
# Address lines default to 0b100. They can be specified if needed. | ||
# eeprom = at24mac_eeprom.AT24MAC(i2c, 0b101) | ||
|
||
# Print out MAC address and serial number | ||
print(eeprom.mac) # Format for use with Wiznet5k | ||
print([hex(val) for val in eeprom.mac]) # Readable format | ||
print(eeprom.serial_number) | ||
print() | ||
|
||
# Write and read to address 0 using the device object like an array | ||
eeprom[0] = 76 | ||
print(eeprom[0]) | ||
print() | ||
|
||
# Write and read to address 100 using array slices | ||
eeprom[100] = [6, 7, 8, 9, 10] | ||
print([val for val in eeprom[100:105]]) | ||
print() | ||
|
||
while True: | ||
pass |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
[build-system] | ||
requires = [ | ||
"setuptools", | ||
"wheel", | ||
"setuptools-scm", | ||
] | ||
|
||
[project] | ||
name = "circuitpython-at24mac-eeprom" | ||
description = "A CircuitPython library to interface with AT24MAC402 and AT24MAC602 devices." | ||
version = "0.0.0+auto.0" | ||
readme = "README.md" | ||
authors = [ | ||
{name = "Adam Cummick", email = "[email protected]"} | ||
] | ||
urls = {Homepage = "https://github.com/facts-engineering/CircuitPython_AT24MAC_EEPROM"} | ||
keywords = [ | ||
"adafruit", | ||
"circuitpython", | ||
"micropython", | ||
"eeprom", | ||
"at24mac", | ||
"at24mac_eeprom" | ||
] | ||
license = {text = "MIT"} | ||
classifiers = [ | ||
"Intended Audience :: Developers", | ||
"Topic :: Software Development :: Libraries", | ||
"Topic :: Software Development :: Embedded Systems", | ||
"Topic :: System :: Hardware", | ||
"License :: OSI Approved :: MIT License", | ||
"Programming Language :: Python :: 3", | ||
] | ||
|
||
[tool.setuptools] | ||
# TODO: IF LIBRARY FILES ARE A PACKAGE FOLDER, | ||
# CHANGE `py_modules = ['...']` TO `packages = ['...']` | ||
py-modules = ["at24mac_eeprom"] |