Overblog
Suivre ce blog Administration + Créer mon blog
26 mai 2017 5 26 /05 /mai /2017 18:04

J'ai fait faire le cadre sur mesure par un encadreur fort recommandable: CCC à Cesson-Sévigné. Il s'agit d'une moulure en chêne à laquelle est collée la boîte arrière, faite en tasseau de 67 x 18.

Comme l'écran exerce un peu de pression sur le miroir j'ai renforcé le collage par 4 fixations métalliques (des obturateurs de slots d'extension de PC!) collées à l'Epoxy aux 4 coins, à la fois sur la partie cadre et sur la partie boîte.

Renforts

La boîte est collée et renforcée avec des équerres vissées. L'extérieur est ciré couleur chêne clair:

L'écran est maintenu en place par des tasseaux vissés ou simplement ajustés pour les deux tasseaux du bas.

J'ai percé des aérations à la scie-cloche. J'y mettrai peut-être des grilles, ou pas :)

Les différents éléments sont maintenus avec de la Patafix. J'aime bien pouvoir démonter facilement en cas de besoin.

J'avais initialement prévu des touches capacitives à travers le miroir, mais d'une part ça ne fonctionne pas toujours correctement, et d'autre part c'est extrêmement salissant!

J'ai donc opté pour des touches sensitives faites avec des attaches parisiennes placées sur la paroi inférieure de la boîte. Pour l'instant j'ai 4 touches avec chacune 2 fonctions (appui court et long) mais je peux encore ajouter 6 touches si besoin.

Arrière

Les attaches parisiennes vues de derrière avec leur connexion vers la carte MPR121:

Et vu de dessous une fois en place:

Touches de commande

Les fonctions de ces touches sont:

  • Touche 1 - court: Profil 'défaut' (modules calendrier, météo, news,  globe system stats affichés)
  • Touche 1 - long: Arrêt du Raspberry Pi
  • Touche 2 - court: Profil 'aéroclub': affichage de la webcam de l'aéroclub d'Avranches
  • Touche 2 - long: Profil 'trafic': affichage du trafic entre la maison et Rennes
  • Touche 3: inutilisé pour l'instant
  • Touche 4 - court: profil 'Kristell': affichage des emails non-lus de ma femme, pour lui éviter de squatter l'ordinateur :)
  • Touche 4 - long: profil 'défaut'

Pour l'instant j'ai abandonné l'utilisation de la reconnaissance vocale offline car j'ai des soucis de déclenchements intempestifs (et je n'ai pas envie d'utiliser les services de google ou autre pour cela, pas envie d'être écouté 24h/24).

J'ai dû déplacer le bloc d'alim du Raspberry Pi car il causait des déclenchements intempestifs de la touche 4.

Je vais ajouter des fonctionnalités petit à petit, mais c'est déjà sympa comme ça.

Un point important si on utilise un miroir sans tain comme moi, c'est qu'il vaut vraiment mieux avoir un écran à LED plutôt que LCD, avec une forte luminosité et un fort taux de contraste afin d'avoir une bonne visibilité dans une pièce éclairée et un fond bien noir. Mon écran LCD d'occasion malgré des specs décentes a un rétro-éclairage qui se voit à travers le miroir dans une pièce sombre, avec une visibilité limite dans une pièce très éclairée. Ca rend l'affichage très discret et plutôt joli mais on aimerait parfois un peu plus de luminosité.

Le miroir une fois en place dans ma salle à manger:

 

Enfin voilà, concernant le budget total, en gros ça se répartit ainsi:

  • Miroir sans tain de 500 x 310 mm: 95 €
  • Cadre sur mesure: 40 €
  • Bois et fournitures pour la boîte: 20 €
  • Raspberry Pi 3 + caméra PI + carte mémoire et alimentation + câble DVI-HDMI: 60 €
  • Carte capteur capacitif MPR121: 15€

Soit 230 €, en gros entre 200 et 250 € suivant la quantité de pièces de récupération (je n'ai pas compté le coût d'impression 3D du support du raspberry Pi et du cache que j'ai mis sur l'intérieur en bas du miroir).

 

Partager cet article
Repost0
10 mai 2017 3 10 /05 /mai /2017 21:15
Le Projet

Ca faisait longtemps déjà que j'avais envie d'un Miroir Magique, dit aussi Miroir Intelligent.

J'ai enfin le temps de m'y consacrer :)

L'idée est donc d'avoir un miroir semi-transparent qui fournit tout un tas de services, le plus intelligemment possible :)

Parmi ce que j'envisage:

  • Affichage d'un calendrier, de l'heure, des prévisions météo
  • Affichage des emails non-lus de ma femme (sur commande)
  • Affichage automatique du trafic entre chez moi et mon lieu de travail (Rennes) le matin (avec affichage sur demande aussi)
  • Affichage de la webcam de l'aéroclub et du flux vidéo venant de l'imprimante 3D.
  • Flux de news
  • Commande vocale
  • Commande tactile
  • Détection de présence via la caméra du Raspberry Pi (voire même détection de visage).

J'ai trouvé un écran d'occasion aux dimensions idéales, et doté de haut-parleurs intégrés, ce qui va me permettre de diffuser des notifications ou messages audio facilement.

J'ai rapidement désossé l'écran, pour constater que je vais devoir garder le cadre arrière en plastique, car toute l'électronique ainsi que les haut-parleurs y sont fixés. C'est pas bien grave, le miroir sera juste un peu plus épais.

 

Le cadre et le miroir sans tain

Les dimensions intérieures du cadre sont 500x310mm. 

N'étant pas équipé pour faire des découpes propres à 45°, je vais le faire faire sur mesure. Reste à voir si la partie "boite" sera faite avec ou si je le fais moi-même.

Concernant le miroir sans tain, dit aussi miroir espion.

Après avoir contacté une miroiterie qui pour une raison étrange ne prenait de devis ni par téléphone ni par email (?!) j'ai suivi les avis déposé sur Google pour Glasren, et je n'ai pas été déçu. Devis rapide et la commande devrait être prête d'ici quelques jours. Bon j'en ai quand même pour 95€ mais le résultat devrait être infiniment mieux qu'un film réfléchisant ou qu'un miroir acrylique.

Je prévois de percer des aérations sur les tranches haute et basse et d'y mettre une grille imprimée à la maison.

Le tout sera fixé au mur via un rail de fixation pour tableau lourd trouvé chez BricoDépôt.

L'Electronique

Le coeur du miroir est un Raspberry Pi 3 équipée de la caméra qui va avec.

Elle sera logée juste sous l'écran, j'ai réservé une zone de 5cm à cet effet.

De part et d'autre de la caméra seront disposées des touches capacitives reliées à une carte MPR121 de chez Sparkfun. Ces touches seront invisibles côté miroir et permettront de commander l'affichage des différents modules (en plus de la commande vocale). Les tests de faisabilité ont montré que la détection est OK à travers une plaque de verre.

Tests de faisabilité des touches capacitives:

J'ai aussi un mini-micro USB pour la commande vocale. Il sera placé près d'une grille d'aération.

La partie logicielle

Le logiciel est construit autour de l'excellent MagicMirror²

C'est en quelque sorte un framework pour développer un miroir magique.

Il dispose de tout un tas de modules et est assez facilement extensible pour peu qu'on connaisse le développement web (HTML/Javascript/CSS et NodeJS).

J'ai d'ailleurs contribué un module qui permet d'utiliser le gestionnaire de touches capacitives MPR121: MMM-MPR121. J'ai aussi contribué à la francisation d'autres modules.

J'ai suivi la procédure décrite ici à la lettre, et ça a fonctionné du premier coup.

A l'heure actuelle, la partie logicielle est quasiment terminée. Il ne reste que le paramétrage final à faire une fois le tout intégré avec l'écran final et les touches capacitives.

Le miroir répond à la voix et affiche les modules en fonction.

Les 2 touches capacitives actuellement câblées permettent de réafficher le profil par défaut, d'éteindre l'écran et d'éteindre le raspberry PI. (on a deux fonction par touche: un appui court  ou long).

Les modules que j'utilise, hormis ceux installés par défaut sont:

  • googlemap
  • iFrame
  • MMM-Globe
  • MMM-ModuleScheduler
  • MMM-ProfileSwitcher
  • MMM-MPR121
  • MMM-Remote-Control
  • MMM-SystemStats
  • motiondetector
  • voicecontrol

 

 

Partager cet article
Repost0
29 avril 2017 6 29 /04 /avril /2017 21:30
Récupération des images

J'ai fait un script grabPicture.sh qui récupère une image issue d'une webcam distante, et le stocke avec un timestamp dans un dossier:

#!/bin/bash

timestamp() {
  date +"%Y-%m-%d_%H-%M-%S"
}

filename="webcam_$(timestamp).jpg"

curl http://monServeur/webcam.jpg > /home/pat/timelapse/images/$filename

Ce script est appelé toutes les minutes via cron.

Editer la crontab de l'utilisateur par un crontab -e et saisir quelque chose comme:

* * * * * /home/pat/timelapse/grabPicture.sh

Affichage du timelapse

J'ai fait un script playJPegAnimation.sh qui utilise mplayer pour jouer les images capturées. Dans ce script il faut adapter la date des fichiers à jouer, et eventuellement la vitesse (ici à 12 images par seconde):

#!/bin/sh
today() {
  date +"%Y-%m-%d"
}

mplayer mf:///home/pat/timelapse/images/webcam_$(today)*.jpg -mf fps=12

Conversion du timelapse en vidéo:

Pour pouvoir uploader ce timelapse sur youtube par exemple, j'ai fait un script encodeTimeLapse.sh qui utilise mencoder pour l'encodage vidéo:

#!/bin/sh
today() {
  date +"%Y-%m-%d"
}

ffmpeg -y -nostats -loglevel 0 -pattern_type glob -i "/home/pat/timelapse/images/webcam_$(today)*.jpg" -r 12 -s hd720 -vcodec libx264 -crf 18 -preset slow /root/timelapse/videos/timelapse.mp4

 

Après import dans un logiciel de montage vidéo, ajout d'une musique (libre de droits), voilà ce que ça donne:

 

On peut aussi ajouter la musique directement lors de la création de la vidéo par ffmpeg:

#!/bin/sh
today() {
  date +"%Y-%m-%d"
}

ffmpeg -y -pattern_type glob -i "/home/pat/timelapse/images/webcam_$(today)*.jpg" -i "/home/pat/timelapse/audio/La-voyageuse.mp3" -shortest -r 12 -s hd720 -vcodec libx264 -crf 18 -preset slow -pix_fmt yuv420p /home/pat/timelapse/videos/timelapse_$(today).mp4

 

Partager cet article
Repost0
24 février 2017 5 24 /02 /février /2017 11:58

Une fois encore l'inspiration m'est venue en lisant l'excellente revue Hackable. Et une fois encore je me suis dis que c'était sympa mais que je pouvais faire mieux ;)

J'ai donc décidé de restaurer deux lampes à pétrole données par un ami à qui j'avais eu la bonne idée de faire part de mon projet ;) avec les objectifs suivants:

  • Aucune consommation d'énergie quand la lampe est éteinte
  • Différents modes d'éclairages
  • Réutilisation de la commande de la mèche de la lampe pour régler l'intensité et/ou la couleur de la lampe.
  • Réutilisation (quand c'est possible) du bouchon du réservoir pour changer de mode d'éclairage.

J'ai une grande et une petite lampe. J'ai rapidement vu que je ne pourrai pas loger le contacteur rotatif à 2x6 contacts sous le bouchon du réservoir de la petite. La petite lampe utilisera donc le poussoir de l'encodeur rotatif pour changer de mode d'éclairage. L'encodeur rotatif étant accouplé à la commande de la mèche (plus de détails plus loin).

Côté éclairage, j'ai commandé des couronnes de LED WS2812B de différents diamètres qui feront très bien l'affaire.

Côté micro-contrôleur, l'ATMega328P est tout indiqué. Il me reste encore quelques unes des géniales cartes Evil-Mad Scientist qui sont tout à fait adaptées à ce besoin. C'est bien plus propre qu'une plaque à trous et bien plus simple que de devoir faire son propre circuit imprimé.

Les différents modes d'éclairage que j'ai envie de proposer sont:

  • Eclairage fixe et réglage de l'intensité via la molette
  • Eclairage fixe et réglage de la couleur via la molette
  • Eclairage "arc-en-ciel" fixe et réglage de l'intensité via la molette
  • Eclairage "arc-en-ciel" rotatif et réglage de l'intensité via la molette
  • Eclairage "simulateur de flamme"

 

Lanternes brutes avant restauration

Lanternes brutes avant restauration

Schéma électrique de la grande lanterne (avec contacteur rotatif):

Schéma électrique de la petite lanterne (sans contacteur rotatif):

Manette de commande

J'ai eu pas mal de boulot côté hardware: découpes au Dremmel, modélisation et impression 3D.

Il a tout d'abord fallu démonter le mécanisme d'alimentation de la mèche et trouver où placer l'encodeur rotatif en prolongement avec l'axe du mécanisme. Le disque de découpe du Dremmel fut un outil précieux dans cette tâche!

Pour coupler la manette avec l'encodeur j'ai essayé deux méthodes:

Pour la première lanterne où j'avais pas mal de place j'ai utilisé du plastique EasyPlast (qui fond à 60°) trempé dans de l'eau chaude puis modelé à la main et au pistolet à air chaud. Ce plastique est  quand même vachement pratique car ça permet modeler à la main des pièces très solides après refroidissement.

 

 

Pour la seconde j'ai usiné le bout de la manette pour qu'il entre à forcer dans l'encoche de l'axe de l'encodeur rotatif.

 

 

 

 


 

Support de LEDs et Diffuseur

J'ai modélisé un support paramétrable pour les anneaux de LED (avec OpenScad) afin de pouvoir poser l'anneau à plat tout en ménageant un espace pour les soudures et les câbles.

Le modèle est disponible dans le dépôt github donné à la fin de ce billet.

 

Il m'a fallu aussi modéliser un diffuseur. J'ai fait pour cela un modèle paramétrique dans OpenScad qui permet de modéliser une calotte sphérique avec un diamètre, une hauteur et une épaisseur paramétrable. J'ai ainsi imprimé des diffuseurs adaptés à chaque lampe, en PLA transparent.

 

Support de la carte électronique

J'ai aussi eu besoin d'un support pour fixer la carte électronique au fond du réservoir.

Là j'ai opté pour Fusion360 qui m'est bien plus simple à utiliser pour ce genre de pièce (le modèle est disponible dans le dépôt github).

J'ai imprimé un support adapté à chaque taille de lampe, fixé par deux vis dans une découpe du fond du réservoir.

Support de la petite lanterne:

 

Fixation du contacteur rotatif

Après avoir réfléchi à plusieurs solutions pour fixer le contacteur rotatif sous le bouchon de réservoir de la grande lanterne j'ai fini par modéliser une bride de fixation (avec Fusion360) sur laquelle le contacteur est vissé, et qui est collée à la colle époxy à l'intérieur du réservoir.

Modèle de la bride de fixation du contacteur

bride collée

 

J'ai aussi usiné un méplat sur l'axe du contacteur.

Enfin concernant le bouchon du réservoir, j'ai dû meuler son pas de vis, percer un trou en son centre et y faire un méplat en collant une barrette métallique à la colle époxy.

Il ne restait plus qu'à faire la partie logicielle :)

 

Logiciel

Côté logiciel j'ai commencé par utiliser la librairie WS2812FX, qui utilise la librairie Neopixel d'Adafruit. Elle n'est pas mal, elle dispose de plein d'effets, mais rien qui ne me convenait.

C'était une bonne occasion d'utiliser la librairie FastLED dont j'avais entendu parler par ailleurs. Elle est relativement bien documentée mais surtout bien plus compacte (et très portable).

J'ai pu sans problème faire ce dont j'avais besoin.

Pour la partie simulateur de flamme, j'ai fait plusieurs essais pas vraiment convaincants et je suis revenu à celui que je préfère pour ce genre d'usage: Celui-ci. Je l'ai simplement adapté pour utiliser FastLED pour piloter mes LED et pour permettre de sortir facilement d'une boucle de simulation quand on change de mode d'éclairage.

C'était la première fois que j'utilisais un encodeur rotatif. J'ai essayé diverses librairies pour finalement retenir celle de PJRC. Elle a l'avantage de pouvoir fonctionner avec ou sans interruptions, d'être simple à mettre en oeuvre et de permettre d'initialiser la valeur que manipule l'encodeur.

Les valeurs de luminosité, de couleur, et de mode (pour la petite lanterne uniquement) sont stockées dans l'EEPROM afin que la lampe se rallume dans le même état qu'elle s'est éteinte (les valeur ne sont écrites qu'en cas de modification).

Le code est commenté, il ne devrait pas poser de problème à ceux qui souhaiteraient le réutiliser.

Le code, le schéma électrique et les modèles 3D sont disponibles sur ce dépôt github.

 

Le résultat final:

 

Partager cet article
Repost0
23 février 2017 4 23 /02 /février /2017 18:15
Partager cet article
Repost0
18 février 2017 6 18 /02 /février /2017 14:11

Ma femme souhaitait disposer d'un sonomètre lumineux afin que ses élèves voient vraiment combien ils sont parfois bruyants :)

J'ai un Sonomètre WENSN WS1361 déjà mis en oeuvre sur un autre projet.

L'idée là est d'avoir un bargraphe lumineux, avec seuil maximal réglable, et la valeur de pic qui reste allumée pendant plusieurs secondes.

Du côté du logiciel, j'ai réutilisé le code Python permettant d'accéder au Sonomètre, en l'adaptant à mon besoin.

Le bargraphe est constitué de deux barres de LED WS2812B (NeoPixel) et piloté via l'excellente librairie  rpi_ws281x. Un exemple d'utilisation est donné sur le site de Adafruit.

Attention: Avec mon RPi3 j'ai dû désactiver l'audio sur le HDMi car ça empêchait le fonctionnement correct de la librairie. Il faut pour cela commenter/ajouter les lignes suivantes au fichier /boot/config.txt.(plus d'infos ici):

#dtparam=audio=on
hdmi_force_hotplug=1
hdmi_force_edid_audio=1

 

Concernant le réglage du seuil maximal, le RPi ne dispose malheureusement pas d'entrée analogique. Comme je voulais garder le montage le plus simple possible, j'ai eu recours à un montage à base de filtre R variable / C branché sur une sortie et une entrée numérique. La mesure de la position du potentiomètre se traduit par une mesure de temps de charge du condensateur, qui lui est proportionnel. C'est rustique et relativement peu précis, mais très suffisant pour l'usage envisagé. Le code dont je me suis inspiré est celui-ci.

J'ai ensuite fait de ce script Python un service SystemD afin qu'il démarre automatiquement au boot. Je me suis inspiré de cet article.

Tout le code est disponible sur ce dépôt GitHub.

Premier prototype, sans boîtier ni diffuseur:

Prototype sans boitier

Côté Hardware j'ai imprimé un boitier pour RPi3 trouvé sur Thingiverse, que j'ai ensuite usiné au Dremmel pour y ménager les ouvertures pour passe les câbles et l'axe du potentiomètre de réglage. 

Le diffuseur est constitué d'un morceau de plastique translucide de récupération sur lequel j'ai collé à la colle chaude les 2 barres de 8 LED Neopixel ainsi que l'adaptateur de niveau 3.3v vers 5v

La base du diffuseur a été modélisée sur Fusion360 (mon outil préféré, avec Openscad pour les pièces paramétriques) et imprimée à la maison.

 

 

 

 

 

 

 

 

 

 

 

 

Le sonomètre lumineux terminé, alimenté par une batterie:

Sonomètre lumineux terminé

 

Le système est depuis utilisé couramment en classe et fait l'unanimité chez les professeurs! :)

J'ai beaucoup de demande et je commence à réfléchir à une version plus compacte et ne nécessitant pas un sonomètre externe.

 

Une petite vidéo de démonstration: 

Partager cet article
Repost0
1 novembre 2016 2 01 /11 /novembre /2016 13:15

Cette année j'avais envie de faire un truc bruyant et lumineux pour Halloween. Et forcément, ça devait faire peur :)

Je me suis d'abord mis en quête de sons d'Halloween effrayants, et j'ai trouvé mon bonheur en quelques minutes.

La partie logicielle principale est  faite en Processing. Un outil vraiment très sympa pour du développement rapide et facile (ainsi que pour l'apprentissage de l'informatique sous une forme créative). 

Reste ensuite à savoir comment déclencher le truc via un appui sur la sonnette et à transmettre l'info au code en Processing.

Le moyen le plus simple est d'utiliser le réseau Wifi de la maison pour cela. Ca tombe bien j'ai un ESP8266 sous la main. Le truc a l'avantage d'avoir une carte WIFI intégrée et se programme en C facilement avec l'IDE Arduino. J'ai prévu un circuit électrique isolé via un optocoupleur, passant par le bouton de la sonnette car avant d'ouvrir le boitier je pensais que le bouton était alimenté en 230V, alors qu'il est en fait alimenté en 9V alternatif.  J'ai donc temporairement débranché l'inter de son transformateur pour l'insérer dans mon circuit, via une alimentation de récup' en 7.5V continu.

Lorsqu'on appuie sur la sonnette, ça envoie simplement un caractère via une socket réseau au programme en Procesing qui tourne dans le garage.

Le code dans l'ESP est le suivant:

 

 

/**
* "interrupteur sans fil" rudimentaire.
**/
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>

//pin mapping
#define D0 16
#define D1 5
#define D2 4
#define D3 0
#define D4 2
#define D5 14
#define D6 12
#define D7 13
#define D8 15
#define D9 3
#define D10 1

#define DEBUG
#define PIN_LED D10
#define PIN_BUTTON D5

//wifi network name
const char* ssid = "monRéseau";
//wifi network password
const char* password = "monPwd";

ESP8266WiFiMulti WiFiMulti;
WiFiClient client;

void connectWifi(){
#ifdef DEBUG
Serial.print("Connecting to ");
Serial.println(ssid);
#endif
WiFiMulti.addAP(ssid,password);
delay(500);
while (WiFiMulti.run() != WL_CONNECTED) {
delay(500);
}
#ifdef DEBUG
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
#endif
}

void disconnectWifi(){
#ifdef DEBUG
Serial.println("Disconnecting Wifi");
#endif
WiFi.disconnect();
}

void setup() {
#ifdef DEBUG
Serial.begin(9600);
while (!Serial); // wait for serial attach
delay(5000);
digitalWrite(PIN_LED,HIGH);
delay(250);
digitalWrite(PIN_LED,LOW);
#endif

connectWifi();
}

//Connexion au PC distant, provoque le démarrage de l'animation
void sendCommand(){
if (!client.connect("192.168.1.12", 12345)) {
Serial.println("connection failed");
return;
}
client.print(65);
client.flush();
client.stop();
}

void loop() {
if(digitalRead(PIN_BUTTON)){
Serial.println("bouton!");
digitalWrite(PIN_LED,HIGH);
sendCommand();
digitalWrite(PIN_LED,LOW);
//simple debouncing
delay(100);
while(digitalRead(PIN_BUTTON));
}
delay(50);
}


Côté Processing il s'agit de jouer des sons associés à des animations lumineuses de circonstance.

Sur le "rire machiavélique" j'ai rapidement eu l'idée d'un fade-in & fade-out entre le rouge et le noir, avec en surimpression une tête de monstre qui apparaîtrait à travers une des fenêtres du garage.

Sur le cri du monstre l'idée était de surprendre et de faire peur et j'ai pas mal tâtonné pour arriver à faire une succession de flashes blanc, noir et rouges à une fréquence variable et aléatoire.

L'animation est déclenchée par un clic (c'est pratique pour tester) et lors de la connexion de l'interrupteur wifi, lorsque quelqu'un sonne à la porte.

Le code est celui-ci:

import processing.net.*;

import ddf.minim.spi.*;
import ddf.minim.signals.*;
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.ugens.*;
import ddf.minim.effects.*;

Minim minim;
AudioPlayer player;
AudioInput input;
Server srv;
Client client;
boolean pressed=false;
int anim=0;
int count=0;
int speed=2;
PImage img;

//dessine le monstre à la bonne taille & position
void drawMonster(){
float scaleFactor = 0.45;
int x = 86;
int y = height - int(img.height * 0.7) ;
image(img,x,y,img.width*scaleFactor,img.width*scaleFactor);
}

void setup()
{
fullScreen();
frameRate(50);
background(#000000);

img = loadImage("monster.png");
minim = new Minim(this);
//se met en attente des conexions de l'interrupteur wifi
srv = new Server(this, 12345);
noStroke();
}

//boucle principale
void draw(){
if (keyPressed == true && pressed == false) {
mousePressed();
pressed = true;
}
if(keyPressed == false){
pressed = false;
}
client = srv.available();
if (client != null) {
client.read();
mousePressed();
}

//séquencement des deux animations
switch (anim){
case 1:
anim1();
break;
case 2:
anim2();
break;
}

count++;
}

void mousePressed()
{
if(anim==0){
count=0;
anim=1;
}
}

//première animation: monstre & flashes
void anim1(){
if(player == null){
player = minim.loadFile("Monster.mp3");
player.play();
}
int modulo = count % speed;
if(modulo >= 0 && modulo <= 1)
background(modulo ==0?#FFFFFF:#FF5555);
else
background(#000000);

speed = floor(count/50)+2;
if(count >= 7.5*50){
anim=2;
count=0;
player = null;
}
}

//seconde animation: rire machiavélique & fade-in-out en rouge
void anim2(){
if(player == null){
player = minim.loadFile("Evil_Laugh.mp3");
player.play();
}
int modulo = count % 1;
if(modulo == 0){
colorMode(HSB, 100);
int b = round(abs(sin(((float)count/1.5)/360*TWO_PI)*100));
background(color(0,100,b));
}
if(count >= 9.5*50){
background(#000000);
anim=0;
player=null;
}
drawMonster();
}

Pour ajouter plus de réalisme à la chose je voulais faire bouger la porte du garage en même temps que le monstre rugit.

N'ayant pas de moteur électrique assez puissant sous la main pour faire un truc qui cogne fort contre la porte, j'ai pris le plus gros moteur pas à pas que j'avais, récupéré sur un photocopieur.

Son rôle est de tirer à plusieurs reprises sur la porte du garage pour qu'elle bouge comme si on essayait de l'ouvrir.

Il est commandé par un Arduino. La séquence de mouvements de déclenche par un capteur de luminosité (dès que les flashes de l'animation démarrent). Le capteur s'auto-calibre au démarrage du sketch et démarre les mouvements si la luminosité mesurée dépasse le seuil déterminé au démarrage de plus de 15%. Ca permet de rendre le truc relativement indépendant de la luminosité ambiante.

 

 

Le code est le suivant:

#include <Stepper.h>

#define INITIAL_DELAY 2000
#define PIN_LDR A0
#define THRESHOLD_FACTOR 1.15

const int stepsPerRevolution = 200;
Stepper myStepper(stepsPerRevolution, 2, 3, 4, 5);
int light=0;
int threshold=0;
int running=0;

//auto-calibrage de la luminosité ambiante au démarrage
void calibrate(){
delay(150);
threshold = analogRead(PIN_LDR);
}

//désactive toutes les sorties pilotant le moteur pas à pas
void disableOutputs(){
digitalWrite(2,LOW);
digitalWrite(3,LOW);
digitalWrite(4,LOW);
digitalWrite(5,LOW);
}

void setup() {
myStepper.setSpeed(200);
calibrate();
}

//boucle principale
void loop() {
if(analogRead(PIN_LDR) > threshold * THRESHOLD_FACTOR){
running = 1;
}
if(running){
delay(INITIAL_DELAY);
myStepper.step(stepsPerRevolution*3+4);
disableOutputs();
delay(1500);
myStepper.step(stepsPerRevolution+4);
disableOutputs();
running = 0;
}
}

Un résumé en images.

Le truc a bien fonctionné  :)

 

Partager cet article
Repost0
19 juin 2016 7 19 /06 /juin /2016 22:00

Depuis que j'ai découvert le bac à sable "augmenté" de O Kreylos j'ai eu envie d'en faire un.

Il se trouve que le chercheur auteur du site et du logiciel a eu la bonne idée de mettre le logiciel en open-source, et surtout de documenter la procédure d'installation et de calibrage d'une façon exceptionnelle. Ca simplifie grandement la mise en oeuvre!

J'ai modifié cette procédure pour l'adapter à mon cas d'usage où l'image projetée n'est pas exactement de la taille du bac à sable. Je ne vais pas reprendre ici tous les détails de la procédure "officielle", extrêmement bien documentée ici, mais seulement donner quelques explications complémentaires.

Pré-requis
  • Il faut impérativement un ordinateur équipé d'une carte graphique nVidia (une carte moyen de gamme suffit).
  • Le système d'exploitation requis est Linux. J'ai choisi la version Linux Mint recommandée, sur laquelle j'ai ensuite installé le pilote graphique nVidia propriétaire requis.
  • Une Kinect Version 1 et un adaptateur pour PC (disponible pour une quinzaine d'euros sur ebay par exemple) afin de pouvoir la brancher sur un port USB.
  • Le vidéoprojecteur utilisé doit être HDMI, de préférence avec une résolution supérieure ou égale à 1024 x 768 pixels, et avec une courte focale, afin de pouvoir avoir une grande image sans trop d'éloignement.
Construction du bac à sable

Aucune indication n'est donnée quant à la construction du bac à sable et du support de projecteur.

La dimension recommandée est d'environ 1m x 0.75m sur 15cm de profondeur.

Je recommande plutôt de mesurer la taille de l'image obtenue avec le projecteur souhaité à une distance de 1.20m environ et de construire le bac à sable à cette dimension.

Mon bac à sable fait donc 1m x 0.75m sur 17.5cm de profondeur.

Il est assemblé à partir de planches découpées et d'un fond de contreplaqué de 15mm d'épaisseur.

Les angles des planches sont "crantés", c'est un peu galère à découper au ciseau à bois mais je trouve que ça offre la meilleure solidité. Le tout est collé et vissé.

J'ai ajouté 4 poignées métalliques pour faciliter le transport.

Le support de vidéoprojecteur est construit à base de profilé en U de 1m de longueur et 20mm de côté et d'équerres métalliques vissées sur ce profilé.

Je conseille plutôt du profilé rectangulaire assez solide (genre 20mm x 35mm) car mon support, bien que solide, est un peu trop souple et oscille quand on le touche.

Le vidéoprojecteur est posé sur une planche en médium.

Le miroir est quant à lui un simple miroir de salle de bain, avec des supports en tasseau, des petites équerres-butées et quelques points de colle chaude. J'avais initialement mis le miroir à 45° mais avec mon projecteur il vaut mieux quelques degrés de plus pour avoir une image centrée.

Enfin le support de la Kinect est constitué de 4 équerres collées à la super-glu sur les profilés alu et de colliers auto-serrants. Il y a juste assez de jeu pour pouvoir enlever la Kinect en la faisant glisser dans son support.

Là encore, en l'absence de documentation sur comment construire un support optimal, cette solution n'est pas la meilleure, car la Kinect ne voit pas tout à faire tout le bac à sable. Il vaudrait mieux soit augmenter un peu la hauteur du support, soit placer la Kinect sur les profilés plutôt qu'au-dessous tel que je l'ai fait.

En ce qui concerne le sable proprement dit, j'ai utilisé du sable blanc de décoration. Il faut compter entre 50 et 75 Kg de sable.

Bac à Sable augmenté DIY, V1 avec un projecteur monté horizontalement

Bac à Sable augmenté DIY, V1 avec un projecteur monté horizontalement

Bac à Sable augmenté DIY, V2 avec un projecteur monté verticalement

Bac à Sable augmenté DIY, V2 avec un projecteur monté verticalement

Désactivation de Secure Boot 

Si comme moi vous avez un ordinateur sous Windows 10 avec un boot UEFI avec Secure Boot activé, il faut le désactiver sinon le pilote Linux de la carte graphique NVidia refusera de se charger.

Pour cela, aller dans les paramètres UEFI (shift + reboot puis suivre les menus) et une fois dans le menu de paramétrage UEFI:

  • Affecter un Manager Password (et notez le!)
  • Vous pourrez alors désactiver Secure Boot (sans Manager Password on ne peut pas le désactiver)
Installation des logiciels et calibrage

L'installation et la compilation des logiciels n'est pas vraiment problématique, même s'il vaut mieux connaitre un peu Linux pour être plus à l'aise.

La procédure de calibration, bien que très bien documentée (pour qui lit et comprend l'anglais) est plus compliquée. Enfin il ne m'a quand même fallu que deux tentatives pour que ça fonctionne parfaitement.

J'ai suivi la procédure décrite ici avec des ajustements car l'image de mon projecteur ne couvre pas tout le bac à sable.

Une info importante qui n'est documentée que dans les commentaires de la vidéo:

  • Pour déplacer l'image dans RawKinectViewer: il faut maintenir la touche z et déplacer la souris.
  • Pour zoomer, il faut utiliser le zoom à deux doigts sur le touchpad.

Cette vidéo décrit bien tout le process en détail.

Quelques infos en vrac, qui peuvent être utiles (elles sont expliquées dans la vidéo):

  • Pour assigner un bouton à une fonction il faut appuyer sur un bouton puis avec la souris sélectionner la fonction qu'on désire assigner.
  • Quand on lance CalibrateProjector, il faut aller sur sa fenêtre (projecteur) et cliquer avec le bouton de gauche une fois, avant de pouvoir faire la procédure de calibration

Les étapes que j'ai adaptées à mon cas:

A l'étape 7, "calculate your sandbox base plane", j'ai simplement pris des mesures sur une zone plus petite que celle indiquée afin d'être sûr de ne mesurer que le fond du bac à sable (cette étape établit une équation du plan du fond du bac à sable par rapport à la Kinect).

A l'étape 8, "Measure the 3D extents of the sand surface", comme j'avais du mal à déterminer les limites de l'image sur la vue "depth image" de la Kinect, j'ai procédé ainsi:

J'ai utilisé un cylindre de 10cm de hauteur et 2 cm de diamètre, que j'ai placé pour chaque mesure, sur le coin dont on mesure les coordonnées (dans l'ordre spécifié par la doc, c'est indispensable).

Une fois les mesures sur les 4 coins réalisées, j'ai simplement ajouté 10 aux mesures de profondeur avant de les copier dans le fichier src/SARndbox/etc/SARndbox-2.8/BoxLayout.txt

ATTENTION: L'ordre des points de mesure doit absolument être: bas-gauche, bas-droite, haut-gauche, haut-droite, dans les positions telles qu'elles apparaissent dans RawKinectViewer (et non sur le bac à sable lui-même. Par ex avec ma position de Kinect (inversée), les positions sur le bac sont toutes autres que celles montrées par RawKinectViewer)

Après avoir complété le fichier src/SARndbox/etc/SARndbox-2.8/BoxLayout.txt avec les infos précédentes, le plus dur est fait.

Il reste alors à faire l'outil de calibrage indiqué, à l'aide d'un vieux CD, un peu de papier et de fil de fer, puis à procéder à la dernière partie du calibrage consistant à calibrer l'image vue par la Kinect et celle affichée par le projecteur (étape 10).

Une fois cette étape effectuée il ne reste plus qu'à lancer l'application SARndbox et à profiter de la magie d'un bac à sable augmenté!

~/src/SARndbox/bin/SARndbox -uhm -fpv -evr -0.002

 

-evr 0.002 est le taux d'évaporation de l'eau (s'il est jugé trop rapide on peut le diviser par deux, ou inversement s'il est trop lent).

 

Post-Configuration

J'ai ensuite suivi la procédure décrite ici pour que la sandbox passe automatiquement en plein écran et pour affecter la touche R à la pluie et D à l'assèchement (Rain / Drain).

Mon fichier SARndbox.cfg est le suivant:

section Vrui
    section Desktop
        section MouseAdapter
            mouseIdleTimeout 5.0
        endsection
        
        section Window
            windowFullscreen true
        endsection
        
        section Tools
            section DefaultTools
                section WaterTool
                    toolClass GlobalWaterTool
                    bindings ((Mouse, r, d))
                endsection
            endsection
        endsection
    endsection
endsection

 

Boutons pour faire pleuvoir et assécher

Je n'ai pas documenté cette partie, mais les boutons sont connectés à une carte à base de microcontroleur ATMega 32U4 Mini, genre ceci.

Cette carte est simplement programmée avec un sketch d'émulation de clavier USB et envoie les touches R et D selon le bouton pressé.

Le code est rudimentaire:

#include <Keyboard.h>
#include <Bounce2.h>

#define BUTTON_PIN_1 A0
#define BUTTON_PIN_2 A1
#define DEBOUNCE_INTERVAL 5

#define CHAR_1 'r'
#define CHAR_2 'd' 

Bounce debouncer1 = Bounce(); 
Bounce debouncer2 = Bounce(); 

void setup() {
  pinMode(BUTTON_PIN_1,INPUT_PULLUP);
  debouncer1.attach(BUTTON_PIN_1);
  debouncer1.interval(DEBOUNCE_INTERVAL);

  pinMode(BUTTON_PIN_2,INPUT_PULLUP);
  debouncer2.attach(BUTTON_PIN_2);
  debouncer2.interval(DEBOUNCE_INTERVAL);

  Keyboard.begin();
}

void loop() {
  debouncer1.update();
  debouncer2.update();

  if(debouncer1.read() == LOW){
    Keyboard.press(CHAR_1);
  }
  if(debouncer1.rose()){
    Keyboard.release(CHAR_1);
  }
  if(debouncer2.read() == LOW){
    Keyboard.press(CHAR_2);
  }
  if(debouncer2.rose()){
    Keyboard.release(CHAR_2);
  }
}

 

Partager cet article
Repost0
31 mai 2016 2 31 /05 /mai /2016 20:30
Un Sonomètre connecté en OpenTSDB

Ayant fait l'acquisition d'un Sonomètre WENSN WS1361 j'ai rapidement cherché à en exploiter les mesures automatiquement.

Après une tentative infructueuse d'utiliser sa sortie analogique, au comportement instable et pour le moins étrange, j'ai préféré utiliser son port USB.

Dans ce monde merveilleux de l'open-source il se trouve que quelqu'un a eu la bonne idée de faire un reverse-engineering du protocole de ce sonomètre et de mettre gracieusement le script python à disposition.

OVH met gratuitement à disposition un serveur OpenTSDB ainsi que des exemples de code dans différents langages, dont Python.

Après quelques adaptations à Python 3 on a une API fonctionnelle de haut niveau, très simple à utiliser.

Le script envoie une mesure toutes les 5 minutes. C'est bien sûr paramétrable tel qu'indiqué dans le README.md du dépôt github.

Cette mesure est une moyenne mobile des 5 dernières minutes. Le capteur fournit, lui, une mesure toutes les 2s, qui est aussi une moyenne mobile sur 2s, envoyée elle aussi une fois toute les 5 minutes et remise à zéro après envoi.

Ces métriques sont envoyée sous les noms:

soundLevel et soundLevel.peak

Le script est lancé au démarrage de l'ordinateur.

Il est possible que je l'installe un jour sur un Raspberry pi.

J'utilise Grafana pour afficher les métriques stockée dans OpenTDSB:

Partager cet article
Repost0
14 mai 2016 6 14 /05 /mai /2016 20:50

Il commence à faire chaud dans mon bureau et dans mes combles et j'avais envie de pouvoir mesurer la température avec plus de précision que le bout de mon nez :)

 

L'ESP8266 est une solution remarquablement adaptée pour cela avec sa connectivité wifi (et HTTPS) intégrée et son faible coût.

J'utilise la version NodeMCU (programmée en C) car pour seulement quelques euros de plus j'ai un port USB et une alimentation 5V via l'USB.

Le capteur de température est un DHT22, bien plus précis qu'un DHT11 et pas beaucoup plus cher.

Carte NodeMCU et capteur DHT22Carte NodeMCU et capteur DHT22

Carte NodeMCU et capteur DHT22

Les broches D0 et Reset sont connectées entre elles afin de permettre à l'ESP8266 de se réveiller correctement après avoir été mis en deep-sleep.

Le DHT22 est connecté sur la broche D4 (GPIO02). Allez comprendre la numérotation des broches!

 

Concernant le stockage et la visualisation des données, j'ai cherché plusieurs solutions.

 

J'ai commencé par jeter un oeil à Thingspeak. C'est très simple à utiliser (un simple post HTTPS (voire HTTP mais c'est pas recommandé niveau sécu) et ça fournit une visualisation rudimentaire, extensible via différents moyens mais pas super bien documentée à mon goût (au choix via MathLab ou via HighCharts et un éditeur javascript/HTML intégré).

 

J'ai découvert il y a peu le PaaS IoT d'OVH (sur leur plateforme de "labs" Runabove).

Ils mettent à disposition un serveur OpenTDSB (protocole open-source de base de données temporelle). Il n'y a pas d'IHM de consultation, mais pour cela on peut utiliser Grafana, qu'il faut alors installer chez soi ou sur un cloud quelconque.

J'ai donc installé Grafana et j'y ai configuré un tableau de bord qui m'affiche des graphes et métriques de température et d'humidité de mes deux thermo-hygromètres connectés (un chez moi et un au boulot).

Voici ce que ça donne par exemple:

 

Le code source est disponible sur mon GitHub.

Il nécessite l'IDE Arduino avec la carte supplémentaire ESP8266.

 

La visualisation des données est faite avec Grafana, qui parle directement au PaaS IoT d'OVH avec le protocole OpenTSDB.

Grafana - Dashboard affichant les mesures de température et d'humidité

Grafana - Dashboard affichant les mesures de température et d'humidité

Partager cet article
Repost0