Рано или поздно, в создаваемых проектах Arduino появится необходимость дистанционного управления. Одним из самых бюджетных решений является использование радиоприемника и радиопередатчика — например, WL101-341 и WL102-341. Подключаются WL101-341 и WL102-341 к Arduino очень просто, библиотеки уже существуют (к примеру RadioHead), так что не составит большого труда обмениваться данными между ардуинками. Простейший пример их использования вы найдёте в данной статье, а дальше всё зависит только от ваших нужд и фантазии.
Технические характеристики WL101-341 и WL102-341
| Параметр | WL101-341 (приёмник) | WL102-341 (передатчик) |
|---|---|---|
| Рабочая частота | 433,92 МГц | 433 МГц |
| Напряжение питания | 3,0–5 В | 2,0–3,6 В |
| Ток потребления | 5–6 мА | — |
| Чувствительность | -110 дБм | — |
| Выходная мощность | — | более 11 дБм |
| Дальность передачи | — | 20–100 м |
Установка библиотеки RadioHead
RadioHead Packet Radio — это библиотека для встроенных микропроцессоров. RadioHead предоставляет полную объектно-ориентированную библиотеку для отправки и получения пакетированных сообщений через различные распространённые радиомодули передачи данных и другие виды транспорта на ряде встроенных микропроцессоров/микроконтроллеров. Установить библиотеку можно двумя способами.
Через менеджер библиотек
В Arduino IDE откройте Скетч → Подключить библиотеку → Управлять библиотеками…, найдите RadioHead (автор Mike McCauley) и нажмите «Установить». Так библиотека обновляется вместе с остальными.
Вручную из архива
1. Скачиваем библиотеку с сайта RadioHead, на данный момент последняя версия: RadioHead-1.146.zip.
2. Распаковываем архив в \Documents\Arduino\libraries.
3. Библиотека установлена.
На скриншотах показана версия 1.89, порядок установки от версии не зависит. Методы класса RH_ASK, которые описаны ниже, проверены по исходникам версии 1.146.
Описание методов класса RH_ASK
Для работы с этими радиомодулями используется класс RH_ASK. Он работает с недорогими радиомодулями ASK (амплитудная манипуляция, OOK): приёмником RX-B1 (он же ST-RX04-ASK), передатчиком TX-C1, приёмопередатчиками DR3100 и FS1000A / XY-MK-5V, модулями HopeRF RFM83C и RFM85, а также с WL101-341 и WL102-341 из этой статьи.
Данные уходят пакетами: к вашим байтам (не больше 60) драйвер добавляет преамбулу, заголовок и контрольную сумму, поэтому приёмник принимает только целые и непоражённые помехами сообщения. Методы сгруппированы по назначению, названия в таблице — ссылки на подробное описание.
| Назначение | Методы |
|---|---|
| Настройка | RH_ASK(), init(), speed() |
| Приём данных | available(), recv() |
| Передача данных | send(), waitPacketSent(), maxMessageLength() |
| Режимы драйвера | setModeIdle(), setModeRx(), setModeTx() |
| Служебные | handleTimerInterrupt() |
Таймер. Драйвер
RH_ASKзанимает аппаратный таймер: по умолчанию Timer1. На Arduino Uno и Nano из-за этого перестаёт работать ШИМ (analogWrite()) на выводах 9 и 10 и библиотекаServo. На ESP8266 используется timer0, и желательно не занимать вывод 11.
RH_ASK()
Конструктор. В скетче может быть только один экземпляр RH_ASK: драйвер занимает аппаратный таймер и использует глобальный обработчик прерывания.
RH_ASK(uint16_t speed = 2000,
uint8_t rxPin = 11,
uint8_t txPin = 12,
uint8_t pttPin = 10,
bool pttInverted = false);
| Параметр | Описание |
|---|---|
speed | Скорость передачи в битах в секунду. Скорость должна быть одинаковой у приёмника и у передатчика. |
rxPin | Вывод, к которому подключён выход данных приёмника (DO у WL101-341). |
txPin | Вывод, к которому подключён вход данных передатчика (DAT у WL102-341). |
pttPin | Вывод, который управляет включением передатчика (EN у WL102-341). Во время передачи на нём ВЫСОКИЙ уровень, в остальное время — НИЗКИЙ (пока pttInverted = false). |
pttInverted | true — уровень на pttPin инвертируется: передатчик включается НИЗКИМ уровнем. По умолчанию false. |
init()
Инициализирует драйвер: настраивает выводы и запускает таймер. Вызывайте метод один раз, в setup(), после создания объекта.
bool init();
Возвращает: true, если инициализация прошла успешно.
available()
Проверяет, принято ли новое сообщение. Если драйвер не передаёт, переводит его в режим приёма (RHModeRx) и держит в нём, пока сообщение не будет принято целиком, после чего возвращает в режим ожидания (RHModeIdle). Можно вызывать многократно в цикле ожидания. Во время передачи всегда возвращает false.
bool available();
Возвращает: true, если принято новое полное сообщение без ошибок, которое ещё не забрали методом recv().
recv()
Включает приёмник, если он ещё не включён (внутри вызывает available(), поэтому отдельно вызывать available() перед recv() не нужно). Если есть принятое сообщение, копирует его в buf, записывает его длину в *len и возвращает true. Если сообщение длиннее буфера, копируется только начало, остальное теряется. Сообщения нулевой длины при приёме допускаются. Вызывайте метод достаточно часто (лучше в каждой итерации loop()), чтобы не пропускать сообщения: пока приёмник не забрал принятое сообщение, следующее не принимается.
bool recv(uint8_t *buf, uint8_t *len);
| Параметр | Описание |
|---|---|
buf | Массив, в который копируется принятое сообщение. |
len | Указатель на переменную с размером буфера buf. После вызова в ней записано, сколько байт на самом деле скопировано. |
Возвращает: true, если сообщение скопировано в buf.
send()
Ждёт окончания предыдущей передачи (waitPacketSent()), добавляет к сообщению заголовок и контрольную сумму, загружает его в передатчик и запускает передатчик. Метод не ждёт окончания передачи: она идёт в фоне, по прерываниям таймера. Поэтому не меняйте массив data и не отключайте передатчик, пока не вызван waitPacketSent(). Сообщение длиной 0 не допускается.
bool send(const uint8_t *data, uint8_t len);
| Параметр | Описание |
|---|---|
data | Массив данных для отправки. |
len | Количество байт для отправки: от 1 до значения maxMessageLength() (60). |
Возвращает: true, если сообщение подходит по длине и поставлено в очередь на передачу; false, если len больше 60.
waitPacketSent()
Ждёт, пока закончится передача сообщения. У метода два варианта: без параметров он ждёт сколько потребуется, с параметром — не дольше заданного времени. Эти методы есть не в самом классе RH_ASK, а в его родителе RHGenericDriver, и в скетчах из этой статьи они вызываются после каждого send().
bool waitPacketSent();
bool waitPacketSent(uint16_t timeout);
| Параметр | Описание |
|---|---|
timeout | Сколько миллисекунд ждать (только для второго варианта). |
Возвращает: true, если передача закончилась. Вариант с timeout возвращает false, если за это время передача не закончилась.
maxMessageLength()
Возвращает максимальную длину сообщения, которую можно отправить через драйвер. Для RH_ASK это 60 байт: из 67 байт буфера вычитаются 4 байта заголовка, байт длины и 2 байта контрольной суммы.
uint8_t maxMessageLength();
Возвращает: максимально допустимая длина сообщения в байтах (60).
setModeIdle()
Переводит драйвер в режим ожидания (RHModeIdle): отключает передатчик, а приёмник перестаёт принимать данные. Обычно вызывать вручную не нужно: драйвер сам меняет режим в методах send() и available().
void setModeIdle();
setModeRx()
Переводит драйвер из режима передачи или ожидания в режим приёма (RHModeRx): передатчик выключается, а приёмник начинает опрашивать вывод rxPin. Этот режим включается автоматически при вызове available() и recv().
void setModeRx();
setModeTx()
Переводит драйвер из режима приёма или ожидания в режим передачи (RHModeTx): включает передатчик (ВЫСОКИЙ уровень на pttPin) и начинает выдавать биты на txPin. Этот режим включается автоматически в конце send().
void setModeTx();
handleTimerInterrupt()
Служебный метод, его вызывает обработчик прерывания таймера (8 раз за период одного бита). Самостоятельно его вызывать не нужно.
void handleTimerInterrupt();
speed()
Возвращает текущую скорость передачи.
uint16_t speed();
Возвращает: скорость в битах в секунду.
Подключение WL101-341 и WL102-341 к Arduino
Схема подключения WL101-341 к Arduino
| Arduino | WL101-341 |
|---|---|
| 3.3В | Vin |
| GND | GND |
| 11 | DO |
Пример скетча для приёмника
Приёмник будет получать числа от 0 до 255 по нарастающей. Если ожидаемое число не будет получено, тогда выводим в терминал «*». Скорость (SPEED) в скетчах приёмника и передатчика должна совпадать.
/*
Добавляем необходимые библиотеки
*/
#include <RH_ASK.h>
#define SPEED (uint16_t)1200
#define RX_PIN (uint8_t)11
#define TX_PIN (uint8_t)12
#define PTT_PIN (uint8_t)10
#define PTT_INVERTED false
/*
Создаём экземпляр класса RH_ASK приёмника
*/
RH_ASK driver(SPEED, RX_PIN, TX_PIN, PTT_PIN, PTT_INVERTED);
void setup() {
/*
задаем скорость общения с компьютером
*/
Serial.begin(115200);
/*
Инициализируем приёмник
*/
if (! driver.init()) {
Serial.println(F("RF init failed!"));
while (true) {
delay(1);
}
}
/*
Настройка встроенного светодиода
*/
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
static uint8_t estdata;
uint8_t data;
uint8_t buflen = sizeof(data);
/*
Проверяем наличие новых данных
*/
if (driver.recv((uint8_t*)&data, &buflen)) {
/*
Гасим светодиод
*/
digitalWrite(LED_BUILTIN, LOW);
/*
Выводим в терминал '*' если полученные данные не совпадают с ожидаемыми
*/
if (data != estdata) {
Serial.print('*');
}
/*
Выводим в терминал полученные данные
*/
Serial.print("RX: ");
Serial.println(data);
/*
Инкрементируем значение
*/
estdata = data + 1;
/*
Включаем светодиод
*/
digitalWrite(LED_BUILTIN, HIGH);
}
}
Схема подключения WL102-341 к Arduino
Вывод данных WL102-341 не толерантный к 5 вольтам, поэтому, если вы используете пятивольтовую Arduino, то подать 3.3 вольта на него можно через резистивный делитель (R1 = 1к, R2 = 2к). Делитель для других номиналов можно рассчитать в калькуляторе делителя напряжения.
На данном модуле линия EN неактивна, так как на плате установлена перемычка, соединяющая 1-й вывод микросхемы (EN) с линией питания, то есть модуль постоянно работает при подаче питания. Можно отпаять перемычку, чтобы иметь возможность управления передатчиком.
| Arduino | WL102-341 |
|---|---|
| GND | GND / − |
| 3.3В | Vin / + |
| 12 | DAT |
| 10 | EN (только если перемычка снята) |
Внимание. Пока перемычка EN на месте, вывод EN к Arduino не подключайте. Библиотека настраивает
pttPin(вывод 10) как выход и при простое держит на нём НИЗКИЙ уровень, то есть вывод Arduino окажется замкнут на линию питания модуля. Без подключения EN передатчик просто работает постоянно, а в скетче всё остаётся как есть.
Пример скетча для передатчика
Передатчик будет отправлять числа от 0 до 255 по нарастающей.
/*
Добавляем необходимые библиотеки
*/
#include <RH_ASK.h>
#define SPEED (uint16_t)1200
#define RX_PIN (uint8_t)11
#define TX_PIN (uint8_t)12
#define PTT_PIN (uint8_t)10
#define PTT_INVERTED false
/*
Создаём экземпляр класса RH_ASK передатчика
*/
RH_ASK driver(SPEED, RX_PIN, TX_PIN, PTT_PIN, PTT_INVERTED);
void setup() {
/*
задаем скорость общения с компьютером
*/
Serial.begin(115200);
/*
Инициализируем передатчик
*/
if (!driver.init()) {
Serial.println(F("RF init failed!"));
while (true) {
delay(1);
}
}
/*
Настройка встроенного светодиода
*/
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
static uint8_t data = 0;
/*
Гасим светодиод
*/
digitalWrite(LED_BUILTIN, LOW);
/*
Передаём данные
*/
driver.send((uint8_t*)&data, sizeof(data));
/*
Ждем пока передача будет окончена
*/
driver.waitPacketSent();
/*
Выводим в терминал отправленные данные
*/
Serial.print("TX: ");
Serial.println(data);
/*
Инкрементируем значение
*/
++data;
/*
Включаем светодиод
*/
digitalWrite(LED_BUILTIN, HIGH);
/*
Ждём
*/
delay(100);
}
Результат
Отправленные данные:
Полученные данные:
Пример передачи данных рандомной длины
Для большего понимания принципа работы библиотеки вот ещё один пример обмена данными между ардуинками. Передатчик будет отправлять массив данных uint8_t data[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}; с интервалом в одну секунду. Количество отправленных байт заранее неизвестно, потому что применяется функция random(): вызов random(1, 11) даёт случайное число от 1 до 10. Принимающая сторона принимает эти данные и выводит их и их количество в терминал.
Скетч для передатчика
/*
Добавляем необходимые библиотеки
*/
#include <RH_ASK.h>
#define SPEED (uint16_t)2000
#define RX_PIN (uint8_t)11
#define TX_PIN (uint8_t)12
#define PTT_PIN (uint8_t)10
#define PTT_INVERTED false
/*
Создаём экземпляр класса RH_ASK передатчика
*/
RH_ASK driver(SPEED, RX_PIN, TX_PIN, PTT_PIN, PTT_INVERTED);
void setup() {
/*
задаем скорость общения с компьютером
*/
Serial.begin(115200);
/*
Инициализируем передатчик
*/
if (! driver.init()) {
Serial.println(F("RF init failed!"));
while (true) {
delay(1);
}
}
/*
Настройка встроенного светодиода
*/
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
/*
Буфер данных для отправки
*/
uint8_t data[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
/*
Гасим светодиод
*/
digitalWrite(LED_BUILTIN, LOW);
/*
Передаём массив данных случайной длины (от 1 до 10)
*/
driver.send(data, random(1, 11));
/*
Ждем пока передача будет окончена
*/
driver.waitPacketSent();
/*
Включаем светодиод
*/
digitalWrite(LED_BUILTIN, HIGH);
/*
Ждём секунду
*/
delay(1000);
}
Скетч для приёмника
/*
Добавляем необходимые библиотеки
*/
#include <RH_ASK.h>
#define SPEED (uint16_t)2000
#define RX_PIN (uint8_t)11
#define TX_PIN (uint8_t)12
#define PTT_PIN (uint8_t)10
#define PTT_INVERTED false
/*
Создаём экземпляр класса RH_ASK приёмника
*/
RH_ASK driver(SPEED, RX_PIN, TX_PIN, PTT_PIN, PTT_INVERTED);
void setup() {
/*
задаем скорость общения с компьютером
*/
Serial.begin(115200);
/*
Инициализируем приёмник
*/
if (! driver.init()) {
Serial.println(F("RF init failed!"));
while (true) {
delay(1);
}
}
/*
Настройка встроенного светодиода
*/
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
/*
Буфер полученных данных
*/
uint8_t data[16];
/*
Размер полученных данных
*/
uint8_t buflen = sizeof(data);
uint8_t i;
/*
Проверяем наличие новых данных
*/
if (driver.recv(data, &buflen)) {
/*
Гасим светодиод
*/
digitalWrite(LED_BUILTIN, LOW);
/*
Выводим в терминал полученные данные
*/
Serial.print("Size: ");
Serial.print(buflen);
Serial.println();
Serial.print("Data: ");
for (i = 0; i < buflen; i++) {
Serial.print((char)data[i]);
Serial.print(' ');
}
Serial.println();
/*
Включаем светодиод
*/
digitalWrite(LED_BUILTIN, HIGH);
}
}
Результат
Материалы
- WL101-341 и WL102-341 — Обзор супергетеродинного приемника и передатчика
- VirtualWire
- RadioHead
- RF 433 MHz модули SYN115/SYN480R и WL101-341/WL102-341












А будет ли у вас статья как работать с этими модулями совместно с raspberry pi и pi4j?
Здравствуйте, Сергей. Да, в планах есть такая статья, только для её написания пойдёт больше времени, так как в pi4j нет библиотеки для работы с радио модулями. По этому придётся взять какую-нибудь рабочую библиотеку ардуино и перевести на Java.
Спасибо за статью! Не подскажите как подобрать скорость в бит/сек? поставил 1250 вместо 2000, работает лучше.
Проблема этих модулей — это их скорость передачи данных. Если я не ошибаюсь, максимальная рабочая скорость где то 1650, но для стабильной работы лучше поставить чуть меньше.
Понял, спасибо, а какова минимальная скорость?