Код с множественными условиями
В моем проекте есть 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