Publicidade:

terça-feira, 22 de setembro de 2015

Arduino - Dicas de programação 08 - int x unsigned int

Arduino é uma plataforma de prototipação que fez sucesso devido a facilidade com que junta eletrônica e programação e apresenta aos interessados nessas áreas. Tanto pessoas com experiência como sem experiências podem começar a implementar seus projetos sem ter que se preocupar muito com os fundamentos dessas duas áreas de conhecimento. Claro que isso é bom, pois democratiza o conhecimento e permite com que ideias saiam do papel e se tornem projetos, sejam simples ou mais complexos, mas isso não significa que não devemos nos aprofundar nos conhecimentos dessas duas áreas de conhecimento.

A IDE padrão do Arduino, utiliza C/C++, e o que eu quero mostrar aqui são algumas pegadinhas em que as vezes programadores menos experientes podem cair.

C/C++ é uma linguagem Fortemente Tipada. Isso significa que sempre que formos declarar uma variável, precisamos definir a que tipo de dados ela pertence.

Os principais tipos de variáveis são:

void
boolean
char
unsigned char
byte
int
unsigned int
word
long
unsigned long
short
float
double
string - char array
String - object
array

Minha intenção não é apresentar e explicar os tipos de dados utilizados para programar o Arduino, mas mostrar alguns detalhes que devemos prestar atenção na hora de programar.

Se olharmos os tipos acimas, veremos que alguns deles, como int, char e long, possuem o tipo unsigned, por exemplo, tem int, e tem unsigned int.

Mas qual a diferença entre esses dois tipos? Para exemplificar vamos pegar os tipos int e unsigned int. Tanto um como outro irá reservar 16 bits de memória para cada variável declarada. A diferença é que int armazena números que vão de  -32.768 a 32.767, ou seja, aceita números negativos. Enquanto que unsigned int, armazena números de 0 a 65.535.

Agora vamos imaginar o seguinte, iremos declarar duas variáveis, uma variável i do tipo int, e uma variável u do tipo unsigned int e inicializamos cada uma com o valor 0.

Se agora fizermos um if verificando se i é igual a u, obviamente o if será verdadeiro, pois ambas possuem valor 0.

Agora supomos que o valor das duas variáveis sejam decrementadas em 1 (i-- e u--). Vamos analisar então o que acontece com cada uma das variáveis.

Se i era igual a 0, agora ela será igual a -1. Já no caso de u, é diferente, pois u não pode armazenar valores negativos. Nesse caso, u-- var resultar no seguinte valor: 65.535. Isso mesmo. O valor assumido é o último valor dos possíveis valores para o tipo unsigned int. Isso sempre acontece quando extrapolamos os valores aceitos por esses tipos de dados, seja no início ou no fim.

Pois bem, considerando então que i é igual a -1 e u igual a 65.535, o que aconteceria se novamente verificássemos se i é igual a u. Apesar de não ser tão óbvio agora, a comparação continuaria resultado verdadeiro. Sim. -1 seria igual a 65.535.

O que acontece aqui é o seguinte: Quando uma comparação entre dois tipos é feita, é preciso que o compilador converta os tipos de dados para um mesmo tipo, e nessa conversão, os dados resultantes da conversão dos tipos de dados vão ser os mesmos, ou seja iguais. O que, dependendo da lógica que está sendo programada, pode levar a erros. E o programador menos experiente pode ficar doidinho tentando entender o que está acontecendo e não conseguir achar o problema.

Sketch com o exemplo acima


int          i = 0;  /*  -32.768 .. 32.767 */
unsigned int u = 0;  /*        0 .. 65.535 */

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.print("i = ");
  Serial.print(i);
  Serial.println("");
  
  
  
  Serial.print("u = ");
  Serial.print(u);
  Serial.println("");

  
  
  if (i == u) {
    Serial.println("iguais");
  } else {
    Serial.println("diferentes");
  }
  
  i--;
  u--;

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





Vamos imaginar agora um exemplo parecido, mas ao invés de decrementarmos as duas variaveis, vamos decrementar apenas i e iremos verificar se i é maior, menor ou igual a u.

int          i = 0;  /*  -32.768 .. 32.767 */
unsigned int u = 0;  /*        0 .. 65.535 */

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.print("i = ");
  Serial.print(i);
  Serial.println("");
  
  
  
  Serial.print("u = ");
  Serial.print(u);
  Serial.println("");

  
  
  if (i == u) {
    Serial.println("iguais");
  } else {
    
    if (i > u) {
      Serial.println("i maior que u");
    } else {
      Serial.println("u maior que i");
    }
    
  }
  
  i--; //apenas i é decrementado

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





Veja que mesmo i sendo negativo, o programa entende que i é maior que zero. Isso acontece pelo mesmo motivo mostrado anteriormente. O compilador precisa converteri, que é int, em unsigned int. pois só pode comparar variáveis do mesmo tipo, e nessa conversão de um tipo para outro, o valor  que era -1, passa a ser 65.535, ou seja, após a conversão, i de fato passa a ser maior que u.

Conclusão

Sempre que for declarar o tipo de dado, pense exatamente o que aquele dado vai representar, se de fato pode ou não haver valores negativos. E se haver, pense nos problemas que podem ocorrer. E procure não misturar os tipos de dados na hora de fazer comparações ou outras operações entre eles. E o principal: Sempre teste seu código com todas as possibilidades de valores.


sexta-feira, 18 de setembro de 2015

Arduino - Pai Nerd

Olá pessoal, ontem postei na página do facebook,  o vídeo abaixo, mostrando um projetinho que fiz pra comemorar o aniversário de um ano do meu filho.


Pai Nerd. Hoje meu filho, o Nicolas, faz 1 ano, e claro que tem Arduino pra comemorar :D

Pra quem estiver interessado, vou deixar o código-fonte, mas já aviso, que foi algo rápido, sem muito planejamento, e que com certeza dá pra ser melhorado.

Uma das melhorias que poderiam ser feitas, é substituir os delays, pelos millis. Tenho um artigo falando sobre como fazer isso. veja aqui.

Outro detalhe é que utilizo uma classe que criei pra gerar números aleatórios não repetidos. Quem quiser saber mais detalhes sobre essa classe, veja o artigo, clicando aqui.

valeu!


código-fonte:



#include "LedControl.h"
 
/**********************************************************************************
************************************CLASSE UNIQUE RANDOM***************************
**********************************************************************************/
   
class UniqueRandom{
  private:
    int _index;
    int _min;
    int _max;
    int _tam;
    int* _list;
    void _init(int min, int max) {
      _list = 0; 
      if (min < max) { _min = min; _max = max; } else { _min = max; _max = min; }
      _tam = _max - _min; 
      _index = 0;
    }    
  public:
    UniqueRandom(int max)           { _init(0,   max); randomize(); } //construtor com 1 parametro
    UniqueRandom(int min, int max)  { _init(min, max); randomize(); } //construtor com 2 parametros
       
    void randomize() {
      if (_list == 0) { _list = (int*) malloc(_tam * sizeof(int)); }  
      for (int i=0; i<_tam; i++) {   _list[i] = _min+i;  }   //preenche a lista do menor ao maior valor
         
      //embaralha a lista
      for (int i=0; i<_tam; i++) {  
        int r = random(0, _tam);     //sorteia uma posição qualquer
        int aux = _list[i];               
        _list[i] = _list[r];
        _list[r] = aux;
      }
    }
       
    int next() {                                  //retorna o proximo numero da lista
      int n = _list[_index++];
      if (_index >= _tam ) { _index = 0;} //após recuper o ultimo numero, recomeça na posicao 0
      return n;
    }
       
       
    ~UniqueRandom(){ free ( _list ); }  //destrutor
};
/**********************************************************************************
************************************FIM CLASSE UNIQUE RANDOM***********************
**********************************************************************************/
 
 
 
/*
 pin 4 is connected to the DataIn 
 pin 6 is connected to the CLK 
 pin 5 is connected to LOAD 
 */
LedControl   lc(4,6,5, 2); //2 max7219
UniqueRandom ur(64); //declaracao do objeto unique random
 
/* we always wait a bit between updates of the display */
unsigned long delaytime=500;
 
void setup() {
  int seed = 0;
     
  for (int i=0; i<10; i++) {
    seed += ( analogRead(A0) + analogRead(A1) + analogRead(A2) + analogRead(A3) + analogRead(A4) + analogRead(A5) ) ;
    delay(10);
  }
  randomSeed(seed);
     
  lc.shutdown(0,false);
  lc.setIntensity(0,1);
  lc.clearDisplay(0);
    
  lc.shutdown(1,false);
  lc.setIntensity(1,1);
  lc.clearDisplay(1);
   
  int lin = 0;
  lc.setColumn(1, lin++, B11111111);
  lc.setColumn(1, lin++, B11111111);
  lc.setColumn(1, lin++, B01110000);
  lc.setColumn(1, lin++, B00111000);
  lc.setColumn(1, lin++, B00011100);
  lc.setColumn(1, lin++, B00001110);
  lc.setColumn(1, lin++, B11111111);
  lc.setColumn(1, lin++, B11111111);
   
  delay(500);
}
 
byte leds_0[8] = {
  B00111000, 
  B00111000, 
  B00011000, 
  B00011000, 
  B00011000, 
  B00011000, 
  B01111110, 
  B01111110
};
 
byte leds_1[8] = {
  B11000011, 
  B11100011, 
  B11110011, 
  B11111011, 
  B11011111, 
  B11001111, 
  B11000111, 
  B11000011
};
 
void loop() { 
 
  int aleatorio = random(0, 9);
  //aleatorio = 8;
  Serial.println(aleatorio);
   
  if (aleatorio == 0) {
    //linhas de cima para baixo
    lc.clearDisplay(0);
    delay(delaytime/2);
    for(int row=0;row<8;row++) { lc.setRow(0,row, leds_0[row]);delay(50); }
    delay(delaytime*5);
  }
   
  if (aleatorio == 1) {
    //linhas de baixo para cima
    lc.clearDisplay(0);
    delay(delaytime/2);
    for(int row=7;row>=0;row--) { lc.setRow(0,row, leds_0[row]);delay(50); }
    delay(delaytime*5);
  }
   
  if (aleatorio == 2) {
    //direita para a esquerda
    lc.clearDisplay(0);
    delay(delaytime/2);
    for (int col=0;col<8;col++){
      for(int row=0;row<8;row++) {  lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); }
      delay(50); 
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 3) {  
    //esquerda para a direita
    lc.clearDisplay(0);
    delay(delaytime/2);
    for (int col=7;col>=0;col--){
      for(int row=0;row<8;row++) {  lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); }
      delay(50); 
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 4) {
    //direita para a esquerda
    lc.clearDisplay(0);
    delay(delaytime/2);
    for(int row=0;row<8;row++) {
      for (int col=0;col<8;col++){  
        lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_0[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 5) {  
    //direita para a esquerda
    lc.clearDisplay(0);
    delay(delaytime/2);
    for(int row=0;row<8;row++) {
      for (int col=7;col>=0;col--){  
        lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_0[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 6) {    
    //------------------------
    lc.clearDisplay(0);
    delay(delaytime/2);
    for (int col=0;col<8;col++){
      for(int row=0;row<8;row++) {  
        lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_0[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
     
  }
   
  if (aleatorio == 7) {  
    //--------------------------
    lc.clearDisplay(0);
    delay(delaytime/2);
    for (int col=7;col>=0;col--){
      for(int row=0;row<8;row++) {  
        lc.setLed(0, row, col,   (leds_0[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_0[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 8) {  
    //--------------------------
    lc.clearDisplay(0);
    delay(delaytime/2);
     
    for(int i=0;i<64;i++) {  
      int r = ur.next();
      int l = r / 8;
      int c = r % 8;
      int val = (leds_0[l] & (1 << (7-c) )) != 0 ;
        
      if ( val ) { delay(100); }
      lc.setLed(0, l, c, val );
    }
     
    ur.randomize();
    delay(delaytime*5);
  }
 
 
 
 
 
 
 
 
 
 
 
 
  aleatorio = random(0, 9);
   
  if (aleatorio == 0) {
    //linhas de cima para baixo
    lc.clearDisplay(1);
    delay(delaytime/2);
    for(int row=0;row<8;row++) { lc.setRow(1,row, leds_1[row]);delay(50); }
    delay(delaytime*5);
  }
   
  if (aleatorio == 1) {
    //linhas de baixo para cima
    lc.clearDisplay(1);
    delay(delaytime/2);
    for(int row=7;row>=0;row--) { lc.setRow(1,row, leds_1[row]);delay(50); }
    delay(delaytime*5);
  }
   
  if (aleatorio == 2) {
    //direita para a esquerda
    lc.clearDisplay(1);
    delay(delaytime/2);
    for (int col=0;col<8;col++){
      for(int row=0;row<8;row++) {  lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); }
      delay(50); 
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 3) {  
    //esquerda para a direita
    lc.clearDisplay(1);
    delay(delaytime/2);
    for (int col=7;col>=0;col--){
      for(int row=0;row<8;row++) {  lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); }
      delay(50); 
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 4) {
    //direita para a esquerda
    lc.clearDisplay(1);
    delay(delaytime/2);
    for(int row=0;row<8;row++) {
      for (int col=0;col<8;col++){  
        lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_1[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 5) {  
    //direita para a esquerda
    lc.clearDisplay(1);
    delay(delaytime/2);
    for(int row=0;row<8;row++) {
      for (int col=7;col>=0;col--){  
        lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_1[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 6) {    
    //------------------------
    lc.clearDisplay(1);
    delay(delaytime/2);
    for (int col=0;col<8;col++){
      for(int row=0;row<8;row++) {  
        lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_1[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
     
  }
   
  if (aleatorio == 7) {  
    //--------------------------
    lc.clearDisplay(1);
    delay(delaytime/2);
    for (int col=7;col>=0;col--){
      for(int row=0;row<8;row++) {  
        lc.setLed(1, row, col,   (leds_1[row] & (1 << (7-col) )) != 0    ); 
        if ( (leds_1[row] & (1 << (7-col) )) != 0 ) {
          delay(100);  
        }
      }
    }
    delay(delaytime*5);
  }
   
  if (aleatorio == 8) {  
    //--------------------------
    lc.clearDisplay(1);
    delay(delaytime/2);
     
    for(int i=0;i<64;i++) {  
      int r = ur.next();
      int l = r / 8;
      int c = r % 8;
      int val = (leds_1[l] & (1 << (7-c) )) != 0 ;
        
      if ( val ) { delay(100); }
      lc.setLed(1, l, c, val );
    }
     
    ur.randomize();
    delay(delaytime*5);
  }
 
 
 
 
   
 
}


quarta-feira, 16 de setembro de 2015

Arduino - como substituir delay() pelo millis()


No Artigo anterior mostrei como gerar números aleatórios, tantos repetidos quanto não repetidos. Pra isso criei uma classe e fiz um vídeo demonstrando o uso da mesma. Para quem quiser o ver o artigo, clique aqui. Como exemplo, fiz um programa bem simples, que ligava aleatoriamente led por led de uma matriz de 8X8 e depois desligava-os um por um. Pra controlar o tempo que cada led era ligado, utilizei a função delay, ou seja, ligava um led, espera alguns milissegundos e depois ligava o próximo led.

Funcionou perfeitamente. Consegui demonstrar o que eu queria. Mas um dos problemas é que o delay utilizado dentro do laço for, me impedia de conseguir incluir uma segunda matriz de leds para fazer outra coisa. Até daria para incluir, mas seria bem trabalhoso, e se eu quisesse incluir um terceiro, quarto ou qualquer outra coisa no arduino, como um teclado, por exemplo, eu teria problemas.

  for(int i=0; i<64; i++) { 
    int r = ur.next(); 
    int l = r / 8;
    int c = r % 8;
      
    delay(100);                        //delay para cada led da matriz.
    lc.setLed(0, l, c, HIGH );
  }

Para solucionar esse problema, resolvi eliminar as chamadas para delay, e para isso criei uma função chamada time:

int time(long timeHigh, long timeLow, long atraso, long mref = 0) {
  long ajuste = mref % (timeHigh + timeLow);
  long resto  = (millis() + timeHigh + timeLow - ajuste - atraso) % (timeHigh + timeLow);
  return (resto < timeHigh ? HIGH : LOW);
}

Os dois primeiros parâmetros são timeHigh e timeLow. Que significa que, se por exemplo, timeHigh for 1000 e timeLow também for 1000, a função irá retornar HIGH nos primeiros 1000 milissegundos e LOW nos últimos 1000 milissegundos. Supondo que eu quisesse piscar um led no Arduino com essa função, poderia ser feito da seguinte maneira:

void loop() { 
  digitalWrite(13,   time(1000, 1000, 0, 0)  );  //pisca o led do pino 13

  //colocar aqui o código para fazer outras coisas durante o pisca led
}


O terceiro parâmetro (long atraso) é usado para iniciar a contagem com um atraso de tempo, em milissegundos, enquanto que o quarto é o millis de referencia. Por exemplo, se eu quiser iniciar a contagem no momento que o usuário apertar um botão, eu passo como referência o valor lido no momento que pressionou o botão.

Para piscar 3 leds ao mesmo, mas cada um com um atraso de 300 milissegundos em relação ao anterior, poderia ser feito da seguinte maneira:

void loop() { 
  digitalWrite(13,   time(1000, 1000,   0, 0)  );  //pisca o led do pino 13
  digitalWrite(12,   time(1000, 1000, 300, 0)  );  //pisca o led do pino 12
  digitalWrite(11,   time(1000, 1000, 600, 0)  );  //pisca o led do pino 11
}


E ainda se eu quisesse piscar os leds apenas enquanto um botão fosse mantido pressionado, poderia ser feito da seguinte maneira:

unsigned long mref = 0;

void loop() {
  if ( digitalRead(4) ) {
    if (mref == 0) { mref = millis(); }  
  } else {
    mref = 0;
  }

  if (mref > 0) {
    digitalWrite(13,   time(1000, 1000,   0, mref)  );  //pisca o led do pino 13
    digitalWrite(12,   time(1000, 1000, 300, mref)  );  //pisca o led do pino 13
    digitalWrite(11,   time(1000, 1000, 600, mref)  );  //pisca o led do pino 13
  } else {
    digitalWrite(13,   LOW  );  //pisca o led do pino 13
    digitalWrite(12,   LOW  );  //pisca o led do pino 13
    digitalWrite(11,   LOW  );  //pisca o led do pino 13
  }
}


Veja no vídeo abaixo a demonstração de como usar a função acima, para mostrar diferentes animações em uma matriz de leds 8x8.



Código-fonte:

#include "LedControl.h"
 
/**********************************************************************************
************************************CLASSE UNIQUE RANDOM***************************
**********************************************************************************/
 
class UniqueRandom{
  private:
    int _index;
    int _min;
    int _max;
    int _size;
    int* _list;
    void _init(int min, int max) {
      _list = 0; 
      if (min < max) { _min = min; _max = max; } else { _min = max; _max = min; }
      _size = _max - _min; 
      _index = 0;
    }    
  public:
    UniqueRandom(int max)           { _init(0,   max); randomize(); } //construtor com 1 parametro
    UniqueRandom(int min, int max)  { _init(min, max); randomize(); } //construtor com 2 parametros
     
    void randomize() {
      if (_list == 0) { _list = (int*) malloc(size() * sizeof(int)); }  
      for (int i=0; i<size(); i++) {   _list[i] = _min+i;  }   //preenche a lista do menor ao maior valor
       
      //embaralha a lista
      for (int i=0; i<size(); i++) {  
        int r = random(0, size());     //sorteia uma posição qualquer
        int aux = _list[i];               
        _list[i] = _list[r];
        _list[r] = aux;
      }
    }
     
    int next() {                                  //retorna o proximo numero da lista
      int n = _list[_index++];
      if (_index >= size() ) { _index = 0;} //após recuper o ultimo numero, recomeça na posicao 0
      return n;
    }
     
    int size() { return _size; }
     
    ~UniqueRandom(){ free ( _list ); }  //destrutor
};
/**********************************************************************************
************************************FIM CLASSE UNIQUE RANDOM***********************
**********************************************************************************/

/**********************************************************************************
************************************FUNÇÃO TIME************************************
**********************************************************************************/
int time(long timeHigh, long timeLow, long atraso, long mref = 0){
  long ajuste = mref % (timeHigh + timeLow);
  long resto  = (millis() + timeHigh + timeLow - ajuste - atraso) % (timeHigh + timeLow);
  return (resto < timeHigh ? HIGH : LOW);
}
/**********************************************************************************
************************************FIM FUNÇÃO TIME********************************
**********************************************************************************/

/*
 pin 4 is connected to the DataIn 
 pin 6 is connected to the CLK 
 pin 5 is connected to LOAD 
 */
 
LedControl   lc(4,6,5, 2); //2 max7219
UniqueRandom ur(64); //declaracao do objeto unique random
 
unsigned long delaytime=500;
 
unsigned long mref0, mref1;
 
void setup() {
  int seed = 0;
   
  for (int i=0; i<10; i++) {
    seed += ( analogRead(A0) + analogRead(A1) + analogRead(A2) + analogRead(A3) + analogRead(A4) + analogRead(A5) ) ;
    delay(10);
  }
  randomSeed(seed);
   
  lc.shutdown(0,false);
  lc.setIntensity(0,1);
  lc.clearDisplay(0);
  
  lc.shutdown(1,false);
  lc.setIntensity(1,1);
  lc.clearDisplay(1);
  
  pinMode(13, OUTPUT);
}
 
void loop() { 
  
  digitalWrite(13,   time(1000, 1000, 0, 0)  );  //pisca o led do pino 13
  

  //display 01 
  long t1 = 250; //tempo entre ligar um led e outro
  if (  time(1000, 64*t1*2, 0, 0)  ) {
    lc.clearDisplay(0);
    ur.randomize();
    mref0 = millis();
  } else {
    for(int i=0; i<64; i++) {  
      int r = ur.next();
      int lin = r / 8;
      int col = r % 8;
      
      lc.setLed(0, lin, col, time(64*t1, 64*t1, i*t1, mref0)    );
    }
  }
  
  
  //display 02
  long t2 = 700; //tempo entre ligar um led e outro
  if (  time(2000, 64*t2, 0)  ) {
    lc.clearDisplay(1);
    mref1 = millis();
  } else {
    for(int i=0; i<64; i++) {  
      int r = i;
      int lin = r / 8;
      int col = r % 8;
      
      lc.setLed(1, lin, col, time(64*t2, 64*t2, i*t2, mref1) );
    }
  }
  
}


segundo-exemplo:



unsigned long mref1=-10000;
unsigned long mref2=-10000;

int t1 = 1000;  //tempo 1
int t2 = 4000;  //tempo 2

/*
A FUNÇÃO TIME ESTÁ ESPLICADA NESSE LINK:
http://fabianoallex.blogspot.com.br/2015/09/arduino-como-substituir-delay-pelo.html
*/
int time(long timeHigh, long timeLow, long atraso, long mref = 0) {
  long ajuste = mref % (timeHigh + timeLow);
  long resto  = (millis() + timeHigh + timeLow - ajuste - atraso) % (timeHigh + timeLow);
  return (resto < timeHigh ? HIGH : LOW);
}

void setup() {
  pinMode(3, OUTPUT);  //led
  pinMode(4, OUTPUT);  //led
  
  pinMode(8, INPUT);  //botao
  pinMode(9, INPUT);  //botao
}

// the loop routine runs over and over again forever:
void loop() {
  
  if (digitalRead(8)  &&  (millis()-mref1) > t1  ){ mref1 = millis(); }
  if (digitalRead(9)  &&  (millis()-mref2) > t2  ){ mref2 = millis(); }

  
  if (  (millis()-mref1) <= t1 && (mref1 > 0)  ) {  digitalWrite(3, time(200,200,0,mref1)  );  } else {  digitalWrite(3, LOW); }
  if (  (millis()-mref2) <= t2 && (mref2 > 0)  ) {  digitalWrite(4, time(200,200,0,mref2)  );  } else {  digitalWrite(4, LOW); }
  
  
  delay(10);
}




Atualização - 21/10/2015


O Vídeo abaixo mostra alguns outros detalhes a ser considerado na hora de trabalhar com temporização no Arduino, pra isso criei uma classe chamada MyTimer, que detecta tanto se tá em timeHigh, timeLow, borda de subida e borda de descida, o que dá mais possibilidades na hora de trabalhar com temporização.






código-fonte:


 /*********************************************************************************************************
************************************CLASSE MYTIMER********************************************************
**********************************************************************************************************/
enum MyTimerValue {MY_TIMER_LOW, MY_TIMER_HIGH, MY_TIMER_UNDETERMINED };
class MyTimer {
  private:
    unsigned long _mref;
    long _timeHigh;
    long _timeLow;
    long _lag; //atraso
    MyTimerValue _last_value; //valor anterior
    MyTimerValue _atual_value; //valor atual
    void _undetermine(){
      _last_value = MY_TIMER_UNDETERMINED;
      _atual_value = MY_TIMER_UNDETERMINED;
      update();
    }
  public:
    MyTimer(long timeHigh, long timeLow, long lag, unsigned long mref){
      _timeHigh = timeHigh;
      _timeLow = timeLow;
      _lag = lag;
      _mref = mref;
      _undetermine();
    }
    void setTimeHigh(long timeHigh);
    void setTimeLow(long timeLow);
    void setMillisRef(unsigned long mref);
    void setLag(long lag);
    void update();
    boolean isRising();       //0->1
    boolean isFalling();      //1->0
    boolean isChanging();     //0->1 ou 1->0
    boolean isHigh();         //1
    boolean isLow();          //0
};
void MyTimer::setTimeHigh(long timeHigh)       { _timeHigh  = timeHigh;  _undetermine(); }
void MyTimer::setTimeLow(long timeLow)         { _timeLow = timeLow;     _undetermine(); }
void MyTimer::setMillisRef(unsigned long mref) { _mref    = mref;        _undetermine(); }
void MyTimer::setLag(long lag)                 { _lag     = lag;         _undetermine(); }
void MyTimer::update(){
  long adjustment = _mref % (_timeHigh + _timeLow);
  long rest       = (millis() + _timeHigh + _timeLow - adjustment - _lag) % (_timeHigh + _timeLow);
  _last_value = _atual_value;
  _atual_value = (rest < _timeHigh ? MY_TIMER_HIGH : MY_TIMER_LOW);
}
boolean MyTimer::isRising()  {  return (_last_value == MY_TIMER_LOW && _atual_value == MY_TIMER_HIGH);  }
boolean MyTimer::isFalling() {  return (_last_value == MY_TIMER_HIGH && _atual_value == MY_TIMER_LOW);  }
boolean MyTimer::isChanging(){  return (isRising() || isFalling());                                     }
boolean MyTimer::isHigh()    {  return (_atual_value == MY_TIMER_HIGH);                                 }
boolean MyTimer::isLow()     {  return (_atual_value == MY_TIMER_LOW);                                  }

/********************************************************************************************************
************************************FIM CLASSE MYTIMER***************************************************
*********************************************************************************************************/

MyTimer t(1000, 2000, 0, 200);

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop() {
  t.update();
  
  if (t.isRising()) { 
    digitalWrite(13, HIGH); 
    Serial.print("RISING");
    Serial.print(" - ");
    Serial.println(millis());
  }
    
  if (t.isFalling()){ 
    digitalWrite(13, LOW); 
    Serial.print("FALLING");
    Serial.print(" - ");
    Serial.println(millis());
  }
  
  if (t.isChanging()){  
    Serial.print("CHANGING");
    Serial.print(" - ");
    Serial.println(millis());
  }
  
  if (t.isHigh()){
    Serial.print("HIGH");
    Serial.print(" - ");
    Serial.println(millis());
  }
  
  if (t.isLow()){
    Serial.print("LOW");
    Serial.print(" - ");
    Serial.println(millis());
  }
  
  delay(100);  //esse delay deve ser removido. usado apenas para facilitar a interpretação no serial monitor
}


Atualização - 03/11/2015

A classe mostrada acima (MyTimer) foi modificada para executar funções de callback. Pra isso fiz um vídeo falando sobre o assunto (ponteiro pra funções e funções de callback). Mais abaixo está o código alterado.

Vídeo:



MyTimer.h
enum MyTimerValue  {MY_TIMER_LOW, MY_TIMER_HIGH, MY_TIMER_UNDETERMINED };
enum MyTimerEvents {MY_TIMER_RISING, MY_TIMER_FALLING};

Sketch
/*********************************************************************************************************
************************************CLASSE MYTIMER********************************************************
**********************************************************************************************************/
#include "MyTimer.h" //se colocar as unicas duas linhas do myTimer.h diretamente aqui, simplesmente não funciona... :/

class MyTimer {
  private:
    unsigned long _mref;
    long _timeHigh;
    long _timeLow;
    long _lag; //atraso
    
    void (*_onRising)();                  //ponteiro para funcao do evento 
    void (*_onFalling)();                 //ponteiro para funcao do evento 
    void (*_onChanging)( MyTimerEvents ); //ponteiro para funcao do evento 
    
    MyTimerValue _last_value; //valor anterior
    MyTimerValue _atual_value; //valor atual
    void _undetermine(){
      _last_value = MY_TIMER_UNDETERMINED;
      _atual_value = MY_TIMER_UNDETERMINED;
      update();
    }
  public:
    MyTimer(long timeHigh, long timeLow, long lag, unsigned long mref){
      _timeHigh = timeHigh;
      _timeLow = timeLow;
      _lag = lag;
      _mref = mref;
      _undetermine();
    }
    void setTimeHigh(long timeHigh);
    void setTimeLow(long timeLow);
    void setMillisRef(unsigned long mref);
    void setLag(long lag);
    void update();
    boolean isRising();       //0->1
    boolean isFalling();      //1->0
    boolean isChanging();     //0->1 ou 1->0
    boolean isHigh();         //1
    boolean isLow();          //0
   
    void setOnFalling(  void (*onFalling)() )               { _onFalling  = onFalling;  }
    void setOnRising(   void (*onRising)() )                { _onRising   = onRising;   }
    void setOnChanging( void (*onChanging)(MyTimerEvents) ) { _onChanging = onChanging; }
};
void MyTimer::setTimeHigh(long timeHigh)       { _timeHigh  = timeHigh;  _undetermine(); }
void MyTimer::setTimeLow(long timeLow)         { _timeLow = timeLow;     _undetermine(); }
void MyTimer::setMillisRef(unsigned long mref) { _mref    = mref;        _undetermine(); }
void MyTimer::setLag(long lag)                 { _lag     = lag;         _undetermine(); }
void MyTimer::update(){
  long adjustment = _mref % (_timeHigh + _timeLow);
  long rest       = (millis() + _timeHigh + _timeLow - adjustment - _lag) % (_timeHigh + _timeLow);
  _last_value = _atual_value;
  _atual_value = (rest < _timeHigh ? MY_TIMER_HIGH : MY_TIMER_LOW);
  
  if ( isRising()   && _onRising   ) { (*_onRising)();      }
  if ( isFalling()  && _onFalling  ) { (*_onFalling)();     }
  if ( isChanging() && _onChanging ) { (*_onChanging)(  isRising() ? MY_TIMER_RISING : MY_TIMER_FALLING ); }
}
boolean MyTimer::isRising()  {  return (_last_value == MY_TIMER_LOW && _atual_value == MY_TIMER_HIGH);  }
boolean MyTimer::isFalling() {  return (_last_value == MY_TIMER_HIGH && _atual_value == MY_TIMER_LOW);  }
boolean MyTimer::isChanging(){  return (isRising() || isFalling());                                     }
boolean MyTimer::isHigh()    {  return (_atual_value == MY_TIMER_HIGH);                                 }
boolean MyTimer::isLow()     {  return (_atual_value == MY_TIMER_LOW);                                  }
 
/********************************************************************************************************
************************************FIM CLASSE MYTIMER***************************************************
*********************************************************************************************************/
 
MyTimer t(1000, 2000, 0, 0);
  
//funcao que trata o evento on changing
void onChanging_t(MyTimerEvents mte ) {
  Serial.print("CHANGING: ");
  Serial.print(mte == MY_TIMER_RISING ? " RISING - " : " FALLING - ");
  Serial.println(millis());
}
 
void onFalling_t(){
  digitalWrite(13, LOW); 
  Serial.print("FALLING - ");
  Serial.println(millis());
}

void onRising_t(){
  digitalWrite(13, HIGH); 
  Serial.print("RISING - ");
  Serial.println(millis());
}
 
void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  
  t.setOnFalling ( onFalling_t  );
  t.setOnRising  ( onRising_t   );
  t.setOnChanging( onChanging_t );
  
}
 
void loop() {
  t.update();
   
  delay(100);  //esse delay deve ser removido. usado apenas para facilitar a interpretação no serial monitor
}


Atualização - 02/12/2015

Mais uma atualização no artigo, agora iremos controlar uma quantidade indefinida de eventos que devem ser executados cada um por um determinado intervalo de tempo. Pra isso criei uma nova versão da classe MyTimer.

vídeo:





 /*********************************************************************************************************
************************************CLASSE MYTIMER********************************************************
**********************************************************************************************************/
class MyTimer{
  private:
    unsigned long _mref;
    unsigned long *_times;
    long          _lag;
    unsigned int  _quantidade;
    int           _index_time;
    int           _last_index_time;
    void          (*_onChanging)( int index_time ); //ponteiro para funcao do evento 
  public:
    MyTimer(unsigned long times[], int quantidade, long lag, unsigned long mref){
      _lag = lag;
      _mref = mref;
      _times = times;
      _quantidade = quantidade;
      _undetermine();
    }
    void _undetermine(){
      _index_time      = -1;
      _last_index_time = -1;
      update();
    }
    void setTimes(unsigned long times[], int quantidade){
      _times = times;
      _quantidade = quantidade;
      _undetermine();
      update();
    };
    void setMillisRef(unsigned long mref)         { _mref    = mref;        _undetermine(); }
    void setLag(long lag)                         { _lag     = lag;         _undetermine(); }
    boolean isChanging()                          { return ( _index_time != _last_index_time); }
    boolean isIndexTime(int index)                { return ( _index_time == index); }
    void setOnChanging( void (*onChanging)(int) ) { _onChanging = onChanging; }
    void update();
};

void MyTimer::update(){
  unsigned long s = 0;
  for (int i=0; i<_quantidade;i++){ s += _times[i]; }
  long adjustment  = _mref % s;
  long rest        = (millis() + s - adjustment - _lag) % s;
  _last_index_time = _index_time;
  s = 0;
  for (int i=0; i<_quantidade;i++){ 
    s += _times[i];
    if (rest < s) {  _index_time = i; break; } 
  }
  if ( isChanging() && _onChanging ) { (*_onChanging)(  _index_time  ); }
}
/*********************************************************************************************************
************************************FIM CLASSE MYTIMER****************************************************
**********************************************************************************************************/


//vermelho 800 milissegundos
//amarelo 200 milissegundos
//azul 500 milissegundos
//amarelo 200 milissegundos
//verde 300 milissegundos
//amarelo 200 milissegundos

unsigned long times[] = {800, 200, 500, 200, 300, 200};

MyTimer t1(times, sizeof(times)/sizeof(unsigned long), 100, 0);  //tem um atraso de 100 milissegundos em relação a referencia 0
MyTimer t2(times, sizeof(times)/sizeof(unsigned long), 300, 0);  //tem um atraso de 300 milissegundos em relação a referencia 0
  
void setup() {
  Serial.begin(9600);
  t1.setOnChanging( onChanging_t1 );
  t2.setOnChanging( onChanging_t2 );
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
}

void loop() {
  t1.update();
  t2.update();
}

void onChanging_t1(int index ) {
  if (index == 0 ){ digitalWrite(8, HIGH); digitalWrite(9, LOW); digitalWrite(10, LOW); }
  if (index == 2) { digitalWrite(8, LOW); digitalWrite(9, HIGH); digitalWrite(10, LOW); }
  if (index == 4) { digitalWrite(8, LOW); digitalWrite(9, LOW); digitalWrite(10, HIGH); }
  
  if (index == 1 || index == 3 || index == 5){ digitalWrite(8, HIGH); digitalWrite(9, LOW); digitalWrite(10, HIGH); }
}

void onChanging_t2(int index ) {
  if (index == 0 ){ digitalWrite(5, HIGH); digitalWrite(6, LOW); digitalWrite(7, LOW); }
  if (index == 2) { digitalWrite(5, LOW); digitalWrite(6, HIGH); digitalWrite(7, LOW); }
  if (index == 4) { digitalWrite(5, LOW); digitalWrite(6, LOW); digitalWrite(7, HIGH); }
  
  if (index == 1 || index == 3 || index == 5){ digitalWrite(5, HIGH); digitalWrite(6, LOW); digitalWrite(7, HIGH); }
}


Atualização 13/12/2015


No início desse artigo mostrei uma função chamada time, bem simples, que recriei agora de modo que de pra ser executado algo a cada tantos millissegundos. Segue o código:

boolean time(unsigned long * mref, unsigned long t){
  if (millis() > t && millis() - *mref > t){ *mref = millis(); return true; }
  return false;
}

void setup() {
  Serial.begin(9600) ;
}

void loop() {
  static unsigned long mref1=0;     //millis de referencia. inicializado em 0
  if (time(&mref1, 2000)){ 
    Serial.print("1-"); 
    Serial.println(millis()); 
  }
  
  static unsigned long mref2=300; //millis de referencia. inicializado em 300
  if (time(&mref2, 1800)){ 
    Serial.print("2-"); 
    Serial.println(millis()); 
  }
}



segunda-feira, 14 de setembro de 2015

Arduino - Números aleatórios (repetidos e não repetidos)

No Arduino temos duas funções que nos permitem gerar números aleatórios.

RandomSeed() e Random().

Na verdade os números aleatórios gerados em conjunto pelas funções acimas não são inteiramente aleatórios, eles são na verdade pseudo aleatórios, o que significa dizer, que parecem números aleatórios, mas não são. Na prática, os números aleatórios gerados pelas funções acima, são pré determinados, dependendo do valor passado para RandomSeed(valor).

Normalmente RandomSeed é chamado no início da execução do programa e inicializado com um valor qualquer, porém se na inicialização de RandomSeed for passado sempre o mesmo valor, a sequencia de números aleatórios gerados será sempre a mesma.

Se a intenção for gerar sequencia de números diferentes a cada inicialização de randomSeed, deve-se então, ser passado valores diferentes a cada inicialização.

Existem algumas alternativas no Arduino para conseguirmos números (seed: que significa semente) diferentes a cada vez que se deseja inicializar o RandomSeed. A mais utilizada é utilizar o valor lido de uma porta analógica não conectada, que por não estar conectada, acaba retornando valores diferentes. Nos casos em que os números aleatórios são gerados a partir de uma ação externa do arduino, como o pressionar de um botão, a leitura de um valor ou qualquer outra ação, o valor a ser utilizado para inicializar o RandomSeed, pode ser baseado no valor de millis() do arduino, que é o tempo em millissegundos já passados desde que o arduino foi ligado. Outra possibilidade seria ainda gravar na eeprom o ultimo utilizado para inicializar, e na próxima vez que o arduino inicializar o RandomSeed, o valor gravado na eeprom ser incrementado.

Como vimos, então, randomSeed é utilizado para gerar uma sequencia aleatória de números. Agora vamos ver como funciona a função random(), que é a função que retorna o número aleatório, quando desejamos um.

random() pode ser chamada com dois, ou com um parâmetros. Quando chamada com dois, significa que queremos números aleatórios entre o primeiro e o ultimo valor, por exemplo, random(10, 20) irá retornar somentes os seguintes valores {10,11,12,13,14,15,16,17,18 e 19}. Percebam que 20 não será retornado.

Quando chamada com um único parâmetro, como por exemplo, random(300), significa que irá retornar qualquer valor entre 0 e 299.

abaixo tem um exemplo retirado da página do Arduino:

https://www.arduino.cc/en/Reference/Random

long randNumber; 
void setup() { 
  Serial.begin(9600); 
  // if analog input pin 0 is unconnected, random analog 
  // noise will cause the call to randomSeed() to generate 
  // different seed numbers each time the sketch runs. 
  // randomSeed() will then shuffle the random function. 

  randomSeed(analogRead(0)); 
} 

void loop() { 
  // print a random number from 0 to 299 
  randNumber = random(300); 
  Serial.println(randNumber); 

  // print a random number from 10 to 19 
  randNumber = random(10, 20); 
  Serial.println(randNumber); 
  delay(50); 
}



Aleatórios não repetidos


Quando projetamos um programa que necessita de valores aleatórios, há basicamente duas situações diferentes, que são os casos onde os números sorteados podem se repetir, e outras onde os números não podem se repetir.

Num bingo por exemplo, os números sorteados nunca se repetem, pois a cada valor sorteado o mesmo é removido do globo, enquanto que numa roleta de cassino os valores sorteados podem ser repetidos.

Para conseguirmos gerar números não repetidos, é necessário gerarmos uma lista de números, embaralharmos os mesmos (muda-los de posição de modo aleatório), e posteriormente lermos os números sequencialmente do início da lista até o fim. Assim garantimos que se tenhamos 10 números na lista não iremos ter sorteios repetidos nos primeiros 10 números sorteados.

Para facilitar esse processo, criei uma classe que faz esse trabalho.


/**********************************************************************************
************************************CLASSE UNIQUE RANDOM***************************
**********************************************************************************/

class UniqueRandom{
  private:
    int _index;
    int _min;
    int _max;
    int _size;
    int* _list;
    void _init(int min, int max) {
      _list = 0; 
      if (min < max) { _min = min; _max = max; } else { _min = max; _max = min; }
      _size = _max - _min; 
      _index = 0;
    }    
  public:
    UniqueRandom(int max)           { _init(0,   max); randomize(); } //construtor com 1 parametro
    UniqueRandom(int min, int max)  { _init(min, max); randomize(); } //construtor com 2 parametros
    
    void randomize() {
      if (_list == 0) { _list = (int*) malloc(size() * sizeof(int)); }  
      for (int i=0; i<size(); i++) {   _list[i] = _min+i;  }   //preenche a lista do menor ao maior valor
      
      //embaralha a lista
      for (int i=0; i<size(); i++) {  
        int r = random(0, size());     //sorteia uma posição qualquer
        int aux = _list[i];               
        _list[i] = _list[r];
        _list[r] = aux;
      }
    }
    
    int next() {                                  //retorna o proximo numero da lista
      int n = _list[_index++];
      if (_index >= size() ) { _index = 0;} //após recuper o ultimo numero, recomeça na posicao 0
      return n;
    }
    
    int size() { return _size; }
    
    ~UniqueRandom(){ free ( _list ); }  //destrutor
};
/**********************************************************************************
************************************FIM CLASSE UNIQUE RANDOM***********************
**********************************************************************************/


/**********************************************************************************
************************************SETUP / LOOP***********************************
**********************************************************************************/

UniqueRandom ur(10, 30); //declaracao do objeto unique random

void setup() {
  Serial.begin(9600);
  
  int seed = 0;
  
  for (int i=0; i<10; i++) {
    seed += ( analogRead(A0) + analogRead(A1) + analogRead(A2) + analogRead(A3) + analogRead(A4) + analogRead(A5) ) ;
    delay(10);
  }
  randomSeed(seed);
  
  Serial.print("Seed: ");
  Serial.println(seed);
  
}

void loop() {
    
  Serial.println("");
  Serial.print( "Iniciando com " );
  Serial.print( ur.size() );
  Serial.print( " itens: " );
    
  ur.randomize();  //gera os numeros aleatoriamente
    
  for (int i=0; i<ur.size(); i++){
    Serial.print( ur.next() );
    Serial.print( " " );
  }
    
  delay(100);
}

/**********************************************************************************
************************************FIM SETUP / LOOP*******************************
**********************************************************************************/




Exemplo de uso com matriz de led 8x8



#include "LedControl.h"

/**********************************************************************************
************************************CLASSE UNIQUE RANDOM***************************
**********************************************************************************/

class UniqueRandom{
  private:
    int _index;
    int _min;
    int _max;
    int _size;
    int* _list;
    void _init(int min, int max) {
      _list = 0; 
      if (min < max) { _min = min; _max = max; } else { _min = max; _max = min; }
      _size = _max - _min; 
      _index = 0;
    }    
  public:
    UniqueRandom(int max)           { _init(0,   max); randomize(); } //construtor com 1 parametro
    UniqueRandom(int min, int max)  { _init(min, max); randomize(); } //construtor com 2 parametros
    
    void randomize() {
      if (_list == 0) { _list = (int*) malloc(size() * sizeof(int)); }  
      for (int i=0; i<size(); i++) {   _list[i] = _min+i;  }   //preenche a lista do menor ao maior valor
      
      //embaralha a lista
      for (int i=0; i<size(); i++) {  
        int r = random(0, size());     //sorteia uma posição qualquer
        int aux = _list[i];               
        _list[i] = _list[r];
        _list[r] = aux;
      }
    }
    
    int next() {                                  //retorna o proximo numero da lista
      int n = _list[_index++];
      if (_index >= size() ) { _index = 0;} //após recuper o ultimo numero, recomeça na posicao 0
      return n;
    }
    
    int size() { return _size; }
    
    ~UniqueRandom(){ free ( _list ); }  //destrutor
};
/**********************************************************************************
************************************FIM CLASSE UNIQUE RANDOM***********************
**********************************************************************************/

/*
 pin 4 is connected to the DataIn 
 pin 6 is connected to the CLK 
 pin 5 is connected to LOAD 
 */

LedControl lc=LedControl(4,6,5,1); //1 max7219
UniqueRandom ur(64); //declaracao do objeto unique random

/* we always wait a bit between updates of the display */
unsigned long delaytime=500;

void setup() {
  int seed = 0;
  
  for (int i=0; i<10; i++) {
    seed += ( analogRead(A0) + analogRead(A1) + analogRead(A2) + analogRead(A3) + analogRead(A4) + analogRead(A5) ) ;
    delay(10);
  }
  randomSeed(seed);
  
  lc.shutdown(0,false);
  lc.setIntensity(0,8);
  lc.clearDisplay(0);
}

void loop() { 
  lc.clearDisplay(0);
  delay(delaytime/2);
  
  ur.randomize();
    
  for(int i=0; i<64; i++) {  
    int r = ur.next();
      
    int l = r / 8;
    int c = r % 8;
     
    delay(100);
    lc.setLed(0, l, c, HIGH );
  }
  
  
  ur.randomize();
    
  for(int i=0; i<64; i++) {  
    int r = ur.next();
      
    int l = r / 8;
    int c = r % 8;
     
    delay(100);
    lc.setLed(0, l, c, LOW );
  }
  
  delay(delaytime*2);
}

Atualização 25/11/2015 - Números do tipo double ou float (com casas decimais)

Essa semana me pediram como fazer para gerar números aleatórios com números números do tipo double ou float, ou seja, com casas decimais. Na verdade a ideia é até bem simples, vamos supor que você queira números aleatórios entre 5.58 e 9.44... considerando os números com até duas casas decimais nesse intervalo.

Nesse caso basta multiplicar o 5.58 por 100 (ou 10² ou pow(10, 2)  ) o que daria um número inteiro igual a 558. Aplicando o mesmo ao 9.44 teremos 944. Se fosse com 3 casas decimais, multiplicaríamos o número por 1000 (ou 10³ ou pow(10,3) ).

Tendo convertido os dois números para inteiros, bastaria agora utilizar a função random normalmente, porém o valor retornado precisa ser divido pelo valor multiplicado anteriormente para que o mesmo volte à mesma faixa utilizada inicialmente, tendo o cuidado de armazenar o resultado em um campo do tipo double, para que as casas decimais não sejam perdidas.

Para facilitar o uso, criei um exemplo com uma função que chamei de randomDouble, onde são passados 3 valores: o valor mínimo, o valor máximo, e a quantidade de casas decimais a ser considerada:



Código:

double randomDouble(double min, double max, int numCasas){
  long _min = min * pow(10, numCasas) + 0.1;  //0.1--> para corrigir erro da funcao pow. funciona até 4 casas. mais que isso da erro no calculo.
  long _max = max * pow(10, numCasas) + 0.1;
  
  return (double) random(_min, _max) / pow(10, numCasas) ; 
}

void setup() { 
  Serial.begin(9600); 
  randomSeed(analogRead(0)); 
} 

void loop() { 
  double randNumber = randomDouble(10.20, 11.05, 2);
  Serial.println(randNumber); 
  
  delay(500); 
}