Comment j’ai fait mon flux RSS avec wordpress

Mes émissions sont rangées dans des articles, dans la catégorie ’emission’. Le flux rss contiendra uniquement les articles de cette catégorie. J’utilise les champs personnalisés pour compléter les informations demandées par itunes et pour inclure les informations du fichier mp3.

RSS Made By Humans

J’ai créé mon plugin wordpress très simplement, voilà comment.

Le plugin

Allez sur votre hébergement de wordpress. Créez un répertoire du nom de votre plugin. Par exemple :

/wp-content/plugins/MBH-my-RSS-generator/

NB. Peu importe le nom. Le plugin n’est pas destiné à aller dans la base de plugins de wordpress. Il doit juste être unique dans votre répertoire de plugins.

Ajoutez un fichier .php qui doit avoir le même nom que le répertoire. Dans notre exemple : 

MBH-my-RSS-generator.php
<?php
/*
Plugin Name: MBH-my-RSS-generator
Plugin URI: https://madebyhumans.fr/
Description: My Podcast RSS Plugin
Version: V0-20180915
Author: Path
Author URI: https://madebyhumans.fr/
License: WTFPL
License URI: http://www.wtfpl.net/
*/

defined( 'ABSPATH' ) or die( 'No script kiddies please!' );

add_action('init', 'addMyRSS');

function addMyRSS(){
add_feed('myrss', 'renderMyRSS');
}

function renderMyRSS (){
include('myrss.php');
}
?>

Le 1ère partie de commentaire sont des informations destinées à wordpress. Mettez ce que vous voulez.

Le code PHP demande à WP de d’exécuter la fonction addMyRSS une fois à l’initialisation.

La fonction addMyRSS ajoute votre flux rss avec le nom ‘myrss‘ et indique la fonction à utiliser (renderMyRSS) au moment où le flux est demandé. Le flux est alors disponible avec ce chemin :

http://<votre domaine>/myrss

Vous pouvez donner le nom que vous voulez tant que ça passe dans un lien web. Ne mettez pas d’accents et pas d’espaces par exemple.

Enfin, la fonction renderMyRSS va chercher le fichier myrss.php qui génère le XML du flux RSS à proprement parler.

Le template XML

Ce fichier contient le template XML du flux RSS personnalisé. C’est le 2e et dernier fichier du plugin. Par exemple myrss.php :

<?php
/**
 * Template Name: Custom RSS Template - Feedname
 * http://www.wpbeginner.com/wp-tutorials/how-to-create-custom-rss-feeds-in-wordpress/
 */
$item_counter = 0;
$postCount = 1000; // The number of posts to show in the feed
$posts = query_posts('showposts=' . $postCount.'&category_name=emission');
//header( 'Content-Type: application/rss+xml' );
header('Content-Type: '.feed_content_type('rss-http').'; charset='.get_option('blog_charset'), true);
echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>';
?><rss version="2.0"
 xmlns:content="http://purl.org/rss/1.0/modules/content/"
 xmlns:wfw="http://wellformedweb.org/CommentAPI/"
 xmlns:dc="http://purl.org/dc/elements/1.1/"
 xmlns:atom="http://www.w3.org/2005/Atom"
 xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
 xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
 xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
 xml:lang="fr-FR"
	<?php do_action('rss2_ns'); ?>>
	<channel>
		<title><![CDATA[<?php bloginfo_rss('name'); ?>]]></title>
		<atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
		<link><?php bloginfo_rss('url') ?></link>
		<description><?php bloginfo_rss('description') ?></description>
		<lastBuildDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_lastpostmodified('GMT'), false); ?></lastBuildDate>
		<language><?php echo get_option('rss_language'); ?></language>
		<sy:updatePeriod><?php echo apply_filters( 'rss_update_period', 'hourly' ); ?></sy:updatePeriod>
		<sy:updateFrequency><?php echo apply_filters( 'rss_update_frequency', '1' ); ?></sy:updateFrequency>
		<generator>MoutMoutRSS</generator>
		<?php while(have_posts()) : the_post(); ?>
		<item>
			<title><?php the_title_rss(); ?></title>
			<link><?php the_permalink_rss(); ?></link>
			<pubDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_post_time('Y-m-d H:i:s', true), false); ?></pubDate>
			<dc:creator><?php the_author(); ?></dc:creator>
			<guid isPermaLink="false"><?php the_guid(); ?></guid>
			<description><![CDATA[<?php the_excerpt_rss() ?>]]></description>
			<content:encoded><![CDATA[<?php the_excerpt_rss() ?>]]></content:encoded>
			<?php rss_enclosure(); ?>
			<itunes:duration><?php echo get_post_meta(get_the_ID(), 'audio_file_duration', true); ?></itunes:duration>
			<itunes:order><?php echo $item_counter++; ?></itunes:order>
			<itunes:duration><?php echo get_post_meta(get_the_ID(), 'audio_file_duration', true); ?></itunes:duration>
		</item>
		<?php endwhile; ?>
	</channel>
</rss>

Au début du template, on note le filtre &category_name=emission sur la commande query_posts

On peut noter dans le fichier template le tag <itunes:order> que l’on fabrique avec un compteur.

Le fichier mp3 est inclus dans le rss avec la commande rss_enclosure();

Champs personnalisés

Pour ajouter, par exemple le tag <itunes:duration>, on va utiliser les champs personnalisés.

Pour cet exemple, j’ai ajouté le champ personnalisé audio_file_duration

NB. Les champs personnalisés ne sont pas visibles par défaut. Pour les afficher, il faut cocher dans le panneau « Options de l’écran » en haut à droite du tableau de bord.

Et voilà !! 🙂

Podcasteur le punk de la radio

C’est le moment de faire un petit point d’étape.

L’histoire remonte au mois d’avril où, dans les coulisses du forum robot-maker.com, on cherchait à créer du contenu. J’ai parlé d’interviews. Je pensais d’abord aux acteurs connus comme le professeur Jacques Gangloff et ses cours en video, le youtubeur James Bruton et d’autres. Mais je n’ai eu que des dédicaces par mail. Bien mais pas très profond. Pour ce type de contenu, j’ai testé l’audio avec certains d’entre nous. En plus de mieux connaître mes interlocuteurs, de partager encore plus, de faire de très bonne rencontres, j’ai obtenu de la matière pour tester le matériel, le montage me roder. Et je trouve ça concluant, ça me donne envie de continuer. C’est encore très perfectible mais je trouve ça top !! Merci encore à ceux qui sont venus !!

A propos du format audio

Je le trouve tout à fait adapté à une utilisation annexe. Comme la radio, on l’écoute pendant qu’on continue à ses occupations. En voiture, en transport, à pieds, pendant qu’on bosse sur son robot ^^. Comparé à la video, d’un coté cela limite l’aspect didactique, de l’autre coté c’est plus facile à produire. Et on a l’écrit sur le forum pour l’aspect didactique. Je trouve aussi que le dialogue direct à l’oral rapproche, renforce l’esprit communautaire et démystifie la complexité de la robotique. Et c’est bien là le but.

A propos de la diffusion

Les gens qui viennent sur youtube cherchent du contenu video. On le voit dans les chiffre donnés par youtube, les gens ne lisent que 10% des interviews que j’ai posté sur ce support. Je vais continuer de diffuser sur youtube mais uniquement pour la « visibilité ». La cible des podcasts c’est itunes, podcloud, mon rss et twitter. Soundcloud n’est pas super en forme. Ils licencient … Sur les supports dédiés aux podcasts, on ne peut pas mesurer la durée de l’écoute. C’est un mp3 qu’on télécharge. Mais on se donne toutes les chances que ça le soit :)

On est au tout début de l’histoire. On va continuer les interviews bien sûr. A coté de cela, on va explorer d’autres voies avec une émission périodique. Affaire à suivre donc.

A propos du podcast

On a aucun objectif de chiffre, aucune pression. Ça sort quand ça sort :) On le fait dans la bonne humeur et sérieusement on le fait à la cool. La préparation du conducteur doit être très limitée. Nous n’avons besoin que de notre passion. On fait du contenu qui est diffusé en se marrant.

C’est quand je veux, si je veux, indépendant des media et des autres podcasts, liberté de format, liberté de ton, un espace pas encore très réglementé, très peu de moyens …  Bref, une radio pirate moderne en quelque sorte.

Discussion sur le forum.

Etat du studio

De quoi s’amuser un bon moment.

  • Un micro XLR.
  • Un enregistreur numérique.
  • Un serveur mumble.
  • Des cartes son externes pour brancher un téléphone et un ordi pour le mumble.
  • Un mixeur analogique et le câblage mix-minus pour que tout le monde se parle.
  • Un répondeur et une ligne téléphonique (ligne SIP gratuite).
  • Un serveur icecast2 et un raspberry pour diffuser le master audio brut en direct sur une page web, avec une image prise toutes les minutes pour illustrer.
  • un site web pour porter ces infos et le RSS de diffusion.

Multitâches avec arduino

C’est une petite réflexion sur le multitâche dans arduino. C’est mon quart d’heure pensée profonde de l’été. C’est de l’apologie assumée du non-bloquant. Je mettais mes idées en ordre avant de me remettre sur Emile.

 

Le concept selon wikipedia :
« La simultanéité apparente est le résultat de l’alternance rapide d’exécution des processus présents en mémoire. Le passage de l’exécution d’un processus à un autre est appelé commutation de contexte. Ces commutations peuvent être initiées par les programmes eux-mêmes (multitâche coopératif) ou par le système d’exploitation lors d’événements externes (multitâche préemptif). »

 

Cool !! Dans la procédure loop(), quand on appelle différentes librairies les unes après les autres, on fait du multitâches peut-être sans le savoir. Le mot « apparente » dans la définition est importante. Notre arduino favori ne fera qu’une seule chose à la fois. Mais tellement vite que cela semble être fait simultanément.

 

Piloter le driver de moteurs, compter les ticks des roues codeuses, afficher un message, commander et lire un capteur, envoyer et recevoir sur un port série, exécuter la logique de décision du robot … On ne met pas un arduino pour chacune de ces fonctions mais on peut vouloir dédier un arduino pour la lecture des ticks des roues codeuses. Car on le fait avec des interruptions. A chacune des interruptions, Arduino sauvegarde l’état des registres du programme principale, exécute la procédure de l’interruption, rétabli les registres et reprend le programme principale. On voit bien qu’il va être difficile pour l’arduino de capter un 2e ticks qui arriverait pendant une interruption. C’est pour cela que les interruptions doivent être très courtes. C’est une manière de faire du multitâches avec arduino en mode préemptif. C’est très pratique mais on voit apparaître une limite. A partir d’une certaine fréquence d’interruption, l’arduino ne pourra plus les exécuter toutes. Avant d’atteindre sa limite, il ne fera plus que traiter ces interruptions. On peut choisir d’ignorer les interruptions pendant un temps dans le programme principale mais pas plus. C’est pour cela qu’on dédie un arduino, pour maximiser le nombre de ces lectures. Malgrés la limite, c’est le meilleur moyen de lire des évènements déclenchés par un composant du robot extérieur à l’arduino.

 

De là à dire qu’il faut 2 arduino dans un robot, j’en suis pas loin. C’est un moyen radicale de faire du multitâche.

 

L’autre manière de faire du multitâches, c’est le mode coopératif. Il ne règle pas le problème qu’on vient de voir. On est toujours dans les limites de fonctionnement d’un micro contrôleur. Il permet de faire du multi tâches en le gérant soit même dans le programme. On le fait quand on est pas dépendant d’évènements extérieur et quand on a pas de contraintes de mesure de temps. Il consiste à faire des sous-programmes avec des fonctions ou des classes. À réserver des variables à chaque sous programmes. Et à appeler ces sous programmes dans la procédure loop(). C’est le même concept que l’interruption quand arduino sauvegarde les registres et les restaure après l’interruption. L’arduino a besoin de remplacer ses variables (ou registres) pour laisser la place aux données de l’interruption. Quand on le fait soit-même dans un programme, on ne va pas remplacer, on va avoir plusieurs variables, autant qu’il en faut pour chacun des sous programmes.

 

Pour illustrer, je vais expliquer pourquoi il faut se passer de la procédure delay(). Et comment ? En faisant du multitâche coopératif. Admettons qu’on veuille piloter la vitesse de rotation d’un servo moteur. Sans pilotage particulier, le servo ira à la position demandée aussi vite qu’il peut. Là, on veut qu’il aille à 10° par secondes. On est dans loop(). On a une variable qui contient la position courante. On commence à 0°, on place le servo à 0°, on attend 1 seconde avec delay(), on avance de 10° et ainsi de suite. Le résultat est un peu saccadé. On se dit qu’on va faire mieux en avançant de 1° à la fois avec des attentes de 100 ms. On est bien à la vitesse voulu et c’est pas saccadé. Génial !! On a réussi. Maintenant, on va placer un 2e servo. À la suite du programme, dans loop(), je vais piloter mon 2e moteur avec le même principe. Je déplace le moteur 1 de 1°, j’attend 100ms, je déplace le moteur 2 de 1°, j’attend 100ms et ainsi de suite. Ça fonctionne mais la vitesse est divisée par 2. Le temps d’attente est maintenant de 200ms. Je réfléchi 5 min et je décide de diviser par 2 les valeurs dans le delay(). Tout fonctionne bien.

 

Pour mon humanoïde avec 13 dof, je vais diviser par 13 pour conserver ma vitesse … J’arrête là ce raisonnement. Tous les moteurs ne fonctionnent pas en même temps et pas tous à la même vitesse. Je n’ai pas envie de calculer chaque valeur de delay() pour mes 13 servo. La solution consiste à ne pas attendre, a ne pas utiliser de fonction bloquante et laisser la boucle loop() s’exécuter aussi vite qu’elle peut. Indépendamment de la période de calcul des servo. Pour utiliser le temps d’attente du delay() et pour laisser le temps aux autres calculs. Il n’y a pas que la vitesse des moteurs à calculer. Alors comment on fait ? Le principe : je connais la position courante et la vitesse que doit avoir chacun des moteurs. A chaque passage dans loop(), je regarde l’heure qu’il est (toujours en ms). Et pour chaque moteur, je calcul la position où il doit être pour cette heure de passage dans loop(). On ne maîtrise pas le délai entre 2 passages dans loop() mais on le mesure et on en déduit la position.

 

Pour y arriver, on va dire que le calcul de la position d’un servo est un sous programme. Chaque servo doit avoir ses propres variables : vitesse, heure de départ, position de départ et position d’arrivée. Faire une classe Servo est tout indiqué. On va utiliser la fonction map().

La durée du parcours est donnée par la formule : vitesse = abs((position d’arrivée – position de départ)) / durée du parcours.

L’heure d’arrivée est donnée par l’heure de départ + durée du parcours.

Et chaque moteur est piloté par la fonction suivante.

long position courante = map(heure qu’il est, heure de départ, heure d’arrivée, position de départ, position d’arrivée);

 

Pour boucler la boucle, il y a le cas du capteur ultrason HC-SR04. Pour avoir une lecture de la distance, la fonction pulseIn va mesurer le temps qu’un pin du module ultrason reste à l’état haut. L’enjeu consiste à détecter avec précision les changements d’état. PulseIn est une instruction bloquante. Le temps d’attente est dépendant de la distance. Pendant ce temps, on voudrait que l’arduino fasse autre chose. Dans ce but, on pourrait avoir envie de mesurer le temps entre le front montant et le front descendant avec un code multitâches coopératif. Dans la boucle, je lis l’état du capteur. s’il passe à haut, je note l’heure (en ms). Je continue de lire à chaque passage de la boucle. S’il repasse à bas, je note aussi l’heure. La différence de temps entre ces 2 horaires maximise la largeur de l’impulsion. C’est possible mais on perd en précision. La précision serait dépendante de la longueur de la boucle. C’est à dire la quantité d’instruction qu’on a placé dans le procédure loop(). Même si la fonction pulseIn() ressemble à la procédure delay() par on caractère bloquant, on peut être tenté de l’utiliser. Parce que c’est simple à faire. Et si on veut en optimiser le résultat, on peut même ignorer les interruptions pendant qu’elle attend … Cela fonctionne très bien, mais là on coupe toute possibilité de multitâches !! Et si on a des roues codeuses à intercepter, soit on arrête le robot pendant qu’on utilise le capteur, soit on prend un 2e arduino. La bonne solution est de passer par les interruptions pour intercepter les changements d’état du capteur. Le seul inconvénient est qu’il faudra un arduino avec plus de 2 pin d’interruption si on souhaite les codeuses en même temps.

 

On en parle ici

Fabrication de la video avec l’onde sonore

La commande ffmpeg permet de créer une video avec une image de fond et d’un mp3. Elle permet aussi d’y ajouter des effets dont une représentation de l’onde sonore du mp3. La commande utilisée :

./ffmpeg -i MakerSecrets.mp3 -loop 1 -i YT_MBH.png -filter_complex \
"[0:a]showwaves=s=854x480:mode=p2p,format=rgba,colorkey=black[fg]; \
[1:v]scale=854:-1,crop=iw:480[bg]; \
[bg][fg]overlay=shortest=1,format=yuv420p[out]" \
-map "[out]" -map 0:a -c:v libx264 -preset fast -crf 18 -c:a libopus MakerSecrets.mkv

Source :

http://astuces.podcloud.fr/transformer-un-podcast-audio-en-video
https://ffmpeg.org/ffmpeg-filters.html

Fiction participative et à vivre

En ce début d’été Phil_Goud (PodCloud) nous propose de vivre une fake news. Un jeu de rôle participatif par message téléphonique. Le pitch est simple : un météore très inquiétant va heurter le nord de l’Europe. Sur le fil du podcast 163, nous suivons presque en direct la réaction de nombreux participants ou survivants. Certains organisent leur fuite, certains observent l’actualité. Moi je choisi de me planquer dans les catacombes de Paris.

http://163.lepodcast.fr/

Partager la conversation mumble avec un invité par téléphone

Pendant une conversation sur mumble, comment prendre un appel téléphonique et permettre à l’invité par téléphone de converser avec les personnes présentes sur le mumble.

J’utilise une table de mixage et je fais un mix-minus. Le principe : envoyer sur une sortie auxiliaire toutes les sources mixées sauf une. Par exemple, envoyer sur le téléphone tout le mix sauf le téléphone. Envoyer sur le mumble tout le mix sauf le mumble.

Par exemple, la Behringer 1204 possède 2 sortie auxiliaire. Elle permet de faire 2 mix-minus, un pour le mumble, un pour le téléphone.

Pour brancher le téléphone à la table de mixage, j’utilise la prise micro-casque. J’ai testé un séparateur Y sans succès. Il faut une interface comme le iRig2.

Pour brancher le mumble sur la table de mixage, j’utilise une carte son externe usb. Elle est reconnu automatiquement et directement. Il faut la sélectionner dans mumble et le tour est joué.

 

Flux audio

Pourquoi un flux audio en direct pour un podcast ? Ce flux est mis en place pour un format de podcast en mode hot line.

Comment ça fonctionne ? Voici la chaîne que j’utilise :

La table de mixage a une carte son usb intégrée. Elle est directement reconnue comme telle par un PC. Le son issue du mix est donc cette prise usb qui sert de master pour le flux.

Le PC qui bénéficie de cette carte son est un Raspberry PI 3. Il est chargé de lire le flux audio qui sort de la table de mixage via usb. Il arrive au format PCM. Il faut le comprimer en MP3 et l’envoyer sur un serveur de diffusion. La lecture est faite avec alsa (arecord). L’encodage est fait avec lame. L’envoi sur le serveur est fait avec ezstream. Cela donne la commande suivante :

arecord --device hw:CARD=CODEC,DEV=0 --format cd --rate 44100 --vumeter=stereo | lame -r -s 44.1 -m s -b 128 --cbr --scale 3  - | ezstream -c /home/pi/ezstream_mp3.xml

Le serveur icecast2 sert à relayer le flux audio aux différents auditeurs. Il faut noter que ce dernier composant crée un délai de quelques secondes.

Pour écouter le flux audio, il faut une balise html audio qui pointe sur le serveur icecast.

<audio src="http://163.172.63.38:8000/live.mp3" volume="1.0" controls autoplay>
Votre navigateur ne supporte pas l'élément <code>audio</code>.
</audio>

Tout cela demande pas mal de recherche et de configuration qu’il est facile de retrouver sur le net.

MP3Paris 2e édition 2017

Aujourd’hui, je suis allé là : http://www.mp3aparis.fr/. C’est une convention de podcasteurs (audio biensûr). Et, attention, pas les youtubeurs, On ne se mélange pas 😀

J’ai échangé avec les podcasteurs. Les vrais, ceux qu’on écoute étaient là en vrai !! Ces sont des amateurs, comme moi. Ils ne m’ont pas pris de haut, loin de là. Je trouve la communauté ultra sympa, chaleureuse et prête à partager ses ficelles.

J’ai causé imprimante 3D avec un certain Picaboubx de Bricolo et Mulot et de http://lavis-des-moutons.lepodcast.fr/. Nous avons tout le deux monté la dagoma disco 200. Lui, il a changé la buse pour qu’elle fonctionne bien …

J’ai causé technique podcast avec un certain Quenton de http://www.techcraft.fr/. Format de fichiers, mixage, rss, montage. Il m’a donné des pistes pour mon pb avec les petits bruits qu’on entends dans le mumble. Je vais voire ça. Il a même pris le temps d’écouter un bout de l’épisode avec Oliver !! Il a trouvé ça top … pour un début … 🙂

J’ai aussi causé technique d’hébergement avec les mecs (Phil_goud) qui on fait https://podcloud.fr/, un annuaire très connu de podcast. Je leur ai dit attention on arrive ^^.

Rien sur la robotique et très peu de tech dans l’assemblée mais j’en sors chaud patate et surtout serein d’avoir échangé avec cette communauté !! J’y retourne l’année prochaine !!

Communication entre raspberry et arduino UNO via USB

Après la compilation et l’upload depuis le PI, toujours sans débrancher la prise USB entre le PI et le Duino, ils se parlent ! cf. http://www.robot-mak…ne-de-commande/
post-9452-0-72697100-1458205262.png

Préalable sur le raspberry

Il faut installer Serial pour python.

sudo apt-get install python-serial

Préalable sur arduino : rien.

Programme de test PI (en python)

#!/usr/bin/python
# -*- coding: utf-8 -*-


import serial
import time

ser = serial.Serial('/dev/ttyACM0', 9600)

while 1 :
    message = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
    ser.write(message)
    print(ser.readline())
    time.sleep(0.5)

Programme de test arduino :

int compteur = 0;
String message;

void setup(){
Serial.begin(9600);
}


void loop() {
message = "-";
if (Serial.available()) {
message = Serial.readString();
}
Serial.print("MSG # ");
Serial.print(compteur);
Serial.print(" read : ");
Serial.println(message);

compteur++;
delay(1000);
}

Le PI envoi la date.
Le Duino lit cette date, ajoute une donnée et retourne la texte modifié.
Le PI affiche ce que Duino lui envoi.

Après compilation et upload, on lance le programme de test serial :

pi@raspberrypi:~/SBR $ ./test-serial-rpi.py
Wed, 16 MSG # 0 read : -

MSG # 1 read : Wed, 16 Mar 2016 21:30:21 +0000

MSG # 2 read : Wed, 16 Mar 2016 21:30:23 +0000

MSG # 3 read : Wed, 16 Mar 2016 21:30:25 +0000

MSG # 4 read : Wed, 16 Mar 2016 21:30:27 +0000

MSG # 5 read : Wed, 16 Mar 2016 21:30:29 +0000

Tuto réalisé dans le cadre de la construction de Ash V1 : http://www.robot-mak…-robot/?p=67997

Introduction à Node JS sur Raspberry PI

C’est quoi Nodejs ?

Nodejs, c’est un portage open source en C++ du moteur d’interpretation et d’exécution javascript que l’on trouve habituellement dans les navigateurs (chrome) : le V8 de google. Nodejs permet de sortir le javascript des navigateurs pour l’exécuter sur une machine windows, mac ou linux en standalone (desktop, serveur ou embarqué).

Les navigateurs étant sécurisés, par exemple, on ne peut pas accéder aux fichiers, à la ligne de commande ou écouter un port. Nodejs ajoute au javascript les fonctions  d’accès au système qui lui faisaient défaut. Nodejs suis les évolutions de la norme ECMAScript (spécifications du langaue) et est devenu une plateforme à la fois simple, léger et mature. Surtout depuis ES6. La doc de l’API exposée par NodeJS en plus du Javascript : https://nodejs.org/d…-v6.x/docs/api/

NodeJS est maintenant couramment utilisé dans le monde de l’IT pour des middleware, des web services temps-réel ou en full-stack (coté serveur et coté client) pour des applications web. On le voit aussi beaucoup dans le monde des objets connectés. Il arrive naturellement sur le raspberry PI pour piloter un robot.

Le langage javascript n’est pas un langage simple contrairement à ce qu’on pense généralement. Ce tuto ne couvre pas le javascript. Il faudra pour cela voir de ce coté : http://javascript.de…ppez.com/cours/. C’est faux de croire que l’on connait javascript si on a affiché un bouton dans une page web. En effet le navigateur masque toute la partie interessante de javascript : sa boucle d’évènements. Le code javascript est exécuté dans un seul thread. Mais derrière tous les appels asynchrones, la mécanique du V8 est parallélisée. Mieux que des mots, la meilleure video que je connaisse pour expliquer comment fonctionne javascript.

Par exemple, hello.js

// attends 1s et affiche le texte
setTimeout (function() {
  console.log("les makers");
},1000);
console.log ("Hello");

$node hello.js (ou C:\node.exe hello.js)

hello.js affiche :
Hello
​les makers
(et pas « les makers Hello »)

Avertissement

Le tuto qui suit montre comment piloter un robot avec NodeJS. Vous l’aurez compris, NodeJS devient intéressant quand on connait javascript.

Ce tuto pré-suppose que le raspberry est installé avec la raspbian : https://www.raspberr…/documentation/

Ce tuto est destiné aux débutants en robotique. Il est réalisé dans le cadre de la conception du robot Ash : http://www.robot-mak…alancing-robot/

Pourquoi PAS NodeJS ?

Javascript n’est pas un langage temps-réel.
Javascript n’est pas compilé. Il n’y a pas de vérification du code avant l’exécution.
Javascript est faiblement typé. Il demande un peu d’attention.

Alors, plus concrètement,

Pourquoi NodeJS ?

  • javascript n’attend pas

Il continue à exécuter le code pendant qu’une instruction asynchrone est en attente. Une lecture de ficher, d’un port série ou réseau. Cela le rend bien plus efficace et performant qu’un système multi-threadé.

  • javascript est un langage à évènements.

Ce n’est pas avec une librairie ou un addon. C’est au coeur du langage.

  • Les streams

L’API stream de NodeJS combine la puissance des évènements avec celle des pipes d’unix. Si vous connaissez les pipes entre les commandes unix, vous savez que c’est une aide précieuse et facile à mettre en place.

  • javascript est full stack

Un seul langage pour le serveur, pour le client et pour les middlewares orienté messages (évènements).

Si on connait son langage, NodeJS est un pont facile à mettre en place entre arduino et une interface de contrôle web. Et disposer d’une interface web permet de ne rien installer sur le terminal de commande (sauf un navigateur) et il est compatible avec tous les smartphone et sur toutes les machines desktop habituels.

NodeJS ouvre le raspberry (et donc le robot qui en est doté) à tout l’Internet domestique ou extérieur.

Enfin, Nodejs bénéficie d’une très large communauté. Il est accompagné d’un gestionnaire de packages (librairies) npm. Pour vous faire une idée, voilà ce qu’on trouve déjà concernant le port gpio chez nodejs : https://www.npmjs.com/search?q=gpio

NPM

Node s’install avec son gestionnaire de packages. Voir : https://docs.npmjs.com/
On se contentera de faire des npm install pour obtenir des modules.

Installation de NodeJS et NPM sur Raspberry PI

Ajouter le repository nodesource :

sudo curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -

Cette commande va aussi  mettre à jour le catalogue.

NB. Adafuit mets aussi node à disposition sur un repository similaire. Avec nodesource, on a une version plus à jour.
NB. Quand la version 8 de NodeJS sera disponible, l’url ci-dessus devra être adaptée. Voir https://github.com/nodejs/LTS/.

Installer nodejs :

sudo apt-get install nodejs

NB. npm (le gestionnaire de package de node) est installé automatiquement avec nodejs.

Vérification :

pi@raspberrypi:~ $ nodejs -v
v6.2.1
pi@raspberrypi:~ $ npm -v
3.9.3

Source : https://nodejs.org/e…x-distributions

Installation d’un module NPM

$npm install <nom du module>

npm va alors créer un répertoire module dans le répertoire courant. Vous devez donc vous placer à la racine de votre projet pour lancer cette commande.

Pour plus d’infos sur NPM : https://docs.npmjs.com/

Interface avec Arduino

Le plus simple est de conserver le câble USB entre le raspberry et arduino dans le montage définitif du robot.

Compilation
Cela permet de compiler le code arduino sur le raspberry directement et d’uploader le programme compilé sur l’arduino directement depuis le raspberry. Voir http://www.robot-mak…ne-de-commande/.

Communication
Pour communiquer avec arduino, on utilise toujours le câble USB.

Coté raspberry, on utilise le module NPM « serialport » dans une classe utilitaire qui fait l’interface avec Arduino :

"use strict";

var serialPortModule = require("serialport");
var EventEmitter = require('events').EventEmitter;
var util = require('util');

function Arduino() {
  EventEmitter.call(this);

  this.serialIsOpened = false;
  var self = this;

  this.serialPort = new serialPortModule.SerialPort("/dev/ttyACM0", {
    baudrate: 115200,
  	parser: serialPortModule.parsers.readline('\n')
  });

  this.serialPort.on("open", function () {
  	self.serialIsOpened = true;
    self.emit('ready', {});

    self.serialPort.on('data', function(data) {
      self.deserializeWhatDuinoSays(data);
    });
  });
}
util.inherits(Arduino, EventEmitter);

Arduino.prototype.deserializeWhatDuinoSays = function(data) {
  // Le message attendu est de la forme COMMANDE:DONNEE:DONNEE:DONNEE\n
  var splitedData = data.split(':');
  var command = splitedData.shift();
  this.emit(command, {args: splitedData});
}

Arduino.prototype.writeSerial = function(message, next) {
  if(this.serialIsOpened) {
    this.serialPort.write(message+"\n", function(err, results) {
      if(next)next();
    });
  }
}

module.exports = Arduino;

On utilise cette classe comme ceci :

var Arduino = require('./modules/arduino.js');

Pour envoyer un message à arduino :

arduino.writeSerial('COMMANDE:DONNEE:DONNEE:DONNEE');

Pour recevoir des messages de arduino :

arduino.on('ready', function() {
  console.log('Arduino ready');
});

arduino.on('CONSOLE', function(data) {
  console.log('[ARDUINO CONSOLE]'+data.args);
});

arduino.on('COUNTCODER', function(data) {
  console.log('Arduino COUNT left: '+data.args[0])
  console.log('Arduino COUNT right: '+data.args[1])
  (...)
})

Du coté Arduino, pour écrire un message à raspberry, on utilisera simplement

Serial.print();

Pour lire les message envoyé par raspberry, il faut les dé-sérialiser et dispatcher les commandes. Par exemple :

String dataFromPI = "";
/*
  Format : COMMAND:value[:value[:value[...]]]
  Command DIST : 0
  Command SERVOH : pos
  Command SERVOV : pos
  Command MOTOR : dirA:pwmA:dirB:pwmB
*/

void parseAndDispatch(String dataFromPI) {
  // get Command
  int dataLength = dataFromPI.length();
  int firstSep = dataFromPI.indexOf(SEPARATOR);
  if(firstSep == -1) return;
  if(firstSep + 1 >= dataLength) return;
  String cmd = dataFromPI.substring(0,firstSep);
  // get args
  String arrayArgs[MAX_ARGS];
  unsigned int index = 0;
  while(firstSep + 1 < dataLength && index < MAX_ARGS) {
    int secondSep = dataFromPI.indexOf(SEPARATOR, firstSep+1);
    // pour le dernier argument
    if(secondSep == -1) {
      secondSep = dataLength;
    }
    String arg = dataFromPI.substring(firstSep+1, secondSep);
    firstSep = secondSep;
    arrayArgs[index] = arg;
    index++;
  }
  if(cmd == "MOTOR") {
    doMotorCommand(arrayArgs);
  }
}


void loop() {
  if (Serial.available()>0)  {
    dataFromPI = Serial.readStringUntil('\n');
    parseAndDispatch(dataFromPI);
  }
}

Alimentation
Le raspberry peut alimenter l’aduino par le port USB donc dans la limite de 500mA.

Interface avec les programmes en python

Par exemple, on va intercepter les valeurs qui arrivent du capteur IMU du senseHAT. Le senseHAT est fourni avec une API en python. On va appeler la librairie du senseHAT et écrire une valeur sur la sortie standard.

#!/usr/bin/python
# -*- coding: utf-8 -*-

from sense_hat import SenseHat
import time
sense = SenseHat()
sense.set_imu_config(False, True, True)  # compass_enabled, gyro_enabled, accel_enabled
while True:
	# Get orientation from hat
	orientation = sense.get_orientation_degrees()

	roll = orientation["roll"]
	roll = round(roll, 2)
        print(roll)
	time.sleep(.005)

Dans NodeJS, on va utiliser le module « python-shell » pour instancier ce programme en python et lire la sortie standard.

var PythonShell = require('python-shell');

var pyOptions = {
  mode: 'text',
  pythonPath: '/usr/bin/python',
  pythonOptions: ['-u'],
  scriptPath: '/home/pi/SBR'//,
  //args: ['value1', 'value2', 'value3']
};

var imu = new PythonShell('imu.py', pyOptions);

imu.on('message', function(data) {
    console.log(data); // data contient une ligne de la sortie standard données par imu.py
  });

Interface web

Pour servir une page static HTML avec node, c’est très simple. On va utiliser le module « express » pour créer un mini serveur web sur le raspberry. On y ajoutera tout de suite une websocket pour obtenir des informations du robot sur la page web en mode push (à l’initiative du serveur).

var app = require('express')();
var server = require('http').createServer(app);
var io = require('socket.io')(server);

io.on('connection', function(socket){

    socket.on('command', function(data) {
      // data contient les données envoyées depuis la page web
    });

    // Pour envoyer des données au navigateur
    io.sockets.emit("COMMAND", data);
});

// Pour servir la page static, on la place dans un répertoire dédié
app.use(express.static(__dirname + '/../static'));

server.listen(3000);

Placez une un fichier nommé index.html dans le répertoire static.

Pour se connecter l’interface web, il faudra se connecter à l’IP du raspberry sur le port 3000 depuis un navigateur. (http://w.x.y.z:3000/). Votre navigateur affichera le contenu html de votre index.html.
A noter, socket.io sert son client websocket au navigateur de façon transparente.

Votre fichier html de commande peut ressembler à ceci :

[attachment=4794:Sans titre.png]

<html>
<head>
  <meta charset="utf-8">
  <title>Ash Control</title>
  <link rel="icon" type="image/png" href="/raspberry_pi_logo.png">
</head>
<body>
<script src="/jquery-2.1.4.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
KP : <input id="input-kp" value="" /><br/>
KI : <input id="input-ki" value="" /><br/>
KD : <input id="input-kd" value="" /><br/>
<button id="buttonPID">Ok</button><br/>
<button id="buttonStopMotor">Stop moteurs</button><br/>
<button id="buttonStartMotor">Start moteurs</button><br/>
<br/>
<button id="buttonForward">Avancer</button><br/>
<button id="buttonBackward">Reculer</button><br/>
<button id="buttonTurnLeft">Gauche</button><br/>
<button id="buttonTurnRight">Droite</button><br/>
<button id="buttonStop">Stop</button>
<script>
  var socket = io.connect('http://raspberrywlan:8080/');
  socket.on('reset', function (data) {
    $('#input-kp').val(data.kp);
    $('#input-ki').val(data.ki);
    $('#input-kd').val(data.kd);
  });
  $('#buttonPID').click(function() {
    var data = {};
    data.kp = $('#input-kp').val();
    data.ki = $('#input-ki').val();
    data.kd = $('#input-kd').val();
    socket.emit('command', {action: 'changePID', data: data});
  });

  $('#buttonStopMotor').click(function() {
    socket.emit('command', {action: 'stopMotors', data: {}});
  });
  $('#buttonStartMotor').click(function() {
    socket.emit('command', {action: 'startMotors', data: {}});
  });

  $('#buttonForward').click(function() {
    socket.emit('command', {action: 'forward', data: {}});
  });
  $('#buttonBackward').click(function() {
    socket.emit('command', {action: 'backward', data: {}});
  });
  $('#buttonTurnLeft').click(function() {
    socket.emit('command', {action: 'left', data: {}});
  });
  $('#buttonTurnRight').click(function() {
    socket.emit('command', {action: 'right', data: {}});
  });
  $('#buttonStop').click(function() {
    socket.emit('command', {action: 'stop', data: {}});
  });

</script>
</doby>
</html>

Pour plus d’infos sur la websocket : https://github.com/socketio/socket.io
Pour plus d’infos sur express : http://expressjs.com/fr/

NB. Il s’agit d’un embryon de serveur web. Mais suffisant pour les besoins de pilotage d’un robot.

Pour aller plus loin

Le V8 de google : https://fr.wikipedia…eur_JavaScript)
NodeJS : https://nodejs.org/en/about/
Liens utiles NodeJS : https://github.com/s…NodeJS-Learning
Javascript : https://developer.mo…/Web/JavaScript

Mise à jour du 9 décembre 2018

nvm : pour faciliter les mises à jour de nodejs.

pm2 : pour garantir la fiabilité de nodejs.

le module serialport a été mis à jour. Un exemple d’utilisation de la nouvelle version.