Информатор для стиральной машины

9b9cb68761030b22beec2f9162c77564.jpg

Когда увлекаюсь работой, забываю обо всём, есть такое. Но бытовые проблемы никуда не исчезают и дают о себе знать. Лет десять назад осознал, что в век интернета уже надо что-то делать с тем, что я постоянно забываю вытащить бельё из стиральной машины. Приспособил к ней систему уведомления. Всё было хорошо, но век гаджетов не долог — нужно было обновить решение, что я и сделал с помощью ESP32 и небольшой программы на Rust. Интересующимся добро пожаловать под кат.

В те давние времена начала 2010х годов еще не было повсеместного распространения стиральных машинок с подключением к интернет и приложениями для смартфонов. Но сами машинки были (и остаются) ничуть не хуже нынешних, раньше на технике не экономили и делали на века. Поэтому просто выбросить старую и хорошо работающую машинку не было даже мысли. К тому же мне нужна была всего одна дополнительная функция — это чтоб машинка оповещала меня как-то об окончании стирки, пока я сижу за компом. Не придумал ничего лучше, кроме как отправить самому себе почтовое сообщение. Пробовал еще СМС отправлять, но почты оказалось достаточно. Как видите, функционал мне нужен был простой, и я без сомнений в успехе взялся за дело.

Первая задача, которую нужно было решить, это как заполучить от машинки информацию о том, когда началась и закончилась стирка. Сделать это можно множеством способов. Я остановился на самом для меня простом. Во всех машинках есть устройство блокировки дверцы. Работает оно различными способами. Туда ставят электромагниты, моторы, но самый распространённый вариант, как я понял, это блокиратор дверцы с помощью биметаллической защелки. При включении машинки на стирку на нагревательный элемент замка (F3) подаётся напряжение, он нагревает биметаллическую пластину, та меняет форму и блокирует дверцу — все просто и надёжно. При окончании стирки нагрев прекращается, пластина остывает о разблокирует замок. Самое главное для меня то, что на всё время стирки на замок подаётся сетевое напряжение.

Схема стиралки БОШ. Блокиратор - F3

Схема стиралки БОШ. Блокиратор — F3

Недолго думая, я купил небольшое сигнальное реле с обмоткой, рассчитанной на 220В переменного тока. Подключил обмотку реле параллельно нагревательному элементу замка блокировки дверцы и получил, таким образом, пару сухих контактов для индикации работы машинки, да еще и с гальванической развязкой — самое оно для подключения к микроконтроллеру. Ток потребления обмотки реле ничтожный по сравнению с потреблением замка. По схеме машинки БОШ ток на замок подаётся мощным симистором. Так что на здоровье машинки такое подключение никакого влияние не оказало, что и подтвердила более чем десятилетняя работа.

На тот момент времени удачным способом отправки себе почтовых сообщений оказалось использование замечательного рутера TP-LINK TL-MR3020 с кастомной прошивкой. Маленькая коробочка с линуксом и WiFi, встроенным ssmtp, возможностью подключения к линиям ввода-вывода. Всё это богатство меньше ардуино размером и бросовой стоимостью. Правда, там крайне мелкий монтаж, поэтому подключение к портам требовало хороших навыков пайки. Тем не менее, задача была решена и уведомление об окончании стирки я получал на почту без всяких проблем. Всю работу выполнял нехитрый шелл скрипт, который стартовал при включении питания рутера. Помимо отправки сообщения он еще моргал светодиодом рутера:

#!/bin/sh 

(echo 29 > /sys/class/gpio/export) >& /dev/null
started=0
led_on=0
led=/sys/devices/platform/leds-gpio/leds/tp-link\:green\:3g/brightness

while true
do
   read_io=$(cat /sys/class/gpio/gpio29/value)
   if [ $read_io –eq 1 ]; then
     if [ $started –eq 0 ]; then
        started=1
        start=$(date +%s)
     fi
   else
     if [ $started –eq 1 ]; then
        started=0
        end=$(date +%s)
        secs=$((end-start))
        echo Secs: $secs 
        cp /root/eml.txt /tmp/newmail.txt
	    echo Wash finished in $secs secs. >> /tmp/newmail.txt
	    ssmtp мояпочта@gmail.com < /tmp/newmail.txt
     fi
   fi

   if [ $led_on –eq 1 ]; then
     led_on=0
     (echo 0 > $led) >& /dev/null
   else
     led_on=1
     (echo 1 > $led) >& /dev/null
   fi

   sleep 5s
done

Но всему хорошему приходит конец. Я поменял домашний маршрутизатор на новую модель, после чего связь по WiFi с TP LINK прервалась. По всей видимости, что-то изменилось в настройках безопасности соединения. Но решить проблему я не смог. За прошедшие годы TP LINK превратился в продукт давно ушедшей цивилизации. Сайты, на которых были прошивки и инструкции по настройке TL-MR3020 прекратили существование. Пришлось искать современную альтернативу.

Я немного упростил себе задачу, решил отказаться от линукса в конструкции, а функции отправки почты возложить на специальный сервер, который обслуживает другие мои домашние самоделки. Это сервер может отправлять почту по HTTP GET запросу. Таким образом, мне не нужно было реализовывать поддержку SSMTP, что резко упростило программное решение.

Далее, я увлекаюсь embedded Rust, поэтому хотелось попробовать что-то с поддержкой Rust. На мою удачу оказалось, что Espressif ведет разработку библиотек для Rust и вполне можно попробовать использовать один из вариантов ESP32 в качестве микроконтроллера с поддержкой WiFi. Ранее я не использовал ESP32 и, соответственно, никогда его не программировал. Т.е. это мой первый опыт на той платформе. Заказал на алиэкспресс пару каких-то модулей NodeMCU ESP-WROOM-32E и на время ожидания погрузился в изучение документации.

Есть очень хорошее руководство для начинающих использовать Rust на ESP32 (https://docs.esp-rs.org/book/). Там в деталях расписано что и как надо установить и настроить для создания среды разработки. Возможно довольно много вариантов, поэтому стоит внимательно прочесть это руководство. Espressif использует собственный форк компилятора Rust, для создания проекта нужно сделать довольно много настроек, поэтому разработчики сделали специальную утилиту, которая создает структуру и все файлы пустого проекта по заданным настройкам. По опыту скажу, что даже сгенерированный по шаблону проект пришлось донастраивать.

Сперва нужно выбрать платформу и ОС разработки, определиться будете ли использовать кросс компилятор в Docker. Потом нужно выбрать чип ESP (у них разные системы команд и, соответственно, компиляторы). Но и это еще не всё.

Espressif разработала два вида библиотек для своих чипов. Первый тип библиотек реализует типичный для Rust HAL полностью на Rust. Второй тип библиотек — это Rust интерфейс к стандартному набору библиотек написанных на С. Только в этом типе есть полная функциональность, к которой привыкли разработчики на C. Нативные Rust библиотеки, как пишут, позволяют более тонко управлять железом, но существенно проще по функциональности. В частности, для Rust пока нет полноценной RTOS. И эту проблему приходится как-то обходить. Какие то элементы RTOS, судя по описанию, портированы в библиотеки, но не все. Кроме того, есть как минимум 2 разновидности HAL — это обычный синхронный вариант и еще отдельно поддержка асинхронного HAL embassy. Примеров использования RTIC не нашёл. Как видите, случайному программисту разобраться что и когда использовать не так просто.

В виду примитивности решаемой мной задачи решил остановиться на самом простом варианте со стандартным нативным HAL. По сути мне нужно отследить изменение логического уровня на пине ввода и, в случае смены уровня, отправить HTTP GET запрос. Проще программы трудно придумать. К счастью в репозитарии Espressif (https://github.com/esp-rs/esp-hal/tree/main/examples) есть все необходимые примеры — как организовать ввод/вывод, как присоединиться к точке доступа WiFi, как выполнить HTTP запрос. Работа с сетью подразумевает обработку множества возможных ошибок, начиная с отсутствия связи с точкой доступа WiFi, до неправильных ответов HTTP сервера. Примеры Espressif не дают развернутого описания корректной обработки ошибок. Поэтому я решил не заморачиваться их подробной обработкой, а добавил в код просто перезапуск модуля по сторожевому таймеру в случае критической ошибки сети. Логика работы программы позволяет такую вольность.

Вот выдержка основного цикла программы:

loop {
        wdt0.feed();
        socket.work();
        // при нажатии кнопки уровень 0 и ставим флаг что машина запущена
        if button.is_low() && wm_started == false {
            wm_started = true;
            log::info!("WM Started!");
        }
        // машина была запущена и остановилась - отправляем письмо
        if button.is_high() && wm_started {
            wdt0.feed();
            wm_started = false;
            // send email
            log::info!("EMail sending...");
            socket
                .open(IpAddress::Ipv4(Ipv4Address::new(192, 168, 1, 5)), 80)
                .unwrap();
            socket
                .write(b"GET /kettle.php?str=washStopped HTTP/1.0\r\n\r\n")
                .unwrap();
            socket.flush().unwrap();
            wdt0.feed();

            let wait_end = current_millis() + 20 * 1000;
            loop {
                wdt0.feed();
                let mut buffer = [0u8; 512];
                if let Ok(len) = socket.read(&mut buffer) {
                    let to_print = unsafe { core::str::from_utf8_unchecked(&buffer[..len]) };
                    print!("{}", to_print);
                } else {
                    break;
                }

                if current_millis() > wait_end {
                    println!("Timeout");
                    break;
                }
            }
            println!();

            socket.disconnect();
        }
        // пробуем каждые полсекунды мигать диодом - heartbeat
        if current_millis() > led_wait_end {
            led.toggle();
            led_wait_end = current_millis() + 500;
        }
    }

Как видите, для корректной работы WiFi нужно просто в цикле обращаться к методу socket.work (), это обеспечивает функционирование сетевого интерфейса.

wdt0.feed () сбрасывает сторожевой таймер. Если программа в цикле подвиснет, то МК должен перезапуститься.

socket.write (b«GET /kettle.php? str=washStopped HTTP/1.0\r\n\r\n»).unwrap () инициирует отправку письма обращением к серверу с помощью HTTP GET. При наличии воображения и желания можно отправить таким образом уведомление в какой-либо мессенджер или СМС шлюз.

Если помните, то сигнал о запуске стиралки приходит от реле. Соответственно, возможен дребезг контактов реле. Я решил не бороться с ним программно, а сделал цепь подавления дребезга на дискретных элементах. В варианте с TL-MR3020 использовал оптрон, но в этот раз решил сделать попроще. Работает вполне удовлетворительно.

Подключение МК к реле в стиралке

Подключение МК к реле в стиралке

Ну и ни одна схема на МК не должна обходиться без моргающего светодиода). Конечно же, его я тоже добавил в конструкцию.

Все собрал на макетной плате из подручных материалов. Коробочка от старого маршрутизатора оказалась великовата, но мне занимаемое место не критично.

60dcc6138383f9d3b6ff4d1c62bae99f.jpg

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

Вот таким образом мне удалось вернуть обратно к жизни утерянную функциональность. Жаль, что электроника очень быстро устаревает и теряет техническую поддержку. В виду чего приходится вместо ремонта создавать совершено новые решения. Полагаю, что когда сломается вариант контроллера на ESP32, будут уже совсем другие МК и языки программирования, а старушка стиральная машина БОШ будет продолжать работать.

Если проект заинтересовал, то полная его версия доступна здесь https://github.com/lesha108/wm_esp

© Habrahabr.ru