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.
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.
This is my function. i am trying to send the word sensors to the COM Port and then get back the result.
def serialportcommunication():
ser = serial.Serial(
port='COM5',
baudrate=115200
)
print('Writing SENSORS command to COM7')
ser.write(b"sensors")
time.sleep(4)
print('Reading 10 bytes from serial buffer')
read_val = ser.read(size=10)
print(read_val)
print('Done printing output')
Output I get of the code above:
Writing SENSORS command to COM7
Reading 10 bytes from serial buffer
b'sensors\r\n '
Done printing output
If I execute the command "sensors" to the COM Port using a Terminal Program like Putty, I get a wall of text from my target device (the sample output is shown below, I had to white out most of the output though).
This text I am reading back, I want to read it in Python using teh command above of ser.read(size = ??), but I don't read anything back.
How to read it back?
MY SCREENSHOT FROM USING PUTTY
Resolved in comments:
Do you have to hit Enter when typing the command manually? Then you need to do the same here - ser.write(b"sensors\r") (or maybe \n, or \r\n). – jasonharper
Yes, when i enter the command in Putty, I to hit enter. Thank you thank you. I added the '\n in the end and it works now!!!! – Neil Dey
I am not a Python programmer but am rather electronic circuit designer, however this time I must process some raw data sent by a microcontroller via RS232 port towards Python script (which is called by PHP script).
I've spent quite a few hours trying to determine the best ways of reading raw bytes from serial (RS232) port using Python and I did get the results - but I would like if someone could clarify certain inconsistencies I noticed during researching and here they are:
1:
I can see a lot of people who asked similar question had been asked whether they are using serial or pySerial module and how did they install the serial library. I can only say I don't really know which module I am using as the module worked out-of-the-box. Somewhere I read serial and pySerial is the same thing but I cannot find if that is true. All I know is I am using Python 2.7.9 with Raspbian OS.
2:
I've read there are read() and readline() methods for reading from the serial port but in the pySerial API docs there is no mention of the readline() method. Futhermore, I discovered the 'number of bytes to read' argument can be passed to readline() method as well as to the read() method (and works the same way, limiting the number of bytes to be read) but I cannot find that to be documented.
3:
When searching for how to determine if all of the data from the RS232 buffer has been read I have here found the following code:
read_byte = ser.read()
while read_byte is not None:
read_byte = ser.read()
print '%x' % ord(read_byte)
but that results with the:
Traceback (most recent call last):
File "./testread.py", line 53, in <module>
read_all()
File "./testread.py", line 32, in read_all
print '%x' % ord(read_byte)
TypeError: ord() expected a character, but string of length 0 found
upon reading the last byte from the buffer and I was able to detect the empty buffer only with the following code:
while True:
c = rs232.read()
if len(c) == 0:
break
print int(c.encode("hex"), 16), " ",
so I am not sure if the code that didn't work for me is for some serial library that is other than mine. My code for openinig port is BTW:
rs232 = serial.Serial(
port = '/dev/ttyUSB0',
baudrate = 2400,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
bytesize = serial.EIGHTBITS,
timeout = 1
)
4:
The data I am receiving from µC is in the format:
0x16 0x02 0x0b 0xc9 ... 0x0d 0x0a
That is some raw bytes + \r\n. Since 'raw bytes' can contain 0x00, can someone confirm that is not a problem regarding reading the bytes into the Python string variable? As I understand that should work well but am not 100% sure.
PySerial works for me although haven't used it on a Pi.
3: Read() returns a string - this will be zero length if no data is read, so your later version is correct. As a string is not a character, you should use e.g. ord(read_byte[0]) to print the number corresponding to the first character (if the length of the string >0)
Your function:
while True:
c = rs232.read()
if len(c) == 0:
break
print int(c.encode("hex"), 16), " ",
Needs something adding to accumulate the data read, otherwise it is thrown away
rcvd = ""
while True:
c = rs232.read()
if len(c) == 0:
break
rcvd += c
for ch in c:
print ord(ch), " ",
4:
Yes you can receive and put nul (0x00) bytes in a string. For example:
a="\x00"
print len(a)
will print length 1
I have my Arduino connected to an OLIMEX-SHIELD-EKG-EMG, and there is this Arduino example for EKG capture and interface to Electric Guru for OLIMEXINO-328/Arduino boards.
I ran the Arduino software and then I made sure it's working using the Guru and it's all fine.
I tried to read the data from the serial port using Python and saving it to a text file, so I can use it for other stuff (Windows commands in my case). When I open the file the data is all garbage.
So what did I do wrong and how can I fix it?
Thank you all in advance.
This is my code:
import serial
from time import sleep
f = open("data2.txt", "w")
port = "\\.\COM4"
ser = serial.Serial(port, 38400, timeout=0)
while True:
data = ser.read(9999)
if len(data) > 0:
print ('Got:',data)
sleep(0.5)
print ('not blocked')
f.write( str(data) + "\n\n" )
f.close()
ser.close()
And this the data I get:
b'\x05\r\x1c\x1e\x10K\x12\x8a\x01\x0e\x14&\x1b\x1bW\x12\x80\x00\x7f\x11 \rZC:\x82\x12_.\x1a?\x10^\x12\x8a\x1a__\\x01:Q\x12\x8a\x15\x01\x1a\x1f\x1c6L\x1a\x82\x00t\x1e\x0c\x18\x19s:\x8a\x05]-\x01\x0e[G\x12\x8a\x15_\x0b9\x17>R\x12\x8a\x08__\x7f\x08:S\x1a\x82\x01\r\x18\x1d4\x10j:\x8a\n_0\x06\x10\x12Y\x10\x8a\nRH\x1c\x0bw#\x12\x80\x08\x7f\x0f7\x1f\x1bT:\x8a+\x7fU%\x18P:\x8a\x00\x0b}\x006\x11K\x12\x8a\x00Z\x19&\x16>\x1a\x82\x01\x02D\x1d\nYb:\x8a*$\x15\x12\x1cX:\x8a\x1b\x7f[\x06\x1bP\x12\x8a+\x0c\x1f?\x1b-F\x1a\x82\x05Z\x13\x0f7\x14}:\x8a\nUb\x19\n7^\x10\x8a\x15_\n\x14\x132_\x12\x80\x1d\x7f_x\x05\x15N:\x8a\x05<`\x025\x11I:\x8a\x0bZ1\x0f4\x18S\x12\x8a\x0bQK8\x0bvB\x1a\x82\x11\x7f\x0e\x1a\x18\x14q\x1a\x82\x07\xc1\xe1\x05\x14\x13q:\x8a+Sb\x02\x1b\x0cM\x12\x8a\x01\x1f\x1b%\x15\x15]\x1a\x82\x01B\x01.(\x1d\x7f\x1a\x82\xc2\xc1\x05,9\x1at\x10\x8a,__X\x024N\x1a\x80(xD!\x15\x10I\x1a\x82\x08|.
That looks to me like binary data. So i guess either each 16 or each 32 bit are a single numeric. You would have to cast the data you have and then format it into a text file.
It looks like ser.read() returns bytes not string. You need to decode that to if you want string. Use data.decode(your_encoding). You need to know the encoding.
For more see docs.
Decoding in UTF-16 could be promising...
data=b'\x05\r\x1c\x1e\x10K\x12\x8a\x01\x0e\x14&\x1b\x1bW\x12\x80\x00\x7f\x11 \rZC:\x82\x12_.\x1a?\x10^\x12\x8a\x1a__\\x01:Q\x12\x8a\x15\x01\x1a\x1f\x1c6L\x1a\x82\x00t\x1e\x0c\x18\x19s:\x8a\x05]-\x01\x0e[G\x12\x8a\x15_\x0b9\x17>R\x12\x8a\x08__\x7f\x08:S\x1a\x82\x01\r\x18\x1d4\x10j:\x8a\n_0\x06\x10\x12Y\x10\x8a\nRH\x1c\x0bw#\x12\x80\x08\x7f\x0f7\x1f\x1bT:\x8a+\x7fU%\x18P:\x8a\x00\x0b}\x006\x11K\x12\x8a\x00Z\x19&\x16>\x1a\x82\x01\x02D\x1d\nYb:\x8a*$\x15\x12\x1cX:\x8a\x1b\x7f[\x06\x1bP\x12\x8a+\x0c\x1f?\x1b-F\x1a\x82\x05Z\x13\x0f7\x14}:\x8a\nUb\x19\n7^\x10\x8a\x15_\n\x14\x132_\x12\x80\x1d\x7f_x\x05\x15N:\x8a\x05<`\x025\x11I:\x8a\x0bZ1\x0f4\x18S\x12\x8a\x0bQK8\x0bvB\x1a\x82\x11\x7f\x0e\x1a\x18\x14q\x1a\x82\x07\xc1\xe1\x05\x14\x13q:\x8a+Sb\x02\x1b\x0cM\x12\x8a\x01\x1f\x1b%\x15\x15]\x1a\x82\x01B\x01.(\x1d\x7f\x1a\x82\xc2\xc1\x05,9\x1at\x10\x8a,__X\x024N\x1a\x80(xD!\x15\x10I\x1a\x82\x08|.'
data.decode('UTF-16')
u'\u0d05\u1e1c\u4b10\u8a12\u0e01\u2614\u1b1b\u1257\x80\u117f\u0d20\u435a\u823a\u5f12\u1a2e\u103f\u125e\u1a8a\u5f5f\u785c\u3130\u513a\u8a12\u0115\u1f1a\u361c\u1a4c\x82\u1e74\u180c\u7319\u8a3a\u5d05\u012d\u5b0e\u1247\u158a\u0b5f\u1739\u523e\u8a12\u5f08\u7f5f\u3a08\u1a53\u0182\u180d\u341d\u6a10\u8a3a\u5f0a\u0630\u1210\u1059\u0a8a\u4852\u0b1c\u4077\u8012\u7f08\u370f\u1b1f\u3a54\u2b8a\u557f\u1825\u3a50\x8a\u7d0b\u3600\u4b11\u8a12\u5a00\u2619\u3e16\u821a\u0201\u1d44\u590a\u3a62\u2a8a\u1524\u1c12\u3a58\u1b8a\u5b7f\u1b06\u1250\u2b8a\u1f0c\u1b3f\u462d\u821a\u5a05\u0f13\u1437\u3a7d\u0a8a\u6255\u0a19\u5e37\u8a10\u5f15\u140a\u3213\u125f\u1d80\u5f7f\u0578\u4e15\u8a3a\u3c05\u0260\u1135\u3a49\u0b8a\u315a\u340f\u5318\u8a12\u510b\u384b\u760b\u1a42\u1182\u0e7f\u181a\u7114\u821a\uc107\u05e1\u1314\u3a71\u2b8a\u6253\u1b02\u4d0c\u8a12\u1f01\u251b\u1515\u1a5d\u0182\u0142\u282e\u7f1d\u821a\uc1c2\u2c05\u1a39\u1074\u2c8a\u5f5f\u0258\u4e34\u801a\u7828\u2144\u1015\u1a49\u0882\u2e7c'
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?!?