why does a print() affect how com1 is read? - python

I'm writing an interface for an old piece of electronic equipment that uses RS232 serial. I've run into a problem that I can't seem to solve. Here is the original code
def readMyPort():
global readPort
while True: #always loop
if readPort: # only do something of the port is active
dataString = b''
while (mySerialPort.inWaiting()>0):
data = mySerialPort.read(1)
if (data == b'') or (data == b'\r') or (data == b'\n'):
if (dataString != b''):
myOutput.insert('1.0', (dataString + b'\n').decode())
dataString = b''
else:
dataString += data
The problem I face is that the instrument sends a string of 12 characters in response to a command, and I only seem to catch the last 4, and no, there are no '', '\r', or '\n' in the string. In an effort to troubleshoot I added a print(), as shown below. Magically I started to get all the data.
def readMyPort():
global readPort
while True: #always loop
if readPort: # only do something of the port is active
dataString = b''
while (mySerialPort.inWaiting()>0):
data = mySerialPort.read(1)
print(data) #<-------- the added line
if (data == b'') or (data == b'\r') or (data == b'\n'):
if (dataString != b''):
myOutput.insert('1.0', (dataString + b'\n').decode())
dataString = b''
else:
dataString += data
Now I don't want to have all that printing going on normally. I tried just adding a print('') and that works as well, but I still have all those \n getting printed. I tried print('', end = '\r') but that didn't work. Does anyone have an idea why? I don't think it is a speed issue. I'm only running 9600 baud. FYI: python 3.2 on a Win32 machine. This routine is launched in it's own thread.

The operation print(data) requires some time (relatively large) to print something to the console. So by adding the line print(data) you just add some delay inside your loop. You may verify this theory by substituting print(data) for time.sleep(0.1 or whatever small value) and checking that the problem is hopefully gone.
I think that the delay helps because without it mySerialPort.inWaiting() may becomes 0 sometimes (receive buffer is empty) before the transaction is actually completed. Your instrument can't simply output data that fast. In your final version of the code you may add time.sleep() instead of print(data).

Following the answer of Konstantin, it appears that the serial port needs some time to adjust the buffer and counters, so adding a small delay in the loop solved my problem. 1 ms is not enough but 10 ms is. The final code is:
def readMyPort():
global readPort
while True: #always loop
if readPort: # only do something if the port is active
dataString = b''
while (mySerialPort.inWaiting()>0): #if something is in the buffer
data = mySerialPort.read(1)
if (data == b'') or (data == b'\r') or (data == b'\n'):
if (dataString != b''): #don't output empty strings
myOutput.insert('1.0', (dataString + b'\n').decode())
dataString = b''
else:
dataString += data
time.sleep(0.01) #slow the loop down a bit
I do continue to wonder if there is a more elegant solution.

Related

start reading incoming data from a serial port, using pyserial, when a specific character appears

I'm trying to read incoming data from a weight scale (Lexus Matrix One). I want the code to start reading the 8 characters after = appears.
The problem is that sometimes the code does that, and other times it starts reading the data at the middle of the measurement sent by the scale, making it impossible to read properly. I'm using the pyserial module on python 3 on windows.
import serial
ser=serial.Serial("COM4", baudrate=9600)
a=0
while a<10:
b=ser.read(8)
a=a+1
print(b)
the expected result is: b'= 0004.0'
but sometimes I get: b'4.0= 000'
I think we would need a little more information on the format of data coming from your weight scale to provide a complete answer. But your current code only reads the first 80 bytes from the stream, 8 bytes at a time.
If you want to read the next 8 bytes following any equals sign, you could try something like this:
import serial
ser = serial.Serial("COM4", baudrate=9600)
readings = []
done = False
while not done:
current_char = ser.read()
# check for equals sign
if current_char == b'=':
reading = ser.read(8)
readings.append(reading)
# this part will depend on your specific needs.
# in this example, we stop after 10 readings
# check for stopping condition and set done = True
if len(readings) >= 10:
done = True

Python 2.7 simple packet sniffer

I wrote simple packet sniffer in Python. I need to receive packets non-stop and send one packet every 10 seconds. I tried this:
current = time.time()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("xx.xx.xx.xx",xxxx))
time.sleep(0.5)
while True:
msg = str(s.recv(4096))
time.sleep(0.010)
print msg
if current==current+10:
s.send("<myPacket/>")
current = time.time()
but it doesn't work good. Anyone have better idea?
Your time handling is bad, use this instead:
While True:
...
time.sleep(10)
Your code doesn't work because:
'current' can never be equal to itself+10.
Also note that time.time() returns a float value e.g: 1440185304.78
which is very accurate to that exact moment, you should never assume you can find that exact float +10 back.
Using a larger/smaller statement is better in this case since the exact value might have passed while your loop is running whatever is in it, e.g:
t = time.time()
while True:
while time.time() < t + 10:
time.sleep(0.1)
t = time.time()
print ("hi")

How to quickly read single byte serial data using Python

I am reading serial data in Python using the following code:
port = "COM11"
baud = 460800
timeout=1
ser = serial.Serial()
ser.port = port
ser.baudrate = baud
ser.timeout = timeout
while 1:
# Read from serial port, blocking
data =ser.read(1)
print data
# some further processing of data
I am sending data at very fast rate but when I use this code I am getting data at a very slow rate, maybe around 2 - 3 data per second. This is too slow because I want to do real time plotting.
So, instead of above code I tried:
while 1:
# Read from serial port, blocking
data =ser.read(1)
data1=(data)
# If there is more than 1 byte, read the rest
n = ser.inWaiting()
data1 = (data1 + ser.read(n))
print data1
Now the speed at which data is updated is the same but instead of a single byte I am checking a number of bytes in input queue and reading them. I am receiving around 3850 bytes per loop so this one appears much faster to me but in fact it is almost the same, the only change is that I am not reading a greater number of bytes.
I want to read a single byte and check for the real time it was received. To do so I cannot use second method where I use ser.inWaiting(). How can I read single byte data faster than using the approaches above?
Here's some test code I wrote for a project that you can try different baud settings with. Basically it sends out some data on Tx (which could be connected directly to Rx) and expects the data to be echoed back. It then compares the returned data with the sent data and lets you know if/when errors occur. Note that if there are no errors then the output will remain blank and at the end of test it will print "0 Comm Errors".
import serial, time
test_data = "hello this is so freakin cool!!!" + '\r' #Must always be terminated with '\r'
echo_timeout = 1 #the time allotted to read back the test_data string in seconds
cycleNum = 0
errors = 0
try:
ser = serial.Serial(port="COM1", baudrate=115200, timeout=1)
ser.flush()
print "starting test"
for x in xrange(100):
cycleNum += 1
d = ser.write(test_data)
ret_char = returned = ''
start_time = time.time()
while (ret_char <> '\r') and (time.time() - start_time < echo_timeout):
ret_char = ser.read(1)
returned += ret_char
if not returned == test_data:
errors += 1
print "Cycle: %d Sent: %s Received: %s" % (cycleNum, repr(test_data), repr(returned) )
except Exception as e:
print 'Python Error:', e
finally:
if 'ser' in locals():
print "%d Comm Errors" % errors
if ser.isOpen():
ser.close()
print "Port Was Successfully Closed"
else:
print "Port Already Closed"
else:
print "Serial Variable Was Never Initialized"

How does paramiko Channel.recv() exactly work?

I'm having a hard time understanding how the recv() function works.
http://docs.paramiko.org/en/1.13/api/channel.html#paramiko.channel.Channel.recv
I understand this is receiving a chunk a data each time you call the function, but can someone elaborate on the structure or size of this data? Lets say I send a command date, I notice:
1st read gets: "date"
2nd read gets: actual response (Mon Jun 9 12:04:17 CDT 2014)
3rd read gets: prompt
But how does this handle debugging messages that appear randomly on the terminal?
Does the previous pattern hold true as long as the actual response is less than maximum bytes (nbytes)?
What happens if it exceeds nbytes?
As per request, I've added a snippet of the code below:
while reads<maxReads:
resp = self.__chan.recv(maxBytes)
print resp
self.__buffer += resp
if resp.endswith('$ ') or resp.endswith('# '):
break
reads += 1
Channel recv() corresponds to a socket.recv(), it does not have any specific structure or size, it just reads whatever data was sent from the remote server, not exceeding maxBytes.
You commonly use recv() in a loop until you get a piece of data that you are waiting for:
def _wait_for_data(self, options, verbose=False):
chan = self.chan
data = ""
while True:
x = chan.recv(1024)
if len(x) == 0:
self.log("*** Connection terminated\r")
sys.exit(3)
data += x
if verbose:
sys.stdout.write(x)
sys.stdout.flush()
for i in range(len(options)):
if re.search(options[i], data):
return i
return -1

check the byte length before sending back to severs in socket programming in python

My client sends a message to server. The server collects the message 16 bytes each time and when it receives anything less than 16 bytes, it returns the message (after concatenation) back to the client.
Here is the code from server.py:
sock=socket.socket(socket.AF_INET,socket.SOCK_STREAM,socket.IPPROTO_IP)
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sock.bind(address)
sock.listen(1)
try:
while True:
conn,add=sock.accept()
addr=(conn,add)
print add
try:
data = ''
while True:
recvdata=conn.recv(16)
print recvdata
data+=recvdata
if len(recvdata)<16:
print >>log_buffer,"sending data"
conn.sendall(data)
break
finally:
conn.close()
except KeyboardInterrupt:
sock.close()
The code works well for messages above 16 and less than 16 byte length, when it is exactly 16 or mutliples of 16, it is not sending back to client. how do I fix for this?
`
you don't say anything about what it should do at exactly a multiple of 16.
The code you posted will not send it back though.
Change < to <=.
< means less than.
<= means less than or equal to (multiple of).
Code:
if len(recvdata)<=16:
print >>log_buffer,"sending data"
conn.sendall(data)
break
Edit:
If you are just getting the message and returning whole bytes you can do the following:
while True:
conn,add=sock.accept()
addr=(conn,add)
print add
try:
recvdata=conn.recv(2048) #however much you need
wholebytes = len(recvdata) / 16 # get the number of whole bytes
# loop to send messages of length 16 with whole bytes
for i in range(wholebytes):
data = recvdata[i*16:(i+1)*16]
print >>log_buffer,"sending data"
conn.sendall(data)
break

Categories