Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number
 
PerlMonks  

Continuous or timed?

by Bod (Chaplain)
on Dec 11, 2020 at 16:13 UTC ( #11125006=perlquestion: print w/replies, xml ) Need Help??

Bod has asked for the wisdom of the Perl Monks concerning the following question:

For the project that was talked about here -> Controlling USB on Raspberry Pi.
The software needs to open and close curtains at specified times each day. 8:30am and sunset. So it really only needs to be active a couple of times each day but those times change. One always does and the other potentially will in the future.

From a design viewpoint this seems to leave three options:

  1. Script runs a continuous loop
  2. CRON kicks it into life ever few (5?) minutes
  3. Script writes to CRON the next time it needs to fire up
It seems to me that 1 is not such a good plan because, if for any reason the script were to fail, the system fails and doesn't restart. 2 would mean that the granularity of times the curtains could actually change state is quite big as set by CRON. Although this is not a big issue for this project I am trying to build something using sound design principles. Which leaves 3 which prevents the next event being changed. Again not a problem here but perhaps not the best design.

Gut feeling is that 2 is the way to go but I'm failing to convince myself...

Which one of these, combination of these or some other option I have not considered would you choose?

Replies are listed 'Best First'.
Re: Continuous or timed?
by hippo (Chancellor) on Dec 11, 2020 at 16:24 UTC

    For this particular requirement I would go with 2 every time. As you say, it doesn't need to be minute-accurate and moreover you know the windows for each operation so it doesn't even need to fire up every 5 mins (eg. not between 23:00 and 05:00 and not between 09:00 and 15:00 (lat/long/TZ depending).

    The other option like 3 is not to use cron but to use at instead. The script can calculate the time of next op and submit itself as an at job for that time. I have used Schedule::At for precisely this in the past.


    🦛

      Thank you hippo

      I have used Schedule::At for precisely this in the past

      I didn't think about at because I mostly use a Windows environment and at has been deprecated in Windows 10 in favour of the more involved schtasks. But, of course, with the Raspberry Pi I am in a Linux environment which I have never used in any significant depth and not at all recently. Schedule::At looks very useful :)

Re: Continuous or timed?
by stevieb (Canon) on Dec 11, 2020 at 17:23 UTC
    "It seems to me that 1 is not such a good plan because, if for any reason the script were to fail, the system fails and doesn't restart."

    That, right there, is precisely why I recommended picking up a $3 microcontroller for the project instead.

    A microcontroller does one thing, and one thing only... executes the code you write. It does nothing more. It is not have the layers upon layers of software and complexity nor the potential problems that a script has running on top of an OS does.

    You've gone the Pi route, so I'll stick with that. If the system fails or doesn't restart, cron isn't going to help you, so 1 & 2 are prone to the same type of failure there. However, if the system does fail, you've got bigger problems anyway.

    Unless you're willing to get into extra complexity of having a watchdog type script running to monitor the main process, I'd stick with cron, ensuring that the script that's being run verifies that it can never start more than one instance of itself, and if it makes such an attempt, fail and send out a warning message of some sort.

      precisely why I recommended picking up a $3 microcontroller for the project instead

      I do get that and it makes a lot of sense.
      However, I see this also as an opportunity to get a better understanding of Linux, plus I want to be able to remotely update the times for closing and opening without having to visit. For future reference, would a microcontroller be able to call out to a web server and retrieve an updated settings file?

      Just from setting up the RPi my knowledge has improved so, on one level this project is a success already 😊

        "For future reference, would a microcontroller be able to call out to a web server and retrieve an updated settings file?"

        100% yes. In fact, in my indoor grow automation system, I have a microcontroller for each grow tent, and all of them reach out to a central system over HTTPS and retrieve their configuration from there (with a fallback default config if the server can't be reached). I don't pull a file however, the system returns back a JSON string which I parse as the config.

Re: Continuous or timed?
by afoken (Canon) on Dec 11, 2020 at 16:24 UTC

    Create a cron job running around midnight or high noon, have that job calculate sunrise and sunset and submit them to the cron system.

    Depending on your cron implementation, it may be sufficient to create a file in a specific directory containing the two times and the open and close commands. Other cron implementations may need to run a command like crontab to submit such a file.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

      Create a cron job running around midnight or high noon, have that job calculate sunrise and sunset and submit them to the cron system.

      That seems a very sensible approach and certainly one I would adopt if higher accuracy were needed. I like that is gets kicked off daily so is immune to the script failing.

Re: Continuous or timed?
by tobyink (Canon) on Dec 11, 2020 at 17:19 UTC

    If the script has a very low start-up cost/time, then I'd go with #2 and set cron to run it very frequently. Every 2 minutes, or perhaps every minute.

      The startup time is negligible and it doesn't really matter as the script will be the only thing running on the device other than the OS. There will be an updater so I can remotely update the script but that will run in the early hours of the morning so it doesn't interfere with anything else...

Re: Continuous or timed?
by perlfan (Vicar) on Dec 11, 2020 at 19:04 UTC
    You are a good nephew. I love when Perl can help people in real life.

    Have you run into MisterHouse? It's specifically for home automation and written in Perl. Seems like it just needs perl and your customized routines. Not sure about device support.

      You are a good nephew

      Very kind of you to say so - I do try to be a good human being as it makes the world a better place :)

      Have you run into MisterHouse?

      I hadn't - thanks for pointing it out
      Something else to add to the growing list of things to investigate :)

Re: Continuous or timed?
by bliako (Prior) on Dec 11, 2020 at 21:13 UTC

    it depends on how complex you want to end up.

    regarding (1) you can restart a failed script by placing it in an infinite loop within a parent shell script if you are sure you debugged it enough so not to end up with an infinite pile of core-dumps. You can also have cron checking on it and respawn it if it disappears from the process list. You can also have cron sending it periodically a signal to dump diagnostics. In any event "perpetual" script or cron, it will have to sleep for a while in a loop. (1) makes it easier to change that sleep interval and also importantly, you can wake it up with a signal in order to respond to emergencies. So "continuously running" has a lot of merrits too. And it can too reschedule its wake up time with a Perl alarm (need example code?).

    nit-picking: (3) is best with at.

    With (1) you can expand your system to scan a queue of future actions from a database: open curtains at 8, close curtains at 5, water plants, etc. And execute if it's time. Each action is consumed unless it is a recurrent type of action (like the curtains). You can have many types of actions and your script would know how to handle each. The database/queue-of-actions can be updated independently and perhaps remotely or by voice. Also a speech synthesizer can read out the entries to the database of actions-to-do when instructed.

    With (1) I would use a cron job to only make sure that the script is alive, or to periodically send it a signal to dump some diagnostics to email them to you. To have the script running even after a reboot, turn it into a service with systemctl - that buys you a free respawning within reason.

    For your current spec I would go with (2): script is executed every 5 mins, if sunrise it opens, if sunset (calculate it) it closes. And it short-circuits if time of day is not interesting e.g. midnight. That's 12*24 times a day. Nothing really.

    edit: basically you end up with a home-cron hehe

      and perhaps remotely or by voice

      As it happens...I've already received a feature request to have it connected to Alexa!

      if sunset (calculate it) it closes

      I'm pulling sunset from an API and an offset from the control file. Until it is actually in use I won't know if the curtains need to close at sunset or a little while before or after.

      For your current spec I would go with (2)

      That seems to be the consensus here and was my gut feeling. So 2 it shall be. I will be writing an updater to pull down a new script version from the server and SSH is enabled so I will be able to update the code remotely (not sure yet how to access the remote unit but that can be sorted).

        FYI Linux, I hear (no experience), has support for blind users, e.g. connecting a braille display and its voice recognition (I also guess) must be adequate too. Making a voice interface to your project must be feasible.

        Options (1), (2) (I have no experience with microcontrollers) are valid depending on where you want to end up, i.e. how far to expand it. This can easily turn into a useful product with lots of capabilities unless re-inventing the wheel.

        SSH, i.e .accessing the remote unit (uncle's) from your home (re:not sure yet how to access the remote unit): hmm you need to sort out uncle's IP somehow as ISPs tend to arbitrarily change it for home users. The problem is solved if the unit (uncle) accesses your servers (the website you have) to download script+data updates once a day - i think this is what you are suggesting. But then you must make sure that this works perfectly as you will be away. You can always install a back-door, it seems now-a-days it's a requirement in order to be MI5-certified :) There are various techniques to enable incoming SSH with minimal risk. But the risk is there none-the-less.

        The RPi has lots of memory. You could preload a year's(or 10 years) worth of sunrise sundown times and reduce the need for as many updates.

        James

        There's never enough time to do it right, but always enough time to do it over...

Re: Continuous or timed?
by tybalt89 (Prior) on Dec 12, 2020 at 15:18 UTC

    If Raspbian uses systemd (I don't remember, I'm running Manjaro on my Pi) you could make it a systemd service. Systemd can be set to restart it if it fails.

Re: Continuous or timed?
by Marshall (Canon) on Dec 12, 2020 at 22:10 UTC
    I think the consensus here is option #2. That's what I would do or something similar.
    For #3, I wouldn't fool with Chron - just calculate number of seconds until next run and sleep() that long. That way if the system restarts, your process runs immediately and calculates the time until the next run time. That way there is no Chron file involved.

    I have a Raspberry Pi. I am just starting to play with it - so I am a Pi newbie. I need more hardware to do what my intended app needs to do (record HDMI video input). My Pi doesn't have a clock chip in it. When it restarts it needs to get the time/date either from me by me typing it in or via the internet. Your app depends upon knowing the current time - what happens if that time isn't known?

    There might be some startup race conditions after a power failure. I'm not sure about the Pi config options are when the Pi cannot get current date/time from the internet? Maybe Pi is "ready" before the router to the internet?

    Think about a status LED - perhaps blinking means good - stuck on(or off) means bad.

      For #3, I wouldn't fool with Chron - just calculate number of seconds until next run and sleep() that long. That way if the system restarts, your process runs immediately and calculates the time until the next run time. That way there is no Chron file involved.

      You are reinventing the wheel here. cron does exactly that. Calculate the sleep time until the next job needs to run, and sleep. If it wakes up early, that almost certainly has happened because someone submitted changes to the job list (i.e. running crontab).

      Oh, and by the way, don't blindly run after sleep returns. sleep may be interrupted, so your process may have slept for a much shorter time than expected.

      There might be some startup race conditions after a power failure. I'm not sure about the Pi config options are when the Pi cannot get current date/time from the internet? Maybe Pi is "ready" before the router to the internet?

      That could be detected in startup scripts. Linux starts at unix time 0, i.e. 1970-01-01 00:00:00 UTC. So if the application detects a system time years before it was written (if (year < 2020) would be sane), it should assume a fresh boot and just wait for ntpd to do its job. Also, there are RTC chips for the Raspi, so that it adjusts the system clock very early in the boot process, before cron and other services run.

      Think about a status LED - perhaps blinking means good - stuck on(or off) means bad.

      Good idea, easy to implement. Add a cheap red LED (based on GaAs, voltage drop about 1.6 V) and a resistor to a GPIO pin. GPIO voltage level is about 3.3 V, LED current should be around 5 mA, so the resistor value is R = U / I = (3.3 V - 1.6 V) / 5 mA = 340 Ohm. Close standard values are 330 Ohm and 390 Ohm. Heat dissapation is P = U * I = 1.7 V * 5 mA = 8.5 mW for 5 mA. Any cheap resistor can easily heat away at least 125 mW. LED should be connected between GPIO pin and GND, with the resistor in series wih the LED. If the LED is too bright, decrease the current by increasing the resistor value. To get 1 mA, you need 1.7 kOhm, close standard value is 1.8 kOhm. Only very few LEDs are visible at less than 1 mA, so that's about the upper limit for the resistor. The Raspi can't sink or source significantly more than 15 mA, and has an additional limit of about 50 mA for the sum of all GPIO pins, so you don't want more that 5 to 10 mA for the LED, so the resistor should not be less than 170 Ohm.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        Oh, and by the way, don't blindly run after sleep returns. sleep may be interrupted, so your process may have slept for a much shorter time than expected.

        Yes, indeed! I had forgotten about that. Sleep() returns the value of unslept seconds. So if its not zero, just sleep again for that return value. Sleep is often implemented with SIGALRM so that's another potential issue.

        Good description of power/brightness for LED's. As another comment, you can get what are called "resistor LED's". In that case, the resistor is built into the LED. Probably the most common value will be 1K Ohms which is a good choice for 5V. For 3.3V perhaps, 470 or 560 Ohms would be fine. The brightness can also be adjusted by pulsing the LED. I forget exact waveforms that I've used in the past for "ON", but as a starting point, probably a square wave with 20-30ms/on-off is about as bright as 100%. Play with different duty cycles for "on" and see what happens... Current and duty cycle of that current vs perceived brightness is a non-linear function.

        BTW, I like blinking as the "good state" -> that means that the processor is running and is able to turn the LED on/off. Also, different cadences of the LED can be used to indicate error conditions. Human brains are excellent at pattern recognition. My router has a particular flashing light sequence that I recognize as "normal" without really thinking about what each individual light means. If the LED is stuck either at ON or OFF, I would surmise that the processor is "dead". If you just have a single LED, perhaps Long blip, followed by short blip means Internet connection bad, etc.

        "Good idea, easy to implement. Add a cheap red LED (based on GaAs, voltage drop about 1.6 V) and a resistor to a GPIO pin."

        No need even for that on a Pi. In my RPi::WiringPi, I use a couple of command line commands to manipulate the power and disk I/O LEDs so that a user can identify by eye which Pi is the one currently being worked on (this was a user request I implemented). Since this is a Perl forum, I've left the CLI commands within the actual code itself. Essentially, the identify routine turns the power LED off completely, and turns the disk IO LED on solid. Using these LEDs to set them to non-standard configuration could easily indicate a problem with custom software.

        sub io_led { my ($self, $tweak) = @_; if ($tweak){ # stop disk activity from operating the green LED `echo none | sudo tee /sys/class/leds/led0/trigger`; # turn on the green LED full-time `echo 1 | sudo tee /sys/class/leds/led0/brightness`; } else { # turn off the green LED from being on full-time `echo 0 | sudo tee /sys/class/leds/led0/brightness`; # start disk activity operating the green LED `echo mmc0 | sudo tee /sys/class/leds/led0/trigger`; } } sub pwr_led { my ($self, $tweak) = @_; if ($tweak){ # turn off the red power LED `echo 0 | sudo tee /sys/class/leds/led1/brightness`; } else { # restore default power LED operation `echo input | sudo tee /sys/class/leds/led1/trigger`; } }
      Your app depends upon knowing the current time - what happens if that time isn't known

      You make a good point. I've not tried it, but I guess that the clock would initialise to sometime in the past (1st January 1970 perhaps) when the system boots until it is able to get the time from the internet. The plan is to not make any changes to the state of the curtains if the system time is before sometime in the past - probably 1st December 2020 to remind me when all this weird activity happened 😜

      Once it knows the time, and knows it knows the time, it can go to work determining what state the curtains need to be in and putting them that way. There will be some 'fiddling' needed with the setup to get everything fine tuned as I cannot develop the system with a set of powered curtains to test it.

      I have a Raspberry Pi. I am just starting to play with it - so I am a Pi newbie

      Good luck!
      It is proving to be an interesting and, at times frustrating process. Using an RPi Zero is frustrating at times as it is rather slow. Perflectly capable for the task it is to perform but very slow to set up and develop.

        A quick google search shows that RPi Zero is a cheap $10 board. I have what I guess is a Pi 4. It has an Ethernet connection, HDMI connection to my monitor, USB for keyboard and mouse. Nothing slow about it at all! By the time I bought my Pi, the case. power supply. and the cables, it was north of $80. A friend burned me an SD card for me with the O/S including Perl and Python. My Pi has a Debian flavor of Unix and it works great - very fast - no problems.
      Think about a status LED - perhaps blinking means good - stuck on(or off) means bad

      I did consider one or more LEDs when the project first came into being.
      But, given that the user is blind, there is nobody to be able to see them! Perhaps we could train Yago, his assistance dog, to look and see if there is a light on...

        For fatal Motherboard errors, there are "audible beep codes". Don't know what that means in context of a modern "all-in-one" motherboard but, a couple of computers ago, when my computer failed to boot, I called a friend and had him look up the beep codes to find out "dead video card". The stock Pi doesn't have a way to beep, but there is probably an add-on for that? If you ever do add some beep codes, think about how annoying it can get if it is beeping all the time! Another problem is ill timed beeping. I had one device that decided to beep at me every night at 3 AM. I have a lot of gizmos that can beep (smoke alarm, CO2 alarm, etc.) I had to wake up at 2:50 AM several nights in a row to wait for this thing and I gradually narrowed it down to find the "unhappy device" and feed it a new battery!
      I have a Raspberry Pi. I am just starting to play with it - so I am a Pi newbie. I need more hardware to do what my intended app needs to do (record HDMI video input)

      My RPi has been installed in a nice box today ready to go to its new home and be connected to the curtain motor that will be its new best friend! So I have started setting a new one up (without the GPIO pins). For the first RPi I bought an SD card with the OS on it but for the second one I have downloaded the Raspberry Pi Imager.

      Browsing through the various OS options I came across Media Player Kodi OS and thought about your application...not sure if you have come across it or not and whether it is helpful or not but thought I'd share it just in case it helps.

Re: Continuous or timed?
by Bod (Chaplain) on Dec 14, 2020 at 14:13 UTC

    The consensus and my gut feeling both suggested 2 so that is the option I have settled on...

    The control software is working - it needs more development but I am up against a time restriction as Christmas is coming and this controller is supposed to be a Christmas gift! I'm using only code modules as installing anything from CPAN seems to fail but this can be addressed later provided I set up a way to SSH onto the RPi before it gets installed in its new home.

    The problem I have hit is with CRON.

    The first few lines of the script are:

    #!usr/bin/perl use lib '.'; use Curtains::Control; use Bod::Config; use strict;
    But, despite the shebang line, it won't run simply by calling curtains.pl it needs perl curtains.pl instead. Perl is definitely installed in /usr/bin/perl and I also get this:
    pi@eric:~ $ /home/pi/Curtains/curtains.pl -bash: /home/pi/Curtains/curtains.pl: usr/bin/perl: bad interpreter: N +o such file or directory
    So I suspect I have something wrong in this setup.

    Within the crontab I have added:

    MAILTO=ian@****.com */2 * * * * perl /home/pi/Curtains/curtains.pl
    but I am not getting emails with any errors from CRON.

    I am using the default pi account which I believe is the RPi equivalent to root.

      #!usr/bin/perl

      Note you're missing a slash before usr. Also, my general advice for running anything from cron is always use absolute paths for everything in the crontab, i.e. "/usr/bin/perl /path/to/script.pl".

      I am not getting emails with any errors from CRON.

      You may be interested in the notes I use to set up my RPis which I've published here, including how to set up postfix. These notes do require some knowledge of *NIX. (I just noticed that the Markdown doesn't seem to be rendering quite right, but if you look at the plaintext/raw version it should be better legible.)

      I am using the default pi account which I believe is the RPi equivalent to root.

      pi is just a regular user, but on a normal Raspbian setup it has the ability to sudo without a password.

      I set up a way to SSH onto the RPi before it gets installed in its new home

      This is possible with configuring port forwarding on the router, but make sure to set up something like fail2ban as I describe in the link above.

        Thank you for all the information :)

        Also, my general advice for running anything from cron is always use absolute paths

        Following this advice I have used the full path so my cron entry is:

        */2 * * * * /usr/bin/perl /home/pi/Curtains/curtains.pl
        If I use /usr/bin/perl /home/pi/Curtains/curtains.pl from the command line then the script runs but it doesn't run from cron. So I have enabled logging in /etc/rsyslog.conf and I get this logged:
        Dec 14 16:12:01 eric CRON[10129]: (pi) CMD (/usr/bin/perl /home/pi/Cur +tains/curtains.pl) Dec 14 16:12:01 eric CRON[10128]: (CRON) info (No MTA installed, disca +rding output) Dec 14 16:14:01 eric CRON[10135]: (pi) CMD (/usr/bin/perl /home/pi/Cur +tains/curtains.pl) Dec 14 16:14:01 eric CRON[10134]: (CRON) info (No MTA installed, disca +rding output)
        That explains why I am not getting any mail! But doesn't explain to me why the script is not running from cron. I have tried chmod 777 curtains.pl and chmod 755 curtains.pl.

        What else should I be looking at to try and debug this problem>

        This (SSH) is possible with configuring port forwarding on the router, but make sure to set up something like fail2ban as I describe in the link above.

        I've set up a Real VNC account. The client comes pre installed on the Raspberry Pi and an account is free for home use. It's been tested using my mobile for one network and my home network for another and I can remotely access RPi without issue and without needing to change the router settings.

      #!/usr/bin/perl ^ | The leading slash isn't optional

      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      "I am using the default pi account which I believe is the RPi equivalent to root."

      That's an incorrect assumption.

      Raspbian, the OS of the Raspberry Pi, which is Linux, creates the pi user account, and it's the default standard user. root is the super user account. You execute commands as root using sudo. It's highly unlikely that with what you're doing, you need super user access. Don't use super user access except when absolutely necessary, and use the super user account for as short a duration as possible.

        Do I need to change the password of root to prevent malicious access as the device will be, and currently is, connected to the internet or is it inaccessible anyway?

        I guess not as I haven't been prompted like I was with pi

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11125006]
Approved by Paladin
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (5)
As of 2021-04-19 14:42 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?