Итак, у меня уже есть скетч, в котором я на скелет нанизал все остальное мясо, программы. Во-первых, у меня подключены все необходимые библиотеки. SPI и Ethernet для работы с Ethernet шилдом и QuadDisplay для работы с дисплейчиком. Затем определены все пины, к которым я подключу устройство согласно нашей таблице: помпу, пищалку, дисплей. И два пина у нас занимает самодельный датчик наличия воды. То есть просто два провода, которые вода будет замыкать. Затем подключаются датчики. Для влажности почвы определение — SOIL_PIN, для температуры — TEMP_PIN, для освещенности LIGHT_PIN. Затем из скелета переехали сюда вот эти вот макроопределения с названиями состояний. Затем я дал названия возможным ответам функции Web Request. Если никакого запроса нету, у нас будет определено «нет клиента» (WEB_NO_CLIENT). Если пришел запрос на принудительный полив — это WEB_FORCED_WATERING. И если просто запрос на отправку данных — будет вот этот WEB_CLIMATE_REQUEST. Затем я сделал макроопределение, в котором хранится количество миллисекунд, на протяжении которых будет осуществляться полив. То есть можно сразу здесь это настроить. Ну у меня это будет секунда. И три еще настройки, которые отвечают за пороговые значения, которые параметры климата должны переступить, чтобы наступили все условия для полива. Затем у нас уже была вот эта строчка, в которой мы создаем глобальную переменную STATE и инициализируем ее значением STATE_STANDBY. И также, помимо gotWater у нас появляется три глобальных переменных для хранения параметров климата. Глобальными я их сделал, чтобы упростить передачу в Интернет. То есть у нас одна функция будет снимать эти показания, сохранять сюда, другая функция будет брать то, что сохранено в этой глобальной переменной и отправлять по запросу. Далее идут строки, которые мы уже видели, когда изучали отправку данных. Это массив с MAC-адресом Ethernet шилда, объект IP с нашим IP-адресом, и указываем также номер порта. Затем объект EthernetClient создаем вот здесь вот. И здесь же у нас создается объект типа строка, в котором будет храниться запрос. В setup у нас настроены все пины. Обращу ваше внимание на то, что тот пин, где мы будем считывать наличие воды, я сделал INPUT_PULLUP, то есть он подтянут к питанию. Поэтому, когда мы будем тестировать наличие воды, мы будем переключать второй пин, откуда уходит сигнал, в состояние low. И, соответственно, когда второй пин — вот этот вот WATER_OUT_PIN — будет в состоянии low, и он будет замкнут с пином WATER_IN_PIN, мы на WATER_IN_PIN получим изменение состояния с высокого на низкое. Собственно, здесь же я запускаю для начала на WATER_OUT_PIN high, и потом его на low буду переключать. А затем у нас запускается, работа Ethernet шилда, и запускается сервер в нем. В лупе у нас все то же самое, что мы только что рассмотрели в скелете программы. Единственное, что я добавил вывод текущего состояния на дисплейчик. Ровно таким же образом выглядит и вот этот switch, только цифры 1 и 2 я заменил на вот эти макроопределения, чтобы сделать код более понятным. Когда мы его будем перечитывать через несколько дней, нам уже будет сложно понять, что такое 1, что такое 2. Здесь становится понятно, о чем идет речь. Остальные части остались такими же. Затем начинается определение всех упомянутых в этом самом случае функций. alarm ничего не возвращает, и внутри нее просто вызывается tone. То есть по большому счету мы могли бы просто вызвать tone в том месте, где вызываем alarm, но, например, мы захотим сделать сигнализацию более изощренной, и тогда у нас код разрастется. Вместо того чтобы у нас распухал автомат, лучше у нас распухнет alarm, и все будет на своем месте. Там мы знаем, что нужно вызвать функцию про сигнализацию, и нам неважно, что там внутри нее. Сама функция сигнализации при необходимости увеличится. Затем такая же нехитрая функция для полива. Она ничего возвращать не будет, а в ней — пин, куда подключен модуль силовой ключ, через который подключена помпа. Включается. Затем мы ждем какое-то время, указанное в макроопределении WATERING_MILLIS. В моем случае — секунда. И выключаем полив. Далее у нас идет определение функции CHECK_CLIMATE, которая проверяет, что там с параметрами климата, и уже возвращает «булево» значение: истину — если наступили условия для полива, и ложь — если климат пока не позволяет поливать. Вначале мы в три объявленные в начале глобальные переменные сохраняем текущие значения с аналоговых входов, куда подключены климатические датчики: влажности почвы, температуры и освещенности. И затем происходит самая простая проверка, пересекли ли эти значения граничные значения. И если все три параметра сходятся — т.е. у нас достаточно светло, т.е. не ночь; у нас достаточно тепло, т.е. не холодно; и почва недостаточно увлажнена, т.е. суха, — вот если всё это совпало, тогда мы будем возвращать «истину», т.е. пора поливать. Иначе — будем возвращать «ложь». Понятно, что это самый примитивный способ ухаживать за растением. Эту функцию CHECK_CLIMATE вы можете усложнять, вот если вы серьезно намерены делать автоматизированный парник, грядку и так далее, сделать ее гораздо более сложной и учитывать разные комбинации условий. То есть, например, можно поливать ночью, но нельзя поливать на холоде. Или при большой влажности почвы и большой освещенности, там, нельзя поливать днем. Ну не знаю, в общем, могут быть разные комбинации. Более того, у вас может быть вовлечено больше датчиков, да, комбинаций становится еще более. И здесь вы можете внутри нее наворотить очень много. Но в итоге она все равно вам вернет «истину» — если поливать пора, и «ложь» — если поливать не пора. Далее у нас идет функция checkWater, т.е. «проверка наличия воды», которая также вернет «булево» значение. Истина — если вода есть, и ложь — если воды нету. Вначале WATER_OUT_PIN мы делаем low. И считываем WATER_IN_PIN. В случае, если между ними контакт есть, на WATER_IN_PIN мы получим низкое значение. А если контакта нету, то высокое. Соответственно, digitalWrite нам вернет истину, если контакта нет. То есть провода не погружены в воду. В этом случае мы говорим, что воды нету. То есть мы присваиваем значение глобальной переменной gotWater. В противном случае, если они погружены в воду, и контакт есть, мы возвращаем «истину». И после этого выключаем, то есть переводим в состояние high вот этот вот WATER_OUT_PIN. И возвращаем gotWater, несмотря на то, что оно глобальное. Да, это не самое изящное решение, но в данном случае так было сделать проще, и заодно мы вспомнили, чем глобальные переменные отличаются от всех остальных. Далее начинается функция webRequest. Она уже будет возвращать данные типа integer — целые числа — поскольку у нее несколько вариантов ответа может быть. Здесь, по сути, перенесены строки, которые мы рассматривали раньше в примере с выдачей страницы. Если хотите, заново пролистайте назад и посмотрите, что это все значит. Мы изменили только то, что в случае наличия в запросе подстроки ?water, т.е. пришла команда на принудительный полив, мы будем возвращать вот это вот значение, спрятанное за этим макроопределением. То есть webRequest говорит, что требуется принудительный полив. И в другом случае, когда пришел конец строки, и строка пустая, мы будем возвращать значение, спрятанное за макроопределением WEB_CLIMATE_REQUEST. Я уже даже сейчас не помню, какая это цифра. Мне это и не нужно знать. Но я знаю, что в автомате мы получим запрос на выдачу данных в сеть. [ПРОКРУЧИВАЕТ СТРОЧКИ] Ну и в случае, если запроса не было, а функция вызвана, мы скажем, что нет клиента. [ПРОКРУЧИВАЕТ СТРОЧКИ] И остается функция sendPage, которая также ничего не возвращает. В ней также перенесены фрагменты из примера с выдачей страницы, собственно, «выдача страницы». Здесь все написано то же самое, что мы видели раньше. Небольшие изменения я внес, опять же, ниже. Во-первых, я убрал цикл, который был в стандартном примере, и там последовательно выводились все значения, считанные с аналоговых входов. Теперь мы пишем названия измеряемого параметра и собственно значение, которое хранится в соответствующей глобальной переменной, про влажность почвы, температуру и освещенность. Напомню, что вот этот тег <br /> является переводом на новую строку. А затем у нас есть некий обусловленный вывод. Если у нас в глобальной переменной gotWater хранится «истина», то есть вода есть, мы будем писать, что с водой порядок и выводить вот эту вот ссылку, которая позволит осуществить принудительный полив. В противном случае, если воды нету, мы просто будем писать, что нет воды, и даже у нас не будет возможности нажать на ссылку запроса на принудительный полив, ну потому что нечем поливать. Вот и все. Таким образом, мы рассмотрели весь код. Наверное, это получился самый большой код за время нашего курса — почти 200 строчек. Впереди может быть будет и больше, но пока что мы такой большой скетч еще не писали. Давайте теперь посмотрим на то, как устроена наша поливалка.