Timer Problem AccelStepper Library

Hallo, ich möchte eine Maschine zur Rotationsentwicklung von analogen Filmen bauen.
Dazu habe ich einen Nema 17 bipolaren Motor den ich mit einem Treiber Board DRVB8825 ansteuere. Mit der AccelStepper Library klappt das soweit gut.
Nun wollte ich noch einen Buzzer dazu nehmen, der jede Minute kurz piept. Sowie die im
Ein-Schalter befindliche LED, welche nach dem Einschalten leuchtet, jede Minute kurz blinken lassen. Das alles auf einem AtTiny85 (passt drauf)

Dazu habe ich folgenden Code genutzt:

/* Sketch für eine Rotationsentwicklungs-Maschine für Jobo Dosen
15xx und 25xx/28xx mit Nema 17 Stepper-Motor und DRV8825
8 Umdrechungen im Uhrzeigersinn und zurück. Ca. 70 U/min */   

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

const int BUZZER = 3;
const int ledPin = 4;

unsigned long ledTimer;
unsigned long BuzzerTimer;
int LedStatus = HIGH;


// Define stepper motor connections and motor interface type. Motor interface type must be set to 1 when using a driver:
#define dirPin 0
#define stepPin 1
#define motorInterfaceType 1

// Create a new instance of the AccelStepper class:
AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin);

void setup() {

  pinMode(ledPin,OUTPUT);
  digitalWrite(ledPin, HIGH);

  // Set the maximum speed and acceleration:
  stepper.setMaxSpeed(470); //Half-Step
  stepper.setAcceleration(400); //Half-Step
}

void loop() {

if(millis() - BuzzerTimer >=60000 ){
    tone(BUZZER, 500,1000);//tone of 500Hz for 1 second/1000 milliseconds
    BuzzerTimer = millis();
}
  
if (LedStatus == HIGH) {
    if (millis() - ledTimer >= 60000 ) {
      digitalWrite(ledPin, LOW);
      ledTimer = millis();
      LedStatus = LOW;
      }
  } else {
    if (millis() - ledTimer >= 250) {
      digitalWrite(ledPin, HIGH);
      LedStatus = HIGH;
}

 {// Set the target position:
  stepper.moveTo(3200); // Half-Step
  // Run to target position with set speed and acceleration/deceleration:
  stepper.runToPosition();

  delay(300);

  // Move back to zero:
  stepper.moveTo(0);
  stepper.runToPosition();

  delay(300);
}

}
}

Beide Code Abschnitte für sich laufen richtig:

Buzzer und LED (ohne den Stepper Anteil in der Loop) oder Motor alleine.

Zusammen läuft der Motor und der Buzzer piept jede Minute. Jedoch die Led geht jede Minute aus und bleibt dann länger als eingestellt (250 ms) aus. Scheinbar un die Zeit länger, die ein kompletter Zyklus Stepper Motor dauert.

Was kann ich da ggf. tuen.

Danke

Branko

delay() ist blockierend und muß durch millis() wie bei der LED ersetzt werden.

Die function runToPosition ( ) ist blockierend.
Das bedeutet der Code läuft erst weiter wenn der Schrittmotor wieder still steht.

Kann man in der Doku zu AccelStepper nachlesen
https://www.airspayce.com/mikem/arduino/AccelStepper/classAccelStepper.html

runToPosition()

void AccelStepper::runToPosition ( )

Moves the motor (with acceleration/deceleration) to the target position and blocks until it is at position. Dont use this in event loops, since it blocks.

Es gibt andere functions in AccelStepper die nicht blockierend sind.
Die müssen aber mit ganz hoher Frequenz immer wieder neu aufgerfufen werden.
Das geht gar nicht mit delay() zusammen.

Dann müssen die delay auf jeden Fall durch nicht blockierendes timing basierend auf millis() verwendet werden.

Die andere Variante ist die library mobaTools zu verwenden.
Da werden die Step-Impulse per Timer-interrupt erzeugt.
Hat der Tiny85 Timer 1? dann geht das.

Frage: muss es unbedingt so ein Winzling wie Tiny85 sein?

Hallo,
ich möchte das die Rotation jeweils kurz stoppt bevor es in die andere Richtung geht.
Wie würde das mit millis() ggf. gehen?
Danke

Da ich mich noch nicht damit zurecht finde:

Wie würde ich eine solche Funktion ohne delay in AccelStepper ausführen?
Danke

Nicht blockierendes Timing funktioniert fundamental anders als delay()
Hier ist die Erklärung

Moin @Branko ,

hier ein Sketch, der Deine Anforderungen (hoffentlich) erfüllen sollte:

/*
  Forum: https://forum.arduino.cc/t/timer-problem-accelstepper-library/1341227
  Wokwi: https://wokwi.com/projects/419796124630104065

  Originalsketch aus Post 1 Wokwi: https://wokwi.com/projects/419795832434931713

  2025/01/11

  Angepasst durch ec2021

*/


/* Sketch für eine Rotationsentwicklungs-Maschine für Jobo Dosen
  15xx und 25xx/28xx mit Nema 17 Stepper-Motor und DRV8825
  8 Umdrechungen im Uhrzeigersinn und zurück. Ca. 70 U/min */

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

const int BUZZER = 3;
const int ledPin = 4;
const unsigned long stepperDelay = 300;
const unsigned long ledIntervall  = 5000;    // 60000;
const unsigned long blinkDuration = 300;
const unsigned long buzzerIntervall = 5000; //60000;
const int stepperRechts = 3200;
const int stepperLinks  = 0;

enum StatusTyp {WARTERECHTS, RECHTS, WARTELINKS, LINKS};
StatusTyp status = WARTERECHTS;

unsigned long ledTimer  = 0;
unsigned long buzzerTimer = 0;
unsigned long stepperTimer = 0;
int LedStatus = HIGH;


// Define stepper motor connections and motor interface type. Motor interface type must be set to 1 when using a driver:
#define dirPin 0
#define stepPin 1
#define motorInterfaceType 1

// Create a new instance of the AccelStepper class:
AccelStepper stepper = AccelStepper(motorInterfaceType, stepPin, dirPin);

void setup() {

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);
  // Set the maximum speed and acceleration:
  stepper.setMaxSpeed(470); //Half-Step
  stepper.setAcceleration(400); //Half-Step
  ledTimer = millis();
  buzzerTimer = millis();
}

void loop() {
  stateMachine();
  beep();
  blink();
}

void stateMachine() {
  switch (status) {
    case WARTERECHTS:
      if (millis() - stepperTimer > stepperDelay) {
        status = RECHTS;
        stepper.moveTo(stepperRechts);
      }
      break;
    case RECHTS:
      stepper.run();
      if (stepper.currentPosition() >= stepperRechts) {
        status = WARTELINKS;
        stepperTimer = millis();
      }
      break;
    case WARTELINKS:
      if (millis() - stepperTimer > stepperDelay) {
        status = LINKS;
        stepper.moveTo(stepperLinks);
      }
      break;
    case LINKS:
      stepper.run();
      if (stepper.currentPosition() <= stepperLinks) {
        status = WARTERECHTS;
        stepperTimer = millis();
      }
      break;
  }
}

void beep() {
  if (millis() - buzzerTimer >= buzzerIntervall ) {
    buzzerTimer = millis();
    tone(BUZZER, 500, 1000); //tone of 500Hz for 1 second/1000 milliseconds
  }
}

void blink() {
  static byte ledStatus = HIGH;
  if (millis() - ledTimer >= ledIntervall && ledStatus == HIGH) {
    ledTimer = millis();
    ledStatus = LOW;
    digitalWrite(ledPin, ledStatus);
  }
  if (millis() - ledTimer >= blinkDuration && ledStatus == LOW) {
    ledStatus = HIGH;
    digitalWrite(ledPin, ledStatus);
  }
}

// ACHTUNG:
// Hilfslösung, da der Attiny85 bei Wokwi tone() auf Pin PB3 nicht unterstützt
//
void tone(byte Pin, int f, int d) {
  pinMode(Pin, OUTPUT);
  for (int i = 0; i < 5; i++) {
    digitalWrite(Pin, HIGH);
    delay(1);
    digitalWrite(Pin, LOW);
  }
}

Der Sketch wurde auf Wokwi geschrieben und getestet, wo tone() am Pin PB3 nicht unterstützt wird. Daher habe ich tone() hilfsweise mit einer eigenen Funktion überschrieben. Diesen Teil (ganz unten im Sketch!) kannst Du löschen, wenn die Funktion an Deinem Attiny85 gegeben ist.

Die Konstanten für die Intervalle zum Blinken und die Tonausgabe habe ich zum Testen auf 5 s gesetzt; hier kannst Du wieder die 60 s eintragen:

const unsigned long ledIntervall  = 5000;    // 60000;
const unsigned long blinkDuration = 300;
const unsigned long buzzerIntervall = 5000; //60000;

Bei Fragen gerne melden.
Viel Spaß!
ec2021

Link: https://wokwi.com/projects/419796124630104065

1 Like

Er hat zwar einen Timer 1, das ist aber nur ein 8-Bit Timer. Die MobaTools benötigen einen 16-Bit Timer.

Hallo,

vielen Dank dafür. Werde mich erst mal damit beschäftigen und komme dann ggf. auf das freundliche Angebot mit dem Fragen zurück :).

Bin immer wieder berührt davon, wie hilfsbereit Viele sind!
Branko

Hallo,
habe mich mit der mobaTools Library befasst. Dabei habe ich fo0lgendes gebaut:

/*  Example for MobaTools
    Moving a stepper back and forth
*/
#include <MobaTools.h>

// Adjust pins, steps and time as needed
const byte stepPin = 9;
const byte dirPin  = 8;
const int stepsPerRev = 800;   // Steps per Revolution ( example with 1/4 microsteps )
const long  targetPos = 6400;         // stepper moves between 0 and targetpos
long nextPos;

const int Buzzer =  10; 
MoToTimer Buzzerzeit;

const int Led =  11; 
MoToTimer Ledzeit;

MoToStepper myStepper ( stepsPerRev, STEPDIR );
MoToTimer stepperPause;                         // Pause between stepper moves
bool stepperRunning;

void setup() {
  myStepper.attach( stepPin, dirPin );
  myStepper.setSpeed( 700 );                  // 70 Rev/Min ( if stepsPerRev is set correctly )
  myStepper.setRampLen( 1200 );
  stepperRunning = true;

  //pinMode(Buzzer, OUTPUT); 

  pinMode(Led, OUTPUT);
  digitalWrite(Led, HIGH);
}

void loop() {
  if ( stepperRunning ) {
    // Wait till stepper has reached target, then set pause time
    if ( !myStepper.moving() ) {
      // stepper has reached target, start pause
      stepperPause.setTime( 1000 );
      stepperRunning = false;
    }
  } else {
    // stepper doesn't move, wait till stepperPause time expires
    if ( stepperPause.expired() ) {
      // stepperPause time expired. Start stepper in opposite direction
      if ( nextPos == 0 ) {
        nextPos = targetPos;
      } else {
        nextPos = 0;
      }
      myStepper.moveTo( nextPos );
      stepperRunning = true;
    }
  }

  // The sketch is not blocked while the stepper is moving nor while it is stopped.
  // Other nonblocking  tasks can be added here

// diese Led blinkt mit unsymetrischem Taktverhältnis
	if ( Ledzeit.running()== false ) {
        // Blinkzeit abgelaufen, Ausgang toggeln und
        // Zeit neu aufziehen
        if ( digitalRead( Led ) == LOW ) {
            digitalWrite( Led, HIGH );
            Ledzeit.setTime( 30000 );
       } else {
            digitalWrite( Led, LOW );
            Ledzeit.setTime( 500 );
        }
	}


if ( Buzzerzeit.running() == false ) { // Buzzerzeit abgelaufen
        
    tone(Buzzer, 440, 500);

      Buzzerzeit.setTime( 30000 ); // Zeit neu aufziehen
    }

}

Dabei sollte doch der Buzzer und die led gleichzeitig blinken. Jedoch läuft nach kurzer Zeit die LED dem Buzzer immer weiter hinterher?

Woran liegt das?
Danke.
Branko

Na bei der LED machst du doch auch noch

Und dann ist der LED-Timer ne halbe Sekunde hinten dran.

Entweder du packst jetzt den buzzer mit in die LED rein
oder du musst für LED 0,5 Sekunden aufleuchten noch einen dritten timer dazubauen.

@MicroBahner

für regelmäßiges Auslösen gibt es das Objekt MotoTimebase

MoToTimebase myBase;

myBase.setBasetime( 5000 );

Wie ist das wenn man jetzt

myBase.tick();

abfragt.

Muss das genau nach 5000 Millisekunden sein ud nur wenn man exakt in der 5000sten Millisekunde abfragt, dann liefert das ein einziges mal true weil dann wird ja das nächste intervall gestartet ?

Oder bleibt das true bis man es einmal abgefragt hat ?

Was bedeuten würde auch wenn ich nach 6000 Millisekunden das erste man tick() aufrufe liefert es true?

Nein. Wenn in dem Fall die 5000ms abgelaufen sind, wird tick() einmal true zurückliefern, egal wann Du abfragst.

Ja, beim nächsten Mal dann aber schon wenn Du nach 4000ms ein weiters Mal abfragst. Solange Du öfter abfragst als es dem Intervall entspricht, bleibt das Raster gleich. Wenn Du allerdings immer zu spät abfragst, funktioniert das nicht mehr. Dann macht die Funktion aber eigentlich keinen Sinn mehr. [EDIT] Das Raster bleibt auch in dem Fall gleich, aber Du verlierst 'ticks'[/EDIT]
D.h. Du solltest tick() auf jeden Fall öfter abfragen als es dem Intervall entspricht, aber Du musst nicht immer genau den Intervallzeitpunkt treffen ( dann würde es ja auch keinen Sinn machen)

So dann kann man das mit den MobaTools so programmieren

/*  Example for MobaTools
    Moving a stepper back and forth
*/
#include <MobaTools.h>

// Adjust pins, steps and time as needed
const byte dirPin  = 8;
const byte stepPin = 9;
const int stepsPerRev = 800;   // Steps per Revolution ( example with 1/4 microsteps )
const long  targetPos = 6400;         // stepper moves between 0 and targetpos
long nextPos;

const byte Buzzer =  10;
const byte Led =  11;

MoToTimebase Buzzerzeit;
MoToTimebase    LedBlitz;

MoToStepper myStepper ( stepsPerRev, STEPDIR );
MoToTimer stepperPause;                         // Pause between stepper moves
bool stepperRunning;

const long BuzzerInterval =  10000;
const long BlitzInterval  =    500;
const long stepperPauseInterval = 2000;
void setup() {
  Serial.begin(115200);
  Serial.println("Setup-Start");
  Buzzerzeit.setBasetime( BuzzerInterval );
  LedBlitz.setBasetime  ( BlitzInterval );

  myStepper.attach( stepPin, dirPin );
  myStepper.setSpeed( 700 );                  // 70 Rev/Min ( if stepsPerRev is set correctly )
  myStepper.setRampLen( 1200 );
  stepperRunning = true;

  //pinMode(Buzzer, OUTPUT);

  pinMode(Led, OUTPUT);
  digitalWrite(Led, HIGH);
}


void loop() {
  if ( stepperRunning ) {
    // Wait till stepper has reached target, then set pause time
    if ( !myStepper.moving() ) {
      Serial.print("targetpos reached myStepper.readSteps()=");
      Serial.println(myStepper.readSteps());
      Serial.println();
      // stepper has reached target, start pause
      stepperPause.setTime( stepperPauseInterval );
      stepperRunning = false;
    }
  }
  else {  // stepper doesn't move, wait till stepperPause time expires    
    if ( stepperPause.expired() ) {
      // stepperPause time expired. Start stepper in opposite direction
      if ( nextPos == 0 ) {
        nextPos = targetPos;
      }
      else {
        nextPos = 0;
      }
      Serial.print("pausing finished ");
      Serial.print("now moving to pos=");
      myStepper.moveTo( nextPos );
      Serial.println(nextPos);
      stepperRunning = true;
    }
  }

  if ( Buzzerzeit.tick() ) {   // Buzzerzeit abgelaufen
    tone(Buzzer, 440, 500);
    digitalWrite( Led, HIGH ); // LED ein
    LedBlitz.setBasetime( BlitzInterval );   // Blitztimer starten
  }

  // Led ist an und Led On-Zeit vorbei
  if ( digitalRead(Led) == HIGH && LedBlitz.tick() ) {
    digitalWrite( Led, LOW );  // Led aus
    LedBlitz.setBasetime( -BlitzInterval );     // timer stoppen
  }

}

WOKWI-Simulationmit geänderten Zeiten zum testen

Hallo, danke nochmals für die vielen Anregungen. Werde mich jetzt daran begeben alle zu testen.

Branko

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.