diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f0dfe6..d8871bf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ #### Breaking changes + - the RP2040 firmware is now on i2c channel 0x2C (previously 0x30). This is to solve an annoying `i2cdetect` issue were it would lock up. - the web server now writes its logs to the same location as the app: `/var/log/pioreactor.log`. Those wishing to keep the old location can use a new configuration parameter `ui_log_file` to `[logging]` section and set it to `/var/log/pioreactorui.log`. - removed `psutil` and `zeroconf` Python packages from new images. We replaced their functionality with built-in routines. - in config.ini, the section `od_config` renamed to `od_reading.config`, and `stirring` is `stirring.config`. When you update, a script will run to automatically update these names in your config.inis. diff --git a/pioreactor/background_jobs/stirring.py b/pioreactor/background_jobs/stirring.py index a609feb1..ae8fa986 100644 --- a/pioreactor/background_jobs/stirring.py +++ b/pioreactor/background_jobs/stirring.py @@ -3,6 +3,7 @@ import json from contextlib import suppress +from threading import Lock from time import perf_counter from time import sleep from time import time @@ -232,6 +233,7 @@ def __init__( pin, config.getfloat("stirring.config", "pwm_hz"), unit=self.unit, experiment=self.experiment ) self.pwm.lock() + self.duty_cycle_lock = Lock() if target_rpm is not None and self.rpm_calculator is not None: self.target_rpm: Optional[float] = float(target_rpm) @@ -292,7 +294,8 @@ def on_disconnected(self) -> None: with suppress(AttributeError): self.rpm_check_repeated_thread.cancel() with suppress(AttributeError): - self.pwm.clean_up() + with self.duty_cycle_lock: + self.pwm.clean_up() with suppress(AttributeError): if self.rpm_calculator: self.rpm_calculator.clean_up() @@ -301,22 +304,26 @@ def start_stirring(self) -> None: self.logger.debug( f"Starting stirring with {'no' if self.target_rpm is None else self.target_rpm} RPM." ) - self.pwm.start(100) # get momentum to start - sleep(0.35) - self.set_duty_cycle(self.duty_cycle) + + with self.duty_cycle_lock: + self.pwm.start(100) # get momentum to start + sleep(0.35) + self.set_duty_cycle(self.duty_cycle) + if self.rpm_calculator is not None: self.rpm_check_repeated_thread.start() # .start is idempotent def kick_stirring(self) -> None: - self.logger.debug("Kicking stirring") - _existing_duty_cycle = self.duty_cycle - self.set_duty_cycle(0) - sleep(0.30) - self.set_duty_cycle(100) - sleep(0.5) - self.set_duty_cycle( - min(1.01 * _existing_duty_cycle, 60) - ) # DC should never need to be above 60 - simply not realistic. We want to avoid the death spiral to 100%. + with self.duty_cycle_lock: + self.logger.debug("Kicking stirring") + _existing_duty_cycle = self.duty_cycle + self.set_duty_cycle(0) + sleep(0.30) + self.set_duty_cycle(100) + sleep(0.5) + self.set_duty_cycle( + min(1.01 * _existing_duty_cycle, 60) + ) # DC should never need to be above 60 - simply not realistic. We want to avoid the death spiral to 100%. def kick_stirring_but_avoid_od_reading(self) -> None: """ @@ -397,12 +404,14 @@ def poll_and_update_dc(self, poll_for_seconds: Optional[float] = None) -> None: def on_ready_to_sleeping(self) -> None: self.rpm_check_repeated_thread.pause() - self.set_duty_cycle(0.0) + with self.duty_cycle_lock: + self.set_duty_cycle(0.0) def on_sleeping_to_ready(self) -> None: self.duty_cycle = self._previous_duty_cycle self.rpm_check_repeated_thread.unpause() - self.start_stirring() + with self.duty_cycle_lock: + self.start_stirring() def set_duty_cycle(self, value: float) -> None: self._previous_duty_cycle = self.duty_cycle diff --git a/pioreactor/hardware.py b/pioreactor/hardware.py index 5297f5a2..8a5988dc 100644 --- a/pioreactor/hardware.py +++ b/pioreactor/hardware.py @@ -59,8 +59,8 @@ # I2C channels used -ADC = 0x48 if (0, 0) < hardware_version_info <= (1, 0) else 0x30 -DAC = 0x49 if (0, 0) < hardware_version_info <= (1, 0) else 0x30 +ADC = 0x48 if (0, 0) < hardware_version_info <= (1, 0) else 0x2C +DAC = 0x49 if (0, 0) < hardware_version_info <= (1, 0) else 0x2C TEMP = 0x4F