Half-duplex serial communications in Python - python

I have a device with a built-in USB/Serial adapter (shows up as a Prolific PL2303). The device documentation provides full details on how to communicate with it, and provides a sample Windows app. However, I need to use it on Linux - ideally with Python but I'm not too precious about that.
The device docs state the device runs at 9600, 8N1 with half duplex. The Windows app works fine - device works fine (so I know it's not a device problem). However, I can't communicate with it using Python on Linux. I'm using pySerial, and I've tried a similar (full duplex, also PL2303) device which works fine.
I've tried several combinations of setting xonxoff, rtscts etc. I've also tried RTS toggling using setRTS(True) and checking for CTS and DSR etc - gets all return False. I can successfully open the device using pySerial (I can see activity light flashing - simple but effective test) and pySerial complains if you unplug it during reads/writes.
Doesn't seem to matter what flags/lines I set or what data I send, always get the same result.
>>> s=serial.Serial()
>>> s.port('/dev/ttyUSB1')
>>> s.open()
>>> s
Serial<id=0x7fe94b533b50, open=True>(port='/dev/ttyUSB1', baudrate=9600, bytesize=8, parity='N', stopbits=1, timeout=3, xonxoff=0, rtscts=0, dsrdtr=0)
>>> s.write('\2CMD2292\r')
>>> s.inWaiting()
0
>>> s.setRTS(True)
>>> s.getCTS()
False
(several iterations of above with different flags).
Ignore the data in the write command - that's just a status check command. Doesn't matter what goes there, the device should either respond with an answer or an error.
I've even resorted to closing and re-opening the device in between setting various ctsrts flags etc., and unplugging / replugging it to force resets. As above it continues to work fine using the Windows test app. I can't use Portmon etc. to sniff the Windows port traffic as it's a 64bit Win7 install and I don't currently have time to build an XP machine.
Any thoughts?
UPDATE: I've also tried all of the above using Python on the same Windows box that the demo app works on. It definitely opens the port and communicates, but again no info back regardless of what's written.
UPDATE2: It looks like it might be device-driver related. Some additional background research suggests that some PL2303 chips have functionality to support half-duplex, but this isn't supported by the Linux drivers. The Windows demo app comes with a dedicated device driver and the app doesn't work on a clean test machine using default Windows drivers. This makes me think that while I can connect to it successfully through Python, I can't control the duplex comms (e.g. even in an STX/ETX way) and therefore this might be a hopeless case. sigh.
UPDATE3: Thanks for the comments below. However, I couldn't find any way round this. I tried a USB protocol analyser, and I tried disassembling the driver but it just became rather time-consuming, so in the end I took the device apart and after some tinkering I managed to replace the existing usb-serial adapter with a proper PL2303 part - on the device side, it was just a basic 2-wire serial interface so it didn't care what was talking to it. I can't seem to close this question so I'll leave it as-is.

Try
s.flush()
just after your s.write call

Related

How to send raw string to a dotmatrix printer using python in ubuntu?

I have a dot-matrix printer LX-300 connected to my computer through the network. How do I send a raw string with ESCP characters directly to my printer in Python?
The computer is connected to the printer through another computer. I need to send a raw string because LX-300 image printing result is blurry.
The Problem
To send data down this route:
Client computer ---> Server (Windows machine) ---> printer (dot-matrix)
...and to not let Windows mess with the data; instead to send the raw data, including printer control codes, straight from the client computer.
My Solution
Here's how I solved a near-identical problem for a small in-house database application:
Step 1) Make the printer network-accessible without Windows getting its fingers in the data routed to it. I accomplished this by installing the printer using the "Generic/Text Only" driver, then installing
RawPrintServer on the Windows machine connected to the printer.
Step 2) Send raw data over the network to the TCP/IP port specified when you set up RawPrintServer (default is 9100). There are various ways to do that, here's what I did:
data = b"\x1B#A String To Print\x1B#" # be sure to use the right codes for your printer
ip_addr = 123.123.123.123 # address of the machine with the printer
port = 9100 # or whatever you set it to
s = socket.socket()
try:
s.connect((ip_addr, port))
s.send(data)
except:
# deal with the error
finally:
s.close()
Background
I thought about the problem in two parts:
Client machine: spitting out the data I need from Python with the correct formatting/control codes for my printer, and sending it across the network
Print server machine: transmitting the data to the locally connected printer
Number 1 is the easy part. There are actually some libraries in PyPI that may help with all the printer codes, but I found most of them are aimed at the little point-of-sale label printers, and were of limited use to me. So I just hard-coded what I needed into my Python program.
Of course, the way you choose to solve number 2 will effect how you send the data from Python. I chose the TCP/IP route to avoid dealing with Samba and Windows print issues.
As you probably discovered, Windows normally tries very hard to convert whatever you want to print to a bitmap and run the printer in graphics mode. We can use the generic driver and dump the data straight into the (local) printer port in order to prevent this.
The missing link, then, is getting from the network to the local printer port on the machine connected to the printer. Again, there are various ways to solve this. You could attempt to access the Windows printer share in some way. If you go the TCP/IP route like I did, you could write your own print server in Python. In my case, the RawPrintServer program "just worked" so I didn't investigate any further. Apparently all it does is grab incoming data from TCP port 9100 and shove it into the local printer port. Obviously you'll have to be sure the firewall isn't blocking the incoming connections on the print server machine. This method does not require the printer to be "shared" as far as Windows is concerned.
Depending on your situation (if you use DHCP), you might need to do some extra work to get the server's IP address in Python. In my case, I got the IP for free because of the peculiarity of my application.
This solution seems to be working out very well for me. I've got an old Panasonic printer running in Epson ESC/P compatibility mode connected to a Windows 7 machine, which I can print to from any other computer on the local network. Incidentally, this general idea should work regardless of what OS the client computer is running.
Ultimately, you will need and want to write your own wrapper/script to do this. And since you are using a distribution of Linux, this is relatively easy.
On a Linux OS, the simplest way to issue a print job is to open a subprocess to the lpr. Generally, using lpr lets you access the printer without the need to be logged in as root (being a superuser), which is desirable considering the amount of damage that can be done while logged in as a "superuser".
Code like the following:
import subprocess
lpr = subprocess.Popen("/usr/bin/lpr", stdin=subprocess.PIPE)
lpr.stdin.write(data_to_send_to_printer)
Should be a good jumping off point for you. Essentially, this code should allow you to accomplish what you need.
Be careful though; depending on your privilege levels, a call to open a subprocess might need root level/Superuser permissions.
Subprocesses generally inherit the User IDs and access rights by the user that is running the command. For example, if the subprocess is created by a root user, then you will need root user/Superuser rights to access that subprocess.
For more information, check out the hyperlinks I've included in the post.
Good luck!

How to send data through parallel port in Python?

I need to send some data via parallel port using Python. And for this I am using psychopy.parallel module.
I have two Windows 7 x64 machines connected via parallel port. This connection is tested and working because it is used by another software to send data via LPT1 port.
I have installed the necessary libraries for psychopy.parallel as described here.
Here is my naive intuition:
Machine 1 (Sending data):
from psychopy import parallel
port = parallel.ParallelPort(address="0xC010")
port.setData(3) # sets just pin 2, 3 high
Checking on Machine 1
port.readData()
> 3
Checking on Machine 2 (Receiving data):
from psychopy import prallel
port = parallel.ParallelPort(address="0xEC00")
port.readData()
> 0
I see that although I can set the pins correctly on Machine 1 and read the result, Machine 2 just gives me a 0.
How to send and receive a signal via parallel port correctly?
There are a few options:
Maybe you haven't got the right driver for pyparallel/psychopy to talk to. You'll need a 32bit parallel port driver if you're using the Standalone PsychoPy (which includes 32bit python). The answer in the link below tells you how to download another test utility that will insert the correct version of both 32bit and 64bit drivers, which you need to do on both machines obviously
your script sending the pules will finish immediately and conceivably the pins are all being set back to low when the script ends? (I think this isn't usually the case but worth checking)
have you set the right address for the parallel port? Your code currently uses the default address but there are various options.
Lastly, the link below tells you how to do it using inpout32.dll directly but I think the drivers are the first thing to get fixed
PsychoPy sending triggers on 64bit OS

Python Pyserial Windows No Port Found

I have just tried to connect to usb mobile to send sms through it using AT commands. But when i use pyserial to connect to it in a windows os, i get error could not open port, the file specified cannot be found.
>>> import serial
>>> ser = serial.Serial(0) # open first serial port
>>> print ser.name # check which port was really used
>>> ser.write("hello") # write a string
>>> ser.close()
even if i replace the 0 with any other value, like 0 -10 or 'com0','com1' etc, i still get error file specified not found, port cannot be open.
There is a command listed in pyserial documentation which lists ports or allows you to open a port
python -m serial.tools.miniterm
This command is supposed to list all serial ports. But it shows none.
I have 3 usb ports on my system. What is causing this issue.
Pyserial is not guaranteed to detect all ports. This depends on how the device and the OS communicate and if pyserial is designed to pick up on this. For Example, pyserial could not detect a LabJack U3-LV or a EPSOM-POS receipt printer I plugged in in a Win8 session (both could be detected using a linux session).
Try serial.tools.list_ports.comports() and see what it gives you.
You could also look at the pyusb module for usb connections.
Also: I recommend being careful when using numbers for ports, as the mapping may not be "common-sense" logical (i.e. 0 may not map to COM0). Use explicit string names instead.
I found the solution myself. Its pretty simple and uses the same code in the question.. I was getting blocked error because there was no modem(nokia phone) in my device manager. although i had plugged in my phone into the usb, there was no nokia pc suite installed. Once you install nokia pc suite and connect your nokia phone, in device manager -> modems your phone will appear. Just check its properties->modem and you will find the com5 or whichever number. Then use that id to connect. If you dont know this, just iterate till you find the right one.
Thanks everyone who tried to help

Identifying serial/usb device python

The solution to this problem is probably pretty simple, but I am new to interfacing with a device dynamically. What I'm doing is I am making a python executable code, so the user doesn't have to have Idle on their computer or any kind of python interpreter, which means I don't know which USB port the device will be plugged in to. The program needs to be able to open a connection to a device that is connected through a serial to usb converter. How can I determine which connected device is the correct device to open a port to? I am using pySerial to interact with the device. Any help would be greatly appreciated.
No matter how you configure your device, at some point you're probably going to have to ask the user where the port is, or poll all serial devices for a known response. (Polling has it's pitfalls though, so read on!). Unlike USB devices, there is no vendor/device ID that is made known to the OS when you attach a plain-old serial device.
First you need to find the serial ports. Here's a question that might help: What is the cross-platform method of enumerating serial ports in Python (including virtual ports)?.
Once you have a list of serial ports, you could ask the user whether they know which one to use. If they do, problem solved!
If they don't, you could offer to poll ALL serial devices with some data that you know will yield a certain response from your device. Keep in mind though that if the user has other serial devices attached, your string of "hello" bytes might actually be the self-destruct sequence for some other device! Hence, you should warn the user that polling may interfere with other devices, and always prompt them before you do so.
Without knowing more about your code (eg. what comms framework, if any, you're using; are you doing this in the console or are you using a GUI toolkit, etc), it's impossible to say what the best way to code this might be. In the simplest case, you could just loop over all of your serial devices, send the greeting and check for a response. (You could also do this in parallel: loop once for the greeting, and loop again to check what's in the buffer. If you're going to get more fancy than that, use a proper library.)
Side note: You might be able to get around this if you have a built-in converter that you can set the vendor/device ID for, but the converter will still be automatically detected by any modern OS and enumerated as a serial port; you won't get to talk to it directly as a USB device. It could be possible to figure out which port goes with which ID, but I've never tried to do that. But this approach is useless if you're not the one who gets to pick the converter (eg. if it's a user-supplied cable).
This is the way that I used for identify Serial-USB conveter device like Arduino.
It checks 'USB' String of device description.
import serial as ser
import serial.tools.list_ports as prtlst
global COMs
COMs=[]
def getCOMs():
global COMs
pts= prtlst.comports()
for pt in pts:
if 'USB' in pt[1]: #check 'USB' string in device description
COMs.append(pt[0])
The only way I can think to get around the problem with probing unknown devices is to have the device send unsolicited "hello" responses continually. That was you can just connect to all serial devices and listen for the "hellos". Connecting and listening to a serial device shouldn't ever mess it up.
The downside is you have these messages cluttering up your serial stream. You could have a "I'm here now, stfu" command but then you can only connect to it once.
FTDI chips have a method of identification but you have to use their library to access the data.

Serial ports not being recognized or found by PySerial

On my new Dell XPS laptop, I've just started loading all of my goodies on -- and I'm embarrassed to say that I can't get pyserial to work properly in Python 2.7. I ask it to do the following:
import serial
s = serial.Serial(0)
s.write("Hello world!")
s.close()
...and the darn thing gives me the following (code simplified to the real problem so you don't have to scrub through forests of my poorly written Python):
SerialException: could not open port COM1:
[Error 2] The system cannot find the file specified.
I've tried asking for a port explicitly (s = serial.Serial(port="COM1", baudrate=9600) but to no avail. I've also tried COM2, COM3, COM4, etc... But I'm not sure how high it's really worth going if they all return the same SerialException, ya dig?
The reason I mention my laptop model is because I've heard rumors that Dell is slowly killing off its serial connectivity, but quite frankly, I don't know nearly enough about serial-ness to even pretend to know what I'm talking about here. So I hereby bequeath this mission upon you experts.
Thanks for any help you can give!
Are you sure you have a serial port? Your description of the problem suggests that you don't. I'd be quite surprised if a laptop built within the last ten years would actually have one.
I could be wrong though (I'm trying to find it in the specs of the XPS as I write this). At any rate, if you're running Windows you can look in the Device Manager under Ports (COM & LPT)... although beware, some PCs will show a couple of serial ports here that are actually used for purely internal purposes.
If you don't have a serial port, you need to get a USB-to-serial adapter. Look in the Device Manager to find the name of the port.
You can get USB/serial adapters from some electronic stores or office supply stores, and off eBay.

Categories