I've got an issue where I am trying to measure the time interval between a serial write and serial read in python. I'm using python 3.8 and a USB->RS485 adaptor on windows. Essentially, the code follows as so:
def write_packet(self, packet, flush=True):
if flush:
self.clear_buffers()
self.ser.write(packet)
self.tx_time = time.time_ns()
and this is immediately followed by:
def read_packet(self):
first_byte = True
while True:
byte = self.ser.read(1)
# Check if the read timed out:
if byte == b'':
**(notify of timeout)**
if byte == b'\x00':
**(end of packet, decode and break)**
else:
if first_byte:
self.time_rx = time.time_ns()
first_byte = False
As you can probably see, I'm trying to capture the time between just after transmitting, and receiving the first byte. After that I do something like this to get the time in ms:
time_diff_ms = (self.rx_time - self.tx_time)/1000000
My issue is the timings time_diff_ms seem to be way off. The scope image below of the RS485 signals shows it should read times of around ~1ms yet the script reads values of 6ms, 11ms, etc., almost random values.
https://i.stack.imgur.com/eJFAJ.jpg
I've also tried running the script on Linux, but not much difference. I'm also working with fairly high baud rates of 921600.
Related
I am trying to write a program in python that connects to a DGT chess board and recieves the data and prints the moves in real time as they are being made (example: ng1f3). Ive tried asking chat gpt but as usual it has been no help. Ive also looked online for similar projects but found nothing like what i am trying to do.
ive tried getting the data using the serial library. the port and the baudrate are both correct. the board sends 8 bytes of data for each move. this code runs but does not print anything when i move the pieces on the board. ive even tried contacting the manufacturer to see if the boards have any sort of protection against this but they said they couldnt help me with this and instead just sent me the document with the description of the comunication protocol (one drive link to file: https://1drv.ms/b/s!AqF5qQ2nPQ81nkBZqQAMfY93mfdJ?e=eia1mO). i am also fairly new to python and this website.
import serial
ser = serial.Serial("COM7", 9600)
while True:
data = ser.read(8)
print(data)
edit: I have since tried:
import serial
ser = serial.Serial("COM7", 9600, timeout=2)
ser.write(bytes([0x45])) # message to request serial number
test = ser.read(20)
output = test.decode('ascii', errors='replace') # convert bytes to string print(output)
A response was received, but it wasn't quite as expected.
If you feel confident that everything is connected properly, try doing this:
import serial
ser = serial.Serial("COM7", 9600, timeout=2)
ser.write(bytes([0x45])) # I think this is the message to request serial number.
test = ser.read(20) # Not sure how many bytes are returned
print(test) # this prints a byte string
print(test.hex()) # this prints the byte string as a string of hex w/out the "0x/" characters
print("The Message ID is " + str(test[0:1])) # You sent a'0x45' command you get back a'0x91' reply
print("The length of the message is " + str(test[1:3]))
print("The Serial number is " + str(test[3:8]))
Does that print anything at all?
edit: Changed the serial write to a byte type.
edit 2: It looks like the board might write something upon start up. Another thing to try is:
import serial
ser = serial.Serial("COM7", 9600, timeout=5) # timeout long enough to turn on board
test = ser.read(1000) # excess amount to read, but we don't want to miss anything
print(test)
edit 3: Now that we have verified communications with the board, we can start experimenting with trying to write a script that will read the movement of the chess pieces. Let's try this:
import serial
ser = serial.Serial("COM7", 9600, timeout=2)
DTG_REQ_UPDATE_BOARD = bytes([0x44])
while 1:
timeout = 0
ser.write(DTG_REQ_UPDATE_BOARD)
while timeout < 10:
movement = ser.read(20)
if movement != 0:
print(movement)
timeout = 10
else:
timeout += 1
continue
Turn on your board.
Start the above script.
Start moving pieces like you would if you were playing a game.
Look for things to start printing out in your console/terminal.
It's a quick and dirty way of looping (it's an infinite loop and you will have to force stop the process when you are done running your script), but it should do the job for testing purposes.
I am performing a sweep, and all works well! Except for when I try to print the current values that it's reading... all it prints is the number of characters of the SPCI command I used. Here is the code:
import pyvisa
import time
import serial
# configure the serial connections (the parameters differs on the device you are connecting to)
ser = serial.Serial()
ser.port = 'COM3'
ser.baudrate = 9600
ser.open()
ser.isOpen
###Sweep
numberofiterations = 10
stepsize = 0.05
voltage = 0
currents = []
voltages = []
ser.write(str.encode('*RST' + '\r\n'))
ser.write(str.encode(':OUTP ON'+'\r\n'))
for iterations in range(numberofiterations):
#record voltage
voltages.append(round(voltage,5))
#supply voltage
ser.write(str.encode(':SOUR:FUNC VOLT'+'\r\n'))
ser.write(str.encode(':SOUR:VOLT:MODE FIX'+'\r\n'))
ser.write(str.encode(':SOUR:VOLT:RANG 20'+'\r\n'))
ser.write(str.encode(':SOUR:VOLT:LEV '+str(voltage)+'\r\n'))
#wait
time.sleep(1)
#measure current
ser.write(str.encode(':SENS:FUNC "CURR"'+'\r\n'))
ser.write(str.encode(':SENS:CURR:PROT 10e-1'+'\r\n'))
ser.write(str.encode(':SENS:CURR:RANG 10e-1'+'\r\n'))
ser.write(str.encode(':SENS:CURR:RANG:AUTO ON'+'\r\n'))
current = ser.write(str.encode(':READ?'+'\r\n')) #measure current
currents.append(current) #record current
#increase voltage
voltage += stepsize
#Print out values
for current in range(len(currents)):
print(currents[current])
for voltage in range(len(voltages)):
print(voltages[voltage])
Specifically, this is the line that's messing up my code:
current = ser.write(str.encode(':READ?'+'\r\n')) #measure current
Why is it not measuring the current on the computer? The Keithley SourceMeter is measuring it properly as the currents are showing on its screen... I just can't get these values onto the computer. What is wrong here?
Thank you!
ser.write writes bytes to the serial port, and simply reports how many bytes were written. You'll need to read the response from the instrument using ser.read (which reads one byte) or some helper function (your own or library-provided), which can understand the response format of an SCPI command.
According to this reference, it looks like the SCPI response is delimited with a newline, so you should be able to write a command using ser.write and then call ser.readline() to read a whole line of instrument output, up to a newline character. Of course, you'll get a response back in the SCPI format, so you'll need to parse it to obtain your circuit observations.
Note that if you get in an odd state where the instrument is not writing output, but you're in a readline call waiting for a response, your program may lock up. Setting a timeout for the serial port will be helpful. I'm not familiar enough with the protocol to know when this might occur.
I am trying to read data out of a Microcontroller, configuration:19200,8 data bits, even parity and 1 stop bit. The controller is transmitting data via rs485 and I am reading the data via rs485 to rs232 converter. The data is transmitted every 4ms to a write and requires a reflection every 4ms on the respective bytes as displayed in the code. I have written a similar code on a microcontroller and everything works fine but I need to implement the same on Python. While reading from python I notice a few bytes are lost while reading through ser.read(). I also have a logic analyzer converted to see what data I am missing. I tried multiple solutions available on similar posts but it hasn't worked for me as the solutions require me to increase my timeout.
e.g. Losing data in received serial string
import serial
import time
ser = serial.Serial()
ser.port='COM1' # This is COM1, use 1 for COM2 etc
ser.baudrate=19200
ser.bytesize=serial.EIGHTBITS
ser.parity=serial.PARITY_EVEN
ser.stopbits=serial.STOPBITS_ONE
ser.xonxoff=0
ser.rtscts=0
ser.timeout=0
while True:
rxbuf = ser.read(1)
print(rxbuf)
if(rxbuf == b'\x05' or rxbuf == b'\x06' or rxbuf == b'\x15'):
time.sleep(0.004)
ser.write(b'\x05')
elif (rxbuf == b'\x02' or rxbuf == b'\x01'):
rxbuf_len = ser.read(16)
print(rxbuf_len)
output:
b'\x05\'
b'\x05\'
b'\x05\'
b'\x05\'
b'\xF5\'
b'\x28\'
b'\x76\'
b'\x02\'
b'\xf2\x97\x00\x00\x8er\x9a\xc0\x14\xff\xff|:F\x18\x00'
This is incorrect there is a 0x02 before 0xF5 that has not been read apart from many other bytes.
I'm relatively new to programming, so bear with me. I'm trying to communicate with the measurement device METEX M-4650CR https://sigrok.org/wiki/Voltcraft_M-4650CR and I'm using a windows 7 64bit OS. I simply want to read out the data the device measures to my python procedure and display it and calculate with it.
I found in the manual http://elektron.pol.lublin.pl/elekp/labor_instr/METEX_M-4650CR_Manual.pdf (page 25ff), that it works with a baudrate of 1200, a bytesize of 7 (with ASCII coding) and 2 stopbits.
Furthermore, it can be requested to send data to the computer by simply giving it the command "M". It then returns 14 bytes to the computer. Without anything to measured connected to it, it should return something like 'DC 00.0000V CR'. CR is the terminator here (I hope that is the right name).
Here is my code:
import pyserial
import time
ser = serial.Serial(port='COM5', baudrate=1200,
bytesize=7, stopbits=2, timeout=1,
rtscts=False, dsrdtr=True)
time.sleep(1)
ser.write("M")
time.sleep(1)
bytestoread = ser.inWaiting()
print bytestoread
output = ''
output += ser.read(1000)
print 'output:' + str(output)
time.sleep(1)
ser.close()
My problem is, that I cannot read out the data properly with pyserial. I send the command "M" to the METEX and in the display it says 'send' for a short moment, so I guess my write command works fine. But after that (it should have send the data), all I get when from ser.inWaitung is '0L' or '1L' and the ser.read command gives nothing at all.
I don't think it is a problem of the hardware, because with another programme, called 'serialwatcher', I'm able read out the data correctly. It gives exactly the characters described in the manual.
I also tried the following while loop, having the problem, that most of the time inWaiting == 0, such that it never initialises the loop.
while ser.inWaiting() > 0:
output += ser.read(1)
if output != '':
output = outpus.rstrip()
print output
So, how can I read out the data correctly, that were send to the serial port? Thanks in advance.
Unfortunately I cannot test your code because I have no serial device with me, but you could try the following:
You could set a flag, e.g. alive when you are expecting data and simply try to read something. This worked for me when I was trying to receive data from a really old spectrometer.
while alive: #loop
text = ser.read(1) #try to read one line
if text: #if there is data
n = ser.inWaiting() #look if there is more to read
if n: #if so
text = text + ser.read(n) #get all of it
A more sophisticated example can be found here wxTerminal - Pyserial example You could also simply try to modify this brilliant code for your purpose and see if you are more successful.
I'm new to Python and want to read my smart meters P1 port using a Raspberry Pi and Python. Problem: the input looks like some component is drunk.
I'm sure it's pretty simple to fix, but after several hours of searching and trying, had to seek help.
When reading the P1 port with CU etc. everything is fine so the hardware etc. is OK. Using a serial to USB converter from dx.com (this one)
Command and (part of) the output: cu -l /dev/ttyUSB0 -s 9600 --parity=none
0-0:96.1.1(205A414246303031363631323463949271)
1-0:1.8.1(03118.000*kWh)
However, when trying to read it from Python, the input becomes gibberish (but at least sort of consistant):
0-0:96.±.±(²05A´±´²´630303±39363±3²3´639·3±3²©
±-0:±.¸.±(03±±¸.000ªë×è©
How to fix this? The code I'm using is:
import serial
ser = serial.Serial()
ser.baudrate = 9600
ser.bytesize=serial.SEVENBITS
ser.parity=serial.PARITY_EVEN
ser.stopbits=serial.STOPBITS_ONE
ser.xonxoff=0
ser.rtscts=0
ser.timeout=20
ser.port="/dev/ttyUSB0"
ser.close()
ser.open()
print ("Waiting for P1 output on " + ser.portstr)
counter=0
#read 20 lines
while counter < 20:
print ser.readline()
counter=counter+1
try:
ser.close()
print ("Closed serial port.")
except:
sys.exit ("Couldn't close serial port.")
Have already tried messing with baudrate etc. but that doesn't make any difference.
I'm not very familiar with the serial module, but I noticed that your cu command assumes there is no parity bit (--parity=none), but your python script assumes there is an even parity bit (ser.parity=serial.PARITY_EVEN). I would try
ser.parity=serial.PARITY_NONE
And if there's no parity bit, you'll also probably want
ser.bytesize=serial.EIGHTBITS
UPDATE: found a workaround by replacing the naughty characters.
This may work for others with the same problem, but I dont know if the bad characters are exactly the same. So the replacement part may need some work to make it work for others.
It's not exactly a solution as the incoming telegram is still messed up, but the following code will work around that. My telegram is completely clean now.
Relevant part of the code I'm using now:
#Define 2 variables
P1_numbers = {'±':'1', '²':'2', '´':'4', '·':'7', '¸':'8'}
P1_rest = {'¯':'/', 'ª':'*', '©':')', 'Æ':'F', 'ë':'k', '×':'W', 'è':'h', 'í':'m'}
# Define function to read the telegram. Calls a function to clean it.
def P1_read(stack):
counter = 0
while counter < TelegramLength:
stack.append(P1_clean(ser.readline()))
counter=counter+1
return stack
# Define function to clean up P1 output
def P1_clean(line):
for i, j in P1_numbers.iteritems():
line = line.replace(i, j)
for i, j in P1_rest.iteritems():
line = line.replace(i, j)
return line
I quess you have a smart meter with P1 protocol: DSMR 3.0?
Then these are the correct serial port settings, which you already had:
serialport = serial.Serial( # Configure Serial communication port
baudrate = 9600,
timeout = 11,
bytesize = serial.SEVENBITS,
parity = serial.PARITY_EVEN,
stopbits = serial.STOPBITS_ONE )
Probably some encoding or interpretation of the data is going wrong at your side. Here is an other method the read the smart meter:
To make the readout of the p1 protocol as easy as possible I'd suggest to use TextIOWrapper, this way you can read the serial port with the readline method. The "!" always ends the P1 telegram, so that can be used to detect the end of the message. when a full telegram has been received, the telegram can be processed. Example:
import io
p1port = io.TextIOWrapper(io.BufferedReader(serialport, buffer_size=1), newline='\n', encoding='ascii')
P1Message = []
while True:
try:
rawline = self.p1port.readline()
except UnicodeDecodeError:
print "Encode error on readline"
if '!' in rawline:
# Process your P1Message here
P1Message = [] # Clear message, wait for new one
else:
P1Message.append(rawline)
The OP is looooong gone, but the problem is of sufficiently general interest, so here's a fresh answer. User #Brionius is right: A look at the bit patterns involved shows that it's definitely a parity problem. Here's how to inspect the bit pattern of the characters "1" and "±":
>>> "{0:b}".format(ord("1"))
'110001'
>>> "{0:b}".format(ord("±"))
'10110001'
Get it? The characters are getting corrupted by having their high (8th) bit turned on. Or you can see this by setting the high bit of ascii "1":
>>> chr(ord("1") | 0b10000000)
'±'
Now, "1", "2" and "4" have three bits set (odd parity), and are corrupted. "0", "3", "5", etc. have even parity (2 or 4 bits set), and are preserved. So the communication channel is using even parity, which is not decoded properly at the receiving end.
I had the same problem, also in the context of the P1 Smart Meter port, and it took me quite a while to find it.
'cu' was displaying the data correctly, but Python wasn't (nor were some other programs). Apparently the parity bit is somehow not handled correctly. The following solves this problem:
p1_raw = ser.readline()
print 'raw:', p1_raw
decoded = ''.join(chr(ord(ch) & 0x7f) for ch in p1_raw)
print 'decoded:', decoded
I still find it strange that this is happening, because this was actually happening when I was trying to read the output of a Smart Meter for the second time. I already had a script successfully monitoring another Smart Meter at a different house for a couple of years and I never ran into this problem.
Perhaps there's a small difference in the USB-Serial adapters that causes this?!?