Problems with sending commands over pySerial - python

I'm trying to talk to a home made card over a serial port, and is therefor using pySerial. In Hyperterminal, everything works fine. I can write:
$ audio on
and the audio is enabled, but if I use
ser = serial.Serial("COM1", 38400)
ser.write("audio on\r\n")
nothing happens. I can read incoming data however, so it's not something wrong with the communication. I doesn't help if I change \r\n to just \n or \r either.
EDIT: Sometime I actually get the feedback: No such command when sending the exact same command as works from HyperTerminal. The setup is also the exact same as in HyperTerminal.
Solved:
To make it work, I had to send one and one character, and ending the transmission with \r.

Get an oscilloscope (you've got one, right?) and watch the serial line. Send one character per second through it and see what comes up on the scope (set it to trigger on the start bit). Serial port bits are in the order: start, LSB .. MSB, parity, stop.
See if there are characters that don't get through, or if there's a pattern. Another possibility is that everything is actually making it out the port, and your board is dropping characters.

Triple check that the baud rate of the device is indeed 38400
Triple check parity, stop bits, etc
Be aware of signal degradation for serial transmissions over long distances (probably not your issue)
If all the above checkout try putting the string into a byte array and sending that through the write command. Just a guess.

Sending characters via Hyperterminal deliver characters at the speed you type them. Sending characters through pyserial they are delivered as a continuous stream. The receiver (especially at high baud rates) could drop them, depending on the nature of the receiver.
Also, when you send commands to an interpreter, you only need the \r terminator (without the \n), (this is all that is sent by hyperterm, normally). HOWEVER, if you are just displaying the values, you may need the \n to generate the new line.

Related

PySerail Readline enters infinite loop

I am working on a BP sensor serial and am using serial readline() to read the output provided by the port, when I initiate the port as serial.Serial('/dev/ttyUSB2',115200,timeout=1) and print the date read using readline() in a loop as follows
for I in range(1,10)
print('integration')
print(ser.readline())
the readline() seems to into infinite loop as the program prints integration once and then goes infinite however when I try the same with timeout=0 the program prints integration 9 times with blank space after each line. Need to know what I am doing wrong as the same code seems to work fine with other sensors like laser distance sensor etc.
Already referred to Reference1 Reference2 and a few more.
ser.readline() expects to find a \n character so it keeps reading data until it finds it.
You need to make sure your sensor is sending this default EOL character. If that is not the case, you can specify a different character.
There used to be a simple way to do that but apparently, it's not working anymore. The workaround is to use TextIOWrapper, see here. Pay attention to the first answer to that question to make sure you change the default buffer size.

Getting ESC characters and non-printables from UDP socket / buffer

I've got a custom script listening on port 161 for UDP packets to come in.
It listens fine, receives the string fine - and when I send a message from a test script (on another box), it displays fine in a log, etc.
I'm gathering the UDP data as follows:
data, addr = sock.recvfrom(1024)
data contains the string with the information I need.
When performing a tcpdump on the interface that the data is coming in, it looks normal, such as:
.1.3.6.1.4.1.3375.2.1.1.2.12.6 .1.3.6.1.4.1.3375.2.1.1.2.12.6 public "THIS IS THE TRAP" .1.3.6.1.4.1.3375.2.1.1.2.12.6 .1.3.6.1.4.1.3375.2.1.1.2.12.6
When I take that incoming data (in python) and print it, or output it to a file, I get a bunch of ESC sequences, or just otherwise unprintable data in the log file.
Everything inside of the Quote is preserved.
I've been able to strip out the ESC sequences and store the 'good stuff' inside of the quotes, but I'm losing my OID's. It's almost as if python thinks those ascii characters are something else.
I did notice that when trying to save the garbled data, if I change encoding to Latin-1 -- it becomes somewhat readable...but still there's some garbled characters in there.
I've tried to duplicate this matter here at home - but no matter what text I feed through my test.py to the listener on port 161, it comes out just fine and readable. This was implemented in a test environment at my work. PS I am not a programmer, but a network guy.
If it matters, the device sending SNMP traps out is an F5 LTM.
I know this is a pretty general question, so I appreciate anyone just taking the time to read my question in its entirety and spend a few minutes thinking about it.

python sending TCP/IP data instantly

I am currently sending data over TCP/IP in myserver using something like this
for str in lst:
data = str + "\n"
self._conn.sendall(data)
Now suppose my list has the following two string in it
1-This is statement 1 in list
2-This is statement 2 in list
My client is receiving half of line 2 like this.
This is statement 1 in list
This is
I would like to send line1 and then line 2 in the list individually. I understand that TCP/IP works this way in which it will send the entire data that is available to send. I think I could put a delay in after calling self._conn.sendall(data) but i wanted to know what other options I have. I cannot make changes to the receiver of the data and I can only make changes to the sender. So far my only option is adding a delay after each send.
TCP works with streams of data, not individual packets. It's like reading data from a file. The sender puts data in its send buffer, and TCP can decide for itself when to send it. The timing of the arrival at the receiving application depends on when the data was sent and on (often unpredictable) network conditions.
TCP deliveries can be made more predicable if you use the TCP_NODELAY flag in your socket (something like socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1). This would cause TCP to send out data as soon as it arrives in its buffer. But still, there would be no guarantees as to arrival times. This is why any time based solution would break, at least in some cases.
The solution is to divide the data stream into chunks yourself. There are several ways of doing that. Here are a few:
Use fixed length messages - if all messages have a fixed length, the receiver just has to recv() the right number of bytes, process the message, then wait for the same number of bytes.
Send the length of the message before each message. If you want to send the string "blah", encode it as "0004blah" or something similar. The receiver will (always) read the first four bytes (which are 0004) to figure out the number of remaining bytes to read. It will then read the required number of bytes, process the message, and then wait for the next one. It's a robust solution that's also easy to implement.
Use a delimiter. Lines in text files are divided by newline characters (\n). Similarly, you can add a special delimiter byte (or bytes) between messages. For example, you can define that messages always end with a dollar sign ($). Then all the receiver has to do is read from the socket byte by byte until it receives a dollar sign. Of course if you take this approach, you have to make sure that the body of the messages doesn't contain the delimiter character.
TCP is based on a stream, not individual messages. So you need to parse the end point of each message yourself. One idea in your case would be to read until you get a newline, then process the line. Note that you might read this:
This is statement 1 in list
This is
Then you need to check to see if you got a newline, process the line, then leave your buffer ready to receive the rest, like this:
This is
TCP has a local buffer that is not sent until it's full. You can force flushing of the local buffer so it's sent after every message, but when the other party receives these packages they get stored in another local buffer and your separation may disappear. TCP is a stream, use it as a stream. You have to use separator characters and when the packets are received you have to separate the messages manually. If you want more control, use UDP packets.

Configure serial port for long commands

Assume you try to interact with a shell via serial port. What happens when you hit the end of the line is that a <space><carriage return> gets inserted. This is already uncomfortable when using screen or minicom because it usually just continues writing on the same line (the linefeed is missing), but it results in buggy code when you need to parse the output stream. I am wondering how I can configure my serial connection to simply do nothing at the end of the line.
Example:
$ python -i -c "import serial; s=serial.Serial('/dev/ttyUSB3', 115200, timeout=1.5)"
>>> s.write("echo \"123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123123213\""); print s.readall().encode('string-escape')
211
echo "12312312312312312312312312312312312312312312312312312 \r31231231231231231231231231231231231231231231231231231231231231231231231231231231 \r23123123123123123123123123123123123123123123123123123123123123123123213"
Solving this by changing the parser is not an option, because the parsing is done by a third party library in my case. Also just setting the line length to a very high number might work but is not what I like to do. I'd rather have control over the port's behaviour when the line is full.
It might be that the problem has nothing to do with pyserial. If you send data with the write command to a serial port, it just sends the data to the serial port regardless of what you send (even binary data). The same applies to read.
So, the extra space and newline are inserted by the shell in the other end of the connection. There is probably no reasonable way around this problem without configuring the other end. For example, in Linux you might want to try setterm -linewrap off or just simply change the terminal width to be large enough wtih stty.
I think this question will receive more complete answers in https://unix.stackexchange.com/ if your remote terminal is a Unix/Linux.

pyserial 2.7, python 3.3, sending carriage returns

I have been attempting to have a Raspberry Pi interface with an embedded circuit using the UART interface. The UART interface on the Pi is in working order and I can receive messages from the circuit, though I am having trouble sending messages to the circuit.
I am using Python 3.3 with Pyserial 2.7. Sample code is available, though it uses Pyserial 2.6. When used with older versions of Python (<2.6), ser.write() accepts strings, but now it only accepts bytearrays.
The problem I am having is in sending carriage returns... The old code supposedly functioned with just:
ser.write("L1\r")
but now I am using the following:
ser.write(bytearray("L1\r", "ascii"))
The circuit does not respond to the command. I think the resultant message is sending \r as two individual characters rather than a carriage return. How would I make sure my code is outputting commands appended with carriage returns?
Notes: I can reasonably expect that the circuit is working well and that the Pi's UART interface is functional. The circuit is an Atlas Scientific Dissolved Oxygen Circuit. The circuit's documentation demands that commands be written in the form l1<cr> or L1<CR>.
Relevant links:
Old sample code (https://www.atlas-scientific.com/_files/code/pi_sample_code.pdf)
Documentation describing write method (http://pyserial.sourceforge.net/pyserial_api.html#classes)
Thanks in advance!
EDIT: Netch makes a strong point: ser.write(b'L1\r') works and is much cleaner. Both methods, however, ARE sending a correct '\r' sequence.. The problem is that the circuit still does not regard L1\r as a valid command. At this point, I think my issue may be some property of my serial port.
My port is declared as such:
ser = serial.Serial(
port = '/dev/ttyAMA0',
baudrate = 38400,
bytesize = serial.EIGHTBITS,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
timeout = 1
)
This port declaration is done with accordance to the circuit's datasheet (I can only post two links unfortunately :( Google brings it up easily).
[EDIT] For future viewers, I just want to point out that for the newer EZO version of the circuit, the command is "L,1" or really "L,1\r"
[/EDIT]
This is a known issue with the circuit. The first read after power up will fail. instead of prepending a \r with every read, just send a bogus command with \r included and then reset input buffer
ser.write('bogus\r'.encode()) # EDIT: had to add .encode() to get it to work. see https://stackoverflow.com/questions/22275079/pyserial-write-wont-take-my-string
ser.flushInput() # or for pyserial 3+ ser.reset_input_buffer()
#now do your thing
EDIT: had to add .encode() to get it to work. see pySerial write() won't take my string
After powering up the EZO™ class circuit when it is in UART mode the
first command sent to it will comeback as an error. This is because
the UART buffer will show that it has received a character during
power up. Simply send a blank character to the pH circuit after it is
powered up, this will clear the buffer.
I have found a solution!! Unfortunately, I cannot explain how it works. Perhaps anyone reading this could elaborate on it and give a proper explanation!
The circuit's documentation demands commands be in the form CMD<CR>. Indeed, sample code provided by the manufacturer sends the L1 command through pyserial as ser.write("L1\r").
Now that ser.write() demands bytes however, I have found that ser.write(b'L1\r') does not work.. The command is received though it is somehow unknown to the circuit.
After toying around for a while, I have discovered that ser.write(b'\rL1\r') works! The debugging led flashes red once before processing the command. It seems like I just need to send a 'dummy command' to get the circuit's attention!
I am not sure if this is the fault of pyserial, the circuit, or my own ignorance. If anyone can shed some light on this, it would be much appreciated! :D
I have linked here the circuits documentation in case anyone is interested. https://www.atlas-scientific.com/_files/_datasheets/_circuit/DO_Circuit_5.0.pdf

Categories