How do I correctly manage the serial resource? [Python] - python

I'm working on an automation project with python and an Arduino. I need to run a test that ensures that the Arduino is connected (and not some other device). I have a simple test case that works, but doesn't manage the serial object's memory at all. I've got a solution that I feel should work, however it causes an issue with the serial connection. The result is that the first read returns an empty string. Subsequent reads return the correct value. For my application I need it to read correctly on the first try.
Here is the test code I used. This code works just fine (it's how I know the issue isn't on the Arduino side of things)
ser = serial.Serial('/dev/tty.usbmodem14101', 9600, timeout=1)
ser.flush()
while True:
text = input("Input: ")
ser.write(bytes(text, 'utf-8'))
time.sleep(0.2)
line = ser.readline().decode('utf-8').rstrip()
print(line)
This is my attempt at creating a better way to manage the serial resource using the context manager and the with statement. I tried 2 things. The ArduinoWith class only returns false (and prints and empty string) with no indication of any input on the Arduino. The ArduinoSelf has the same result the first time, but returns True the second time.
import serial
import time
from contextlib import contextmanager
#contextmanager
def arduino_serial_connection(path, rate, timeout=1):
connection = serial.Serial(path, rate, timeout=timeout)
yield connection
connection.close()
class ArduinoWith:
def __init__(self):
pass
def connection_test(self):
try:
with arduino_serial_connection('/dev/tty.usbmodem14101', 9600) as connection:
connection.write(b"S\n")
time.sleep(0.2)
answer = connection.readline().decode('utf-8').rstrip()#connection.read(8)
if answer == "H":
return True
else:
print("Answer: \"" + answer +"\"")
except serial.serialutil.SerialTimeoutException:
print("Timeout")
except Exception:
print("Execption")
return False
class ArduinoSelf:
def __init__(self):
self.ser = serial.Serial('/dev/tty.usbmodem14101', 9600, timeout=1)
def connection_test(self):
try:
self.ser.write(b"S\n")
time.sleep(0.2)
answer = self.ser.readline().decode('utf-8').rstrip()#connection.read(8)
if answer == "H":
return True
else:
print("Answer: \"" + answer +"\"")
except serial.serialutil.SerialTimeoutException:
print("Timeout")
except Exception:
print("Execption")
return False
a1 = ArduinoWith()
print(a1.connection_test())
time.sleep(1)
a2 = ArduinoSelf()
print(a2.connection_test())
time.sleep(1)
print(a2.connection_test())
I'm not exactly sure why running the method a second time on the ArduinoSelf class worked, but it did and that made me think there must be something that I'm not initializing correctly.

Related

Scapy variable sniff stop

i found a similar problem:
(Instance variables not being updated Python when using Multiprocessing),
but still do not know the solutionn for my task.
The task is to stop a scapy sniff function after the completness of a testskript. the running duration of single testscripts can vary greatly (from some seconds till hours). My sniff function runs in a separate threat. The testscript calls an init Funktion in the beginning which calls the sniff Function from an other modul.
#classmethod
def SaveFullTrafficPcap(self, TestCase, Termination):
try:
Full_Traffic = []
PktList = []
FullPcapName = Settings['GeneralSettings']['ResultsPath']+TestCase.TestCaseName +"Full_Traffic_PCAP.pcap"
#while Term.Termination < 1:
Full_Traffic = sniff(lfilter=None, iface=str(Settings['GeneralSettings']['EthInterface']), store=True, prn = lambda x: Full_Traffic.append(x), count=0, timeout=Term.Termination)
print(Full_Traffic)
wrpcap(FullPcapName, Full_Traffic)
except(Exception):
SYS.ABS_print("No full traffic PCAP file wirtten!\n")
At the end of the testscript an exit function is called. In the exit function I set Term.Termination parameter to 1 and wait for 5 sec, but it doesnt work. The sniff function is stoped by the system and i get no file"FullPCAPName"
If count or timeout get a value, the code works without problemms and i get my FullPCAPName file with he complet traffic on my Interface.
Have anybody hinds how i can stopt the sniff function regulary after finisching the testscript?
Use of the stop_filter command as specified here worked for me. I've duplicated HenningCash's code below for convenience:
import time, threading
from scapy.all import sniff
e = threading.Event()
def _sniff(e):
a = sniff(filter="tcp port 80", stop_filter=lambda p: e.is_set())
print("Stopped after %i packets" % len(a))
print("Start capturing thread")
t = threading.Thread(target=_sniff, args=(e,))
t.start()
time.sleep(3)
print("Try to shutdown capturing...")
e.set()
# This will run until you send a HTTP request somewhere
# There is no way to exit clean if no package is received
while True:
t.join(2)
if t.is_alive():
print("Thread is still running...")
else:
break
print("Shutdown complete!")
However, you still have to wait for a final packet to be sniffed, which might not be ideal in your scenario.
now i solved the problem with global variables. It is not nice, but it works well.
Nevertheless I am interested in a better solution for the variable sniff stop.
stop_var = ""
def stop():
global stop_var
stop_var.stop()
def start():
"""
your code
"""
global stop_var
stop_var = AsyncSniffer(**arg)
stop_var=start()

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.

Cherrypy and JSON Arduino web interface

EDIT #2: So guess what, I'm almost there! I am facing what seems to be the last of my problems, well, as long as programming is concerned.
This is actually very interesting, I never ran into such a thing before.
The matter is in the following code, my javascript function. I usually never post for problems that seem very simple to solve, but I really have no idea what's going on here.
The issue seems to be in the first condition of the update function. See the line that says alert('hey'); ? Well, if I erase that line, for some unknown reason, nothing is sent to the action function. Nor to the Arduino, to the console... Just nothing happens. It's absolutely fascinating, as I like to call it. I have no idea. I thought maybe the alert() created some kind of delay that was necessary to read the arduino output, but when i create a delay with setTimeout, nothing happens either. It's incredible.
Just one more time: without the alert, the action function is not called, I checked by making the function print something if it's called. Nothing is printed, nothing. It's just not called. But with the alert, the function is called and the arduino turns on the LED.
Do you have any explanation? Here is my code:
function update(command=0) {
// if command send it
if (command!=0) {
$.getJSON('/action?command='+command);
alert('hey');
}
// read no matter what
$.getJSON('/read', {}, function(data) {
if (data.state != 'failure' && data.content != '') {
$('.notice').text(data.content);
$('.notice').hide().fadeIn('slow');
setTimeout(function () { $('.notice').fadeOut(1000); }, 1500);
}
setTimeout(update, 5000);
});
}
update();
I am attempting to create a web interface accessible from any computer to control my Arduino.
I'm getting closer. One of my problems is that, using the following code, when I press a button to send a command to the Arduino, the Arduino does get it (the LED blinks as configured), then does send a message, and the Python script does retrieve the data, but it does NOT display it properly. The string misses some characters, and index.html is not returned as desired.
Basically, a function is called when a button is pressed, and I need to return the result of the function in a different function than the one the result was generated from.
Here is the code:
# -*- coding: utf-8 -*-
import cherrypy, functools, json, uuid, serial, threading, webbrowser, time
try:
ser = serial.Serial('COM4', 9600)
time.sleep(2)
ser.write('1')
except:
print('Arduino not detected. Moving on')
INDEX_HTML = open('index.html', 'r').read()
def timeout(func, args = (), kwargs = {}, timeout_duration = 10, default = None):
class InterruptableThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.result = default
def run(self):
self.result = func(*args, **kwargs)
it = InterruptableThread()
it.start()
it.join(timeout_duration)
if it.isAlive():
return it.result
else:
return it.result
def get_byte(useless):
return ser.read().encode('Utf-8')
def json_yield(command):
#functools.wraps(command)
def _(self, command):
if (command == 'Turn the LED on'):
ser.write('2')
time.sleep(2)
print('wrote to port')
print('ok, ok')
try:
m = ''
while 1:
print('reading...')
byte = timeout(get_byte, ('none',), timeout_duration = 5)
if byte == '*' or byte == None: break
m = m + byte
content = m
time.sleep(1)
return json.dumps({'state': 'ready', 'content':content})
except StopIteration:
return json.dumps({'state': 'done', 'content': None})
return _
class DemoServer(object):
#cherrypy.expose
def index(self):
return INDEX_HTML
#cherrypy.expose
#json_yield
def yell(self):
yield 'nothing'
#cherrypy.expose
#json_yield
def command(self):
yield 'nothing'
if __name__ == '__main__':
t = threading.Timer(0.5, webbrowser.open, args=('http://localhost:8080',))
t.daemon = True
t.start()
cherrypy.quickstart(DemoServer(), config='config.conf')
First of all, I did'nt think about telling you about that in your previous question, but a while back I wrote a software called pyaler that does exactly what you want (except it did not support long polling requests, as it is (was?) a wsgi limitation).
To answer your question, why don't you make your form a javascript query that sends the action, and gets the result as JSON that you can parse and update your current page with the result? That's way more elegant, simple and 2013...
Sadly I can't really tell, given your code, why you're not getting the result. It's too much over-sophisticated to understand if it really does what you want it to do... KISS!
Avoid doing anything at the module scope, except if you put it in if __name__ == "__main__", or the day you'll want to extend your module by importing it, you'll execute incidentally some code, and it forces making a better design for your code.
You can take the InterruptableThread() class out of your timeout function, and give it the default as a parameter. InterruptableThread(default) and def __init__(self, default): self.result = default. But talking about that part, why do you do such over-sophisticated thing whereas you got a timeout argument you can use in when you create your serial connection?
Here is a slight modification I'd make to your code:
# -*- coding: utf-8 -*-
import cherrypy, functools, json, uuid, serial, threading, webbrowser, time
def arduino_connect(timeout=0):
try:
ser=serial.Serial('COM4', 9600, timeout=timeout)
time.sleep(2)
ser.write('1')
return ser
except:
raise Exception('Arduino not detected. Moving on')
class ArduinoActions(object):
def __init__(self, ser):
self.ser = ser
def get_data(self):
content = ""
while True:
print('reading...')
data = self.ser.read().encode('utf-8')
if not data or data == '*':
return content
content += data
def turn_led_on(self):
ser.write('2')
time.sleep(2)
print('wrote led on to port')
def turn_led_off(self):
ser.write('2') # Replace with the value to tur the led off
time.sleep(2)
print('wrote to led off port')
class DemoServer(ArduinoActions):
def __init__(self, ser):
ArduinoActions.__init__(self, ser)
with open('index.html', 'r') as f:
self.index_template = f.read()
#cherrypy.expose
def index(self):
return self.index_template
#cherrypy.expose
def action(self, command):
state = 'ready'
if command == "on":
content = self.turn_led_on()
elif command == "off":
content = self.turn_led_off()
else:
content = 'unknown action'
state = 'failure'
return {'state': state, 'content': content}
#cherrypy.expose
def read(self):
content = self.get_data()
time.sleep(1)
# set content-type to 'application/javascript'
return content
if __name__ == '__main__':
ser = arduino_connect(5)
# t = threading.Timer(0.5, webbrowser.open, args=('http://localhost:8080',))
# t.daemon = True
# t.start()
cherrypy.quickstart(DemoServer(), config='config.conf')
In your javascript code, you you're calling the yell resource that returns literally nothing.
You'd better make an action method (as I modified the given python code) and a separate read() method.
So you the action method will act on the arduino by writing bytes, thus commands to the arduino, and the
read method will read the output.
As the webserver may create parallel calls to the read/write method on the serial object, and
you can't read in parallel the same object, you may want to make an independant thread, by creating
a new class that inherits from threading.Thread, you implement an infinite loop reading the output
of the serial (by giving the serial object as parameter to that class' __init__ function).
Then you push each new content data to a list, if you want to keep a log of all previous data, or
a Queue.Queue if you want only the last reading returned. Then from ArduinoActions, in the read()
method you just need to return that list that will grow with any new reading from the arduino, and
thus make a log of all the data (or get the last data if you have a queue).
$(function() {
function update(command) {
// if you give a command argument to the function, it will send a command
if (command) {
$.getJSON('/action?command='+command);
}
// then it reads the output
$.getJSON('/read, {}, function(data) {
if (data.state !== 'failure' && data.content !== '') {
$('.notice').text(data.content);
$('.notice').hide().fadeIn('fast');
setTimeout(function () { $('.notice').fadeOut('fast'); }, 1500);
}
// and rearms the current function so it refreshes the value
setTimeout(update(), 2); // you can make the updates less often, you don't need to flood your webserver and anyway the arduino reading are blocking
});
}
update();
});
Always use === or !== in javascript so type don't get coerced. You can call again the function less often
If you do not valuate a javascript argument, it is set to undefined per default.
That's only a little update on what you wrote, it's getting late now, so I hope you'll make something nice out of that!
HTH

Stop python program

I want to write a program for my network course and i have a socket that listen to receive data if it listen and receive no data i should terminate the program, i use threading.Timer to act like timer and have a line like t = threading.Timer(5, end_data) in my function that listen for receive data but i cant terminate program in end_data that is:
def end_data():
sys.exit()
can any one help me?
i also test below code bud did not terminate running program in terminal :(
def end_data():
try:
sys.exit()
except:
print"exception"
i expect that when stop terminal print Tinas-MacBook-Pro:~ tina$
i'm listening to socket in function named receive not main and when elapse 5 second with no data receiving it will run end_data and seems never return to receive function that part of this function is like below
def receive():
s2 = socket(AF_INET, SOCK_DGRAM)
s2.bind(addr3_2)
global end_call
global base_window
write=open('pictur.jpg','wb')
s = socket(AF_INET, SOCK_DGRAM)
s.bind(addr3)
while(end_call==0):
t = threading.Timer(5, end_data)
t.start()
recv_data, addr = s.recvfrom(3500)#size ro hala badan check kon
t.cancel()
first i decide to set global var end_call after 5 second but it didn't work because it never come back to receive function
some thing that is very interesting for me is if define data_end like:
def end_data():
os._exit
print "Hi"
Hi will print in output :O
Maybe try a setup like this
run_program = True
def end_data() :
global run_program
run_program = False
t = threading.Timer(5, end_data)
t.start()
while run_program:
#do stuff
time.sleep(1)
t.join() #maybe not needed
Make sure you called t.start() :)
To answer your second question, with try, except.
This is the expected behavior per the pydoc:
"Since exit() ultimately “only” raises an exception, it will only exit
the process when called from the main thread, and the exception is not intercepted."
Example:
def end_data():
try:
sys.exit("error!")
except SystemExit, e:
print "Exception caught: ", e.args
print "begin"
end_data()
print "end"
Output:
$ ./test.py
begin
Exception caught: ('error!',)
end
What about using threading Events ?
import threading
event = threading.Event()
def signal_stop():
try:
# do what you have to do
finally:
event.set()
t = threading.Timer(5, signal_stop)
print 'start thread and wait for it'
t.start()
event.wait()
print 'done'

Run a python server on a background

I am using gevent-socketio to to push real time data from a python script to a browser. However I would like it to work interactively from the interpreter so that the server would continue to run in the background. Some pseudo code:
#start server with the application and keep it running on the background
server_inst=MyAppServer()
#send data to the sever that pushes it to a browser (prints "foo" to the browser)
server_inst.send_data('foo')
Having looked at threading I am still confused how it could/should be done. Any pointers appriciated.
Is there a reason to have the server and the console program as the same process?
If not I would suggest the use of two separate processes and named pipes. If this solution is not ideal for whatever reason can you please give more details.
Anyway, here is some code that might be useful to you for implementing a Pipe class. I'm not sure exactly what form the data that you need to communicate looks like so you could make the Pipe class abstract some simple protocol and/or use pickle.
def __init__(self,sPath):
"""
create the fifo. if it already exists just associate with it
"""
self.sPath = sPath
if not os.path.exists(sPath):
try:
os.mkfifo(sPath)
except:
raise Exception('cannot mkfifo at path \n {0}'.format(sPath))
self.iFH = os.open(sPath,os.O_RDWR | os.O_NONBLOCK)
self.iFHBlocking = os.open(sPath,os.O_RDWR)
def base_read(self,iLen,blocking=False):
iFile = self.iFHBlocking if blocking else self.iFH
while not self.ready_for_reading():
import time
time.sleep(0.5)
lBytes = ''.encode('utf-8')
while len(lBytes)<iLen:
self.lock()
try:
lBytes += os.read(iFile,1)
self.unlock()
except OSError as e:
self.unlock()
if e.errno == 11:
import time
time.sleep(0.5)
else:
raise e
return lBytes
def base_write(self,lBytes,blocking = False):
iFile = self.iFHBlocking if blocking else self.iFH
while not self.ready_for_writing():
import time
time.sleep(0.5)
while True:
self.lock()
try:
iBytesWritten = os.write(iFile, lBytes)
self.unlock()
break
except OSError as e:
self.unlock()
if e.errno in [35,11]:
import time
time.sleep(0.5)
else:
raise
if iBytesWritten < len(lBytes):
self.base_write(lBytes[iBytesWritten:],blocking)
def get_lock_dir(self):
return '{0}_lockdir__'.format(self.sPath)
def lock(self):
while True:
try:
os.mkdir(self.get_lock_dir())
return
except OSError as e:
if e.errno != 17:
raise e
def unlock(self):
try:
os.rmdir(self.get_lock_dir())
except OSError as e:
if e.errno != 2:
raise e
def ready_for_reading(self):
lR,lW,lX = select.select([self.iFH,],[],[],self.iTimeout)
if not lR:
return False
lR,lW,lX = select.select([self.iFHBlocking],[],[],self.iTimeout)
if not lR:
return False
return True
def ready_for_writing(self):
lR,lW,lX = select.select([],[self.iFH,],[],self.iTimeout)
if not lW:
return False
return True
I have a more complete implementation of the class that I am willing to share but I think the protocol I abstract with it would probably be useless to you... How you would use this is create an instance in your server and your console application using the same path. You then use base_read and base_write to do the dirty work. All the other stuff here is to avoid weird race conditions.
Hope this helps.

Categories