i have a Tkinter GUI and the acceleration sensor MMA8452Q. I set up the sensor that if a threshold is reached, a interrupt flag gets set and data for a specific period of time gets collected and plotted through the GUI.
Sometimes when I read the data or at any other point of using an I2C read function to read a register i get the error "I2C read failed" (when the error happens in the check_flags() method) and sometimes I get something like this:
interrupt 1 True
4 0 1810275105
Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python3.7/threading.py", line 917, in _bootstrap_inner
self.run()
File "/usr/lib/python3.7/threading.py", line 865, in run
self._target(*self._args, **self._kwargs)
File "/home/pi/.../tkinter/mix/mix_interrupt.py", line 307, in check_flags
pi.i2c_read_byte_data(acc_sensor, FF_MT_SRC)
File "/usr/lib/python3/dist-packages/pigpio.py", line 2874, in i2c_read_byte_data
return _u2i(_pigpio_command(self.sl, _PI_CMD_I2CRB, handle, reg))
File "/usr/lib/python3/dist-packages/pigpio.py", line 1011, in _u2i
raise error(error_text(v))
pigpio.error: 'I2C read failed'
Is there maybe something wrong with my code or should i could the baudrate be the fault of this problem? It doesn't happen all the time, but seemingly at random times. Just to clarify, if the error happens in the try-except-block, my data still gets plotted, but some data gets missing and the plot looks sometimes incomplete
My important code parts:
This is the thread:
threading.Thread(target=self.check_flags).start() #This is in the init method
This function runs in a thread like this and checks if self.interrupt_flag is set
def check_flags(self):
while(True):
#print("check the flags")
if (self.interrupt_flag == True):
pi.i2c_read_byte_data(acc_sensor, FF_MT_SRC)
global scale_variable
self.x_graph = []
self.y1_graph = []
self.y2_graph = []
self.y3_graph = []
a = datetime.datetime.now()
one_time = True
start_time = time.time()*1000
while(time.time()*1000-start_time) < self.timeframe:
try:
b = datetime.datetime.now()
delta = b-a
y1 = self.readACCx()
y2 = self.readACCy()
y3 = self.readACCz()
self.y1_graph.append(y1)
self.y2_graph.append(y2)
self.y3_graph.append(y3)
self.x_graph.append(delta.total_seconds()*1000)
if (y1 > self.threshold or y2 > self.threshold): # this is for catching the whole movement if its longer than the set timeframe
self.timeframe += 5
except pigpio.error as e:
print("error:", e)
self.save_data()
self.plot_data()
self.interrupt_flag = False
This is one of the functions i use to read sensor data. The other two are almost identical
def readACCx(self): #reads x axis value 12 bit
global scale_variable
comp_acc_x2 = pi.i2c_read_byte_data(acc_sensor, OUT_X_MSB)
comp_acc_x1 = pi.i2c_read_byte_data(acc_sensor, OUT_X_LSB)
acc_combined = ((comp_acc_x2 << 8) | comp_acc_x1) >>4
if acc_combined < 2048:
acc_combined = acc_combined*scale_variable
return acc_combined
else:
acc_combined = acc_combined - 4096
acc_combined = acc_combined*scale_variable
return acc_combined
This is my interrupt routine:
def my_callback1(self, gpio, level, tick): #first interrupt function
self.interrupt_flag = True
print("interrupt 1", self.interrupt_flag)
print(gpio, level, tick)
EDIT (ALMOST PERFECT) WORKING CODE:
protect = threading.Lock() # this is outside of any class at the top
def check_flags(self): # new data capture method
while(True):
#print("check the flags")
if (self.interrupt_flag == True):
protect.acquire()
ff_flag = pi.i2c_read_byte_data(acc_sensor, FF_MT_SRC)
print("interrupt cleared", ff_flag)
global scale_variable
self.x_graph = []
self.y1_graph = []
self.y2_graph = []
self.y3_graph = []
a = datetime.datetime.now()
one_time = True
start_time = time.time()*1000
while(time.time()*1000-start_time) < self.timeframe:
try:
b = datetime.datetime.now()
delta = b-a
y1 = self.readACCx()
y2 = self.readACCy()
y3 = self.readACCz()
self.y1_graph.append(y1)
self.y2_graph.append(y2)
self.y3_graph.append(y3)
self.x_graph.append(delta.total_seconds()*1000)
if (y1 > self.threshold or y2 > self.threshold):
self.timeframe += 5
except pigpio.error as e:
print("error:", e)
self.save_data()
self.plot_data()
self.interrupt_flag = False
self.timeframe = self.config.getint('setting', 'timeframe')
print("scale_variable: ", scale_variable)
protect.release()
Your traceback doesn't match your code. The traceback shows the i2c_read_byte_data result not being saved anywhere. The I2C clock must be right, otherwise you'd get nothing at all.
The problem is probably synchronization. When you said "interrupt", that raised a red flag. If your interrupt handler tries to do an I2C operation while your mainline code is also doing one, the two will clash, especially since you have to do MSB and LSB separately. I suggest you use a threading.Lock to make sure your sequences aren't interrupted.
Related
I am currently working working on a program that tries to replicate labview functionality by establishing a connection to SR830 via RS-232. For some reasons, I am not able to record data and I cannot seem to figure out why. I tried to google about it but I did not find anything. If someone could please help me figure out what I am doing wrong.
Here is my code:
import serial
import tkinter as tk
from datetime import datetime
import time
import os
import atexit
import threading
import numpy as np
import math
import matplotlib
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation
class SR830:
'''SR830 Class creates an object that can interface with and control an SR830 via RS-232.
Class data structure allows the SR830 code to be imported into other scripts via "from SSTR_Serial import SR830"
This can be used to encapsulate SR830 interface mechanics into a larger control scheme...
by passing the class a frame to pack itself into as the frame argument.'''
def command(self, command_string, command_value): #Basic command protocol, can be used as a template for other command functions.
#Note: queries should have null command_value arguments (command_value = '').
packet = command_string+str(command_value)+'\r' #Can be adjusted to include checksums/parity if needed.
try:
self.inst.write(packet.encode('ascii'))
except AttributeError as AE:
print(AE)
def create_log(self):
try: #Makes log file if it does not exist already.
log_dir = self.name+'_logs'
os.mkdir(log_dir)
except OSError:#Log file already exists
pass
self.log_open = datetime.now() #Var to hold time log was created (Different from when timer is started.)
date = str(self.log_open).split(' ')[0] #Date that log file was opened.
h = 0
a = False
while not a:
h += 1
log_str = log_dir+'/'+self.name+"_"+date+'_'+str(h)+'.txt'
log_path = os.path.join(os.getcwd(), log_str)
if os.path.exists(log_path):
pass
else:
a = True
self.log = open(log_str, "w+")
self.log.write(self.name+" log created on "+ str(self.log_open)+"\nTime \tShift (Degrees)\tAmplitude (mV)\tR ()\tTheta ()\n") #Writing opening lines.
def start_graph(self): #Should be threaded at begining. This begins making the graph and recording data when the start/record buttons are pressed.
def get_vals(self):
current_time = time.time()#Get current time
if (current_time - self.previous_time) >= self.sample_time: #Time is equal or passed sampling time.
time_elapsed = current_time - self.start_time #Total time elapsed since transmission begun.
#Transmission zone:
try: #Querying and storing commands below
self.command("PHAS?",'')
phase_shift = self.inst.read(5).decode('ascii').replace('\r','')
self.command('SLVL?','')
phase_amplitude = self.inst.read(5).decode('ascii').replace('\r','')
self.shifts = np.append(self.shifts,[phase_shift]); self.amplitudes = np.append(self.amplitudes,[phase_amplitude]); self.times = np.append(self.times,[time_elapsed]) #Will only append time if both other arrays are appended.
#^ Append on one line to keep all lengths consistant when switching threads.
#Variables that are queried but not logged.
self.command('OEXP?1','')
R = self.inst.read(5).decode('ascii').replace('\r','')
self.command('OEXP?2','')
Theta = self.inst.read(5).decode('ascii').replace('\r','')
except AttributeError as AE:#Exceptions for testing without connection to device
print(AE)
'''
#This area generates random sine waves. Only use for testing without connection to actual device.---------
self.times = np.append(self.times,[len(self.times)])
self.shifts = np.append(self.shifts,[math.sin(len(self.times))])
self.amplitudes = np.append(self.amplitudes, [5*math.cos(len(self.times))])
phase_shift = 5
phase_amplitude = 7
R = 10
Theta = 11
print(str(len(self.times)))
print(str(len(self.shifts)))
print(str(len(self.amplitudes)))
#End test zone ---------------------------------------------------------------------------------------
'''
#Remove extra elements to reduce memeory usage for long runs
while len(self.times) > 200: #Integer on RHS of inequality is max number of elements.
self.shifts = self.shifts[2:-1:1]; self.amplitudes = self.amplitudes[2:-1:1]; self.times=self.times[2:-1:1]
#Log writing zone:
if self.recording is True: #Updates log, otherwise only updates graph.
self.log.write(str(round(time_elapsed-self.pause_time,4))+'\t'+str(phase_shift)+'\t\t'+str(phase_amplitude)+'\t\t'+str(R)+'\t'+str(Theta)+'\n')
self.previous_time = current_time #Resets time value for calculating interval.
while self.running: #Loops as long as running is true and kills thread once runing is false.
get_vals(self)
def animate(self, i): #Only redraw matplotlib aspects that get cleared with a.clear() and a2.clear() to conserve memory.
#Todo - optimize drawing order
try:
self.a.clear()
self.a.plot(self.times,self.shifts, label='Phase shift ()', color='tab:blue')
self.a.set_ylabel('Shift')
self.a.set_ylabel('Shift', color='tab:blue')
self.a.set_xlabel('Time (s)')
self.a.legend(loc='upper left')
self.a2.clear()
self.a2.plot(self.times,self.amplitudes,label='Phase amplitude ()',color='tab:red')
self.a2.set_ylabel('Amplitude', color='tab:red')
self.a2.legend(loc='upper right')
self.a.get_yaxis_transform()#Fits axis to widget area.
except ValueError as VE:
print(VE)
def clear_graph(self): #Clears all arrays to make graph reset. Does not erase logged data.
self.times = np.array([])
self.shifts = np.array([])
self.amplitudes = np.array([])
def startstop(self): #Flips button to begin or stop graphing.
if self.running is False: #Recording and graphing is off. Calling function will turn it on and activate the following lines
self.running = True
self.clear_graph() #Clears last run.
self.run_btn.config(text='Stop', bg='red')
#self.start_graph()
self.sample_time = float(self.sample_box.get())
self.pause_time = 0 #Resets pause adjustment time.
self.start_time = time.time() #Resets start time. TODO-Add adjustments for pauses
self.pause_point = time.time() #Last time that pause activated. Used to calculate pause_time later.
self.graph_thread = threading.Thread(target=self.start_graph)
self.graph_thread.start()
else: #graphing is on. Calling function will turn it off and activate the following line
self.running = False
self.run_btn.config(text='Start',bg='green')
self.recording=False
self.record_btn.config(text='Start recording', bg="green")
try: #Shuts down log file if it exists.
self.log.close()
except AttributeError as AE:
print(AE)
def startstop_record(self): #Flips recording button.
if self.running is False:
self.startstop() #Makes sure data is being transmitted before recording starts.
if self.recording is False: #Device is running, but not recording. Command turns recording on.
self.record_btn.config(text='Pause recording', bg="yellow")
try:
self.log.write('') #Attempts to write an empty string to test if file is open.
except AttributeError: #File has not been created -> run function to open a new log.
self.create_log()
except ValueError: #Previous log file has been closed -> run function to open new log.
self.create_log()
self.recording = True
self.pause_time += time.time() - self.pause_point #Adjusts for
else: #Device is running AND recording. Command pauses recording.
self.recording = False
self.pause_point = time.time()
self.record_btn.config(text='Start recording', bg="green")
def __init__(self, name, frame):
self.name = name
self.running = False
self.recording = False
#Read config -----------------------------------------------------------------------
config = open('SR830 config.txt','w+')
try:
config_read = config.readlines()
config_read = config_read[1].split('\t')
except IndexError: #config coudl not be read - creates default file.
config.write('COM Baudrate\ttimeout\tsampling time\n10\t9600\t0.1\t1')
config.close()
config = open('SR830 config.txt','r')
config_read = config.readlines()
config_read = config_read[1].split('\t')
com = config_read[0]
baud = int(config_read[1])
self.config_timeout = float(config_read[2])
self.sample_time = float(config_read[3])
config.close()
#establish communication -----------------------------------------------------------
com = 'COM'+str(com) #TODO- make this read from config
try:
SR_inst = serial.Serial(port=com, baudrate=baud, timeout=self.config_timeout) #Opens communication.
#Note: SR830 has adjustablebaud rate and parity, but defaults to 9600 and none, respectively.
self.inst = SR_inst
self.command('OUTX',0) #Tells device to output responses via RS-232.
print(self.inst.read(5)) #Prints response to OUTX command.
#self.command('OUTX?','') #TEST - see if communication has switched.
#print(self.inst.read(10)) #Should print b'0\r' for RS-232 communication.
self.command('DDEF',110)#Sets CH1 to R.
print(self.inst.read(5))
self.command('DDEF',210)#Sets CH2 to Theta.
print(self.inst.read(5))
except ValueError as ve:
SR_inst = 0
print(ve)
except AttributeError as ae:
SR_inst = 0
print (ae)
except NameError as ne:
print(ne)
SR_inst = 0
except serial.SerialException as SE:
SR_inst = 0
print(SE)
#Create Tkinter GUI------------------------------------------------------------------
id_label=tk.Label(frame, text=name)
id_label.grid(row=0,column=0, sticky=tk.N+tk.S+tk.E+tk.W)
version_label = tk.Label(frame, text = 'Version 0.1')
version_label.grid(row=0, column=1, sticky=tk.N+tk.S+tk.E+tk.W)
sample_label = tk.Label(frame, text="Sampling time =")
sample_label.grid(row=1,column=0,sticky=tk.N+tk.S+tk.E+tk.W)
self.sample_box = tk.Entry(frame)
self.sample_box.insert(tk.END, self.sample_time)
self.sample_box.grid(row=1,column=1,sticky=tk.N+tk.S+tk.E+tk.W)
self.run_btn = tk.Button(frame, text='Start', command = self.startstop)
self.run_btn.grid(row=2,column=0,sticky=tk.N+tk.S+tk.E+tk.W)
self.run_btn.config(bg="green")
self.record_btn = tk.Button(frame, text='Start recording', command = self.startstop_record)
self.record_btn.grid(row=2,column=1,sticky=tk.N+tk.S+tk.E+tk.W)
self.record_btn.config(bg="green")
#Graph setup:
self.f = Figure(figsize=(5,5),dpi=100) #Figure that graphs appears in
self.a = self.f.add_subplot() #111 means there is only 1 chart. use a.plot to plot lists.
self.a2 = self.a.twinx()
self.graph_canvas = FigureCanvasTkAgg(self.f, frame)
self.graph_canvas.get_tk_widget().grid(row=3, column=0,columnspan=2)
#Make grid resizeable
for row in range(4): #Number of rows
try:
tk.Grid.rowconfigure(frame,row,weight=2)
except AttributeError as AE: #FOr unfilled rows & columns
print(AE)
for column in range(2): #Number of columns
try:
#root.grid_columnconfigure(column, weight=1)
tk.Grid.columnconfigure(frame,column,weight=1)
except AttributeError as AE: #For unfilled rows & columns
print(AE)
#Extra declarations
self.sample_time = float(self.sample_box.get())-2*self.config_timeout #Adjusts for timeout of device.
self.times = np.array([]) #Holds time values queried (May not be recorded)
self.shifts = np.array([])#Holds shift values queried.
self.amplitudes = np.array([])#Holds amplitude values queried.
#self.start_time = time.time() #TODO - Move this somewhere better
self.previous_time = 0
time.sleep(1)
self.ani = animation.FuncAnimation(self.f,self.animate, interval=1000)
def SR830_exit(self): #Exit handler.
self.running = False
self.recording = False
try:
self.inst.close()
except AttributeError as AE:
print(AE)
root.destroy()
exit()
#TEST AREA: ------------------------------------------------------------------------------
# Create tk root -------------------
root = tk.Tk()
root.title('SR830')
frame1 = tk.Frame(root)
frame1.pack()
#Create instrument object ---------
sr1 = SR830("SSTR", frame1) #TODO - remove once testing is done.
atexit.register(sr1.SR830_exit)
root.protocol('WM_DELETE_WINDOW', sr1.SR830_exit)
root.mainloop()
I'm working on a Raspberry Pi (3 B+) making a data collection device and I'm
trying to spawn a process to record the data coming in and write it to a file. I have a function for the writing that works fine when I call it directly.
When I call it using the multiprocess approach however, nothing seems to happen. I can see in task monitors in Linux that the process does in fact get spawned but no file gets written, and when I try to pass a flag to it to shut down it doesn't work, meaning I end up terminating the process and nothing seems to have happened.
I've been over this every which way and can't see what I'm doing wrong; does anyone else? In case it's relevant, these are functions inside a parent class, and one of the functions is meant to spawn another as a thread.
Code I'm using:
from datetime import datetime, timedelta
import csv
from drivers.IMU_SEN0 import IMU_SEN0
import multiprocessing, os
class IMU_data_logger:
_output_filename = ''
_csv_headers = []
_accelerometer_headers = ['Accelerometer X','Accelerometer Y','Accelerometer Z']
_gyroscope_headers = ['Gyroscope X','Gyroscope Y','Gyroscope Z']
_magnetometer_headers = ['Bearing']
_log_accelerometer = False
_log_gyroscope= False
_log_magnetometer = False
IMU = None
_writer=[]
_run_underway = False
_process=[]
_stop_value = 0
def __init__(self,output_filename='/home/pi/blah.csv',log_accelerometer = True,log_gyroscope= True,log_magnetometer = True):
"""data logging device
NOTE! Multiple instances of this class should not use the same IMU devices simultaneously!"""
self._output_filename = output_filename
self._log_accelerometer = log_accelerometer
self._log_gyroscope = log_gyroscope
self._log_magnetometer = log_magnetometer
def __del__(self):
# TODO Update this
if self._run_underway: # If there's still a run underway, end it first
self.end_recording()
def _set_up(self):
self.IMU = IMU_SEN0(self._log_accelerometer,self._log_gyroscope,self._log_magnetometer)
self._set_up_headers()
def _set_up_headers(self):
"""Set up the headers of the CSV file based on the header substrings at top and the input flags on what will be measured"""
self._csv_headers = []
if self._log_accelerometer is not None:
self._csv_headers+= self._accelerometer_headers
if self._log_gyroscope is not None:
self._csv_headers+= self._gyroscope_headers
if self._log_magnetometer is not None:
self._csv_headers+= self._magnetometer_headers
def _record_data(self,frequency,stop_value):
self._set_up() #Run setup in thread
"""Record data function, which takes a recording frequency, in herz, as an input"""
previous_read_time=datetime.now()-timedelta(1,0,0)
self._run_underway = True # Note that a run is now going
Period = 1/frequency # Period, in seconds, of a recording based on the input frequency
print("Writing output data to",self._output_filename)
with open(self._output_filename,'w',newline='') as outcsv:
self._writer = csv.writer(outcsv)
self._writer.writerow(self._csv_headers) # Write headers to file
while stop_value.value==0: # While a run continues
if datetime.now()-previous_read_time>=timedelta(0,1,0): # If we've waited a period, collect the data; otherwise keep looping
print("run underway value",self._run_underway)
if datetime.now()-previous_read_time>=timedelta(0,Period,0): # If we've waited a period, collect the data; otherwise keep looping
previous_read_time = datetime.now() # Update previous readtime
next_row = []
if self._log_accelerometer:
# Get values in m/s^2
axes = self.IMU.read_accelerometer_values()
next_row += [axes['x'],axes['y'],axes['z']]
if self._log_gyroscope:
# Read gyro values
gyro = self.IMU.read_gyroscope_values()
next_row += [gyro['x'],gyro['y'],gyro['z']]
if self._log_magnetometer:
# Read magnetometer value
b= self.IMU.read_magnetometer_bearing()
next_row += b
self._writer.writerow(next_row)
# Close the csv when done
outcsv.close()
def start_recording(self,frequency_in_hz):
# Create recording process
self._stop_value = multiprocessing.Value('i',0)
self._process = multiprocessing.Process(target=self._record_data,args=(frequency_in_hz,self._stop_value))
# Start recording process
self._process.start()
print(datetime.now().strftime("%H:%M:%S.%f"),"Data logging process spawned")
print("Logging Accelerometer:",self._log_accelerometer)
print("Logging Gyroscope:",self._log_gyroscope)
print("Logging Magnetometer:",self._log_magnetometer)
print("ID of data logging process: {}".format(self._process.pid))
def end_recording(self,terminate_wait = 2):
"""Function to end the recording multithread that's been spawned.
Args: terminate_wait: This is the time, in seconds, to wait after attempting to shut down the process before terminating it."""
# Get process id
id = self._process.pid
# Set stop event for process
self._stop_value.value = 1
self._process.join(terminate_wait) # Wait two seconds for the process to terminate
if self._process.is_alive(): # If it's still alive after waiting
self._process.terminate()
print(datetime.now().strftime("%H:%M:%S.%f"),"Process",id,"needed to be terminated.")
else:
print(datetime.now().strftime("%H:%M:%S.%f"),"Process",id,"successfully ended itself.")
====================================================================
ANSWER: For anyone following up here, it turns out the problem was my use of the VS Code debugger which apparently doesn't work with multiprocessing and was somehow preventing the success of the spawned process. Many thanks to Tomasz Swider below for helping me work through issues and, eventually, find my idiocy. The help was very deeply appreciated!!
I can see few thing wrong in your code:
First thing
stop_value == 0 will not work as the multiprocess.Value('i', 0) != 0, change that line to
while stop_value.value == 0
Second, you never update previous_read_time so it will write the readings as fast as it can, you will run out of disk quick
Third, try use time.sleep() the thing you are doing is called busy looping and it is bad, it is wasting CPU cycles needlessly.
Four, terminating with self._stop_value = 1 probably will not work there must be other way to set that value maybe self._stop_value.value = 1.
Well here is a pice of example code based on the code that you have provided that is working just fine:
import csv
import multiprocessing
import time
from datetime import datetime, timedelta
from random import randint
class IMU(object):
#staticmethod
def read_accelerometer_values():
return dict(x=randint(0, 100), y=randint(0, 100), z=randint(0, 10))
class Foo(object):
def __init__(self, output_filename):
self._output_filename = output_filename
self._csv_headers = ['xxxx','y','z']
self._log_accelerometer = True
self.IMU = IMU()
def _record_data(self, frequency, stop_value):
#self._set_up() # Run setup functions for the data collection device and store it in the self.IMU variable
"""Record data function, which takes a recording frequency, in herz, as an input"""
previous_read_time = datetime.now() - timedelta(1, 0, 0)
self._run_underway = True # Note that a run is now going
Period = 1 / frequency # Period, in seconds, of a recording based on the input frequency
print("Writing output data to", self._output_filename)
with open(self._output_filename, 'w', newline='') as outcsv:
self._writer = csv.writer(outcsv)
self._writer.writerow(self._csv_headers) # Write headers to file
while stop_value.value == 0: # While a run continues
if datetime.now() - previous_read_time >= timedelta(0, 1,
0): # If we've waited a period, collect the data; otherwise keep looping
print("run underway value", self._run_underway)
if datetime.now() - previous_read_time >= timedelta(0, Period,
0): # If we've waited a period, collect the data; otherwise keep looping
next_row = []
if self._log_accelerometer:
# Get values in m/s^2
axes = self.IMU.read_accelerometer_values()
next_row += [axes['x'], axes['y'], axes['z']]
previous_read_time = datetime.now()
self._writer.writerow(next_row)
# Close the csv when done
outcsv.close()
def start_recording(self, frequency_in_hz):
# Create recording process
self._stop_value = multiprocessing.Value('i', 0)
self._process = multiprocessing.Process(target=self._record_data, args=(frequency_in_hz, self._stop_value))
# Start recording process
self._process.start()
print(datetime.now().strftime("%H:%M:%S.%f"), "Data logging process spawned")
print("ID of data logging process: {}".format(self._process.pid))
def end_recording(self, terminate_wait=2):
"""Function to end the recording multithread that's been spawned.
Args: terminate_wait: This is the time, in seconds, to wait after attempting to shut down the process before terminating it."""
# Get process id
id = self._process.pid
# Set stop event for process
self._stop_value.value = 1
self._process.join(terminate_wait) # Wait two seconds for the process to terminate
if self._process.is_alive(): # If it's still alive after waiting
self._process.terminate()
print(datetime.now().strftime("%H:%M:%S.%f"), "Process", id, "needed to be terminated.")
else:
print(datetime.now().strftime("%H:%M:%S.%f"), "Process", id, "successfully ended itself.")
if __name__ == '__main__':
foo = Foo('/tmp/foometer.csv')
foo.start_recording(20)
time.sleep(5)
print('Ending recording')
foo.end_recording()
I'm looking to control a script via Zigbee/XBee using X-CTU. I've created a script named zb_control.py. Now I'm trying to start and stop another script within this script. A script adxl345test.py is used to collect data from an attached accelerometer on my Raspberry Pi.
The idea behind the zb_control.py script is that I run it and then if I type "run" in X-CTU the script will start running adxl345test.py and collect data.
I'm trying to create a script within a script that can also be stopped again and then still have the zb_control.py running ready to recieve new input from X-CTU.
As you can tell I've tried different things:
import serial, time, sys, os, subprocess
from subprocess import check_call
from subprocess import call
while True:
ser=serial.Serial('/dev/ttyUSB0',9600,timeout=2)
inc=ser.readline().strip()
if inc=='run':
print("---------------")
print("Collecting data")
print("---------------")
p = subprocess.Popen("/home/pi/adxl345test.py", stdout=subprocess.PIPE, shell=True)
elif inc=='stop':
# check_call(["pkill", "-9", "-f", adxl345test.py])
# serial.write('\x03')
# os.system("pkill –f adxl345test.py")
# call(["killall", "adxl345test.py"])
p.kill()
print("-----------------------")
print("Script has been stopped")
print("-----------------------")
I got it to run and it's now collecting data properly. However now the problem is stopping the adxl345test.py again. As you can tell from the script from above I'm using p.kill() but the script doesn't stop collecting data. When I type "stop" in XCTU my zb_control.py does print the print-commands but the p.kill() isn't being executed. Any suggestions?
I've tried using p.terminate() alone and together with p.kill() aswell as the commands by themselves however it doesn't stop the adxl345test.py script. I can tell that the .csv-file is still increasing in size and therefore the script must still be collecting data.
Here is the adxl345test.py script for those interested:
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Example on how to read the ADXL345 accelerometer.
# Kim H. Rasmussen, 2014
import sys, math, os, spidev, datetime, ftplib
# Setup SPI
spi = spidev.SpiDev()
#spi.mode = 3 <-- Important: Do not do this! Or SPI won't work as intended, or even at all.
spi.open(0,0)
spi.mode = 3
# Read the Device ID (should be xe5)
id = spi.xfer2([128,0])
print 'Device ID (Should be 0xe5):\n'+str(hex(id[1])) + '\n'
# Read the offsets
xoffset = spi.xfer2([30 | 128,0])
yoffset = spi.xfer2([31 | 128,0])
zoffset = spi.xfer2([32 | 128,0])
accres = 2
accrate = 13
print 'Offsets: '
print xoffset[1]
print yoffset[1]
# print str(zoffset[1]) + "\n\nRead the ADXL345 every half second:"
# Initialize the ADXL345
def initadxl345():
# Enter power saving state
spi.xfer2([45, 0])
# Set data rate to 100 Hz. 15=3200, 14=1600, 13=800, 12=400, 11=200, 10=100 etc.
spi.xfer2([44, accrate])
# Enable full range (10 bits resolution) and +/- 16g 4 LSB
spi.xfer2([49, accres])
# Enable measurement
spi.xfer2([45, 8])
# Read the ADXL x-y-z axia
def readadxl345():
rx = spi.xfer2([242,0,0,0,0,0,0])
#
out = [rx[1] | (rx[2] << 8),rx[3] | (rx[4] << 8),rx[5] | (rx[6] << 8)]
# Format x-axis
if (out[0] & (1<<16 - 1 )):
out[0] = out[0] - (1<<16)
# out[0] = out[0] * 0.004 * 9.82
# Format y-axis
if (out[1] & (1<<16 - 1 )):
out[1] = out[1] - (1<<16)
# out[1] = out[1] * 0.004 * 9.82
# Format z-axis
if (out[2] & (1<<16 - 1 )):
out[2] = out[2] - (1<<16)
# out[2] = out[2] * 0.004 * 9.82
return out
# Initialize the ADXL345 accelerometer
initadxl345()
# Read the ADXL345 every half second
timetosend = 60
while(1):
with open('/proc/uptime','r') as f: # get uptime
uptime_start = float(f.readline().split()[0])
uptime_last = uptime_start
active_file_first = "S3-" + str(pow(2,accrate)*25/256) + "hz10bit" + str(accres) + 'g' + str(datetime.datetime.utcnow().strftime('%y%m%d%H%M')) $
active_file = active_file_first.replace(":", ".")
wStream = open('/var/log/sensor/' + active_file,'wb')
finalcount = 0
print "Creating " + active_file
while uptime_last < uptime_start + timetosend:
finalcount += 1
time1 = str(datetime.datetime.now().strftime('%S.%f'))
time2 = str(datetime.datetime.now().strftime('%M'))
time3 = str(datetime.datetime.now().strftime('%H'))
time4 = str(datetime.datetime.now().strftime('%d'))
time5 = str(datetime.datetime.now().strftime('%m'))
time6 = str(datetime.datetime.now().strftime('%Y'))
axia = readadxl345()
wStream.write(str(round(float(axia[0])/1024,3))+','+str(round(float(axia[1])/1024,3))+','+str(round(float(axia[2])/1024,3))+','+time1+','+ti$
# Print the reading
# print axia[0]
# print axia[1]
# print str(axia[2]) + '\n'
# elapsed = time.clock()
# current = 0
# while(current < timeout):
# current = time.clock() - elapsed
with open('/proc/uptime', 'r') as f:
uptime_last = float(f.readline().split()[0])
wStream.close()
def doftp(the_active_file):
session = ftplib.FTP('192.0.3.6','sensor3','L!ghtSp33d')
session.cwd("//datalogger//")
file = open('/var/log/sensor/' + active_file, 'rb') # file to send
session.storbinary('STOR' + active_file, file) # send the file
file.close()
session.quit
My suggestions:
If you're doing something at a specified interval, you're probably better off using threading.Timer rather than checking the time yourself.
As I said in the comment, you can check for an exit condition instead of brutally killing the process. This also allows to properly clean up what you need.
Those time1...time6 really don't look nice, how about a list? Also, time2, time3, time4, time5, time6 are not used.
You don't actually need strftime to get hour, day, month, year, etc. They're already there as attributes.
You can do something like:
cur_time = datetime.datetime.now()
cur_hour = cur_time.hour
cur_minute = cur_time.minute
...And so on, which is a bit better. In this specific case it won't matter, but if you start measuring milliseconds, the time will be slightly different after a few lines of code, so you should store it and use it from the variable.
As for the rest, if you want an example, here I check that a file exists to determine whether to stop or not. It's very crude but it should give you a starting point:
from threading import *
from os.path import exists
def hello():
print('TEST') # Instead of this, do what you need
if (exists('stop_file.txt')):
return
t = Timer(0.5, hello)
t.start()
hello()
Then in the other create you create the stop file when you want it to stop (don't forget to add a line to remove it before starting it again).
I am streaming data into a class in chunks. For each chunk of data, two different types of np.convolve() are executed on the same ProcessPoolExecutor. The type of convolve that was called is determined by a return variable.
The order of the data must be maintained, so each future has an associated sequence number. The output function enforces that only data from contiguous futures is returned (not shown below). From what I understand I am properly calling the ProcessPoolExecutor.shutdown() function, but I am still getting a IOError:
The errors is:
$ python processpoolerror.py
ran 5000000 samples in 3.70395112038 sec: 1.34990982265 Msps
Traceback (most recent call last):
File "/usr/lib/python2.7/multiprocessing/queues.py", line 268, in _feed
send(obj)
IOError: [Errno 32] Broken pipe
Sorry it's a bit long, but I have pruned this class down as much as possible while keeping the error. On my machine Ubuntu 16.04.2 with a Intel(R) Core(TM) i7-6700K CPU # 4.00GHz the paired down code always gives this error. In the non-pruned version of this code, the Broken pipe occurs 25% of the time.
If you edit line 78 to True, and print during the execution, the error is not thrown. If you reduce the amount of data on line 100, the error is not thrown. What am I doing wrong here? Thanks.
import numpy as np
from concurrent.futures import ProcessPoolExecutor
import time
def _do_xcorr3(rev_header, packet_chunk, seq):
r1 = np.convolve(rev_header, packet_chunk, 'full')
return 0, seq, r1
def _do_power3(power_kernel, packet_chunk, seq):
cp = np.convolve(power_kernel, np.abs(packet_chunk) ** 2, 'full')
return 1, seq, cp
class ProcessPoolIssues():
## Constructor
# #param chunk_size how many samples to feed in during input() stage
def __init__(self,header,chunk_size=500,poolsize=5):
self.chunk_size = chunk_size ##! How many samples to feed
# ProcessPool stuff
self.poolsize = poolsize
self.pool = ProcessPoolExecutor(poolsize)
self.futures = []
# xcr stage stuff
self.results0 = []
self.results0.append((0, -1, np.zeros(chunk_size)))
# power stage stuff
self.results1 = []
self.results1.append((1, -1, np.zeros(chunk_size)))
self.countin = 0
self.countout = -1
def shutdown(self):
self.pool.shutdown(wait=True)
## Returns True if all data has been extracted for given inputs
def all_done(self):
return self.countin == self.countout+1
## main function
# #param packet_chunk an array of chunk_size samples to be computed
def input(self, packet_chunk):
assert len(packet_chunk) == self.chunk_size
fut0 = self.pool.submit(_do_xcorr3, packet_chunk, packet_chunk, self.countin)
self.futures.append(fut0)
fut1 = self.pool.submit(_do_power3, packet_chunk, packet_chunk, self.countin)
self.futures.append(fut1)
self.countin += 1
# loops through thread pool, copying any results from done threads into results0/1 (and then terminating them)
def cultivate_pool(self):
todel = []
for i, f in enumerate(self.futures):
# print "checking", f
if f.done():
a, b, c = f.result()
if a == 0:
self.results0.append((a,b,c)) # results from one type of future
elif a == 1:
self.results1.append((a,b,c)) # results from another type of future
todel.append(i)
# now we need to remove items from futures that are done
# we need do it in reverse order so we remove items from the end first (thereby not affecting indices as we go)
for i in sorted(todel, reverse=True):
del self.futures[i]
if False: # change this to true and error goes away
print "deleting future #", i
# may return None
def output(self):
self.cultivate_pool() # modifies self.results list
# wait for both results to be done before clearing
if len(self.results0) and len(self.results1):
del self.results0[0]
del self.results1[0]
self.countout += 1
return None
def testRate():
chunk = 500
# a value of 10000 will throw: IOError: [Errno 32] Broken pipe
# smaller values like 1000 do not
din = chunk * 10000
np.random.seed(666)
search = np.random.random(233) + np.random.random(233) * 1j
input = np.random.random(din) + np.random.random(din) * 1j
pct = ProcessPoolIssues(search, chunk, poolsize=8)
st = time.time()
for x in range(0, len(input), chunk):
slice = input[x:x + chunk]
if len(slice) != chunk:
break
pct.input(slice)
pct.output()
while not pct.all_done():
pct.output()
ed = time.time()
dt = ed - st
print "ran", din, "samples in", dt, "sec:", din / dt / 1E6, "Msps"
pct.shutdown()
if __name__ == '__main__':
testRate()
This is probably happening because you're exceeding the buffer size of the pipe when you try sending in larger chunks at once.
def _do_xcorr3(rev_header, packet_chunk, seq):
r1 = np.convolve(rev_header, packet_chunk, 'full')
return 0, seq, r1
def _do_power3(power_kernel, packet_chunk, seq):
cp = np.convolve(power_kernel, np.abs(packet_chunk) ** 2, 'full')
return 1, seq, cp
the values r1 and cp are very large because you are convolving with the square of the chunks.
Hence, when you try to run this with larger chunk sizes, the buffer of IO Pipe can't handle it. Refer this for clearer understanding.
As for the second part of the question,
if False: # change this to true and error goes away
print "deleting future #", i
Found this in the py3 docs:
16.2.4.4. Reentrancy
Binary buffered objects (instances of BufferedReader, BufferedWriter, BufferedRandom and BufferedRWPair) are not reentrant. While reentrant calls will not happen in normal situations, they can arise from doing I/O in a signal handler. If a thread tries to re-enter a buffered object which it is already accessing, a RuntimeError is raised. Note this doesn’t prohibit a different thread from entering the buffered object.
The above implicitly extends to text files, since the open() function will wrap a buffered object inside a TextIOWrapper. This includes standard streams and therefore affects the built-in function print() as well.
I am writing a simple python program on a raspberry pi and I am quite new to python programming. I have defined a function called GetMessage which has no parameters and returns a variable which I called data, but I am getting an error which states
File "Raspberry_pi.py", line 39
return none
^
SyntaxError: invalid syntax
import os
import glob
import time
import RPi.GPIO as GPIO
from math import *
from bluetooth import *
from RPIO import PWM
os.system('sudo hciconfig hci0 pisca')
os.system('sudo hciconfig hci0 name "De Quadcoptur"')
servo = PWM.Servo()
StartSpin()
server_sock=BluetoothSocket( RFCOMM )
server_sock.bind(("",PORT_ANY))
server_sock.listen(1)
port = server_sock.getsockname()[1]
GetMessage()
DecodeInput()
uuid = "94f39d29-7d6d-437d-973b-fba39e49d4ee"
def GetMessage():
advertise_service( server_sock, "XT1032", #phone bluetooth name
service_id = uuid,
service_classes = [ uuid, SERIAL_PORT_CLASS ],
profiles = [ SERIAL_PORT_PROFILE ],
#protocols = [ OBEX_UUID ]
)
client_sock, client_info = server_sock.accept()
try:
data = client_sock.recv(1024)
if len(data) == 0: break
print "received [%s]" % data
client_sock.close()
server_sock.close()
except IOError:
pass
break
return data
def StartSpin():
# Set servo on GPIO17 to 1200µs (1.2ms)
servo.set_servo(17, 1000)
servo.set_servo(18, 1000)
servo.set_servo(19, 1000)
servo.set_servo(20, 1000)
time.sleep(1)
servo.stop_servo(17)
servo.stop_servo(18)
servo.stop_servo(19)
servo.stop_servo(20)
#Check if more pulses is faster
time.sleep(2000)
PWM.add_channel_pulse(0, 17, start = 1000, width = 100)
PWM.add_channel_pulse(0, 17, start = 1000, width = 100)
PWM.add_channel_pulse(0, 17, start = 1000, width = 100)
PWM.add_channel_pulse(0, 17, start = 1000, width = 100)
PWM.add_channel_pulse(0, 17, start = 1000, width = 100)
servo.stop_servo(17)
servo.stop_servo(18)
servo.stop_servo(19)
servo.stop_servo(20)
return None
def DecodeInput():
data = GetMessage()
if(data == 'start')
StartSpin()
return 0
else if(data[0] == 'U')
data.strip('U')
UpPower = int(data)
SetUpPower(UpPower)
else if(data[0] == 'P')
data.strip('P')
PitchPower = int(data)
SetPitchPower
else
data.strip('P')
RollPower = int(data)
SetPower(UpPower, PitchPower, RollPower)
return None
def SetPower(UpPower, PitchPower, RollPower):
#Make Arbitrary Values
Motor1Power = UpPower #Front Left
Motor2Power = UpPower #Front Right
Motor3Power = UpPower #Back Left
Motor4Power = UpPower #Back Right
PitchPower = PitchPower /2
RollPower = RollPower /2
if(PitchPower < 25)
Motor1Power = Motor1Power + abs(25-PitchPower)
Motor2Power = Motor1Power + abs(25-PitchPower)
else
Motor3Power = Motor3Power + (PitchPower-25)
Motor4Power = Motor4Power + (PitchPower-25)
if(RollPower < 25)
Motor1Power = Motor1Power + abs(25-RollPower)
Motor3Power = Motor3Power + abs(25-RollPower)
else
Motor2Power = Motor2Power + (RollPower - 25)
Motor4Power = Motor4Power + (RollPower - 25)
What is causing this error and how can I fix it?
Edit: I have defined data as a global variable and the error now is
File "Raspberry_pi.py", line 39
return data
^
SyntaxError: invalid syntax
There are a number of syntax problems in your code. Because of the nature of SyntaxError exceptions (which are raised when the interpreter doesn't understand the code syntax), the error messages may not identify the right line as the source of the problem.
The first syntax error I see is that you're using break in the GetMessage function without it being in a loop. A break statement is only useful within a for or while block, and using one elsewhere (in an except block in this case) is a syntax error.
The next set of errors have to do with missing colons. Each of the conditional branches in DecodeInput and SetPower need to have a colon after the condition: if condition1:, elif condition2:, else:
It's also an error to use else if rather than elif (you could make it work if you added a colon, a newline and an extra level of indentation after else:, then used a separate if statement, but that would be wasteful of space).
There are some additional issues, but they're not syntax errors. For instance, you're calling your functions from top-level code before they've been defined, and DecodeInput has a line with the bare expression SetPower which doesn't do anything useful (you probably want to call SetPower with some argument).
Hopefully this will get you on the right track.
Once you get your colons fixed, you'll probably run into a problem with your GetMessage syntax. You cannot break unless you're inside of a loop. If you intend to return from an exception, you don't need the pass call. An example (simplified from your code) of how this method could (should?) look:
def GetMessage():
data = None
try:
data = [1,2]
if len(data) == 0:
return None
except IOError:
return None
return data
Clearly you'll want to replace the bulk of the method with your own code, and determine if you really want to return from the function at the points where you put breaks.