Ah maintenant j’ai compris la question… Je suis lent à la détente, désolé !
Oui, il faut paramétrer comme dans la doc :
- Adresse : autre chose que 1 puisque 1 est pris par la passerelle (mets 101 par exemple)
- Parité : paire, a priori sans bit de stop (si j’ai bien compris)
- Vitesse 19200 → ok
Pour la câblage, c’est bien :
Ton adaptateur USB/RS485 est compatible RTU ?
Et du coup, comme je pensais, à tord, que c’était le configuration de la passerelle, j’ai modifié le script pour y coller mais il faut revenir en arrière pour cette partie :
#!/usr/bin/env python3
import logging
import argparse
from pymodbus import pymodbus_apply_logging_config
from pymodbus.client import ModbusSerialClient
from pymodbus.exceptions import ModbusException
from pymodbus.pdu import ExceptionResponse
from pymodbus import FramerType
pymodbus_apply_logging_config(logging.ERROR)
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(message)s')
_logger = logging.getLogger(__file__)
def parse_args():
parser = argparse.ArgumentParser(description="Script de test de la fonctionnalité de lecture/écriture avec FC23")
parser.add_argument("--port", type=str, default="/dev/ttyACM0", help="Interface série à utiliser")
parser.add_argument("--address", type=int, default=1, help="Device ID. Numéro de l'esclave")
parser.add_argument("--idb", type=str, default="0217C1", help="Bus ID IDB (18 bit dans 3 octets)")
parser.add_argument("--idal", type=str, default="0E", help="Bus ID IDAL (5 bit dans 1 octet)")
parser.add_argument("--tabNr", type=int, default=0, help="Tab number")
parser.add_argument("--index", type=int, default=1, help="Index")
parser.add_argument("--baudrate", type=int, default=19200, help="Vitesse de transmission")
parser.add_argument("--bytesize", type=int, default=8, help="Longueur des données")
parser.add_argument("--parity", type=str, default="E", help="Parité")
parser.add_argument("--stopbits", type=int, default=0, help="Bits de stop")
return parser.parse_args()
def main() -> None:
args = parse_args()
_logger.info("### Début du programme")
assert 0 <= args.address <= 247, "L'adresse doit être entre 0 et 247"
assert len(args.idb) == 6, "IDB doit être long de 6 caractères pour représenter 3 octets"
assert len(args.idal) == 2, "IDAL doit être long de 2 caractères pour représenter 1 octet"
assert 0 <= args.tabNr <= 4, "Tab number doit être entre 0 et 4"
assert 0 <= args.index <= 255, "Index doit être entre 0 et 255"
assert args.baudrate in [300,600,1200,2400,4800,9600,14400,19200,38400,56000,57600,115200,128000,230400,256000], "Vitesse de transmission invalide"
assert args.bytesize in [7,8], "Longueur des données invalide"
assert args.parity in ["N","E","O"], "Parité invalide"
assert args.stopbits in [0,1,2], "Bits de stop invalide"
# Construction des registres à envoyer
registers = idx2reg(args.idal, args.idb)
registers.append(args.tabNr)
registers.append(args.index)
# valeur à utiliser d'une manière ou d'une autre
value = 0
write_value = ModbusSerialClient.convert_to_registers(value, ModbusSerialClient.DATATYPE.FLOAT32)
registers.extend(write_value) # valeur à écrire
_logger.info("### Connexion au client")
client = ModbusSerialClient(
port=args.port,
baudrate=args.baudrate,
bytesize=args.bytesize,
parity=args.parity,
stopbits=args.stopbits,
framer=FramerType.RTU,
timeout=5,
)
client.connect()
_logger.info("### Client connecté")
error = False
try:
# write_address => 0: read, 1: write d'après la documentation
rr = client.readwrite_registers(read_address=0, read_count=6, write_address=0, values=registers, slave=args.address)
except ModbusException as exc:
_logger.error(f"Exception Modbus : {exc!s}")
error = True
if not error and rr.isError():
_logger.error(f"Erreur")
error = True
if not error and isinstance(rr, ExceptionResponse):
_logger.error(f"Response exception : {rr!s}")
error = True
if not error:
_logger.info(f"Réponse: {rr.registers}")
value = client.convert_from_registers(rr.registers[-4], client.DATATYPE.FLOAT32)
_logger.info(f"Valeur lue: {value}")
else:
_logger.error("Une erreur est survenue")
client.close()
_logger.info("### Fin du programme")
def idx2reg(idal, idb) -> list[int]:
registers = bytearray()
registers.extend(bytes.fromhex(idal))
registers.extend(bytes.fromhex(idb))
ret = []
for i in range(0, len(registers), 2):
ret.append(int.from_bytes(registers[i:i+2], byteorder='big', signed=False))
return ret
if __name__ == "__main__":
main()
J’ai rajouté des paramètres avec lesquels tu vas pouvoir « jouer » :
./fc23.py --port /dev/ttyUSB1 --address 1 --idb 0E --idal 0217C1 --tabNr 0 --index 1 --baudrate 19200 --bytesize 8 --parity E --stopbits 0
Voici les nouveau paramètres optionnels possibles :
- baudrate peut avoir une de ces valeur : 300,600,1200,2400,4800,9600,14400,19200,38400,56000,57600,115200,128000,230400 ou 256000
Si pas précisé baudrate vaut 19200
- bytesize peut prendre la valeur 7 ou 8
Si pas précisé c’est 8
- parity peut valoir N (none/aucune), E (even/paire) ou O (odd/impaire)
Si pas précisé c’est E
- stopbits peut valoir 0, 1 ou 2
Si pas précisé c’est 0
Les valeurs par défaut sont tirées de la documentation. Regarde le chapitre 3 pour les valeurs acceptées par la passerelle. Le script accepte plus de possibilités puisqu’il s’appuie sur les possibilités de pymodbus, mais limite-toi à ce que sait faire la passerelle. La parité et le nombre de bits de stop sont liés, regarde bien la doc.
Les paramètres port et address peuvent être omis, par défaut c’est : --port /dev/ttyACM0 --address 1
Tu peux donc te concentrer sur les autres paramètres :
./fc23.py --idb 039195 --idal 1B --tabNr 0 --index 1
→ fonctionnera pareil
C’est peut-être lié au fait que j’ai changé la parité, maintenant par défaut c’est la bonne parité dans le script. Il faudrait peut-être aligner la configuration de l’adaptateur USB/RS485… pas sûr.
Ou alors il faut faire quelque chose pour activer la passerelle ? Je ne sais pas…