ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question

Revision history [back]

Max Carr did a fantastic job, thanks man! Having said that, I had a hard time with (1) getting the Arduino code figured out, and (2) a C++ program to communicate with the Arduino. My contribution (if you can call it that) to this topic is to provide the following:

  • Arduino code that uses sockets (not HTML or HTTP) to communicate to a C++ program. Arduino program can receive different types of requests from C++, and dish out the requested data.
  • C++ easily sends messages to Arduino, and collects the response.

You still have to convert the C++ code to a ROS package, but that is where Max Carr's answer is not at all ambiguous, and there are a ton of resources to figure that out on the ROS website. I will touch on that at the end. However the bulk of thsi answer deals with Arduino to C++ communication via sockets.

Quick note on the Arduino side of things - I see Arduino as a cheap, and effective way to collect the status of sensors (both analog and digital), and control simple devices (lights, relays, etc...). Since you are running ROS, you have ample processing power in your PC, so I don't see you sending the Arduino tons of data to crunch. right? So, the code below receives a 3 character request, where the first two characters are capital letters, followed by a ~ as the end of transmission terminator. The Arduino code receives this message, and depending on the combination of letters (ex: AA~, or AB~, or ZZ~, etc....) the Arduino will take the appropriate action (send a pre-canned command to some device, turn on actuators, or in my case respond with the status of various sensors). In my case I am sending 8 variables back to the C++ program (6 int and 2 float). I am packaging each data type with letters. so the response looks like this: A123B C456D E98F H-23.4532I .... ~~~ With the 3 ~ characters serving as the end of transmission.

Arduino Code

#include <SPI.h>
#include <Ethernet.h>
#include <SimpleTimer.h> //see: https://playground.arduino.cc/Code/SimpleTimer
#include "arduino2.h" //see: https://www.codeproject.com/Articles/732646/Fast-digital-I-O-for-Arduino

// The Mac address is BADA5512456 (BAD A55 [ASS] followed by 123456).
byte mac[] = {0xBA, 0xDA, 0x55, 0x12, 0x34, 0x56};

//The IP address is obviously static, and there is no conflicts with any 
//other device on the robot. 
IPAddress ip(192, 168, 0, 21);

// Initialize the Ethernet server library and assign  
// and port you want to use (port 80 is default for HTTP):
EthernetServer server(23500);

//variables that allow me to figure out what the client is requesting
int iterator = 0;
//made bigher than 3 elements to accomodate bad requests that don't follow my AA~ protocol;
//where the client sends a letter, followed by a letter followed by a ~ character to 
//let the server know the request is over. This allows the server to processed 
//more than just a single type of request
char message[9] = "0000000000"; 

//*************************************************************
put all your global variables here for your application!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//*************************************************************

void setup()
{
    //DO YOUR PROGRAM INITIALIZATION HERE


    // Open serial communications and wait for port to open:
    Serial.begin(9600);//TEMP<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    while (!Serial) 
    {
          ; // wait for serial port to connect. Needed for native USB port only
    }

    Serial.println("Ethernet WebServer Example");

    // start the Ethernet connection and the server:
    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.");
     }

     // start the server
     server.begin();
     Serial.print("server is at ");
     Serial.println(Ethernet.localIP());
}

void loop() 
{  
    //*************PART #1 PROCESS I/O*************************
    put all your code that processes the arduino inputs here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //*******************************************************************
    //******************PART 2 - Send Message to Client***************************************
    // listen for incoming clients
    EthernetClient client = server.available();

    //true when there is an incoming connection
    if (client) 
    {
         Serial.println("new client");

         //as long as a client is connected, (or there is data for the client which is still being
         //written out) this loop will keep going.
         while (client.connected()) 
         {
              //Gets a client that is connected to the server and has data available for reading. 
              //The connection persists when the returned client object goes out of scope; you can 
              //close it by calling client.stop(). The available() method will return a Client object; 
              //if no Client has data available for reading, this object will evaluate to false in an if-statement.
              //basically a client with data for the server can connected to this server.
              if (client.available()) 
              {
                   //read each ASCII character as it comes in.
                   char c = client.read();
                   //the first time the client connects, iterator is at 0, so we store the first
                   //character in element 0 of the message array. We store the incomming message
                   //in the message array.
                   message[iterator] = c;

                   //print the character we just got from the connected client to serial (for troubleshooting)
                   Serial.write(c);

                   // I decided that the communication between the cpp and arduino will be two letters followed by
                   // a tilda '~'. This gives me 26 * 26 cobinations, and I know the message is finished when 
                   // I see the ~ character. Below I process the request I have recieved based on the request.
                   if ((message[0]=='A') && (message[1]=='A') && (message[2]=='~'))
                   {
                        //send all 6 wheel values to CPP
                        sendAllDataToCpp(client);

                        //this break allows me to exit the 'while' loop.
                        break;
                   }

                   //this is a test message to make sure my arduino code can process multiple
                  //cpp requests. 
                  if ((message[0]=='A') && (message[1]=='B') && (message[2]=='~'))
                  {
                        //sends a simple test message
                        sendTestMessage(client);

                        //exit the while loop.
                        break;
                   }

                   //if a message is bad, we need to terminate connection with client. Bad meaning:
                   //(1) if iterator is larger than 3 that means client sent more than XX~
                   //(2) if client sent 3 characters, but the 3rd is not a ~ character
                   //I stopped trying to catch other types of violations.
                   if((iterator >= 3) || ((iterator >= 2) && (message[2] != '~')))
                   {
                        //sends a simple test message
                        sendBadProtocolMessage(client);

                        //exit the while loop.
                        break;
                    }

                   //this value starts at 0 when a new client connects, and with every ascii character
                   //the arduion recieves from the client, this value is incremented by 1.
                   iterator = iterator+1;

                   //troubleshooting only
                   Serial.println(iterator);
             }
       }

       //end of transmission (let cpp know everything is done.
       client.println("~~~");
       // We have broken out of the while loop; meaning we recieved a proper
       //request (or not), and we have sent a response. Time to close connection,
       // reset iterator, and wipe old message - this prepares us for a new client.
       client.stop();// close the connection
       Serial.println("client disconnected"); //testing only
       iterator = 0; //reset iterator
       message[0]=0; //wipe message clean
       message[1]=0; //wipe message clean
       message[2]=0; //wipre message clean  
       //if your C++ might send more than 3 chars, wipe the entire char array....
    }
}


//*****************************CUSTOM METHODS***************************************************
//for custom method refrence see: https://playground.arduino.cc/Code/Function
//PUT ALL YOUR CUSTOM METHODS HERE (that process your I/O)!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

//This is a test message to demonstarte that I can make different requests
//to arduino.
void sendTestMessage(EthernetClient client)
{
      client.println("TEST PASSED");
}

 //This is a message to a bad client (not following format)
 void sendBadProtocolMessage(EthernetClient client)
{
       client.println("YOU ARE NOT FOLLOWING THE PROTOCOL - GOODBYE DUMMY");
}

//send the wheel data to cpp
void sendAllDataToCpp(EthernetClient client)
{
      //>>>>>put all the values you want to send to cpp here<<<<<<<

      //show the motion status of the right wheel. true == moving; false==stopped
      client.println("A");
      client.println(rightWheelMotionStatus);
      client.println("B");

     //show the directoin of the right wheel (0==stopped; 1==forward; 2== backwards)
     client.println("C");
     client.println(rightWheelDirectionOfMotion);
     client.println("D");

     //The total pulses for the right wheel (additive & subtracive ralative to start position)
     client.println("E");
     client.println(rightWheelEncoderCountSinceStart);
     client.println("F");

     //Report the position of the right wheel in degrees.
     client.println("G");
     client.println(rightWheelPositionInDegrees);
     client.println("H");

     //LEFT WHEEL<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<To Do post Right wheel.

     //show the motion status of the right wheel (true == moving; false==stopped)
     client.println("I");
     client.println(leftWheelMotionStatus);
     client.println("J");

     //show the directoin of the right wheel, 0==stopped; 2==LOGICAL forward; 1==LOGICAL backward
     client.println("K");
     client.println(leftWheelDirectionOfMotion);
     client.println("L");

     //show the total pulses for the left wheel (additive & subtracive ralative to start position)
     client.println("M");
     client.println(leftWheelEncoderCountSinceStart);
     client.println("N");

     //report the position of the right wheel in degrees
     client.println("O");
     client.println(leftWheelPositionInDegrees);
     client.println("P");
}

That was pretty easy, no? This code is pretty easy to modify for your needs; simply put your code where the !!!!!!!! characters are. Oh, two more key points:

  1. NEVER put !!! anywhere in the Arduino code, not even in the comments. I remember I had !!! in my comments and the code would not compile. It was a bug, and don't know if it's fixed (probably).
  2. Uhm... I forget. If

Max Carr did a fantastic job, thanks man! Having said that, I had a hard time with (1) getting the Arduino code figured out, and (2) a C++ program to communicate with the Arduino. My contribution (if you can call it that) to this topic is to provide the following:

  • Arduino code that uses sockets (not HTML or HTTP) to communicate to a C++ program. Arduino program can receive different types of requests from C++, and dish out the requested data.
  • C++ easily sends messages to Arduino, and collects the response.

You still have to convert the C++ code to a ROS package, but that is where Max Carr's answer is not at all ambiguous, and there are a ton of resources to figure that out on the ROS website. I will touch on that at the end. However the bulk of thsi answer deals with Arduino to C++ communication via sockets.

Quick note on the Arduino side of things - I see Arduino as a cheap, and effective way to collect the status of sensors (both analog and digital), and control simple devices (lights, relays, etc...). Since you are running ROS, you have ample processing power in your PC, so I don't see you sending the Arduino tons of data to crunch. right? So, the code below receives a 3 character request, where the first two characters are capital letters, followed by a ~ as the end of transmission terminator. The Arduino code receives this message, and depending on the combination of letters (ex: AA~, or AB~, or ZZ~, etc....) the Arduino will take the appropriate action (send a pre-canned command to some device, turn on actuators, or in my case respond with the status of various sensors). In my case I am sending 8 variables back to the C++ program (6 int and 2 float). I am packaging each data type with letters. so the response looks like this: A123B C456D E98F H-23.4532I .... ~~~ With the 3 ~ characters serving as the end of transmission.

Arduino Code

#include <SPI.h>
#include <Ethernet.h>
#include <SimpleTimer.h> //see: https://playground.arduino.cc/Code/SimpleTimer
#include "arduino2.h" //see: https://www.codeproject.com/Articles/732646/Fast-digital-I-O-for-Arduino

// The Mac address is BADA5512456 (BAD A55 [ASS] followed by 123456).
byte mac[] = {0xBA, 0xDA, 0x55, 0x12, 0x34, 0x56};

//The IP address is obviously static, and there is no conflicts with any 
//other device on the robot. 
IPAddress ip(192, 168, 0, 21);

// Initialize the Ethernet server library and assign  
// and port you want to use (port 80 is default for HTTP):
EthernetServer server(23500);

//variables that allow me to figure out what the client is requesting
int iterator = 0;
//made bigher than 3 elements to accomodate bad requests that don't follow my AA~ protocol;
//where the client sends a letter, followed by a letter followed by a ~ character to 
//let the server know the request is over. This allows the server to processed 
//more than just a single type of request
char message[9] = "0000000000"; 

//*************************************************************
put all your global variables here for your application!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//*************************************************************

void setup()
{
    //DO YOUR PROGRAM INITIALIZATION HERE


    // Open serial communications and wait for port to open:
    Serial.begin(9600);//TEMP<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    while (!Serial) 
    {
          ; // wait for serial port to connect. Needed for native USB port only
    }

    Serial.println("Ethernet WebServer Example");

    // start the Ethernet connection and the server:
    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.");
     }

     // start the server
     server.begin();
     Serial.print("server is at ");
     Serial.println(Ethernet.localIP());
}

void loop() 
{  
    //*************PART #1 PROCESS I/O*************************
    put all your code that processes the arduino inputs here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //*******************************************************************
    //******************PART 2 - Send Message to Client***************************************
    // listen for incoming clients
    EthernetClient client = server.available();

    //true when there is an incoming connection
    if (client) 
    {
         Serial.println("new client");

         //as long as a client is connected, (or there is data for the client which is still being
         //written out) this loop will keep going.
         while (client.connected()) 
         {
              //Gets a client that is connected to the server and has data available for reading. 
              //The connection persists when the returned client object goes out of scope; you can 
              //close it by calling client.stop(). The available() method will return a Client object; 
              //if no Client has data available for reading, this object will evaluate to false in an if-statement.
              //basically a client with data for the server can connected to this server.
              if (client.available()) 
              {
                   //read each ASCII character as it comes in.
                   char c = client.read();
                   //the first time the client connects, iterator is at 0, so we store the first
                   //character in element 0 of the message array. We store the incomming message
                   //in the message array.
                   message[iterator] = c;

                   //print the character we just got from the connected client to serial (for troubleshooting)
                   Serial.write(c);

                   // I decided that the communication between the cpp and arduino will be two letters followed by
                   // a tilda '~'. This gives me 26 * 26 cobinations, and I know the message is finished when 
                   // I see the ~ character. Below I process the request I have recieved based on the request.
                   if ((message[0]=='A') && (message[1]=='A') && (message[2]=='~'))
                   {
                        //send all 6 wheel values to CPP
                        sendAllDataToCpp(client);

                        //this break allows me to exit the 'while' loop.
                        break;
                   }

                   //this is a test message to make sure my arduino code can process multiple
                  //cpp requests. 
                  if ((message[0]=='A') && (message[1]=='B') && (message[2]=='~'))
                  {
                        //sends a simple test message
                        sendTestMessage(client);

                        //exit the while loop.
                        break;
                   }

                   //if a message is bad, we need to terminate connection with client. Bad meaning:
                   //(1) if iterator is larger than 3 that means client sent more than XX~
                   //(2) if client sent 3 characters, but the 3rd is not a ~ character
                   //I stopped trying to catch other types of violations.
                   if((iterator >= 3) || ((iterator >= 2) && (message[2] != '~')))
                   {
                        //sends a simple test message
                        sendBadProtocolMessage(client);

                        //exit the while loop.
                        break;
                    }

                   //this value starts at 0 when a new client connects, and with every ascii character
                   //the arduion recieves from the client, this value is incremented by 1.
                   iterator = iterator+1;

                   //troubleshooting only
                   Serial.println(iterator);
             }
       }

       //end of transmission (let cpp know everything is done.
       client.println("~~~");
       // We have broken out of the while loop; meaning we recieved a proper
       //request (or not), and we have sent a response. Time to close connection,
       // reset iterator, and wipe old message - this prepares us for a new client.
       client.stop();// close the connection
       Serial.println("client disconnected"); //testing only
       iterator = 0; //reset iterator
       message[0]=0; //wipe message clean
       message[1]=0; //wipe message clean
       message[2]=0; //wipre message clean  
       //if your C++ might send more than 3 chars, wipe the entire char array....
    }
}


//*****************************CUSTOM METHODS***************************************************
//for custom method refrence see: https://playground.arduino.cc/Code/Function
//PUT ALL YOUR CUSTOM METHODS HERE (that process your I/O)!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

//This is a test message to demonstarte that I can make different requests
//to arduino.
void sendTestMessage(EthernetClient client)
{
      client.println("TEST PASSED");
}

 //This is a message to a bad client (not following format)
 void sendBadProtocolMessage(EthernetClient client)
{
       client.println("YOU ARE NOT FOLLOWING THE PROTOCOL - GOODBYE DUMMY");
}

//send the wheel data to cpp
void sendAllDataToCpp(EthernetClient client)
{
      //>>>>>put all the values you want to send to cpp here<<<<<<<

      //show the motion status of the right wheel. true == moving; false==stopped
      client.println("A");
      client.println(rightWheelMotionStatus);
      client.println("B");

     //show the directoin of the right wheel (0==stopped; 1==forward; 2== backwards)
     client.println("C");
     client.println(rightWheelDirectionOfMotion);
     client.println("D");

     //The total pulses for the right wheel (additive & subtracive ralative to start position)
     client.println("E");
     client.println(rightWheelEncoderCountSinceStart);
     client.println("F");

     //Report the position of the right wheel in degrees.
     client.println("G");
     client.println(rightWheelPositionInDegrees);
     client.println("H");

     //LEFT WHEEL<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<To Do post Right wheel.

     //show the motion status of the right wheel (true == moving; false==stopped)
     client.println("I");
     client.println(leftWheelMotionStatus);
     client.println("J");

     //show the directoin of the right wheel, 0==stopped; 2==LOGICAL forward; 1==LOGICAL backward
     client.println("K");
     client.println(leftWheelDirectionOfMotion);
     client.println("L");

     //show the total pulses for the left wheel (additive & subtracive ralative to start position)
     client.println("M");
     client.println(leftWheelEncoderCountSinceStart);
     client.println("N");

     //report the position of the right wheel in degrees
     client.println("O");
     client.println(leftWheelPositionInDegrees);
     client.println("P");
}

That was pretty easy, no? This code is pretty easy to modify for your needs; simply put your code where the !!!!!!!! characters are. Oh, two more key points:

  1. NEVER put !!! anywhere in the Arduino code, not even in the comments. I remember I had !!! in my comments and the code would not compile. It was a bug, and don't know if it's fixed (probably).
  2. Uhm... Natively the Arduino IO is very slow. However there is a library to speed up your I/O 10 fold! and its super easy!! have a look here: https://www.codeproject.com/Articles/732646/Fast-digital-I-O-for-Arduino

Next is the C++ side of things. since ROS is a thing that programmers do, I forget. am sure you programmers can criticize the heck out of this code. If that is the case, please just fix it. I used C++11, so that will be important. This code sends a request to the arduino (using my AA~ "protocol"), and once the Arduino responds the message is collected. In my case, like I said I am getting 8 values, and I process the message to extract the values and save them as 6 ints and 2 floats.

C++ CODE

#include <iostream> //for std::cout & std::endl
#include<arpa/inet.h> //inet_addr
#include<cstring> // for strlen (never use string.h. See https://stackoverflow.com/questions/9257665/difference-between-string-and-string-h#9257719
#include<string> //for std::stoi
//#include<typeinfo> // for typeid. See https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c#20170989

//>>>>>>>>>>>>>>>>>>>RIGHT WHEEL VALUES<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//show the motion status of the right wheel. true == moving; false==stopped (A___B)
static int g_rightWheelMotionStatus = -1;

//show the directoin of the right wheel (0==stopped; 1==forward; 2== backwards) (C___D)
static int g_rightWheelDirectionOfMotion = -1;

//The total pulses for the right wheel (additive & subtracive ralative to start position) (E___F)
static int g_rightWheelEncoderCountSinceStart = 0;

//Report the position of the right wheel in degrees. (G___H)
static float g_rightWheelPositionInDegrees = -0.001;


//>>>>>>>>>>>>>>>>>>>LEFT WHEEL VALUES<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//show the motion status of the right wheel (true == moving; false==stopped) (I___J)
static int g_leftWheelMotionStatus = -1;

//show the directoin of the right wheel, 0==stopped; 2==LOGICAL forward; 1==LOGICAL backward (K___L)
static int g_leftWheelDirectionOfMotion = -1;

//show the total pulses for the left wheel (additive & subtracive ralative to start position) (M___N)
static int g_leftWheelEncoderCountSinceStart = 0;

//report the position of the right wheel in degrees (O___P)
static float g_leftWheelPositionInDegrees = -0.001;
///////////////////////////////////////////////////////////////////////////////////////////////

//forward declaration. for passing by reference see:
//https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToIntAndAssignToGlobalVariables(std::string, std::string, std::string, int &);
void processStringToFloatAndAssignToGlobalVariables(std::string, std::string, std::string, float &);

int main()
{
    //initializing the socket with the socket() function.
    //arg 1 - Address Family - AF_INET (this is IP version 4)
    //arg 2 - Type - SOCK_STREAM (this means connection oriented TCP protocol)
    //        SOCK_DGRAM indicates the UDP protocol.
    //arg 3 - Protocol - 0 [ or IPPROTO_IP This is IP protocol]
    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;
    }

    //the struct is defined in "/usr/include/netinet/in.h", and it uses a
    //data type called "in_addr_t" which is defined in "/usr/include/arpa/inet.h".
    //since arpa contains a "#include netinet/in.h" we can get away with using
    //only a "#include arpa/inet.h" header.

    struct sockaddr_in connectionToServer;

    //this code leverages the sockaddr_in structure, where I assign
    //values to a few of the structure's elements
    //connectionToServer.sin_addr.s_addr = inet_addr("172.217.2.99");//google IP
    connectionToServer.sin_addr.s_addr = inet_addr("192.168.0.21");//arduino IP address
    connectionToServer.sin_family = AF_INET; //type of IP addres. where AF_INET is IPv4
    connectionToServer.sin_port = htons(23500); //port is set via a method

    //Connect to remote server. ...sys/socket.h, which contains a definition for connect,
    // is part of the O/S code files. I searched for it, and found at least 20 socket.h
    //files all over the O/S.
    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 = "AA~";
   if( send(my_socket , message , strlen(message) , 0) < 0)
   {
        std::cout << "Send failed" << std::endl;
        return 1;
    }

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


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

    //create a temp array. This will hold a single line recieved from
    //the arduino. Array is initialized with NULs (which makes making a
    //string out of a char array with nuls a pain in the ass!!)
    char tempBuffer [300] = {};

    //fill array with money. this special character will allow us to know
    //the location where the data we wrote in meets the original '$' contents.
    std::fill(&tempBuffer[0], &tempBuffer[300], '$');


    //the number of lines I want to recieve 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 feteches!
    //(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. The data is written into the tempBuffer array which has
        //been pre-filled with $ characters.
        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. However once the data the
        //recv has finished ($ character is seen) or it is the end of the transmission (~), then
        //exit the loop.
        for(int j = 0; j < arduinoNumberOfCharsPerLine; j++ )
        {
            //kill the loop the moment there is a $ character. When i created the
            //array i initialized it with $ characters. so if I am seeing a $
            //character it means that the data recv recieved from the arduino has all been
            //given to the string.
            if(tempBuffer[j] == '$' )
            {
                //std::cout << "I ran... See ya" << std::endl;
                break;
            }

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

            totalResults = totalResults+tempBuffer[j];
            //test only
            //std::cout << "i: " << j << " value recv read: " << tempBuffer[j]<< std::endl;
        }

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

        // 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 << "recieved message: " <<std::endl;
    std::cout << totalResults << std::endl;
    std::cout << "length of string: " << totalResults.length() << std::endl;

    //process the int and float values we recieved and push them into memory
    //RIGHT wheel int
    processStringToIntAndAssignToGlobalVariables(totalResults, "A", "B", g_rightWheelMotionStatus);
    processStringToIntAndAssignToGlobalVariables(totalResults, "C", "D", g_rightWheelDirectionOfMotion);
    processStringToIntAndAssignToGlobalVariables(totalResults, "E", "F", g_rightWheelEncoderCountSinceStart);
    //RIGHT wheel float
    processStringToFloatAndAssignToGlobalVariables(totalResults, "G", "H", g_rightWheelPositionInDegrees);

    //LEFT wheel int
    processStringToIntAndAssignToGlobalVariables(totalResults, "I", "J", g_leftWheelMotionStatus);
    processStringToIntAndAssignToGlobalVariables(totalResults, "K", "L", g_leftWheelDirectionOfMotion);
    processStringToIntAndAssignToGlobalVariables(totalResults, "M", "N", g_leftWheelEncoderCountSinceStart);
    //LEFT wheel float
    processStringToFloatAndAssignToGlobalVariables(totalResults, "O", "P", g_leftWheelPositionInDegrees);

    //TESTING<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    //std::cout << "global Variable value: " << g_rightWheelMotionStatus << std::endl;

    return 0;
}

//for passing by reference see: https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToIntAndAssignToGlobalVariables(std::string allData, std::string start, std::string finish, int &globalVariable)
{
    //get the value.See:
    //https://stackoverflow.com/questions/30073839/c-extract-number-from-the-middle-of-a-string#30074453
    //http://www.cplusplus.com/reference/string/string/find/
    int p1 = allData.find(start);
    int p2 = allData.find(finish);

    //arg #1 - start position
    //arg #2 - length
    //See http://www.cplusplus.com/reference/string/string/substr/
    //indeed returns a string....
    std::string stringResult = allData.substr(p1 +1 , p2 - p1 -1);

    std::cout << "********************START*********************" << std::endl;
    std::cout << "what we extracted: _" << stringResult << "_"<< std::endl;

    //convert the string value to an integer. See claudios answer in:
    //https://stackoverflow.com/questions/7663709/how-can-i-convert-a-stdstring-to-int#7664227
    //also see: http://www.cplusplus.com/reference/string/stoi/
    int finalValue = std::stoi(stringResult);

    //value of passed in global value
    std::cout << "int global value as argument's value: " << globalVariable << std::endl;

    //assign the integer I recovered to the global variable to which it belongs
    globalVariable = finalValue;

    std::cout << "int global value is now: " << globalVariable << std::endl;
}

//for passing by reference see: https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToFloatAndAssignToGlobalVariables(std::string allData, std::string start, std::string finish, float &floatGlobalVariable)
{
    //get the value.See:
    //https://stackoverflow.com/questions/30073839/c-extract-number-from-the-middle-of-a-string#30074453
    //http://www.cplusplus.com/reference/string/string/find/
    int p1 = allData.find(start);
    int p2 = allData.find(finish);

    //arg #1 - start position
    //arg #2 - length
    //See http://www.cplusplus.com/reference/string/string/substr/
    //indeed returns a string....
    std::string stringResult = allData.substr(p1 +1 , p2 - p1 -1);

    std::cout << "********************START*********************" << std::endl;
    std::cout << "what we extracted: _" << stringResult << "_"<< std::endl;

    //convert the string value to an integer. See claudios answer in:
    //https://stackoverflow.com/questions/7663709/how-can-i-convert-a-stdstring-to-int#7664227
    //also see: http://www.cplusplus.com/reference/string/stoi/
    float finalValue = std::stof(stringResult);

    //value of passed in global value
    std::cout << "float global value as argument's value: " << floatGlobalVariable << std::endl;

    //assign the integer I recovered to the global variable to which it belongs
    floatGlobalVariable = finalValue;

    std::cout << "float global value is now: " << floatGlobalVariable << std::endl;
}

Max Carr did a fantastic job, thanks man! Having said that, I had a hard time with (1) getting the Arduino code figured out, and (2) a C++ program to communicate with the Arduino. My contribution (if you can call it that) to this topic is to provide the following:

  • Arduino code that uses sockets (not HTML or HTTP) to communicate to a C++ program. Arduino program can receive different types of requests from C++, and dish out the requested data.
  • C++ easily sends messages to Arduino, and collects the response.

You still have to convert the C++ code to a ROS package, but that is where Max Carr's answer is not at all ambiguous, and there are a ton of resources to figure that out on the ROS website. I will touch on that at the end. However the bulk of thsi answer deals with Arduino to C++ communication via sockets.

The hardware is a PC and an Arduino Mega with an ethernet shield. I have a switch in between.

image description

Quick note on the Arduino side of things - I see Arduino as a cheap, and effective way to collect the status of sensors (both analog and digital), and control simple devices (lights, relays, etc...). Since you are running ROS, you have ample processing power in your PC, so I don't see you sending the Arduino tons of data to crunch. right? So, the code below receives a 3 character request, where the first two characters are capital letters, followed by a ~ as the end of transmission terminator. The Arduino code receives this message, and depending on the combination of letters (ex: AA~, or AB~, or ZZ~, etc....) the Arduino will take the appropriate action (send a pre-canned command to some device, turn on actuators, or in my case respond with the status of various sensors). In my case I am sending 8 variables back to the C++ program (6 int and 2 float). I am packaging each data type with letters. so the response looks like this: A123B C456D E98F H-23.4532I .... ~~~ With the 3 ~ characters serving as the end of transmission.

Arduino Code

#include <SPI.h>
#include <Ethernet.h>
#include <SimpleTimer.h> //see: https://playground.arduino.cc/Code/SimpleTimer
#include "arduino2.h" //see: https://www.codeproject.com/Articles/732646/Fast-digital-I-O-for-Arduino

// The Mac address is BADA5512456 (BAD A55 [ASS] followed by 123456).
byte mac[] = {0xBA, 0xDA, 0x55, 0x12, 0x34, 0x56};

//The IP address is obviously static, and there is no conflicts with any 
//other device on the robot. 
IPAddress ip(192, 168, 0, 21);

// Initialize the Ethernet server library and assign  
// and port you want to use (port 80 is default for HTTP):
EthernetServer server(23500);

//variables that allow me to figure out what the client is requesting
int iterator = 0;
//made bigher than 3 elements to accomodate bad requests that don't follow my AA~ protocol;
//where the client sends a letter, followed by a letter followed by a ~ character to 
//let the server know the request is over. This allows the server to processed 
//more than just a single type of request
char message[9] = "0000000000"; 

//*************************************************************
put all your global variables here for your application!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//*************************************************************

void setup()
{
    //DO YOUR PROGRAM INITIALIZATION HERE


    // Open serial communications and wait for port to open:
    Serial.begin(9600);//TEMP<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    while (!Serial) 
    {
          ; // wait for serial port to connect. Needed for native USB port only
    }

    Serial.println("Ethernet WebServer Example");

    // start the Ethernet connection and the server:
    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.");
     }

     // start the server
     server.begin();
     Serial.print("server is at ");
     Serial.println(Ethernet.localIP());
}

void loop() 
{  
    //*************PART #1 PROCESS I/O*************************
    put all your code that processes the arduino inputs here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //*******************************************************************
    //******************PART 2 - Send Message to Client***************************************
    // listen for incoming clients
    EthernetClient client = server.available();

    //true when there is an incoming connection
    if (client) 
    {
         Serial.println("new client");

         //as long as a client is connected, (or there is data for the client which is still being
         //written out) this loop will keep going.
         while (client.connected()) 
         {
              //Gets a client that is connected to the server and has data available for reading. 
              //The connection persists when the returned client object goes out of scope; you can 
              //close it by calling client.stop(). The available() method will return a Client object; 
              //if no Client has data available for reading, this object will evaluate to false in an if-statement.
              //basically a client with data for the server can connected to this server.
              if (client.available()) 
              {
                   //read each ASCII character as it comes in.
                   char c = client.read();
                   //the first time the client connects, iterator is at 0, so we store the first
                   //character in element 0 of the message array. We store the incomming message
                   //in the message array.
                   message[iterator] = c;

                   //print the character we just got from the connected client to serial (for troubleshooting)
                   Serial.write(c);

                   // I decided that the communication between the cpp and arduino will be two letters followed by
                   // a tilda '~'. This gives me 26 * 26 cobinations, and I know the message is finished when 
                   // I see the ~ character. Below I process the request I have recieved based on the request.
                   if ((message[0]=='A') && (message[1]=='A') && (message[2]=='~'))
                   {
                        //send all 6 wheel values to CPP
                        sendAllDataToCpp(client);

                        //this break allows me to exit the 'while' loop.
                        break;
                   }

                   //this is a test message to make sure my arduino code can process multiple
                  //cpp requests. 
                  if ((message[0]=='A') && (message[1]=='B') && (message[2]=='~'))
                  {
                        //sends a simple test message
                        sendTestMessage(client);

                        //exit the while loop.
                        break;
                   }

                   //if a message is bad, we need to terminate connection with client. Bad meaning:
                   //(1) if iterator is larger than 3 that means client sent more than XX~
                   //(2) if client sent 3 characters, but the 3rd is not a ~ character
                   //I stopped trying to catch other types of violations.
                   if((iterator >= 3) || ((iterator >= 2) && (message[2] != '~')))
                   {
                        //sends a simple test message
                        sendBadProtocolMessage(client);

                        //exit the while loop.
                        break;
                    }

                   //this value starts at 0 when a new client connects, and with every ascii character
                   //the arduion recieves from the client, this value is incremented by 1.
                   iterator = iterator+1;

                   //troubleshooting only
                   Serial.println(iterator);
             }
       }

       //end of transmission (let cpp know everything is done.
       client.println("~~~");
       // We have broken out of the while loop; meaning we recieved a proper
       //request (or not), and we have sent a response. Time to close connection,
       // reset iterator, and wipe old message - this prepares us for a new client.
       client.stop();// close the connection
       Serial.println("client disconnected"); //testing only
       iterator = 0; //reset iterator
       message[0]=0; //wipe message clean
       message[1]=0; //wipe message clean
       message[2]=0; //wipre message clean  
       //if your C++ might send more than 3 chars, wipe the entire char array....
    }
}


//*****************************CUSTOM METHODS***************************************************
//for custom method refrence see: https://playground.arduino.cc/Code/Function
//PUT ALL YOUR CUSTOM METHODS HERE (that process your I/O)!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

//This is a test message to demonstarte that I can make different requests
//to arduino.
void sendTestMessage(EthernetClient client)
{
      client.println("TEST PASSED");
}

 //This is a message to a bad client (not following format)
 void sendBadProtocolMessage(EthernetClient client)
{
       client.println("YOU ARE NOT FOLLOWING THE PROTOCOL - GOODBYE DUMMY");
}

//send the wheel data to cpp
void sendAllDataToCpp(EthernetClient client)
{
      //>>>>>put all the values you want to send to cpp here<<<<<<<

      //show the motion status of the right wheel. true == moving; false==stopped
      client.println("A");
      client.println(rightWheelMotionStatus);
      client.println("B");

     //show the directoin of the right wheel (0==stopped; 1==forward; 2== backwards)
     client.println("C");
     client.println(rightWheelDirectionOfMotion);
     client.println("D");

     //The total pulses for the right wheel (additive & subtracive ralative to start position)
     client.println("E");
     client.println(rightWheelEncoderCountSinceStart);
     client.println("F");

     //Report the position of the right wheel in degrees.
     client.println("G");
     client.println(rightWheelPositionInDegrees);
     client.println("H");

     //LEFT WHEEL<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<To Do post Right wheel.

     //show the motion status of the right wheel (true == moving; false==stopped)
     client.println("I");
     client.println(leftWheelMotionStatus);
     client.println("J");

     //show the directoin of the right wheel, 0==stopped; 2==LOGICAL forward; 1==LOGICAL backward
     client.println("K");
     client.println(leftWheelDirectionOfMotion);
     client.println("L");

     //show the total pulses for the left wheel (additive & subtracive ralative to start position)
     client.println("M");
     client.println(leftWheelEncoderCountSinceStart);
     client.println("N");

     //report the position of the right wheel in degrees
     client.println("O");
     client.println(leftWheelPositionInDegrees);
     client.println("P");
}

That was pretty easy, no? This code is pretty easy to modify for your needs; simply put your code where the !!!!!!!! characters are. Oh, two more key points:

  1. NEVER put !!! anywhere in the Arduino code, not even in the comments. I remember I had !!! in my comments and the code would not compile. It was a bug, and don't know if it's fixed (probably).
  2. Natively the Arduino IO is very slow. However there is a library to speed up your I/O 10 fold! and its super easy!! have a look here: https://www.codeproject.com/Articles/732646/Fast-digital-I-O-for-Arduino

Next is the C++ side of things. since ROS is a thing that programmers do, I am sure you programmers can criticize the heck out of this code. If that is the case, please just fix it. I used C++11, so that will be important. This code sends a request to the arduino (using my AA~ "protocol"), and once the Arduino responds the message is collected. In my case, like I said I am getting 8 values, and I process the message to extract the values and save them as 6 ints and 2 floats.

C++ CODE

#include <iostream> //for std::cout & std::endl
#include<arpa/inet.h> //inet_addr
#include<cstring> // for strlen (never use string.h. See https://stackoverflow.com/questions/9257665/difference-between-string-and-string-h#9257719
#include<string> //for std::stoi
//#include<typeinfo> // for typeid. See https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c#20170989

//>>>>>>>>>>>>>>>>>>>RIGHT WHEEL VALUES<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//show the motion status of the right wheel. true == moving; false==stopped (A___B)
static int g_rightWheelMotionStatus = -1;

//show the directoin of the right wheel (0==stopped; 1==forward; 2== backwards) (C___D)
static int g_rightWheelDirectionOfMotion = -1;

//The total pulses for the right wheel (additive & subtracive ralative to start position) (E___F)
static int g_rightWheelEncoderCountSinceStart = 0;

//Report the position of the right wheel in degrees. (G___H)
static float g_rightWheelPositionInDegrees = -0.001;


//>>>>>>>>>>>>>>>>>>>LEFT WHEEL VALUES<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//show the motion status of the right wheel (true == moving; false==stopped) (I___J)
static int g_leftWheelMotionStatus = -1;

//show the directoin of the right wheel, 0==stopped; 2==LOGICAL forward; 1==LOGICAL backward (K___L)
static int g_leftWheelDirectionOfMotion = -1;

//show the total pulses for the left wheel (additive & subtracive ralative to start position) (M___N)
static int g_leftWheelEncoderCountSinceStart = 0;

//report the position of the right wheel in degrees (O___P)
static float g_leftWheelPositionInDegrees = -0.001;
///////////////////////////////////////////////////////////////////////////////////////////////

//forward declaration. for passing by reference see:
//https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToIntAndAssignToGlobalVariables(std::string, std::string, std::string, int &);
void processStringToFloatAndAssignToGlobalVariables(std::string, std::string, std::string, float &);

int main()
{
    //initializing the socket with the socket() function.
    //arg 1 - Address Family - AF_INET (this is IP version 4)
    //arg 2 - Type - SOCK_STREAM (this means connection oriented TCP protocol)
    //        SOCK_DGRAM indicates the UDP protocol.
    //arg 3 - Protocol - 0 [ or IPPROTO_IP This is IP protocol]
    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;
    }

    //the struct is defined in "/usr/include/netinet/in.h", and it uses a
    //data type called "in_addr_t" which is defined in "/usr/include/arpa/inet.h".
    //since arpa contains a "#include netinet/in.h" we can get away with using
    //only a "#include arpa/inet.h" header.

    struct sockaddr_in connectionToServer;

    //this code leverages the sockaddr_in structure, where I assign
    //values to a few of the structure's elements
    //connectionToServer.sin_addr.s_addr = inet_addr("172.217.2.99");//google IP
    connectionToServer.sin_addr.s_addr = inet_addr("192.168.0.21");//arduino IP address
    connectionToServer.sin_family = AF_INET; //type of IP addres. where AF_INET is IPv4
    connectionToServer.sin_port = htons(23500); //port is set via a method

    //Connect to remote server. ...sys/socket.h, which contains a definition for connect,
    // is part of the O/S code files. I searched for it, and found at least 20 socket.h
    //files all over the O/S.
    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 = "AA~";
   if( send(my_socket , message , strlen(message) , 0) < 0)
   {
        std::cout << "Send failed" << std::endl;
        return 1;
    }

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


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

    //create a temp array. This will hold a single line recieved from
    //the arduino. Array is initialized with NULs (which makes making a
    //string out of a char array with nuls a pain in the ass!!)
    char tempBuffer [300] = {};

    //fill array with money. this special character will allow us to know
    //the location where the data we wrote in meets the original '$' contents.
    std::fill(&tempBuffer[0], &tempBuffer[300], '$');


    //the number of lines I want to recieve 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 feteches!
    //(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. The data is written into the tempBuffer array which has
        //been pre-filled with $ characters.
        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. However once the data the
        //recv has finished ($ character is seen) or it is the end of the transmission (~), then
        //exit the loop.
        for(int j = 0; j < arduinoNumberOfCharsPerLine; j++ )
        {
            //kill the loop the moment there is a $ character. When i created the
            //array i initialized it with $ characters. so if I am seeing a $
            //character it means that the data recv recieved from the arduino has all been
            //given to the string.
            if(tempBuffer[j] == '$' )
            {
                //std::cout << "I ran... See ya" << std::endl;
                break;
            }

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

            totalResults = totalResults+tempBuffer[j];
            //test only
            //std::cout << "i: " << j << " value recv read: " << tempBuffer[j]<< std::endl;
        }

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

        // 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 << "recieved message: " <<std::endl;
    std::cout << totalResults << std::endl;
    std::cout << "length of string: " << totalResults.length() << std::endl;

    //process the int and float values we recieved and push them into memory
    //RIGHT wheel int
    processStringToIntAndAssignToGlobalVariables(totalResults, "A", "B", g_rightWheelMotionStatus);
    processStringToIntAndAssignToGlobalVariables(totalResults, "C", "D", g_rightWheelDirectionOfMotion);
    processStringToIntAndAssignToGlobalVariables(totalResults, "E", "F", g_rightWheelEncoderCountSinceStart);
    //RIGHT wheel float
    processStringToFloatAndAssignToGlobalVariables(totalResults, "G", "H", g_rightWheelPositionInDegrees);

    //LEFT wheel int
    processStringToIntAndAssignToGlobalVariables(totalResults, "I", "J", g_leftWheelMotionStatus);
    processStringToIntAndAssignToGlobalVariables(totalResults, "K", "L", g_leftWheelDirectionOfMotion);
    processStringToIntAndAssignToGlobalVariables(totalResults, "M", "N", g_leftWheelEncoderCountSinceStart);
    //LEFT wheel float
    processStringToFloatAndAssignToGlobalVariables(totalResults, "O", "P", g_leftWheelPositionInDegrees);

    //TESTING<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    //std::cout << "global Variable value: " << g_rightWheelMotionStatus << std::endl;

    return 0;
}

//for passing by reference see: https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToIntAndAssignToGlobalVariables(std::string allData, std::string start, std::string finish, int &globalVariable)
{
    //get the value.See:
    //https://stackoverflow.com/questions/30073839/c-extract-number-from-the-middle-of-a-string#30074453
    //http://www.cplusplus.com/reference/string/string/find/
    int p1 = allData.find(start);
    int p2 = allData.find(finish);

    //arg #1 - start position
    //arg #2 - length
    //See http://www.cplusplus.com/reference/string/string/substr/
    //indeed returns a string....
    std::string stringResult = allData.substr(p1 +1 , p2 - p1 -1);

    std::cout << "********************START*********************" << std::endl;
    std::cout << "what we extracted: _" << stringResult << "_"<< std::endl;

    //convert the string value to an integer. See claudios answer in:
    //https://stackoverflow.com/questions/7663709/how-can-i-convert-a-stdstring-to-int#7664227
    //also see: http://www.cplusplus.com/reference/string/stoi/
    int finalValue = std::stoi(stringResult);

    //value of passed in global value
    std::cout << "int global value as argument's value: " << globalVariable << std::endl;

    //assign the integer I recovered to the global variable to which it belongs
    globalVariable = finalValue;

    std::cout << "int global value is now: " << globalVariable << std::endl;
}

//for passing by reference see: https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToFloatAndAssignToGlobalVariables(std::string allData, std::string start, std::string finish, float &floatGlobalVariable)
{
    //get the value.See:
    //https://stackoverflow.com/questions/30073839/c-extract-number-from-the-middle-of-a-string#30074453
    //http://www.cplusplus.com/reference/string/string/find/
    int p1 = allData.find(start);
    int p2 = allData.find(finish);

    //arg #1 - start position
    //arg #2 - length
    //See http://www.cplusplus.com/reference/string/string/substr/
    //indeed returns a string....
    std::string stringResult = allData.substr(p1 +1 , p2 - p1 -1);

    std::cout << "********************START*********************" << std::endl;
    std::cout << "what we extracted: _" << stringResult << "_"<< std::endl;

    //convert the string value to an integer. See claudios answer in:
    //https://stackoverflow.com/questions/7663709/how-can-i-convert-a-stdstring-to-int#7664227
    //also see: http://www.cplusplus.com/reference/string/stoi/
    float finalValue = std::stof(stringResult);

    //value of passed in global value
    std::cout << "float global value as argument's value: " << floatGlobalVariable << std::endl;

    //assign the integer I recovered to the global variable to which it belongs
    floatGlobalVariable = finalValue;

    std::cout << "float global value is now: " << floatGlobalVariable << std::endl;
}

Ok, that's it. Everything works for me. Writing this code took so long, I am hoping that this template will save you some time. In due time, I will post a few steps to get the C++ code transformed into a ROS node.

Max Carr did a fantastic job, thanks man! Having said that, I had a hard time with (1) getting the Arduino code figured out, and (2) a C++ program to communicate with the Arduino. My contribution (if you can call it that) to this topic is to provide the following:

  • Arduino code that uses sockets (not HTML or HTTP) to communicate to a C++ program. Arduino program can receive different types of requests from C++, and dish out the requested data.
  • C++ easily sends messages to Arduino, and collects the response.

You still have to convert the C++ code to a ROS package, but that is where Max Carr's answer is not at all ambiguous, and there are a ton of resources to figure that out on the ROS website. I will touch on that at the end. However the bulk of thsi answer deals with Arduino to C++ communication via sockets.

The hardware is a PC and an Arduino Mega with an ethernet shield. I have a switch in between.

image description

Quick note on the Arduino side of things - I see Arduino as a cheap, and effective way to collect the status of sensors (both analog and digital), and control simple devices (lights, relays, etc...). Since you are running ROS, you have ample processing power in your PC, so I don't see you sending the Arduino tons of data to crunch. right? So, the code below receives a 3 character request, where the first two characters are capital letters, followed by a ~ as the end of transmission terminator. The Arduino code receives this message, and depending on the combination of letters (ex: AA~, or AB~, or ZZ~, etc....) the Arduino will take the appropriate action (send a pre-canned command to some device, turn on actuators, or in my case respond with the status of various sensors). In my case I am sending 8 variables back to the C++ program (6 int and 2 float). I am packaging each data type with letters. so the response looks like this:

A123B
 C456D
 E98F
 H-23.4532I
 ....
 ~~~

With the 3 ~ characters serving as the end of transmission.

Arduino Code

#include <SPI.h>
#include <Ethernet.h>
#include <SimpleTimer.h> //see: https://playground.arduino.cc/Code/SimpleTimer
#include "arduino2.h" //see: https://www.codeproject.com/Articles/732646/Fast-digital-I-O-for-Arduino

// The Mac address is BADA5512456 (BAD A55 [ASS] followed by 123456).
byte mac[] = {0xBA, 0xDA, 0x55, 0x12, 0x34, 0x56};

//The IP address is obviously static, and there is no conflicts with any 
//other device on the robot. 
IPAddress ip(192, 168, 0, 21);

// Initialize the Ethernet server library and assign  
// and port you want to use (port 80 is default for HTTP):
EthernetServer server(23500);

//variables that allow me to figure out what the client is requesting
int iterator = 0;
//made bigher than 3 elements to accomodate bad requests that don't follow my AA~ protocol;
//where the client sends a letter, followed by a letter followed by a ~ character to 
//let the server know the request is over. This allows the server to processed 
//more than just a single type of request
char message[9] = "0000000000"; 

//*************************************************************
put all your global variables here for your application!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//*************************************************************

void setup()
{
    //DO YOUR PROGRAM INITIALIZATION HERE


    // Open serial communications and wait for port to open:
    Serial.begin(9600);//TEMP<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    while (!Serial) 
    {
          ; // wait for serial port to connect. Needed for native USB port only
    }

    Serial.println("Ethernet WebServer Example");

    // start the Ethernet connection and the server:
    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.");
     }

     // start the server
     server.begin();
     Serial.print("server is at ");
     Serial.println(Ethernet.localIP());
}

void loop() 
{  
    //*************PART #1 PROCESS I/O*************************
    put all your code that processes the arduino inputs here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //*******************************************************************
    //******************PART 2 - Send Message to Client***************************************
    // listen for incoming clients
    EthernetClient client = server.available();

    //true when there is an incoming connection
    if (client) 
    {
         Serial.println("new client");

         //as long as a client is connected, (or there is data for the client which is still being
         //written out) this loop will keep going.
         while (client.connected()) 
         {
              //Gets a client that is connected to the server and has data available for reading. 
              //The connection persists when the returned client object goes out of scope; you can 
              //close it by calling client.stop(). The available() method will return a Client object; 
              //if no Client has data available for reading, this object will evaluate to false in an if-statement.
              //basically a client with data for the server can connected to this server.
              if (client.available()) 
              {
                   //read each ASCII character as it comes in.
                   char c = client.read();
                   //the first time the client connects, iterator is at 0, so we store the first
                   //character in element 0 of the message array. We store the incomming message
                   //in the message array.
                   message[iterator] = c;

                   //print the character we just got from the connected client to serial (for troubleshooting)
                   Serial.write(c);

                   // I decided that the communication between the cpp and arduino will be two letters followed by
                   // a tilda '~'. This gives me 26 * 26 cobinations, and I know the message is finished when 
                   // I see the ~ character. Below I process the request I have recieved based on the request.
                   if ((message[0]=='A') && (message[1]=='A') && (message[2]=='~'))
                   {
                        //send all 6 wheel values to CPP
                        sendAllDataToCpp(client);

                        //this break allows me to exit the 'while' loop.
                        break;
                   }

                   //this is a test message to make sure my arduino code can process multiple
                  //cpp requests. 
                  if ((message[0]=='A') && (message[1]=='B') && (message[2]=='~'))
                  {
                        //sends a simple test message
                        sendTestMessage(client);

                        //exit the while loop.
                        break;
                   }

                   //if a message is bad, we need to terminate connection with client. Bad meaning:
                   //(1) if iterator is larger than 3 that means client sent more than XX~
                   //(2) if client sent 3 characters, but the 3rd is not a ~ character
                   //I stopped trying to catch other types of violations.
                   if((iterator >= 3) || ((iterator >= 2) && (message[2] != '~')))
                   {
                        //sends a simple test message
                        sendBadProtocolMessage(client);

                        //exit the while loop.
                        break;
                    }

                   //this value starts at 0 when a new client connects, and with every ascii character
                   //the arduion recieves from the client, this value is incremented by 1.
                   iterator = iterator+1;

                   //troubleshooting only
                   Serial.println(iterator);
             }
       }

       //end of transmission (let cpp know everything is done.
       client.println("~~~");
       // We have broken out of the while loop; meaning we recieved a proper
       //request (or not), and we have sent a response. Time to close connection,
       // reset iterator, and wipe old message - this prepares us for a new client.
       client.stop();// close the connection
       Serial.println("client disconnected"); //testing only
       iterator = 0; //reset iterator
       message[0]=0; //wipe message clean
       message[1]=0; //wipe message clean
       message[2]=0; //wipre message clean  
       //if your C++ might send more than 3 chars, wipe the entire char array....
    }
}


//*****************************CUSTOM METHODS***************************************************
//for custom method refrence see: https://playground.arduino.cc/Code/Function
//PUT ALL YOUR CUSTOM METHODS HERE (that process your I/O)!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

//This is a test message to demonstarte that I can make different requests
//to arduino.
void sendTestMessage(EthernetClient client)
{
      client.println("TEST PASSED");
}

 //This is a message to a bad client (not following format)
 void sendBadProtocolMessage(EthernetClient client)
{
       client.println("YOU ARE NOT FOLLOWING THE PROTOCOL - GOODBYE DUMMY");
}

//send the wheel data to cpp
void sendAllDataToCpp(EthernetClient client)
{
      //>>>>>put all the values you want to send to cpp here<<<<<<<

      //show the motion status of the right wheel. true == moving; false==stopped
      client.println("A");
      client.println(rightWheelMotionStatus);
      client.println("B");

     //show the directoin of the right wheel (0==stopped; 1==forward; 2== backwards)
     client.println("C");
     client.println(rightWheelDirectionOfMotion);
     client.println("D");

     //The total pulses for the right wheel (additive & subtracive ralative to start position)
     client.println("E");
     client.println(rightWheelEncoderCountSinceStart);
     client.println("F");

     //Report the position of the right wheel in degrees.
     client.println("G");
     client.println(rightWheelPositionInDegrees);
     client.println("H");

     //LEFT WHEEL<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<To Do post Right wheel.

     //show the motion status of the right wheel (true == moving; false==stopped)
     client.println("I");
     client.println(leftWheelMotionStatus);
     client.println("J");

     //show the directoin of the right wheel, 0==stopped; 2==LOGICAL forward; 1==LOGICAL backward
     client.println("K");
     client.println(leftWheelDirectionOfMotion);
     client.println("L");

     //show the total pulses for the left wheel (additive & subtracive ralative to start position)
     client.println("M");
     client.println(leftWheelEncoderCountSinceStart);
     client.println("N");

     //report the position of the right wheel in degrees
     client.println("O");
     client.println(leftWheelPositionInDegrees);
     client.println("P");
}

That was pretty easy, no? This code is pretty easy to modify for your needs; simply put your code where the !!!!!!!! characters are. Oh, two more key points:

  1. NEVER put !!! anywhere in the Arduino code, not even in the comments. I remember I had !!! in my comments and the code would not compile. It was a bug, and don't know if it's fixed (probably).
  2. Natively the Arduino IO is very slow. However there is a library to speed up your I/O 10 fold! and its super easy!! have a look here: https://www.codeproject.com/Articles/732646/Fast-digital-I-O-for-Arduino

Next is the C++ side of things. since ROS is a thing that programmers do, I am sure you programmers can criticize the heck out of this code. If that is the case, please just fix it. I used C++11, so that will be important. This code sends a request to the arduino (using my AA~ "protocol"), and once the Arduino responds the message is collected. In my case, like I said I am getting 8 values, and I process the message to extract the values and save them as 6 ints and 2 floats.

C++ CODE

#include <iostream> //for std::cout & std::endl
#include<arpa/inet.h> //inet_addr
#include<cstring> // for strlen (never use string.h. See https://stackoverflow.com/questions/9257665/difference-between-string-and-string-h#9257719
#include<string> //for std::stoi
//#include<typeinfo> // for typeid. See https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c#20170989

//>>>>>>>>>>>>>>>>>>>RIGHT WHEEL VALUES<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//show the motion status of the right wheel. true == moving; false==stopped (A___B)
static int g_rightWheelMotionStatus = -1;

//show the directoin of the right wheel (0==stopped; 1==forward; 2== backwards) (C___D)
static int g_rightWheelDirectionOfMotion = -1;

//The total pulses for the right wheel (additive & subtracive ralative to start position) (E___F)
static int g_rightWheelEncoderCountSinceStart = 0;

//Report the position of the right wheel in degrees. (G___H)
static float g_rightWheelPositionInDegrees = -0.001;


//>>>>>>>>>>>>>>>>>>>LEFT WHEEL VALUES<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//show the motion status of the right wheel (true == moving; false==stopped) (I___J)
static int g_leftWheelMotionStatus = -1;

//show the directoin of the right wheel, 0==stopped; 2==LOGICAL forward; 1==LOGICAL backward (K___L)
static int g_leftWheelDirectionOfMotion = -1;

//show the total pulses for the left wheel (additive & subtracive ralative to start position) (M___N)
static int g_leftWheelEncoderCountSinceStart = 0;

//report the position of the right wheel in degrees (O___P)
static float g_leftWheelPositionInDegrees = -0.001;
///////////////////////////////////////////////////////////////////////////////////////////////

//forward declaration. for passing by reference see:
//https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToIntAndAssignToGlobalVariables(std::string, std::string, std::string, int &);
void processStringToFloatAndAssignToGlobalVariables(std::string, std::string, std::string, float &);

int main()
{
    //initializing the socket with the socket() function.
    //arg 1 - Address Family - AF_INET (this is IP version 4)
    //arg 2 - Type - SOCK_STREAM (this means connection oriented TCP protocol)
    //        SOCK_DGRAM indicates the UDP protocol.
    //arg 3 - Protocol - 0 [ or IPPROTO_IP This is IP protocol]
    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;
    }

    //the struct is defined in "/usr/include/netinet/in.h", and it uses a
    //data type called "in_addr_t" which is defined in "/usr/include/arpa/inet.h".
    //since arpa contains a "#include netinet/in.h" we can get away with using
    //only a "#include arpa/inet.h" header.

    struct sockaddr_in connectionToServer;

    //this code leverages the sockaddr_in structure, where I assign
    //values to a few of the structure's elements
    //connectionToServer.sin_addr.s_addr = inet_addr("172.217.2.99");//google IP
    connectionToServer.sin_addr.s_addr = inet_addr("192.168.0.21");//arduino IP address
    connectionToServer.sin_family = AF_INET; //type of IP addres. where AF_INET is IPv4
    connectionToServer.sin_port = htons(23500); //port is set via a method

    //Connect to remote server. ...sys/socket.h, which contains a definition for connect,
    // is part of the O/S code files. I searched for it, and found at least 20 socket.h
    //files all over the O/S.
    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 = "AA~";
   if( send(my_socket , message , strlen(message) , 0) < 0)
   {
        std::cout << "Send failed" << std::endl;
        return 1;
    }

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


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

    //create a temp array. This will hold a single line recieved from
    //the arduino. Array is initialized with NULs (which makes making a
    //string out of a char array with nuls a pain in the ass!!)
    char tempBuffer [300] = {};

    //fill array with money. this special character will allow us to know
    //the location where the data we wrote in meets the original '$' contents.
    std::fill(&tempBuffer[0], &tempBuffer[300], '$');


    //the number of lines I want to recieve 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 feteches!
    //(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. The data is written into the tempBuffer array which has
        //been pre-filled with $ characters.
        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. However once the data the
        //recv has finished ($ character is seen) or it is the end of the transmission (~), then
        //exit the loop.
        for(int j = 0; j < arduinoNumberOfCharsPerLine; j++ )
        {
            //kill the loop the moment there is a $ character. When i created the
            //array i initialized it with $ characters. so if I am seeing a $
            //character it means that the data recv recieved from the arduino has all been
            //given to the string.
            if(tempBuffer[j] == '$' )
            {
                //std::cout << "I ran... See ya" << std::endl;
                break;
            }

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

            totalResults = totalResults+tempBuffer[j];
            //test only
            //std::cout << "i: " << j << " value recv read: " << tempBuffer[j]<< std::endl;
        }

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

        // 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 << "recieved message: " <<std::endl;
    std::cout << totalResults << std::endl;
    std::cout << "length of string: " << totalResults.length() << std::endl;

    //process the int and float values we recieved and push them into memory
    //RIGHT wheel int
    processStringToIntAndAssignToGlobalVariables(totalResults, "A", "B", g_rightWheelMotionStatus);
    processStringToIntAndAssignToGlobalVariables(totalResults, "C", "D", g_rightWheelDirectionOfMotion);
    processStringToIntAndAssignToGlobalVariables(totalResults, "E", "F", g_rightWheelEncoderCountSinceStart);
    //RIGHT wheel float
    processStringToFloatAndAssignToGlobalVariables(totalResults, "G", "H", g_rightWheelPositionInDegrees);

    //LEFT wheel int
    processStringToIntAndAssignToGlobalVariables(totalResults, "I", "J", g_leftWheelMotionStatus);
    processStringToIntAndAssignToGlobalVariables(totalResults, "K", "L", g_leftWheelDirectionOfMotion);
    processStringToIntAndAssignToGlobalVariables(totalResults, "M", "N", g_leftWheelEncoderCountSinceStart);
    //LEFT wheel float
    processStringToFloatAndAssignToGlobalVariables(totalResults, "O", "P", g_leftWheelPositionInDegrees);

    //TESTING<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    //std::cout << "global Variable value: " << g_rightWheelMotionStatus << std::endl;

    return 0;
}

//for passing by reference see: https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToIntAndAssignToGlobalVariables(std::string allData, std::string start, std::string finish, int &globalVariable)
{
    //get the value.See:
    //https://stackoverflow.com/questions/30073839/c-extract-number-from-the-middle-of-a-string#30074453
    //http://www.cplusplus.com/reference/string/string/find/
    int p1 = allData.find(start);
    int p2 = allData.find(finish);

    //arg #1 - start position
    //arg #2 - length
    //See http://www.cplusplus.com/reference/string/string/substr/
    //indeed returns a string....
    std::string stringResult = allData.substr(p1 +1 , p2 - p1 -1);

    std::cout << "********************START*********************" << std::endl;
    std::cout << "what we extracted: _" << stringResult << "_"<< std::endl;

    //convert the string value to an integer. See claudios answer in:
    //https://stackoverflow.com/questions/7663709/how-can-i-convert-a-stdstring-to-int#7664227
    //also see: http://www.cplusplus.com/reference/string/stoi/
    int finalValue = std::stoi(stringResult);

    //value of passed in global value
    std::cout << "int global value as argument's value: " << globalVariable << std::endl;

    //assign the integer I recovered to the global variable to which it belongs
    globalVariable = finalValue;

    std::cout << "int global value is now: " << globalVariable << std::endl;
}

//for passing by reference see: https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToFloatAndAssignToGlobalVariables(std::string allData, std::string start, std::string finish, float &floatGlobalVariable)
{
    //get the value.See:
    //https://stackoverflow.com/questions/30073839/c-extract-number-from-the-middle-of-a-string#30074453
    //http://www.cplusplus.com/reference/string/string/find/
    int p1 = allData.find(start);
    int p2 = allData.find(finish);

    //arg #1 - start position
    //arg #2 - length
    //See http://www.cplusplus.com/reference/string/string/substr/
    //indeed returns a string....
    std::string stringResult = allData.substr(p1 +1 , p2 - p1 -1);

    std::cout << "********************START*********************" << std::endl;
    std::cout << "what we extracted: _" << stringResult << "_"<< std::endl;

    //convert the string value to an integer. See claudios answer in:
    //https://stackoverflow.com/questions/7663709/how-can-i-convert-a-stdstring-to-int#7664227
    //also see: http://www.cplusplus.com/reference/string/stoi/
    float finalValue = std::stof(stringResult);

    //value of passed in global value
    std::cout << "float global value as argument's value: " << floatGlobalVariable << std::endl;

    //assign the integer I recovered to the global variable to which it belongs
    floatGlobalVariable = finalValue;

    std::cout << "float global value is now: " << floatGlobalVariable << std::endl;
}

Ok, that's it. Everything works for me. Writing this code took so long, I am hoping that this template will save you some time. In due time, I will post a few steps to get the C++ code transformed into a ROS node.

Max Carr did a fantastic job, thanks man! Having said that, I had a hard time with (1) getting the Arduino code figured out, and (2) a C++ program to communicate with the Arduino. My contribution (if you can call it that) to this topic is to provide the following:

  • Arduino code that uses sockets (not HTML or HTTP) to communicate to a C++ program. Arduino program can receive different types of requests from C++, and dish out the requested data.
  • C++ easily sends messages to Arduino, and collects the response.

You still have to convert the C++ code to a ROS package, but that is where Max Carr's answer is not at all ambiguous, and there are a ton of resources to figure that out on the ROS website. I will touch on that at the end. However the bulk of thsi answer deals with Arduino to C++ communication via sockets.

The hardware is a PC and an Arduino Mega with an ethernet shield. I have a switch in between.

image description

Quick note on the Arduino side of things - I see Arduino as a cheap, and effective way to collect the status of sensors (both analog and digital), and control simple devices (lights, relays, etc...). Since you are running ROS, you have ample processing power in your PC, so I don't see you sending the Arduino tons of data to crunch. right? So, the code below receives a 3 character request, where the first two characters are capital letters, followed by a ~ as the end of transmission terminator. The Arduino code receives this message, and depending on the combination of letters (ex: AA~, or AB~, or ZZ~, etc....) the Arduino will take the appropriate action (send a pre-canned command to some device, turn on actuators, or in my case respond with the status of various sensors). In my case I am sending 8 variables back to the C++ program (6 int and 2 float). I am packaging each data type with letters. so the response looks like this:

A123B
C456D
E98F
H-23.4532I
....
~~~

With the 3 ~ characters serving as the end of transmission.

Arduino Code

#include <SPI.h>
#include <Ethernet.h>
#include <SimpleTimer.h> //see: https://playground.arduino.cc/Code/SimpleTimer
#include "arduino2.h" //see: https://www.codeproject.com/Articles/732646/Fast-digital-I-O-for-Arduino

// The Mac address is BADA5512456 (BAD A55 [ASS] followed by 123456).
byte mac[] = {0xBA, 0xDA, 0x55, 0x12, 0x34, 0x56};

//The IP address is obviously static, and there is no conflicts with any 
//other device on the robot. 
IPAddress ip(192, 168, 0, 21);

// Initialize the Ethernet server library and assign  
// and port you want to use (port 80 is default for HTTP):
EthernetServer server(23500);

//variables that allow me to figure out what the client is requesting
int iterator = 0;
//made bigher than 3 elements to accomodate bad requests that don't follow my AA~ protocol;
//where the client sends a letter, followed by a letter followed by a ~ character to 
//let the server know the request is over. This allows the server to processed 
//more than just a single type of request
char message[9] = "0000000000"; 

//*************************************************************
put all your global variables here for your application!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//*************************************************************

void setup()
{
    //DO YOUR PROGRAM INITIALIZATION HERE
HERE!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


    // Open serial communications and wait for port to open:
    Serial.begin(9600);//TEMP<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    while (!Serial) 
    {
          ; // wait for serial port to connect. Needed for native USB port only
    }

    Serial.println("Ethernet WebServer Example");

    // start the Ethernet connection and the server:
    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.");
     }

     // start the server
     server.begin();
     Serial.print("server is at ");
     Serial.println(Ethernet.localIP());
}

void loop() 
{  
    //*************PART #1 PROCESS I/O*************************
    put all your code that processes the arduino inputs here!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    //*******************************************************************
    //******************PART 2 - Send Message to Client***************************************
    // listen for incoming clients
    EthernetClient client = server.available();

    //true when there is an incoming connection
    if (client) 
    {
         Serial.println("new client");

         //as long as a client is connected, (or there is data for the client which is still being
         //written out) this loop will keep going.
         while (client.connected()) 
         {
              //Gets a client that is connected to the server and has data available for reading. 
              //The connection persists when the returned client object goes out of scope; you can 
              //close it by calling client.stop(). The available() method will return a Client object; 
              //if no Client has data available for reading, this object will evaluate to false in an if-statement.
              //basically a client with data for the server can connected to this server.
              if (client.available()) 
              {
                   //read each ASCII character as it comes in.
                   char c = client.read();
                   //the first time the client connects, iterator is at 0, so we store the first
                   //character in element 0 of the message array. We store the incomming message
                   //in the message array.
                   message[iterator] = c;

                   //print the character we just got from the connected client to serial (for troubleshooting)
                   Serial.write(c);

                   // I decided that the communication between the cpp and arduino will be two letters followed by
                   // a tilda '~'. This gives me 26 * 26 cobinations, and I know the message is finished when 
                   // I see the ~ character. Below I process the request I have recieved based on the request.
                   if ((message[0]=='A') && (message[1]=='A') && (message[2]=='~'))
                   {
                        //send all 6 wheel values to CPP
                        sendAllDataToCpp(client);

                        //this break allows me to exit the 'while' loop.
                        break;
                   }

                   //this is a test message to make sure my arduino code can process multiple
                  //cpp requests. 
                  if ((message[0]=='A') && (message[1]=='B') && (message[2]=='~'))
                  {
                        //sends a simple test message
                        sendTestMessage(client);

                        //exit the while loop.
                        break;
                   }

                   //if a message is bad, we need to terminate connection with client. Bad meaning:
                   //(1) if iterator is larger than 3 that means client sent more than XX~
                   //(2) if client sent 3 characters, but the 3rd is not a ~ character
                   //I stopped trying to catch other types of violations.
                   if((iterator >= 3) || ((iterator >= 2) && (message[2] != '~')))
                   {
                        //sends a simple test message
                        sendBadProtocolMessage(client);

                        //exit the while loop.
                        break;
                    }

                   //this value starts at 0 when a new client connects, and with every ascii character
                   //the arduion recieves from the client, this value is incremented by 1.
                   iterator = iterator+1;

                   //troubleshooting only
                   Serial.println(iterator);
             }
       }

       //end of transmission (let cpp know everything is done.
       client.println("~~~");
       // We have broken out of the while loop; meaning we recieved a proper
       //request (or not), and we have sent a response. Time to close connection,
       // reset iterator, and wipe old message - this prepares us for a new client.
       client.stop();// close the connection
       Serial.println("client disconnected"); //testing only
       iterator = 0; //reset iterator
       message[0]=0; //wipe message clean
       message[1]=0; //wipe message clean
       message[2]=0; //wipre message clean  
       //if your C++ might send more than 3 chars, wipe the entire char array....
    }
}


//*****************************CUSTOM METHODS***************************************************
//for custom method refrence see: https://playground.arduino.cc/Code/Function
//PUT ALL YOUR CUSTOM METHODS HERE (that process your I/O)!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

//This is a test message to demonstarte that I can make different requests
//to arduino.
void sendTestMessage(EthernetClient client)
{
      client.println("TEST PASSED");
}

 //This is a message to a bad client (not following format)
 void sendBadProtocolMessage(EthernetClient client)
{
       client.println("YOU ARE NOT FOLLOWING THE PROTOCOL - GOODBYE DUMMY");
}

//send the wheel data to cpp
void sendAllDataToCpp(EthernetClient client)
{
      //>>>>>put all the values you want to send to cpp here<<<<<<<

      //show the motion status of the right wheel. true == moving; false==stopped
      client.println("A");
      client.println(rightWheelMotionStatus);
      client.println("B");

     //show the directoin of the right wheel (0==stopped; 1==forward; 2== backwards)
     client.println("C");
     client.println(rightWheelDirectionOfMotion);
     client.println("D");

     //The total pulses for the right wheel (additive & subtracive ralative to start position)
     client.println("E");
     client.println(rightWheelEncoderCountSinceStart);
     client.println("F");

     //Report the position of the right wheel in degrees.
     client.println("G");
     client.println(rightWheelPositionInDegrees);
     client.println("H");

     //LEFT WHEEL<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<To Do post Right wheel.

     //show the motion status of the right wheel (true == moving; false==stopped)
     client.println("I");
     client.println(leftWheelMotionStatus);
     client.println("J");

     //show the directoin of the right wheel, 0==stopped; 2==LOGICAL forward; 1==LOGICAL backward
     client.println("K");
     client.println(leftWheelDirectionOfMotion);
     client.println("L");

     //show the total pulses for the left wheel (additive & subtracive ralative to start position)
     client.println("M");
     client.println(leftWheelEncoderCountSinceStart);
     client.println("N");

     //report the position of the right wheel in degrees
     client.println("O");
     client.println(leftWheelPositionInDegrees);
     client.println("P");
}

That was pretty easy, no? This code is pretty easy to modify for your needs; simply put your code where the !!!!!!!! characters are. Oh, two more key points:

  1. NEVER put !!! anywhere in the Arduino code, not even in the comments. I remember I had !!! in my comments and the code would not compile. It was a bug, and don't know if it's fixed (probably).
  2. Natively the Arduino IO is very slow. However there is a library to speed up your I/O 10 fold! and its super easy!! have a look here: https://www.codeproject.com/Articles/732646/Fast-digital-I-O-for-Arduino

Next is the C++ side of things. since ROS is a thing that programmers do, I am sure you programmers can criticize the heck out of this code. If that is the case, please just fix it. I used C++11, so that will be important. This code sends a request to the arduino (using my AA~ "protocol"), and once the Arduino responds the message is collected. In my case, like I said I am getting 8 values, and I process the message to extract the values and save them as 6 ints and 2 floats.

C++ CODE

#include <iostream> //for std::cout & std::endl
#include<arpa/inet.h> //inet_addr
#include<cstring> // for strlen (never use string.h. See https://stackoverflow.com/questions/9257665/difference-between-string-and-string-h#9257719
#include<string> //for std::stoi
//#include<typeinfo> // for typeid. See https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c#20170989

//>>>>>>>>>>>>>>>>>>>RIGHT WHEEL VALUES<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//show the motion status of the right wheel. true == moving; false==stopped (A___B)
static int g_rightWheelMotionStatus = -1;

//show the directoin of the right wheel (0==stopped; 1==forward; 2== backwards) (C___D)
static int g_rightWheelDirectionOfMotion = -1;

//The total pulses for the right wheel (additive & subtracive ralative to start position) (E___F)
static int g_rightWheelEncoderCountSinceStart = 0;

//Report the position of the right wheel in degrees. (G___H)
static float g_rightWheelPositionInDegrees = -0.001;


//>>>>>>>>>>>>>>>>>>>LEFT WHEEL VALUES<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
//show the motion status of the right wheel (true == moving; false==stopped) (I___J)
static int g_leftWheelMotionStatus = -1;

//show the directoin of the right wheel, 0==stopped; 2==LOGICAL forward; 1==LOGICAL backward (K___L)
static int g_leftWheelDirectionOfMotion = -1;

//show the total pulses for the left wheel (additive & subtracive ralative to start position) (M___N)
static int g_leftWheelEncoderCountSinceStart = 0;

//report the position of the right wheel in degrees (O___P)
static float g_leftWheelPositionInDegrees = -0.001;
///////////////////////////////////////////////////////////////////////////////////////////////

//forward declaration. for passing by reference see:
//https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToIntAndAssignToGlobalVariables(std::string, std::string, std::string, int &);
void processStringToFloatAndAssignToGlobalVariables(std::string, std::string, std::string, float &);

int main()
{
    //initializing the socket with the socket() function.
    //arg 1 - Address Family - AF_INET (this is IP version 4)
    //arg 2 - Type - SOCK_STREAM (this means connection oriented TCP protocol)
    //        SOCK_DGRAM indicates the UDP protocol.
    //arg 3 - Protocol - 0 [ or IPPROTO_IP This is IP protocol]
    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;
    }

    //the struct is defined in "/usr/include/netinet/in.h", and it uses a
    //data type called "in_addr_t" which is defined in "/usr/include/arpa/inet.h".
    //since arpa contains a "#include netinet/in.h" we can get away with using
    //only a "#include arpa/inet.h" header.

    struct sockaddr_in connectionToServer;

    //this code leverages the sockaddr_in structure, where I assign
    //values to a few of the structure's elements
    //connectionToServer.sin_addr.s_addr = inet_addr("172.217.2.99");//google IP
    connectionToServer.sin_addr.s_addr = inet_addr("192.168.0.21");//arduino IP address
    connectionToServer.sin_family = AF_INET; //type of IP addres. where AF_INET is IPv4
    connectionToServer.sin_port = htons(23500); //port is set via a method

    //Connect to remote server. ...sys/socket.h, which contains a definition for connect,
    // is part of the O/S code files. I searched for it, and found at least 20 socket.h
    //files all over the O/S.
    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 = "AA~";
   if( send(my_socket , message , strlen(message) , 0) < 0)
   {
        std::cout << "Send failed" << std::endl;
        return 1;
    }

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


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

    //create a temp array. This will hold a single line recieved from
    //the arduino. Array is initialized with NULs (which makes making a
    //string out of a char array with nuls a pain in the ass!!)
    char tempBuffer [300] = {};

    //fill array with money. this special character will allow us to know
    //the location where the data we wrote in meets the original '$' contents.
    std::fill(&tempBuffer[0], &tempBuffer[300], '$');


    //the number of lines I want to recieve 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 feteches!
    //(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. The data is written into the tempBuffer array which has
        //been pre-filled with $ characters.
        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. However once the data the
        //recv has finished ($ character is seen) or it is the end of the transmission (~), then
        //exit the loop.
        for(int j = 0; j < arduinoNumberOfCharsPerLine; j++ )
        {
            //kill the loop the moment there is a $ character. When i created the
            //array i initialized it with $ characters. so if I am seeing a $
            //character it means that the data recv recieved from the arduino has all been
            //given to the string.
            if(tempBuffer[j] == '$' )
            {
                //std::cout << "I ran... See ya" << std::endl;
                break;
            }

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

            totalResults = totalResults+tempBuffer[j];
            //test only
            //std::cout << "i: " << j << " value recv read: " << tempBuffer[j]<< std::endl;
        }

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

        // 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 << "recieved message: " <<std::endl;
    std::cout << totalResults << std::endl;
    std::cout << "length of string: " << totalResults.length() << std::endl;

    //process the int and float values we recieved and push them into memory
    //RIGHT wheel int
    processStringToIntAndAssignToGlobalVariables(totalResults, "A", "B", g_rightWheelMotionStatus);
    processStringToIntAndAssignToGlobalVariables(totalResults, "C", "D", g_rightWheelDirectionOfMotion);
    processStringToIntAndAssignToGlobalVariables(totalResults, "E", "F", g_rightWheelEncoderCountSinceStart);
    //RIGHT wheel float
    processStringToFloatAndAssignToGlobalVariables(totalResults, "G", "H", g_rightWheelPositionInDegrees);

    //LEFT wheel int
    processStringToIntAndAssignToGlobalVariables(totalResults, "I", "J", g_leftWheelMotionStatus);
    processStringToIntAndAssignToGlobalVariables(totalResults, "K", "L", g_leftWheelDirectionOfMotion);
    processStringToIntAndAssignToGlobalVariables(totalResults, "M", "N", g_leftWheelEncoderCountSinceStart);
    //LEFT wheel float
    processStringToFloatAndAssignToGlobalVariables(totalResults, "O", "P", g_leftWheelPositionInDegrees);

    //TESTING<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    //std::cout << "global Variable value: " << g_rightWheelMotionStatus << std::endl;

    return 0;
}

//for passing by reference see: https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToIntAndAssignToGlobalVariables(std::string allData, std::string start, std::string finish, int &globalVariable)
{
    //get the value.See:
    //https://stackoverflow.com/questions/30073839/c-extract-number-from-the-middle-of-a-string#30074453
    //http://www.cplusplus.com/reference/string/string/find/
    int p1 = allData.find(start);
    int p2 = allData.find(finish);

    //arg #1 - start position
    //arg #2 - length
    //See http://www.cplusplus.com/reference/string/string/substr/
    //indeed returns a string....
    std::string stringResult = allData.substr(p1 +1 , p2 - p1 -1);

    std::cout << "********************START*********************" << std::endl;
    std::cout << "what we extracted: _" << stringResult << "_"<< std::endl;

    //convert the string value to an integer. See claudios answer in:
    //https://stackoverflow.com/questions/7663709/how-can-i-convert-a-stdstring-to-int#7664227
    //also see: http://www.cplusplus.com/reference/string/stoi/
    int finalValue = std::stoi(stringResult);

    //value of passed in global value
    std::cout << "int global value as argument's value: " << globalVariable << std::endl;

    //assign the integer I recovered to the global variable to which it belongs
    globalVariable = finalValue;

    std::cout << "int global value is now: " << globalVariable << std::endl;
}

//for passing by reference see: https://stackoverflow.com/questions/18147038/passing-object-by-reference-in-c
void processStringToFloatAndAssignToGlobalVariables(std::string allData, std::string start, std::string finish, float &floatGlobalVariable)
{
    //get the value.See:
    //https://stackoverflow.com/questions/30073839/c-extract-number-from-the-middle-of-a-string#30074453
    //http://www.cplusplus.com/reference/string/string/find/
    int p1 = allData.find(start);
    int p2 = allData.find(finish);

    //arg #1 - start position
    //arg #2 - length
    //See http://www.cplusplus.com/reference/string/string/substr/
    //indeed returns a string....
    std::string stringResult = allData.substr(p1 +1 , p2 - p1 -1);

    std::cout << "********************START*********************" << std::endl;
    std::cout << "what we extracted: _" << stringResult << "_"<< std::endl;

    //convert the string value to an integer. See claudios answer in:
    //https://stackoverflow.com/questions/7663709/how-can-i-convert-a-stdstring-to-int#7664227
    //also see: http://www.cplusplus.com/reference/string/stoi/
    float finalValue = std::stof(stringResult);

    //value of passed in global value
    std::cout << "float global value as argument's value: " << floatGlobalVariable << std::endl;

    //assign the integer I recovered to the global variable to which it belongs
    floatGlobalVariable = finalValue;

    std::cout << "float global value is now: " << floatGlobalVariable << std::endl;
}

Ok, that's it. Everything works for me. Writing this code took so long, I am hoping that this template will save you some time. In due time, I will post a few steps to get the C++ code transformed into a ROS node.