LusoRobótica - Robótica em Português

Sistemas específicos => Arduino / AVR => Tópico iniciado por: r16 em 27 de Dezembro de 2014, 19:49

Título: como ter um delay ou millis sem este interferir com o loop.
Enviado por: r16 em 27 de Dezembro de 2014, 19:49
vivas companheiros,
tenho o seguinte codigo:

tenho um sensor dht11, que le a temperatura e humidade, e quero ter no mesmo codigo um outro que me ligue um pin durante 5 segundos que espere 20 e volte a ligar o pin por mais 5 segundos e por ai adiante...
so que eu fiz com um delay... e este delay que fiz interfere com o resto do codigo e eu nao queria isso alguem me ajuda a modificar sff:

Código: [Seleccione]
#include <DHT11.h>
int motor = 10; // Motor liga no pino 10 do mini pro
int fogger = 11; // fogger liga no pino 11 do mini pro

int sensor=4;
DHT11 dht11(sensor);
void setup()
{
   
    pinMode(fogger, OUTPUT);
   pinMode(motor, OUTPUT);
   Serial.begin(9600);
  while (!Serial) {
      ;
    }
 }

void loop()
{
digitalWrite(motor, HIGH); //Motor Liga durate 5 segundos
delay(5000);
digitalWrite(motor, LOW);
delay(20000); //Espera 20 segundos


{
  int err;
  float temp, humi;
  if((err=dht11.read(humi, temp))==0)
  {
    Serial.print("temperatura:");
    Serial.print(temp);
    Serial.print(" humidade:");
    Serial.print(humi);
      Serial.println();
  if (humi<=40){
      Serial.print(" FOGGER LIGADO");
       Serial.println();
      digitalWrite(fogger,HIGH);
     }
  if (humi>=50){
      Serial.print(" FOGGER DESLIGADO");
       Serial.println();
         digitalWrite(fogger,LOW);
     }
   
     

   
  delay(DHT11_RETRY_DELAY); //delay for reread
 
 
 }
  }
  }
 
 
 
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: Njay em 27 de Dezembro de 2014, 19:59
Podes usar "multitasking cooperativo":

http://troniquices.wordpress.com/2010/05/24/arduino-a-fazer-varias-coisas-ao-mesmo-tempo/ (http://troniquices.wordpress.com/2010/05/24/arduino-a-fazer-varias-coisas-ao-mesmo-tempo/)

Se aprenderes a técnica, que é simples, depois fazes qualquer coisa.

Quando já tiveres "pro" podes aprender mais umas coisas no 2º artigo, mas este só tenho em Inglês:

http://embeddeddreams.com/site/2011/07/23/more-on-doing-several-things-at-the-same-time/ (http://embeddeddreams.com/site/2011/07/23/more-on-doing-several-things-at-the-same-time/)
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: senso em 27 de Dezembro de 2014, 20:07
Ou a solução básica, abrir o exemplo blink without delay no IDE do arduino, está lá, comentado e explicado, faz-me confusão como é que tanta gente nem para os exemplos olha  ::)
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: r16 em 27 de Dezembro de 2014, 20:17
senso foi a primeira coisa que fiz,...
no entanto esse exemplo faz com que o led pisque 1 em 1 segundo.
nao entendo como é que a partir dele consigo fazer com que ligue 5 seg e espere 20
obrigado
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: senso em 27 de Dezembro de 2014, 20:47
É mudar os tempos no if.
Introduz mais que um if, faz um if para meter on, outro para meter off.
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: r16 em 27 de Dezembro de 2014, 22:10
ok,,,alterei para:

Código: [Seleccione]
#include <DHT11.h>
    int motor = 13; // Motor liga no pino 10 do mini pro
    int fogger = 11; // fogger liga no pino 11 do mini pro
    unsigned long time = 0;
    int sensor=4;
    int aciona = 0;
    DHT11 dht11(sensor);
    void setup()
    {
       
        pinMode(fogger, OUTPUT);
       pinMode(motor, OUTPUT);
       Serial.begin(9600);
      while (!Serial) {
          ;
        }
     }
     

     
     
     
     
    void loop()
    {
      if(millis() - time < 4999 && aciona ==0)
    {
      digitalWrite(motor,HIGH);
      aciona = 1;
    }
    if (millis() - time > 5000 && millis() - time < 20000 && aciona ==1)
    {
    digitalWrite(motor, LOW);
    time = millis();
    aciona = 0;
    }
     
     
    {
      int err;
      float temp, humi;
      if((err=dht11.read(humi, temp))==0)
      {
        Serial.print("temperatura:");
        Serial.print(temp);
        Serial.print(" humidade:");
        Serial.print(humi);
          Serial.println();
      if (humi<=40){
          Serial.print(" FOGGER LIGADO");
           Serial.println();
          digitalWrite(fogger,HIGH);
         }
      if (humi>=50){
          Serial.print(" FOGGER DESLIGADO");
           Serial.println();
             digitalWrite(fogger,LOW);
         }
       
         
     
       
      delay(DHT11_RETRY_DELAY); //delay for reread
     
     
     }
      }
      }


no entanto o pino 10 fica ligado por 5 segundos depois desliga mas nao volta a ligar passado os 20 segundos.... onde estou a errar?
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: senso em 27 de Dezembro de 2014, 22:24
só fazes o update da variavel time num dos milis, para esse tipo de coisas prefiro usar uma variavel para cada if.
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: r16 em 27 de Dezembro de 2014, 22:46
nao consigo atinar com isto.... ajuda -me  :o
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: ralex em 27 de Dezembro de 2014, 23:29
Boa noite.

Dá uma olhada a este link...

https://learn.adafruit.com/multi-tasking-the-arduino-part-1?view=all (https://learn.adafruit.com/multi-tasking-the-arduino-part-1?view=all)
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: r16 em 28 de Dezembro de 2014, 00:10
Boa noite.

Dá uma olhada a este link...

https://learn.adafruit.com/multi-tasking-the-arduino-part-1?view=all (https://learn.adafruit.com/multi-tasking-the-arduino-part-1?view=all)

obrigado era mesmo isto
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: Srgiofonseca em 28 de Dezembro de 2014, 02:58
Eu não percebi bem o que queres
mas se for o que penso teria resolvido assim,
/************************************************************************************
**** Autor: Sérgio Fonseca
**** Data: 28-12-2014
**** Versão: 1.1
****
**** Descrição: Envia para a porta serial a temperatura a cada segundo e ao
**** mesmo tempo executa ciclicamente ligar um LED 5 segundos e desligar 20 segundos
****
**** Observações: Não tem grande precisão no tempo uma vez que cada instrução influencia o tempo
****
***********************************************************************************/

/*____Declarações e variáveis ________________________________*/
int led1=13;         //LED Que acende 5 segundos e esta apagado 20segundos (cíclico)
int sensor=4;       // Sensor de temperatura ligado no pin 4 (analógico)
int contador;        // contador que incrementa aproximadamente a cada 1 segundo
int temperatura;   //variável que guarda o valor da temperatura

/*________________Rotina de configuração ao iniciar o Arduíno______________________*/
void setup()
{
 
  Serial.begin(9600);
  pinMode(led1,OUTPUT);
  contador=0;
}

/*________________Programa____________________________________*/
void loop()
{
  if (contador <4)   
    {ligaled();}       // se contador for menor que 4 chama a função ligaled
  desligaled();       // se for maior que 4 chama a função desligaled

}

/*_______________Função ligaled_______________________________________*/

void ligaled()
{
  digitalWrite(led1, HIGH);        // liga o led
  incrementa();                        // chama a função incrementa
}

/*___________Função desligaled_________________________________________*/

void desligaled()
{
  if (contador > 4)
  {digitalWrite(led1, LOW);}       // se o contador for maior que 4 desliga o led
  incrementa();                         // caso o contador seja inferior a 4 chama a função incrementa
}

/*___________________Função incrementa____________________________________________*/

void incrementa()                            // função que demora aproximadamente um segundo a ser executada
{
//____ trata os dados do sensor de temperatura e envia__________
 
  temperatura = analogRead(sensor);     //Coloca o valor analógico do pino na variavel
  Serial.println("sensor temperatura");   //envia o valor da variável para a porta serie com quebra de linha
  Serial.println(temperatura);
  Serial.println(contador);     

// ________Fim do tratamento de dados a enviar___________       

  contador = contador + 1;              // incrementa o contador
  delay(998);                                 // espera aproximadamente um segundo (1s)
  if (contador == 25)                     // 20 segundos + 5 segundos
  {contador = 0;}                           // inicia o contador
}

/*__________________________FIM____________________________________________________*/


Agradecia que comentassem o código, este é apenas um exemplo.
O fórum serve para (ajudar) aprender e ensinar.
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: samc em 28 de Dezembro de 2014, 15:04
O link que enviaram acima está muito claro e deve ter sido só fazer copy paste para ter o problema resolvido
O teu código abaixo "não interfere" com o loop de uma forma um bocado relativa. Ou seja, não está na função principal mas quando é chamado utilizas delays e caso leias o link da adafruit vês que essa não é a forma mais "profissional" de fazeres as coisas
Simplesmente estás a dizer ao microcontrolador para durante esses milisegundos ficar em idle e não executar nenhuma tarefa (de forma simplista)
O ideal é usar a função milis e mais algum código para verificar a alteração no tempo em vez de utilizares delays. É claro que nesse exemplo simples e meio de experimentação não vale a pena elaborar muito o código. Em aplicações mais complexas, isso sim elimina o delay por completo (para mim a única função mais razoável que poderia ter seria na inicializacao do programa em que podes querer estabilizar as leituras de alguns sensores ou escrever algum texto via série para debug)
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: senso em 28 de Dezembro de 2014, 15:40
Com ifs e milis(ou implementação caseira do mesmo), por exemplo código retirado de um projecto adormecido para exemplo:
Código: [Seleccione]
if(milis() - lastMilis > 250){
LEDPORT ^= ((1<<LED1));
lastMilis = milis();
//printEncoders();
encoder1Count += encode1_read4();
encoder2Count += encode2_read4();

setDac(0,0,1,encoder1Count);

getPowerData();
debug1();
updateScreen();
}

if(milis() - buttonsMilis > 10){
buttonsMilis = milis();
getBtnStatus();
}

if(milis() - measureMilis > 50){
measureMilis = milis();

}
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: LuísR.A. em 28 de Dezembro de 2014, 16:46
vou só aproveitar um pouco para perguntar uma cena.

Njay tive uma idea e gostava da tua opinião.
Para fazer tipo 2-3 programas a correr em paralelo poderia fazer isto?

Criar um interrupt periódico com um timer de 1 em 1 ms.
Programa1 começa.
Interrupt é chamado - salta para Programa2.
Interrupt é chamado - salta para Programa3.
Interrupt é chamado - salta para Programa1.
Por ai a fora.

A ideia é ter um interrupt handler que tem tipo um variavel que vai de 0-2, ou seja o numero do programa a correr (usando um if), e chama essa função.
O meu problema é que isto teria de ser de forma a que o interrupt chama-se a função mas era mesmo um Jump, não um jump and link para voltar. Não é?
Claro que teria de ter cuidados para os programas não se atropelarem mas isso é trabalho que teria de ser feito e cuidados a ter. Tambem algumas funções criticas podiam desativar o interrupt por momentos.
Isto seria para um ARM-M0 de 48Mhz ou um MCU mais rápido.
Se disser alguma asneira calem-me :p
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: senso em 28 de Dezembro de 2014, 17:15
Tens de guardar stack para garantir que não dás cabo das variaveis todas.
Tens tanto RTOS para micros que até doi, o mais comum actualmente é o ChibiOS, permite fazer o que queres, e muito mais.
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: StarRider em 28 de Dezembro de 2014, 17:56
vou só aproveitar um pouco para perguntar uma cena.

Njay tive uma idea e gostava da tua opinião.
Para fazer tipo 2-3 programas a correr em paralelo poderia fazer isto?

Criar um interrupt periódico com um timer de 1 em 1 ms.
Programa1 começa.
Interrupt é chamado - salta para Programa2.
Interrupt é chamado - salta para Programa3.
Interrupt é chamado - salta para Programa1.
Por ai a fora.

A ideia é ter um interrupt handler que tem tipo um variavel que vai de 0-2, ou seja o numero do programa a correr (usando um if), e chama essa função.
O meu problema é que isto teria de ser de forma a que o interrupt chama-se a função mas era mesmo um Jump, não um jump and link para voltar. Não é?
Claro que teria de ter cuidados para os programas não se atropelarem mas isso é trabalho que teria de ser feito e cuidados a ter. Tambem algumas funções criticas podiam desativar o interrupt por momentos.
Isto seria para um ARM-M0 de 48Mhz ou um MCU mais rápido.
Se disser alguma asneira calem-me :p

Boas,

A primeira questão que deves  colocar é se realmente necessitas de um verdadeiro multitasking ou
se uma simples state machine pode servir. Em termos de desempenho uma state machine tem
vantagens, um sistema verdadeiramente multitasking é muito mais versátil.

Nos cores ARM uso o Systick (e respectiva interrupção) para implementar um round-robin com uma
fracção de 5us por tarefa. Cada tarefa deve ter uma estrutura associada onde vais guardar o
estado da mesma, e podes até implementar um sistema de prioridades, e cada estrutura deve ter
uma área que sirva de stack. O switching de tarefas é relativamente simples, guardar o code pointer, stack pointer, interrups state, etc, e activar o novo code pointer, stack pointer, etc. Deves também
manter e actualizar (na dita INTR) uma variável com o tickcount que é muito útil para implementar
delays, sendo que para isso deves ter uma função de delay global que seja transversal a todas as
tarefas de forma a que os timings sejam assegurados.

É claro que numa verdadeira multi-tarefa é depois necessário controlar o acesso aos periféricos, e
uma vez que podes aceder aos mesmos dentro de diversas tarefas concorrentes, deves implementar
um sistema de semáforos para evitar que duas tarefas possam aceder por exemplo à UART1 ou usar
a mesma buffer da mesma enquanto a anterior não libertar este periférico. E isto é aplicável a todos
os periféricos, memória partilhada, canais DMA, etc.

Deves ponderar muito bem se necessitas mesmo de multi-tarefa, uma state-machine mesmo que
seja muito rude pode resolver a questão sem estes problemas.

Se realmente pretendes multi-tarefa tens toda a vantagem em recorrer a um RTOS que já tenha
dado provas, não vale a pena queimar neurónios e correr riscos a reinventar a roda,  a não ser que
seja por um desafio pessoal.

Geralmente só uso o RTEMS nos cores SPARC (MCUs LEON)  e o RTX da ARM/Keil nos cores ARM
mas existem por ai muitos RTOS com licença FOSS que podes usar sem problemas.

Abraços,
PA
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: Njay em 28 de Dezembro de 2014, 18:31
O já aqui foi dito, sendo que saliento a pergunta: qual é a aplicação?

Se é para pura recreação tua, aprender e tal, fiquei um pouco na dúvida se os teus 3 programas são interrompidos "sem saberem", mas vamos assumir que sim. Em C há um par de funções que pode ajudar a fazer esse tipo de coisa, setjmp e longjmp (http://en.wikipedia.org/wiki/Setjmp.h (http://en.wikipedia.org/wiki/Setjmp.h)) . Precisas de ver a documentação do compilador que estiveres a usar para ficares a par de todos os detalhes. Já usei isto por exemplo para "simular" excepções, em que podes estar algures dentro duma função que chamou outra que chamou outra que chamou outra que ... e voltar "instantaneamente" para a função "de topo". Lembro-me de ter usado na implementação dum parser preditivo.

A decisão que vais tomar na interrupção sobre qual o próximo "programa" a executar é o trabalho do "scheduler", e podes implementar N politicas de atribuição do CPU.

vou só aproveitar um pouco para perguntar uma cena.

Njay tive uma idea e gostava da tua opinião.
Para fazer tipo 2-3 programas a correr em paralelo poderia fazer isto?

Criar um interrupt periódico com um timer de 1 em 1 ms.
(...)
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: StarRider em 28 de Dezembro de 2014, 19:36

A ideia é ter um interrupt handler que tem tipo um variável que vai de 0-2, ou seja o numero do programa a correr (usando um if), e chama essa função.

Boas novamente,

Todo o código que é executado dentro de uma INTR deve ser o mais conciso possível, chamar funções que
tenham loops ou bastante processamento de dentro de uma ISR alem de ser má pratica vai possivelmente
causar stack overflow mesmo em ISR que não sejam reentrantes.

Vais chamar a "função" dentro da ISR e esperar que esta retorne ? Mesmo sendo uma função não blocking
vais ter que dentro dessa mesma função ter um state machine se pretendes executar código mais complexo,
e nunca vais poder ter ciclos infinitos.

Abraços,
PA
Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: LuísR.A. em 28 de Dezembro de 2014, 19:38
Obrigado pelo info. Não sabia sobre o setjmp e longjmp.

Para multitasking a sério usaria um RTOS de certeza, isso dá demasiado trabalho.

Eu em todos os programas tenho sempre o SysTick de 1ms em 1ms e uma função delay parecida ao do arduino. Isto para poder contar tempo.

A idea por agora é mesmo por recreação. Isto porque andei a mexer nos legos com programação de blocos - dá para ter 3 linhas de código e fiquei curioso
A ideia é começar a aprender mais sobre como controlar vários actuadores que dependem de varios sensores.
Neste caso é mais uma curiosidade, os programas não precisam de saber se foram interrompidos.

Vou ter de ver mais sobre isto depois :p



Título: Re: como ter um delay ou millis sem este interferir com o loop.
Enviado por: LuísR.A. em 28 de Dezembro de 2014, 19:41

Boas novamente,

Todo o código que é executado dentro de uma INTR deve ser o mais conciso possível, chamar funções que
tenham loops ou bastante processamento de dentro de uma ISR alem de ser má pratica vai possivelmente
causar stack overflow mesmo em ISR que não sejam reentrantes.


Era isso uma das duvidas que tinha, obrigado!

Então é melhor ir para os RTOS se precisar de tal coisa.

Por agora vou ver melhores praticas simples. Tenho de começar a para de usar delays :p