OpenPLC no soporta el protocolo HTTP de forma nativa, pero existe una solución alternativa. En este blog aprenderemos a crear una extensión de Arduino para OpenPLC y usarla para enviar o recibir información mediante HTTP.
Prerequisite OpenPLC Tutorials for HTTP Communication on M-Duino
This blog is part of a series of blogs about OpenPLC on Arduino and M-Duino. If you still have not familiarized yourself with the software, we recommend you go through the previous blogs first:
First steps with OpenPLC
Modbus on Arduino with OpenPLC
OpenPLC on M-Duino 42+
Mapping any M-Duino on OpenPLC
MQTT on M-Duino with OpenPLC
Hardware Requirements for HTTP Communication with M-Duino PLC
Para el servidor usamos un ordenador con Node-RED, aunque puedes usar cualquier dispositivo capaz de ejecutar Node-RED, incluido nuestro Raspberry PLC. Incluso puedes usar otro M-Duino como servidor si lo configuras mediante una extensión de Arduino.
El cliente utilizado es un M-Duino 42+, but you can use any M-Duino or any OpenPLC-compatible board.
El cliente se conecta al servidor por Ethernet. Si se quisiera usar WiFi, habría que realizar algunas modificaciones en el código de la extensión de Arduino.
Setting Up an HTTP Server in Node-RED for M-Duino
Primero configuraremos un servidor sencillo en Node-RED para gestionar peticiones POST y GET.
Para gestionar peticiones POST, coloca un nodo "http in" y conéctalo a un nodo "http response". En el nodo "http in", cambia el método a POST y la URL a /hello. En el "http response", establece el código de estado a 200.
Para gestionar peticiones GET, coloca otro par de nodos "http in" y "http response". Configura el "http in" para aceptar GET en /hello. Entre los dos nodos HTTP, añade un nodo template y establece su propiedad a msg.payload y su contenido a Hello World!.
Finally, add debug nodes on the "http in" nodes to see what they receive. The end result should look like this:

If you haven't familiarized yourself with Node-RED, we recommend you go through this free course:
Develop your SCADA Application based on Node-RED
Updating OpenPLC Runtime on M-Duino for HTTP Support
Antes de continuar, asegúrate de que estás usando la última versión del OpenPLC Editor. Ve a "File > Check for updates..." y deja que se actualice. Para verificar que estás usando una versión adecuada, abre el ejemplo Blink, haz clic en el icono gris de suma y comprueba que aparece "Arduino extension".

If instead of "Arduino extension" you see something like "C extension", then your OpenPLC Editor is not up to date.
Installing the Arduino HTTP Extension in OpenPLC Editor
Abre el ejemplo Blink y añade una extensión de Arduino mediante el botón gris de suma. Aparecerá un nuevo archivo en la ventana del proyecto. Este archivo funciona igual que un sketch de Arduino en el IDE, con sus propios métodos setup y loop. Las extensiones de Arduino nos permiten ejecutar código Arduino en OpenPLC para aprovechar las bibliotecas de Arduino y enviar o recibir información mediante HTTP.
Depending on whether you want to use POST or GET, you'll follow different steps:
Usando el ejemplo Blink como punto de partida, crearemos un proyecto OpenPLC que envíe la cadena Hello World! al servidor Node-RED. Abre la extensión de Arduino recién creada y configura las variables así:

Estas variables se compartirán entre la extensión de Arduino y el diagrama ladder. Una breve explicación de cada una:
- send: cuando es true, envía el HTTP POST.
- serverIP: IP del servidor.
- plcIP: IP del PLC.
- path: ruta de la URL donde enviamos el POST.
- message: mensaje que queremos enviar.
- serverPort: puerto del servidor donde enviamos el POST.
Como la IP del PLC solo se asigna una vez durante el setup, ya está inicializada con su valor definitivo. El resto de variables recibirán sus valores desde el diagrama ladder.
Ahora puedes copiar el código de la extensión de Arduino.
//////////////////////////////// sketch section ////////////////////////////////
#include <ethernet.h>
#include <spi.h>
#include <string.h>
#include <ethernetclient.h>
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xAE };
IPAddress server;
IPAddress ipPLC;
char *serverIPString;
char *plcIPString;
unsigned port;
EthernetClient client;
void sketch_setup()
{
Serial.begin(9600);
plcIPString = plcIP.body;
ipPLC.fromString(plcIPString);
for(int i = 2; i<6;i++)
mac[i]=ipPLC[i-2];
Ethernet.begin(mac, ipPLC);
}
void sketch_loop()
{
if(!send)
return;
port = serverPort;
String PostData = message.body;
serverIPString = serverIP.body;
server.fromString(serverIPString);
char *pathString = path.body;
String postLine = "POST " + String(pathString) + " HTTP/1.1\r\n";
String hostLine = "Host: " + String(serverIPString) + "\r\n";
if (!client.connect(server, port)) {
int i=0;
do
{
if(i>=5) //delete these two lines if you want to try to reconnect forever
return; //
i++;
Serial.println("not connected, trying to reconnect");
delay(500);
Ethernet.begin(mac, ipPLC);
}while(!client.connect(server, port));
}
Serial.println("connected");
client.print(postLine);
client.print(hostLine);
client.print("User-Agent: Arduino/1.0\r\n");
client.print("Connection: close\r\n");
client.print("Content-Length: ");
client.println(PostData.length());
client.println();
client.println(PostData);
}
//////////////////////////////// sketch section ////////////////////////////////
#include <ethernet.h>
#include <spi.h>
#include <string.h>
#include <ethernetclient.h>
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xAE };
IPAddress server;
IPAddress ipPLC;
char *serverIPString;
char *plcIPString;
unsigned port;
EthernetClient client;
void sketch_setup()
{
Serial.begin(9600);
plcIPString = plcIP.body;
ipPLC.fromString(plcIPString);
for(int i = 2; i<6;i++)
mac[i]=ipPLC[i-2];
Ethernet.begin(mac, ipPLC);
}
void sketch_loop()
{
if(!send)
return;
port = serverPort;
String PostData = message.body;
serverIPString = serverIP.body;
server.fromString(serverIPString);
char *pathString = path.body;
String postLine = "POST " + String(pathString) + " HTTP/1.1\r\n";
String hostLine = "Host: " + String(serverIPString) + "\r\n";
if (!client.connect(server, port)) {
int i=0;
do
{
if(i>=5) //delete these two lines if you want to try to reconnect forever
return; //
i++;
Serial.println("not connected, trying to reconnect");
delay(500);
Ethernet.begin(mac, ipPLC);
}while(!client.connect(server, port));
}
Serial.println("connected");
client.print(postLine);
client.print(hostLine);
client.print("User-Agent: Arduino/1.0\r\n");
client.print("Connection: close\r\n");
client.print("Content-Length: ");
client.println(PostData.length());
client.println();
client.println(PostData);
}
Este código ha sido diseñado pensando en usuarios no familiarizados con C++; no es necesario modificar nada. Toda la funcionalidad del código se gestiona desde el diagrama ladder modificando las variables apropiadas.
Ahora pasamos al diagrama ladder. Para acceder a las extensiones de Arduino desde el diagrama ladder, debemos declararlas con el mismo nombre y establecer su clase como External. Configura las variables así:

Ahora configura tu diagrama ladder:

Cada vez que se activa "blink_led", también se activa "send" y se envía un HTTP POST. Los parámetros del POST se definen por los valores enviados a las variables externas.
Alternativamente, puedes crear un bloque de función personalizado para asignar los valores a las variables externas en lugar de hacerlo directamente en el diagrama ladder.
Tras cargar el código en el M-Duino y conectarlo al servidor por Ethernet, deberías empezar a ver mensajes de depuración con la cadena 'hello world':

Al igual que hicimos para el POST, usaremos el ejemplo Blink como punto de partida. Este proyecto OpenPLC enviará una petición GET al servidor y recibirá la información solicitada. En la extensión de Arduino recién creada, configura las variables así:

Estas variables se compartirán entre la extensión de Arduino y el diagrama ladder. Una breve explicación de cada una:
- recieve: cuando es true, envía la petición HTTP GET y recibe su respuesta.
- serverIP: IP del servidor.
- plcIP: IP del PLC.
- path: ruta de la URL donde enviamos la petición GET.
- message: variable que almacenará los datos de la respuesta recibida.
- serverPort: puerto del servidor donde enviamos la petición GET.
Como la IP del PLC solo se asigna una vez durante el setup, ya está inicializada con su valor definitivo. El resto de variables recibirán sus valores desde el diagrama ladder.
Ahora puedes copiar el código de la extensión de Arduino.
//////////////////////////////// sketch section ////////////////////////////////
#include <Ethernet.h>
#include <SPI.h>
#include <string.h>
#include <EthernetClient.h>
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xAE };
IPAddress server;
IPAddress ipPLC;
char *serverIPString;
char *plcIPString;
unsigned port;
EthernetClient client;
void sketch_setup()
{
Serial.begin(9600);
plcIPString = plcIP.body;
ipPLC.fromString(plcIPString);
for(int i = 2; i<6;i++)
mac[i]=ipPLC[i-2];
Ethernet.begin(mac, ipPLC);
}
void sketch_loop()
{
if(!recieve)
return;
String payload ="";
port = serverPort;
String PostData = message.body;
serverIPString = serverIP.body;
server.fromString(serverIPString);
char *pathString = path.body;
String postLine = "GET " + String(pathString) + " HTTP/1.1\r\n";
String hostLine = "Host: " + String(serverIPString) + "\r\n";
if (!client.connect(server, port)) {
int i=0;
do
{
if(i>=5)
return;
Serial.println("not connected, trying to reconnect");
delay(500);
Ethernet.begin(mac, ipPLC);
}while(!client.connect(server, port));
}
Serial.println("connected");
client.print(postLine);
client.print(hostLine);
client.print("User-Agent: Arduino/1.0\r\n");
client.print("Connection: close\r\n");
client.println();
char c="";
char previousC=c;
bool startRead=false;
bool firstChar=true;
while(client.connected() && !client.available()) delay(1);
while (client.connected() || client.available())
{
previousC=c;
c = client.read();
if(startRead){
if(!firstChar){
payload+=c;
}
else
firstChar=false;
}
if(c=='\r' && previousC=='\n')
startRead=true;
}
Serial.println();
Serial.println("disconnecting.");
Serial.println("==================");
Serial.println();
client.stop();
message.len=payload.length();
if (message.len < sizeof(message.body)) {
memcpy(message.body, payload.c_str(), message.len);
if (message.len < sizeof(message.body)) {
message.body[message.len] = '\0';
}
} else {
Serial.println("len error");
}
char *testPayload = message.body;
}
//////////////////////////////// sketch section ////////////////////////////////
#include <Ethernet.h>
#include <SPI.h>
#include <string.h>
#include <EthernetClient.h>
byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xAE };
IPAddress server;
IPAddress ipPLC;
char *serverIPString;
char *plcIPString;
unsigned port;
EthernetClient client;
void sketch_setup()
{
Serial.begin(9600);
plcIPString = plcIP.body;
ipPLC.fromString(plcIPString);
for(int i = 2; i<6;i++)
mac[i]=ipPLC[i-2];
Ethernet.begin(mac, ipPLC);
}
void sketch_loop()
{
if(!recieve)
return;
String payload ="";
port = serverPort;
String PostData = message.body;
serverIPString = serverIP.body;
server.fromString(serverIPString);
char *pathString = path.body;
String postLine = "GET " + String(pathString) + " HTTP/1.1\r\n";
String hostLine = "Host: " + String(serverIPString) + "\r\n";
if (!client.connect(server, port)) {
int i=0;
do
{
if(i>=5)
return;
Serial.println("not connected, trying to reconnect");
delay(500);
Ethernet.begin(mac, ipPLC);
}while(!client.connect(server, port));
}
Serial.println("connected");
client.print(postLine);
client.print(hostLine);
client.print("User-Agent: Arduino/1.0\r\n");
client.print("Connection: close\r\n");
client.println();
char c="";
char previousC=c;
bool startRead=false;
bool firstChar=true;
while(client.connected() && !client.available()) delay(1);
while (client.connected() || client.available())
{
previousC=c;
c = client.read();
if(startRead){
if(!firstChar){
payload+=c;
}
else
firstChar=false;
}
if(c=='\r' && previousC=='\n')
startRead=true;
}
Serial.println();
Serial.println("disconnecting.");
Serial.println("==================");
Serial.println();
client.stop();
message.len=payload.length();
if (message.len < sizeof(message.body)) {
memcpy(message.body, payload.c_str(), message.len);
if (message.len < sizeof(message.body)) {
message.body[message.len] = '\0';
}
} else {
Serial.println("len error");
}
char *testPayload = message.body;
}
Ahora puedes configurar las variables del diagrama ladder; recuerda establecer como External las variables del sketch de Arduino:

Ahora configura el diagrama ladder:

Este diagrama ladder enviará una petición GET cada vez que "recieve" sea activado por "blink_led". El mensaje recibido se guardará en la variable "message", que se comparará con la variable local que contiene la cadena "Hello World!". Si son iguales, se activará la salida Q0.0 del M-Duino. Si todo funciona correctamente, deberías ver encenderse el LED Q0.0 en la parte superior del PLC. La cadena 'Bye' se usa para desactivar Q0.0 cuando se pierde la conexión.
Para que OpenPLC pueda interactuar con las E/S del M-Duino, debes configurar correctamente la E/S en OpenPLC. Consulta este blog para recordar cómo hacerlo:
Mapping any M-Duino PLC for OpenPLC
Al igual que con el POST, también puedes crear un bloque de función personalizado para interactuar con las variables externas en lugar de hacerlo directamente.
Next blog:
MQTT on M-Duino with OpenPLC and Raspberry PLC