Python Serial readline doesn't work when called from within Object - python

So I'm writing a simple serial driver for an agilent power supply with pyserial. Everything works fine until I package it up as a class and try to run some simple serial write/read from within the object that I create.
A code snippet of the class:
class agilent:
"""
Class for controlling the chroma class power supplies
Basically a wrapper around the serial interface to the chromas
"""
def __init__(self, port_name="/dev/ttyUSB0", baud_rate=9600):
self.ser = serial.Serial(port=port_name, baudrate=9600, bytesize=8, parity=serial.PARITY_NONE, stopbits=2, dsrdtr=1, timeout=5)
self.reset()
self.identify() #Calls the IDN function to see if this is a multi-service device
print("Connected to: " + self.idn)
if self.multi_service:
print("This is a multi-service device, please make sure to specify one of: P25V, P25N, or P6V when setting limits")
def reset(self):
"""
Reset the agilent power supply to a reasonable(safe) settings
"""
self.ser.write("*CLS\n") # Clear any errors on the chroma
self.ser.write("system:remote\n") # set the controls back to remote
def identify(self):
self.ser.write('*IDN?\n')
self.idn = self.ser.readline()
if 'E3631' in self.idn:
self.multi_service=True
else:
self.multi_service=False
return self.idn
When I call the identify() function from the __init__() function the readline() times out and returns an empty string.
When running in ipython and import the class and create an object, I can call the identify function manually on the object and it works just fine....so either I'm doing something wrong with class methods or some funny business is going on with the serial class.
I know the right function is being called with the right context because if I put a
print(self.ser.port)
in the identify function it returns the right instance information.
Any ideas?
A typical run looks likes this:
from agilent_ps_controller import agilent
remote_ps = agilent() # << This hangs until the readline in __init__() times-out
remote_ps.reset() # Serial message is correctly written on wire
remote_ps.identify() # << this works just fine! Why didn't it work when the object constructor calls it?
remote_ps.__init__() # << this also times-out the readline.

I had the same problem exactly. It turns out that pyserial's readline() uses \n as the default eol character. If your device uses something else, such as \r\n as eol (as Arduino's println() does, for instance), this might be the problem.
So, to make a long story short, try calling readline() this way:
self.ser.readline(eol=b'\r\n')
or any other eol character(s) your device uses.

Related

Programming via TCP/socket to a Laser

I have the task at hand to program all of the commands from a laser GUI to python so that we can use python instead of the GUI to control the laser. The problem: I don't really know where to start. I don't have any socket programming experience nor do I have knowledge of networking but I do know a bit of programming. To give you an idea of what I need to do, here is the PDF of what I have to program. PDF (p.s. if you don't have a Dropbox, you can just download the pdf)
That's the entire agenda. I have to define a class and use the commands there to make everything available in python.
My advisor started the program already but I am not sure what the code really means. I asked him where to go from here but he insists everything is covered in the pdf. The code is below.
I was hoping someone could point me in the right direction of what I should read/watch to understand how to code the rest. I read through some socket intros and TCP/IP intros and I understand the gist. The thing is, socket programming is a huge area and I don't know where to cut the wheat from the chaff. Note: the if statement at the end is already done, meaning there is nothing else to add (so says my supervisor). I only need to work on the class. The code is not really cemented in. If I find a/an better/easier way to work with the commands, then I could revamp the code. Any help is greatly appreciated.
Here is the code:
#!/usr/bin/env python
import PySide.QtNetwork as Network
import PySide.QtCore as Core
import configparser
class Spitlight():
"""The classe spitlight provides an interface so that we can the Innolas Spitlight Laser from python parameterize and operate.
"""
def __init__(self, *args, **kwargs):
"""constructor"""
pass
def ReadConfig(self, ConfigFile):
"""The parameters to operate the laser will come from the configurations file with the file ending *.ini"""
self.config = configparser.ConfigParser()
self.config.read(ConfigFile)
""" here the single parameters will be read and the parameters thoroughly checked. Later we will add parameter value limits so the laser will not function without being within the value limits.
Initial relevant options are:
flashbulb voltage, Repetition rate, pump duration, pockels cell control (Balance, Holdoff), delays
Futhermore the communication information is in the IP/Port of the laser server
return 0 """
def Start(self):
"""
Open socket with information from configuration
Send parameters to the laser and confirm that everything is in order e.g:
send SET_OSCILLATOR_AMPLITUDE with FLHighvoltage aus Config
"""
self.initSocket()
self.set_oscillator_amplitude()
def set_oscillator_amplitude(self):
self.tcpSocket.write("SET_OSCILLATOR_AMPLITUDE={}".format(self.Config['Laser parameter']['FLHighvoltage ']))
def set_oscillator_delay(self):
self.tcpSocket.write("SET_OSCILLATOR_DELAY={}".format(self.Config['Laser parameter']['FLDelay']))
def initSocket(self):
""" Socket initialize"""
self.tcpSocket = Network.QTcpSocket()
self.tcpSocket.disconnected.connect(self.connectSocket)
self.tcpSocket.readyRead.connect(self.readData)
self.connectSocket()
# regularly reconnect is stabilerregelmäßiger reconnect is more stable as leaving the socket the entire time open
self.reconnectTimer = Core.QTimer()
self.reconnectTimer.setInterval(120000) # 2 minutes
self.reconnectTimer.timeout.connect(self.reconnectSocket)
self.reconnectTimer.start()
return 0
#Core.Slot()
def connectSocket(self):
"""Socket connect"""
return self.tcpSocket.connectToHost(self.proxyAddress, self.proxyPort)
#Core.Slot()
def reconnectSocket(self):
"""Socket Status verify and again connect"""
""" Available states of tcpSocket.state() are:
QAbstractSocket.UnconnectedState The socket is not connected.
QAbstractSocket.HostLookupState The socket is performing a host name lookup.
QAbstractSocket.ConnectingState The socket has started establishing a connection.
QAbstractSocket.ConnectedState A connection is established.
QAbstractSocket.BoundState The socket is bound to an address and port (for servers).
QAbstractSocket.ClosingState The socket is about to close (data may still be waiting to be written).
QAbstractSocket.ListeningState For internal use only.
"""
if self.tcpSocket.state() in [ Network.QAbstractSocket.HostLookupState, Network.QAbstractSocket.ConnectingState, Network.QAbstractSocket.ListeningState ]:
return 0
if self.tcpSocket.state() == Network.QAbstractSocket.ConnectedState:
return self.tcpSocket.disconnectFromHost()
#Core.Slot()
def readData(self):
data = self.tcpSocket.readAll()
if str(data).startswith("ERROR="):
pass
elif str(data).startswith("WARNING="):
pass
elif:
pass
def FlOn(self):
"""send FLASHLAMP ON
verify with GET_LASER_STATE"""
return 0
"""testing framework:
laser start, parametrierize, flashbulb on, shutter off, shutter closed, flashbulb off, every of the functions gives a 0 when they were successful and a error message when not
"""
if __name__ == "__main__":
SpitligthControl = Spitlight()
SpitligthControl.ReadConfig('SpitlightConfig.ini')
SpitligthControl.Start()
SpitligthControl.FlOn()
SpitligthControl.StartEmission()
SpitligthControl.StopEmission()
SpitligthControl.FlOff()

Python reading and writing to tty

BACKGROUND: If you want, skip to the problem section
I am working on a front end for test equipment. The purpose of the front end is to make it easier to write long test scripts. Pretty much just make them more human readable and writable.
The equipment will be tested using a Prologix GPIB-USB Controller (see prologix.biz). We found a tutorial at http://heliosoph.mit-links.info/gpib-on-debian-linux-the-easy-way/ and did all of the steps, and it worked!
As we don't have the test equipment yet, we wanted to write an emulator in Python using openpty. We do have the GPIB-USB Controller, just not what gets connected to that. I got the emulator working as a perfect replacement for the GPIB-USB. This means that I would follow the "GPIB on Debian ..." tutorial (above) and get output that I programmed the emulator to return. The input and output were done in the same manner as the tutorial just reading and writing to/from a pty device (ie /dev/pts/2) instead of the tty (ie /dev/ttyUSB0).
Now that the emulator works, we want to write a front end that can be used to write scripts easily. The goal is to make a kind of macro system that writes a bunch of commands when we call a function.
PROBLEM: exists using both the emulator and the device
I am using the following Python functions to read, write, and open the tty/pty devices, but I am not getting the same result that I get if I just use echo and cat in bash.
tty = os.open(tty_path, os.O_RDWR)
os.read(tty, 100)
os.write(tty, "++ver")
for example, I would expect the following to be equivalent
$ cat < /dev/pty/2 & # According to the tutorial, this must be run in parallel
$ echo "++ver" > /dev/pty/2
Prologix GPIB Version 1.2.3.4 ...
and
tty = os.open("/dev/pyt/2", os.o_RDWR)
os.read(tty, 100) # In separate Thread to be run in parallel
os.write(tty, "++ver") # in main thread
The output is very different, please explain why and how I can fix it.
FULL CODE is here: http://pastebin.com/PWVsMjD7
Well, I asked too soon. I hope someone benefits from this self answer.
So this works to read and write from both the emulator and the actual device. I am not exactly sure why, and would appreciate an explanation, but this does work in all of my tests
import serial
class VISA:
def __init__(self, tty_name):
self.ser = serial.Serial()
self.ser.port = tty_name
# If it breaks try the below
#self.serConf() # Uncomment lines here till it works
self.ser.open()
self.ser.flushInput()
self.ser.flushOutput()
self.addr = None
self.setAddress(0)
def cmd(self, cmd_str):
self.ser.write(cmd_str + "\n")
sleep(0.5)
return self.ser.readline()
def serConf(self):
self.ser.baudrate = 9600
self.ser.bytesize = serial.EIGHTBITS
self.ser.parity = serial.PARITY_NONE
self.ser.stopbits = serial.STOPBITS_ONE
self.ser.timeout = 0 # Non-Block reading
self.ser.xonxoff = False # Disable Software Flow Control
self.ser.rtscts = False # Disable (RTS/CTS) flow Control
self.ser.dsrdtr = False # Disable (DSR/DTR) flow Control
self.ser.writeTimeout = 2
def close(self):
self.ser.close()
You do not actually have to use any special module to read from TTY.
Option O_NOCTTY solved my problems with CDCACM example MCU app.
I'm sure it will work for you (as you work on Linux too).
#!/usr/bin/env python3
import io, os
tty = io.TextIOWrapper(
io.FileIO(
os.open(
"/dev/ttyACM1",
os.O_NOCTTY | os.O_RDWR),
"r+"))
for line in iter(tty.readline, None):
print(line.strip())
Stumbled on this while looking into pty/tty usage in python.
I think the original code did not work because echo will add a newline and the python os.write will not.
This is shown in your self answer here self.ser.write(cmd_str + "\n")
So the original code may have worked if it were os.write(tty, "++ver\n")

Incomplete string received from slot with PySide

I have an application which sends data over the serial port (using pyserial) to an external module which answers back upon reception. I have a thread that monitors the incoming data and when there is, sends a signal through an emit function. In the slot, I then analyze the packet received against a simplified hdlc protocol. It's working fine but the only problem is that if the frame contains zeros (0x00) the string received by the slot is truncated. So I'm assuming that the emit function passes the string up to a '0'. Here is the code for the signal and for the slot.
def ComPortThread(self):
"""Thread that handles the incoming traffic. Does the basic input
transformation (newlines) and generates an event"""
while self.alive.isSet(): #loop while alive event is true
text = self.serial.read(1) #read one, with timeout
if text: #check if not timeout
n = self.serial.inWaiting() #look if there is more to read
if n:
text = text + self.serial.read(n) #get it
self.incomingData.event.emit(text)
#QtCore.Slot(str)
def processIncoming(self, dataIn):
"""Handle input from the serial port."""
for byte in dataIn:
self.hexData.append(int(binascii.hexlify(byte),16))
....
For example, if I print the content of the variable "text" in ComPortThread I could get:
7e000a0300030005
and if I do the same for "dataIn", I get:
7e
I've read that QByteArray would keep the '0' but I've not been successful in using it (although I'm not sure if I used it right).
Hmm okay looking over the QtSlot decorator the form is:
PyQt4.QtCore.pyqtSlot(types[, name][, result])
Decorate a Python method to create a Qt slot.
Parameters:
types – the types that define the C++ signature of the slot. Each type may be a Python type object or a string that is the name of a C++ type.
name – the name of the slot that will be seen by C++. If omitted the name of the Python method being decorated will be used. This may only be given as a keyword argument.
result – the type of the result and may be a Python type object or a string that specifies a C++ type. This may only be given as a keyword argument.
Also it looks like since pyserial's read returns a bytes python type:
read(size=1)¶
Parameters:
size – Number of bytes to read.
Returns:
Bytes read from the port.
Read size bytes from the serial port. If a timeout is set it may return less characters as requested. With no timeout it will block until the requested number of bytes is read.
Changed in version 2.5: Returns an instance of bytes when available (Python 2.6 and newer) and str otherwise.
Though as noted with version 2.5 and python 2.6. With that in mind I'd look into making sure you're up to both versions mentioned and try:
#QtCore.Slot(bytes)
def processIncoming(self, dataIn):
"""Handle input from the serial port."""
for byte in dataIn:
self.hexData.append(int(binascii.hexlify(byte),16))
....
And see if that works for you.

How to Disconnect a python DBus connection?

I made some kind of answering machine for pidgin client that uses Linuxes DBus to make connection with pidgin. the code is this:
class DBus_Answer():
def __init__(self, text = "No text"):
self.answer = text
bus_loop = DBusQtMainLoop(set_as_default=True)
self.bus = dbus.SessionBus()
self.bus.add_signal_receiver(self.pidgin_control_func,
dbus_interface="im.pidgin.purple.PurpleInterface",
signal_name="ReceivedImMsg")
def pidgin_control_func(self, account, sender, message, conversation, flags):
obj = self.bus.get_object("im.pidgin.purple.PurpleService", "/im/pidgin/purple/PurpleObject")
purple = dbus.Interface(obj, "im.pidgin.purple.PurpleInterface")
purple.PurpleConvImSend(purple.PurpleConvIm(conversation), self.answer)
now I want to use it as a module in another program. I called it like this:
answering_machine.DBus_Answer(message)
the problem is, when I stop the second program (the program that has this one as a module) and then start it again, I'll get a segmentation fault because it want to make another connection to the DBus and it seams it's not regular!
Other wise I want to give the chance of disabling this module to user. I tried to use an if statement. It will work for the first time. but if user run the module for once, he can't disable it any more.
segmentation faults occur because in a python module (written in C) a pointer is NULL, or because it points to random memory (probably never initialized to anything), or because it points to memory that has been freed/deallocated/"deleted".so your problem is probably with your memory.try trace the segfault using methods described here

How do I override PySerial's "__del__" method in Python?

I have a simple Python script set up to send Characters to a device on COM3.
(This is running on Windows XP)
The device receives the Characters fine, but when the script ends, or I call
ser.close() #ser is an instance of the `serial` method `Serial`
It sets the DTR Line on the serial port High, Resetting my Device.
Perplexed by this, I poked around in the PySerial Documentation (http://pyserial.sourceforge.net/) and found that the Module has a __del__ method, which calls the ser.close() function when the Serial instance is deleted.
Is there anyway to Override this __del__ method?
(I would prefer to not have to disconnect the Reset Line of my device from the DTR line)
Below is the basic code I am using:
import serial
ser = serial.Serial()
ser.port = 2 #actually COM3
ser.baudrate = 9600
ser.open()
ser.write("Hello")
ser.close()
Sure, go ahead and override it. You can either create a subclass of Serial and specify a do-nothing __del__() method and use the subclass instead of Serial, or delete that method from the actual Serial class (monkey-patching) using the del statement. The former method is preferred but the second may be necessary if code you don't have any control over is using the serial port. Be sure the __del__() method doesn't do anything else important before overriding it, of course.
Subclassing:
import serial
class mySerial(serial.Serial):
def __del__(self):
pass
ser = mySerial()
# rest of script the same
Monkey-patching:
import serial
del serial.Serial.__del__
If you have Python source for this module (i.e. it is not written in C), a third approach would be to create your own copy of it and just edit it not to do that. (You can also do this for C modules, but getting it running is a bit more difficult if you don't have C expertise.)
Of course, changing the __del__() method to not call ser.close() isn't going to help much in the code you give, since you're calling ser.close() yourself. :-)
Have you tried something like this?
MySerial = serial.Serial
def dummy_del(obj):
pass
MySerial.__del__ = dummy_del
ser = MySerial()

Categories

Resources