Multi-tarefa no Arduino Mega

Bom dia galera,
Mais uma vez venho pedir uma ajuda ao grupo... Estou realizando um projeto com sensor biométrico embarcado no arduino.
Toda a parte de operação (Keypad e LCD) e tratamento de erros estão funcionando normalmente (SEM ETHERNET SHIELD), a segunda etapa do projeto é no próprio arduino, rodar um WebServer (no SD Card) para mostrar algumas informações de logs e sobre o próprio Arduino.
Qual a dificuldade? Deixar o Ethernet Shield (W5100) rodando em tempo real (via Browser) enquanto um usuário cadastro uma nova digital por exemplo.
No loop principal tenho o seguinte sketch:

void loop() // loop
{

count = 0;
idcount = 0;
PARTE DO ETHERNET SHIELD DEVERIA RODAR AQUI.

lcd.clear(); 
lcd.setCursor(1,0);
lcd.println("BioMult Access ");

while (1){   //Mantém uma mensagem no display LCD (como data e hora e nome do //projeto. Se remover o While, o LCD fica piscando loucamente

lcd.setCursor(4,1);
lcd.print(rtc.getTimeStr());

val = digitalRead(sensorir); //variavel que lê o IR e habilita o Sensor de Impressão Digital
customKey = customKeypad.getKey();

if (val == HIGH){ //lógica para atuar IR
lcd.clear();
verify();
}
else if(customKey == '*'){

value = 0;
estado = true;
lcd.clear(); 
lcd.setCursor(0,0);
lcd.print("Digite o ID: ");
while(count < 3) 
{

customKey = customKeypad.getKey();
if(customKey >= '0' && customKey <= '9') {
value = value * 10 + customKey - '0';
lcd.setCursor(12,0);
lcd.print(value);
delay(200);
idcount += 1;
}
if(customKey == '*'){
lcd.clear();
lcd.print("Digite o ID: ");
value = 0;
}

if((customKey == '#') || (idcount == 3)){
if(value < 254){ //limita até 253 o numero de usuários, devido a eeprom
id = value;
enroll();
value = 0;
}

if(value > 254){
lcd.clear();
lcd.print(" **NUMERO** ");
lcd.setCursor(0,1);
lcd.print(" ***INVALIDO*** ");
delay(3000);
loop();
}
}
} 
} 

else if(customKey == 'D'){
value = 0;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Excluir ID: ");
while(customKey = 2){
customKey = customKeypad.getKey();
if(customKey >= '0' && customKey <= '9') {
value = value * 10 + customKey - '0';
lcd.setCursor(12,0);
lcd.print(value);
delay(200);
}
if(customKey == '#'){
if(value == (pin = EEPROM.read(value))){
id = value;
deletar(); 
}
else if(value != (pin = EEPROM.read(value))){
lcd.clear();
lcd.setCursor(0,0);
lcd.print(" ****ERRO**** ");
lcd.setCursor(0,1);
lcd.print(" ***EEPROM*** ");
delay(3000);
loop();
}
}
if(customKey == '*'){
lcd.clear();
lcd.print("Excluir ID: ");
value = 0;
}
} 
}
}
}

Poderiam me ajudar?

Acredito que dê pra fazer sem problemas amigo, vai depender mais do tamanho da página, pois o Arduino tem pouca RAM. Acho que seria melhor Usar a lib Flash.h para armazenar a página e o SD apenas para salvar os dados caso precise.
http://arduiniana.org/libraries/flash/

Vê também se esse link ajuda, não é bem o que você quer mas tá bem explicadinho

http://www.ladyada.net/learn/arduino/ethfiles.html

Aqui os metódos do SD e Ethernet

Explica-me o que pretendes dizer com isto:

Deixar o Ethernet Shield (W5100) rodando em tempo real (via Browser) enquanto um usuário cadastro uma nova digital por exemplo.

De que dados e que vais fazer logs?

Bubu, eu quis dizer que as páginas deveriam ser mostradas no browser, independente do que o usuário estivesse fazendo no teclado/sensor biométrico.

Por exemplo, as páginas não ficaram no SD Card, eu estou configurando um servidor no RPi, para exibir páginas em css. O Arduino apenas vai ir até o IP do raspberry.

O meu problema é não poder exibir as telas, devido não entender como faria para duas tarefas sejam executadas sem afetar uma na outra. Tentei usar millis, mas a página HTML nem abre. Eu estou pensando em todas as alternativas para resolver. Até em usar um Arduino UNO somente para Ethernet, mas ficaria muito trabalhoso em enviar tudo que acontece no Mega para o UNO.

Bom dia Osias. Tudo bem?

Velho, eu estou configurando um raspberry para servir as páginas web, ficará até mais rápido. Usei o SD nos testes. Com raspberry eu posso usar css, para dar um efeito nas páginas.
A maior dificuldade é ter o processo do Ethernet web rodando, e não travar o programa principal. Não consigo deixar os dois trabalhando de forma simultânea. Eu vi ontem a noite o site da Layada, vai me ajudar em algumas coisas!!!

Ok... Creio não ter percebido o que queres fazer...

O RPi vai ter a página web e o Arduino vai aceder a ela?

Em todo o caso, para o Arduino enviar dados para o RPi, podes sempre criar um programa para correr no RPi que esteja sempre ligado ao Arduino.

Ainda melhor, podes meter o Arduino a enviar os dados por UDP e o RPi a ouvir (procura por beej networking guide para ver como programar isto em Posix). Desta forma o Arduino enviava sem parar (muito) e se o RPi perdesse um telegrama não haveria muito problema se a frequência for elevada o suficiente.

Já agora, guardar dados num cartão SD não é aconselhável, principalmente com um sistema operativo por trás.

Sim, o RPi irá hospedar a página, e o Arduino vai apenas exibir as informações.

Bubu, eu tenho pouca experiência com a plataforma RPi, estou testando o RPi somente a 2 semanas.

O Arduino tem alguma forma de executar o Ethernet Shield simultaneamente com uma programação rodando aguardando a entrada de uma tecla no keypad?

Ok... Já experimentaste isso que dizes? De ter a página num lado e o Arduino meter as informações nessa página?

Para mim, parece-me um tanto ou quanto difícil de fazer... Tu vais aceder ao Arduino ou ao RPi para te servir a página?

Se acederes ao Arduino, o CSS vai ser carregado de onde? Do RPi através do Arduino para o computador, ou do RPi directamente para o computador?

A resposta à tua questão é sim... e a razão pela qual não funciona é esta instrução:

while (1){

O teu código fica preso dentro deste while... logo é impossível que envie seja o que for. Por outro lado, se calhar terás de notar que o código não precisa de ficar na instrução de ler a tecla para a apanhar... se leres o teclado uma vez por ciclo (existe quem faça com menos frequência devido ao debounce dos botões), apanhas qualquer input do utilizador sem parar o programa.

Agora, eu não acho que meter um servidor Web a rolar em cima disso vá funcionar muito bem... Já experimentaste? Tirar o while(1) e colocar o exemplo do servidor web do Arduino?

A minha sugestão é a mesma do Arduino enviar os dados via UDP, por exemplo, para o RPi e este guardá-los numa base de dados ou algo desse género. O facto de ser UDP torna o programa do Arduino menos dependente do RPi... pois não precisa de aguardar por ligações...

Tens conhecimentos de redes para fazeres um programa no RPi que leia UDP??

Olá!

Já pensou em usar um servidor PHP para armazenar as páginas?

Veja o código que uso no meu controlador.

https://github.com/FernandoGarcia/Feduino_with_webcontrol_beta/blob/master/Feduino_with_webcontrol_beta/Webserver.ino

A função que verifica se há cliente é a processRequest().

Mesmo tendo esta função no programa é possível executar outras tarefas cada uma no seu tempo.

Veja quantas funções eu tenho no loop.

https://github.com/FernandoGarcia/Feduino_with_webcontrol_beta/blob/master/Feduino_with_webcontrol_beta/Loop.ino#L21

No meu caso a comunicação segue esta ordem:

Browser > Servidor PHP > Arduino

Arduino > Servidor PHP > Browser.

O arduino nunca envia dados diretamente para o "browser".

A URL é algo deste tipo:

http://192.168.0.177:5000/?cmd=ZmVybmFuAZG9nYXJjaWFE6MTIzNDU2,0,K

Onde "ZmVybmFuAZG9nYXJjaWFE6MTIzNDU2" é a senha em base 64.

0 é a função que será executada.

K indica que o comando terminou.

No começo armazenava as páginas no cartão SD inclusive o CSS mas, demorava muito para carregá-las.

Veja algumas páginas.

Abraço.

Boa tarde bubu.

Eu pretendo colocar o CSS no RPi.

eu mudei o código, e coloquei desta forma:

void loop()

{
  
  
  
ethernet();
//verify();
senIR();

ticks++;

delay(tick_delay);

}

void ethernet(void)
{
  static int contador_func1 = 0;
  if(contador_func1-- == 0){
    contador_func1 = 0;


  
    EthernetClient client = server.available();  // try to get client

    if (client) {  // got client?
      boolean currentLineIsBlank = true;
      while (client.connected()) {
        if (client.available()) {   // client data available to read
          char c = client.read(); // read 1 byte (character) from client
          // buffer first part of HTTP request in HTTP_req array (string)
          // leave last element in array as 0 to null terminate string (REQ_BUF_SZ - 1)
          if (req_index < (REQ_BUF_SZ - 1)) {
            HTTP_req[req_index] = c;          // save HTTP request character
            req_index++;
          }
          // last line of client request is blank and ends with \n
          // respond to client only after last line received
          if (c == '\n' && currentLineIsBlank) {
            // send a standard http response header
            client.println("HTTP/1.1 200 OK");
            client.println("Content-Type: text/html");
            client.println("Connection: keep-alive");
            client.println();
            // Ajax request
            if (StrContains(HTTP_req, "ajax_switch")) {
              // read switch state and send appropriate paragraph text
              usuarioOnBrowser(client);
            }
            else {  // web page request
              // send web page
              webFile = SD.open("index.htm");        // open web page file
              if (webFile) {
                while(webFile.available()) {
                  client.write(webFile.read()); // send web page to client
                }
                webFile.close();
              }
            }
            // display received HTTP request on serial port
            Serial.println(HTTP_req);
            // reset buffer index and all buffer elements to 0
            req_index = 0;
            StrClear(HTTP_req, REQ_BUF_SZ);
            break;
          }
          // every line of text received from the client ends with \r\n
          if (c == '\n') {
            // last character on line of received text
            // starting new line with next character read
            currentLineIsBlank = true;
          } 
          else if (c != '\r') {
            // a text character was received from client
            currentLineIsBlank = false;
          }
        } // end if (client.available())
      } // end while (client.connected())
      delay(1);      // give the web browser time to receive the data
      client.stop(); // close the connection
    } // end if (client)
  }
}

//Le sensor IR

void senIR(void)
{
  static int contador_func3 = 0;
  if(contador_func3-- == 0){
    contador_func3 = 0;
    
    val = digitalRead(sensorir);
    if (val == HIGH){
      lcd.clear();
      verify();
    }
  }
}
//Verify

void verify(void)                     // void de verify
{

  {
    Serial.end();
    Serial.begin(9600);
    //lcd.begin(16,2);
  }
 
  lcd.setCursor(0,0);
  getFingerprintID(); //realiza a busca nos switch case abaixo

}

O código não está completo, pois é muito grande. De toda forma, nesta configuração eu consigo acesso o WebServer e se algum usuário digitar alguma tecla ele irá entender. Porém, até a função que o usuário entrou, nao sair, o ethernet não envia e não recebe nenhum dado.
Eu não possuo conhecimentos para desenvolver, mas posso ver alguns exemplos na internet sobre o envio UDP.

O que desejo enviar/receber é apenas qual usuário passou sua impressão digital no leitor.
Estou pensando em conectar o arduino via USB no RPi e pelo RPi acessar os dados do Arduino via USB.

Olá Fernando,

O seu projeto é realmente bem complexo. Eu queria poder chegar neste ponto, mas não possuo tempo e conhecimento suficiente.

Eu vi o tanto de informações rodam no loop, o ethernet funciona em paralelo sem problemas?

Eu só preciso que ele saiba quem foi reconhecido ou não na tela do Browser.

Pergunta estupida, se tens um raspberry pi com muitos mais recursos que o mega porque é que queres uma pagina a correr num cartao sd no mega?
o raspberry tambem tem GPIO que podes usar para eventualemente interligares ao teu mega.
Em resumo com um só core NUNCA vais ter multitask!

Olá Hugo!

Eu quero colocar as paginas no RPi, é algo que decidi.

Os pinos GPIO do RPi vão receber os pinos IO do mega? E com isto a lógica será feita no RPi?

EU quero usar o Arduino, ligando via porta USB do RPi. Sabes como poderia implementa-lo?

Obrigado

Se quer usar o arduino no seu projeto ESQUEÇA a execução de tarefas simultâneas.

O que ocorre é que o arduino e o controlador estão sempre trabalhando ao mesmo tempo porém, a informação recebida do browser fica na porta esperando que o arduino a receba. Se o arduino não responder em tempo hábil o browser vai mostrar uma mensagem de tempo limite excedido.

O código que utilizava com arquivos no SD era basicamente o que tenho agora, a diferença é que por aqui tinha uma função que abria o arquivo e enviava as páginas.

https://github.com/FernandoGarcia/Feduino_with_webcontrol_beta/blob/master/Feduino_with_webcontrol_beta/Webserver.ino#L32

Aqui está a referência que usei para o meu programa:

http://interestingelectronics.net/2013/07/15/the-arduino-web-server/

Se você QUER usar o raspberry talvez essa seja uma alternativa seguindo o que coloquei no post anterior.

http://www.instructables.com/id/Raspberry-Pi-Web-Server/all/?lang=pt

Os pinos GPIO do RPi vão receber os pinos IO do mega? E com isto a lógica será feita no RPi?

Nao.O RPI tem por exemplo I2C e o arduino tambem.Com apenas 2 fios consegues ligar o teu RPI ao teu mega e trocar informaçao por la.Constroes o teu programa no arduino que interage com o teu hardware e eventualemente podes mandar informaçao para o RPI via I2C ou o RPI a fazer um polling de informaçao ao arduino tambem por I2C.

Pessoal, vou fazer como vocês já orientaram. Usarei o RPi para ser um Servidor do Arduino, já integrei ele com MySQL e to testando o PHP dele com Serial.

É possível fazer o PHP ler apenas um bloco de informação serial, no arduino? Como se fosse uma função()?

function GetSwitchState()
        {
            nocache = "&nocache=" + Math.random() * 1000000;
            var request = new XMLHttpRequest();
            request.onreadystatechange = function()
            {
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        if (this.responseText != null) {
                            document.getElementById("switch_txt").innerHTML = this.responseText;
                        }
                    }
                }
            }
            request.open("GET", "ajax_switch" + nocache, true);
            request.send(null);
            setTimeout('GetSwitchState()', 1000);
        }

Este exemplo é com AJAX, usava ele no SD Card do Arduino. Poderia usa-lo no RPi, para fazer esta requisição apenas no bloco GetSwitchState?

Obrigado!