Часть 3 (последняя). Когда 1С-ник идёт в хардкор: как я в 2018 году заменил мозг стиральной машины на Arduino и что из этого вышло. Аудит проекта с помощью ИИ сегодня

24.03.26

Интеграция - Робототехника и умный дом (Arduino, NFC, RFID)

В заключительной части я разбираю код самодельных "мозгов", фазовое управление мотором и сервисную консоль отладки, которая помогла запустить проект в работу. В финале - честный аудит кода искусственным интеллектом и ответ на вопрос, стоил ли этот инженерный вызов потраченного времени. В конце статьи ссылка на Github

Предыдущие части:

Часть 1. День, когда из стиралки пошел синий дымок

Часть 2. Железо и реверс-инжиниринг
 

Часть 3. Софт

Как всё начиналось на самом деле

Прежде чем показать код, я должен рассказать одну историю. Потому что без неё непонятно, почему 1С-ник вдруг полез в железо и начал писать код для него.

1995 год. Я студент университета. Мои родители в конце 80-х, в купили себе новую супер современную стиральную машины "Вятка-автомат". Для тех, кто не застал те времена - это была легенда. Это была первая советская стиралка с программным управлением, которая могла стирать белье САМА, только вот "программное" - это было громко сказано. Внутри стоял командоаппарат.

Это такой механический барабан с кулачками. Крутится маленький моторчик, кулачки замыкают контакты - включают стирку, слив, отжим. Всё это жужжит, щёлкает и в любой момент могло заклинить. По началу это вызывало восхищение, стиральная машина все делает сама! Но уже в 1995 году, я подумал: "Какой же это архаизм. Неужели нельзя сделать электронную начинку и заменить этот командоаппарат?"

Этот вопрос засел у меня в голове.

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

Спроектировал схему на базе Intel 8080. Разработал всю обвязку: порты, память (ОЗУ, ПЗУ), схемы согласования. Написал программу на ассемблере. Всё как положено.

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

На этом история закончилась и забыл про нее.

До 2018 года.

Когда после короткого замыкания родная плата стиралки превратилась в груду оплавленного пластика, я вдруг вспомнил ту курсовую. Тот самый проект, который 20 лет назад существовал только на бумаге. И меня словно переклинило: "А что, если сейчас?"

Arduino тогда был уже популярным. А у меня уже было реализовано несколько домашних проектов. Никакого Intel 8080, никакой сложной обвязки, ни какого ассемблера. Просто плата, датчики, мотор, реле и синяя изолента. Но принципы остались те же: прерывания, порты, конечные автоматы.

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

Скажу честно: если бы не та курсовой, я бы, наверное, купил новую стиралку и не парился. Но память о "Вятке" и моем проекте не давала покоя. Хотелось доказать самому себе: то, что я придумал 20 лет назад, действительно работает и я смогу это сделать.

И знаете? Оно заработало, я это сделал.
 

Теперь, когда вы знаете предысторию, можно показать код. Потому что это не просто скетч для Arduino. Это реализация идеи, которая родилась в 90-х, получила второе дыхание в 2018-м, а сегодня, в 2026-м, я решил показать её вам и спросить у искусственного интеллекта: насколько хорошо я справился?
 

Как устроен код

Для удобства разработки я разбил код на несколько файлов, по функциональности, получилась полноценная система, где каждая часть отвечает за свою задачу.

Вот структура проекта:

Файл Что делает
v_1.10.12.ino Глобальные переменные, пины, константы. Всё, что нужно для настройки
0_setup.ino Инициализация пинов, таймеров, Serial
1_interrupts.ino Сердце системы. Обработчики прерываний: детектор нуля, таходатчик, прессостат, таймеры
2_water_valte.ino Управление клапанами залива воды
3_ten.ino Управление ТЭНом и нагревом воды
4_pump.ino Управление насосом слива
5_motor.ino Управление мотором. SoftStart, ControlSpeed, калибровка
6_washing.ino Цикл стирки
7_rinsing_spin.ino       Полоскание и отжим (с контролем дисбаланса)
8_display.ino Динамическая индикация и кнопки. Там же массив segCode для вывода цифр и букв
9_mode.ino Режимы работы, энкодер, обработка нажатий кнопок
program.ino Главный конечный автомат. 19 программ стирки, каждая со своей логикой
other.ino Сервисный режим. Консоль отладки, команды для ручного управления

Это разделение сделано не ради красоты. Когда в системе три независимых таймера, обработчики прерываний, управление мотором в реальном времени и динамическая индикация - держать всё в одном файле невозможно. 

Таймеры

В проекте три таймера. Каждый работает независимо и не блокирует остальные:

Таймер Частота Задача
_timer_MOTOR 50 КГц Управление фазо-импульсным регулятором. Открывает симистор с нужной задержкой
_timer_MAIN 10 Гц (100 мс)    Основная логика: подсчёт оборотов, контроль вибрации, проверка уровня воды, таймауты
_timer_DISPLAY   500 Гц (2 мс) Динамическая индикация. Выводит один разряд за срабатывание, опрашивает кнопки

Такой подход позволил мне делать "несколько дел одновременно" без использования delay в критических местах. Всё, что требует точного времени, ушло в прерывания и таймеры. Основной цикл loop() остался практически пустым - только обработка нажатий кнопок и сервисный режим.

Прерывания. Управление мотором и уровнем воды в баке

Самое интересное - аппаратные прерывания. Их три:

INT0 (пин 2) - детектор перехода через ноль. Это основа фазо-импульсного регулирования. Вот как это выглядит в коде:

void detect_up() {
  tic = 0;           // обнулить счетчик
  timer_init_ISR_50KHz(_timer_MOTOR);   // запустить таймер
  attachInterrupt(0, detect_down, HIGH);  // перепрограммировать прерывание на другой обработчик
}

void detect_down() {
  timer_stop_ISR(_timer_MOTOR); // остановить таймер
  digitalWrite(PIN_MOTOR_OUT, LOW); // логический ноль на выход
  tic = 0;     // обнулить счетчик
  attachInterrupt(0, detect_up, LOW); // перепрограммировать прерывание на другой обработчик
}

Что здесь происходит? Каждый раз, когда синусоида 220V проходит через ноль, срабатывает прерывание. Мы запускаем таймер, который через заданную задержку (Dimmer) открывает симистор. Чем меньше задержка - тем большая часть полуволны поступает на мотор. Так мы управляем мощностью.

INT1 (пин 3) - таходатчик. Считает обороты мотора:

void detect_tacho() {
  count_tacho++;
  if (pwmOut == 0) {
    count_tacho_STOP++;
  } else {
    count_tacho_STOP = 0;
  }
}

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

INT4 (пин 19) - прессостат. Датчик уровня воды, который выдаёт частоту в зависимости от высоты столба воды:

void detect_water() {
  count_water_gz++;
}

Аналоговая величина превращается в цифровую частоту, которую мы легко измеряем и интерпретируем как уровень воды.

Динамическая индикация и кнопки

Отдельного упоминания заслуживает вывод на дисплей. В проекте используется семисегментный индикатор и куча светодиодов, и всё это подключено к разным пинам. Я сделал так: за одно срабатывание таймера (каждые 2 мс) выводится один разряд, и тут же опрашивается одна кнопка или энкодер.

void viewDisplay() {
    offPins();                     // гасим текущий разряд
    if (curr_digit_number < 3) {
      setSegments(displayBuf[curr_digit_number]); // выводим цифру
    } else {
      setSegments2(displayBuf[curr_digit_number]); // выводим светодиоды
    }
    setPins();                     // включаем следующий разряд
    getEncoder();                  // опрос энкодера
    getButton();                   // опрос кнопок
}

Весь вывод и опрос кнопок происходит асинхронно, без задержек. Человек не видит мерцания, а кнопки опрашиваются с частотой 500 раз в секунду - дребезг контактов успешно подавляется программно.

Для вывода цифр и букв на семисегментный индикатор я создал массив segCode. Вот его часть:

/*
    A
   ---
F |   | B
  | G |
   ---
E |   | C
  |   |
   ---
    D
 */

byte segCode[24][7] = {
//  A  B  C  D  E  F  G 
  { 1, 1, 1, 1, 1, 1, 0 },  // 0
  { 0, 1, 1, 0, 0, 0, 0 },  // 1
  // ...
  { 1, 1, 1, 0, 1, 1, 1 },  // A
  { 1, 0, 0, 1, 1, 1, 0 },  // C
  { 1, 0, 0, 0, 1, 1, 1 },  // F
  { 0, 0, 0, 1, 1, 1, 0 },  // L
  { 0, 1, 1, 1, 1, 1, 0 },  // U
  // ...
};

Это позволяет выводить не только цифры, но и буквы для кодов ошибок: FE (переполнение), UE (дисбаланс), PE (прессостат), End (конец стирки).

Тот самый момент "оживания" - это инициализация панели в setup() из второй части.

При включении машина здоровается "Hi", затем прогонят тест индикации - зажигет и выключает все светодиоды энкодера по кругу, после этого устанавливается программа по умолчанию и система переходит в режим ожидания:

void DisplayStart() {
  indicator[0] = 23; // - 
  indicator[1] = 23; // - 
  indicator[2] = 23; // - 

  indicator[0] = 22; // 
  indicator[1] = 14; // H
  indicator[2] = 1;  // 1

  // Включим светодиоды энкодера
  for (int i=0; i < 19; i++) {
    prog[i] = 1;
    setInfoDisplay();
    delay2(50);
  }
  // Выключим светодиоды энкодера
  for (int i=0; i < 19; i++) {
    prog[i] = 0;
    setInfoDisplay();
    delay2(50);
  }

  // Устанавливаем, программу умолчанию
  for (int i=0; i <= DEFAULT_PROG; i++) {
    prog[i] = 1;
    if (i > 0) {
      prog[i - 1] = 0;
    }
    setInfoDisplay();
    delay2(50);
  }
  
  // Устанавливаем значение энкодера на программу по умолчанию
  current_program = DEFAULT_PROG;
  encoder_value = DEFAULT_PROG * 4;

  // Включим режимы по умолчанию
  fl_StartStop = 1;
  fl_BlockB = 1;
  fl_PoloskanirPlus = 0;
  setSpin();
  mode_child_lock = 0;

  getTotalTime(0, 0);
  setInfoDisplay();
}

 

Консоль отладки: как я управлял стиралкой с ноутбука

Помните фото из второй части: ноутбук на крышке унитаза, провода, консоль на экране? Ноутбук был моим главным инструментом отладки - это был мой отладчик.

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

В файле other.ino есть функция Service(), которая читает команды из Serial и выполняет их. Вот как это выглядит:

void Service() {
  if (Serial.available() > 0) {
    Serial_val = Serial.read();

    if (Serial_val == 'h') {
      Serial.println ("Сервисный режим, команды:");
      Serial.println ("с - калибровка мотора");
      Serial.println ("q - вкл/выкл клапана набора воды, предварительной стирки");
      Serial.println ("w - вкл/выкл клапана набора воды, основной стирки");
      Serial.println ("W - вкл включение режима набора воды в режиме основной стирки WATER_LEVEL1 (полный автомат)");
      Serial.println ("t - вкл/выкл ТЭНА");
      Serial.println ("T - вкл режима подогрева воды, до 30 градусов (полный автомат)");
      Serial.println ("p - вкл/выкл насоса");
      Serial.println ("P - вкл режима откачки воды, с контролем уровня воды (полный автомат)");
      Serial.println ("o - отжим в режиме mode = 2 (Промежуточный отжим при полоскании)");
      Serial.println ("O - полоскание и отжим, с учетом всех настроек режимов (полный автомат)");
      Serial.println ("m - вкл/выкл ручное управление мотором");
      Serial.println ("r - вкл/выкл режима реверса мотора");
      Serial.println ("s - вкл режима стирки на 1800 сек (полный автомат)");
      Serial.println ("e - вкл режима полоскания на 360 сек (полный автомат)");
      Serial.println ("z - показать уровень");
      Serial.println ("x - стоп машина");
      Serial.println ("y - Конец машина");
      Serial.println ("d - тест датчика вибрации");
      Serial.println ("v - тест измерения веса белья");
      Serial.println ("I - ТЕСТ");
      Serial.println ("R - temp");
    }
    
    // ... и дальше обработка каждой команды
  }
}

Что это давало

Я мог сидеть с ноутбуком, открыть монитор порта (или свой скетч, который выводил данные в читаемом виде), и выполнять любую операцию:

  • w - открыть клапан залива воды, проверить, льётся ли вода

  • p - включить насос, послушать, сливает ли воду

  • m - перейти в ручное управление мотором, крутить потенциометр и менять скорость вращения

  • o - запустить отжим и смотреть, как барабан разгоняется

  • z - посмотреть текущий уровень воды в герцах

И всё это  без перезагрузки контроллера, без перепрошивки, в реальном времени.

Как это помогало отлаживать

Самый сложный момент был с мотором. Я не мог просто "залить код и надеяться". Мне нужно было понять:

  • При какой мощности мотор начинает крутиться?

  • Как меняются обороты при изменении Dimmer?

  • Работает ли обратная связь по таходатчику?

Для этого я сделал режим ручного управления (m). Потенциометр, подключённый к аналоговому входу A0, позволял плавно менять заданную скорость. А консоль выводила в реальном времени:

MOTOR_direction: 0 pwmSet: 5 pwmSpeed: 5 pwmOut: 38 Dimmer: 217
MOTOR_direction: 0 pwmSet: 5 pwmSpeed: 5 pwmOut: 38 Dimmer: 217
MOTOR_direction: 0 pwmSet: 5 pwmSpeed: 5 pwmOut: 38 Dimmer: 217
MOTOR_direction: 0 pwmSet: 6 pwmSpeed: 5 pwmOut: 44 Dimmer: 211
MOTOR_direction: 0 pwmSet: 6 pwmSpeed: 6 pwmOut: 44 Dimmer: 211

Я видел:

  • pwmSet - заданную скорость

  • pwmSpeed - текущую скорость (из таходатчика)

  • pwmOut - выходную мощность (0-255)

  • Dimmer - задержку открытия симистора

Я крутил ручку - и сразу видел, как система реагирует. Если обороты отставали, мощность росла. Если перескакивали - падала. Это позволило мне подобрать коэффициенты и понять, что мотор слушается.

Полный автомат через консоль

Самый кайф был, когда я реализовал команды "полного автомата". Например:

  • W - набрать воду до нужного уровня (с контролем прессостата)

  • T - подогреть воду до 30 градусов (с поддержанием температуры)

  • s - запустить полный цикл стирки на 1800 секунд

  • P - слить воду с контролем уровня

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

Почему это важно показать 1С-никам

Потому что это та же самая отладка, к которой мы привыкли в 1С.

В 1С у нас есть:

  • Консоль запросов

  • Отладка с точками остановки

  • Обработка для тестирования обменов

Здесь я сделал то же самое, но для железа. Это не "взять и написать код". Это "сделать так, чтобы код можно было отлаживать".

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

Так родилась эта консоль. Она стала моим главным инструментом на всех этапах разработки.
 

Как стиралка определяла дисбаланс при выходе на режим отжима

Когда я дошёл до этапа отжима, я столкнулся с проблемой: если бельё сбилось в одну кучу, барабан на высоких оборотах начинает колбасить так, что стиралка прыгает по ванной. А если прыгает слишком сильно - это не просто шум, это риск разбить машину или повредить ванную комнату.

Мне нужно было научить контроллер определять, что бельё разложено неравномерно и попытаться это исправить. А если не получается - остановить отжим и показать ошибку UE (Unbalance Error).

Я начал искать, как это делают в заводских стиралках. Нашёл патенты LG. В них описывался простой, но гениальный принцип:

Если бельё сбилось в комок, то при вращении барабана скорость не постоянна. Когда комок поднимается - мотору тяжелее, скорость падает. Когда комок падает - мотору легче, скорость резко возрастает.

То есть дисбаланс определяется не по "средней скорости", а по пульсациям скорости в пределах одного оборота.

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

Как это работало на практике

У меня получилась двухуровневая система контроля дисбаланса:

Первый уровень - алгоритм из патента LG. На скорости около 50–60 об/мин (MOTOR_SPEED_STRAIGHTEN_UNBALANCE) я измерял пульсации скорости через таходатчик. Если разница между max и min скоростью превышала порог, я считал это дисбалансом.

Второй уровень - датчик вибрации. Если физика обманывала или бельё вело себя нестандартно, срабатывал обычный датчик удара. Это была "последняя линия обороны".

Если срабатывал любой из уровней, машина:

  1. Останавливала мотор

  2. Включала насос на 20 секунд (чтобы вода не мешала, т.к. с мокрого белья, при попытке отжима в бак могло набраться воды)

  3. Делала 2 цикла "расправления" - вращала барабан в разные стороны на случайной скорости

  4. Пыталась запустить отжим заново

Всего попыток было 20 (DEFAULT_SPIN_COUNT). Если ни одна не удалась - машина пищала, показывала ошибку UE (Unbalance Error) и останавливалась.

После того как мы купили новую стиралку с прямым приводом, я начал анализировать, как она определяет дисбаланс. И мне пришла идея: вместо того чтобы просто смотреть на пульсации скорости, можно было бы строить реальную диаграмму распределения белья. Если бы я записывал скорость и анализировал форму сигнала, можно было бы не просто определить "есть дисбаланс или нет", а понять где именно лежит комок и направленными импульсами попытаться "раскатать" его. Т.е. крутишь барабан медленно, записываешь скорость, получаешь синусоиду. Пик - там, где комок. И потом делаешь несколько коротких рывков в противофазе, чтобы бельё распределилось.

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

Циклограммы стирки: как я воспроизвёл заводскую логику и добавил своё

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

Я нашёл в интернете циклограммы стирки для машин LG того поколения. Это были таблицы и графики, где расписано:

  • сколько минут длится стирка при какой температуре

  • сколько раз барабан вращается в одну сторону, сколько стоит

  • сколько времени занимает набор воды, нагрев, полоскание, отжим

  • какие паузы между циклами

Я взял это за основу. Каждый этап - стирка, нагрев, полоскание, отжим - был реализован с точностью до секунд, как в оригинале.

Вот как это выглядит в коде (program.ino). Например, программа "Хлопок 60":

case 11: // Хлопок 60
  rinsing_speed = rinsing_speed2;
  auto_weighting();
  Del_payten();
  getTotalTime(1, 0);
  water_pour(2, WATER_LEVEL1);
  getTotalTime(2, 0);
  Bio();
  water_heating(60, 34);
  delay2(PROGRAM_PAUSE);
  getTotalTime(3, 0);
  washing(TIME_Washing_4 * 60, 60, washing_speed2, washing_pause1);
  delay2(PROGRAM_PAUSE);
  getTotalTime(4, 0);
  drain_water();
  delay2(PROGRAM_PAUSE);
  spin(1); 
  rinsing_spin();
  delay2(PROGRAM_PAUSE);
  break;

Здесь всё по шагам: набор воды, нагрев, стирка, слив, отжим, полоскание. Времена вынесены в константы, но они соответствуют заводским циклограммам.

Но я не остановился на простом копировании. Я добавил функционал, которого не было в оригинале - специально под требования супруги.
 

Что не вошло в финальную версию

Я реализовал функцию измерения веса белья, которой не было в оригинале.
Принцип простой: раскручиваешь барабан, резко отключаешь мотор и считаешь, сколько импульсов ещё придёт с таходатчика. Чем больше импульсов - тем легче бельё. Теоретически это позволяет автоматически подбирать уровень воды. На практике я от этого отказался. Результаты у меня были нестабильными - одно и то же бельё выдавало разные значения. Нужны были дополнительные тесты и опыты, чтобы понять закономерности. Кроме того, данный функционал зависел от состояния подшипников, которые у меня начали выходить из строя.
Поэтому функция осталась в коде, но закомментированной. Думал, что вернусь к этому, после замены подшипников.

 

Аудит кода ИИ

Я попросил claude оценить мой проект, вот таким промтом:
 

Проведи аудит проекта 2018 года управления стиральной машиной на базе Arduino
Как тебе архитектура проекта?
обработка ошибок?
Сервисная консоль?
Управление мотором, исполнительными механизмами.
Управление индикацией
и т.д. Оцени в целом проект, что сделано хорошо, а что можно было бы сделать лучше?

Общая оценка: это серьёзный любительский проект, а не учебный пример

Это не мигание светодиодом. Человек написал полноценную систему управления реальной стиральной машиной LG WD80187, заменив родной контроллер на Arduino Mega. Проект рабочий — это уже само по себе достижение.

Архитектура

Что сделано хорошо. Код разбит на смысловые модули по функциональности: прерывания, дисплей, мотор, клапаны, насос, ТЭН, программы стирки, ошибки — каждое в своём .ino файле. Такое деление для Arduino-проекта нетипично и говорит о том, что автор думал о поддерживаемости. Все пины вынесены в #define в одном месте, что правильно.

Что можно было сделать лучше. Архитектура — это один большой супер-цикл loop() плюс три таймерных прерывания. Вся логика программ стирки выполняется блокирующими while-циклами и самописным delay2(). Это означает, что пока выполняется washing(), никакой другой логики (кроме прерываний) не работает. Такой подход называют "sequential blocking state machine" — он прост, но хрупок: невозможно изменить программу на лету, сложно добавить паузу без рефакторинга. Классическое решение — конечный автомат (FSM) с явными состояниями и переходами, либо кооперативный планировщик (например, библиотека TaskScheduler). Глобальных переменных в файле v_1_10_12.ino насчитывается больше 80 штук — это почти всё состояние системы, что делает рефакторинг и тестирование крайне сложными.

Обработка ошибок

Что сделано хорошо. Коды ошибок соответствуют оригинальному сервисному руководству LG: FE, 1E, 0E, PE, CE, dE, UE — это очень правильное решение. При ошибке FE (переполнение) вызывается drain_water() перед остановкой — то есть аварийная реакция есть. Есть контроль критического уровня воды WATER_LEVEL4 и аппаратный таймаут мотора.

Что тревожит. В коде очень много критически важных проверок закомментировано или отключено. По всей видимости, эти проверки отключили в процессе отладки и так и не включили обратно.

Сервисная консоль

Это одна из самых сильных частей проекта. Через Serial (115200 бод) доступно около 20 команд: ручное управление каждым исполнительным механизмом, тест датчика вибрации, измерение веса белья, калибровка мотора, полный автоматический цикл стирки/полоскания, просмотр уровня воды и температуры. Команда h выводит справку. Это профессиональный подход — наличие такого инструмента сильно упрощает отладку на реальном железе.

Управление мотором

Это технически наиболее сложная часть и реализована она достаточно серьёзно. Коллекторный мотор управляется через симистор (TRIAC) с фазовым регулированием: прерывание INT0 ловит переход синусоиды через ноль, после чего таймер _timer_MOTOR на 50 кГц отсчитывает угол открытия симистора. Скорость измеряется по таходатчику (INT1). Реализован замкнутый ПИД-подобный регулятор ControlSpeed() с плавным стартом SoftStart(), авто-калибровкой минимальной мощности MOTOR_POWER_MINIMAL через скользящее среднее по массиву 100 значений, и определением дисбаланса через анализ колебаний скорости в функции delay3()

Управление исполнительными механизмами

Клапаны, насос и ТЭН реализованы через флаги и временные метки: при включении записывается millis() + timeout, а в прерывании таймера проверяется превышение. Это хороший паттерн для Arduino — не блокирует выполнение. Каждый механизм имеет собственный флаг состояния (flag_pump, flag_ten и т.д.) и функции включения/выключения с логированием.

Слабое место: нет защиты от одновременного включения несовместимых механизмов (например, ТЭН без воды). Уровень воды WATER_LEVEL7 для минимально безопасного включения ТЭН определён, но проверка в коде не всегда используется.

Управление индикацией

Реализован мультиплексный вывод на дисплей из таймера _timer_DISPLAY на 500 Гц, что выглядит без мерцания. В этом же прерывании опрашиваются энкодер и кнопки — неплохое решение для сокращения кода. Таблица символов segCode[24][7] покрывает цифры 0–9 и буквы A, C, E, F, H, L, P, U, d, n, r, t, пробел и минус — достаточно для отображения всех кодов ошибок.

Интересный момент: при включённой блокировке от детей дисплей мигает между текущим значением и "CL" прямой записью в displayBuf из прерывания — обходя нормальный путь setInfoDisplay(). Это работает, но создаёт скрытую зависимость.

Что сделано хорошо — итог

Фазовое управление мотором с таходатчиком и авто-калибровкой — это не тривиально. Уровни воды через пресостат (частотный датчик) с измерением в Гц — правильный выбор. Многоуровневый алгоритм отжима с определением дисбаланса через вибродатчик и анализ тахо-сигнала. Измерение веса белья по инерции барабана — нестандартная идея. Сервисная консоль с полным набором команд. Система логирования addLog() с временными метками millis(). 19 программ стирки с подробными параметрами. Документация пинов в doc.ino.

Что стоило сделать иначе

Перейти с блокирующего while + delay2 на конечный автомат — это дало бы возможность паузы, изменения программы на лету и более отзывчивый UI. Включить обратно закомментированные проверки безопасности. Сохранять ошибки в EEPROM. Добавить volatile и атомарные секции для переменных, разделяемых между прерываниями и основным циклом. Заменить магические числа (indicator[0] = 22) именованными константами. Использовать структуру или массив структур для описания программ стирки вместо switch/case на 200 строк. Добавить WDT (Watchdog Timer) — особенно с учётом того, что Reset() через JMP 0 определён, но WDT надёжнее.

Общая оценка: крепкая самодельная разработка уровня "умелый любитель / начинающий инженер". Для 2018 года и формата Arduino — достойно. Машина, судя по всему, реально стирала.

7.5/10 — и это честная оценка, а не комплимент

Для понимания контекста: это не учебный проект и не макет. Человек взял реальную сломанную стиральную машину, изучил оригинальные циклограммы LG, разобрался в фазовом управлении симистором, подключил таходатчик, пресостат, MPU-6050, написал 15 файлов кода и получил машину, которая реально стирает. В 2018 году, на Arduino Mega. Это само по себе закрывает планку "любительский проект" и выходит на уровень "прикладная инженерия".

Что тянет вверх: алгоритм управления мотором с обратной связью — это работа, которую не стыдно показать профессионалу. Сервисная консоль говорит о производственном мышлении: автор понимал, что систему придётся отлаживать на живом железе. Циклограммы воспроизведены точно, включая нетривиальные детали вроде параллельного нагрева в "Быстро 30" и охлаждения воды перед сливом в программе 95°C.

Что тянет вниз: одна вещь перевешивает всё остальное — закомментированные проверки безопасности в системе с водой и 220В. Это не архитектурный долг, это незакрытый риск. Всё остальное — глобальные переменные, блокирующий цикл, магические числа — это нормальные издержки hobby-разработки на Arduino, за них снижать оценку было бы несправедливо.

Если бы проверки безопасности не были бы закомментированы — 8.5/10 без разговоров.
 

Рефлексия: стоило ли оно того

Сейчас, когда я пишу эти строки, прошло почти 8 лет с момента, когда из стиралки пошёл синий дым. Я пересматриваю фотографии 2018 года, читаю свой код и честно задаю себе вопрос: доволен ли я этим проектом?

Да. Доволен.

Несмотря на то что я потратил почти 6 месяцев своей жизни по вечерам, я всё-таки сделал то, что хотел. Я сделал рабочую стиральную машину, которая полностью повторяла все программы оригинала. Плюс я некоторые моменты доработал, например добавил ещё один цикл полоскания, потому что в оригинале, когда стиралось постельное бельё, машина, по словам супруги, не до конца выполаскивала и т.д.

Технически я победил.

Сделал бы я то же самое сегодня?

Вопрос сложный.

Если рассматривать с технической стороны, то сегодня всё было бы гораздо проще. Готовые решения для коммутации 220V (ТЭН, насос, драйвер мотора) можно просто купить и подключить к Arduino, не паяя ничего самому. Всё уже есть.

А с появлением искусственного интеллекта написание программы упростилось бы не просто в разы - на порядок. С помощью того же Claude я бы написал этот код за дни, а не за месяцы. Не нужно было бы тратить огромное количество времени на отладку каждого обработчика прерываний вручную.

Готов ли я повторить сейчас? Сначала я думал, что нет. Но, честно говоря, если бы передо мной снова встала такая задача, наверное, я бы взялся (наверное). Просто подошёл бы к ней иначе: с современными инструментами, с готовыми модулями и с ИИ-помощником.

Но есть одно "но". Современные стиральные машины устроены иначе. В них нет отдельного мотора и ремня - теперь это прямой привод. Управление таким мотором сложнее: там не просто фазо-импульсное регулирование, а полноценный инверторный драйвер. Схемотехника там уже совсем другая. Повторить такое в домашних условиях было бы гораздо сложнее, если вообще возможно.
 

Что случилось с машиной потом?

Самый частый вопрос, который мне задают: сколько она проработала с новыми "мозгами"?

Честно? После того, как я сдал машинку супруге в "промышленную эксплуатацию". Полгода. Да коллеги, в итоге получился не "успешный успех", а реальная жизнь.

Но давайте по порядку.

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

Просто, через полгода я получил полностью готовое решение. Включаешь, выбираешь программу, выбираешь режимы и она стирает в полном соответствии с оригиналом. Всё работало, как часы.

Но помните, с чего началась первая часть? Капли воды из сальника. Когда я начинал разработку, подшипник ещё не шумел и я думал так: сначала сделаю электронный блок, а потом заодно поменяю подшипники, сальник и амортизаторы.

Я сделал проект. Машина стирала полгода под управлением Arduino. А потом начали шуметь подшипники. Вода снова попадала через сальник и стало понятно: надо снова разбирать.

Я купил два подшипника, сальник, новые амортизаторы. Разобрал машину, достал бак и тут меня ждал неприятный сюрприз.

Бак у машины оказалась настолько старым и изношенным, что посадочные места под подшипник и сальник уже выработались. Даже если поставить новые детали, они бы не встали герметично. Максимум ещё полгода и снова разбирать.

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


Итог

Полгода разработки -> Полгода использования -> Новая стиральная машина

Стоило оно того? Для меня - да.

Я закрыл свой гештальт, который тянулся со студенческих времён. Я доказал самому себе, что могу сделать не просто программу, а целую систему, которая управляет реальным миром. Я вышел из зоны комфорта 1С-ника, повозился с железом, с опасным напряжением, с ошибками, сожжёнными деталями и синей изолентой.

Если перевести это на язык 1С - это тот самый проект, который "никто не просил", "не окупится" и "проще купить готовое решение".

Да - рационально было купить новую стиралку сразу.

Но такие проекты делаются не ради экономии. Они делаются, чтобы проверить границы своих возможностей. Иногда полезно выйти из привычной среды, где ты всё понимаешь и сделать что-то, где ты снова новичок. Потому что именно там происходит рост.

В 90-х у меня был только ассемблер и фантазия.
В 2018 - Arduino и упорство.
В 2026 - появился ещё и искусственный интеллект.

Инструменты меняются, а суть остается. Если тебе действительно что-то интересно - ты всё равно это сделаешь.

Ссылка на github  https://github.com/Nihixi/The-control-unit-washing-machine-LG-WD80187-on-arduino-mega

Другие статьи автора:

Маркировка в "древней" УТ 10.3 (10.3.6.8) и полноценный ТСД (Online) или как обойтись без перехода на УТ 11.5
Как подключить маркировку в древней УТ 10.3 без перехода на УТ 11.5 - все необходимые объекты, модули и доработки
Автоматическое обновление токенов Честного Знака в 1С
Автоматическое обновление токенов Честного Знака в 1С - готовое решение для УТ, КА, ERP, УНФ, Розницы и Бухгалтерии, которое избавляет от ручных обновлений и остановки процессов.
Дубликатор кодов маркировки (КИЗ) DataMatrix: Расширение 1С с проверкой в Честном Знаке (копирует ЛЮБЫЕ КИЗы!)
Автоматическое обновление токенов Честного Знака в 1С - готовое решение для УТ, КА, ERP, УНФ, Розницы и Бухгалтерии, которое избавляет от ручных обновлений и остановки процессов.
Маркировка остатков товаров на складе: Как сделать все быстро и без ошибок (мой практический опыт)
Маркировка остатков 10 000+ товаров без ошибок — готовое решение, которое исключает человеческий фактор, автоматизирует процесс и работает напрямую с 1С. Пошаговый опыт и готовое расширение внутри.
Маркировка остатков в распределенной рознице: Как промаркировать более 100 тыс. товаров в нескольких десятках магазинов без хаоса и ошибок
Маркировка остатков 100 000+ товаров в рознице без хаоса и ошибок — клиент-серверное решение, где сканируешь ШК в магазине и сразу получаешь КМ на принтере, независимо от кассового ПО. Практический опыт, регламент и готовый комплект кода внутри.

Вступайте в нашу телеграмм-группу Инфостарт

См. также

Робототехника и умный дом (Arduino, NFC, RFID) Бесплатно (free)

История продолжается. Во второй части - реверс-инжиниринг панели индикации, рукописные схемы и первое оживление. Фазо-импульсное управление мотором, три попытки собрать драйвер и финальный успех. Железо готово, всё работает!

23.03.2026    297    GarriSoft    4    

1

Робототехника и умный дом (Arduino, NFC, RFID) Бесплатно (free)

Стиральная машина сгорела. Через несколько дней я решил не чинить её, а полностью заменить её электронику на Arduino. Это оказалось не ремонтом, а полноценным инженерным проектом: с реверс-инжинирингом, работой с опасным напряжением и серией неудачных попыток. Первая часть - это начало пути: синий дым, первые ошибки и момент, когда стало ясно, что это уже не просто "починить технику".

19.03.2026    591    GarriSoft    3    

4

Робототехника и умный дом (Arduino, NFC, RFID) 1С 8.5 Отраслевые Абонемент ($m)

Увлекаюсь "умным домом", но нестабильная работа облачных сервисов производителей IOT (смарт) устройств, проблемы со связью с облачными серверами, сбои в работе интернета и как результат не корректная работа (или не работа) умных устройств, выход новой платформы 1С, открытие Tasmota для себя, и другое сподвигло вернуться к Умному дому на 1С.

1 стартмани

09.02.2026    659    osivv    3    

5

Робототехника и умный дом (Arduino, NFC, RFID) Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Управлять умными устройствами можно не только через Алису... Как насчет 1С?

1 стартмани

15.08.2024    2284    3    vasilievil    1    

4

Робототехника и умный дом (Arduino, NFC, RFID) Бесплатно (free)

Обладая минимальными навыками инженера, вполне можно сделать «умными» относительно недорогие устройства, и управлять всей логикой на 1С. Возможности огромны: управление светом, включением-выключением розеток, автополив, сигнализация и т.д. О самостоятельной реализации в 1С нестандартной логики управления устройствами пойдет речь в статье.

29.02.2024    4169    atland    6    

29

Робототехника и умный дом (Arduino, NFC, RFID)

Кто сказал, что умный дом на 1С сделать невозможно? На конференции Infostart Event 2021 Moscow Premiere Юрий Лазаренко из компании «Цифровой Кот» показал, какие уникальные прикольные вещи можно делать на базе IoT (интернета вещей), где в этом всем 1С, и как это все между собой интегрируется.

19.06.2023    7079    TitanLuchs    28    

20

Мобильная разработка Робототехника и умный дом (Arduino, NFC, RFID) 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Хотите попробовать свои силы в разработке собственного мобильного приложения на 1С для управления светом и электроприборами в своём доме? Добро пожаловать! Хочу поделиться собственными наработками в этой области. Это будет приложение, разработанное для мобильных устройств на базе Android. Основное предназначение: управление включением/выключением устройств, работающих от сети 220 V (свет, телевизор, чайник, вентилятор, микроволновка и т.д.). Управлять будем: из приложения, голосом, на заданный промежуток времени, интенсивностью света, расписанием работы.

1 стартмани

19.12.2022    5488    osivv    9    

25
Для отправки сообщения требуется регистрация/авторизация