Mot-clé - python

Fil des billets

mardi 25 octobre 2016

Un couple d'enfer : ESP8266 et DS18B20, un petit objet connecté à quelques euros qui mesure la température et la transmet par Wifi

Nous avons parlé de l'ESP8266 propulsé par micropython dans le billet précent. Parmi la multitude d'application possible, nous allons voir aujourd'hui comment coupler l'ESP8266 à une sonde DS18B20 pour relever périodiquement la température d'une pièce et transférer la valeur en Wifi à une base InfluxDB.

Principe

Nous parlons ici de mesure de température dans un lieu de vie : une prise de température toutes les 10 minutes est suffisante. Nous allons dès lors mettre en place le fonctionnement suivant pour l'ESP8266 :

  1. allumage, mise en route du Wifi, connexion au Wifi local
  2. mesure à 10 reprises de la température
  3. suppression des 2 valeurs extrêmes supérieures et des 2 valeurs extrêmes inférieures
  4. moyenne des 6 valeurs restantes
  5. envoi de la valeur à une base de données InfluxDB au moyen d'une requête POST
  6. mise en sommeil de l'ESP8266 pour 10 minutes

A la sortie de la mise en sommeil le cycle recommence.

En outre, un minuteur logiciel provoquera un redémarrage de l'ESP8266 s'il est bloqué 2 minutes sans arriver au bout de son cycle.

Branchements

Rien de sorcier : on alimente la sonde 1-wire DS18B20 en 3.3V (comme l'ESP8266), on en relie les masses et on place le pin de données de la sonde 1-wire sur un GPIO adéquat de l'ESP8266. Il ne faudra pas oublier la résistance de 4.7 kOhms entre le pin Vin et le pin Data de la sonde ! Et pour permettre la sortie du sommeil, il faudra relier le pin RESET (RST) de l'ESP8266 avec le GPIO 16 (celui de l'alarme RTC).

Code sur l'ESP8266

Nous allons placer le code ci-dessous dans le fichier main.py qui sera ensuite envoyé sur l'ESP8266 à l'aide des outils évoqués dans le billet précent :

./webrepl_cli.py /path/to/main.py 192.168.4.1:/

Le code intégral, fortement commenté, est là :

import socket
import time
import machine #pour accéder aux GPIOs
import onewire, ds18x20 #pour lire la sonde 1-wire
import urequests  #pour envoyer la requête POST
from machine import Timer

#Faire flasher i fois la petite LED bleue de la puce ESP8266
def do_flashes(i):
    if i<1:
        i=1
    pin = machine.Pin(2, machine.Pin.OUT)
    for z in range(0,i):
        time.sleep_ms(250)
        pin.value(not pin.value())
        time.sleep_ms(250)
        pin.value(not pin.value())
    pin.value(1) #On s'assure que la LED finit dans l'état "éteint"

#Se connecter au Wifi local
def do_connect():
    import network
    sta_if = network.WLAN(network.STA_IF)
    if not sta_if.isconnected():
        print('connecting to network...')
        sta_if.active(True)
        sta_if.connect('SSID', 'password') #Remplacer le nom du SSID et le mot de passe de connexion
        while not sta_if.isconnected():
            pass
    print('network config:', sta_if.ifconfig())  #On imprime l'IP de la puce une fois connectée, pratique pour déboguer sur le port série
    do_flashes(4)

#Effectuer une requête POST sur une URL InfluxDB (ou autre)
def http_post(data):
    url = 'https://path.to.influx.db/write?db=dbname'
    data = ("temperature,tag1=x1,tag2=x2 value=%s" %
(str(data)))
    resp = urequests.post(url, data=data)

#Effectuer une mesure sur le GPIO 4
def do_ds18b20():
    dat = machine.Pin(4)
    ds = ds18x20.DS18X20(onewire.OneWire(dat)) #On crée l'objet OneWire
    roms = ds.scan()  #On scanne le bus pour trouver toutes les sondes
    print('found devices:', roms)

    #On mesure 10 fois la température
    if len(roms)>0:
        templist = []
        for i in range(10):
            ds.convert_temp()
            time.sleep_ms(1000) #On attend 1 seconde entre chaque prise de mesure pour laisser le temps à la sonde de se réinitialiser
            temp = ds.read_temp(roms[0])
            templist.append(temp)
        templist.remove(max(templist)) #On supprime les valeurs extrêmes
        templist.remove(max(templist))
        templist.remove(min(templist))
        templist.remove(min(templist))
        sum_of_temps = 0
        for temp in templist:
            sum_of_temps = sum_of_temps + temp
        final_temp = sum_of_temps/6
        print('Final temp: ', final_temp)
        return final_temp

#Redémarre l'ESP8266, "machine.reset" est équivalent à un redémarrage électrique
def force_reset():
    print("Machine inactive for too long, this is not normal, rebooting!")
    machine.reset()

#Regarde si l'ESP8266 se réveille d'un sommeil profond (et non d'un (re)démarrage électrique)
if machine.reset_cause() == machine.DEEPSLEEP_RESET:
    print('Woke from a deep sleep')

#On active le minuteur qui provoque le redémarrage dans 2 minutes
tim = Timer(-1)
tim.init(period=120000, mode=Timer.ONE_SHOT, callback=lambda
t:force_reset())

do_flashes(2) #On signale le démarrage du travail par 2 clignotements
do_connect() #Connexion au Wifi
time.sleep_ms(2000) #On attend 2 secondes
temp = do_ds18b20() #On mesure la température
http_post(temp) #On l'envoie sur InfluxDB

print('set RTC alarm to wake up later')
rtc = machine.RTC() 
rtc.irq(trigger=rtc.ALARM0, wake=machine.DEEPSLEEP) #On configure RTC.ALARM0 pour être capable de re-démarrer le périphérique
rtc.alarm(rtc.ALARM0, 600000) #  Après 600 secondes RTC.ALARM0 redémarrera la machine !
#Petit compte à rebours, pratique pour l'interrompre par Ctrl+C, quand l'on veut se connecter à l'ESP8266 pour en modifier le comportement
print('deep sleep in 20 seconds')
time.sleep_ms(15000)
print('deep sleep in 5 seconds')
time.sleep_ms(2000)
print('deep sleep in 3 seconds')
time.sleep_ms(1000)
print('deep sleep in 2 seconds')
time.sleep_ms(2000)
print('deep sleep in 1 seconds')
time.sleep_ms(1000)
print('deep sleep now! waking up in 600 seconds')
machine.deepsleep()

Et voilà une petite sonde qui prend la température toutes les 10 minutes.

Consommation ?

Un petit test rapide a montré que mon ESP8266 monté sur un kit de développement NodeMCU v1.0 (je pressens que la consommation serait moindre sans le kit de développement) :

  • demande ~100 mA lors des périodes de fonctionnement
  • demande <~10 mA lors de la phase de sommeil

C'est à creuser plus finement mais cela donne déjà quelques ordres de grandeur.

dimanche 23 octobre 2016

Micropython sur ESP8266, l'électronique toujours plus libre et puissante !

L'ESP8266 est un petite puce développée par Espressif qui a de multiples qualités :

  • elle dispose d'un petit coeur programmable
  • elle se connecte en Wifi ou/et se comporte comme un point d'accès Wifi
  • elle est fort peu onéreuse (quelques euros à peine)
  • elle a été largement adoptée par de nombreux bricoleurs de la blogosphère et on trouve une abondante littérature à son sujet

Initialement, elle a été beaucoup utilisée comme extension Wifi peu chère pour montages Arduino.

Mais ses qualités l'ont peu à peu rendu intéressante et utilisable seule : différents micro-systèmes ont été portés sur l'ESP8266 et l'ont peu à peu transformé en puce Wifi programmable. Ainsi, sur certains montages simples (internet des choses, domotique...) elle remplacera aisément un Arduino ou un Raspberry Pi et apportera en sus faible coût, faible consommation et encombrement réduit.

Nous allons voir dans ces lignes comment démarrer avec cette carte en l'équipement du micro-système micropython. Micropython a été porté récemment sur ESP8266 et le code en a été libéré suite à une campagne Kickstarter fructueuse.

Commençons par le matériel

La puce ESP8266 ressemble à ceci dans sa version 12 (qui est la plus performance notamment avec le plus grand nombre d'entrées-sorties) : esp8266-esp12.jpg

Pour un usage plus facile, on pourra la préférer pré-montée au sein d'un kit de développement qui en rendra :

  • l'alimentation facile (en USB 5V, la puce elle-même ne supporte que 3.3V)
  • le flashage aisé (toujours au travers d'un port USB fourni)
  • également le reset facilité à l'aide d'un bouton poussoir
  • enfin l'utilisation facile sur une breadboard pour prototypage

En inconvénient, la consommation électrique sera un peu plus forte et l'encombrement sera plus important. Mais passée la phase de prototypage, rien n'empêche d'utiliser la carte seule !

Parmi les kits de développement pratiques, on peut noter les planches NodeMCU (prendre la v1) qui sont très bien, sont libres (code source ici) et ressemblent à ceci : NodeMCU_DEVKIT_1.0.jpg

Attention toutefois à la correspondance des pins de la planche de développement et les GPIOs de l'ESP8266 : D1, D2, ... ne correspondent pas à GPIO1, GPIO2... Cf. le schéma ci-dessous : NODEMCU_DEVKIT_V1.0_PINMAP.png

Envoyons Micropython

Une fois en possession d'une puce ESP8266 et de son kit de développement, il suffit de s'y connecter à l'aide d'un câble USB et un nouveau périphérique /dev/ttyUSB0 doit devenir disponible pour le système.

On peut alors :

  • télécharger la dernière version du système micropython ici en prenant bien la version pour ESP8266
  • installer esptools.py sur sa machine par exemple avec la commande
pip install esptool
  • vider la mémoire actuelle de l'ESP8266 (pour éviter tout problème) avec la commande
esptool.py --port /dev/ttyUSB0 erase_flash
  • puis envoyer micropython (remplacer esp8266.bin par le fichier que vous avez obtenu sur le site de micropython)
esptool.py --port /dev/ttyUSB0 --baud 460800 write_flash --flash_size=8m 0 /path/to/esp8266.bin

Premier usage via le port série

Après redémarrage de la puce, on peut s'y connecter en série au travers de l'USB par exemple avec picocom (ou un autre logiciel de son choix) :

picocom /dev/ttyUSB0 -b 115200

et on obtient alors une console REPL Python qui permet d'exécuter toute commande de son choix dans l'environnement micropython.

Par exemple pour changer l'état d'un GPIO (attention à la numérotation, cf supra), on pourra appeler les commandes suivantes :

pin = machine.Pin(2, machine.Pin.OUT)
pin.value(1)
pin.value(0)

qui feront basculer en état HIGH/LOW le GPIO2 (D4 sur la planche de développement).

Usage au travers de WebREPL

Encore plus pratique, il est possible de se connecter à l'ESP8266 et à la console Python au travers du Wifi. Pour ce faire, l'ESP8266 se positionne comme un point d'accès Wifi auquel on peut se connecter : le mot de passe de connexion est "micropythoN".

Une fois connecté, on peut appeler l'ESP8266 à l'adresse 192.168.4.1 et se connecter à la console Python à l'aide de WebREPL, une page avec un peu de javascript qui aide à se connecter à la console REPL depuis un navigateur.

Il suffit de télécharger le contenu du dépôt WebREPL et lancer le fichier webrepl.html.

Envoyer un fichier sur l'ESP8266

Pour envoyer un fichier sur l'ESP8266, par exemple un fichier main.py qui sera exécuté à chaque lancement après initialisation, on pourra utiliser le commande outil webrepl_cli.py livré dans le dépôt WebREPL.

Connecté au point d'accès de l'ESP8266, la commande :

./webrepl_cli.py /path/to/main.py 192.168.4.1:/

se chargera de copier le fichier main.py à la racine de l'ESP8266.

Main.py pour tout contrôler

Dès lors, il est possible de placer le code de son choix dans le fichier main.py à la racine de l'ESP8266 pour l'asservir et lui faire exécuter le code de son choix.

On pourra par exemple :

  • désactiver le point d'accès
  • faire se connecter l'ESP8266 à un réseau WIfi local
  • faire agir l'ESP8266 sur le réseau (envoi de paquets, exécution de requêtes diverses...)
  • jouer avec les ports GPIOs

Les idées ne vous manqueront certainement pas pour peupler le fichier main.py. Voilà un bon point de départ dans la documentation de micropython sur ESP8266.

Python to look for hexadecimal patterns in a file

It is quite easy to look for hexadecimal patterns in a file in Python, for example match for the magic numbers for JPG or PNG:

import re

with open("~/file", "rb") as f:
  pattern =
b'(\xFF\xD8\xFF\xE0|\xFF\xD8\xFF\xE1|\xFF\xD8\xFF\xDB|\x89\x50\x4E\x47\x0D\x0A\x1A\x0A)'
  regex = re.compile(pattern)
  for match in regex.finditer(f.read()):
    print "%s" % (match.start())

This should print all the positions in the file where the given hexadecimal patterns were seen.