#include <Adafruit_GFX.h> // Grundlegende Grafikfunktionen (Text, Linien, Rechtecke)
#include <Adafruit_ILI9341.h> // Treiber für das ILI9341 TFT Display
#include <SPI.h> // SPI-Kommunikation (wird vom Display genutzt)
// ================= DISPLAY =================
#define TFT_CS 10 // Chip Select Pin (wählt das Display aus)
#define TFT_DC 9 // Data/Command Pin (unterscheidet Daten/Befehle)
#define TFT_RST 8 // Reset Pin für das Display
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST); // Display-Objekt erzeugen
// ================= PINS =================
#define CURRENT_PIN A0 // Analogeingang für Stromsensor (Potentiometer simuliert ACS758)
#define SPEED_PIN 2 // Digitaler Pin für Radsensor (Interrupt-Pin!)
// ================= SYSTEM =================
const float VREF = 5.0; // Referenzspannung des Arduino (5V)
const float ZERO_V = 2.5; // Spannung bei 0A (Offset des ACS758)
const float SENS = 0.01; // Sensitivität (10mV/A = 0.01V/A)
const float WHEEL = 2.1; // Radumfang in Metern (2100 mm)
const float SYS_VOLT = 24.0; // Systemspannung für Leistungsberechnung
// ================= STATE =================
volatile unsigned long pulses = 0; // Impulszähler (volatile wegen Interrupt!)
float fCurrent = 0; // Gefilterter Stromwert
float fSpeed = 0; // Gefilterte Geschwindigkeit
float power = 0; // Leistung in Watt
float distance = 0; // Gesamtstrecke in Metern
// ================= TIMING =================
unsigned long lastTime = 0; // Speichert letzten Berechnungszeitpunkt
// ================= GRAPH =================
int graphX = 0; // Aktuelle X-Position im Diagramm
// ================= WIDGET CACHE =================
float wCurrent = 9999; // Letzt angezeigter Strom (zum Vergleich)
float wSpeed = 9999; // Letzt angezeigte Geschwindigkeit
float wPower = 9999; // Letzt angezeigte Leistung
float wDistance = 9999; // Letzt angezeigte Strecke
// ================= INTERRUPT =================
void countPulse() {
pulses++; // Wird bei jeder Radumdrehung aufgerufen → Zähler erhöhen
}
// ================= SETUP =================
void setup() {
tft.begin(); // Display initialisieren
tft.setRotation(1); // Querformat einstellen
tft.fillScreen(ILI9341_BLACK); // Bildschirm schwarz löschen
pinMode(SPEED_PIN, INPUT_PULLUP); // Eingang mit internem Pullup (stabiler Signalpegel)
// Interrupt aktivieren:
// Bei FALLING Flanke wird countPulse() ausgeführt
attachInterrupt(digitalPinToInterrupt(SPEED_PIN), countPulse, FALLING);
drawStaticUI(); // Feste UI-Elemente einmal zeichnen
lastTime = millis(); // Startzeit speichern
}
// ================= UI STATIC =================
void drawStaticUI() {
tft.setTextSize(2); // Schriftgröße einstellen
tft.setTextColor(ILI9341_WHITE); // Textfarbe weiß
// Beschriftungen (bleiben statisch → werden nie gelöscht)
tft.setCursor(10, 10); tft.print("I:"); // Strom
tft.setCursor(10, 50); tft.print("v:"); // Geschwindigkeit
tft.setCursor(10, 90); tft.print("P:"); // Leistung
tft.setCursor(10, 130); tft.print("km:"); // Strecke
// Rahmen für Strombalken
tft.drawRect(20, 200, 280, 20, ILI9341_WHITE);
// Rahmen für Graph
tft.drawRect(20, 230, 280, 80, ILI9341_WHITE);
}
// ================= LOOP =================
void loop() {
// ===== STROMMESSUNG =====
int raw = analogRead(CURRENT_PIN); // ADC-Wert (0–1023) einlesen
float voltage = (raw * VREF) / 1023.0; // Umrechnung in Spannung (Volt)
float current = (voltage - ZERO_V) / SENS; // Strom berechnen (ACS758 Formel)
// Glättung (Low-Pass Filter → reduziert Rauschen)
fCurrent = fCurrent * 0.8 + current * 0.2;
// ===== GESCHWINDIGKEIT =====
unsigned long now = millis(); // aktuelle Zeit holen
if (now - lastTime >= 300) { // alle 300 ms berechnen
noInterrupts(); // Interrupts kurz deaktivieren (kritischer Bereich)
unsigned long p = pulses; // aktuelle Impulse sichern
pulses = 0; // Zähler zurücksetzen
interrupts(); // Interrupts wieder aktivieren
float dt = (now - lastTime) / 1000.0; // Zeitdifferenz in Sekunden
float freq = p / dt; // Impulse pro Sekunde (Frequenz)
float speed = freq * WHEEL * 3.6; // Geschwindigkeit in km/h
// Glättung der Geschwindigkeit
fSpeed = fSpeed * 0.7 + speed * 0.3;
distance += p * WHEEL; // Strecke erhöhen (Meter)
lastTime = now; // Zeit aktualisieren
}
// ===== LEISTUNG =====
power = fCurrent * SYS_VOLT; // Leistung = Strom * Spannung
// ===== UI UPDATE =====
updateCurrentWidget(); // Stromanzeige aktualisieren
updateSpeedWidget(); // Geschwindigkeitsanzeige aktualisieren
updatePowerWidget(); // Leistungsanzeige aktualisieren
updateDistanceWidget(); // Streckenanzeige aktualisieren
// ===== GRAPH =====
updateGraph(fCurrent); // Stromverlauf zeichnen
delay(20); // kleine Pause (CPU entlasten)
}
// ================= WIDGET: CURRENT =================
void updateCurrentWidget() {
if (abs(fCurrent - wCurrent) < 0.2) return; // Nur aktualisieren wenn Änderung groß genug
tft.fillRect(60, 10, 120, 30, ILI9341_BLACK); // Alten Wert löschen
tft.setCursor(60, 10); // Position setzen
tft.setTextSize(3); // Große Schrift
// Farbe je nach Vorzeichen
tft.setTextColor(fCurrent < 0 ? ILI9341_RED : ILI9341_GREEN);
tft.print(fCurrent, 1); // Stromwert mit 1 Nachkommastelle
tft.print("A"); // Einheit
wCurrent = fCurrent; // neuen Wert speichern
}
// ================= WIDGET: SPEED =================
void updateSpeedWidget() {
if (abs(fSpeed - wSpeed) < 0.5) return;
tft.fillRect(60, 50, 140, 30, ILI9341_BLACK);
tft.setCursor(60, 50);
tft.setTextSize(3);
tft.setTextColor(ILI9341_CYAN);
tft.print(fSpeed, 1);
tft.print("km/h");
wSpeed = fSpeed;
}
// ================= WIDGET: POWER =================
void updatePowerWidget() {
if (abs(power - wPower) < 2) return;
tft.fillRect(60, 90, 140, 30, ILI9341_BLACK);
tft.setCursor(60, 90);
tft.setTextSize(2);
tft.setTextColor(ILI9341_YELLOW);
tft.print(power, 0);
tft.print("W");
wPower = power;
}
// ================= WIDGET: DISTANCE =================
void updateDistanceWidget() {
if (abs(distance - wDistance) < 5) return;
tft.fillRect(60, 130, 140, 30, ILI9341_BLACK);
tft.setCursor(60, 130);
tft.setTextSize(2);
tft.setTextColor(ILI9341_MAGENTA);
tft.print(distance / 1000.0, 2); // Meter → km
tft.print("km");
wDistance = distance;
}
// ================= GRAPH =================
void updateGraph(float value) {
// Alte Spalte löschen (nur 1 Pixel breit → kein Flackern!)
tft.drawFastVLine(20 + graphX, 231, 78, ILI9341_BLACK);
// Wert auf Displayhöhe abbilden
int y = map(value, -100, 100, 308, 232);
// neuen Punkt zeichnen
tft.drawPixel(20 + graphX, y, ILI9341_GREEN);
graphX++; // nächste X-Position
if (graphX >= 280) { // wenn rechts angekommen
graphX = 0; // wieder links beginnen
}
}Loading
ili9341-cap-touch
ili9341-cap-touch