I am using an Arduino Due and Visual Studio 2010. I am programming in C/C++. Below you see my programs and after this my explanations of what is going wrong.
This is the really simple code thats on my Arduino. Its sending a lot of A's to the PC.
void setup()
{
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
void loop()
{
Serial.println('A');
delay(1); // delay in between reads for stability
}
The further code for reading the Serial Port I found over here:
http://playground.arduino.cc/Interfacing/CPPWindows
And this is my modified version of this code at the moment:
Header:
#ifndef SERIALCLASS_H_INCLUDED
#define SERIALCLASS_H_INCLUDED
#define ARDUINO_WAIT_TIME 2000
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
class Serial
{
private:
//Serial comm handler
HANDLE hSerial;
//Connection status
bool connected;
//Get various information about the connection
COMSTAT status;
//Keep track of last error
DWORD errors;
public:
//Initialize Serial communication with the given COM port
Serial(char *portName);
//Close the connection
//NOTA: for some reason you can't connect again before exiting
//the program and running it again
~Serial();
//Read data in a buffer, if nbChar is greater than the
//maximum number of bytes available, it will return only the
//bytes available. The function return -1 when nothing could
//be read, the number of bytes actually read.
int ReadData(char *buffer, unsigned int nbChar);
//Writes data from a buffer through the Serial connection
//return true on success.
bool WriteData(char *buffer, unsigned int nbChar);
//Check if we are actually connected
bool IsConnected();
};
#endif // SERIALCLASS_H_INCLUDED
CPP:
#include "stdafx.h"
#include <stdio.h>
#include <tchar.h>
#include "SerialClass.h"
#include <string>
#include <windows.h>
#include <assert.h>
#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <conio.h>
using namespace System;
using namespace std;
// Serial::Serial looks, if Serial Connection from PC to the Device is proper and everything is working. Then it sets a few Parameters and waits
Serial::Serial(char *portName)
{
//We're not yet connected
this->connected = false;
//Try to connect to the given port throuh CreateFile
this->hSerial = CreateFileA(portName,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
//Check if the connection was successfull
//if not...show error
if(this->hSerial==INVALID_HANDLE_VALUE)
{
//If not success full display an Error
if(GetLastError()==ERROR_FILE_NOT_FOUND){
//Print Error if neccessary
printf("ERROR: Handle was not attached. Reason: %s not available.\n", portName);
Sleep(2000);
}
else
{printf("ERROR!!!");}
}
// else = connection is working, then -> continue
else
{
//If connected we try to set the comm parameters
DCB dcbSerialParams = {0};
//Try to get the current Parameters
if (!GetCommState(this->hSerial, &dcbSerialParams))
{
//If impossible, show an error
printf("failed to get current serial parameters!");
}
else
{
//Define serial connection parameters for the arduino board
dcbSerialParams.BaudRate=CBR_9600;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
//Set the parameters and check for their proper application
if(!SetCommState(hSerial, &dcbSerialParams))
{
printf("ALERT: Could not set Serial Port parameters");
}
else
{
//If everything went fine we're connected
this->connected = true;
//We wait 2s as the arduino board will be reseting
Sleep(ARDUINO_WAIT_TIME);
}
}
}
}
//Has a look if SerialPort is still connected.
//If yes, it disconnects and closes the Serial Handler.
Serial::~Serial()
{
//Check if we are connected before trying to disconnect
if(this->connected)
{
//We're no longer connected
this->connected = false;
//Close the serial handler
CloseHandle(this->hSerial);
}
}
// reads data out of Serial Port
int Serial::ReadData(char *buffer, unsigned int nbChar)
{
//Number of bytes we'll have read
DWORD bytesRead;
//Number of bytes we'll really ask to read
unsigned int toRead;
//Use the ClearCommError function to get status info on the Serial port
ClearCommError(
this->hSerial, // Handle to the communications device, CreateFile Function returns this value
&this->errors, // a pointer to a variable that receives a mask indicating the type of rror
&this->status);// a pointer to a COMSTAT structure in which the devices status information is returned. if this parameter is NULL, no status information is returned
//Check if there is something to read
if(this->status.cbInQue>0) // cbInQue: Number of Bytes received by the Serial provider, but not yet read by a ReadFile operation
{
//If there is we check if there is enough data to read the required number
//of characters, if not we'll read only the available characters to prevent
//locking of the application.
if(this->status.cbInQue>nbChar)
{toRead = nbChar;}
else
{toRead = this->status.cbInQue;}
//Try to read the require number of chars, and return the number of read bytes on success
if(ReadFile(
this->hSerial, // Handle to a device
buffer, // a pointer to the buffer that receives the data read from a file or device
toRead, // NumberofBytesToRead: the maximum number of bytes to be read
&bytesRead, // NumberofBytesRead: a pointer to the variable that receives the number of bytes read when using a synchronours hFile parameter.
NULL) // Overlapped
&& bytesRead // Value of bytesRead after ReadFile function
!= 0)
{return bytesRead;
Sleep(1000);} // returns Value of BytesRead
}
//If nothing has been read, or that an error was detected return -1
return -1;
}
bool Serial::IsConnected() // simply returns connection status
{
//Simply return the connection status
return this->connected;
}
Main:
// application reads from the specified serial port and reports the collected data
int _tmain(int argc, _TCHAR* argv[])
{
printf("Welcome to the serial test app!\n\n");
Serial* SP = new Serial("\\\\.\\COM3"); // adjust as needed
if (SP->IsConnected())
printf("We're connected");
// defines how much Data will be catched
// don't forget to pre-allocate memory
char incomingData[1025] = "";
incomingData[1024]='\0';
int dataLength = 1024; // maximum Length of one DataBit/Word
int readResult = 0;
// gives out the Number! of DataBits that could be collected/catched
readResult = SP->ReadData(incomingData,dataLength);
printf("Bytes read: (-1 means no data available) %i\n",readResult);
// transforms the char incomingData into a String and prints it
std::string test(incomingData);
printf("%s \n", incomingData);
printf("Bytes read: (-1 means no data available) %i\n",readResult);
Sleep(10000);
}
So now here is my problem:
The programm works fine, as long as the Amount of Bits I am sending from the Arduino is less than DataLength. (here = 1024) I got those less bits just through setting the Delay from the Arduino programm quite high (~100ms).
Then I have a console window with an Output similar to this:
A
A
A
A
A
... goes on like this
But if the Arduino sends more than 1024 Bits (Delay ~ 1ms)/ the PC receices more Bits than the Value of DataLength, something in the Serial::ReadData Loop seems to get wrong.
My Console Output is than a little bit corrupted and some Bits look like this:
A
ßA
A A
A
... goes on similar to this.
What is wrong with my program? I thought that it could be, that one parameter of the ReadFile() Function is not right, but I do not know what to change and I am not 100% sure about this.
Related
I have a code to open and read serial COM port in vs2012 c++ which is working fine when I run the code separately in an individual solution.The code is as follow:
Serial* SP = new Serial("\\\\.\\COM3"); // adjust as needed
if (SP->IsConnected()) // check com port availability
printf("We're connected"); // send the result
char incomingData[512] = ""; // don't forget to pre-allocate memory
int dataLength = 256;
int readResult = 0; //if there is no reading it is -1
while(SP->IsConnected())
{
readResult = SP->ReadData(incomingData,dataLength);
//std::string test(incomingData);
res1=strtol(incomingData,&pos1,10); //receive data in right patern
res2=atof(pos1); //convert the character to integer
res3=(double)res2; // convert integer to double (as my desired output is a double)
printf("%f\n",res2); // print the result
Sleep(50); // pause so that I can see the coming data
}
in which Serial,ReadData and other functions and headers are defined in a separate header and .cpp file.
My problem occurs when I want to plug the code in my other solution (SOFA Simulation) which I want to use to make a graphical interface. but I get the INVALID_HANDLE_VALUE error and the get last error gives me ERROR_FILE_NOT_FOUND. this is my code in the solution I want to use:
namespace sofa
{
namespace component
{
namespace behaviormodel
{
MyBehaviorModel::MyBehaviorModel():
customUnsignedData(initData(&customUnsignedData, (unsigned)1,"Custom Unsigned Data","Example of unsigned data with custom widget")),
regularUnsignedData(initData(®ularUnsignedData, (unsigned)1,"Unsigned Data","Example of unsigned data with standard widget"))
{
customUnsignedData.setWidget("widget_myData");
}
MyBehaviorModel::~MyBehaviorModel()
{
}
void MyBehaviorModel::init()
{
}
void MyBehaviorModel::reinit()
{
}
void MyBehaviorModel::updatePosition(SReal dt)
{
Serial* SP = new Serial("\\\\.\\COM3"); // adjust as needed
if (SP->IsConnected())
printf("We're connected");
char incomingData[512] = ""; // don't forget to pre-allocate memory
int dataLength = 256;
int readResult = 0;
while(SP->IsConnected())
{
readResult = SP->ReadData(incomingData,dataLength);
//std::string test(incomingData);
res1=strtol(incomingData,&pos1,10);
res2=atof(pos1);
res3=(double)res2;
printf("%f\n",res2);
Sleep(50);
}
dx=0.01;
dy=0.01;
dz+=0.01;
using core::behavior::MechanicalState;
mState1 = dynamic_cast<MechanicalState<sofa::defaulttype::Rigid3dTypes> *> (this->getContext()->getMechanicalState());
helper::WriteAccessor<sofa::core::objectmodel:: Data<sofa::defaulttype::Rigid3dTypes::VecCoord> > xp = *mState1- >write(core::VecCoordId::position());
xp[0].getCenter()=sofa::defaulttype::Vec<3,Real>((Real)dx,(Real)dy,(Real)(res2);
}
SOFA_DECL_CLASS(MyBehaviorModel)
int MyBehaviorModelClass = core::RegisterObject("Dummy component with a custom widget.").add< MyBehaviorModel >();
} // namespace behaviormodel
} // namespace component
} // namespace sofa
I really can not figure out what the problem is because as I said the problem is not from my serial reader code as I tested it and I know it works fine separately.can you find out where the problem lies?
thanks in advance!
This is my Serial constructor:
Serial::Serial(char *portName)
{//We're not yet connected
this->connected = false;
//Try to connect to the given port throuh CreateFile
this->hSerial = CreateFile((LPCWSTR)portName,
GENERIC_READ ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
//Check if the connection was successfull
if(this->hSerial==INVALID_HANDLE_VALUE)
{
//If not success full display an Error
if(GetLastError()==ERROR_FILE_NOT_FOUND){
//Print Error if neccessary
printf("ERROR: Handle was not attached. Reason: %s not available.\n", portName);
}
else
{
printf("ERROR!!!");
}
}
else
{
//If connected we try to set the comm parameters
DCB dcbSerialParams = {0};
//Try to get the current
if (!GetCommState(this->hSerial, &dcbSerialParams))
{
//If impossible, show an error
printf("failed to get current serial parameters!");
}
else
{
//Define serial connection parameters for the arduino board
dcbSerialParams.BaudRate=CBR_9600;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
//Setting the DTR to Control_Enable ensures that the Arduino is properly
//reset upon establishing a connection
dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;
//Set the parameters and check for their proper application
if(!SetCommState(hSerial, &dcbSerialParams))
{
printf("ALERT: Could not set Serial Port parameters");
}
else
{
//If everything went fine we're connected
this->connected = true;
//Flush any remaining characters in the buffers
PurgeComm(this->hSerial, PURGE_RXCLEAR | PURGE_TXCLEAR);
//We wait 2s as the arduino board will be reseting
Sleep(ARDUINO_WAIT_TIME);
}
}
}
}
The function CreateFile is actually a Macro that is mapped to either CreateFileA or CreateFileW depending on your project (unicode) configuration. As others have mentioned, you should not use a type cast to LPCWSTR to hide the fact that your code is not correct, you just need to use the right type of string.
If a function expects a widestring (LPCWSTR) and you pass it a chunk of memory that contains an ANSI string, it will never work. In this particular case, you can use the function CreateFileA directly so that you can pass your ANSI string to it.
I'm using the following Arduino websocket library, I had a problem when trying to send messages over than 65535 characters, I got handshake fail error.
As long as the message doesn't exceeds this length, it worked perfectly
There's a note on the main web page of the library that states:
Because of limitations of the current Arduino platform (Uno at the time of this writing),
this library does not support messages larger than 65535 characters.
In addition, this library only supports single-frame text frames.
It currently does not recognize continuation frames, binary frames, or ping/pong frames.
In the client header file named WebSocketClient.h there's the following comment:
// Don't allow the client to send big frames of data. This will flood the arduino memory and might even crash it.
#ifndef MAX_FRAME_LENGTH
#define MAX_FRAME_LENGTH 256
#endif
I'm using this old library because it is the only one worked for me on my Arduino WIFI shield, I couldn't find other libraries that support WiFi shield since most of the webscket libraries are written for Arduino Eathernet Shield support, which I don't have.
My Arduino Code is
/*DS18 Libs*/
#include <dht.h>
#include <OneWire.h>
#include <DallasTemperature.h>
/*Websocket Libs*/
#include <WebSocketServer.h>
#include <WebSocketClient.h>
#include <sha1.h>
#include <MD5.h>
#include <global.h>
#include <Base64.h>
#include <SPI.h>
#include <WiFiUdp.h>
#include <WiFiServer.h>
#include <WiFiClient.h>
#include <WiFi.h>
#include <string.h>
char ssid[] = "AMM";
char pass[] = "027274792";
int status = WL_IDLE_STATUS;
IPAddress server(192, 168, 1, 3);
WiFiClient WiFiclient;
WebSocketClient WSclient;
// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
//Humidture
dht DHT;
#define DHT11_PIN 4
void setup()
{
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
//check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
// don't continue:
while (true);
}
// attempt to connect to Wifi network:
while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network:
status = WiFi.begin(ssid, pass);
}
// you're connected now, so print out the data:
Serial.print("You're connected to the network");
/* Connect to the websocket server*/
if (WiFiclient.connect(server, 8080)) {
Serial.println("Connected");
}
else {
Serial.println("Connection failed.");
while (1) {
// Hang on failure
}
}
// Handshake with the server
WSclient.path = "/MyServer/endpoint/testtest/device/d6220ae7-caa9-48b5-92db-630c4c296ec4";
WSclient.host = "192.168.1.3:8080";
if (WSclient.handshake(WiFiclient)) {
Serial.println("Handshake successful");
}
else {
Serial.println("Handshake failed.");
while (1) {
// Hang on failure
}
}
/*DS18*/
sensors.begin();
}
void loop()
{
WSclient.sendData("{\"service_code\":\"89c4da72-a561-47db-bf62-8e63f8c4bbf0\",\"data\":[" + getHumidtureValue() + "],\"service_type\":\"TemperatureHumidityAnalysis\"}");
WSclient.sendData("{\"service_code\":\"bdc0f984-6550-4712-881f-b09071da5a73\",\"data\":" + getCBodyTempretureValue() + ",\"service_type\":\"TemperatureGaugeMonitor\"}");
//line-3 commented WSclient.sendData("{\"service_code\":\"8c212432-a86e-4c18-a956-9dc0dbb648d4\",\"data\":[" + getHumidtureValue() + "],\"service_type\":\"HumidityGaugeMonitor\"}");
}
String getCBodyTempretureValue()
{
sensors.requestTemperatures(); // Send the command to get temperatures
char charVal[10];
return dtostrf(sensors.getTempCByIndex(0), 4, 2, charVal);
}
String getHumidtureValue()
{
String str = "";
for (int i = 0; i < 2; i++)
{
int chk = DHT.read11(DHT11_PIN);
switch (chk)
{
case DHTLIB_OK:
Serial.println("OK,\t");
break;
case DHTLIB_ERROR_CHECKSUM:
Serial.println("Checksum error,\t");
break;
case DHTLIB_ERROR_TIMEOUT:
Serial.println("Time out error,\t");
break;
default:
Serial.println("Unknown error,\t");
break;
}
char charVal[10];
double tempF = (DHT.temperature * 9) / 5 + 32;
str = dtostrf(tempF, 3, 1, charVal);
str = str + "," + dtostrf(DHT.humidity, 3, 1, charVal);
Serial.println(str);
delay(200);
}
return str;
}
The code above works perfectly, when I uncomment the third send statement in the loop function, I got the handshake failed error.
-Is it safe to modify the value of MAX_FRAME_LENGTH for the new versions of Arduino board, considering this library is an old one?
-Is there any other libraries better than this one that can support websocket on WiFi shield?
Any solution or idea will appreciated.
Thanks in advance.
Without having looked at the code of the library it is likely not safe to change the max frame length, because the websocket protocol encodes the payload length differently depending on how long it is:
Payload length: 7 bits, 7+16 bits, or 7+64 bits.
The length of the "Payload data", in bytes: if 0-125, that is the payload length. If 126, the following 2 bytes interpreted as a 16-bit unsigned integer are the payload length. If 127, the following 8 bytes interpreted as a 64-bit unsigned integer (the most significant bit MUST be 0) are the payload length.
When the library says it doesn't support payload length above 65535 byte, it likely means that it has no implementation for the 64-bit length encoding.
After many trials and many times the program behaves very strangely, which drives me crazy, I found the problem is that I'm using too much strings in my program which makes the Arduino-Uno easily runs out of RAM.
The main reason I got handshake failed error is that the Arduino cannot read the "Sec-WebSocket-Accept" header of the handshake response message (as many other headers also) which I made sure that they are sent, by debugging code on the server.
Actually this problem and many other strange behaviors keep happening until I reduce the amount of the memory used during program run.
I am working on a project which needs a Linux PC to take data from a micro controller on UART, for which I have used an already available open source code for serial port in C++ for linux. (Ros (robotic operating system) based code)
The code goes as below:
#define DEFAULT_BAUDRATE 115200
#define DEFAULT_SERIALPORT "/dev/ttyUSB0"
//Global data
FILE *fpSerial = NULL; //serial port file pointer
ros::Publisher ucResponseMsg;
ros::Subscriber ucCommandMsg;
int ucIndex; //ucontroller index number
int FileDesc;
unsigned char crc_sum=0;
//Initialize serial port, return file descriptor
FILE *serialInit(char * port, int baud)
{
int BAUD = 0;
int fd = -1;
struct termios newtio, oldtio;
FILE *fp = NULL;
//Open the serial port as a file descriptor for low level configuration
// read/write, not controlling terminal for process,
fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
ROS_INFO("FileDesc : %d",fd);
if ( fd<0 )
{
ROS_ERROR("serialInit: Could not open serial device %s",port);
return fp;
}
// save current serial port settings
tcgetattr(fd,&oldtio);
// clear the struct for new port settings
bzero(&newtio, sizeof(newtio));
//Look up appropriate baud rate constant
switch (baud)
{
case 38400:
default:
BAUD = B38400;
break;
case 19200:
BAUD = B19200;
break;
case 115200:
BAUD = B115200;
break;
case 9600:
BAUD = B9600;
break;
case 4800:
BAUD = B4800;
break;
case 2400:
BAUD = B2400;
break;
case 1800:
BAUD = B1800;
break;
case 1200:
BAUD = B1200;
break;
} //end of switch baud_rate
if (cfsetispeed(&newtio, BAUD) < 0 || cfsetospeed(&newtio, BAUD) < 0)
{
ROS_ERROR("serialInit: Failed to set serial baud rate: %d", baud);
close(fd);
return NULL;
}
// set baud rate, (8bit,noparity, 1 stopbit), local control, enable receiving characters.
newtio.c_cflag = BAUD | CRTSCTS | CS8 | CLOCAL | CREAD;
// ignore bytes with parity errors
newtio.c_iflag = IGNPAR;
// raw output
newtio.c_oflag = 0;
// set input mode to non - canonical
newtio.c_lflag = 0;
// inter-charcter timer
newtio.c_cc[VTIME] = 0;
// blocking read (blocks the read until the no.of charcters are read
newtio.c_cc[VMIN] = 0;
// clean the line and activate the settings for the port
tcflush(fd, TCIFLUSH);
tcsetattr (fd, TCSANOW,&newtio);
//Open file as a standard I/O stream
fp = fdopen(fd, "r+");
if (!fp) {
ROS_ERROR("serialInit: Failed to open serial stream %s", port);
fp = NULL;
}
ROS_INFO("FileStandard I/O stream: %d",fp);
return fp;
} //serialInit
//Process ROS command message, send to uController
void ucCommandCallback(const geometry_msgs::TwistConstPtr& cmd_vel)
{
unsigned char msg[14];
float test1,test2;
unsigned long i;
// build the message packet to be sent
msg = packet to be sent;
msg[13] = crc_sum;
for (i=0;i<14;i++)
{
fprintf(fpSerial, "%c", msg[i]);
}
tcflush(FileDesc, TCOFLUSH);
} //ucCommandCallback
//Receive command responses from robot uController
//and publish as a ROS message
void *rcvThread(void *arg)
{
int rcvBufSize = 200;
char ucResponse[10];//[rcvBufSize]; //response string from uController
char *bufPos;
std_msgs::String msg;
std::stringstream ss;
int BufPos,i;
unsigned char crc_rx_sum =0;
while (ros::ok()) {
BufPos = fread((void*)ucResponse,1,10,fpSerial);
for (i=0;i<10;i++)
{
ROS_INFO("T: %x ",(unsigned char)ucResponse[i]);
ROS_INFO("NT: %x ",ucResponse[i]);
}
msg.data = ucResponse;
ucResponseMsg.publish(msg);
}
return NULL;
} //rcvThread
int main(int argc, char **argv)
{
char port[20]; //port name
int baud; //baud rate
char topicSubscribe[20];
char topicPublish[20];
pthread_t rcvThrID; //receive thread ID
int err;
//Initialize ROS
ros::init(argc, argv, "r2SerialDriver");
ros::NodeHandle rosNode;
ROS_INFO("r2Serial starting");
//Open and initialize the serial port to the uController
if (argc > 1) {
if(sscanf(argv[1],"%d", &ucIndex)==1) {
sprintf(topicSubscribe, "uc%dCommand",ucIndex);
sprintf(topicPublish, "uc%dResponse",ucIndex);
}
else {
ROS_ERROR("ucontroller index parameter invalid");
return 1;
}
}
else {
strcpy(topicSubscribe, "uc0Command");
strcpy(topicPublish, "uc0Response");
}
strcpy(port, DEFAULT_SERIALPORT);
if (argc > 2)
strcpy(port, argv[2]);
baud = DEFAULT_BAUDRATE;
if (argc > 3) {
if(sscanf(argv[3],"%d", &baud)!=1) {
ROS_ERROR("ucontroller baud rate parameter invalid");
return 1;
}
}
ROS_INFO("connection initializing (%s) at %d baud", port, baud);
fpSerial = serialInit(port, baud);
if (!fpSerial )
{
ROS_ERROR("unable to create a new serial port");
return 1;
}
ROS_INFO("serial connection successful");
//Subscribe to ROS messages
ucCommandMsg = rosNode.subscribe("cmd_vel" /*topicSubscribe*/, 100, ucCommandCallback);
//Setup to publish ROS messages
ucResponseMsg = rosNode.advertise<std_msgs::String>(topicPublish, 100);
//Create receive thread
err = pthread_create(&rcvThrID, NULL, rcvThread, NULL);
if (err != 0) {
ROS_ERROR("unable to create receive thread");
return 1;
}
//Process ROS messages and send serial commands to uController
ros::spin();
fclose(fpSerial);
ROS_INFO("r2Serial stopping");
return 0;
}
You can leave the ROS part aside, but the problem is with the serial port code.
When I run this code, I receive the data from the controller correctly, but even when controller stops sending the data also I see the same data coming on the printfs continuously. Is this the problem of not flushing the input buffers?
But I am unable to send the data from the Linux PC to the controller, no idea what is happening, can read and write happen simultaneously on serial port in linux?
Strange observation, when I open the port in H-term (an uART visualizer similar to that of hyper terminal) with my serial port code running at the back end, still the H-term doesn't give any error, but ideally H-term should give an error saying "the port can not be opened it is locked", but this doesn't happen, is my code not acquiring a lock on the serial port?
And when I connect the port using H-term with mu serial port code running then I can see the data coming on the UART from linux-PC to the micro controller?
Can any one have any insights to the problems I am facing here?
Thanks in advance.
One problem is here:
BufPos = fread((void*)ucResponse,1,10,fpSerial);
because there is no check whether BufPos is zero or less than 10
Rather than ros::ok, use feof() (after receiving 0 bytes) to check for a closed connection, and ferror() to check for errors. Or stop calling fread when you know (according to the protocol) that a packet of data has been received.
It is possible to use a serival port in full duplex mode (i.e., read and write) not exactly "simultaneously" but alternatively. Partners must follow protocol to avoid misunderstandings.
Don't mix fprintf and fread/fwrite. For sending, fwrite is indicated.
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'm currently working on a project using sockets via WinSock and have come across a peculiar problem. I'll attach the code before I start explaining.
#include "Connection.h"
Connection::Connection(SOCKET sock, int socketType)
: m_sock(sock), m_recvCount(0), m_sendCount(0), m_socketType(socketType)
{
printf("Succesfully created connection\n");
}
Connection::~Connection(void)
{
printf("Closing socket %d", m_sock);
closesocket(m_sock);
}
void Connection::ProcessMessage(const NetMessage *message){
printf("Got network message: type %d, data %s\n", message->type, message->data);
}
bool Connection::ReadSocket(){
// Call this when the socket is ready to read.
// Returns true if the socket should be closed.
// used to store count between the sockets
int count = 0;
if(m_socketType == SOCK_STREAM){
// attempt to read a TCP socket message
// Receive as much data from the client as will fit in the buffer.
count = recv(m_sock, &m_recvBuf[m_recvCount], sizeof(m_recvBuf) - m_recvCount, 0);
}
else if(m_socketType == SOCK_DGRAM){
// attempt to read UDP socket message
// temporarily stores details of the address which sent the message
// since UDP doesn't worry about whether it's connected to the
// sender or not
sockaddr_in fromAddr;
int fromAddrSize = sizeof(fromAddr);
count = recvfrom(m_sock, &m_recvBuf[m_recvCount], sizeof(m_recvBuf) - m_recvCount, 0, (sockaddr*) &fromAddr, &fromAddrSize);
}
else{
printf("Unknown socket type %d\n", m_socketType);
return true;
}
if (count <= 0)
{
printf("Tried to receive on socket %d and got %d bytes\n", m_sock, count);
printf("Client connection closed or broken\n");
return true;
}
// if we get to this point we have essentially received a complete message
// and must process it
printf("Received %d bytes from the client (total %d)\n", count, m_recvCount);
m_recvCount += count;
// Have we received a complete message?
// if so, process it
if (m_recvCount == sizeof NetMessage)
{
ProcessMessage((const NetMessage *) m_recvBuf);
m_recvCount = 0;
}
return false;
}
bool Connection::WriteSocket(){
// Sends the data in the send buffer through the socket
int count;
if(m_socketType == SOCK_STREAM){
// attempt to read TCP socket message
count = send(m_sock, m_sendBuf, m_sendCount, 0);
}
else if(m_socketType == SOCK_DGRAM){
// attempt to read UDP socket message
count = sendto(m_sock, m_sendBuf, m_sendCount, 0, 0, 0);
}
else{
// unhandled type of socket, kill server
printf("Unknown socket type %d", m_socketType);
return true;
}
if (count <= 0)
{
// we have received an error from the socket
printf("Client connection closed or broken\n");
return true;
}
m_sendCount -= count;
printf("Sent %d bytes to the client (%d left)\n", count, m_sendCount);
printf("Data: %s", m_sendBuf);
// Remove the sent data from the start of the buffer.
memmove(m_sendBuf, &m_sendBuf[count], m_sendCount);
return false;
}
bool Connection::WantWrite(){
if(m_sendCount > 0){
return true;
}
return false;
}
bool Connection::WantRead(){
return true;
}
bool Connection::SetMessage(const NetMessage *message){
// store contents of the message in the send buffer
// to allow us to send later
if (m_sendCount + sizeof(NetMessage) > sizeof(m_sendBuf))
{
return true;
}
memcpy(&m_sendBuf, message, sizeof(message));
m_sendCount += sizeof(NetMessage);
return false;
}
and the protocol
/* Definitions for the network protocol that the client and server use to communicate */
#ifndef PROTOCOL_H
#define PROTOCOL_H
// Message types.
enum MessageType
{
MT_UNKNOWN = 0,
MT_WELCOME = 1,
MT_KEYPRESS = 2,
MT_CHATMESSAGE = 3
};
// The message structure.
// This is a "plain old data" type, so we can send it over the network.
// (In a real program, we would want this structure to be packed.)
struct NetMessage
{
MessageType type;
char* data;
NetMessage()
: type(MT_UNKNOWN)
{
}
};
#endif
Essentially the protocol holds the definition of the messages that the client and server throw around to each other. The problem I am having is that, in connection.cpp line 132 (memcpy), the message becomes garbled in sendBuf.
http://imgur.com/MekQfgm,9ShRtHi
The image above shows exactly what is happening. As said in protocol.h the struct is a POD so when I do memcpy it should transfer the number of bytes as is held in the struct (so for example the message type should be 1 byte, followed by 7 or 8 bytes of data, in the example).
Can anyone shed some light on this? It's driving me crazy.
The line you wrote will copy 4 bytes (sizeof(pointer)) on 32bit systems:
memcpy(&m_sendBuf, message, sizeof(message));
what you probably meant is:
memcpy(&m_sendBuf, message, sizeof(NetMessage));
Edit:
In addition, as a commenter remarked, your data type is NOT a POD. It holds a pointer. You transfer that pointer. At the target system, it will point to the same place in RAM, but there will not be anything there. You need to actually make your datatype a POD by using an array or you need to find a way to transfer the data pointed to. You can achieve this by transfering the type, a length and a number of characters. That means that your receiver can NOT rely on messages being of fixed size.