15 de marzo de 2011

Accediendo a un PLC Siemens desde Visual Basic: LIBNODAVE

Libnodave es una biblioteca libre de funciones para comunicar con PLC Siemens, utilizando adaptadores MPI/PPI o Ethernet. Está disponible para descarga directa aquí y viene con el código fuente, programas de prueba, bibliotecas para Linux, Windows (32 bits), .NET y ejemplos. Para programar podemos elegir una gran variedad de lenguajes y entornos: C, C++, C#, Delphi, Pascal, Perl, Visual Basic y Visual Basic for Aplications.

Para probarlo voy a partir de la configuración que tengo hecha de las dos entradas anteriores en las que comunicaba un S5-95U con un 315-2DP por Profibus. Ahora lo que pretendo es conectarme desde mi ordenador al S7-300 para visualizar, en una aplicación en Visual Basic, las lecturas que hago de la periferia integrada del S5.

Para comunicar mi PC con el 315-2DP tengo un adaptador serie/MPI de Siemens referencia 6ES7901-2BF00-0AA0. No viene en la lista de adaptadores probados en la página de Libnodave pero ha funcionado bien, incluso a través de un adaptador USB/serie de la marca Prolific.

Para clarificar, en el siguiente esquema represento el conexionado que tengo entre los diferentes elementos:

Lo que voy a hacer en este proyecto es una aplicación en Visual Basic 2010 Express a la que voy a incorporar la biblioteca Libnodave para leer y escribir en el bloque de datos donde interactúo con la periferia del S5-95U: lo único que hará mi aplicación es leer y escribir en el DB10 del 315-2DP.


En los programas de Step5 y Step7 no tengo que modificar nada, el acceso es transparente para los PLC; la aplicación en el PC funcionará como HMI.

Lo primero es crear un nuevo proyecto en el entorno de Visual Basic. Una vez creado vamos a sus propiedades:

En la pestaña Referencias vamos a agregar una referencia nueva:

Vamos al directorio donde hayamos descomprimido Libnodave y dentro del subdirectorio Dot.NET seleccionamos la biblioteca libnodave.net.dll:

Al aceptar, en la lista de referencias ya debería aparecer libnodave.net:

Con esto ya tenemos acceso a las funciones para programar nuestra comunicación. Sin embargo, si ejecutamos la aplicación nos saldrá un error de que falta una biblioteca DLL. La solución la encontré en el foro de libnodave. Resulta que la biblioteca que hemos incorporado al proyecto es un "wrapper", o sea, una adaptación de la biblioteca de Win32 a .NET. Así que es necesario copiar manualmente la biblioteca libnodave.dll que se encuentra en el subdirectorio win en el mismo directorio donde esté el ejecutable de nuestro programa, esto es en los subdirectorios \bin\Debug y \bin\Release del directorio del proyecto en Visual Basic.

Realizado este paso ya estamos listos para iniciar la programación. Con la biblioteca Libnodave vienen un par de ejemplos en Visual Basic, en los que me he basado. He creado la clase NDA_LND para gestionar la conexión con las funciones Conectar y Desconectar y un par de funciones para leer y escribir en un DB. El código de la clase es el siguiente:
Public Class NDA_LND
Dim daveSerie As libnodave.daveOSserialType
Dim daveInter As libnodave.daveInterface
Dim daveConex As libnodave.daveConnection

Public BufferLectura(1000) As Byte
Public BufferEscritura(1000) As Byte

Public Conectado As Boolean = False
Public Mensaje As String

Public Function Conectar(Optional ByVal localMPI As Integer = 0, _
Optional ByVal plcMPI As Integer = 2, _
Optional ByVal portSerial As String = "COM1", _
Optional ByVal baud As String = "19200") As Boolean
If Conectado Then
Mensaje = "Conexión abortada, ya existe una conexión."
Conectar = False
Exit Function
End If

Dim Respuesta As Integer

Mensaje = "Abriendo una conexión serie..."
daveSerie.rfd = libnodave.setPort(portSerial, baud, AscW("O"))
daveSerie.wfd = daveSerie.rfd

If daveSerie.rfd > 0 Then
Mensaje = "Conexión serie OK, creando interface..."

daveInter = New libnodave.daveInterface(daveSerie, "My Interface 1", _
localMPI, _
libnodave.daveProtoMPI2, _
libnodave.daveSpeed187k)
daveInter.setTimeout(1000000) 'Make this longer if you have a very long response time

Mensaje = "Inicializando adaptador..."
Respuesta = daveInter.initAdapter()

If Respuesta = 0 Then
Mensaje = "Inicialización del adaptador OK, creando conexión MPI..."
daveConex = New libnodave.daveConnection(daveInter, plcMPI, 0, 0) 'Rack and slot don't matter in case of MPI
Respuesta = daveConex.connectPLC()
If Respuesta = 0 Then
Mensaje = "Conexión MPI correcta, lista para operar."
Conectado = True
Else
daveInter.disconnectAdapter()
libnodave.closePort(daveSerie.rfd)
Mensaje = "Error al abrir la conexión MPI [" & _
libnodave.daveStrerror(Respuesta) & "]"
Conectado = False
End If

Else
libnodave.closePort(daveSerie.rfd)
Mensaje = "Error al inicializar el adaptador [" & _
Respuesta & "] " & libnodave.daveStrerror(Respuesta)
Conectado = False
End If

Else
Mensaje = "Error al abrir el puerto serie " & portSerial
Conectado = False
End If

Conectar = Conectado

End Function

Public Function Desconectar() As Boolean
If Conectado Then
daveConex.disconnectPLC()
daveInter.disconnectAdapter()
libnodave.closePort(daveSerie.rfd)
Conectado = False
Mensaje = "Conexión correctamente terminada."
Desconectar = True
Else
Mensaje = "No existe conexión activa."
Desconectar = False
End If
End Function

Public Function LeerBytesDB(ByVal NumDB As Integer, _
ByVal Dir As Integer, _
ByVal NumBytes As Integer) As Boolean

Dim Respuesta As Integer

Respuesta = daveConex.readBytes(libnodave.daveDB, NumDB, Dir, NumBytes, BufferLectura)

If Respuesta = 0 Then
Mensaje = "Leídos " & NumBytes & " bytes a partir de la dirección " & _
Dir & " en el DB " & NumDB
LeerBytesDB = True
Else
Mensaje = "Error al leer " & NumBytes & " bytes a partir de la dirección " & _
Dir & " en el DB " & NumDB
LeerBytesDB = False
End If

End Function

Public Function EscribirBytesDB(ByVal NumDB As Integer, _
ByVal Dir As Integer, _
ByVal NumBytes As Integer) As Boolean
Dim Respuesta As Integer

Respuesta = daveConex.writeBytes(libnodave.daveDB, NumDB, Dir, NumBytes, BufferEscritura)

If Respuesta = 0 Then
Mensaje = "Escritos " & NumBytes & " bytes a partir de la dirección " & _
Dir & " en el DB " & NumDB
EscribirBytesDB = True
Else
Mensaje = "Error al escribir " & NumBytes & " bytes a partir de la dirección " & _
Dir & " en el DB " & NumDB
EscribirBytesDB = False
End If

End Function

End Class
Aunque no está comentado, el código creo que es fácil de entender. Con la función Conectar abrimos el puerto serie, inicializamos el adaptador y la conexión MPI, dejándola lista para operar. Comprobando el booleano que devuelve podemos saber si la operación se completó correctamente. Se le pasan por parámetros la dirección MPI del adaptador (por lo general la 0), la dirección MPI del PLC, el puerto serie por el cual comunicará y la velocidad de conexión:
S7MPI = New NDA_LND
S7MPI.Conectar(LocalMPI, PLCMPI, "COM1", "19200")
Los datos se leen con la función LeerBytesDB y se depositan en BufferLectura; por ejemplo, para leer las entradas analógicas se haría así:
S7MPI.LeerBytesDB(10, 26, 16) 'DB10, 16 bytes a partir de la dirección 26
'Entradas analógicas
EW40.Value = libnodave.getU16from(S7MPI.BufferLectura, 0)
EW42.Value = libnodave.getU16from(S7MPI.BufferLectura, 2)
EW44.Value = libnodave.getU16from(S7MPI.BufferLectura, 4)
EW46.Value = libnodave.getU16from(S7MPI.BufferLectura, 6)
EW48.Value = libnodave.getU16from(S7MPI.BufferLectura, 8)
EW50.Value = libnodave.getU16from(S7MPI.BufferLectura, 10)
EW52.Value = libnodave.getU16from(S7MPI.BufferLectura, 12)
EW54.Value = libnodave.getU16from(S7MPI.BufferLectura, 14)
y los que necesitemos escribir en BufferEscritura transfiriéndolo con la función EscribirBytesDB especificando el número de DB, la dirección de byte dentro del DB y el número de bytes a escribir:
S7MPI.BufferEscritura(8) = Valor16bits \ 256 'División entera
S7MPI.BufferEscritura(9) = Valor16bits Mod 256
S7MPI.EscribirBytesDB(10, 42, 10) 'DB10, 10 bytes a partir de la dirección 42
Una vez terminadas la operaciones debemos Desconectar, si no se quedará el puerto serie ocupado.
S7MPI.Desconectar()
El proyecto completo en Visual Basic lo tienes aquí. Si lo ejecutas tendrá el siguiente aspecto:

Con Libnodave viene documentación que deberás leer para adaptar el código a otros adaptadores. En mi caso, al inicializar el adaptador tuve que cambiar el protocolo a daveProtoMPI2 para que funcionase.

Aunque en mi ejemplo solo leo y escribo de bloques de datos se puede acceder a más áreas de memoria, habría que completar las funciones de la clase. Si alguien completa el código me gustaría echarle un vistazo.

La velocidad de comunicación no es muy buena, el adaptador serie/MPI se comunica con el PC a 19200 baudios, lo cual constituye un cuello de botella. Libnodave también permite la comunicación Ethernet, si tengo ocasión la probaré, el incremento de velocidad tiene que ser notable.

Actualización: en esta entrada pruebo Libnodave a través de ethernet.

En resumen, el autor de Libnodave ha hecho un trabajo fantástico, en las pruebas ha tenido un comportamiento impecable. Sin embargo no debemos olvidar que no tiene el soporte de una empresa detrás y que si lo usamos es por nuestra cuenta y riesgo.

Cualquier comentario será bienvenido.

16 comentarios:

  1. Muy buen Tutorial, y de lo poquito que hay en Castellano sobre libnodave, un muy buen recurso. Muchas gracias

    ResponderSuprimir
  2. hola soy martin de uruguay,
    debo hacer un proyecto de una aeronave programada para que ralize determinado vuelo, puedo aplicar esta tecnologia para dicho proyecto?
    saludos, y valoro tu ayuda

    ResponderSuprimir
  3. Hola Martín,

    No das muchos detalles de tu proyecto, pero si lo que ves en este blog te sirve adelante.

    Un saludo y suerte.

    ResponderSuprimir
  4. Hola GR,

    Buen tutorial!
    Yo estoy programando una aplicacion con C# y necesito conectarme via S7ONLINE y no lo he conseguido. Con los otros protocolos si que me ha ido pero con el runtime este no.¿Has provado de usarlo?

    Yo por este tema al final he tenido que usar las PRODAVE de Siemens :(

    Saludos

    ResponderSuprimir
  5. Buen tutorial para empezar,
    Yo estoy intentado hacer lectura de DB's por ethernet y con java, he intentado aclararme con el libnodave (no veas que curro) y intentar implementarlo en java, pero estoy llegando al punto de que no se como he de enviar la o las PDU.
    Alguien puede aclararme un poco el uso de las PDU y su estructura para S7/300.

    Saludos y gracias por anticipado

    ResponderSuprimir
  6. Estimado, muy buen tutorial, queria consultarle si con estas librerias se puede acceder a un S7-1200. Muchas gracias. Saludos.

    ResponderSuprimir
  7. Pues no lo sé, en la página de LibNoDave no pone nada y no tengo ningún 1200 para hacer pruebas.

    Un saludo.

    ResponderSuprimir
  8. Yo sigo con mis pruebas en java y queria preguntar si con el libnodave hay que configurar algo en el PLC.
    Yo por el momento he conseguido conectar el socket pero no leo ningún byte.

    Si alguien me puede responder estaría muy agradecido.
    Saludos.

    ResponderSuprimir
  9. Perdonar, la conexión es via TCP/IP

    ResponderSuprimir
  10. Hola. respondo a akyra:

    Yo tengo ya desarrollado un servidor en Java que utiliza libnodave y permite leer y escribir datos en un plc S7-300 desde un cliente remoto, a travñes de este servidor.
    El componente es ya operativo, si bien tengo un problema a la hora de visualizar los valores leídos con getFloat (REAL en plc), dado que no consigo ver los valores en otro formato que no sea representación científica (0.8345E5, por ejemplo). Salvo este inconveniente, el resto tengo muy claro cómo debe hacerse y ya lo tengo implementado (servidor y cliente de prueba).

    Si estás interesado, podemos intercambiar experiencias. Mi intención es desarrollar un SCADA GNU que pueda correr de manera remota en diferentes plataformas.

    Saludos.

    A. Sancho

    alfredo_sancho@telefonica.net

    ResponderSuprimir
  11. Hola Alfredo,
    Me parece interesante tu idea, yo hace años estuve haciendo uno con VB6 y no es fàcil.

    Yo actualmente no estoy trabajando habitualmente con PLC's, profesionalmente estoy más metido en temas de scada de Wonderware (Archestra IAS), pero si te puedo ayudar, encantado.

    Saludos

    ResponderSuprimir
  12. Paco en BarcelonaNov 21, 2011 10:10 AM

    Bueno, la verdad es que estoy asombrado de esta entrada. Muy buen tutorial, aunque a mi me cuesta un poquito entender algunas cosas. Al final todo se consigue.

    Mi objetivo en conseguir enlazar un scada basado en Delphi 7 con un plc de siemens a traves del protocolo MPI.

    Una pregunta, ¿sabes de algun drive que no sea libnodave y que tenga respaldo tecnico, pero que sea de libre distribucion?

    Me reconozco ya un tanto mayor y me cuesta entender algunos terminos y necesitaria ayuda.

    Un saludo y muchos animos para que continues publicando todos tu trabajos.

    ResponderSuprimir
  13. Hola Paco, comercialmente Siemens tiene disponible Prodave http://support.automation.siemens.com/WW/llisapi.dll?func=cslib.csinfo&lang=es&objid=6ES78074BA020YA0&caller=view

    Espero que te sirva, un saludo.

    ResponderSuprimir
  14. Buenas, soy nuevo en todo esto, lo mío es la programación en .net pero me interesa mucho el tema de los PLCs.
    Mi pregunta es la siguiente ¿se podría aplicar esta biblioteca o librería, y por consiguiente este tutorial, para un PLC Twido?

    Gracias, un saludo.

    ResponderSuprimir
  15. Esta biblioteca está diseñada para PLC Simatic y no te serviría. Desconozco los Twido, pero imagino que podrás usar tecnología OPC para acceder a sus datos.

    Un saludo.

    ResponderSuprimir
  16. Saludos.
    Una pregunta al foro. ¿Hay alguna forma de conetarse usando un cable MPI-USB?

    Gracias

    ResponderSuprimir

Por favor, no pidas copias de programas comerciales, licencias o números de serie.