Publicidade:

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()); 
  }
}



13 comentários:

  1. Obrigado por compartilhar seu trabalho!!!

    ResponderExcluir
    Respostas
    1. obrigado.
      Incluí mais um vídeo sobre o assunto, tá no final desse mesmo artigo se lhe interessar.
      abraço!

      Excluir
  2. Obrigado por compartilhar seu trabalho!!!

    ResponderExcluir
  3. Fabiano, parabéns pelo trabalho, obrigado mesmo por compartilhar seu trabalho, me ajudou muuuuito, grande abraço.

    ResponderExcluir
  4. Boa noite amigo Fabiano, estava precisando de uma função "sem delay"para manter umas solenoides ligadas por alguns segundos e mais uma vez seu trabalho foi crucial para resolver este dilema.
    Fiz pouquíssimas mudanças na sua função e agora posso escrever digitalWrite(pino,tempoligado(xxx)); e nao preciso me preocupar com os delays... segue a função modificada abaixo:

    int tempoligado(long timeHigh) {
    long resto = (millis() - timeHigh );
    return (resto < timeHigh ? HIGH : LOW);
    }

    Muuuito Obrigado por dispor do seu tempo a ensinar estes pobres mortais!!!
    Nao abusando você poderia escrever um artigo que como usar o Atmega 16/32 com Arduino?

    ResponderExcluir
    Respostas
    1. Olá Ernando. Desculpa a demora em responder. Fim de ano, férias, a gente da uma desligada. Que bom que funcionou, é bom saber que o conteudo tá ajudando. Sobre o Atemega16/32 infelizmente não tenho experiencia com eles ainda. vou ficar devendo. abraço.

      Excluir
    2. Olá Ernando. Desculpa a demora em responder. Fim de ano, férias, a gente da uma desligada. Que bom que funcionou, é bom saber que o conteudo tá ajudando. Sobre o Atemega16/32 infelizmente não tenho experiencia com eles ainda. vou ficar devendo. abraço.

      Excluir
  5. Olá Fabiano, primeiramente quero parabenizar pelo excelente trabalho que está realizando.Tenho uma dúvida a respeito da função que você criou MyTime, no local onde seta o tempo para o ciclo MyTimer t(1000, 2000, 0, 0); tem como editar esse tempo via software?

    ResponderExcluir
  6. Boa noite Fabiano,
    Tem como montar o em um único arquivo o programa por inteiro, somente com um temporizador e uma saída. Onde encontro a biblioteca "MyTimer.h"?

    ResponderExcluir
  7. Buenas noches, Fabiano, como te puedes dar cuenta hablo español, necesito tu ayuda, solo quiero un programa el cual sea editable, que jale con tiempos de atraso y retraso me podrias dar tu correo, lo mas pronto posible para pasarte lo que llevo de mi programa, por ejemplo yo hago los codigos sin clases, solo realizo metodos que mando llamar en el void loop(), eso esta mal?, en mis proyectos de universidad me funcionan, entonces nose cual sea el caso de agruparlos en clase, pienso yo que si es por leerlo bien, a mi se me dificulta leerlo por clases, espero tu pronto EMAIL y tu pronta respuesta por correo.

    De antemano, gracias.

    Estudiante de la carrera de ingenieria Mecatronica.

    ResponderExcluir
  8. Estava com dificuldade em fazer uma seta para um carrinho ps2+arduino, toda vez que usava delay afetava todo o código, seu codigo foi ótimo, exatamente o que eu precisava.

    ResponderExcluir
  9. Boa noite Fabiano, estou com alguns problemas para montar um código que utiliza seis saídas de relés, duas são por sensor de temperatura e as outras quatro por temporizador. O problema é que nas saídas temporizadas, cada uma tem um início e um fim específicos dentro de um período de 24 horas. Gostaria de saber se você pode me auxiliar.

    ResponderExcluir
  10. Fabiano parabéns pelo trabalho e obrigado por compartilhar, sempre que aparece uma dúvida recorro aos seus posts ou vídeos, você é o cara

    ResponderExcluir