I have a very basic device with which I am trying to interact via a serial connection on Linux. I am on the steep part of the learning curve here, still, so please be gentle!
One of the functions involves sending data to an attached printer. You send a command to the device, which then relays the data you input to the printer attached to the device. The command looks like this:
Send "EXEX*". The device echoes back "EXEX" (the '*' is not echoed yet)
Send a single byte indicating the length of the data you will send, including a LF at the end.
Send the data (the device will now echo back the *).
Send "#". The device will now be ready for another command.
I have a small C++ program to communicate with the device, and I can successfully send single characters and such, but when I try to send this command, I do not get the expected results.
Using Hyperterminal in Windows, it is particularly easy, using alt-key combinations to send ASCII control codes. Just connect and:
Type "EXEX*"
Type Alt+010 to send a LF character, indicating that you are sending 10 bytes to the printer (nine characters and a LF).
Type the data you wish to send: "123456789" (nine bytes in length).
Type Alt+010 again to send a final LF character to the printer.
Type "#" to finish.
Here is what I cobbled together to try in C++:
#include <SerialStream.h>
#include <string>
#include <iostream>
#include <fstream>
using namespace std;
using namespace LibSerial;
int main(){
char buffer [50];
int n;
n=sprintf (buffer, "EXEX*%c123456789%c#",10,10);
printf("Variable buffer was set to a %d character string: %s\n",n,buffer);
SerialStream my_serial_stream;
my_serial_stream.Open("/dev/ttyS0") ;
my_serial_stream.SetBaudRate( SerialStreamBuf::BAUD_19200 ) ;
my_serial_stream.SetCharSize( SerialStreamBuf::CHAR_SIZE_8 ) ;
my_serial_stream.SetFlowControl( SerialStreamBuf::FLOW_CONTROL_NONE ) ;
my_serial_stream.SetParity( SerialStreamBuf::PARITY_NONE ) ;
my_serial_stream.SetNumOfStopBits(1) ;
my_serial_stream.SetVTime(1);
my_serial_stream.SetVMin(100);
cout<<"Sending Command:\n";
my_serial_stream << buffer;
//my_serial_stream << printf("%s",buffer);
//my_serial_stream << "EXEX*\n123456789\n#";
my_serial_stream.read(next_char,100);
cout<<"Result: "<<next_char<<"\n";
my_serial_stream.Close();
return 0;
}
I also tried both of the commented out lines, and neither worked. The device does not receive the proper characters on the other end.'
I'm certain that this is pretty basic, perhaps something is grabbing the control characters in the middle? If anyone has any ideas on a better way to do this, I would really appreciate it. Specifically, I might need to send a byte with a value anywhere between 1 and 40, depending on the length of the data I wish to send to the printer.
My apologies for being unclear, please let me know if I need to break this down farther.
Many thanks,
Tom
The line you send doesn't include the # that you mention in the character sequence.
Have you checked serial comms works on /dev/ttyS0 using gtkterm / cutecom etc?
To test your interface you could read back the serial port. If you have a second port or computer, you could do that by connecting to another port via a null modem. Otherwise you could short pins 2 and 3 of your serial port and check that you are receiving back the characters you send.
You may want to check the return values of the calls to make to the serial library, to see if any errors are returned.
Perhaps there are timing requirements on the printer, and you may need to wait between sending some characters.
I compiled the code and checked the output on another serial port with gtkterm, it does receive the string you would expect:
45 58 45 58 2A 0A 31 32 - 33 34 35 36 37 38 39 0A EXEX*.12 3456789.
It won't affect the sending part of the code, but the receiving looks suspicious. If the read() member function is like the system call and if next_char is a character array, then it won't null terminate the string. Instead you have to look at the return value to get the size, and then null terminate if you are going to use it as a null-terminated C string.
Related
I have a device that sends 23 characters (numbers and alpha's) via RS232 serial in the following format:
$02 T AAAAA Q CCC PP ZZZ S RR I I NFF $0D
(the spaces in the above string are for readability only)
In this 23 character string the:
$02 represents the start of text 2 hex ( I am not sure what hex this is?)
$0D represents a Carriage Return 13 decimal.
I am currently reading this information in via Python mostly successfully but I still feel I am not doing it properly. I rarely program in Python but I have to use a raspberry PI so decided to go with python for the coding.
I setup my RPI serial port with the following function:
def setupSerialPort():
ser = serial.Serial(
port='/dev/ttyAMA0',
baudrate = 9600,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1,
xonxoff=0,
rtscts=0
)
return ser
From a while loop I read the port as follow:
# setup serial port
cSerial = setupSerialPort()
while 1:
inbuff = cSerial.inWaiting()
if inbuff > 0:
msgCOM = cSerial.read(inbuff)
#vMsgCOM = re.sub('[^A-Za-z0-9]+', '', msgCOM)
//insert value into database
sleep(1)
at which point I insert the value "vMsgCOM" or "msgCOM" string into a mysql database as I read/receive the data. At first I thought that this works pretty well but after a week of data it became clear that I sometimes only capture partial data which splits over two database rows as mentioned previously. I'll give an example:
A correct 23 char string will look like this: K00000E1120002000063B00.
Now sometimes the string is split into two rows like
(1) K00000E11200020
(2) 00063B00
Another variation of the above is the multiple 23 chunks returned as:
K00000E1120002000063B00K00000E1120002000063B00K00000E1120002000063B00
This happens roughly 15 times out of 400 reads for the above.
Can anyone help me in terms of coding to somehow ensure that I always read the buffer correctly when the 23 string arrives. I know timing can be an issue hence the timeout=1 but somehow I read to quickly (or to long) when the read is not complete.
I had a look at this code (haven't tried it yet): pySerial inWaiting returns incorrect number of bytes (the def read_all(port, chunk_size=200) function part)
but thought it best to rather ask advice from those in the know.
I have in my code a bit of corrective code to concat the two rows and split the multiple chunk event should these instance(s) happen but I still think it is not the best way of doing things.
If anyone can help me with some example code I will really appreciate it.
I have to send information between a client and a server in C++. I have found that the following code allows me to send integers over the connection.
Server:
int number = 5;
send(clientSocket, (char*)&number, sizeof(number), 0); //Integer is sent to client
Client:
int number;
recv(serverSocket, (char*)&number, sizeof(number), 0); //Integer is received and assigned to 'number'
However, I am now trying to send a wstring variable using the exact same code (replace 'int' with 'wstring') and it fails to send the value correctly. How can I send a wstring over the socket and receive it on the client?
Step 1:
Sender and receiver agree on a shared character encoding. If you are writing both sender and receiver, this is easy. Pick one. Preferably pick one with a good C++ support library on both the sending and receiving platforms. UTF-8 generally has good support.
Step 2:
Sender and receiver agree on how you will signal the end of a string. Common choices are read until NULL and sending the length of the string before you send the string. Some times you want to send the length of the encoded string in bytes to make reading and buffering the encoded string easier.
Step 3:
Sender encodes the string using the agreed upon encoding into a char buffer. This is, unfortunately, messier in C++ than it should be. You may find yourself going back to step 1 and re-choosing the encoding because the encoding you chose doesn't have as good library support as you thought.
Step 4 A:
Write the buffer from step 3 and the length-determining method was selected in step 2 into the socket.
Step 4B:
Read the buffer from the socket, stopping when the terminating condition chosen in step 2 is met. If looking for an end of string marker or a count of strings, you will have to read and buffer, then convert byte by byte, reading and buffering more as required. If you sent the size of the encoded string, you can read and buffer the whole string in one shot and then let the decoder loose.
To send a C++ class (which std::wstring is an example of) you have to serialize it: convert it to the format which can be sent and received. A binary object representation (which you get by taking an address of an object) is not suitable for that purpose.
Luckily, serializing std::wstring is not much of a trouble. You just need to access it's character buffer using it's c_str() method. The only one thing you need to remeber is that you also need to tell the receiver how long the string is - so that receiver know how much to read.
I made a simple protocol for my game:
b = bool
i = int
sINT: = string whose length is INT followed by a : then the string
m = int message id.
Example:
m133s11:Hello Worldi-57989b0b1b0
This would be:
Message ID 133
String 'Hello World' length 11
int -57989
bool false
bool true
bool false
I did not know however that TCP could potentially only send PART of a message. I'm not sure exactly how I could modify this such that I can do the following:
on receive data from client:
use client's chunk parser
process data
if has partial message then try to find matching END
if no partial messages then try to read a whole message
for each complete message in queue, dispatch it
I could do this by adding B at the beginning of a message and E at the end, then parsing through for the first char to be B and last to be E.
The only problem is what if
I receive something silly in the middle that does not follow the protocol. Or, what if I was supposed to just receive something that is not a message and is just a string. So if I was somehow intended to receive the string HelloB, then I would parse this as hello and the beginning of a message, but I would never receive that message because it is not a message.
How could I modify my protocol to solve these potential issues? As much as I anticipate only ever receiving correctly formed messages, it would be a nightmare if one was poorly encoded and set everything out of whack.
Thanks
I decided to add the length at the beginning and keep track of if I'm working on a message or not:
so:
p32m133s11:Hello Worldi-57989b0b1b0
I then have 3 states, reading to find 'p', reading to find the length after 'p' or reading bytes until length bytes have been read.
What do you think?
It seems to work great.
What you are doing is pretty old-school, magnetic tape stuff. Nice.
The issue you might have is that if a part of the message is received, you cannot tell if you are partway through a token.
E.g. if you receive:
m12
Is this Message 12, or is it the first part of message 122?
If you receive:
i-12
Is this an integer -12 or is it the first part of an integer -124354?
So I think you need to change it so that the message numbers are fixed width (e.g. four digits), the string length is fixed (e.g. 6 digits) and the integer width is fixed at 10 digits.
So your example would be:
m_133s____11:Hello Worldi____-57989b0b1b0
That way if you get the first part of a message you can store it and wait for the remainder to be received before you process it.
You might also consider using control characters to separate message parts. There are ascii control codes often used for this purpose, RS, FS, GS and US. So a message could be
[RS]FieldName[US]FieldValue[RS]fieldName[US]FieldValue[GS].
You know when you have a complete message because the [GS] marks the end. You can then divide it up into fields using the [RS] as a separator, and split each into name/value using the [US].
See http://en.wikipedia.org/wiki/C0_and_C1_control_codes for a brief bit of information.
I am developing a Qt/C++ programme in QtCreator that reads and writes from/to the serial port using QextSerialPort. My programme sends commands to a Rhino Mark IV controller and must read the response of those commands (just in case they produce any response). My development and deployment platform is Windows XP Professional.
When the Mark IV sends a response to a command and my programme reads that response from the serial port buffer, the data are not properly encoded; my programme does not seem to get plain ASCII data. For example, when the Mark IV sends an ASCII "0" (decimal 48) followed by a carriage return (decimal 13), my buffer (char *) gets -80 and 13. Characters are not properly encoded, but carriage returns are indeed. I have tried using both read (char *data, qint64 maxSize) and readAll ().
I have been monitoring the serial port traffic using two monitors that interpret ASCII data and display the corresponding characters, and the data sent in both ways seem to be correctly encoded (they are actually displayed correctly). Given that QByteArray does not interpret any character encoding and that I have tried using both read (char *data, qint64 maxSize) and readAll (), I have discarded that the problem may be caused by Qt. However, I am not sure if the problem is caused by QextSerialPort, because my programme send (writes) data properly, but does not read the correct bytes.
I have also tried talking to the Mark IV controller by hand using HyperTerminal, and the communication takes place correctly, too. I set up the connection using HyperTerminal with the following parammeters:
Baud rate: 9600
Data bits: 8
Parity bits: 0
Stop bits: 1
Flow control: Hardware
My programme sets up the serial port using the same parammeters. HyperTerminal works, my programme does not.
I started using QextSerialPort 1.1 from qextserialport.sourceforge.net and then tried with the latest source code from QextSerialPort on Google Code, and the problem remains.
What is causing the wrong character encoding?
What do I have to do to solve this issue?
48 vs. -80 smells like a signed char vs. unsigned char mismatch to me. Try with explicit unsigned char* instead of char*.
Finally, I have realized that I was not configuring the serial port correctly, as suggested by Judge Maygarden. I did not find that information in the device's manual, but in the manual of a software product developed for that device.
The correct way to set up the serial port for connecting to the Mark IV controller is to set
Baud rate: 9600
Data bits: 7
Parity: even
Stop bits: 2 bits
Flow control: Hardware
However, I am still wondering why did HyperTerminal show the characters properly even with the wrong configuration.
c++
#define BUF_LEN 1024
the below code only receives one byte when its called then immediately moves on.
output = new char[BUF_LEN];
bytes_recv = recv(cli, output, BUF_LEN, 0);
output[bytes_recv] = '\0';
Any idea how to make it receive more bytes?
EDIT: the client connecting is Telnet.
The thing to remember about networking is that you will be able to read as much data as has been received. Since your code is asking for 1024 bytes and you only read 1, then only 1 byte has been received.
Since you are using a telnet client, it sounds like you have it configured in character mode. In this mode, as soon as you type a character, it will be sent.
Try to reconfigure your telnet client in line mode. In line mode, the telnet client will wait until you hit return before it sends the entire line.
On my telnet client. In order to do that, first I type ctrl-] to get to the telnet prompt and then type "mode line" to configure telnet in line mode.
Update
On further thought, this is actually a very good problem to have.
In the real world, your data can get fragmented in unexpected ways. The client may make a single send() call of N bytes but the data may not arrive in a single packet. If your code can handle byte arriving 1 by 1, then you know it will work know matter how the data arrives.
What you need to do is make sure that you accumulate your data across multiple receives. After your recv call returns, you should then append the data a buffer. Something like:
char *accumulate_buffer = new char[BUF_LEN];
size_t accumulate_buffer_len = 0;
...
bytes_recv = recv(fd,
accumulate_buffer + accumulate_buffer_len,
BUF_LEN - accumulate_buffer_len,
0);
if (bytes_recv > 0)
accumulate_buffer_len += bytes_recv;
if (can_handle_data(accumulate_buffer, accumulate_buffer_len))
{
handle_data(accumulate_buffer, accumulate_buffer_len);
accumulate_buffer_len = 0;
}
This code keeps accumulating the recv into a buffer until there is enough data to handle. Once you handle the data, you reset the length to 0 and you start accumulating afresh.
First, in this line:
output[bytes_recv] = '\0';
you need to check if bytes_recv < 0 first before you do that because you might have an error. And the way your code currently works, you'll just randomly stomp on some random piece of memory (likely the byte just before the buffer).
Secondly, the fact you are null terminating your buffer indicates that you're expecting to receive ASCII text with no embedded null characters. Never assume that, you will be wrong at the worst possible time.
Lastly stream sockets have a model that's basically a very long piece of tape with lots of letters stamped on it. There is no promise that the tape is going to be moving at any particular speed. When you do a recv call you're saying "Please give me as many letters from the tape as you have so far, up to this many.". You may get as many as you ask for, you may get only 1. No promises. It doesn't matter how the other side spit bits of the tape out, the tape is going through an extremely complex bunch of gears and you just have no idea how many letters are going to be coming by at any given time.
If you care about certain groupings of characters, you have to put things in the stream (ont the tape) saying where those units start and/or end. There are many ways of doing this. Telnet itself uses several different ones in different circumstances.
And on the receiving side, you have to look for those markers and put the sequences of characters you want to treat as a unit together yourself.
So, if you want to read a line, you have to read until you get a '\n'. If you try to read 1024 bytes at a time, you have to take into account that the '\n' might end up in the middle of your buffer and so your buffer may contain the line you want and part of the next line. It might even contain several lines. The only promise is that you won't get more characters than you asked for.
Force the sending side to send more bytes using Nagle's algorithm, then you will receive them in packages.