Question
The OP already has a working python program which logs the readings of an accelerometer MPU6050 and write to a CSV file.
Now he wishes to have advice and direction on how to write another python program which reads a button and starts and stops the above accelero data logging program.
Answer
Short Answer
I read the OP's data logging program and found it well structured and documented.
A quick and short answer would be something like below.
(1) Google a simple, newbie friendly Rpi python button reading program.
(2) Modify the button program to do the following:
(a) import the data logging program as a module,
(b) add a loop to detect button presses and starts and stops the imported data logging program.
- There are minor modification/editing needed to make the original program "importable", "startable" and "stoppable", as will be explained in detail later, in the long answer below. I will also briefly describe the minor editing here, after I have completed the long answer.
Long answer
(1) I read the OP's original accelerometer and data logging program which includes too many tedious details which are irrelevant to the main trick suggested here to control one program by another program. Therefore I am simplifying the OP's situation and requirement to a simple "read button" and "write Hello World" program. In other words, the very short Hello World program will replace/simulate the OP's big program in this demonstrative answer.
(2) One subtle but critical python trick/idiom "if name == “main” might confuse the OP and most python newbies. I think all newbies need to know this trick well, before they can develop even slightly more complex program beyond simple Blink LED and Read Button programs.
I will start writing the Hello World program, which I said earlier, is a minimal version of the OP' big program.
Update 2020may14hgkt1900
(4) Now I have written a python program to do the following:
(a) Define only two functions:
(i) Greeting(...) repeatedly saying Hello World,
(2) SendCommandList(...) repeatedly send commands to a 'queue'
(b) Setup the greeting function as a concurrent/multiprocess process, running in the background.
(c) Setup a 'Queue' which is used to communicate between the Greeting( ) process and the SendCommandList() function/process.
(5) Start the Greeting process (called amyProcess),which starts repeatedly saying Hello World in the background, until she received the command to stop.
(6) Run the SendCommandList() as a process in the foreground. Now the background and the foreground processes are running in parallel.
(7) The SendCommandList() process sends commands to the Queue (called amyQueue) 'Non Stop', 'Non Stop' ... and finally 'Stop Process 0'. Concurrently the Greeting process gets the messages from the queue when saying Hello World. When the 'Stop Process 0' comes, Greeting process stops.
The Minimal, Complete, Verifiable, Example (MCVE) is only 40 lines of python, as fully listed in Appendix H below, to gether with a sample output. The Thonny IDE screen capture is also pasted in the same Appendix.
The demo program can just copy, paste and run in Thonny. So even newbies without any knowledge can try it, to get a taste of python concurrent/parallel/multiprocessing/ programming.
/ to continue, ...
References
(1) Python multiprocessing
(2) What does if __name__ == '__main__' do? - StackOverflow
(3) How to create a Minimal, Reproducible Example - StackOverflow Help Center
(4) Rpi Essentials - GPIO Zero [LED/Button] Electronics - MagPi Free eBook
(5) Multiprocessing in Python | Set 1 (Introduction) - GeeksForGeeks
(6) Multiprocessing in Python | Set 2 (Communication between processes) - GeeksForGeeks
(7) GpioZero 1.5.1 Documentation
(8) GpioZero Source/Values (Led/Button, Declarative Programming, Background Thread Example - Warning: not for newbies)
/ to continue, ...
Appendices
Contents
A - The OP's Original accelerometer datalogging program listing
B - First Simplification
C - Second Simplification
*D - What does if _name_ == '_main_' do?*
E - Multiprocessing Program
F - How to create a Minimal, Complete, Reproducible/Verifiable Example (MCVE)
G - MagPi Rpi gpioZero Blink LED and Read Button Free eBook
H - Twp concurrent processes demo program
I - Three concurrent process demo program
Appendix A - Original Accelerometer Data Logging Program
"""
Read Gyro and Accelerometer by Interfacing Raspberry Pi with MPU6050 using Python
http://www.electronicwings.com
"""
import os
import argparse
import datetime
from csv import DictWriter
# SMBus module of I2C
from time import sleep, time
import smbus
# MPU6050 Registers and their Address
DEVICE_ADDRESS = 0x68
# 0 for older version boards
BUS_NO = 1
PWR_MGMT_1 = 0x6B
SMPLRT_DIV = 0x19
CONFIG = 0x1A
GYRO_CONFIG = 0x1B
INT_ENABLE = 0x38
ADDRESS_MAP = {'gx': 0x43, 'gy': 0x45, 'gz': 0x47, 'ax': 0x3B, 'ay': 0x3D, 'az': 0x3F}
VALUE_NORMS = {'gx': 131., 'gy': 131., 'gz': 131., 'ax': 16384., 'ay': 16384., 'az': 16384.}
OUT_DIR = 'output'
def mpu_init():
# write to sample rate register
bus.write_byte_data(DEVICE_ADDRESS, SMPLRT_DIV, 7)
# write to power management register
bus.write_byte_data(DEVICE_ADDRESS, PWR_MGMT_1, 1)
# write to Configuration register
bus.write_byte_data(DEVICE_ADDRESS, CONFIG, 0)
# write to Gyro configuration register
bus.write_byte_data(DEVICE_ADDRESS, GYRO_CONFIG, 24)
# write to interrupt enable register
bus.write_byte_data(DEVICE_ADDRESS, INT_ENABLE, 1)
def read_raw_data(addr):
# accelerometer and gyro value are 16-bit
high = bus.read_byte_data(DEVICE_ADDRESS, addr)
low = bus.read_byte_data(DEVICE_ADDRESS, addr+1)
# concatenate higher and lower value
value = ((high << 8) | low)
# to get signed value from mpu6050
if value > 32768:
value = value - 65536
return value
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--filename', type=str, help='Name of the file to write data to')
parser.add_argument(
'--log', default=list(ADDRESS_MAP.keys()), nargs='+', choices=list(ADDRESS_MAP.keys()),
help='What parameters you would like to log from the MPU'
)
parser.add_argument('--hertz', type=int, default=100, help='Sampling rate from the MPU')
args = vars(parser.parse_args())
bus = smbus.SMBus(BUS_NO)
mpu_init()
if not os.path.isdir(OUT_DIR):
os.makedirs(OUT_DIR)
sleep_time = 1 / args['hertz']
if args['filename'] is not None:
output_path = os.path.join(OUT_DIR, f'{args["filename"]}.csv')
else:
output_path = os.path.join(OUT_DIR, f'{datetime.datetime.now().strftime("%Y%m%d_%H%M%S")}.csv')
log_keys = sorted(args['log'])
print(f'Outputting data to {output_path:s}')
print(f'Logging at a sampling rate of {args["hertz"]:d} Hz')
print(f'Logging: {log_keys}')
start = time()
with open(output_path, 'w', newline='') as fp:
csv_writer = DictWriter(fp, delimiter=',', fieldnames=log_keys + ['time(ms)'])
csv_writer.writeheader()
while True:
csv_writer.writerow({
**{key: f'{read_raw_data(ADDRESS_MAP[key]) / VALUE_NORMS[key]:.3f}' for key in log_keys},
**{'time(ms)': f'{(time() - start) * 1000:.1f}'}
})
sleep(sleep_time)
# End of Program
Appendix B - First Simplification of Original Program
# *** Abstraction/Simplification/Pseudo Code of Original Accelero Datalogging Program V0.1 tlfon01 2020may13hkt1418
import os
import argparse
import datetime
from csv import DictWriter
from time import sleep, time
import smbus
# MPU6050 Registers and their Address
DEVICE_ADDRESS = 0x68; BUS_NO = 1; PWR_MGMT_1 = 0x6B; SMPLRT_DIV = 0x19; CONFIG = 0x1A; ...
def mpu_init():
# write to sample rate register
# write to power management register
# write to Configuration register
# write to Gyro configuration register
# write to interrupt enable register
return
def read_raw_data(addr):
# accelerometer and gyro value are 16-bit
# concatenate higher and lower value
# to get signed value from mpu6050
return value
if __name__ == '__main__':
# (1) *** Config parser ***
parser = argparse.ArgumentParser()
parser.add_argument(...)
parser.add_argument(...)
parser.add_argument(...)
args = vars(parser.parse_args())
bus = smbus.SMBus(BUS_NO)
# (2) *** Initialize MPU ***
mpu_init()
# (3) *** Setup directory and datalogging file ***
if not os.path.isdir(OUT_DIR): os.makedirs(OUT_DIR)
sleep_time = ...
if args['filename'] is not None:
...
else:
...
# (4) *** Print Logging Info
print(f'Outputting data to {output_path:s}')
print(f'Logging at a sampling rate of {args["hertz"]:d} Hz')
print(f'Logging: {log_keys}')
# (5) *** Start Logging ***
start = time()
with open(output_path, 'w', newline='') as fp:
csv_writer = ...
csv_writer.writeheader()
while True:
csv_writer.writerow(...)
sleep(sleep_time)
# End of Program
Appendix C - Second Simplification of original program
# *** Abstraction/Simplification/Pseudo Code of Original Accelero Datalogging Program V0.2 tlfon01 2020may13hkt1510
import os
import argparse
import datetime
from csv import DictWriter
from time import sleep, time
import smbus
# MPU6050 Registers and their Address
DEVICE_ADDRESS = 0x68; BUS_NO = 1; PWR_MGMT_1 = 0x6B; SMPLRT_DIV = 0x19; CONFIG = 0x1A; ...
def mpu_init():
# write to sample rate, power management, config, Gyro config, interrupt register, ...
return
def read_raw_data(addr):
# concatenate higher and lower value to get signed value from mpu6050,
...
return value
if __name__ == '__main__':
# (1) *** Config parser ***
...
# (2) *** Initialize MPU ***
mpu_init()
# (3) *** Setup directory and datalogging file ***
...
# (4) *** Print Logging Info
print(f'Outputting data to {output_path:s}')
...
# (5) *** Start Logging ***
with open(.. ) as fp:
csv_writer.writeheader()
while True:
csv_writer.writerow(...)
sleep(sleep_time)
# End of Program
Appendix D - [Why] What does if name == 'main' do?
[Why] What does if name == 'main': do? - StackOverflow, asked 11 years ago, 33 Answers,Viewed 2.9m times
...
Why Does It Work This Way?
You might naturally wonder why anybody would want this. Well, sometimes
you want to write a .py file that can be both used by other programs and/or modules as a module, and can also be run as the main program itself.
Examples:
Your module is a library, but you want to have a script mode where it runs some unit tests or a demo.
Your module is only used as a main program, but it has some unit tests, and the testing framework works by importing .py files like your script and running special test functions.
You don't want it to try running the script just because it's importing the module.
Your module is mostly used as a main program, but it also
provides a programmer-friendly API for advanced users.
Appendix E - Python Multiprocessing Demo Program V0.1
This little program demonstrats how to setup a concurrent process (repeatedly saying hello Word), along side a front end looping function repeatedly printing something, concurrently.
The front end repeatedly printing something function is to be later changed to a function to ask the use to hit the 't 'key of the keyboard. This simulates the user pressing a button, toggling the start and stop of the accelerometer data logging program
# helloworld03.py tlfong01 2020may13hkt1949
# Rpi4B buster (r2020feb13), linux 4.19.97
# python 3.7.3 (r2019dec20) thonny v3.2.7 (r2020jan22)
from time import sleep
from datetime import datetime
from multiprocessing import Process
def askUser(question, totalCount, pauseSecond):
for count in range(totalCount):
#reply = input(' count, Continue (y/n <enter>)? ' )
reply = 'ask user'
print(str(datetime.now())[0:19], ' ', str(count).rjust(4, ' '), ' ', reply)
sleep(pauseSecond)
return
def helloWorld(name, totalCount, pauseTime):
for count in range(totalCount):
print(str(datetime.now())[0:19], str(count).rjust(4, ' '), ' Hello World', name)
sleep(pauseTime)
return
if __name__ == '__main__':
print('-------------------------------------------------------------------------')
print(' Process 1 Main loop')
print('-------------------------------------------------------------------------')
amy = Process(target = helloWorld, args = ('Amy', 16, 0.1)) # Say Hello World to Amy 16 times, pause 0.25 second each time
amy.start()
askUser('Continue (y/n <enter>)? ', 6, 0.5) # Ask user 6 times, pause 0.5 second each time
# *** End of program ***
'''
# *** Sample Output ***
>>> %Run helloworld02.py
-------------------------------------------------------------------------
Process 1 Main loop
-------------------------------------------------------------------------
2020-05-13 19:47:41 0 ask user
2020-05-13 19:47:41 0 Hello World Amy
2020-05-13 19:47:41 1 Hello World Amy
2020-05-13 19:47:41 2 Hello World Amy
2020-05-13 19:47:41 3 Hello World Amy
2020-05-13 19:47:41 4 Hello World Amy
2020-05-13 19:47:41 1 ask user
2020-05-13 19:47:41 5 Hello World Amy
2020-05-13 19:47:41 6 Hello World Amy
2020-05-13 19:47:41 7 Hello World Amy
2020-05-13 19:47:42 8 Hello World Amy
2020-05-13 19:47:42 9 Hello World Amy
2020-05-13 19:47:42 2 ask user
2020-05-13 19:47:42 10 Hello World Amy
2020-05-13 19:47:42 11 Hello World Amy
2020-05-13 19:47:42 12 Hello World Amy
2020-05-13 19:47:42 13 Hello World Amy
2020-05-13 19:47:42 14 Hello World Amy
2020-05-13 19:47:42 3 ask user
2020-05-13 19:47:42 15 Hello World Amy
2020-05-13 19:47:43 4 ask user
2020-05-13 19:47:43 5 ask user
>>>
'''
Appendix F - How to create a Minimal, Reproducible Example
How to create a Minimal, Reproducible Example - StackOverflow Help Center
When asking a question, people will be better able to provide help if you provide code that they can easily understand and use to reproduce the problem. This is referred to by community members as creating a minimal, reproducible example (reprex), a minimal, complete and verifiable example (mcve), or a minimal, workable example (mwe). Regardless of how it's communicated to you, it boils down to ensuring your code that reproduces the problem follows the following guidelines:
Your code examples should be…
(1) Minimal – Use as little code as possible that still produces the same problem
(2) Complete – Provide all parts someone else needs to reproduce your problem in the question itself
(3) Reproducible – Test the code you're about to provide to make sure it reproduces the problem
The rest of this help article provides guidance on these aspects of writing a minimal, reproducible example.
Minimal
The more code there is to go through, the less likely people can find your problem. Streamline your example in one of two ways:
Restart from scratch. Create a new program, adding in only what is needed to see the problem. Use simple, descriptive names for functions and variables – don’t copy the names you’re using in your existing code.
Divide and conquer. If you’re not sure what the source of the problem is, start removing code a bit at a time until the problem disappears – then add the last part back.
Minimal and readable
Don't sacrifice clarity for brevity when creating a minimal example. Use consistent naming and indentation, and include code comments if needed. Use your code editor’s shortcut for formatting code. Also, use spaces instead of tabs – tabs might not get correctly formatted on Stack Overflow.
Complete
Make sure all information necessary to reproduce the problem is included in the question itself:
If the problem requires some server-side code as well as some XML-based configuration, include code for both. If a web page problem requires HTML, some JavaScript, and a stylesheet, include code for all three. The problem might not be in the code that you think it is in.
Use individual code blocks for each file or snippet you include. Provide a description for the purpose of each block.
Use Stack Snippets to include runnable HTML, JavaScript, or CSS.
DO NOT use images of code. Copy the actual text from your code editor, paste it into the question, then format it as code. This helps others more easily read and test your code.
Reproducible
To help you solve your problem, others will need to verify that it exists:
Describe the problem. "It doesn't work" isn't descriptive enough to help people understand your problem. Instead, tell other readers what the expected behavior should be. Tell other readers what the exact wording of the error message is, and which line of code is producing it. Use a brief but descriptive summary of your problem as the title of your question.
Eliminate any issues that aren't relevant to the problem. If your question isn’t about a compiler error, ensure that there are no compile-time errors. Use a program such as JSLint to validate interpreted languages. Validate any HTML or XML.
Double-check that your example reproduces the problem! If you inadvertently fixed the problem while composing the example but didn't test it again, you'd want to know that before asking someone else to help.
It might help to shut the system down and restart it, or transport the example to a fresh environment to confirm it really does provide an example of the problem.
Appendix G - GpioZero Blink LED Read Button Tutorial
Rpi Essentials - GPIO Zero Electronics - MagPi Free eBook
Chapter 1 - Get started with GPIO Zero
Chapter 2 - Blink a LED
Chapter 3 - Read a push button
Appendix H - Multiprocessing Demo Program V1.0


# multiproc06.py tlfong01 2020may14hkt1652, # Rpi4B buster (r2020feb13)
# python 3.7.3 (r2019dec20) thonny v3.2.7 (r2020jan22)
from time import sleep
from datetime import datetime
from multiprocessing import Process, Queue
controlByteDict = { '0.5 second': 0.5, '1 second': 1, '10 times': 10, 'Process 0' : 0, 'Process 1' : 1, 'Process 8' : 8,
'Stop Process 0' : 0, 'Non Stop' : 9, }
def greeting(processNum, queue, greeting, name, totalCount, pauseTime):
for count in range(controlByteDict[totalCount]):
stopProcessCommand = queue.get()
stopProcessNum = controlByteDict[stopProcessCommand]
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), ' ', 'Command =', stopProcessCommand)
if stopProcessNum == controlByteDict[processNum]:
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), ' ', 'Process stopped')
break
sleep(controlByteDict[pauseTime])
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), ' ', greeting, name)
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), ' ', 'Process ends')
return
def sendCommandList(processNum, queue, commandList, pauseTime):
count = 0
for command in commandList:
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), command)
queue.put(command)
count = count + 1
return
if __name__ == '__main__':
queueAmy = Queue()
processAmy = Process(target = greeting, args = ('Process 0', queueAmy, 'Hello World,', 'Amy', '10 times', '0.5 second'))
processAmy.start()
sendCommandList('Process 8', queueAmy, ['Non Stop', 'Non Stop', 'Non Stop', 'Non Stop', 'Stop Process 0',], '1 second')
# *** End of program ***
'''
# *** Sample Output ***
%Run multiproc06.py
... 3 Non Stop
4 Stop Process 0
>>>
2020-05-14 18:09:21 0 Hello World, Amy
2020-05-14 18:09:21 1 Command = Non Stop
2020-05-14 18:09:22 1 Hello World, Amy
2020-05-14 18:09:22 2 Command = Non Stop
2020-05-14 18:09:22 2 Hello World, Amy
2020-05-14 18:09:22 3 Command = Non Stop
2020-05-14 18:09:23 3 Hello World, Amy
2020-05-14 18:09:23 4 Command = Stop Process 0
2020-05-14 18:09:23 4 Process stopped
2020-05-14 18:09:23 4 Process ends
'''
Appendix I - Three Concurrent Processes, two slave (Hello World, Accelero Data Log), one master (Command to start/stop slave processes) Draft V0.1
# multiproc08.py tlfong01 2020may15hkt1534
# Rpi4B buster (r2020feb13), python 3.7.3 (r2019dec20), thonny v3.2.7 (r2020jan22)
# ** Imports ***
from time import sleep
from datetime import datetime
from multiprocessing import Process, Queue, Value, Array
# *** Control Byte Dictionaries ***
controlByteDict = { '0.25 second': 0.25, '0.3 second': 0.3, '0.5 second': 0.5, '1 second': 1, '1.1 second': 1.1,
'4 times': 4, '5 times': 5, '8 times': 8, '10 times': 10,
'Process 0' : 0, 'Process 1' : 1, 'Process 2' : 2,
'Stop Process 0' : 0, 'Non Stop' : 9, }
# *** Hello World Function ***
def printColumnNames():
print('----------------------------------------------------------------------------------------------------------------')
print('Date/Time Process 0 Process 1 Process 2')
print(' Hello Amy Welcome Betty Command Start/Stop')
print('----------------------------------------------------------------------------------------------------------------')
return
def greeting(processNum, sharedInteger, sharedIntegerArray, greetMsg, greetPerson, totalCount, pauseTime):
for count in range(controlByteDict[totalCount]):
sleep(controlByteDict[pauseTime])
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), ' ', greetMsg, greetPerson)
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), ' ', 'Process stops')
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), ' ', 'Process ends')
return
def command(processNum, sharedInteger, sharedIntegerArray, commandMsg, commandName, totalCount, pauseTime):
for count in range(controlByteDict[totalCount]):
sleep(controlByteDict[pauseTime])
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), ' ', commandMsg, commandName)
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), ' ', 'Process stops')
print(str(datetime.now())[0:19], (' ' * 30 * controlByteDict[processNum]), str(count).rjust(4, ' '), ' ', 'Process ends')
return
if __name__ == '__main__':
print('multiproc08.py tlfong01 2020may15hkt1534\n')
printColumnNames()
queueAmy = Queue()
intAmy = Value('i')
intArrayAmy = Array('i', 8)
intAll = Value('i')
intArrayAll = Array('i', 8)
processAmy = Process(target = greeting, args = ('Process 0', intAmy, intArrayAmy, 'Hello World,', 'Amy', '5 times', '0.5 second'))
processBetty = Process(target = greeting, args = ('Process 1', intAll, intArrayAll, 'Welcome World,', 'Betty', '8 times', '0.3 second'))
processCommand = Process(target = greeting, args = ('Process 2', intAll, intArrayAll, 'Non Stop,', 'Master', '4 times', '1.1 second'))
processAmy.start()
processBetty.start()
processCommand.start()
processAmy.join()
processBetty.join()
processCommand.join()
# *** End of program ***
'''
# *** Sample Output 2020may15hkt1627 ***
>>> %Run multiproc07.py
multiproc08.py tlfong01 2020may15hkt1534
----------------------------------------------------------------------------------------------------------------
Date/Time Process 0 Process 1 Process 2
Hello Amy Welcome Betty Command Start/Stop
----------------------------------------------------------------------------------------------------------------
2020-05-15 16:32:18 0 Welcome World, Betty
2020-05-15 16:32:18 0 Hello World, Amy
2020-05-15 16:32:18 1 Welcome
>>>
# *** End of Sample Output ***
'''
Note: This answer is hitting the StackExchange's 30,000 words limit. So I need to shorten some sections.
End of answer