CAMARA DIGITAL CONTROLADA POR ARDUINO USANDO MODBUS_RTU

CAMARA DIGITAL CONTROLADA POR ARDUINO USANDO MODBUS_RTU CONTROL REMOTO CAMARA DIGITAL Es muy probable que mas de una persona tenga en un cajon guardada alguna camara digital en relativamente buen estado, que probablemente ya no se usa, pues los telefonos celulares actuales tienen camaras de mayor resolucion. A estas camaras se les podria dar un segundo uso, como camaras para fotografia de naturaleza mediante deteccion de movimiento con un sensor PIR, o una camara a intervalos (time lapse) o como camara para fotografiar automaticamente elementos en una pequeña cadena de produccion, etc. Existen formas de reusar estas camaras en otros propositos de forma relativamente simple, si dicha camara posee conexion para un interruptor externo que accione el obturador, o si es una de las afortunadas camaras que tienen soporte para el CHDK (Canon Hack Developmen Kit) en el cual se pueden hacer cosas muy interesantes. Si ninguna de las anteriores opciones aplica para el modelo/camara que se tenga, existe una alternativa un poco menos "elegante" que consiste en extender las conexiones de los interruptores que ejecutan acciones en la camara, como puede ser el obturador, el enfoque, el zoom, menus, etc. Este proceso requiere de paciencia, pues se debera desarmar la camara (en lo posible usar una herramienta para abrir celulares para evitar daños en los seguros plasticas) para llegar a los interruptores de interes y soldar cables lo mas finos posibles. Una de las cuestiones que se debera determinar es si los interruptores son activos por alto, o activos por bajo, esto requiere un poco de malicia a no ser que se posean los planos de la camara o los manuales de reparacion y/o fallas!. Mas informacion: Automatizanos.com @automatizanos En este caso en particular se han soldado los interruptores del enfoque y el obturador en unos pads de prueba que habia en la tarjeta, de otra forma se habrian tenido que soldar directamente a los pines del interruptor. Como se determino que son activos por bajo, tambien se ubico en esta tarjeta un punto de voltaje negativo (gnd) y se soldo un alambre tambien. Se agrego un poco de cinta kapton para proteger los alambres ARDUINO Y PROTOCOLO MODBUS RTU El protocolo ModBus es ampliamente difundido en equipo de automatizacion industrial como PLC, HMI, variadores de velocidad,etc. Generalmente se usa sobre alambres trenzados usando el estandar RS-485 hasta distancias de 1200 metros. Debido a que es un protocolo abierto, bastante difundido, bien documentado y de facil implementacion en multiples lenguajes/plataformas (Java, python, Arduino, PIC) es un candidato ideal para mandar los comandos para el manejo remoto de la camara. La idea del circuito es usar un Arduino Nano para recibir los comandos (enfocar, tomar fotografia) y que este realice las respectivas pulsaciones de botones como si se estuviera haciendo manualmente. Se utilizaron optoacopladores igual que en un proyecto anterior para aislar electricamente la camara del circuito de control remoto. La camara en cuestion, en este caso una Panasonic Lumix DMC-LS3 cuenta con un un doble interruptor. Al presionarlo hasta la mitad, la camara realiza el enfoque, y al presionarlo hasta el fondo, la camara toma la fotografia. Se debera mantener pulsado los interruptores un tiempo determinado, pues si se pulsan demasiado rapido, el algoritmo interno de antirebote de la camara omitira la accion. Estos tiempos deberan hallarse experimentalmente pues varian mucho de camara a camara. Mas informacion: Automatizanos.com @automatizanos Se implementaron varios comandos. 1. 2. 3. Presionar y mantener presionado el boton del enfoque. Soltar el boton del enfoque. presionar el boton del enfoque, mantener presionado por un tiempo determinado, presionar el interruptor del obturador, dejar presionado por un tiempo y soltar ambos. Los dos primeros son utiles para mantener la camara encendida, si el tiempo entre una toma de fotografia y la otra es muy largo algunas camaras se apagan automaticamente para ahorrar energia. En algunas camaras este auto apagado puede ser desactivado o modificado el tiempo, pero en otras no. Puede usarse el comando de tocar el enfoque y soltar, cada cierto tiempo a manera de "ping" para mantener la camara encendida. El tercer comando es el que realiza la toma de fotografia y emula los pasos como lo hara un usuario humano de forma manual. Para la ejecucion de cada comando, debera escribirse un numero diferente de cero (0) en el registro modbus respectivo. PRUEBAS INICIALES CON PC Para esta prueba se utilizo un computador personal con una pequeña aplicacion en Pyton para realizar peticiones ModBus RTU. Se utilizo un conversor USB a RS-485. Este haria las veces de maestro ModBus Se conecto el circuito de la camara controlada con el Arduino y el conversor mediante un simple cable trenzado. Este haria las veces del esclavo ModBus Mas informacion: Automatizanos.com @automatizanos Se envio un comando para escribir, en este caso en el registro holding 10, un valor diferente de 0. Este registro activa el comando de toma de fotografia. Puede notarse los leds que se usaron como depuracion para determinar el tiempo de permanencia de los interruptores de foco y de obturador.. PRUEBA EN RED RS485 RASPBERRY PLC Y ARDUINO Para simular una red ModBus RTU mas compleja, se creo una red mas grande, y se utilizo un equipo de control industrial como parte de ella. La configuracion de la red quedo asi: • • • Raspberry Pi, con convertidor USB a RS-485 que hara las veces de maestro de la red PLC panasonic FPX-C14 con un convertidor de RS232 a RS485 que hara las veces de esclavo El circuito con el Arduino Nano y la camara que hara las veces de esclavo En esta prueba el maestro leera un valor almacenado en un registro del PLC, si este valor es mayor que un determinado numero entonces se enviara el comando para la toma de fotografia al Arduino Nano. Se cargo en el PLC un programa de forma tal que el valor de dicho registro puede aumentarse o decrementarse mediante un potencionetro que posee el PLC. La logica de comandos ModBus RTU que hay dentro del Raspberry PI en esta ocasion fue implementada mediante Node-RED. Se creo una pequeña implementacion didactica ModBus RTU, que solo requiere del nodo de puerto serial y ninguna otra dependencia o libreria. Si bien es ineficiente, es muy didactica pues se puede ver el flujo de como se construye la trama de peticion y como se va analizando la trama de respuesta que se recibe. Tambien es muy facil hacer cambios, agregar nuevas funcionalidades, etc Mas informacion: Automatizanos.com @automatizanos Finalmente para darle un poco de aspecto mas "industrial" se ubico la Raspberry Pi dentro de una caja, y se le añadieron accesorios para montaje en un riel din. PRUEBAS Y CONCLUSIONES • Se realizo una prueba inicial con un PC Celeron 900 Mhz, 2GB RAM, Lubuntu 14.04, Node-Red v0.15.2, Node.js v0.10.25. Se configuro el disparador del Node-RED para hacer ciclos de lectura al PLC y escritura al arduino cada 15 segundos. No se noto un retraso evidente en los tiempos programados. • Se realizo una segunda prueba con un Raspberry Pi (1 B+) Broadcom 700MHz, 512MB RAM, Raspbian Jessi Lite 4.4, Node-Red v0.15.2, Node.js v0.10.29. Se noto una considerable demora de (10 segundos adicionales) a los 15 programados en los ciclos de lectura. Si bien no hay grandes diferencias entre el Node-RED y el Node.js de ambos dispositivos, si hay una gran brecha en el hardware que puede ser el causante de dicho retraso. Hay que recordar que la aplicacion no fue escrita de una manera optima! • En cualquiera de las 2 pruebas el resultado fue el mismo: Solo se toma la fotografia, cuando la lectura del registro del PLC esta por debajo del limite pre-establecido en la logica de Node-RED • La implementacion de ModBus en Arduino funciona bastante bien, para poder ser utilizado como parte de una red ModBus RS-485 con otros dispositivos industriales • Como mejoras futuras, se pretende miniaturizar el circuito para instalarlo permanentemente en la camara. • Explorar modos de bajo consumo para el microcontrolador y poder alimentarlo con la misma fuente de alimentacion de la camara. Mas informacion: Automatizanos.com @automatizanos • Node-RED es una herramienat muy util para implementar protocolos de comunicacion de forma grafica y rapida. ENLACES Video demostrativo de la conexion de distintos componentes, ademas de las pruebas realizadas: https://youtu.be/MohWrl19UXg Arduino Nano Clon: http://s.click.aliexpress.com/e/jYRzRZN Herramienta para abrir aparatos electronicos: http://s.click.aliexpress.com/e/JQFAqRf Conversor USB a RS-485: http://s.click.aliexpress.com/e/eAAiEyJ Accesorio soporte para montaje en riel Din: http://s.click.aliexpress.com/e/AEeieEe ESQUEMATICO DEL CIRCUITO DE CONTROL REMOTO DE CAMARA DIGITAL CON ARDUINO USANDO MODBUS RTU Mas informacion: Automatizanos.com @automatizanos CODFIGO FUENTE ARDUINO PARA CONTROL REMOTO DE CAMARA DIGITAL USANDO MODBUS RTU // **************************************************** // Automatizanos.com // **************************************************** #include #define #define controla #define controla #define ledPin 13 // led de la tarjeta obturadormedioPin 2 // conectado al led del optoacoplador que el obturador medio ( enfoque ) obturadorcompletoPin 3 // conectado al led del optoacoplador que el obturador completo ( toma fotografia ) buttonPin 7 // push button #define haga sus #define haga sus delaymedio 2500 propios experimentos! delaycompleto 500 propios experimentos! // El valor depende del tipo de la camara, // El valor depende del tipo de la camara, /* This example code has 9 holding registers. 6 analogue inputs, 1 button, 1 digital output and 1 register to indicate errors encountered since started. Function 5 (write single coil) is not implemented so I'm using a whole register and function 16 to set the onboard Led on the Atmega328P. The modbus_update() method updates the holdingRegs register array and checks communication. Note: The Arduino serial ring buffer is 128 bytes or 64 registers. Most of the time you will connect the arduino to a master via serial using a MAX485 or similar. In a function 3 request the master will attempt to read from your slave and since 5 bytes is already used for ID, FUNCTION, NO OF BYTES and two BYTES CRC the master can only request 122 bytes or 61 registers. In a function 16 request the master will attempt to write to your slave and since a 9 bytes is already used for ID, FUNCTION, ADDRESS, NO OF REGISTERS, NO OF BYTES and two BYTES CRC the master can only write 118 bytes or 59 registers. Using the FTDI USB to Serial converter the maximum bytes you can send is limited to its internal buffer which is 60 bytes or 30 unsigned int registers. Thus: Mas informacion: Automatizanos.com @automatizanos In a function 3 request the master will attempt to read from your slave and since 5 bytes is already used for ID, FUNCTION, NO OF BYTES and two BYTES CRC the master can only request 54 bytes or 27 registers. In a function 16 request the master will attempt to write to your slave and since a 9 bytes is already used for ID, FUNCTION, ADDRESS, NO OF REGISTERS, NO OF BYTES and two BYTES CRC the master can only write 50 bytes or 25 registers. */ // // // // Since it is assumed that you will mostly use the Arduino to connect to a master without using a USB to Serial converter the internal buffer is set the same as the Arduino Serial ring buffer which is 128 bytes. Using the enum instruction allows for an easy method for adding and removing registers. Doing it this way saves you #defining the size of your slaves register array each time you want to add more registers and at a glimpse informs you of your slaves register layout. //////////////// registers of your slave /////////////////// enum { // just add or remove registers and your good to go... // The first register starts at address 0 ADC0, ADC1, ADC2, ADC3, ADC4, ADC5, LED_STATE, BUTTON_STATE, SOSTENER_OBTURADOR_MEDIO, SOLTAR_OBTURADOR_MEDIO, CLIC_OBTURADOR_MEDIO_COMPLETO, TOTAL_ERRORS, // leave this one TOTAL_REGS_SIZE // total number of registers for function 3 and 16 share the same register array }; unsigned int holdingRegs[TOTAL_REGS_SIZE]; // function 3 and 16 register array boolean medioactivadod; boolean completoactivado; unsigned long tiempomedio; unsigned long tiempocompleto; //////////////////////////////////////////////////////////// void setup() { /* parameters(long baudrate, Mas informacion: Automatizanos.com @automatizanos unsigned unsigned unsigned unsigned char ID, char transmit enable pin, int holding registers size, char low latency) The transmit enable pin is used in half duplex communication to activate a MAX485 or similar to deactivate this mode use any value < 2 because 0 & 1 is reserved for Rx & Tx. Low latency delays makes the implementation non-standard but practically it works with all major modbus master implementations. */ } modbus_configure(9600, 1, 4, TOTAL_REGS_SIZE, 0); pinMode(ledPin, OUTPUT); pinMode(obturadormedioPin, OUTPUT); pinMode(obturadorcompletoPin, OUTPUT); pinMode(buttonPin, INPUT); void loop() { // modbus_update() is the only method used in loop(). It returns the total error // count since the slave started. You don't have to use it but it's useful // for fault finding by the modbus master. holdingRegs[TOTAL_ERRORS] = modbus_update(holdingRegs); for (byte i = 0; i < 6; i++) { holdingRegs[i] = analogRead(i); delayMicroseconds(50); } if(holdingRegs[SOSTENER_OBTURADOR_MEDIO] > 0) { digitalWrite(obturadormedioPin, HIGH); digitalWrite(ledPin, HIGH); } if(holdingRegs[SOLTAR_OBTURADOR_MEDIO] > 0) { digitalWrite(obturadormedioPin, LOW); holdingRegs[SOSTENER_OBTURADOR_MEDIO]=0; holdingRegs[SOLTAR_OBTURADOR_MEDIO]=0; digitalWrite(ledPin, LOW); } if(holdingRegs[CLIC_OBTURADOR_MEDIO_COMPLETO] > 0 && medioactivadod==0 ) { Mas informacion: Automatizanos.com @automatizanos digitalWrite(obturadormedioPin, HIGH); medioactivadod=1; tiempomedio = millis(); } digitalWrite(ledPin, HIGH); if(medioactivadod==1 && completoactivado==0) { if (millis() > tiempomedio + delaymedio) { digitalWrite(obturadorcompletoPin, HIGH); completoactivado=1; tiempocompleto = millis(); } } if( completoactivado==1 ) { if (millis() > tiempocompleto + delaycompleto) { digitalWrite(obturadormedioPin, LOW); digitalWrite(obturadorcompletoPin, LOW); medioactivadod=0; completoactivado=0; holdingRegs[CLIC_OBTURADOR_MEDIO_COMPLETO] = 0; digitalWrite(ledPin, LOW); } } } // **************************************************** // Automatizanos.com // **************************************************** Mas informacion: Automatizanos.com @automatizanos APLICACION DE LOGICA EN NODE-RED PARA CONTROL REMOTO DE CAMARA DIGITAL CON ARDUINO USANDO MODBUS RTU [{"id":"68022ccb.861a04","type":"tab","label":"Flow 1"}, {"id":"38ed8b9d.6bbd74","type":"serial out","z":"68022ccb.861a04","name":"SERIAL TX","serial":"cedb070b.f339a8","x":1716.9639892578125,"y":917.6626586914062,"w ires":[]},{"id":"af6baf8b.e1065","type":"serial in","z":"68022ccb.861a04","name":"SERIAL RX","serial":"cedb070b.f339a8","x":483.5039596557617,"y":1605.5829491615295,"w ires":[["69556b5f.4121d4","47b9b055.66b86"]]}, {"id":"ff63d6ea.ea9c78","type":"switch","z":"68022ccb.861a04","name":"Switch F","property":"payload.function","propertyType":"msg","rules": [{"t":"eq","v":"0x01","vt":"num"},{"t":"eq","v":"0x02","vt":"num"}, {"t":"eq","v":"0x03","vt":"num"},{"t":"eq","v":"0x04","vt":"num"}, {"t":"eq","v":"0x05","vt":"num"},{"t":"eq","v":"0x06","vt":"num"}, {"t":"eq","v":"0x0F","vt":"num"},{"t":"eq","v":"0x10","vt":"num"},{"t":"else"} ],"checkall":"true","outputs":9,"x":775.6306838989258,"y":920.9960017204285,"w ires":[["7a316526.dae9fc"],["5288e1c3.f925a"],["7f37de1f.d5333"], ["4e518240.0efa8c"],["c74d58fa.81f818"],["911d54c8.b28228"], ["6d7037d5.6bbf08"],["1a8a7567.6bbc2b"],["d94408e0.41ba58"]]}, {"id":"e2aa477.ad303b8","type":"function","z":"68022ccb.861a04","name":"CHEQUE O CRC RTU","func":"// Automatizanos.com\n//\nvar len = msg.paylo

4 downloads 247 Views 6MB Size

Recommend Stories


D-360L INSTRUCCIONES CAMARA DIGITAL
PREPARATIVOS TOMA DE FOTOGRAFIAS INSTRUCCIONES INDICACION DE FOTOGRAFIAS EN EL MONITOR LCD IMPRESION DE FOTOGRAFIAS CAMARA DIGITAL D-360L EMPLEO

Fotografia de barrido con camara digital improvisada
Rochester Institute of Technology RIT Scholar Works Articles 2002 Fotografia de barrido con camara digital improvisada Andrew Davidhazy Follow thi

DIETA CONTROLADA EN POTASIO
DIETA CONTROLADA EN POTASIO El potasio es un ión fundamentalmente intracelular. Sus funciones se relacionan con la excitabilidad nerviosa, el equilibr

Story Transcript

CAMARA DIGITAL CONTROLADA POR ARDUINO USANDO MODBUS_RTU

CONTROL REMOTO CAMARA DIGITAL Es muy probable que mas de una persona tenga en un cajon guardada alguna camara digital en relativamente buen estado, que probablemente ya no se usa, pues los telefonos celulares actuales tienen camaras de mayor resolucion. A estas camaras se les podria dar un segundo uso, como camaras para fotografia de naturaleza mediante deteccion de movimiento con un sensor PIR, o una camara a intervalos (time lapse) o como camara para fotografiar automaticamente elementos en una pequeña cadena de produccion, etc. Existen formas de reusar estas camaras en otros propositos de forma relativamente simple, si dicha camara posee conexion para un interruptor externo que accione el obturador, o si es una de las afortunadas camaras que tienen soporte para el CHDK (Canon Hack Developmen Kit) en el cual se pueden hacer cosas muy interesantes. Si ninguna de las anteriores opciones aplica para el modelo/camara que se tenga, existe una alternativa un poco menos "elegante" que consiste en extender las conexiones de los interruptores que ejecutan acciones en la camara, como puede ser el obturador, el enfoque, el zoom, menus, etc. Este proceso requiere de paciencia, pues se debera desarmar la camara (en lo posible usar una herramienta para abrir celulares para evitar daños en los seguros plasticas) para llegar a los interruptores de interes y soldar cables lo mas finos posibles. Una de las cuestiones que se debera determinar es si los interruptores son activos por alto, o activos por bajo, esto requiere un poco de malicia a no ser que se posean los planos de la camara o los manuales de reparacion y/o fallas!.

Mas informacion:

Automatizanos.com

@automatizanos

En este caso en particular se han soldado los interruptores del enfoque y el obturador en unos pads de prueba que habia en la tarjeta, de otra forma se habrian tenido que soldar directamente a los pines del interruptor. Como se determino que son activos por bajo, tambien se ubico en esta tarjeta un punto de voltaje negativo (gnd) y se soldo un alambre tambien. Se agrego un poco de cinta kapton para proteger los alambres

ARDUINO Y PROTOCOLO MODBUS RTU El protocolo ModBus es ampliamente difundido en equipo de automatizacion industrial como PLC, HMI, variadores de velocidad,etc. Generalmente se usa sobre alambres trenzados usando el estandar RS-485 hasta distancias de 1200 metros. Debido a que es un protocolo abierto, bastante difundido, bien documentado y de facil implementacion en multiples lenguajes/plataformas (Java, python, Arduino, PIC) es un candidato ideal para mandar los comandos para el manejo remoto de la camara. La idea del circuito es usar un Arduino Nano para recibir los comandos (enfocar, tomar fotografia) y que este realice las respectivas pulsaciones de botones como si se estuviera haciendo manualmente. Se utilizaron optoacopladores igual que en un proyecto anterior para aislar electricamente la camara del circuito de control remoto. La camara en cuestion, en este caso una Panasonic Lumix DMC-LS3 cuenta con un un doble interruptor. Al presionarlo hasta la mitad, la camara realiza el enfoque, y al presionarlo hasta el fondo, la camara toma la fotografia. Se debera mantener pulsado los interruptores un tiempo determinado, pues si se pulsan demasiado rapido, el algoritmo interno de antirebote de la camara omitira la accion. Estos tiempos deberan hallarse experimentalmente pues varian mucho de camara a camara.

Mas informacion:

Automatizanos.com

@automatizanos

Se implementaron varios comandos. 1. 2. 3.

Presionar y mantener presionado el boton del enfoque. Soltar el boton del enfoque. presionar el boton del enfoque, mantener presionado por un tiempo determinado, presionar el interruptor del obturador, dejar presionado por un tiempo y soltar ambos.

Los dos primeros son utiles para mantener la camara encendida, si el tiempo entre una toma de fotografia y la otra es muy largo algunas camaras se apagan automaticamente para ahorrar energia. En algunas camaras este auto apagado puede ser desactivado o modificado el tiempo, pero en otras no. Puede usarse el comando de tocar el enfoque y soltar, cada cierto tiempo a manera de "ping" para mantener la camara encendida. El tercer comando es el que realiza la toma de fotografia y emula los pasos como lo hara un usuario humano de forma manual. Para la ejecucion de cada comando, debera escribirse un numero diferente de cero (0) en el registro modbus respectivo.

PRUEBAS INICIALES CON PC Para esta prueba se utilizo un computador personal con una pequeña aplicacion en Pyton para realizar peticiones ModBus RTU. Se utilizo un conversor USB a RS-485. Este haria las veces de maestro ModBus Se conecto el circuito de la camara controlada con el Arduino y el conversor mediante un simple cable trenzado. Este haria las veces del esclavo ModBus Mas informacion:

Automatizanos.com

@automatizanos

Se envio un comando para escribir, en este caso en el registro holding 10, un valor diferente de 0. Este registro activa el comando de toma de fotografia. Puede notarse los leds que se usaron como depuracion para determinar el tiempo de permanencia de los interruptores de foco y de obturador..

PRUEBA EN RED RS485 RASPBERRY PLC Y ARDUINO Para simular una red ModBus RTU mas compleja, se creo una red mas grande, y se utilizo un equipo de control industrial como parte de ella. La configuracion de la red quedo asi: • • •

Raspberry Pi, con convertidor USB a RS-485 que hara las veces de maestro de la red PLC panasonic FPX-C14 con un convertidor de RS232 a RS485 que hara las veces de esclavo El circuito con el Arduino Nano y la camara que hara las veces de esclavo

En esta prueba el maestro leera un valor almacenado en un registro del PLC, si este valor es mayor que un determinado numero entonces se enviara el comando para la toma de fotografia al Arduino Nano. Se cargo en el PLC un programa de forma tal que el valor de dicho registro puede aumentarse o decrementarse mediante un potencionetro que posee el PLC. La logica de comandos ModBus RTU que hay dentro del Raspberry PI en esta ocasion fue implementada mediante Node-RED. Se creo una pequeña implementacion didactica ModBus RTU, que solo requiere del nodo de puerto serial y ninguna otra dependencia o libreria. Si bien es ineficiente, es muy didactica pues se puede ver el flujo de como se construye la trama de peticion y como se va analizando la trama de respuesta que se recibe. Tambien es muy facil hacer cambios, agregar nuevas funcionalidades, etc

Mas informacion:

Automatizanos.com

@automatizanos

Finalmente para darle un poco de aspecto mas "industrial" se ubico la Raspberry Pi dentro de una caja, y se le añadieron accesorios para montaje en un riel din.

PRUEBAS Y CONCLUSIONES •

Se realizo una prueba inicial con un PC Celeron 900 Mhz, 2GB RAM, Lubuntu 14.04, Node-Red v0.15.2, Node.js v0.10.25. Se configuro el disparador del Node-RED para hacer ciclos de lectura al PLC y escritura al arduino cada 15 segundos. No se noto un retraso evidente en los tiempos programados.



Se realizo una segunda prueba con un Raspberry Pi (1 B+) Broadcom 700MHz, 512MB RAM, Raspbian Jessi Lite 4.4, Node-Red v0.15.2, Node.js v0.10.29. Se noto una considerable demora de (10 segundos adicionales) a los 15 programados en los ciclos de lectura. Si bien no hay grandes diferencias entre el Node-RED y el Node.js de ambos dispositivos, si hay una gran brecha en el hardware que puede ser el causante de dicho retraso. Hay que recordar que la aplicacion no fue escrita de una manera optima!



En cualquiera de las 2 pruebas el resultado fue el mismo: Solo se toma la fotografia, cuando la lectura del registro del PLC esta por debajo del limite pre-establecido en la logica de Node-RED



La implementacion de ModBus en Arduino funciona bastante bien, para poder ser utilizado como parte de una red ModBus RS-485 con otros dispositivos industriales



Como mejoras futuras, se pretende miniaturizar el circuito para instalarlo permanentemente en la camara.



Explorar modos de bajo consumo para el microcontrolador y poder alimentarlo con la misma fuente de alimentacion de la camara.

Mas informacion:

Automatizanos.com

@automatizanos



Node-RED es una herramienat muy util para implementar protocolos de comunicacion de forma grafica y rapida.

ENLACES Video demostrativo de la conexion de distintos componentes, ademas de las pruebas realizadas: https://youtu.be/MohWrl19UXg Arduino Nano Clon: http://s.click.aliexpress.com/e/jYRzRZN Herramienta para abrir aparatos electronicos: http://s.click.aliexpress.com/e/JQFAqRf Conversor USB a RS-485: http://s.click.aliexpress.com/e/eAAiEyJ Accesorio soporte para montaje en riel Din: http://s.click.aliexpress.com/e/AEeieEe ESQUEMATICO DEL CIRCUITO DE CONTROL REMOTO DE CAMARA DIGITAL CON ARDUINO USANDO MODBUS RTU

Mas informacion:

Automatizanos.com

@automatizanos

CODFIGO FUENTE ARDUINO PARA CONTROL REMOTO DE CAMARA DIGITAL USANDO MODBUS RTU // **************************************************** // Automatizanos.com // **************************************************** #include #define #define controla #define controla #define

ledPin 13 // led de la tarjeta obturadormedioPin 2 // conectado al led del optoacoplador que el obturador medio ( enfoque ) obturadorcompletoPin 3 // conectado al led del optoacoplador que el obturador completo ( toma fotografia ) buttonPin 7 // push button

#define haga sus #define haga sus

delaymedio 2500 propios experimentos! delaycompleto 500 propios experimentos!

// El valor depende del tipo de la camara, // El valor depende del tipo de la camara,

/* This example code has 9 holding registers. 6 analogue inputs, 1 button, 1 digital output and 1 register to indicate errors encountered since started. Function 5 (write single coil) is not implemented so I'm using a whole register and function 16 to set the onboard Led on the Atmega328P. The modbus_update() method updates the holdingRegs register array and checks communication. Note: The Arduino serial ring buffer is 128 bytes or 64 registers. Most of the time you will connect the arduino to a master via serial using a MAX485 or similar. In a function 3 request the master will attempt to read from your slave and since 5 bytes is already used for ID, FUNCTION, NO OF BYTES and two BYTES CRC the master can only request 122 bytes or 61 registers. In a function 16 request the master will attempt to write to your slave and since a 9 bytes is already used for ID, FUNCTION, ADDRESS, NO OF REGISTERS, NO OF BYTES and two BYTES CRC the master can only write 118 bytes or 59 registers. Using the FTDI USB to Serial converter the maximum bytes you can send is limited to its internal buffer which is 60 bytes or 30 unsigned int registers. Thus:

Mas informacion:

Automatizanos.com

@automatizanos

In a function 3 request the master will attempt to read from your slave and since 5 bytes is already used for ID, FUNCTION, NO OF BYTES and two BYTES CRC the master can only request 54 bytes or 27 registers. In a function 16 request the master will attempt to write to your slave and since a 9 bytes is already used for ID, FUNCTION, ADDRESS, NO OF REGISTERS, NO OF BYTES and two BYTES CRC the master can only write 50 bytes or 25 registers.

*/ // // // //

Since it is assumed that you will mostly use the Arduino to connect to a master without using a USB to Serial converter the internal buffer is set the same as the Arduino Serial ring buffer which is 128 bytes.

Using the enum instruction allows for an easy method for adding and removing registers. Doing it this way saves you #defining the size of your slaves register array each time you want to add more registers and at a glimpse informs you of your slaves register layout.

//////////////// registers of your slave /////////////////// enum { // just add or remove registers and your good to go... // The first register starts at address 0 ADC0, ADC1, ADC2, ADC3, ADC4, ADC5, LED_STATE, BUTTON_STATE, SOSTENER_OBTURADOR_MEDIO, SOLTAR_OBTURADOR_MEDIO, CLIC_OBTURADOR_MEDIO_COMPLETO, TOTAL_ERRORS, // leave this one TOTAL_REGS_SIZE // total number of registers for function 3 and 16 share the same register array }; unsigned int holdingRegs[TOTAL_REGS_SIZE]; // function 3 and 16 register array boolean medioactivadod; boolean completoactivado; unsigned long tiempomedio; unsigned long tiempocompleto; //////////////////////////////////////////////////////////// void setup() { /* parameters(long baudrate, Mas informacion:

Automatizanos.com

@automatizanos

unsigned unsigned unsigned unsigned

char ID, char transmit enable pin, int holding registers size, char low latency)

The transmit enable pin is used in half duplex communication to activate a MAX485 or similar to deactivate this mode use any value < 2 because 0 & 1 is reserved for Rx & Tx. Low latency delays makes the implementation non-standard but practically it works with all major modbus master implementations. */

}

modbus_configure(9600, 1, 4, TOTAL_REGS_SIZE, 0); pinMode(ledPin, OUTPUT); pinMode(obturadormedioPin, OUTPUT); pinMode(obturadorcompletoPin, OUTPUT); pinMode(buttonPin, INPUT);

void loop() { // modbus_update() is the only method used in loop(). It returns the total error // count since the slave started. You don't have to use it but it's useful // for fault finding by the modbus master. holdingRegs[TOTAL_ERRORS] = modbus_update(holdingRegs); for (byte i = 0; i < 6; i++) { holdingRegs[i] = analogRead(i); delayMicroseconds(50); }

if(holdingRegs[SOSTENER_OBTURADOR_MEDIO] > 0) { digitalWrite(obturadormedioPin, HIGH); digitalWrite(ledPin, HIGH); } if(holdingRegs[SOLTAR_OBTURADOR_MEDIO] > 0) { digitalWrite(obturadormedioPin, LOW); holdingRegs[SOSTENER_OBTURADOR_MEDIO]=0; holdingRegs[SOLTAR_OBTURADOR_MEDIO]=0; digitalWrite(ledPin, LOW);

}

if(holdingRegs[CLIC_OBTURADOR_MEDIO_COMPLETO] > 0 && medioactivadod==0 ) { Mas informacion:

Automatizanos.com

@automatizanos

digitalWrite(obturadormedioPin, HIGH); medioactivadod=1; tiempomedio = millis(); }

digitalWrite(ledPin, HIGH);

if(medioactivadod==1 && completoactivado==0) { if (millis() > tiempomedio + delaymedio) { digitalWrite(obturadorcompletoPin, HIGH); completoactivado=1; tiempocompleto = millis(); } } if( completoactivado==1 ) { if (millis() > tiempocompleto + delaycompleto) { digitalWrite(obturadormedioPin, LOW); digitalWrite(obturadorcompletoPin, LOW); medioactivadod=0; completoactivado=0; holdingRegs[CLIC_OBTURADOR_MEDIO_COMPLETO] = 0; digitalWrite(ledPin, LOW); }

}

} // **************************************************** // Automatizanos.com // ****************************************************

Mas informacion:

Automatizanos.com

@automatizanos

APLICACION DE LOGICA EN NODE-RED PARA CONTROL REMOTO DE CAMARA DIGITAL CON ARDUINO USANDO MODBUS RTU

[{"id":"68022ccb.861a04","type":"tab","label":"Flow 1"}, {"id":"38ed8b9d.6bbd74","type":"serial out","z":"68022ccb.861a04","name":"SERIAL TX","serial":"cedb070b.f339a8","x":1716.9639892578125,"y":917.6626586914062,"w ires":[]},{"id":"af6baf8b.e1065","type":"serial in","z":"68022ccb.861a04","name":"SERIAL RX","serial":"cedb070b.f339a8","x":483.5039596557617,"y":1605.5829491615295,"w ires":[["69556b5f.4121d4","47b9b055.66b86"]]}, {"id":"ff63d6ea.ea9c78","type":"switch","z":"68022ccb.861a04","name":"Switch F","property":"payload.function","propertyType":"msg","rules": [{"t":"eq","v":"0x01","vt":"num"},{"t":"eq","v":"0x02","vt":"num"}, {"t":"eq","v":"0x03","vt":"num"},{"t":"eq","v":"0x04","vt":"num"}, {"t":"eq","v":"0x05","vt":"num"},{"t":"eq","v":"0x06","vt":"num"}, {"t":"eq","v":"0x0F","vt":"num"},{"t":"eq","v":"0x10","vt":"num"},{"t":"else"} ],"checkall":"true","outputs":9,"x":775.6306838989258,"y":920.9960017204285,"w ires":[["7a316526.dae9fc"],["5288e1c3.f925a"],["7f37de1f.d5333"], ["4e518240.0efa8c"],["c74d58fa.81f818"],["911d54c8.b28228"], ["6d7037d5.6bbf08"],["1a8a7567.6bbc2b"],["d94408e0.41ba58"]]}, {"id":"e2aa477.ad303b8","type":"function","z":"68022ccb.861a04","name":"CHEQUE O CRC RTU","func":"// Automatizanos.com\n//\nvar len = msg.payload.rxpacket.length;\nvar dataStr = msg.payload.rxpacket;\nvar crc = 0xFFFF;\n\nfor ( var pos = 0; pos < (len-2) ; pos++ ) \n{\n crc = crc ^ dataStr[pos]; \n \n for (var i = 8; i !== 0; i--) \n { // Loop over each bit\n \n if ((crc & 0x0001) !== 0) \n { // If the LSB is set\n crc = crc >> 1; // Shift right and XOR 0xA001\n crc = crc ^ 0xA001;\n }\n else // Else LSB is not set\n crc = crc >> 1; // Just shift right \n }\n \n}\n\n// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)\n\nvar crclow = crc >> 8;\nvar crchi = crc & 0x00FF;\n\nif ( crchi === dataStr[len-2] && crclow === dataStr[len-1] ) \n { \n //flow.set('CRCrcv',0x01);\n msg.payload.okcrc = 0x01;\n}\nelse \n //flow.set('CRCrcv',0x00);\n msg.payload.okcrc = 0x00;\n\n\nreturn msg; \n","outputs":1,"noerr":0,"x":2022.1865234375,"y":1623.8526611328125,"wires": [["5f25cafe.ba8d54"]]}, {"id":"fd449bd9.d15f98","type":"switch","z":"68022ccb.861a04","name":"Minimo tamano de paquete?","property":"payload.rxpacket.length","propertyType":"msg","rules": [{"t":"gt","v":"3","vt":"num"},{"t":"lt","v":"4","vt":"num"} ],"checkall":"true","outputs":2,"x":1774.2976989746094,"y":1624.9957427978516, "wires":[["e2aa477.ad303b8"],["e05a0625.a7c738"]]}, {"id":"e05a0625.a7c738","type":"function","z":"68022ccb.861a04","name":"Error paquete muy pequeno","func":"// Automatizanos.com\n//\nnode.error(\"Error : paquete muy pequeno\", msg);","outputs":1,"noerr":0,"x":2040.614990234375,"y":1690.106689453125,"wire s":[[]]}, {"id":"5f25cafe.ba8d54","type":"switch","z":"68022ccb.861a04","name":"OK es el mismo?\nif( CRC?","property":"payload.okcrc","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"num" coilrx != tmpreg-1 )\n{\n node.status({fill:\"red\",shape:\"dot Mas informacion:

Automatizanos.com

@automatizanos

\",text:\"error\"}); \n node.error(\"F15 Error : registro no coincide\", msg);\n}\n// Cantidad es la misma?\nelse if(qtytx != qtyrx)\n{\n node.status({fill:\"red\",shape:\"dot\",text:\"error\"}); \n node.error (\"F15 Error : cantidad no coincide\", msg);\n}\n// Esclavo es el mismo?\nelse if(msg.payload.slave != global.get('MBSlaveId'))\n{\n node.status({fill: \"red\",shape:\"dot\",text:\"error\"}); \n node.error(\"F15 Error : esclavo no coincide\", msg);\n}\n// Funcion es la misma?\nelse if (msg.payload.function != global.get('MBFunction'))\n{\n node.status({fill: \"red\",shape:\"dot\",text:\"error\"}); \n node.error(\"F15 Error : funcion no coincide\", msg);\n}\nelse\n{\n node.status({fill:\"green \",shape:\"dot\",text:\"exito\"}); \n return msg;\n}\n \n","outputs":1,"noerr":0,"x":3020.7778329849243,"y":1770.468183517456,"wires" :[[]]}, {"id":"8f6da68f.83f6e8","type":"function","z":"68022ccb.861a04","name":"F16 analizar paquete","func":"// Automatizanos.com\n//\n// F16: Escribir a multiples registros\nvar regrx = (msg.payload.rxpacket[2] * 256) | (msg.payload.rxpacket[3]);\nvar tmpreg = global.get('MBRegister'); \nvar qtytx = global.get('MBQuantity');\nvar qtyrx = (msg.payload.rxpacket[4] * 256) | (msg.payload.rxpacket[5]);\n\n// Registro es el mismo?\nif( regrx != tmpreg-1 )\n{\n node.status({fill:\"red\",shape:\"dot\",text:\"error\"}); \n node.error(\"F16 Error : registro no coincide\", msg);\n}\n// Cantidad es la misma?\nelse if(qtytx != qtyrx)\n{\n node.status({fill:\"red\",shape: \"dot\",text:\"error\"}); \n node.error(\"F16 Error : cantidad no coincide\", msg);\n}\n// Esclavo es el mismo?\nelse if(msg.payload.slave != global.get('MBSlaveId'))\n{\n node.status({fill:\"red\",shape:\"dot\",text: \"error\"}); \n node.error(\"F16 Error : esclavo no coincide\", msg);\n} \n// Funcion es la misma?\nelse if(msg.payload.function != global.get ('MBFunction'))\n{\n node.status({fill:\"red\",shape:\"dot\",text:\"error \"}); \n node.error(\"F16 Error : funcion no coincide\", msg);\n}\nelse \n{\n node.status({fill:\"green\",shape:\"dot\",text:\"exito\"}); \n return msg;\n}\n \n","outputs":1,"noerr":0,"x":3017.7621240615845,"y":1832.531626701355,"wires" :[[]]}, {"id":"befb0ece.17b","type":"function","z":"68022ccb.861a04","name":"Error respuesta a funcion no implementada","func":"// Automatizanos.com\n// \nnode.error(\" Error : respuesta a funcion no implementada\", msg);","outputs":1,"noerr":0,"x":3090.3332147598267,"y":1910.2460289001465,"wi res":[[]]}, {"id":"d43f24b6.3a4318","type":"inject","z":"68022ccb.861a04","name":"Prueba manual","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"", "once":false,"x":160.69032287597656,"y":299.5555419921875,"wires": [["66364e93.90b2c"]]},{"id":"4095c054.1d447","type":"link out","z":"68022ccb.861a04","name":"","links": ["c4418a52.0f4ee8"],"x":828.0711803436279,"y":255.03174781799316,"wires":[]}, {"id":"c4418a52.0f4ee8","type":"link in","z":"68022ccb.861a04","name":"MB Call","links": ["4095c054.1d447","ec1b8171.1ad07"],"x":159.42857360839844,"y":920.53173828125 ,"wires":[["7c539030.3d893"]]}, {"id":"78d46b14.d90394","type":"function","z":"68022ccb.861a04","name":"Valore s MB desde ejemplos de funciones de lectura","func":"// Automatizanos.com\n// \n// ****************************************\n// Ejemplo F01: Leer estados de bobinas #000034 a la 000040\n// desde el esclavo 19\n\n//var arrtmp = global.get('MBArray');\n//var coil34status = arrtmp[33];\n//var coil35status = arrtmp[34];\n//var coil36status = arrtmp[35];\n//var Mas informacion:

Automatizanos.com

@automatizanos

coil37status = arrtmp[36];\n//var coil38status = arrtmp[37];\n//var coil39status = arrtmp[38];\n//var coil40status = arrtmp[39];\n\n// ****************************************\n// Ejemplo F02: Leer estado de entradas #100100 a la 100101\n// desde el esclavo 28\n\n//var arrtmp = global.get('MBArray');\n//var input100status = arrtmp[99];\n//var input101status = arrtmp[100];\n\n// **************************************** \n// Ejemplo F03: Leer registro Holding #40000F\n// desde el esclavo 3\n\nvar arrtmp = global.get('MBArray');\nvar holding14value = arrtmp [14];\nglobal.set('MBGlobalValue',holding14value);\n//console.log (holding14value);\n\n// ****************************************\n// Ejemplo F04: leer registros de entrada #300006 al 300009\n// desde el esclavo 30\n\n//var arrtmp = global.get('MBArray');\n//var input6value = arrtmp[5];\n//var input7value = arrtmp[6];\n//var input8value = arrtmp[7]; \n//var input9value = arrtmp[8];\n\nreturn msg;","outputs":1,"noerr":0,"x":1685.0238342285156,"y":325.0000123977661,"wire s":[["b5fedf65.73d1b"]]}, {"id":"1baeaad6.73f155","type":"function","z":"68022ccb.861a04","name":"PONGA AQUI SU RUTINA DE MANEJO DE ERRORES","func":"// Automatizanos.com\n//\nreturn msg;","outputs":1,"noerr":0,"x":1515.0237884521484,"y":254.99999284744263,"wir es":[[]]}, {"id":"d5fcda27.2397f8","type":"comment","z":"68022ccb.861a04","name":"Automat izanos.com","info":"Automatizanos.com","x":153.02367401123047,"y":97.999991893 76831,"wires":[]}, {"id":"821dc398.392f7","type":"inject","z":"68022ccb.861a04","name":"Auto ejecucion primera vez","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","on ce":true,"x":361.2500305175781,"y":769.1666870117188,"wires": [["dea083d2.3ca03"]]}, {"id":"66364e93.90b2c","type":"function","z":"68022ccb.861a04","name":"Leer Holding 40000F desde esclavo 3 ","func":"// Automatizanos.com\n//\n// Cambiar los valores en las variables globales\n// para modificar el comportamiento modbus\n// MBSlaveId : Direccion del esclavo Modbus Slave\n// MBFunction: Funcion ModBus\n// MBRegister: Registro o Bobina a escribir o leer.\n// Primer registro a escribir o leer\n// si se llama a una funcion de multiples registros/bobinas\n// MBQuantity: Cantidad de registros/bobinas\n// a escribir o leer. Solo se use\n// en funciones de multiples registros/bobinas\n// MBArray: Arreglo donde se pondra la informacion a ser escrita,\n// o donde se almacenaran los datos despues de una operacion \n// de lectura\n// MBRs458Enable: Remover caracteres de eco\n// producidos por algunos conversores \n// USB/RS232 a RS458 \n\n\n\n// ****************************************\n// Ejemplo F03: Leer el registro Holding #40000F \n// desde el esclavo 3 usando convertidor RS485\n\nglobal.set('MBSlaveId',0x03);\nglobal.set ('MBFunction',0x03);\nglobal.set('MBRegister',0x000F);\nglobal.set ('MBQuantity',0x0001);\nglobal.set('MBRs458Enable',0x0001);\n\n\n\nreturn msg;","outputs":1,"noerr":0,"x":465,"y":255,"wires":[["eecb3307.e8ca"]]}, {"id":"47b9b055.66b86","type":"debug","z":"68022ccb.861a04","name":"","active" :true,"console":"false","complete":"false","x":684.3095245361328,"y":1782.1664 714813232,"wires":[]}, {"id":"69556b5f.4121d4","type":"switch","z":"68022ccb.861a04","name":"RS485 Habilitado?","property":"MBRs458Enable","propertyType":"global","rules": [{"t":"eq","v":"0","vt":"num"},{"t":"else"} ],"checkall":"true","outputs":2,"x":675.476188659668,"y":1605.571434020996,"wi Mas informacion:

Automatizanos.com

@automatizanos

res":[["93dc28e0.b37488"],["f4cc5e1.5cd27a"]]}, {"id":"f4cc5e1.5cd27a","type":"function","z":"68022ccb.861a04","name":"Remover caracteres de eco","func":"var actualsize = msg.payload.length;\nvar txsize = global.get('MBTxcharsCnt');\n\nif( actualsize - txsize > 0)\n{\n var trimmedsize = actualsize - txsize;\n BinRxBuff = new Buffer(trimmedsize);\n for(var i=0 ; i < trimmedsize ; i++)\n {\n BinRxBuff[i]=msg.payload [i+txsize];\n }\n \n}\nelse\n{\n BinRxBuff = new Buffer(0);\n}\n\n \nmsg.payload=BinRxBuff;\nreturn msg;","outputs":1,"noerr":0,"x":911.0714416503906,"y":1654.2856311798096,"wire s":[["93dc28e0.b37488"]]}, {"id":"7e1165c4.bfb7ec","type":"function","z":"68022ccb.861a04","name":"Escrib ir registro holding 40000B al esclavo 1","func":"// Automatizanos.com\n//\n// Cambiar los valores en las variables globales\n// para modificar el comportamiento modbus\n// MBSlaveId : Direccion del esclavo Modbus Slave \n// MBFunction: Funcion ModBus\n// MBRegister: Registro o Bobina a escribir o leer.\n// Primer registro a escribir o leer \n// si se llama a una funcion de multiples registros/ bobinas\n// MBQuantity: Cantidad de registros/bobinas\n// a escribir o leer. Solo se use\n// en funciones de multiples registros/bobinas\n// MBArray: Arreglo donde se pondra la informacion a ser escrita,\n// o donde se almacenaran los datos despues de una operacion \n// de lectura\n// MBRs458Enable: Remover caracteres de eco\n// producidos por algunos conversores \n// USB/RS232 a RS458 \n\n\n// ****************************************\n// Ejemplo F06: Escribir valor >0 al esclavo 1\n// en el registro de salida holding 40000B\n// usando conversor RS485 \n\nglobal.set ('MBSlaveId',0x01);\nglobal.set('MBFunction',0x06);\nglobal.set ('MBRegister',0x000B);\n\nvar arrtmp = global.get('MBArray');\narrtmp [(global.get('MBRegister')-1)]=0x00FF;\nglobal.set('MBArray',arrtmp); \nglobal.set('MBRs458Enable',0x01);\n\n\nreturn msg;","outputs":1,"noerr":0,"x":2259.166675567627,"y":310.8333435058594,"wires ":[["a6b26e3a.c1027"]]}, {"id":"b5fedf65.73d1b","type":"switch","z":"68022ccb.861a04","name":"Valor > 500 ?","property":"MBGlobalValue","propertyType":"global","rules": [{"t":"gt","v":"500","vt":"num"},{"t":"else"} ],"checkall":"true","outputs":2,"x":1968.7501220703125,"y":325.2500305175781," wires":[["7e1165c4.bfb7ec"],["608a56c3.228838"]]}, {"id":"608a56c3.228838","type":"function","z":"68022ccb.861a04","name":"","fun c":"\nreturn msg;","outputs":1,"noerr":0,"x":2140.416759490967,"y":382.91666984558105,"wire s":[[]]},{"id":"ec1b8171.1ad07","type":"link out","z":"68022ccb.861a04","name":"","links": ["c4418a52.0f4ee8"],"x":2687.750244140625,"y":310.8333435058594,"wires":[]}, {"id":"eecb3307.e8ca","type":"function","z":"68022ccb.861a04","name":"sequence =0","func":"global.set('sequence',0x00);\nreturn msg;","outputs":1,"noerr":0,"x":705.8333206176758,"y":254.99998474121094,"wire s":[["4095c054.1d447"]]}, {"id":"be0420d6.78d2c","type":"switch","z":"68022ccb.861a04","name":"sequence= =0?","property":"sequence","propertyType":"global","rules": [{"t":"eq","v":"0","vt":"num"},{"t":"else"} ],"checkall":"true","outputs":2,"x":1383.333309173584,"y":325.00001525878906," wires":[["78d46b14.d90394"],["846b09a4.eb0398"]]}, {"id":"846b09a4.eb0398","type":"function","z":"68022ccb.861a04","name":"","fun c":"\nreturn Mas informacion:

Automatizanos.com

@automatizanos

msg;","outputs":1,"noerr":0,"x":1583.333333333333,"y":391.66666666666663,"wire s":[[]]}, {"id":"a6b26e3a.c1027","type":"function","z":"68022ccb.861a04","name":"sequenc e=1","func":"global.set('sequence',0x01);\nreturn msg;","outputs":1,"noerr":0,"x":2579.333251953125,"y":311,"wires": [["ec1b8171.1ad07"]]}, {"id":"166d2f9d.4c557","type":"inject","z":"68022ccb.861a04","name":"Repetitiv o","topic":"","payload":"","payloadType":"date","repeat":"6","crontab":"","onc e":false,"x":149.99999237060547,"y":208.33332061767578,"wires": [["66364e93.90b2c"]]},{"id":"cedb070b.f339a8","type":"serialport","z":"","serialport":"/dev/ ttyUSB0","serialbaud":"9600","databits":"8","parity":"none","stopbits":"1","ne wline":"50","bin":"bin","out":"time","addchar":false}]

Mas informacion:

Automatizanos.com

@automatizanos

IMPLEMENTACION DIDACTICA DE MODBUS RTU EN NODE-RED

[{"id":"2f8ccd56.fa5192","type":"tab","label":"Flow 2"}, {"id":"c5484fab.af2c4","type":"serial out","z":"2f8ccd56.fa5192","name":"SERIAL","serial":"e8869665.d81788","x":1648 .9403667449951,"y":863.9960479736328,"wires":[]}, {"id":"b027223b.93be6","type":"serial in","z":"2f8ccd56.fa5192","name":"","serial":"e8869665.d81788","x":279.5279235 839844,"y":1554.3607921600342,"wires":[["2d4ba7a6.c766c8"]]}, {"id":"27c04a81.c1a3c6","type":"switch","z":"2f8ccd56.fa5192","name":"F switch","property":"payload.function","propertyType":"msg","rules": [{"t":"eq","v":"0x01","vt":"num"},{"t":"eq","v":"0x02","vt":"num"}, {"t":"eq","v":"0x03","vt":"num"},{"t":"eq","v":"0x04","vt":"num"}, {"t":"eq","v":"0x05","vt":"num"},{"t":"eq","v":"0x06","vt":"num"}, {"t":"eq","v":"0x0F","vt":"num"},{"t":"eq","v":"0x10","vt":"num"},{"t":"else"} ],"checkall":"true","outputs":9,"x":717.6070098876953,"y":865.3293685913086,"w ires":[["cce84c93.39e66"],["efb2e4c.2695118"],["16fcc5e3.3334da"], ["b4fd7c1e.523a4"],["8bd92de8.2f2d6"],["e56948db.d38688"],["5086aaea.4863c4"], ["f9390b5.3cd9df8"],["b950ef6d.6d458"]]}, {"id":"4d9f73ac.438a6c","type":"function","z":"2f8ccd56.fa5192","name":"RTU CRC CHECK","func":"// Absolutelyautomation.com\n//\nvar len = msg.payload.rxpacket.length;\nvar dataStr = msg.payload.rxpacket;\nvar crc = 0xFFFF;\n\nfor ( var pos = 0; pos < (len-2) ; pos++ ) \n{\n crc = crc ^ dataStr[pos]; \n \n for (var i = 8; i !== 0; i--) \n { // Loop over each bit\n \n if ((crc & 0x0001) !== 0) \n { // If the LSB is set\n crc = crc >> 1; // Shift right and XOR 0xA001\n crc = crc ^ 0xA001;\n }\n else // Else LSB is not set\n crc = crc >> 1; // Just shift right\n } \n \n}\n\n// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes)\n\nvar crclow = crc >> 8;\nvar crchi = crc & 0x00FF;\n\nif ( crchi === dataStr[len-2] && crclow === dataStr[len-1] ) \n { \n //flow.set('CRCrcv',0x01);\n msg.payload.okcrc = 0x01;\n}\nelse \n //flow.set('CRCrcv',0x00);\n msg.payload.okcrc = 0x00;\n\n\nreturn msg; \n","outputs":1,"noerr":0,"x":1735.162817955017,"y":1568.8527536392212,"wires" :[["83794718.433368"]]}, {"id":"95cc1cdd.ea3a8","type":"switch","z":"2f8ccd56.fa5192","name":"Min packet size?","property":"payload.rxpacket.length","propertyType":"msg","rules": [{"t":"gt","v":"3","vt":"num"},{"t":"lt","v":"4","vt":"num"} ],"checkall":"true","outputs":2,"x":1526.2739782333374,"y":1575.995810508728," wires":[["4d9f73ac.438a6c"],["8ac5c87c.f72718"]]}, {"id":"8ac5c87c.f72718","type":"function","z":"2f8ccd56.fa5192","name":"Packet too short error","func":"// Absolutelyautomation.com\n//\nnode.error(\"Packet too short Error\", msg);","outputs":1,"noerr":0,"x":1746.5913076400757,"y":1617.1067714691162,"wi res":[[]]}, {"id":"83794718.433368","type":"switch","z":"2f8ccd56.fa5192","name":"OK CRC?","property":"payload.okcrc","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"num" {\n node.status({fill:\"red\",shape:\"dot\",text:\"error\"}); \n node.error(\"F05 function mismatch\", msg);\n}\nelse\n{\n node.status ({fill:\"green\",shape:\"dot\",text:\"sucess\"}); \n return msg;\n}\n \n\n Mas informacion:

Automatizanos.com

@automatizanos

\n","outputs":1,"noerr":0,"x":2793.8651428222656,"y":1610.3572435379028,"wires ":[[]]}, {"id":"1374657c.35484b","type":"function","z":"2f8ccd56.fa5192","name":"F06 analyze packet","func":"// Absolutelyautomation.com\n//\n// F06: Preset single register\n\nvar regrx = (msg.payload.rxpacket[2] * 256) | (msg.payload.rxpacket[3]) ;\nvar tmpreg = global.get('MBRegister') \nvar arrtmp = global.get('MBArray');\nvar valuetx = arrtmp[tmpreg-1];\nvar valuerx = (msg.payload.rxpacket[4] * 256) | (msg.payload.rxpacket[5]) ;\n \n// Register is the same?\nif( regrx != tmpreg-1 )\n{\n node.status ({fill:\"red\",shape:\"dot\",text:\"error\"}); \n node.error(\"F06 Register mismatch\", msg);\n}\n// Value is ok?\nelse if(valuetx != valuerx) \n{\n node.status({fill:\"red\",shape:\"dot\",text:\"error\"}); \n node.error(\"F06 Value mismatch\", msg);\n}\n// Slave is the same?\nelse if (msg.payload.slave != global.get('MBSlaveId'))\n{\n node.status({fill: \"red\",shape:\"dot\",text:\"error\"}); \n node.error(\"F06 slave mismatch\", msg);\n}\n// Function is the same?\nelse if (msg.payload.function != global.get('MBFunction'))\n{\n node.status ({fill:\"red\",shape:\"dot\",text:\"error\"}); \n node.error(\"F06 function mismatch\", msg);\n}\nelse\n{\n node.status({fill:\"green \",shape:\"dot\",text:\"sucess\"}); \n return msg;\n}\n \n","outputs":1,"noerr":0,"x":2792.7540893554688,"y":1663.6904573440552,"wires ":[[]]}, {"id":"b950ef6d.6d458","type":"function","z":"2f8ccd56.fa5192","name":"Unimple mented Function request error","func":"// Absolutelyautomation.com\n// \nnode.error(\"Unimplemented Function request Error\", msg);","outputs":1,"noerr":0,"x":1022.7540044784546,"y":1131.468183517456,"wir es":[[]]}, {"id":"54e01ea0.892c7","type":"function","z":"2f8ccd56.fa5192","name":"F15 analyze packet","func":"// Absolutelyautomation.com\n//\n// F15: Force multiple coils\nvar coilrx = (msg.payload.rxpacket[2] * 256) | (msg.payload.rxpacket[3]);\nvar tmpreg = global.get('MBRegister'); \nvar qtytx = global.get('MBQuantity');\nvar qtyrx = (msg.payload.rxpacket[4] * 256) | (msg.payload.rxpacket[5]);\n\n// Register is the same?\nif( coilrx ! = tmpreg-1 )\n{\n node.status({fill:\"red\",shape:\"dot\",text:\"error\"} ); \n node.error(\"F15 Register mismatch\", msg);\n}\n// Qty is the same?\nelse if(qtytx != qtyrx)\n{\n node.status({fill:\"red\",shape: \"dot\",text:\"error\"}); \n node.error(\"F15 Qty mismatch\", msg);\n} \n// Slave is the same?\nelse if(msg.payload.slave != global.get ('MBSlaveId'))\n{\n node.status({fill:\"red\",shape:\"dot\",text:\"error \"}); \n node.error(\"F15 slave mismatch\", msg);\n}\n// Function is the same?\nelse if(msg.payload.function != global.get('MBFunction'))\n{\n node.status({fill:\"red\",shape:\"dot\",text:\"error\"}); \n node.error (\"F15 function mismatch\", msg);\n}\nelse\n{\n node.status({fill:\"green \",shape:\"dot\",text:\"sucess\"}); \n return msg;\n}\n \n","outputs":1,"noerr":0,"x":2792.7541122436523,"y":1721.4682512283325,"wires ":[[]]}, {"id":"219a0ccc.69bc54","type":"function","z":"2f8ccd56.fa5192","name":"F16 analyze packet","func":"// Absolutelyautomation.com\n//\n// F16: Preset multiple registers\n\nvar regrx = (msg.payload.rxpacket[2] * 256) | (msg.payload.rxpacket[3]);\nvar tmpreg = global.get('MBRegister'); \nvar qtytx = global.get('MBQuantity');\nvar qtyrx = (msg.payload.rxpacket[4] * 256) | (msg.payload.rxpacket[5]);\n\n// Register is the same?\nif( regrx != tmpreg-1 )\n{\n node.status({fill:\"red\",shape:\"dot\",text:\"error\"}); \n node.error(\"F16 Register mismatch\", msg);\n}\n// Qty is the same?\nelse if Mas informacion:

Automatizanos.com

@automatizanos

(qtytx != qtyrx)\n{\n node.status({fill:\"red\",shape:\"dot\",text:\"error \"}); \n node.error(\"F16 Qty mismatch\", msg);\n}\n// Slave is the same?\nelse if(msg.payload.slave != global.get('MBSlaveId'))\n{\n node.status({fill:\"red\",shape:\"dot\",text:\"error\"}); \n node.error (\"F16 slave mismatch\", msg);\n}\n// Function is the same?\nelse if (msg.payload.function != global.get('MBFunction'))\n{\n node.status({fill: \"red\",shape:\"dot\",text:\"error\"}); \n node.error(\"F16 function mismatch\", msg);\n}\nelse\n{\n node.status({fill:\"green\",shape:\"dot \",text:\"sucess\"}); \n return msg;\n}\n \n","outputs":1,"noerr":0,"x":2789.7384033203125,"y":1783.5316944122314,"wires ":[[]]}, {"id":"8d850c78.dde56","type":"function","z":"2f8ccd56.fa5192","name":"Unimple mented Function answer error","func":"// Absolutelyautomation.com\n// \nnode.error(\"Unimplemented Function answer Error\", msg);","outputs":1,"noerr":0,"x":2852.3094940185547,"y":1861.246096611023,"wir es":[[]]}, {"id":"af1c57a4.284a68","type":"function","z":"2f8ccd56.fa5192","name":"MB Setup examples","func":"// Absolutelyautomation.com\n//\n// Change values in global variables to \n// modify ModBus behavior\n// MBSlaveId : Id of the Modbus Slave\n// MBFunction: ModBus function\n// MBRegister: Register or Coil to be written or read.\n// First register be written or read if\n// a multi-register/coil is called\n// MBQuantity: Quantity of registers/coils\n// to be writter/readed. Only used\n// in multi-register/coil functions\n// MBArray: Array to put data to be written,\n// or when data is stored after a \n// read operation\n// MBRs458Enable: Remove loopbacked chars\n// produced by some USB/RS232 to RS458 \n// converters\n\n\n// ****************************************\n// Example F01: Read coil status #000034 to 000040\n// from slave 19 via RS485\n\n//global.set ('MBSlaveId',0x13);\n//global.set('MBFunction',0x01);\n//global.set ('MBRegister',0x0022);\n//global.set('MBQuantity',0x0007);\n//global.set ('MBRs458Enable',0x01);\n\n// ****************************************\n// Example F02: Read input status #100100 to 100101\n// from slave 28 via RS232\n\n//global.set('MBSlaveId',0x1C);\n//global.set ('MBFunction',0x02);\n//global.set('MBRegister',0x0064);\n//global.set ('MBQuantity',0x0002);\n//global.set('MBRs458Enable',0x00);\n\n// ****************************************\n// Example F03: Read Holding registers #400499 to 400501\n// from slave 150 via RS458 \n \nglobal.set('MBSlaveId',0x96);\nglobal.set('MBFunction',0x03);\nglobal.set ('MBRegister',0x01F3);\nglobal.set('MBQuantity',0x0003);\nglobal.set ('MBRs458Enable',0x01);\n\n// ****************************************\n// Example F04: Read Input registers #300006 to 300009\n// from slave 30 via RS232\n\n//global.set('MBSlaveId',0x1E);\n//global.set ('MBFunction',0x04);\n//global.set('MBRegister',0x0006);\n//global.set ('MBQuantity',0x0004);\n//global.set('MBRs458Enable',0x00);\n\n// ****************************************\n// Example F05: Force single coil #000081 to ON status\n// from slave 200 via RS485\n\n//global.set ('MBSlaveId',0xC8);\n//global.set('MBFunction',0x05);\n//global.set ('MBRegister',0x0051);\n//var arrtmp = global.get('MBArray');\n//arrtmp [(global.get('MBRegister')-1)]=0xFF00;\n//global.set('MBArray',arrtmp);\n// global.set('MBRs458Enable',0x01);\n\n// ****************************************\n// Example F06: Write value 1234 to slave 3\n// into output holding register 400017\n// Mas informacion:

Automatizanos.com

@automatizanos

via RS232\n\n//global.set('MBSlaveId',0x03);\n//global.set('MBFunction',0x06); \n//global.set('MBRegister',0x0011);\n\n//var arrtmp = global.get('MBArray'); \n//arrtmp[(global.get('MBRegister')-1)]=0x007B;\n//global.set ('MBArray',arrtmp);\n//global.set('MBRs458Enable',0x00);\n\n// ****************************************\n// Example F15: Force multiple coils #000204,#000205,#000206 to ON,ON,OFF status\n// from slave 12 via RS485\n\n//global.set('MBSlaveId',0x0C);\n//global.set('MBFunction',0x0F);\n// global.set('MBRegister',0x00CC);\n//global.set('MBQuantity',0x0003);\n//var arrtmp = global.get('MBArray');\n//arrtmp[(global.get ('MBRegister')-1)]=0xFF00;\n//arrtmp[(global.get('MBRegister'))]=0xFF00;\n// arrtmp[(global.get('MBRegister')+1)]=0x0000;\n//global.set('MBArray',arrtmp); \n//global.set('MBRs458Enable',0x01);\n\n// ****************************************\n// Example F16: Preset multiple registers #401002,#401003,#000206 to 777,888 values\n// from slave 80 via RS232\n\n//global.set('MBSlaveId',0x50);\n//global.set ('MBFunction',0x10);\n//global.set('MBRegister',0x03E8);\n//global.set ('MBQuantity',0x0002);\n//var arrtmp = global.get('MBArray');\n//arrtmp [(global.get('MBRegister')-1)]=0x0309;\n//arrtmp[(global.get ('MBRegister'))]=0x0378;\n//global.set('MBArray',arrtmp);\n//global.set ('MBRs458Enable',0x00);\n\nreturn msg;","outputs":1,"noerr":0,"x":532.6427993774414,"y":218.88890171051025,"wire s":[["d7e71ce2.e587a"]]}, {"id":"f3d0f1c9.12fe4","type":"inject","z":"2f8ccd56.fa5192","name":"Manual single test","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","o nce":false,"x":316.99997329711914,"y":218.8889136314392,"wires": [["af1c57a4.284a68"]]},{"id":"d7e71ce2.e587a","type":"link out","z":"2f8ccd56.fa5192","name":"","links": ["734d7ce1.8466b4"],"x":681.2141799926758,"y":218.19844150543213,"wires":[]}, {"id":"734d7ce1.8466b4","type":"link in","z":"2f8ccd56.fa5192","name":"MB Call","links": ["d7e71ce2.e587a"],"x":217.73801231384277,"y":863.3412685394287,"wires": [["591b11ac.e48c2"]]}, {"id":"8b3c07e5.73eea8","type":"function","z":"2f8ccd56.fa5192","name":"MB Values from reading functions examples","func":"// Absolutelyautomation.com \n//\n// ****************************************\n// Example F01: Read coil status #000034 to 000040\n// from slave 19\n\n//var arrtmp = global.get('MBArray');\n//var coil34status = arrtmp[33];\n//var coil35status = arrtmp[34];\n//var coil36status = arrtmp[35];\n//var coil37status = arrtmp [36];\n//var coil38status = arrtmp[37];\n//var coil39status = arrtmp[38];\n// var coil40status = arrtmp[39];\n\n// **************************************** \n// Example F02: Read input status #100100 to 100101\n// from slave 28\n\n//var arrtmp = global.get('MBArray');\n//var input100status = arrtmp[99];\n//var input101status = arrtmp[100];\n\n// ****************************************\n// Example F03: Read Holding registers #400499 to 400501\n// from slave 150\n\nvar arrtmp = global.get('MBArray');\nvar holding499value = arrtmp[498];\nvar holding500value = arrtmp[499];\nvar holding501value = arrtmp[500];\n\n// ****************************************\n// Example F04: Read Input registers #300006 to 300009\n// from slave 30\n\n//var arrtmp = global.get ('MBArray');\n//var input6value = arrtmp[5];\n//var input7value = arrtmp[6]; \n//var input8value = arrtmp[7];\n//var input9value = arrtmp[8];\n\nreturn msg;","outputs":1,"noerr":0,"x":1217.3333892822266,"y":258.33336353302,"wires" :[[]]}, Mas informacion:

Automatizanos.com

@automatizanos

{"id":"f39eda35.486a08","type":"function","z":"2f8ccd56.fa5192","name":"YOUR ERROR HANDLER FUNCTION","func":"// Absolutelyautomation.com\n//\nreturn msg;","outputs":1,"noerr":0,"x":1194.000099182129,"y":188.33337020874023,"wire s":[[]]}, {"id":"e2602952.f6e6a8","type":"comment","z":"2f8ccd56.fa5192","name":"Absolut elyautomation.com","info":"Absolutelyautomation.com","x":115,"y":42.3333587646 4844,"wires":[]}, {"id":"9226f54b.6cc228","type":"inject","z":"2f8ccd56.fa5192","name":"First time auto run","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","on ce":true,"x":320.22635650634766,"y":713.5000553131104,"wires": [["d1318291.0cc0a"]]}, {"id":"71342885.b0a0e8","type":"function","z":"2f8ccd56.fa5192","name":"Remove loopback chars","func":"var actualsize = msg.payload.length;\nvar txsize = global.get('MBTxcharsCnt');\n\nif( actualsize - txsize > 0)\n{\n var trimmedsize = actualsize - txsize;\n BinRxBuff = new Buffer(trimmedsize);\n for(var i=0 ; i < trimmedsize ; i++)\n {\n BinRxBuff[i]=msg.payload [i+txsize];\n }\n \n}\nelse\n{\n BinRxBuff = new Buffer(0);\n}\n\n \nmsg.payload=BinRxBuff;\nreturn msg;","outputs":1,"noerr":0,"x":692.119026184082,"y":1591.57133102417,"wires": [["9803a1b3.adcb7"]]}, {"id":"2d4ba7a6.c766c8","type":"switch","z":"2f8ccd56.fa5192","name":"RS485 Enabled?","property":"MBRs458Enable","propertyType":"global","rules": [{"t":"eq","v":"0","vt":"num"},{"t":"else"} ],"checkall":"true","outputs":2,"x":466.8253860473633,"y":1555.2062797546387," wires":[["9803a1b3.adcb7"],["71342885.b0a0e8"]]}, {"id":"e8869665.d81788","type":"serial-port","z":"","serialport":"/dev/ ttyUSB0","serialbaud":"9600","databits":"8","parity":"none","stopbits":"1","ne wline":"25","bin":"bin","out":"time","addchar":false}]

Mas informacion:

Automatizanos.com

@automatizanos

APLICACION DE PRUEBA EN PYTHON MODBUS RTU PARA ENVIO DE PETICIONES #!/usr/bin/env python #---------------------------------------------------------------------------# # Automatizanos.com #---------------------------------------------------------------------------# ''' Pymodbus Synchrnonous Client Examples -------------------------------------------------------------------------The following is an example of how to use the synchronous modbus client implementation from pymodbus. It should be noted that the client can also be used with the guard construct that is available in python 2.5 and up:: with ModbusClient('127.0.0.1') as client: result = client.read_coils(1,10) print result

''' #---------------------------------------------------------------------------# # import the various server implementations #---------------------------------------------------------------------------# #from pymodbus.client.sync import ModbusTcpClient as ModbusClient #from pymodbus.client.sync import ModbusUdpClient as ModbusClient from pymodbus.client.sync import ModbusSerialClient as ModbusClient #---------------------------------------------------------------------------# # configure the client logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # choose the client you want #---------------------------------------------------------------------------# # make sure to start an implementation to hit against. For this # you can use an existing device, the reference implementation in the tools # directory, or start a pymodbus server. #---------------------------------------------------------------------------# #client = ModbusClient('127.0.0.1') client = ModbusClient("rtu", port="/dev/ttyUSB0", baudrate=9600, timeout=2) #---------------------------------------------------------------------------# # example requests #---------------------------------------------------------------------------# # simply call the methods that you would like to use. An example session # is displayed below along with some assert checks. #---------------------------------------------------------------------------# Mas informacion:

Automatizanos.com

@automatizanos

#rq = client.write_coil(1, True,unit= 0x01) #rr = client.read_coils(1,1,unit= 0x01) #assert(rq.function_code < 0x80) # test that we are not an error #assert(rr.bits[0] == True) # test the expected value #rq = client.write_coils(1, [True]*8,unit= 0x01) #rr = client.read_coils(1,8,unit= 0x01) #assert(rq.function_code < 0x80) # test that we are not an error #assert(rr.bits == [True]*8) # test the expected value #rq = client.write_coils(1, [False]*8,unit= #rr = client.read_discrete_inputs(1,8,unit= #assert(rq.function_code < 0x80) # test #assert(rr.bits == [False]*8) # test

0x01) 0x01) that we are not an error the expected value

#rq = client.write_register(8, 200,unit= 0x01) #rq = client.write_register(9, 200,unit= 0x01) rq = client.write_register(10, 200,unit= 0x01) #rr = client.read_holding_registers(8,200,unit= 0x01) #assert(rq.function_code < 0x80) # test that we are not an error #assert(rr.registers[0] == 10) # test the expected value #rq = client.write_registers(1, [10]*8,unit= 0x01) #rr = client.read_input_registers(1,8,unit= 0x01) #assert(rq.function_code < 0x80) # test that we are not an error #assert(rr.registers == [10]*8) # test the expected value #rq = client.readwrite_registers(1, [20]*8,unit= 0x01) #rr = client.read_input_registers(1,8,unit= 0x01) #assert(rq.function_code < 0x80) # test that we are not an error #assert(rr.registers == [20]*8) # test the expected value #---------------------------------------------------------------------------# # close the client #---------------------------------------------------------------------------# client.close()

Mas informacion:

Automatizanos.com

@automatizanos

Get in touch

Social

© Copyright 2013 - 2024 MYDOKUMENT.COM - All rights reserved.