Ардуированная кофемашина

image
Привет, обитатели ХабраХабр!

Возможно, по сути статьи я ошибся ресурсом, за мое 9 летнее отсутствие появился некий geektimes, возможно, я должен написать это там, но напишу сюда — на хабрахабр. Если не прав — не бейте ногами.

Я люблю делать простые как топор вещи, но одновременно жутко полезные, например, как прошлая статья 9 летней давности — Установка Ubuntu Linux с винчестера. Скрипт, вот и сейчас настал час для такой деятельности.
Под катом вы найдете подробнейшую инструкцию как на основе Arduino и дешевых и доступных средств автоматизировать подачу чистой свежей воды в кофемашину, а также организовать отвод жидкости из поддона кофемашины, на которой не предусмотрено подключение к внешним коммуникациям.


История такова, что в маленькой, но очень гордой фирме по производству «громадных квадрокоптеров» SKYF купили кофемашину, ближайшие водопроводные и канализационные коммуникации были в метрах 100 от нее. В процессе эксплуатации оказалось, что коллектив из 30 человек заполнял поддон для капель за ~10 кружек кофе, а потом нужно было идти через три двери до ближайшего санузла, балансируя с заполненным водой поддоном, как на Сабантуе с яйцом в ложке, дабы не пролить, ни капли на коверистый пол, ни, тем более, на штаны. Куда меньшую проблему доставляло наполнение резервуара чистой воды, т.к. диспенсер был в полуметре от кофемашины и нужно было лишь 1–2 минуты постоять в руках с пластиковым резервуаром для чистой воды. Но я твердо решил, что «хватит это терпеть» © и вооружившись завалявшейся у меня платкой Arduino Uno, решил автоматизировать процесс наполнения бачка и слива отработанной жидкости.

Предварительно изучил вопрос рентабельности сей идеи автоматизации, оказалось что различие в цене приобретенной кофемашины и кофемашины с возможностью подключения к канализации составляет 2 раза минимум! Приведу пример кофемашин в ценах на начало 2018 года:
— Купленная кофемашина ~60 000 рублей;
— Затраты на дополнение функции подключения к «внешним коммуникациям» ~ 3000 руб;
— Кофемашина с возможностью подключения к водопроводу и канализации — ~от 110 000 до ∞ руб.
Цены просто небо и земля. Определенно есть смысл автоматизировать купленную кофемашину.


Цели:
— увеличить интервал слива жидкости с поддона с раз в 1 час на раз в сутки минимум;
— реализовать удобный способ транспортировки отработанной жидкости до канализационных коммуникаций;
— забыть что такое снимать емкость чистой воды и стоять 1–2 минуты около диспенсера для заполнения емкости.

Способы решения задачи:
— сделать человекоподобного робота или нанять роботоподобного человека, который занимался этими проблемами вручную;
— сделать автоматическую систему, которая сократит частоту ручного труда пользователей кофемашины.

Очевидно, что первый способ очень долг и интересен, оставлю это на будущее. На досуге можно сделать робота, доставляющий приготовленный кофе от кофемашины работнику и, который занимался бы автоматизацией оставшегося ручного труда. Второй способ с секретаршей скучен, банален, так поступали испокон веков, это не наш метод. Выбираем третий способ, он оптимален по соотношению трудоемкость/цена.

Решаем поставленные цели в соответствии с выбранным способом.

Наш пациент выглядит таким образом:

image

Слева у кофемашины съемный бачок для чистой воды, в нижней части выдвижной поддон для капель объемом ~400 мл.
Задача решается организационно и с помощью элементов системы автоматического управления:
1) берем две бутылки 19 литров, которые всегда под рукой в офисе, одна будет для чистой воды, другая для отработанной;
2) в случае снижения уровня в бачке кофемашины до минимума подаем из бутыли чистой воды при помощи насоса воду до отметки максимум;
3) соединяем ПВХ трубкой поддон с внешним резервуаром и контроллируем уровень в нем, саму бутыль, дабы не портить аппетит цветом стоков, закрываем чехлом, которые предлагают многие службы доставки воды.
Например, вот такой чехол:
image

Если у вас очень близко водопровод и канализация, но в кофемашине не предусмотрена возможность подключения к водопроводу и канализации, то эта статья тоже вам подойдет. Вместо насоса у вас будет соленоидный клапан, которым тоже можно управлять с помощью 12 Вольт, клапан соединяется, например, с фильтром воды (который обычно идет с краном чистой воды). А насчет слива — трубку в канализацию, датчик контроля уровня в резервуаре слива надо будет убрать из программы ниже.

Выбираем средства автоматизации
Берем Arduino Uno и начинаем автоматизировать. Нам потребуется:
— Датчик уровня воды в резервуаре кофемашины;
— Насос подачи воды из бутыли в резервуар;
— Датчик уровня воды в резервуаре стоков.
Логика проста:
ЕСЛИ уровень в резервуаре кофемашины ниже минимума → включить насос;
ЕСЛИ уровень в резервуаре кофемашины выше максимума → остановить насос;
ЕСЛИ уровень в резервуаре стоков выше максимума — вопить.
Казалось бы логика проста, но при реализации оказалось что не все так просто, все подробности в разделе программирования.
Выбираем насос
Требования к насосу:
1) должен проходить в горлышко обычной офисной бутылки воды 19 литров, диаметр горлышка 53 мм;
2) должен иметь напряжение питания не более 12 Вольт, дабы согласовать с питанием Arduino и не иметь опасности поражения электрическим током;
3) желательно должен быть погружным в бутыль, чтобы не захламлять пространство вокруг кофемашины и не попадаться на глаза;
4) должен иметь возможность создать давление для подъема воды на 1.5 метра.

Этим критериям удовлетворили следующие насосы:
— погружной насос AMP-X157 от «Амперка» на 5–12 Вольт;
image
— погружной насос BI0002156 от китайцев на 6–15 Вольт.
image
Сначала выбрал первый вариант, просто потому что он продавался в моем городе, некогда ждать месяц китайцев, кофе без забот хочется пить прямо здесь и сейчас. Но при эксплуатации выяснилось из-за выступающего штуцера не пролезает через горлышко в другую бутылку. Потом купил китайский насос, он подошел идеально.
Характеристики китайского насоса (полужирным шрифтом выделены важные для нас параметры):
Рабочее напряжение 6–15 В
Номинальный ток: 1.2A
Максимальный расход: 600л/ч
Размеры: прибл. 10.8 х 4 см (В х Ш)
Идеально. Берем. На этот насос в ближайшем строительном магазине прикупил прозрачную ПВХ трубку с внутренним диаметром 12. На первый насос, которая «амперка» шла трубка диаметром 8 и 10 см (внутренний, внешний) длиной 1 м из нее был сделан слив, так что придется купить 2 трубки, кто хочет просто пройтись по инструкции.

Выбираем датчики уровня
Требования к датчику уровня:
1) должен давать информацию о низком и высоком уровне в бачке воды;
2) по возможности не должен требовать вмешательства в конструкцию бачка;
3) должен быть компактным;
4) должен быть эстетичным и не выделяться;
5) должен запитываться безопасным и совместимым с ардуино питанием 5–12 Вольт;
6) должен быть доступным в городе (мое требование здесь и сейчас);
7) должен работать с пресной водой;

Что предлагает нам рынок:
1) Датчик уровня специально для ардуино
image
Удовлетворяет пунктам: 2, 3, 5, 6.
Не удовлетворяет пунктам: 1, 4. По мне он слишком выделяется и не эстетичный, к тому же его длина не позволит измерять всю высоту уровня (высота бачка около 22 см, данный датчик 6.2 см)

2) Поплавковый уровень
image
Удовлетворяет пунктам: 1 (два датчика), 3, 5, 6.
Не удовлетворяет пунктам: 2, 4. Нужно сверлить бак, выделяется, но его приметим, он нам еще пригодится.
Также есть в виде штанги, тоже не подходят — сложно подобрать нужного размера

3) Инфракрасный датчик уровня (оптопара)
image
Удовлетворяет пунктам: 1 (два датчика), 3, 4, 5, 6.
Не удовлетворяет пунктам: 2. Нужно сверлить бак.

4) Ультразвуковой датчик расстояния HC-SR04
image
Удовлетворяет всем пунктам. У бачка есть крышка, к которой можно прикрепить данный датчик и измерять расстояние до поверхности, благодаря что звук от воды отражается обратно на 99% («эхо гуляет по Яхреньгскому озеру» © язь), то этот датчик подходит идеально: совместим с ардуино, безопасное напряжение, ничего сверлить не надо, будет скрыт под крышкой (в теории), в магазинах навалом, измеряет уровень полностью от дна до полного.

Работает сам датчик следующим образом: у датчика есть излучатель и приемник, излучатель посылает звуковую волну, а приемник слушает когда придет эхо, по всем известным формулам физики и времени возврата эха можно вычислить расстояние до отраженной поверхности. Берем.

Также потребуется блок питания 220В→12Вольт им запитаем, и Arduino, и насос. Управлять насосом будем с помощью реле ардуино (подойдет любой модуль реле с управляющим напряжением 5 Вольт и не более 20 мА, вы можете его сделать сами, а можете просто купить реле от Ардуино).
image

Для общения с человеческим видом ардуинке прикупим пьезопищалку, дабы оповещать о неисправностях, и кнопку, через которую будем считывать хотелки и поведение человека.

Также нам потребуется корпус для складывания всего этого хозяйства, я использовал 3д модель для ардуино уно и прикрепил реле с пищалкой к самому корпусу ардуино. Тут уж кто на что горазд.

Собираем, паяем, сверлим, клеим
Итого что нам потребуется (какое то «пока все дома»…):
1) 2 бутылки 19 литров;
2) Arduino Uno;
3) Блок питания 12 Вольт;
4) Насос погружной BI0002156;
5) Ультразвуковой датчик расстояния HC-SR04 (буду называть сонаром);
6) Поплавковый уровень прямой;
7) Кнопка для взаимодействия с человеком;
8) Реле на 5 Вольт;
9) Пьезопищалка;
10) Корпус;
11) До кучи перемычек папа-мама, мама-мама;

Из инструментов: изолента, нож, герметик, сверла, дрель, паяльник со всеми причитающимся.

Общая схема изображена ниже.
image

Собираем по «принципиальной» схеме ниже всю конструкцию. GND от каждого элемента тянем на любой GND выход Arduino, та же история с +5V. Следование номеру каналов даст вам преимущество не переделывать номера пинов в программе, которая будет ниже.

image

Хотелось бы отметить:
1. Я решал проблему с нехваткой разъемов GND и 5В тем, что припаял проводку к этим выводам и нужные мне датчики запитал от клемников WAGA, куда можно 5 проводов подцепить.

2. Сонар удобно крепится резинками к крышке бачка. Постарайтесь добиться перпендикуляторности излучателя к поверхности воды.

3. На бачек с помощью канцелярского зажима крепится шланг чтобы не выскочил из бачка.

4. Отвод капель из поддона я сделал через распечатанный на 3D принтере штуцер. В низшей точке сливного поддона кофемашины сверлим отверстие 9 мм под него, вставляем его туда и герметизируем соединение. Модель для штуцера под шланг диаметров 8/10 мм ниже по ссылке.

Штуцер / Choke by slimercorp on Sketchfab


Программируем
Спасибо Chupakabra303 за статью на GeekTimes! Без таймеров, детекторов фронтов сигнала не справился бы с программированием. Честно говоря, очень тяжело работать с Arduino, который изначально не заточен под псевдопараллельность задач как настоящие ПЛК.

Измерять уровень будем раз в 0.5 с, это необходимо чтобы эхо от прошлого измерения затихло в почти замкнутом пространстве бачка с водой, если чаще измерять — ничего не толкового не выйдет.

Общается Arduino следующим способом. При ошибке он пищит N раз с паузой 10 секунд, N — номер ошибки.

N писков:
1 — переполнение бутыли для слива или обрыв в линии связи датчика в этой бутыли с контроллером.
2 — проблема с ультразвуковым датчиком уровня (сонар), он либо утоплен, либо отошел какой-либо из проводов.
3 — не подается вода при попытке ее налить. Проблемы: нет воды в бутылке чистой воды, насос, реле, питание, провода.

Чтобы квитировать ошибку (жест «я понял»), человек жмет на кнопку 1 раз, Arduino высоко пищит в течении 1 секунды, говорит что принято, я молчу. Человек в это время принимает меры для исправления проблемы, Arduino в этом режиме пищит 1 раз в 1 минуту, напоминая о себе. После исправления проблемы человек жмет 2 раза кнопку в течении 3 секунд (жест «проверь систему»), далее Arduino либо снова выдаст ошибку писком, либо 1 раз высоко пропищит, сказав что все хорошо, «работаю дальше».

Помимо этого реализовано:
— debug информация по COM порту, для отладки системы, например, отлаживать положение сонара, чтобы он выдавал нужный уровень;
— писк пищалкой без останова исполнения программы (соответственно с контролем нажатий кнопки).

Исходный код:

#include 
#include 
//=============================================================
//Формальное объявление необходимых триггеров и переменных
R_TRIG R_TRIG1;
R_TRIG R_TRIG2;
R_TRIG R_TRIG3;
R_TRIG R_TRIG4;
F_TRIG F_TRIG1;

bool TrashFull; //Высокий уровень в отходах
bool igotit; //Обратная связь от человека, что он ошибку понял
bool check_pls; //Признак запроса на проверку состояния системы
bool PumpUp; // Начать качать воду в резервуар чистой воды
bool NoFlow; //Признак отсутствия потока воды от насоса
bool Pressed; //Признак того что кнопка нажата
bool beep_pause_superlong; //Признак супер долгой паузы писка
bool beep_pause_superlong_end; // Признак окончания супер долгой паузы писка
bool beep_pause_long; //Признак долгой паузы писка
bool beep_pause_long_end; //Признак окончания долгой паузы писка
bool beep_pause_short; //Признак короткой паузы писка
bool beep_pause_short_end; //Признак окончания паузы писка
bool beep_on; //Признак присутствия писка на выходе пьезопищалки
bool beep_duration_end; //признак окончания писка
bool beep_granted; //Признак разрешения на писк
bool Pressed_now; //кнопка нажата только что
bool UnPressed_now; //кнопка отжата только что
bool time_end; //время контрольное кончилось
bool reset; // сброс контроля по уровню

int mode; //режим программы
int error; //вид ошибки
int n_pressed; //количество нажатий вовремя интервала 2 секунды
int k_beep; //количество писков за контрольный интервал
float us_level_raw; //расстояние от датчика до воды, сырое значение с датчика
float us_level_filt; ////расстояние от датчика до воды, сырое значение с датчика фильтрованный сигнал
float level; //уровень в баке
float level0; //уровень в баке в момент включения насоса
long t0; //Момент времени при котором начался контроль за нажатием кнопки
long time_to_destroy; //Подсчет времени до разрушения контроля за количеством нажатий кнопок в интервале 2 секунды
//=============================================================

//=============================================================
//Настройка железа
Ultrasonic ultrasonic(4, 3); // 3 - Echo, 4 - Trig
const int LevelTrashPin = 2; //Вход для дискретного датчика в сливе
const int RelayPin = 5; // Выход управления реле
const int BeepPin =  6; // Выход пищалки
const int ButtonPin = 7; //Вход кнопки
//=============================================================


//=============================================================
//Настройка таймеров
//мусорка антидребезг на аварию
TON TON1(50); // таймер на 50мс, инициализация 

//кнопка антидребезг на зажатие и отжатие
TON TON2(50); // таймер на 50мс, инициализация кнопка
TOF TOF1(50); // таймер на 50мс, инициализация

TON TON3(10000); // таймер на 10 секунд, инициализация КОНТРОЛЬНОЕ ВРЕМЯ ПРОВЕРКИ РАБОТЫ НАСОСА

TON TON4(1500); // таймер на 1.5с, инициализация ДЛИТЕЛЬНОСТЬ ПИСКА
TON TON5(1500); // таймер на 1.5с, инициализация ДЛИТЕЛЬНОСТЬ КОРОТКОЙ ПАУЗЫ
TON TON6(10000); // таймер на 10 секунд, инициализация  ДЛИТЕЛЬНОСТЬ ДЛИННОЙ ПАУЗЫ
TON TON7(60000); // таймер на 1 минуту, инициализация ДЛИТЕЛЬНОСТЬ СУПЕРДЛИННОЙ ПАУЗЫ

TON TON8(1000); // таймер на 1 секунду, инициализация ПЕРИОД DEBUG INFO
TON TON9(500); // таймер на 0.5 сек, инициализация ПЕРИОД измерений уровня
//=============================================================

//=============================================================
//Настройка констант, параметров системы
const int f_mode=500; //Частота в Гц при подтверждении принятия желания человека
const int f_error=100; //Частота в Гц для ошибок
const int t_nc=5000; //Время отсутствия контроля за ошибками после старта ардуино в мс (time_no_control)
const int dLevel_cp=3; //Уровень должен подняться на столько см за контрольный период (cp - control period)
const int button_cp=3000; //Контрольный период нажатия кнопки в мс

const float w_level=0.3; //Вес фильтра сонара
const float us_level_filt_fault=0.1; //Значение уровня при котором и ниже считаем датчик в аварии
const float HeightTank = 20; //Высота резервуара в см
const float HighLevel = 15; //Уровень высокий в см
const float LowLevel = 3; //Уровень низкий в см

const bool debug_on=true; //включить ли отладку по Serial
//=============================================================

void setup() {
Serial.begin(9600); // Это команда для Ардуино : задействовать COM порт для передачи данных.
pinMode(LevelTrashPin, INPUT_PULLUP);
pinMode(RelayPin, OUTPUT);
pinMode(BeepPin, OUTPUT);
pinMode(ButtonPin,INPUT_PULLUP);


igotit=false;
check_pls=false;
NoFlow=false;
beep_pause_long=false;
beep_pause_short=false; 
beep_on=false;
beep_pause_superlong=false;
beep_pause_long=false;
beep_pause_short=false;
beep_on=false;

k_beep=0;
t0=millis();
mode=1;
tone(BeepPin,f_mode,1000); //пищим что все ок, инициализация завершена
}

void loop() {
//=============================================================
//Секция считывания данных
//Полный - высокий уровень, Пустой - низкий уровень, Обрыв - высокий уровень.
//антидребезг появления признака переполнения, смена состояния с ПОЛНОГО на ПУСТОЙ происходит без задержки
  TrashFull=TON1.Run((digitalRead(LevelTrashPin))); 
  
//антидребезг появления признака нажатия и отжатия
//кнопка нормально разомкнута, вход с подтягиванием, один конец на земле поэтому
//никто не нажимает - уровень высокий  на входе
//кто то нажал - уровень низкий на входе
//никто не нажимает на кнопку, но прошелся кот и выдернул провод - обрыв
//обрыв - уровень высокий на входе, диагностировать линию в данной конфигурации нельзя,
//но не очень то и нужно (с) Путин
//не очень то и хотелось (с) Таня

  if (TOF1.Run((digitalRead(ButtonPin)))==false) {Pressed=true;}
  if (TON2.Run((digitalRead(ButtonPin)))==true) {Pressed=false;}

//фильтрация значений с датчика уровня
  if (TON9.Run(!TON9.Q)) {us_level_raw=ultrasonic.distanceRead();};
  us_level_filt=(1-w_level)*us_level_filt+w_level*us_level_raw;
  //Уровень в бачке кофемашины от 0 до HeightTank в см;
  level=constrain((HeightTank-us_level_filt), 0, HeightTank) ; 
//=============================================================


//=============================================================
//Секция интерпретирования поведения системы
//NoFlow,igotit,check_pls
  //при нормальной работе за контрольное время работы уровень поднимается на dLevel_cp см и больше.
  //Если такое не происходит, значит у нас либо не работает насос, либо нет воды в бачке
  time_end=TON3.Run(PumpUp && !reset);
  if ((R_TRIG1.Run(PumpUp)==true) | (reset==true)) {level0=level;} //запоминаем уровень при старте насоса
  if ((time_end==true) && (level-level0=0))  {n_pressed=n_pressed+1;} //считываем сколько раз отпустили кнопку
      if (time_to_destroy<0) {n_pressed=0;}
    }
    
  //Считываем жест человека - "i got it"/я понял. Это означает что человек подтвердил сообщение о неисправности
  //и принял в работу для исправления, пищать больше не нужно. Человек показывает это следующим действием:
  //нажимает кнопку 1 раз за 3 секунды
  if ((mode==2) && (n_pressed==1)) {igotit=true; n_pressed==0;}

  //Считываем жесть человека - "check_pls"/проверь пожалуйста. Это означает что человек предпринял меры для устранения
  //неисправности или провел необходимые работы по пополнению бутыли исходной воды и слива отработанной воды и предлагает
  //вернуться к работе. Человек показывает это следующим действием:
  //нажимает кнопку 2 раза за 3 секунды
  if ((mode==3) && (n_pressed==2)) {check_pls=true; n_pressed==0;}
//=============================================================




//=============================================================
//Секция переключения режимов
  //Если появился признак переполнения в бутыли слива или обрыва датчика, то переходим в режим ошибки и сообщаем код ошибки 1
  if ((mode==1) && (TrashFull==true) && (millis()>t_nc)) {mode=2; error=1;}
  //Если появился признак обрыва до датчика или утопление самого датчика, то переходим в режим ошибки и сообщаем код ошибки 2
  if ((mode==1) && (us_level_filtt_nc)) {mode=2; error=2;}
  //Если появился признак отсутствия подачи воды от насоса, то переходим в режим ошибки и сообщаем код ошибки 3
  if ((mode==1) && (NoFlow==true) && (millis()>t_nc)) {mode=2; error=3;}

  //Если у нас есть ошибка и человек подтвердил - переход в режим ожидания и сбрасываем код ошибки
  if ((mode==2) && (igotit==true)) {mode=3; error=0; igotit=false;}
  
  //Если мы в режиме ожидания и нас попросили проверить все ли ок с системой то переходим в нормальным режим и сбрасываем запрос проверки
  if ((mode==3) && (check_pls==true)) {mode=1; check_pls=false;} 
//=============================================================



//=============================================================
//Основная логика работы в зависимости от режима
   //статус программы: 1 - ВСЕ ОК, работаем; 2 - ОШИБКА, не подтвержденная; 3 - ОШИБКА подтверждена, ожидаем отмашки от человека;
  if (mode==1)
  {
    //Проверяем уровень
    if (level<=LowLevel) {PumpUp=true;} //Уровень ниже минимума - включаем насос
    if (level>HighLevel) {PumpUp=false;} //Уровень выше максимума - выключаем насос
   }

  if ((mode==2) | (mode==3)) {PumpUp=false;} //тут же выключаем насос  чтобы не перегрелся
//=============================================================


//=============================================================
//Секция действий по результату работы программы (Digital Output)
//включаем и отключаем насос по признаку
if (PumpUp==true) {digitalWrite(RelayPin, LOW);} else {digitalWrite(RelayPin, HIGH);}

//Нижестоящий код реализован для писка без delay, который тормозил бы программу и делал бы ее неработоспособной

//При переходе в режим 1-ый делаем секундный писк, что все ок, я перешел
if (R_TRIG3.Run(mode==1)==true) {tone(BeepPin,f_mode,1000); beep_on=false; beep_pause_short=false; beep_pause_long=false; beep_pause_superlong=false; k_beep=0;}


beep_duration_end=TON4.Run(beep_on); //отсчет длительности писка
beep_pause_short_end=TON5.Run(beep_pause_short); //отсчет длительности короткой паузы
beep_pause_long_end=TON6.Run(beep_pause_long); //отсчет длительности длинной паузы
beep_pause_superlong_end=TON7.Run(beep_pause_superlong); //отсчет длительности супер длинной паузы

//пищим в режиме 2-ом, количество писков соответствует номеру ошибки
if (mode==2)
{
  //блокировки писка:
  beep_granted=!beep_pause_long && !beep_pause_short && (k_beepk_beep) {beep_pause_short=true; beep_pause_long=false;} else {beep_pause_long=true; beep_pause_short=false;}
  }
  //когда закончилась короткая пауза, выключаем признак
  if (beep_pause_short_end==true) {beep_pause_short=false;}

  //когда закончилась длинная пауза, выключаем признак
  if (beep_pause_long_end==true) {beep_pause_long=false; k_beep=0;}
}

//при переходе в другой режим, пищим высоко и инициализируем переменые писков
if (R_TRIG4.Run(mode==3)==true) {tone(BeepPin,f_mode,1000); beep_on=false; beep_pause_short=false; beep_pause_long=false; beep_pause_superlong=true;}

//пищим если мы в режиме ожидание, пищим раз в супердолгий интервал
if (mode==3)
{
  beep_granted=!beep_pause_superlong; //не пищим если только у нас есть пауза длительностью в 1 минуту  
  if (beep_granted==true) {tone(BeepPin, f_error); beep_on=true;} 
  //когда длительность писка закончилась, включаем паузу супердолгую
  if (beep_duration_end==true)
    {
      noTone(BeepPin);
      beep_on=false; //выключаем признак писка
      beep_pause_superlong=true;
    }
  //когда закончилась супердлинная пауза, выключаем признак
  if (beep_pause_superlong_end==true) {beep_pause_superlong=false;}
}
//=============================================================


//=============================================================
//Секция Debug, активируется из секции констант
if ((debug_on==true) && (TON8.Run(!TON8.Q)))
{
 //Чистим окно у клиента 
  Serial.write(27);
  Serial.print("[2J"); // clear screen
  Serial.write(27);
  Serial.print("[H"); // cursor to home
 //И начинаем писать всякую информацию о состоянии системы
  Serial.println("=================Debug info of ArduOsch=================");
  Serial.println("=================DicreteInputs==========================");
  Serial.print("LevelTrashPin="); Serial.print((digitalRead(LevelTrashPin)));  Serial.print("| ButtonPin="); Serial.print(digitalRead(ButtonPin)); Serial.print("| US_raw="); Serial.print(us_level_raw); Serial.print("| US_filtered="); Serial.println(us_level_filt); 
  Serial.println("=================DicreteInputs after processing=========");
  Serial.print("TrashFull="); Serial.print(TrashFull);  Serial.print("| ButtonPressed="); Serial.print(Pressed); Serial.print("| Level="); Serial.println(level);
  Serial.println("=================NoFlow condition=======================");
  Serial.print("PumpUp="); Serial.print(PumpUp); Serial.print(" Time_end="); Serial.print(time_end); Serial.print(" Reset="); Serial.print(reset);  Serial.print("| Level0==="); Serial.print(level0); Serial.print("| TON3_EST="); Serial.print(TON3.PT-TON3.ET); Serial.print("| NoFlow="); Serial.println(NoFlow);
  Serial.println("=================How mush was man clicked on button?====");
  Serial.print("TimeToDestroy="); Serial.print(time_to_destroy); Serial.print("| t0="); Serial.print(t0); Serial.print("| n_pressed="); Serial.println(n_pressed);
  Serial.println("=================What is the system state?==============");
  Serial.print("Mode="); Serial.print(mode); Serial.print("| error="); Serial.println(error); 
  Serial.println("=================Beep code==============");
  Serial.print("beep_granted="); Serial.print(beep_granted); Serial.print("| beep_pause_superlong="); Serial.print(beep_pause_superlong); Serial.print("| beep_pause_long="); Serial.print(beep_pause_long); Serial.print("| beep_pause_short="); Serial.println(beep_pause_short);
  Serial.print("k_beep="); Serial.print(k_beep); Serial.print("| beep_on="); Serial.println(beep_on);
}
//=============================================================

}



Видео и фото

Видеодемонстрация как все это работает:

Кому не хочется смотреть видео, вот пару фоточек:

image

c-yledn6mesxopgt0unlg5tgdim.jpeg

n4gq6dfncqkiwew23s7_ywn151k.jpeg

cs0lucvenbgg7gkggej3yxxskju.jpeg

Как чехол использовал почти единственно доступный чехол в Казани, просто серый, как временное решение (что постояннее, чем временное?), потом можно будет красивые чехлы заказать, например, с логотипом фирмы.

Вместо заключения


Теперь меняем чистую воду раз в 4 дня, жидкость из бутылки для слива выливаем в пятницу, за 5 дней запахов не ощущалось. Все счастливы. Знай только выливать да менять бутыли. Никакого Сабантуя, хватит.

Делал это все на энтузиазме, которого у меня порой бывает хоть отбавляй, уж очень хотелось пользоваться кофемашиной без нервов, что ей нужно поддон почистить. Сделал первый вариант, где пару строчек, которая запускает/останавливает насос по уровню и пищит при переполнении «мусорки». Но потом подумал и решил сделать на совесть, с диагностикой датчиков, исполнительных механизмов, дабы насос прожил подольше при нашей интенсивной эксплуатации, не было протечек и недопонимания между ArduOsch и моими коллегами, в итоге все это вылилось в довольно таки большой код. Писал статью в стиле беллетристики, чтобы могли посмотреть-почитать с утра за чашечкой кофе, посмеяться чем люди занимаются иногда.

Всем всех благ!

© Geektimes