Overblog Suivre ce blog
Administration Créer mon blog
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é  :)

 

Repost 0
Published by breizhmakers - dans Arduino ESP8266
commenter cet article
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é

Repost 0
Published by breizhmakers - dans ESP8266
commenter cet article