Программирование ПЛК для управления вашим приводом может быть одним из наиболее сложных проектов. Это требует проб и ошибок, тестирования и кучи терпения; Хотя результаты могут быть невероятно функциональными и полезными.
Код в этом уроке является полной программой для функциональной Серийный реагирующий приводПолем Использование последовательного монитора с помощью PLC позволит вам взаимодействовать с вашей системой Arduino с другими компьютерами, модулями или системами автоматизации.
Мы рассмотрим каждую функцию и раздел кода по ходу дела, чтобы вы могли извлечь конкретные функции из этого урока. Обратите внимание, что некоторые функции обращаются к другим функциям, которые не включены в тот же кодовый блок, поэтому вам нужно будет проверить свой код, чтобы убедиться, что он будет выполнять должным образом.
Полный код будет опубликован в конце этой статьи. Если вы хотите использовать полный код, скопируйте и вставьте его в программное обеспечение IDE.
Обзор программы
При инициативе эта программа будет домой и откалибровать ваши приводы, найдя концы движения для устройства. Затем он перемещает привод к средней точке удара, чтобы ждать дальнейших команд. Затем вы можете ввести число в свой последовательный монитор (с вашего ПК), и привод перейдет на эту позицию. Эта программа не запутывается в результате отключения в конце удара и работает с любым датчиком эффекта зала или приводом оптического датчика.
Список деталей
Компоненты с (*) включены в Arduino Starter Kit
- Arduino plc (Ун* или Мега 2560)
- Двигатель водителя (Высокий ток AD-MD6321) - 1 за привод.
- 12 В питания Для приводов (превышение совокупного розыгрыша сила).
- 1 макет, по мере необходимости*
- Перемычки для Arduino plc - каждый высокий ток MD потребует 6 Женские связи.
- 1-контактный провод для ПЛК* (M-F 10pc/PK)
- Комплект для перемычки (M-M, 75pc/pk; различные длины);
- Приводы с датчиками обратной связи и монтажные кронштейны при приводе, по мере необходимости. (Датчик эффекта зала или приводы оптического датчика, только для этого кода)
Функции
Резюме функций
-
void setup()
: Инициализируют контакты, начинают последовательную связь и запускают процедуры самонавочного и калибровки. -
void homingRoutine()
: Инициирует домашнее хозяйство, перемещает привод к полной ретракции и устанавливает минимальный ход. -
void calibrateActuator()
: Инициирует калибровку, перемещает привод к полному расширению и устанавливает максимальный ход. -
void loop()
: Проверяет на получение последовательного ввода, обновления целевой позиции и управляет движением привода. -
void movement()
: Контролирует направление движения привода на основе целевой позиции. -
void readSensor()
: Считает значения датчиков, положение обновлений и печатает чтения датчиков. -
void stopMotor()
: Останавливает движение привода. -
bool isEndOfStroke()
: Проверяет на окончание условий удара и обрабатывает тайм -ауты.
Переменные
Переменные в вашем коде должны быть вызваны до их использования. Лучшая практика программирования - назначить и установить ваши переменные в верхней части вашего кода, чтобы облегчить изменение или «настройку» ваших переменных.
Это переменные, используемые для этого кода, с комментарием, объясняющим их функцию и возможный диапазон.
//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
Настройка/инициализация
Void setup (); Функция - это базовая функция Arduino, которая инициализирует ПЛК, начинает последовательный монитор и назначает входы и выходы в качестве запрограммированных.
Эта функция настройки назначает 4 контакта в качестве входов или выходов, а затем запускает процедуры домашнего и калибровки.
void setup() {
pinMode(Xpin, OUTPUT);
pinMode(Rpin, OUTPUT);
pinMode(sensorPin, INPUT_PULLUP);
pinMode(sensorPin2, INPUT_PULLUP);
Serial.begin(115200);
homingRoutine();
calibrateActuator();
}
Распродажа по дому
Этот фрагмент кода является функцией для дома ваших приводов обратной связи. Этот код будет отозвать ваш привод до тех пор, пока не будет обнаружен окончание удара, и назначит эту позицию значение «0» - затем инициализирует функцию калибровки.
В полной программе функции домашнегожинг и калибровки запускаются один раз, во время запуска и не вызываются снова.
Этот кодовый фрагмент вызовов Дополнительные функции, которые не включены в этот блок.
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");
}
Калибровочная процедура
Далее у нас есть наша процедура калибровки. Эта функция полностью продлит ваш привод после того, как функция самонавочного дома полностью отозвала устройство. Цель этой процедуры - подсчитать датчик импульсирует от одного конца удара привода к другому. Это значение затем сохраняется как переменная MaxStroke и используется для позиционирования. Этот код может быть изменен для обработки обратной связи потенциометра путем отображения напряжения с положением, а не подсчета импульсов.
Этот кодовый фрагмент вызовов Дополнительные функции, которые не включены в этот блок.
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 () - это Основное тело вашего кода Arduino. Все, что записано в функции Loop (), будет выполняться неоднократно во время программы.
Программисты примечание: Лично мне легче всего сохранить функцию петли максимально простой. Это часть кода, которая позвонит большинство других функций в коде и не может быть поднятым без остальной части кода.
В этой функции цикла программа проверяет для последовательного монитора для ввода, обновляет целевую позицию с вашей командой и вызывает функцию движения. Большинство сообщений, которые ПЛК отправляет на последовательный монитор, поступают из этого фрагмента кода.
Есть строка кода прокомментирована Ниже - при включении эта строка кода отображает вход 0-100 в привод. Т.е. Ваш вклад в настоящее время составляет процент от удара привода. Эта строка также может быть изменена для использования других входов в аналогичном формате.
Этот фрагмент кода вызывает дополнительные функции, которые не включены в этот блок.
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");
}
}
Движение
Функция движения контролирует направление движения привода на основе целевого положения. Это единственная функция в этой программе, которая говорит двигателю двигать привод.
Этот фрагмент кода вызывает дополнительные функции, которые не включены в этот блок.
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;
}
Полный код
//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;
}