В наброске, где мы реализуем автомат, я сделал макроопределение, где дал всем состояниям имена. С точки зрения контроллера это просто числа, но чтобы нам было удобнее и понятнее писать программу, я каждое из чисел назвал каким-то образом: состояние ожидания, полива, нет воды и отправка данных, отправка страницы. Затем я сделал глобальную переменную STATE, в которой будет храниться текущее состояние, и для начала положил в нее STATE_STANDBY, то есть 0 – состояние ожидания, из которого мы начнем работу. Также я сделал глобальную переменную, в которой будет храниться наличие воды. Истина, если вода есть и ложь, если воды нет. Пока что я не трогаю setup, потому что в нем ничего касающегося работы автомата нет. И сразу посмотрим, что происходит в основном цикле. Основной цикл у нас состоит из отслеживания состояния и перехода из состояния в состояние. Для этого я использую новую для нас управляющую конструкцию switch. Давайте посмотрим, как она работает. Ей можно передать какое-то выражение, в данном случае переменную, и внутри нее рассмотреть разные случаи, которые зависят от значения данной переменной. Каждый случай рассматривается после ключевого слова case. То есть в случае, если переменная STATE равна вот этому выражению... в нашем случае это STATE_STANDBY, то есть там число 0, мы будем делать все вот эти действия, сейчас разберем, какие. И в конце здесь написано ключевое слово break, которое выходит из конструкции switch. О нем полезно не забывать, потому что у вас могут случиться ситуации, когда выполнится что-то лишнее, если из switch не выйти. Итак, что же у нас будет происходить в состоянии ожидания? Для начала будем проверять наличие воды. Функции checkWater пока что нету, но она будет нами описана, мы уже понимаем, что нам нужно проверить, есть ли вода. Потому что если ее нет, мы не сможем перейти в состояние полива, он будет запрещен. Затем, если у нас пришел некий запрос из сети и есть вода, мы перейдем в состояние «полив». Соответственно нам потребуется еще одна функция webRequest, которая сможет вернуть разные значения в зависимости от того, какого типа запрос пришел. Здесь мы ожидали запрос типа 1, это запрос на принудительный полив. И в ветке else if мы можем проверить, не пришел ли запрос другого типа, то есть просто запрос на выдачу страницы. И если он пришел, независимо от наличия воды мы можем перейти в состояние отправки данных STATE_SENDPAGE. Затем помимо проверки запросов из сети мы проверяем состояние всех датчиков. Если у нас функция checkClient вернула истину, то есть сложились все условия для полива, а также есть вода в это же время, мы перейдем в состояние «полив». Это, как вы помните, еще одна стрелка в состояние «полив». И если же в переменной gotWater лежит значение «ложь», то есть if not gotWater, мы перейдем в состояние «нет воды». Откуда возьмется значение в gotWater? Допустим, наша функция checkWater будет работать с той самой глобальной переменной, которую мы завели, и для всех вот этих вот выражений уже будет вычислено значение в самом начале вот этого case. Посмотрим дальше. Дальше описан случай с состоянием полива. Здесь мы будем просто вызывать специальную функцию для полива, мы ее опишем в дальнейшем. Здесь нас подробности ее действия не будут интересовать. После окончания полива у нас случается событие «конец полива» и мы возвращаемся в состояние ожидания. И снова break, выходим из switch. Далее. Случай, когда мы в состоянии «нет воды». Здесь нам нужно подавать сигнал звуковой. Тоже опишем это в функции alarm. И на всякий случай проверять, не хочет ли какой-то пользователь в это время посмотреть, что у нас с климатом. То есть снова проверяем, нет ли запроса. Цифры 1 и 2, которые у нас обозначали разные типы ответов от webRequest, мы также зашифруем в макроопределении. Вот такие вот. Это ответы функции webRequest, которые, еще раз, эквиваленты цифрам 1 и 2. Так, согласитесь, более понятно. Если запрос из Интернета является запросом климата, значит, мы переходим в состояние отправки данных. А если запроса не пришло, мы здесь проверим, а не появилась ли вода, пока мы были в состоянии «нет воды». Если она появилась, то есть checkWater нам вернет истину, мы переходим в состояние ожидания. И последнее состояние case STATE_SENDPAGE, то есть состояние отправки данных будет вызывать у нас функцию отправки данных, и по ее окончании переходить в состояние ожидания. И снова break. Кроме этого я здесь написал, но сразу же закомментировал еще одно ключевое слово – default. Раз уж мы с вами познакомились с конструкцией switch, о нем нельзя не сказать. Его можно использовать для описания тех действий, которые нужно предпринять, если не случилось ни одно из предусмотренных состояний. То есть все case не сработали. У нас такого быть не должно, потому что мы из состояния в состояние четко переходим и не можем оказаться где-то между ними. Поэтому я этот самый default закомментировал. Ну, в противном случае здесь можно было бы описать еще и поведение по умолчанию, скажем так. Теперь мы расширим этот код, дописав все вот эти вот функции, которые мы обозначили в автомате. Более того, нам понятно, кто из них должен возвращать какие-то значения, и какие значения они должны возвращать, чтобы все сработало верно. Ну а кроме этого давайте на секунду взглянем в нашу основную схему, и я предлагаю ее дополнить дисплеем, то есть мы подключим еще одно служебное устройство, дисплей, на котором будем выводить текущее состояние. Нам это может пригодиться при отладке, мы будем видеть, что устройство считает, в каком оно сейчас состоянии находится.