Dica dia dos namorados nerd

Tá sem ideia do que dar pra namorada?! Faça você mesmo um presente nerd!

Tem gente que se expressa com musicas, outros com pinturas, eu sou razoável na arte de ser nerd :-P. Se você é que nem eu, provavelmente as instruções de como montei o presente de 4 anos de casado pra minha esposa (beijocas pra dona Laila) possa te inspirar!

Quer fazer igual?!?! seguem as instruções:

Você vai precisar:

  • Um pouco de conhecimento de arduino e eletrônica.  Se estiver começando, dê uma olhada nos meus tutoriais aqui.
  • Um baú. Comprei o meu no multi coisas
Veio um dentro do outro. Foi menos que 50 reais. usei o menor de cobaia :-P

Veio um dentro do outro. Foi menos que 50 reais. usei o menor de cobaia 😛

  • Arduino nano (cerca de 15 reais no ebay com frete)
  • Um sensor ultra-sônico (cerca de 3 reais no ebay com frete)
Sensor HC-SR04

Sensor HC-SR04

  • Um motor servo MG995 (começa em 12 reais no ebay)
  • Um mp3 shield (35 reais no ebay)
Mp3 Shield. Existem varias versões, a que eu usei é o da elechouse.

Mp3 Shield. Existem varias versões, a que eu usei é o da elechouse.

  • Uma mini caixa de som (16 reais no dealextreme)
desmontei essa, a Hamburger Style Mini Speaker for Laptop + Cell Phone + MP3 - Black

desmontei essa, a Hamburger Style Mini Speaker for Laptop + Cell Phone + MP3 – Black

  • Um monte de leds

A idéia aqui é simples. Assim que a sua amada colocar a mão próxima do baú, a música começa a tocar, o baú abre e os leds piscam, mostrando a ela o quanto nerd você é e quanto você a ama. (awwwnnn)

Para isso, vamos precisar de duas bibliotecas, uma para o sensor ultra-sônico (NewPing) e outra para o servo (Servo – já vem na IDE do arduino).

Segue o esquema do bau. É importante notar algumas coisas: os leds estão nas portas 3 e 5 pois elas são pwm e conseguem dimmerizar os leds. O mp3 shield se comunica com o arduino através da porta serial, logo você tem que lembrar de tirar a conexão toda vez que vai subir uma nova versão para o arduino.

Esquema de ligações. O arduino nano não tem a mesma disposição de portas, mas é só fazer o dê-para

Esquema de ligações. O arduino nano não tem a mesma disposição de portas, mas é só fazer o dê-para

Agora o código. Veja que implementei uma máquina de estados interpolando a abertura do baú com o tempo da música. Além disso, você vai precisar calibrar os valores de abertura dependendo de como você montou o servo (no meu caso, fechado é 20). No código estão algumas funções de acesso ao mp3 shield, cortesia desse instructable. Note que estou puxando da memória interna do shield, ele tem 60mbits (algo entorno de 7mbytes) e é possível copiar músicas pra lá ligando ele com o cartão sd e segurando o botão clone (veja detalhes no datasheet). O código está comentado, mas se tiverem dúvidas é só perguntarem. (desculpem, mas o wordpress é muito ruim pra mostrar código. Vou ver se migro para uma versão hospedada ou troco de plataforma de blog… aparentemente não é possível copiar código identado)

#include <NewPing.h>
#include <Servo.h>
#include "Blink.h"

#define TRIGGER_PIN 12 // Arduino pin tied to trigger pin on the ultrasonic sensor.
#define ECHO_PIN 11 // Arduino pin tied to echo pin on the ultrasonic sensor.
#define MAX_DISTANCE 200 // Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
//variaveis do MP3 shield
unsigned char Data[10];
unsigned char i;

int treasurePort = 9;
int whiteLedPin = 3;
int yellowGreenLedPin = 5;

Blink white(3);
Blink yg(5);

//representa o sonar
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);

unsigned long state2Init;
int servoAngle;

//guardo as leituras de marcos de tempo
unsigned long firstRead;
unsigned long state3Init = 0;
unsigned long state4Init;

//motores
Servo myservo;

//controle da interrupçao.
int buttonState = 0;

//armazena o estado da aplicacao
int state = 0; //0= leitura, 1=lendo, 2= abrindo, 3= mantendo aberto, 4= fechando

boolean whiteLeds = true;

//estados das piscadas de luzes
int sBIdx = 0;
unsigned long sBIdx0 = 0;
unsigned long sBIdx2 = 0;
unsigned long sBIdx3 = 0;
unsigned long sBIdx4 = 0;
unsigned long sBIdx5 = 0;
unsigned long sBIdx6 = 0;
unsigned long sBIdx7 = 0;
unsigned long lastUpdate = 0;

//velocidade das piscadas
int vel1 = 605;
int int1 = 2420;

int vel2 = 330;
int int2 = 2640;

int vel3 = 150;
int int3 = 1800;

int vel4 = 250;
int int4 = 750;

int vel5 = 750;
int int5 = 1500;

int vel6 = 1000;

void setup() {
 Serial.begin(9600);
 delay (1000);
 //fecha o bau
 myservo.attach(treasurePort);
 servoAngle = 20;
 myservo.write(servoAngle);
 delay(500);
 myservo.detach();
}

//abre o bau
void openTreasure(int perc) {
 int angle = 25 + (int) ((float) perc / 100.0f * 75.0f);
 myservo.write(angle);
}

//alterna entre pause e play
void play_pause() {
 Data[0] = 0x7E; // START
 Data[1] = 0x02; // Length
 Data[2] = 0xA3; // Command
 Data[3] = 0x7E; //Mode parameter
 Command(Data, 4);
}

//seleciona a origem e a faixa de musica a ser tocada
void FileSource(char type, byte track) {
 play_pause(); // Pause
 Data[0] = 0x7E;
 Data[5] = 0x7E;
 switch (type) {
 case 'SD_card':
 // START
 Data[1] = 0x04; // Length
 Data[2] = 0xA0; // Command
 Data[3] = 0x00; // file number high byte
 Data[4] = track; // file number low byte
 break;
 case 'SPI_Flash':
 Data[1] = 0x04; // Length
 Data[2] = 0xA1; // Command spi flash 0XA1
 Data[3] = 0x00; // file number high byte
 Data[4] = track;
 break;
 case 'U_Disk':
 Data[1] = 0x04; // Length
 Data[2] = 0xA2; // Command
 Data[3] = 0x00; // file number high byte
 Data[4] = track; // file number low byte
 break;

 }
 Command(Data, 5);
 play_pause(); // Pause
}

//seta o volume
void SetVolume(int vol) {
 Data[0] = 0x7E; // START
 Data[1] = 0x03; // Length Not 0x02
 Data[2] = 0xA7; // Command
 Data[3] = vol; // new volume
 Data[4] = 0x7E; // END
 Command(Data, 5);
}

//seta o modo de reproducao
void SetPlayMode(char type) {
 Data[0] = 0x7E; // START
 Data[4] = 0x7E; // START
 switch (type) {
 case 'Single_play':
 Data[1] = 0x02; // Length
 Data[2] = 0xA9; // Command
 Data[3] = 0x00; //Mode parameter
 break;
 case 'Repeat_single':
 Data[1] = 0x02; // Length
 Data[2] = 0xA9; // Command
 Data[3] = 0x01; //Mode parameter
 break;
 case 'Repeat_all':
 Data[1] = 0x02; // Length
 Data[2] = 0xA9; // Command
 Data[3] = 0x02; //Mode parameter
 break;
 case 'Play_Random':
 Data[1] = 0x02; // Length
 Data[2] = 0xA9; // Command
 Data[3] = 0x03; //Mode parameter
 break;
 }
 Command(Data, 5);
}

//envia o comando para o mp3 shield
void Command(unsigned char *Data, int length) {
 for (int i = 0; i < length; i++) {
 Serial.write(Data[i]);
 }

}

//reseta o estado da piscada de LED
void resetBlink() {
 sBIdx = 0;
}

//trata a piscada durante a musica
void handleBlink(unsigned long now) {

 if (sBIdx == 0) {
 sBIdx0 = now;
 sBIdx = 1;
 lastUpdate = now - vel6;
 }
 if (sBIdx == 1) {
 unsigned long delta = now - lastUpdate;
 if (delta >= vel6) {
 if (whiteLeds) {
 white.init(now, vel6, 255);
 } else {
 yg.init(now, vel6, 255);
 }
 whiteLeds = !whiteLeds;
 lastUpdate = now;
 }

 }

}

//trata a piscada durante a abertura
//cada estado lida com uma velocidade de piscadas
//para acompanhar a musica
void handleOpeningBlink(unsigned long now) {

 if (sBIdx == 0) {
 sBIdx0 = now;
 sBIdx = 1;

 }
 if (sBIdx == 1) {
 //Serial.println ("1");
 if ((now - sBIdx0) > 1000) {
 sBIdx = 2;
 sBIdx2 = now;
 lastUpdate = now - vel1;

 }

 }

 if (sBIdx == 2) {
 // Serial.println ("3");
 unsigned long delta = now - lastUpdate;
 if (delta >= vel1) {
 if (whiteLeds) {
 white.init(now, vel1, 255);
 } else {
 yg.init(now, vel1, 255);
 }
 whiteLeds = !whiteLeds;
 lastUpdate = now;
 }
 if ((now - sBIdx2) > int1) {
 sBIdx = 3;
 sBIdx3 = now;
 lastUpdate = now - vel2;
 }

 }
 if (sBIdx == 3) {
 //Serial.println ("5");
 unsigned long delta = now - lastUpdate;
 if (delta >= vel2) {
 if (whiteLeds) {
 white.init(now, vel2, 255);
 } else {
 yg.init(now, vel2, 255);
 }
 whiteLeds = !whiteLeds;
 lastUpdate = now;
 }
 if ((now - sBIdx3) > int2) {
 sBIdx = 4;
 sBIdx4 = now;
 lastUpdate = now - vel3;

 }

 }

 if (sBIdx == 4) {
 //Serial.println ("7");
 unsigned long delta = now - lastUpdate;
 if (delta >= vel3) {
 if (whiteLeds) {
 white.init(now, vel3, 255);
 } else {
 yg.init(now, vel3, 255);
 }
 whiteLeds = !whiteLeds;
 lastUpdate = now;
 }
 if ((now - sBIdx4) > int3) {
 sBIdx = 5;
 sBIdx5 = now;
 }

 }
 if (sBIdx == 5) {
 unsigned long delta = now - sBIdx5;
 if (delta >= 600) {
 sBIdx = 6;
 sBIdx6 = now;
 lastUpdate = now - vel4;
 }
 }

 if (sBIdx == 6) {
 //Serial.println ("7");
 unsigned long delta = now - lastUpdate;
 if (delta >= vel4) {
 if (whiteLeds) {
 white.init(now, vel4, 255);
 } else {
 yg.init(now, vel4, 255);
 }
 whiteLeds = !whiteLeds;
 lastUpdate = now;
 }
 if ((now - sBIdx6) > int4) {
 sBIdx = 7;
 sBIdx7 = now;
 lastUpdate = now - vel5;
 }

 }
 if (sBIdx == 7) {
 //Serial.println ("7");
 unsigned long delta = now - lastUpdate;
 if (delta >= vel5) {
 if (whiteLeds) {
 white.init(now, vel5, 255);
 } else {
 yg.init(now, vel5, 255);
 }
 whiteLeds = !whiteLeds;
 lastUpdate = now;
 }
 if ((now - sBIdx6) > int5) {
 sBIdx = 8;

 }

 }

}

void loop() {

 unsigned int uS = sonar.ping(); // Send ping, get ping time in microseconds (uS).
 //calcula os CMs do sonar
 unsigned int cm = uS / US_ROUNDTRIP_CM;

 //se for 0, a medida nao eh valida e jogamos para o infinito
 if (cm == 0)
 cm = 1000;

 //estado 0. Leu medicao < 10 cm
 if (state == 0) {
 if (cm < 10) {
 firstRead = millis();
 state = 1;
 } else
 state = 0;
 }

 if (state == 1) {
 if (cm > 10)
 state = 0;
 else {
 unsigned long now = millis();
 if (now - firstRead > 500) {
 //se a medicao entre 0-10 cm durar meio segundo, entao ligamos a musica e vamos pro proximo estado
 state = 2;
 state2Init = millis();
 myservo.attach(treasurePort);
 SetVolume(31); // set volume from 0-31
 FileSource('SPI_Flash',0x01);
 SetPlayMode('Single_play');
 resetBlink();
 }
 }
 }

 //abertura
 if (state == 2) {
 unsigned long now = millis();
 long delta = now - state2Init;
 float firstStep = 7500;
 float secondStep = 11000;
 float diffStep = secondStep - firstStep;
 if (delta > 0) {
 //cuida da primeira perna de abertura, lenta e dramatica lol
 if (delta <= firstStep) {
 servoAngle = 25 + (int) ((delta / firstStep) * 20.0f);
 Serial.println(servoAngle);
 myservo.write(servoAngle);
 } else if (delta > firstStep && delta <= secondStep) {
 //cuida da segunda abertura, mais rapida, acompanhando a musica
 servoAngle = 45
 + (int) (((delta - firstStep) / diffStep) * 50.0f);
 Serial.println(servoAngle);
 myservo.write(servoAngle);
 } else if (delta > secondStep) {
 //começa a tocar a musica romantica e passa pro proximo estado
 state = 3;
 state3Init = millis();
 resetBlink();
 SetVolume(29);
 FileSource('SPI_Flash',0x02);
 SetPlayMode('Single_play');

 }
 }

 }
 //deixa o bau aberto durante a musica romantica
 if (state == 3) {
 unsigned long now = millis();
 unsigned long delta = now - state3Init;
 //270000 é o tempo de duracao da musica
 if (delta > 270000) {
 state = 4;
 state4Init = millis();
 }
 }

 //fecha lentamente o bau (durante os proximos 10 segundos)
 if (state == 4) {
 unsigned long now = millis();
 unsigned long delta = now - state4Init;
 if (delta <= 10000) {
 servoAngle = 100 - (int) ((delta / 10000.0f) * 80.0f);
 myservo.write(servoAngle);

 } else {
 state = 0;
 myservo.detach();
 }
 }
 unsigned long now = millis();
 //trata os leds piscando durante a abertura
 if (state == 2)
 handleOpeningBlink(now);

 //trata a luz piscando durante a musica romantica
 if (state >= 3 && state <= 4)
 handleBlink(now);

 //atualizam os LEDS
 white.update(now);
 yg.update(now);
}

Criei também uma classe para controlar as piscadas dos leds. Coloque na mesma pasta do .ino.


#ifndef __BLINK_H_
#define __BLINK_H_

#include <Arduino.h>

class Blink {

  public:
  
  Blink (int pin) {
    bPin = pin;
    pinMode(bPin, OUTPUT);
    bInit=0;
    bDuration=0;
    bLevel=0;
  }
  
  void init (unsigned long now, unsigned long duration, int level) {
    bInit = now;
    bDuration = duration;
    bLevel = level;
  }
  
  void update (unsigned long now) {
    unsigned long delta = now - bInit;
    if (delta >= bDuration) {
      analogWrite(bPin, 0);
      return;
    }
    float d2 = ((float) bDuration *0.5f);
    

  float level  = 0;
  if (delta < d2) {
     level = 0 + 255 * (delta/d2);
  } else {
      if (delta > bDuration)
        delta = bDuration;
       level = 0 + 255  * ( (bDuration - delta)/d2);
  }
//Serial.print (blinkPin);
//Serial.print ("-");
//Serial.println ((int) level);
  analogWrite(bPin, (int) level);
  
    
    
  }
private:
  int bPin;
  unsigned long bInit;
  unsigned long bDuration;
  int bLevel;
};

#endif

Por fim, algumas fotos das entranhas e da parte de trás, com a entrada de energia e o interruptor para por no modo zueira, que o baú começa a cantar e dançar U cant touch this (nem postei o código porque não deu muito certo, é muito ruim sincronizar com precisão a execução do mp3shield e a execução do código do arduino.

Detalhes - entranhas, conector e força e interruptor do modo dança. Pena não ter dado muito certo o modo dança, tinha até motores laterais pra fazer o bau dançar lol.

Detalhes – entranhas, conector e força e interruptor do modo dança. Pena não ter dado muito certo o modo dança, tinha até motores laterais pra fazer o bau dançar lol.

Qualquer duvida, postem nos comentários.

Anúncios

1 comentário

Arquivado em Arduino, Desenvolvimento, Eletrônica, Gadget, Geek, Geral, Variados, Zueira

Uma resposta para “Dica dia dos namorados nerd

  1. Bin Xu

    Hello,

    I would like to make one like this for my friend. could you please send me your code of Dica dia dos namorados nerd.
    Thank you so much.
    regards,

    Bin

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s