Mot-clé - raspberry

Fil des billets

dimanche 23 octobre 2016

Mesurer l'humidité avec une sonde DHT22 et la température avec une sonde DS18B20 sur Arduino, transférer l'information à un Raspberry Pi

L'objectif de ce billet est d'illustrer l'appariement et le travail synchronisé d'un Arduino et d'un Raspberry Pi. Concrètement, l'Arduino mesure l'humidité avec une sonde DHT22 et la température avec une sonde DS18B20 et l'objectif est de transférer cette information à un Raspberry Pi qui traite l'information et l'envoie sur Internet.

Certains se demanderont pourquoi ne pas tout faire directement avec le Raspberry Pi :

  1. on peut imaginer que tous les ports du Pi sont déjà utilisés
  2. ou que le montage se complique et qu'il n'est plus possible de tout gérer depuis le Pi mais qu'il est plus aisé de lui adjoindre une (ou des) planche(s) Arduino

Principe de communication entre Raspberry Pi et Arduino

Nous allons utiliser un lien série entre les 2 composants au travers d'une connexion USB qui relie les deux équipements. Concrètement, le Raspberry Pi enverra des ordres à l'Arduino sur le port série et écoutera attentivement la réponse qui viendra également sur le port série.

Code Arduino

Mesurer la température avec une sonde DS18B20

Pour faciliter les choses, on utilisera la bibliothèque DallasTemperature.h (disponible ici sur GitHub). Le code est développé ci-dessous et ne présente aucune difficulté particulière :

#include <OneWire.h> // Inclusion de la librairie OneWire
#include "DallasTemperature.h" //Librairie du capteur

// Initialize 1-wire
OneWire oneWire(10); //Bus 1-Wire sur la pin 10 de l'arduino
DallasTemperature sensors(&oneWire); //Utilistion du bus Onewire pour les capteurs
DeviceAddress sensorDeviceAddress; //Vérifie la compatibilité des capteurs

void doMeasures() {
  sensors.requestTemperatures(); //Demande la température aux capteurs
  Serial.print("temperature=");
  Serial.println(sensors.getTempCByIndex(0)); //Récupération de la température en degrés celsius du capteur n°0
}

// setup()
void setup() {
  sensors.begin(); //Activation des capteurs
  sensors.getAddress(sensorDeviceAddress, 0); //Demande l'adresse du capteur à l'index 0 du bus
  sensors.setResolution(sensorDeviceAddress, 11); //Résolutions possibles: 9,10,11,12
}

Pour mesurer la température, on appellera la fonction doMeasures().

Mesurer l'humidité avec une sonde DHT22

Là encore, le code n'est pas bien compliqué tant que l'on utilise la librairie DHT.h (par exemple celle d'Adafruit).

#include "DHT.h"

// Initialisation du capteur DHT
#define DHTPIN 12    // Pin sur lequel la sonde DHT 22 est attachée
#define DHTTYPE DHT22   // Type de sonde DHT22 : AM2302, AM2321
DHT dht(DHTPIN, DHTTYPE);

void doMeasures() {
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
  }

  // Compute heat index in Celsius (isFahreheit = false)
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print("humidity=");
  Serial.println(h);
  Serial.print("temperature="); //Oui, un DHT22 ça mesure aussi la température :-) 
  Serial.println(t);
  Serial.print("heat_index=");
  Serial.println(hic);
}

// setup()
void setup() {
  dht.begin();
}
Écoute sur le port série et actions

Là encore, pas de surprise. La boucle loop() est utilisée pour réagir quand des données sont présentes sur l'interface série. On détecte la nature de l'appel et on peut imaginer différents comportements selon le message reçu. Par exemple, un caractère pourra déclencher le relevé de l'humidité sur DHT22 tandis qu'un autre caractère reçu déclenchera la prise de température sur la sonde DS18B20.

//setup()
void setup() {
  Serial.begin(9600); // Initialisation du port série
}

// for incoming serial data
int incomingByte = 0;
String res = "";

// loop()
void loop() {
  // On écoute sur le port série
  if (Serial.available() > 0) {
          // On lit le contenu du message série et selon le caractère reçu on déclenche une action différente
          incomingByte = Serial.read(); 
          if (incomingByte == 103) { //g
            doAction1();
          }
          if (incomingByte == 104) { //h
            doAction2();
          }
  }
}
Il suffit alors de tout combiner !

Il n'est pas difficile de combiner ces trois morceaux de code !

#include "DHT.h"
#include <OneWire.h>
#include "DallasTemperature.h" 

// Initialize DHT sensor.
#define DHTPIN 12
#define DHTTYPE DHT22  
DHT dht(DHTPIN, DHTTYPE);

// Initialize 1-wire
OneWire oneWire(10);
DallasTemperature sensors(&oneWire); //Utilistion du bus Onewire pour les capteurs
DeviceAddress sensorDeviceAddress; //Vérifie la compatibilité des capteurs

// for incoming serial data
int incomingByte = 0;
String res = "";

void doMeasures() {
  // Reading temperature or humidity takes about 250 milliseconds!
  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {
    Serial.println("Failed to read from DHT sensor!");
  }

  // Compute heat index in Celsius (isFahreheit = false)
  float hic = dht.computeHeatIndex(t, h, false);

  Serial.print("humidity=");
  Serial.println(h);
  Serial.print("temperature=");
  Serial.println(t);
  Serial.print("heat_index=");
  Serial.println(hic);

  sensors.requestTemperatures(); //Demande la température aux capteurs
  Serial.print("temperature=");
  Serial.println(sensors.getTempCByIndex(0)); //Récupération de la température en celsius du capteur n°0
}

// setup()
void setup() {
  Serial.begin(9600); // Initialisation du port série
  dht.begin();
  sensors.begin(); //Activation des capteurs
  sensors.getAddress(sensorDeviceAddress, 0); //Demande l'adresse du capteur à l'index 0 du bus
  sensors.setResolution(sensorDeviceAddress, 11); //Résolutions possibles: 9,10,11,12
}

// loop()
void loop() {
  // send data only when you receive data:
  if (Serial.available() > 0) {
          // read the incoming byte:
          incomingByte = Serial.read();
          if (incomingByte == 103) { //g
            doMeasures();
          }
  }
}

Code sur le Raspberry Pi

Nous allons utiliser un script Python qui se connectera via le port série à l'Arduino et :

  1. émettra le signal déclenchant l'action (dans notre cas le caractère "g")
  2. écoutera pendant 20 secondes l'éventuelle réponse
  3. transmettra la réponse à un serveur distant dans une requête GET
import serial
import os
import glob
import time
import requests

#Timeout de 20 secondes
tf = time.time()+20

#Initialisation du port série /dev/ttyUSB0 (cela peut varier selon la version de Raspbian et le type de planche Arduino, /dev/ttyUSB0 semble le défaut avec Raspbian Jessie et un Arduino Nano328)
ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=1)

#On attend 2 secondes : si l'on écrit trop vite après l'ouverture du bus série, la communication risque d'échouer
time.sleep(2)

#On écrit le caractère "g" (ASCII 103) sur le port série
print("Now, we are sending the command to Arduino!")
ser.write("g".encode())

#Et on boucle désormais en lecture sur le port série
while time.time()<tf:
  line = ser.readline()
  if line:
    result = str(line).rstrip('\r\n')
    print(result)
    try:
      #Dans notre cas, le résultat brut de l'Arduino est passé à un serveur distant qui stocke l'information
      r = requests.post('https://monserveurdecollectedesdonnees', data=result, timeout=5.0)
      print(str(r.status_code)+" "+result)
    except:
      print("Failed, with payload: "+result)

#On ferme le canal de communication
ser.close()

Ce script peut alors être placé dans le crontab du système pour être exécuté périodiquement.

samedi 22 octobre 2016

Volumio 2, pour un Raspberry Pi musical

Bonne nouvelle, Volumio 2 est tout juste sorti et cette nouvelle version améliore encore ce splendide logiciel libre.

Qu'est-ce que c'est ?

Volumio est une version de Raspbian spécialement préparée pour transformer un Raspberry Pi (ou d'autres cartes comme Odroid, Beaglebone...) en serveur musical. Ainsi équipé de Volumio, un Raspberry Pi servira de la musique disponible localement (par exemple dans un disque dur attaché à Volumio) ou sur le réseau local (par exemple un NAS). La première version de Volumio était déjà super (on en parlait ici), la nouvelle édition embellit l'interface, facilite la gestion de systèmes avec plusieurs Volumio (pour des installations dans différentes pièces) tout en conservant les caractéristiques qui faisaient la force de la première itération.

À quoi ça ressemble ?

Voilà l'écran principal de Volumio : Screenshot_2016-10-21_09-13-20.png

Et voici l'affichage des différents Volumio indépendants mais connectés à un même réseau : il n'a jamais été aussi commode de passer de l'un à l'autre ! Screenshot_2016-10-21_09-13-31.png

Comment l'installer ?

C'est d'une grande simplicité. Tout a été déployé et a été fonctionnel sur mon Raspberry Pi équipé d'Hifiberry Amp+ en 15 minutes.

  1. D'abord, télécharger l'image correspondant à votre matériel sur le site de Volumio
  2. Ensuite, sortir le fichier .img de son archive
  3. Le transférer sur une carte micro-SD (/dev/sdb dans l'exemple ci-dessous, à adapter, attention à ne pas se tromper !) avec la commande dd :
dd if=volumio.img of=/dev/sdb bs=4M
  1. Insérer la carte dans le Raspberry Pi et le démarrer
  2. Et voilà ;-)

Après chargement, l'interface graphique est disponible via le navigateur internet que l'on fera pointer vers http://volumio ou vers l'adresse IP du Pi.

Enfin, il est toujours possible d'accéder en SSH au Raspbian sous-jacent avec le nom d'utilisateur "volumio" et le mot de passe "volumio".

L'essayer, c'est l'adopter !

samedi 18 juillet 2015

Raspberry Pi, un relai, quelques branchements : interrupteur intelligent

La mission du jour était simple : redémarrer électriquement un serveur lorsqu'il cessait de répondre au ping. Défi relevé à l'aide d'un Pi, d'un relai et de quelques lignes de bash (et aussi un peu de python).

Parlons d'abord du relai

Un relai est un petit équipement électronique qui joue le rôle d'interrupteur contrôlable. Il existe de nombreuses cartes (pour Arduino notamment) qui comportent des relais en nombre variable : partons sur carte simple munie d'un seul canal.

relai.jpg

Le principe de fonctionnement d'un relai est assez simple : une langue métallique, asservie par un ressort, ferme par défaut un circuit. En présence d'un champ électrique dans une bobine, un champ magnétique induit déplace la langue métallique qui vient alors fermer un second circuit. Comme un simple dessin vaut mieux qu'un long discours :

principe-relai.jpg

Beaucoup plus d'information sur le principe d'un relai se trouve ici : http://www.astuces-pratiques.fr/electronique/le-relais-principe-de-fonctionnement.

Les relais que nous trouvons classiquement comportent pour chaque canal :

  • un bornier destiné au courant alternatif (attention lors de la manipulation ; ce n'est plus du 5V dans le circuit mais du 220V - il convient donc d'être particulièrement prudent notamment lors de l'usage en ne laissant pas le circuit électrique en libre accès !)
  • 3 connecteurs pour le contrôle :
* un lien à la terre (par ex. GND ou G)
* un lien à l'alimentation 5V (par ex. Vcc ou V) : à brancher sur les pins d'alimentation
* un pôle pour le contrôle (par ex. IN ou S)

Par défaut, en absence de toute alimentation, 2 des bornes du bornier alternatif sont reliées. Lorsque le relai sera alimenté et que le pin d'entrée (IN ou S) sera amené au potentiel 5V, une bascule de circuit ouvert/fermé s'effectuera. Selon le cas d'usage, on pourra préférer avoir une alimentation active par défaut ou bien une alimentation coupée par défaut... il suffira d'adapter le branchement dans le bornier alternatif.

Branchement du relai sur le Pi et contrôle du relai par le Pi

Tout simplement : Rpi_et_relai.png

et dès que l'on commandera au Pi de basculer le GPIO (7e pin sur la planche, selon la numérotation de la planche) alors le relai basculera.

Je ne présente pas ici le branchement au niveau du relai qui n'est pas bien compliqué si on le considère comme un simple interrupteur. Il conviendra seulement de placer l'interrupteur sur la phase (il est toujours plus prudent de couper la phase et non le neutre !).

Pour contrôler le relai, on pourra exécuter le code suivant en Python sur le Pi :

#! /usr/bin/python
import RPi.GPIO as GPIO ## Import GPIO library
GPIO.setmode(GPIO.BOARD) ## Use board pin numbering
GPIO.setup(7, GPIO.OUT) ## Setup GPIO Pin 7 to OUT
GPIO.output(7,True) ## Turn on GPIO pin 7

GPIO.output(7,True) pourra bien sûr être remplacé par GPIO.output(7,False) pour effectuer la bascule inverse.

Dans mon cas (c'est selon le branchement du relai), l'ouverture du circuit (i.e. son interruption) est commandée par ouvrir-circuit.py :

#! /usr/bin/python
import RPi.GPIO as GPIO ## Import GPIO library
GPIO.setmode(GPIO.BOARD) ## Use board pin numbering
GPIO.setup(7, GPIO.OUT) ## Setup GPIO Pin 7 to OUT
GPIO.output(7,True) ## Turn on GPIO pin 7

et la fermeture du circuit par fermer-circuit.py :

#! /usr/bin/python
import RPi.GPIO as GPIO ## Import GPIO library
GPIO.setmode(GPIO.BOARD) ## Use board pin numbering
GPIO.setup(7, GPIO.OUT) ## Setup GPIO Pin 7 to OUT
GPIO.output(7,False) ## Turn off GPIO pin 7

Et ce petit script bash (eteint-redemarre.sh) effectue une déconnexion/reconnexion de l'alimentation avec 5 secondes de pause intercalaire :

#!/bin/sh
/path/to/ouvrir-circuit.py 
sleep 5
/path/to/fermer-circuit.py 

Détecter la chute du serveur pour en provoquer le redémarrage

On peut alors demander au Pi de surveiller un hôte particulier et d'en provoquer le redémarrage électrique à l'aide de ce script :

#! /bin/sh
HOST=192.168.1.3
LOGFILE=/tmp/ce_que_le_pi_surveille.log
ping -c5 $HOST > /dev/null 2> /dev/null
if [ $? -eq 0 ] 
then 
	echo `date +%Y-%m-%d\ %H:%M` Host found >> $LOGFILE 
else
	echo `date +%Y-%m-%d\ %H:%M` Host not found >> $LOGFILE
	/path/to/eteint-redemarre.sh
fi

Et voilà, le tour est joué !

Volumio sur Rasperry Pi avec Hifiberry Amp+ : une vraie petite chaîne Hifi connectée et pilotable à distance

Dans un précédent article, nous présentions le branchement d'Hifiberry Amp+ sur le Raspberry Pi (Pi B+ ou Pi 2). Allons un peu plus loin en équipant le Pi de Volumio.

Volumio en quelques mots

"a revolutionary audiophile music player" annonce fièrement le site de Volumio. Il faut admettre que l'outil est admirable pratique et bien construit. Au travers d'une interface graphique web, il permet de contrôler sa bibliothèque de morceaux audio, de gérer des listes de lecture et de contrôler la sortie audio du Pi. Volumio est intelligent et il sera capable de facilement indexer le contenu d'un disque dur (USB) branché sur le Pi ou de partages réseau (SMB/CIFS ou NFS). Pratique pour servir le contenu d'un petit serveur domestique qui porterait déjà toute la musique locale en son sein.

Screenshot_2015-07-18_16-14-16.png

Une installation sans histoire

  • On télécharge l'image de Volumio sur le site https://volumio.org/
  • On la déploie sur la carte SD à l'aide de la commande dd
  • On démarre le Pi

Et hop une machine au nom (attendu !) de volumio apparaît sur le réseau local.

Pour une utilisation basique, on pourra se contenter de s'y connecter à l'aide du navigateur à l'adresse http://volumio.local (ou http://volumio). A défaut, il faudra trouver son adresse IP (par exemple en regardant les périphériques connectés sur votre routeur local) et utiliser l'adresse IP jusqu'à avoir paramétré une bonne résolution de nom.

Accéder à Volumio en SSH

Rien de plus simple :

ssh root@volumio.local

et le mot de passe par défaut est... volumio.

Faire fonctionner Volumio avec l'Hifiberry Amp+

Ce n'est pas très compliqué - presque plus facile que sur une Raspbian classique !

On désactive le système audio par défaut (via le Jack) en commentant la ligne suivante dans /etc/modules :

snd_bcm2835

On ajoute la ligne suivante dans /boot/config.txt :

dtoverlay=hifiberry-amp

puis ceci dans /etc/asound.conf à destination d'Alsa :

dtoverlay=hifiberry-amp

Et le tour est joué.

On peut alors rebooter le système Volumio et le son sort désormais de l'amplificateur Amp+.

On remarquera cependant assez vite que le contrôle de volume ne répond pas comme attendu. Pour corriger ce point, j'ai modifié comme suit la section audio_output de /etc/mpd.conf

audio_output {
                 type           "alsa"
                 name           "Output"
                 device         "hw:0,0"
                 mixer_control  "Master"
                 mixer_device   "hw:0"
                 mixer_index    "0"
                 dop    "no"
}

et on redémarre MPD (/etc/init.d/mpd restart).

Fixer le volume par défaut au redémarrage du Pi

Pour éviter que le système ne redémarre avec un son trop fort, j'ai choisi le volume médian satisfant à l'aide d'alsamixer puis

alsactl store

Bonne écoute musicale ! La grosse chaîne Hifi du passé peut être remplacée par le petit Pi économe et discret !