Всем привет! Этот пост - продолжение серии про написание софта для посудомоечной машины с реверс-инжинирингом.
Часть 1. Вступление
Часть 2. Написание каркаса программы
Собственно говоря, какое-то время меня не было, т.к. был вынужден заниматься добычей пропитания (собственно, как и все). Но речь не об этом. Были сделаны доработки, проведены первые испытания на машине:
Сама программа за прошедшее время немного увеличилась и обросла различными багами и костылями:
Давайте по порядку. Кое-что из поставленных ранее задач было выполнено, однако, до готового продукта ещё есть некоторая дистанция. На текущий момент необходимо было сделать обработку входных датчиков. Как уже упоминалось в первом посте, датчики у нас были следующие: датчик температуры, датчик воды, датчик соли и 3 микропереключателя. Последние обрабатываются очень просто, достаточно лишь добавить такие макроподстановки:
Т.е. грубо говоря, текущее значение напряжения на данном порту (логическое) сравнивается с требуемым значением. Для этого входной регистр сдвигается на такое число раз, каков номер исследуемого пина, после чего с помощью простого логического И с 0x01 от него "отрезается" крайний разряд. Соответственно, далее делаем сравнение с требуемым логическим уровнем. Если условие удовлетворяется, то делаем какое-то действие. Самое простое - проверка аквастопа (аварии) и датчика соли с компаратора:
Аналогичным образом проверяются кнопки (в нашем случае у нас одна кнопка питания). Т.е. если была нажата кнопка питания или же сработал аквастоп, то вызывается процедура остановки машины. Она отключает выходной порт и выводит сообщение об ошибке.
Впрочем, это не самое интересное. Далее дошла очередь до вывода FM. Вот тут сказалось то, что я плохо знал устройство машины. Я предполагал, что геркон FM срабатывает при заполнении бака, аналогично аквастопу. По факту же оказалось, что сигнал с выхода FM по сути представляет собой стробирование. А сам FM - расходомер, в котором циклично крутится поплавок. Поступила задача - реализовать электронный счетчик этого самого расхода воды. Для этого навесить на контроллер ещё дополнительный функционал. Благо, задача эта несложная. Наш контроллер ATMEGA328 имеет для этого все необходимые мощности. Вот, как выглядит этот самый расходомер. Видно платку и геркон:
Существовало два варианта обработки этого датчика - при помощи сложения сигналов (для этого внутренний таймер нужно настроить на ту же частоту, что и расходомер, после чего выполнить дискретизацию, и записать общее время совпадения, в момент, когда сигнал станет равным нулю). Но этот метод не очень подходит по следующим причинам:
1. Период оборота геркона может быть разным, он зависит от напора, так же попавший пузырёк воды может сбить фазу и привести к неверным выходным данным.
2. Необходимо перенастраивать имеющийся таймер, или же задействовать другой.
Поэтому для реализации счетчика воды решено было использовать второй вариант - прерывания. ATMEGA328 поддерживает два их вида - INT и PCINT. Первое - хорошее дело, так как на оба вывода прерывания выделяется отдельный обработчик. Но увы, они у нас висят на порту D, который в нашем случае хорошо зарекомендовал себя, как выходной порт (т.к. изначально я не думал, что прерывания мне там понадобятся). Так что придется задействовать программные прерывания PCINT, которые можно навесить на любой порт. К тому же, в используемом мною методе обработки это не имеет особого значения. Для начала необходимо определить тайминги. Для этого снимается осциллограмма со входа:
Как можно видеть, в среднем, весь пик занимает интервал в 30 мс. Поэтому для борьбы с помехами и дребезгом мы используем в прерывании метод двух замеров:
Внизу инициализируется прерывание для группы 1 пина 2 - PCINT10, что в нашем случае PINC-2. Пока что оно не согласуется с конфигом, это такой небольшой костылёк, возможно, он будет потом исправлен. В самом прерывании происходит следующее: подстановка FLUID_HOLD - это проверка состояния PINC-2. То есть, прерывание PCINT возникает всегда, когда происходит событие на линии PCINT. Но если при этом у нас это не высокий уровень сигнала на пине C2, то оно идёт мимо и не занимает много лишних тактов. Если же это нужное прерывание, то следом вызывается задержка, спустя 20 мс делается второй замер по этому же пину. Если значение не изменилось, то "горбик" сигнала засчитывается.
Тут есть щекотливый момент: подразумевается, что прерывания не будут вызываться вторым этажом. Иначе же прерыванию необходимо запрещать себя, выполнить задержку, после чего восстанавливаться. Возможно, это место будет переделано - это увеличит точность борьбы с дребезгом. В общем и целом, на осциллограмме это выглядит так:
Красная линия - произошло событие на шине. Оранжевая - событие подтвердилось, это была не помеха. Для контроля получившегося "протокола" к системе была быстро подключена ардуина, выдающая пачку импульсов. Количество импульсов должно быть равным как на передатчике, так и на приёмнике:
Было передано 99 импульсов, пришло 100. Это сделано специально, первый импульс приходит при перезагрузке Arduino. В общем и целом всё соответствует. Интервал 30 мс. Потерь нет, лишних срабатываний тоже нет. Но ардуино связывается по притянутой к земле помехоустойчивой шине. Поэтому при реальных испытаниях всё же возможны ложные срабатывания из-за недостатков алгоритма.
Давайте попробуем залить бак машины, для начала, по времени. Для этого используется цикл из предыдущего поста. Он был подвергнут изменениям, а именно, убраны команды на отключение агрегата. Подразумевается, что вся шина обнуляется, а затем включаются нужные приборы. Тут палка о двух концах - при таком способе каждое состояние обязано хранить лишние команды на включение. Но, при этом, не надо отслеживать, если что-то забыли выключить:
Итак, включаем заливку по времени. На 25 секунд. Опытным путём установлено, что этого времени достаточно для того, чтобы заполнить бак до определенной черты. Давайте посмотрим, сколько импульсов насчитает расходомер:
В полевом варианте всё это выполняется на стареньком ноуте:
Теперь немного из начального курса метрологии. Мы получили значение в 271 попугай. Теперь надо определять, есть ли ошибка и какова случайная составляющая данной величины. Давайте повторим эту же процедуру 6 раз, и сравним результат. Стандартное отклонение, в нашем тяжелом случае, не должно превышать 5.
Для сельской местности сойдёт. В принципе, результаты не берутся с потолка, при прочих равных условиях (одинаковый напор воды, одинаковое время включения) величина ошибки не слишком уж и велика. Далее оценим линейность измерения, чтобы определить накопленную ошибку, характерную для счетчиков. Для этого выберем интервалы от 5 до 30 сек и проведем замеры:
Как видно, небольшая ошибочка всё-таки накапливается. Но стоит учесть тот факт, что при заливке в течение 30 секунд, вода уже начинает выливаться из бачка машины. Т.е. имеется ввиду, что более этого времени счетчик работать не будет, он будет обнуляться. Поэтому даже ошибка в 7% в данном случае приемлема. Хотя, конечно, хотелось бы более точных результатов.
Но тут, как говорится, живы будем - не помрём, если сильно загорится - код перепишем. Кстати говоря, явно заметно кореллирование наших попугаев с секундами. Не знаю, было ли так задумано производителем или случайно так вышло.
В общем, осталось только физическое обоснование. Делается оно аналогичным образом. За 30 секунд машинка наливает воду до определенной черты. Давайте просто сольём машину (DRAIN_ENABLE на 30 секунд) и до этой же черты просто нальём воду в бак из мерного стакана.
Для заливки до отмеченной ранее черты понадобился 1 литровый мерный стакан и ещё примерно 550 мл воды. В нашем случае получается, что один попугай примерно равен 6 мл водицы. Следовательно, расход воды в машинке примерно равен 3л/мин при стандартном напоре по 12мм трубе. Так и запишем. В дальнейшем в программе при заливке воды будем считать уже не по таймеру, а по попугаям.
Впрочем, тут придумалась небольшая физическая задачка. Сколько времени понадобится, чтобы в случае поломки расходомера залило соседа снизу? Ответ: при кухне в 8 метров квадратных, если учесть, что залитие произойдет при уровне воды в 1 см, для этого достаточно будет лишь 26 минут зависания программы с открытым клапаном. Поэтому, ребята, посудомойки без присмотра лучше не оставлять. А нам лучше сделать дополнительный контроль по таймеру, помимо аквастопа. Чтобы если в течение минуты требуемые попугаи не набежали, была возможность этот самый клапан прикрыть до выяснения обстоятельств. Эти проверки легко реализуются в различных функциях программы, я оставлю их за кадром пока.
В части поста 3.2. Будем измерять реализовывать термодатчик, дабы можно было ещё и пару поддать, а не только воду экономить.
Всем спасибо, с вами был Kekovsky.