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
asked Nov 6, 2019 at 23:45
1
  • 1
    please format all of your code, not just parts of it Commented Nov 7, 2019 at 1:39

3 Answers 3

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.

answered Nov 7, 2019 at 16:00
4
  • 1
    Awesome!! I have fooled around for two weeks just to try and get this going! A simple wait/sleep that fixed the problem. Commented Nov 7, 2019 at 17:20
  • Another problem; How would I make it so if the connection is broken so it reconnects? Commented Nov 9, 2019 at 15:31
  • When the server side looses power at power outages but the client doesn't how do I check if that happens to shut down the connection on both ends and reconnect? Commented Nov 9, 2019 at 15:35
  • or when the client looses power/shuts down how do I reconnect them? Commented Nov 9, 2019 at 15:35
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
answered Nov 7, 2019 at 9:41
2
  • 1
    Sorry Guys!, I don't know why it didn't format the code right, I pasted it and look good to me, after posting it was all jumbled together. Commented Nov 7, 2019 at 16:26
  • 1
    I know I need to make sure that all resources are running before executing, not sure how though. Commented Nov 7, 2019 at 16:27
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.

answered Nov 7, 2019 at 11:46

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.