import time
import network
from machine import I2C, Pin, reset
from umqtt.simple import MQTTClient
import ssd1306
# --- 1. CONFIGURATIE ---
WIFI_SSID = "Wokwi-GUEST"
WIFI_PASS = ""
MQTT_BROKER = "broker.hivemq.com"
MQTT_TOPIC = b"Display_Aansturing/pcf_8575"
MQTT_ID = "Ontvanger_keypad" # Zorg dat deze anders is dan de zender!
# I2C Setup
i2c = I2C(0, scl=Pin(22), sda=Pin(21), freq= 100000)
reset_button = Pin(14, Pin.IN, Pin.PULL_UP)
oled_1 = ssd1306.SSD1306_I2C(128, 64, i2c)
oled_2 = ssd1306.SSD1306_I2C(128, 64, i2c, addr=0x3D)
print("--- I2C BUS SCAN START ---")
devices = i2c.scan()
if len(devices) == 0:
print("FOUT: Geen I2C apparaten gevonden! Check je bedrading (SCL/SDA).")
else:
print(f"Gevonden apparaten: {len(devices)}")
for device in devices:
print(f" --> Apparaat gevonden op adres: {hex(device)} (decimaal: {device})")
print("--- I2C BUS SCAN KLAAR ---\n")
# --- 2. TABEL: Code -> (Adres, Pin op PCF) ---
# Formaat: 'Ingevoerde_code': (I2C_Adres, Pin_Nummer)
# Code 10 -> Bord 0x20, Pin P10
pinnr_tabel = {
#Exp1
18: (0x20, 0), 118: (0x20, 1), 119: (0x20, 2), 120: (0x20, 3), 235: (0x20, 4), 236: (0x20, 5), 237: (0x20,6), 320: (0x20, 7),
321: (0x20, 10), 351: (0x20, 11), 350: (0x20, 12), 331: (0x20, 13), 330: (0x20, 14), 311: (0x20, 15), 310: (0x20, 16), 1171:(0x20, 17),
#Exp2
1: (0x21, 0), 2: (0x21, 1), 4: (0x21, 2), 5: (0x21, 3), 6: (0x21, 4), 7: (0x21, 5), 8: (0x21, 6), 9: (0x21, 7),
10: (0x21, 10), 11: (0x21, 11), 12: (0x21, 12), 13: (0x21, 13), 14: (0x21, 14), 15: (0x21, 15), 16: (0x21, 16), 17: (0x21, 17),
#Exp3
1170: (0X22, 0), 381: (0X22,1), 380: (0X22,2), 371: (0x22, 3), 370: (0x22, 4), 1101: (0x22, 5), 1100: (0x22, 6), 124: (0x22, 7),
391: (0x22, 10), 390: (0x22,11), 33: (0x22, 12), 1111: (0X22, 13), 1110: (0X22, 14), 30: (0X22, 15), 1121: (0X22, 16), 1120: (0X22, 17),
}
# --- 3. STATUS BEHEER ---
# Hier onthouden we de bytes per bordje zodat ze elkaar niet overschrijven
# Formaat: Adres : [Byte_Poort_A, Byte_Poort_B]
pcf_geheugen = {
0x20: [0xFF, 0xFF],
0x21: [0xFF, 0xFF],
0x22: [0xFF, 0xFF],
0x23: [0xFF, 0xFF],
0x24: [0xFF, 0xFF] #vergeet de komma niet wanneer de rest wordt toegevoegd
# 0x25: [0xFF, 0xFF],
# 0x26: [0xFF, 0xFF],
# 0x27: [0xFF, 0xFF]
}
#Geheugen chip AT24C256
EEPROM_ADDR = 0x50
def monitor_gpio():
while True:
status = [p.value() for p in pins]
print(status)
time.sleep(1)
def write_eeprom(mem_addr, data_byte):
# Schrijf 1 byte naar een 16-bits adres (addrsize=16)
i2c.writeto_mem(EEPROM_ADDR, mem_addr, bytes([data_byte]), addrsize=16)
time.sleep_ms(5)
def read_eeprom(mem_addr):
# Lees 1 byte vanaf een 16-bits adres
data = i2c.readfrom_mem(EEPROM_ADDR, mem_addr, 1, addrsize=16)
return data[0]
EXPANDER_ADDRS = [0x21, 0x22, 0x23]
def restore_expanders():
print("--- HERSTEL UIT EEPROM ---")
update_oled_2("Laatste status"," uit EEPROM","")
for i, addr in enumerate([0x20, 0x21, 0x22]): # ook deze veranderen als er meer expanders zijn aangesloten
try:
low_byte = read_eeprom(i * 2)
high_byte = read_eeprom((i * 2) + 1)
# CRUCIAAL: Als de simulator-chip leeg is (0x00 of 0xFF),
# zet de pinnen op 0xFF (ALLES UIT).
if (low_byte == 0 and high_byte == 0) or (low_byte == 255 and high_byte == 255):
low_byte, high_byte = 0xFF, 0xFF
# Sla de status op in het geheugen van de code
pcf_geheugen[addr] = [low_byte, high_byte]
# Stuur direct naar de hardware
i2c.writeto(addr, bytearray([low_byte, high_byte]))
print(f"Bord {hex(addr)} hersteld naar: {hex(low_byte)} {hex(high_byte)}")
except Exception as e:
print(f"Fout bij herstel {hex(addr)}: {e}")
def save_expander_state(exp_index, low_byte, high_byte):
# exp_index is 0, 1 of 2 (voor bord 1, 2 of 3)
write_eeprom(exp_index * 2, low_byte)
write_eeprom((exp_index * 2) + 1, high_byte)
print(f"Status opgeslagen voor expander {exp_index}")
def update_oled_1(regel1, regel2,regel3):
oled_1.fill(0) # Scherm leegmaken
oled_1.text("INVOER Monitor", 0, 0) # Titel
oled_1.text("-" * 15, 0, 10)
oled_1.text(regel1, 0, 30) # Laat zien wat er gebeurt
oled_1.text(regel2, 30, 45)
oled_1.text(regel3, 0, 55)
oled_1.show() # Update het fysieke scherm
def update_oled_2(regel1, regel2,regel3):
oled_2.fill(0) # Scherm leegmaken
oled_2.text("STATUS Monitor", 0, 0) # Titel
oled_2.text("-" * 15, 0, 10)
oled_2.text(regel1, 5, 25) # Laat zien wat er gebeurt
oled_2.text(regel2, 0, 40)
oled_2.text(regel3, 0, 55)
oled_2.show() # Update het fysieke scherm
# Startscherm
update_oled_1("Wachten op", "invoer...","")
def update_expander(adres, pin_nr, status):
global pcf_geheugen
# 1. Haal de LIJST op (niet kopiëren, maar refereren)
data = pcf_geheugen[adres]
# 2. Pas de juiste bit aan in de LIJST
if pin_nr <= 7:
bit = pin_nr
if status == 0:
# LED AAN: we maken de bit 0 (pinnr naar GND trekken)
data[0] |= (1 << bit)
else:
# LED UIT: we maken de bit 1 (pinnr naar VCC trekken)
data[0] &= ~ (1 << bit)
else:
bit = pin_nr - 10
if status == 0:
# LED AAN: we maken de bit 0
data[1] |= (1 << bit)
else:
# LED UIT: we maken de bit 1
data[1] &= ~ (1 << bit)
# 3. Schrijf de LIJST terug en verzend
pcf_geheugen[adres] = data
try:
# We sturen altijd 2 bytes: Poort A (index 0) en Poort B (index 1)
i2c.writeto(adres, bytes(data))
# NIEUW: Opslaan in EEPROM
# We bepalen de index op basis van het adres (0x20=0, 0x21=1, 0x22=2)
exp_index = adres - 0x20
save_expander_state(exp_index, data[0], data[1])
print(f"Hardware: {hex(adres)} -> {data} (Opgeslagen in EEPROM)")
except:
print("I2C Fout!")
update_oled_2("Er is een fout","Check I2C verbinding", "Met geheugen")
# --- 4. MQTT CALLBACK (Wat gebeurt er bij ontvangst?) ---
def sub_cb(topic, msg):
try:
code = int(msg.decode())
print(f"MQTT Ontvangen: {code}")
# Wis het scherm eerst, anders schrijf je over de oude tekst heen
update_oled_1(" Ontvangen"," "+str(code),"")
# Toon de vaste tekst
#oled_1.text("INVOER monitor", 0, 0)
#oled_1.text("Ontvangen:", 30, 20)
# Toon de variabele 'code' op een nieuwe regel
# We gebruiken str(code) om van het nummer tekst te maken
#oled_1.text(str(code), 50, 40)
#oled_1.show()
#update_oled_2(" "+str(code),"Status: OK","Opgeslagen")
# Belangrijk: stuur de data naar het fysieke scherm
if code in pinnr_tabel:
adres, pin = pinnr_tabel[code]
# 1. Bepaal de huidige staat
huidige_bytes = pcf_geheugen.get(adres, [0, 0])
if pin <= 7:
is_aan = not ((huidige_bytes[0] >> pin) & 1)
else:
is_aan = not ((huidige_bytes[1] >> (pin-10)) & 1)
# 2. Bepaal de nieuwe status en de tekst voor het scherm
nieuwe_status = 0 if is_aan else 1
led_tekst = "LED: UIT" if nieuwe_status == 0 else "LED: AAN"
# 3. Update de schermen
# We sturen de 'led_tekst' mee als regel 2
update_oled_2(" "+str(code), led_tekst, "Opgeslagen")
# 4. Voer de hardware wijziging uit
update_expander(adres, pin, nieuwe_status)
except Exception as e:
print(f"Fout in verwerking: {e}")
# --- 5. OPSTARTEN ---
def check_wifi():
wlan = network.WLAN(network.STA_IF)
if not wlan.isconnected():
print("Wifi verloren! Opnieuw verbinden...")
wlan.connect(WIFI_SSID, WIFI_PASS)
# Wacht maximaal 10 seconden op herstel
pogingen = 0
while not wlan.isconnected() and pogingen < 20:
time.sleep(0.5)
pogingen += 1
if wlan.isconnected():
print("Wifi hersteld!")
else:
print("Herstel mislukt, we proberen het later opnieuw.")
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASS)
while not wlan.isconnected():
time.sleep(0.5)
print("Wi-Fi Verbonden!")
connect_wifi()
try:
restore_expanders()
except Exception as e:
print(f"EEPROM Herstel mislukt: {e}")
client = MQTTClient(MQTT_ID, MQTT_BROKER)
client.set_callback(sub_cb)
client.connect()
client.subscribe(MQTT_TOPIC)
print("Systeem Klaar. Wachten op codes...")
while True:
check_wifi()
client.check_msg() # Luister continu naar de broker
#print(f"SDA: {Pin(21, Pin.IN).value()} | SCL: {Pin(22, Pin.IN).value()} | VCC: 3.3V ")
# Stel de tekst samen voor de onderste regel
#status = f"SDA:{Pin(21, Pin.IN).value()} SCL:{Pin(22, Pin.IN).value()}:3.3V"
# Schrijf naar het scherm (vervang 'oled_2' door jouw objectnaam als die anders is)
#oled_2.text(status, 0, 56)
#oled_2.show()
time.sleep(0.2)
118
18
119
120
235
236
W2A
W2R
237
W5A
W5R
W3A
W3R
W1A
W1R
W17A
1
2
4
5
6
7
8
9
17
16
15
14
13
12
11
10
W8A
W8R
W17R
W7A
W7R
W10A
W10R
124