Servotester mit der Arduino Servo Library

Oxymoron

User
Ich habe mir mit einem Arduino UNO R3 und der Servo Library for Arduino 1.1.8 einen Servotester gebaut, dessen Funktion ich für eine spezielle Anwendung gerne modifizieren möchte.

Das Servo soll anstatt stufenlos mit dem Poti mit zwei Tastern in zwei definierte (End-)Positionen gesteuert werden.

Die gewünschte Funktion ist wie folgt:
Das Servo soll grundsätzlich in Mittelstellung (Impulslänge 1,5ms) stehen, solange kein Taster betätigt ist. Erst wenn einer der beiden Taster betätigt wird, soll das Servo (Anschluss Pin D9) jeweils eine definierte Position (Impulslänge 1,0ms bzw. 2,0ms) ansteuern, solange die jeweilige Taste (Anschluss Pin D6 bzw. D7) gedrückt wird. Außerdem soll nach einer kurzen Verzögerung (td ~1-2 Sekunden) ein Relais (Anschluss Pin D13) aktiviert werden. Beim Loslassen des jeweiligen Tasters soll das Relais sofort wieder abfallen und das Servo in seine Mittelstellung zurückkehren.

Wie kann ich das am besten realisieren?
Ist vielleicht jemand bereit mir die erforderlichen Anpassungen im Sketch anzugeben oder mir dabei auf die Sprünge zu helfen?
 

wno158

User
erforderlichen Anpassungen im Sketch
;) Dazu müssten wir einen Sketch sehen (Standardspruch aus dem Arduino-Forum).

Spaß beiseite: Das ist eine lösbare Aufgabe.
Deine Spezifikation lässt noch ein paar Fragen offen:
- Können beide Tasten gleichzeitig betätigt werden (und was soll dann passieren)?
- Was soll mit dem Relais passieren, wenn die Taste während der Wartezeit wieder losgelassen wird?
- Ist die Zeit zum Anfahren der Endpositionen kritisch (wegen der erforderlichen Entprellung der Taster)?

Ich würde erwägen, statt der Servo-Lib die MobaTools (PDF-Doku hier) einzusetzen - da hast Du mit MoToServo und MoToButtons gleich alles benötigte zusammen. Lässt sich über die Bibliotheksverwaltung installieren.
 
Programmier eine State-Machine/Zustandsautomat. Damit ist das ratzfatz gemacht. Ich habe so was für meine Doorsequenzer gemacht und es war grandios einfach.
 

Oxymoron

User
Deine Spezifikation lässt noch ein paar Fragen offen:
- Können beide Tasten gleichzeitig betätigt werden (und was soll dann passieren)?
- Was soll mit dem Relais passieren, wenn die Taste während der Wartezeit wieder losgelassen wird?
- Ist die Zeit zum Anfahren der Endpositionen kritisch (wegen der erforderlichen Entprellung der Taster)?
  • Wenn beide Taster simultan betätigt werden, sollte das Servo in Mittelstellung zurückkehren und das Relais abfallen
  • Das Relais soll nicht aktiviert werden, wenn die Taste vor Ablauf der Verzögerung wieder losgelassen wird
  • Die Zeit zum Erreichen der jeweiligen Endposition ist unkritisch (eine Entprellung der Taster habe ich vorgesehen)
Vielen Dank für die Hilfe, ich werde mal die MobaTools von den Eisenbahnern in Augenschein nehmen…
 

wno158

User
Ich hab' da mal was gebastelt...
Die Wahl von Pin 13 für das Relais ist etwas ungünstig - da hängt ja bei den Arduinos immer die interne LED dran. Das wird also beim Einschalten/nach Reset etwas klappern.

C++:
// https://www.rc-network.de/threads/servotester-mit-der-arduino-servo-library.11896290/


#define MAX8BUTTONS
#include <MobaTools.h>

#define DEBUG_OUT
#if defined(DEBUG_OUT)
#define DEB_B(x) Serial.begin(x)
#define DEB_P(x) Serial.print(x)
#define DEB_PL(x) Serial.println(x)
#else
#define DEB_B(x)
#define DEB_P(x)
#define DEB_PL(x)
#endif

// Einstellungen für das Relais
enum RelaisState {RELAIS_AUS = LOW, RELAIS_AN = HIGH};
const byte pinRelais = 13;
const unsigned long wartezeitRelais = 1500;  // in Millisekunden

// Einstellungen für die Taster
enum TasterNr {tasterLinks = 0, tasterRechts = 1};
const byte pinTasterArray[] = {6, 7};
const byte anzahlTaster = sizeof(pinTasterArray);
const byte debounceTime = 30;
const uint16_t longPressTime = 2000;

// Servo-Einstellungen
const byte pinServo = 9;
//   Impulslängen in [ms]
const int servoMitte = 1500; 
const int servoLinks = 1000;
const int servoRechts = 2000;

// Sytemzustände
enum class States {MITTE, LINKS, RECHTS, LINKS_AKTIV, RECHTS_AKTIV};
States systemState = States::MITTE;

MoToServo servo;
MoToButtons taster(pinTasterArray, anzahlTaster, debounceTime, longPressTime);
MoToTimer timer;

void setup()
{
  DEB_B(115200);
  DEB_PL(__FILE__);
  DEB_PL(__DATE__);

  servo.attach(pinServo);
  servo.write(servoMitte);
  pinMode(pinRelais, OUTPUT);

  DEB_PL(F("Setup() Ende"));
}

void loop()
{
  taster.processButtons();

  switch (systemState)
  {
    case States::MITTE:
      handleButtonsPressed();
      break;
    case States::LINKS:
      if (taster.released(tasterLinks))
      {
        DEB_PL(F("links losgelassen"));
        changeToIdle();
      }
      if (timer.expired())
      {
        DEB_PL(F("Timer abgelaufen (links)"));
        digitalWrite(pinRelais, RELAIS_AN);
        systemState = States::LINKS_AKTIV;
      }
      break;
    case States::RECHTS:
      if (taster.released(tasterRechts))
      {
        DEB_PL(F("rechts losgelassen"));
        changeToIdle();
      }
      if (timer.expired())
      {
        DEB_PL(F("Timer abgelaufen (rechts)"));
        digitalWrite(pinRelais, RELAIS_AN);
        systemState = States::RECHTS_AKTIV;
      }
      break;
    case States::LINKS_AKTIV:
      if (taster.released(tasterLinks))
      {
        DEB_PL(F("links losgelassen (Relais aktiv)"));
        changeToIdle();
      }
      break;
    case States::RECHTS_AKTIV:
      if (taster.released(tasterRechts))
      {
        DEB_PL(F("rechts losgelassen (Relais aktiv)"));
        changeToIdle();
      }
      break;
  }
}

void changeToIdle()
{
  timer.stop();
  digitalWrite(pinRelais, RELAIS_AUS);
  servo.write(servoMitte);
  systemState = States::MITTE;
}

void handleButtonsPressed()
{
  if (taster.state(tasterLinks) && taster.state(tasterRechts))
  {
    DEB_PL(F("beide Taster gedrückt"));
    changeToIdle();
    return;
  }
 
  if (taster.pressed(tasterLinks))
  {
    DEB_PL(F("links gedrückt"));
    servo.write(servoLinks);
    timer.setTime(wartezeitRelais);
    systemState = States::LINKS;
    return;
  }
 
  if (taster.pressed(tasterRechts))
  {
    DEB_PL(F("rechts gedrückt"));
    servo.write(servoRechts);
    timer.setTime(wartezeitRelais);
    systemState = States::RECHTS;
    return;
  }
}
 

Oxymoron

User
Hallo @wno158, vielen Dank für den Sketch und die Mühe, die Du Dir gemacht hast! Ich weiß das als Arduino Einsteiger sehr zu schätzen!

Tja, da habe ich bei Pin 13 nicht berücksichtigt, dass mir der Optiboot bootloader dazwischen funkt - die integrierte LED sollte als visuelle Statusanzeige für das Relais fungieren… kann ich das mit define LED_START_FLASHES 0 in der optiboot.c Datei anpassen?
Ansonsten konfiguriere ich einen anderen Pin für das Relais.

Ich werde den Sketch morgen mal laden und testen! :)
 

S_a_S

User
kannst ja zwei pins gleichzeitig (bzw. im Code nacheinander) schalten, an einem hängt das Relais, an 13 die LED. Die flackert dann ungewollt beim Bootloader, aber das Relais bleibt ruhig.

Grüße Stefan
 

wno158

User
kann ich das mit define LED_START_FLASHES 0 in der optiboot.c Datei anpassen?
Vielleicht ja, ich habe (noch) nicht in den Optiboot-Code geschaut. Es hat aber Nebeneffekte: Damit erzeugst Du bei Dir lokal eine Spezialversion des Bootloaders und müsstest die auf alle Geräte, die den Servotester-Sketch bekommen sollen, auch erst noch flashen.

Bin da eher bei Stefan:
kannst ja zwei Pins gleichzeitig (bzw. im Code nacheinander) schalten, an einem hängt das Relais, an 13 die LED.
Genau. Jetzt rächt sich, dass ich für das Schalten des Relais nicht gleich eine eigene Funktion vorgesehen habe.
Das ist aber schnell erledigt:
C++:
// https://www.rc-network.de/threads/servotester-mit-der-arduino-servo-library.11896290/


#define MAX8BUTTONS
#include <MobaTools.h>

#define DEBUG_OUT
#if defined(DEBUG_OUT)
#define DEB_B(x) Serial.begin(x)
#define DEB_P(x) Serial.print(x)
#define DEB_PL(x) Serial.println(x)
#else
#define DEB_B(x)
#define DEB_P(x)
#define DEB_PL(x)
#endif

// Einstellungen für das Relais
enum RelaisState {RELAIS_AUS = LOW, RELAIS_AN = HIGH};
const byte pinLED = LED_BUILTIN;  // das ist beim Uno/Nano Pin 13
const byte pinRelais = 12;
const unsigned long wartezeitRelais = 1500;  // in Millisekunden

// Einstellungen für die Taster
enum TasterNr {tasterLinks = 0, tasterRechts = 1};
const byte pinTasterArray[] = {6, 7};
const byte anzahlTaster = sizeof(pinTasterArray);
const byte debounceTime = 30;
const uint16_t longPressTime = 2000;

// Servo-Einstellungen
const byte pinServo = 9;
//   Impulslängen in [ms]
const int servoMitte = 1500;
const int servoLinks = 1000;
const int servoRechts = 2000;

// Sytemzustände
enum class States {MITTE, LINKS, RECHTS, LINKS_AKTIV, RECHTS_AKTIV};
States systemState = States::MITTE;

MoToServo servo;
MoToButtons taster(pinTasterArray, anzahlTaster, debounceTime, longPressTime);
MoToTimer timer;

void setup()
{
  DEB_B(115200);
  DEB_PL(__FILE__);
  DEB_PL(__DATE__);

  servo.attach(pinServo);
  servo.write(servoMitte);

  pinMode(pinLED, OUTPUT);    // bei Pin 13 überflüssig
  pinMode(pinRelais, OUTPUT);
  setRelais(RELAIS_AUS);

  DEB_PL(F("Setup() Ende"));
}

void loop()
{
  taster.processButtons();

  switch (systemState)
  {
    case States::MITTE:
      handleButtonsPressed();
      break;
    case States::LINKS:
      if (taster.released(tasterLinks))
      {
        DEB_PL(F("links losgelassen"));
        changeToIdle();
      }
      if (timer.expired())
      {
        DEB_PL(F("Timer abgelaufen (links)"));
        setRelais(RELAIS_AN);
        systemState = States::LINKS_AKTIV;
      }
      break;
    case States::RECHTS:
      if (taster.released(tasterRechts))
      {
        DEB_PL(F("rechts losgelassen"));
        changeToIdle();
      }
      if (timer.expired())
      {
        DEB_PL(F("Timer abgelaufen (rechts)"));
        setRelais(RELAIS_AN);
        systemState = States::RECHTS_AKTIV;
      }
      break;
    case States::LINKS_AKTIV:
      if (taster.released(tasterLinks))
      {
        DEB_PL(F("links losgelassen (Relais aktiv)"));
        changeToIdle();
      }
      break;
    case States::RECHTS_AKTIV:
      if (taster.released(tasterRechts))
      {
        DEB_PL(F("rechts losgelassen (Relais aktiv)"));
        changeToIdle();
      }
      break;
  }
}

void setRelais(byte zustand)
{
  if (zustand)
  {
    digitalWrite(pinLED, HIGH);
    digitalWrite(pinRelais, RELAIS_AN);
  }
  else
  {
    digitalWrite(pinLED, LOW);
    digitalWrite(pinRelais, RELAIS_AUS);
  }
}

void changeToIdle()
{
  timer.stop();
  setRelais(RELAIS_AUS);
  servo.write(servoMitte);
  systemState = States::MITTE;
}

void handleButtonsPressed()
{
  if (taster.state(tasterLinks) && taster.state(tasterRechts))
  {
    DEB_PL(F("beide Taster gedrückt"));
    changeToIdle();
    return;
  }

  if (taster.pressed(tasterLinks))
  {
    DEB_PL(F("links gedrückt"));
    servo.write(servoLinks);
    timer.setTime(wartezeitRelais);
    systemState = States::LINKS;
    return;
  }

  if (taster.pressed(tasterRechts))
  {
    DEB_PL(F("rechts gedrückt"));
    servo.write(servoRechts);
    timer.setTime(wartezeitRelais);
    systemState = States::RECHTS;
    return;
  }
}

Der Code ist spärlich kommentiert. Wenn Du Fragen hast: Nur zu!
 
Ansicht hell / dunkel umschalten
Oben Unten