domingo, 28 de agosto de 2016

Faça seu Arduino falar


INTRODUÇÃO

          Observei em alguns fóruns a necessidade da comunidade de fazedores em criar projetos que sintetize a voz, por isso, escrevo este artigo para auxiliar nossos companheiros nesta jornada. Não iremos propriamente sintetizar voz, mas criar uma técnica dos nossos projetos reportarem seus resultados de forma audível através da concatenação de arquivos ".wav".

        Inúmeras são as possibilidades de aplicação desta técnica, onde não necessariamente teremos de ficar observando uma tela para obter o resultado da variável que estamos monitorando, podemos fazer outras coisas enquanto esperamos o sistema reproduzir audivelmente o que queremos. Aplicações de inclusão social e acessibilidade podem ser desenvolvidas por exemplo: Para pessoas com deficiência visual ou baixa visão. A imaginação de cada um de nós será o limite.


DESCRIÇÃO

          Sintetizar voz não é uma tarefa simples e fácil, em minhas buscas na web encontrei alguns poucos exemplos com placas eletrônicas dedicadas e shields para Arduino que são funcionais mas pouco atraentes por causa de suas características e limitações por exemplo: voz robotizada, língua inglesa, alto custo entre outros.

          Nossa proposta é utilizar arquivos de áudio no formato wave utilizando o padrão PCM que trabalha confortavelmente com o Arduino. Apesar da facilidade de uso desses arquivos de áudio, um problema que constatei foi a baixa qualidade do som reproduzido pelo padrão de Modulação por Código de Pulso.

PCM - 8KHz de amostragem - 8 bits de resolução - canal mono - Usado em Sistema Telefônico


Teoria da Amostragem:

Qualidade de som = Amostra de Frequência * Resolução da Amostra

  • 16 Kbps - Rádio de ondas curtas
  • 32 Kbps - Rádio AM
  • 96 Kbps - Rádio FM
  • 128 Kbps - Próximo ao CD
  • 256 Kbps - Estúdio de Som

Felizmente com um pouco mais de trabalho podemos obter um resultado satisfatório.


Vamos demonstrar nossa técnica através de um termômetro que fala a temperatura registrada!!!

  • Inicialmente vamos usar uma biblioteca chamada TMRpcm. Com ela podemos reproduzir claramente arquivos wav de 22050 Hz 8-bit PCM mono.

  • Usei o aplicativo Easy Voice Recorder com as configurações acima para gravar em arquivos separados uma voz feminina, os arquivos contém as pronúncias temperatura, graus Celcius e os números 20 até 40.

  • Editei os arquivos de áudio no software Sony Vegas.


Segue o código fonte do nosso projeto:

#include <SD.h>
#include <TMRpcm.h>

#define pinoChipSelectSD 53

TMRpcm audioCartaoSD;

const int SENSOR_LM35 = A0;
const float CONVERSAO_CELSIUS = 0.49;

int leituraSensor;
float temperaturaAtual, temperaturaAnterior;
String valorTemp;

void setup(){  
    Serial.begin(9600);
    if (!SD.begin(pinoChipSelectSD)) {
        Serial.println("Falha no cartao SD");  
    }

    pinMode(SENSOR_LM35, INPUT);
    audioCartaoSD.speakerPin = 11;
}

void loop(){  
    temperaturaAtual = analogRead(SENSOR_LM35) * CONVERSAO_CELSIUS;
    if(temperaturaAtual != temperaturaAnterior) {
        temperaturaAnterior = temperaturaAtual;
                
        valorTemp = String(int(temperaturaAtual));
        valorTemp.concat(".wav");

        char nomeArquivo[valorTemp.length( ) + 1];
        valorTemp.toCharArray(nomeArquivo, sizeof(nomeArquivo));        
        
        audioCartaoSD.play("temp.wav");
        while(audioCartaoSD.isPlaying());
        audioCartaoSD.play(nomeArquivo);
        while(audioCartaoSD.isPlaying());
        audioCartaoSD.play("graus.wav");

        Serial.println(temperaturaAtual);
        delay(5000);        
    }
}

O componentes usados foram:

  1. Arduino Mega 2560
  2. Leitor de Cartão Micro SD
  3. Sensor de Temperatura LM35
  4. Buzzer para escutar que posteriormente foi conectado a um caixa de som de computador

As conexões dos componentes foram:

  1. Saída de som para fone de ouvido, caixa de som, amplificador, etc. Pino 11 para Arduino Mega e pino 9 para Arduino Uno.
  2. Formate o cartão micro SD em FAT32 e copie os arquivos de áudio.
  3. Terminal 2 (terminal do meio) do LM35 ao pino A0 do Arduino.

Leitor de cartão SD:

Arduino Mega
Cartão SD
50
MISO
51
MOSI
52
SCK
53
CS
GND
GND
5 volts
VCC

Arduino Uno
Cartão SD
12
MISO
11
MOSI
13
SCK
4
CS
GND
GND
5 volts
VCC


Assista o vídeo abaixo ou clique AQUI para abrir uma nova página para o youtube.



LINKS E DOWNLOADS

Biblioteca TMRpcm  https://github.com/TMRh20/TMRpcm/wiki

Download Direto TMRpcm  https://github.com/TMRh20/TMRpcm/archive/master.zip

Código Fonte do Termômetro  https://www.dropbox.com/s/7zlk8c1s6cllq1e/FacaArduinoFalar.ino?dl=0

Arquivos de Áudio  https://www.dropbox.com/s/uegr1sjvmxr0c37/Arquivos%20de%20Audio.rar?dl=0

domingo, 21 de agosto de 2016

Reduzindo o Consumo de Energia no Arduino


INTRODUÇÃO

          Quando estamos desenvolvendo e testando nossos projetos com Arduino, não nos preocupamos com o consumo de energia elétrica do mesmo, pois, estamos usando a porta USB para alimentar o circuito e podemos usar uma fonte externa normalmente. Porém, em alguns casos, onde temos que fazer acessos remotos, aquisição de dados em locais de difícil acesso ou distantes, entre outros, reduzir o consumo de energia é importante para prolongar a autonomia da bateria.


DESCRIÇÃO

          Existe uma função loseSomeTime na classe Sleepy que faz parte da biblioteca JeeLib, que será o foco do nosso estudo.

          O que a função loseSomeTime faz é colocar o microcontrolador em estado de sono profundo e, obrigatoriamente temos que ativar o temporizador Watchdog (cão de guarda) para que seu temporizador seja usado com o objetivo de acordar o microcontrolador.

Observe os exemplos abaixo com seus respectivos resultados usando a placa Arduino Mega 2560:


Primeiro exemplo sem usar a função loseSometime.
int led = 13;

void setup() {
    pinMode(led, OUTPUT);
}
void loop() {
    digitalWrite(led, HIGH);
    delay(10000);

    digitalWrite(led, LOW);
    delay(10000);
}

Temos os consumos de corrente com o LED ON e LED OFF respectivamente.




Segundo exemplo usando a função loseSomeTime.
#include <JeeLib.h>

int led = 13;

ISR(WDT_vect) { Sleepy::watchdogEvent(); }

void setup() {
    pinMode(led, OUTPUT);
}
void loop() {
    digitalWrite(led, HIGH);
    Sleepy::loseSomeTime(10000);

    digitalWrite(led, LOW);
    Sleepy::loseSomeTime(10000);
}

Observem que adicionamos a biblioteca #include <JeeLib.h>.

Em seguida ISR(WDT_vect) { Sleepy::watchdogEvent(); } é necessário para definirmos o manipulador de interrupção WDT.

E por fim Sleepy::loseSomeTime(10000).

          O tempo é definido em milissegundos, porém, não possui precisão ao usar o temporizador Watchdog, temos um pouco de perda na contagem que pode ser administrado sem problemas de acordo com a necessidade de cada projeto. Fica por conta de você leitor analisar a melhor situação.

          O valor máximo de tempo aceito pela função é 60000 milissegundos (60 segundos), caso o leitor deseje um tempo maior, será necessário criar um loop.

Por exemplo, um tempo de três horas (180 minutos):

for(int x = 0; x < 180; ++x)
    Sleepy::loseSomeTime(60000);


Veja agora os consumos de corrente com LED ON e LED OFF respectivamente.




          Em nosso exemplo, obtivemos uma redução de 62% no consumo de corrente elétrica. Este percentual irá variar de acordo com a placa Arduino e quantidade de portas usadas mas, sem dúvida teremos uma redução muitas vezes significativa.

          Para vocês terem uma ideia, se aplicarmos neste exemplo baterias CR2032 usadas em placas-mãe de computadores teremos o seguinte resultado:

A bateria pode fornecer algo em torno de 150 mA.

No exemplo 1, sem a JeeLib teríamos uma autonomia em torno de 2 horas de funcionamento.

No exemplo 2, com a JeeLib teríamos uma autonomia de torno de 5 horas e 20 minutos.


Segue um vídeo de apoio:




DOWNLOAD

Biblioteca JeeLib  https://github.com/jcw/jeelib

Documentação JeeLib  http://jeelabs.org/pub/docs/jeelib/index.html


sábado, 6 de agosto de 2016

Integrando Arduino com Excel


INTRODUÇÃO

          Muitas vezes desejamos obter informações e guardá-las para uma análise posterior. Pensando nisso, apresento uma ferramenta que pode ser conectada ao Arduino e salvar os dados em planilhas do Excel e com isso, podemos plotar gráficos, salvar em arquivos .txt, em banco de dados, criar sistemas de aquisição de dados, sistemas supervisórios, etc.


DESCRIÇÃO

          A ferramenta, é o aplicativo PLX-DAQ da Parallax. Através dele podemos enviar dados do Arduino para uma planilha do Excel ou ler dados da planilha para o Arduino.

Vamos iniciar!!!

Faça o download do aplicativo PLX-DAQ e instale-o.


Procure o atalho no Menu Iniciar ou na Área de Trabalho.



Após executar o PLX-DAQ, uma planilha será aberta e provavelmente com as macros desabilitadas.

Pressione o botão "Opções..." para habilitar as macros.


Em seguida...


Pronto!!! Você concluiu a instalação do PLX-DAQ.



Vamos testar. Carregue o código abaixo para sua placa Arduino.

int x = 0;
int linha = 0;

void setup() {
    Serial.begin(9600);
    Serial.println("CLEARDATA");
    Serial.println("LABEL, Data, Hora, x, Sen(x), Cos(x)");
}

void loop() {
    Serial.print("DATA, DATE, TIME,");
    Serial.print(x);
    Serial.print(",");
    Serial.print(sin(x * PI / 180));
    Serial.print(x);
    Serial.print(",");
    Serial.println(cos(x * PI / 180));

    linha++;
    x++;  
    
    if (linha > 360) {
        linha = 0;
        Serial.println("ROW,SET,2");
    }
    delay(100);
}

Com o PLX-DAQ aberto e o Arduino conectado ao computador, selecione a porta COM e a velocidade conforme sua placa Arduino.


Em seguida click no botão "Connect" e você verá o Arduino enviando dados para o Excel.


Ou


Segue uma vídeo aula ensinando como fazer.


DOWNLOADS

Código Fonte  https://www.dropbox.com/s/52wgh092pr06t01/ArduinoExcel.ino?dl=0

PLX-DAQ  https://www.dropbox.com/s/35dxfvzz89ylhc5/plx_daq_install.exe?dl=0

Site Oficial  https://www.parallax.com/downloads/plx-daq

terça-feira, 26 de julho de 2016

DICA: Comandos AT no módulo bluetooth HC-05


INTRODUÇÃO

          Esta é uma breve postagem, a intensão é ensinar o "pulo do gato" sobre configuração do módulo bluetooth HC-05 através de comandos AT. Este post não trás informações completas sobre os módulos existentes.


DESCRIÇÃO

          No semestre passado, eu e uma colega apresentamos um trabalho na faculdade onde utilizamos um módulo bluetooth HC-05, o projeto foi concluído sem maiores problemas. Mas, não conseguimos configurar o módulo via comandos AT.

          Agora sabemos o que aconteceu! Depois de muita dor de cabeça e alguns dias "ralando", repasso para a comunidade de estudantes as informações para ao menos minimizar os problemas em seus futuros projetos.

          Temos dois módulos disponíveis, o HC-05 e o HC-06 para uso pessoal, pois temos outros para aplicação industrial. HC-03, HC-04, HC-07, HC-08 e HC-09, estes são os que identifiquei.

          A diferença entre eles é que o HC-05 opera como master (mestre) ou slave (escravo) e a placa possui 6 pinos para conexões e o HC-06 opera apenas como slave (escravo) e a placa possui 4 pinos para conexões. Ocorre que estes dois módulos são comercializados e produzidos por mais de um fabricante, isso resulta em algumas pequenas diferenças de firmware e construção.

Segue imagens do HC-05 e HC-06 respectivamente.



          Observem que o HC-05 possui um pino chamado KEY, (você pode encontrar outras nomenclaturas como EN ou WAKEUP) este deve ser colocado em nível alto (HIGH) para que o módulo entre no modo AT e desconectado logo após ser configurado para que volte a operar em modo normal.

O módulo que utilizei (HC-05), está a esquerda na imagem abaixo


          Vejam que neste caso temos no lugar do pino KEY um outro chamado EN e um botão de RESET indicado por um quadrado vermelho. Se colocar o pino EN em nível alto (HIGH) este módulo NÃO entrará no modo AT. Neste caso, com o módulo desligado da fonte VCC, pressione o botão RESET e em seguida ligue o VCC do módulo e após 3 segundos solte o botão RESET.

          Pronto! Seu módulo entrou em modo AT, observe que antes o LED piscava rápido e agora está mais lento. Este módulo possui uma taxa de transmissão de 9600 baud em operação normal e de 38400 baud em modo AT.

          Segue o código fonte utilizado nos testes e imagens do resultado. Este código pode ser usado em Arduino Nano, Mega, etc.

#include <SoftwareSerial.h>

SoftwareSerial bluetooth(10, 11); // RX, TX

void setup() {
  delay(500);
  Serial.begin(9600);
  Serial.println("Digite os comandos AT :");
  bluetooth.begin(9600);
}

void loop() {  
  if (bluetooth.available())
    Serial.write(bluetooth.read());

  if (Serial.available())
    bluetooth.write(Serial.read());
}

          Para testar os comandos AT foi usado o Serial Monitor do arduino IDE e o software Termite. Segue suas respectivas imagens:





sexta-feira, 15 de julho de 2016

Construindo Game Pong com Arduino


INTRODUÇÃO

          No post anterior, introduzimos uma ideia de como usar a biblioteca TVout com o arduino. Agora, vamos criar algo mais interessante, o "velho" Game Pong.


DESCRIÇÃO

          Este jogo foi criado do zero de forma simples e objetiva, para que os iniciantes em desenvolvimento de jogos ou mesmo iniciantes com arduino, possam ter uma visão ampla e mínima necessária do processo de criação e funcionamento de um jogo.

          O mesmo pode ser melhorado e algum eventual bug corrigido mas, deixei como está, para que o código ficasse o mais "limpo" possível e o leitor possa ter uma melhor compreensão.


MONTAGEM

A imagem abaixo ilustra a montagem dos joysticks:


Mesmo que na imagem tenhamos um arduino uno, isso não quer dizer que seja exclusividade dele, qualquer placa pode ser usada desde que, seja observado a compatibilidade entre o arduino usado e a biblioteca para geração de imagem no televisor.
  1. Os potenciômetros usados foram de 50 kΩ
  2. Os capacitores eletrolíticos são de 2,2 µF por 16 v
Obs.: Os capacitores foram adicionados ao projeto porque ocorre uma instabilidade nas entradas analógica da placa arduino, provocando uma variação constante da tensão e com isso as barras que representam os players 1 e 2 ficaram "vibrando" constantemente.

A instabilidade ocorre em qualquer tipo de projeto que faça uso das entradas analógicas (continuo pesquisando para entender porque isso acontece), normalmente os desenvolvedores resolvem o problema via software, utilizando filtros ou calculando uma média das medições em um dado intervalo.

Para que nosso código continuasse simples e pequeno, resolvi o problema inicialmente adicionando dois capacitores eletrolítico de 2,2 µF. Caso o leitor não fique satisfeito, substitua os capacitores por outros de 4,7 µF. A tensão de trabalho da placa é de 5 volts então, qualquer capacitor de 10 volts ou mais servirá normalmente.



          O esquema de ligação da placa arduino no televisor segue conforme descrito no post anterior e imagens abaixo para arduino UNO ou MEGA respectivamente:




A seguinte tabela mostra os pinos que serão usados de acordo com o arduino escolhido.
 ARDUINO
SINCRONISMO 
VIDEO 
AUDIO 
 UNO
 9
11 
 MEGA
11 
29 
10 

A imagem abaixo ilustra o diagrama de ligação entre o arduino e o televisor.






Segue o código do nosso game pong :
/*
 * 
 * Arduinovaçao 15/07/16
 * 
 * http://arduinovacao.blogspot.com.br/
 * 
 * Diego Santos
 * 
 */
 
#include <TVout.h>
#include <fontALL.h>

#define LARGURA_TELA 120
#define ALTURA_TELA 96
#define LIMITE_SUP 9
#define LIMITE_INF 86

TVout TV;

// CONFIGURAÇOES
int velocidadeJogo = 25;
int velocidadeBolaX = 1;
int velocidadeBolaY = -1;
int entradaJoystickPlayer1 = A0;
int entradaJoystickPlayer2 = A1;
boolean startGame = false;
boolean donoDaBola = true;

// COORDENADAS DOS PLAYERS
int posicaoPlayer1X = 5;
int posicaoPlayer1Y;
int posicaoPlayer2X = 114;
int posicaoPlayer2Y;
int sentidoPlayer1;
int sentidoPlayer2;

// COORDENADA DA BOLA
int posicaoBolaX = 7;
int posicaoBolaY;

// PONTUAÇAO DOS PLAYERS
int pontosPlayer1 = 0;
int pontosPlayer2 = 0;

void setup() {
    TV.begin(_PAL, LARGURA_TELA, ALTURA_TELA);

    posicaoPlayer1Y = map(analogRead(entradaJoystickPlayer1), 0, 1023, 16, 79);
    posicaoPlayer2Y = map(analogRead(entradaJoystickPlayer2), 0, 1023, 16, 79);

    sentidoPlayer1 = posicaoPlayer1Y;
    sentidoPlayer2 = posicaoPlayer2Y;

    posicaoBolaY = posicaoPlayer1Y;
}

void loop() {
    TV.clear_screen();
    TV.select_font(font6x8);
    TV.print(44, 0, pontosPlayer1);
    TV.print(70, 0, pontosPlayer2);
    TV.select_font(font4x6);
    TV.print(35, 90, "Arduinovacao");

    desenhaCenario();

    verificaMovimentoPlayers();

    movimentaBola();
    
    delay(velocidadeJogo);
    
    if(pontosPlayer1 == 5) {
        gameOver(1);
    } else if(pontosPlayer2 == 5) {
        gameOver(2);
    }
}

void desenhaCenario() {
    // DESENHA QUADRA RETANGULAR
    TV.draw_rect(0, 8, LARGURA_TELA - 1, ALTURA_TELA - 17, WHITE);
    
    // DESENHA COLUNA CENTRAL DA QUADRA
    for(int i = 9; i <= 85; i = i + 5) {
        TV.draw_column((LARGURA_TELA / 2) - 1, i, i + 2, WHITE);
    }

    // DESENHA PLAYER 1
    TV.draw_column(posicaoPlayer1X, (posicaoPlayer1Y - 7), (posicaoPlayer1Y + 7), WHITE);

    // DESENHA PLAYER 2
    TV.draw_column(posicaoPlayer2X, (posicaoPlayer2Y - 7), (posicaoPlayer2Y + 7), WHITE);

    // DESENHA BOLA
    TV.set_pixel(posicaoBolaX, posicaoBolaY, WHITE);
}

void verificaMovimentoPlayers() {
    posicaoPlayer1Y = map(analogRead(entradaJoystickPlayer1), 0, 1023, 16, 79);
    if(sentidoPlayer1 < posicaoPlayer1Y) {
        if(!startGame && donoDaBola) {
            velocidadeBolaY = 1;
            startGame = true;
        }        
    } else if(sentidoPlayer1 > posicaoPlayer1Y) {
        if(!startGame && donoDaBola) {
            velocidadeBolaY = -1;
            startGame = true;
        }        
    }

    posicaoPlayer2Y = map(analogRead(entradaJoystickPlayer2), 0, 1023, 16, 79);
    if(sentidoPlayer2 < posicaoPlayer2Y) {
        if(!startGame && !donoDaBola) {
            velocidadeBolaY = 1;
            startGame = true;
        }
    } else if(sentidoPlayer2 > posicaoPlayer2Y) {
        if(!startGame && !donoDaBola) {
            velocidadeBolaY = -1;
            startGame = true;
        }
    }
    
    sentidoPlayer1 = posicaoPlayer1Y;
    sentidoPlayer2 = posicaoPlayer2Y;
}

void movimentaBola() {
    int bolaX = posicaoBolaX;
    int bolaY = posicaoBolaY;
  
    if(startGame) {
        bolaX = posicaoBolaX + velocidadeBolaX;
        bolaY = posicaoBolaY + velocidadeBolaY;      
    }
    
    // VERIFICA COLISOES SUPERIOR E INFERIOR DA BOLA COM A QUADRA
    if(bolaY == LIMITE_SUP) {
        velocidadeBolaY = 1;
        sonsDoJogo(440, 20); // SONORO QUANDO A BOLA BATE NA PARTE SUPERIOR
    }
    else if(bolaY == LIMITE_INF) {
        velocidadeBolaY = -1;
        sonsDoJogo(440, 20); // SONORO QUANDO A BOLA BATE NA PARTE INFERIOR
    }

    // VERIFICA COLISOES DA BOLA COM OS PLAYERS
    // PLAYER 1
    if(bolaX == 7) {
        if(posicaoBolaY < posicaoPlayer1Y + 8 && posicaoBolaY > posicaoPlayer1Y - 8) {
            velocidadeBolaX = 1;            
            if(startGame) {
                sonsDoJogo(220, 20); // SONORO QUANDO PLAYER 1 BATE NA BOLA
            }            
        }
    }else if(bolaX == 0) {
        startGame = false;
        donoDaBola = false;
        bolaX = 112;
        bolaY = posicaoPlayer2Y;
        pontosPlayer2++;
        sonsDoJogo(100, 300); // SONORO QUANDO PLAYER 2 FAZ PONTO
    }
    // PLAYER 2
    if(bolaX == 112) {
        if(posicaoBolaY < posicaoPlayer2Y + 8 && posicaoBolaY > posicaoPlayer2Y - 8) {
            velocidadeBolaX = -1;
            if(startGame) {
                sonsDoJogo(220, 20); // SONORO QUANDO PLAYER 2 BATE NA BOLA
            }            
        }
    }else if(bolaX == LARGURA_TELA) {
        startGame = false;
        donoDaBola = true;
        bolaX = 7;
        bolaY = posicaoPlayer1Y;
        pontosPlayer1++;
        sonsDoJogo(100, 300); // SONORO QUANDO PLAYER 1 FAZ PONTO
    }
        
    posicaoBolaX = bolaX;
    posicaoBolaY = bolaY;
}

void sonsDoJogo(int frequencia, int duracao) {
    TV.tone(frequencia, duracao);
}

void gameOver(int vencedor) {
    startGame = false;
    pontosPlayer1 = 0;
    pontosPlayer2 = 0;
    TV.clear_screen();
    TV.select_font(font8x8);
    sonsDoJogo(520, 500); // SONORO QUANDO PLAYER 1 OU PLAYER 2 VENCE O JOGO

    if(vencedor == 1) {
        TV.println(27, 40, "Vencedor");
        TV.println(27, 50, "PLAYER 1");
        donoDaBola = true;
        delay(5000);
    }else if(vencedor == 2) {
        TV.println(27, 40, "Vencedor");
        TV.println(27, 50, "PLAYER 2");
        donoDaBola = false;
        delay(6000);
    }
}


CÓDIGO FONTE

Game Pong  https://www.dropbox.com/s/4lu8zk0bni7vx5c/Game_Pong_Arduino.ino?dl=0