簡體   English   中英

如何使用套接字將Arduino連接到C ++?

[英]How to connect Arduino to C++ using sockets?

我的C ++客戶端套接字代碼僅收集Arduino套接字服務器提供的輸出的第一行(我想我可以這樣稱呼)。 您能告訴我我的C ++代碼在哪里出問題以及如何解決嗎? 如果問題太冗長,請跳至底部的C ++代碼。

硬件設置 :帶有以太網卡(屏蔽)的Arduino Mega和帶Ubuntu 16.04的英特爾NUC。 使用電纜和非管理型交換機連接兩個設備。

在此處輸入圖片說明

Arduino Side :我從Arduino以太網庫中的Web服務器示例開始,然后修改了代碼,直到能夠收集所有I / O的狀態,處理I / O數據並將結果提供給Web客戶端。 下圖顯示了我的Arduino提供的HTML的快照。 在此處輸入圖片說明

處理事物的套接字服務器端的Arduino代碼如下(一切正常,但認為包括在內可以顯示所有內容):

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = {0xBA, 0xDA, 0x55, 0x12, 0x34, 0x56};

IPAddress ip(192, 168, 0, 21);

EthernetServer server(80);

void setup() 
{
  Ethernet.begin(mac, ip);

  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) 
  {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware.");
   while (true) 
   {
      delay(1); // do nothing, no point running without Ethernet hardware
   }
}

if (Ethernet.linkStatus() == LinkOFF) 
{
    Serial.println("Ethernet cable is not connected.");
}

server.begin();
}

void loop()
{
    //true when there is an incoming connection
   if (client) 
   {
     Serial.println("new client");
     // an http request ends with a blank line
     boolean currentLineIsBlank = true;
     while (client.connected()) 
     {
       if (client.available()) 
       {
          //the next two lines print the client HTTP GET request to serial.
          char c = client.read();
          Serial.write(c);
          // if you've gotten to the end of the line (received a newline
          // character) and the line is blank, the http request has ended,
          // so you can send a reply
          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: close");  
             client.println("Refresh: 1");  
             client.println();
             client.println("<!DOCTYPE HTML>");
             client.println("<html>");

             //whole bunch of client.println("...."); to dish out a web page.

              client.println("</html>");
              break;
            }

            if (c == '\n') 
            {
               // you're starting a new line
               currentLineIsBlank = true;
            } else if (c != '\r') 
            {
              // you've gotten a character on the current line
              currentLineIsBlank = false;
            }
         }
      }
     // give the web browser time to receive the data
     //delay(1);
     // close the connection:
     client.stop();
     Serial.println("client disconnected");
  }
}

英特爾NUC與Ubuntu 16.04和C ++ 11 :我一直在關注一個教程( https://www.binarytides.com/socket-programming-c-linux-tutorial/ )來找出套接字客戶端,並學習C ++來自https://www.learncpp.com 到目前為止,我已經能夠向Google服務器發送請求,並使用套接字收集HTML頁面,然后將HTML打印到終端: 在此處輸入圖片說明

這是我的C ++代碼:

#include <iostream> //for std::cout & std::endl
#include<arpa/inet.h> //inet_addr
#include<string.h> // for strlen

int main()
{
    int my_socket = socket(AF_INET , SOCK_STREAM , 0);

    //make sure the socket we got is OK
    if (my_socket == -1)
    {
         std::cout << "problem creating a socket." << std::endl;
    }

    struct sockaddr_in connectionToServer;

    connectionToServer.sin_addr.s_addr = inet_addr("172.217.2.99");//google IP
    connectionToServer.sin_family = AF_INET; //type of IP addres. where AF_INET is IPv4
    connectionToServer.sin_port = htons(80); //port is set via a method


    if (connect(my_socket , (struct sockaddr *)&connectionToServer , sizeof(connectionToServer)) < 0)
    {
        std::cout << "connect error" << std::endl;
        return 1;
    }

    std::cout << "Connected" << std::endl;

    //send a request to get a page
    char *message = "GET / HTTP/1.1\r\n\r\n";
    if( send(my_socket , message , strlen(message) , 0) < 0)
    {
        std::cout << "Send failed" << std::endl;
        return 1;
    }

    std::cout << "Data Sent\n" << std::endl;


    char buffer [20000] = {};
    if( recv(my_socket, buffer , sizeof(buffer) , 0) < 0)
    {
        std::cout << "recv failed" << std::endl;
    }

    for(int i=0; i<20000 ; i++)
    {
        std::cout<< buffer[i];
    }

    return 0;
 }

問題 :當我在C ++程序中將IP地址從Google更改為Arduino時,cpp客戶端套接字程序僅收集Arduino套接字服務器輸出的第一行。 我知道這是第一行,因為我通過添加“ ..但沒關系”修改了Arduino服務器的第一行,並且更改顯示在c ++程序的標准輸出窗口中。 我需要C ++程序來收集整個輸出,而不僅僅是第一行。 對於我的一生,我不知道怎么做。

在此處輸入圖片說明

您能幫我解決以下問題:

  1. 我如何收集整個Arduino消息(而不僅僅是第一行)? 我需要對C ++程序進行哪些修改? 該系統需要能夠將數據從一台設備傳遞到另一台設備。
  2. 該設置的整個目標是將2個浮點數和6個整數從Arduino傳遞到我的C ++程序。 很快我將取消整個HTML內容。 您建議在傳遞數據時使用哪種協議? 我正在考慮將每個值都用字母填充。 例如:“ Aint1A Bint2B Cint3C Dfloat1D ...”,依此類推。 您是否可以推薦一些帖子/教程/網頁,以提供最好的方式來打包和處理通過套接字到達C ++程序的數​​據?

我為“ fix-my-codez”問題事先表示歉意,但是我讀過的所有問題對我來說都太高級了,因為它們解決了緩沖區溢出,安全性,字節序問題,錯誤處理,格式錯誤的消息等。遠遠超出了我的需求(能力可能更准確)。 非常感謝您的時間和幫助。

非常感謝Jonathan Potter和Remy Lebeau在評論中提供了有效的答案。 我執行了您的建議/答案,一切正常。 我沒有觸摸Arduino代碼,而是在CPP代碼中進行了以下更改:

在問題中的CPP代碼中,從char buffer [20000] = {};刪除所有內容(包括其中) char buffer [20000] = {}; 並替換為:

//***********receive the results**************************
//initialize a string
std::string totalResults;

//create a temp array. This will hold a single line recieved from
//the arduino.
char tempBuffer [300] = {};

//the number of lines I want to recieve from the arduino
int magicNumber = 100;

//recieve the arduino response
for(int i = 0; i < magicNumber ; i++)
{
    //call the recv method over and over as it gets a single arduino line with
    //every iteration.
    if( recv(my_socket, tempBuffer , sizeof(tempBuffer) , 0) < 0)
    {
    std::cout << "recv failed" << std::endl;
    }

    //write out the single line we recieved to a string (which grows on the fly)
    for(int i = 0; i < 300; i++ )
    {
        totalResults = totalResults+tempBuffer[i];

        //kill the loop the moment there is a null character. When i created the
        //array i initialized it with NULL characters. so if I am seeing a null
        //character it means that the data I recieved from the arduino has all been
        //given to the string.
        if(tempBuffer[i] == NULL)
        {
            break;
        }
    }

    //empty array - see: https://stackoverflow.com/questions/632846/clearing-a-char-array-c
    std::fill(&tempBuffer[0], &tempBuffer[300], 0);

}

//print the results to the standard output.
std::cout << totalResults << std::endl;

這些變化(我確信可以從這里到月球和從月球來回批評)使我能夠獲取arduino發送的數據而不會丟失單個ASCII字符。 謝謝!

在此處輸入圖片說明

另外,感謝David Schwartz和Remy Lebeau指出我使用的HTTP協議非常糟糕。 我使用HTTP,因為我知道這是Arduino代碼中的一個有效示例。 現在的目標是刪除HTML,並找到一種更有效的方法(僅使用套接字)將值傳遞給cpp代碼。 非常感謝您的評論!

****編輯****

好的,因此,如果您仍在閱讀本文,則必須使用以太網將一些信息從Arduino傳遞到cpp程序。 如果是這樣,請繼續閱讀。 在指導我如何接收完整的Arduino響應之后,我刪除了所有HTML和HTTP,並通過字母填充(例如:Aint1B,Cint2D,Efloat1F等)從Arduino發送了我需要的值。 我用~~~字符標記了Arduino的傳輸結束。 涼。 但是,由於某種原因,有時我會得到整個Arduino響應,有時它會丟失一些消息尾部。 這是我學到的:

  1. recv讀取的位置(我不知道的內存或系統調用)有時只能具有一個char值。
  2. 有時\\n字符全部都是recv提取的!
  3. 有時recv讀取的位置可以有多個值! 有時recv方法返回6個字符,有時僅返回4個字符。此行為似乎不可預測。

考慮到此行為,我修改了cpp代碼。 該代碼接收到整個消息,並且當它收到時,將停止recv方法的其他不必要的循環。 希望你覺得它有用:

//***********receive the results**************************
//initialize a string
std::string totalResults = "";

//create a temp array. This will hold a single line recieved from
//the arduino.
char tempBuffer [300] = {};

//the number of lines I want to receive from the Arduino. This is an unusual
//value for the following reasons (figured out via println):
//(1) sometimes the buffer where the recv method reads from has only one value.
//    ex: letter A only (as per my,*ahem", "protocol".
//(2) sometimes the \n is all a recv fetches!
//(3) sometimes the buffer where the recv method reads has multiple values, so
//    the recv fetches many items that get unpacked in the second loop. This is
//    why sometimes we increase the value by only 1, but get WAY more values. I
//    observed this behaviour to be non repeating. Sometimes it reads 5 values,
//    and sometimes it reads only 3 values.
// At a value of 60 I am always getting the message, and run the recv command
// unnecesserily. For this reason I have implemented the "end transmission"
// characters (~~~), which allow me to kill the for loop once the full message is
// retrieved.
int numberOfTimesRecvRuns = 60;

//number of characters per line. do not reduce as it is needed to be this size to
// get the full insult if the protocol is not followed.
int arduinoNumberOfCharsPerLine = 50;

bool fullResponseRecieved = false;

//recieve the entire arduino response. The magic number is the number of times
// we call the recv method (which reads a line from the socket).
for(int i = 0; i < numberOfTimesRecvRuns; i++)
{
    //call the recv method over and over as it gets a single arduino line with
    //every iteration.
    if( recv(my_socket, tempBuffer , sizeof(tempBuffer) , 0) < 0)
    {
    std::cout << "recv failed" << std::endl;
    }

    //write out the single line we recieved to a string (which grows on the fly). 300 because
    //i dont believe I will have more than 300 characters per line.
    for(int j = 0; j < arduinoNumberOfCharsPerLine; j++ )
    {
        totalResults = totalResults+tempBuffer[j];
        std::cout << "i: " << j << " value recv read: " << tempBuffer[j]<< std::endl;

        //kill the loop the moment there is a null character. When i created the
        //array i initialized it with NULL characters. so if I am seeing a null
        //character it means that the data I recieved from the arduino has all been
        //given to the string.
        if(tempBuffer[j] == NULL )
        {
            std::cout << "I ran... See ya" << std::endl;
            break;
        }

        //end of transmission detected
        if(tempBuffer[j] == '~')
        {
            fullResponseRecieved = true;
        }
    }

    //empty array - see: https://stackoverflow.com/questions/632846/clearing-a-char-array-c
    std::fill(&tempBuffer[0], &tempBuffer[300], 0);

    // A '~' character means the full message has been recieved and there is no
    // need to keep looping for the purpose of running the recv method.
    if(fullResponseRecieved == true)
    {
        //reset the value
        fullResponseRecieved = false;
        std::cout << "killing recv loop" << std::endl;
        break;
    }

}

//print the results to the standard output.
std::cout << totalResults << std::endl;

return 0;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM