J'ai depuis quelque temps deux Raspberry Pi équipés de "HAT" Chistera Pi 1.2 qui trainent sur mon bureau (dont l'un m'est gentiment prêté par le FAI (neutre et) associatif toulousain Tétaneutral.net, dont je suis adhérent). Le Chistera Pi, attention au jeu de mots, est une petite carte que l'on branche sur un Raspberry Pi. Elle contient simplement deux puces radio : une RFM22 et une RFM95 (attention il existe deux versions de ChisteraPi, la 1.2 avec un RFM95 et la 1.1 avec un RFM69HCW). À noter surtout que la RFM95 gère la modulation LoRa. Il faudra que je trouve un peu de temps pour jouer avec des liens longue distance LoRa, et poster des articles qui suivent ces expérimentations...
Pour une première prise en main de tout ça je renvoie aux tutos proposés par Snootlab (ah oui j'ai oublié : les Chistera Pi proviennent de cette très chouette startup toulousaine). Certainement d'autres tutos devraient arriver bientôt dans ce forum.
L'idée de ce petit article est d'explorer rapidement comment on peut controler ces deux puces radio depuis le Raspberry Pi (avec du code en Python). Et ce en prenant pour prétexte d'aller lire la température du RFM22. En effet le RFM22 possède un capteur de température intégré !
Plongée dans la datasheet
Commencont donc, comme il se doit, par découvrir la Datasheet du RFM22B. Tout de suite je vous conseille d'imprimer (au moins) les pages 59-60 qui contiennent un tableau récapitulatif de tous les registres disponibles (vous allez voir on va ne faire que lire et écrire dans ces différents registres).
La partie qui nous intéresse ici, pour aller lire la température, est la section 8.4 "Temperature Sensor" (page 49). On nous donne directement la procédure pour lire la température :
- configurer le capteur de température comme entrée de l'ADC : registre
0x0F
(ADC Configuration) avecadcsel[2:0] = 000
; - configurer la tension de référence pour l'ADC, toujours le registre
0x0F
mais ici avecadcref[1:0] = 00
; - configurer la plage de fonctionnement de l'ADC : registre
0x12
(Temperature Sensor Calibration) avec les bitstsrange[1:0]
; - mettre à
1
le bitentsoffs
du registre0x12
(Temperature Sensor Calibration); - déclencher une conversion de l'ADC en mettant à
1
le bitadcstart
du registre0x0F
(ADC Configuration); - enfin, lire la valeur de température dans le registre
0x11
(ADC Value);
Il suffit donc de savoir lire et écrire dans des registres... nous allons y venir tout de suite. Notons toutes fois qu'il faut configurer la "plage de fonctionnement" avec les bits tsrange[1:0]
du registre 0x12
(Temperature Sensor Calibration), les valeurs possibles sont résumées dans le tableau 16, reproduit ici :
Il faudra bien faire attention à convertir correctement les valeurs lues en fonction de la plage de température choisie (et certaineebt un peu de calibration).
Comment lire/écrire dans un registre ? SPI ?
Voyons maintenant comment faire pour lire et écrire dans tous ces registres ! Pour ça nous allons devoir remonter dans la datasheet jusqu'à la section 3 : Controller interface (page 14).
On apprend ici que RFM22 utilisent une interface SPI (à priori on le savait déjà, c'est aussi le cas pour le RFM95, et en général toutes les puces radio similaires) mais surtout que la communication se fait par des transactions de 16 bits :
- 1 bit qui indique si l'on est en lecture (
0
) ou écriture (1
), - 7 bits qui indiquent l'adresse du registre à lire ou écrire,
- 8 bits qui donnent les données à écrire (valeur sans importance, mais obligatoire, pour une lecture).
Tout ceci est bien résumé par les figures 3 et 4, reproduites ici:
Bref assez simple. Reste un point : on a deux périphériques de branchés sur le port SPI du Raspberry Pi, comment on choisit avec lequel on discute ? voyons le câblage du ChisteraPi:
On remarque que le RFM22 et le RFM95 sont câblés ensemble sur les pins MOSI
, MISO
et SCK
du Raspberry Pi. Le RFM95 à un pin DIO0
sur le GPIO4
du rPi et un pin NNS
sur le CE0
du rPi. Le RFM22, lui, à un port NIRQ
sur le GPIO22
et un pin NSEL
sur le CE1
du rPi. Ce sont les pins CE0
et CE1
qui nous intéressent ici. En effet NNS
, NSE
ou CE*
(ou encore par fois CS
, nCS
, nSS
, SS
, c'est la même chose) permettent au master de choisir le slave avec qui il discute. Dit autrement, un périphérique ne va être actif que si son pin NSEL
(ou autre dénomination) est à 0
.
Le Raspberry Pi possède une interface SPI hardware qui comprend deux pins de sélection de périphériques (CE0
et CE1
). Ces deux pins sont "mappés" par le driver Linux sur les fichiers /dev/spidev0.0
et /dev/spidev0.0
. Vu le câblage on a donc :
- Le RFM95 est câblé sur
/dev/spidev0.0
- Le RFM22B est câblé sur
/dev/spidev0.1
Et donc en Python ? Facile !
Aller on démarre la pratique. On suppose que l'on à un Raspberry Pi avec une Rasbian ou une Minibian qui tourne. La première chose à faire est d'activer le driver SPI, pour ça cf la doc officiel. On peut vérifier que c'est bon avec un simple ls
:
$ ls /dev/spi*
/dev/spidev0.0 /dev/spidev0.1
Ensuite on va utiliser la bibliothèque Python spidev (d'autres existent, en particulier WiringPi propose une interface SPI). Pour installer spidev :
$ sudo pip install spidev
L'installation va demander une compilation de wrapper C, et donc python.h
est nécessaire. Pour cela il faut installer le paquet python-dev
(avec apt-get, je vous laisse vous débrouiller).
Ensuite on peut simplement tester une lecture de registre du RFM22 avec ipython
:
$ ipython
...
In [1]: import spidev
In [2]: spi = spidev.SpiDev()
In [3]: spi.open(0, 1)
In [4]: spi.xfer2([0x00, 0x00])
Out[4]: [0, 8]
In [5]: spi.xfer2([0x01, 0x00])
Out[5]: [0, 6]
Alors c'est quoi la température ?
Voici donc un bout de code qui permet de lire la température :
#-*- coding:utf-8 -*-
import sys, time
import spidev
def rfm22_read(spi, registre):
registre = registre & 0x7F
return spi.xfer2([registre, 0x00])[1]
def rfm22_write(spi, registre, value):
registre = registre | 0x80
return spi.xfer2([registre, value])[1]
def main():
spi = spidev.SpiDev()
spi.open(0, 1)
# 1. Set the input for ADC to the temperature sensor
# 2. Set the reference for ADC
adc_conf = rfm22_read(spi, 0x0F)
rfm22_write(spi, 0x0F, adc_conf & 83) # adcsel[2:0] = 000 & adcref[1:0] = 00
# 3. Set the temperature range for ADC,
# 4. Set entsoffs = 1,
temp_conf = rfm22_read(spi, 0x12)
rfm22_write(spi, 0x12, temp_conf & 0x3f | 0x20) # tsrange[1:0] = 00 & entsoffs = 1
# 5. Trigger ADC reading, "Register 0Fh. ADC Configuration"—
adc_conf = rfm22_read(spi, 0x0F)
rfm22_write(spi, 0x0F, adc_conf | 0x80) # adcstart = 1
# Wait until convertion done
while rfm22_read(spi, 0x0f) & 0x80 == 0: pass
# 6. Read temperature value—Read contents of "Register 11h. ADC Value"
temp = rfm22_read(spi, 0x11)
# Convert température
temp_c = temp/2. - 64.
print("%1.2f°C" % temp_c)
# close SPI
spi.close()
if __name__ == '__main__':
sys.exit(main())
Rien d'extraordinaire. On juste factorisé un peu de code dans les deux fonctions rfm22_read
et rfm22_write
, pour lire et écrire dans un registre et ensuite suivit la procédure indiquée dans la datasheet.
Notons que plutôt que d'attendre bêtement 100ms, l'on doit pouvoir attendre que le 7e bit du registre Update: En fait il faut attendre que le registre repasse à 0x0F
repasse a 0
(c'est ce qu'indique la datasheet). Seulement ça ne semble pas fonctionner, ce bit reste à 1
... je dois louper quelque chose.1
. Le pdf qui détaille les registres est beaucoup plus clair (page 20) :
Voila ce que ca donne :
# python read_temp_rfm22.py
37.00°C
La température demanderait à être calibrée. C'est-à-dire faire des mesures dans un environnement connu, et d'adapter en fonction les paramètres de transformation. Il est aussi possible d'enregistrer ces paramètres issus d'une calibration dans des registres spéciaux du RFM22 (voir datasheet).
Enfin, notons que l'on mesure avec ça la température de la puce, qui à priori chauffe ! Ce n'est donc pas vraiment recommandé si l'on souhaite mesurer la température de la pièce environnante.
Bref ce n'est pas la fonctionnalité la plus intéressante de cette puce radio ! mais c'était un bon petit prétexte pour la prendre en main... suite au prochain épisode !