image

В этой статье речь пойдёт о новом модуле приёмника сигналов системы nooLite для Ардуино и микроконтроллеров. Чем же замечателен этот модуль? До сих пор не существовало способов получать информацию от датчиков и контролировать прохождение в эфире сигналов от пультов системы nooLite на микроконтроллерах и Ардуино, такая возможность существовала только для компьютеров, при помощи специальных USB адаптеров.

Теперь, с выпуском модуля MR1132, появилась возможность принимать данные о температуре, влажности, освещённости и присутствии людей в помещении с беспроводных датчиков системы nooLite в ваших скетчах на Ардуино, появилась возможность отслеживать команды, отдаваемые силовым блокам nooLite пультами-выключателями и многое другое из того, что раньше было недоступно.

В этой статье я расскажу вам о работе этого модуля и дам работающий скетч, на основе которого вы можете с лёгкостью создавать свои устройства на MR1132. В последующих статьях я расскажу об интеграции этого модуля с популярной системой Arduino Mega Server и о тех замечательных возможностях, которые появятся в связи с этой интеграцией.

Модуль


Модуль MR1132 очень похож на своего брата, модуль MT1132 (о котором уже был цикл статей на Гиктаймс раз, два, три). Разница заключается в том, что модуль MT1132 это передатчик, а модуль MR1132 это приёмник и наибольший эффект можно получить при их совместном использовании.

Ещё один существенный момент заключается в том, что передатчик MT1132 универсален и работает как от напряжения 3,3 В, так и от напряжения 5 В (а значит работает и с 3,3-вольтовыми контроллерами и с 5-вольтовыми), а приёмник — только от 5 В. Это нужно учитывать при проектировании ваших устройств и при необходимости использовать согласователи логических уровней.

image

Подключение такое же простое, как и у модуля MT1132 — напряжение питания, земля и два контакта RX и TX. RTS можно подтянуть к напряжению питания, но у меня всё работало и без подключения этого вывода.

Работа


Работа модуля происходит по последовательному интерфейсу на скорости 9600. Модуль принимает команды от контроллера и, в свою очередь, выдаёт данные, принимаемые из эфира (от сенсоров и пультов nooLite).

Давайте поподробнее разберём этот момент, чтобы у вас сложилось чёткое понимание того, как работает этот модуль.

Все команды, которые контролер может посылать модулю по последовательному интерфейсу, относятся к «привязке» или «отвязке» приборов nooLite, с которых модуль потом будет получать данные. Больше никаких управляющих команд для этого модуля не существует. Например, вы привязали датчик температуры nooLite PT112 на нулевом канале (всего каналов 32, что отражено в названии модуля) и после этого модуль начнёт принимать данные с этого датчика и выдавать их в последовательный интерфейс (где наша задача их «отловить» и использовать для наших целей).

Если получать данные с какого-либо датчика нам больше не нужно, то мы можем послать команду отвязки (в данном случае на нулевом канале) и приёмник больше не будет выдавать контроллеру информацию с этого датчика.

Что касается информации, принимаемой модулем MR1132 от приборов nooLite, то существует отдельный документ, который описывает формат всех возможных команд. Нас, в контексте этого повествования, из всего этого обширного списка будут интересовать команды датчиков температуры, влажности и движения. Подробно эти команды будут рассмотрены чуть ниже, на примере работы скетча.

Датчики


В наших экспериментах будут использоваться три датчика.

image

Датчик температуры PT112 и датчик температуры и влажности PT111. Выглядят они одинаково и работают полностью аналогично, разница заключается в том, что PT112 выдаёт только информацию о температуре, а PT111 — о температуре и влажности. Эти датчики могут работать в режиме как просто датчика, так и в режиме термостата и гигростата, но нас будет интересовать режим просто датчика.

image

Третий датчик — PM111. Это датчик движения, который может непосредственно управлять силовыми блоками nooLite, но нас он будет интересовать только как источник информации о движении и присутствии людей в помещении.

Контроллер



В качестве управляющего контроллера будет использоваться Arduino Mega 2560, и два его последовательных интерфейса — Serial (для визуального контроля информации) Serial2 (для общения с модулем приёмника). Serial1 зарезервирован за модулем передатчика MT1132.

image

Скетч


В секции setup() запускаются два интерфейса, выводится информационное сообщение о старте скетча и выдаётся команда на привязку приборов nooLite на нулевом канале. Это тестовая команда и вы можете её закомментировать или заменить на другую команду (например, привязки на другом канале или отвязки).

void setup() {
  Serial.begin(9600);
  Serial2.begin(9600);
  Serial.println("*** Start sketch ***");
  mrBind(0);
  //mrUnbind(0);
}

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

PT112 — нулевой канал
PT111 — первый канал
PM111 — второй канал

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

mrBind(0);
mrBind(1);
mrBind(2);

и каждый раз нажимать кнопку на соответствующем датчике. Это нужно проделать только один раз, затем команду привязки можно закомментировать. Или вместо неё поставить команду отвязки и отвязать любой из привязанных датчиков.

В секции loop() находится только одна функция mrCheck(), ответственная за «ловлю» сообщений модуля MR1132 из последовательного интерфейса Serial2.

void mrCheck() {
  if (Serial2.available() == 8) {
    mrBuf[0] = Serial2.read();
    mrBuf[1] = Serial2.read();
    mrBuf[2] = Serial2.read();
    mrBuf[3] = Serial2.read();
    if (mrBuf[0] == 79 && mrBuf[1] == 75 && mrBuf[2] == 13 && mrBuf[3] == 10) {
      Serial.println("OK");
    } else {
        mrBuf[4] = Serial2.read();
        mrBuf[5] = Serial2.read();
        mrBuf[6] = Serial2.read();
        mrBuf[7] = Serial2.read();
        mrNewData();
      }
  }
}

Эта функция заполняет массив mrBuf[8] приходящими от модуля данными или транслирует в Serial сообщение «OK», выдаваемые модулем MR1132. Далее, содержимое массива mrBuf[8] разбирается в соответствии с форматом данных команд системы nooLite и этим занимаются соответствующие функции скетча.

Функция mrNewData() выделяет основные данные из массива mrBuf[8] и, в зависимости от пришедшей команды, выдаёт в Serial нужную информацию (канал, команда, температура, влажность, состояние батареи датчика и т. д.).

void mrNewData() {
  mrClearData();
  mrPrintHeader();
  
  mrSetBindState();
  mrPrintBindState();
  mrSetChannel();
  mrPrintChannel();

  mrSetCommand();
  mrSetDatas();

  switch (mrCommand) {
    case 0:
      Serial.print("PIR command: ");Serial.println("OFF");
      break;
    case 2:
      Serial.print("PIR command: "); Serial.println("ON");
      break;
    case 21:
      mrSetDeviceType();
      mrPrintDeviceType();
      if (mrDeviceType == 1) {
        mrSetTemperature();
        mrPrintTemperature();
      }
      if (mrDeviceType == 2) {
        mrSetTemperature();
        mrPrintTemperature();
        mrSetHumidity();
        mrPrintHumidity();
      }
      break;
    default: 
      ;
  } // switch
  mrSetBatteryState();
} // newData()

Функции, содержащие в своём названии слово Print, занимаются выдачей информации в Serial интерфейс для визуального контроля.

Следующие функции занимаются расшифровкой данных, находящихся в массиве mrBuf[8], в соответствии с с форматом данных команд системы nooLite:

mrSetTogl() — значение счётчика пришедших команд
mrSetBindState() — состояние модуля (привязка/норма)
mrSetReceiveBit() — контрольный бит приёма новой команды
mrSetChannel() — номер канала
mrSetCommand() — команда
mrSetFormat() — формат данных
mrSetDeviceType() — тип датчика
mrSetDatas() — заполнение четырёх байт данных
mrSetTemperature() — получение значения температуры
mrSetHumidity() — получение значения влажности
mrSetBrightness() — получение значения освещённости
mrSetBatteryState() — состояние батареи датчика

Посмотреть подробности реализации этих функций вы можете в полном коде скетча, прилагаемом ниже.

Вот полный текст скетча.

Полный код скетча
// TX2 16
// RX2 17
// TX1 18
// RX1 19

// nooLite MR1132 data
byte mrBuf[8];
int mrTogl = -1;
int mrBindState = -1;
int mrReceiveBit = -1;
int mrChannel = -1;
int mrCommand = -1;
int mrFormat = -1;
int mrData0 = -1;
int mrData1 = -1;
int mrData2 = -1;
int mrData3 = -1;
int mrDeviceType = -1;
int mrBatteryState = -1;
int mrHumidity = -1;
int mrBrightness = -1;
float mrTemp = -1.0;

// nooLite MR1132 bind/unbind

void mrSerialChannel(byte ch) {
switch (ch) {
case 0: Serial.println («0»); break;
case 1: Serial.println («1»); break;
case 2: Serial.println («2»); break;
case 3: Serial.println («3»); break;
case 4: Serial.println («4»); break;
case 5: Serial.println («5»); break;
case 6: Serial.println («6»); break;
case 7: Serial.println («7»); break;
case 8: Serial.println («8»); break;
case 9: Serial.println («9»); break;
case 10: Serial.println(«10»); break;
case 11: Serial.println(«11»); break;
case 12: Serial.println(«12»); break;
case 13: Serial.println(«13»); break;
case 14: Serial.println(«14»); break;
case 15: Serial.println(«15»); break;
case 16: Serial.println(«16»); break;
case 17: Serial.println(«17»); break;
case 18: Serial.println(«18»); break;
case 19: Serial.println(«19»); break;
case 20: Serial.println(«20»); break;
case 21: Serial.println(«21»); break;
case 22: Serial.println(«22»); break;
case 23: Serial.println(«23»); break;
case 24: Serial.println(«24»); break;
case 25: Serial.println(«25»); break;
case 26: Serial.println(«26»); break;
case 27: Serial.println(«27»); break;
case 28: Serial.println(«28»); break;
case 29: Serial.println(«29»); break;
case 30: Serial.println(«30»); break;
case 31: Serial.println(«31»); break;
} // switch
} // mrSerialChannel( )

void mrSerial2Channel(byte ch) {
switch (ch) {
case 0: Serial2.print(«00»); break;
case 1: Serial2.print(«01»); break;
case 2: Serial2.print(«02»); break;
case 3: Serial2.print(«03»); break;
case 4: Serial2.print(«04»); break;
case 5: Serial2.print(«05»); break;
case 6: Serial2.print(«06»); break;
case 7: Serial2.print(«07»); break;
case 8: Serial2.print(«08»); break;
case 9: Serial2.print(«09»); break;
case 10: Serial2.print(«10»); break;
case 11: Serial2.print(«11»); break;
case 12: Serial2.print(«12»); break;
case 13: Serial2.print(«13»); break;
case 14: Serial2.print(«14»); break;
case 15: Serial2.print(«15»); break;
case 16: Serial2.print(«16»); break;
case 17: Serial2.print(«17»); break;
case 18: Serial2.print(«18»); break;
case 19: Serial2.print(«19»); break;
case 20: Serial2.print(«20»); break;
case 21: Serial2.print(«21»); break;
case 22: Serial2.print(«22»); break;
case 23: Serial2.print(«23»); break;
case 24: Serial2.print(«24»); break;
case 25: Serial2.print(«25»); break;
case 26: Serial2.print(«26»); break;
case 27: Serial2.print(«27»); break;
case 28: Serial2.print(«28»); break;
case 29: Serial2.print(«29»); break;
case 30: Serial2.print(«30»); break;
case 31: Serial2.print(«31»); break;
} // switch
} // mrSerial2Channel( )

void mrPrintBind(byte ch) {
Serial.print(«Bind on channel „);
mrSerialChannel(ch);
}

void mrBind(byte ch) {
mrPrintBind(ch);
Serial2.print(“bind_mode_cell_»);
mrSerial2Channel(ch);
Serial2.write(3); // End of Text — B00000011(BIN)
}

void mrPrintUnbind(byte ch) {
Serial.println(«Unbind on channel „);
mrSerialChannel(ch);
}

void mrUnbind(byte ch) {
mrPrintUnbind(ch);
Serial2.print(“clear_one_cell_»);
mrSerial2Channel(ch);
Serial2.write(3);
}

void mrBindStop() {
Serial.println(«Bind mode off»);
Serial2.print(«bind_mode_off»);
Serial2.write(3);
}

void mrClearAll() {
Serial.println(«Clear all cell»);
Serial2.print(«clear_all_cell»);
Serial2.write(3);
}

// nooLite MR1132 print works

void mrPrintHeader() {
Serial.println();
}

void mrPrintDeviceType() {
Serial.print(«Device: „);
if (mrDeviceType == 1) {
Serial.println(“PT112»);
}
if (mrDeviceType == 2) {
Serial.println(«PT111»);
}
}

void mrPrintBindState() {
if (mrBindState == 1) {
Serial.print(«Bind State: „);
Serial.println(“ON»);
}
}

void mrPrintBatteryState() {
if (mrBatteryState == 1) {
Serial.print(«Battery State: „);
Serial.println(“LOW!»);
}
}

void mrPrintChannel() {
Serial.print(«Channel: „);
Serial.println(mrChannel);
}

void mrPrintTemperature() {
Serial.print(“Temp: „);
Serial.println(mrTemp);
}

void mrPrintHumidity() {
Serial.print(“Humidity: „);
Serial.println(mrHumidity);
}

// nooLite MR1132 data works

void mrClearData() {
mrTogl = -1;
mrBindState = -1;
mrReceiveBit = -1;
mrChannel = -1;
mrCommand = -1;
mrFormat = -1;
mrData0 = -1;
mrData1 = -1;
mrData2 = -1;
mrData3 = -1;
mrDeviceType = -1;
mrBatteryState = -1;
mrHumidity = -1;
mrBrightness = -1;
mrTemp = -1.0;
}

void mrSetTogl() {
byte b0 = bitRead(mrBuf[0], 0);
byte b1 = bitRead(mrBuf[0], 1);
byte b2 = bitRead(mrBuf[0], 2);
byte b3 = bitRead(mrBuf[0], 3);
byte b4 = bitRead(mrBuf[0], 4);
byte b5 = bitRead(mrBuf[0], 5);
mrTogl = 32*b5 + 16*b4 + 8*b3 + 4*b2 + 2*b1 + b0;
}

void mrSetBindState() {
mrBindState = bitRead(mrBuf[0], 6);
}

void mrSetReceiveBit() {
mrReceiveBit = bitRead(mrBuf[0], 7);
}

void mrSetChannel() {
mrChannel = mrBuf[1];
}

void mrSetCommand() {
mrCommand = mrBuf[2];
}

void mrSetFormat() {
mrFormat = mrBuf[3];
}

void mrSetDeviceType() {
byte tp1 = bitRead(mrBuf[5], 4);
byte tp2 = bitRead(mrBuf[5], 5);
byte tp3 = bitRead(mrBuf[5], 6);
mrDeviceType = 4*tp3 + 2*tp2 + tp1;
}

void mrSetDatas() {
mrData0 = mrBuf[4];
mrData1 = mrBuf[5];
mrData2 = mrBuf[6];
mrData3 = mrBuf[7];
}

void mrSetTemperature() {
byte t8 = bitRead(mrData1, 0);
byte t9 = bitRead(mrData1, 1);
byte t10= bitRead(mrData1, 2);
int temp2 = 1024*t10 + 512*t9 + 256*t8;

int temp = mrData0 + temp2;

byte t11 = bitRead(mrData1, 3);
if (t11 == 1) {
temp = (4096 — temp) * -1;
}
mrTemp = (float)temp / 10.0;
}

void mrSetBatteryState() {
mrBatteryState = bitRead(mrBuf[5], 7);
}

void mrSetHumidity() {
mrHumidity = mrData2;
}

void mrSetBrightness() {
mrBrightness = mrData3;
}

void mrNewData() {
mrClearData();
mrPrintHeader();

mrSetBindState();
mrPrintBindState();
mrSetChannel();
mrPrintChannel();

mrSetCommand();
mrSetDatas();

switch (mrCommand) {
case 0:
Serial.print(“PIR command: „); Serial.println(“OFF»);
break;
case 2:
Serial.print(«PIR command: „); Serial.println(“ON»);
break;
case 21:
mrSetDeviceType();
mrPrintDeviceType();
if (mrDeviceType == 1) {
mrSetTemperature();
mrPrintTemperature();
}
if (mrDeviceType == 2) {
mrSetTemperature();
mrPrintTemperature();
mrSetHumidity();
mrPrintHumidity();
}
break;
default:
;
} // switch
mrSetBatteryState();
} // newData()

void mrCheck() {
if (Serial2.available() == 8) {
mrBuf[0] = Serial2.read();
mrBuf[1] = Serial2.read();
mrBuf[2] = Serial2.read();
mrBuf[3] = Serial2.read();
if (mrBuf[0] == 79 && mrBuf[1] == 75 && mrBuf[2] == 13 && mrBuf[3] == 10) {
Serial.println(«OK»);
} else {
mrBuf[4] = Serial2.read();
mrBuf[5] = Serial2.read();
mrBuf[6] = Serial2.read();
mrBuf[7] = Serial2.read();
mrNewData();
}
}
}

void setup() {
Serial.begin(9600);
Serial2.begin(9600);
Serial.println("*** Start sketch ***");
mrBind(0);
//mrUnbind(0);
}

void loop() {
mrCheck();
} // loop()


Заключение


Вот, собственно, и всё, что необходимо вам, чтобы начать работать с модулем MR1132 и использовать его в своих скетчах и проектах. Этот модуль можно также использовать для контроля сигналов от настенных пультов nooLite и ваша система теперь может знать где и какой пульт сработал и какую команду он послал силовым блокам.

Скоро выходит новая 0.15 версия популярной системы Arduino Mega Server и в этой версии появится встроенная поддержка модулей MR1132 и удобное управление ими прямо с веб-странички и много чего ещё интересного.

Комментарии (15)


  1. totuin
    18.04.2016 20:06

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

    Вот здесь:
    void mrSerialChannel(byte ch) {
    switch (ch) {
    case 0: Serial.println («0»); break;
    case 1: Serial.println («1»); break;
    case 2: Serial.println («2»); break;
    case 3: Serial.println («3»); break;
    case 4: Serial.println («4»); break;
    case 5: Serial.println («5»); break;
    case 6: Serial.println («6»); break;
    case 7: Serial.println («7»); break;
    case 8: Serial.println («8»); break;
    case 9: Serial.println («9»); break;
    case 10: Serial.println(«10»); break;
    case 11: Serial.println(«11»); break;
    case 12: Serial.println(«12»); break;
    case 13: Serial.println(«13»); break;
    case 14: Serial.println(«14»); break;
    case 15: Serial.println(«15»); break;
    case 16: Serial.println(«16»); break;
    case 17: Serial.println(«17»); break;
    case 18: Serial.println(«18»); break;
    case 19: Serial.println(«19»); break;
    case 20: Serial.println(«20»); break;
    case 21: Serial.println(«21»); break;
    case 22: Serial.println(«22»); break;
    case 23: Serial.println(«23»); break;
    case 24: Serial.println(«24»); break;
    case 25: Serial.println(«25»); break;
    case 26: Serial.println(«26»); break;
    case 27: Serial.println(«27»); break;
    case 28: Serial.println(«28»); break;
    case 29: Serial.println(«29»); break;
    case 30: Serial.println(«30»); break;
    case 31: Serial.println(«31»); break;
    } // switch
    }
    


    1. smart_alex
      18.04.2016 20:18

      Скетч написан в первом приближении и там непочатый край для оптимизации. Спасибо за ваши дополнения. Мне лично не очень нравится функция mrCheck(), она работает корректно, но явно требует переделки.


  1. Sleuthhound
    18.04.2016 20:20

    Ждем совмещенный модуль с приемником и передатчиком.


    1. smart_alex
      18.04.2016 20:25

      Вообще, да, но насколько мне известно такой вариант не планируется. В Arduino Mega Server будет поддержка двух модулей и с их помощью можно будет реализовать многие возможности.


  1. Kidar
    18.04.2016 22:25

    Долго ждал подобное решение.
    У меня к лампам проведена витая пара и теперь можно сделать так.
    1. Берем 1-кнопочный пульт PB211 или 2-кнопочный пульт PB411 (описание в статье "Кнопочные выключатели nooLite — первые впечатления").
    2. Устанавливаем один MR1132 из этой статьи, к которому подключаем контроллер.
    3. Контроллер расшифровывает принимаемые команды от пультов управления и по витой паре (интерфейс RS-485 или другой) отправляет команду на конечные устройства, в качестве которых установлен простейший контроллер с силовым блоком.

    Преимущества:
    1. Конечное устройство получится размером меньше и значительно проще, чем стандартный силовой блок системы.
    2. Общая цена системы в моем случае (к лампам проведены информационный кабели) получится меньше за счет уменьшения количества стандартных радиоуправляемых силовых блоков.
    3. По этой же витой паре можно подавать питание на конечные устройства, что также позволит уменьшить их размеры.

    Минус — необходимо «колхозить» контролер для расшифровки сигналов от MR1132 или изучить в конце концов Arduino.


    1. AotD
      19.04.2016 09:00

      А в качестве «установлен простейший контроллер с силовым блоком» что используется?


      1. Kidar
        19.04.2016 09:07

        В данный момент его нет. Можно поискать что-то из готовых блоков, но смогу сделать сам. Это будет преобразователь интерфейса, шести- или восьминогий процессор и силовой элемент.


  1. Usmekhaiouschiysia
    19.04.2016 10:24

    Порадовала формулировка — Разница заключается в том, что модуль MT1132 это передатчик, а модуль MR1132 это приёмник и наибольший эффект можно получить при их совместном использовании.
    Получается, эффект при использовании приёмника без передатчика и передатчика без приёмника тоже неплох?


    1. FlarGargoyl
      21.04.2016 15:12

      Да. Передатчик — отдает команды управления. Приемник — исключительно принимает их. В коридоре поставил датчик движения\освещения, включает свет, если есть движение, при определенном уровне освещения.

      Используя передатчик — смогу управлять включением силового блока лампы отдельно (пультом или с компа)

      Используя приёмник, смогу следить — было ли движения или нет.

      Используя и то и другое, могу:
      управлять той лампой (выключатели или пулты сейчс не берем в расчет, там свои приемопередатчики) например с вебморды
      с нее же следить за наличием движения
      привязывать события на движение\освещенность при помощи сервака, который обрабатывает инфу, полученную приемником — а не при помощи внутренней логики. Пример с другим датчиком.

      Аналогично с датчиком температуры и влажности. Для его применения ему ни к чему передатчик — от него лучше ПОЛУЧАТЬ инфу, т.е. нужен приемник. Датчик температуры и влажности + приемник = мониторинг этих показателей в помещении = легко заводится демон, который сможет дать команду на силовой блок «включить электричество в кондиционере или вентиляторе» и так далее.

      Так что да, их можно применять как раздельно, так и совместно, но наиболее широкий вариант комбинирования — таки вместе.


  1. mcarr0w
    19.04.2016 12:30

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


    1. smart_alex
      19.04.2016 16:03

      Обещают к лету двустороннее оборудование — как будет на самом деле — посмотрим.


  1. Helpa
    20.04.2016 23:12

    А вы не интересовались, какая потребляемая мощность силовых блоков?


    1. mcarr0w
      21.04.2016 08:46

      мне отвечали в прошлых постах. что-то вроде нескольких ватт


      1. Helpa
        21.04.2016 16:39

        Спросил через их сайт: Мощность потребляемая силовым блоком приблизительно 0.1 Вт


        1. mcarr0w
          22.04.2016 14:19

          хм… значит память подвела, не могу найти