domingo, 16 de dezembro de 2012

Arduino TCS3200D Sensor de cor RGB




O TCS3200D é um sensor estático de cor. Ele possui 16 foto transistores com filtro Vermelho (R), 16 com filtro Verde  (G), 16 com filtro para azul(B) e 16 sem filtro (branco). Os filtros impedem a passagem das demais cores permitido que o fototraisistor seja excitado apenas pela luz quista. Através de dois pinos é possível selecionar qual grupo de fototransistor estará ativo e consequentemente qual luz será captada pelo sensor. A saída do sinal no sensor em uma onda quadra com frequência proporciona a intensidade da cor medida R, G, B, W.

O sensor

Como mencionado na introdução o sensor é basicamente constituído por quatro grupos de fototrasistores três desses grupos com filtros.

Os fototransistores são simplificadamente, transistores cujas bases são expostas. A incidência de luz sobre a base faz com que o transistor conduza. A variação dessa luz provoca variação proporcional na corrente que circula no fototransistor até o limite de saturação. Colocando um filtro que só deixe passar luz vermelha, por exemplo, apenas a luz vermelha fara conduzir o fototransistor. Então a corrente que circula no fototransistor será função da luz vermelha sobre ele.
É isso que ocorre no sensor, Um grupo de sensores não tem filtro algum o que o torna sensível a toda luz que incida sobre ele e cada um dos três outros grupos de sensores é dotado de filtro para uma determinada cor de luz vermelho, verde e azul.


Através dos pinos S2 e S3 é possível selecionar qual grupo de fototransistores estará ativo e consequentemente qual luz o sensor estará medindo e mostrando em sua saída. A tabela ao lado mostra as combinações destes pinos e o respectivos sensores selecionados.


Outra possibilidade de configuração do sensor é escalonar a frequência de saída escolhendo através dos pinos S0 e S1. A tabela ao lado mostra as combinações destes pinos e as respectivas escalas.



Recebendo os dados do sensor



A saída do sensor e uma onda com frequência diretamente proporcional a intensidade da cor medida naquele instante. Queremos obter a frequência em ciclos por segundo. Como se trada de uma onda quadrada com "duty cicle" de 50% (metade do ciclo em nível alto e metade em nível baixo). Então o programa deve contar todos os ciclos que ocorrem em um segundo. Ou estimar contando por 1/2 segundo e multiplicando por 2.

Uma forma de contar os ciclos para determinar a frequência é usando uma interrupção externa e um timmer o timer é setado para um segundo, por exemplo, e durante esse tempo uma variável é incrementada toda vez que a saída do sensor ligada ao pino de interrupção correspondente sai do nível baixo para o alto.


Para receber  os dados do sensor deve-se selecionar o filtro através de S1, S2, S3 e S4 conforme a tabela abaixo e receber e decodificar a informação sobre a cor selecionada através do pino OUT.


O programa para o arduino mostrado abaixo foi adaptado do programa encontrado neste site. usa a solução timme e interrupção externa. O programa para processing foi escrito suando como base o exemplo que acompanha a IDE do arduino "Arduino call response". Na primeira leitura o programa ajusta o balanço de branco, por isso é importante expor o sensor a referência de branco ao iniciar o arduino. No vídeo eu coloco uma folha em branco sobre o sensor ao iniciar o arduino para ter esta referência.

Código para o Arduino:

// este codigo e uma adaptação do coodigo encontrado em:
//http://www.elecfreaks.com/1666.html

//é necessario a biblioteca Timer1 que pode ser baixada aqui:
//http://www.arduino.cc/playground/Code/Timer1
// baixe renomei-a para TimerOne e adcione-a na pasta libriries da ide do arduino

#include <TimerOne.h>

#define OUT  2
#define S2   5
#define S3   6
#define S0   3
#define S1   4

volatile int g_count =0;
int g_flag = 0;
int g_array[3] = {0};
float g_SF[3] = {0};

void setup()
{
  // Init TSC230 and setting Frequency.
  pinMode(S0, OUTPUT);
  pinMode(S1, OUTPUT);
  pinMode(S2, OUTPUT);
  pinMode(S3, OUTPUT);
  pinMode(OUT, INPUT);

  digitalWrite(S0, LOW);  // OUTPUT FREQUENCY SCALING 2%
  digitalWrite(S1, HIGH);
  
  Serial.begin(115200);
  Timer1.initialize();             // defaulte is 1s
  Timer1.attachInterrupt(TSC_Callback);
  attachInterrupt(0, TSC_Count, RISING);

  delay(4000);

  g_SF[0] = 255.0/ g_array[0];     //R Scale factor
  g_SF[1] = 255.0/ g_array[1] ;    //G Scale factor
  g_SF[2] = 255.0/ g_array[2] ;    //B Scale factor

  estabeleceContato(); // aguarda contato com o processing
}
void loop()
{
   g_flag = 0;
   for(int i=0; i<3; i++)
   {
   int val = int(g_array[i] * g_SF[i]);
   val = constrain(val, 0, 255);
   Serial.write(val);
   }
   delay(4000);

}

// seta o filtro
void TSC_FilterColor(int Level01, int Level02)
{
  if(Level01 != 0)
    Level01 = HIGH;

  if(Level02 != 0)
    Level02 = HIGH;

  digitalWrite(S2, Level01);
  digitalWrite(S3, Level02);
}
// incrementa g_cont cada vex que a sáida do sensor
// vai do nivel baixo ao alto
void TSC_Count()// finção chamada pela interrupção
{
  g_count ++ ;  // incrementa g_cont
}

void TSC_Callback()
{
  switch(g_flag)
  {
    case 0:
          TSC_WB(LOW, LOW);     //Red
         break;
    case 1:
         g_array[0] = g_count;
         TSC_WB(HIGH, HIGH);    //Green
         break;
    case 2:
         g_array[1] = g_count;
         TSC_WB(LOW, HIGH);     //Blue
         break;
    case 3:
         g_array[2] = g_count;
         TSC_WB(HIGH, LOW);     //Clear(no filter)
         break;
   default:
         g_count = 0;
         break;
  }
}

void TSC_WB(int Level0, int Level1) //balanceamento de branco
{
  g_count = 0;
  g_flag ++;
  TSC_FilterColor(Level0, Level1);
  Timer1.setPeriod(1000000);    // define periodo de 1 segundo
}

 void estabeleceContato() {
    while (Serial.available() <= 0) {
    Serial.write('A');   // envia A ate estabelecer contato
    delay(300);
  }
 }

Código para o Processing:

// Adaptado do exemplo SerialCallResponse que acompanha a IDE do Arduino

import processing.serial.*;

 int comando =0;
 int y_01 = 55;
 
Serial myPort; // porta serial
int[] serialInArray = new int[3]; // vetor para armazenar o valores recebidos
int serialCount = 0;           // usado para contar o numero de bytes recebidos

int[] buffer = new int[3];

boolean firstContact = false;  // Whether we've heard from the microcontroller

void setup() {
  // imprime a lista de portas seriais
  println(Serial.list());

  String portName = Serial.list()[0];
  // substituir COM9 pela porta onde esta o Arduino
  myPort = new Serial(this, "COM9", 115200); 
  size(800, 600);  //janela, largura, altura
  smooth();
  
}

void draw() {

 
    background(buffer[0],buffer[1],buffer[2]);      
    
}


void serialEvent(Serial myPort) {
  
  // le os bytes da porta serial
  int inByte = myPort.read();
  // se receber o primeiro byte e for 'A'
  if (firstContact == false) {
    if (inByte == 'A') { 
      myPort.clear();       // Limpa o buffer da porta serial
      firstContact = true;  // o primeiro contato ja ocorreu
      myPort.write('A');    // envia "A" para que o arduino 
                            // reconheça o primeiro contato
    } 
  } 
  else {
    // se não for o primeiro byte adiciona-o ao vetor:
    serialInArray[serialCount] = inByte;
    serialCount++;

    // quando receber os 12 bytes armazena-os em buffer[i]
    if (serialCount > 2) {
      for(int i =0; i<3; i++)
      {
      buffer[i] =  serialInArray[i];
      }       
        // envia comandos caso existam
      myPort.write(comando);
      // Reset serialCount:
      serialCount = 0;
    }
  }
}

sábado, 8 de setembro de 2012

Arduino sensor de toque humano




Tenho garimpado alguns sensores no ebay e outros sites do gênero. O problema e que muitos sensores colocados à venda têm pouca ou nenhuma documentação disponível na internet. É preciso usar de engenharia reversa pra entender seu funcionamento e usá-los bem. Esta postagem mostra um desses sensores o sensor de toque humano.



Este sensor usa o comparador LM393 e tem como elemento sensor um transistor darligton MPSA13. O segredo é que a base do transistor não está ligada a nada, fica enrolada no 
corpo do transistor onde pode ser tocada. Ao tocar a base o transistor conduz o que através do LM393 coloca a saída digital do sensor (SD) em nível alto. Nos meus testes a saída ficou oscilando entre nivela alto e baixo quando o sensor era tocado. Foi preciso considerar isso na hora de programar.

O sensor também possui uma saída analógica (SA), direto do elemento sensor sem passar pelo comparador. Se ligada a uma entrada analógica do arduino, por exemplo, teremos um valor entre 1023 e 0. Na pratica, ao tocar no sensor o valor salta rapidamente de próximo do 1023 a próximo do 0. Um dos testes que fiz foi usar um pequeno pedaço de fio, como antena, conectado à base do transistor para aumentar sensibilidade. Nestes testes aproximar a mão de antena fazia aumentar o valor lido da saída analógica do sensor.


No experimento mostrado no vídeo a saída digital foi ligada no pino digital 3 do arduino. O comportamento seria idêntico ao de um botão se não fosse pelo fato da saída varia entre o nível alto e baixo, varias vezes por segundo, quando há o toque no elemento sensor. Para minimizar o esse efeito foi usado um laço. Enquanto a saída está em nível baixo o programa permanece nesse laço. Uma pequena mudança da saída para o nível alto faz o programa sair do laço e alterar o estado do pino 13 que, por sua vez controla o acendimento da lâmpada.


Programa


const int sensorPin = 3;
const int ledPin =  13;     

int sensorState = 0;        
int sensorState_aux = 0;    

void setup() {
  pinMode(ledPin, OUTPUT);      
  pinMode(sensorPin, INPUT);     
}

void loop(){

  sensorState_aux = digitalRead(sensorPin);
  delay(30);
  
  // sem ser tocado a sai­da do sensor permanece em nivel baixo
  // quand tocado a saída alterna entre nível baixo e alto
  // para eviatar ocilacões aguarda a saída ir ao nivel alto
  while( sensorState_aux != HIGH) 
  {
  sensorState_aux = digitalRead(sensorPin);
  delay(30);
  }
  //inverte o estado aterior do pino 13 do arduino
  // se HIGH passa para LOW e vice-versa
  sensorState = !sensorState;
  
  if (sensorState == HIGH) {     

    digitalWrite(ledPin, HIGH);  
  } 
  else {

    digitalWrite(ledPin, LOW); 
  }
   delay(500);
}

sábado, 11 de agosto de 2012

Motorizando patinete infantil




Uma postagem anterior mostrava a motorização de um carrinho de criança usando um motor de vidro elétrico, um ESC com relés tendo como "celebro" um PICAXE 08M, veja aqui. Desta vez o alvo é uma patinete da Barbie, brinquedo de minha filha de cinco anos. O motor usado, bem como as adaptações feitas no conjunto motor redutor são as mesmas do carrinho. Como não há necessidade de ré os relés foram dispensados. A placa, usada para controlar a velocidade tem basicamente o Picaxe, um mosfet e um regulador de voltagem.

Adaptação mecânica

Algumas modificações foram feitas. O eixo traseiro foi substituído por uma barra roscada de 3/8”. Essa barra passo por dentro da engrenagem centrar do conjunto motor redutor. Assim como no carrinho a barra é presa no motor e nas rodas com uso de porca e contra porca. Dois pequenos pedaços de tubo de PVC 1/2" foram usados para afastar as rodas da patinete. Foi feito um furo na parte traseira para que fosse possível abrigar a placa de controle no interior da patinete e prender o motor com auxilio de uma abraçadeira feita de chapa de alumínio.

Parte eletroeletrônica

Ao lado é possível ver o diagrama do circuito usado para controlar a velocidade de rotação o motor. O acelerador está representado como um potenciometro mas, na verdade, não sei o que ele realmente tem dentro. Consegui usa-ló depois de alguns testes. Dois pinos do PICAXE 08M são usados, uma como entrada analógica para o acelerador e outro como saída PWM para o mosfet, que aciona o motor. Um acoplador ótico é usado entre o picaxe e o mosfet. A placa ainda possui um regulador de voltagem 7805 responsável pela alimentação do PICAXE (5V).

O programa

O programa é extremamente simples Nele o picaxe lê o valor do acelerador através de uma entrada analógica. Vale ressaltar que o acelerador usado aqui quando na posição de descano envia um valor maior que zero. Depois de alguns testes percebeu-se que 250 seria o valor suficiente para garantir que a patinete permanecesse parada quando não houvesse acionamento do acelerador. Quando o valor é menor que 250 o programa mantém o motor parado. Quando o valor lido é maior que 250 esse valor e usado para gerar o PWM que será aplicado ao motor através do pino 2.


symbol acel_value = w1

symbol motor_pin =  2

main: 'loop principal

' le o valor do acelerador no pino 4 e armazena
' em acel_value (w1)

READADC10 4, acel_value  

'se o valor for menor que 250 mantem o motor parado

if acel_value < 250  then 

pwmout motor_pin,255,0

'se o valor for maior que 250 aplica-o (PWM) ao motor

else

pwmout motor_pin,255,acel_value

endif

goto main 'fim do loop principal

terça-feira, 7 de agosto de 2012

Arduino LDR matriz



Dada a impossibilidade de usar uma câmera como sensor no arduino, pensei na possibilidade de construir uma espécie de CCD de muito baixa resolução, utilizando LDRs. Pesquisando na internet encontre este site com um excelente trabalho na criação de uma mesa com 64x64 LDRs. Decidi então construir uma pequena matriz, 3x4, para testes.
 

O LDR (Light Dependent Resistor) como o próprio nome sugere é um resistor que tem sua resistência variada em função da luz que incide sobre ele. Quanto maior a luz menor a resistência. Se ligado a uma entrada analógica do arduino tem-se um valor proporcional à luz que incide sobre o LDR teoricamente entre 0 e 1024.

A ideia principal é ler através da porta analógica do Arduino cada LDR e enviar esses valores ao processig que, pro sua vez usa estes valores para determinas o tom de cinza de um retângulo formado por doze quadrados, cada um corresponde ao LDR na mesma posição.

Ao lado é mostrado o diagrama dos LDRs e uma animação que ilustra como a leitura é feita na matriz. Coloca-se o pino 2 em nível alto e procede a leitura das entradas analógicas A0, A1, A2 e A3 armazenado os valores nas variáveis correspondentes ao primeiro, segundo, terceiro e quarto LDRs o mesmo acontece na segunda linha, pino 3, LDRs cinco a oito e na terceira linha, pino 4. LDRs nove a doze.

A montagem foi feita em uma placa perfurada padrão. Os diodos são 1N4148, mas podem ser substituídos por equivalentes. Eles são responsáveis em garantir que um LDR não interfira na leitura de outro. Os resistires são de 10 kohm.

Os programas foram baseados no exemplo SerialCallResponse que acompanha a IDE do Arduino. O exemplo ensina como enviar bytes do arduino a processing e do processing ao arduino. No programa abaixo o arduino envia 12 bytes, um para cada valor lido nos LDRs. Antes de ser enviado o valor é ajustado para um valor entre 0 e 255. O processing recebe os bytes e usa-os para determinar o tom de cinza do quadrado correspondente.

Programa para o arduino:

 const int ldr[4] = {A0, A1, A2, A3};  
 const int vccPin[3] = {2,3,4};  
 int ldrValue;  
 void setup() {  
  Serial.begin(19200);  
  pinMode(vccPin[0], OUTPUT);   
  pinMode(vccPin[1], OUTPUT);   
  pinMode(vccPin[2], OUTPUT);   
  estabeleceContato(); // Envia um byte "A" ate obter resposta do processing   
 }  
 void loop() {  
   for(int i = 0; i < 3; i++)  
   {  
    digitalWrite( vccPin[i],HIGH);  
      for(int j = 0; j < 4; j++)  
      {  
       ldrValue = analogRead(ldr[j]);  // le o LDR correspondente  
       ldrValue = constrain(ldrValue, 0, 800); // ajusta valor lido  
       ldrValue = map(ldrValue,0,800,0,255);  // ajusta valor lido  
       Serial.write(ldrValue); // envia valor via porta serial   
      }  
    digitalWrite( vccPin[i],LOW);  
   }           
 } // fim do loop principal  
 void estabeleceContato() {  
   while (Serial.available() <= 0) {  
   Serial.write('A');  // envia A ate estabelecer contato  
   delay(300);  
  }  
  }  


Programa para o Processing:

 // Adaptado do exemplo SerialCallResponse que acompanha a IDE do Arduino  
 import processing.serial.*;  
  int comando =0;  
  int y_01 = 55;  
 Serial myPort; // porta serial  
 int[] serialInArray = new int[12]; // vetor para armazenar o valores recebidos  
 int serialCount = 0;      // usado para contar o numero de bytes recebidos  
 int[] buffer = new int[12];  
 boolean firstContact = false; // Whether we've heard from the microcontroller  
 void setup() {  
  // imprime a lista de portas seriais  
  println(Serial.list());  
  String portName = Serial.list()[0];  
  // substituir COM9 pela porta onde esta o Arduino  
  myPort = new Serial(this, "COM9", 19200);   
  size(800, 600); //janela, largura, altura  
  background(0);  
  smooth();  
 }  
 void draw() {  
   stroke(255); // borda   
   fill(buffer[0]);    // determina a cor     
   rect(0, 0, 200, 200); // desenha o quadrado 1  
              // posicao x, posicao y, largura , altura  
   fill(buffer[1]);     
   rect(200, 0, 200, 200);   
   fill(buffer[2]);     
   rect(400, 0, 200, 200);    
   fill(buffer[3]);      
   rect(600, 0, 200, 200);    
   fill(buffer[4]);      
   rect(0, 200, 200, 200);   
   fill(buffer[5]);       
   rect(200, 200, 200, 200);    
   fill(buffer[6]);    
   rect(400, 200, 200, 200);  
   fill(buffer[7]);     
   rect(600, 200, 200, 200);  
   fill(buffer[8]);        
   rect(0, 400, 200, 200);   
   fill(buffer[9]);         
   rect(200, 400, 200, 200);  
   fill(buffer[10]);     
   rect(400, 400, 200, 200);  
   fill(buffer[11]);      
   rect(600, 400, 200, 200);   
 }  
 void serialEvent(Serial myPort) {  
  // le os bytes da porta serial  
  int inByte = myPort.read();  
  // se receber o primeiro byte e for 'A'  
  if (firstContact == false) {  
   if (inByte == 'A') {   
    myPort.clear();    // Limpa o buffer da porta serial  
    firstContact = true; // o primeiro contato ja ocorreu  
    myPort.write('A');  // envia "A" para que o arduino   
               // reconheça o primeiro contato  
   }   
  }   
  else {  
   // se não for o primeiro byte adiciona-o ao vetor:  
   serialInArray[serialCount] = inByte;  
   serialCount++;  
   // quando receber os 12 bytes armazena-os em buffer[i]  
   if (serialCount > 11) {  
    for(int i =0; i<12; i++)  
    {  
    buffer[i] = serialInArray[i];  
    }      
     // envia comandos caso existam  
    myPort.write(comando);  
    // Reset serialCount:  
    serialCount = 0;  
   }  
  }  
 }  

quinta-feira, 12 de julho de 2012

Robô de sumô 500g



O robô mostrado aqui foi construído para participar de campeonato de sumô de robôs na categoria quinhentas gramas. Neste tipo de competição o robô deve mover-se dentro de uma arena preta com bordas brancas procurando seu oponente para empurra-lo para fora da arena. Na categoria do Eniac Jr., como foi batizado, além de não poder pesar mais que quinhentos gramas, o robô não deve ter largura e comprimento superiores a 10 cm. 

A estrutura

 A estrutura é composta basicamente por duas placas de compensado 4 mm e quatro pedaços de barra roscada de 4mm de diâmetro e 9cm de comprimento. Na primeira plataforma (8cm x 6 cm) estão os servo motores, presos por abraçadeiras, e os sensores de linha. Na segunda (10cm x 8 cm), o arduino com uma sensorshield, que é a placa sobre o arduino, usada para facilitar a conexão dos sensores e servos, o UBEC (regulador 5V x 5A), é necessário pro que os servos são forçados e não é conveniente alimenta-los diretamente pelo arduino. O sensor de oponente e demais componentes eletrônicos também estão na segunda plataforma. 
Apenas duas rodas são usadas. Foram adaptadas do rolo de tração de uma velha impressora. Uma pequena chapa de alumínio substitui a roda frontal.

A eletrônica

Sensor de linha




Os motores usados são servomotores modificados para rotação. Usar servos trás a vantagens deles já possuirem circuito de acionamento interno, necessitando apenas do sinal de controle e alimentação que neste caso e de 5 V/ 5A, provida por um UBEC (Universal Battery Eliminator Circuit). A bateria usada é uma LIPO 3 células 1300 mAh.


 Sensores


O Eniac Jr possui quatro sensores. Tanto os dois sensores de linha frontal quanto o sensor de linha traseira, que não está sensdo usado por ainda não ser necessário no programa, foram montados conforme digrama ao lado.

O quarto sensor é usado para detectar o oponente. Trada-se de um sensor infravermelho sharp GP2Y0A21. Um excelente sensor para este tipo de aplicação por sua velocidade e facilidade de uso.

O microcontrolador

Para controlar o robô foi escolhido o arduino, uma plataforma excepcional por sua capacidade, versatilidade e enorme quantidade de material de consulta. Este projeto está usando o arduino Uno, equipado com o atmega 328, o mais comumente usado hoje. Talvez fosse melhor usar o arduino mini 3.0 por seu tamanho reduzido.

O código fonte que embarca o arduino pode ser visto, com comentários, abaixo. É um programa básico pretendo incrementa-lo. A dinâmica de funcionamento é a seguinte:

Depois de ligado o robô aguada cinco segundos e inicia seu funcionamento andando para frete. Caso encontre a linha branca retorna e gira em torno de si mesmo procurando o oponente com auxilio do sensor frontal. Se encontrar o oponente o robô para de girar e volta a andar para frete. Não encontrando o adversário ele anda para frente após o fim do tempo de procura.



  #include <Servo.h>  
  Servo esquerdo;   
  Servo direito;   
  //******************* PINOS **********************//  
  int direito_pin = 10;  
  int esquerdo_pin = 11;  
  int SLF_esq = 8;  // pino do sensor de linha frontal esquerdo  
  int SLF_dir = 9;  // pino do sensor de linha frontal direito  
  int led_pin = 13;  // led no pino 13  
  int sens_front = 5; // pino do sensor oponente frontal (SHARP)( pino analogico 0 )  
 //****************** VARIAVEIS ******************//  
  long cm;    // variaveis para sensor frontal  
  float sharp = 0;  // usada na leitura dos sensores analogicos  
  float distancia = 0; // usada na leitura dos sensores analogicos  
  int tmp_Re = 5;  // tempo de reversão dos motores quando o robo encontra a linha*200 milissegundos  
  int tmp_Busc =3.4; // tempo de procura do adverssario, girando, em segundos  
  long raio_front = 35; // raio de procura frontal em centimetros  
  long tmpRound = 90000;  // tempo do round 90.000 milissegundos. 1 minuto e 30 segundos  
  long prev_tmpRound;  // variavel para contar tempo do round  
  unsigned long current_tmpRound; // variavel para contar tempo do round  
  long previousMillis;  // variavel para contar tempo entre buscas  
  unsigned long currentMillis; // variavel para contar tempo entre buscas  
  int aux_SLF = 0; // auxiliar para leitura do sensor de linha frontal  
  int aux_SLT = LOW; //0; // auxiliar para leitura do sensor de linha traseiro  
  int aux_front = 0;  // auxiliar para leitura do sensor frontal  
 //**************** SETUP *****************************//  
 void setup() {   
  Serial.begin(19200);   
  pinMode(led_pin, OUTPUT);  
  pinMode(SLF_esq, INPUT);  
  pinMode(SLF_dir, INPUT);  
 // conta os cinco segundos piscando o led do pin 13  
  for(int conte = 0; conte < 5; conte++)   
  {  
   digitalWrite(led_pin, HIGH);  
   delay(300);  
    digitalWrite(led_pin, LOW);  
   delay(700);  
  }  
  esquerdo.attach(esquerdo_pin,1000,2000);  //servo1 no pino 2, min, max  
  direito.attach(direito_pin,1000,2000);  //servo1 no pino 3   
  prev_tmpRound = millis();  // prev_tmpRound recebe o tempo atua de execução do programa marcando a contagem do round  
  current_tmpRound = millis();  
  // procurar(); // inicia procurando o oponente  
 }  
 //***************** FIM SETUP ****************************//  
  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
  ///////////////////////////////////////////////FUNÇÃO PRINCIPAL///////////////////////////////////////////////////////////////////  
 void loop()   
 {  
  int a1 =0;  
  int proc = 0;  
  int cnt =0;  
  int cnt_tmp =0;  
  static int direcao = LOW;  
  frente();  
  aux_SLF = ler_digital(SLF_esq , SLF_dir); // verifica sensor frontal de linha ( HIGH para branco LOW para preto)  
 // aqui o robo comeca efetivamente seu trabalho andar no tatame a procurar o oponente   
 // ele para apos 90 segundos (tempo do round)  
  do    
  {     
   if(aux_SLF == HIGH) // se um dos sensores de linha dianteiros encontrou a linha branca  
    {  
      re();  
      while(cnt_tmp < tmp_Re*4)  
      {  
       delay(50);  
       cnt_tmp++;  
      }  
      cnt_tmp =0;  
      procurar();  
    }  
   else  
    {  
    frente(); //se nenhum dos sensores for acionado continua indo em frente  
    }  
     aux_SLF = ler_digital(SLF_esq , SLF_dir); // verifica sensor frontal de linha ( HIGH para branco LOW para preto)  
  current_tmpRound = millis(); // atualisa current_tmpRound com o tempo atua de execução do programa  
   }while(current_tmpRound - prev_tmpRound <= tmpRound);//O ROBO ANDA ATE SE PASSAR O TEMPO DE UM ROUND  
   parar(999999); // para o robo após fim do round  
 } // fim do void loop()  
 ///////////////////FUNÇÃO PARAR /////////////////////  
 // como a modificação dos servos não ficou 100% é dificil para-los totalmente  
 void parar(int time)  
 {  
   direito.write(92.5);  
   esquerdo.write(89.5);  
   delay(time);  
 }  
 ///////////////////FUNÇÃO FRENTE /////////////////////  
 void frente()  
 {  
   direito.write(0);  
   esquerdo.write(180);  
 }  
 ///////////////////FUNÇÃO RÉ /////////////////////  
 void re()  
 {  
   direito.write(180);  
   esquerdo.write(0);  
 }  
 ///////////////////FUNÇÃO DIREITA /////////////////////  
 void direita()  
 {  
   direito.write(180);  
   esquerdo.write(180);  
 }  
 ///////////////////FUNÇÃO ESQUERDA /////////////////////  
 void esquerda()  
 {  
   direito.write(0);  
   esquerdo.write(0);  
 }  
 ///////////////////FUNÇÃO ler sensores analogicos sharp/////////////////////  
 // adaptada de:  
 /* www.ebay.com/itm/5-x-Arduino-IR-Sensor-GP2Y0A21YK0F-Measuring-Detecting-Distance-  
 10-80cm-/261031972852?_trksid=p4340.m263&_trkparms=algo%3DSIC%26its%3DI%252BC%26itu%3DUCI%  
 252BIA%252BUA%252BFICS%252BUFI%26otn%3D15%26pmod%3D260944545770%26ps%3D63%26clkid%3D56665851  
 1840405440&_qi=RTM1062687 */  
 int ler_analog(int sensor, int raio)  
 {  
  sharp = analogRead(sensor);   
  distancia = (6762/(sharp-9))-4;  
  if(distancia <= raio)  
  {  
  digitalWrite(led_pin, HIGH);   
  return(HIGH); // retorna HIGH caso o opnente esteja dentro do raio de alcance definido  
  }  
  else  
  {  
  digitalWrite(led_pin, LOW);  
  return(LOW); // retorna LOW caso o opnente NÃO esteja dentro do raio de alcance definido  
  }  
 }  
 ///////////////////FUNÇÃO ler sensores digitais /////////////////////  
 int ler_digital(int sens1 , int sens2)  
 {  
  if(digitalRead(sens1) == LOW || digitalRead(sens2) == LOW)  
  return(HIGH);  
  else  
  return(LOW);  
 }  
 ////função procura oponete chamada depois que o robo encontra a linha e da re/////////////////////////////  
  void procurar()  
  {  
   int cnt1 =0;  
   int a =0;  
   static int direcao = LOW;  
   static int aux_sFront_ant = LOW;  
   static int aux_sFront = LOW;  
  previousMillis = millis();  
  currentMillis = millis();  
     aux_front = ler_analog(sens_front, raio_front); // verifica sensor procura frontal  
     aux_SLF = ler_digital(SLF_esq , SLF_dir); // verifica sensor frontal de linha ( HIGH para branco LOW para preto)  
  while(currentMillis - previousMillis <= (tmp_Busc * 1000) && aux_SLF != HIGH && aux_front != HIGH)  
  {  
   direita();  
   delay(30);  
  currentMillis = millis();  
  aux_front = ler_analog(sens_front, raio_front); // verifica sensor procura frontal  
  aux_SLF = ler_digital(SLF_esq , SLF_dir); // verifica sensor frontal de linha ( HIGH para branco LOW para preto)  
  }  
  }  

terça-feira, 15 de maio de 2012

delay x millis no Arduino


A função delay é muito usada em programas para o arduino. É uma função simples e útil quando queremos pausa a execução de um programa pelo arduino.

O funcionamento dela é simples:
Imagine duas crianças brincando de esconder. A criança que conta deve ficar parada apenas contando até cem, por exemplo, para depois procurar a outra criança. A função delay(tempo e microssegundos) tem funcionamento similar o microcontrolado interrompe a execução do programa até o final do tempo determinado. Nem sempre é aceitável  esperar o microcontrolador  fazer a contagem de tempo através da função delay().


A função millis retorna o tempo de execução do programa até o momento em que ela é chamada. Como exemplo suponha que uma pessoa tem que falar por quinze minutos. Não seria possível contar e falar ao mesmo tempo então ela verifica a hora de inicio em um relógio e durante a palestra verifica novamente a hora subtrai a hora de inicio a fim de saber se já passaram os quinze minutos. Por exemplo, se a pessoa iniciou às 17h45min e olha o relógio às 17h55min saberá que ainda não passou 15 minutos, pois 17h55min - 17h45min = 10 minutos menor que 15. Mas se quando olhar para o relógio o palestrante e verificar que são 18h01min intenderá que é hora de parar já que 18h01min - 17h45min = 16 minutos que é obviamente maior que 15. Perceba que tanto faz verificar o tempo de inicio somar o tempo de duração do evento e verificar periodicamente se o tempo  final resultante da soma já  foi alcançado ou subtrair a cada período o tempo atual do tempo de inicio e comparar com o tempo de duração.

A função millis pode ser entendida como o relógio do exemplo acima. Sabendo-se que o programa começa a rodar no tempo 0 ms, se depois de um segundo chamarmos a função mills ela retornará 1000.

No programa abaixo, baseado no exemplo "Blink without Delay" que acompanha a interface de programação do arduino, enquanto aguardo o intervalo de um segundo entre as piscada do led no pino 13 o arduino imprime na tela do terminal "lendo sensor" o que poderia ser substituído por uma leitura real de um sensor ou outro tipo de ação.

int ledPin =  13;       // pino onde esta ligado o LED
int ledState = LOW;     // ledState usado para amazena o estado atual do led aceso o apagado
long intervalo = 1000;  // intervalo de um segundo
  
void setup() 
{
  pinMode(ledPin, OUTPUT); 
  Serial.begin(9600);      
}

void loop()
{
   long previousMillis = millis();
   int cnt=0;
   
    if (ledState == LOW)     // se o led estava apagado
        ledState = HIGH;    //
    else                   // se o led estava aceso
        ledState = LOW;   //

    digitalWrite(ledPin, ledState);
       
    // Mantem o led aceso ou apagado por um segundo
  while(millis() - previousMillis < intervalo) 
  {
       // pode-se fazer muita coisa dentro deste laço enquanto aguarda o final do tempo para mudar o estad do led
       // ler um sensor ou algo parecido, por exemplo
       if (cnt % 100 == 0)
       {
       Serial.print("Lendo sensor"); 
       Serial.print(" \n ");
       }
       cnt++;
  }

}

sexta-feira, 6 de abril de 2012

Arduino comando por voz


-->

Um dia desses fuçando o Ebay encontrei um módulo de comando por voz baseado no CI SPCE061A. Ele é capaz de armazenar até 15 comandos divididos em 3 bancos de 5 comandos. No projeto descrito aqui foram gravados cinco comandos no banco "1" com a finalidade de controlar o acendimento, apagamento e frequência de piscada de uma lâmpada comum ligada ao pino 13 do arduino através de um módulo de força baseado em um triac.

A comunicação com o módulo é  feita via interface serial. As mensagens de comando devem ser formadas pela chave 0xAA seguida do comando propriamente dito. Para gravar o banco um, por exemplo, basta envia via terminal o valor em 0xAA seguido do comando 0x11 (é sempre bom lembra que o "0x" informa que o número que o segue esta em hexadecimal) e, seguindo as mensagens de retorno no terminal ou observando os dois leds na placa,  pronunciar e repetir cada um dos cinco comandos desejados para o banco em questão.
 
Após a gravação é possível acessar o banco 1 através do comando 0xAA + 0x21 que serve para importar o banco 01. Importar, neste caso, é ativar um determinado grupo de comandos, pois apenas um dos grupos pode estar ativo por vês. Isso faz com que o módulo compare o comando falado com os cinco gravados naquele banco.


O manual do modulo pode ser encontrado aqui. Ele é bastante completo inclusive indica um programa gratuito para
-->comunicação serial, o AccessPort que pode ser baixado aqui.

-->
Resumidamente para fazer funcionar o módulo com o arduino deve-se:
1. Selecionar a velocidade de comunicação serial enviando, por exemplo, o comando "AA34" para 19200bps ou "AA33" para 9600bps. Por padrão a velocidade é 9600;

2. Selecionar o modo de comunicação: modo comum ou compacto. O modo determina se o módulo irá responder aos comandos com respostas longas (mensagens) ou curtas (hexadecimal);

3. Iniciar a gravação de um dos três bancos enviando o comando correspondente "AA11" para gravar o banco um "AA12" para o dois ou "AA13" para o banco três.

-->
Ao iniciar a gravação do banco um, por exemplo, o módulo irá solicitar os cinco comandos  de voz que deverão ser pronunciados e repetidos conforme mensagens de solicitação. Estes comandos de voz são associados a  números. No banco um os números 0x11, 0x12, 0x13, 0x14 e 0x15 são associados aos comandos de voz um a cinco respectivamente. Sendo que o comando de voz um é o primeiro que foi gravado no banco  e assim sucessivamente. Se um banco é ativado e o módulo recebe um comando de voz válido ele envia o valor correspondente via serial.

Quando um comando de gravação é enviado através do terminal, por exemplo, o modulo enviará um das seguintes respostas em hexadecimal ou em texto:


"ERROR! \ n" (0xe0) Erro de instrução
"START \ n" (0x40) Pronto para gravar
"No voice \ n" (0x41) Não detectada voz
"Again \ n" (0x42) Repita a instrução  ( após proximo "START")
"Too loud \ n" (0x43) Muito alto para gravar
"Different \ n" (0x44) Repetição da instrução diferente da primera aguarde o START para repetir o comando de voz
"Finish one \ n" (0x45) Uma instrução gravada com sucesso. O proximo START é para o comando de voz seguinte. Isso se repete até o quinto comando
"Group1 finished! \ n" (0x46) Fim da gravação do grupo 1

-->É possível também acompanhar as mensagens através dos dois leds existentes no módulo. O que torna possível, por exemplo, gravar um banco comunicando-se com o modulo através do arduino sem o auxilio de um computador. Os detalhes para isso estão no  manual.

-->
No programa, mostrado abaixo, o arduino inicializa o banco 1 e fica aguardando o comando enviado pelo modulo que por sua vez aguarda comandos de voz compatíveis com os gravados.


/*
 comandos de voz:
 
 1 - liga
 2 - desliga
 3 - pisca
 4 - mais
 5 - menos
 
 */

#include <Servo.h> 

long dist = 0;

int lampState = LOW;   

long previousMillis = 0;  
long intervalo = 1000; 
unsigned long currentMillis;

int lampada =13;
byte com = 0; 

void setup()
{
  

Serial.begin(9600);

pinMode(lampada, OUTPUT);

delay(2000);

// O modulo aceita comandos, em hexadecimal no seguinte formato:
// AA + comando 
Serial.write(0xAA); 
Serial.write(0x37); //configura o modulo para responder em modo compacto
delay(1000);        // no modo compacto as respostas são numeros em hexadecimal
Serial.write(0xAA);
Serial.write(0x21);//importa o grupo 1


}

void loop() // inicio do programa principal
{
  

while(Serial.available())
{
com = Serial.read();  //recebe o byte referente ao comando

if(com == 0x11) // ao recxeber o comando de voz "liga" o modulo envia o valor em hexadecimal: 11
digitalWrite(lampada, HIGH); // se receber 0x11 acende a lampada

if(com == 0x12) // ao recxeber o comando de voz "desiga" o modulo envia o valor em hexadecimal: 12
digitalWrite(lampada, LOW);// se receber 0x12 apaga a lampada

if(com == 0x13)// ao recxeber o comando de voz "pisca" o modulo envia o valor em hexadecimal: 13
{
 do  // se receber 0x13 entra neste laço que faz a lamada piscar. Aqui existem tres possibilidades:
     // Receber 0x14 "mais" e aumentar o intervalo entre as piscadas
     // Receber 0x15 "menos" e diminuir o intervalo entre as piscadas
     // Receber 0x11 (liga) 0u 0x12 (desliga) parar de piscar e fazer o que o comando pede
  {

   com = Serial.read();  //recebe o byte referente ao comando
    
     // piscar sem delay
       currentMillis = millis(); //armazena o valor referente ao tempo de execução do programa
        if(currentMillis - previousMillis > intervalo) 
         {   
          previousMillis = currentMillis;   

          if (lampState == LOW)
             lampState = HIGH;
          else
            lampState = LOW;

          digitalWrite(lampada, lampState);
         }
     //
    
    // se receber 0x14 aumenta o intervalo entre as piscadas   
    if(com == 0x14 && intervalo < 1000) 
    {
    intervalo +=100;
    }
    // se receber 0x14 diminue o intervalo entre as piscadas 
    if(com == 0x15 && intervalo > 100)
    {
    intervalo -=100;
    }
   
  // se receber 0x11 (liga) 0u 0x12 (desliga) para de piscar
  }while(com != 0x11 && com != 0x12);
}
}
}