Arduino如何从PHP脚本中保存数据?

时间:2016-02-03 19:11:32

标签: php arduino arduino-uno

我创建了一个PHP脚本,因此我可以从我的在线数据库(MySQL)中检索数据,但是我无法将该信息传递给我的arduino + Ethernet Shield(使用Ethercard.h)。

我想要的是将我的PHP脚本作为字符串向量重新获得。

我的PHP脚本返回的示例:

ABCDF
GHYEJ
JDYDI
HSTSU
PIFYF

我希望它能在我的Arduino中成为现实:

char* test = {"ABCDF", "GHYEJ". "JDYDI", "HSTSU", "PIFYF" };

任何人都可以帮我这个吗?

编辑: 所以,我正在尝试@ frarugi87解决方案,但我的DNS失败了。以下是我使用的代码:

#include <enc28j60.h>
#include <EtherCard.h>
#include <net.h>

#define HTTP_HEADER_OFFSET 0

/* Setup for Ethernet Library */
static byte mymac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x31 };
static byte myip[] = {192,168,1,58};
static byte gatewayip[] = {192,168,1,1};
static byte dnsip[] = {8,8,8,8}; //Google DNS
const char website[] PROGMEM = "http://arksecurity.net16.net";

byte Ethernet::buffer[700];
static uint32_t timer = 0;

static void response_callback (byte status, word off, word len){
  Serial.print((const char*) Ethernet::buffer + off + HTTP_HEADER_OFFSET);  
}

void setup() {
  Serial.begin(57600);
  if (ether.begin(sizeof Ethernet::buffer, mymac, 8) == 0)
    Serial.println("Failed to access Ethernet controller");
  else
    Serial.println("Ethernet controller initialized");
    ether.staticSetup(myip, gatewayip, dnsip);

  if(!ether.dnsLookup(website)){
    Serial.println("DNS failed");  
    while(1);
  }
  else
    Serial.println("DNS resolution done");

    ether.printIp("SRV IP:\t", ether.hisip);
  Serial.println();
}
void loop() {
  word pos = ether.packetLoop(ether.packetReceive());

  if (millis() > timer){
    timer = millis() + 5000;
    ether.browseUrl("/", "test.php", website, response_callback);
    }
}

我在这里做错了吗?

Edit2:好吧,我让它运行了。 DNS问题是由我的路由器引起的,所以我不得不将其设置为192.168.1.1并且我还必须对ether.browseUrl进行一些调整,因为它也返回错误。 这就是我现在的代码:

#include <enc28j60.h>
#include <EtherCard.h>
#include <net.h>

#define HTTP_HEADER_OFFSET 50

/* Setup for Ethernet Library */
static byte mymac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x31 };
static byte myip[] = {192,168,1,58};
static byte gatewayip[] = {192,168,1,1};
static byte dnsip[] = {192,168,1,1}; //Google DNS
const char website[] PROGMEM = "www.arksecurity.net16.net";

byte Ethernet::buffer[700];
static uint32_t timer = 0;

static void response_callback (byte status, word off, word len){
  Serial.print((const char*) Ethernet::buffer + off + HTTP_HEADER_OFFSET);  
}

void setup() {
  Serial.begin(57600);
  if (ether.begin(sizeof Ethernet::buffer, mymac, 8) == 0)
    Serial.println("Failed to access Ethernet controller");
  else
    Serial.println("Ethernet controller initialized");
    ether.staticSetup(myip, gatewayip, dnsip);

  if(!ether.dnsLookup(website)){
    Serial.println("DNS failed");  
    while(1);
  }
  else
    Serial.println("DNS resolution done");

    ether.printIp("SRV IP:\t", ether.hisip);
  Serial.println();
}
void loop() {
  word pos = ether.packetLoop(ether.packetReceive());

  if (millis() > timer){
    timer = millis() + 5000;
    ether.browseUrl(PSTR("/test."),"php", website, response_callback);
    }
}

现在我必须将我得到的数据保存到char * []中。有什么想法吗?

EDIT3:

所以这就是我的代码到目前为止的样子。问题是,在最后的比较中我做了#34; for(int i = 0; i&lt; 20 - 1; i ++)&#34;我得到所有&#34;错误&#34;就像前两个响应回拨一样,之后就开始得到一个&#34; OK&#34;喜欢它。是预期的吗?

之后,我想让它每天只调用一次response_callback,就像在午夜一样。有可能吗?

#define HTTP_HEADER_OFFSET 163
#define MAX_STRINGS 20
#define MAX_STRING_LENGTH 8
#define REQUEST_EVERY_X_MS 5000

/* Setup for Ethernet Library */
static byte mymac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x31 };
const char website[] PROGMEM = "www.arksecurity.net16.net"; 
const char device[] = "0001"; 
char test[MAX_STRINGS][MAX_STRING_LENGTH+1];
String test2 = "1234";
char * comp[20];
uint8_t receivedResponse;
unsigned long timer;

byte Ethernet::buffer[700];

static void response_callback (byte status, word off, word len) {

    int i_string = 0;
    int i_char = 0;
    int i_ethBuff = off + HTTP_HEADER_OFFSET;
    char carat;
    for (i_ethBuff = off + HTTP_HEADER_OFFSET; (carat =(char)Ethernet::buffer[i_ethBuff]) != 0; i_ethBuff++)
    {
        if (carat == '\n')
        { // New line char = new string
            if (i_string < MAX_STRINGS - 1){
                i_string++;
                i_char = 0;
                }
            else
                break; 
        }
        else
        {
            if (i_char < MAX_STRING_LENGTH)
            {
                test[i_string][i_char] = carat;
                i_char++;
            } // otherwise discard the char (max length of string reached)
        }
    }
  receivedResponse = 1;
}
void setup() {
  if (ether.begin(sizeof Ethernet::buffer, mymac, 8) == 0)
  {
    Serial.println("Failed to access Ethernet controller");
    while(1);
  }
  else
    Serial.println("Ethernet controller initialized");
  Serial.println();

  if (!ether.dhcpSetup())
  {
    Serial.println("Failed to get configuration from DHCP");
    while(1);
  }
  else
    Serial.println("DHCP configuration done");

  if (!ether.dnsLookup(website))
  {
    Serial.println("DNS failed");
    while(1);
  }
  else 
    Serial.println("DNS resolution done"); 

  ether.printIp("SRV IP:\t", ether.hisip);
  Serial.println();
  timer = millis() - REQUEST_EVERY_X_MS;
}

void loop() {    
  word pos = ether.packetLoop(ether.packetReceive());

  if (millis() - timer > REQUEST_EVERY_X_MS){
    timer += REQUEST_EVERY_X_MS;
    receivedResponse = 0;
    ether.browseUrl(PSTR("/DevicesQuery.php?device="),device , website, response_callback);

while (!receivedResponse);
for(int i=0; i < 20 - 1; i++){
      comp[i] = test[i];
      if(test2.equals(comp[i])){
      Serial.println("OK");
        }
        else
        Serial.println("Wrong");
       } 

    }

}

Edit4:使用另一种比较。我希望它每5秒捕获一次数据,直到他收到任何东西并让它等待很长时间。问题是,当我使timer2 = 500000;它实际上使它更快地捕获数据。那是为什么?

#include <enc28j60.h>
#include <EtherCard.h>
#include <net.h>

#define HTTP_HEADER_OFFSET 163
#define MAX_STRINGS 10
#define MAX_STRING_LENGTH 20
#define REQUEST_EVERY_X_MS 5000


/* Setup for Ethernet Library */
static byte mymac[] = { 0x74, 0x69, 0x69, 0x2D, 0x30, 0x31 };
const char website[] PROGMEM = "www.arksecurity.net16.net"; //link para o banco de dados do usuário em questão
const char device[] = "0001"; // adicionar aqui o número do dispositivo equivalente ao que aparece no Bando de Dados
char test[MAX_STRINGS][MAX_STRING_LENGTH+1];
String test2 = "9999";
//uint8_t receivedResponse;
unsigned long timer;
unsigned long timer2 = 5000;

byte Ethernet::buffer[700];

static void response_callback (byte status, word off, word len) {
    for(int i=0;i<MAX_STRINGS;i++)
        for(int j=0;j<=MAX_STRING_LENGTH;j++)
            test[i][j] = 0;

    int i_string = 0;
    int i_char = 0;
    int i_ethBuff = off + HTTP_HEADER_OFFSET;
    char carat;
    for (i_ethBuff = off + HTTP_HEADER_OFFSET; (carat = (char)Ethernet::buffer[i_ethBuff]) != 0; i_ethBuff++)
    {
        if (carat == '\n')
        { // New line char = new string
            if (i_string < MAX_STRINGS - 1){
                i_string++;
                i_char = 0;
                }
            else
                break; // Limite de memória do Arduino
        }
        else
        {
            if (i_char < MAX_STRING_LENGTH)
            {
                test[i_string][i_char] = carat;
                i_char++;
            } // otherwise discard the char (max length of string reached)
        }
    }
  //receivedResponse[0] = 1;
}


void setup() {
  Serial.begin(57600);
  if (ether.begin(sizeof Ethernet::buffer, mymac, 53) == 0)
  {
    Serial.println("Failed to access Ethernet controller");
    while(1);
  }
  else
    Serial.println("Ethernet controller initialized");
  Serial.println();

  if (!ether.dhcpSetup())
  {
    Serial.println("Failed to get configuration from DHCP");
    while(1);
  }
  else
    Serial.println("DHCP configuration done");

  if (!ether.dnsLookup(website))
  {
    Serial.println("DNS failed");
    while(1);
  }
  else 
    Serial.println("DNS resolution done"); 

  ether.printIp("SRV IP:\t", ether.hisip);
  Serial.println();

  timer = millis() - timer2;
}

void loop() {

  word pos = ether.packetLoop(ether.packetReceive());

  if (millis() - timer > timer2)
  {
    ether.browseUrl(PSTR("/DevicesQuery.php?device="),device , website, response_callback);

    if(test[0][0] != 0){
    Serial.println("Data Received");
    Serial.println(test[0]);
    Serial.println(test[1]);
    Serial.println(test[2]);
    Serial.println(test[3]);
    Serial.println(test[4]);
    Serial.println(test[5]);
    timer2 = 500000;
    }
    else{
      Serial.println("Nothing");
      timer2 = 5000;
     }
    timer += timer2;
}    

Edit5:我将这部分添加到我的代码中,所以我也可以将数据发送到我的服务器,但我出于某种原因只发送了一个数据,即使我很难使用循环。为什么会这样?

代码:

for(int i = 0; i < countRegister ; i++)
{
   register[i].toCharArray(tempRegister, 80);
   ether.browserUrl(PTSR("/log.php"), tempRegister , browser_callback);
}


static void browser_callback (byte status, word off, word len)
{
 Serial.println("Data sent");
}

1 个答案:

答案 0 :(得分:0)

首先,所有这一切都没有经过测试。我还在等待我的董事会到来,所以...我正在使用我在互联网上找到的一套很好的教程(但是他们使用的是意大利语)。特别是here是关于如何从服务器获取数据的教程。

好的,首先必须设置ENC28J60电路板,其中包含所有必需的参数,例如MAC地址和IP配置。在我使用DHCP的示例中,您可以使用静态配置(希望您知道如何操作)。

然后你必须检查你是否可以解析网站的名称(DNS查询) - 不确定这是否真的有必要。)

然后只需轮询网站(在此示例中每5秒一次)并分析数据包。

以下是代码:

#include <EtherCard.h>

#define HTTP_HEADER_OFFSET 0
static byte mymac[] = {0x12,0x34,0x56,0x78,0x90,0xAB};
char website[] PROGMEM = "www.yourwebsite.com";

byte Ethernet::buffer[700];
static uint32_t timer = 0;

static void response_callback (byte status, word off, word len) {
  Serial.print((const char*) Ethernet::buffer + off + HTTP_HEADER_OFFSET);
} 

void setup () {
  Serial.begin(57600);

  if (!ether.begin(sizeof Ethernet::buffer, mymac, 10))
  {
    Serial.println("Failed to access Ethernet controller");
    while(1);
  }
  else
    Serial.println("Ethernet controller initialized");
  Serial.println();

  if (!ether.dhcpSetup())
  {
    Serial.println("Failed to get configuration from DHCP");
    while(1);
  }
  else
    Serial.println("DHCP configuration done");

  if (!ether.dnsLookup(website))
  {
    Serial.println("DNS failed");
    while(1);
  }
  else 
    Serial.println("DNS resolution done"); 

  ether.printIp("SRV IP:\t", ether.hisip);
  Serial.println();
}

void loop() {
  ether.packetLoop(ether.packetReceive());

  if (millis() > timer) {
    timer = millis() + 5000;
    ether.browseUrl("/", "yourpage.php", website, response_callback);
  }
}

这个草图在串行控制台上的输出将是这样的(图片来自我之前提到的教程)

Http header

正如您所看到的,在实际页面之前有一个标题。你只需计算它们有多少个字符,并将此值放在HTTP_HEADER_OFFSET中。

另一种方法,如果此标题不同,则在当前消息之前添加一些内容(例如将所有内容包装在<body>标记中)并搜索它。

无论如何,在response_callback功能中,您将拥有Ethernet::buffer + off + header_offset中的所有数据。然后你必须解析它并把它放到一个数组中,但我希望你能自己做。

编辑:

要使用静态IP,您必须以这种方式设置:

static byte myip[] = {192,168,1,10};
static byte gatewayip[] = {192,168,1,1};
//static byte dnsip[] = {8,8,8,8}; // Google DNS; change if you want to use another
static byte dnsip[] = {192,168,1,1}; // The OP said that with google dns something was not working
...
ether.staticSetup(myip, gatewayip, dnsip);

EDIT2:

好吧,因为你已经将数据作为字符串,所以很容易将它分成块。一种可能的解决方案:

#define MAX_STRINGS 10
#define MAX_STRING_LENGTH 10

char test[MAX_STRINGS][MAX_STRING_LENGTH+1];

static void response_callback (byte status, word off, word len) {
    for(int i=0;i<MAX_STRINGS;i++)
        for(int j=0;j<=MAX_STRING_LENGTH;j++)
            test[i][j] = 0;

    int i_string = 0;
    int i_char = 0;
    int i_ethBuff = off + HTTP_HEADER_OFFSET;
    char carat;
    for (i_ethBuff = off + HTTP_HEADER_OFFSET; (carat = (char)Ethernet::buffer[i_ethBuff]) != 0; i_ethBuff++)
    {
        if (carat == '\n')
        { // New line char = new string
            if (i_string < MAX_STRINGS-1)
                i_string++;
            else
                break; // Too many strings; discarding all the other ones
        }
        else
        {
            if (i_char < MAX_STRING_LENGTH)
            {
                test[i_string][i_char] = carat;
                i_char++;
            } // otherwise discard the char (max length of string reached
        }
    }
}

如果其中一个长度小于255,则可以将int计数器更改为uint8_t。

EDIT3:

要将字符串检测为分隔符,它有点棘手。例如,要检测"<br>",您可以:

char separator[] = "<br>";

for (i_ethBuff = off + HTTP_HEADER_OFFSET; (carat = (char)Ethernet::buffer[i_ethBuff]) != 0; i_ethBuff++)
{
    if (i_char < MAX_STRING_LENGTH)
    {
        test[i_string][i_char] = carat;
        i_char++;

        uint8_t stringDetected = 0;
        if (i_char >= sizeof(separator))
        {
            int i_sep;
            stringDetected = 1;
            for (i_sep = 0; i_sep < sizeof(separator) && (stringDetected); i_sep++)
            { // You can try to detect it
                if (test[i_string][i_char - sizeof(separator) + i_sep] != separator[i_sep]);
                    stringDetected = 0;
            }
        }

        if (stringDetected)
        {
            test[i_string][i_char - sizeof(separator)] = '\0';
            if (i_string < MAX_STRINGS-1)
                i_string++;
            else
                break; // Too many strings; discarding all the other ones
        }
    }
}

我希望这有效(也许你必须通过+/- 1校正计数器(不知道sizeof是否会返回尾随\ 0)。

请记住增加单个字符串的空间(他们也需要存储分隔符)

编辑4:

至于错误的测试,我认为是因为你尚未收到回复。在测试之前,您可以等待响应回来。

此外,我注意到计时器中有一个小错误:你必须使它长整齐,并测试millis() - 计时器而不是millis()&gt;计时器,否则你会在定时器翻转时遇到问题。我在这段代码中修复了它:

// Before the setup
#define REQUEST_EVERY_X_MS 5000
uint8_t receivedResponse;
unsigned long timer;

// At the end of setup
timer = millis() - REQUEST_EVERY_X_MS;

// Inside response callback
for (i_ethBuff = off + HTTP_HEADER_OFFSET; (carat = (char)Ethernet::buffer[i_ethBuff]) != 0; i_ethBuff++)
{
    ...
}
receivedResponse = 1;

// In the main loop
if (millis() - timer > REQUEST_EVERY_X_MS)
{
    timer += REQUEST_EVERY_X_MS;
    receivedResponse = 0;
    ether.browseUrl(PSTR("/DevicesQuery.php?device="),device , website, response_callback);

    while (!receivedResponse);
    for(int i=0; i < 20 - 1; i++)
    {
        comp[i] = test[i];
        if(test2.equals(comp[i]))
            Serial.println("OK");
        else
            Serial.println("Wrong");
    }
}

至于每天执行一次,如果你想在午夜准确执行它,你需要一些方法来跟踪时间(例如RTC或使用带以太网的NTP协议)。这不是很简单,但你可以找到很多关于如何使用RTC的例子。

另一方面,如果你想每天只执行一次(无论时间),只需将define REQUEST_EVERY_X_MS更改为正确的值(24h = 1440 min = 86400s = 86400000ms,所以{ {1}})它应该在你打开它然后每24小时运行一次。您可以使用最长为4294967294毫秒的时段,或大约49天。

编辑5:

好的,看看其他人的想法(主要来自OP在下面的评论中提供的链接)它看起来是

  1. browseUrl不希望在完成上一次之前被调用两次
  2. 回调不是在中断中,而是在主代码中。因此,您无法阻止该程序。
  3. 所以..试着为后续调用实现这个:

    #define REQUEST_EVERY_X_MS 86400000

    希望这能解决所有问题;)