My C++ client side socket code only collects the very first line of the output provided by an Arduino socket server (I guess I can call it that). Can you please tell me where I am going wrong with my C++ code and how to fix it? If the question is too verbose please jump to the C++ code at the bottom.
Hardware setup: Arduino Mega with an Ethernet card (shield) and an Intel NUC with Ubuntu 16.04. The two devices are connected using cable and an unmanaged switch.
Arduino Side: I started out with a web server example from the Arduino Ethernet library, and modified the code until I was able to collect the status of all I/O, process the I/O data, and make results available to a web client. Image below shows a snapshot of the HTML my Arduino dishes out.
The Arduino code that handles the socket server side of things is as follows (everything works, but thought to include to show everything):
#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");
}
}
Intel NUC with Ubuntu 16.04 and C++11: I have been following a tutorial (https://www.binarytides.com/socket-programming-c-linux-tutorial/) to figure out the socket client side, and learning C++ from https://www.learncpp.com. So far I am able to send a request to a Google server and collect the HTML page using my socket, and print the HTML to the terminal:
Here is my C++ code:
#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;
}
Problem: when I change the IP address from Google to my Arduino in my C++ program, the cpp client socket program only collects the very first line that the Arduino socket server outputs. I know it is the very first line as I modified the first line the Arduino server dishes out by adding "..but it doesn't matter" and the change showed up in the standard output window of the c++ program. I need the C++ program to collect the entire output, not just the first line. for the life of me I can't figure out how.
Could you please help me with the following questions:
- How do I collect the entire Arduino message (and not just the first line)? What modification do I need to do to my C++ program? The system needs to be able to pass data from one device to another.
- The entire objective of this setup is to pass 2 floating point numbers and 6 integers from the Arduino to my C++ program. Very soon I am going to do away with the whole HTML thing. What kind of protocol would you recommend I use when passing the data over? I was thinking of padding each value in letters. Ex: “Aint1A Bint2B Cint3C Dfloat1D ...” and so on. Can you recommend some post/tutorial/web page that suggests the best way to package and process data that arrives to a C++ program via a socket?
I apologize in advance for the 'fix-my-codez' question, but all the questions I have read are a bit too advanced for me as they address buffer overflows, security, endian issues, error handling, malformed messages and so on, which is way beyond my needs (ability is probably more accurate). Thank you very much for your time and help.