Disable DTR in pyserial from code
Asked Answered
P

5

15

I'm trying to use pyserial to send data to an arduino. But when I open the COM port it sets DTR low and resets the board. However, I have my arduino code setup such that I have to put it into serial receive mode by holding two buttons for 1 second. I would rather not have to do the serial input on boot of the arduino, if possible.

Apparently you can modify serialWin32.py file, changing the line that reads:

self._dtrState = win32.DTR_CONTROL_ENABLE

to:

self._dtrState = win32.DTR_CONTROL_DISABLE

But, is there a way to just disable this directly in my python script? I also need to do this for all systems. I would rather not force people to change their base serial config just to use this script.

The serial port is opened as follows:

 com = serial.Serial(port, baud, timeout=1);

Update: In the end I found a solution that works fine for my setup. Since I didn't need to do Serial data all the time, only when I put the device into serial receive mode I found a way to disable the reset on serial connect from the arduino itself.

Many posts have said you can disable DTR reset by placing a ~100 Ohm resistor between 5V and reset. But I didn't want this to be a permanent thing. So, instead I placed a resistor between PD5 and reset. Then, in software:

void setup() {
    //.......
    DDRD &= ~(_BV(PD5)); //Set PD5 as input initially
    PORTD |= (_BV(PD5)); //Set high
    //.......
}

inline void setResetDisable(bool state)
{
  if(state)
    DDRD |= (_BV(PD5)); //Set PD5 as output to put 5V on reset line
  else
    DDRD &= ~(_BV(PD5)); //set back to input mode
}

So now, when I want to be in serial mode, I call setResetDisable(true) which throws 5V on that 100 ohm resistor and the reset pin, preventing DTR from pulling it low and resetting the chip :) I then just call setResetDisable(false) when I leave serial mode so the chip can be programmed as normal.

Policy answered 17/3, 2013 at 12:48 Comment(0)
S
15

You ought to be able to disable DTR before opening the port, like this:

com = serial.Serial()
com.port = port
com.baudrate = baud
com.timeout = 1
com.setDTR(False)
com.open()

However, doing so on the current release of pyserial (2.6) on Windows throws the following exception:

..., line 315, in setDTR
ValueError: Attempting to use a port that is already open

This seems to be a bug which is fixed in the latest version of the source, SVN revision 445 on 29th December 2011 (see http://pyserial.svn.sourceforge.net/viewvc/pyserial/trunk/pyserial/serial/serialwin32.py?view=log) with comment:

allow setRTS, setDTR before opening on Win32 (to set initial state), doc update

Which looks like it may have just missed the 2.6 release (uploaded on 2nd November 2011 see: https://pypi.python.org/pypi/pyserial).

Furthermore, looking at the current implementation of setDTR() for POSIX (in serialposix.py) it looks like this bug is not fixed and an exception is thrown if the port is not open, so a cross-platform solution looks unlikely.

Stromboli answered 18/3, 2013 at 14:21 Comment(4)
This doesn't work for me. Even with DTR=False, the Arduino still resets during serial reconnection. I'm using most recent pyserial 3.0.1 release on Ubuntu.Olympus
@Olympus Had the same problem, but setting DTR before opening the port works for me now (Python 3.5.1 on Windows 8.1 64 bit and the current pySerial 3.1). Also see the issue at the "new" repository at GitHub: github.com/pyserial/pyserial/issues/124Abrams
I also noticed that doing stty -hup on the port stops the problem as well. Why is this? How is that connected to DTR?Clink
Thanks, that works for me! (Arduino Nano 328p, Windows 11 python 3.9)Demetri
W
6

Disabling DTR does not work for me:
ser.dtr = None
(Linux 4.4.0 x86_64 / Python 2.7.12 / PySerial 3.4)

But this works:

import serial
import termios

port = '/dev/ttyACM0'
f = open(port)
attrs = termios.tcgetattr(f)
attrs[2] = attrs[2] & ~termios.HUPCL
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
f.close()
se = serial.Serial()
se.baudrate = 115200
se.port = port
print 'dtr =', se.dtr
se.open()

I found it here.

Widower answered 3/8, 2017 at 4:49 Comment(1)
Note that it will reset Arduino anyway on the first run after plugging in USB. Short version: Arduino resets on connect, this prevents disconnect so first connect is last. Long version: Arduino resets when DTR goes from Low to High. After plugging in USB it is Low by default, the first open() will set it High and cause a reset, and after that the code prevents it from going back to Low so no further resets happen. There seems to be no way to prevent DTR going High on open, and at least on my hardware setting it back Low immediately with an ioctl doesn't work fast enough to prevent the reset.Poock
I
2

The method you describe seems to be the most common fix for this problem that I've seen - so I suspect that there's no simpler software-based solution. You can of course manually change the state of the DTR line using ser.setDTR(level) - however I haven't tried this specifically in the case of the Arduino autoreset, and I suspect that even toggling the line immediately after opening the serial port may not be fast enough to prevent the reset.

The other options I can see that you have available would be to prevent the autoreset of the Arduino in hardware (see here), or to change your code slightly so that you allow the Arduino to reboot after initially making the serial connection, and then when you manually trigger your serial receive mode send an initial signal from the Arduino to show that it is now ready to receive data. Or alternatively you could include a modified version of the pySerial library with your script.

Iatry answered 18/3, 2013 at 13:37 Comment(0)
S
0

This is an old topic, but I'm working on a program sending serial data to an arduino right now. This is the line that works reliably for me:

ser = serial.Serial(port = 'COM4', baudrate = 115200, timeout = 0.1, dsrdtr=None)

Note: the arg is "dsrdtr" and the param is "None", not "False"

tested on: Python 3.8.8, anaconda 3 (2021.05), pyserial 3.5 Arduino Mega2560, MKR WiFi 1010, Wemos Mega2560 + ESP8266

Stramonium answered 1/12, 2021 at 17:52 Comment(0)
M
-3

Here is a software solution, i've been using it myself for a while and it works like a charm:

ser = serial.Serial("/dev/ttyUSB0", 115200, timeout=1)
ser.setDTR(False)
time.sleep(0.5)

Note that the sleep is the tricky part, without it it wont work

Mulford answered 28/8, 2013 at 22:2 Comment(3)
That's because the sleep waits long enough for the arduino to reset. The dtr here isn't doing anything.Sunlit
Mark, I know but this is the only way to get this working. The other solutions involve fixing a bug in pyserial. This is a pretty good workaround if you want to get things going, and it is cross platform.Mulford
This is no solution at all, since the question was how to disable DTR (in the context of stopping the arduino rebooting on serial open) and your solution is to NOT disable DTR and let it reboot. huh? how does that in any way answer the question.Lipp

© 2022 - 2024 — McMap. All rights reserved.