Bus pirate – Utiliser un capteur de température DS18B20

J’avais déjà présenté le bus pirate et fait un petit Hello World dans ce billet.

Aujourd’hui voici un cas pratique d’utilisation. La lecture de la température à partir d’un capteur.

Au programme, bus 1-Wire, bus pirate, résistance de pull-up et script python !

IMG_20170930_212808985.jpg

 

Mais avant tout, présentons tout d’abord le capteur de température utilisé. Il s’agit du DS18B20, très populaire sur la toile et dont la datasheet se trouve ici : [1] Datasheet du DS18B20.

Le capteur existe en 3 boitiers T0-92 (3-pin), SO (8-pin), µSOP (8-pin), voir Figure 1 :

pinout

Figure 1 Boitiers disponibles pour le DS18B20

J’ai choisi le boitier TO-92 chez dealextreme (frais de ports gratuits mais il ne faut pas être pressé).

Et voici, toujours le schéma de câblage, toujours d’après la datasheet :

power

Figure 2 Datasheet : câblage du DS18B20, simple non ?

Quelques observations sur la Figure 2 :

  • VDD doit être comprise entre 3V et 5.5V (page 2, DC Electrical Characteristics), parfait puisque notre bus pirate délivre du 3.3V !
  • Le bus utilisé est un bus 1-Wire. J’y reviendrai probablement dans un autre billet. Avec le bus pirate inutile de s’attarder au fonctionnement de ce bus ! Il suffit de relier la pin DQ du capteur à la pin OWD (One Wire Data) du bus pirate.
  • Le capteur nécessite une résistance de pull-up pour mettre la ligne sur un état haut lorsqu’elle n’est pas utilisée, ce que le bus pirate peut également fournir !

Le fonctionnement de la résistance de pull-up du bus pirate est défini sur le lien suivant : [2] Practical guide to Bus Pirate pull-up resistors.

Voici en deux mots ce qu’il faut retenir de son fonctionnement (Figure 3) :

Figure 3 Fonctionnement de la résistance de pull-up

  • La pin Vpu (V pull-up pin) doit être reliée à une alimentation (entre 0 et 5V), dans notre cas ce sera du 3.3V.
  • La résistance de pull-up doit être activée (signal enable sur la Figure 3), pour cela dans le terminal de communication avec le bus pirate taper ‘P’ (‘p’, pour désactiver).
  • Un switch analogique (4066) distribue l’alimentation aux 4 résistances de pull-up de 10K des pins MOSI/CLOCK/MISO/CS.

Ce qui donne en résumé le schéma de câblage suivant (Figure 4) :

pirate

Figure 4 Bus pirate : câblage du DS18B20

Note : la pin « MOSI » est en réalité la pin « OWD » en mode 1-Wire. OWD signifie One Wire Data.

Mapping mémoire du capteur :

2 types de mémoires (Figure 5) :

  • SRAM (8 octets) nommée scratchpad (bloc-note en anglais)
  • EEPROM (3 octets) dont le contenu est recopié au démarrage ou sur commande.

Figure 5 Mapping mémoire du capteur, voir détails page 8 de la datasheet

Je ne vais pas revenir ici sur la façon de commander le bus pirate par la console. Pour cela se reporter au précédent billet.

Donc dans un premier temps on va passer en mode 1-Wire (« m 2 ») puis activer respectivement les alimentations (« W») et la résistance de pull-up (« P ») :

Bus Pirate v3b

Firmware v5.10 (r559) Bootloader v4.4

DEVID:0x0447 REVID:0x3046 (24FJ64GA002 B8)

http://dangerousprototypes.com

HiZ>m 2

1WIRE routines (C) 2000 Michael Pearce GNU GPL

Ready

1-WIRE>W

Power supplies ON

1-WIRE>P

Pull-up resistors ON

Puis en tapant (0) on voit la liste des macros disponibles en mode 1-Wire :

1-WIRE>(0)

0.Macro menu

Macro 1WIRE address

No device, try (ALARM) SEARCH macro first

1WIRE ROM COMMAND MACROs:

51.READ ROM (0x33) *for single device bus

85.MATCH ROM (0x55) *followed by 64bit address

204.SKIP ROM (0xCC) *followed by command

236.ALARM SEARCH (0xEC)

240.SEARCH ROM (0xF0)

La macro (0xF0) scanne le bus à la recherche des esclaves connectés et détecte naturellement notre capteur :

1-WIRE>(0xF0)

SEARCH (0xF0)

Macro 1WIRE address

1.0×28 0x37 0x8F 0x39 0x05 0x00 0x00 0x3C

*DS18B20 Prog Res Dig Therm

Device IDs are available by MACRO, see (0).

La dernière ligne nous dit que l’ID du composant est sauvegardé dans la macro (1). Ceci évite à l’avenir de taper les 8 octets (64 bits) composant le code ROM du capteur. Bien pratique… Si on retape (0), on constate que la liste des macros disponibles s’est enrichie de la macro (1) contenant l’ID du capteur :

1-WIRE>(0)

0.Macro menu

Macro 1WIRE address

1.0×28 0x37 0x8F 0x39 0x05 0x00 0x00 0x3C

*DS18B20 Prog Res Dig Therm

1WIRE ROM COMMAND MACROs:

51.READ ROM (0x33) *for single device bus

85.MATCH ROM (0x55) *followed by 64bit address

204.SKIP ROM (0xCC) *followed by command

236.ALARM SEARCH (0xEC)

240.SEARCH ROM (0xF0)

Donc à partir de là pour adresser notre capteur nous utiliserons la macro (0x55) suivi de la macro (1). Voir détail de la commande 0x55 page 11 de la datasheet [1].

Dans notre cas nous allons sélectionner une résolution de 9 bits, ce qui donne une température précise à 0.5°C (largement suffisant). Par le même occasion cela donne un temps de conversion plus court (voir Table 2, [1]).

Pour configurer le capteur il faut utiliser la commande 0xBE qui permet d’écrire 3 octets dans le scratchpad dans cet ordre-là :

TH, TL, CONFIGURATION REGISTER (voir Figure 5 ci-dessus). Les seuils ne sont pas utilisés dans notre exemple. Le registre de configuration sera mis à 0x1F (9 bits voir Table 2, [1]).

1-WIRE>(0x55) (1) 0x4e 0 0 0x1f

BUS RESET OK

MATCH ROM (0x55)

ADDRESS MACRO 1: 0x28 0x37 0x8F 0x39 0x05 0x00 0x00 0x3C

WRITE: 0x4E

WRITE: 0x00

WRITE: 0x00

WRITE: 0x1F

Maintenant, pour lire la température on lance une conversion (commande 0x44). En fin de conversion le résultat sur 2 octets est placé dans le scratchpad (octets 0 et 1).

On lit les 9 octets du scratchpad à l’aide de la commande «0xBE r :9 » :

1-WIRE>(0x55) (1) 0x44

BUS RESET OK

MATCH ROM (0x55)

ADDRESS MACRO 1: 0x28 0x37 0x8F 0x39 0x05 0x00 0x00 0x3C

WRITE: 0x44

1-WIRE>(0x55) (1) 0xBE r:9

BUS RESET OK

MATCH ROM (0x55)

ADDRESS MACRO 1: 0x28 0x37 0x8F 0x39 0x05 0x00 0x00 0x3C

WRITE: 0xBE

READ: 0xA0 0x01 0x00 0x00 0x1F 0xFF 0x10 0x10 0x06

Notez les octets 2, 3, 4 qui contiennent les valeurs précédemment écrites (0, 0, 0x1F). La dernière valeur est un CRC 8 bits qui peut être recalculé pour vérifier l’intégrité des données reçues (voir page 9, [1]). Et les deux premiers octets donnent la température selon le schéma suivant :

(voir Figure 4, [1]). Ce qui donne une température de : LS BYTE = 0x A0 = 23 + 21 = 11 + MS BYTE = 0x01 = 24 = 16 = 27°C.

Pour terminer il est possible de sauvegarder la configuration en EEPROM (3 octets) à l’aide de la commande 0x48 mais je n’en parlerai pas plus ici.

Enfin, pour éviter de taper à la main toutes ces commandes, on peut les regrouper dans un script python qui va permettre de lire et afficher la température toutes les 10 secondes.

Le but de ce billet n’est pas de présenter python ni comment l’installer mais de présenter un cas d’utilisation pratique du bus pirate et une façon de le commander simplement par script. Dans mon cas j’ai utilisé python sur eclipse (via pydev). Sachez juste que pour les besoins de notre script nous allons utiliser un module d’accès au port série, pyserial. L’install et la documentation se trouvent sur [3] Installation et utilisation de pyserial.

Avant de mettre le script complet, voici une grille de lecture, pour le reste j’ai mis des commentaires.

Le script se compose de deux fonctions :

  • ReadFromSerial : fonction qui lit depuis le port série la température renvoyée par le capteur ;
  • Send : fonction qui envoie une commande ASCII (PC vers Bus Pirate) et attends la réponse (appel de ReadFromSerial).

Le programme principal ouvre le port COM du bus pirate, passe en mode 1-Wire, active le 3.3V et la résistance de pull-up puis configure et lit le capteur.

Je ne détaille pas plus cela reprend ce qui a été vu plus haut et j’ai largement commenté le script. Le script complet :

# -*- coding: utf-8 -*-

'''
Created on 19 oct. 2016

@author: KB
'''

import serial
import time

BUSPIRATE_PORT = 'COM12'  # 'à remplacer par le port COM du Bus Pirate sur votre configuration

# FONCTION 1, lecture du capteur via le port série#
def ReadFromSerial(ser):
    TempIsNext = 0
    for line in ser.readlines():  # tant qu'il y a une réponse
        mystr = line.decode('utf-8').strip()  # suppression des espaces aux extrémités
        #print(mystr) # pour du debug
        if (TempIsNext == 1):
            TempIsNext = 0  # RAZ flag pour la prochaine lecture #

            strlist = mystr.split()  # liste à partir de la chaîne, séparateur par défaut : espace #
            if(len(strlist) == 3):
                temp = ((int(strlist[2], 16)&7) << 4) | (int(strlist[1], 16) >> 4)  # décodage temperature, partie entiére #
                ftemp = float(temp) + ((int(strlist[1], 16) >> 3) & 1) / 2  # décodage temperature, partie décimale #
                if (int(strlist[2], 16) >> 3): #test du bit de signe (bit 3 ici)
                    ftemp *= -1

                print(ftemp);  #  affiche la température sur stdout #

                # Bonus : stockage dans un fichier #
                file = open("temp.txt", "a")
                file.write(str(ftemp) + "\n");
                file.close();
                # fin Bonus #
        elif ("WRITE: 0xBE" in mystr): # Si cette chaîne est trouvée, la prochaine instruction donne la température
            TempIsNext = 1  # mémorisation via un flag #

# FONCTION 2 commande du capteur via le port série
def Send(ser, cmd):
    ser.write(str(cmd + '\n').encode('ascii'))  # écriture sur le port série de la commande ASCII #
    ReadFromSerial(ser)  # attente réponse, décodage et affichage #

#----------------------------------------------------------------------------#
# Programme principal #

print("Configuration et ouverture du port",BUSPIRATE_PORT)

# ouverture du port COM (exception si le port est occupé) :
ser = serial.Serial(BUSPIRATE_PORT, 115200, timeout=1)
assert ser.isOpen()

print("Configuration du Bus Pirate...")
# Configuration du Bus Pirate #
Send(ser, '#')  # reset du bus #
Send(ser, 'm 2')  # mode 1-Wire #

Send(ser, 'W')  # Alimentations activées (dont 3.3V) #
Send(ser, 'P')  # résistance de pull-up activée #
Send(ser, '(0xF0)')  # scan des devices connectés au bus 1-Wire, les composants trouvés sont stockés dans les 10 macros (1)..(10) #
Send(ser, '(0x55) (1) 0x4e 0 0 0x1f') 

print("Lecture en cours...")

# Lecture toutes les 10 secondes, boucle infinie #
while(1):
    Send(ser, 'A(0x55) (1) 0x44')  # Envoi de la commande de conversion de température (44). Le résultat est stocké dans le Scratchpad (2 octets little endian)#
    Send(ser, '(0x55) (1) 0xBE r:2')  # lecture des deux premiers octets du Scratchpad avec la commande (BE) suivi de r:2 #
    time.sleep(10)

Bien pratique pour tester rapidement un composant ce petit bus pirate !

That’s all folks!

Référence :

[1] Datasheet du DS18B20 ;

[2] Practical guide to Bus Pirate pull-up resistors ;

[3] Installation et utilisation de pyserial.

Publicités