CatFeed Counter
Da bei uns Daheim die Katze mit mitleiderregendem Auftreten alle Femilienmitglieder dazu bringt, sie zu füttern und wir komplett den Überblick verlieren, möchte ich einen FutterZähler bauen.
Dieser soll auf einem LCD Display den aktuellen Tagesstand der Fütterungen anzeigen und um Mitternacht resetted werden.
Ich hatte als Hardware noch ein wenig Zeug "rumliegen" und arbeite seit Tagen immer mal wieder an der Umsetzung...
ChatGPT Prompt
Diese Anleitung beschreibt die vollständige Umsetzung Ihres Projekts unter Verwendung des **ESP32-S3-DevKitC-1** und eines **4-Kanal I2C Pegelwandlers**. Ziel ist es, ein **1602 I2C LCD Display** und einen **433 MHz RF-Empfänger** mit dem ESP32-S3 zu verbinden, um einen Portionenzähler zu realisieren, der bei Empfang eines bestimmten RF-Codes erhöht wird.
Benötigte Komponenten
- **ESP32-S3-DevKitC-1**
- **1602 I2C LCD Display** (5V-Version)
- **433 MHz RF-Empfänger** (z.B. RXB6 oder kompatibel)
- **4-Kanal I2C Pegelwandler** (3,3V ↔ 5V bidirektional)
- **Verbindungsdrähte**
- **USB-Kabel** (für die Verbindung zwischen ESP32-S3 und Computer)
- **Optional:** Pull-Up-Widerstände (4,7 kΩ bis 10 kΩ) für I2C-Leitungen (falls erforderlich)
- **Netzteil oder USB-Stromversorgung** (falls zusätzliche Stromversorgung benötigt wird)
Hardware-Verbindungen
- **ESP32-S3-DevKitC-1**:
- **Versorgungsspannung:** Über USB-Kabel
- **5V-Pin:** Versorgung des RF-Empfängers und des LCD-Displays
- **3,3V-Pin:** Versorgung der Low-Voltage-Seite des Pegelwandlers
- **GND-Pin:** Gemeinsame Masse für alle Komponenten
- **4-Kanal I2C Pegelwandler**:
- **High-Voltage-Seite (HV):** Verbunden mit 5V-Geräten (RF-Empfänger, LCD-Display)
- **Low-Voltage-Seite (LV):** Verbunden mit ESP32-S3 (3,3V Logikpegel)
2.2. Anschluss des 4-Kanal I2C Pegelwandlers
2.2.1. Spannungsversorgung des Pegelwandlers
- **HV (High Voltage) Pin:** Mit **5V-Pin** des ESP32-S3 verbinden
- **HV GND Pin:** Mit **GND-Pin** des ESP32-S3 verbinden
- **LV (Low Voltage) Pin:** Mit **3,3V-Pin** des ESP32-S3 verbinden
- **LV GND Pin:** Mit **GND-Pin** des ESP32-S3 verbinden
2.2.2. Kanäle des Pegelwandlers
Wir verwenden drei der vier verfügbaren Kanäle:
- **Kanal 1 (HV1 ↔ LV1):** RF Data Signal
- **Kanal 2 (HV2 ↔ LV2):** I2C SCL-Leitung
- **Kanal 3 (HV3 ↔ LV3):** I2C SDA-Leitung
2.3. Anschluss des RF-Empfängers
- **Data Pin des RF-Empfängers:**
- **HV1** (High Voltage Seite Kanal 1) des Pegelwandlers anschließen
- **HV GND:** Mit **GND** des RF-Empfängers verbinden
- **VCC des RF-Empfängers:** Mit **5V-Pin** des ESP32-S3 verbinden
- **GND des RF-Empfängers:** Mit **GND-Pin** des ESP32-S3 verbinden
**Low Voltage Seite:**
- **LV1** (Low Voltage Seite Kanal 1): Mit **GPIO14** (Pin D14) des ESP32-S3 verbinden
2.4. Anschluss des LCD-Displays
- **SCL (Clock) des LCD-Displays:**
- **HV2** (High Voltage Seite Kanal 2) des Pegelwandlers anschließen
- **SDA (Data) des LCD-Displays:**
- **HV3** (High Voltage Seite Kanal 3) des Pegelwandlers anschließen
- **VCC des LCD-Displays:** Mit **5V-Pin** des ESP32-S3 verbinden
- **GND des LCD-Displays:** Mit **GND-Pin** des ESP32-S3 verbinden
**Low Voltage Seite:**
- **LV2** (Low Voltage Seite Kanal 2): Mit **GPIO9** (SCL, Pin D9) des ESP32-S3 verbinden
- **LV3** (Low Voltage Seite Kanal 3): Mit **GPIO8** (SDA, Pin D8) des ESP32-S3 verbinden
2.5. Zusammenfassung der Verbindungen
| Komponente | ESP32-S3 Pin | Pegelwandler LV | Pegelwandler HV | Externes Gerät Pin |
|------------------|---------------|-----------------|-----------------|--------------------|
| RF Data Signal | GPIO14 (D14) | LV1 | HV1 | Data Pin RF-Empfänger |
| I2C SCL | GPIO9 (D9) | LV2 | HV2 | SCL Pin LCD-Display |
| I2C SDA | GPIO8 (D8) | LV3 | HV3 | SDA Pin LCD-Display |
**Spannungsversorgung:
- **3,3V (ESP32-S3):** LV Pin Pegelwandler
- **5V (ESP32-S3):** HV Pin Pegelwandler, VCC RF-Empfänger, VCC LCD-Display
- **GND:** Alle GND-Pins miteinander verbinden (ESP32-S3, Pegelwandler, RF-Empfänger, LCD-Display)
Programmierung mit MicroPython
Benötigte Bibliotheken
- **`lcd_api.py`**: Basis-Klasse für die LCD-Ansteuerung.
- **`esp32_i2c_lcd.py`**: Anpassung der LCD-Ansteuerung für den ESP32-S3.
- **`main.py`**: Hauptprogramm.
Erstellung der Skripte
#### **4.2.1. `lcd_api.py`**
```python
# lcd_api.py
import time
class LcdApi:
# Implementierung der LCD-API-Klasse
# (Inhalt entsprechend Ihrer bisherigen Version)
# ...
```
#### **4.2.2. `esp32_i2c_lcd.py`**
```python
# esp32_i2c_lcd.py
from lcd_api import LcdApi
from machine import I2C
import time
class I2cLcd(LcdApi):
# Implementierung der I2C-LCD-Klasse für den ESP32
# (Anpassungen entsprechend Ihrer Hardware)
# ...
```
`main.py`**
```python
# main.py
from machine import Pin, I2C
import network
import utime
import _thread
from esp32_i2c_lcd import I2cLcd
import ntptime
# WLAN-Konfiguration
SSID = 'Ihr_WLAN_SSID'
PASSWORD = 'Ihr_WLAN_Passwort'
# I2C-Konfiguration
I2C_ADDR = 0x27
I2C_NUM_ROWS = 2
I2C_NUM_COLS = 16
# Initialisierung des I2C-Busses
i2c = I2C(1, scl=Pin(9), sda=Pin(8), freq=100000) # Frequenz auf 100 kHz reduziert
# LCD initialisieren
lcd = I2cLcd(i2c, I2C_ADDR, I2C_NUM_ROWS, I2C_NUM_COLS)
# RF-Empfänger an GPIO14
rf_pin = Pin(14, Pin.IN)
# WLAN-Verbindung herstellen
def connect_wifi():
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, PASSWORD)
max_wait = 15
while max_wait > 0:
if wlan.isconnected():
print('WLAN verbunden:', wlan.ifconfig())
break
max_wait -= 1
print('Warten auf WLAN-Verbindung...')
utime.sleep(1)
if not wlan.isconnected():
print('Konnte keine WLAN-Verbindung herstellen.')
return False
return True
# WLAN verbinden
if not connect_wifi():
raise SystemExit
# Zeit synchronisieren
try:
ntptime.settime()
print('Zeit synchronisiert:', utime.localtime())
except Exception as e:
print('Konnte Zeit nicht synchronisieren:', e)
# Portion-Zähler
portion_counter = 0
# Display aktualisieren
def update_display():
lcd.clear()
lcd.putstr("Portionen: {}".format(portion_counter))
# RF-Signal dekodieren
def decode_rf_signal():
import micropython
micropython.alloc_emergency_exception_buf(100)
CODE_TO_MATCH = 0xED2C78
timings = []
last_time = utime.ticks_us()
def pin_callback(pin):
nonlocal last_time, timings
current_time = utime.ticks_us()
duration = utime.ticks_diff(current_time, last_time)
last_time = current_time
level = pin.value()
timings.append((duration, level))
rf_pin.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=pin_callback)
while True:
if len(timings) > 100:
rf_pin.irq(handler=None)
code = parse_timings(timings)
timings = []
rf_pin.irq(handler=pin_callback)
if code == CODE_TO_MATCH:
print("Code empfangen:", hex(code))
increment_counter()
utime.sleep_ms(100)
def parse_timings(timings):
bits = []
for duration, level in timings:
if 200 < duration < 700:
bits.append(0)
elif 700 <= duration < 1200:
bits.append(1)
else:
return None
if len(bits) >= 24:
code = 0
for bit in bits[:24]:
code = (code << 1) | bit
return code
return None
def increment_counter():
global portion_counter
portion_counter += 1
update_display()
# Zähler um Mitternacht zurücksetzen
def reset_counter_at_midnight():
global portion_counter
while True:
current_time = utime.localtime()
if current_time[3] == 0 and current_time[4] == 0:
portion_counter = 0
update_display()
print('Portion-Zähler um Mitternacht zurückgesetzt')
utime.sleep(60)
utime.sleep(30)
# Threads starten
_thread.start_new_thread(decode_rf_signal, ())
_thread.start_new_thread(reset_counter_at_midnight, ())
# Initiales Display-Update
update_display()
# Hauptschleife
while True:
utime.sleep(1)
```