From 09ac0bda50a9c9c0e6731597b6a93a1b92e92ee0 Mon Sep 17 00:00:00 2001 From: Geens Date: Sat, 3 Aug 2024 17:26:46 +0200 Subject: [PATCH] Switched modbus implementation and added state machine --- main.py | 24 ++++++---- requirements.txt | 2 +- src/__init__.py | 3 +- src/buttons_timers_and_relays.py | 79 ++++++++++++++++++++++++++++++++ src/hardware.py | 25 +++++----- src/logic.py | 7 ++- uno_relay_modbus.ino | 2 +- 7 files changed, 116 insertions(+), 26 deletions(-) create mode 100644 src/buttons_timers_and_relays.py diff --git a/main.py b/main.py index a9b31a3..cae7d30 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,4 @@ -import minimalmodbus +from pymodbus.client import ModbusSerialClient import time import traceback import argparse @@ -16,7 +16,7 @@ def main(): help='COM port for the Modbus connection') parser.add_argument('--slave_id', type=int, default=1, help='Slave ID for the Modbus device') - parser.add_argument('--cycle_time', type=float, default=0.01, + parser.add_argument('--cycle_time', type=float, default=0.05, help='Cycle time in seconds for the main loop') args = parser.parse_args() @@ -26,14 +26,17 @@ def main(): while True: try: - instrument = minimalmodbus.Instrument(com_port, slave_id) - instrument.serial.baudrate = 9600 - instrument.serial.bytesize = 8 - instrument.serial.parity = minimalmodbus.serial.PARITY_NONE - instrument.serial.stopbits = 1 - instrument.serial.timeout = 1 + client = ModbusSerialClient( + port=com_port, + baudrate=57600, + bytesize=8, + parity='N', + stopbits=1, + timeout=1 + ) + client.connect() time.sleep(3) - hardware = Hardware(instrument) + hardware = Hardware(client, slave_id) while True: start_time = time.time() @@ -55,6 +58,9 @@ def main(): print(f"Error: {e}") print(traceback.format_exc()) time.sleep(5) + finally: + if 'client' in locals(): + client.close() if __name__ == "__main__": main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3897fc5..5cdb094 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1 @@ -minimalmodbus==1.0.2 \ No newline at end of file +pymodbus==3.1.0 \ No newline at end of file diff --git a/src/__init__.py b/src/__init__.py index 6d32adb..1abece1 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -1,2 +1,3 @@ from .hardware import Hardware -from .logic import loop \ No newline at end of file +from .logic import loop +from .buttons_timers_and_relays import ButtonsTimersAndRelays \ No newline at end of file diff --git a/src/buttons_timers_and_relays.py b/src/buttons_timers_and_relays.py new file mode 100644 index 0000000..76ed0cf --- /dev/null +++ b/src/buttons_timers_and_relays.py @@ -0,0 +1,79 @@ +from enum import Enum +import time + +class ButtonsTimersAndRelays: + class State(Enum): + RELAY_0_LOW = 0 + RELAY_0_HIGH = 1 + BUTTON_1_PRESSED = 2 + RELAY_1_HIGH_1 = 3 + RELAY_1_LOW = 4 + RELAY_1_HIGH_2 = 5 + + def __init__(self): + self.current_state = self.State.RELAY_0_LOW + self.state_change_time = time.time() + self.time_in_current_state = 0 + + def loop(self, hardware): + next_state = self.current_state + + # State transitions + match self.current_state: + case self.State.RELAY_0_LOW: + if hardware.get_button_0(): + next_state = self.State.RELAY_0_HIGH + elif hardware.get_button_1(): + next_state = self.State.BUTTON_1_PRESSED + case self.State.RELAY_0_HIGH: + if not hardware.get_button_0() and hardware.get_button_1(): + next_state = self.State.BUTTON_1_PRESSED + case self.State.BUTTON_1_PRESSED: + if hardware.get_button_0(): + next_state = self.State.RELAY_0_HIGH + elif not hardware.get_button_1(): + next_state = self.State.RELAY_1_HIGH_1 + case self.State.RELAY_1_HIGH_1: + if hardware.get_button_0(): + next_state = self.State.RELAY_0_HIGH + elif self.time_in_current_state > 1: + next_state = self.State.RELAY_1_LOW + case self.State.RELAY_1_LOW: + if hardware.get_button_0(): + next_state = self.State.RELAY_0_HIGH + elif self.time_in_current_state > 1: + next_state = self.State.RELAY_1_HIGH_2 + case self.State.RELAY_1_HIGH_2: + if hardware.get_button_0(): + next_state = self.State.RELAY_0_HIGH + elif self.time_in_current_state > 1: + next_state = self.State.RELAY_0_LOW + + # Time calculation + if next_state != self.current_state: + self.state_change_time = time.time() + self.time_in_current_state = 0 + else: + self.time_in_current_state = time.time() - self.state_change_time + self.current_state = next_state + + # Outputs + match self.current_state: + case self.State.RELAY_0_LOW: + hardware.set_relay_0(False) + hardware.set_relay_1(False) + case self.State.RELAY_0_HIGH: + hardware.set_relay_0(True) + hardware.set_relay_1(False) + case self.State.BUTTON_1_PRESSED: + hardware.set_relay_0(False) + hardware.set_relay_1(False) + case self.State.RELAY_1_HIGH_1: + hardware.set_relay_0(False) + hardware.set_relay_1(True) + case self.State.RELAY_1_LOW: + hardware.set_relay_0(False) + hardware.set_relay_1(False) + case self.State.RELAY_1_HIGH_2: + hardware.set_relay_0(False) + hardware.set_relay_1(True) \ No newline at end of file diff --git a/src/hardware.py b/src/hardware.py index e9f4de8..da26946 100644 --- a/src/hardware.py +++ b/src/hardware.py @@ -1,22 +1,23 @@ -import minimalmodbus +from pymodbus.exceptions import ModbusException class Hardware: - def __init__(self, instrument): - self.instrument = instrument - self.relay_0_address = 0 - self.relay_1_address = 1 - self.button_0_address = 0 - self.button_1_address = 1 + def __init__(self, client, slave_id): + self.client = client + self.slave_id = slave_id self.relay_0 = False self.relay_1 = False self.button_0 = False self.button_1 = False - + def update(self): - self.instrument.write_bit(self.relay_0_address, self.relay_0, functioncode=5) - self.instrument.write_bit(self.relay_1_address, self.relay_1, functioncode=5) - self.button_0 = self.instrument.read_bit(self.button_0_address, functioncode=2) - self.button_1 = self.instrument.read_bit(self.button_1_address, functioncode=2) + try: + self.client.write_coils(0, [self.relay_0, self.relay_1], slave=self.slave_id) + input_response = self.client.read_discrete_inputs(0, 2, slave=self.slave_id) + if not input_response.isError(): + self.button_0 = input_response.bits[0] + self.button_1 = input_response.bits[1] + except Exception as e: + print(f"Modbus error: {e}") def set_relay_0(self, value): self.relay_0 = value diff --git a/src/logic.py b/src/logic.py index 56039d9..595a081 100644 --- a/src/logic.py +++ b/src/logic.py @@ -1,3 +1,6 @@ +from src.buttons_timers_and_relays import ButtonsTimersAndRelays + +buttons_timers_relays = ButtonsTimersAndRelays() + def loop(hardware): - hardware.set_relay_0(hardware.get_button_0() != hardware.get_button_1()) - hardware.set_relay_1(hardware.get_button_0() and hardware.get_button_1()) \ No newline at end of file + buttons_timers_relays.loop(hardware) \ No newline at end of file diff --git a/uno_relay_modbus.ino b/uno_relay_modbus.ino index 3e8c34e..2b2d813 100644 --- a/uno_relay_modbus.ino +++ b/uno_relay_modbus.ino @@ -1,6 +1,6 @@ #include -#define BAUD_RATE 9600 +#define BAUD_RATE 57600 #define SLAVE_ID 1 #define COIL_COUNT 4 #define COIL_START 1