Код с множественными условиями
В моем проекте есть 4 поплавковых выключателя, 2 клапана и 3 насоса. Я создал скетч и загрузил его. Но мой проект все время держит все «ВЫХОД» на «ВЫСОКОМ». Не могу найти, в чем проблема, можете помочь?
Это мой набросок:
#include <DS3231.h>
#include <Wire.h>
#include <LiquidCrystal.h>
DS3231 rtc(SDA, SCL);
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int x = 45;
int y = 47;
int z = 41;
int w = 43;
int WaterIn = 40;
int WaterOut = 42;
int Pump1 = 25;
int Pump2 = 24;
int Pump3 = 27;
void setup()
{
rtc.begin();
lcd.begin(16,2);
// Запускаем интерфейс I2C
Wire.begin();
// Запускаем последовательный интерфейс
Serial.begin(9600);
//rtc.setDOW(ВТОРНИК); // Установить день недели на ВОСКРЕСЕНЬЕ
//rtc.setTime(1, 15, 0); // Установите время на 12:00:00 (24-часовой формат)
//rtc.setDate(6, 6, 2017); // Месяц день год
//Настройка контактов поплавкового переключателя
pinMode (x, INPUT);
pinMode (y, INPUT);
pinMode (z, INPUT);
pinMode (w, INPUT);
//Настройка ирригационных клапанов
pinMode(WaterIn, OUTPUT); //клапан полива "in"
pinMode(WaterOut, OUTPUT); //ирригационный "выходной" клапан
//Настройка насосов
pinMode(Pump1, OUTPUT);
pinMode(Pump2, OUTPUT);
pinMode(Pump3, OUTPUT);
} // конец настройки
void Calendar(){
lcd.setCursor(0,0);
lcd.print("Real Time Clock ");
lcd.setCursor(0,1);
lcd.print("Time: ");
lcd.print(rtc.getTimeStr());
delay(1000);
lcd.setCursor(0,1);
lcd.print("Date: ");
lcd.print(rtc.getDateStr());
delay(1000);
lcd.setCursor(0,1);
lcd.print("Day: ");
lcd.print(rtc.getDOWStr());
lcd.print(" ");
delay(1000);
lcd.setCursor(0,1);
lcd.print("Temp: ");
lcd.print(rtc.getTemp());
lcd.print(" C");
lcd.print(" ");
delay(1000);
} // конец настройки
void Valves(){
int pinToTurnHigh = 24;
if ((x == HIGH) && (y == HIGH) && (z == HIGH) && (w == LOW)) //Условие 1
{
pinToTurnHigh = WaterIn;
pinToTurnHigh = Pump1;
pinToTurnHigh = Pump2;
pinToTurnHigh = Pump3;
}
if ((x == LOW) && (y == LOW) && (z == HIGH) && (w == HIGH)) //Условие 2
{
pinToTurnHigh = WaterOut;
}
resetAllPumpValve();
digitalWrite(pinToTurnHigh, HIGH);
} // конец настройки
void resetAllPumpValve(){
digitalWrite(WaterIn, LOW);
digitalWrite(Pump1, LOW);
digitalWrite(Pump2, LOW);
digitalWrite(Pump3, LOW);
digitalWrite(WaterOut, LOW);
} // конец настройки
void loop()
{
Calendar();
Valves();
resetAllPumpValve();
} // конец цикла
@user56886, 👍2
Обсуждение3 ответа
Лучший ответ:
Крисл провел отличный анализ вашей программы и проблем
в нем. Я просто хотел бы добавить пункт о том, как справиться с
Функция Календарь()
. Вам было предложено ознакомиться с
Учебное пособие по Blink Without Delay по Arduino, и я поддерживаю это предложение.
Это действительно первое, что вы должны сделать.
Однако после того, как вы изучите этот учебник и попытаетесь применить его к ваша конкретная проблема, вы заметите, что ваша несколько сложнее чем мигать светодиодом. Причина, по которой это сложнее, заключается в том, что вы должны управлять более сложное состояние системы. Светодиод имеет только два состояния, и он периодически переходит между ними как:
НИЗКИЙ
→ВЫСОКИЙ
→НИЗКИЙ
Вместо этого ваша подпрограмма отображения будет иметь четыре состояния и выполнять следующие действия: переходы:
SHOW_TIME
→SHOW_DATE
→SHOW_DAY
→SHOW_TEMP
→SHOW_TIME
Подходящая концепция программирования для работы с такими системами
называется «конечным автоматом», и я предлагаю вам также изучить это
руководство по конечному автомату. Этот учебник предлагает очень
общая управляющая структура switch
/case
для программирования FSM.
Однако, если есть какое-то поведение, общее для всех состояний (в вашем случае:
тот факт, что каждый из них длится ровно одну секунду), вы можете учесть это
поведение вне конструкции switch
/case
.
Вот что я бы предложил в качестве неблокирующего Calendar()
. Во-первых, поставить
это в верхней части программы вместе с другими константами:
const LCD_DISPLAY_TIME = 1000; // display each item for 1,000 ms
Затем setup()
, сразу после инициализации LCD:
lcd.setCursor(0, 0);
lcd.print("Real Time Clock ");
Как правило, setup()
— это правильное место для всего, что только
нужно сделать один раз, в начале программы.
И, наконец, функция Calendar()
, которая будет вызываться из loop()
:
void Calendar()
{
// Ничего не делать, если не пришло время обновить дисплей.
static uint32_t last_change = -LCD_DISPLAY_TIME;
if (millis() - last_change < LCD_DISPLAY_TIME) return;
last_change += LCD_DISPLAY_TIME;
// Переход к следующему элементу: Время -> Дата -> День -> Температура -> Время
lcd.setCursor(0, 1);
static enum { SHOW_TEMP, SHOW_TIME, SHOW_DATE, SHOW_DAY } state;
switch (state) {
case SHOW_TEMP:
lcd.print("Time: ");
lcd.print(rtc.getTimeStr());
state = SHOW_TIME;
break;
case SHOW_TIME:
lcd.print("Date: ");
lcd.print(rtc.getDateStr());
state = SHOW_DATE;
break;
case SHOW_DATE:
lcd.print("Day: ");
lcd.print(rtc.getDOWStr());
state = SHOW_DAY;
break;
case SHOW_DAY:
lcd.print("Temp: ");
lcd.print(rtc.getTemp());
lcd.print(" C");
state = SHOW_TEMP;
break;
}
lcd.print(" "); // накладка
}
Непонятно, что на самом деле должен делать код (какого поведения вы пытаетесь добиться), поэтому я рассмотрю только очевидные проблемы программирования.
Следующие объявления в глобальной области действия кажутся номерами контактов:
int x = 45; int y = 47; int z = 41; int w = 43; int WaterIn = 40; int WaterOut = 42; int Pump1 = 25; int Pump2 = 24; int Pump3 = 27;
Но в функции
Valves()
вы обращаетесь с ними так, как будто они представляли значение этих выводов. Это не относится к делу. Если вы хотите прочитать состояние контакта, вы не можете сделатьif ((x == HIGH) && (y == HIGH) && (z == HIGH) && (w == LOW))
Вы просто сравниваете номер контакта с
HIGH
, который определяется как1
. Это не может работать. Вы должны прочитать контакт, используя его вывод, который выглядит следующим образом:if( digitalRead(x) == HIGH && digitalRead(y) == HIGH && digitalRead(z) == HIGH && digitalRead(w) == HIGH)
И это можно сократить до:
if( digitalRead(x) && digitalRead(y) && digitalRead(z) && digitalRead(w) )
В упомянутых операторах if вы устанавливаете переменную несколько раз, ничего с ней не делая. Любая последовательная запись в эту переменную перезапишет предыдущее значение. После блока кода
pinToTurnHigh = WaterIn; pinToTurnHigh = Pump1; pinToTurnHigh = Pump2; pinToTurnHigh = Pump3;
Переменная
pinToTurnHigh
равна 27 (Pump3). Значения, которые вы присвоили непосредственно перед этой строкой, теряются. Вам действительно нужно что-то делать с переменной, иначе первые 3 строки этого блока не имеют смысла.В большинстве случаев не рекомендуется использовать переменные с изменяющимися значениями для обработки номеров выводов, поскольку это затруднит понимание кода. Схема в основном зашита, она не изменится. Таким образом, нет необходимости присваивать номера выводов изменяющейся переменной. Вместо этого вы можете напрямую делать то, что вам нужно, с постоянным номером контакта. (На самом деле оптимизатор кода, идущий в комплекте с компилятором, может оптимизировать неизменяемые переменные с номером контакта, чтобы вы прописали их в своем коде, но в самом коде их может и не быть.) Тут вроде бы и нужно чтобы записать значение
HIGH
для всех упомянутых контактов. Вы можете сделать это напрямую:digitalWrite(WaterIn, HIGH); digitalWrite(Pump1, HIGH); digitalWrite(Pump2, HIGH); digitalWrite(Pump3, HIGH);
Здесь важно понимать, что
digitalWrite()
одновременно работает только с одним выводом. Если вы хотите установить с ним несколько контактов, вам придется вызывать его несколько раз с соответствующими номерами контактов.Теперь время вашего кода: функция
Calendar()
заблокирует остальную часть кода примерно на 4 секунды, потому что вы использовали так многоdelay()
. В это время Arduino ни на что не реагирует. Переключатели тоже ничего не сделают (поскольку Arduino занят ожиданием вdelay()
вместо проверки переключателей). Код, который вызывается после функцииCalendar()
, не содержитdelay()
, поэтому он будет работать очень быстро. В функцииValves()
вы сначала сбрасываете все насосы и клапаны наLOW
. Затем вы пишетеHIGH
на один контакт (как упоминалось выше, я предполагаю, что вы хотели написать их все при условии). После этого программа вернется к функцииloop()
и снова выполнит функциюresetAllPumpValve()
, которая установит для всех выходов значениеLOW
.В целом это означает, что Arduino возится с функцией
Calendar()
, не делая ничего другого. Затем он будет сбрасывать, устанавливать и снова сбрасывать контакты. Поскольку там нет временного кода, это произойдет очень быстро. Таким образом, контакты должны бытьLOW
большую часть времени и включаться примерно каждые 4 секунды в течение небольшого промежутка времени (небольшое количество миллисекунд, так как это примерно время для выполненияdigitalWrite). ()
или его братья и сестры). При этом моторы или ценности вряд ли успеют сдвинуться с места.Чтобы решить эту проблему, вы можете изучить пример
BlinkWithoutDelay
, поставляемый с Arduino IDE. Он показывает неблокирующий стиль кодирования, который будет что-то делать, только если пришло время это сделать. Так же, как когда вы печете пиццу. Вы не будете сидеть перед духовкой и терпеливо ждать, пока пицца полностью пропечется. Вы поставите ее в духовку и регулярно (может быть, каждые 5 минут) смотрите, готова ли пицца к употреблению. Тем временем вы займетесь другими делами. Это то, что там делается с помощью функцииmillis()
, которая вернет количество миллисекунд, прошедших с момента запуска программы. Вы можете изучить пример и узнать больше о нем в Google. Это неоднократно обсуждалось в Интернете.
Это пока. Чтобы действительно помочь вам с логикой программы, вам сначала нужно правильно понять эти части.
Развлекайтесь во время обучения :-)
**Выдающийся** анализ проблем с этим кодом. (проголосовал) Я прошел часть пути и развел руками. Я не мог понять, что он должен был делать, и видел так много проблем, что отказался от попыток их всех решить. Вы были очень внимательны и терпеливы, и дали четкие объяснения. Ура!, @Duncan C
Я бы добавил к ответу Крисла, сказав, что если вы хотите сделать код более компактным, вы можете представить 4 логические переменные одним байтом,
например, w HIGH + x HIGH + y LOW + z LOW = 1100.
например w HIGH + x HIGH + y LOW + z HIGH = 1101.
Тогда было бы проще реализовать оператор switch
вместо if( digitalRead(x) == HIGH && digitalRead(y) == HIGH && digitalRead (z) == HIGH && digitalRead(w) == HIGH)
и т. д.
Например:
byte state;
void setup(){}
void loop(){
// вставляем код для чтения состояния
switch (state)
{
case 1:
// вставляем код для запуска, если state = 0b0001;
break;
case 13:
// вставляем код для запуска, если state = 0b1101;
break;
default: // код для запуска, если состояние не соответствует ни одному из случаев
}
// вставляем другой код
}
Если вы хотите использовать цифровые контакты 30-37 ATmega, вы можете использовать команду порта AVR PINC
для одновременного считывания всех состояний HIGH/LOW с помощью одной инструкции. Я оставляю это вам в качестве упражнения!
Это может затруднить чтение и понимание программы. Бывают ситуации, когда ваше предложение сделает программу _более_ читаемой, например, если необходимо рассмотреть все 16 случаев и нет простого способа факторизации случаев. Но если нужно обработать только два или три случая, if
/else
, скорее всего, будет более компактным и более читаемым., @Edgar Bonet
Совершенно верно, но я написал «если вы хотели сделать код более компактным», не обязательно читабельным., @MichaelT
Я исправил Calendar(), он работает отлично., @user56886
- Что лучше использовать: #define или const int для констант?
- Какой стандарт C++ поддерживает язык Arduino?
- Как отправить групповое сообщение нескольким получателям с помощью GSM900 и Arduino Mega2560?
- Существуют ли какие-либо стандартные способы сообщения об ошибке времени выполнения?
- Стандарты имен переменных, например, лучший способ отправки motion_detect=true
- когда я закончил кодировать свои 3 двигателя постоянного тока, все эти ошибки накопились, одна из них сказала, что в программе есть случайный «/ 302».
- Сомнение в части кодировании мигания без задержки
- Ставите ли вы разрыв после последнего случая в переключателе?
используйте serial.print() для отображения отладочной информации на последовательной консоли .... убедитесь, что поток программы соответствует вашим ожиданиям., @jsotola
Я новичок в ардуино, можете объяснить? Вы имеете в виду заменить lcd.print() на serial.print()?, @user56886
Нет, скорее, в дополнение к командам
lcd.print()
. Таким образом, вы можете видеть на последовательном мониторе, куда идет ваш код во время выполнения.delay()
также может пригодиться., @sa_leinad