DigitalSpirit / Blog

Les entrailles d'OpenAlarm : Une première interface PC en Python

Je vous ai présenté dans le précédent article le principe de fonctionnement d'un OpenAlarm Node, c'est parfaitement fonctionnel et utilisable mais nous pouvons simplifier grandement tout ça et c'est là qu'intervient l'interface que je vais vous présenter dans cet article.

Un programme en Python nommé simplement base.py permet les interactions avec les OpenAlarm Node, il s'utilise en ligne de commande :

$ python3 base.py --help
OpenAlarm Base
Usage:
base.py [options] nodeid <nodeid>
base.py [options] config write <config>
base.py [options] config set <key> <value>
base.py [options] profile write <profile_name> [<profile_id>]
base.py [options] profile set <profile_id>
base.py [options] node write <node_name>
base.py [options] node read
base.py [options] listen [--csv <csv_file>]
base.py [options] remote <node_name> --set <commands>...
base.py --version
Options:
-p            Serial port
-f            Force node write even when different nodeid
-h --help     Show this screen
-d --debug    Debug mode
-v --verbose  Verbose mode
$ 

Je vais passer en revue toutes les possibilités offertes par ce programme.

nodeid

python3 base.py -p /dev/usbserial0 nodeid 2

Cette commande va donner au Node dont le périphérique série est /dev/usbserial0 l'identifiant de node 2

config write

python3 base.py -p /dev/usbserial0 config write default

Cette commande va écrire la configuration nommée default dans le node lié au périphérique /dev/usbserial0

Mais d'oû sort la configuration default ?

Toute la configuration d'OpenAlarm tient dans un fichier au format Yaml et default correspond dans l'exemple ci-dessus à la configuration du même nom du nœud config.

Ce n'est pas clair ? Regardons de plus près une partie du fichier de configuration oa.yaml :

[...]
configs:
    default: &default
        group: 210
        freq: 433
        ack: yes
        cmdtimeout: 15
        usbtimeout: 15
        autostart: yes
        power: 0
        remote:
            active: yes
            wait_error_cycle: 7
[...]

Nous y trouvons une suite de clefs / valeurs (les mêmes clefs que nous avons aperçu dans l'article précédent sur les entrailles du firmware) qui vont permettre de configurer comme nous le souhaitons notre Node.

Ainsi, dans l'exemple ci-dessus, nous donnons au Node le groupe 210, nous les spécifions une fréquence de 433Mhz, avec accusé de réception (ack = true), bref, je suppose que vous avez compris le principe de fonctionnement...

La commande config write va donc lire la configuration et l'envoyer au Node directement.

config set

python3 base.py -p /dev/usbserial0 config set group 220

Cette commande permet de modifier directement des variables de configuration, utile si on ne veut changer qu'un seule des paramètres.

profile write

python3 base.py -p /dev/usbserial0 profile write pir0

C'est assez explicite, avec cette commande, nous écrivons le profile nommé pir0 dans le périphérique /dev/usbserial0 (pour en savoir plus sur les profiles, voir le dernier article)

pir0 correspond au nœud du même nom du fichier oa.yaml :

[...]
    pir0: &profilepir
        description: Module avec capteur infrarouge 0
        feedback: yes
        period: 3
        eintwait: 3
        external_interrupts:
            io0: rising
        ios:
            io0: [ input, nopullup ]
        frame:
id: 2
content:
- counter
- waketype
- wakearg
- [ input0, input1, input2 ]
- voltage
- temperature
[...]

profile set

python3 base.py -p /dev/usbserial0 profile set 0

Change le profile courant du Node.

node write

python3 base.py -p /dev/usbserial0 node write xxx

Nous avons vu au dessus le fichier Yaml avec les nœuds configs et profiles, il existe un autre type de nœud, il s'agit de 'nodes' qui contient tous les paramètres pour un Node nommé.

Voici le contenu du nœud nodes du fichier oa.yaml :

[...]
nodes:
    pir0:
        id: 2
        config: *default
        key: abcdefghijklmnop
        profile:
            0: *profilepir
    temp:
        id: 1
        config: *default
        key: AKdlIqdjMKAQwJKz
        profile:
            0: *profiletemp
[...]

node read

python3 base.py -p /dev/usbserial0 node read

Lit le Node et affiche des informations sur sa configuration.

listen

python3 base.py -p /dev/usbserial0 node listen

Se connecte au Node via la liaison série et passe en mode promiscuous, une sorte de mode sniffer ou toutes les frames valident envoyées par les OpenAlarm Node sont retournées via l'interface série.

Si nous nous connections au node directement via l'interface série et que nous entrons la commande série, voici le format du contenu retourné :

$ ino serial
listen
OKX 010206000103047A0B16
OKX 010207000103007A0B16
OKX 010208000202007A0B16
OKX 010209000103007A0B16

À chaque réception d'une frame valide, elle est retournée sous la forme OKX 010206000103047A0B16, ou OK indique un paquet valide, X indique que le format de la frame est hexa, puis, suivi d'un espace, nous avons la frame brute.

Ce contenu n'est pas franchement lisible et c'est pour cela que nous utilisons le programme en Python qui nous simplifie grandement la vie, voici la même fonction listen mais utilisée via l'interface en Python :

$ python3 base.py -p /dev/usbserial0 node listen
Use device /dev/usbserial0
Start listen mode !
Nodeid: 1, frame type: 2 (0 second(s))
    Input       : bit0:0, bit1:0, bit2:0
    Voltage     : 2.94
    Temperature : 21.00
Nodeid: 1, frame type: 2 (5 second(s))
    Input       : bit0:0, bit1:0, bit2:0
    Voltage     : 2.94
    Temperature : 21.00
Nodeid: 1, frame type: 2 (5 second(s))
    Input       : bit0:0, bit1:0, bit2:0
    Voltage     : 2.94
    Temperature : 21.00
Nodeid: 1, frame type: 2 (5 second(s))
    Input       : bit0:0, bit1:0, bit2:0
    Voltage     : 2.94
    Temperature : 21.00

C'est tout de même beaucoup plus clair ainsi ! Non ?

Par curiosité, rendons cela un peu plus verbeux...

$ python3 base.py -p /dev/usbserial0 --verbose node listen
Verbose mode !
Use device /dev/usbserial0
Start listen mode !
verbose get
-> 0
verbose set 0
-> OK
listen raw
Nodeid: 1, frame type: 2, payload: 64000202007A0B15 (2 second(s))
    Counter     : 100
    Waketype    : 2
    Wakearg     : 2
    Input       : bit0:0, bit1:0, bit2:0
    Voltage     : 2.94
    Temperature : 21.00
Nodeid: 1, frame type: 2, payload: 65000202007A0B15 (5 second(s))
    Counter     : 101
    Waketype    : 2
    Wakearg     : 2
    Input       : bit0:0, bit1:0, bit2:0
    Voltage     : 2.94
    Temperature : 21.00
Nodeid: 1, frame type: 2, payload: 66000202007A0B15 (5 second(s))
    Counter     : 103
    Waketype    : 2
    Wakearg     : 2
    Input       : bit0:0, bit1:0, bit2:0
    Voltage     : 2.94
    Temperature : 21.00

Une fois ces données reçue, c'est à vous d'en faire ce que vous voulez...
Un paramètre csv vous permet de spécifier un fichier afin de stocker toutes ces informations dans un fichier.

node remote

python3 base.py -p /dev/usbserial0 remote pir --set period=1

Cette commande va se connecter à l'OpenAlarm Node connecté sur l'USB puis tenter une liaison distante avec le Node nommé pir (voir fichier format Yaml) afin de changer le paramètre period.


Voilà, vous savez tout sur l'interface actuelle en Python, si vous voulez en savoir, vous pouvez consulter le code et au sujet du fichier oa.yaml, je vous invite également à le consulter !

Ouvrir l'article

Mise à jour de RaspiO'Mix : RaspiO'Mix+

RaspiO'Mix est, comme son nom l'indique, l'évolution logique de RaspiO'Mix pour les RaspberryPi dit « Plus » et Raspberry 2.

RaspiO'Mix est une carte fille (également appelée hats) pour RaspberryPi qui vous permet de connecter vos capteurs / actionneurs Grove (le système Grove chez Lextronic) au Raspberry simplement, sans connaissance en électronique.
RaspiO'Mix est un projet libre et ouvert, tous les plans sont disponibles en ligne.

product-plus.png

Caractéristiques

  • Compatible Raspberry A+, Raspberry B+, Raspberry 2
  • 8 entrées / sorties tolérantes 5V
  • 8 entrées analogiques, 0-5V, 18 bits de résolution
  • 2 entrées numériques via DIP switch
  • Horloge temps réel avec batterie de sauvegarde
  • 3 connecteurs pour I2C
  • 1 connecteur pour communication série
  • Alimentation 5V via jack ou bornier à vis

Utilisation en Python

Des exemples en Python sont présents sur GitHub et vous montreront à quel point il est simple de dialoguer avec les capteurs / actionneurs Grove.

Par exemple, pour faire clignoter une LED présente sur le port IO0 et afficher la valeur analogiques lue sur le port AN0.

# On importe les librairies qui nous seront utiles
from raspiomix import Raspiomix import RPi.GPIO as GPIO import time
r = Raspiomix()
GPIO.setmode(GPIO.BOARD)
# On configure le port IO0 de RaspiO'Mix en sortie GPIO.setup(r.IO0, GPIO.OUT)
# Et on boucle ! while True: GPIO.output(r.IO0, not GPIO.input(r.IO0))
print("%f Volt !" % r.readAdc(0))
time.sleep(1)

Difficile de faire plus simple ! Non ?

Plus d'informations

Tout ce dont vous avez besoin pour avancer avec RaspiO'Mix+ est disponible sur le site www.raspiomix.org :

Et bien entendu, pour commander votre RaspiO'Mix+, cela se passe sur www.raspiomix.org !

Ouvrir l'article

Des nouvelles de RaspiO'Mix

J'ai peu donné de nouvelle récemment, notamment au sujet de RaspiO'Mix et pourtant, il y a à dire...

Un site dédié

Un site dédié permet de commander directement les RaspiO'Mix : www.raspiomix.org, tant qu'il y a du stock, vous recevrez votre RaspiO'Mix en 48h.

Je n'exclus pas de proposer les RaspiO'Mix aux revendeurs intéressés, merci de me contacter directement.

Librairie Python

La librairie Python à quelque peu évoluée et est devenue encore plus simple.

Pour lire une tension sur une des entrées analogiques suivi d'une lecture des 4 entrées analogiques :

$ python
>>> from raspiomix import Raspiomix
>>> r = Raspiomix()
>>> r.readRtc()
2014-11-12T20:41:26
>>> print(r.readAdc(0))
[4.0669732000000005]
>>> print(r.readAdc((0, 1, 2, 3)))
[4.066934600000001, 0.010923800000000001, 0.08515160000000001, 0.2866822]
>>> 

Cette librairie est disponible sur GitHub : GitHub / RaspiOMix / raspiomix.py

RaspiO'Mix+

Présentation

RaspiO'Mix poursuit son évolution avec la RaspiO'Mix+ créés pour être utilisée comme vous pouvez vous en douter avec un Raspberry+.

J'ai repris le tableau d'un précédent article sur RaspiO'Mix comparant cette dernière avec la carte GrovePi :

Fonctionnalité GrovePi RaspiO'Mix RaspiO'Mix+
Entrées / Sorties 7 4 8
Entrées analogiques 3 4 8
Résolution CAN 10bits 18bits 18bits
Lignes I2C 4 2 3
Lignes série 1 1 1
Horloge Non Oui (via DS1307) avec batterie de sauvegarde Oui (via DS1307) avec batterie de sauvegarde
Interrupteur 0 2 2
Alimentation via le Raspberry via le Raspberry ou une prise jack / bornier via le Raspberry ou une prise jack / bornier
Option - - Capteur I2C TMP10

Note: GrovePi+, l'évolution pour RaspberryPi+ possède les même caractéristiques que GrovePi.

RaspiO'Mix+ est au format Raspberry HATs+ et donc bien plus petite que la version originale, la densité de composants est donc plus forte.

RaspiO'Mix comparée à RaspiO'Mix+ :
Comparatif de taille RaspiO'Mix vs RaspiO'Mix+

Une carte RaspiO'Mix+ sur un RasberryPi :
RaspiO'Mix+ sur un RaspberryPi+

En détail

Le schéma de principe :
schema.png

J'ai ajouté un deuxième MCP3424 afin de doubler le nombre d'entrées analogiques, un second convertisseur de niveau à 4 entrées / sorties (TXB0104PWR) rejoint le TXS0108PWR et sera utilisé pour les entrées sorties de type Push / Pull (le TXS0108PWR ayant un mode haute impédance).

Une EEPROM série rejoint la carte afin de répondre à la spécification HAT mais je ne sais pas si elle sera montée sur les cartes (quoiqu'il arrive, son emplacement sera laissé sur le pcb).

J'ai également ajouté un capteur de température I2C (TMP10x) sous la carte permettant la mesure de la température rayonnée par le Raspberry mais le circuit intégré ne sera pas disponible de série, ou alors, uniquement en option (sauf si j'arrive à les obtenir par quantité à un coût intéressant).

La suite

Pour le moment, la carte RaspiO'Mix est fonctionnelle mais non disponible à la vente, quelques modifications sur l'emplacement des composants sur le pcb doivent encore être faite (merci Seeedstudio de proposer des librairies Eagle légèrement foireuse).

Ouvrir l'article

Dernières avancées de Bleuette

Un petit billet pour vous donner des nouvelles de Bleuette...

Alimentation

Pour fonctionner correctement, Bleuette à besoin d'une tension de 5V@1A pour alimenter son cerveau (un RaspberryPi) et d'une autre tension de 6V@3A pour alimenter les servos et la guirlande de led.

Le développement d'une seconde carte fille permettant de générer les alimentations est prévue mais pour le moment, j'ai fait au plus simple, ainsi, pour le 6V, j'ai branché en parallèle 2 modules régulateurs de tension à découpage facilement trouvable sur le net, notamment, sur DealExtreme, ce sont des modules très pratiques que j'utilise assez souvent, ils sont capables de débiter 3A en pointe, mis en parallèle, nous avons donc théoriquement 6A, c'est amplement suffisant pour Bleuette qui comme dit plus haut doit avoir besoin de 3A maximum.

Note: Concernant la mise en parallèle des régulateurs à découpage, prenez garde à parfaitement bien régler la même tension sur les 2 modules à l'aide d'un multimètre fiable.

Pour le 5V du Raspberry, j'ai fait très simple en récupérant le régulateur inclu dans une prise allume cigare, voir Régulateur à découpage à très faible coût.

La source de tension est un accu LiPo de 11.1V @ 1.3A, c'est plutôt faible mais je n'ai que ça sous la main pour le moment...

Logiciel embarqué : le Pic

Sur la carte BleuettePi se trouve un Pic 18F452 qui est là afin d'assurer les mêmes tâches que sur la version Arduino (Bleuette Shield) mais avec quelques différences :

  • Les ports utilisés pour piloter les servos ne sont pas les mêmes
  • Le RaspberryPi n'ayant pas de convertisseur Analogique / Numérique, c'est le Pic qui s'en charge et met à disposition 8 entrées analogiques
  • Il surveille la tension de la batterie et prévient le RaspberryPi (via INTD) en cas de passage sous un seuil
  • Il surveille le courant consommé par les servos et prévient le RaspberryPi (via INTD) en cas de dépassement d'un seuil

La communication avec le Pic se fait via une liaison série à l'aide de commande :

Divers :

  • Version Retourne la version du micrologiciel
  • Status Retourne des informations courantes sur le système

Spécifique au pilotage des servos :

  • Init Initilisation
  • Pause Met en pause les servos
  • Resume Sort d'une pause
  • Clear Passe à 0 toutes les consignes
  • Set Spécifie les positions des servos

Spécifique au convertisseur analogique :

  • Current Retourne la dernière valeur du courant consommé par les servos
  • Set Max Spécifie la valeur max du courant à ne pas dépasser
  • Read x Lit la valeur de l'entrée analogique (x vaut de 0 à 7)

Logiciel : le RaspberryPi

Avec l'utilisation de BleuettePi, la programmation de Bleuette se fait directement en Python, quel bonheur de développer en haut niveau sans les innombrables contraintes que j'avais sur Arduino, plus de limitation en taille, en vitesse, en programme qu'il faut compiler à chaque itération puis envoyer à la carte Arduino...

J'ai repris la même logique que sur l'Arduino pour l'enregistrement des séquences, un tableau est utilisé pour stocker les positions des pattes à chaque étapes (une structure en C sur la version Arduino) qui est contenu dans une classe Python (une autre structure pour la version en C), toutes les positions sont envoyées par la liaison série.

Bleuette est maintenant pilotable via une interface web tournant grâce à Tornado (un framework web en Python), toute la communication se fait via un websocket, et Bleuette pousse régulièrement toutes les informations issues de ses capteurs (tension, courant consommé, valeur du magnétomètre, de l'accéléromètre, etc...).

Sur l'interface, il est possible de régler finement la position des servos (trims), de régler les valeurs limites à ne pas dépasser, et même de voir une vue 3D de Bleuette et des mouvements de ses pattes (grâce à l'utilisation de Three.js) etc...

Voici les captures d'écran des différentes pages :

Page d'accueil Vue des mouvements de pattes Réglage des positions limites des pattes Mouvement des pattes Vue des capteurs Vue 3D de Bleuette et des mouvements

Un mode de test permet de tester le logiciel sans avoir de Bleuette sous la main, il vous suffit de suivre les instructions de cette page : Tester le serveur.

Problèmes rencontrés

Chevauchement des ordres

Les ordres reçus depuis l'interface utilisateur sur l'ordinateur distant arrivent dans Bleuette au travers d'un websocket, ces informations sont asynchrones, on ne peut pas savoir quand elles vont arriver et des fois, elles pouvaient se chevaucher, ainsi, si j'envoie un ordre pour modifier la position des servos (16 octets : 1 commande, 14 positions, 1 contrôle) et que pendant l'envoi d'un octet de la trame, une autre commande est exécutée, l'ordre de position va échouer (somme de contrôle erronée), des octets vont rester en attente, la communication série va expirée, remonter une exception, etc...

Exemple concret: Le programme du Pic fait des lectures régulières du courant consommé par les servos, si la valeur dépasse un seuil, le Pic déclenche une interruption à destination du Raspberry (INTD), ce dernier va alors faire une demande de lecture de Status (voir au dessus) au Pic lui permettant de savoir quel type d'interruption à été levé mais cette demande passe bien entendu par la ligne série et comme elle peut survenir n'importe quand, elle peut aussi survenir lors de l'envoi du longue trame de positionnement de patte.

Pour palier à cela, j'ai créé un thread portant le doux nom de Servo_Sequencer qui se charge de l'envoi des positions, seul lui est capable de le faire, chaque position envoyée est mise en cache et dès que la communication est possible, elle est envoyée, il est également possible de faire des sortes de transaction à l'aide du concept de context manager de Python.

Exemple :

with ServoSeq:
    ServoSeq.setPosition(0, 120)
    ServoSeq.setPosition(2, 120)

Une fois sortie du contexte introduit par with, les positions sont ajoutées dans le buffer et seront transmises par ordre d'arrivée au contrôleur de servos.

Ligne série saturée

La liaison série est pas mal occupée lorsque Bleuette marche car toutes les communications du Raspberry au Pic inclu dans BleuettePi se font par celle-ci, or, pour lire les tensions analogiques, il faut passer par cette liaison, il est difficile de faire une demande de lecture analogique au moment ou Bleuette doit déplacer une patte sans que cela n'induise des latences.

Pour ces 2 problèmes, chevauchement des ordres et saturation de la ligne série, la prochaine évolution sera l'ajout du Pic sur le bus I2C permettant ainsi de garder la ligne série uniquement pour le pilotage de Bleuette.
J'envisage aussi d'ajouter un convertisseur analogique numérique I2C évitant ainsi au Pic d'avoir à faire les conversions...

Amplificateur audio

La carte BleuettePi inclu un amplificateur audio (LM386) permettant ainsi de donner la voix à Bleuette, le problème est l'utilisation d'une clef Usb Wifi qui rayonne assez fort et qui vient gentiment ronronner dans la partie audio, lorsque du son est émis, pas de souci, le ronronnement est noyé mais dans le cas contraire, c'est plutôt gênant.

Je pense donc déplacer la partie amplification sur une carte fille, j'utiliserai certainement un amplificateur un peu plus puissant.

Carte fille accéléromètre / magnétomètre

La carte fille (GY-27) comprenant un accéléromètre et un magnétomètre est fixée sur BleuettePi via un simple connecteur (type barette sécable), il sera certainement déporté afin d'être fixé au corps de Bleuette pour éviter qu'elle ne vibre et renvoie des informations non fiables.

Grâce à la récente acquisition d'un four à refusion, il est également possible que la future carte BleuettePi intégre directement les capteurs.

La liste des évolutions de Bleuette sera maintenue sur cette page : BleuettePi : Future version

Ouvrir l'article