Position eines Continous Servos mittels Rotary Encoder ermitteln

Guten Morgen,

in einem Projekt möchte ich via Fingerprintsensor meine Haustür von Außen öffnen. Dazu habe ich mir ein Schloss mit Notfallfunktion besorgt. Jetzt dreht der Schlüssel sich drinnen mit, wenn man von außen die Tür öffnet.

Ich stellte mir die Frage, mit welchem Motor ich das Projekt generell realisieren kann. Wollte zunächst einen normalen Servo nehmen, jedoch reicht der Winkel damit nicht, von Hand lässt sich der Servo aufgrund der Übersetzung oder Sperrvorrichtung auch nicht bewegen. Daher fällt der schonmal aus. Ich habe noch einen Continous Servo, dieser lässt sich per Hand auch drehen, wenn kein Strom drauf ist.

Problem: Die Position lässt sich bekanntlicherweise nicht bestimmen. Meine Lösung wäre eine Kopplung des Servos via Zahnrad mit dem Encoder Übersetzung 1:1. Dieser würde beim Drehen dann einen Wert erzeugen.

Soweit die Theory. Zum Einbinden des Rotary Encoders habe ich folgende Lib verwendet:

https://funduino.de/nr-32-der-rotary-encoder-ky-040

Mein Problem dabei:

Der Encoder zählt egal in welche Richtung gedreht wird fast immer lediglich hoch. Manchmal kurz auch mal runter, aber nicht so wie ich es mir vorstelle. Ich kann per Hand spürbare Klicks beim Drehen spüren und hören, jedoch ist auch der Wert der erzeugt wird nicht immer +1 sondern mal mehr, mal weniger. Hört sich alles sehr ungenau an.

Grundsätzliche Frage: Wie soll der Rotary Encoder denn wirklich funktionieren? Ist meiner möglicherweise defekt? Oder liegt es am Skatch?

Andere Frage zum Projekt?

Hat jemand von euch Erfahrung mit meinem Vorhaben? Tipps Anregungen?

Vielen Dank!

An welchem Sketch?

Gruß Tommy

Welchen Winkel brauchst Du denn?

720° zum Abschließen
und ggf 60° in die andere Richtung zum Aufschließen also insgesamt 800°?

Der Sketch aus dem Link. Siehe unten.

#include <Encoder.h>    // Verwendung der <Encoder.h> Bibliothek 

const int CLK = 6;      // Definition der Pins. CLK an D6, DT an D5. 
const int DT = 5;
const int SW = 2;       // Der Switch wird mit Pin D2 Verbunden. ACHTUNG : Verwenden Sie einen interrupt-Pin!
long altePosition = -999;  // Definition der "alten" Position (Diese fiktive alte Position wird benötigt, damit die aktuelle Position später im seriellen Monitor nur dann angezeigt wird, wenn wir den Rotary Head bewegen)

Encoder meinEncoder(DT,CLK);  // An dieser Stelle wird ein neues Encoder Projekt erstellt. Dabei wird die Verbindung über die zuvor definierten Varibalen (DT und CLK) hergestellt.


void setup()   // Beginn des Setups

{
  Serial.begin(9600); 
    
  pinMode(SW, INPUT);   // Hier wird der Interrupt installiert.
  
  attachInterrupt(digitalPinToInterrupt(SW), Interrupt, CHANGE); // Sobald sich der Status (CHANGE) des Interrupt Pins (SW = D2) ändern, soll der Interrupt Befehl (onInterrupt)ausgeführt werden.
}


void loop()

{

    long neuePosition = meinEncoder.read();  // Die "neue" Position des Encoders wird definiert. Dabei wird die aktuelle Position des Encoders über die Variable.Befehl() ausgelesen. 

        if (neuePosition != altePosition)  // Sollte die neue Position ungleich der alten (-999) sein (und nur dann!!)...
        {
        altePosition = neuePosition;       
        Serial.println(neuePosition);      // ...soll die aktuelle Position im seriellen Monitor ausgegeben werden.
        }

}


void Interrupt() // Beginn des Interrupts. Wenn der Rotary Knopf betätigt wird, springt das Programm automatisch an diese Stelle. Nachdem...

{
  Serial.println("Switch betaetigt"); //... das Signal ausgegeben wurde, wird das Programm fortgeführt.

}

Es gibt Segelwinden-Servos mit mehreren Umdrehungen, hier 828°.

Darin könnte das Ausschlußkriterium bestehen, dazu fehlen mir aber leider Erfahrungen.

Hallo Zortrax,

es gibt so einige Demo-Codes die in der Interruptroutine etwas auf dei serielle Schnittstelle ausgeben. Als democode mag das (gerade noch) funktionieren. In "richtigem" code ist das ganz schlecht.

Um die Drehrichtung des Encoders zu erkennen muss man den Schaltzustand beider Anschlüsse auswerten. Das macht der Demo-Sketch gar nicht. Da ist wohl aus dem Sketch etwas verloren gegangen.

So ganz preiswerte Dreh-Encoder haben mechanische Schalter die prellen. Das heisst der Kontakt schließt und öffnet sich in schneller folge mehrmals. Der Code der den Encoder auswertet muss das mit berücksichtigen.

Die simplen Encoder-libraries machen das nicht und deswegen funktionieren sie mit den preiswerten encodern nur unzuverlässig.

Ich habe gute Erfahrungen mit der library NewEnoder gemacht

hier der einfachst mögliche Democode

#include "Arduino.h"
#include "NewEncoder.h"

// Pins 2 and 3 should work for many processors, including Uno. 
// explanation:

// NewEncoder myEncoder(2, 3, -20, 20, 0, FULL_PULSE);

// create an encoder-object called "myEncoder"
// "2" IO-pin for first channel of rotary-encoder
// "3" IO-pin for second channel of rotary-encoder
// "-20" lower limit to count down to
// "20"  upper limit to count up to
// 0  the value at initialisation
// "FULL_PULSE" type of pulse (other option HALF_PULSE)
// Use FULL_PULSE for encoders that produce one complete quadrature pulse per detnet, such as: https://www.adafruit.com/product/377
// Use HALF_PULSE for endoders that produce one complete quadrature pulse for every two detents, such as: https://www.mouser.com/ProductDetail/alps/ec11e15244g1/?qs=YMSFtX0bdJDiV4LBO61anw==&countrycode=US&currencycode=USD
// 
NewEncoder myEncoder(2, 3, -20, 20, 0, FULL_PULSE);

int value = 0;
int lastValue = -1;

void setup() {
  Serial.begin(115200);
  Serial.println("Starting");

  if (!myEncoder.begin()) {
    Serial.println("Encoder Failed to Start. Check pin assignments and available interrupts. Aborting.");
    while (1) {
      yield();
    }
  } else {
      Serial.print("Encoder Successfully Started at value = ");
      Serial.println(myEncoder);
  }
}

void checkEncoder() {
  value = myEncoder;

  if(value != lastValue) {
    Serial.print("Encoder value= ");
    Serial.println(value);
    lastValue = value;    
  }
}

void loop() {
  checkEncoder();
}

// the behaviour of the code that you can see in the serial monitor is
// the encoder-vale starts with 0 and depending on the rotating-direction
// counts up until 20 but not higher or counts down to -20 but not lower 
// so in the serial monitor you should see
/*
Starting
Encoder Successfully Started at value = 0
Encoder value= 0
Encoder value= 1
Encoder value= 2...counting up
Encoder value= 20
Encoder value= 19
Encoder value= 18...counting down
Encoder value= 2
Encoder value= 1
Encoder value= 0
Encoder value= -1
Encoder value= -2... counting down
Encoder value= -19
Encoder value= -20
*/

hier der Link zur library bei GitHub

vgs

Eher an der verwendeten Bibliothek, die wohl von mit Kondensatoren entprellten Tastern ausgeht. Beim bei Funduino abgebildeten Encoder gibt es die mutmaßlich nicht, kannst ja mal auf Deine Platine schauen. Bestenfalls sind da PullUp-Widerstände verbaut. Es gibt auch Encoder mit Kondensator.

Möglicherweise hilft Dir meine Anleitung: Drehimpulsgeber (rotary encoder) KY-040 zur Eingabe .

Hallo zusammen, danke für eure Beiträge.

Die NewEncoder Lib gibts anscheinend nicht mehr. Der Link den du gepostet hast funktioniert nicht. Und bei Google scheint auch die Lib nicht mehr auf GitHub zu existieren... Hast du ggf. davon noch eine Zip die du zur Verfügung stellen kannst? Dann würde ich es damit + deinem Sketch nochmal versuchen.

Der Segelwindservo wäre auch eine gute Idee. Jedenfalls vom Winkel her. Ich denke auch nur, der wird per Hand wahrscheinlich nicht drehbar sein. Da müsste ich höchstens mal den Support anschreiben wie sich das verhält.

Tja ich habe hier 2 Rotary Encoder liegen. Einer ist von sunfounder, der andere auch, aber ein etwas anderes Model. Nach welcher Methodik die arbeiten, lässt sich nur erahnen. Ich hatte bereits über Amazon einen Satz neuer Encoder bestellt, weil ich dachte naja vielleicht sind meine defekt...

Viele Wege führen nach Rom. Vielleicht ist der Rotary Encoder ja nicht die geeignete Wahl dafür. Wenn sich die Umdrehungen nicht einigermaßen korrekt erfassen lassen.

Was geht noch? Ich benötige also eine Methode, um die Umdrehung des Türschlüssels zu erfassen um den Motor dann in gewünschter Endstellung zu stoppen.

Eine Idee die mir grade noch kommt wäre das ganze mit einem Lichtwiderstand zu lösen. Bauteilmäßig habe ich dafür eigentlich alles da. 3D Drucker, CNC Fräse für die Umhausungen... Da kann ich sicher was von benutzen. Was wäre denn wenn ich den Schlüssel umhause und in Abständen kleine Löcher bohre durch die Licht auf einen Lichtwiederstand fällt zB über eine LED oder ein Lasermodul dahinter. Sobald sich der Schlüssel dann dreht, würde der Lichtwiederstand dann den Wert zwischen Hell und Dunkel analog unterscheiden können. Das könnte man zählen und daraus die Rotation ableiten. Die Drehrichtung wird sowieso durch eine bestimmte Aktion festgelegt... Könnte auch funktionieren. Was meint Ihr?

nein, frag nicht: "was geht noch".
Dein Ausgangspost bezieht sich auf die EncoderLib von Paul Stoffregen.
Die macht genau das, was auch in deinem Funduino-Sketch drin ist.
Was hast Du für einen Encoder? (link)
Wie ist der angeschlossen? (Foto?)

Das der Link nicht gehen soll ist ja komisch
ich habe es eben noch einmal getestet
geht mit firefox und geht mit chrome
Dann habe ich das Handy genommen mit opera gegoogelt

GitHub NewEncoder
geht auch.
Probiere es noch einmal

vgs

Komisch, heute funktioniert der Link. Gestern war die Seite definitiv down...

Wie gesagt die Rotary Encoder sind von Sunfounder und waren bei einem Sensorpack dabei. Das dürfte dann dieser hier sein.

Angeschlossen habe ich den folgendermaßen: 5V;GND;CLK-PIN 6;DT-PIN 5;MS-nicht angeschlossen.

MS ist der Button, den brauche ich nicht.

github war gestern eine Zeit lang tot.

Gruß Tommy

Die NewEncoder-library arbeitet mit Interrupts. Nicht jeder IO-Pin ist bei jedem Microcontroller interrupt-fähig mit welchem Microcontroller möchtest du das machen?

Wenn du das noch gar nicht entschieden hast welchen Microcontroller dann beschreibe mal was du insgesamt alles machen willst. Auch vielleicht erst später realisierte Optionen.

Wenn du nur Fingerprint Sensor auslesen, ein Servo ansteuern und einen Encoder auslesen willst dann könnte man dafür selbst den Seeeduino XIAO für 5,60 Euro nehmen.

Wenn du WLAN mit dabei haben willst den ESP8266

vgs

Ich habe die Lib jetzt runtergeladen und dein Sketch mal mit eingebunden. Allerdings initiiert der den rotary nicht...

Encoder Failed to Start. Check pin assignments and available interrupts. Aborting.

Ich habe jetzt Pin 8 und 6 verwendet. statt 2 und 3 weil diese schon belegt sind. Naja den Interrupt habe ich jetzt auch mal auf der 0 angeschlossen. Aber trotzdem funktioniert das noch nicht...

Ich nehme einen Uno dafür.

Auf dem Uno sind nur die IO-pins 2 und 3 interrupt-fähig. Alle anderen nicht.
Und deshalb kommt auch die Fehlermeldung.

Also entweder du machst die IO-pins 2 und 3 frei für den Encoder oder du must bei einem Arduino Uno mit sogenanntem polling arbeiten.
Polling heißt dein Code muss alle 10 Millisekunden am besten noch öfter den Zustand der beiden Encoder-Pins abfragen.

Ein "Zwischending" wäre noch einen Timer-Interrupt zu benutzen der jede Millisekunde ausgelöst wird um den Encoder abzufragen. Da habe ich aber keine encoder-library für zur Hand.

vgs

Falsch! Es gibt PCINT.

Gruß Tommy

Aha! Gut zu wissen. Bedeutet PCINT PinCountInterrupt?
Hast du auch einen Demo-Code der das gleich für Encoder umsetzt?
vgs

Es heißt PinChangeInterrupt.
Ich wollte Dir nur klar machen, dass Deine Aussage "Alle anderen nicht." nicht der Realität entspricht.

Gruß Tommy

OK. Danke dir Tommy.

Ich habe inzwischen gegoogelt und eine timer-interrupt-basierte library gefunden
Der Autor folgt der - in meinen Augen - Unsitte seine library einfachst möglichst encoder.h zu nennen was sehr konfliktträchtig ist. Außerdem fehlte im Democode der include-Befehl.
Da war ich dann so frei die library umzubenennen den Democode abzuändern und neu in eine ZIP-Datei zu packen

Hier der Demo-Code

// USAGE FOR ONE ENCODER:
#include <Timer_ISR_Encoder.h>
// define the input pins
#define pinA 6 // channel A
#define pinB 8 // channel B
#define pinP 7 // Push-Button

// create an encoder object initialized with their pins
Encoder myEncoder( pinA, pinB, pinP );

int delta; 
int currentValue = 0;
int lastValue = -1;

boolean lastButtonState    = true;
boolean currentButtonState = true;
// setup code
void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  // start the encoder time interrupts
  EncoderInterrupt.begin( &myEncoder );
}

// loop code
void loop()
{
  // read the debounced value of the encoder button
  currentButtonState = myEncoder.button();

  // depending on the wiring of the button Pullup or Pulldown-resistor 
  // you might have to invert the messages
  if (currentButtonState != lastButtonState) {
    lastButtonState = currentButtonState;
    
    if (currentButtonState) {
      Serial.println("button released");
    }
    else {
      Serial.println("button pressed down");
    }
  }
  // get the encoder variation since our last check, it can be positive or negative, or zero if the encoder didn't move
  // only call this once per loop cicle, or at any time you want to know any incremental change
  delta = myEncoder.delta();
  currentValue += delta;

  if (currentValue != lastValue) {
    Serial.print("encoder new value=");
    Serial.println(currentValue);
    lastValue = currentValue;
  }
}

und hier die ZIP-library
Timer_ISR_Encoder.zip (6.3 KB)

Bleibt noch die Frage diese Encoder-lib benutzt Timer2. Kommt sich das mit der Servo-library ins Gehege? Nein. Servo.h benutzt timer1
na dann probier mal Zotrax