How do I close serial connection when I interrupt code - python

I have the following Python code which is just reading from an Arduino, and writing to a file:
import serial
from datetime import datetime
import time
ser = serial.Serial('COM3', 9600, timeout=0)
text_file = open("voltages.txt", 'w')
while 1:
x=ser.readline()
print(str(datetime.now()), x)
data = [str(datetime.now()),x]
text_file.write("{}\n".format(data))
text_file.flush()
time.sleep(1)
Whenever I interrupt the script, I always have to type ser.close() in the console, otherwise I cannot start the script again (in the same console).
How can I close the serial communication automatically whenever I interrupt the script?

You can use the with statement.
import serial
from datetime import datetime
import time
with serial.Serial('COM3', 9600, timeout=0) as ser, open("voltages.txt", 'w') as text_file:
while True:
x=ser.readline()
print(str(datetime.now()), x)
data = [str(datetime.now()),x]
text_file.write("{}\n".format(data))
text_file.flush()
time.sleep(1)
The with statement will automatically close ser and text_file when execution leaves the with statement. This can be an exception in your case. The with statement has been introduced by PEP 343 to remove try/finaly statements by using context managers.

Use a try/finally block to insure close is called:
try:
while 1:
x = ser.readline()
....
finally:
ser.close()

Related

Incoming serial data is not getting printed on the console using pyserial

I am building a GUI in Tkinter and I want to receive incoming data from Serial (from a microcontroller).
Also, I want to print that out onto the text-editor widget in Tkinter (similar to the serial monitor console).
For that, I am right now trying to write a receiving code in Python without Tkinter but I am unable to receive any data in the console.
The code is as follows:
import serial
import time
import threading
global serial_open
serial_open = False
def serial_read():
global ser
global var
while True:
if serial_open == True:
var = ser.readline()
if var != "":
print(var)
else:
pass
ser = serial.Serial('COM3', baudrate = 19200)
time.sleep(3)
serial_open = True
print("COM3 Connected")
threading.Thread(target = serial_read).start()
time.sleep(10)
ser.close()
print("Disconnected")

How to use pyserial expect on a string from serial console, then send a charater

I'm a new to python, hoping someone knows something about the issue I have. I want to control a device using serial console. A command will be sent to reboot the device, while the device is rebooting, a string is printed. I want to catch the string and then send a character "h" which will abort the reboot. Code looks like this
#! /bin/env python
import serial
import sys
import pexpect
from time import sleep
from serial import SerialException
ser = serial.Serial()
ser.baudrate = 9600
ser.port="/dev/ttyUSB0"
ser.stopbits=serial.STOPBITS_ONE
ser.xonxoff=0
try:
ser.open()
except:
sys.exit ("Error opening port")
print "Serial port opened"
ser.write('rebootnow\r')
temp = ser.expect('press h now to abort reboot..')
if i == 0:
print ('Gotcha, pressing h')
ser.sendline('h')
print ('Reboot aborted')
else:
print ('Ouch, got nothing')
time.sleep(1)
ser.close()
exit()
When I run the code, I get the error
AttributeError: 'Serial' object has no attribute 'expect'
at line
temp = ser.expect('press h now to abort reboot..')
Any ideas?
This thread is quite old, but I hope my answer can still be useful to you.
The expect methods are from pexpect, not from pyserial. You should do something like:
temp = pexpect.fdpexpect.fdspawn(ser)
temp.expect("message")
Basically, from temp onwards you should call methods on the temp object, not on the serial object. This includes the sendline() call, later on.

atexit not writing to file

I'm testing to see if the atexit module runs properly by attempting to print to file. The actual purpose of atexit will be to close the open port, however this is to test if atexit is working in the first place. However, atexit is not printing to file, any ideas why not? Any help is appreciated.
import atexit
import scala5
from scala5 import sharedvars
scalavars = sharedvars()
import serial
myPort=serial.Serial()
myPort.baudrate = 9600
myPort.port = "COM4"
myPort.parity=serial.PARITY_NONE
myPort.stopbits=serial.STOPBITS_ONE
myPort.bytesize=serial.EIGHTBITS
myPort.timeout=2
myPort.writeTimeout=2
try:
myPort.close()
except:
print("didn't need to close")
def port_exit():
fo = open("test.txt", "w")
fo.write("This works!")
fo.close()
myPort.open()
while True:
x = myPort.readline()
if x == "1\r\n":
scalavars.door = x
scala5.ScalaPlayer.Sleep(10)
atexit.register(port_exit)
port_exit()
You are registering the function after your while loop, which I am assuming you are exiting by killing the script (meaning the function port_exit isn't registered). If you register the function before the while then it should trigger and write the file when the script exits.
atexit.register(port_exit)
while True:
x = myPort.readline()
if x == "1\r\n":
scalavars.door = x
scala5.ScalaPlayer.Sleep(10)

Python + Raspberry Pi - PY-SERIAL ISSUE - why doesn't baud rate matter in my code?

MY PROBLEM:
The code below achieves all of my goals for my application except for one problem: my tkinter spinbox sets baud rate and functions correctly (i think). It populates the pyserial baud rate target variable, but no matter what baud rate I specify in the spinbox, the serial communication i receive from my arduino at 9600 continues to be received and displayed fine. (SHOULDN'T IT FAIL IF I SET BAUD TO DIFFERENT RATE?)
I've pared down the code upon request. Below is the code without any UI, simply printing serial data received to the terminal. No matter what I set the baud rate to, it continues to work fine. But the arduino sending serial data is definitely sending at 9600.
#!/usr/bin/env python
#Import Libraries--------------------------
import serial
import Tkinter as tk
from time import sleep
import threading
import sys
import glob
import smtplib
import csv
import tkMessageBox
import datetime
#Setup--------------------------
data = True #I'm not sure what this is for
#Thread--------------------------
class SensorThread(threading.Thread):
def run(self):
try:
i = 0
while True:
ser = serial.Serial('/dev/ttyACM0', 90000)
happenen = ser.readline()
print(happenen)
i += 1
sleep(1)
except KeyboardInterrupt:
exit()
#------------------------------------------------------------------
if __name__ == "__main__":
SensorThread().start()
#------------------------------------------------------------------

python serial 100% cpu

A year ago I needed a script to capture input from a serial device and send it to a web browser. (A touch sensor attached to a 3d printed Egyptian tablet in a Museum.) I had originally intended to use Perl but as that wasn't playing ball and I only had a few hours before launch I opted for Python (I'm not a python dev). I made a script that worked fine and has been for some time, with the only issue being that the script uses 100% CPU. How can I get Python to read from the serial port without using the whole CPU, while still bring responsive regardless of when the input is pressed?
My script is below:
#!/usr/bin/python
import time
import serial
import sys
from subprocess import call
import traceback
myport = 0
ser = serial.Serial()
def readkey():
while 1:
out = '';
while ser.inWaiting() > 0:
out += ser.read(1);
if out != '\xf8' and out != '\xf9':
call(["xdotool", "key", "F8"])
call(["xdotool", "type", str(ord(out))])
call(["xdotool", "key", "F9"])
out = ''
def main_sys():
global ser
print "Opening Stela serial port"
ser.open();
ser.isOpen();
print "Starting Stela subsystem"
while 1:
try:
readkey()
break
except Exception as e:
print "caught os error".format(e)
time.sleep(1)
main_sys()
def init():
global ser
global myport
while 1:
try:
theport = '/dev/ttyACM'+str(myport)
print "Trying " + theport
ser = serial.Serial(
port=theport,
baudrate=115200,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS
)
main_sys()
break;
except Exception as e:
traceback.print_exc()
myport += 1
if myport > 5:
myport = 0
time.sleep(1)
init()
init()
Add a time.sleep for a short period at the end of your readKey-loop. It will let other processes run.
Also, be aware that call is blocking until the operation is finished.
I would try to avoid the ser.inWaiting() call. Instead I'd directly call ser.read(1). This way the call will block until more data is available. During this time the CPU will be free to do other things:
def readkey()
out = ser.read(1);
if out != '\xf8' and out != '\xf9':
call(["xdotool", "key", "F8"])
call(["xdotool", "type", str(ord(out))])
call(["xdotool", "key", "F9"])
This should behave identically but with almost no CPU load.

Categories