Создание IR-моста для SmartThings на Arduino UNO и ThingShield
Компоненты и материалы
| Компонент | Количество |
|---|---|
| Arduino UNO | 1 |
| SmartThings Shield для Arduino (ThingShield) | 1 |
| IR-светодиод (излучатель) | 1 |
| IR-приемник (фотодиод) | 1 |
| Резистор 100 Ом (опционально) | 1 |
О проекте
Введение
Обычный пульт от телевизора способен на гораздо большее, чем просто переключение каналов, когда подключен к облаку SmartThings. С минимальным набором деталей и быстрым результатом этот проект — отличный способ изучить платформу.
Основы Arduino и ThingShield
SmartThings ThingShield упрощает создание прототипов для SmartThings на базе Arduino. Шилд подключается напрямую к UNO, связываясь с хабом SmartThings по протоколу Zigbee. Переключатель на шилде позволяет выбрать использование пинов 0,1 или 2,3 для связи с Arduino. ThingShield уже резервирует пин 6.
Подробнее о начале работы с Arduino: arduino.cc
Шаг 1: Необходимые детали
- (x1) SmartThings Arduino ThingShield
- (x1) Arduino Uno
- (x1) IR-приемник
- (x1) IR-светодиод-излучатель
- (x1) Резистор 100 Ом (опционально)
Шаг 2: Установка IR-библиотеки
Проект использует библиотеку Arduino от Кена Ширрифа: GitHub
Установите библиотеку стандартным способом: руководство
После установки нужно изменить IRRemoteInt.h для использования таймера на пине 9. В этом примере пин 3 используется для связи с ThingShield.
Шаг 3: Скетч Arduino
Загрузите скетч в Arduino (см. раздел Код).
Шаг 4: Сборка
Сборка простая. Сначала установите ThingShield на Arduino. На схеме ниже показан только Arduino, но расположение пинов одинаковое с установленным шилдом.
IR-приемник: соедините соответствующие пины с GND и 5V. Вывод данных — на пин 11 Arduino.
IR-светодиод: анод на пин 9, катод на GND. Резистор 100 Ом опционален — светодиод выдерживает пиковый ток Arduino при быстрой модуляции. При постоянном токе он бы сгорел.
Шаг 5: Создание кастомного Device Type в SmartThings
В SmartThings IDE создайте новый Device Type для ThingShield.
- Перейдите в «My Device Types», нажмите «New SmartDevice» справа.
- Дайте имя: «Ir Bridge» (будем ссылаться на это имя в SmartApp).
- Определите Attributes (переменные) и Commands (функции). Для урока создадим атрибуты и команды для двух программируемых кнопок и кнопки записи. Добавьте показанные атрибуты и команды. Нажмите «Create» для перехода к кодированию устройства.
- Скопируйте определение устройства в окно кода IDE (см. раздел Код). Нажмите «Save», затем «Publish» для доступности устройства.
Шаг 6: Обновление ThingShield до нового Device Type
Добавьте ThingShield к хабу SmartThings, если еще не сделано. Для сопряжения: установите ThingShield на Arduino, подайте питание. Удерживайте кнопку «Switch» на шилде 6 секунд. В приложении SmartThings нажмите Add. Нажмите физическую кнопку «switch» еще раз — хаб должен обнаружить ThingShield.
В IDE перейдите к вашему Arduino ThingShield: «devices» на главном экране. Выберите устройство, нажмите «Edit». В выпадающем меню «Type» выберите созданный SmartDevice type. Дайте понятное имя (Label). Нажмите Update.
Примечание: при будущих обновлениях кастомных Device Types вернитесь сюда, чтобы убедиться, что физические устройства остались связанными с правильным типом после публикации обновления.
Шаг 7: Написание SmartApp
Последний кусок кода — сам SmartApp. Перейдите в «My SmartApps», начните «New SmartApp» кнопкой справа. Дайте имя, описание, категорию («My apps»). Нажмите «Create» для кодирования.
Скопируйте код SmartApp (см. раздел Код). Нажмите «Save», затем «Publish» для доступности в iOS/Android приложении SmartThings.
Обратите внимание: мы ссылаемся на SmartDevice type по имени «IrBridge» без пробела, который был в «Ir Bridge»:
input "irDevice", "device.IrBridge"
Строка позволяет показывать только устройства типа «Ir Bridge» для выбора в SmartApp. CamelCasing специфичен: при ссылке на device types в smart apps пробелы удаляются, первый символ и символы после пробелов становятся заглавными, остальные — строчные.
Шаг 8: Активация SmartApp
В приложении SmartThings на смартфоне свяжите новый SmartApp с нашим IR-устройством и выберите коммутаторы для управления. Нажмите «install».
Для программирования кнопок в приложении: зайдите в детали тайла (шестеренка в углу). Нажмите большую кнопку записи — она станет красной (режим записи). Нажмите тайл (Play/Pause или B), которым хотите управлять — он станет желтым. Наведите пульт на ThingShield и нажмите кнопку для обучения. ThingShield отправит код в облако SmartThings и свяжет его с выбранной кнопкой. Запрограммированная кнопка станет зеленой, запись вернется в белую.
При следующем нажатии этой кнопки на пульте вы переключите связанный в SmartApp коммутатор.
Код
Arduino скетч (C/C++)
//*****************************************************************************
// Arduino SmartThings IR Shield
//*****************************************************************************
#include <SoftwareSerial.h>
#include <SmartThings.h>
#include <IRremote.h>
//*****************************************************************************
// Pin Definitions
//*****************************************************************************
#define PIN_LED 13
#define PIN_RECV 11
#define PIN_THING_RX 3
#define PIN_THING_TX 2
//*****************************************************************************
// Global Variables
//*****************************************************************************
SmartThingsCallout_t messageCallout;
SmartThings smartthing(PIN_THING_RX, PIN_THING_TX, messageCallout);
bool isDebugEnabled;
int stateLED;
IRrecv irrecv(PIN_RECV);
IRsend irsend;
decode_results results;
//*****************************************************************************
// API Functions
//*****************************************************************************
void setup() {
isDebugEnabled = true;
stateLED = 0;
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, LOW);
if (isDebugEnabled) {
Serial.begin(9600);
Serial.println("setup..");
}
irrecv.enableIRIn();
}
void loop() {
smartthing.run();
if (irrecv.decode(&results)) {
blue();
irrecv.resume();
Serial.println(results.value, HEX);
String irCmd;
if (results.decode_type == NEC) {
irCmd = String(results.value, HEX) + "," + "NEC" + String(results.bits, DEC) + ":" + String(results.value, HEX);
} else if (results.decode_type == SONY) {
irCmd = String(results.value, HEX) + "," + "SNY" + String(results.bits, DEC) + ":" + String(results.value, HEX);
} else if (results.decode_type == RC5) {
irCmd = String(results.value, HEX) + "," + "RC5" + String(results.bits, DEC) + ":" + String(results.value, HEX);
} else if (results.decode_type == RC6) {
irCmd = String(results.value, HEX) + "," + "RC6" + String(results.bits, DEC) + ":" + String(results.value, HEX);
} else {
irCmd = String(results.value, HEX) + "," + "RAW" + String(results.bits, DEC) + ":";
}
Serial.println(irCmd);
smartthing.send(irCmd);
irCmd = "";
}
}
void messageCallout(String message) {
smartthing.shieldSetLED(0, 0, 0);
if (isDebugEnabled) {
Serial.print("Rx: '");
Serial.print(message);
Serial.println("' ");
}
String type = message.substring(0,3);
int startCode = message.indexOf(':');
String lenStr = message.substring(3,startCode);
String codeStr = message.substring(startCode + 1);
unsigned long code;
if(type != "RAW") code = stringToNum(codeStr,16);
int len = stringToNum(lenStr,10);
if(type == "NEC") {
Serial.println("NEC-SEND");
Serial.println(len);
Serial.println(code,HEX);
irsend.sendNEC(code,len);
irrecv.enableIRIn();
} else if(type == "SNY") {
irsend.sendSony(code,len);
irrecv.enableIRIn();
} else if(type == "RC5") {
irsend.sendRC5(code,len);
irrecv.enableIRIn();
} else if(type == "RC6") {
irsend.sendRC6(code,len);
irrecv.enableIRIn();
}
}
void dump(decode_results *results) {
int count = results->rawlen;
if (results->decode_type == UNKNOWN) Serial.print("Unknown encoding: ");
else if (results->decode_type == NEC) Serial.print("Decoded NEC: ");
else if (results->decode_type == SONY) Serial.print("Decoded SONY: ");
else if (results->decode_type == RC5) Serial.print("Decoded RC5: ");
else if (results->decode_type == RC6) Serial.print("Decoded RC6: ");
else if (results->decode_type == PANASONIC) {
Serial.print("Decoded PANASONIC - Address: ");
Serial.print(results->panasonicAddress,HEX);
Serial.print(" Value: ");
} else if (results->decode_type == JVC) Serial.print("Decoded JVC: ");
Serial.print(results->value, HEX);
Serial.print(" (");
Serial.print(results->bits, DEC);
Serial.println(" bits)");
Serial.print("Raw (");
Serial.print(count, DEC);
Serial.print("): ");
for (int i = 0; i < count; i++) {
if ((i % 2) == 1) Serial.print(results->rawbuf[i]*USECPERTICK, DEC);
else Serial.print(-(int)results->rawbuf[i]*USECPERTICK, DEC);
Serial.print(" ");
}
Serial.println("");
}
unsigned long stringToNum(String s, int base) {
unsigned long i = 0, value = 0, place = s.length();
char c;
unsigned long sign = 1;
for(i; i < s.length(); i++) {
place--; c = s[i];
if(c == '-') sign = -1;
else if (c >= '0' && c <= '9') value += (c - '0') * exponent(base,place);
else if (c >= 'A' && ((c - 'A' + 10) < base)) value += ((c - 'A') + 10) * exponent(base,place);
else if (c >= 'a' && (c - 'a' + 10) < base) value += ((c - 'a') + 10) * exponent(base,place);
}
value *= sign;
return value;
}
unsigned long exponent(int num, int power) {
unsigned long total = num, i = 1;
for(power; i < power; i++) total *= num;
return (power == 0) ? 1 : total;
}
void green() { smartthing.shieldSetLED(0, 1, 0); }
void blue() { smartthing.shieldSetLED(0, 0, 1); }
void red() { smartthing.shieldSetLED(1, 0, 0); }
void off() { delay(100); smartthing.shieldSetLED(0, 0, 0); }
Определение устройства (Groovy)
/**
* Smart Ir
* Author: danny@smartthings.com
* Date: 2013-03-06
*/
metadata {
simulator {}
tiles {
standardTile("recStatus", "device.recStatus", width: 2, height: 2, canChangeIcon: true, canChangeBackground: true) {
state "off", label: 'record', action: "record", backgroundColor: "#ffffff"
state "on", label: 'record', action: "record", backgroundColor: "#ff0000"
}
standardTile("buttonA", "device.buttonAStatus", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
state "unset", label: '', action: "buttonA", icon: "st.custom.buttons.play-pause", backgroundColor: "#cccccc"
state "prog", label: '', action: "buttonA", icon: "st.custom.buttons.play-pause", backgroundColor: "#FDE910"
state "set", label: '', action: "buttonA", icon: "st.custom.buttons.play-pause", backgroundColor: "#79b821"
}
standardTile("buttonB", "device.buttonBStatus", width: 1, height: 1, canChangeIcon: true, canChangeBackground: true) {
state "unset", label: '', action: "buttonB", icon: "st.custom.buttons.b", backgroundColor: "#cccccc"
state "prog", label: '', action: "buttonB", icon: "st.custom.buttons.b", backgroundColor: "#FDE910"
state "set", label: '', action: "buttonB", icon: "st.custom.buttons.b", backgroundColor: "#79b821"
}
main (["buttonA","buttonB"])
details (["recStatus","buttonA","buttonB"])
}
}
def parse(String description) {
def value = zigbee.parse(description)?.text
def codeParts = value.split(/,/)
if(device.currentValue("recStatus") == "on") {
if(device.currentValue("lastButton") != "") {
def buttonStatus = "${device.currentValue("lastButton")}Status"
def buttonHex = "${device.currentValue("lastButton")}Hex"
def buttonCode = "${device.currentValue("lastButton")}Code"
return [
createEvent(name:buttonStatus, value:"set", isStateChange:true),
createEvent(name:buttonHex, value:codeParts[0], isStateChange:true),
createEvent(name:buttonCode, value:codeParts[1], isStateChange:true),
createEvent(name:"recStatus", value:"off", isStateChange:true),
createEvent(name:"lastButton", value:"", isStateChange:true)
]
}
} else {
if(codeParts[0] == device.currentValue("buttonAHex")) {
return createEvent(name: "button", value: "A", isStateChange:true)
} else if(codeParts[0] == device.currentValue("buttonBHex")) {
return createEvent(name: "button", value: "B", isStateChange:true)
}
}
return createEvent(name: null, value: "")
}
def record() {
clearLast()
if(device.currentValue("recStatus") == "on") {
sendEvent(name:"recStatus", value:"off", isStateChange:true)
} else {
sendEvent(name:"recStatus", value:"on", isStateChange:true)
}
}
def buttonA() {
if(device.currentValue("recStatus") == "on") {
clearLast()
sendEvent(name:"buttonAStatus", value:"prog", isStateChange:true)
sendEvent(name:"lastButton", value:"buttonA", isStateChange:true)
} else if(device.currentValue("buttonAStatus") == "set") {
zigbee.smartShield(text: "${device.currentValue("buttonACode")}").format()
}
}
def buttonB() {
clearLast()
if(device.currentValue("recStatus") == "on") {
sendEvent(name:"buttonBStatus", value:"prog", isStateChange:true)
sendEvent(name:"lastButton", value:"buttonB", isStateChange:true)
} else if(device.currentValue("buttonBStatus") == "set") {
zigbee.smartShield(text: "${device.currentValue("buttonBCode")}").format()
}
}
def clearLast() {
if(device.currentValue("lastButton") != "") {
sendEvent(name:"${device.currentValue("lastButton")}", value:"unset", isStateChange:true)
sendEvent(name:"lastButton", value:"", isStateChange:true)
}
}
SmartApp код (Groovy)
/**
* IR Receiver
* Author: danny@smartthings.com
* Date: 2013-03-31
*/
preferences {
section("Pick an IR device...") {
input "irDevice", "device.IrBridge"
}
section("Button A turns on or off..."){
input "switch1", "capability.switch", title: "This light", required: false
}
section("Button B turns on or off..."){
input "switch2", "capability.switch", title: "This light", required: false
}
}
def installed() {
subscribe(irDevice, "button.B", handleB)
subscribe(irDevice, "button.A", handleA)
}
def updated() {
unsubscribe()
subscribe(irDevice, "button.B", handleB)
subscribe(irDevice, "button.A", handleA)
}
def handleA(evt) {
if (switch1.currentValue("switch") == "on") switch1.off()
else switch1.on()
}
def handleB(evt) {
if (switch2.currentValue("switch") == "on") switch2.off()
else switch2.on()
}
Производственный процесс
- Создание робота на Raspberry Pi и Python: полное руководство
- Сборка самобалансирующегося Segway на Raspberry Pi
- Использование данных в Интернете надежности: стратегии эффективного управления
- Высокоскоростная фотография капель воды на Arduino Nano — DIY-триггер для вспышки
- Датчик CO2 K30: мониторинг качества воздуха в реальном времени на Arduino
- Устройство для общения глухосліпых людей с помощью 1Sheeld и Arduino
- Умная копилка: управление монетоприёмником на Arduino Nano
- Управление светодиодом по Bluetooth с Arduino: простой DIY-гид
- Мониторинг температуры дома из любой точки через Arduino IoT Cloud
- Строительство рабочей силы завтрашнего дня: Как интеллектуальная автоматизация улучшает опыт сотрудников