Overblog Suivre ce blog
Editer l'article 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é  :)

 

Partager cet article

Repost 0
Published by breizhmakers - dans Arduino ESP8266
commenter cet article

commentaires