Publié le 8 août 2016 - par

Prenez la température en Go

htu32d_250pxDu simple clignotement de LED, je vais vous emmener à la mesure d’une température à l’aide d’une sonde pilotée via le bus I2C et à la consultation de cette température par le biais d’une page Web, le tout en Go !

Tout un programme qui je l’espère ne vous donnera pas de sueur froide.

Description du projet

L’application lira la température de la sonde à interval régulier via le bus I2C du Raspberry Pi. La sonde sera de type HTU21D (sonde de température et d’humidité). J’ai effectué tous mes développements avec la carte miniature de Sparkfun. La valeur de la température sera stockée dans une variable. L’application servira de serveur Web et délivrera une page avec la température stockée dans la variable.

Je couperai le programme en 2 parties :

  1. L’utilisation d’I2C en Go pour interagir avec la sonde
  2. Réalisation du serveur Web

Définition du bus I2C

Le protocole permet de mettre en communication un composant maître (le Raspberry Pi) et plusieurs périphériques esclaves (sonde HTU21D dans cet article). Plusieurs maîtres peuvent partager le même bus, et un même composant peut passer du statut d’esclave à celui de maître, ou inversement. Toutefois, la communication n’a lieu qu’entre un seul maître et un seul esclave. Notons également que le maître peut également envoyer un ordre à tous ses esclaves simultanément (par exemple, une mise en sommeil ou une demande de réinitialisation).
(Source : GNU/Linux Magazine Hors-Série n°75)

Spécification du composant HTU21D

HTU21DInterface de communication : bus I2C
Précision de la sonde d’humidité : ±2%
Précision de la sonde de température : ±0.3°C
Fonctionnement de 0% à 100% d’humidité, toutefois le composant ne doit pas être mis en contact avec l’eau (comme la pluie)
Alimentation : 3,3V
Limitation : 1 composant par bus I2C vu que le composant ne dispose que d’une seule adresse de communication

Schéma de câblage

image03

3V3 -> Plot +
GND -> Plot –
SDA -> Plot DA
SCL -> Plot CL

Installation de Go

Reportez vous aux instructions de mon précédent article ici

Configuration du Rasberry Pi

Initialement, le noyau Linux de Raspbian ne détecte aucun contrôleur I2C.
Pour l’activer, lancez raspi-config depuis la console :

sudo raspi-config

Choisissez “Advanced Options”.

image01

Choisissez “I2C”.

Validez l’option “Oui”.

Le contrôleur I2C de votre Raspberry Pi est maintenant actif.

Programmation

La première partie du programme va se contenter de lire à intervalle régulier la température, de la stocker et de l’afficher dans la console.

Installez depuis la console une librairie native Go pour piloter les composants I2C ainsi que ma librairie liée au composant HTU21D :

go get github.com/davecheney/i2c
go get github.com/mlgd/sensors

Créez le dossier du projet :

mkdir -p $HOME/go/src/mondomaine.ext/webtemp

Créez le fichier source depuis la console :

cd $HOME/go/src/mondomaine.ext/webtemp
nano webtemp.go

Le contenu du programme est le suivant :

package main

import (
    "fmt"
    "log"
    "os"
    "os/signal"
    "sync"
    "syscall"
    "time"

    "github.com/mlgd/sensors"
)

var (
    // Variable globale de stockage de la dernière température lue
    temperature float32
    // Variable d'accès au composant de la sonde via I2C
    htu21d *sensors.HTU21D

    wg         sync.WaitGroup
    finLecture chan bool
)

func main() {
    // Initialisation de la variable pour transmettre la fin de lecture
    finLecture = make(chan bool, 1)

    // Création d’une variable pour l’interception du signal de fin de programme
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    signal.Notify(c, syscall.SIGTERM)
    signal.Notify(c, syscall.SIGKILL)
    // Go routine (thread parallèle) d’attente de fin du programme
    go func() {
        <-c

        // Notification de fin de lecture pour quitter la focntion litSonde()
        finLecture <- true
        // Attend que le compteur de groupe soit à 0
        wg.Wait()

        htu21d.Close()

        os.Exit(0)
    }()

    var err error
    htu21d, err = sensors.NewHTU21D(sensors.HTU21D_ADDR, 1)
    if err != nil {
        log.Println("Erreur d'initialisation du composant HTU21D")
        return
    }

    // Incrémente de 1 le compteur de groupe
    wg.Add(1)
    // Lecture infinie de la sonde
    litSonde()
}

func litSonde() {
    // Instructions exécutées lorsque la fonction sera quitter
    defer func() {
        // Décrémente de 1 le compteur de groupe
        wg.Done()
    }()

    for {
        select {
        case <-finLecture:
            // Fin de lecture demandée, quitter la fonction
            return
        default:
            // Lecture de la température
            if temp, err := htu21d.ReadTemperature(); err == nil {
                // Si aucune erreur, mémoriser la valeur
                temperature = temp
                fmt.Printf("Température : %.2f° C\n", temperature)
            }
        }
        // Attente d’une seconde
        time.Sleep(time.Second)
    }
}

Exécution du programme

Exécutez la commande de compilation :

go build webtemp.go

Puis lancez le programme :

sudo ./webtemp

Observez votre console qui affiche la température :

image00

Évolution du programme

Maintenant que vous avez créé la base du programme pour lire la température, nous voici à la deuxième partie du programme.

Vous allez rendre la consultation plus “user-friendly” (conviviale) en transformant l’application en un serveur Web répondant sur le port TCP 8080 de l’IP locale de votre Raspberry Pi. Cela vous permettra de consulter la température depuis un smartphone par exemple.

Commencez par remplacer les 2 dernières lignes de la fonction main() :

// Lecture infinie de la sonde
litSonde()

par

// Go routine (thread parallèle) de lecture de la sonde
go litSonde()

// Démarrage du serveur Web
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)

Ajoutez la fonction suivante à la fin du fichier :

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "<div style='text-align: center'><p>Température</p><h1>%0.2f° C</h1></div>", temperature)
}

Retirez la ligne suivante de la fonction litSonde() pour rendre le programme moins verbeux :

fmt.Printf("Température : %.2f° C\n", temperature)

Ce qui vous donne le programme suivant :

package main

import (
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "sync"
    "syscall"
    "time"

    "github.com/mlgd/sensors"
)

var (
    // Variable globale de stockage de la dernière température lue
    temperature float32
    // Variable d'accès au composant de la sonde via I2C
    htu21d *sensors.HTU21D

    wg         sync.WaitGroup
    finLecture chan bool
)

func main() {
    // Initialisation de la variable pour transmettre la fin de lecture
    finLecture = make(chan bool, 1)

    // Création d’une variable pour l’interception du signal de fin de programme
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)
    signal.Notify(c, syscall.SIGTERM)
    signal.Notify(c, syscall.SIGKILL)
    // Go routine (thread parallèle) d’attente de fin du programme
    go func() {
        <-c

        // Notification de fin de lecture pour quitter la focntion litSonde()
        finLecture <- true
        // Attend que le compteur de groupe soit à 0
        wg.Wait()

        htu21d.Close()

        os.Exit(0)
    }()

    var err error
    htu21d, err = sensors.NewHTU21D(sensors.HTU21D_ADDR, 1)
    if err != nil {
        log.Println("Erreur d'initialisation du composant HTU21D")
        return
    }

    // Incrémente de 1 le compteur de groupe
    wg.Add(1)
    // Go routine (thread parallèle) de lecture de la sonde
    go litSonde()

    // Démarrage du serveur Web
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}

func litSonde() {
    // Instructions exécutées lorsque la fonction sera quitter
    defer func() {
        // Décrémente de 1 le compteur de groupe
        wg.Done()
    }()

    for {
        select {
        case <-finLecture:
            // Fin de lecture demandée, quitter la fonction
            return
        default:
            // Lecture de la température
            if temp, err := htu21d.ReadTemperature(); err == nil {
                // Si aucune erreur, mémoriser la valeur
                temperature = temp
            }
        }
        // Attente d’une seconde
        time.Sleep(time.Second)
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "<div style='text-align: center'><p>Température</p><h1>%0.2f° C</h1></div>", temperature)
}

Exécutez la commande de compilation :

go build webtemp.go

Puis lancez le programme :

sudo ./webtemp

Vous constaterez que le programme se lance et reste en attente (si vous avez bien retiré la ligne de la fonction litSonde()) :

image05

Pour consulter la température, utilisez un navigateur Web et entrez l’adresse IP de votre Raspberry Pi suivie du port 8080. Ex : http://192.168.0.114:8080

image07

Pour ceux d’entre vous qui ne connaîtrait pas l’adresse IP de leur Raspberry Pi, exécutez la commande suivante dans votre console :

ifconfig

Votre adresse IP apparaîtra comme ceci :

image06

Conclusion

Vous voilà armés de quelques clés supplémentaires pour transformer votre Raspberry Pi en objet connecté avec Go. Le code source du programme reste très sommaire et quelques évolutions sont facilement intégrables pour les plus débutants, comme par exemple la lecture du taux d’humidité (inclus dans la sonde) et son affichage sur la page Web. Le site de Golang reste une très bonne source de départ pour faire évoluer les fonctionnalités Web : Writing Web Application

À propos Mickael GALLARD

Développeur Go, Obj-C, Swift, WinDev, Java, PHP, Flex, Action Script

2 réflexions au sujet de « Prenez la température en Go »

  1. Ping : Prenez la température en Go | Radioamateur France

  2. Thierry

    Merci de ces articles avec Go, ça va me remotiver à en faire à nouveau.
    C’est plaisant mais je trouve un peu lourd la mise en place de l’environnement des projets pour un langage si léger à manipuler ensuite.

    Répondre

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Complétez ce captcha SVP *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.