如何使用套接字将Arduino连接到C ++?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用套接字将Arduino连接到C ++?相关的知识,希望对你有一定的参考价值。
我的C ++客户端套接字代码只收集Arduino套接字服务器提供的输出的第一行(我想我可以称之为)。你能告诉我我的C ++代码在哪里出错以及如何修复它吗?如果问题太冗长,请跳到底部的C ++代码。
硬件设置:带有以太网卡(屏蔽)的Arduino Mega和带有Ubuntu 16.04的Intel 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 == '
' && 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 == '
')
{
// you're starting a new line
currentLineIsBlank = true;
} else if (c != '
')
{
// 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/)来找出套接字客户端,并从https://www.learncpp.com学习C ++。到目前为止,我可以向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
";
if( send(my_socket , message , strlen(message) , 0) < 0)
{
std::cout << "Send failed" << std::endl;
return 1;
}
std::cout << "Data Sent
" << 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 ++程序来收集整个输出,而不仅仅是第一行。对于我的生活,我无法弄清楚如何。
能否帮我解决以下问题:
- 如何收集整个Arduino消息(而不仅仅是第一行)?我需要对C ++程序进行哪些修改?系统需要能够将数据从一个设备传递到另一个设备。
- 此设置的整个目标是将Arduino中的2个浮点数和6个整数传递给我的C ++程序。我很快就会废除整个HTML的东西。你推荐我在传递数据时使用什么样的协议?我在考虑用字母填充每个值。例如:“Aint1A Bint2B Cint3C Dfloat1D ......”等等。你能推荐一些帖子/教程/网页,它建议打包和处理通过套接字到达C ++程序的数据的最佳方法吗?
我提前为'fix-my-codez'问题道歉,但我读到的所有问题对我来说都有点过于先进,因为它们解决了缓冲区溢出,安全性,字节序问题,错误处理,格式错误的消息等问题,超出了我的需求(能力可能更准确)。非常感谢您的时间和帮助。
非常感谢Jonathan Potter和Remy Lebeau在评论中提供了一个有效的答案。我执行了你的建议/答案,一切顺利。我没有触及Arduino代码,并在CPP代码中进行了以下更改:
在问题中的CPP代码中删除(包括)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,并简单地从Arduino中使用字母填充发送了我需要的值(例如:Aint1B,Cint2D,Efloat1F等等)。我用~~~
字符标记了Arduino的传输结束。凉。但是,由于某些原因,有时我会得到整个Arduino响应,有时它会丢失一些消息的尾部。这是我学到的:
- recv从中读取的位置(内存或系统调用我不知道)有时只能有一个char值。
- 有时
- 有时,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
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;
以上是关于如何使用套接字将Arduino连接到C ++?的主要内容,如果未能解决你的问题,请参考以下文章
使用 C++ API 将 mq 客户端连接到具有加密通道的 mq 服务器
如何将我的 mysql 数据库连接到我正在使用此代码的网站:[重复]