Motorisierter Kameraslider Hilfe

Gude liebe Leute,

ein Kommilitone und ich bauen für ein Uniprojekt gerade einen motorisierten Kamerslide mit Hilfe eines Arduino.

Bautteile:
Nema 17, A4988 Treiber, OLED SSD1306, Rotary Encoder, Arduino Nano, Limit Switches, Slider, Pulley, Riemen

Wir haben schon ein bisschen Code zusammengebastelt, aber kommen nicht mehr weiter. Wir sind beide keine Arduino Virtuosen und versuchen durch andere Codes und Trail & Error unsere Zwecke zu erfüllen.
Deshalb die Frage, ob uns hier jemand unter die Arme greifen könnte und uns hier und da ein paar Tipps oder Denkanstöße geben könnte. Ich möchte nicht dass mir alles vorgekaut wird und erwarte das auch nicht, wenn ihr versteht was ich meine.

Ich erkläre mal kurz was wir uns von dem Slider vorgestellt haben.
Slider mit Endanschlag an beiden Seiten.
Slider soll entweder immer hin und her fahren, oder nur einmal die Strecke zurücklegen.
Einstellung der Geschwindigkeit in Zeit für die Strecke Anschlag rechts zu Anschlag links.
Steuerung über den Rotary Encoder und Anzeige auf Display.
Das heißt das Display braucht einen Start, Stop Button, einen Loop oder Single Button und einen Bereich wo man die Geschwindigkeit (Zeit) einstellen kann.

Vorerst haben wir versucht das Zusammenspiel von Rotary Encoder zu Motor und Display zu verstehen.
Leider sind wir schon dabei gescheitert. :frowning:
Folgendes Problem. Mit unserem Code können wir den Motor von der Geschwindigkeit 1 bis 400 laufen lassen und die Zahl wird im seriellen Monitor angezeigt. Der Motor ändert auch seine Geschwindigkeit. Gut. Auf dem Display können wir das ganze auch anzeigen lassen und das funktioniert auch, aber unser Motor dreht dann nicht mehr wie er soll. Keine Ahnung wo daran der Fehler liegt. Ohne Display funktioniert alles normal. Vielleicht liegt das an der Abtastrate und der Arduino ist zu schwach... Keine Ahnung.

Hat da vielleicht jemand einen Tipp woran das liegen kann oder sagt; hey, guckt euch mal die Bibliothek dazu an. Damit läuft das was ihr machen wollt viel einfacher.

Hier mal die Codes

Ohne Display. Funktioniert alles:

#include <Arduino.h>
#include "A4988.h"


// this pin should connect to Ground when want to stop the motor
#define STOPPER_PIN 7



// Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
#define MOTOR_STEPS 200
// Microstepping mode. If you hardwired it to save pins, set to the same value here.
#define MICROSTEPS 16

#define DIR 8
#define STEP 9

// Rotary Encoder Inputs
#define PinCLK 3
#define PinDT 2



#define MS1 10
#define MS2 11
#define MS3 12
A4988 stepper(MOTOR_STEPS, DIR, STEP, MS1, MS2, MS3);


int rpm = 200;

const int dirPin = 8;  // Direction
const int stepPin = 9; // Step
int currentStateCLK;
int lastStateCLK;
String currentDir = "";
volatile boolean TurnDetected;
volatile boolean up;


void Rotary()
{
  delay(75);
  if (digitalRead(PinCLK))
    up = !digitalRead(PinDT);
  else
    up = digitalRead(PinDT);
  TurnDetected = true;
  delay(75);
}


void setup() {
  Serial.begin(115200);

  // Configure stopper pin to read HIGH unless grounded
  pinMode(STOPPER_PIN, INPUT_PULLUP);

  // Set encoder pins as inputs
  pinMode(PinCLK, INPUT);
  pinMode(PinDT, INPUT);


  stepper.begin(rpm, MICROSTEPS);
  // if using enable/disable on ENABLE pin (active LOW) instead of SLEEP uncomment next line
  // stepper.setEnableActiveState(LOW);
  stepper.enable();

  // Read the initial state of CLK
  // lastStateCLK = digitalRead(CLK);
  /*
    // Call updateEncoder() when any high/low changed seen
    // on interrupt 0 (pin 2), or interrupt 1 (pin 3)
    attachInterrupt(0, updateEncoder, CHANGE);
    attachInterrupt(1, updateEncoder, CHANGE);
  */
  attachInterrupt (0, Rotary, RISING);


  Serial.println("START");
  delay(1000);




}

void loop() {

  if (TurnDetected) {
    TurnDetected = false;
    if (up) {
      if (rpm < 20) {
        rpm = rpm + 1;
      }
      else {
        rpm = rpm + 5;
      }
      if (rpm >= 400) {
        rpm = 400;
      }
    }
    else {
      if (rpm <= 20) {
        rpm = rpm - 1;
      }
      else {
        rpm = rpm - 5;
      }
      if (rpm <= 1) {
        rpm = 1;
      }
    }


  }
  Serial.println(rpm);
  stepper.setRPM(rpm);
  stepper.rotate(1);
}

Hier der Code mit Display, welcher den Motor stört.

#include <Arduino.h>
#include "A4988.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     4 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
#define OLED_ADDR 0x3c


// this pin should connect to Ground when want to stop the motor
#define STOPPER_PIN 7



// Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
#define MOTOR_STEPS 200
// Microstepping mode. If you hardwired it to save pins, set to the same value here.
#define MICROSTEPS 16

#define DIR 8
#define STEP 9

// Rotary Encoder Inputs
#define PinCLK 3
#define PinDT 2



#define MS1 10
#define MS2 11
#define MS3 12
A4988 stepper(MOTOR_STEPS, DIR, STEP, MS1, MS2, MS3);


int rpm = 200;

const int dirPin = 8;  // Direction
const int stepPin = 9; // Step
int currentStateCLK;
int lastStateCLK;
String currentDir = "";
volatile boolean TurnDetected;
volatile boolean up;


void Rotary()
{
  delay(75);
  if (digitalRead(PinCLK))
    up = !digitalRead(PinDT);
  else
    up = digitalRead(PinDT);
  TurnDetected = true;
  delay(75);
}



void setup() {
  Serial.begin(115200);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();


  // Configure stopper pin to read HIGH unless grounded
  pinMode(STOPPER_PIN, INPUT_PULLUP);

  // Set encoder pins as inputs
  pinMode(PinCLK, INPUT);
  pinMode(PinDT, INPUT);


  stepper.begin(rpm, MICROSTEPS);
  // if using enable/disable on ENABLE pin (active LOW) instead of SLEEP uncomment next line
  // stepper.setEnableActiveState(LOW);
  stepper.enable();

  // Read the initial state of CLK
  // lastStateCLK = digitalRead(CLK);
  /*
    // Call updateEncoder() when any high/low changed seen
    // on interrupt 0 (pin 2), or interrupt 1 (pin 3)
    attachInterrupt(0, updateEncoder, CHANGE);
    attachInterrupt(1, updateEncoder, CHANGE);
  */
  attachInterrupt (0, Rotary, RISING);


  Serial.println("START");
  delay(1000);




}

void loop() {
  {

  if (TurnDetected) {

    if (up) {
      if (rpm < 20) {
        rpm = rpm + 1;
      }
      else {
        rpm = rpm + 5;
      }
      if (rpm >= 400) {
        rpm = 400;
      }
    }
    else {
      if (rpm <= 20) {
        rpm = rpm - 1;
      }
      else {
        rpm = rpm - 5;
      }
      if (rpm <= 1) {
        rpm = 1;
      }
    }
    TurnDetected = false;


}
  stepper.setRPM(rpm);
  stepper.rotate(1);
}
   Serial.println(rpm);
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(5, 0);
  display.print(rpm);
  display.display();

 }

Vielen Dank schonmals fürs durchlesen und sorry wenn es etwas unübersichtlich geworden ist :blush:

(deleted)

Wie gesagt, ich fuchse mich gerade erst ins Thema rein. Switch Cases muss ich mir erst noch angucken zur Steuerung. Wir versuchen gerade uns in kleinen Schritten voran zu bewegen und alles zu verstehen.

Peter-CAD-HST:
Einfach gesagt: Einen endlichen Automaten programmieren und keinen SpaghettiCode.

Wie genau meinst du das gerade hier im Codebeispiel, oder was das aufs Allgemeine bezogen?

(deleted)

Das Thema Steuerung eines Kamerasliders hat es hier natürlich noch nie gegeben :wink:

Inzwischen würde ich anstelle AccelStepper die MobaTools bevorzugen, weil die die Bewegung im Hintergrund per Interrupt steuern. Außerdem gibt es das Entprellen von mechanischen Kontakten und die Zeitsteuerung obendrauf.

MobaTools Library: Stepper mit Rampe - V1.1.1 freigegeben

Für das Display finde ich u8g2 von Oli Kraus besser, weil beispielsweise veränderliche Zahlen besser dargestellt werden.

Möglicherweise findest Du was Nützliches, wenn Du im Forum nach "agmue anleitung" suchst.

Aufteilung der Aufgabe in Funktionen, nach dem EVA Prinzip, und deren Abhängigkeiten.

EVA Prinzip meint Eingabe Verarbeitung Ausgabe. Mann sammelt zuerst alles Notwendige; Verarbeitet die Daten und gibt dann das Ergebnis / Die Aktionen bedingt durch das Ergebnis aus.
Das vereinfacht das Verständnis des Sketches.

Grüße Uwe

Ein Schrittmotor kann schon mit sich ständig ändernder Geschwindigkeit betrieben werden.
Dabei muss man aber trotzdem auf ein präzises Timing achten wann der jeweils nächste Schrittimpuls ausgegeben wird.

Standardanwendung eines Schrittmotors wäre setze eine Drehzahl und dann werden wenigstens 100 Schritte am Stück mit dieser einen konstanten Drehzahl ausgegeben.

Wenn der Befehl

stepper.rotate(1);

genau einen einzelnen Schritt ausgibt und dann der Motor wieder angehalten wird
und
Jetzt sollen im gleichen Durchlauf auch noch Zeichen auf dem Display ausgegeben werden.
Dann beißt sich das. Damit der Schrittmotor gleichmäßig läuft müssen die Schrittimpulse gleichmäßig ausgegeben werden.

Oder bewirkt der Befehl

stepper.rotate(1);

das sich der Schrittmotor mit eingestellter Geschwindigkeit endlos dreht?

Wenn es einen solchen Befehl gibt und die Schrittausgabe über einen Timer-interrupt gemacht wird dann müsste das gehen. So ganz tief kenne ich mich aber auch nicht aus. Es könnte sein, dass das Display wenn es über die hardwareI2C-Schnittstelle angesteuert wird da auch Timing-probleme entstehen könnten.

Jetzt ist die Frage was habt ihr für Anforderungen an die Reaktionszeit zwischen Änderung der Soll-Geschwindigkeit durch den Drehgeber und Reaktion der Ist-Geschwindigkeit am Motor.

Wird da ständig die Geschwindigkeit herauf und heruntergeregelt am besten zum Takt von Musik?
oder wird da eine Geschwindigkeit eingestellt und dann bleibt sie für wenigstens 0,5 Sekunden auf diesem Wert?

Die Interruptroutine ist auch nicht das gelbe vom Ei. Interrupts sollen so schnell wie möglich abgearbeitet werden
aber da stehen zwei delay(75)-Befehle in der Interrupt-routine.

Das heißt das Abarbeiten der Interrupt-Routine braucht 0,15 Sekunden.
Es gibt andere interruptroutinen für Drehencoder die das ohne delay hinbekommen.

Wenn die Geschwindigkeit wenigstens 0,5 Sekunden konstant bleibt kann man die Schrittmotorroutine mehrere Schritte (wenigstens 50 ausgeben lassen) und währenddessen etwas auf dem Display ausgeben.

viele Grüße Stefan

Die Interruptroutine ist auch nicht das gelbe vom Ei. Interrupts sollen so schnell wie möglich abgearbeitet werden
aber da stehen zwei delay(75)-Befehle in der Interrupt-routine.

In der Interruptroutine dürfen keine delay() verwendet werden !!!!

Grüße Uwe

StefanL38:
Wenn es einen solchen Befehl gibt und die Schrittausgabe über einen Timer-interrupt gemacht wird dann müsste das gehen.

Die Accelstepper machen das über Aufrufe in der daher blockadearm zu programmierenden loop, die MobaTools mit Timer-Interrupts, da dreht der Motor auch bei delay weiter.

StefanL38:
Oder bewirkt der Befehl

stepper.rotate(1);

das sich der Schrittmotor mit eingestellter Geschwindigkeit endlos dreht?

Genau das bewirkt der Befehl. Das Vorzeichen bestimmt die Richtung, bei 0 stoppt der Motor ( mit Rampe ).
Wobei der Motor genaugenommen nicht wirklich endlos dreht. Er bewegt sich zum weitest möglichen vom 'Nullpunkt' entfernten Punkt, den die Lib noch verfolgen kann - das sind ca. 2Mrd Steps vom Nullpunkt. Wobei man ihn natürlich jederzeit woandershin dirigieren oder stoppen kann.

Genau das bewirkt der Befehl. Das Vorzeichen bestimmt die Richtung, bei 0 stoppt der Motor ( mit Rampe ).

Der TO benutzt

#include "A4988.h"

Für den Schrittmotor. Also nicht die MoBa-Tools. Da könnte rotate auch anders funktionieren

EDIT: So habe es nachgeschaut. Vermutlich wird die stepper-lib von laurb9 verwendet
da ist der Befehl rotate so definiert

* Rotate the motor a given number of degrees (1-360)
     */
    void rotate(long deg);
    inline void rotate(int deg){
        rotate((long)deg);
    };

viele Grüße Stefan