-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathAT24MAC_EEPROM.py
198 lines (160 loc) · 6.76 KB
/
AT24MAC_EEPROM.py
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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.git"
_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