* WiFi-роутер DLink DIR320 с настройками узла автоматики
* Arduino Mega
* Блок реле (16 шт)
* Обычные выключатели
Каждый выключатель и источник света отдельным проводом идёт в щиток, где это дело всё коммутируется приблизительно по такой схеме:
(на схеме показано только одно реле и один выключатель в виде кнопки, хотя на самом деле это обычный двух-позиционный выключателем с положением вкл-выкл)
Контроллер прошит таким образом, что изменение состояния выключателя автоматически приводит в нужное состояние соответствующее реле, т.е. даже без внешней связи свет включается как и должен. Дополнительно контроллер управляется WiFi-роутером, принимая команды извне, а так же уведомляя об изменении положения выключателей.
Код прошивки контроллера достаточно простой.
#define PIN_LED (13) // INDICATOR
unsigned int unique_device_id = 0;
unsigned int packet_received_id = 0;
long int uptime = 0;
long int old_uptime = 0;
String inData;
#define TOTAL_OUTPUTS (16)
#define TOTAL_INPUTS (10)
int outputPins[TOTAL_OUTPUTS] = {22, 24 , 26, 28, 30, 32, 34 ,36 , 23, 25, 27, 29, 31, 33, 35, 37};
int inputPins[TOTAL_INPUTS] = {38, 39, 40, 41, 43, 45, 47, 49, 51, 53};
int directLinkedPins[TOTAL_INPUTS] = {22,24,26,28,30,32,34,36,23,25};
int inputPinsStatus[TOTAL_OUTPUTS];
struct SEND_DATA_STRUCTURE {
//put your variable definitions here for the data you want to send
//THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
//Struct can'e be bigger then 26 bytes for VirtualWire version
unsigned int device_id;
unsigned int destination_id;
unsigned int packet_id;
byte command;
int data;
};
SEND_DATA_STRUCTURE mydata;
//EasyTransferVirtualWire ET;
//This function will write a 2 byte integer to the eeprom at the specified address and address + 1
void EEPROMWriteInt(int p_address, unsigned int p_value)
{
byte lowByte = ((p_value >> 0) & 0xFF);
byte highByte = ((p_value >> 8) & 0xFF);
EEPROM.write(p_address, lowByte);
EEPROM.write(p_address + 1, highByte);
}
//This function will read a 2 byte integer from the eeprom at the specified address and address + 1
unsigned int EEPROMReadInt(int p_address)
{
byte lowByte = EEPROM.read(p_address);
byte highByte = EEPROM.read(p_address + 1);
return ((lowByte << 0) & 0xFF) + ((highByte << 8) & 0xFF00);
}
void setup()
{
randomSeed(analogRead(0));
pinMode(PIN_LED,OUTPUT);
Serial.begin(9600);
Serial.print("Initializing inputs ... ");
int i;
for (i = 0; i < TOTAL_INPUTS; i = i + 1) {
pinMode(inputPins,INPUT);
inputPinsStatus=digitalRead(inputPins);
}
Serial.println("DONE");
Serial.print("Initializing outputs ... ");
for (i = 0; i < TOTAL_OUTPUTS; i = i + 1) {
turnOffPin(outputPins);
pinMode(outputPins,OUTPUT);
turnOffPin(outputPins);
}
Serial.println("DONE");
// Device ID
Serial.print("Getting Device ID... ");
unique_device_id=EEPROMReadInt(0);
if (unique_device_id<10000 || unique_device_id>60000 || unique_device_id==26807) {
Serial.print("N/A, updating... ");
unique_device_id=random(10000, 60000);
EEPROMWriteInt(0, unique_device_id);
}
Serial.println(unique_device_id);
}
void blinking(int count) {
for(int i=0;i<count;i++) {
digitalWrite(PIN_LED, HIGH);
delay(1000);
digitalWrite(PIN_LED, LOW);
delay(1000);
}
}
void turnOnPin(int number) {
Serial.print("OUT");
Serial.print(number);
Serial.println(" set to 1");
digitalWrite(number, LOW);
}
void turnOffPin(int number) {
Serial.print("OUT");
Serial.print(number);
Serial.println(" set to 0");
digitalWrite(number, HIGH);
}
void loop() {
int i;
for (i = 0; i < TOTAL_INPUTS; i = i + 1) {
int currentStatus=digitalRead(inputPins);
if (currentStatus!=inputPinsStatus) {
delay(100); // de-bounce delay
int currentStatusUpdated=digitalRead(inputPins);
if (currentStatusUpdated==currentStatus) {
inputPinsStatus=currentStatus;
Serial.print("IN");
Serial.print(i);
Serial.print(" set to ");
Serial.println(currentStatus);
if (directLinkedPins[i]!=0 && currentStatus==HIGH) {
turnOnPin(directLinkedPins[i]);
}
if (directLinkedPins[i]!=0 && currentStatus==LOW) {
turnOffPin(directLinkedPins[i]);
}
}
}
}
uptime=round(millis()/1000);
if (uptime!=old_uptime) {
Serial.print("Uptime: ");
Serial.println(uptime);
old_uptime=uptime;
}
if (Serial.available()) {
char c=Serial.read();
if (c == '\n' || c == ';')
{
Serial.println(inData);
int commandProcessed=0;
if (inData.equals("blink")) {
commandProcessed=1;
Serial.println("BLINKING!");
blinking(3);
}
if (inData.startsWith("turnon")) {
commandProcessed=1;
inData.replace("turnon","");
turnOnPin(outputPins[inData.toInt()]);
}
if (inData.startsWith("turnoff")) {
commandProcessed=1;
inData.replace("turnoff","");
turnOffPin(outputPins[inData.toInt()]);
}
if (inData.startsWith("status")) {
commandProcessed=1;
inData.replace("status","");
int currentStatus=digitalRead(inData.toInt());
Serial.println(currentStatus);
}
if (commandProcessed==0) {
Serial.print("Unknown command: ");
Serial.println(inData);
}
inData="";
Serial.flush();
} else {
inData += (c);
}
}
}
Роутер, как я уже писал, настроен как узел автоматики с правилом уведомления об изменении выключателя в систему:
В систему заведены объекты lcw0-lcw10 (пока не все реле подключены) класса Relays подкласса LightController1:
Свойства класса (дополнительно к родительскому классу добавлено свойство Pin):
Методы класса:
(метод changed как раз используется в сработке условия wifi-узла)
Код метода changed:
Код: Выделить всё
$this->setProperty('status',$params['status']);
$this->callMethod('onChange');
Код: Выделить всё
getURL("http://192.168.0.67/cgi-bin/master?op=send&line=turnoff".$this->getProperty('Pin').";",0);
$this->setProperty("status",0);
Код: Выделить всё
getURL("http://192.168.0.67/cgi-bin/master?op=send&line=turnon".$this->getProperty('Pin').";",0);
$this->setProperty("status",1);
В интерфейсе системы оно представлено в меню:
Пример настройки одного выключателя в меню:
В сценариях, соответственно, можно использовать callMethod('lcw1.turnOff'); или callMethod('lcw1.turnOn'); для выключения/включения отдельного элемента освещения.
P.S. Контроллер можно было и по-меньше, но я туда ещё планирую поставить каких датчиков (температуры, CO2 и т.п.), раз уж всё равно подключен и в гараже находится.