[PYTHON] Visualized the usage status of the sink in the company

Background

I'm the type of person who brings lunch to the office, but it's a pain to wash. The process of going home and washing the lunch box is very painful. So, I want to wash it in the sink of the company. However, there are quite a few people in the company who think the same way, and they have to wait for a vacancy in the daytime or the sink. This can be a hassle, and there was a story that I would like you to be notified when it becomes available. And I think this is rarely common, but I heard that an unused Raspberry PI (hereinafter referred to as Raspberry Pi) was dropped in the company and I thought "I think I can notify if there is a person with this?" This is the background.

What to make

It notifies you when there are people in the sink for a certain period of time, and when there are no people for a certain period of time, it notifies you that you are gone. Use Slack as the notification destination.

Procurement of sensors

I need a sensor and a jumper wire, but I have everything at Marutsu Akihabara Main Store. We also sell the Raspberry Pi itself.

What to prepare (the one used this time)

If you don't want to put a Raspberry Pi naked,

Or I think that there is also a self-made version such as a plastic version.

Prerequisites

procedure

Connect and test the motion sensor

Follow the link below to connect the motion sensor and Raspberry Pi and copy and run the program. [Raspberry Pi] How to use and use the self-made motion sensor -CHASUKE \ .com

If it works without any problem, the connection is OK.

Try to throw to Slack if people are still there

The motion sensor detects a human and reacts immediately, so it notifies you when a person passes by. So, I will notify you when I stay for a certain period of time. In addition, there were many cases where it was detected that there were no people even if there were people, so if there are no people for N seconds, it is judged that the sink has been used. That is the following code.

sink_observer.py


from datetime import datetime
import RPi.GPIO as GPIO
import json
import logging
import requests
import sys
import time
​
GPIO_PIN = 18
SLACK_SETTINGS = {
    'url'        : '{URL of Incoming Webhook}',
    'botname'    : 'who's eyes are those',
    'icon'       : ':eyes',
    'start_using': 'There are people in the sink.',
    'finished'   : 'It seems that you have finished using it.'
}
is_using = False
​
formatter = '%(levelname)s \t%(asctime)s\t%(message)s'
logging.basicConfig(filename='sink_observer.log', level=logging.INFO, format=formatter)
​
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
​
def push_to_slack(status):
    datas = {
        'username'   : SLACK_SETTINGS['botname'],
        'icon_emoji' : SLACK_SETTINGS['icon'],
        'text'       : SLACK_SETTINGS[status],
        'contentType': 'application/json'
    }
    payload = json.dumps(datas)
    requests.post(SLACK_SETTINGS['url'], payload)
​
def start_using():
    global is_using
​
    if is_using:
        return
    is_using = True
    push_to_slack('start_using')
    logger.info('start_using')
​
def finished():
    global is_using
​
    if not is_using:
        return
    is_using = False
    push_to_slack('finished')
    logger.info('finished')
​
def main(sleeptime, interval, retry):
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(GPIO_PIN, GPIO.IN)
​
    detect_cnt = 0
    no_detect_cnt = 0
    try:
        logger.info('End:Ctrl+C')
        while True:
            if GPIO.input(GPIO_PIN) == GPIO.HIGH:
                detect_cnt += 1
                #Log for operation check
                logger.info('detect\tdetect_cnt:{}'.format(str(detect_cnt)))
                if detect_cnt <= 2: # HACK:Actually, it is easier to adjust if you can also specify here
                    time.sleep(interval)
                    continue
                detect_cnt = 0
                no_detect_cnt = 0
                start_using()
                time.sleep(sleeptime)
            else:
                no_detect_cnt += 1
                logger.info('no detect\tdetect_cnt:{}'.format(str(detect_cnt)))
                if no_detect_cnt <= retry:
                    time.sleep(interval)
                    continue
                detect_cnt = 0
                no_detect_cnt = 0
                finished()
                time.sleep(interval)
    except KeyboardInterrupt:
        logger.info('In the process of termination...')
    finally:
        GPIO.cleanup()
        logger.info('GPIO clean completed')
​
if __name__ == '__main__':
    sleeptime = 10
    interval  = 3
    retry     = 10
    #For parameter adjustment
    if len(sys.argv) > 4:
        sleeptime = int(sys.argv[1])
        interval  = int(sys.argv[2])
        retry     = int(sys.argv[3])
​
    main(sleeptime, interval, retry)

Start-up

SSH to the Raspberry Pi and do the following: You only need to install pip for the first time.

Do only the first time

$ pip3 install requests

From the next time onwards, this is all you need

$ nohup python3 sink_observer.py &

Since it does nohup and&, it keeps working even if the SSH connection is disconnected. If you want to quit, you can look up the process number with ps aux | grep sink_observer.py and quit withkill {process number}.

Reuse Raspberry Pi Box

I thought it was naked, so I processed the plastic box that contained the Raspberry Pi. I drew a marker appropriately where the GPIO / USB / power supply / LAN cable was passed, made a hole with a pin vise, and then trimmed the edge with a design knife. The box I processed this time was made of polypropylene-based plastic, and it broke when I wore it sideways, so it was important to make a hole with a pin vise. After that, make a hole in a suitable place in the case, pass a wire, and fix the motion sensor, and you're done. It turned out to be something like this. The note paper is in the foreground because the motion sensor has a viewing angle of 100 °, which limits the field of view to prevent false positives (a complete first aid measure). IMG_5364.JPG

Postscript (2019/01/23)

I made the following because it makes a false positive too badly. 2DA5332C-AF54-4545-B96F-2FC82EF70528.jpeg

As a result of doing this, the false positive rate dropped sharply.

About false positives

It's just a hypothesis. From the conclusion, it is the theory that the sensor responded to visible light. The specific content of false positives

It was that. The characteristic of the sensor is an infrared sensor that uses a pyroelectric element. The pyroelectric element itself can detect infrared rays and light (visible light) in a rough interpretation. The theory is that the human body emits infrared rays, so it can be used as a motion sensor. However, it does not detect only infrared rays, but also reacts to ** visible light, that is, the light we see **. The Raspberry Pi and Wi-Fi dongle clearly emitted visible light, so detecting this would result in a false positive. The improvements made were made with the aim of limiting the 100 ° viewing angle of the sensor and giving it directivity, while keeping the sensor away from the light emitted by the Raspberry Pi and the light emitted by the Wi-Fi dongle. is. I made a large-scale one with plastic boards and plastic sticks, but if I can do the same thing, I think it is not necessary to make it so far. This eliminates false positives caused by some people passing in front of the sink and false positives caused by detecting Raspberry Pi / Wi-Fi dongle light at night. After doing this, the false positive rate seems to be less than 1%.

Summary

~~ It will be falsely detected. It's unavoidable because there is a sink in a busy place ... However, it is functioning properly, so if you devise an algorithm or adjust the parameters, it will be at a practical level. Now you don't have to wait for the sink or go back to your seat because you're using it ... I hope!

Recommended Posts

Visualized the usage status of the sink in the company
Analyze the usage status of the contact confirmation application (COCOA) posted in "Image" / Tesseract
[Blender] Know the selection status of hidden objects in the outliner
The story of participating in AtCoder
The story of the "hole" in the file
The meaning of ".object" in Django
Visualize the response status of the census 2020
I checked the usage status of the parking lot from satellite images.
Using TensorFlow in the cloud integrated development environment Cloud9 ~ Basics of usage ~
[Understanding in 3 minutes] The beginning of Linux
The story of an error in PyOCR
Implement part of the process in C ++
Scraping the usage history of the community cycle
The result of installing python in Anaconda
Let's claim the possibility of pyenv-virtualenv in 2021
The usage of TensorBoard has changed slightly
The basics of running NoxPlayer in Python
Display the status of COVID 19 infection in Japan with Splunk (GitHub version)
Non-logical operator usage of or in python
In search of the fastest FizzBuzz in Python
Status of each Python processing system in 2020
Output the number of CPU cores in Python
The meaning of {version-number} in the mysql rpm package
[Python] Sort the list of pathlib.Path in natural sort
Change the font size of the legend in df.plot
Get the caller of a function in Python
Match the distribution of each group in Python
Make a copy of the list in Python
Find the number of days in a month
Read the output of subprocess.Popen in real time
Find the divisor of the value entered in python
The story of finding the optimal n in N fist
Fix the argument of the function used in map
Find the solution of the nth-order equation in python
The story of reading HSPICE data in Python
[Note] About the role of underscore "_" in Python
Put the second axis in 2dhistgram of matplotlib
Solving the equation of motion in Python (odeint)
Output in the form of a python array
The story of viewing media files in Django
Search by the value of the instance in the list
Make progress of dd visible in the progress bar
Organize the super-basic usage of Autotools and pkg-config
Factfulness of the new coronavirus seen in Splunk
Check the status of your data using pandas_profiling
Extract the status code error in the 400,500 range of the apache access log by specifying the time range.
Scraping the usage history of the community cycle PhantomJS version
Experience the good calculation efficiency of vectorization in Python
Specify the lighting Model of SCN Material in Pythonista
Used from the introduction of Node.js in WSL environment
How to get the number of digits in Python
In search of the best random dot stereogram (RDS).
The story of building the fastest Linux environment in the world
Count the number of parameters in the deep learning model
○○ Solving problems in the Department of Mathematics by optimization
Omit the decimal point of the graph scale in matplotlib
Python --Explanation and usage summary of the top 24 packages
[python] Get the list of classes defined in the module
Make a note of the list of basic Pandas usage
About testing in the implementation of machine learning models
The story of FileNotFound in Python open () mode ='w'