From bc55458642266378f54e0b521916a08cfc590eb5 Mon Sep 17 00:00:00 2001 From: yohan <783b8c87@scimetis.net> Date: Fri, 7 Jun 2024 22:03:40 +0200 Subject: [PATCH] Add relay scripts. --- Dockerfile | 2 + README_relay.txt | 76 +++++++++++++++++++++++++++++++++ TODO | 3 ++ find_ttyUSB.sh | 10 +++++ relay.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+) create mode 100644 README_relay.txt create mode 100644 TODO create mode 100755 find_ttyUSB.sh create mode 100755 relay.py diff --git a/Dockerfile b/Dockerfile index 0b33e24..1d1757f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,8 @@ RUN apt-get update && apt-get -y install gunicorn sqlite3 python3-pip python3-re ENV PIP_BREAK_SYSTEM_PACKAGES 1 RUN pip install flask-restx==1.3.0 WORKDIR /root +COPY find_ttyUSB.sh /root/ +COPY relay.py /root/ COPY thermostat.py /root/ ENV FLASK_APP thermostat.py ENTRYPOINT ["/usr/bin/gunicorn", "thermostat:gunicorn_app"] diff --git a/README_relay.txt b/README_relay.txt new file mode 100644 index 0000000..5fccd3a --- /dev/null +++ b/README_relay.txt @@ -0,0 +1,76 @@ +***************************************************************************** +Configuration port série: 1 start, 8 bit, 1 stop (vitesse 9600 bauds) +***************************************************************************** +La trame est composée de 5 caractères (minuscule ou majuscule) pouvant etre envoyés en ASCII ou en Hexadécimal + +**************** +Code ascii +**************** + +Start of frame: RLY +N° channel: 0 à 8 +CMD: 0 ou 1 + +Exemple de trame: + +RLY11 // commute le relais 1 en position travail +RLY10 // commute le relais 1 en position repos +RLY21 // commute le relais 2 en position travail +........................................................................... +RLY80 commute le relais 8 en position repos + +En cas de mauvaise commande la carte renvoi un retour chariot et le caractère ? + +**************** +Code hexa +**************** +Start of frame: 0x52 0x4C 0x59 ou 0x72 0x6C 0x79 +N° channel: 0x31 à 0x38 +Cmd: 0x30 ou 0x31 + + +Exemple de trame: + +0x52 0x4C 0x59 0x31 0x31 // commute le relais 1 en position travail +0x52 0x4C 0x59 0x31 0x30 // commute le relais 1 en position repos +0x52 0x4C 0x59 0x32 0x31 // commute le relais 2 en position travail +........................................................................... +0x52 0x4C 0x59 0x38 0x30 commute le relais 8 en position repos + +Pour tout autre caractère ou chaines de caractères la carte renvoi le code suivant: +0x0D 0x3F +******************************************************************************* +Modification du :04/09/2009 +******************************************************************************* + +Rajout du mode mémoire: la carte garde la dernière configuration en mémoire en cas de coupure d'alimentation. + + +**************** +Code ascii +**************** +M0 // Mode mémore désactivé +M1 // Mode mémoire activé + +**************** +Code hexa +**************** +0x4D 0x30 // Mode mémore désactivé +0x4D 0x31 // Mode mémoire activé +******************************************************************************* + +Rajout d'une commande pour connaitre l'état des relais. + + +**************** +Code ascii +**************** +?RLY // Renvoi l'état logique des 8 relais sous la forme >00000000 (le caractère le plus à droite correspond au relais 8) + +**************** +Code hexa +**************** +0X3F 0x52 0x4C 0x59 // Renvoi l'état logique des 8 relais sous la forme >00000000 (le caractère le plus à droite correspond au relais 8) + + +****************************************************************************** diff --git a/TODO b/TODO new file mode 100644 index 0000000..8161b4c --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +- Upgrade relay.py to Python3 +- Retry after failure in relay.py +- Store relays state in DB so we can have the info in /status API endpoint diff --git a/find_ttyUSB.sh b/find_ttyUSB.sh new file mode 100755 index 0000000..a224fe3 --- /dev/null +++ b/find_ttyUSB.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +for sysdevpath in $(find /sys/bus/usb/devices/usb*/ -name dev); do + syspath="${sysdevpath%/dev}" + devname="$(udevadm info -q name -p $syspath)" + [[ "$devname" == "ttyUSB"* ]] || continue + eval "$(udevadm info -q property --export -p $syspath)" + [[ -z "$ID_SERIAL" ]] && exit + echo "/dev/$devname - $ID_SERIAL" +done diff --git a/relay.py b/relay.py new file mode 100755 index 0000000..4b36627 --- /dev/null +++ b/relay.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# Pilotage GCE USB 8 Relay Board +import time +import serial +import argparse +import sys +import subprocess + +parser = argparse.ArgumentParser(description='Control relays.') +parser.add_argument('relay_list', type=str, + help='list of relays in ["1", ..., "8"] separated by comma, no spaces. "all" means all relays.') +parser.add_argument('action', type=str, choices=['on', 'off', 'status'], + help='Action on the list of relays.') + +args = parser.parse_args() + +if args.relay_list == 'all': + relays = [ 'RLY'+str(i) for i in range(1,9)] +else: + relays = [] + for relay in args.relay_list.split(','): + if relay in [ str(i) for i in range(1,9)]: + relays.append('RLY'+relay) + else: + print("ERROR: Relay '"+relay+"' does not exist.") + sys.exit(1) + +if args.action == 'on': + action = '1' +elif args.action == 'off': + action = '0' +else: + action = 'status' + +#print(relays) + +find_ttyUSB_output = subprocess.check_output(["./find_ttyUSB.sh"]) +device = None +for line in find_ttyUSB_output.split("\n"): + if "FTDI_FT232R_USB_UART_A50285BI" in line: + device = line.split(" ")[0] + break + +if device is None: + print ("Relay board not found.") + sys.exit(1) + + +def init_serial(pPort): + global ser + ser = serial.Serial() + ser.baudrate = 9600 + ser.port = pPort + ser.bytesize = 8 + ser.parity = 'N' + ser.timeout = None + ser.xonxoff = False + ser.rtscts=False + ser.dsrdtr=False + ser.timeout = 1 + ser.open() + + if ser.isOpen(): + pass + #print('Connected to ' + ser.portstr) + else: + print('Could not connect to ' + ser.portstr) + sys.exit(1) + +init_serial(device) +if action != 'status': + for relay in relays: + ser.write(relay.encode('ascii')+action+'\r\n') + bytes = ser.readline() + #print ('Renvoie :\r\n' + bytes) + +ser.write('?RLY\r\n') +answer = ser.readline() +status = '' +for char in answer: + if char in ['0', '1']: + status+= char +if len(status) != 8: + print(len(status)) + print("ERROR: status cannot be parsed.") + print('status:\r\n' + status) + sys.exit(1) +elif action != 'status': + print ('status:\r\n' + status) + +if action != 'status': + for i in range(1,9): + relay = status[i-1] + if relay not in ['0', '1']: + print("ERROR: unrecognized value '"+relay+"' for relay "+str(i)) + elif relay in relays and relay != action: + print("ERROR: wrong status '"+relay+"' for relay "+str(i)) +else: + for i in [int(relay[3]) for relay in relays]: + relay = status[i-1] + if relay not in ['0', '1']: + print("ERROR: unrecognized value '"+relay+"' for relay "+str(i)) + else: + print(relay, end='') + +ser.close()