Kann mir jemand helfen

Ich habe ein schönes Programm für den Nano gefunden und möchte zusätzlich einen Piepser ansteuern, wenn der Timer abgelaufen ist. Kann mir jemand helfen, das umzusetzen. Ich bin absoluter Neuling und habe nur den Code gefunden und eingespielt. Funktioniert sowie ich das brauche, nur ein Piepton nach ablauf des Timers wäre perfekt. Leider kenn ich mich noch nicht mit der Materie aus und würde mich um Eure Hilfe freuen

_________________________________________________________________

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <EEPROM.h>

#define SCREEN_WIDTH 128    // Breite des OLED-Displays in Pixel
#define SCREEN_HEIGHT 32    // Höhe des OLED-Displays in Pixel
#define SCREEN_ADDRESS 0x3C // Adresse siehe Datenblatt; 0x3D für 128x64, 0x3C für 128x32
#define OLED_RESET 4        // Reset-Pin # (oder -1, wenn der Arduino-Reset-Pin geteilt wird)

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

/*
PIN CONNECTION
------------------------------------        ------------------------------------  
| Arduino pin |     Nama tombol    |        | Arduino pin  |  OLED display pin |
------------------------------------        ------------------------------------
|      2      |     Left Button    |        |      A4      |       SDA         |
|      3      |     Right Button   |        |      A5      |       SCL         |
|      4      |     OK Button      |        ------------------------------------
------------------------------------        
*/
// --------------------- VARIABLEN FÜR BOX CURSOR ---------------------------
byte page_index = 0;
byte _x, _y, _z = 0;
byte cursor_index = 1;
byte cursor_row_pos = 0;
byte max_cursor_index = 1;

// --------------------- VARIABLEN FÜR TASTEN  --------------------------------
// Der Tastenstatus ist normalerweise hoch, da er auf INPUT_PULLUP gesetzt ist, bei Betätigung ist der Ausgabewert = LOW
#define left_button 2
#define right_button 3
#define ok_button 4

#define relay_pin 12

bool paused = false;
bool blinking = false;
bool showCursor = true;
bool timer_active = false;
bool last_ok_button = HIGH;
bool last_left_button = HIGH;
bool last_right_button = HIGH;

// -------------------- ANGEBEN DER TIMER-ADRESSE IM EEPROM -----------------------
const byte HOUR_ADDRESS = 10;
const byte MINUTE_ADDRESS = 12;
const byte SECOND_ADDRESS = 14;

byte jam = 0 ;
byte menit = 0;
byte detik = 0;

byte _time[3];      // Array-Variable, wenn Index = 0 -> Stunden, Index = 1 -> Minuten, Index = 2 -> Sekunden
byte temp_time = 0; // temporäre Variablen / Hilfsvariablen

unsigned long last_blink_time = 0;
unsigned long last_millis_timer = 0;
unsigned long last_time_button_release = 0;
int elapsed_time = 0;  // verbleibende Verlängerung nach 1 Sekunde

void readTimeFromEEPROM() {
    _time[0] = EEPROM.read(HOUR_ADDRESS);
    _time[1] = EEPROM.read(MINUTE_ADDRESS);
    _time[2] = EEPROM.read(SECOND_ADDRESS);
}

void setup() {
    // Stellen Sie den Pin als INPUT/OUTPUT ein
    pinMode(left_button, INPUT_PULLUP);
    pinMode(right_button, INPUT_PULLUP);
    pinMode(ok_button, INPUT_PULLUP);
    pinMode(relay_pin, OUTPUT);

    // Stellen Sie den Ausgang beim Hochfahren auf „Low“ ein
    digitalWrite(relay_pin, LOW);
    // Laden Sie die im EEPROM gespeicherten Timer-Daten und speichern Sie sie in einer Array-Variablen
    readTimeFromEEPROM();

    // ----------------------------------- OLED Setup ----------------------------------------
    if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
        // Serial.println(F("SSD1306-Zuweisung fehlgeschlagen"));
        for(;;); // Fahren Sie nicht fort, sondern wiederholen Sie die Schleife für immer
    }

    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.cp437(true);
    display.setTextWrap(false);
}

void printText(bool clear_display, int col, int row, char *message, bool visible) {
    if (clear_display) display.clearDisplay();

    display.setCursor(col, row);
    display.print(message);

    if (visible) display.display();
}

void drawRectangleCursor(byte row_pos, byte x, byte y, byte z) {
    
    for (byte i = 0; i < y - x; i++) {
        display.drawPixel(x + i, row_pos, SSD1306_WHITE); // obere horizontale Linie
        display.drawPixel(x + i, z, SSD1306_WHITE);       // untere horizontale Linie

        if (i < (z - row_pos)) {
            display.drawPixel(x, row_pos + i, SSD1306_WHITE); // linke vertikale Linie
            display.drawPixel(y, row_pos + i, SSD1306_WHITE); // rechte vertikale Linie
        }
    }
}

void updateCursorPos() {
    if (paused || timer_active && !paused) {
        cursor_row_pos = 21;

        if (cursor_index == 0) { // Cursor im Lebenslaufmenü
            _x = 9;
            _y = 50;
            _z = 31;
        }
        else { // Cursor im Stoppmenü
            _x = 57;
            _y = 86;
            _z = 31;
        }
    }
    else if (paused == false && timer_active == false) {
        if (page_index == 0) {      
            if (cursor_index == 0) cursor_row_pos = 0; else cursor_row_pos = 18;
        }   
        else {
            cursor_row_pos = 0;
            // Wenn Cursor_Index 0..2 bedeutet, dass sich der Cursor in der Timer-Position befindet (0 = Stunde, 1 = Minute, 2 = Sekunde)
            switch (cursor_index) {
                case 0:
                    _x = 2; 
                    _y = 29;
                    _z = 18;
                    break;
                case 1:
                    _x = 38;
                    _y = 65;
                    _z = 18;
                    break;
                case 2:
                    _x = 74;
                    _y = 101;
                    _z = 18;
                    break;
                case 3:
                    cursor_row_pos = 20;
                    _x = 10;
                    _y = 38;
                    _z = 31;
                    break;
                default:
                    cursor_row_pos = 20;
                    _x = 57;
                    _y = 96;
                    _z = 31;
            }
        }
    }
}

void showPageOneMenu() {
    max_cursor_index = 1;
    printText(true, 13, 0, "SETTING", false);
    printText(false, 13, 18, "START", false);    
    printText(false, 0, cursor_row_pos, ">", true);
}

void setMenu(char *left_option, char *right_option) {
    updateAndShowTimer();
    display.setTextSize(1);
    printText(false, 13, 22, left_option, false);
    printText(false, 60, 22, right_option, false);
    display.setTextSize(2);
}

void showPageTwoMenu() {
    max_cursor_index = 4;
    setMenu("save", "cancel");
}

void showPausedMenu() {
    display.clearDisplay();
    max_cursor_index = 1;
    drawRectangleCursor(cursor_row_pos, _x, _y, _z);
    setMenu("resume", "stop");
    display.display();  
}

void runAndShowTimer() {
    display.clearDisplay();
    max_cursor_index = 1;
    drawRectangleCursor(cursor_row_pos, _x, _y, _z);
    setMenu("pause", "stop");
    display.display();
}

void readTime() {
    jam   = _time[0];
    menit = _time[1];
    detik = _time[2];
}

void updateAndShowTimer() {
    char timer_char[9];

    // Wenn der Timer nicht aktiv ist, werden die Stunden-, Minuten- und Sekundenwerte nur zur Anzeige geladen und nicht subtrahiert
    if (!timer_active) {
        readTime();
    }
    else {
        if (millis() - last_millis_timer >= 1000) {
            if (!paused) {
                // Sparen Sie die verbleibende überschüssige Zeit
                elapsed_time = (millis() - last_millis_timer) - 1000;

                if (detik <= 0) {
                    detik = 60;

                    if (menit > 0) 
                        menit--;
                    else if (menit <= 0 && jam > 0) {
                        jam--;
                        menit = 59;
                    }
                }                

                detik--;
                last_millis_timer = millis() - elapsed_time;

                if (jam <= 0 && menit <= 0 && detik <= 0) timer_active = false;                
            }
        }
    }

    sprintf_P(timer_char, PSTR("%02d:%02d:%02d"), jam, menit, detik);
    printText(false, 5, 2, timer_char, false);
}

void showMenu() {
    updateCursorPos();
    
    if (timer_active) {
        if (paused) {
            showPausedMenu(); 
            digitalWrite(relay_pin, HIGH);
        }
        else {
            runAndShowTimer();
            digitalWrite(relay_pin, LOW);
        }
    }
    else {
        paused = false;
        digitalWrite(relay_pin, HIGH);

        if (page_index == 0) {
            showPageOneMenu();
            blinking = false;                
        }
        else {
            display.clearDisplay(); // Reinigen Sie den Bildschirm
            showPageTwoMenu();      // Menüseite 2 anzeigen

            // Wenn das Blinken aktiv ist und die Blinkzeit >= 300 ms beträgt, dann wird ein Cursorfeld angezeigt, damit ein Blinkeffekt entsteht
            if (blinking && millis() - last_blink_time >= 300) {
                showCursor = !showCursor;
                last_blink_time = millis();
            }
            
            // Wenn das Blinken nicht aktiv ist, ist kein Blinkeffekt im Cursorfeld erforderlich (Cursor immer anzeigen).
            if (!blinking) showCursor = true;
            
            if (showCursor) drawRectangleCursor(cursor_row_pos, _x, _y, _z);

            display.display(); // den Text erneut auf dem Bildschirm anzeigen
        }
    }
}

bool checkButtonState(bool btn_name, bool &last_button_state) {
    bool result = false;
    
    // Wenn btn_name == LOW, bedeutet dies, dass die Taste gedrückt wird
    if (btn_name == LOW) {
        // Wenn der vorherige Tastenstatus = HIGH ist, bedeutet dies, dass die Taste zuvor nicht gedrückt wurde
        if (last_button_state == HIGH) 
            result = true;
        // Wenn der vorherige Tastenstatus = NIEDRIG ist, bedeutet dies, dass die Taste gedrückt und gehalten wird
        // Funktionen zum schnelleren Einstellen des Timerwerts durch einfaches Drücken und Halten der Taste, anstatt die Tasten einzeln zu drücken
        // um den Timerwert beim Einstellen des Timers zu ändern
        else {
            // wenn die Taste länger als 2 Sekunden gedrückt und gehalten wird und blinkt = wahr (stellt die Zeit ein)
            if (millis() - last_time_button_release >= 2000 && blinking) result = true;
        }

        last_button_state = LOW;
    }
    else {
        result = false;
        last_button_state = HIGH;
    }
    
    return result;
}

void loop() {
    showMenu();

    // ---------------------- Schaltflächenstatus lesen ----------------------------
    bool left_button_state = digitalRead(left_button);
    bool right_button_state = digitalRead(right_button);
    bool ok_button_state = digitalRead(ok_button);

    bool ok_button_pressed = checkButtonState(ok_button_state, last_ok_button);
    bool left_button_pressed = checkButtonState(left_button_state, last_left_button);
    bool right_button_pressed = checkButtonState(right_button_state, last_right_button);

    if (left_button_state == 1 && right_button_state == 1 && ok_button_state == 1) {
        last_time_button_release = millis();
    }

    // wenn die Rechts-/Auf-Taste gedrückt wird
    if (right_button_pressed) {
        // Wenn Blinken = wahr ist, bedeutet dies, dass Sie den Timerwert auf Stunden, Minuten oder Sekunden einstellen
        if (blinking) { // Einstellen des Timerwerts (Inkrement)
            byte max_value = 99;
            // Der Indexcursor bestimmt, ob die Zeit in Stunden, Minuten oder Sekunden eingestellt wird                    
            // Wenn der Cursorindex > 0 bedeutet, dass Minuten oder Sekunden eingestellt werden, ist der Maximalwert = 59
            if (cursor_index > 0) max_value = 59;
            
            temp_time++;
            if (temp_time > max_value) temp_time = 0;

            _time[cursor_index] = temp_time;
        }
        else { // Legen Sie den Wert „cursor_index“ fest
            cursor_index++;

            if (cursor_index > max_cursor_index) cursor_index = 0;
        }
    } 
    // wenn linke Taste / nach unten gedrückt wird
    else if (left_button_pressed) {
        // Wenn Blinken = wahr ist, bedeutet dies, dass Sie den Timerwert auf Stunden, Minuten oder Sekunden einstellen
        if (blinking) { // Einstellen des Timerwerts (Subtraktion)
            byte max_value = 99;

            if (cursor_index > 0) max_value = 59;
            
            temp_time--;
            // Überprüfen Sie erneut, ob temp_time > max_value ist, da Sie eine Variable vom Typ Byte verwenden
            // und der Wert < 0, dann beträgt der Wert 255
            if (temp_time > max_value) temp_time = max_value; else if (temp_time < 0) temp_time = max_value;

            _time[cursor_index] = temp_time;
        }
        else {
            cursor_index--;

            if (cursor_index < 0 || cursor_index > max_cursor_index) cursor_index = max_cursor_index;
        }
    }
    // wenn die OK-Taste gedrückt wird
    else if (ok_button_pressed) {
        
        if (timer_active) {
            // Wenn Cursor_Index = 0 ist, bedeutet dies, dass der Cursor auf das Pause- oder Fortsetzungsmenü fokussiert ist
            if (cursor_index == 0) {
                // Gibt den Wert der angehaltenen Variablen zurück. Wenn er zuvor wahr war, wird er falsch und umgekehrt
                if (paused) {
                    paused = false;
                    last_millis_timer = millis();
                }
                else
                    paused = true;
            }
            // Wenn Cursor_Index = 0 ist, bedeutet dies, dass der Cursor auf das Stoppmenü fokussiert ist
            else
                timer_active = false;
        }
        // wenn der Timer nicht aktiv ist
        else {
            if (page_index == 0) {
                // Gehe zur nächsten Indexseite, wenn der Cursorindex = 0 oder im Einstellungsmenü
                if (cursor_index == 0) 
                    page_index++;
                else {
                    // cursor index = 1 --> Der Cursor konzentriert sich auf das START-Menü und aktiviert dann den Timer
                    timer_active = true;

                    // Laden Sie den in der Array-Variablen gespeicherten Timerwert
                    readTime();

                    last_millis_timer = millis();
                    cursor_index = 1;
                }
            }
            else {
                // Geben Sie den Seitenindex = 1 und den Cursorindex = 3 oder 4 ein, stellen Sie die gewünschte Menüposition ein, speichern Sie sie oder brechen Sie sie ab
                if (cursor_index == 3 || cursor_index == 4) {                    
                    // Wenn der Cursorindex = 3 ist, bedeutet dies, dass die Taste in der Menüposition „Speichern“ (Daten speichern) gedrückt wurde.
                    // Dies bedeutet, dass der Timer nicht blinkt (kein Timer aktiviert) oder dass der Timer nicht mehr im EEPROM gespeichert ist
                    // Die Talgdrüse ist nicht so lang, dass die Zeitdauer = 0 ist
                    bool valid = false;
                    readTime();

                    // Als ich aufwachte, dass ich 0 oder 10 Mal im Menü abgebrochen habe, war gültig = wahr
                    if (jam > 0 || menit > 0 || detik > 0 || cursor_index == 4) valid = true;

                    if (valid) {
                        if (cursor_index == 3) {
                            EEPROM.write(HOUR_ADDRESS, jam);
                            EEPROM.write(MINUTE_ADDRESS, menit);
                            EEPROM.write(SECOND_ADDRESS, detik);
                        }
                        // Wenn Sie den Vorgang abbrechen, wird der Timer nicht mehr angezeigt
                        else
                            readTimeFromEEPROM();
                        
                        // Der Seitenindex wird angezeigt und der Cursorindex wird auf 0 gesetzt
                        page_index--;
                        cursor_index = 0;
                    }
                }
                else {
                    // Wenn Sie den Timer-Marmelade-Kurs wählen, müssen Sie sich die Zeit nehmen und die Timer-Funktion nutzen
                    if (!blinking) {
                        temp_time = _time[cursor_index]; // Laden Sie die Variable Array k und temp_time
                        blinking = true;
                    }
                    // Dies bedeutet, dass blinkend = wahr ist, aber Sie werden nicht wissen, was passiert ist
                    else 
                        blinking = false;
                }                        
            }
        } // else jika timer tidak aktif
    }   
}

Im englischen Teil des Forum müssen die Beiträge und Diskussionen in englischer Sprache verfasst werden. Deswegen wurde diese Diskussion in den deutschen Teil des Forums verschoben.

mfg ein Moderator.

Die Zeile

if (jam <= 0 && menit <= 0 && detik <= 0) timer_active = false;

dient dazu, den Timer zu deaktivieren. Sie prüft, ob sowohl die Stunden (jam), Minuten (menit) als auch die Sekunden (detik) auf 0 sind. Wenn dies der Fall ist, wird die Variable timer_active auf false gesetzt, was bedeutet, dass der Timer nicht mehr aktiv ist.

Um einen Buzzer hinzuzufügen, können Sie einen Piezo an Pin 5 anschließen (D5 <---> R 220Ω <---> Piezo + und GND <---> Piezo —) und zu Beginn des Codes mit den anderen Pins hinzufügen:

#define buzzerPin 5

Sie fügen der setup() Funktion hinzu:

pinMode(buzzerPin, OUTPUT);

Dann ändern Sie die Zeile

if (jam <= 0 && menit <= 0 && detik <= 0) timer_active = false;

zu

if (jam <= 0 && menit <= 0 && detik <= 0) {
  timer_active = false;
  tone(buzzerPin, 500 /* Hz */, 2000ul /* ms */);  // spiele einen 500Hz Ton für 2 Sekunden (nicht blockierend)
}

Vielen herzlichen Dank für die Super schnelle Hilfe. Genau das brauche ich. TOP!!!!

Gibt es eine Möglichkeit in diesen Code noch ein Voltmeter einzubauen oder muss ich hier eine weiteren nano dafür nutzen?

Auch das geht noch. Bitte aber genau beschreiben, was wann passieren soll.

Ich möchte einen 12V Batterietester bauen, der eine Belastung erzeugt, die z.B. nach 5 min wieder abgeschaltet wird und mir bei Belastung die Batteriespannung auf dem Display angezeigt wird. Auch der Belastungsstrom wäre super wenn er im Display angezeigt wird.
Die Zeitschaltung wäre mit dem vorhanden Code perfekt abgedeckt. Nur müsste die Spannung ggf. Strom auch im Display zur ablaufenden Zeit angezeigt werden. Ich bin hier leider mit der Programmierung etwas überfordert. Die Elektronik ist kein Problem :slightly_smiling_face:

Dann sag mal, was zur Verfügung steht?

Wenn Du den Nano zum Messwert erfassen willst, dann hast Du Analogpins zur Verfügung mit einer Auflösung von 10 bit.
Bitte beachte, dass die Genauigkeit nicht 100% ist. Alles wichtige findet sich im Datenblatt unter 23.

Ansonsten kannst Du natürlich auch externe A/D-Wandler mit höherer Auflösung nutzen.
Die Abfrage funktioniert ähnlich.

Das mit der Genauigkeit hab ich schon bei einem Testaufbau gemerkt. Dann wäre ein A/D Wandler besser. Sollte ja doch ein realistischer Wert angezeigt werden. Ich hab den Nano ggf. auch den ESP hier und ein Relais zum Ansteuern von der Last. Das Programm für die Zeitschaltung s.o. läuft so wie ich mir das vorstelle. Kann man hier noch die Spannung einfügen und anzeigen lassen?

Das Ding ist am Analogpin ein Schätzeisen :slight_smile:

Möglich ist alles.
Kennst Du das Verhältnis des Spannungsteilers?

Bau Dir das elektrisch auf und dann gibts nen Kurzsketch, mit dem Du Deinen Abgleich machen machen kannst.
Wenn die Werte stimmen, dann kannst das integrieren.
Übrig bleibt dann nur eine Variable, die auf dem Display positioniert wird.

Stimmt nicht so ganz :wink:
Zu dem gibt's Funktion was das korrigiert.

Ja, aber vergiss deinen gefundenen Code und schreib einen neuen, der nur die Spannung misst.
Erst wenn du das hast, kannst du aus beiden einen gemeinsamen Sketch machen.
Ich sehe zwar noch nicht, was die beiden Aufgaben gemeinsam haben, aber eine gemeinsame Aufgabenstellung, die Spannungsmessung, Display, Menü mit Tasten, Zeitablauf, Buzzer-Ansteuerung enthält, kannst du sicherlich erstellen.
Den dies realisierenden Sketch zu finden ist allerdings nicht zielführend. Sketche werden gemacht (und tausendmal modifiziert), nicht gefunden.

Jetzt hab ich alles soweit zusammengebaut und ich Depp hab das Display falsch eingebaut.Gibt es eine Möglichkeit die Anzeige bei dem Code um 180Grad zu drehen oder muss ich mein Gerät umbauen :thinking:

Ja die gibt es bestimmt.
Schau mal in die Beispiele der Library, da wirst du sicher fündig.
Leider habe ich die Lib nicht hier und kann nicht nachschauen.

Pech gehabt die adafruit kann das nicht. Musst
mal gucken UCGLIB das kann.

Das ist natürlich besonders ärgerlich.
Und ja, einfach die Library wechseln.

DIE U8g2 vom Oli Krause hat eine Funktion

setDisplayRotation()

Die tut man in Setup.
Achtung die LIb ist ziemlich groß = Verbraucht viel Speicher.
Die u8x8 kann das auch, 0 = normal, 1 = gedreht

 u8x8.setFlipMode(0);

Ohne Gewehr, nicht getestet.

Nach dem, was ich weiss, sollte die Adafruit_SSD1306 das können:

Und ist auch implementiert:

Die GFX habe übersehen die SSD kann das nicht = hat nicht in Keywords.

in der GFX wurde es aus der SSD übernommen, wie sich aus dem issue ableiten lässt.
Läuft denn die SSD noch standallone ohne die GFX?