MODBUS TCP para PIC (parte I)

De todos es sabido que Modbus es uno de los protocolos de comunicación industrial más extendido y, claro está, con la aparición de ethernet se ‘actualizó’ para seguir estando en tre los top 5. También es cierto que desde que Microchip lanzó sus controladoras de ethernet y sus micros con ethernet embedded (también antes con los RTL,…) se abrieron las puertas hacia el mundo de las comunicaciones.
Evidentetemente no es mi intención soltar un discurso de como se implementa el protocolo, basta con ir a cualquier buscador y encontraréis 1000 referencias al respecto. Mi intención es simplemente poner un pequeño ejemplo de como se puede implementar un servidor MODBUS TCP en un PIC que soporte las funciones 03 y 16, que son las que básicamente se usan sobre tcp.

Vijeo conectado a la explorer 16

El código es muy simple, consisten en dos ficheros, ModbusTcp.c y ModbusTcp.h que se integran en el stack tcp de microchip, un par de opciones de configuración y un par de funciones a llamar en nuestro main.c

el primer paso será copiar los dos ficheros en la carpeta del stack tcp y en la de includes, luego en el TCPIPConfig.h creamos un nuevo tipo de socket, básicamente para tener controlado el que estamos usando…

#define TCP_PURPOSE_MODBUS_TCP_SERVER 12

y

{TCP_PURPOSE_MODBUS_TCP_SERVER, TCP_ETH_RAM, 200, 200},

a partir de aquí podemos ediar el ModbusTcp.h y cambiar nuestra configuración si es necesario, creo que con los comentarios está suficientemente claro
//////////////////////////////////////////////////////////
//
// CONFIGURACION DE USUARIO
//
//////////////////////////////////////////////////////////
//Puerto TCP para MODBUS
#define MODBUS_PORT 502

// para usar el ORDEN DE BYTE deseado, si nuestro cliente tiene la posibilidad de cambiarlo se puede
// comentar esta linea y ‘dejar trabajar al PC’, es el caso del OPC de kepware.
// para los equipos que no pueden hacerlo dejarlo sin comentar (caso de las Magelis de Telemecanique)

#define MODBUS_ORIGINAL

//numero de WORDS disponibles para funcion 03
#define numerowordsmodbus0316 50u

//////////////////////////////////////////////////////////

A partir de aquí hacemos referencia en nuestro código al stack con un include…
#include “TCPIP Stack/ModbudTcp.h”

y luego creamos las dos funciones de callback a las que llamará el stack Modbus cada vez que se reciba o se transmita un mensaje. En ellas tenemos acceso a la dirección inicial y al número de elementos recibidos o pedidos por si necesitamos alguna manipulación de la información:

void ModbusRx(WORD_VAL *datos,int inicio, int cantidad)
{

    if(datos[8].Val==1)
{
LED3_IO=1;
datos[8].Val=0;
}
if(datos[8].Val==2)
{
LED3_IO=0;
datos[8].Val=0;
}
if(datos[9].Val==1)
{
LED4_IO=1;
datos[9].Val=0;
}
if(datos[9].Val==2)
{
LED4_IO=0;
datos[9].Val=0;
}
}

void ModbusTx(WORD_VAL *datos,int inicio, int cantidad)
{

    static int peticiones;
char cadena[32];
//    memset(LCDText, ‘ ‘, 32);
//    sprintf(cadena, “ini %u cant %u  ” ,inicio,cantidad);
//    strcpy((char*)LCDText, (char*)cadena);
//    LCDUpdate();
datos[0].Val=BUTTON0_IO; //estado del boton 1
datos[1].Val=BUTTON1_IO; //estado del boton 2
datos[2].Val=BUTTON2_IO; //estado del boton 3
datos[3].Val=BUTTON3_IO; //estado del boton 4
*(DWORD *)&datos[4]=TickGet(); // Valor del Tick
datos[6].Val=(WORD)ADC1BUF0; //valor del potenciometro
datos[7].Val=peticiones++;     //numero de peticiones que hemos procesado
sprintf((char *)cadena,”Peticiones %Lu”,peticiones); // cadena de texto con el numero de peticiones
strncpy(&datos[12].Val,&cadena[0],20);
}

es posible no usar estas funciones y acceder directamente al mapa compartido por Modbus usando el array MAPA_MODBUS[] pero es conveniente hacerlo con las callbacks para estar seguro de lo que hacemos…
con esto ya sólo falta inicializar el stack Modbus Tcp con la llamada a la función:

IniciaModbusTCP(ModbusRx,ModbusTx);

y llamar cíclicamente al stack dentro de nuestro bucle principal:

ModbusTCPServer();

y esto es todo, un stack totalmente operativo para usar con cualquier software Modbus Tcp.
Evidentemente este código se puede optimizar, o añadir funciones nuevas o soporte para más de un cliente simultaneo, pero eso lo dejamos para otro post….

dejo una captura de pantalla con ele KEPSERVER comunicando con la Explorer 16.


explorerkepserverTags en el Kepserver.
Descarga el código fuente aqui

———- actualización 06/2010———-

He recompilado para C18, pero la verdad es que no tengo ahora plataforma hardware para probarlo.
básicamente hay que reordenar las declaraciones de variables al inicio de las funciones y eliminar los punteros a funciones (de momento).
por lo demás debe funcionar.
el fichero .c se copia en el directorio C:\Microchip Solutions\Microchip\TCPIP Stack y los .h en el directorio C:\Microchip Solutions\Microchip\Include\TCPIP Stack. en el main solo es necesario incluir #include “TCPIP Stack/modbus.h” y llamar a la función ModbusTCPServer(); dentro del bucle principal.
Con eso se ‘comparte’ el array MAPA_MODBUS…….

Descarga el código fuente aqui

Sería interesante algún feedback de alguien que lo tenga andando en C18.

También dejo algunos pantallazos de la configuración básica del KEPSERVER…

Configuración KEPSERVER 1
Configuración del KEPSERVER 2
COnfiguración del KEPSERVER 3

23 Responses to “MODBUS TCP para PIC (parte I)”

  1. carlos789 Says:

    Estimado Sasian:
    Cual es el uso de “MODBUS_ORIGINAL” , ya que al definirla me salta el siguiente error:
    C:\Microchip Solutions\Modbus\ModbusTcp.c:265:Error: syntax error
    Desde ya muchas gracias

  2. carlos789 Says:

    Hola Félix:
    Qué función cumple ” MODBUS_ORIGINAL”, porque al incluirlo me salta el siguiente error:
    C:\Microchip Solutions\Modbus\ModbusTcp.c:265:Error: syntax error
    Muchos saludos
    Carlos

  3. carlos789 Says:

    Hola Félix:
    Al utilizar los archivos de MODBUSTCP de su blog y querer aplicarlos en una WEBServer con un micro PIC18F97J60, el compilador C18 genera los siguientes errores , con la siguiente línea comentada en el header ModbusTCP.h //#define MODBUS_ORIGINAL:
    C:\Microchip Solutions\Microchip\TCPIP Stack\ModbusTcp.c:86:Error [1131] type mismatch in assignment C:\Microchip Solutions\Microchip\TCPIP Stack\ModbusTcp.c:87:Error [1131] type mismatch in assignment C:\Microchip Solutions\Microchip\TCPIP Stack\ModbusTcp.c:119:Warning [2058] call of function without prototype C:\Microchip Solutions\Microchip\TCPIP Stack\ModbusTcp.c:160:Warning [2058] call of function without prototype C:\Microchip Solutions\Microchip\TCPIP Stack\ModbusTcp.c:236:Warning [2058] call of function without prototype C:\Microchip Solutions\Microchip\TCPIP Stack\ModbusTcp.c:237:Warning [2058] call of function without prototype This application has requested the Runtime to terminate it in an unusual way. Please contact the application’s support team for more information. Halting build on first failure as requested.
    Y si la des-comento a #define MODBUS_ORIGINAL, sale el siguiente error:
    C:\Microchip Solutions\Microchip\TCPIP Stack\ModbusTcp.c:265:Error: syntax error Halting build on first failure as requested.
    Utilizo el Stack versión 5.20 de Microchip
    Agradecería mucho su ayuda.
    Muchos saludos
    Carlos
    Neuquen Argentina

  4. felix Says:

    “MODBUS_ORIGINAL” sirve para establecer el orden de los bytes dentro de las word en la trama modbus, normalmente los clientes permiten definir ese orden y podemos dejarlo comentado en el pic para optimizar el rendimiento. caso de querer conectar un cliente que necesite usar el orden original (como es el caso de las pantallas MAGELIS de Telemecanique) no tenemos mas remedio que definir MODBUS_ORIGINAL (no podemos serializar con un puntero al array…)

    respecto al error no lo he compilado nunca en C18, pero si me envias el proyecto le puedo dar una mirada.
    un saludo

  5. carlos789 Says:

    Hola Félix:
    Desde ya muchas gracias, te paso el link de mi proyecto
    http://www.4shared.com/file/bbq4NwGL/Modbus.html
    Muchos saludos
    Carlos

  6. Suky Says:

    Hola Félix! Primeramente te felicito por el aporte. Yo también tuve inconvenientes al compilarlo en C18. Principalmente es como se trabaja con los punteros a funciones, y por más que lo modifique a como se usa en C18 no logré compilarlo, así que lleve las funciones del main.c a ModbusTCP.c.

    Entiendo que por parte del micro ya está funcionando, pero no logro comunicarme con KepServer. Como configuras la comunicación? Yo en Canal, selecciono Modbus Ethernet. Luego en dispositivo, en identificación coloco el IP 254.169.0.1 y Port 502, pero no logro comunicación. :S

    Sería de gran ayuda algun comentario al respecto ;)

    Saludos!

  7. jmvc04 Says:

    Estimado Sasian:

    Estoy trabajando con un webserver ya implementado (kit SBC65EC) y no logro unir bien sus librerías de ModBus.

    Es posible que me envíe su proyecto completo, para entenderlo bien. Y lograr unirlo con mi proyecto.

    Gracias.

  8. felix Says:

    Actualizado 28/jun/2010
    aunque no uso C18 he recompilado para solucionar los errores que da en ese compilador. espero que sea útil.

    félix

  9. expo3871 Says:

    Hola, he podido leer los datos utilizando el kepserver, pero cuando trato de hacerlo con un PLC modicon quantum no he podido, debo realizar algun ajuste previo en el codigo? Saludos.

  10. felix Says:

    hay un par de temas a mirar, primero el Quantum debe ejercer de ‘esclavo’ del pic y segundo debes verificar el direccionamiento que usas en el PLC y preparar tu codigo para que el pic responda a ellas (direccion inicial de tu area=0,4000,…..).
    espero que tengas suerte.

  11. expo3871 Says:

    Hola felix, he estado revisando un poco el codigo y no entiendo porque al final de cada respuesta envias un byte con 0xFF
    Esta en el ModbusTcp.c

    case 35: //enviar los datos al cliente
    .
    .
    .

    TCPPut(MODBUSSERVER.ModbusSocket,0xFF);
    Agradezco tu respuesta. Saludos.

  12. felix Says:

    No es al final, entiendo que hablas del unit identifier, que segun la especificacion debe ser oxFF.
    fijate en la pagina 23
    http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0b.pdf

    saludos

  13. agrauer Says:

    Hola. Antes que nada, muy buen aporte!
    Ahora….. necesito establecer una conexion entre un PIC18F97J60 y un PLC por el re485 y con modbus RTU.
    Sinceramente no tengo mucha idea de por donde empezar…. tu podrías ayudarme un poco si es que sabes como?

    Muchas Gracias

  14. expo3871 Says:

    Hola felix, he estado analizando detenidamente la trama y compare con las tramas generadas por la comunicacion con 2 plc quatum y note lo siguiente:
    Al final se esta enviando un 0xFF que no se deberia estar enviando, por lo tanto comente esa linea que envia el 0xFF pero me da un error con la longitud, ya que al parecer en el calculo de la longitud del paquete se esta contando ese byte, entonces me da un error porque el lenght tiene un valor mas del que deberia de ser, entonces el plc al ver que esta esperando x paquetes y le llegan x-1 pues descarta el mensaje. Donde necesito tu ayuda es para ver donde puedo acomodar para que el calculo del lenght no me tome en cuenta ese byte de 0xFF ya que esta demas.
    Saludos

  15. expo3871 Says:

    Hola felix he estado analizando detenidamente la trama de la explorer 16 y compara con las tramas generadas entre la comunicacion de 2 plcs quantum y note lo siguiente: Al final de cada respuesta se esta enviando un oxFF que no se deberia estar enviando, por lo tanto comente esta linea que envia el 0xFF pero me da un error con la longitud, ya que al parecer para el calculo de la longitud del paquete se esta tomanto en cuenta ese byte, entonces me da un error porque el length tiene un valor mas del que deberia ser, entonces el PLC cuando ve q la trama deberia de ser de x longitud y le llega x-1 pues lo ve como un error y la descarta. Necesito tu ayuda para acomodar para que el calculo del length no tome en cuenta este ultimo byte de 0xFF ya que esta de mas. Gracias de antemano.

    Saludos.

  16. Donde Publicar Articulos Says:

    Me gustaria leer su blog….

    Está haciendo un trabajo genial….

  17. songotag Says:

    Hello Felix, did you make some adjustement to the code ? I whant to use it with a sbc68ec…

    thanks for your help

  18. songotag Says:

    Hola Felix

    Did you done another version of code for the Modbus ?

    I whant to use a sbc65ec and a magelis from telemecanique

    thanks for your help

  19. jceduvi Says:

    Saludos, ando realizando mi proyecto de tesis en el cual quiero utilizar una comunicación a travez de un microcontrolador con un controlador de la marca delta,
    alguien me podría ayudar a orientarme un poco eso pues el controlador se comunica por 485 con modbus, a mas estoy utilizando para esto el mikroc para programar el pic, no se si alguien me podria dar una ayuda al respecto.

  20. felix Says:

    Hi Songotag,
    no new versions, I’ve some pics conected to my Kepware OPC and some others conected to a magelis XBTG5330 for more than 1 year.
    do you have some problem running the code?
    felix

  21. felix Says:

    Hola jceduvi,
    hay sutiles diferencias entre el código para modbus tcp y el necesario para rs485. Afortunadamente creo recordar que, por ejemplo CCS tiene la librería completa que necesitas para RS485.
    un saludo

  22. dariooirad Says:

    Hola Felix
    Soy algo nuevo en esto y me gustaria mucho realizar este proyecto, ¿cual seria el hardware que tengo que implementar, diagramas electricos, pcb, etc? para empezar a probar.
    Te felicito por el proyecto.
    Te mando Saludos

  23. arthur.rush Says:

    I’m a newbie and not a C programmer, I have many years in programming Modicon PLC and in some PLCs in basic code.
    But I need to interface some sensor that are I2C data bus so I need to use a controller with I2C bus.
    I found a SBC65EC controller with a I2C bus I can use. I hopping to copy and pace Modbus RTU TCP/IP code into my SBC65EC source code.I copy and paced to Modbus TCP codes into my project, but received syntex errors 1099 and 270. Do you know how fix this errors?
    I can send you me project if you wont it
    arthur.rush@csus.edu

Leave a Reply

You must be logged in to post a comment.