Подключение WL101-341 и WL102-341 к Arduino

Рано или поздно, в создаваемых проектах 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.

Установка библиотеки RadioHead - Скачиваем библиотеку

2. Распаковываем архив в \Documents\Arduino\libraries.

Установка библиотеки RadioHead - Распаковываем архив

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).
pttInvertedtrue — уровень на 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

Схема подключения приёмника WL101-341 к Arduino
ArduinoWL101-341
3.3ВVin
GNDGND
11DO

Пример скетча для приёмника

Приёмник будет получать числа от 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к). Делитель для других номиналов можно рассчитать в калькуляторе делителя напряжения.

Схема подключения WL102-341 к Arduino

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

WL102-341 - линия EN (перемычка)
ArduinoWL102-341
GNDGND / −
3.3ВVin / +
12DAT
10EN (только если перемычка снята)

Внимание. Пока перемычка 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);
}

Результат

Отправленные данные:

Подключение WL101-341 к Arduino - Отправленные данные

Полученные данные:

Подключение WL101-341 к Arduino - Полученные данные

Пример передачи данных рандомной длины

Для большего понимания принципа работы библиотеки вот ещё один пример обмена данными между ардуинками. Передатчик будет отправлять массив данных 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 к Arduino - Полученные рандомных данные

Материалы

5 комментариев к “Подключение WL101-341 и WL102-341 к Arduino”

  1. А будет ли у вас статья как работать с этими модулями совместно с raspberry pi и pi4j?

    Ответить
    • Здравствуйте, Сергей. Да, в планах есть такая статья, только для её написания пойдёт больше времени, так как в pi4j нет библиотеки для работы с радио модулями. По этому придётся взять какую-нибудь рабочую библиотеку ардуино и перевести на Java.

      Ответить
  2. Спасибо за статью! Не подскажите как подобрать скорость в бит/сек? поставил 1250 вместо 2000, работает лучше.

    Ответить
    • Проблема этих модулей — это их скорость передачи данных. Если я не ошибаюсь, максимальная рабочая скорость где то 1650, но для стабильной работы лучше поставить чуть меньше.

      Ответить

Оставьте комментарий

Отправляя комментарий, вы соглашаетесь с обработкой ваших данных (имя, адрес электронной почты, текст комментария) в соответствии с политикой конфиденциальности.