I am in bit of a problematic situation here:
I have a simple C server (can't use Qt TcpServer, limitations of the embedded board) which sends double data (generated by GPIO ports) over tcpsocket using send(). If I have a simple C client (in my Linux PC) then I am able to get the real time data as is generated with the help of recv(). Here is my server code:
int main (void){
int s,b,l,fd,sa,bytes,on=1;
char buf[BUF_SIZE],fname[255];
struct sockaddr_in channel;
long long sum=0;
double average=0;
int i,j;
int srno=0;
s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(s<0)
{
printf("socket creation failure");
exit(0);
}
setsockopt(s,SOL_SOCKET,SO_REUSEADDR,(char *)&on,sizeof(on));
memset(&channel,0,sizeof(channel));
channel.sin_family=AF_INET;
channel.sin_addr.s_addr=htonl(INADDR_ANY);
channel.sin_port=htons(SERVER_PORT);
b=bind(s, (struct sockaddr *)&channel,sizeof(channel));
if(b<0)
{
printf("bind error");
exit(0);
}
listen(s,5);
while(1)
{
printf("waiting for request\n");
sa=accept(s,0,0);
if(sa<0)
{
printf("accept failure");
}
for (i=1; i<=56; i++) //for 10 sec >> 56
{
for(j=1;j<=20;j++)
{
//more than 100 lines of code here
}
average=(sum/20)*1.22;
send(sa, &average, sizeof(average), 0);
//int tmp = htonl((uint32_t)average); //tried this also
//send(sa, &tmp, sizeof(tmp), 0);
// int n = write(sa, &average, sizeof(average)) //and tried this also
// if(n<0){
// printf("error");}
printf("%lld \n", average);
srno++;
sum=0;
average=0;
}
}
remove_gpio();
close(s);
return 0;
}
But I am unable to get to display the data in Qt Client, it is driving me crazy. In my QtextEdit I get non-ASCII characters, whatever I try. inside my startRead() I did
...
QByteArray Data = socket->readAll();
ui->textEdit->append(QString(Data));
...
I also tried using other methods bytesavailable() canreadLine(), and also went through other links but was unsuccessful.
Non-ASCII crazy characters is what I get everytime. Sorry not able to give a pic due to low rep.
I need help regarding sending double data through socket and be able to work with the data or atleast display them in a QTextEdit/QPlainTextEdit.
Thanks.
as ciphor said you are trying to display binary directly as text
you'll want to use a datastream to convert
QByteArray Data = socket->readAll();
QDataStream dstream (Data);
double d;
while(!dstream.atEnd())
{
dstream >> d;
ui->textEdit->append(QString::number(d));
}
You need to actually design, specify, and implement a protocol. Otherwise, it's as if your client is speaking Chinese and your server listening for Spanish. Because TCP is a byte-stream protocol, you need to precisely specify how the two programs will interchange data as a stream of bytes. Then design both sides to send a stream of bytes in accord with the specification and receive a stream of bytes in accord with the specification. To help you get started, you should thoroughly study the specifications for existing protocols layered on top of TCP such as IRC, NNTP, SMTP, IMAP, and so on.
The conversion specifier for double is "lf".
The "lld" the code is using is for long long integer.
So use:
printf("%lf\n", average);
You are sending the data in format of double precision binary, but trying to display as a string in your client, that is the reason of non-ASCII.
If you want to send ASCII, you should convert the data into string format before sending it at server side.
char buf[20] = {0};
average=(sum/20)*1.22;
sprintf(buf, "%lld", average);
send(sa, &buf[0], strlen(buf), 0);
Related
I am finding way to send hex data via serial communication
i searched it several times and followed some ways but it didn't work.
i checked that protocol is working with using other software that sending hex data to device
below is my code
const char data[]={0xAA,0xAA,0x01,0x00,0x00,0x0E,0x00,0x01,0x00,0x00,0x00,0x2D,0x37,0x1D,0xAA,0xAA,0x01,0x00,0x00,0x0E,0x00,0x0C,0x10,0x00,0x00,0x01,0x76,0x13};
serial->setPortName(("COM8"));
initSerialPort(); // baud rate and etc
if(serial->open(QIODevice::ReadWrite))
{
qDebug()<<"Port is open!";
if(serial->isWritable())
{
qDebug()<<"Yes, i can write to port!";
int size = sizeof(data);
serial->write(data,size);
}
}
and when i use other declare like uint16_t, uchar, write function cannot convert argument 1 from uint16_t (or uchar) to const char *
i did try also this form
QByteArray hex("AAAA0100000E00010000002D371DAAAA0100000E000C100000017613");
QByteArray data = QByteArray::fromHex(hex);
and it also didnt work
You can do this using only QByteArray like:
connect(serial, &QSerialPort::readyRead, this, &YourClass::doSomeStuff);
QByteArray arr;
arr += static_cast<char>(0xAA);
arr += static_cast<char>(0x01);
<...>
serial->setPortName("COM8");
initSerialPort(); // baud rate and etc
if(serial->open(QIODevice::ReadWrite) && serial->isWritable())
serial->write(arr);
For a project i need to establish 2 way xbee communication. But I have a problem sending data from my pc. I use cpp with termios to transmitt a char array but on the xbee tx pin I do only get a signal (I observe this on an oscilloscope) when one of the chars is 0x0A.
The XBee module is on a 30011662-02 board, which is connected to my pc via usb.
I thought maybe this is some kind of starting parameter needed by the xbee board to transmit but couldnt find any information on this.
ctx->debug = debug;
//open USB port for read/write and check success
ctx->fd = open(devFileName, O_RDWR | O_NOCTTY); //opens the usb port for reading
if (ctx->fd < 0) {
cerr << "Could not open the USB Port. Try adding User to group dialout!" << endl;
return 0;
}
//is the opened port a terminal?
if(!isatty(ctx->fd)) {
close(ctx->fd);
errno = ENOTTY;
return 0;
}
//setup termios
tcgetattr(ctx->fd, &(ctx->oldtio));
cfmakeraw(&newtio);
cfsetispeed(&newtio, baudrate);
cfsetospeed(&newtio, baudrate);
tcsetattr(ctx->fd, TCSANOW, &newtio); //connects fd to newtio
tcflush(ctx->fd, TCIOFLUSH); //discards data not transmitted or received
lseek(ctx->fd, 0, SEEK_END);
ctx->bufIO = fdopen(ctx->fd, "r+");
bool connection_status=0;
unsigned char frame_id=0x00;
unsigned char checksum=0xff;
int j=0;
while(!connection_status){
checksum=0xFF;
unsigned char buffer[] = {
0x7E, //start delimiter
0x00,0x07,//length of the data packet
0x01,//API identifier (refer to XBee module manual for further details)
frame_id++, //frame id
0x00,0x0B,//destination address
0x00,//options
0x02,0x03,//data: 0,receiver address,mode
0x00}; //checksumm
for(unsigned int i=0;i<sizeof(buffer);i++){
checksum-=buffer[i];
}
cout << buffer << endl;
j++;
buffer[10]=checksum;
fwrite(buffer,sizeof(char),sizeof(buffer),ctx->bufIO);
usleep(2000000);
}
I do expect to see data on the xbee tx pin in every itteration of the while loop but so far it only works when frame_id is 0x0A or i manually enter 0x0A in the array buffer. But still it does not seem to be sending the correct data. Maybe you have some hints for me.
You're probably missing a setting (maybe O_NDELAY?) for the serial port (tty) and it's in line mode. You might want to look at this serial driver used in an Open Source XBee Host Library written in ANSI C (instead of C++). You might even be able to build your application on top of that library. At the very least, you could compile the samples and see if they work with your module as a way to verify your wiring and XBee configuration.
I am writing some code that involves using an inertia cube tracker, that actively changes its yaw pitch and roll (in degrees) and I need to set up a server that reads that information in order to network the info. So far I have created a client and server, but the problem I am having is either to send the information in one chunck then read it back as three and print it, or to specify which send matches with which recieve.
if( currentTrackerH > 0 )
{
int iSendResult1;
int iSendResult2;
int iSendResult3;
char EulerBuffer0[64];
char EulerBuffer1[64];
char EulerBuffer2[64];
showStationData( currentTrackerH, &TrackerInfo,
&Stations[station-1], &data.Station[station-1],
&StationsHwInfo[currentTrackerH-1][station-1],
showTemp);
//send to the server
do{
sprintf(EulerBuffer0, "%f", data.Station[station-1].Euler[0]);
iSendResult1= send(Connection, EulerBuffer0, sizeof(data.Station[station-1].Euler[0]), NULL);
sprintf(EulerBuffer1, "%f", data.Station[station-1].Euler[1]);
iSendResult2= send(Connection, EulerBuffer1, sizeof(data.Station[station-1].Euler[1]), NULL);
sprintf(EulerBuffer2, "%f", data.Station[station-1].Euler[2]);
iSendResult3= send(Connection, EulerBuffer2, sizeof(data.Station[station-1].Euler[2]), NULL);
}while ((iSendResult1 || iSendResult2 || iSendResult3)>0);
//shutdown the socket when there is no more data to send
iSendResult1 = shutdown(Connection, SD_SEND);
if (iSendResult1 == SOCKET_ERROR)
{
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(Connection);
WSACleanup();
return 1;
}
}
}
This is my client side and here I will put my server side. The networks connect and my tracker code works just fine but sending and recieving is where it all gets wonky.
//begin recieving data
char yaw[256];
char pitch[256];
char roll[256];
int iResult1;
int iResult2;
int iResult3;
float fyaw, fpitch, froll;
do{
do {
iResult1= recv(newConnection, yaw,sizeof(yaw),NULL);
} while( iResult1 == 0 );
fyaw = atof(yaw);
do {
iResult2= recv(newConnection, pitch,sizeof(pitch),NULL);
} while( iResult1 == 0 );
fpitch = atof(pitch);
do {
iResult3= recv(newConnection, roll,sizeof(roll),NULL);
} while( iResult1 == 0 );
froll = atof(roll);
printf("(%f,%f,%f)deg \n",
fyaw, fpitch, froll);
}while(1);
my knowledge of c++ is not fantastic and any help would be lovely. Thanks!
There is all kinds of wrong in your code. Let's try to break down and correct misconceptions (I assume you're using TCP.) You are sending buffers of one size, but recv'ing potentially a buffer of another size. sizeof(yaw) which is a float, is not the same as the size of the string representation of this float.
Calling send/recv for individual item is slow. Ideally you would define a simple protocol. A message in this protocol would be a string containing all the values you wish to transmit. You send that message using a single send() On the receiving side you read in the stream of data, and look for specific markers that tell you when you have received a complete message. You then process that message, splitting out the different components into your yaw/pitch/roll variables.
An example of a string message would be: "{yaw=1.34;pitch=2.45;roll=5.67}"
Then on the client you continually read into a buffer your data until you reach the "}" Then you can process this message and parse out the different components.
Currently I try to write a serial port communication in VC++ to transfer data from PC and robot via XBee transmitter. But after I wrote some commands to poll data from robot, I didn't receive anything from the robot (the output of filesize is 0 in the code.). Because my MATLAB interface works, so the problem should happen in the code not the hardware or communication. Would you please give me help?
01/03/2014 Updated: I have updated my codes. It still can not receive any data from my robot (the output of read is 0). When I use "cout<<&read" in the while loop, I obtain "0041F01C1". I also don't know how to define the size of buffer, because I don't know the size of data I will receive. In the codes, I just give it a random size like 103. Please help me.
// This is the main DLL file.
#include "StdAfx.h"
#include <iostream>
#define WIN32_LEAN_AND_MEAN //for GetCommState command
#include "Windows.h"
#include <WinBase.h>
using namespace std;
int main(){
char init[]="";
HANDLE serialHandle;
// Open serial port
serialHandle = CreateFile("\\\\.\\COM8", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
// Do some basic settings
DCB serialParams;
DWORD read, written;
serialParams.DCBlength = sizeof(serialParams);
if((GetCommState(serialHandle, &serialParams)==0))
{
printf("Get configuration port has a problem.");
return FALSE;
}
GetCommState(serialHandle, &serialParams);
serialParams.BaudRate = CBR_57600;
serialParams.ByteSize = 8;
serialParams.StopBits = ONESTOPBIT;
serialParams.Parity = NOPARITY;
//set flow control="hardware"
serialParams.fOutX=false;
serialParams.fInX=false;
serialParams.fOutxCtsFlow=true;
serialParams.fOutxDsrFlow=true;
serialParams.fDsrSensitivity=true;
serialParams.fRtsControl=RTS_CONTROL_HANDSHAKE;
serialParams.fDtrControl=DTR_CONTROL_HANDSHAKE;
if (!SetCommState(serialHandle, &serialParams))
{
printf("Set configuration port has a problem.");
return FALSE;
}
GetCommState(serialHandle, &serialParams);
// Set timeouts
COMMTIMEOUTS timeout = { 0 };
timeout.ReadIntervalTimeout = 30;
timeout.ReadTotalTimeoutConstant = 30;
timeout.ReadTotalTimeoutMultiplier = 30;
timeout.WriteTotalTimeoutConstant = 30;
timeout.WriteTotalTimeoutMultiplier = 30;
SetCommTimeouts(serialHandle, &timeout);
if (!SetCommTimeouts(serialHandle, &timeout))
{
printf("Set configuration port has a problem.");
return FALSE;
}
//write packet to poll data from robot
WriteFile(serialHandle,">*>p4",strlen(">*>p4"),&written,NULL);
//check whether the data can be received
char buffer[103];
do {
ReadFile (serialHandle,buffer,sizeof(buffer),&read,NULL);
cout << read;
} while (read!=0);
//buffer[read]="\0";
CloseHandle(serialHandle);
return 0;
}
GetFileSize is documented not to be valid when used with a serial port handle. Use the ReadFile function to receive serial port data.
You should use strlen instead of sizeof here:
WriteFile(serialHandle,init,strlen(init),&written,NULL)
You would be even better off creating a function like this:
function write_to_robot (const char * msg)
{
DWORD written;
BOOL ok = WriteFile(serialHandle, msg, strlen(msg), &written, NULL)
&& (written == strlen(msg));
if (!ok) printf ("Could not send message '%s' to robot\n", msg);
}
But that's only the appetizer. The main trouble is, as MDN says:
You cannot use the GetFileSize function with a handle of a nonseeking device such as a pipe or a communications device.
If you want to read from the port, you can simply use ReadFile until it returns zero bytes.
If you already know the max size of your robot's response, try reading that many characters.
Continue reading until the read reports an actual number of bytes read inferior to the size of the buffer. For instance:
#define MAX_ROBOT_ANSWER_LENGTH 1000 /* bytes */
const char * read_robot_response ()
{
static char buffer[MAX_ROBOT_ANSWER_LENGTH];
DWORD read;
if (!ReadFile (serialHandle, buffer, sizeof(buffer), &read, NULL))
{
printf ("something wrong with the com port handle");
exit (-1);
}
if (read == sizeof(buffer))
{
// the robot response is bigger than it should
printf ("this robot is overly talkative. Flushing input\n");
// read the rest of the input so that the next answer will not be
// polluted by leftovers of the previous one.
do {
ReadFile (serialHandle, buffer, sizeof(buffer), &read, NULL);
} while (read != 0);
// report error
return "error: robot response exceeds maximal length";
}
else
{
// add a terminator to string in case Mr Robot forgot to provide one
buffer[read] = '\0';
printf ("Mr Robot said '%s'\n", buffer);
return buffer;
}
}
This simplistic function returns a static variable, which will be overwritten each time you call read_robot_response.
Of course the proper way of doing things would be to use blocking I/Os instead of waiting one second and praying for the robot to answer in time, but that would require a lot more effort.
If you feel adventurous, you can use overlapped I/O, as this lenghty MDN article thoroughly explores.
EDIT: after looking at your code
// this reads at most 103 bytes of the answer, and does not display them
if (!ReadFile(serialHandle,buffer,sizeof(buffer),&read,NULL))
{
printf("Reading data to port has a problem.");
return FALSE;
}
// this could display the length of the remaining of the answer,
// provided it is more than 103 bytes long
do {
ReadFile (serialHandle,buffer,sizeof(buffer),&read,NULL);
cout << read;
}
while (read!=0);
You are displaying nothing but the length of the response beyond the first 103 characters received.
This should do the trick:
#define BUFFER_LEN 1000
DWORD read;
char buffer [BUFFER_LEN];
do {
if (!ReadFile(
serialHandle, // handle
buffer, // where to put your characters
sizeof(buffer) // max nr of chars to read
-1, // leave space for terminator character
&read, // get the number of bytes actually read
NULL)) // Yet another blody stupid Microsoft parameter
{
// die if something went wrong
printf("Reading data to port has a problem.");
return FALSE;
}
// add a terminator after last character read,
// so as to have a null terminated C string to display
buffer[read] = '\0';
// display what you actually read
cout << buffer;
}
while (read!=0);
I advised you to wrap the actual calls to serial port accesses inside simpler functions for a reason.
As I said before, Microsoft interfaces are a disaster. They are verbose, cumbersome and only moderately consistent. Using them directly leads to awkward and obfuscated code.
Here, for instance, you seem to have gotten confused between read and buffer
read holds the number of bytes actually read from the serial port
buffer holds the actual data.
buffer is what you will want to display to see what the robot answered you
Also, you should have a documentation for your robot stating which kind of answers you are supposed to expect. It would help to know how they are formatted, for instance whether they are null-terminated strings or not. That could dispense to add the string terminator.
I've been stuck on this issue for awhile where I'm unable to send a file through a socket. I've sent other information just fine using this method, but the problem seems to appear when I try to send a PNG file as a string.
These are the methods I use to to send and receive information:
// Sends a Message to the specified Socket
void Server::sendMessage(int socket, string message)
{
// Write the Message Size to the Socket
send(socket, itoa((message.length() + 1)), sizeof(size_t));
// Wait for Write Confirmation
bool response;
receive(socket, &response, 2);
// Write the Message to the Socket
send(socket, (char*) message.c_str(), message.length() + 1);
// Wait for Write Confirmation
receive(socket, &response, 2);
}
// Receives Message from the specified Socket
string Server::receiveMessage(int socket)
{
// Read the Message Size from the Socket
int size;
receive(socket, &size, sizeof(size_t));
// Send Write Confirmation
send(socket, itoa(true), 2);
// Receive the Message from the Socket
char message[size];
receive(socket, message, size);
// Send Write Confirmation
send(socket, itoa(true), 2);
// Return the Message as a String
string msg(message);
return msg;
}
The send and receive methods are just relays for write and read respectively. I'm only doing error checking in those methods, and it's the send method that's telling me that the write isn't working. In case it matters, this is my send method:
// Sends a Data Packet to the specified Socket
int Server::send(int socket, void* data, int size)
{
// Write the Data to the Socket
int count = write(socket, data, size);
// Make sure the Write Succeeded
if(count == -1)
{
print("$f1Error: $f0Unable to Write to Socket $t1%i$t0\n", socket);
exit(1);
}
return count;
}
I should note that the Server operates as a Thread, therefore the above three functions are static. The Client also contains the same four networking functions.
The command line breaking this happens in a separate static function which I use to handle Clients. Here is the relevant portion of said method:
// Handles each Client with a Thread
void* Server::server_handleClient(void* arg)
{
// Determine the Socket Descriptor
int socket = *((int*) arg);
free(arg);
// Create the Rover
Rover* rover = new Rover();
// Loop Indefinitely
while(true)
{
...
// Take a Picture and Send it
sendMessage(socket, rover -> takePicture());
...
}
// Delete the Rover
delete rover;
// Close the Socket
close(socket);
// Return a Successful Status
return (void*) new int(0);
}
Here you can see that I make use of a method from another class I've created. Here is the takePicture method from the Rover class, which is where I actually grab the picture:
// Takes a Picture and Returns the Photo as a String
inline string Rover::takePicture()
{
// Open the Picture File
ifstream picture;
string filepath = "./Server/Pictures/" + getDirection() + ".png";
picture.open(filepath.c_str());
// Make sure the File Opened
if(!picture.is_open())
return "";
// Read the File into a String Buffer
stringstream buffer;
buffer << picture.rdbuf();
return buffer.str();
}
So in short, the Server gets a picture from the Rover which it then sends to a Client. When I check the contents of the string for the photo, it's all there. All possible photos are reasonable in size (the photo used for testing is 674,962 bytes, and the buffer size sent is 674,963 which is expected).
I've used these methods for sending various messages, and all of that worked fine. I'm able to send strings (Like "Hello World!") and integers just fine.
Is there something that I'm doing wrong? Is the file that I'm trying to send simply too large? Is there some information that I'm missing? I need help...
Edit:
I've made a few changes with a little progress. I made one small change to the sendMessage command. The current problem is that the picture isn't being sent properly.
New sendMessage function:
// Sends a Message to the specified Socket
void Server::sendMessage(int socket, string message, bool data = false)
{
// Write the Message Size to the Socket
send(socket, itoa((message.length() + 1)), sizeof(size_t));
// Wait for Write Confirmation
bool response;
receive(socket, &response, 2);
// Determine the Type of Data to Send
if(data)
{
// Write the Message Data to the Socket
send(socket, (char*) message.data(), message.length() + 1);
}
else
{
// Write the Message to the Socket
send(socket, (char*) message.c_str(), message.length() + 1);
}
// Wait for Write Confirmation
receive(socket, &response, 2);
}
The Client's copy of this function has been updated to match as well.
Now that we're working on getting the PNG file saved, here's the function that deals with that as well:
// Handles each Client with a Thread
void* Client::client_handleServer(void* arg)
{
// Define Socket Variables
int socket = *((int*) arg);
free(arg);
...
// Export the Picture to the Client's Directory
message = receiveMessage(socket);
ofstream picture;
picture.open("./Client/Pictures/Picture.png", std::ifstream::binary);
picture << message;
picture.close();
...
}
Currently you are opening the file in textmode. that means any characters in the files which contain newlines "\n" are converted to new line + carriage returns "\r\n".
Open your file in binary mode, like so
picture.open(filepath.c_str(), std::ifstream::binary);
then it may work.
void Server::sendMessage(int socket, string message)
The problem is right here. Don't use string as a container for binary data. Pass the image around as a byte array. Same applies to this:
string Server::receiveMessage(int socket)
I eventually figured everything out in the long run.
Pictures are binary files, and I was using Strings which use ASCII Characters. The issue with this is that binary data does not always translate to ASCII, and Strings are terminated by null characters, whereas binary data can contain null data within it. Long story short, strings do not work.
To preserve the message handling I had in place, I ended up just converting the binary data to hexadecimal data (0-F) which could be displayed in a String.