В этой статье речь пойдёт о новом модуле приёмника сигналов системы nooLite для Ардуино и микроконтроллеров. Чем же замечателен этот модуль? До сих пор не существовало способов получать информацию от датчиков и контролировать прохождение в эфире сигналов от пультов системы nooLite на микроконтроллерах и Ардуино, такая возможность существовала только для компьютеров, при помощи специальных USB адаптеров.
Теперь, с выпуском модуля MR1132, появилась возможность принимать данные о температуре, влажности, освещённости и присутствии людей в помещении с беспроводных датчиков системы nooLite в ваших скетчах на Ардуино, появилась возможность отслеживать команды, отдаваемые силовым блокам nooLite пультами-выключателями и многое другое из того, что раньше было недоступно.
В этой статье я расскажу вам о работе этого модуля и дам работающий скетч, на основе которого вы можете с лёгкостью создавать свои устройства на MR1132. В последующих статьях я расскажу об интеграции этого модуля с популярной системой Arduino Mega Server и о тех замечательных возможностях, которые появятся в связи с этой интеграцией.
Модуль
Модуль MR1132 очень похож на своего брата, модуль MT1132 (о котором уже был цикл статей на Гиктаймс раз, два, три). Разница заключается в том, что модуль MT1132 это передатчик, а модуль MR1132 это приёмник и наибольший эффект можно получить при их совместном использовании.
Ещё один существенный момент заключается в том, что передатчик MT1132 универсален и работает как от напряжения 3,3 В, так и от напряжения 5 В (а значит работает и с 3,3-вольтовыми контроллерами и с 5-вольтовыми), а приёмник — только от 5 В. Это нужно учитывать при проектировании ваших устройств и при необходимости использовать согласователи логических уровней.
Подключение такое же простое, как и у модуля MT1132 — напряжение питания, земля и два контакта RX и TX. RTS можно подтянуть к напряжению питания, но у меня всё работало и без подключения этого вывода.
Работа
Работа модуля происходит по последовательному интерфейсу на скорости 9600. Модуль принимает команды от контроллера и, в свою очередь, выдаёт данные, принимаемые из эфира (от сенсоров и пультов nooLite).
Давайте поподробнее разберём этот момент, чтобы у вас сложилось чёткое понимание того, как работает этот модуль.
Все команды, которые контролер может посылать модулю по последовательному интерфейсу, относятся к «привязке» или «отвязке» приборов nooLite, с которых модуль потом будет получать данные. Больше никаких управляющих команд для этого модуля не существует. Например, вы привязали датчик температуры nooLite PT112 на нулевом канале (всего каналов 32, что отражено в названии модуля) и после этого модуль начнёт принимать данные с этого датчика и выдавать их в последовательный интерфейс (где наша задача их «отловить» и использовать для наших целей).
Если получать данные с какого-либо датчика нам больше не нужно, то мы можем послать команду отвязки (в данном случае на нулевом канале) и приёмник больше не будет выдавать контроллеру информацию с этого датчика.
Что касается информации, принимаемой модулем MR1132 от приборов nooLite, то существует отдельный документ, который описывает формат всех возможных команд. Нас, в контексте этого повествования, из всего этого обширного списка будут интересовать команды датчиков температуры, влажности и движения. Подробно эти команды будут рассмотрены чуть ниже, на примере работы скетча.
Датчики
В наших экспериментах будут использоваться три датчика.
Датчик температуры PT112 и датчик температуры и влажности PT111. Выглядят они одинаково и работают полностью аналогично, разница заключается в том, что PT112 выдаёт только информацию о температуре, а PT111 — о температуре и влажности. Эти датчики могут работать в режиме как просто датчика, так и в режиме термостата и гигростата, но нас будет интересовать режим просто датчика.
Третий датчик — PM111. Это датчик движения, который может непосредственно управлять силовыми блоками nooLite, но нас он будет интересовать только как источник информации о движении и присутствии людей в помещении.
Контроллер
В качестве управляющего контроллера будет использоваться Arduino Mega 2560, и два его последовательных интерфейса — Serial (для визуального контроля информации) Serial2 (для общения с модулем приёмника). Serial1 зарезервирован за модулем передатчика MT1132.
Скетч
В секции 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() — состояние батареи датчика
Посмотреть подробности реализации этих функций вы можете в полном коде скетча, прилагаемом ниже.
Вот полный текст скетча.
// 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 и удобное управление ими прямо с веб-странички и много чего ещё интересного.
Автор: smart_alex