1

I'm running a bell app connected by ENet between two RPI 4. The button end works well, but the server end only works when I sign in to activate it. How do I get it to start at start-up in Crontab?

@reboot python3 /home/pi/Coding/BellButton.py >> /home/pi/Coding/BellButton.log

Log has nothing

#!/usr/bin/env python3
import threading
import pygame.mixer
import automationhat
import datetime
from datetime import date
import time
import socket

class ButtonServer(threading.Thread):
    def __init__(self, channel, serverIP, serverPort):
        threading.Thread.__init__(self)
        self.channel = channel
        self.serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.serv.bind((serverIP,serverPort))
        self.serv.listen(5)
        self.BellButtonOne = -1
        self.BellButtonTwo = -1
        self.BellButtonThree = -1
        self.deamon = True
        self.start()

    def run(self):
        while True:
            conn, addr = self.serv.accept()
            from_client = ""

            while True:
                from_client = ""
                data = conn.recv(4096)

                if not data:
                    break

                from_client = data.decode()

                if from_client == "Input 1 On":
                    self.BellButtonOne = 1
                if from_client == "Input 1 Off":
                    self.BellButtonOne = 0

                if from_client == "Input 2 On":
                    self.BellButtonTwo = 1
                if from_client == "Input 2 Off":
                    self.BellButtonTwo = 0

                if from_client == "Input 3 On":
                    self.BellButtonThree = 1
                if from_client == "Input 3 Off":
                    self.BellButtonThree = 0
            conn.close()

B8 = ButtonServer(36,"123.456.789.123",12345)

pygame.mixer.init(48000, -16, 1, 1024)
pygame.mixer.init()

sndAChr = pygame.mixer.Sound("/home/pi/Public/Active/ChristmasChurchBell.wav")
sndBChr = pygame.mixer.Sound("/home/pi/Public/Active/ChristmasDinnerBell.wav")
sndA = pygame.mixer.Sound("/home/pi/Public/Active/ChurchBell.wav")
sndB = pygame.mixer.Sound("/home/pi/Public/Active/DinnerBell.wav")
sndB1 = pygame.mixer.Sound("/home/pi/Public/Active/DinnerBell2.wav")
sndB2 = pygame.mixer.Sound("/home/pi/Public/Active/DinnerBell3.wav")
sndC = pygame.mixer.Sound("/home/pi/Public/Active/Doing.wav")

snd1 = pygame.mixer.Sound("/home/pi/Public/Active/Doing.wav")
snd15 = pygame.mixer.Sound("/home/pi/Public/Active/Quarter.wav")
snd30 = pygame.mixer.Sound("/home/pi/Public/Active/Half.wav")
snd45 = pygame.mixer.Sound("/home/pi/Public/Active/3Quarter.wav")
snd100 = pygame.mixer.Sound("/home/pi/Public/Active/Hour.wav")

sc1 = pygame.mixer.Channel(1)
sc1.play(snd1)
playDoing = False

DinnerBellNo = 0
while True:
    tnow = datetime.datetime.now()
    today = date.today()
    ChristStartYear = date(today.year,11,19)
    ChristEndYear = date(today.year + 1,1,7)

    if B8.BellButtonOne == 1:
        if pygame.mixer.get_busy():
            playDoing = False
            pygame.mixer.stop()
        time.sleep(0.2)
        if B8.BellButtonOne == 0:
            if today >= ChristStartYear and today <= ChristEndYear:
                sc1.play(sndAChr)
            else:
                sc1.play(sndA)
            time.sleep(0.2)

    if B8.BellButtonTwo == 1:
        if pygame.mixer.get_busy():
            playDoing = False
            pygame.mixer.stop()
        time.sleep(0.2)
        if B8.BellButtonTwo == 0:
            if today >= ChristStartYear and today <= ChristEndYear:
                sc1.play(sndBChr)
            else:
                if DinnerBellNo == 2:
                    DinnerBellNo += 1
                    sc1.play(sndB2)
                if DinnerBellNo == 1:
                    DinnerBellNo += 1
                    sc1.play(sndB1)
                if DinnerBellNo == 0:
                    DinnerBellNo += 1
                    sc1.play(sndB)
                if DinnerBellNo == 3:
                    DinnerBellNo = 0
                time.sleep(0.2)

    if B8.BellButtonThree == 1:
        if pygame.mixer.get_busy():
            playDoing = False
            pygame.mixer.stop()
        time.sleep(0.2)
        if B8.BellButtonThree == 0:
            sc1.play(sndAChr)
            time.sleep(0.2)



    #On Board Buttons
    #if automationhat.input.one.is_on():
    #    if pygame.mixer.get_busy():
    #        playDoing = False
    #        pygame.mixer.stop()
    #    time.sleep(0.2)
    #    if automationhat.input.one.is_off():
    #        if today >= ChristStartYear and today <= ChristEndYear:
    #            sc1.play(sndAChr)
    #        else:
    #            sc1.play(sndA)
    #        time.sleep(0.2)

    #if automationhat.input.two.is_on():
    #    if pygame.mixer.get_busy():
    #        playDoing = False
    #        pygame.mixer.stop()
    #    time.sleep(0.2)
    #    if automationhat.input.two.is_off():
    #        if today >= ChristStartYear and today <= ChristEndYear:
    #            sc1.play(sndBChr)
    #        else:
    #            if DinnerBellNo == 2:
    #                DinnerBellNo += 1
    #                sc1.play(sndB2)
    #            if DinnerBellNo == 1:
    #                DinnerBellNo += 1
    #                sc1.play(sndB1)
    #            if DinnerBellNo == 0:
    #                DinnerBellNo += 1
    #                sc1.play(sndB)
    #            if DinnerBellNo == 3:
    #                DinnerBellNo = 0
    #        time.sleep(0.2)

    #if automationhat.input.three.is_on():
    #    if pygame.mixer.get_busy():
    #        playDoing = False
    #        pygame.mixer.stop()
    #    time.sleep(0.2)
    #    if automationhat.input.three.is_off():
    #        sc1.play(sndAChr)
    #        time.sleep(0.2)

    #Chime Clock
    if tnow.hour >= 6 and tnow.hour <= 22:
        if tnow.hour % 1 == 0 and tnow.minute == 0 and tnow.second == 0:
            if not pygame.mixer.get_busy():
                sc1.play(snd100)
                playDoing = True
            time.sleep(0.2)

    if tnow.hour >= 6 and tnow.hour <= 21:
        if tnow.minute % 15 == 0:
            if tnow.minute == 15 and tnow.second == 0:
                if not pygame.mixer.get_busy():
                    sc1.play(snd15)
                    time.sleep(0.2)
            if tnow.minute == 30 and tnow.second == 0:
                if not pygame.mixer.get_busy():
                    sc1.play(snd30)
                    time.sleep(0.2)
            if tnow.minute == 45 and tnow.second == 0:
                if not pygame.mixer.get_busy():
                    sc1.play(snd45)
                    time.sleep(0.2)

    if playDoing == True and not pygame.mixer.get_busy():
        h=tnow.hour
        if h > 12:
            h -= 12
        sc1.play(snd1, h-1)
        playDoing = False

3 Answers3

3

@Ingo has provided a good and correct answer. This answer is provided to augment Ingo's answer & provide details that may be helpful to your understanding.

Know that cron is useful, but to use it effectively, you should understand its limitations.

1. cron has no knowledge of the state of your system during the boot process.

This means that it is your responsibility to ensure that the services required to execute your @reboot entry in your crontab file are available before your script/program is started. In general, this can often be accomplished by simply adding a sleep statement to your @reboot entry in crontab.

2. cron runs under a different environment than your user id.

When you are logged in under a user id (pi for example), the OS has created an environment that includes (among other things) a default path in which to find executables. However, when your cron job executes, it is NOT executed with all of the same environment variables as your user id. Since cron's PATH environment variable is not defined same as your user id, then you should use a "complete" file specification (path) for your executable to ensure your system knows which file(s) you intend to execute or use.

If you want to explore this business of the environment in more detail:

  • To get your (user id) environment at the bash command line: printenv

  • To get the cron environment, use this technique

3. a failed cron job needs your help to tell you if or why it fails to execute.

This is related to the limitation above; i.e. your cron job is not actually run under your user id. Linux utilizes three (3) "streams" to communicate with a user: stdin, stderr and stdout. When you use a terminal to interact with your RPi, the system knows where to direct your input/commands at the terminal (stdin to a process), and it also knows to direct any output (stderr and stdout) from that process to your terminal. HOWEVER, unless you tell the system, it doesn't know where to direct the cron user's output or error messages. Thus, when a cron job fails to run, an error message is (may be) generated, but the system doesn't know where this "stream" should be directed, and you (your user id) doesn't have the benefit of this feedback. It's a bit like driving with your eyes closed. Recovering the stderr output from cron is accomplished with a simple "redirect" to a file.

OVERCOMING cron's LIMITATIONS

Fortunately, it's fairly straightforward to deal with all three of these limitations. I'll use your cron job to illustrate how you might deal with all these limitations by modifying your one line cron job:

@reboot ( /bin/sleep 30; /usr/bin/python3 /home/pi/Coding/BellButton.py  > /home/pi/cronjoblog 2>&1)   

If any of this is unclear, please let us know.

Seamus
  • 21,900
  • 3
  • 33
  • 70
2

You tagged the question with python-3 but you call everywhere python that is calling python 2. Because you don't tell us how do you start the script after login it is unclear why it then unexpected runs. You should also use full path calls in crontab:

@reboot /usr/bin/python3 /home/pi/Coding/BellButton.py >> /home/pi/Coding/BellButton.log

Also correct the shebang (first line) in your script to:

#!/usr/bin/env python3
Ingo
  • 42,107
  • 20
  • 85
  • 197
2

In addition to what @ingo has stated, crontab can often start running your script before the Pi is able to do any networking, so it might be worth adding a sleep function to your code after your imports.

Marc Scott
  • 31
  • 1