Raspberry Pi : Mesurer la température avec un capteur DS18B20

Raspberry Pi : Mesurer la température avec un capteur DS18B20

Le capteur DS18B20 est un capteur à coût réduit (10 € les 5 capteurs étanches avec câble de 1 mètre ou 8 € les 10 capteurs de base), qui utilise le protocole 1-wire. On peut connecter plusieurs capteurs DS18B20 en série sur un même port du Raspberry Pi. Nous allons voir ici comment connecter un seul thermomètre et en écrire la valeur dans un fichier txt par un script Shell ou PHP.

Connecter le capteur DS18B20

Câblage d'un capteur de température DS18B20 à un Raspberry PiUne description détaillée du capteur DS18B20 est disponible en anglais ici. Le capteur peut mesurer des températures entre -55°C et +125°C et reste précis à 0.5°C sur l’intervalle -10°C à +85°C. Chaque capteur a un numéro de série unique (64 bits) et peut donc être identifié précisément.

Pour une seule sonde, on câble comme dans le schéma de droite (note du 29/12/2019 : ATTENTION, le schéma de droite est faux, le connecteur 1 est raccordé à GND, le connecteur 3 à VCC 3.3V !).

On peut mettre plusieurs capteurs en série (en théorie autant qu’on veut, en pratique il semble qu’il vaille mieux se limiter à 10 capteurs). Dans ce cas, on connecte une seule résistance 4.7 K et plusieurs sondes selon le schéma ci-dessous (note du 29/12/2019 : qui est juste, connecteur 1 raccordé à GND, 3 à VDD 3.3 V).

Câblage de plusieurs DS18B20 en série

Paramétrer le Pi pour lire la / les températures

Editer /boot/config.txt pour qu’il contienne la ligne suivante puis redémarrer le Pi :

dtoverlay=w1-gpio

Tester le/les capteurs

En ligne de commande, taper les lignes suivantes

sudo modprobe w1-gpio
sudo modprobe w1-therm
cd /sys/bus/w1/devices
ls
cd 28-xxxx (changer pour correspondre aux numéros de série qui s'affichent)
cat w1_slave 

Si le capteur est correctement câblé, on voit apparaître quelque chose qui ressemble à ce qui suit, où t=20187 signifie que la température est 20.187°C :

43 01 4b 46 7f ff 0d 10 bd : crc=bd YES
43 01 4b 46 7f ff 0d 10 bd t=20187

S’il y a plus d’un capteur de température,  ls dans /sys/bus/w1/devices affichera plusieurs répertoires.

Installer les drivers sur le Pi

modprobe w1-gpio et modprobe w1-therm sont les drivers pour les capteurs de température. Ils doivent démarrer lorsque le Pi démarre. Pour celà, ajouter les lignes suivantes à /etc/modules  :

w1‐gpio
w1‐therm

On redémarre le Pi et les drivers sont maintenant chargés.

Un script PHP pour lire les différentes températures

Noter que pour exécuter un script PHP en ligne de commande, il faut avoir préalablement installé PHP-CLI, avec la commande apt-get install php5-cli .

Le script read-temperature.php (exécutable) contient :

#!/usr/bin/env php
<?php

if (!defined("THERMOMETER_SENSOR_PATH")) {
	define("THERMOMETER_SENSOR_PATH", "/sys/devices/w1_bus_master1/28-0000046766b0/w1_slave"); 	
}


// Open resource file for thermometer
$thermometer = fopen(THERMOMETER_SENSOR_PATH, "r"); 

// Get the contents of the resource
$thermometerReadings = fread($thermometer, filesize(THERMOMETER_SENSOR_PATH)); 

// Close resource file for thermometer
fclose($thermometer); 

// We're only interested in the 2nd line, and the value after the t= on the 2nd line
preg_match("/t=(.+)/", preg_split("/\n/", $thermometerReadings)[1], $matches);
$temperature = $matches[1] / 1000; 


// Output the temperature
print $temperature . " °C. \n\r"; 
?>

On l’exécute avec la commande en ligne php read-temperature.php .

Dans mon cas, il affiche 20.875 ▒C.

Un script Shell pour lire la température

Voir ce projet, en anglais. Il lit plusieurs températures à la fois et les affiche à l’écran du Pi sous la forme 28-0000046766b0=20.937 .

Pour ma part, je n’ai qu’une seule température à lire et je veux l’écrire dans un fichier texte que j’enverrai ultérieurement à un serveur distant.

Je crée le script read-status.sh, (avec encodage UTF8 sans BOM, retours chariot UNIX et droits d’accès 744) qui contient :

#!/bin/bash

# afficher les variables d'environnement (utile pour un cron job)
printenv

# check my Hostname variable 
ThisHost=$(hostname) 

# Get UTC time
UtcNow= date -u 

# There should be no ECHO in a CRON job, so comment this !
echo $ThisHost

# $1 is the variable appended to the file when executed
# example : ./send-status.sh essai would put "essai" in $1

# local file to write into
FILE=$ThisHost"-status.txt" 
# local directory to write to file and pick it for upload
REPERTOIRE="/home/jf/temp/"
echo $REPERTOIRE$FILE

#Read last temperature 
temperature=$(find /sys/bus/w1/devices/ -name "28-*" -exec cat {}/w1_slave \; | grep "t=" | awk -F "t=" '{print $2/1000}')

# create file and add content to it
cd $REPERTOIRE
cat > $FILE <<EOF # single > will empty and overwrite the file
pi hostname, time, statut, temperature
$ThisHost, $UtcNow, $1, $temperature 
EOF

Attention, ce script ne lit que le dernier des capteurs de température.

On l’exécute avec la commande en ligne ./read-status.sh OK  .

Dans le répertoire temp, j’ai maintenant un fichier piname-status.txt, qui contient :

pi hostname, time, statut, temperature
piname, Sun 2 Apr 15:01:44 CEST 2017, OK, 21.312

Et maintenant ?

Je peux lire la température mesurée par le Pi et la noter dans un fichier texte à chaque fois que j’exécute ./read-status.sh OK  en commande en ligne.

La prochaine étape sera d’envoyer les informations du fichier texte à un serveur externe de manière automatique. J’ai déjà décrit l’utilisation d’un cron job dans l’article « Cron Job sur un hébergement mutualisé OVH » mais je vais le réaliser sur un Raspberry Pi. Cest l’objet de l’article « Raspberry Pi : envoyer en ftp un fichier à un serveur, par Cron Job ».

Manipuler des fichiers en PHP

Manipuler des fichiers en PHP

J’ai besoin de lire le contenu de fichiers créés par une application tierce. Il faut :

  • lister les fichiers avec extension ‘log’ dans un répertoire ;
  • pour chaque fichier, lire chaque ligne ;
  • pour chaque ligne, transformer son contenu texte en un array de données ;
  • selon la valeur de certains éléments de la ligne, réaliser une action spécifique ;
  • une fois le fichier traité, en modifier l’extension (devient ‘fait’).

Pour cela, j’utilise un script PHP installé dans le répertoire /var/www/html/tests  d’un Raspberry Pi sur lequel j’ai installé apache et PHP (cf article Un serveur Web sur mon Raspberry Pi).

De quoi je pars ?

/var/www/html/tests  contient le fichier test-explode.php  et un sous répertoire data .

/var/www/html/tests/data  contient 3 fichiers dont le nom est sous la forme domoAL_20150910.log . Chacun de ces fichiers contient 1000 à 2000 lignes de type :

1441868688	!	Thu, 10 Sep 2015 07:04:48 GMT ! dev_0x05 !	2756	2	1462	1831	1931	0
1441868744	!	Thu, 10 Sep 2015 07:05:44 GMT ! dev_0x05 !	2761	2	1462	1825	1918	0
1441868799	!	Thu, 10 Sep 2015 07:06:39 GMT ! dev_0x05 !	2756	2	1462	1825	1950	0
1441868854	!	Thu, 10 Sep 2015 07:07:34 GMT ! dev_0x05 !	2760	2	1468	1831	1993	868
1441868910	!	Thu, 10 Sep 2015 07:08:30 GMT ! dev_0x05 !	2754	2	1468	1843	2037	0
1441868965	!	Thu, 10 Sep 2015 07:09:25 GMT ! dev_0x05 !	2762	2	1468	1856	2075	0

Lister les fichiers d’un répertoire

Objectif : lister les fichiers avec extension ‘log’ dans un répertoire.

Ma solution est fortement inspirée de cette page de php.net.

<section class="data">
<H3>Test list each file in a directory</h3>	

<?php
// http://php.net/manual/fr/function.scandir.php
echo "<br /><hr />" ;
$directory = '../tests/data';
$file_read = array( 'log' );

$scanned_directory = array_diff( scandir( $directory ), array( '..', '.' ) );

foreach ( $scanned_directory as $key => $value ) {

	$type = explode( '.', $value ); 
	$type = array_reverse( $type );
	if( !in_array( $type[0], $file_read ) ) {		// continue if not ".log"
		continue;
	}

	$lines = 0;
	$handle = fopen( $directory . DIRECTORY_SEPARATOR . $value, 'r' );

	while ( !feof( $handle ) ) {

		if ( is_bool( $handle ) ) {
			break;
		}

		$line = fgets( $handle );
		$lines++;
	}

	fclose( $handle );

	$result['lines_html'][] = array ( $directory . '/' . $value ,  $lines ) ; 
	$result['lines_count'] = $result['lines_count'] + $lines;
	$result['files_count'] = $result['files_count'] + 1;
	
}
var_dump( $result ) ;
echo "<br /><hr />" ;

?>

</section>

ce script affiche un array sur ma page (que j’ai mis en forme à la main) :

array(3) { 
	["lines_html"]=> array(3) {
		[0]=> array(2) {
			[0]=> string(33) "../tests/data/domoAL_20150910.log" 
			[1]=> int(1561) 
		} 
		[1]=> array(2) { 
			[0]=> string(33) "../tests/data/domoAL_20150913.log" 
			[1]=> int(1560) 
		} 
		[2]=> array(2) { 
			[0]=> string(33) "../tests/data/domoAL_20150914.log" 
			[1]=> int(1560) 
		} 
	} 
	["lines_count"]=> int(4681) 
	["files_count"]=> int(3) 
}

Le script trouve donc bien les 3 fichiers et le nombre de ligne de chaque fichier.

Lire les lignes d’un fichier

Objectif : pour chaque fichier, lire chaque ligne.

Créer un array avec la liste des fichiers

Je crée une fonction qui génère un array avec le nom de chaque fichier du répertoire.

<?php
// http://php.net/manual/fr/function.scandir.php

// list all log files in the ../tests/data directory in an array
$list_files = al_list_files_in_dir( '../tests/data', array( 'log' ) ) ;
echo "<br />liste des fichiers<hr />" ;
var_dump( $list_files ) ;
echo "<br /><hr />" ;

function al_list_files_in_dir( $directory, $file_read ) {

	$scanned_directory = array_diff( scandir( $directory ), array( '..', '.' ) );
	
	foreach ( $scanned_directory as $key => $value ) {
	
		$type = explode( '.', $value ); 
		$type = array_reverse( $type );
		if( !in_array( $type[0], $file_read ) ) {		// continue if not ".log"
			continue;
		}

		$result[] = $directory . '/' . $value ; 
	}
	
	return $result ;
	
}

?>

J’obtiens un array comme celui ci (mis en forme à la main) :

array(3) {
	[0]=> string(33) "../tests/data/domoAL_20150910.log" 
	[1]=> string(33) "../tests/data/domoAL_20150913.log" 
	[2]=> string(33) "../tests/data/domoAL_20150914.log" 
}

Dans chaque fichier, lire les lignes une à une

<H4>read each line of each log file</h4>	

<?php
foreach ( $list_files as $key => $value ) {
	
	$file_content = al_read_lines_in_file( $value ) ;
	echo "<br />contenu des fichiers<hr />" ;
	var_dump( $file_content ) ;
	echo "<br /><hr />" ;		
}

function al_read_lines_in_file( $file ) {
	
	$handle = fopen( $file, "r" );

	$count = 0 ;
	if ($handle) {
		
		while (($line = fgets($handle)) !== false) {
			$content[] = $line ;
		}
	
		fclose($handle);
		return $content ;
		
	} else {
		echo "can't open file \n" ;
	} 
	
}


?>

Affiche 3 gigantesques arrays de 1500 lignes chacun environ.

Mettre le contenu d’une ligne dans un array

Objectif : pour chaque ligne, transformer son contenu texte en un array de données.

Chaque ligne contient des données séparées soit par « ! », soit par une tabulation. Je veux la transformer en une série de données.

<H3>Store the content of each line in an array</h3>	
<?php

foreach ( $list_files as $key => $value ) {

	$file_content = al_read_lines_in_file( $value ) ;
	$line_count = 0 ;

	foreach ( $file_content as $contents => $content ) {
		
		$file_data[ $line_count ] = al_store_lines_in_array( $content ) ;
		$line_count++ ;
	}

	echo "<br />données" . $value . "<hr />" ;
	var_dump( $file_data ) ;
	echo "<br /><hr />" ;	
}



function al_store_lines_in_array( $text ) {

	$val = explode("!", $text);
	
	// split on tabulation
	$temp = preg_split( "/[\t]/", trim( $val[3] ) );

	// strip whitespace from the beginning and end of string.	
	$val[0] = trim( $val[0] );
	$val[1] = trim( $val[1] );
	$val[2] = trim( $val[2] );	
	$val[3] = trim( $val[3] );
	$val[4] = trim( $temp[0] );
	$val[5] = trim( $temp[1] );
	$val[6] = trim( $temp[2] );
	$val[7] = trim( $temp[3] );
	$val[8] = trim( $temp[4] );
	$val[9] = trim( $temp[5] );	

	return $val ;
}

?>

Pour le fichier ../tests/data/domoAL_20150910.log , ça me donne un array de 1560 arrays sous forme :

array(1560) { 
	[0]=> array(10) { 
		[0]=> string(10) "1441843202" 
		[1]=> string(29) "Thu, 10 Sep 2015 00:00:02 GMT" 
		[2]=> string(8) "dev_0x05" 
		[3]=> string(23) "2755	2	1531	1906	2168	0" 
		[4]=> string(4) "2755" 
		[5]=> string(1) "2" 
		[6]=> string(4) "1531" 
		[7]=> string(4) "1906" 
		[8]=> string(4) "2168" 
		[9]=> string(1) "0" 
	} 
	...
}

tout est sous la forme de texte (string). Il va falloir maintenant que je transforme la date « 1441843202 » et la dernière valeur (0 pour la ligne affiché ci-dessus) en données sur lesquelles je pourrai valider ou non une condition.

Mettre des conditions sur le contenu de l’array

Objectif : selon la valeur de certains éléments de la ligne, réaliser une action spécifique. Si le dernier élément de l’array n’est pas nul, l’afficher avec sa date. Si le temps écoulé depuis le dernier élément affiché est supérieur à 15 minutes, afficher la ligne même si sa dernière valeur est nulle.

conditions sur deux données

Je crée un array contenant des données comme celles qui doivent être produites par la lecture d’un des fichiers log. Je transforme les deux données sur lesquelles je veux pouvoir faire une condition en entiers avec la fonction intval() .

Pour le timestamp, je veux qu’il puisse être affiché de manière lisible : $date_humain = date ( « d/m/Y H:i:s » , $date_stamp ) ;  va transformer 1441843313 en 10/09/2015 02:01:53 .

Le script suivant lit le contenu de l’array de données puis :

  1. si la dernière valeur ($value[9] ) est nulle, met $save à faux, sauf si ça fait plus de 5 minutes que $save est faux.
  2. si $save est true, imprime les données

Vu l’array que j’ai construit pour cet essai, je devrais avoir 4 lignes imprimées et une non (celle qui correspond à la deuxième valeur de l’array, qui a un timestamp très proche du premier).

<H3>Test conditions sur éléments de l'array de données</h3>	

<?php

$data = array (
	0 => array(
		0 => "1441843202" ,
		1 => "Thu, 10 Sep 2015 00:00:02 GMT" ,
		2 => "dev_0x05" ,
		3 => "2755	2	1531	1906	2168	0" ,
		4 => "2755" ,
		5 => "2" ,
		6 => "1531" ,
		7 => "1906" ,
		8 => "2168" ,
		9 => "0" 	
	),
	1 => array(
		0 => "1441843258" ,
		1 => "Thu, 10 Sep 2015 00:00:58 GMT" ,
		2 => "dev_0x05" ,
		3 => "2761	2	1537	1906	2137	0" ,
		4 => "2761" ,
		5 => "2" ,
		6 => "1537" ,
		7 => "1906" ,
		8 => "2137" ,
		9 => "0"
	),
	2 => array(
		0 => "1441843313" ,
		1 => "Thu, 10 Sep 2015 00:01:53 GMT" ,
		2 => "dev_0x05" ,
		3 => "2761	2	1537	1906	2137	0" ,
		4 =>  "2761" ,
		5 => "2" ,
		6 => "1537" ,
		7 => "1906" ,
		8 => "2137" ,
		9 => "216"		
	),
	3 => array(
		0 => "1441849352" ,
		1 =>  "Thu, 10 Sep 2015 01:42:32 GMT" ,
		2 =>  "dev_0x05" ,
		3 =>  "2761	2	1537	1906	2137	0" ,
		4 =>  "2761" ,
		5 =>  "2" ,
		6 =>  "1537" ,
		7 => "1906" ,
		8 => "2137" ,
		9 => "0"		
	),
	4 => array(
		0 => "1441851457" ,
		1 => "Thu, 10 Sep 2015 02:17:37 GMT" ,
		2 => "dev_0x05" ,
		3 => "2761	2	1537	1906	2137	0" ,
		4 => "2761" ,
		5 => "2" ,
		6 => "1537" ,
		7 => "1906" ,
		8 => "2137" ,
		9 => "51"		
	)
) ;

// var_dump( $data ) ;
// echo "<hr /><br />" ;

echo "<hr />" ;	

$ref_time = 0000000 ;		

foreach ( $data as $key => $value ) {

	$date_stamp = intval( $value[0] ) ;
	$date_humain = date ( "d/m/Y H:i:s" , $date_stamp ) ;
	
	$time_lapsed = $date_stamp - $ref_time ; // temps écoulé depuis dernière ligne à imprimer
	
	$chaudiere = intval( $value[9] ) ;
	
	if ( $chaudiere == 0 ) {
		
		$save = false ; 
		
		if ( $time_lapsed > 300 ) {	// plus de 5 minutes
			
			$save = true ;
			$ref_time = $date_stamp ;
		}
		
		echo "<hr />" ;
		
	} else {
		
		$save = true ;
		
	}
	
	echo $key . " | \t" . $time_lapsed . " | \t" ; 

	if ( $save == true ) {
		
		echo $chaudiere . " | \t" . $save  . " | \t" . $date_stamp  . " | \t" . $date_humain ;
		
	}	else {
		
		echo "do not print" ;
		
	}
	
	echo "<hr />" ;
	
}


echo "<br />" ;	

?>

et le résultat est conforme à mes attentes :

tests en php de manipulation de tableau

Modifier l’extension d’un fichier

Objectif : une fois le fichier traité, en modifier l’extension (devient ‘fait’).

ici j’ai fait beaucoup usage de /var/log/apache2/error.log  !!! J’ai ainsi compris que j’obtenais une erreur « Permission denied » lorsque je voulais renommer.

Je n’ai jamais réussi à réaliser l’opération, même en la remplaçant par une copie puis une suppression du fichier initial… Puis, j’ai eu l’idée que c’est normal ! Heureusement que l’on ne peut pas supprimer ou renommer des fichiers au travers d’un navigateur internet…

j’ai donc créé un fichier test-rename.php (droits 740) dans le répertoire /home/jf/exec du Raspberry Pi. Dans le répertoire /home/jf/snd/data, j’ai placé les 3 fichiers à renommer. Et ça fonctionne !

#!/usr/bin/php

<?php
$directory = '/home/jf/snd/data';
$file_read = array( 'log' );

$scanned_directory = array_diff( scandir( $directory ), array( '..', '.' ) );


foreach ( $scanned_directory as $key => $value ) {

	$type = explode( '.', $value ); 
	// var_dump( $type ) ;
	
	$type = array_reverse( $type );
	if( !in_array( $type[0], $file_read ) ) {		// continue if not ".log"
		continue;
	}

	$old_name = $directory . DIRECTORY_SEPARATOR . $value ;
	$new_name = $directory . DIRECTORY_SEPARATOR . $type[1] . ".fait" ;
	
	$return_val = rename( $old_name, $new_name );
			
	if ( $return_val == 1 ) { 
	   echo "success : " . $old_name . " renamed " . $new_name . "\r\n"; 
	} else { 
	   echo "failed to rename " . $old_name . " to : " . $new_name . "\r\n" ; 
	} 		

}

?>

Ensuite dans la console de commande du Raspberry Pi :

cd /home/jf/exec
./test-rename.php

Et ça y est, la console affiche

success : /home/jf/snd/data/domoAL_20150910.log renamed /home/jf/snd/data/domoAL_20150910.fait
success : /home/jf/snd/data/domoAL_20150913.log renamed /home/jf/snd/data/domoAL_20150913.fait
success : /home/jf/snd/data/domoAL_20150914.log renamed /home/jf/snd/data/domoAL_20150914.fait

Et si je vérifie, les 3 fichiers ont bien été renommés.

Et maintenant ?

J’ai fait fonctionner les prototypes de code dont je vais avoir besoin. Je vais les utiliser pour faire échanger des données entre un Raspberry Pi (qui contient les fichiers log) et une box domotique qui va archiver et afficher les données.

Motion : Régler la sensibilité de détection

Motion : Régler la sensibilité de détection

Dans l’article précédent de cette série , j’indiquais que motion crée beaucoup d’images et vidéos qui ne correspondent pas à de véritables mouvements devant la caméra.

motion permet de nombreux réglages. Je l’ai choisi pour ça. En particulier, il semble efficace pour éliminer les mouvements liés au vent.

Pour comprendre les différents réglages, il faut lire attentivement cette aide (en anglais) sur les réglages de la détection de mouvement avec motion.

La situation initiale

Dans /etc/motion/motion.conf , j’ai entre autres les réglages suivants :

# Je ne comprends pas comment ça fonctionne. Valeur par défaut = EedDl 
despeckle_filter EedDl

# Ignore les changements soudains de lumière - de 0 (arrêt) à 100%.
lightswitch 0

# Smartmask est un masque auto-apprenant qui va bloquer la détection dans les endroits de l'image où il y a des mouvements fréquents, comme des branches qui bougent à cause du vent. 0 (off) à 10 rapide. 
smart_mask_speed 0

# nombre minimum de photos avec du mouvement avant détection : defaut 1 : tous mouvements détectés.
minimum_motion_frames 1

# base pour déclarer un mouvement. C'est le nombre de pixels changés après le filtrage du bruit, le masque et le "despeckle". 
threshold 1500

# réglage automatique du threshold - ne fonctionne pas 
threshold_tune off

# niveau de "bruit" pour distinguer le bruit de la caméra d'un mouvement
noise_level 32

# si on, ajustement automatique du bruit
noise_tune off

Avec ce réglage, j’obtiens (sur une journée avec du vent) beaucoup trop de faux positifs.

Les faux positifs contiennent du « bruit » la nuit, l’éclairage de la lampe à côté de la caméra, et des mouvements liés au vent :

"bruit" la nuit

« bruit » la nuit

Réglages du 1er juillet – soir

J’ai modifié le niveau de lumière qui déclenche un mouvement, le nombre d’images modifiées avant la détection de mouvement, le ‘theshold’ et le niveau de bruit.

despeckle_filter EedDl 
lightswitch 25
smart_mask_speed 5
minimum_motion_frames 3
threshold 3000
noise_level 100
noise_tune off

Nota : après chaque réglage de motion.conf, il faut redémarrer le service motion avec :

service motion restart

Le 2 juillet, de 0h à 14h (heure du réglage suivant, il n’y a eu que 24 fichiers créés ( 1 Mo) et 24 fichiers correspondent à un mouvement effectif (ma voiture qui part puis qui revient).

Mais la caméra ne détecte pas une personne qui se déplace à pied.

Réglages du 2 juillet – 11h45

Je n’ai corrigé que le « threshold ».

despeckle_filter EedDl  
lightswitch 25
smart_mask_speed 5
minimum_motion_frames 3
threshold 1500
noise_level 100
noise_tune off

Pendant la journée tout va bien : pas de fausse détection (mais le système ne détecte toujours pas une personne à pied) mais la nuit, il y a eu plein de faux positifs. Dès que le jour s’est levé, il n’y a plus de souci.

Réglages du 3 juillet – détecter une personne à pied

Pour détecter une personne à pied, j’ai modifié les paramètres suivants successivement jusqu’à ce que je déclenche une détection de mouvement :

minimum_motion_frames 1
noise_level 32
smart_mask_speed 10
motion : détection d'une personne

motion : détection d’une personne

Ce n’est qu’au dernier changement que j’ai détecté le mouvement d’une personne.

Ces paramètres détectent aussi bien une voiture qu’une personne (12h20 = OK) :

despeckle_filter EedDl  
lightswitch 25
smart_mask_speed 10
minimum_motion_frames 3
threshold 1500
noise_level 32
noise_tune off

Je laisse les paramètres suivants en l’état :

despeckle_filter EedDl  
lightswitch 25
smart_mask_speed 10
minimum_motion_frames 3
noise_tune off

et j’étudie l’impact de changements sur threshold ou noise_level :

Threshold Noise_level Détecte ?
1500 100 12h42 ne me détecte pas et fait un faux positif
3000 32 12h26 ME détecte = OK
3000 100 12h34  NON, ne me détecte pas

Les réglages suivants sont donc satisfaisants :

despeckle_filter EedDl  
lightswitch 25
smart_mask_speed 10
minimum_motion_frames 3
threshold 3000
noise_level 32
noise_tune off

Maintenant reste à faire un bilan sur environ 24h (depuis 13h le 3 juillet) :

  • détection d’une personne : Oui
  • détection d’une voiture : oui
  • faux positifs : 2 en une nuit.

Je considère donc que ces réglages sont corrects.

Autres notes :

On peut mettre motion en mode deboguage pour voir comment se passe la détection : voir cette page sur le wiki de motion.

 

une caméra IP avec une vieille tablette android ?

une caméra IP avec une vieille tablette android ?

Dans cet article de la série , nous allons transformer une vieille tablette android en caméra IP dont le flux sera suivi par un Raspberry Pi, avec motion. Nous verrons aussi comment configurer motion pour qu’il suive deux caméras.

transformer une tablette android en caméra IP

Ma tablette est une Samsung Galaxy tab 7.7 (GT-P6210) équipée d’android 4.0.4. Elle n’est plus utilisable comme tablette car sa batterie ne tient plus. Je l’ai changé mais la nouvelle batterie n’a pas duré longtemps. Je vais donc alimenter en permanence cette tablette, l’accrocher à un mur d’où elle filmera le couloir. La détection de présence sera assurée par motion, sur le Raspberry Pi qui gère déjà le flux d’une caméra IP extérieure.

J’ai essayé de mettre une version plus récente d’android sur la tablette mais je n’ai pas réussi. Je reste donc en version 4.0.4.

Pour transformer la tablette en caméra IP, c’est très simple :

  • installer (via google play) l’application gratuite IP Webcam
  • Régler l’application :
    • Main camera : front camera
    • motion and sound detection : « disable » pour tout
    • connection settings : port 8090, login/password not set
    • cloud streaming : off

Application android IP webcam : réglages

Quand les réglages sont faits, tout en bas : Service Control / start server : lance le serveur de la caméra IP.

Et voilà, maintenant si je tape « http://192.168.1.17:8090/video  » dans un navigateur, j’ai accès au flux vidéo de la tablette. Et avec « http://192.168.1.17:8090 « , je vois un écran de réglage.

Donner une adresse IP fixe à la tablette

J’ai trouvé la solution ici (en anglais).

Appuyer sur le réseau wifi auquel on est connecté puis choisir « avancé ». Si l’on règle sur IP statique au lieu de DHCP, on peut entrer l’adresse fixe (pour moi 192.168.1.202).

Selon cette page (en anglais), “Network Prefix length” correspond au « Masque de sous-réseau IP » de notre routeur. « 255.255.255.0 » est entré comme « 24 ». « 255.255.0.0 » comme « 16 ». Pour moi c’est bien 24 qui doit figurer dans cette case.

Tablette android : mettre en IP fixe

On vérifie : « http://192.168.1.202:8090/video  » permet bien de visualiser le flux vidéo de notre tablette.

NOTA : pour que le flux vidéo ait la bonne adresse, il faut redémarrer la tablette une fois.

Réglage de motion sur le Raspberry Pi pour gérer deux caméras

La configuration de motion est faite avec etc/motion/motion.conf lorsqu’il y a une seule caméra. Mais lorsqu’il y en a plusieurs, il faut créer plusieurs configurations. Le principe général (cf wiki de motion) est de laisser motion.conf avec les réglages communs à toutes les caméras et créer des fichiers distincts, nommés threadX.conf , pour chaque caméra (X est le numéro de caméra).

  • Créer les fichiers thread0.conf  et thread1.conf , avec droits 664.
  • A la fin de motion.conf, décommenter les lignes qui y font référence.
thread /etc/motion/thread0.conf
thread /etc/motion/thread1.conf
; thread /etc/motion/thread2.conf
  • dans motion.conf, identifier les lignes qui sont spécifiques à chaque caméra et les copier dans un éditeur de texte. On peut les laisser telles quelles puisqu’elles seront moins prioritaires que les instructions spécifiques des fichiers threadX.conf.
  • Dans chaque fichier threadX.conf, insérer les lignes spécifiques et faire les réglages.

Mon fichier thread1.conf  contient :

# caméra galaxy tab 7.7 GT-P6210
# thread1.conf

videodevice /dev/video1

netcam_url http://192.168.1.202:8090/video

############################################################

threshold 3000
threshold_tune off
noise_level 32
noise_tune off
despeckle_filter EedDl
smart_mask_speed 10
lightswitch 25
minimum_motion_frames 3

############################################################

# Target base directory for pictures and films
target_dir /home/jf/motion/galaxy

snapshot_filename g-%v-%Y%m%d%H%M%S-snapshot
picture_filename g-%v-%Y%m%d%H%M%S-%q
movie_filename g-%v-%Y%m%d%H%M%S
timelapse_filename g-%Y%m%d-timelapse

############################################################

stream_port 8082
webcontrol_port 8086

Le fichier thread0.conf  contient les mêmes lignes avec les paramètres que j’avais réglé dans les articles précédents de cette série . Evidemment stream_port  et webcontrol_port  sont différents (respectivement 8081 et 8085).

Redémarrer le service motion avec

service motion restart

Et maintenant les deux adresses http://nautilus:8082/?action=stream  et http://nautilus:8081/?action=stream  affichent le flux capturé de chacune des deux caméras par le Pi nautilus.

La caméra de la vieille tablette détecte bien les mouvements et stocke les fichiers générés dans le répertoire /home/jf/motion/galaxy . Idem pour la caméra 0 (foscam extérieure).

Modifier le cron de suppression de vieux fichiers

Maintenant au lieux de supprimer les vieux fichiers de /home/jf/motion, il faut supprimer dans /home/jf/motion/foscam et /home/jf/motion/galaxy.

Je modifie donc /home/jf/exec/cameradeletecron.sh qui contient maintenant : 

#!/bin/bash

# efface les fichiers avi et jpg de plus de deux jours 
find /home/jf/motion/foscam -maxdepth 1 -name '*.avi' -type f -mtime +2 -exec rm {} \;
find /home/jf/motion/foscam -maxdepth 1 -name '*.jpg' -type f -mtime +2 -exec rm {} \;

find /home/jf/motion/galaxy -maxdepth 1 -name '*.avi' -type f -mtime +2 -exec rm {} \;
find /home/jf/motion/galaxy -maxdepth 1 -name '*.jpg' -type f -mtime +2 -exec rm {} \;

Mise en veille de la tablette

La tablette se met en veille (l’écran est noir). Mais si les réglages de gestion d’énergie de l’application IP Webcam sont comme sur la copie d’écran ci-dessous (aucune option n’est cochée), la caméra va continuer à travailler même si l’écran est noir.

Réglage gestion d'énergie IP Webcam

Et maintenant ?

Il faudra que je règle la détection de mouvement de la tablette lorsque je l’aurai mise à sa place définitive. En attendant, elle a les mêmes réglages que ceux de la caméra extérieure. D’autres articles de la série sont également disponibles.

Enfin, si vous souhaitez visualiser le flux de la caméra de cette tablette, il suffit de taper http://nautilus:8082/?action=stream  dans n’importe quel navigateur d’un appareil connecté au réseau local.

Moi, j’ai décidé de voir les deux caméras et d’autres informations sur une page « dashboard » et j’ai créé une application android qui permet de la visualiser et d’interdire l’extinction de l’écran de la tablette sur laquelle elle est utilisée. Si ça vous intéresse, c’est ici : Une application android pour afficher une page web d’un Raspberry Pi.