Aller au contenu

Module modbus

Le module modbus permet d'interagir avec des appareils Modbus via TCP/RTU.

Import

Pour importer :

local modbus = require("modbus1")

Configuration YAML

Le module modbus nécessite une configuration spécifique pour fonctionner. Cette configuration est définie dans le fichier de configuration de l'agent.

Pour la configuration voici un exemple de toutes les options possibles :

iot:
  modules:
    modbus: # module modbus
      - name: modbus1 # nom du module
        transmissionmode: RTU # mode de transmission (auto, TCP, RTUoverTCP, ASCIIoverTCP, RTU, ASCII, file)
        controller: file:///dev/ttyUSB0 # contrôleur (file, tcp)
        baudrate: 9600 # baudrate
        databits: 8 # databits
        parity: N # parity (N, E, O)
        stopbits: 1 # stopbits
        slaveid: 1 # slaveid
        timeout: 5 # timeout en secondes
        debug: false # permet d'activer le mode debug
        rs485: # configuration rs485 
          enabled: false # activer rs485
          delay_rts_before_send: 2 # délai avant l'envoi
          delay_rts_after_send: 2 # délai après l'envoi
          rts_high_during_send: false # rts haut pendant l'envoi
          rts_high_after_send: false # rts haut après l'envoi
          rx_during_tx: false # rx pendant tx
      - name: modbus2 # nom du module
        transmissionmode: TCP # mode de transmission
        controller: tcp://127.0.0.1:502 # contrôleur
        slaveid: 1 # slaveid

Détails de la configuration

  • name : Nom du module. Il doit être unique et ne pas contenir d'espaces ou de caractères spéciaux.
  • transmissionmode : Mode de transmission. Il peut s'agir de auto, TCP, RTUoverTCP, ASCIIoverTCP, RTU, ASCII, ou file.
  • controller : Contrôleur. Il peut s'agir d'un fichier (ex. /dev/ttyUSB0, file:///dev/ttyUSB0) ou d'une adresse TCP (ex. tcp://127.0.0.1:502).
  • baudrate : Baudrate pour la communication série (ex. 9600).
  • databits : Nombre de bits de données (ex. 8).
  • parity : Parité pour la communication série. Il peut s'agir de N (aucune), E (pair), ou O (impair).
  • stopbits : Nombre de bits d'arrêt (ex. 1).
  • slaveid : ID de l'esclave Modbus (ex. 1).
  • timeout : Délai d'attente pour les opérations Modbus, en secondes (ex. 5).
  • debug : Permet d'activer le mode debug pour le module Modbus (ex. false).
  • rs485 : Configuration pour le mode RS485.
  • enabled : Activer le mode RS485 (ex. false).
  • delay_rts_before_send : Délai avant l'envoi, en millisecondes (ex. 2).
  • delay_rts_after_send : Délai après l'envoi, en millisecondes (ex. 2).
  • rts_high_during_send : RTS haut pendant l'envoi (ex. false).
  • rts_high_after_send : RTS haut après l'envoi (ex. false).
  • rx_during_tx : RX pendant TX (ex. false).

API Lua

Liste des fonctions

Fonction Signature Lua Rôle Retour (succès) Retour (échec)
connect bool, err = modbus.connect() Établit une connexion Modbus. true, nil nil, "msg"
disconnect bool, err = modbus.disconnect() Ferme la connexion Modbus. true, nil nil, "msg"
readCoils tbl, err = modbus.readCoils(address, quantity) Lit les coils (bits). table, nil nil, "msg"
writeSingleCoil bool, err = modbus.writeSingleCoil(address, value) Écrit un coil unique. true, nil nil, "msg"
writeMultipleCoils bool, err = modbus.writeMultipleCoils(address, values) Écrit plusieurs coils. true, nil nil, "msg"
readDiscreteInputs tbl, err = modbus.readDiscreteInputs(addr, byteorder[, resultType]) Lit les entrées discrètes. table, nil nil, "msg"
readHoldingRegisters tbl, err = modbus.readHoldingRegisters(addr, byteorder[, resultType, qty]) Lit les registres de maintien. table, nil nil, "msg"
writeSingleRegister bool, err = modbus.writeSingleRegister(address, value) Écrit un registre unique. true, nil nil, "msg"
writeMultipleRegisters bool, err = modbus.writeMultipleRegisters(addr, byteorder, qty, val[, type]) Écrit plusieurs registres. true, nil nil, "msg"
readInputRegisters tbl, err = modbus.readInputRegisters(addr, byteorder[, resultType, qty]) Lit les registres d'entrée. table, nil nil, "msg"
setSlaveId modbus.setSlaveId(slaveId) Change l'ID esclave. nil
sunspecDetect addr, err = modbus.sunspecDetect() Détecte l'adresse SunSpec. number, nil nil, "msg"
sunspecScan models, err = modbus.sunspecScan(base_addr) Scanne les modèles SunSpec. table, nil nil, "msg"
sunspecReadModel data, units, err = modbus.sunspecReadModel(id, base_addr) Lit un modèle SunSpec. table, table, nil nil, nil, "msg"
sunspecReadAll data, units, err = modbus.sunspecReadAll(base_addr) Lit tous les modèles SunSpec. table, table, nil nil, nil, "msg"
Champs modbus.name, modbus.type Métadonnées du module.

modbus.connect()

Établit une connexion avec l'appareil Modbus.

  • Paramètres : Aucun
  • Retour :
  • true si la connexion est réussie.
  • En cas d'échec, retourne nil suivi d'un message d'erreur.
local modbus = require("modbus1")

local status, err = modbus.connect()
if not status then
    print("Erreur lors de la connexion Modbus : " .. err)
    return
end

modbus.disconnect()

Ferme la connexion avec l'appareil Modbus.

  • Paramètres : Aucun
  • Retour :
  • true si la déconnexion est réussie.
  • En cas d'échec, retourne nil suivi d'un message d'erreur.
local status, err = modbus.disconnect()

if not status then
    print("Erreur lors de la déconnexion Modbus : " .. err)
    return
end

modbus.readCoils(address, quantity)

Lit les coils (bobines) depuis l'appareil Modbus à l'adresse spécifiée.

  • Paramètres :
  • address (int) : L'adresse de départ des coils à lire.
  • quantity (int) : Le nombre de coils à lire.

  • Retour :

  • Un tableau Lua contenant les valeurs booléennes (true/false) des coils lus.
  • nil suivi d'un message d'erreur en cas d'échec.
local modbus = require("modbus1")

local address = 0 -- adresse de départ
local quantity = 8 -- nombre de coils à lire

local coils, err = modbus.readCoils(address, quantity)
if not coils then
    print("Erreur lors de la lecture des coils : " .. err)
else
    for i, v in ipairs(coils) do
        print("Coil " .. (address + i - 1) .. ": " .. tostring(v))
    end
end

modbus.writeSingleCoil(address, value)

Écrit une valeur unique dans un coil à l'adresse spécifiée.

  • Paramètres :
  • address (int) : L'adresse du coil à écrire.
  • value (bool) : La valeur à écrire (true ou false).

  • Retour :

  • true si l'écriture est réussie.
  • En cas d'échec, retourne nil suivi d'un message d'erreur.
local modbus = require("modbus1")

local address = 0 -- adresse du coil
local value = true -- valeur à écrire

local status, err = modbus.writeSingleCoil(address, value)
if not status then
    print("Erreur lors de l'écriture du coil : " .. err)
else
    print("Coil " .. address .. " écrit avec succès.")
end

modbus.writeMultipleCoils(address, values)

Écrit plusieurs valeurs dans des coils à l'adresse spécifiée.

  • Paramètres :
  • address (int) : L'adresse à partir de laquelle commencer l'écriture.
  • values (string) : Une chaîne représentant les valeurs ('1' ou '0').

  • Retour :

  • true si l'écriture est réussie.
  • En cas d'échec, retourne nil suivi d'un message d'erreur.
local modbus = require("modbus1")

local address = 0 -- adresse de départ
local values = "1100" -- valeurs à écrire (ex. '1100')

local status, err = modbus.writeMultipleCoils(address, values)
if not status then
    print("Erreur lors de l'écriture des coils : " .. err)
else
    print("Coils écrits avec succès à partir de l'adresse " .. address .. ".")
end

modbus.readDiscreteInputs(address, byteorder[, resultType])

Lit les entrées discrètes depuis l'appareil Modbus à l'adresse spécifiée.

  • Paramètres :
  • address (int) : L'adresse à partir de laquelle commencer la lecture.
  • byteorder (string) : L'ordre des octets.
  • resultType (string, optionnel) : Le type de résultat attendu (par défaut int16).

  • Retour :

  • Un tableau Lua contenant les résultats ou nil suivi d'un message d'erreur en cas d'échec.
local modbus = require("modbus1")

local address = 0 -- adresse de départ
local byteorder = "big" -- ordre des octets
local resultType = "int16" -- type de résultat (optionnel)

local inputs, err = modbus.readDiscreteInputs(address, byteorder, resultType)
if not inputs then
    print("Erreur lors de la lecture des entrées discrètes : " .. err)
else
    for i, v in ipairs(inputs) do
        print("Input " .. (address + i - 1) .. ": " .. v)
    end
end

modbus.readHoldingRegisters(address, byteorder[, resultType, quantity])

Lit les registres de maintien (holding registers) depuis l'appareil Modbus.

  • Paramètres :
  • address (int) : L'adresse du premier registre à lire.
  • byteorder (string) : L'ordre des octets.
  • resultType (string, optionnel) : Le type de résultat attendu (par défaut int16).
  • quantity (int, optionnel) : Le nombre de registres à lire (défaut : déterminé par resultType).

  • Retour :

  • Un tableau Lua contenant les résultats ou nil suivi d'un message d'erreur en cas d'échec.
local modbus = require("modbus1")

local address = 0 -- adresse de départ
local byteorder = "big" -- ordre des octets
local resultType = "int16" -- type de résultat (optionnel)
local quantity = 10 -- nombre de registres à lire (optionnel)

local registers, err = modbus.readHoldingRegisters(address, byteorder, resultType, quantity)
if not registers then
    print("Erreur lors de la lecture des registres de maintien : " .. err)
else
    for i, v in ipairs(registers) do
        print("Register " .. (address + i - 1) .. ": " .. v)
    end
end

modbus.writeSingleRegister(address, value)

Écrit une valeur unique dans un registre à l'adresse spécifiée.

  • Paramètres :
  • address (int) : L'adresse du registre à écrire.
  • value (int) : La valeur à écrire.

  • Retour :

  • true si l'écriture est réussie.
  • En cas d'échec, retourne nil suivi d'un message d'erreur.
local modbus = require("modbus1")

local address = 0 -- adresse du registre
local value = 12345 -- valeur à écrire

local status, err = modbus.writeSingleRegister(address, value)
if not status then
    print("Erreur lors de l'écriture du registre : " .. err)
else
    print("Register " .. address .. " écrit avec succès.")
end

modbus.writeMultipleRegisters(address, byteorder, quantity, value[, valueType])

Écrit plusieurs registres dans l'appareil Modbus.

  • Paramètres :
  • address (int) : L'adresse à partir de laquelle commencer l'écriture.
  • byteorder (string) : L'ordre des octets.
  • quantity (int) : Le nombre de registres à écrire.
  • value (variable) : La valeur à écrire.
  • valueType (string, optionnel) : Le type de la valeur (défaut : int16).

  • Retour :

  • true si l'écriture est réussie.
  • En cas d'échec, retourne nil suivi d'un message d'erreur.
local modbus = require("modbus1")

local address = 0 -- adresse de départ
local byteorder = "big" -- ordre des octets
local quantity = 2 -- nombre de registres à écrire
local value = 12345 -- valeur à écrire
local valueType = "int16" -- type de valeur (optionnel)

local status, err = modbus.writeMultipleRegisters(address, byteorder, quantity, value, valueType)
if not status then
    print("Erreur lors de l'écriture des registres : " .. err)
else
    print("Registres écrits avec succès à partir de l'adresse " .. address .. ".")
end

modbus.readInputRegisters(address, byteorder[, resultType, quantity])

Lit les registres d'entrée (input registers) depuis l'appareil Modbus.

  • Paramètres :
  • address (int) : L'adresse du premier registre à lire.
  • byteorder (string) : L'ordre des octets.
  • resultType (string, optionnel) : Le type de résultat attendu (par défaut int16).
  • quantity (int, optionnel) : Le nombre de registres à lire (défaut : déterminé par resultType).

  • Retour :

  • Un tableau Lua contenant les résultats ou nil suivi d'un message d'erreur en cas d'échec.
local modbus = require("modbus1")

local address = 0 -- adresse de départ
local byteorder = "big" -- ordre des octets
local resultType = "int16" -- type de résultat (optionnel)
local quantity = 10 -- nombre de registres à lire (optionnel)

local registers, err = modbus.readInputRegisters(address, byteorder, resultType, quantity)
if not registers then
    print("Erreur lors de la lecture des registres d'entrée : " .. err)
else
    for i, v in ipairs(registers) do
        print("Input Register " .. (address + i - 1) .. ": " .. v)
    end
end

modbus.setSlaveId(slaveId)

Change l'ID de l'esclave pour la connexion Modbus.

  • Paramètres :
  • slaveId (int) : Le nouvel ID de l'esclave.

  • Retour :

  • true si l'ID de l'esclave est défini avec succès.
  • En cas d'échec, retourne nil suivi d'un message d'erreur.
local modbus = require("modbus1")

local newSlaveId = 2 -- nouvel ID de l'esclave

local status, err = modbus.setSlaveId(newSlaveId)
if not status then
    print("Erreur lors de la définition de l'ID de l'esclave : " .. err)
else
    print("ID de l'esclave défini avec succès à " .. newSlaveId .. ".")
end

Support SunSpec

Le module Modbus intègre le support du protocole SunSpec pour la communication avec les onduleurs solaires et compteurs d'énergie. SunSpec est un standard industriel qui définit des modèles de données normalisés accessibles via Modbus.

modbus.sunspecDetect()

Détecte automatiquement l'adresse de base SunSpec sur l'appareil connecté. La fonction recherche la signature "SunS" aux adresses standard (40000, 50000, 0).

  • Paramètres : Aucun
  • Retour :
  • L'adresse de base SunSpec (number).
  • nil et un message d'erreur si la signature n'est pas trouvée.
local modbus = require("modbus1")

modbus.connect()
local base_addr, err = modbus.sunspecDetect()
if not base_addr then
    print("Erreur de détection SunSpec : " .. err)
else
    print("Signature SunSpec trouvée à l'adresse " .. base_addr)
end
modbus.disconnect()

modbus.sunspecScan(base_addr)

Scanne les modèles SunSpec disponibles à partir de l'adresse de base spécifiée.

  • Paramètres :
  • base_addr (int) : Adresse de base SunSpec (obtenue via sunspecDetect()).

  • Retour :

  • Une table Lua contenant les modèles découverts.
  • nil et un message d'erreur en cas d'échec.

Chaque modèle dans la table contient : - id : Identifiant du modèle SunSpec - length : Longueur du modèle en registres - base_addr : Adresse de début du modèle - name : Nom du modèle (si disponible) - label : Label descriptif (si disponible) - desc : Description du modèle (si disponible)

local modbus = require("modbus1")

modbus.connect()
local base_addr, err = modbus.sunspecDetect()
if not base_addr then
    print("Erreur : " .. err)
    modbus.disconnect()
    return
end

local models, err = modbus.sunspecScan(base_addr)
if not models then
    print("Erreur lors du scan SunSpec : " .. err)
else
    for _, model in ipairs(models) do
        print("Modèle " .. model.id .. " (" .. (model.label or "inconnu") .. ") à l'adresse " .. model.base_addr)
    end
end
modbus.disconnect()

modbus.sunspecReadModel(model_id, base_addr)

Lit les données d'un modèle SunSpec spécifique.

  • Paramètres :
  • model_id (int) : L'identifiant du modèle SunSpec à lire.
  • base_addr (int) : Adresse de base SunSpec.

  • Retour :

  • data : Table Lua contenant les valeurs du modèle avec les facteurs d'échelle appliqués.
  • units : Table Lua contenant les unités pour chaque champ (ex. {W = "W", A = "A"}).
  • nil, nil, err en cas d'échec.
local modbus = require("modbus1")

modbus.connect()
local base_addr, err = modbus.sunspecDetect()
if not base_addr then
    print("Erreur : " .. err)
    modbus.disconnect()
    return
end

-- Lecture du modèle 103 (onduleur triphasé)
local data, units, err = modbus.sunspecReadModel(103, base_addr)
if not data then
    print("Erreur lors de la lecture SunSpec : " .. err)
else
    print("Puissance AC : " .. data.W .. " " .. (units.W or ""))
    print("Tension phase A : " .. data.PhVphA .. " " .. (units.PhVphA or ""))
    print("Courant total : " .. data.A .. " " .. (units.A or ""))
end
modbus.disconnect()

modbus.sunspecReadAll(base_addr)

Lit tous les modèles SunSpec disponibles à l'adresse de base spécifiée.

  • Paramètres :
  • base_addr (int) : Adresse de base SunSpec.

  • Retour :

  • data : Table Lua avec les données de chaque modèle, indexée par model_N (ex. model_1, model_103).
  • units : Table Lua avec les unités de chaque modèle, même indexation.
  • nil, nil, err en cas d'échec.
local modbus = require("modbus1")
local json = require("json")

modbus.connect()
local base_addr, err = modbus.sunspecDetect()
if not base_addr then
    print("Erreur : " .. err)
    modbus.disconnect()
    return
end

local data, units, err = modbus.sunspecReadAll(base_addr)
if not data then
    print("Erreur : " .. err)
else
    -- Accès aux données du modèle Common (1)
    if data.model_1 then
        print("Fabricant : " .. (data.model_1.Mn or ""))
        print("Modèle : " .. (data.model_1.Md or ""))
    end

    -- Accès aux données de l'onduleur (103)
    if data.model_103 then
        print("Puissance : " .. data.model_103.W .. " " .. (units.model_103.W or ""))
    end
end
modbus.disconnect()

Modèles SunSpec courants

Model ID Description Usage
1 Common Model Identification de l'appareil (fabricant, modèle, série)
101 Inverter (Single Phase) Onduleur monophasé
102 Inverter (Split Phase) Onduleur split-phase
103 Inverter (Three Phase) Onduleur triphasé
111-113 Extended Inverter Onduleurs avec valeurs float
124-126 Storage Batteries et stockage
201-204 Meter Compteurs d'énergie

Facteurs d'échelle

Les facteurs d'échelle SunSpec (type sunssf) sont automatiquement appliqués aux valeurs retournées. Par exemple, si un registre de tension contient 2400 avec un facteur d'échelle de -1, la valeur retournée sera 240.0 V.

Les valeurs "not implemented" (marqueurs SunSpec 0x8000, 0x80000000, NaN) sont automatiquement filtrées et n'apparaissent pas dans les données retournées.

Champs Lua associés

  • modbus.name : Retourne le nom de l'instance Modbus.
  • modbus.type : Retourne le type du module (ici, modbus1).