La programación de un PLC para controlar su actuador puede ser uno de los proyectos más difíciles de emprender. Requiere prueba y error, pruebas y un montón de paciencia; Aunque los resultados pueden ser increíblemente funcionales y gratificantes.
El código en este tutorial es un programa completo para una funcional Actuador de respuesta en serie. La utilización del monitor en serie con su PLC le permitirá interactuar su sistema Arduino con otras computadoras, módulos o sistemas de automatización.
Revisaremos cada función y sección del código a medida que avanzamos, por lo que puede extraer funciones específicas de este tutorial. Tenga en cuenta que algunas funciones solicitan otras funciones que no están incluidas en el mismo bloque de código, por lo que deberá verificar su código para asegurarse de que se ejecutará correctamente.
El código completo se publicará al final de este artículo. Si desea usar el código completo, copie y péguelo en su software IDE.
Descripción general del programa
Cuando se inicializa, este programa estará en casa y calibrará a sus actuadores, encontrando los fines de movimiento para la unidad. Luego mueve al actuador al punto medio de accidente cerebrovascular para esperar más comandos. Luego puede ingresar un número en su monitor serial (desde su PC) y el actuador se moverá a esa posición. Este programa no se confunde con el cierre del final del golpe, y funciona con cualquier sensor de efecto Hall o actuador de sensor óptico.
Lista de piezas
Los componentes con un (*) están incluidos en el Kit de inicio de Arduino
- Arduino plc (Desatado* o Mega 2560)
- Conductor del motor (AD-MD6321 de alta corriente) - 1 por actuador.
- Fuente de alimentación de 12V Para los actuadores (exceder el sorteo de amperaje acumulativo)
- 1 placa de pan, según sea necesario*
- Cables de puente para Arduino PLC: cada MD de alta corriente requerirá 6 Conexiones femeninas.
- Cable de 1 pin para PLCS* (M-F 10pc/PK)
- Kit de alambre de puente (M-M, 75pc/PK; varias longitudes);
- Actuadores con sensores de retroalimentación y soportes de montaje del actuador, según sea necesario. (Sensor de efecto Hall o actuadores de sensores ópticos solamente, para este código)
Funciones
Resumen de funciones
-
void setup()
: Inicializa pines, comienza la comunicación en serie y ejecuta rutinas de referencia y calibración. -
void homingRoutine()
: Inicia la referencia, mueve el actuador a una retracción completa y establece el accidente cerebrovascular mínimo. -
void calibrateActuator()
: Inicia la calibración, mueve el actuador a una extensión completa y establece el trazo máximo. -
void loop()
: Comprueba la entrada en serie, actualiza la posición del objetivo y controla el movimiento del actuador. -
void movement()
: Controla la dirección del movimiento del actuador en función de la posición objetivo. -
void readSensor()
: Lee los valores del sensor, la posición de las actualizaciones e imprime las lecturas del sensor. -
void stopMotor()
: Detiene el movimiento del actuador. -
bool isEndOfStroke()
: Verifica el final de las condiciones de accidente cerebrovascular y maneja los tiempos de espera.
Variables
Se deben llamar a las variables en su código antes de que se usen. La mejor práctica de programación es asignar y establecer sus variables en la parte superior de su código, para facilitar la modificación o 'ajustar' sus variables.
Estas son las variables utilizadas para este código, con un comentario que explica su función y su posible rango.
//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
Configuración/inicialización
La configuración vacía (); La función es una función base arduino que inicializa el PLC, comienza el monitor en serie y asigna entradas y salidas según lo programado.
Esta función de configuración asigna 4 pines como entradas o salidas, luego ejecuta las rutinas de referencia y calibración.
void setup() {
pinMode(Xpin, OUTPUT);
pinMode(Rpin, OUTPUT);
pinMode(sensorPin, INPUT_PULLUP);
pinMode(sensorPin2, INPUT_PULLUP);
Serial.begin(115200);
homingRoutine();
calibrateActuator();
}
Rutina de referencia
Este fragmento de código es la función para anular sus actuadores de comentarios. Este código retraerá su actuador hasta que se detecte el final del accidente cerebrovascular, y le asignará a esa posición un valor de "0", luego inicializa la función de calibración.
En el programa completo, las funciones de referencia y calibración se ejecutan una vez, durante el inicio, y no se vuelven a llamar.
Este fragmento de código llama funciones (s) adicionales que no están incluidas en este bloque.
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");
}
Rutina de calibración
A continuación, tenemos nuestra rutina de calibración. Esta función extenderá completamente su actuador después de que la función de referencia haya retraído completamente la unidad. El propósito de esta rutina es contar los pulsos del sensor de un extremo del accidente cerebrovascular del actuador hacia el otro. Este valor se guarda como la variable Maxstroke y se usa para el posicionamiento. Este código se puede modificar para procesar la retroalimentación del potenciómetro mediante el mapeo de voltaje a la posición, en lugar de contar pulsos.
Este fragmento de código llama funciones (s) adicionales que no están incluidas en este bloque.
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;
}
}
}
Bucle
La función de bucle void () es la cuerpo principal de su código Arduino. Cualquier cosa escrita dentro de la función Loop () se ejecutará repetidamente durante el programa.
Nota de los programadores: Personalmente, me parece más fácil mantener la función del bucle lo más simple posible. Esta es la parte del código que llamará la mayoría de las otras funciones en el código y no puedo ser levantado sin el resto del código.
En esta función de bucle, el programa verifica el monitor en serie para la entrada, actualiza la posición de destino con su comando y llama a la función de movimiento. La mayoría de los mensajes que envía el PLC al monitor en serie proviene de este fragmento de código.
Hay un línea de código comentada A continuación, cuando se incluye, esta línea de código asigna una entrada de 0-100 al actuador. Es decir. Su aporte ahora es un porcentaje del accidente cerebrovascular del actuador. Esta línea también se puede modificar para utilizar otras entradas en un formato mapeado similar.
Este fragmento de código llama funciones adicionales que no están incluidas en este bloque.
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");
}
}
Movimiento
La función de movimiento controla la dirección del movimiento del actuador en función de la posición objetivo. Esta es la única función en este programa que le dice al controlador del motor que mueva el actuador.
Este fragmento de código llama funciones adicionales que no están incluidas en este bloque.
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
}
}
Lectura del sensor
La función de procesamiento del sensor lee valores del sensor, actualiza la posición e imprime lecturas del sensor en el monitor en serie. Los comandos en serie se pueden usar para la depuración, o comentados para una lectura menos consecutiva.
Este fragmento de código se puede usar en cualquier programa y dará una lectura precisa de pulsos de un sensor de retroalimentación.
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;
}
Parada del motor y desactivación
Detiene el movimiento del actuador y vuelve a habilitar la unidad para leer el monitor en serie para obtener más comandos. Este es el único fragmento de código en este programa que detendrá la moción del actuador.
Este fragmento de código se puede usar en cualquier programa con cualquier actuador, ya que simplemente apaga el motor y cambia el estado 'activo'.
void stopMotor() {
if (active) {
active=false;
digitalWrite(Xpin,LOW);
digitalWrite(Rpin,LOW);
}
}
Cheque de finalización de accidente cerebrovascular
Verifica el final de las condiciones de accidente cerebrovascular y maneja los tiempos de espera. Cuando el actuador llega al final de la carrera y deja de moverse, esta función activará y restablecerá el actuador para nuevas entradas de comando, incluso si el número de destino aún no se ha alcanzado.
Este fragmento de código llama funciones (s) adicionales que no están incluidas en este bloque.
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;
}
Código 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;
}