// Подключаем библиотеку для работы с жидкокристаллическим
// экраном (англ. Liquid Crystal Display или просто LCD)
#include <LiquidCrystal.h>
// Объявляем объект, для управления дисплеем. Для его создания
// необходимо указать номера пинов, к которым он подключен в
// порядке: RS E DB4 DB5 DB6 DB7
LiquidCrystal lcd(PIN_LCD_RS, PIN_LCD_E, PIN_LCD_DB4, PIN_LCD_DB5, PIN_LCD_DB6, PIN_LCD_DB7);
// Вспомогательные переменные
// - Состояние нажатий клавиш, по умолчанию не нажаты
boolean button_state[3] = { false, false, false };
// - Определение открытого резервуара
byte open_servo_id = 0;
// Состояние системы
int currentState = STATE_INIT;
boolean isFirstCall = true; // true если состояние проходит первый цикл
// Заданные величины
int levelOne = 0;
int levelRes = 0;
int timeSeconds = 0;
// Функция установки запускается один раз при нажатии reset или включения питания на плате
void setup() {
// Инициализация пинов как Input, Output
// Светодиоды
pinMode(PIN_LED_POWER, OUTPUT);
// ...
// Кнопки
pinMode(PIN_BUTTON_PLUS, INPUT);
// ...
// Сервоприводы
pinMode(PIN_SERVO_1, OUTPUT);
pinMode(PIN_SERVO_2, OUTPUT);
// Мотор
pinMode(PIN_MIX, OUTPUT);
// Датчики уровня воды
pinMode(PIN_WATER_LEVEL_1, INPUT);
// ...
// начинаем работу с экраном. Сообщаем объекту количество
// строк и столбцов. Опять же, вызывать pinMode не требуется:
// функция begin сделает всё за нас
lcd.begin(16, 2);
}
// Функция loop выполняется снова и снова
void loop() {
// На всякий случай проверяем переполнение резервуара
if (isWaterLevel(PIN_WATER_LEVEL_DANGER)) {
changeStatus(STATE_DANGER); // устанавливаем соответствующее состояние
}
switch (currentState) {
case STATE_INIT: // инициализации
stateInit();
break;
case STATE_ENTER_FIRST_LEVEL: // ввод первого уровня жидкости
stateEnterFirstLevel();
break;
case STATE_ENTER_SECOND_LEVEL: // ввод результирующего уровня
stateEnterSecondLevel();
break;
case STATE_ENTER_TIME_MIX: // ввод времени перемешивания
stateEnterTimeMix();
break;
case STATE_FILL: // заливаем
stateFill();
break;
case STATE_MIX: // мешаем
stateMix();
break;
case STATE_DANGER: // жидкости слишком много
stateDanger();
break;
}
isFirstCall = false;
}
void stateInit() {
lcd.clear();
lcd.print("Загрузка...");
// включаем светодиод, что устройство работает
digitalWrite(PIN_LED_POWER, HIGH);
delay(2000); // 2 sec
changeStatus(STATE_ENTER_FIRST_LEVEL);
}
void stateEnterFirstLevel() {
if (isFirstCall) {
lcd.clear();
lcd.print("Ввод ур 1 ж");
}
if (isClicked(BUTTON_PLUS)) {
levelOne++;
} else if (isClicked(BUTTON_MINUS)) {
levelOne--;
}
if (levelOne < 1) levelOne = 1;
if (levelOne > 10) levelOne = 10;
lcd.setCursor(0, 1);
lcd.print(levelOne);
if (isClicked(BUTTON_ACCEPT)) {
changeStatus(STATE_ENTER_SECOND_LEVEL);
}
}
void stateEnterSecondLevel() { ... }
void stateEnterTimeMix() { ... }
void stateFill() {
if (isFirstCall) {
lcd.clear();
lcd.print("Подача жидкости");
// включаем светодиод подачи воды
digitalWrite(PIN_LED_FILL, HIGH);
// открываем 1 клапон
digitalWrite(PIN_SERVO_1, HIGH);
open_servo_id = 1;
}
if (open_servo_id == 1 && isWaterLevel(getPinFromLevel(levelOne))) {
// закрываем 1 клапон
digitalWrite(PIN_SERVO_1, LOW);
// открываем 2 клапон
digitalWrite(PIN_SERVO_2, HIGH);
open_servo_id = 2;
} else if (open_servo_id == 2 && isWaterLevel(getPinFromLevel(levelOne))) {
// закрываем 2 клапон
digitalWrite(PIN_SERVO_2, LOW);
open_servo_id = 0;
// выключаем светодиод подачи воды
digitalWrite(PIN_LED_FILL, LOW);
changeStatus(STATE_MIX); // мешаем
}
}
void stateMix() {
if (isFirstCall) {
lcd.clear();
lcd.print("Перемешивание");
lcd.setCursor(0, 1);
// включаем светодиод перемешивания
digitalWrite(PIN_LED_MIX, HIGH);
// включаем мотор
digitalWrite(PIN_MIX, HIGH);
}
lcd.setCursor(0, 1);
lcd.print(timeSeconds);
delay(1000); // 1 sec
timeSeconds--;
if (timeSeconds < 0) {
// выключаем мотор
digitalWrite(PIN_MIX, LOW);
// выключаем светодиод перемешивания
digitalWrite(PIN_LED_MIX, LOW);
changeStatus(STATE_INIT);
}
}
void stateDanger() {
lcd.clear();
lcd.print("Превышен уровень жидкости");
lcd.setCursor(0, 1);
lcd.print("Устройство будет переведено в начальное состояние");
// выключаем светодиоды и мотор, а также закрываем сервоприводы
digitalWrite(PIN_LED_POWER, LOW);
digitalWrite(PIN_LED_FILL, LOW);
digitalWrite(PIN_LED_MIX, LOW);
digitalWrite(PIN_SERVO_1, LOW);
digitalWrite(PIN_SERVO_2, LOW);
digitalWrite(PIN_MIX, LOW);
delay(3000); // 3 sec
changeStatus(STATE_INIT);
}
void changeStatus(int status) {
currentState = status;
isFirstCall = true;
}
// возвращает пин в соответствии с уровнем
int getPinFromLevel(int level) { ... }
// Функция возвращает true, если уровень жидкости превысил заданный уровень
boolean isWaterLevel(int pinLevel) {
return digitalRead(pinLevel) == HIGH;
}
// Функция возвращает true, если пользователь отпустил кнопку
boolean isClicked(byte buttonId) { ... }