Funções de controle serial para atuadores de feedback

Programando um PLC para controlar seu atuador pode ser um dos projetos mais difíceis de realizar. Requer tentativa e erro, testes e um monte de paciência; Embora os resultados possam ser incrivelmente funcionais e gratificantes.

O código deste tutorial é um programa completo para um funcional Atuador responsivo em série. A utilização do monitor serial com o seu PLC permitirá que você interaja seu sistema Arduino com outros computadores, módulos ou sistemas de automação.

Analisaremos cada função e seção do código à medida que avançamos, para que você possa retirar funções específicas deste tutorial. Observe que algumas funções solicitam outras funções que não estão incluídas no mesmo bloco de código, portanto, você precisará verificar seu código para garantir que ele seja executado corretamente.

O código completo será publicado no final deste artigo. Se você deseja usar o código completo, copie e cole -o no seu software IDE.

Visão geral do programa

Quando inicializado, este programa abrigará e calibrará seus atuadores, encontrando as extremidades do movimento para a unidade. Em seguida, move o atuador para o ponto intermediário do AVC para aguardar outros comandos. Você pode inserir um número no seu monitor serial (do seu PC) e o atuador passará para essa posição. Este programa não fica confuso com o desligamento do final do AVC e trabalha com qualquer sensor de efeito Hall ou atuador do sensor óptico.

Lista de peças

Os componentes com um (*) estão incluídos no Kit inicial do Arduino

Funções

Resumo das funções

  1. void setup(): Inicializa os pinos, inicia a comunicação em série e executa rotinas de homing e calibração.
  2. void homingRoutine(): Inicia o Homing, move o atuador para a retração total e define o golpe mínimo.
  3. void calibrateActuator(): Inicia a calibração, move o atuador para a extensão total e define o curso máximo.
  4. void loop(): Verifica a entrada serial, atualiza a posição de destino e controla o movimento do atuador.
  5. void movement(): Controla a direção do movimento do atuador com base na posição alvo.
  6. void readSensor(): Lê valores do sensor, atualiza a posição e imprime as leituras do sensor.
  7. void stopMotor(): Interrompe o movimento do atuador.
  8. bool isEndOfStroke(): Verifica o final das condições do AVC e lida com os tempos limite.

Variáveis

As variáveis ​​em seu código devem ser chamadas antes de serem usadas. A melhor prática de programação é atribuir e definir suas variáveis ​​na parte superior do seu código, para facilitar a modificação ou o 'ajuste' suas variáveis.

Essas são as variáveis ​​usadas para este código, com um comentário explicando sua função e possível intervalo.

//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

Configuração/inicialização

O void setup (); A função é uma função base do arduino que inicializa o PLC, inicia o monitor serial e atribui entradas e saídas conforme programado.

Esta função de configuração atribui 4 pinos como entrada ou saída e executa as rotinas de homing e calibração.

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

  homingRoutine();
  calibrateActuator();
}

Rotina de homing

Este snippet de código é a função para homenagear seus atuadores de feedback. Este código retrairá seu atuador até que o final do AVC seja detectado e atribuirá a essa posição um valor de "0" - então ele inicializa a função de calibração.

No programa completo, as funções de homing e calibração são executadas uma vez, durante a startup, e não são chamadas novamente.

Este snippet de código chama funções adicionais que não estão incluídas neste bloco.

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");
}

Rotina de calibração

Em seguida, temos nossa rotina de calibração. Esta função estenderá completamente seu atuador depois que a função de homing retraiu totalmente a unidade. O objetivo dessa rotina é contar os pulsos do sensor de uma extremidade do golpe do atuador para o outro. Esse valor é então salvo como variável maxstroke e usado para posicionamento. Esse código pode ser modificado para processar o feedback do potenciômetro, mapeando a tensão para a posição, em vez de contar pulsos.

Este snippet de código chama funções adicionais que não estão incluídas neste bloco.

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;
    }
  }
}

 

Laço

A função Void Loop () é o corpo principal do seu código Arduino. Qualquer coisa escrita na função loop () será executada repetidamente durante o programa.

Os programadores observam: Pessoalmente, acho mais fácil manter a função do loop o mais simples possível. Esta é a parte do código que ligará A maioria das outras funções no código, e não pode ser levantado sem o restante do código.

Nesta função de loop, o programa verifica o monitor serial para obter entrada, atualiza a posição de destino com seu comando e chama a função de movimento. A maioria das mensagens que o PLC envia para o monitor serial vem deste trecho de código.

Há um linha de código comentou Abaixo - Quando incluído, essa linha de código mapeia uma entrada de 0-100 para o atuador. Ou seja, Sua opinião agora é uma porcentagem do derrame do atuador. Essa linha também pode ser modificada para utilizar outras entradas em um formato mapeado semelhante.

Este snippet de código chama funções adicionais que não estão incluídas neste bloco.

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

A função de movimento controla a direção do movimento do atuador com base na posição alvo. Esta é a única função neste programa que diz ao motorista do motor para mover o atuador. 

Este snippet de código chama funções adicionais que não estão incluídas neste bloco.

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
  }
}

Sensor Read

A função de processamento do sensor lê valores do sensor, atualiza a posição e imprime as leituras do sensor para o monitor serial. Os comandos seriais podem ser usados ​​para depuração ou comentados para uma leitura menos usada.

Este snippet de código pode ser usado em qualquer programa e fornecerá uma leitura precisa dos pulsos de um sensor de 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;
}

Parada de motor e desativação

Interrompe o movimento do atuador e reerena a unidade para ler o monitor serial para outros comandos. Este é o único trecho de código neste programa que interromperá o movimento do atuador.

Este snippet de código pode ser usado em qualquer programa com qualquer atuador, pois apenas desliga o motor e altera o estado "ativo".

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

Fim da verificação de derrame

Verifica o final das condições do AVC e lida com o tempo limite. Quando o atuador atinge o final do acidente vascular cerebral e para de se mover, essa função aciona e redefinirá o atuador para novas entradas de comando, mesmo que o número de destino ainda não tenha sido alcançado.

Este snippet de código chama funções adicionais que não estão incluídas neste bloco.

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;
}
Share This Article
Tags: