如何让我的 Arduino 只从 Web 服务器读取特定字符?

How can I make my Arduino read only a specific character from a Web Server?

我刚刚开始使用 Web 编程和 Arduino。我正在尝试了解 Arduino 如何与 Web 服务器通信。

我有这个 Arduino UNO,它通过 SimComm SIM808 GSM-GPRS 模块与我的 Web Server 通信。我的 Web 服务器上有一个名为 retrieve.php 的文件,它基本上从数据库中获取数据并将它们传送给提出请求的任何人。

我的 Arduino 程序正在读取文件,但它读取的内容超出了应有的范围。

retrieve.php回显两个值1&1,大家可以看看here

问题是当我让我的 Arduino 调用这个文件时,它会读取所有内容,这就是我最终得到的

GSM Shield testing.

status=READY
status=ATTACHED

100.107.219.185
H
Number of data received:
1

Data received:
H
TTP/1.1 200 OK
Date: Sat, 04 Jun 2016 13:07:05 GMT
Server: Apache
X-Powered-By: PHP/5.5.35
Content-Length: 2
Connection: close
Content-Type: text/html

11
CLOSED

我的Arduino代码如下:-

#include "SIM900.h"
#include <SoftwareSerial.h>
#include "inetGSM.h"
//#include "sms.h"
//#include "call.h"

//To change pins for Software Serial, use the two lines in GSM.cpp.

//GSM Shield for Arduino
//www.open-electronics.org
//this code is based on the example of Arduino Labs.

//Simple sketch to start a connection as client.

InetGSM inet;
//CallGSM call;
//SMSGSM sms;

char msg[50];
int numdata;
char inSerial[50];
int i=0;
boolean started=false;

void setup()
{
     //Serial connection.
     Serial.begin(9600);
     Serial.println("GSM Shield testing.");
     //Start configuration of shield with baudrate.
     //For http uses is raccomanded to use 4800 or slower.
     if (gsm.begin(2400)) {
          Serial.println("\nstatus=READY");
          started=true;
     } else Serial.println("\nstatus=IDLE");

     if(started) {
          //GPRS attach, put in order APN, username and password.
          //If no needed auth let them blank.
          if (inet.attachGPRS("TATA.DOCOMO.INTERNET", "", ""))
               Serial.println("status=ATTACHED");
          else Serial.println("status=ERROR");
          delay(1000);

          //Read IP address.
          gsm.SimpleWriteln("AT+CIFSR");
          delay(5000);
          //Read until serial buffer is empty.
          gsm.WhileSimpleRead();

          //TCP Client GET, send a GET request to the server and
          //save the reply.
          numdata=inet.httpGET("www.boat.esy.es", 80, "/retrieve.php", msg, 1);
          //Print the results.
          Serial.println("\nNumber of data received:");
          Serial.println(numdata);
          Serial.println("\nData received:");
          //Serial.println(msg);

          char* content = strstr(msg,"\r\n\r\n");
          content = content+4;
          Serial.println(content);
     }
};

void loop()
{
     //Read for new byte on serial hardware,
     //and write them on NewSoftSerial.
     serialhwread();
     //Read for new byte on NewSoftSerial.
     serialswread();
};

void serialhwread()
{
     i=0;
     if (Serial.available() > 0) {
          while (Serial.available() > 0) {
               inSerial[i]=(Serial.read());
               delay(10);
               i++;
          }

          inSerial[i]='[=11=]';
          if(!strcmp(inSerial,"/END")) {
               Serial.println("_");
               inSerial[0]=0x1a;
               inSerial[1]='[=11=]';
               gsm.SimpleWriteln(inSerial);
          }
          //Send a saved AT command using serial port.
          if(!strcmp(inSerial,"TEST")) {
               Serial.println("SIGNAL QUALITY");
               gsm.SimpleWriteln("AT+CSQ");
          }
          //Read last message saved.
          if(!strcmp(inSerial,"MSG")) {
               Serial.println(msg);
          } else {
               Serial.println(inSerial);
               gsm.SimpleWriteln(inSerial);
          }
          inSerial[0]='[=11=]';
     }
}

void serialswread()
{
     gsm.SimpleRead();
}

这是我的PHP代码

<?php 

    $server = "mysql.hostinger.in";
    $user = "xxxxxxxxxx";
    $password = "xxxxxx";
    $db = "xxxxxxx";

    $lck;
    $ign;

    $conn = mysqli_connect($server,$user,$password,$db);

    if($conn->connect_error)
    {
        die("Connection Failed:" . $conn->connect_error);
    }

    $query = "SELECT unlck_lck, ignition FROM BOATOP";
    $res = $conn->query($query);

    if($res->num_rows>0)
    {
        $row = $res->fetch_assoc();
        $lck = $row["unlck_lck"];
        $ign = $row["ignition"];

        echo $lck;
        echo $ign;
    }

    mysqli_close($conn);


?>

我希望我的 Arduino 只读取我的 PHP 文件回显的输出。但目前它正在阅读所有内容。

这里我还需要提一件事。对于我的 Arduino GSM Shield,我没有使用 Arduino 安装包附带的示例或库,因为这些库与我的 GSM Shield 不兼容。相反,我使用了一些我在网上找到的库。我没有在此处 post 这些文件,因为它们会使我的 post 变得很长。如果您也需要查看它们,请告诉我。

更新 1

内容显示为空

Arduino修改

content = strstr(msg,"\r\n\r\n");
if(content == NULL) {
    Serial.println("ERROR IN CONTENT READING");
} else {
    content = content + 4;
    Serial.println("The Content Is:");
    Serial.println(content);
}

结果:

GSM Shield testing.

status=READY
status=ATTACHED

100.110.244.218

Number of data received:
50

Data received:

ERROR IN CONTENT READING
HTTP/1.1 200 OK
Date: Sat, 04 Jun 2016 17:17:12 GMT
Server: Apache
X-Powered-By: PHP/5.5.35
Content-Length: 2
Connection: close
Content-Type: text/html

11
CLOSED

你的问题其实不是一个。您应该阅读有关 HTTP 协议规范的更多信息,以完全了解您的问题所在。

当您的 PHP 脚本 return 有一些输出时,它正在由 HTTP 服务器提供服务。您的客户端正在使用记录良好的协议与服务器通信,例如:

  1. 客户端→服务器:

请给我这个地址的页面!

GET www.boat.esy.es/retrieve.php HTTP/1.1
  1. 服务器→客户端:

您好,这是关于您的请求的一些详细信息,然后是您的内容

HTTP/1.1 200 OK
Date: Sat, 04 Jun 2016 13:07:05 GMT
Server: Apache
X-Powered-By: PHP/5.5.35
Content-Length: 2
Connection: close
Content-Type: text/html

11

给出的上下文通常可以帮助您的网络浏览器更好地处理您的数据。但在嵌入式环境中它也可以帮助你。例如,如果 Content-Length 对于您的变量来说太大,您可以检测到并显示一个错误,表明您无法将那么多数据保存到内存中!

因此,作为 arduino 代码中的一个简单解决方案,您需要读取网络服务器的输出,丢弃它输出的每一行,直到它到达 \r\n\r\n,这在 HTTP 协议中是 [=69 之间的分隔符=]s 和内容。然后以下所有数据将是您的内容,因为它已由您的 PHP 代码输出。

一种方法是:

numdata=inet.httpGET("www.boat.esy.es", 80, "/retrieve.php", msg, 1);
// look up substring '\r\n\r\n' within msg, and allocate content with it
char * content = strstr(msg, "\r\n\r\n");
if (content == NULL) {
    Serial.println("Couldn't find the contents within the message!");
} else {
    // then we skip the '\r\n\r\n' substring to only keep the contents
    content = content+4; // we skip 4 characters
    Serial.print("The content is: ");
    Serial.println(content);
}

如果您要尝试提取 content-length header,您可以这样做:

// lookup the content-length header
char* content = strstr(msg, "Content-Length: ");
// skip the size of the looked up string
content = content+strlen("Content-Length: ");
// store the size of the contents in number of characters
int nb_chars = atoi(content);
// lookup the content
content = strstr(msg, "\r\n\r\n");
// check that the substring has been found
if (content == NULL) {
    Serial.println("Couldn't find the contents within the message!");
} else {
    // skip the header-content delimiter
    content = content+4;
    // and do useful things with the extracted data
    if (nb_chars == 2) {
        Serial.print("The content is:");
        Serial.println(content);
    } else {
        Serial.println("There's been an error processing the request.");
    }
}

您将在此处找到代码的 运行 版本:


作为测试,您可能想要打印字符串的每个字符的 ASCII 代码,以对其进行测试,因此代替您原来的 Serial.println(msg) 尝试:

for (int c=0; c<strlen(msg); ++c) {
    Serial.print((int)msg[c], HEX);
    Serial.print(" ");
}
Serial.println("");

应该产生以下输出:

0048 0054 0054 0050 002F 0031 002E 0031 0020 0032 0030 0030 0020 004F 004B 000D 000A 0044 0061 0074 0065 003A 0020 0053 0061 0074 002C 0020 0030 0034 0020 004A 0075 006E 0020 0032 0030 0031 0036 0020 0031 0033 003A 0030 0037 003A 0030 0035 0020 0047 004D 0054 000D 000A 0053 0065 0072 0076 0065 0072 003A 0020 0041 0070 0061 0063 0068 0065 000D 000A 0058 002D 0050 006F 0077 0065 0072 0065 0064 002D 0042 0079 003A 0020 0050 0048 0050 002F 0035 002E 0035 002E 0033 0035 000D 000A 0043 006F 006E 0074 0065 006E 0074 002D 004C 0065 006E 0067 0074 0068 003A 0020 0032 000D 000A 0043 006F 006E 006E 0065 0063 0074 0069 006F 006E 003A 0020 0063 006C 006F 0073 0065 000D 000A 0043 006F 006E 0074 0065 006E 0074 002D 0054 0079 0070 0065 003A 0020 0074 0065 0078 0074 002F 0068 0074 006D 006C 000D 000A 000D 000A 0031 0031 000D 000A 
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 ^^^^ ^^^^ ^^^^ ^^^^

这里的000D 000A分别是\r\n。因此,您应该在 ^^^^ ^^^^ ^^^^ ^^^^.

突出显示的相同位置找到 000D 000A 000D 000A 序列

这应该可以帮助您仔细检查服务器生成的字符串是否是我们在使用 strstr() 进行解析时所期望的字符串。

HTH