Hm, sounds like the software didn’t clean up something properly. I would ignore it for now, and change the following in the schedule relay file:
def on_disconnected(self) -> None:
from contextlib import suppress
with suppress(AttributeError):
self.repeated_thread.cancel()
with suppress(AttributeError):
self.pwm.clean_up()
pioreactor@pio001:~/.pioreactor/plugins $ pio run schedualed_relay
2024-01-08T19:21:10+0000 DEBUG [schedualed_relay] Init.
2024-01-08T19:21:11+0000 DEBUG [PWM@GPIO-16] Initialized GPIO-16 using software-timing, initial frequency = 10 hz.
2024-01-08T19:21:11+0000 INFO [schedualed_relay] Ready.
2024-01-08T19:21:11+0000 DEBUG [schedualed_relay] schedualed_relay is blocking until disconnected.
2024-01-08T19:22:11+0000 INFO [schedualed_relay] Turning on relay for 20s.
^C2024-01-08T19:22:45+0000 DEBUG [schedualed_relay] Exiting caused by signal Interrupt.
2024-01-08T19:22:46+0000 DEBUG [PWM@GPIO-16] Cleaned up GPIO-16.
2024-01-08T19:22:46+0000 INFO [schedualed_relay] Disconnected.
2024-01-08T19:22:47+0000 DEBUG [schedualed_relay] Disconnected successfully from MQTT.
Aborted!
pioreactor@pio001:~/.pioreactor/plugins $ rm /tmp/.lgd-nfy*
pioreactor@pio001:~/.pioreactor/plugins $ pio run schedualed_relay
2024-01-08T19:23:10+0000 DEBUG [schedualed_relay] Init.
2024-01-08T19:23:10+0000 DEBUG [PWM@GPIO-16] Initialized GPIO-16 using software-timing, initial frequency = 10 hz.
2024-01-08T19:23:10+0000 INFO [schedualed_relay] Ready.
2024-01-08T19:23:10+0000 DEBUG [schedualed_relay] schedualed_relay is blocking until disconnected.
2024-01-08T19:24:10+0000 INFO [schedualed_relay] Turning on relay for 20s.
^C2024-01-08T19:24:48+0000 DEBUG [schedualed_relay] Exiting caused by signal Interrupt.
2024-01-08T19:24:48+0000 DEBUG [PWM@GPIO-16] Cleaned up GPIO-16.
2024-01-08T19:24:49+0000 INFO [schedualed_relay] Disconnected.
2024-01-08T19:24:50+0000 DEBUG [schedualed_relay] Disconnected successfully from MQTT.
Aborted!
To rule out software issues, see if you can measure the voltage on GPIO pin 16 on the 40pin header (which controls PWM output 3). Try your voltmeter between that pin and any ground pin on the header, and see if you can measure 3.3V when the PWM is set to 100%.
I’ll talk to our hardware team about hardware tests you can try too. Will report back tomorrow.
I didn’t even check the voltage, as it started a 1s CO2 sparge cycle… So all the hardware seems fine.
Here’s my full code in case I’ve introduced something stupid:
# -*- coding: utf-8 -*-
from __future__ import annotations
from time import sleep
import click
from pioreactor.background_jobs.base import BackgroundJobContrib
from pioreactor.config import config
from pioreactor.hardware import PWM_TO_PIN
from pioreactor.utils.pwm import PWM
from pioreactor.whoami import get_latest_experiment_name
from pioreactor.whoami import get_unit_name
from pioreactor.utils.timing import RepeatedTimer
__plugin_summary__ = "A relay that turns on for X seconds every hour."
__plugin_version__ = "0.0.1"
__plugin_name__ = "Schedualed Relay"
__plugin_author__ = "Cam DP"
class SchedualedRelay(BackgroundJobContrib):
job_name = "schedualed_relay"
published_settings = {
'relay_on_for': {"datatype": "float", "settable": True, "unit": "s"},
}
def __init__(self, unit: str, experiment: str) -> None:
super().__init__(unit=unit, experiment=experiment, plugin_name="schedualed_relay")
self.relay_on_for = 20 # seconds
# looks at config.ini/configuration on UI to match
self.pwm_pin = PWM_TO_PIN[config.get("PWM_reverse", "relay")]
self.pwm = PWM(
self.pwm_pin, hz=10, unit=unit, experiment=experiment
) # since we also go 100% high or 0% low, we don't need hz, but some systems don't allow a very low hz (like hz=1).
self.pwm.lock()
# this is core logic to turn on every hour: run `turn_relay_on_for_period_of_time` every 60*60 seconds
self.repeated_thread= RepeatedTimer(
60, # 5 * 60 * 60, # seconds
self.turn_relay_on_for_period_of_time,
job_name=self.job_name
).start()
def turn_relay_on_for_period_of_time(self):
self.logger.info(f"Turning on relay for {self.relay_on_for}s.")
self.pwm.change_duty_cycle(100)
sleep(self.relay_on_for)
self.pwm.change_duty_cycle(0)
def on_ready_to_sleeping(self) -> None:
self.repeated_thread.pause()
def on_sleeping_to_ready(self) -> None:
self.repeated_thread.unpause()
def on_disconnected(self) -> None:
from contextlib import suppress
with suppress(AttributeError):
self.repeated_thread.cancel()
with suppress(AttributeError):
self.pwm.clean_up()
@click.command(name="schedualed_relay")
def click_schedualed_relay() -> None:
"""
Start the relay
"""
job = SchedualedRelay(
unit=get_unit_name(),
experiment=get_latest_experiment_name(),
)
job.block_until_disconnected()
I just ran it again (pasted into nano from VS Code to be sure what I sent you was what was in there) and it immediately started a 1s sparge cycle - that has me thoroughly confused as in addition to the issue you mentioned above* it’s supposed to start by waiting 60s…
*I may have put your original suggestion straight into nano rather than VS Code, hence potentially reverting that issue.
I have now added the pwm.start() and am still getting an immediate 1s on off cycle.
# -*- coding: utf-8 -*-
from __future__ import annotations
from time import sleep
import click
from pioreactor.background_jobs.base import BackgroundJobContrib
from pioreactor.config import config
from pioreactor.hardware import PWM_TO_PIN
from pioreactor.utils.pwm import PWM
from pioreactor.whoami import get_latest_experiment_name
from pioreactor.whoami import get_unit_name
from pioreactor.utils.timing import RepeatedTimer
__plugin_summary__ = "A relay that turns on for X seconds every hour."
__plugin_version__ = "0.0.1"
__plugin_name__ = "Schedualed Relay"
__plugin_author__ = "Cam DP"
class SchedualedRelay(BackgroundJobContrib):
job_name = "schedualed_relay"
published_settings = {
'relay_on_for': {"datatype": "float", "settable": True, "unit": "s"},
}
def __init__(self, unit: str, experiment: str) -> None:
super().__init__(unit=unit, experiment=experiment, plugin_name="schedualed_relay")
self.relay_on_for = 20 # seconds
# looks at config.ini/configuration on UI to match
self.pwm_pin = PWM_TO_PIN[config.get("PWM_reverse", "relay")]
self.pwm = PWM(
pwm_pin, hz=10, unit=unit, experiment=experiment
) # since we also go 100% high or 0% low, we don't need hz, but some systems don't allow a very low hz (like hz=1).
self.pwm.lock()
self.pwm.start(0)
# this is core logic to turn on every hour: run `turn_relay_on_for_period_of_time` every 60*60 seconds
self.repeated_thread= RepeatedTimer(
60, # 5 * 60 * 60, # seconds
self.turn_relay_on_for_period_of_time,
job_name=self.job_name
).start()
def turn_relay_on_for_period_of_time(self):
self.logger.info(f"Turning on relay for {self.relay_on_for}s.")
self.pwm.change_duty_cycle(100)
sleep(self.relay_on_for)
self.pwm.change_duty_cycle(0)
def on_ready_to_sleeping(self) -> None:
self.repeated_thread.pause()
def on_sleeping_to_ready(self) -> None:
self.repeated_thread.unpause()
def on_disconnected(self) -> None:
from contextlib import suppress
with suppress(AttributeError):
self.repeated_thread.cancel()
with suppress(AttributeError):
self.pwm.clean_up()
@click.command(name="schedualed_relay")
def click_schedualed_relay() -> None:
"""
Start the relay
"""
job = SchedualedRelay(
unit=get_unit_name(),
experiment=get_latest_experiment_name(),
)
job.block_until_disconnected()
It also works in shell, but running schedualed_relay.py gives:
pioreactor@pio001:~/.pioreactor/plugins $ pio run schedualed_relay
2024-01-08T23:26:31+0000 DEBUG [schedualed_relay] Init.
Traceback (most recent call last):
File "/usr/local/bin/pio", line 8, in <module>
sys.exit(pio())
^^^^^
File "/usr/local/lib/python3.11/dist-packages/click/core.py", line 1157, in __call__
return self.main(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/click/core.py", line 1078, in main
rv = self.invoke(ctx)
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/click/core.py", line 1688, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/click/core.py", line 1688, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/click/core.py", line 1434, in invoke
return ctx.invoke(self.callback, **ctx.params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/click/core.py", line 783, in invoke
return __callback(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/pioreactor/.pioreactor/plugins/schedualed_relay.py", line 75, in click_schedualed_relay
job = SchedualedRelay(
^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/dist-packages/pioreactor/background_jobs/base.py", line 98, in __call__
obj = type.__call__(cls, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/pioreactor/.pioreactor/plugins/schedualed_relay.py", line 37, in __init__
pwm_pin, hz=10, unit=unit, experiment=experiment
^^^^^^^
NameError: name 'pwm_pin' is not defined
2024-01-08T23:26:32+0000 DEBUG [schedualed_relay] Exiting caused by Python atexit.
2024-01-08T23:26:32+0000 INFO [schedualed_relay] Disconnected.
2024-01-08T23:26:32+0000 DEBUG [schedualed_relay] Disconnected successfully from MQTT.