Сегодня мы рассмотрим передачу данных по воздуху. Для начала определимся с тем, что нам нужно и какие варианты использования у нас есть.

Как я уже говорил в предыдущих статьях, Arduino плохо приспособлена для работы с видео и звуком, потому объемы данных, которыми мы будем оперировать будут довольны малы. В этом примере я буду рассматривать использование радиоканала для передачи состояния датчиков с удаленных моделей на главный. Также мы рассмотрим передачу команд на выполнение на удаленный модель (например, включение-выключение реле). То есть с задачей определились.

Выбор способа реализации.

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

1. Wifi.

Казалось бы, оптимальный вариант. Но давайте посмотрим все ли так радужно. Из недорогих вариантов для реализации Wi-fi у нас есть модули ESP8266. Стоят ни недорого – около 120-200 рублей. Продаются в нескольких вариантах исполнения, который отличаются количеством пинов ввода-вывода, наличие антенны, форм-фактором исполнения и прочим, но принципиальной разницы в работе с разными модификациями нет. И, если вы хотите соединить 2-3 удаленных модуля, проблем не возникнет. Но, если вам надо использовать десять или больше удаленных контроллеров (а для создания, например “Умного дома” далеко это не предел), то стоимость уже возрастает довольно существенно. Тем более, как уже сказано выше, объемы передаваемых данных будут довольно малы и, в нашем случае, пропускная способность Wi-fi является избыточной. Тем более, что с точки зрения программной реализации данное решение является наиболее затратным по размеру кода, а соответственно, времени. Также из минусов отмечу немаловажный фактор, что для подключения модуля нам потребуется источник питания строго от 3.3 вольт до 3.5 вольт (как мы помним из предыдущих статей, для Arduino подойдет любое напряжение в диапазоне от 5 до 12 вольт). В случае с ESP8266, конечно можно обойтись вообще без Arduino для считывания удаленных датчиков прямо с ESP8266, но вот команды на исполнение с этим Wifi-модулем выполнить не получится (точнее получится, но не совсем так, как нам надо). В связи с вышеописанным – откажемся от реализации по Wi-fi.

2. Радиоканал на частоте 433 MHz.

Довольно распространённая частота для использования домашней автоматики. Отличается низкой стоимостью модулей (около 30 рублей за штуку), простой использования и подключения (всего 3 провода, включая питание и землю). Дальность работы передатчиков в условиях городской квартиры – 50-60 метров, в условиях загородного дома – до 120 метров. Скорость передачи невысокая, но более чем достаточная для нашей задачи. Также, сигнал на этой частоте довольно хорошо подходит через всевозможные препятствия. Из минусов – однонаправленность: для одновременного приема и передачи нам понадобится подключить приемник и передатчик, но при способе их подключения это не такая уже и проблема. Второй минус – почти всегда модулю недостаточно встроенной антенны, так что придется припаять её вручную. Замечу, что процедура весьма простая, и в качестве антенны можно использовать просто кусок провода, длинной 17 см). Третьей недостаток – передаваемые данные будут приниматься всеми приемниками в радиусе действия и вам необходимо программно будет отсекать данные, предназначенные для конкретного модуля.

3. Радиоканал на частоте 2.4 Ghz.

Для его реализации можно использовать модуль nRF24L01+. По удобству работы и подключения – это промежуточный вариант между Wi-Fi и 433 MHZ. По стоимости – те же 30 рублей, но правда уже за двунаправленный модуль. Пропускная способность чуть больше, чем у 433 MHz. Для подключения используется 7 проводов – много. А вот с дальностью тут выходит засада. Дело в том, что эта частота довольно чувствительна к геометрии антенны, а реализация встроенных антенн на моделях одной партии может существенно отличаться. При хорошей внешней антенне дальность в помещении немного ниже, чем у 433 MHz, но с такой антенной теряем компактность. По моим наблюдениям – довольно капризные модули. Так что я бы не советовал использовать этот вариант для начинающих.

Итак, что у нас в остатке? Для связи между 2-3 модулями особой разницы нет, но для чего-то более масштабного я бы рекомендовал все же реализовать радиоканал на частоте 433 MHz. Вот его и будем рассматривать.

Что мы хотим сделать?

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

Что нам понадобится.

1. Дополним главный модуль приемником и передатчиком.

Линии Data приемника и передатчика подключим к любым двум пинам. Обязательное условие – пины должны работать с ШИМ. В нашем примере это будут пин 3 для приемника и пин 5 для передатчика. Выглядеть это будет примерно так:

Arduino прием и передача данных

Таким образом нам понадобится:

– Уже готовая схема на Arduino.

– Передатчик FS1000A или аналогичный с припаянной антенной. Как я уже говорил антенна должна быть длинной 17 см. На схеме антенны обозначены бело-желтым и бело-зеленым цветом (~30 рублей).

– Приемник XY-MK-5V или аналогичный с припаянной антенной (~30 рублей).

2. Беспроводной сенсор.

Схема удаленного датчика температуры будет выглядеть следующим образом:

Схема удаленного датчика температуры Arduino

Нам понадобится:

– Arduino (В нашем примере Nano) (~150 рублей).

– Передатчик FS1000A или аналогичный с припаянной антенной (~30 рублей).

– Датчик температуры DHT. В принципе, он может быть заменен на любое устройство, данные с которого нам надо получить. В примере я взял именно этот датчик по той причине, что хотелось бы рассмотреть передачу 2 и более значений.

– В данном случае температуры и влажности.

3. Исполняющий модуль (фактически беспроводной выключатель).

Реле с беспроводной связью будет выглядеть следующим образом:

Arduino реле с беспроводной связью

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

Для реализации этого модуля нам понадобится:

– Arduino (в нашем примере Nano) (~150 рублей).

– Приемник XY-MK-5V или аналогичный с припаянной антенной (~30 рублей).

– Реле SRD-05VDC-SL-C или аналогичное (~50 рублей).

Разработка протокола.

В случае передачи одного значения код получается крайне простым. Но такая система выйдет довольно бесполезной или относительно дорогой (неужели для передачи значений влажности и температуры нам использовать два передатчика?). Если же мы хотим передать несколько значений, то необходимо разработать протокол передачи. Не стоит пугаться слов “разработка протокола” – не все так сложно. Для чего же желательно использовать протокол:

  1. Мы хотим, чтобы передача данных происходила не отдельными значениями, а наоборот величин, которые будут описывать от какого модуля пришли данных, какому модулю предназначаются, что с ними делать и, собственно, сами данные. Этим мы достигнем универсальности и простоты подключения новых компонентов в нашу систему.
  2. При всем вышеописанном желательно, чтобы размер одного пакета был минимальным. Этим мы уменьшим количество поврежденных пакетов при передаче.
  3. Также, используя пакеты мы можем указывать их контрольную сумму, что позволит легко вычислить поврежденный пакет и попросить удаленное устройство повторить его.
  4. В конце концов мы можем шифровать данные при передаче для повышения безопасности.

Часть этих тем выходит за рамки статьи. Я рассмотрю только основы, остальное, при необходимости вы сможете изучить сами. Итак, попробуем представить структуру нашего пакета:

НазваниеТип поляКакие значения может приниматьОписание
device_idЦелое число, 1 байт1-255Номер устройства, с которого отправляется пакет. Мне хватит максимум 255 устройств (то есть размер поля будет в 1 байт). Номер устройства 0 я зарезервировал для дальнейших нужд. Если вам не хватает этого количества – увеличиваем размер до 2 байт (тогда получим 65535 значений).
desination_idцелое число, 1 байт.0-255Номер устройства-получателя. А вот тут 0 используется для того, чтобы этот пакет был принят всеми устройствами.
packet_idцелое число, 2 байта0-65635Идентификатор пакета. В него записывается случайное число, которое идентифицирует пакет как уникальный (относительно уникальный, но опять таки, для моих нужд этого хватает). Для чего используется? Ну например, чтобы попросить перенаправить битый пакет.
commandЦелое число, 1 байт.0-255Номер команды. По сути дела это инструкция, что делать с данными этого пакета. Это поле рассмотрено ниже более подробно
dataЦелое число, 2 байта0-65535Собственно, сами данные. Но если вы внимательно читали предыдущие статьи вы спросите: “Но ведь датчик DHT выдает не только целые значения”. Все верно. Но в связи с трудностью обработки числе с плавающей точкой мы будем передавать значения от этого датчика, умноженное на 100. При получении – делить на 100. В результате мы получим нужное нам значение с плавающей точкой на выходе, используя целочисленный тип при передаче.

Вроде все ясно, за исключением поля команды. Ниже приведу созданную для этого проекта таблицу команд.

НомерОписание команды
1Температура с датчика DHT_23. Значение датчика увеличено в 100 раз.
2Влажность с датчика DHT_23. Значение датчика увеличено в 100 раз.
3Выключатель в мастерской

Таким образом мы видим, что если в поле команды содержится значение “1”, то в поле данные у нас будет значение температуры от датчика DHT_23, которое при получении надо разделить на 100.

Вот собственно и весь наш протокол передачи. Переходим к программированию. В самом посте не буду приводить полностью код всей прошивки, остановлюсь только на некоторых новых (по сравнению с предыдущим проектом) моментах.

Совет: при отладке кода не забывайте о возможности вывода в консоль через функцию Serial.print. Этих строк в коде нет, но вы всегда можете их вставить в нужных местах.

Модуль беспроводного датчика.

Arduino код беспроводной модуль

В строках 1-4 подключаем библиотеки. VirtualWire и EasyTransferVirtualWire нужны для работы радиомодулей. Библиотека EEPROM обеспечивает работу с внутренней энергонезависимой памятью микроконтроллера. Мы можем использовать 512 байт памяти. В нашем случае мы помещаем в нее номера передаваемых пакетов и ID самого устройства.

В строках 7-9 задаем пины, к которым подключены устройства, в строках 11-14 – команды, которые рассмотрели выше.

В строке 16 задаем начальное значение кода устройства. Если этот микроконтроллер мы используем первый раз, то переменная помогает определить это.

В строке 19 объявляем новый объект для работы с передатчиком, а в строке 22 – для работы с термодатчиком.

В строках 25-32 описывается структура нашего пакета, который мы разобрали выше, в описании протокола и создаем переменную mydata, в которой будет содержаться данные нашего пакета. Это новый для вас тип переменной – т.н. составная переменная (в языке С) она называется “структура”. Чтобы обратиться к каждой части этой структуры мы должны указать имя структуры и, через точку – имя её компонента. Например, чтобы прочитать поле команды из нашего пакета, мы должны написать mydata.command, поле данных – mydata.data

Ардуино код беспроводной модуль

Тут мы видим две функции работы с памятью – записи (строки 38-45) и чтения (строки 51-57) ячеек памяти EEPROM. В качестве параметров в них передается адрес, из которого надо считать 2 байта (переменная типа int имеет как раз 2 байта). Останавливаться на этом не буду, вам достаточно будет просто знать, что передавать, в эти функции (параметр в скобках), и что они возвращают (сами тип функции).

В EEPROMWiteInt передаем адрес ячейки памяти и значение, которое надо туда записать. Функция возвращает пустое значение (слово void перед функцией, видите?) т.е., говоря простым языком, не возвращает ничего.

EEPROMReadInt возвращает прочитанное из ячейки памяти “p_address” значение типа “unsigned int”.

Что такое слово unsigned? Дело в том, что, как мы говорили уже выше максимальное значение, которое может быть записано в переменную типа int – 65535, а минимальное – 0. Но как же отрицательные числа, спросите вы, как их представить? Вот как раз в связи с необходимостью работать с отрицательными числами этот диапазон (65535) был разделен на 2 части. По умолчанию числа от 0 до 32767 представляются как целе, а вот 32768 – как “-1”, 32769 – как “-2” и т.д. Таким образом у нас получится диапазон от -32769 до 32769. Если же нам не нужны отрицательные числа – то перед типом переменной надо указать, что она unsigned (т.е. беззнаковая) – в этом случае все числа будут считаться целыми и диапазон от 0 до 65535.

Кстати, на примере этих функций вы можете увидеть такую мощную часть языка С++, как работу с памятью.

Arduino код

в этой функции мы инициализируем все наши подключенные устройства.

В строке 63 задаем пину 13 работу на вывод (вы же помните, что по умолчанию все пины Arduino работают на вход?).

Строки 65-67 устанавливают начальное значение для нашего передатчика, как то, указывают структуру пакета для передачи, номер пина передатчика и скорость передачи – 2000 байт в секунду чем достаточно, при больших значениях будет больше ошибок при передаче. Если прием плохой – советую уменьшить скорость.

Строка 69 инициализирует датчик случайных чисел (псевдослучайных).

В строках 71-77 мы проверяем, установлено ли ID-устройства (оно содержит в 0 и 1 байтах EEPROM). Если не установлено, то задаем его.

Arduino code

тут у нас идёт функция формирования пакета, заполнения его данными и, собственно, передача. В функцию передаются значения id передающего устройства, id устройства назначения, код формирования и данные. Возвращает функция номер сформированного пакета.

В строках 89-93 мы заполняем поля структуры данными. Обратим внимание, что все поля структуры должны быть определены перед отправкой. Далее, в строках 95-96 мигнем светодиодом, который просигнализирует нам, что пакет отправляется.

Сама отправка происходит в строках 99. в строке 100 мы возвращаем id отправленного пакета.

Arduino код установки беспроводного модуля

Ну и теперь наши любимые функции setup и loop. Остановлюсь только на строках 117-120, где вызывается функция передачи пакета за значением температуры и 121-124 со значением влажности.

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

Итак, давайте рассмотрим модуль приемника. Функционально у нас получилось беспроводное реле, способное работать в сети на 220В.

Принцип работы реле крайне прост: при подаче сигнала HIGH на управляющий контакт реле (на схеме он подключен оранжевым проводом) замыкаются 2 выхода реле, подключенные к 220В(они наверху схемы). Ещё раз приведу схему:

Arduino схема

Код беспроводного реле почти полностью соответствует коду описанного мной раннее беспроводного сенсора, за несколькими отличиями. Во-первых, из кода убраны все упоминания о датчика DHT (и правда, зачем он нам тут?). Далее.

5 #define LED_PIN 13 //светодиод
6 #define RECEIVE_PIN 5 //куда подключен передатчик
7 #define RELAY_PIN 6 //куда подключено реле

Мы видим, что на пин 5 подключен передатчик, а на пин 6 – управляющий выход реле.

Ардуино код

Функция инициализации приемника initReceiver() – это почти полный аналог инициализации передатчика из передатчика с начала статьи. Из отличий:

– в строке 50 и 52 указываем режим работы пина с подключенным реле и переводим реле в выключенное состояние.

– В строке 51 инициализируем консоль. В консоль мы будем выдавать сообщение к примеру, о неправильной команде. В реальном устройстве эти сообщения могут быть заменены на вызов функций обработчиков.

– в строке 56 мы переводим устройство в режим приема.

Ардуино код беспроводного модуля

А теперь рассмотрим функцию приема команд и их исполнения. В строке 71 мы проверяем, был ли принят пакет. Если нет, то не делаем ничего, а вот если принят – смотрим дальше. В строке 74-75 анализируем в полученном пакете значение desination_id (вы же помните, что в этом поле указывается номер устройства-приемника, которому был предназначен пакет?). Если пакет предназначен всем (BROADCAST) или данному устройству – то будем обрабатывать команды, иначе – выходим. В строке 77 проверяем, какая команда поступила. Предположим, что команда “3” (помните таблиук с кодами команд в начале статьи?) получена, включаем светодиод и будем смотреть что за данные мы получили.

Небольшое отступление №1: Операторы “=” и “==”.

Вы уже должны были заметить, что в скобках у операторов if указан странный символ двойного “=”. Надо заметить, что в С++ некоторые вещи не являются тем, чем кажутся (в общем-то это справедливо не только для С, да и не только языков программирования).

Так вот, знак “=” в С\С++ не являются знаком равенства. Этот знак называется “оператором присваивания” и говорит о том, что значению слева от знака надо присвоить значение, находящееся справа от знака “=”, т.е. запись “variable=10” говорит о том, что значение переменной vaiable после этого оператора будет иметь значение 10. Скорее всего вы это уже поняли, но не задумывались над этим вопросом.

Другое дело оператор “==”. Это как раз и есть “знак равенства” в его математическом понимании. И результат выражения “vaiable == 10” , будет зависеть от того, чему равно значение переменной vaiable. Если vaiable было равно 10, то такая конструкция выдаст логическое значение “true” (истина), в противном случае – “false” (ложь). И в зависимости от этого значения оператор if будет выполнять кусок куда в фигурных скобках после него (при значении “true”) или пропустит (при значении “false”).

Немного отступление №2: Оператор switch-case.

В принципе, для того, чтобы проанализировать какой код команды получен и отправить его на выполнение можно воспользоваться несколькими “if”, но при наличии 3 и более более вариантов выбора это просто неудобно и код получается крайне запутанным. Посему рассмотрим вот такую конструкцию:

switch (variable)
{
case 2:
function1();
break;
case 4:
function2();
function3();
break;
case n:

break;
default:

break;
}

Тут мы видим так называемый оператор множественного выбора. Что же он делает? А все очень просто: в зависимости от того, какие значения принимает переменная “vatiable” будет выполняться кусок кода, описанный в соответствующем “case”. Например, если varible == 2, то будет выполнена функция function1(), если varible ==4, то function2() и function3(). Таких кейсов вы можете написать очень много, главное, чтобы каждый из них заканчивался командой “break”.

И вы уже заметили, что в одном месте вместо слова “case …” используется “default”. В принципе этот кусок может отсутствовать вообще, но, если он есть, в нем мы можем описать, что надо делать программе, если переменная variable будет равна какому-то другому значению, кроме как указанному нами в кейсах.

Вернемся к нашему скетчу. Теперь не составит труда прочитать, что при получении значении “1” мы включим реле, при значении “0” – выключим, а при любом другом значении – выведем в консоль сообщение об ошибке.

Что же можно поменять в этой схеме, что улучшить?

Ну, во-первых. в реальности нам нет нужды использовать вывод в консоль (не будет же у нас подключен компьютер к каждому устройству?).

Во-вторых, мы можем вместо реле использовать димер. В этом случае надо будет в данных к команде передавать не “1” или “0”, а несколько значений, которые будут соответствовать нашему уровню освещения (например, числа в диапазоне от 0 до 255).

В-третьих, а что мешает нам использовать этот модуль для управления несколькими реле? Например, в разных зонах комнаты или даже в нескольких помещениях? Или не только светом, но, например, ещё и вентиляторами в гараже. Надо всего лишь добавить на свободный пин управляющий контакт от другого реле или димера (напомню, что для димера это должен быть пин с ШИМ), и предусмотреть в командах (в таблице команд и обработчике switch-case) это устройство.

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

Как это будет работать?

соберем вот такую схему:

Arduino схема беспроводной контроллер

С помощью приемника, подключенного к пину 3 мы будем получать данные с радиосенсора, рассмотренного в начале статьи. Кнопка на 6 пине будет отвечать за включение беспроводного реле, описанного в середине статьи. Работает кнопка так:

– изначально она выключена.

– При нажатии и отпускании кнопки включается светодиод и передается сигнал на включение реле.

– При повтором нажатии гасим светодиод и передаем сигнал на реле о выключении.

Таким образом мы видим, что при включенном реле у нас горит светодиод, при выключенном – погашен.

Что нам понадобится?

– Arduino Nano/Uno (при большом количестве внешних модулей – Arduino Mega).

– Приемник и передатчик на 433 MHZ.

– Тактовая кнопка.

– Резистор на 220 ОМ.

– Светодиод.

Как я уже говорил, полученные данные нам надо будет куда-то выводить. Для этого можно использовать дисплей (например, LCD-1602) или Ethernet-shield. Описание подключения этих устройств выходит за рамки темы этой статьи, так как подключение дисплея хорошо описаны на сайте Амперки, а работу с сетевым модулем я освещал в посте про метеостанцию.

Схему собрали, теперь рассмотрим скетч.

Скетч.

Фактически, скетч по большей части будет состоять из написанных нами функций из начала и середины статьи. Рассмотрим основные отличия:

код беспроводной контроллер

в строках 5-13 описываем используемые пины и команды. Обратите внимание, в строке 19 мы создаем два объекта – для приемника (ETT) и передатчика (ETR). Далее, в строке 20 объявляем переменную, которая отслеживает состояние кнопки – нажата или нет. Переменная строке 21 отвечает за состояние реле и светодиода (true при включенном и false при выключенном состоянии).

Переходим к обработке приема пакетов.

программирование беспроводной контроллер

Тут должно быть все понятно, код почти аналогичен тому, что был в модуле беспроводного реле. Обращу внимание, что строки 97-98 и 101-102 надо заменить на вызов функции для вывода соответствующего значения.

Теперь посмотрим, как будет обрабатываться нажатие кнопки.

Arduino программирование

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

Наконец, нам осталось рассмотреть функции setup и loop.

Ардуино программирование

В строках 150-152 задаем направление работы пинов Arduino, к которым подключены светодиод, передатчик и кнопка. Пин приемника не определяем (вы же помните, что по умолчанию пины работают на вход?).

Остановлюсь на режиме INPUT_PULLUP. Этот режим выдает false(0), когда кнопка нажата и true(1), если отпущена.

В функции loop в строке 160 мы принимаем и разбираем данные с модуля сенсора. В строке 161 мы проверяем был ли изменен статус кнопки. Если да – то (в строках 163-166) отправляем значение переменной ledEnabled на модуль реле. Помните, там мы писали. что в команде 3 при значении 1 мы включаем реле, при 0 – выключаем? Дело в том, что тип переменной Boolean (который может принимать только 2 значения: true и false) также может быть представлен в числовом эквиваленте, где 0=false, а 1=true.

Все. У нас получился контроллер нашей беспроводной сети. Сеть, конечно, небольшая, но ведь надо с чего-то начинать? Тем более, что добавить новый датчик или модуль исполнения не составит труда. Единственное, замечу, что в зависимости от того, куда вы будете выводить данные, после подключения 3-5 модулей у вас может не хватить памяти на Arduino. В этом случае для вашего контроллера я рекомендую использовать Arduino Mega, у которого гораздо больше памяти.
автор: ansealk (Пикабу)

Arduino. Передаем данные по воздуха.
15 оценок, Средняя оценка: 5 из 5

  1. Забавно. Искал обновленную библиотеку VirtualWire, наткнулся на статью. Смотрю и думаю – где-то я уже это видел. Спасибо за указание авторства :)

    1. Шикарная статья. :) Когда увидел её в первый раз на пикабу, решил сохранить к себе. Надеюсь Вы не против.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *