Проблема: Срабатывание концевого выключателя для остановки двигателя постоянного тока.
Я хочу создать проект по управлению автоматическими воротами и гаражными воротами через приложение Android и подключение через модуль Bluetooth HC-05. Каждые ворота и гаражные ворота оснащены двумя концевыми выключателями (расположенными на каждом конце хода) для остановки двигателя постоянного тока.
Я написал код и провожу исследования, но двигатель постоянного тока останавливается только тогда, когда я нажимаю на него, если я отпускаю его снова, он автоматически запускается. Я хочу, чтобы он останавливался, пока модуль Bluetooth снова не отправит код. Я подключил понижающий резистор к концевому выключателю.
Кто-нибудь может мне помочь?
Вот мой текущий код, спасибо
// Двигатель для ворот (MotorA)
int IN1 = 2;
int IN2 = 3;
int EnableMotorA = 9;
// Мотор для гаражных ворот (MotorB)
int IN3 = 4;
int IN4 = 5;
int EnableMotorB = 10;
const int LimitSwitch1 = A1;
const int LimitSwitch2 = A2;
const int LimitSwitch3 = A3;
const int LimitSwitch4 = A4;
int state;
void setup(){
pinMode(IN1,OUTPUT);
pinMode(IN2,OUTPUT);
pinMode(EnableMotorA,OUTPUT);
pinMode(IN3,OUTPUT);
pinMode(IN4,OUTPUT);
pinMode(EnableMotorB,OUTPUT);
pinMode(LimitSwitch1, INPUT);
pinMode(LimitSwitch2, INPUT);
pinMode(LimitSwitch3, INPUT);
pinMode(LimitSwitch4, INPUT);
digitalWrite(LimitSwitch1, LOW);
digitalWrite(LimitSwitch2, LOW);
digitalWrite(LimitSwitch3, LOW);
digitalWrite(LimitSwitch4, LOW);
Serial.begin(9600);
}
void loop(){
if(Serial.available() > 0){
state = Serial.read();
}
if(state == 'A') {
OpenGate();
if(digitalRead(LimitSwitch2) == HIGH){
StopMotorA();
}
}
else if(state == 'B') {
CloseGate();
if(digitalRead(LimitSwitch1) == HIGH){
StopMotorA();
}
}
else if(state == 'C') {
OpenGarageDoor();
if(digitalRead(LimitSwitch4) == HIGH){
StopMotorB();
}
}
else if(state == 'D') {
CloseGarageDoor();
if(digitalRead(LimitSwitch3) == HIGH){
StopMotorB();
}
}
}
//******************** Управление двигателем А *******************
void OpenGate(){
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
analogWrite(EnableMotorA, 250);
}
void CloseGate(){
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(EnableMotorA, 250);
}
void StopMotorA(){
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
analogWrite(EnableMotorA, 0);
}
//******************** Управление двигателем B *******************
void OpenGarageDoor(){
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
analogWrite(EnableMotorB, 250);
}
void CloseGarageDoor(){
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
analogWrite(EnableMotorB, 250);
}
void StopMotorB(){
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
analogWrite(EnableMotorB, 0);
}
//*********************************************************
1 ответ
Лучший ответ:
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я не тестировал код в этом ответе, но надеюсь, что проблем не возникнет.
В вашем коде есть серьезная проблема: что происходит, когда одни ворота движутся, а вы запускаете другое движение? (ответ: что-то плохое). Эту проблему проектирования можно решить, используя стандартный способ проектирования такого рода конечных автоматов.
В вашем случае небольшой конечный автомат — это то, что лучше всего подходит для вашей проблемы. Лучше дайте вашим состояниям осмысленные имена, не так ли?
Итак, для начала давайте определим состояния и способы перехода через них.
У вас в основном состояние ожидания, открытые ворота, закрытые ворота, открытые ворота гаража, закрытые ворота гаража. Что вам следует делать в этих состояниях, как при въезде в состояние, так и во время нахождения в состоянии? Вот таблица для обобщения
State | Enter | During time
----------------------------------------------
Idle | Stop motors | -
OpenGate | Start A dir 1 | -
CloseGate | Start A dir 2 | -
OpenGarage | Start B dir 1 | -
CloseGarage | Start B dir 2 | -
Переходы довольно просты. Выходите из режима ожидания, когда получаете (действительную) команду, выходите из других режимов, когда достигаете конечной остановки.
Реализация довольно стандартная:
// Двигатель для ворот (MotorA)
const byte IN1 = 2;
const byte IN2 = 3;
const byte EnableMotorA = 9;
// Мотор для гаражных ворот (MotorB)
const byte IN3 = 4;
const byte IN4 = 5;
const byte EnableMotorB = 10;
const byte LimitSwitch1 = A1;
const byte LimitSwitch2 = A2;
const byte LimitSwitch3 = A3;
const byte LimitSwitch4 = A4;
const byte State_Idle = 0;
const byte State_OpenGate = 1;
const byte State_CloseGate = 2;
const byte State_OpenGarage = 3;
const byte State_CloseGarage = 4;
byte currentState;
void setup(){
pinMode(IN1,OUTPUT);
pinMode(IN2,OUTPUT);
pinMode(EnableMotorA,OUTPUT);
pinMode(IN3,OUTPUT);
pinMode(IN4,OUTPUT);
pinMode(EnableMotorB,OUTPUT);
pinMode(LimitSwitch1, INPUT);
pinMode(LimitSwitch2, INPUT);
pinMode(LimitSwitch3, INPUT);
pinMode(LimitSwitch4, INPUT);
Serial.begin(9600);
currentState = 0xFF; // Принудительное изменение для перехода в состояние ожидания
}
void loop(){
// Проверяем, нужно ли нам изменить состояние
byte nextState = currentState;
switch (currentState)
{
case State_Idle:
{ // Если мы получили действительную команду, начинаем операцию
if(Serial.available() > 0)
{
switch (Serial.read())
{
case 'A':
nextState = State_OpenGate;
break;
case 'B':
nextState = State_CloseGate;
break;
case 'C':
nextState = State_OpenGarage;
break;
case 'D':
nextState = State_CloseGarage;
break;
default:
// Здесь вы можете сообщить о том, что была выполнена неверная команда
break;
}
}
}
break;
case State_OpenGate:
if(digitalRead(LimitSwitch2)) // == HIGH не обязательно
nextState = State_Idle;
break;
case State_CloseGate:
if(digitalRead(LimitSwitch1)) // == HIGH не обязательно
nextState = State_Idle;
break;
case State_OpenGarage:
if(digitalRead(LimitSwitch4)) // == HIGH не обязательно
nextState = State_Idle;
break;
case State_CloseGarage:
if(digitalRead(LimitSwitch3)) // == HIGH не обязательно
nextState = State_Idle;
break;
default:
nextState = State_Idle;
break;
}
// Если произошло изменение, переключить состояние и активировать условие входа
if (nextState != currentState)
{
currentState = nextState; // Перейти в новое состояние
switch (currentState)
{ // Переход в новое состояние
case State_Idle:
// Остановить двигатель А
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
analogWrite(EnableMotorA, 0);
// Остановить двигатель B
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
analogWrite(EnableMotorB, 0);
break;
case State_OpenGate:
// Запустить двигатель А
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
analogWrite(EnableMotorA, 250);
break;
case State_CloseGate:
// Запустить двигатель А
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(EnableMotorA, 250);
break;
case State_OpenGarage:
// Запустить двигатель B
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
analogWrite(EnableMotorB, 250);
break;
case State_CloseGarage:
// Запустить двигатель B
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
analogWrite(EnableMotorB, 250);
break;
}
}
}
(Примечание: я изменил некоторые типы, потому что ваш бедный 8-битный uC попросил меня не усложнять его вычисления без необходимости)
Одной из модификаций вашего алгоритма является разъединение двух циклов, поскольку они кажутся разъединенными.
// Двигатель для ворот (MotorA)
const byte IN1 = 2;
const byte IN2 = 3;
const byte EnableMotorA = 9;
// Мотор для гаражных ворот (MotorB)
const byte IN3 = 4;
const byte IN4 = 5;
const byte EnableMotorB = 10;
const byte LimitSwitch1 = A1;
const byte LimitSwitch2 = A2;
const byte LimitSwitch3 = A3;
const byte LimitSwitch4 = A4;
const byte StateA_Idle = 0;
const byte StateA_Open = 1;
const byte StateA_Close = 2;
byte currentStateA;
const byte StateB_Idle = 0;
const byte StateB_Open = 1;
const byte StateB_Close = 2;
byte currentStateB;
void setup(){
pinMode(IN1,OUTPUT);
pinMode(IN2,OUTPUT);
pinMode(EnableMotorA,OUTPUT);
pinMode(IN3,OUTPUT);
pinMode(IN4,OUTPUT);
pinMode(EnableMotorB,OUTPUT);
pinMode(LimitSwitch1, INPUT);
pinMode(LimitSwitch2, INPUT);
pinMode(LimitSwitch3, INPUT);
pinMode(LimitSwitch4, INPUT);
Serial.begin(9600);
currentStateA = 0xFF; // Принудительное изменение для перехода в состояние ожидания
currentStateB = 0xFF; // Принудительное изменение для перехода в состояние ожидания
}
void loop()
{
byte readChar = 0;
if(Serial.available() > 0)
readChar = Serial.read();
stateMachineA(readChar);
stateMachineB(readChar);
}
void stateMachineA(byte readChar)
{
// Проверяем, нужно ли нам изменить состояние
byte nextState = currentStateA;
switch (currentStateA)
{
case StateA_Idle:
{ // Если мы получили действительную команду, начинаем операцию
if(readChar > 0)
{
switch (readChar)
{
case 'A':
nextState = StateA_Open;
break;
case 'B':
nextState = StateA_Close;
break;
default:
// Здесь вы можете сообщить о том, что была выполнена неверная команда
break;
}
}
}
break;
case StateA_Open:
if(digitalRead(LimitSwitch2)) // == HIGH не обязательно
nextState = StateA_Idle;
break;
case StateA_Close:
if(digitalRead(LimitSwitch1)) // == HIGH не обязательно
nextState = StateA_Idle;
break;
default:
nextState = StateA_Idle;
break;
}
// Если произошло изменение, переключить состояние и активировать условие входа
if (nextState != currentStateA)
{
currentStateA = nextState; // Перейти в новое состояние
switch (currentStateA)
{ // Переход в новое состояние
case StateA_Idle:
// Остановить двигатель А
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
analogWrite(EnableMotorA, 0);
break;
case StateA_Open:
// Запустить двигатель А
digitalWrite(IN1, LOW);
digitalWrite(IN2, HIGH);
analogWrite(EnableMotorA, 250);
break;
case StateA_Close:
// Запустить двигатель А
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(EnableMotorA, 250);
break;
}
}
}
void stateMachineB(byte readChar)
{
// Проверяем, нужно ли нам изменить состояние
byte nextState = currentStateB;
switch (currentStateB)
{
case StateB_Idle:
{ // Если мы получили действительную команду, начинаем операцию
if(readChar > 0)
{
switch (readChar)
{
case 'C':
nextState = StateB_Open;
break;
case 'D':
nextState = StateB_Close;
break;
default:
// Здесь вы можете сообщить о том, что была выполнена неверная команда
break;
}
}
}
break;
case StateB_Open:
if(digitalRead(LimitSwitch4)) // == HIGH не обязательно
nextState = StateB_Idle;
break;
case StateB_Close:
if(digitalRead(LimitSwitch3)) // == HIGH не обязательно
nextState = StateB_Idle;
break;
default:
nextState = StateB_Idle;
break;
}
// Если произошло изменение, переключить состояние и активировать условие входа
if (nextState != currentStateB)
{
currentStateB = nextState; // Перейти в новое состояние
switch (currentStateB)
{ // Переход в новое состояние
case StateB_Idle:
// Остановить двигатель B
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
analogWrite(EnableMotorB, 0);
break;
case StateB_Open:
// Запустить двигатель B
digitalWrite(IN3, LOW);
digitalWrite(IN4, HIGH);
analogWrite(EnableMotorB, 250);
break;
case StateB_Close:
// Запустить двигатель B
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
analogWrite(EnableMotorB, 250);
break;
}
}
}
В этой второй реализации есть два параллельных конечных автомата (совместно использующих только последовательный вход). Это позволит вам управлять обоими воротами одновременно (например, когда ворота закрываются, вы можете открыть гараж)
О, боже! Это прекрасно работает для моей схемы. Я планировал сделать ее похожей на вашу вторую реализацию. Спасибо большое! :D, @Husna Amiliansyah
@HusnaAmiliansyah ты уже проверила? Работает? Правда? :-P, @frarugi87
конечно, работает xD, я проверил на макетной плате, @Husna Amiliansyah
кстати, константный байт StateA_Idle = 0; константный байт StateA_Open = 1; константный байт StateA_Close = 2; что означает эта часть?, @Husna Amiliansyah
@HusnaAmiliansyah есть куча шуток о программном обеспечении, которое запускается в первый раз ([например, это](http://s2.quickmeme.com/img/bd/bd3f1533188649c2212e400515e73479eba5ba40778d7299961e6d3889bea03e.jpg)) ;) в любом случае эти строки должны давать осмысленные имена состояниям. Легче написать (и понять) nextstate = StateA_Open;
, чем nextstate = 1;
. Для того, чтобы сделать это, у вас обычно есть три способа: использовать #define
, использовать enum
или использовать некоторые константы. Я предпочитаю третий, потому что для меня это самый простой способ принудительно задать 8-битный размер переменной состояния и значений, @frarugi87
Хорошо, понял!! Большое спасибо за помощь!! :) Кстати, извините, я не могу проголосовать за этот ответ, потому что у меня недостаточно баллов:(, @Husna Amiliansyah
@HusnaAmiliansyah не волнуйтесь, самое главное, что вы это поняли ;) и отметили ответ как принятый, чтобы другие люди, просматривающие ваш вопрос, могли понять, что ответ «дает ответ» на него., @frarugi87
Ладно, я уже давно это принял :D. Кстати, у меня есть еще один вопрос... Если я хочу добавить зуммер внутри состояний, нужно ли мне менять 'byte'? И есть ли какие-либо ограничения на количество состояний в одной функции (например, в функции statemachineA)?, @Husna Amiliansyah
Тип байта может содержать значения от 0 до 255, следовательно, если вам нужно закодировать 257 состояний, вам нужно как минимум 2-байтовое int, в то время как для 65537 состояний вам нужно как минимум 3 байта. Но IMHO, когда вы превышаете 20-30 состояний, вы заходите слишком далеко. Так что теоретически предела нет (вам просто нужно сделать переменную, содержащую состояние, больше), но любой конечный автомат с более чем несколькими десятками состояний слишком велик для эффективного управления, и, возможно, переосмысление автомата (например, как серии вложенных автоматов) может стать лучшим решением, @frarugi87
- Управление двигателем Arduino Uno с помощью 2 реле, 2 концевых выключателей и Bluetooth. Вперед Назад Стоп
- Заменить механический переключатель электрическим переключателем или реле.
- Arduino сбрасывается или зависает во время работы, только когда подключены двигатели постоянного тока
- Можно ли объединить модуль Bluetooth h HC-05 и уменьшенную плату Arduino ATtiny45/85 для управления двигателем, светодиодами или другими компонентами?
- Как управлять коммутатором (proteus) с помощью Bluetooth-карты (HC-05) и arduino?
- Энкодер соединен с валом двигателя. Энкодер выдает более высокие значения, чем ожидалось.
- Можно ли измерить скорость акселерометром? Насколько точно?
- Управление скоростью вентилятора с помощью библиотеки Arduino PID
не запускайте двигатель после замыкания переключателя. запускайте его только с помощью BT. .... нарисуйте блок-схему на бумаге, затем создайте код, который имитирует блок-схему., @jsotola
Извините, но что такое BT? Я сделал блок-схему, она выглядит так: двигатель постоянного тока вращается по часовой стрелке -> нажат ли концевой выключатель? -> если да, остановите двигатель постоянного тока. И я думал, что сделал это таким образом, @Husna Amiliansyah
BT ... bluetooth ..... вы получаете свои состояния из последовательного соединения. Это нормально, но вам также нужны внутренние состояния. Например:
состояние E --- статус: все остановлено .... вход из состояния B при активации концевого выключателя (и из других)
, @jsotolaвам нужно как минимум 4 отдельных состояния
стоп
... дверь открыта, дверь закрыта, остановка двери при открытии, остановка двери при закрытии. .... выполните поиск в Интернете по запросуstate machine
илиarduino state machine
, @jsotolaсостояние ссылается на символ, полученный от BT. Включен ли оператор if для концевого выключателя также в состояние остановки? хорошо, я проведу исследование по этому вопросу, спасибо, @Husna Amiliansyah