Hogar rápido hogar inteligente

Los módulos de Master Kit continúan compartiendo soluciones con nosotros :
“Como experimento, decidí aquí intentar hacer algún tipo de prototipo de“ casa inteligente ”a toda prisa y a un costo mínimo. Había muchos hoteleros: luz, ventilación, ventanas, agua y control IR de electrodomésticos. Al principio, decidí limitarme a un mínimo de tareas: ventilación e iluminación en la habitación.



El sistema se basaba en el Arduino Uno, con la capacidad de controlar cuatro pines independientemente uno del otro, y varios módulos de control inalámbrico de Master Kit: los relés MP3328 y MP3330 de un solo canal y dos canales asumieron el papel de dispositivos ejecutivos , y las señales se transmiten a ellos utilizando un transmisor de ocho canales MP3329 a una frecuencia de 433 MHz.





En la MP3330, colgué el control de dos tiras de LED sobre el sofá, - una acogedora luz de fondo para la lectura noche, - y en el MP3328 - el control de un servo-máquina para abrir / cerrar la ventana.

Construí la estructura de la unidad a partir de materiales improvisados, es decir, de las partes del diseñador LEGO.



Bueno, y, por supuesto, el simple control de radio no fue suficiente para mí: necesitaba acceso a todo el sistema a través de una interfaz web y monitoreo de estado en tiempo real.

Como recurso de almacenamiento, tomé MySQL estándar, donde creé una tabla con los identificadores de los controladores, pines y el valor de estado de cada uno de ellos, por razones de seguridad, vinculando un identificador de sistema común a cada uno. Además, conecté la capacidad de configurar un temporizador fijo para cambiar el estado, y también se proporciona un campo para esto.



Cuando trabajaba con sockets web, Arduino estaba un poco defectuoso y no había tiempo para descubrir las razones, por lo tanto, lo bosquejé de una manera simple: con solicitudes ajax cada 100 ms. Esto, por supuesto, es desastroso para el tráfico (casi 30 MB por día), pero para un apartamento con Internet ilimitado por primera vez, es suficiente.

La interfaz web más simple: 4 botones, uno para cada pin. Una sola pulsación cambia el estado, y una pulsación larga establece el temporizador (tengo 3 minutos programados hasta ahora) para cambiarlo.



Como resultado, la versión simplificada de la lógica es la siguiente:

la interfaz web accede al servidor y comprueba el estado de los controladores, después de lo cual muestra la interfaz para trabajar con el sistema: 4 botones en el estado correspondiente:

Guiones
$.ajax({
			url: 'engine/ajax.php',
			type: 'POST',
			dataType: 'json',
			data: {action: 'getStates'},
		})
		.done(function(data) {
			for (key in data.success){
				var controller = data.success[key];
				if($('.btn#btn_' + controller.id).length === 0){
					html = '';
					html += '<div ';
					if (controller.timer_switch > 0){
						var seconds = 60  - (controller.timer_switch % 60)
						html += 'seconds="' + seconds + '"';
					}
					html += 'id="btn_' + controller.id + '" class="btn" state="' + controller.state + '"><div class="btn_timer"></div></div>';
					$('#btn_placer').append(html);
				} else {
					if (controller.state == 1){
						$('.btn#btn_' + controller.id).removeClass('btn_off').addClass('btn_on');
					} else if (controller.state == 0){
						$('.btn#btn_' + controller.id).removeClass('btn_on').addClass('btn_off');
					}
					if (controller.timer_switch > 0){
						var seconds = 60 - controller.timer_switch % 60;
						var minutes = Math.floor(controller.timer_switch / 60);
						$('.btn#btn_' + controller.id).addClass('seconds').css('background-position', (-seconds * 100) + 'px 0px').find('.btn_timer').text(minutes + 'M');
					} else {
						$('.btn#btn_' + controller.id).css('background-position', '0px 0px').removeClass('seconds').find('.btn_timer').text('');
					}
				}
			}
			setTimeout(function(){
				getStates();
			}, 1000);
		})
		.fail(function(data) {
			console.log('error');
		});



function getStates($sql){
	$result = $sql->query("SELECT * FROM `controllers` WHERE `home_id` = '1' ORDER BY `order`");
	if (isset($result->rows)){
		$result = $result->rows;
		foreach ($result as $key => $value) {
			if (strtotime($result[$key]['timer']) > -62169990000){
				// echo strtotime($result[$key]['timer']);
				$timer_switch = strtotime($result[$key]['timer']) - strtotime(date("Y-m-d H:i:s"));
				$result[$key]['timer_switch'] = $timer_switch;
				if ($timer_switch < 0){
					$sql->query("UPDATE `controllers` SET `state` = '".$result[$key]['timer_state']."', `timer` = '0000-00-00 00:00:00', `timer_state` = '' WHERE `id` = '".$result[$key]['id']."'");
				}
			}
		}
		$res['success'] = $result;
	} else {
		$res['error'] = ' ';
	}
	return $res;
}




Cuando se presiona el botón correspondiente, se envía una solicitud POST al archivo ajax.php:

Javascript
$(document).on('mousedown', '.btn', function(event){
			event.preventDefault();
			var id = parseInt($(this).attr('id').replace('btn_', ''));
			click_wait = false;
			mousetimer = setTimeout(function(){
				click_wait = true;
				setTimer(id);	
			}, 2000);
		});

		$(document).on('mouseup', '.btn', function(){
			clearTimeout(mousetimer);
			if (!click_wait){
				var id = parseInt($(this).attr('id').replace('btn_', ''));
				switchController(id);
				console.log('click !!!');
				click_wait = false;
			}
		});
	function switchController(id){
		var el = $('.btn#btn_' + id);
		var state = parseInt($(el).attr('state'));
		var need_state;
		if (state == 0){
			need_state = 1;
		} else if (state == 1){
			need_state = 0;
		}
		$(el).addClass('waiting');
		$.ajax({
			url: 'engine/ajax.php',
			type: 'POST',
			dataType: 'json',
			data: {action: 'setState', id: id, state: state, need_state: need_state},
		})
		.done(function(data) {
			if (data.success == 'ok'){
				$(el).attr('state', need_state);
				$(el).removeClass('waiting').removeClass('btn_on').removeClass('btn_off');
				if(need_state == 1){
					$(el).addClass('btn_on');
				} else if(need_state == 0){
					$(el).addClass('btn_off');
				}
			}
		})
		.fail(function(data) {
			console.log('error');
		});
	}
	function setTimer(id){
		var el = $('.btn#btn_' + id);
		var state = parseInt($(el).attr('state'));
		var need_state;
		if (state == 0){
			need_state = 1;
		} else if (state == 1){
			need_state = 0;
		}
		$(el).attr('seconds', 0);
		$(el).addClass('seconds');
		$.ajax({
			url: 'engine/ajax.php',
			type: 'POST',
			dataType: 'json',
			data: {action: 'setTimer', id: id, state: state, need_state: need_state},
		})
		.done(function(data) {
		})
		.fail(function(data) {
			console.log('error');
		});
	}




En la variable id, pasamos el identificador del controlador, y en el estado variable, el valor de su estado. En el archivo ajax.php, recibimos una solicitud POST y colocamos los nuevos datos en los valores de registro de la identificación correspondiente.

Php
function setState($sql){
	$need_state = (int)$_POST['need_state'];
	$state = (int)$_POST['state'];
	$id = (int)$_POST['id'];

	$result = $sql->query("UPDATE `controllers` SET `state` = '".$need_state."', `timer` = '0000-00-00 00:00:00' WHERE `id` = '".$id."'");
	//   Arduino
	$result = $sql->query("SELECT `state` FROM `controllers` WHERE `id` = '".$id."'");
	if ($need_state == $result->row['state']){
		$res['success'] = 'ok';
	} else {
		$res['success'] = 'err';
	}
	return $res;
}

function setTimer($sql){
	$need_state = (int)$_POST['need_state'];
	$state = (int)$_POST['state'];
	$id = (int)$_POST['id'];

	$result = $sql->query("UPDATE `controllers` SET `timer` = NOW() + INTERVAL 3 MINUTE, `timer_state` = '".$need_state."' WHERE `id` = '".$id."'");
	//   Arduino
	$result = $sql->query("SELECT `state` FROM `controllers` WHERE `id` = '".$id."'");
	if ($need_state == $result->row['state']){
		$res['success'] = 'ok';
	} else {
		$res['success'] = 'err';
	}
	return $res;
}




Arduino, a su vez, accede al servidor web a través de Ethernet, verifica la clave única del sistema, los valores del controlador, analiza la cadena recibida y cambia los valores del pin.

Php
if (isset($_GET['key']) && $_GET['key'] !== ''){
		$key = $_GET['key'];
	} else {
		die;
	}
	
$key = $sql->escape($key);
	$result = $sql->query("SELECT * FROM `controllers` WHERE `home_id` = '1' AND `key` = '".$key."' ORDER BY `order`");
	foreach ($result->rows as $key => $value) {
		if ($value['id'] == '1'){
			if ($value['state'] == '1'){
				$ar .= 'Q';
			} else if ($value['state'] == '0') {
				$ar .= 'q';
			}
		}
		if ($value['id'] == '2'){
			if ($value['state'] == '1'){
				$ar .= 'W';
			} else if ($value['state'] == '0') {
				$ar .= 'w';
			}
		}
		if ($value['id'] == '3'){
			if ($value['state'] == '1'){
				$ar .= 'E';
			} else if ($value['state'] == '0') {
				$ar .= 'e';
			}
		}
	}




Después de eso, una línea de la forma "% qUerTY" se transfiere al boceto donde se analiza, según las reglas cableadas: cada letra corresponde a su número de pin y el registro es responsable del valor final: mayúscula - 1, minúscula - 0.

Arduino
void readData(){
  if (led_connect){
    digitalWrite(6, HIGH);
  } else {
    digitalWrite(6, LOW);
  }
  digitalWrite(4, LOW);
  previousMillis = currentMillis;
  
  led_connect = !led_connect;
    while (client.available()){      
      switch (char c = client.read()) {
        case 'Q':
          digitalWrite(9, HIGH);
          break;
        case 'q':
          digitalWrite(9, LOW);
          break;
        case 'W':
          digitalWrite(8, HIGH);
          break;
        case 'w':
          digitalWrite(8, LOW);
          break;
        case 'E':
          digitalWrite(7, HIGH);
          myservo.write(0);
          break;
        case 'e':
          digitalWrite(7, LOW);
          myservo.write(180);
          break;
      }
    }
}



Puede transferir valores de 0 a 180 (grados de rotación) al pin responsable de la servomáquina y, por ejemplo, al atenuador, hasta 255. Todavía tengo dos valores: 0 o 1.





No hubo problemas para conectar los módulos: ambos los relés se emparejan con el transmisor una vez, sin la necesidad de volver a vincular, y realizan perfectamente su tarea.





Pero con el poder de la máquina servo, calculé un poco mal: tomé 6 kg, lo que, en general, es suficiente para cubrir y soltar el marco de la ventana, que se abrió por su propio peso, pero es mejor llevar algo más poderoso, en caso de fuerte viento o corriente de aire.

En general, el alcance de la creatividad es ilimitado: por ejemplo, puede conectar sensores, por ejemplo, CO2 o temperatura, y abrir / cerrar la ventana cuando se alcanzan ciertos indicadores.
Bueno, por supuesto, planeo reescribir todo el sistema en los sockets web, facilitando las solicitudes, y hacer que la transferencia y el análisis de los valores de los parámetros sean más flexibles.

Boceto para arduino

Pequeño video

Luz de fondo de trabajo


Ventana de apertura


Dmitry Kuznetsov "

Source: https://habr.com/ru/post/es383679/


All Articles