Arduino PIR-Relais mit manuellem Überbrückungsschalter

Ich richte ein PIR-aktiviertes Relais (mit einigen Lichtern verbunden) mit einem Arduino Nano ein.

Ich habe den PIR-Code funktioniert (erhalten von einer Drittanbieter-Website), aber ich hätte gerne die Option, den PIR-Eingang basierend auf einem Eingang von einem Druckknopfschalter zu überschreiben. Um noch mehr Komplexität hinzuzufügen, möchte ich eine LED aufleuchten lassen, um mich darüber zu informieren, wenn die PIR manuell außer Kraft gesetzt wird (Relais immer eingeschaltet).

Der Knopf ist ein einfacher Druckknopf.

Standardmäßig möchte ich, dass sich das Gerät im PIR-Modus befindet. Das Relais wird aktiviert, wenn eine Bewegung erkannt wird, und schaltet dann nach X Sekunden ab. Wenn ich die Taste einmal drücke, wird sie manuell übersteuert und das Relais wird (dauerhaft) eingeschaltet und die blaue LED wird aktiviert, um mich darüber zu informieren, dass das Gerät ständig eingeschaltet ist. Wird die Taste erneut gedrückt, schaltet das Gerät wieder in den PIR-Modus.

Hier ist der Code, den ich bisher habe - aber die LED und das Relais scheinen ständig aktiv zu sein. Der PIR liest immer noch (laut seriellem Monitor).

/*
 * PIR sensor tester
 */ 


int PIR_override = 3;         // the number of the switch pin
int overridestatus = 12;       // the number of the led pin

int relaypin = 13;                // choose the pin for the LED
int inputPin = 2;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status
int DELVAR = 3000;             // Delay in milliseconds
int override_state = LOW;      // the current state of the output pin

// set states for PIR override switch
int state = LOW;      // the current state of the output pin
int reading;           // the current reading from the input pin
int previous = HIGH;    // the previous reading from the input pin

///////////////////////////////////////////////////////
void setup() {
  pinMode(relaypin, OUTPUT);      // declare LED as output
  pinMode(inputPin, INPUT);     // declare sensor as input

  Serial.begin(9600);
}

void loop()
{
    digitalRead(PIR_override);  // read input value of PIR override switch
    if (val == LOW)
    {            // check if the input is LOW
        val = digitalRead(inputPin);  // read input value
        if (val == HIGH)
        {            // check if the input is HIGH
            digitalWrite(relaypin, HIGH);  // turn LED ON
            if (pirState == LOW)
            {
                // we have just turned on
                Serial.println("Motion detected!");
                // We only want to print on the output change, not state
                pirState = HIGH;
                delay(DELVAR);    // maximum delay is 32776 millisecons,
                //  delay(DELVAR);    // so add multiple delays together to get
                // delay(DELVAR);    // so add multiple delays together to get
            }
        }
        else
        {
            digitalWrite(relaypin, LOW); // turn LED OFF
            if (pirState == HIGH)
            {
                // we have just turned of
                Serial.println("Motion ended!");
                // We only want to print on the output change, not state
                pirState = LOW;
            }
            else
            {
                digitalWrite(overridestatus, HIGH);  // turn override status LED ON
                digitalWrite(relaypin, HIGH);  // turn relay ON
            }
        }
    }
}

Hier ist der Code, den ich bisher habe - aber die LED und das Relais scheinen ständig aktiv zu sein. Der PIR liest immer noch (laut seriellem Monitor).

Mit meiner if-else-Schleife stimmt eindeutig etwas nicht, aber ich bin mir nicht ganz sicher, wo ich anfangen muss zu suchen.

Ich füge auch ein einfaches Steckbrettbild der Schaltung bei (es ist mein erster Fritz, also seien Sie vorsichtig!) Außerdem ignorieren Sie bitte den Pin auf der Relaisplatine. Ich verwende ein SRD-05VDC-SL-C, aber es befindet sich auf einer etwas anderen Platine mit nur 3 Pins (IN, VCC, GND).

Geben Sie hier die Bildbeschreibung ein

Antworten (2)

Ich denke, Ihr Hauptproblem besteht darin, dass Sie keine Variable zuweisen oder eine ifAnweisung mit Ihrem verwenden digitalRead(PIR_override);, aber selbst damit können Sie den Tastendruck in der Schleife verpassen. Wenn es sich um einen momentanen Schalter handelt, geht er nur für die Zeit hoch / niedrig, in der er gedrückt wird, sodass der Prozessor dies zum richtigen Zeitpunkt in der While-Schleife sehen müsste. Sie können die Taste einfach etwa eine Sekunde lang gedrückt halten oder die Taste an Pin 2 oder 3 anschließen und einen Interrupt verwenden, bei dem es so aussieht, als wäre Ihre Taste gemäß Ihrem Code mit Pin 3 verbunden. Ich denke, das ist eine bessere Lösung. In der eingebetteten Welt sind Interrupts Ihr Freund.

Laut Dokumentation ( https://www.arduino.cc/en/Reference/AttachInterrupt ) könnten Sie also so etwas tun:

const byte PIR_override = 3;        // the number of the switch pin
volatile boolean override = false;          // Use this to know if your currently  in override mode or not when looking at the button press

Innen Setup()hinzufügen:

pinMode(PIR_override, INPUT);
attachInterrupt(digitalPinToInterrupt(PIR_override), motionOverride, LOW); // the sense may need to be changed - from your image it looks like the button goes low when pressed

So motionOverridewird jetzt aufgerufen, wenn die Taste gedrückt wird. Geben Sie einfach in diese Funktion ein, was passieren soll. Etwas wie das:

void motionOverride()
{
    if(override) // overriding motion and the button has been pressed - got to PIR mode
    {
        digitalWrite(overridestatus, LOW);  // turn override status LED OFF
        digitalWrite(relaypin, LOW);  // turn relay OFF - you may not want to do this... depends on what you want
        override = false;               // set the override flag
    }
    else // not overriding motion and the button has been pressed - go to override mode
    {
        digitalWrite(overridestatus, HIGH);  // turn override status LED ON
        digitalWrite(relaypin, HIGH);  // turn relay ON
        override = true;                // set the override flag
    }

   // I guess Arduino takes care of clearing the interrupt for you
}

Dann schauen Sie sich in Ihrem Hauptfenster loop()einfach das Override-Flag an (Hinweis: Ich habe keine Ihrer PIR-Bewegungserkennungslogik überprüft und hoffentlich habe ich Ihre Klammern dort nicht durcheinander gebracht):

void loop()
{
     if(!override)
     {
         val = digitalRead(inputPin);  // read PIR input value
         if (val == HIGH)  // check if the input is HIGH
         {           
              digitalWrite(relaypin, HIGH);  // turn LED ON
              if (pirState == LOW) 
              {
                   // we have just turned on
                   Serial.println("Motion detected!");
                   // We only want to print on the output change, not state
                   pirState = HIGH;
                   delay(DELVAR);    // maximum delay is 32776 millisecons,
                   //  delay(DELVAR);    // so add multiple delays together to get
                   // delay(DELVAR);    // so add multiple delays together to get
             }
        } 
        else
        {
            digitalWrite(relaypin, LOW); // turn LED OFF
            if (pirState == HIGH)
            {
                 // we have just turned of
                 Serial.println("Motion ended!");
                 // We only want to print on the output change, not state
                 pirState = LOW;
            } 
        }
     }
}

Probieren Sie das aus, wenn Sie möchten. Wenn es einen Fehler gibt, werde ich versuchen, damit zu helfen.

Danke DigitalNinja. Der von Ihnen vorgeschlagene Code scheint "fast" das zu tun, wonach ich suche. Allerdings scheint der Zustand des Schalters etwas flockig zu sein. Ich habe mich ein wenig eingelesen und es sieht so aus, als müsste ich möglicherweise etwas "Entprellen" verwenden. Wo würden Sie vorschlagen, den Entprellcode einzufügen - in der Funktion selbst oder in der Hauptschleife?
Wahrscheinlich definitiv entprellt. Eine Hardwarelösung wäre besser, dafür müssten Sie den Standardzustand des Schalters so ändern, dass er normalerweise niedrig gezogen wird und dann beim Drücken hoch geht. Wenn Sie das getan haben, brauchen Sie nur einen kleinen Kondensator am Eingang zum Prozessor und an Masse. Für Software würde ich für das, was Sie tun, nur eine kleine Verzögerung am Ende der ISR setzen. Dadurch wird verhindert, dass der Interrupt sofort nach dem ersten Mal erneut ausgelöst wird.
Je nachdem, wie Arduino den Interrupt löscht, funktioniert eine Verzögerung möglicherweise nicht, denn wenn sie in die ISR eintritt und das Flag nach der Verzögerung löscht, kann sie bei der anstehenden Unterbrechung einfach wieder in die ISR eintreten. Außerdem müssen Sie verwenden, delayMicroseconds()da es selbst keine Interrupts verwendet. Wenn Sie die Hardwarelösung nicht durchführen können und die Verzögerung am Ende der ISR nicht funktioniert, denke ich, dass ich eine Problemumgehung habe, die funktioniert. Lassen Sie mich wissen, wie es geht.
Wahrscheinlich eine dumme Frage, aber wenn Sie sagen, fügen Sie einen Kondensator am Eingangsprozessor hinzu, meinen Sie damit, ihn an den Schalterstift anzuschließen (PIR_override = 3)?
Ja, setzen Sie den Kondensator zwischen den Schalterausgang und den Prozessoreingang. Denken Sie jedoch daran, dass Sie den Standardzustand des Schalters so ändern müssen, dass er niedrig ist (über Ihren Widerstand auf Masse gezogen wird) und beim Drücken auf hoch geht (sieht in Ihrem Bild aus wie 5 VDC). Wenn es hoch geht, lädt sich der Kondensator auf und filtert das Prellen heraus. Wenn Sie dies tun, denken Sie auch daran, den Interrupt-Sinn in Ihrem Code auf HIGH statt LOW zu ändern.
Ich habe nicht viel Glück mit der Kondensatormethode. Habe den Widerstand so getauscht, dass er auf Masse zieht. Sobald ich einen Kondensator hinzufüge, verhält es sich noch unregelmäßiger. Ich habe verschiedene Kondensatorwerte von 20uF bis 220uf ausprobiert. Ich denke, ich würde das gerne in der Software beheben, da dies im Moment so ziemlich alles in einer Projektbox verdrahtet ist und ich es nicht weiter entdrahten möchte! Ich habe noch nicht versucht, der ISR eine Verzögerung hinzuzufügen - ist die ISR die motionOverride-Funktion oder der Abschnitt "attachInterrupt" unter setup() ?
Eigentlich - habe gerade einen wirklich nützlichen Artikel auf hackaday [link] hackaday.com/2015/12/09/… gesehen , der mir ein Diagramm für einen Entpreller zusammen mit der Erklärung gibt, welcher Pull-up-Widerstand in Kombination mit welchem ​​Nennkondensator zu verwenden ist (viel niedriger als ich getestet hatte). Ich glaube, ich habe es falsch verstanden, da ich keinen anderen kostenlosen Widerstand zum Hochziehen / Herunterziehen verwendet habe, sondern nur einen einzelnen 1k-Widerstand zum Hochziehen. Wenn ich nach Hause komme, probier ich noch ein bisschen.
Ja, vielleicht habe ich es nicht sehr gut erklärt, aber wenn Sie "Knopf-Entprellschaltung" googeln, erhalten Sie viele Beispiele, und dies kann nur mit einem Widerstand und einem Kondensator (kleiner Wert wie etwa 0,1 uF) erfolgen. Die ISR ist die motionOverrideFunktion. Ich bin mir nur nicht ganz sicher, wie es sich auf der Arduino-Plattform verhalten wird, deshalb habe ich die Hardwarelösung vorgeschlagen.

Ich habe eine Bearbeitung Ihres Codes vorgeschlagen; Einzüge waren nicht vorhanden, also habe ich sie hinzugefügt, um verschachtelte if-else-Anweisungen klarer zu machen.

Ich möchte einige Probleme ansprechen, die ich habe; Bitte ändern Sie Ihre PIN-Definitionen:

int PIR_override = 3;         // the number of the switch pin
int overridestatus = 12;       // the number of the led pin
int relaypin = 13;                // choose the pin for the LED
int inputPin = 2;               // choose the input pin (for PIR sensor)

zu etwas wie:

#define PIR_override 3         // the number of the switch pin
#define overridestatus 12       // the number of the led pin

#define relaypin 13                // choose the pin for the LED
#define inputPin 2               // choose the input pin (for PIR sensor)

Bei Ihrer derzeitigen Verwendung von Integer-Variablen ist es möglich, dass sich deren Wert ändert, was Sie sicherlich nicht möchten. Die Verwendung von Ganzzahlen für Pin-Definitionen verbraucht auch Platz auf Ihrem Stack. Dies ist vielleicht nie ein echtes Problem, aber es ist etwas, das man in Betracht ziehen sollte.

Ausführlichere Informationen zu Präprozessordirektiven finden Sie an anderer Stelle.

Wenn Sie Ihren PIR_override-Pin lesen:

digitalRead(PIR_override);  // read input value of PIR override switch

Der Wert wird nicht verwendet. Diese müssen Sie einer Variablen zuweisen.

Ich sehe auch ein paar Probleme mit Ihrer if-else-Logik. Nehmen Sie zum Beispiel diesen Code:

if (val == LOW)
{            // check if the input is LOW
    val = digitalRead(inputPin);  // read input value

Anfänglich ist val auf niedrig gesetzt. Das ist in Ordnung. Die if-Anweisung ist wahr und val wird neu zugewiesen. Aber was ist, wenn val neu zugewiesen wird, um HIGH zu sein? Sobald das Ende der Schleife erreicht und neu gestartet wurde, gibt es keine else-Anweisung, um val == HIGH zu behandeln!

Es scheint mir, dass das, was Sie mit Ihrer verschachtelten if-else-Logik zu tun versuchen, einer Zustandsmaschine nahe kommt. Wenn Sie sich noch nicht damit befasst haben, würde ich vorschlagen, nach „if-else-Zustandsmaschinen“ oder „Switch-Case-Zustandsmaschinen“ zu suchen.

Andernfalls, wenn Sie mit dem Code fortfahren möchten, den Sie jetzt haben, würde ich erwägen, die Startvariablenwerte zu schreiben und Ihren Code manuell "durchzulaufen".