简体   繁体   English

Arduino客户端+ PHP cURL服务器两次执行命令

[英]Arduino client + PHP cURL server executing command twice

I have this project I'm doing for work in which I command several Arduino (Arduino core + ENC28J60 Ethernet + x4 Relay actuator) modules from a single PHP server to activate a relay on any of the Arduino modules. 我正在做这个项目,我从一个PHP服务器命令几个Arduino(Arduino内核+ ENC28J60以太网+ x4继电器执行器)模块来激活任何Arduino模块上的继电器。 The server has a list of all the events and executes them as the time is right for each one. 服务器具有所有事件的列表,并在每个事件合适的时间执行它们。 What's wrong is that whenever the commands are more than 4 minutes apart (ie >=5 minutes) the command gets executed twice by the Arduino. 错的是,每当命令间隔超过4分钟(即> = 5分钟)时,Arduino就会执行两次命令。 That is, Arduino activates the relay I commanded twice in a row. 也就是说,Arduino连续两次激活了我命令的继电器。

What the code does is this: 1. thor.php is executed lineally once (the task is repeated by a crontab) 2. thor.php searches within its arrays for an occurrence of an event to take place at the current time 3. for each occurrence it generates a task that's delivered to the curl multi handler 4. all the tasks are sent in parallel to each arduino module. 该代码的作用是:1. thor.php被线性执行一次(任务由crontab重复执行)2. thor.php在其数组中搜索当前时间发生的事件3. for每次发生时,它都会生成一个任务,该任务将交付给curl多重处理程序4。所有任务都并行发送到每个arduino模块。 5. when an Arduino receives a request, checks whether it comes from a known IP address and through the allowed port, analyzes the command in the parameters, and activates the relays as requested. 5.当Arduino收到请求时,检查它是否来自已知IP地址并通过允许的端口,分析参数中的命令,并根据请求激活继电器。 6. the Arduino then sends a response page with a hidden field that will work in the future for control. 6. Arduino然后发送带有隐藏字段的响应页面,该页面将在将来用于控制。

In theory all works well, but whenever the commands are 5 minutes or more apart, the Arduino executes the command twice. 从理论上讲一切正常,但是每隔5分钟或更长时间执行一次命令,Arduino将执行两次命令。

I put the whole code next. 接下来我将整个代码。 Here's the Arduino: (Pardon the comments in Spanish) 这是Arduino :(请用西班牙语评论)

    #include "etherShield.h"

    //MAC ADDRESS.
    static uint8_t mymac[6] = {
      0x54,0x55,0x58,0x10,0x00,0x24}; 
    //IP ADDRESS THOR.
    static uint8_t myip[4] = {
      172,0,0,101};
    //Unica IP de Origen aceptada.
    static uint8_t ip_origen[4] = {
      172,0,0,10};
    //TCP PORT
    static uint16_t myport = 5566;
    //Setear los pines de los relays. Solo se setea el primero. Se necesitan 4 pines consecutivos libres
    static int primerrelay = 2;

    //Variables globales usadas para el feedbak del modulo en una peticion tcp.
    int16_t comando_rel, comando_tmp;
    //Estado de los relays
    uint8_t estado;

    //Definiciones propias de Arduino. Especifica el tamaño maximo del buffer y lo inicializa.
    #define BUFFER_SIZE 500
    static uint8_t buf[BUFFER_SIZE+1];

    EtherShield es=EtherShield();

    void setup(){

      /*initialize enc28j60*/
      es.ES_enc28j60Init(mymac);
      es.ES_enc28j60clkout(2); // change clkout from 6.25MHz to 12.5MHz
      delay(10);

      /* Magjack leds configuration, see enc28j60 datasheet, page 11 */
      // LEDA=greed LEDB=yellow
      //
      // 0x880 is PHLCON LEDB=on, LEDA=on
      // enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
      es.ES_enc28j60PhyWrite(PHLCON,0x880);
      delay(500);
      //
      // 0x990 is PHLCON LEDB=off, LEDA=off
      // enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
      es.ES_enc28j60PhyWrite(PHLCON,0x990);
      delay(500);
      //
      // 0x880 is PHLCON LEDB=on, LEDA=on
      // enc28j60PhyWrite(PHLCON,0b0000 1000 1000 00 00);
      es.ES_enc28j60PhyWrite(PHLCON,0x880);
      delay(500);
      //
      // 0x990 is PHLCON LEDB=off, LEDA=off
      // enc28j60PhyWrite(PHLCON,0b0000 1001 1001 00 00);
      es.ES_enc28j60PhyWrite(PHLCON,0x990);
      delay(500);
      //
      // 0x476 is PHLCON LEDA=links status, LEDB=receive/transmit
      // enc28j60PhyWrite(PHLCON,0b0000 0100 0111 01 10);
      es.ES_enc28j60PhyWrite(PHLCON,0x476);
      delay(100);

      //init the ethernet/ip layer:
      es.ES_init_ip_arp_udp_tcp(mymac,myip,myport);

      //################################
      //Setup de los pines de salida
      for(int i = 0; i < 4; i++)
      {
        pinMode(i + 2, OUTPUT);
      }

      //Lamp-test
      digitalWrite(primerrelay, HIGH);
      delay(100);
      digitalWrite(primerrelay, LOW);
      comando_rel = -1;
      comando_tmp = -1;
    }

    void loop(){
      uint16_t plen, dat_p;

      plen = es.ES_enc28j60PacketReceive(BUFFER_SIZE, buf);

      /*plen will be unequal to zero if there is a valid packet (without crc error) */
      if(plen!=0){

        // arp is broadcast if unknown but a host may also verify the mac address by sending it to a unicast address.
        if(es.ES_eth_type_is_arp_and_my_ip(buf,plen)){
          es.ES_make_arp_answer_from_request(buf);//*******
          return;
        }

        // check if ip packets are for us:
        if(es.ES_eth_type_is_ip_and_my_ip(buf,plen)==0){
          return;
        }

        if(buf[IP_PROTO_P]==IP_PROTO_ICMP_V && buf[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V){
          es.ES_make_echo_reply_from_request(buf,plen);
          return;
        }

        // tcp port www start, compare only the lower byte
        // En la siguiente linea esta la clave para poder implementar puertos mayores a 254
        if (buf[IP_PROTO_P]==IP_PROTO_TCP_V&&buf[TCP_DST_PORT_H_P]==highByte(myport)&&buf[TCP_DST_PORT_L_P]==lowByte(myport)){
          if (buf[TCP_FLAGS_P] & TCP_FLAGS_SYN_V){
            es.ES_make_tcp_synack_from_syn(buf); // make_tcp_synack_from_syn does already send the syn,ack
            return;     
          }
          if (buf[TCP_FLAGS_P] & TCP_FLAGS_ACK_V){
            es.ES_init_len_info(buf); // init some data structures
            dat_p=es.ES_get_tcp_data_pointer();
            if (dat_p==0){ // we can possibly have no data, just ack:
              if (buf[TCP_FLAGS_P] & TCP_FLAGS_FIN_V){
                es.ES_make_tcp_ack_from_any(buf);
                //es.ES_make_tcp_ack_from_any(buf, plen, 1);//************
              }
              return;
            }
            //Comparacion de la ip de origen.
            uint8_t match_ip_origen = 1;

            for (int i=0; i<4; i++)
            {
              if(buf[IP_SRC_P + i] != ip_origen[i])
              {
                match_ip_origen = 0;
                break;
              }
            }/**/

            if (match_ip_origen==1)
            {
              if (strncmp("GET ",(char *)&(buf[dat_p]),4)!=0){
                // head, post and other methods for possible status codes see:
                // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
                plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>200 OK</h1>"));
                goto SENDTCP;
              }
              if (strncmp("/ ",(char *)&(buf[dat_p+4]),2)==0){
                plen=print_webpage(buf);
                goto SENDTCP;
              }

              //Calculo el estado de los pines
              estado = 0; //Se setea en cero antes de hacer la comprobacion
              estado += digitalRead(primerrelay) * 1 + digitalRead(primerrelay + 1) * 2 + digitalRead(primerrelay + 2) * 4 + digitalRead(primerrelay + 3) * 8;

              //#######################################################################
              //Analisis de los parametros y ejecucion de las acciones correspondientes

              if (strncmp("/?cmd=",(char *)&(buf[dat_p+4]),6)==0)
              {
                //cargar los comandos a las variables globales
                analyse_cmd((char *)&(buf[dat_p+10]));
                //Analizar el tiempo. Si es mayor que 0 y menor que 10 (1-9)
                //guardar el estado actual, ejecutar el comando solicitado, y volver al estado anterior.
                //Si el tiempo es positivo menor que 10, setear el estado temporalmente
                if(comando_tmp > 0 && comando_tmp < 10)
                {
                  //Si el valor es aceptable (0-15), se ejecuta el comando
                  if(comando_rel > -1 && comando_rel < 16)
                  {
                    //Generar un estado derivado aplicando un OR a nivel de bits con el estado actual
                    uint8_t r = comando_rel | estado;
                    //Ejecutar el nuevo estado obtenido
                    ejecutar_comando(r);
                    //Esperar el tiempo especificado
                    delay(comando_tmp * 1000);
                    //Volver al estado anterior.
                    ejecutar_comando(estado);
                  }
                }
                //Si el tiempo es igual a cero, setear el nuevo estado indefinidamente
                else if(comando_tmp == 0)
                {
                  //Si el valor es aceptable (0-15), se ejecuta el comando
                  if(comando_rel > -1 && comando_rel < 16)
                  {
                    //Ejecutar el comando y no revertirlo
                    ejecutar_comando(comando_rel);
                  }
                }
              }

              plen=print_webpage(buf);
    SENDTCP:  
              es.ES_make_tcp_ack_from_any(buf); // send ack for http get//***************
              es.ES_make_tcp_ack_with_data(buf,plen); // send data      
            }
          }
        }
      }
    }

    void ejecutar_comando(uint8_t comando)
    {
      //Realiza un and logico con el parametro a nivel de bits. 
      //Enciende o apaga el relay correspondiente.
      //Si el and logico resulta en 0, escribe LOW.
      //Si es diferente a 0, escribe HIGH.
      digitalWrite(primerrelay, (comando & 1));
      digitalWrite(primerrelay + 1, (comando & 2)); 
      digitalWrite(primerrelay + 2, (comando & 4)); 
      digitalWrite(primerrelay + 3, (comando & 8));
    }

    void analyse_cmd(char *x)
    {
      //por por default si no hubieran llegado comandos o estan mal 
      comando_rel = -1;
      comando_tmp = -1;
      //verificar que esten todos los caracteres requeridos
      uint8_t i = 0;
      while(x[i]!=' ' && x[i]!='\0' && i < 10){
        i++;
      }
      //si tiene 4 son los caracteres necesarios: 2 para los reles y 2 para el timer
      if(i==4){
        String aux = "";
        //verificar por el nro de los reles
        if(is_integer(x[0]) && is_integer(x[1])){
          aux = String(x[0]) + String(x[1]);
          comando_rel = aux.toInt();
        }
        aux = "";
        //verificar por el nro de segundos del timer
        if(is_integer(x[2]) && is_integer(x[3])){
          aux = String(x[2]) + String(x[3]);
          comando_tmp = aux.toInt();
        }          
      }
    }

    uint8_t is_integer(char c){
      uint8_t r = 0;
      if (c < 0x3a && c > 0x2f){
        r = 1;
      }  
      return r;
    }

    uint16_t print_webpage(uint8_t *buf)
    {
      uint16_t plen, dat_p;
      dat_p=es.ES_get_tcp_data_pointer();

      plen=es.ES_fill_tcp_data_p(buf,0,PSTR("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n"));

      plen=es.ES_fill_tcp_data_p(buf,plen,PSTR("<center><p><h1>Modulo Thor V1.0  </h1></p></br></hr> "));

      String x = String(buf[IP_DST_P]) + "." + String(buf[IP_DST_P+1]) + "." + String(buf[IP_DST_P+2]) + "." + String(buf[IP_DST_P+3]) + " llamado desde ";
      char *s = getCharArray(x);
      plen=es.ES_fill_tcp_data(buf,plen,s);

      x = String(buf[IP_SRC_P]) + "." + String(buf[IP_SRC_P+1]) + "." + String(buf[IP_SRC_P+2]) + "." + String(buf[IP_SRC_P+3]) + "</br></center>";
      s = getCharArray(x);
      plen=es.ES_fill_tcp_data(buf,plen,s);

      //Al haberse ejecutado un comando el estado resultante debe ser actualizado.
      //Calculo del estado de los pines
      estado = 0; //Se setea en cero antes de hacer la comprobacion
      estado += digitalRead(primerrelay) * 1 + digitalRead(primerrelay + 1) * 2 + digitalRead(primerrelay + 2) * 4 + digitalRead(primerrelay + 3) * 8;

      x = "REL: " + String(comando_rel) + "</br>TMP: " + String(comando_tmp) + "</br>STA: " + String(estado) + "</br></br>"
+ "<input type=\"hidden\" name=\"status\" value=\"" + (String)estado + "\">";
      s = getCharArray(x);
      plen=es.ES_fill_tcp_data(buf,plen,s);

      return(plen);
    }

    char* getCharArray(String s)
    {
      char charBuf[s.length() + 1];
      s.toCharArray(charBuf,s.length() + 1);
      return charBuf;
    }

    void reset()
    {
      for (int i = primerrelay; i < primerrelay + 4; i++)
      {
        digitalWrite(i, LOW);
      }
    }

thor.php: thor.php:

    <?php
    //Requiere tener instalado php5-curl
    require 'thorconfig.php';
    require 'common.php';
    $tareas = array();
    //Recorrer las configuraciones y armar la lista de tareas
            foreach($modulos as $modulo) //Recorrer cada módulo
            {
        foreach($modulo["eventos"] as $evento) //Recorrer cada evento de un módulo
        {
            //Si el día y la hora del evento coinciden con el día y la hora actuales
            if(strcmp(date("w"), $evento["dia"]) == 0 && strcmp(date("H:i"), $evento["hora"]) == 0)
            {
                //Añadir una tarea con el formato "http://direccion_ip:puerto/?cmd=reltmp"
                $tareas[] = "http://".$modulo["ip"].":".$modulo["puerto"]."/?cmd=".$evento["rel"].$evento["tmp"];
            }
        }
    }
    $curl = array();
    //Inicializar el handler de tareas
    $curlHandle = curl_multi_init();
    //Recorrer las tareas y añadirlas al handler
    foreach($tareas as $tarea)
        $curl[] = addHandle($curlHandle, $tarea);
    //Ejecutar el handler
    ExecHandle($curlHandle);
    echo "\n";
    //Recuperar la respuesta de cada tarea ejecutada
    for($i = 0; $i < sizeof($tareas); $i++)
    {
        $respuesta = curl_multi_getcontent($curl[$i])."\n";
        if(!strpos($respuesta, "<input type=\"hidden\" name=\"status\""))
        {
            $message = "Ha ocurrido un error al intentar ejecutar el siguiente comando: ".$tareas[$i];
            sendMail($server["from"], $server["from"], $server["to"], $server["to"], "Error en Thor", $message, $server);
        }
        else
        {
            echo $respuesta;
        }
    }
    //Remover cada tarea del handler
    foreach($curl as $handle)
        curl_multi_remove_handle($curlHandle, $handle);
    //Cerrar el handler
    curl_multi_close($curlHandle);
    ?>

thorconfig.php thorconfig.php

    <?php
        $modulos = [
            "modulo 0" => [
                "ip" => "172.24.51.101", //Teológico
                "puerto" => 6174,
                "eventos" => [
                    ////////////////////// Lunes //////////////////////
                    "evento 0" => [
                        "dia" => 1,
                        "hora" => "07:30",
                        "rel" => "01",
                        "tmp" => "03"
                    ],
                    "evento 1" => [
                        "dia" => 1,
                        "hora" => "08:25",
                        "rel" => "01",
                        "tmp" => "03"
                    ]
    .
    .
    .

           ]
    ]

        $server = [
            "host" => "172.16.0.40",
            "puerto" => 25,
            "smtpuser" => "user",
            "smtppass" => "pass",
            "to" => "mail@uap.edu.ar",
            "from" => "mail@uap.edu.ar"
        ];
    ?>

common.php: common.php:

    <?php
    //Función que ejecuta el handler
    function ExecHandle(&$curlHandle)
    {
            $flag=null;
            do {
            //fetch pages in parallel
                    curl_multi_exec($curlHandle,$flag);
            } while ($flag > 0);
    }

    //Función que añade un recurso al handler
    function addHandle(&$curlHandle,$url)
    {
            $cURL = curl_init();
            curl_setopt($cURL, CURLOPT_URL, $url);
            curl_setopt($cURL, CURLOPT_HEADER, 0);
            curl_setopt($cURL, CURLOPT_RETURNTRANSFER, 1);
            curl_multi_add_handle($curlHandle,$cURL);
            return $cURL;
    }

    function sendMail($from, $namefrom, $to, $nameto, $subject, $message, $server)
    {
            $smtpServer = $server["host"];   //ip address of the mail server.  This can also be the local domain name
            $port = $server["puerto"];                    // should be 25 by default, but needs to be whichever port the mail server will be using for smtp
            $timeout = "45";                 // typical timeout. try 45 for slow servers
            $username = $server["smtpuser"]; // the login for your smtp
            $password = $server["smtppass"];           // the password for your smtp
            $localhost = "127.0.0.1";      // Defined for the web server.  Since this is where we are gathering the details for the email
            $newLine = "\r\n";           // aka, carrage return line feed. var just for newlines in MS
            $secure = 0;                  // change to 1 if your server is running under SSL

            //connect to the host and port
            $smtpConnect = fsockopen($smtpServer, $port, $errno, $errstr, $timeout);
            $smtpResponse = fgets($smtpConnect, 4096);
            if(empty($smtpConnect)) {
                     $output = "Failed to connect: $smtpResponse";
                     echo $output;
                     return $output;
            }
            else {
                     $logArray['connection'] = "<p>Connected to: $smtpResponse";
                     echo "<p />connection accepted<br>".$smtpResponse."<p />Continuing<p />\n";
            }

            //you have to say HELO again after TLS is started
            fputs($smtpConnect, "HELO $localhost". $newLine);
            $smtpResponse = fgets($smtpConnect, 4096);
            $logArray['heloresponse2'] = "$smtpResponse";
            //request for auth login
            fputs($smtpConnect,"AUTH LOGIN" . $newLine);
            $smtpResponse = fgets($smtpConnect, 4096);
            $logArray['authrequest'] = "$smtpResponse";

            //send the username
            fputs($smtpConnect, base64_encode($username) . $newLine);
            $smtpResponse = fgets($smtpConnect, 4096);
            $logArray['authusername'] = "$smtpResponse";

            //send the password
            fputs($smtpConnect, base64_encode($password) . $newLine);
            $smtpResponse = fgets($smtpConnect, 4096);
            $logArray['authpassword'] = "$smtpResponse";

            //email from
            fputs($smtpConnect, "MAIL FROM: <$from>" . $newLine);
            $smtpResponse = fgets($smtpConnect, 4096);
            $logArray['mailfromresponse'] = "$smtpResponse";

            //email to
            fputs($smtpConnect, "RCPT TO: <$to>" . $newLine);
            $smtpResponse = fgets($smtpConnect, 4096);
            $logArray['mailtoresponse'] = "$smtpResponse";

            //the email
            fputs($smtpConnect, "DATA" . $newLine);
            $smtpResponse = fgets($smtpConnect, 4096);
            $logArray['data1response'] = "$smtpResponse";

            //construct headers
            $headers = "MIME-Version: 1.0" . $newLine;
            $headers .= "Content-type: text/html; charset=iso-8859-1" . $newLine;
            $headers .= "To: $nameto <$to>" . $newLine;
            $headers .= "From: $namefrom <$from>" . $newLine;

            //observe the . after the newline, it signals the end of message
            fputs($smtpConnect, "To: $to\r\nFrom: $from\r\nSubject: $subject\r\n$headers\r\n\r\n$message\r\n.\r\n");
            $smtpResponse = fgets($smtpConnect, 4096);
            $logArray['data2response'] = "$smtpResponse";

            // say goodbye
            fputs($smtpConnect,"QUIT" . $newLine);
            $smtpResponse = fgets($smtpConnect, 4096);
            $logArray['quitresponse'] = "$smtpResponse";
            $logArray['quitcode'] = substr($smtpResponse,0,3);
            fclose($smtpConnect);
            //a return value of 221 in $retVal["quitcode"] is a success
            return($logArray);  
    }
    ?>

Any ideas why it executes only once, as it should, when I execute commands within less than 4 minutes and it executes twice otherwise? 有什么想法为什么当我在不到4分钟的时间内执行命令时,它只执行一次,而应该执行两次?

EDIT: I discarded the problem being in the PHP code. 编辑:我放弃了在PHP代码中的问题。 I installed a lynx text browser in the server and manually executed commands more than 5 minutes apart and got the same result: a duplicated action from Arduino. 我在服务器中安装了天猫座文本浏览器,并且手动执行了间隔超过5分钟的命令,并得到了相同的结果:Arduino的重复动作。 I leave the PHP code just in case someone is interested in it and may use it. 我留下PHP代码是为了以防万一有人对它感兴趣并且可以使用它。 I'll continue to experiment to find a solution. 我将继续尝试寻找解决方案。

EDIT 2: I discarded the problem being in the Arduino Hardware. 编辑2:我放弃了在Arduino硬件中的问题。 I tested an out-of-the-box new Arduino Uno (same model) with the same code and it still had the same bug. 我用相同的代码测试了一个开箱即用的新Arduino Uno(相同型号),它仍然具有相同的错误。

EDIT 3: Just an idea. 编辑3:只是一个想法。 Is it possible that the PHP server is expecting an immediate response and as it isn't given by Arduino right away then sends the packet again thus getting a double (late) response from Arduino? PHP服务器是否有可能期望立即响应,而Arduino并没有立即给出响应,然后再次发送数据包,从而得到Arduino的双重(后期)响应? Here's another one: Is it possible that the Arduino is passing through the buffer twice and not realizing it? 这是另一个:Arduino是否有可能两次通过缓冲区而没有意识到呢? (The second options seems less likely to me). (第二种选择对我来说似乎不太可能)。

Use a network sniffer on your server (ie wireshark) to see what realy gets send. 使用服务器上的网络嗅探器(即Wireshark)来查看实际发送的内容。 That way you can easily test for your idea no 3. wireshark might also have an option to replay traffic, thus making testing easier. 这样,您就可以轻松测试第3个想法。wireshark还可以选择重播流量,从而使测试更加容易。

I "fixed" the problem by adding a Patch (Not a permanent solution). 我通过添加补丁(不是永久解决方案)“解决了”问题。 I still need to find the root of the problem. 我仍然需要找到问题的根源。

Added this after the #include "etherShield.h" line #include "etherShield.h"行之后添加了此内容

#include <TimedAction.h>

//Para control de ejecuciones
int16_t ultimo_comando_ejecutado[5];
int16_t ultimo_tiempo_ejecutado[5];
//Number of seconds since last command execution or execution register.
int8_t segundos;
TimedAction ta = TimedAction(1000,revisar);

this in the setup() 这在setup()

resetear_registro();
segundos = 0;

this in the first line of loop() loop()的第一行

ta.check();

this on command execution 这在命令执行

if(ejecutado(comando_rel, comando_tmp) == 0)
{
  ejecutar_comando(r);
  registrar_ejecucion(comando_rel, comando_tmp);
  //Esperar el tiempo especificado
  delay(comando_tmp * 1000);
  //Volver al estado anterior.
  ejecutar_comando(estado);
}

and this 和这个

if(ejecutado(comando_rel, comando_tmp) == 0)
{
  ejecutar_comando(comando_rel);
  registrar_ejecucion(comando_rel, comando_tmp);
}

Finally, these functions at the end 最后,这些功能最后

void registrar_ejecucion(int16_t cmd, int16_t tmp)
{
  for(int i = 0; i < 4; i++)
  {
    ultimo_comando_ejecutado[i] = ultimo_comando_ejecutado[i+1];
    ultimo_tiempo_ejecutado[i] = ultimo_tiempo_ejecutado[i+1];
  }
  ultimo_comando_ejecutado[4] = cmd;
  ultimo_tiempo_ejecutado[4] = tmp;
  segundos = 0;
}

uint8_t ejecutado(int16_t cmd, int16_t tmp)
{
  uint8_t ejec = 0;
  for(int i = 0; i < 5; i++)
  {
    if(ultimo_comando_ejecutado[i] == cmd && ultimo_tiempo_ejecutado[i] == tmp)
    {
      ejec = 1;
      break;
    }
  }
  return ejec;
}

void resetear_registro()
{
  for(int a = 0; a < 5; a++)
  {
    ultimo_comando_ejecutado[a] = -1;
    ultimo_tiempo_ejecutado[a] = -1;
  }
}

void revisar()
{
  segundos++;
  if(segundos > 59)
  {
    resetear_registro();
    segundos = 0;
  }
}

Basically what it does is check the last 5 commands (relays AND time) executed within the last minute and if there's a match, the command is ignored. 基本上,它的工作是检查在最后一分钟内执行的最后5条命令(继电器和时间),如果有匹配项,则忽略该命令。 On the low scale it solves the problem. 在低规模上,它可以解决问题。 But I am conscious that it is only a patch and that problems may arise. 但是我意识到这只是一个补丁,可能会出现问题。 For now I'm going to implement the code as is (plus patch). 现在,我将按原样(加上补丁)实现代码。 But if anyone finds a better and more permanent solution, I'm open to suggestions. 但是,如果有人找到更好,更持久的解决方案,我欢迎您提出建议。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM