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()
Related
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.
I am on a Windows 7 computer, trying to use Python to run a simple program on my Arduino to make it blink.
For some reason, whenever I attempt to run the script in PowerShell, I receive an error that the port is already open. I have tried restarting my computer, changing Arduinos, changing the USB port that I have plugged into, running a different program that I am quite sure closes the computers connection to the port. However, I am still getting the same error.
I am quite new to Python and Arduino, so there might be something stupidly easy that I am forgetting. I have asked a friend who is more experienced and he spoke of it being a problem with Windows.
Here is the relevant code. The class I used in Python is as follows.
import serial
import socket
import glob
import pickle
from time import sleep,time
class Ardustat():
def __init__(self):
self.port = ""
self.ser = serial.Serial()
self.mode = "serial"
self.debug = False
def findPorts(self):
"""A commands to find possible ardustat ports with no Arguments, """
return glob.glob("COM*")
def connect(self,port):
if self.mode == "serial":
self.ser = serial.Serial(port,57600)
self.ser.timeout = 1 # what does this do?
self.ser.open()
return "connected to serial"
And the Python code is
from ardustat_library_simple_dan import Ardustat
import time
serial_port = 'COM6'
ard = Ardustat()
#Connect to the irritating ardustat
ard.connect(serial_port)
for a in range(0,5):
ard.blink(serial_port)
time.sleep(.2)
The Arduino code is long, but in the setup there is the following code.
void setup()
{
//Startup Serial
Serial.begin(57600);
// Serial.println("Hi Dan!");
Is there anything wrong?
I can see a few reasons why this is not working:
You have your serial monitor running while you're running your script:
The serial port can only be open once on a given host, if you try to open it twice, the second program to open it will report it as already opened.
You did not open the serial connection on the Arduino side:
You say you run a blink sketch. If that's the default blink.ino, it does not open the serial port and your system may report the serial port is not openable (and I don't know Windows, but I wouldn't be surprised to see an inconsistent error happening)
You wrote your own blink sketch, but you did not put Serial.begin(115200); in the setup() of your sketch file.
You open the port twice (or more) in the Python code.
You shall have only one serial.open() statement in your code.
So first, check your code to be sure you do actually use the serial port from within the Arduino sketch. You can paste your code in your question so we can check that out.
Then, check your Python code to be sure you do actually open the serial port once and only once within your script.
Finally, be sure to not have another program opening the serial port while you're running your script, usually one tend to test the connection with Arduino's serial monitor, let it open and try to open the port using a script, which does not work. I saw that very often from beginners.
And I vote for... problem #3 \o/
class Ardustat():
def __init__(self):
self.port = ""
self.ser = serial.Serial() # Here you open the port once
self.mode = "serial"
self.debug = False
def findPorts(self):
"""A commands to find possible ardustat ports with no Arguments, """
return glob.glob("COM*")
def connect(self,port):
if self.mode == "serial":
self.ser = serial.Serial(port,57600) # Here you open the port twice
I did not check pySerial's source code, but I'm pretty sure serial.Serial() opens up the first serial port it finds available at its default speed. Therefore, as you don't close the connection from the first instance of self.ser, the second instance gets crazy saying "dude, it's already opened!".
That's why you'd better not add an abstraction (Artustat) over a correct abstraction (pyserial.Serial), but just use correctly the tools and do something like:
with serial.Serial(port, speed) as ser:
do_blink(ser)
where do_blink() is a function where you do your stuff. The with statement over serial.Serial will take care of the life of the serial port object (opening it when you enter the block, closing it as you exit the block).
I am reading serial data like this:
connected = False
port = 'COM4'
baud = 9600
ser = serial.Serial(port, baud, timeout=0)
while not connected:
#serin = ser.read()
connected = True
while True:
print("test")
reading = ser.readline().decode()
The problem is that it prevents anything else from executing including bottle py web framework. Adding sleep() won't help.
Changing "while True"" to "while ser.readline():" doesn't print "test", which is strange since it worked in Python 2.7. Any ideas what could be wrong?
Ideally I should be able to read serial data only when it's available. Data is being sent every 1,000 ms.
Using a separate thread is totally unnecessary. Just follow the example below for your infinite while loop instead.
I use this technique in my eRCaGuy_PyTerm serial terminal program here (search the code for inWaiting() or in_waiting).
Notes:
To check your python3 version, run this:
python3 --version
My output when I first wrote and tested this answer was Python 3.2.3.
To check your pyserial library (serial module) version, run this--I first learned this here:
python3 -c 'import serial; \
print("serial.__version__ = {}".format(serial.__version__))'
This simply imports the serial module and prints its serial.__version__ attribute.
My output as of Oct. 2022 is: serial.__version__ = 3.5.
If your pyserial version is 3.0 or later, use property in_waiting in the code below. If your pyserial version is < 3.0, use function inWaiting() in the code below. See the official pyserial documentation here: https://pyserial.readthedocs.io/en/latest/pyserial_api.html#serial.Serial.in_waiting.
Non-blocking, single-threaded serial read example
import serial
import time # Optional (required if using time.sleep() below)
ser = serial.Serial(port='COM4', baudrate=9600)
while (True):
# Check if incoming bytes are waiting to be read from the serial input
# buffer.
# NB: for PySerial v3.0 or later, use property `in_waiting` instead of
# function `inWaiting()` below!
if (ser.inWaiting() > 0):
# read the bytes and convert from binary array to ASCII
data_str = ser.read(ser.inWaiting()).decode('ascii')
# print the incoming string without putting a new-line
# ('\n') automatically after every print()
print(data_str, end='')
# Put the rest of your code you want here
# Optional, but recommended: sleep 10 ms (0.01 sec) once per loop to let
# other threads on your PC run during this time.
time.sleep(0.01)
This way you only read and print if something is there. You said, "Ideally I should be able to read serial data only when it's available." This is exactly what the code above does. If nothing is available to read, it skips on to the rest of your code in the while loop. Totally non-blocking.
(This answer originally posted & debugged here: Python 3 non-blocking read with pySerial (Cannot get pySerial's "in_waiting" property to work))
pySerial documentation: http://pyserial.readthedocs.io/en/latest/pyserial_api.html
UPDATE:
27 Dec. 2018: added comment about in_waiting vs inWaiting(). Thanks to #FurkanTürkal for pointing that out in the comments below. See documentation here: https://pyserial.readthedocs.io/en/latest/pyserial_api.html#serial.Serial.in_waiting.
27 Oct. 2018: Add sleep to let other threads run.
Documentation: https://docs.python.org/3/library/time.html#time.sleep
Thanks to #RufusV2 for bringing this point up in the comments.
Note on multi-threading:
Even though reading serial data, as shown above, does not require using multiple threads, reading keyboard input in a non-blocking manner does. Therefore, to accomplish non-blocking keyboard input reading, I've written this answer: How to read keyboard input?.
References:
Official pySerial serial.Serial() class API - https://pyserial.readthedocs.io/en/latest/pyserial_api.html
Put it in a separate thread, for example:
import threading
import serial
connected = False
port = 'COM4'
baud = 9600
serial_port = serial.Serial(port, baud, timeout=0)
def handle_data(data):
print(data)
def read_from_port(ser):
while not connected:
#serin = ser.read()
connected = True
while True:
print("test")
reading = ser.readline().decode()
handle_data(reading)
thread = threading.Thread(target=read_from_port, args=(serial_port,))
thread.start()
http://docs.python.org/3/library/threading
I would warn against using blocking IO in a thread. Remember Python has a GIL and at one time only one thread can execute. Now please note that pyserial module is a wrapper over an OS implementation of accessing the serial port. That means it calls code external to the Python. If that code blocks, then the interpreter also get blocked and nothing will execute in the Python program, even the main thread.
This can even happen when using non-blocking IO or timeout based polling if the underlying device driver does not implement timeout well.
A more robust approach is to use multiprocessing module with a queue. Run serial read code in a separate process. This will make sure main and other threads don't block and the program can exit in clean way.
Use a timer driven event to test and read the serial port.
Untested example:
import threading
class serialreading():
def __init__(self):
self.active = True
self.test()
def test(self):
n_in =comport.in_waiting()
if n_in> 0:
self.data = self.data + comport.read(size=n_in)
if len(self.data) > 0:
print(self.data)
self.data=""
if self.active:
threading.Timer(1, test).start() # start new timer of 1 second
def stop(self):
self.active = False
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
I am using PySerial (a Python API for serial communication) to send AT commands to a Nokia phone via bluetooth.
import serial
com = serial.Serial()
com.port = 19
com.timeout = 0 #also tried a timeout value greater than 0.
try:
com.open()
# at this point I turn off the phone.
com.write("AT\r\n")
print com.readlines()
except SerialException, e:
print e
Just after I open() the com, I turn off the phone. Then, I write("AT\r\n"). At this point, the function blocks and the runtime hangs.
Do you have any solution?
With a timeout set to 0, you desactivate the timeout parameter, the read()/readlines() becomes a blocking call. The caller will be blocked until the device answers.
Try to set a non-zero timeout value to your serial connection com = serial.Serial(timeout=0.5).
If it still hangs, the problem should be in the bluetooth stack.
Actually, what you are looking for is the writeTimeout and not the timeout argument. Late answer, I know, but I still needed it and I guess I'm not the only one.