Le Sense Hat, une extension spatiale pour la Raspberry Pi

Oui, vous ne rêvez pas, les RMLLd de 2016 auront leur propre station météo affichant en temps réel la température, l’hygrométrie et la pression atmosphérique.

L’appareil sur lequel ces données seront mesurées et affichées est formé

Le Sense Hat est une extension du Raspberry Pi développée pour Astro Pi (un ordinateur à base de Raspberry Pi utilisé dans la station spatiale internationale). Son succès en ISN vient de son seul actuateur (virtuel ou en tout cas, optique) qui est la matrice de LEDs qui le recouvre presque complètement. Voici par exemple un jeu de Candy Crush programmé par des élèves d’ISN sur le Sense Hat:

Mais ce sont les nombreux et discrets senseurs qui font l’intérêt du Sense Hat pour la station spatiale internationale. Par exemple la mesure régulière de la pression atmosphérique et du taux d’humidité de l’air donne le graphique suivant (fait avec le logiciel libre R):

Les données ont été enregistrées dans un fichier csv à l’aide de ce script:

from sense_hat import SenseHat
import time
sense = SenseHat()

fichier = open("hygro.csv","w")

for n in range(240):
        fichier.write(str(sense.get_humidity()))
        fichier.write(" , ")
        fichier.write(str(sense.get_pressure()))
        fichier.write('\n')
        time.sleep(60)

fichier.close()

Et oui! C’est en Python (langage) qu’on programme le Sense Hat [1]! On verra des exemples de mini-projets d’ISN qu’on peut faire avec le Sense Hat, comme

Alain Busser

Les scripts présentés aux RMLL décentralisées sont sous licence libre BSD [2]

Tout d’abord, le script ayant permis de relever des données météorologiques sur le village des RMLL:

from sense_hat import SenseHat
import time
sense = SenseHat()

fichier = open("temperature.csv","w")

for n in range(30):
        fichier.write(str(sense.get_humidity()))
        fichier.write(" , ")
        fichier.write(str(sense.get_temperature()))
        fichier.write('\n')
        time.sleep(20)

fichier.close()

Le vieillissement prématuré de la carte micro-SD ayant déterioré le fichier csv obtenu, seul le début de ce fichier
est visible ci-dessous, alors que le fichier était censé être un exemple de donnée ouverte. Ce sont les alea de la
technologie...

Voici donc la moitié survivante des relevés météorologiques:

Hygrométrie (%) Température (°C)
63.7816543579 28.8969802856
63.3552246094 29.0296974182
63.8029785156 29.1434555054
63.394317627 29.12449646
62.9039230347 29.2003364563
62.9145812988 29.2382545471
62.9430122375 29.3330535889
62.8506164551 29.3330535889
62.0368499756 29.4088935852
61.3794403076 29.5036907196
61.7099227905 29.5605697632
61.5962104797 29.5416107178
61.4469604492 29.6174507141
61.2230834961 29.6932888031
61.3865509033 29.7312088013
61.7525672913 29.6932888031
61.2017631531 29.7880859375

Cependant, grâce au trop peu connu logiciel Gnuplot, l’intégralité des données a été dessinée sous la forme de ce nuage de points
qui montre que lors des mesures, l’humidité a régulièrement baissé en même temps que la température a augmenté: Le processeur ARM
du Raspberry Pi a agi comme un sèche-cheveux sur le capteur d’hygrométrie et celui de température:

Ensuite, le script ayant affiché directement sur le Sense Hat, et en direct, la météo des RMLL:

from sense_hat import SenseHat

sense = SenseHat()
white = (255,255,255)
for n in range(8):
        message = "Welcome to the RMLL, the meteo today: "
        message += "temperature: " + str(round(sense.get_temperature(),1)) + " Celsius;  "
        message += "Pressure: " + str(int(sense.get_pressure())) + " hPa; "
        message += "humidity: " + str(int(sense.get_humidity())) + "% "
        sense.show_message(message,text_colour=white)

Ensuite, le script ayant permis de transformer le Raspberry Pi (avec le Sense Hat) en niveau à bulle d’air:

from sense_hat import SenseHat

sense = SenseHat()
on_continue = True


while on_continue:
        vecteur = sense.get_accelerometer_raw()
        x = -vecteur['x']*2 + 3.5
        y = -vecteur['y']*2 + 3.5
       
        for i in range(8):
                for j in range(8):
                        n = int(100*max(2-(i-x)**2-(j-y)**2,0))
                        sense.set_pixel(i,j,n,n,n)
       
        if (x-4)**2>20 or (y-4)**2>20:
                on_continue = False

Et enfin, au grand bonheur de tous, le script de la fourmi de Langton sur le Sense Hat;
la première partie consisteà définir les instructions du langage de programmation de la fourmi:

  • mettre le niveau de bleu à 0 ou 255 (afficher ou cacher le robot)
  • mettre le niveau de rouge à 0 ou 255 (dessiner ou effacer)
  • tourner à gauche (de 90°)
  • tourner à droite
  • avancer

La seconde partie du script est la programmation, dans ce langage, de la fourmi de Langton:

from time import *
from sense_hat import SenseHat


sense = SenseHat()
sense.clear()

def mettre_bleu(x,y,n):
        (r,g,b) = sense.get_pixel(x,y)
        sense.set_pixel(x,y,r,g,n)

def mettre_rouge(x,y,n):
        (r,g,b) = sense.get_pixel(x,y)
        sense.set_pixel(x,y,n,g,b)


def gauche():
        global direction
        direction = (direction+1) % 4

def droite():
        global direction
        direction = (direction+3) % 4

def avance():
        global direction, x,y
        mettre_bleu(x,y,0)
        x = (x+dx[direction]) % 8
        y = (y+dy[direction]) % 8
        mettre_bleu(x,y,255)

dx = [1,0,-1,0]
dy = [0,1,0,-1]
direction = 1
x,y = 4,4

sense.set_pixel(x,y,0,0,255)


while 2+2==4:
        avance()
        (r,g,b) = sense.get_pixel(x,y)
        if r>0:
                gauche()
                mettre_rouge(x,y,0)
        else:
                droite()
                mettre_rouge(x,y,255)
        sleep(0.1)

Lors de la seconde séance, on s’est aussi amusés, en plus, avec ce jeu de la vie de Conway:

from time import sleep as attendre
from random import *
from sense_hat import SenseHat

sense = SenseHat()
sense.clear()

def yadurouge(x,y):
        r,g,b = sense.get_pixel(x,y)
        return int(r>0)

def voisinage(x,y):
        voisins = yadurouge(x,(y+1)%8)
        voisins += yadurouge(x,(y+7)%8)
        voisins += yadurouge((x+1)%8,(y+7)%8)
        voisins += yadurouge((x+7)%8,(y+7)%8)
        voisins += yadurouge((x+1)%8,(y+1)%8)
        voisins += yadurouge((x+7)%8,(y+1)%8)
        voisins += yadurouge((x+7)%8,y)
        voisins += yadurouge((x+1)%8,y)
        return voisins

def alive(x,y):
        return yadurouge(x,y)

for x in range(5):
        sense.set_pixel(randrange(3),randrange(3),0,255,0)

for generation in range(20):
        for x in range(8):        # le vieillissement fait passer du vert au rouge
                for y in range(8):
                        r,g,b = sense.get_pixel(x,y)
                        r = g//8
                        g = 0
                        sense.set_pixel(x,y,r,g,b)
        for x in range(8):
                for y in range(8):
                        r,g,b = sense.get_pixel(x,y)
                        if voisinage(x,y) is 3 or (alive(x,y) and voisinage(x,y) is 2):
                                g = 255
                        else:
                                g = 0
                        sense.set_pixel(x,y,r,g,b)
        for x in range(8):
                for y in range(8):
                        r,g,b = sense.get_pixel(x,y)
                        sense.set_pixel(x,y,0,g,b)
        attendre(0.004)

et avec ce simulacre de mouvement brownien sur un tore (trois particules rouge, verte et bleue):

from random import *
from time import *
from sense_hat import SenseHat


sense = SenseHat()
sense.clear()

dx = [1,0,7,0]
dy = [0,1,0,7]

x1,y1 = 4,4
x2,y2 = 0,0
x3,y3 = 0,7

sense.set_pixel(x1,y1,0,0,255)
sense.set_pixel(x2,y2,255,0,0)
sense.set_pixel(x3,y3,0,255,0)


while 2+2==4:
        (r,g,b) = sense.get_pixel(x1,y1)
        sense.set_pixel(x1,y1,r,g,0)
        indice = randrange(4)
        x1 = (x1+dx[indice]) % 8
        y1 = (y1+dy[indice]) % 8
        (r,g,b) = sense.get_pixel(x1,y1)
        sense.set_pixel(x1,y1,r,g,255)
        (r,g,b) = sense.get_pixel(x2,y2)
        sense.set_pixel(x2,y2,0,g,b)
        indice = randrange(4)
        x2 = (x2+dx[indice]) % 8
        y2 = (y2+dy[indice]) % 8
        (r,g,b) = sense.get_pixel(x2,y2)
        sense.set_pixel(x2,y2,255,g,b)
        (r,g,b) = sense.get_pixel(x3,y3)
        sense.set_pixel(x3,y3,r,0,b)
        indice = randrange(4)
        x3 = (x3+dx[indice]) % 8
        y3 = (y3+dy[indice]) % 8
        (r,g,b) = sense.get_pixel(x3,y3)
        sense.set_pixel(x3,y3,r,255,b)
        sleep(0.1)

Footnotes

[1Le code source de l’interface est sur github.

[2Pourquoi cette licence et pas une autre? Parce que les exemples "officiels" du Sense-Hat sont déjà sous cette licence et que ce n’est pas plus mal de ne pas se compliquer la vie.