Funzioni di controllo seriale per attuatori di feedback

La programmazione di un PLC per controllare l'attuatore può essere uno dei progetti più difficili da intraprendere. Richiede tentativi ed errori, test e un mucchio di pazienza; Sebbene i risultati possano essere incredibilmente funzionali e gratificanti.

Il codice in questo tutorial è un programma completo per un funzionale Attuatore reattivo seriale. L'utilizzo del monitor seriale con il PLC ti consentirà di interfacciarsi il sistema Arduino con altri computer, moduli o sistemi di automazione.

Esamineremo ogni funzione e sezione del codice mentre procediamo, quindi puoi estrarre funzioni specifiche da questo tutorial. Si prega di notare che alcune funzioni richiedono altre funzioni che non sono incluse nello stesso blocco di codice, quindi dovrai controllare il codice per assicurarsi che eseguirà correttamente.

Il codice completo verrà pubblicato alla fine di questo articolo. Se desideri utilizzare il codice completo, copialo e incollalo nel tuo software IDE.

Panoramica del programma

Quando è inizializzato, questo programma home e calibra i tuoi attuatori, trovando le estremità del movimento per l'unità. Quindi sposta l'attuatore nel punto centrale della corsa in attesa di ulteriori comandi. È quindi possibile inserire un numero nel monitor seriale (dal tuo PC) e l'attuatore si sposterà in quella posizione. Questo programma non si confonde dallo shutoff di end-of-stroke e funziona con qualsiasi sensore di effetto Hall o attuatore del sensore ottico.

Elenco delle parti

I componenti con un (*) sono inclusi in Arduino Starter Kit

Funzioni

Riepilogo delle funzioni

  1. void setup(): Inizializza i pin, inizia la comunicazione seriale e esegue routine di homing e calibrazione.
  2. void homingRoutine(): Avvia l'homing, sposta l'attuatore in piena retrazione e imposta la corsa minima.
  3. void calibrateActuator(): Avvia la calibrazione, sposta l'attuatore in piena estensione e imposta la corsa massima.
  4. void loop(): Controlla l'input seriale, aggiorna la posizione target e controlla il movimento dell'attuatore.
  5. void movement(): Controlla la direzione del movimento dell'attuatore in base alla posizione target.
  6. void readSensor(): Legge i valori dei sensori, aggiorna la posizione e stampa le letture del sensore.
  7. void stopMotor(): Ferma il movimento dell'attuatore.
  8. bool isEndOfStroke(): Controlla la fine delle condizioni di ictus e gestisce i timeout.

Variabili

Le variabili nel codice devono essere chiamate prima che vengano utilizzate. La migliore pratica di programmazione è assegnare e impostare le tue variabili nella parte superiore del codice, per facilitare la modifica o la messa a punto "le tue variabili.

Queste sono le variabili utilizzate per questo codice, con un commento che spiega la loro funzione e la possibile gamma.

//Actuator Specifications
int maxStroke; // Leave undefined, set during calibration
int minStroke; // Leave undefined, set during homing
// Input Variables (Pins) - Change to match your pinout
const int Xpin=10;
const int Rpin=11;
// Sensor Read Pin 1
const int sensorPin=3;
// Hall Effect Sensor Input (Sensor Read pin 2) - Change to match your pinout
const int sensorPin2=4;
int sensorCount2;
// Position Target Function Variables
int targetNumber;
int currentPosition;
int lastPosition=0;
// Motor Control Variables
bool active = false; // Actuator On / Off toggle flag
bool EOSFlag=false; // End of Stroke Flag
// Sensor Readings
int sensorValue;
int lastSensorValue = LOW;
int sensorValue2;
int lastSensorValue2 = LOW;
// Variables for Debounce and End of Stroke Sensing
const unsigned long motionTimeout = 2000;  
// Adjust this value based on your requirements (in milliseconds) 2000=2s
const unsigned long CALIBRATION_TIMEOUT=3000;
// Adjust this value based on your requirements (in milliseconds) 3000=3s
unsigned long lastMotionTime = millis(); //Timer function for EOS function
// Position Variables
unsigned long pulseCount = 0; 
int direction = 0;  // Used for incremental movement count
// 0: Stopped, 1: Moving Forward, -1: Moving Backward

Setup/Inizializzazione

Il vuoto setup (); La funzione è una funzione Arduino di base che inizializza il PLC, inizia il monitor seriale e assegna ingressi e output come programmati.

Questa funzione di configurazione assegna 4 pin come input o output, quindi esegue le routine di homing e calibrazione.

void setup() {
  pinMode(Xpin, OUTPUT);
  pinMode(Rpin, OUTPUT);
  pinMode(sensorPin, INPUT_PULLUP);
  pinMode(sensorPin2, INPUT_PULLUP);
  Serial.begin(115200);

  homingRoutine();
  calibrateActuator();
}

Routine di homing

Questo snippet di codice è la funzione per la casa dei tuoi attuatori di feedback. Questo codice ritirerà il tuo attuatore fino alla fine della corsa e assegnerà quella posizione un valore di "0" - quindi inizializza la funzione di calibrazione.

Nel programma completo, le funzioni di homing e calibrazione vengono eseguite una volta, durante l'avvio e non più chiamate.

Questo frammento di codice chiama il codice funzionalità aggiuntive che non sono incluse in questo blocco.

void homingRoutine() {
  active=true;
  Serial.println("Homing Initiated");
  digitalWrite(Xpin, LOW);
  digitalWrite(Rpin, HIGH);
  while (!EOSFlag) {
    direction=-1;
    readSensor();
    isEndOfStroke();
    // Move actuator to full retraction
    
  }
  direction=0;
  minStroke=currentPosition;
  Serial.println("Homing Completed");
}

Routine di calibrazione

Successivamente, abbiamo la nostra routine di calibrazione. Questa funzione estenderà completamente l'attuatore dopo che la funzione di homing ha ritirato completamente l'unità. Lo scopo di questa routine è di contare gli impulsi del sensore da un'estremità della corsa dell'attuatore all'altra. Questo valore viene quindi salvato come variabile MaxStroke e utilizzato per il posizionamento. Questo codice può essere modificato per elaborare il feedback del potenziometro mappando la tensione in posizione, piuttosto che contare gli impulsi.

Questo frammento di codice chiama il codice funzionalità aggiuntive che non sono incluse in questo blocco.

void calibrateActuator() {
  Serial.println("Calibration Initiated");
  active = true;
  // Reset variables
  pulseCount = 0;
  currentPosition = 0;
  lastMotionTime=millis();

  // Move actuator to full extension
  digitalWrite(Xpin, HIGH);
  digitalWrite(Rpin, LOW);
  direction=1;

  // Wait until the end of stroke is reached during calibration
  while (!isEndOfStroke()) {
    readSensor();

    // Add a timeout condition to avoid infinite loop
    if (millis() - lastMotionTime > motionTimeout) {
      Serial.println("Calibration Timeout");
      stopMotor();
      maxStroke=currentPosition;
      direction=0;
      // Print the calibration results
        Serial.print("Calibration Complete. Minimum Stroke: ");
        Serial.print(minStroke);
        Serial.print(" Maximum Stroke: ");
        Serial.println(maxStroke);
        targetNumber=((maxStroke+minStroke)/2);
        break;
    }
  }
}

 

Ciclo continuo

La funzione void loop () è la corpo principale del tuo codice Arduino. Qualunque cosa scritta all'interno della funzione Loop () verrà eseguita ripetutamente durante il programma.

Programmatori Nota: Personalmente trovo più semplice mantenere la funzione Loop il più semplice possibile. Questa è la parte del codice che chiamerà la maggior parte delle altre funzioni nel codice, e non può essere sollevato senza il resto del codice.

In questa funzione Loop, il programma controlla il monitor seriale per l'input, aggiorna la posizione di destinazione con il comando e richiama la funzione di movimento. La maggior parte dei messaggi inviati dal PLC al monitor seriale provengono da questo frammento di codice.

C'è un riga di codice commentato Di seguito: quando inclusa, questa riga di codice mappa un input di 0-100 all'attuatore. Cioè. Il tuo contributo è ora una percentuale della corsa dell'attuatore. Questa linea può anche essere modificata per utilizzare altri input in un formato mappato simile.

Questo frammento di codice chiama funzionalità aggiuntive che non sono incluse in questo blocco.

void loop() {
if (!active && Serial.available() > 0) {
  String serialInput = Serial.readStringUntil('\n');
  Serial.print("Received: ");
  Serial.println(serialInput);
  if (serialInput.length() > 0) {
    targetNumber = serialInput.toInt();
    //targetNumber = map(targetNumber, 0, 100, minStroke, maxStroke);

/*If the above line is active, you will input a value between 0-100,
The program will use this input as a % of stroke.*/
Serial.print("Target number: "); Serial.println(targetNumber); EOSFlag = false; } // Clear the serial buffer while (Serial.available()) { Serial.read(); } } if (targetNumber != currentPosition) { active = true; movement(); } /* if (!active) { Serial.println("Waiting for Input"); return; } */ if (active && targetNumber == currentPosition) { stopMotor(); Serial.println("Target Met"); } }

 

Movimento

La funzione di movimento controlla la direzione del movimento dell'attuatore in base alla posizione target. Questa è l'unica funzione in questo programma che dice al driver del motore di spostare l'attuatore. 

Questo frammento di codice chiama funzionalità aggiuntive che non sono incluse in questo blocco.

void movement() {
  if (targetNumber > currentPosition) {
    digitalWrite(Xpin,HIGH);
    digitalWrite(Rpin,LOW);
    //Serial.println(" Extending");
    direction = 1;
  } else if (targetNumber < currentPosition) {
    digitalWrite(Rpin,HIGH);
    digitalWrite(Xpin,LOW);
    direction = -1;
    //Serial.println("Retracting");
  } else if (targetNumber == currentPosition) {
    stopMotor();
    delay(10);
  }
  if(active) {
    readSensor();
  }
  if (isEndOfStroke()) {
    return;  // Skip further movement actions
  }
}

Sensore Read

La funzione di elaborazione del sensore legge i valori del sensore, aggiorna la posizione e stampa le letture del sensore al monitor seriale. I comandi seriali possono essere utilizzati per il debug o commentati per una lettura meno busidica.

Questo frammento di codice può essere utilizzato in qualsiasi programma e fornirà una lettura accurata degli impulsi da un sensore di feedback.

void readSensor() {
  sensorValue = digitalRead(sensorPin);
  if(lastSensorValue != sensorValue) {
    lastSensorValue = sensorValue;
    pulseCount = pulseCount + direction;
    Serial.print("Sensor 1: ");
    Serial.println(pulseCount);
  }
  sensorValue2 = digitalRead(sensorPin2);
  if(lastSensorValue2 != sensorValue2) {
    lastSensorValue2 = sensorValue2;
    sensorCount2=sensorCount2+direction;
    pulseCount = pulseCount + direction;
    Serial.print("Sensor 2: ");
    Serial.println(sensorCount2);
    Serial.print("Current Position: ");
    Serial.println(currentPosition);
  }
  currentPosition = pulseCount;
}

Arresto del motore e disattivazione

Interrompe il movimento dell'attuatore e riabilita l'unità per leggere il monitor seriale per ulteriori comandi. Questo è l'unico frammento di codice in questo programma che fermerà il movimento dell'attuatore.

Questo frammento di codice può essere utilizzato in qualsiasi programma con qualsiasi attuatore, in quanto disattiva il motore e cambia lo stato "attivo".

void stopMotor() {
 if (active) {
   active=false;
   digitalWrite(Xpin,LOW);
   digitalWrite(Rpin,LOW);
  }
}

Controllo della corsa

Controlla la fine delle condizioni di ictus e gestisce i timeout. Quando l'attuatore colpisce la fine della corsa e smette di muoversi, questa funzione attiverà e ripristinerà l'attuatore per nuovi input di comando, anche se il numero di destinazione non è stato ancora raggiunto.

Questo frammento di codice chiama il codice funzionalità aggiuntive che non sono incluse in questo blocco.

bool isEndOfStroke() {
  // Check if there is motion (changes in the pulse count)
  if (active && (currentPosition != lastPosition)) {
    lastMotionTime = millis();  // Update the time of the last motion
    lastPosition = currentPosition;
    EOSFlag=false;
  }

  // Check if there is no motion for the specified timeout
  if (active && ((millis() - lastMotionTime) > motionTimeout)) {
    if(EOSFlag!=true) {
      Serial.print("Timeout - ");
      Serial.println("At limit");
      EOSFlag=true;
    }
    direction=0;
    stopMotor();
    return true;
  }
  return false;
}

Codice completo

//Actuator Specifications
int maxStroke;
int minStroke;

// Input Variables (Pins)
const int Xpin=10;
const int Rpin=11;
const int sensorPin=3;
// Hall Effect Sensor Input
const int sensorPin2=4;
int sensorCount2;


// Motor Function Variables
int targetNumber;
int currentPosition;
int lastPosition=0;
bool active = false;
bool EOSFlag=false;

// Sensor Readings
int sensorValue;
int lastSensorValue = LOW;
int sensorValue2;
int lastSensorValue2 = LOW;

// Variables for Debounce
const unsigned long motionTimeout = 2000;  // Adjust this value based on your requirements (in milliseconds)
const unsigned long CALIBRATION_TIMEOUT=3000; // Adjust this value based on your requirements (in milliseconds)
unsigned long lastMotionTime = millis();

// Position Variables
unsigned long pulseCount = 0;
int direction = 0;  // 0: Stopped, 1: Moving Forward, -1: Moving Backward




void setup() {
  pinMode(Xpin, OUTPUT);
  pinMode(Rpin, OUTPUT);
  pinMode(sensorPin, INPUT_PULLUP);
  pinMode(sensorPin2, INPUT_PULLUP);
  Serial.begin(115200);

  homingRoutine();
  calibrateActuator();
}

void homingRoutine() {
  active=true;
  Serial.println("Homing Initiated");
  digitalWrite(Xpin, LOW);
  digitalWrite(Rpin, HIGH);
  while (!EOSFlag) {
    direction=-1;
    readSensor();
    isEndOfStroke();
    // Move actuator to full retraction
    
  }
  direction=0;
  minStroke=currentPosition;
  Serial.println("Homing Completed");
}

void calibrateActuator() {
  Serial.println("Calibration Initiated");
  active = true;
  // Reset variables
  pulseCount = 0;
  currentPosition = 0;
  lastMotionTime=millis();

  // Move actuator to full extension
  digitalWrite(Xpin, HIGH);
  digitalWrite(Rpin, LOW);
  direction=1;

  // Wait until the end of stroke is reached during calibration
  while (!isEndOfStroke()) {
    readSensor();

    // Add a timeout condition to avoid infinite loop
    if (millis() - lastMotionTime > motionTimeout) {
      Serial.println("Calibration Timeout");
      stopMotor();
      maxStroke=currentPosition;
      direction=0;
      // Print the calibration results
        Serial.print("Calibration Complete. Minimum Stroke: ");
        Serial.print(minStroke);
        Serial.print(" Maximum Stroke: ");
        Serial.println(maxStroke);
        targetNumber=((maxStroke+minStroke)/2);
        break;
    }
  }
}

void loop() {
if (!active && Serial.available() > 0) {
  String serialInput = Serial.readStringUntil('\n');
  Serial.print("Received: ");
  Serial.println(serialInput);
  if (serialInput.length() > 0) {
    targetNumber = serialInput.toInt();
    Serial.print("Target number: ");
    Serial.println(targetNumber);
    EOSFlag = false;
  }
  // Clear the serial buffer
  while (Serial.available()) {
    Serial.read();
  }
}

  if (targetNumber != currentPosition) {
    active = true;
    movement();
  } 
  /*
  if (!active) {
   Serial.println("Waiting for Input"); 
   return;
  } */
  if (active && targetNumber == currentPosition) {
   stopMotor();
   Serial.println("Target Met");
  }
}

void movement() {
  if (targetNumber > currentPosition) {
    digitalWrite(Xpin,HIGH);
    digitalWrite(Rpin,LOW);
    //Serial.println(" Extending");
    direction = 1;
  } else if (targetNumber < currentPosition) {
    digitalWrite(Rpin,HIGH);
    digitalWrite(Xpin,LOW);
    direction = -1;
    //Serial.println("Retracting");
  } else if (targetNumber == currentPosition) {
    stopMotor();
    delay(10);
  }
  if(active) {
    readSensor();
  }
  if (isEndOfStroke()) {
    return;  // Skip further movement actions
  }
}

void readSensor() {
  sensorValue = digitalRead(sensorPin);
  if(lastSensorValue != sensorValue) {
    lastSensorValue = sensorValue;
    pulseCount = pulseCount + direction;
    Serial.print("Sensor 1: ");
    Serial.println(pulseCount);
  }
  sensorValue2 = digitalRead(sensorPin2);
  if(lastSensorValue2 != sensorValue2) {
    lastSensorValue2 = sensorValue2;
    sensorCount2=sensorCount2+direction;
    pulseCount = pulseCount + direction;
    Serial.print("Sensor 2: ");
    Serial.println(sensorCount2);
    Serial.print("Current Position: ");
    Serial.println(currentPosition);
  }
  currentPosition = pulseCount;
}


void stopMotor() {
 if (active) {
   active=false;
   digitalWrite(Xpin,LOW);
   digitalWrite(Rpin,LOW);
  }
}

bool isEndOfStroke() {
  // Check if there is motion (changes in the pulse count)
  if (active && (currentPosition != lastPosition)) {
    lastMotionTime = millis();  // Update the time of the last motion
    lastPosition = currentPosition;
    EOSFlag=false;
  }

  // Check if there is no motion for the specified timeout
  if (active && ((millis() - lastMotionTime) > motionTimeout)) {
    if(EOSFlag!=true) {
      Serial.print("Timeout - ");
      Serial.println("At limit");
      EOSFlag=true;
    }
    direction=0;
    stopMotor();
    return true;
  }
  return false;
}
Share This Article
Tags: