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).
Related
i wrote a script in python for serial communication between my M5Stack Stick C (like raduino) and the raspberry pi.
all work fine. i can send "X","Y" or "Z" from raspberry py to the stick and he will reply the value (G-Force) back to the raspi! so far so good
Codes:
Python on raspy:
import serial
import time
import threading
ser = serial.Serial('/dev/rfcomm5') #init serial port
input_line = []#init input char array
def process_data(_data):
#called every time a sream is terminated by \n
#and the command string is ready to use
command = convert(_data)
print(command)
def convert(s): #convert the char list in a string
new = "" #init string to append all chars from char array
for x in s: # traverse in the string
new += str(x)
return new # return string
def processIncomingByte(inByte):#adding incoming chars to input_line
global input_line# globalize the input_line
if(inByte == '\n'):#if \n is incoming, end the chararray and release process data method
process_data(input_line)
input_line = []#reset input_line for next incoming string
elif(inByte == '\r'):
pass
else:#put all incoming chars in input_line
input_line.append(inByte)
while True:
while(ser.in_waiting > 0):#while some data is waiting to read....
processIncomingByte(ser.read())#.... process bytes whit method
ser.write(b'X\n')
time.sleep(0.5)
before the script work, i have to manually bind the m5Stak Stick-C over Blueman
to /dev/Rfcomm5. it work just fine over GUI or Console....
but now i would like to connect the stick via python to rfcomm5 (just by know the MAC adress, will be found in a config file later on...)
i startet to investigate a bit, but the more i research the more confused i am!!
i read some stuff over sockets and server-client aproaches. over a seperated script and so on....
i tested this code:
from bluetooth import *
target_name = "M5-Stick-C"
target_address = None
nearby_devices = discover_devices()
for address in nearby_devices:
if (target_name == lookup_name( address )):
target_address = address
break
if (target_address is not None):
print ("found target bluetooth device with address ", target_address)
else:
print ("could not find target bluetooth device nearby")
and indeed it found the device (just testing)!
but do i realy need to make a second script/process to connect to from my script?
is the the M5stack Stick-C the server? (i think so)
im so confused about all that stuff. i coded a lot, but never whit sockets, server-client stuff.
basically the communication (server/client?) works.
i just need to connect the device i found in the second script via macadress to rfcomm5 (or whatever rfcomm).
do i need a bluetooth socket? like in this example
https://gist.github.com/kevindoran/5428612
isnt the rfcomm the socket or am i wrong?
There are a number of layers that are used in the communication process and depending where you tap into that stack will depend what coding you need to do. The other complication is that BlueZ (the Bluetooth stack on linux) changed how it works over recent times leaving a lot of out of date information on the internet and easy for people to get confused.
With two Bluetooth devices, they need to establish a pairng. This is typically a one off provisioning step. This can be done with tools like Blueman or on the command line with bluetoothctl. Once you have a pairing established between your RPi and the M5Stack Stick, you shouldn't need to discover nearby devices again. Your script should just be able to connect if you tell it which device to connect to.
The M5Stack stick is advertising as having a Serial Port Profile (SPP). This is a layer on top of rfcomm.
There is a blog post about how this type of connection can be done with the standard Python3 installation: http://blog.kevindoran.co/bluetooth-programming-with-python-3/
My expectation is that you will only have to do the client.py on your RPi as the M5Stack Stick is the server. You will need to know its address and which port to connect on. Might be some trial and error on the port number (1 and 3 seem to be common).
Another library that I find helpful for SPP, is bluedot as it abstracts away some of the boilerplate code: https://bluedot.readthedocs.io/en/latest/btcommapi.html#bluetoothclient
So in summary, my recommendation is to use the standard Python Socket library or Bluedot. This will allow you to specify the address of the device you wish to connect to in your code and the underlying libraries will take care of making the connection and setting up the serial port (as long as you have already paired the two devices).
Example of what the above might look like with Bluedot
from bluedot.btcomm import BluetoothClient
from signal import pause
from time import sleep
# Callback to handle data
def data_received(data):
print(data)
sleep(0.5)
c.send("X\n")
# Make connection and establish serial connection
c = BluetoothClient("M5-Stick-C", data_received)
# Send initial requests
c.send("X\n")
# Cause the process to sleep until data received
pause()
Example using the Python socket library:
import socket
from time import sleep
# Device specific information
m5stick_addr = 'xx:xx:xx:xx:xx:xx'
port = 5 # This needs to match M5Stick setting
# Establish connection and setup serial communication
s = socket.socket(socket.AF_BLUETOOTH, socket.SOCK_STREAM, socket.BTPROTO_RFCOMM)
s.connect((m5stick_addr, port))
# Send and receive data
while True:
s.sendall(b'X\n')
data = s.recv(1024)
print(data)
sleep(0.5)
s.close()
I'm having difficulties getting pyserial to play nicely with a virtual port. I know this is an area which a few others have written about, but I couldn't find anything which solved my problem in those answers. Forgive me if I'm just being dense, and the solution exists ready-made elsewhere.
This is what I'm trying to achieve: I want to set up a virtual port, to which I can write data in one .py file, and from which I can then read data in another .py file. This is for the purposes of development and testing; I don't always have access to the device around which my current project is built.
This is my code so far:
dummy_serial.py
import os, pty, serial, time
master, slave = pty.openpty()
m_name = os.ttyname(master)
s_name = os.ttyname(slave)
# This tells us which ports "openpty" has happened to choose.
print("master: "+m_name)
print("slave: "+s_name)
ser = serial.Serial(s_name, 9600)
message = "Hello, world!"
encoded = message.encode("ascii")
while True:
ser.write(encoded)
time.sleep(1)
reader.py
import serial, time
# The port will change, depending on what port "openpty" (in the other file)
# happens to choose.
ser = serial.Serial("/dev/pts/1", 9600)
while True:
time.sleep(1)
incoming_bytes = ser.inWaiting()
# This print statement gives us an idea of what's going on.
print(incoming_bytes)
if incoming_bytes != 0:
data = ser.read(incoming_bytes)
print(data)
At present, dummy_serial.py seems to run okay. However, reader.py just keeps saying that there are no bytes waiting to be read, and hence reads no data.
What I would like:
An explanation of why ser.inWaiting() keeps returning 0, and a solution which makes ser.read(x) actually spit out "Hello, world!"
Or an explanation of why what I'm trying to do is fundamentally silly, and a better means of creating a writeable/readable virtual port.
I'm currently trying to set up a communication with a power supply (RND 320-KA3005P) through RS232 with pyserial.
The Problem is that after sending "*DIN?", which should return manufacturer, model name and software version, the connection seems to receive nothing. Also no other command was successful. Using the included software, after connecting, a LabView program opens and the device is remote controllable, so I think the problem is in my code.
Code Example:
Connection parameters as described in the manual.
As end-of-line characters I tried "\r\n","\r","\n" which seems to solve many other problems, but here with no success.
import io
import serial
import time
import sys
ser = serial.Serial('COM4',
baudrate = 9600,
bytesize=8,
timeout=1,
stopbits = serial.STOPBITS_ONE,
parity = serial.PARITY_NONE,
xonxoff = False)
eol_char = '\r\n'
sio = io.TextIOWrapper(io.BufferedReader(ser),newline=eol_char)
while True:
sending = input("type:\n")
ser.write((sending + eol_char).encode('utf-8'))
time.sleep(0.2)
ans = sio.read()
sys.stdout.write('received: ' + str(ans))
print('\ntry again\n')
Which after entering the command gives:
type:
*DIN?
received:
try again
type:
Also when I run the script on the loopback
ser = serial.serial_for_url('loop://',timeout=1)
...
output:
type:
*DIN?
received: *DIN?
try again
type:
it seems to work fine. I'm using Windows 10 and a USB to RS232 converter. Does anyone have an idea about what I'm missing here?
Thanks in advance.
The solution I figured out is rather disappointing. Turns out that with a USB to RS232 converter cable, instead of the USB connection I used before, the commands to set and get values work as expected. The command '*DIN?', which I tried initially, which I got from the manual:), still gives no reaction - probably just not supported.
I believe the *DIN? command, found under "Functionality check" which should return: "Manufacturer, model name, software version.", is actually a typo. When all commands are later listed in the manual, number 11 says *IDN? and should return the KA3005P identification.
It is also ironic that the example they give under number 11 says: "* IDN?"...
I'm working on making my own 3D mouse, sort of like 3D Connexion's own model. I'm using Autodesk's Fusion 360 as my application, and Fusion's API(Application Program Interface) to interact with the mouse. I have a gyroscope + accelerometer that spits raw measurements into an Arduino, which then sends those values(through the serial port using the Serial library). I'm then using the PySerial library(because Fusion's API uses python for it's language) to read that data coming from the Arduino, and spit it into Fusion, where I can control the camera position. The issue that I'm running into is that, for testing, I can print the values of the gyro + accelerometer to the Command Prompt just fine with a Python script. But when I try the exact same script in Fusion's code editor(Spyder), it doesn't work. I know that the Serial library is being imported, but the script doesn't work. Is it possible that Fusion 360 doesn't have access to the USB ports? If so, how could I fix it?
Here is a little snipet of my code:
#This is a python script that when run in the CMD, it works just fine.
import serial
ser = serial.Serial()
ser.baudrate = 9600
ser.port = 'COM3'
ser.open()
while True:
result = ser.readline()
result = str(result)
print (result)
All that this code does, is it reads the value of a potentiometer hooked up to an Arduino. It then prints those values into the CMD.
Here is the code that comes from the Fusion 360 API:
#This is the Fusion 360 code that doesn't work.
import adsk.core, adsk.fusion, adsk.cam, traceback
import serial
def get(app):
try:
ui = app.UserInterface
ser = serial.win32.Serial()
ser.baudrate = 9600
ser.port = 'COM3'
ser.open()
value = ser.read()
value = str(value)
adsk.doEvents()
ui.messageBox(value)
except:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def Main():
try:
app = adsk.core.Application.Get()
ui = app.UserInterface
ui.messageBox("Everything is working till this point")
adsk.doEvents()
get(app)
except:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
Main()
exit()
Thanks in advance!
In answer to your question, it may be so that Fusion360 does not have access to serial ports directly.
Fusion360 has a JavaScript API available and had to add custom calls to allow access to local machine resources like files.
(JavaScript does not have that natively).
That may be the nature of the problem you are up against, even though your program is written in Python. (I think F360 uses its JavaScript API to display its own User Interface).
Also, I see a difference between the code snippets:
ser = serial.win32.Serial()
...
ser = serial.Serial()
Perhaps you can call the first program from F360, and simply write to a file or something like that, to test it.
I've seen many code samples using the serial port and people say they are working codes too. The thing is, when I try the code it doesn't work.
import serial
ser = serial.Serial(
port=0,
baudrate=9600
# parity=serial.PARITY_ODD,
# stopbits=serial.STOPBITS_TWO,
# bytesize=serial.SEVENBITS
)
ser.open()
ser.isOpen()
print(ser.write(0xAA))
The error it gives me is : "SerialException: Port is already opened".
Is it me using python3.3 the problem or is there something additional I need to instal ? Is there any other way to use COM ports with Python3.3 ?
So the moral of the story is.. the port is opened when initialized. ser.open() fails because the serial port is already opened by the ser = serial.Serial(.....). And that is one thing.
The other problem up there is ser.write(0xAA) - I expected this to mean "send one byte 0xAA", what it actually did was send 170(0xAA) zeros. In function write, I saw the following :
data = bytes(data) where data is the argument you pass. it seems the function bytes() doesn't take strings as arguments so one cannot send strings directly with: serial.write(), but ser.write(bytearray(TheString,'ascii')) does the job.
Although I am considering adding:
if(type(data) == type('String')):
data = bytearray(data,'ascii')
in ser.write(), although that would make my code not work on other PCs.