Quick home smart home

Nous continuons à partager des solutions basées sur des modules Master Kit :
«À titre expérimental, j'ai décidé ici d'essayer de faire une sorte de prototype de« maison intelligente »à la hâte et à un coût minime. Il y avait beaucoup d'hôteliers: lumière, ventilation, fenêtres, eau et contrôle infrarouge des appareils électriques. Au début, j'ai décidé de me limiter à un minimum de tâches: ventilation et éclairage dans la pièce.



Le système était basé sur Arduino Uno, avec la capacité de contrôler quatre broches indépendamment les unes des autres, et plusieurs modules de contrôle sans fil de Master Kit: les relais MP3328 et MP3330 à canal unique et à deux canaux ont joué le rôle d'appareils exécutifs , et les signaux leur sont transmis à l'aide d'un émetteur à huit canaux. MP3329 à une fréquence de 433 MHz.





Sur le MP3330, j'ai suspendu le contrôle de deux bandes LED sur le canapé, - un rétro-éclairage confortable pour la lecture du soir, - et sur le MP3328 - le contrôle d'une servo-machine pour ouvrir / fermer la fenêtre.

J'ai construit la structure d'entraînement à partir de matériaux improvisés, à savoir, à partir des pièces du concepteur LEGO.



Eh bien, et bien sûr, une simple radiocommande ne me suffisait pas: j'avais besoin d'accéder à l'ensemble du système via une interface Web et une surveillance de l'état en temps réel.

En tant que ressource de stockage, j'ai pris MySQL standard, où j'ai créé une table avec les identifiants des contrôleurs, les broches et la valeur d'état de chacun d'eux, pour des raisons de sécurité, en liant un identifiant système commun à chacun. De plus, j'ai câblé la possibilité de définir une minuterie fixe pour changer l'état, et cela fournit également un champ.



Lorsque vous travaillez avec des sockets Web, Arduino était légèrement bogué, et il n'y avait pas de temps pour en comprendre les raisons, donc, je l'ai esquissé de manière simple: avec des demandes ajax toutes les 100 ms. Bien sûr, cela est désastreux pour le trafic (près de 30 Mo par jour), mais pour un appartement avec Internet illimité pour la première fois, cela suffit.

L'interface Web la plus simple: 4 boutons, un pour chaque broche. Une seule pression modifie l'état et une pression longue règle la minuterie (j'ai 3 minutes câblées jusqu'à présent) pour le changer.



En conséquence, la version simplifiée de la logique est la suivante: L'

interface Web accède au serveur et vérifie l'état des contrôleurs, après quoi elle affiche l'interface pour travailler avec le système: 4 boutons dans l'état correspondant:

Scripts
$.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;
}




Lorsque le bouton correspondant est enfoncé, une requête POST est envoyée au fichier 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');
		});
	}




Dans la variable id, on passe l'identifiant du contrôleur, et dans l'état variable, la valeur de son état. Dans le fichier ajax.php, nous obtenons une demande POST et mettons les nouvelles données dans les valeurs d'enregistrement de l'id correspondant.

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, à son tour, accède au serveur Web via Ethernet, vérifie la clé système unique, les valeurs du contrôleur, analyse la chaîne reçue et commute les valeurs des broches.

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';
			}
		}
	}




Après cela, une ligne du formulaire «% qUerTY» est transférée vers une esquisse où elle est analysée, en fonction des règles câblées: chaque lettre correspond à son numéro de broche et le registre est responsable de la valeur finale: capital - 1, minuscule - 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;
      }
    }
}



Vous pouvez transférer des valeurs de 0 à 180 (degrés de rotation) à la broche responsable de la servo-machine et, par exemple, au variateur - jusqu'à 255. J'ai encore deux valeurs: 0 ou 1.





Il n'y a eu aucun problème de connexion des modules: les deux les relais sont jumelés avec l'émetteur une seule fois, sans avoir besoin de relier, et remplissent parfaitement leur tâche.





Mais avec la puissance de la servo-machine, j'ai mal calculé un peu: j'ai pris 6 kg, - ce qui, en général, suffit pour couvrir et lâcher le châssis de la fenêtre, qu'il a ouvert sous son propre poids - mais il vaut mieux prendre quelque chose de plus puissant, sur cas de vent fort ou de tirant d'eau.

En général, les possibilités de créativité sont illimitées: par exemple, vous pouvez connecter des capteurs, par exemple le CO2 ou la température, et ouvrir / fermer la fenêtre lorsque certains indicateurs sont atteints.
Bien sûr, je prévois de réécrire tout le système sur les sockets Web, ce qui rend les demandes plus faciles - et rend le transfert et l'analyse des valeurs des paramètres plus flexibles.

Croquis pour arduino

Petit

rétroéclairage de travail vidéo


Ouverture de fenêtre


Dmitry Kuznetsov "

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


All Articles