I use pyserial to communicate with a 3D printer (Monoprice Select Mini V2) via USB. Everything works well when I connect to the printer for the first time, but when I try to reopen a connection I can still send commands but do not receive any character.
This happens when I close the port and reopen it in the same program or when I rerun for the second time a python script opening the port after the first script has returned. The only way to reconnect properly is to restart my printer or to unplug and replug it. Changing the timeout value or trying to read only one byte does not solve the problem.
Short nonworking example:
import serial
ser = serial.Serial('/dev/ttyACM0', baudrate=115200, timeout=5)
ser.write("\n".encode())
print(ser.readline().decode())
# prints 'echo:Unknown command: "~"' (Not sure why)
print(ser.readline().decode())
# prints 'ok N0 P15 B15'
ser.write("M105\n".encode())
# prints expected response
ser.close()
print(ser.isOpen())
# prints 'False'
ser.open()
print(ser.isOpen())
# prints 'True'
ser.write("\n".encode())
print(ser.readline().decode())
# times out
ser.write("M105\n".encode())
print(ser.readline().decode())
# times out
I've coded a simple script for Windows that works fine and I have adapted it to Linux (Ubuntu). The problem is that it doesn't read the byte sent.
I tried all the different serial ports available according to the Arduino IDE but the problem persists.I also used \n and \r without success and different encodings.
Code working on win10:
import serial
import time
import keyboard
arduino = serial.Serial('COM4', 9600, timeout=0)
while True:
arduino.write('a'.encode())
time.sleep(0.1)
print(arduino.readline())
Code not working on Ubuntu:
import serial, time
arduino = serial.Serial('/dev/ttyAMC0', 9600, timeout = 0)
while True:
arduino.write('a'.encode())
time.sleep(0.1)
print(arduino.readline())
So the first script prints continuously a\r\n, the second doesn't. Simply shows b'' continuously. So I think it doesn't simply write the letter.
Solved. No idea what exactly was thr issue but worked sending capital letter.
I just made a tiny code to change the colors of my led strip in Linux too (I already did it in C# on Windows).
import serial
ser = serial.Serial('/dev/ttyACM0', 9600)
ser.write(b'a')
When I type this into bash like this:
$sudo python2
>>>import serial
>>>ser = serial.Serial('/dev/ttyACM0', 9600)
>>>ser.write(b'a')
1
it's working fine, but if I then execute the .py script like this:
$sudo python2 light.py
The ser.write part seems not to work. I dont get an err msg or anything. But I know that it's communicatin with the arduino cause the Onboard LED flashes when I execute the script.
Okay, got it, the Arduino resets after getting serial input, I just added a 3 Seconds delay before writing the Serial data
I have the following code:
import sys,serial
ser = serial.Serial()
ser.baudrate=57600
ser.port = sys.argv[1]
ser.dsrdtr = True
ser.open();
ser.setDTR(level=False)
print ser.readline()
The thing is that my Arduino UNO receives a DTR and restarts, how can I disable this (in software)? My python code is running from a Mac mini with a usb connection to my UNO.
(I'm fully aware of this but hardware is not an option for me)
I'm having similar issues, but haven't been able to find a solution for a while. Looks like this is possible on Windows with some hackery, but the issue lies deeper on posix.
Ideally you should be able to setDTR before you open the connection. Like this:
import sys,serial
ser = serial.Serial()
ser.baudrate=57600
ser.port = sys.argv[1]
ser.dsrdtr = True
ser.setDTR(level=False)
ser.open();
print ser.readline()
But that throws a portNotOpenError in serialposix.py:
def setDTR(self,on=1):
"""set terminal status line"""
if not self.fd: raise portNotOpenError
if on:
fcntl.ioctl(self.fd, TIOCMBIS, TIOCM_DTR_str)
else:
fcntl.ioctl(self.fd, TIOCMBIC, TIOCM_DTR_str)
I took a dive into serialposix.py, and you'll see where the root issue lies. That self.fd defined above is actually:
self.fd = os.open(self.portstr, os.O_RDWR|os.O_NOCTTY|os.O_NONBLOCK)
If you write a little script that opens your device using os.open(device, flags), you'll see it resets, even if you open it as read only with the flag os.O_RDONLY.
Digging deeper into the meaning of the os.open flags- we find that the open command actually wraps the unix command open(2). The man pages are here.
Let me know if you ever find a more satisfactory solution.
I have an Arduino microcontroller listening on COM3. Using the arduino IDE and the Serial monitor works fine to send and receive data.
I would like to send and receive data from Python, but it's not immediately obvious how to do so. (I'd also be fine doing it in C# if it was substantially easier.)
I found arduino_serial.py, but it only works for Unix. Fortunately, I have a Ubuntu 10.10 VBox set up. However, I have no idea if that VM can access serial ports or if special steps are required to do so.
I also found pySerial, which looks pretty legitimate. However, I'm also unsure how to use it. It wants serial port names. How do I find out what valid values for these are?
For example, pySerial mentions that you can "Open named port at “19200,8,N,1”, 1s timeout" with the following command:
>>> ser = serial.Serial('/dev/ttyS1', 19200, timeout=1)
But I have no idea how you would know that /dev/ttyS1 was a valid port name.
Is there good documentation for getting started on this?
Update: I'm using Ubuntu with arduino_serial, but still having trouble.
This program is running on the Arduino:
void setup() {
Serial.begin(9600);
}
void loop() {
if (Serial.available()) {
Serial.print((char)Serial.read());
}
}
I see that a port called tty0 is available:
foo#bar:~/baz$ dmesg | grep tty
[ 0.000000] console [tty0] enabled
I then try to connect with arduino_serial:
foo#bar:~/baz$ sudo python
[sudo] password for foo:
Python 2.6.6 (r266:84292, Sep 15 2010, 15:52:39)
[GCC 4.4.5] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import arduino_serial
>>> sp = arduino_serial.SerialPort("/dev/tty0", 9600)
>>> sp.write("foo")
>>> sp.read_until("\n")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "arduino_serial.py", line 107, in read_until
n = os.read(self.fd, 1)
OSError: [Errno 11] Resource temporarily unavailable
Why am I getting this error? What am I doing wrong?
pySerial may or may not be built in to Python. Regardless, if it's not, pySerial is the library to download and install.
And since you already know the Arduino is on COM3, just use this:
import serial
ser = serial.Serial("COM3", 19200, timeout=1)
ser.write("Whatever")
For a Linux box, it's relatively easy to find out what serial port your Arduino is using:
dmesg | grep tty
This will give you some output similar to this: [ 7.944654] usb 1-1.6: FTDI USB Serial Device converter now attached to ttyUSB0
So my Arduino is on ttyUSB0. This means you can use the following code to talk to the Arduino on a Linux box:
import serial
ser = serial.Serial("/dev/ttyUSB0", 19200, timeout=1)
ser.write("Whatever")
Note: If you use a baud rate of 9600 on the Arduino, as most people do, you can simply use serial.Serial("COM3") or serial.Serial("/dev/ttyUSB0") without any other parameters.
EDIT:
You should also keep in mind that in the real world, it may take a second to actually open the port and get it ready for transmitting data. This means that performing a write IMMEDIATELY after the serial.Serial() call may not actually do anything. So the code I would use is as follows:
import serial
import time
ser = serial.Serial("/dev/ttyUSB0", 19200, timeout=1)
time.sleep(1.5)
ser.write("Whatever")
Kind of a hack, but it's the only way I know how to get it to work on my system.
The serial ports are named COM1 onwards on Windows, /dev/ttyS0->COM1. I wrote some code in Python for our Quadcopter controller which works both on Windows and Linux (given you supply the port name properly) using Pyserial.
Try passing COM3 to Pyserial on Windows. On the VM you will have to first pass the USB-to-serial adapter to the VM or set up the serial ports section (I use VirtualBox). If you go the USB route the serial devices are enumerated under /dev/ttyUSBxx.
I have a project called Yaam on CodePlex that uses C# to send data through the serial port. Check that out for an example. On the C# side (see Yaam\Yaam.xaml.cs), simply use the SerialPort class in the System.IO.Ports namespace. Once you instantiate the object and set the properties (baud rate, com port, etc), simply call.Open() . There are also plenty of other examples on the web. Take a look at these:
http://jtoee.com/2009/02/talking-to-an-arduino-from-net-c/
http://www.instructables.com/id/Interfacing-your-arduino-with-a-C-program/
"But I have no idea how you would know that /dev/ttyS1 was a valid port name."
PySerial's serial port initialiser accepts a number instead of a name as an argument. These numbers will correspond to "normal" serial ports (/dev/ttySX on Linux, COMX on Windows). You can then get the name from the created object. There's no way to know in advance what numbers to try, though, so as you'll see in the following code, you just have to try and fail.
This won't always discover simulated ports (created using socat or com0com), or USB ports though, so for those you need to use the glob module (I don't think it makes a huge difference whether you use globbing or indices for the dev/ttySX device nodes). This is what pySerial's own examples do. The following code is adapted from those examples:
import glob, os
import serial
USB_SERIAL_GLOB = "/dev/ttyUSB*"
def try_open(port, args = (), kwargs = {}):
try:
port = serial.Serial(port, *args, **kwargs)
except serial.SerialException:
return None
else:
return port
def serial_scan(max_range = 32, args = (), kwargs = {}):
for i in range(max_range):
port = try_open(i, args, kwargs)
if port is not None:
yield port
# Look for USB serial ports:
if os.name == 'posix':
for fn in glob.glob(USB_SERIAL_GLOB):
port = try_open(fn)
if port is not None:
yield port
if __name__ == "__main__":
for port in serial_scan(kwargs = {'baudrate':9600, 'timeout':0.5}):
port.close()
print "Found: %s" % port.name