Uncategorized

python concurrent programming

Asked 
Active today
Viewed 63 times
0

I would really appreciate if anyone could give an advice or directions on how to start and stop data logging for accelerometer MPU6050 with a button press. So far I have a script that I start manually through raspberry pi os. So when I test the script it’s working and collecting data in the csv file with no problems. The only thing that I need, if I use accelerometer outside for testing, is to start data logging on a button press and to stop data logging when the button is pressed again. The code that I currently use is below.

thank you,

M

"""
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)
share  edit  follow  close 3   flag
 New contributor
  • 1
    Hi @Maksym Kepskyy, Welcome and nice to meet you. Ah, let me see. Can I “simplify” your specific question to a bit generic, like this? Suppose I have a “printing” program that repeatedly prints “Hello World” every second. Now I want to have another “master” “button” program which reads a button and starts or stops a “slave” program, perhaps the printing program. Now say, if I press button once, the printing starts, and if I press button again, printing stops. If you think my simplified, generic question solves your complicated, “accelero + csv” question, I can try to answer. Cheers. – tlfong01 May 12 at 4:24
  • 2
    This seems to be a general programming question, i.e. not specific to the Pi. – joan May 12 at 7:59
  • 1
    Thanks for replying @tlfong01. Yes, if you could write your answer, it would be great. I am new to it and look into any answer as a resource I can learn from. – Maksym Kepskyy May 13 at 4:40
  • Hi @Maksym Kepskyy, thank you for your confirmation and invitation. So I am going to write a newbie friendly answer which can also be used as a learning resource. I would try to write my answer (1) “as simple as possible”, at the same time (2) “live”, (3) “flexible”, and (4) “complete”. – tlfong01 May 13 at 5:32
  • A note on “flexibility”: You question asks for “Advice and Direction” on the specific task of how to write a button program to control another accelerometer datalogging program. Though my answer is also on a specific case of Rpi button and printing, but the problem solving, programming knowledge and skills used should be flexible enough to apply to many more Rpi “control” applications. In a way, I am using “Problem Based Learning” and “Case Study Research ” approaches at the same time. – tlfong01 May 13 at 6:03
  • A note of “live answer”. I am trying to build up my answer incrementally and interactively, which means I am thinking aloud as I move on, adding appendices which are summaries the the steps I am developing. So you are welcome to interrupt me any time, asking me to explain something you don’t understand. Often I anticipate some subtle concepts newbies should find confusing, then I would interrupt myself and try to point out that this is a difficult thing that newbies must stop and think over. An example is the weird thing “if __name__ == “__main__” thing which is handled in Appendix D. – tlfong01 May 13 at 7:40
  • Now I have started writing a “Minimal”, “Complete”, “Reproducible” “Example” (MCRE) to of two concurrent processes/functions: (1) Repeatedly printing Hello World. This function is a simplified version of the original accerleometer data logging program, (2) Ask the user to hit a key function. This is a simulation of the read button function. I think for troubleshooting, reading the keyboard is more flexible to read a button. I am using python multiprocessing to denote the accelero data logging and the read button programs. / to continue, … – tlfong01 2 days ago
  • The first version of my demo program is only two concurrent printing functions. Next step is to change the second print function to a user hit keyboard function. Then the hit key function will be enhanced to become also a concurrent function (instead of a front end loop function), reading user key input to start or stop the data log program. I have have made up my mind of the final version. You might like to skim my preliminary versions and perhaps also read the references and then comment or make counter suggestions of the testing plan. No hurry at all. Cheers. – tlfong01 2 days ago

2 Answers

1

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

/ 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

mp06 1

mp06 2

# 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


share  edit   delete  flag
  • 1
    Thank you @tlfong01. Quite a bit of information to process but useful. Let me play around a bit and will get back with more questions. – Maksym Kepskyy 2 days ago
  • Hi @Maksym Kepskyy, You are welcome. No hurry at all. I am just experimenting things, and do not have a clear direction. You might like to sit back and watch for now – tlfong01 2 days ago
  • Hi @Maksym Kepskyy, in case you are impatient to wait, you can now skim my update Section 4, and also Appendix H of my answer. The demo program is already more or less a template which you can modify, say replacing the say hello function by your accelero/csv program, the the sendCommandList function by your yet to write read button program (See Ref 4). But I would suggest you to wait perhaps a week or so, until I convert my demo program to become a concurrent process Library/Server with newbie friendly APIs. You are welcome to comment or counter suggest anything I am doing so far. Cheers. – tlfong01 yesterday
  • 1
    Thanks again for helping out! – Maksym Kepskyy 10 hours ago
  • 1
    I modified and also simplified a couple of things: I added a power button that turns on and off Pi: howchoo.com/g/mwnlytk3zmm/… and then added a crone command to run the script on start up: @reboot python3 /home/pi/my_script.py – Maksym Kepskyy 9 hours ago
  • 1
    For now I have: Pi starts on button press. When the device is awake, the script for accelerometer starts running automatically and logs the data while the devise on On. When I press the button again, the device shuts down and safes the file with logged data. My next step then is to add another button that will do an interrupt: stops the script and start again if needed and so on. – Maksym Kepskyy 9 hours ago
  • Thank your for your progress report. I am glad that you have solved the problem by the power button + cron method. For now your mpu6050 uses a while True foreve loop. IF you data logging is NOT time dependent, ie can start and stop “almost” time, and can loop shorter periods, say three minutes loops, but every time you start each time, you can also decide how many loops to do, everything ends and return to the shell. Of course in this case you can have a big master ask user if start or stop loop of small slave accelero and data log loops. – tlfong01 26 mins ago
  • You suggestion of next step using an Interrupt GPIO button is also a good solution. GPIO interrupt button is actually a sort of background (very efficient hardware loop) process. So in this case, you have the foreground accelero data log loop process, and the background interrupt monitoring loop process. The interrupt GPIO button with call back function to start/stop a foreground process is actually not that difficult to write, if you do not have many nested interrupts. I am just thinking aloud and not proof reading at all. Sorry for the typos. – tlfong01 22 mins ago
  • So to repeat, with the interrupt GPIO button program, your job is almost done. I would suggest you to try the interrupt program and if stuck, ask for help here. I guess mnay people would help you for interrupt trouble. – tlfong01 19 mins ago
  • Now let me come back to my concurrent python programming solution. I find it feasible and not difficult to write the hello program and auto scheduled command program running concurrently, with a “queue” for interprocess communication (master porcess sending stop/stop commands to the slave print Hello World program. The good thing of using multiprocessing is that it is every easy to scale up. Eg, I can add more concurrent processes, accelero, gyro magnetic, etc and perhaps more master command processes easily. The messy part is to develop a library/template/API/server. / to continue, … – tlfong01 12 mins ago
  • My next step is to do something like this: (1) Write three LED blinking program and let them run concurrently. (2) Write another looping (yes, no interrupt) program to detect one or more GPIO buttons. The one master buttons and three or more LED programs would run as concurrently, using the following interprocess communication methods: (1) queue, (2) pipe, (3) shared (by master and one or more processes) integer, (4) shared integer array. It might take me one or two weeks to complete. I would suggest you to check my progress only after I pass some milestones. Cheers. – tlfong01 3 mins ago   Edit
  • I will be using GpioZero and other python multiprocessing tricks as explained in the couple of tutorials in the references. In case you would like to have a sneak preview of the development steps of my concurrent server/library/AP, you need to skim those tutorials to get a rough idea first. – tlfong01 just now   Edit
1

You may need a few changes first.

Run a groups command on the Pi and make sure your current user (most likely: pi) is a part of gpio group. If you’re not, run the following: usermod -a -G gpio pi (the general syntax: usermod -a -G <group> <user>, command is *nix specific, not just Pi, read more here: https://www.raspberrypi.org/forums/viewtopic.php?t=74453)

When you add the user, check again with groups command and if all looks hunky-dory, reboot the Pi: sudo reboot now (required for the group changes to take effect; logout-login may be enough, but I reboot)

Then you’ll need to read a digital input. Here is some sample python code:

#!/usr/bin/python

from gpiozero import Button
import json

# class gpiozero.Button(pin, *, pull_up=True, active_state=None, bounce_time=None, 
hold_time=1, hold_repeat=False, pin_factor$
pin16 = Button(23, False)
pin18 = Button(24, False)

my_data = {"pin16":1 if pin16.is_pressed else 0, "pin18":1 if pin18.is_pressed else 0}

print(json.dumps(my_data))

In my case, the script prints a JSON object like: {"pin16": 1, "pin18": 0}; you may not need JSON, but I hope it is clear in the code how JSON is generated, change at will.

In order to wire a button, here is one of many tutorials https://raspberrypihq.com/use-a-push-button-with-raspberry-pi-gpio/

Just make sure that you DO NOT connect a GPIO pin to a 5V source, make sure it is 3.3V, otherwise you will damage that pin or possibly the whole board. You’ve been warned 🙂

Look up the Pi docs for reading GPIO. The class is called Button (which conveniently matches with what you need, but in my case, it is actually a generic digital input, not a button…). Also note that my sample code is for Pi Zero, depending on which Pi you use, this may be a tad bit different (but the overall idea should still be the same).

Once that’s understood, you’ll need to create a script (or a compiled binary) that runs in the background and monitors an input for status change, you can look up how to create a background process in Linux. For all you care, it could be a python script in the startup, just make sure to run it with & at the end (i.e. python /path/to/monitor_button.py&). That puts the command in background and moves on with the script that launched it (check if it’s running with jobs command).

share  edit  follow    flag
  • 1
    Thanks for detailed explanation. I tried to play around with the button events so i start to get an idea of needed steps. Next thing is to look up how create a background process in Linux. – Maksym Kepskyy May 13 at 4:21
.END

Categories: Uncategorized

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: