How to send HEX commands in C/C++ over TCP/IP to control a relays board

Rocco Galati
Rocco Galati used Ask the Experts™
on
I have an electronics board which has 16 relays and it works over TCP/IP.
The IP address of the board is 192.168.1.4 and the port is 3000.
I would like to control it with C/C++ under Ubuntu.

There is a list of HEX commands that it is possible to use in order to remotely switch ON and OFF each relay on the board.
This is the list:
"580112000000016C",  // switch on the relay 1

"580111000000016B",  // switch off the relay 1
"580112000000026D", // switch on the relay 2
"580111000000026C", // switch off the relay 2
"580112000000036E", // so on..
"580111000000036D",
"580112000000046F",
"580111000000046E",
"5801120000000570",
"580111000000056F",
"5801120000000671",
"5801110000000670",
"5801120000000772",
"5801110000000771",
"5801120000000873",
"5801110000000872",
"5801120000000974",
"5801110000000973",
"5801120000000A75",
"5801110000000A74",
"5801120000000B76",
"5801110000000B75",
"5801120000000C77",
"5801110000000C76",
"5801120000000D78",
"5801110000000D77",
"5801120000000E79",
"5801110000000E78",
"5801120000000F7A",
"5801110000000F79",
"580112000000107B",
"580111000000107A",
"5801130000FFFF77",
"580113000000007B",
"5801100000000069"

I'm correctly able to switch on and off each relay by sending command line commands under Ubuntu:

echo '580112000000016C' | xxd -r -p | nc 192.168.1.4 3000

Open in new window

The above code correctly turns on the relay.

I would like to do the same with C/C++ code since I want to control the board from a WxWidgets application.
For the moment, I'm starting from base and I'm just using C/C++ code in order to test the tcp connection.

This is my code:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include <unistd.h>

using namespace std;

int main(int argc, char**argv) {
    int sockfd, n;
    struct sockaddr_in servaddr;
    char sendline[1000];
    char recvline[1000];

    std::string serveraddr = "192.168.1.4";

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(serveraddr.c_str());
    servaddr.sin_port = htons(3000);

    connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

    int number = 0x580112000000016C;

    int sendsize;
    sendsize = snprintf(sendline, sizeof(sendline), "%x", number);

    send(sockfd, sendline, sendsize * sizeof(char), 0);

}

Open in new window


How should I send the HEX commands?
When I compile this code I get this warning:

g++ tcp.c -o tcp
tcp.c: In function ‘int main(int, char**)’:
tcp.c:29:18: warning: overflow in implicit constant conversion [-Woverflow]
     int number = 0x580112000000016C;
                  ^~~~~~~~~~~~~~~~~~

and obviously the program doesn't work.

Can you help me please? I'm a beginner and I'm not understanding how to correctly send the HEX instructions.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Qlemo"Batchelor", Developer and EE Topic Advisor
Top Expert 2015

Commented:
You have to send the hex digits as a string, like with your echo, instead of as integer. If you define your constant as char[], and just send that, it should work.
Rocco GalatiR&D Engineer

Author

Commented:
Thank you for your support.

I tried to do it in this way:

uint8_t buffer[] = { 0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x01, 0x6C};
    int sendsize;
    sendsize = snprintf(sendline, sizeof(sendline), "%u", buffer);

    send(sockfd, sendline, sendsize * sizeof(char), 0);

Open in new window


but it still doesn't work.
David FavorFractional CTO
Distinguished Expert 2018

Commented:
6341369541523669356 is the integer equivalent of your HEX string, so will over flow almost any compiler's default int type.

As Qlemo said, almost all HEX commands sent are sent as character strings or are written to individual 8/16/32 bit registers.

To understand how best to accomplish this, refer to docs of your specific embedded device (electronics board).

If docs are incomplete, just pull the chip number from the chip closest to your incoming wiring + likely the manufacturer data sheet will provide required info.
11/26 Forrester Webinar: Savings for Enterprise

How can your organization benefit from savings just by replacing your legacy backup solutions with Acronis' #CyberProtection? Join Forrester's Joe Branca and Ryan Davis from Acronis live as they explain how you can too.

Rocco GalatiR&D Engineer

Author

Commented:
I also tried to send it like a string:

char buffer[] ="580112000000016C";
    int sendsize;
    sendsize = snprintf(sendline, sizeof(sendline), "%s", buffer);

    send(sockfd, sendline, sendsize * sizeof(char), 0);

Open in new window


but nothing changed.
your echo command implicetly sends a \n character as well perhaps it is required.  just change

buffer to "580112000000016C\n";
to make a first test
Rocco GalatiR&D Engineer

Author

Commented:
Thank you for your help, gelonida.

I tried to add the \n, but it seems nothing changed.

char buffer[] ="580112000000016C\n";
    int sendsize;
    sendsize = snprintf(sendline, sizeof(sendline), "%s", buffer);

    send(sockfd, sendline, sendsize * sizeof(char), 0);
Distinguished Expert 2017

Commented:
The control transmissions have to come sequentially or in a sequence,
Currently your example sends a sequence without checking whether it is expected.
Buffer handling on the other side in a blind charcter, string send might receive the first but miss the seei daily/subsequent.

Your working example uses xxd as the intermediary converter
Try to see what is output from that

echo '580112000000016C' | xxd -r -p | cat -v

Open in new window


See what it sends, I.e. '\0' or '^@' as terminator between control characters
Rocco GalatiR&D Engineer

Author

Commented:
This is the output:

echo '580112000000016C' | xxd -r -p | cat -v
X^A^R^@^@^@^Al

Can't understand why I get the Al terminal character.
Distinguished Expert 2017

Commented:
cat -v tells the system to pass everything including the control characters.
^@ is the equivalent of \0 end of string in c/c++
so you are sending X ctrl-A ctrl-R \0 \0 ctrl-A I
It is not AI it is ctrl-A and then I.

try the following
printf (   "%c%c%c%c%c%c%c%c",'X','0x01','0x12','\0','\0','\0',''0x01','I')
Rocco GalatiR&D Engineer

Author

Commented:
Where should I try it? should I send it via TCP?
Distinguished Expert 2017

Commented:
yes, replace the string you are trying to send with the formulation the printf i provided as an example and see if it makes a difference

or use the

in your code
sendsize = snprintf(sendline, sizeof(sendline), "%s", buffer);

sendline is 1000 you are only passing it 8-10  characters that might be your issue, it needs to be size of buffer

sendsize = snprintf(sendline, sizeof(buffer), "%s", buffer);
You can setup  binary fies as follows:
echo '580112000000016C' | xxd -r -p > r1_on.txt

Open in new window

In case you are interested in seeing what binary you are sending, you can do this:
$ od -x r1_on.txt 
0000000 0158 0012 0000 6c01
0000010

Open in new window

(On my x86, we are actually sending the binary equivalent of 0x58 followed by 0x01 due to its endianess. Your od -x results may vary if using a different endianess.)
For starters, you can now do this:
 #include <stdlib.h>
int main()
{
system("cat r1_on.txt |  nc 192.168.1.4 3000");
}

Open in new window

Distinguished Expert 2017

Commented:
Depending on the asker goal, but using system calls from a c/c++ will defeat the purpose for which the application, program is being developed.


If the intent was there, the asker might have done this in bash as a script.
>> Depending on the asker goal
That is why I said "For starters". For finishing, we can wait for the asker goals.

My intent was to avoid having to analyze exactly what is being sent, and instead, just pointing out how to capture the binary message to a binary file. Then we don't have to interpret and try to capture the binary details in a local variable.
Rocco GalatiR&D Engineer

Author

Commented:
unfortunately I need to use it with C/C++ code. Honestly, the final goal is to send commands through my WxWidgets application but I started with C code in order to keep the things easier; I will import the code in my wxwidgets program when I'll get something work correctly.

I can't use system() because it doesn't work with wxwidgets.
Distinguished Expert 2017

Commented:
All suggestion I made presume that your connection setup works. After the most recent thread, you are not checking whether the connection has been established. Etc.

You shoukd test/confirm at each stage that the preceding step has succeeded.
Just read the r1_on.txt file and note the number of bytes read in. Then send that buffer out.
Software Developer
Commented:
580112000000016C represents 8 bytes on the wire. You can encode it in C++ as
char test[8] = {0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x01, 0x6C};

Open in new window

Rocco GalatiR&D Engineer

Author

Commented:
I solved the problem by using the byte notation as you suggested.
Thank you!
Distinguished Expert 2017

Commented:
did you also change the sizeof to the buffer length?
Rocco GalatiR&D Engineer

Author

Commented:
I did in this way:

uint8_t command[] = {0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x03, 0x6E}; // and so on..

    int bytes_to_send = sizeof(command);
    int bytes_sent = 0;

    do
    {
        n = send(sockfd, command + bytes_sent, bytes_to_send - bytes_sent, 0);
        if ( n < 0 )
        {
            cerr << "Error writing to socket!" << strerror(errno) << endl;
            close(sockfd);
            return 1;
        }
        bytes_sent += n;
    }
    while (bytes_sent < bytes_to_send);
Duncan RoeSoftware Developer

Commented:
Nicely done.
You might just like to guard against EINTR from some random event (like re-sizing the terminal window, which signals SIGWINCH)
#define SYSCALL(x, y) do x = y; while(x == -1 && errno == EINTR)
...
SYSCALL(n, send(sockfd, command + bytes_sent, bytes_to_send - bytes_sent, 0));

Open in new window

I wrap all my system calls that way
Thanks for accepting, btw
Rocco GalatiR&D Engineer

Author

Commented:
Thank you, Duncan!

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial