1

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.

user75374
  • 21
  • 4
  • 2
    This might not solve your issue but it might make sense to first do `p.terminate()` in order to send the SIGTERM signal to the process and allow it to terminate gracefully with proper cleanup, etc. Note that the process will take some time to stop after receiving SIGTERM. – Kos Jan 19 '18 at 09:55
  • +1 to using SIGTERM. Here's how you can handle it gracefully in python: https://stackoverflow.com/questions/18499497/how-to-process-sigterm-signal-gracefully – Bailey Parker Jan 19 '18 at 10:17
  • Is `adxl345test.py` a script that you wrote or something that you found somewhere? If it's your own code you might want to consider changing it so it can properly stop. Either by trapping a signal (kill or terminate), or checking some kind of event, even something as crude as checking the existence of a `oh_god_please_stop.txt` file in some directory. – ChatterOne Jan 23 '18 at 08:31

2 Answers2

0

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
user75374
  • 21
  • 4
0

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).

ChatterOne
  • 3,381
  • 1
  • 18
  • 24