Новый модуль приёмника nooLite MR1132 для Arduino

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

© Geektimes