Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Yet another shutdown solution
#1
Hi all

After playing with a few different options for shutting down my Pi safely, I kind of gave up until a discussion on Facebook.  A very clever guy called Ronald who is doing stuff using the can-bus suggested using Bluetooth detection.  I'd done a load of stuff on this in the house a while ago to detect presence and adjust the heating so I can't believe I was so close to a solution and didn't think of it.

So, this is my Python script *so far*.  I've called it inoutboard.py because that's where I got the initial code from and it's in a directory under my home directory called presence:

Code:
import bluetooth
import time
from subprocess import call
while True:
    result = bluetooth.lookup_name('xx:xx:xx:xx:xx:xx', timeout=10)
    if (result != None):
        score = 1
        #print "Detected"
    else:
        #print "Not detected"
        time.sleep(90)
        result = bluetooth.lookup_name('xx:xx:xx:xx:xx:xx', timeout=10)
        if (result == None):
            call("sudo shutdown -h now", shell=True)
    time.sleep(30)

You just need to get your Bluetooth MAC address from your phone and put that in instead of the xx bit in 2 places.

I know this isn't the most elegant code but it works and I'll carry on improving it over time.

So this looks for the Bluetooth address of my phone.  If it's found then it just waits 30 seconds and looks again.  If it's not found, it waits 90 seconds and checks again.  If it's still not found then it shuts down the Pi.  I might change this later.  I was thinking that I would set it to 15 minutes.  That should be long enough to do something like fill up with fuel and pay without the Pi shutting down so you don't have to wait for it to come up again.

I'm running this as a service on the Pi so it start on boot and should restart if it crashes or anything.  The only thing you need to ensure is that "Wait for network at boot" is enabled in raspi-config > Boot options.  Otherwise the service fails to start.

To run this as a service, create a file called detect.service and copy it to /etc/systemd/system.  The file should look like this:

Code:
[Unit]
Description=My service
After=network.target

[Service]
ExecStart=/usr/bin/python -u inoutboard.py
WorkingDirectory=/home/pi/presence
StandardOutput=inherit
StandardError=inherit
Restart=always
User=pi

[Install]
WantedBy=multi-user.target

Then run this to start the service:

sudo systemctl start detect.service

The next command will tell you if the service is active or not.  Hopefully it's active.

sudo systemctl is-active detect.service

And finally, this will enable the service when you boot

sudo systemctl enable detect.service

Now when you boot your Pi, the service should start.  If Bluetooth is on then the Pi will just keep running.  If you turn Bluetooth off or your phone disconnects because you got home then the Pi should shut down.

This, of course, assumes that you car is parked far enough away from your house that the Bluetooth will disconnect.  I turn my phone onto airplane mode at night so it's unlikely to kill the car battery if, by chance, it stays connected for a few hours but needs to be considered.

There are a few improvements I'm going to make.

1.  If I change the timeout to 15 minutes then I want to add something that will turn the screen off sooner.
2.  I want to add a score so that instead of just relying on 2 failed detects, it will detect multiple times over the timeout period and only shut down if they all fail.  That should take care of any temporary glitches.  Checking at the start and end is probably good enough but you may get an instance where just by sheer coincidence it can't see your phone for those 2 times.
3. I have a "Tile" which uses BLE.  I might try with this instead.  I tried this for presence at home but it was a bit flaky because the Tile only sends out a beacon message every now and then.  So you have to be sure that your detection period coincides with when the Tile is active.  My Tile is on my keyring so it's not then dependant on having the phone in the car and would then work for multiple phones - if my other half borrows my car and uses her phone, for example.

If anyone has any other improvements, spots any problems or can't get it working I'd be happy to hear and help if I can.  Otherwise, feel free to use this yourself.  I've been messing with it this morning and it seems to work fine.

Cheers
Andy
Reply
#2
Nice solution. But if I understand it correctly you cannot use oap if you lose your phone, or if you leave it at home.

Oh and one more question. How do you start your rpi after shutdown because it's connected to constant bat+ 12V.
Reply
#3
I wouldn't use OAP without my phone anyway.  But it would be simple enough to add a button to the apps page to stop the service if you wanted to use the Pi without a phone.  I'm still going to try with my Tile as well.  They're cheap as chips to buy.  It's just this notion of the Tile only being "alive" for short periods - I guess that's how they make the battery last for months and months.  There may be other BT beacon type devices that would work.

I've got a momentary switch connected between gnd and the "Run" pin on the circuit board.  Shorting them out will reboot if the Pi is up and wake it if it's in a halt state.

I've done a bit of a rewrite on the code now as well so it looks like this:

Code:
#!/usr/bin/python2
import bluetooth
import time
from subprocess import call
while True:
        score = 0
        for x in range(15):
                result = bluetooth.lookup_name('xx:xx:xx:xx:xx:xx', timeout=5)
                if (result == None):
                        score = score +1
                        #print "Not detected"
                        #print score
                        if (score == 2):
                                call("echo 1 > /sys/class/backlight/rpi_backlight/bl_power", shell=True)
                                #print "Screen off"
                        time.sleep(10)
                else:
                        score = 0
                        #print "Detected"
                        #print score
                        call("echo 0 > /sys/class/backlight/rpi_backlight/bl_power", shell=True)
                        #print "Screen on again"
                        time.sleep(10)
        if (score == 15):
                call("sudo shutdown -h 15", shell=True)
                #print "Gone away forever"
        else:
                call("sudo shutdown -c", shell=True)
                #print "Back again"
        time.sleep(30)

So this now checks every 10 seconds.  If the phone is not detected then it increments the score.  If the phone is detected, it resets the score and starts again.

If the score gets to 2 then it turns the screen off.  But it will turn it on again if the phone is detected.

If the score gets to 15 (15 "not detected" in a row over 150 seconds), it tells the Pi to shutdown in 15 minutes.

If during the 15 minute count down it detects the phone again it cancels the shutdown.

I've left some print statements in just for when I want to debug.

I'm not a coder.  There are probably much more efficient ways of coding this but it's the best I can do with my knowledge and it seems to work in the tests that I've been running.
Reply
#4
this needs to be tested then added as an option to OA Smile but for now, i also use a momentry switch hooked up to a gpio to shutdown
Reply
#5
The "tested" bit is probably the most important. It seems I have a flaw in there somewhere.

I tested it several times last night when I was playing. Went to bed and turned my phone onto airplane mode with the Pi running downstairs. Came down this morning, screen is off so I thought the Pi must have shutdown. Turned airplane mode off and a few seconds later the screen came back on so the Pi had not shut down last night. Not sure why that is. Looking at the logs, it seems that it sometimes gets a "hit" even when my phone is in airplane mode. Maybe BT still sends a ping in airplane mode? I'm going to try it again by specifically switching BT off to more closely simulate losing the connection.
Reply
#6
Ok, I've done a bit more work in the code.  The code above worked some of the time but is a bit scrappy.  This seems to be working a lot better.  I haven't managed to get it to fail yet.

Code:
#!/usr/bin/python2
import bluetooth
import time
from subprocess import call
timeout = 15
scoreOUT = 0
scoreIN = 0
while True:
        result = bluetooth.lookup_name('xx:xx:xx:xx:xx:xx', timeout=5)
        if (result == None):
        scoreOUT = scoreOUT +1
        scoreIN = 0
        if (scoreOUT == 2):
            call("echo 1 > /sys/class/backlight/rpi_backlight/bl_power", shell=True)
        if (scoreOUT == timeout*60/10):
                    call("sudo shutdown -h now", shell=True)
        print "Score: ", scoreOUT
        time.sleep(10)
    else:
        scoreIN = scoreIN +1
        if (scoreIN > 1):
            call("echo 0 > /sys/class/backlight/rpi_backlight/bl_power", shell=True)
            scoreOUT = 0
            scoreIN = 0
        time.sleep(10)

This works slightly differently.  I haven't bothered with a timeout on the shutdown command now.  If it's not connected during the loop then there's not much point giving it extra time on top.

The timeout variable is *roughly* in minutes.  So in this case, it runs the loop for 15 minutes.  If it fails to connect in that time, it shuts down.  It switches the screen off after 2 misses, and back on with 2 consecutive hits.  2 consecutive hits also resets the counter.  So it has to go a full 15 minutes before it shuts down.  This is my fill up with petrol timer.

You just need to add your BT MAC address and change the timeout if you want it different to 15 minutes.

I'm fitting a new exhaust if it stops raining so I might not have the chance to test this for real in the car today.  If the rain carries on then I might do this instead.

Cheers
A
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)