Vorteile der Klassenprogrammierung
Bei grossen Programmen oder um die Lesbarkeit zu verbessern ist es empfehlenswert mit Klasse zu arbeiten.
Beispielklasse für IOPiPlus
Wenn man das folgende Programm als
iopi_plus_class.py speichert
Code: Alles auswählen
from machine import I2C, Pin
import time
class IOPiPlus:
def __init__(self, i2c_bus=0, sda_pin=21, scl_pin=22):
"""
IO-Pi-Plus Klasse für ESP32 mit beiden Chips
"""
self.i2c = I2C(i2c_bus, sda=Pin(sda_pin), scl=Pin(scl_pin), freq=100000)
# Beide Chip-Adressen
self.chip1_addr = 0x20 # Erster MCP23017
self.chip2_addr = 0x21 # Zweiter MCP23017
# Register Adressen für MCP23017
self.REGISTERS = {
'IODIRA': 0x00, 'IODIRB': 0x01, # I/O direction
'GPIOA': 0x12, 'GPIOB': 0x13, # GPIO pins
'GPPUA': 0x0C, 'GPPUB': 0x0D, # Pull-up
'OLATA': 0x14, 'OLATB': 0x15 # Output latch
}
# Initialisiere beide Chips
self._init_chip(self.chip1_addr)
self._init_chip(self.chip2_addr)
print("IO-Pi-Plus initialisiert mit 2 Chips (32 I/O Pins)")
print(f"Chip 1: 0x{self.chip1_addr:02x}, Chip 2: 0x{self.chip2_addr:02x}")
def _init_chip(self, address):
"""Initialisiert einen MCP23017 Chip"""
self._write_byte(address, self.REGISTERS['IODIRA'], 0xFF) # Alle Pins Input
self._write_byte(address, self.REGISTERS['IODIRB'], 0xFF) # Alle Pins Input
def _write_byte(self, chip_addr, reg, value):
"""Schreibt ein Byte in ein Register"""
try:
self.i2c.writeto_mem(chip_addr, reg, bytes([value]))
return True
except OSError:
print(f"Fehler beim Schreiben an Chip 0x{chip_addr:02x}, Register 0x{reg:02x}")
return False
def _read_byte(self, chip_addr, reg):
"""Liest ein Byte aus einem Register"""
try:
result = self.i2c.readfrom_mem(chip_addr, reg, 1)
return result[0]
except OSError:
print(f"Fehler beim Lesen von Chip 0x{chip_addr:02x}, Register 0x{reg:02x}")
return 0
def get_chip_and_pin(self, pin):
"""
Bestimmt Chip und internen Pin für Pin 1-32
Returns: (chip_address, bank, bit_position)
"""
if pin < 1 or pin > 32:
raise ValueError("Pin muss zwischen 1 und 32 sein")
if pin <= 16:
chip_addr = self.chip1_addr
internal_pin = pin
else:
chip_addr = self.chip2_addr
internal_pin = pin - 16
if internal_pin <= 8:
bank = 'A'
bit_pos = internal_pin - 1
else:
bank = 'B'
bit_pos = internal_pin - 9
return chip_addr, bank, bit_pos
def set_pin_direction(self, pin, direction):
"""
Setzt Pin Richtung (Input/Output)
Args:
pin: 1-32
direction: 0 = Output, 1 = Input
"""
chip_addr, bank, bit_pos = self.get_chip_and_pin(pin)
reg = self.REGISTERS[f'IODIR{bank}']
current = self._read_byte(chip_addr, reg)
if direction == 1: # Input
new_value = current | (1 << bit_pos)
else: # Output
new_value = current & ~(1 << bit_pos)
self._write_byte(chip_addr, reg, new_value)
def set_pin_pullup(self, pin, enable):
"""
Aktiviert/Deaktiviert Pull-up Widerstand
Args:
pin: 1-32
enable: True = Pull-up aktiv, False = deaktiviert
"""
chip_addr, bank, bit_pos = self.get_chip_and_pin(pin)
reg = self.REGISTERS[f'GPPU{bank}']
current = self._read_byte(chip_addr, reg)
if enable:
new_value = current | (1 << bit_pos)
else:
new_value = current & ~(1 << bit_pos)
self._write_byte(chip_addr, reg, new_value)
def write_pin(self, pin, value):
"""
Schreibt einen Wert auf einen Output-Pin
Args:
pin: 1-32
value: 0 = LOW, 1 = HIGH
"""
chip_addr, bank, bit_pos = self.get_chip_and_pin(pin)
reg = self.REGISTERS[f'GPIO{bank}']
current = self._read_byte(chip_addr, reg)
if value:
new_value = current | (1 << bit_pos)
else:
new_value = current & ~(1 << bit_pos)
self._write_byte(chip_addr, reg, new_value)
def read_pin(self, pin):
"""
Liest einen Input-Pin
Args:
pin: 1-32
Returns:
0 = LOW, 1 = HIGH
"""
chip_addr, bank, bit_pos = self.get_chip_and_pin(pin)
reg = self.REGISTERS[f'GPIO{bank}']
value = self._read_byte(chip_addr, reg)
return (value >> bit_pos) & 1
def write_port(self, chip_num, port, value):
"""
Schreibt einen Wert auf einen ganzen Port (8 Pins)
Args:
chip_num: 1 oder 2
port: 'A' oder 'B'
value: 8-bit Wert (0-255)
"""
chip_addr = self.chip1_addr if chip_num == 1 else self.chip2_addr
reg = self.REGISTERS[f'GPIO{port}']
self._write_byte(chip_addr, reg, value)
def read_port(self, chip_num, port):
"""
Liest einen ganzen Port (8 Pins)
Args:
chip_num: 1 oder 2
port: 'A' oder 'B'
Returns:
8-bit Wert (0-255)
"""
chip_addr = self.chip1_addr if chip_num == 1 else self.chip2_addr
reg = self.REGISTERS[f'GPIO{port}']
return self._read_byte(chip_addr, reg)
kann man z.B. mit
io.write_pin(17, 1) einfach den Ausgang 1 auf Port 2 setzen, im folgenden ein Beispiel, wie die Klasse aufgerufen wird:
Code: Alles auswählen
from iopi_plus_class import IOPiPlus
from machine import Pin, PWM
import time
led = Pin(2, Pin.OUT) # Built-in LED auf GPIO2
io = IOPiPlus() # Klasse Instanzieren
print("IO-Pi-Plus erfolgreich initialisiert")
try:
# Warte kurz damit alles bereit ist
time.sleep(0.1)
# Pin 17 konfigurieren
io.set_pin_direction(17, 0) # Output
print("Pin 17 als Output konfiguriert")
# Sicherheitshalber erstmal ausschalten
io.write_pin(17, 0)
time.sleep(0.1)
print("Starte Blink-Programm...")
while True:
io.write_pin(17, 1)
led.on() # on-Board (ESP32) LED ON
print("✓ LED intern/extern AN")
time.sleep(0.5)
io.write_pin(17, 0)
led.off() # on-Board (ESP32) LED OFF
print("✗ LED intern/extern AUS")
time.sleep(0.5)
except Exception as e:
print(f"Fehler: {e}")
# Programm läuft weiter aber ohne Fehler
Wünsche viel Vergnügen beim Programmieren!