Web Server PLC FX3U

 

Web Server PLC FX3U


Web Server là gì?

Web server là máy chủ cài đặt các chương trình phục vụ các ứng dụng web. Webserver có khả năng tiếp nhận request từ các trình duyệt web và gửi phản hồi đến client thông qua giao thức HTTP hoặc các giao thức khác. Có nhiều web server khác nhau như: Apache, Nginx, IIS, …

Web Server cho PLC FX

Thông qua một bộ điều khiển nhỏ của Coder96 các bạn có thể tạo được một Web Server không chỉ cho PLC FX3U mà cho hàng nghìn thiết bị khác, thông qua chuẩn giao tiếp rất phổ biến trên thế giới đó là RS485 và RS232. Bo mạch của Coder96 được thiết kế theo chuẩn công nghiệp với khả năng chống nhiễu tốt và hoạt động dựa trên sóng không dây Wifi tốc độ cao. Bạn có thể tự lập trình cho mình một ứng dụng riêng biệt với bo mạch này.

Board ESP8266


Source code

/////////////////////////////////////////////////////////////////////////////////////////////
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <ModbusRtu.h>
#include <SoftwareSerial.h>

#define RS485_PIN_DIR 13
SoftwareSerial SerialPLC(5, 0);

const char *ssid = "WebServer";
const char *password = "12345678";

AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebServer FX3U</title>
<style>
body{
margin: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
header{
display: flex;
width: 100%;
justify-content: center;
align-items: center;
background-color: darkblue;
}
header span{
font-size: 2rem;
padding: 20px 0px 20px 0px;
color: #fff;

}
.container{
max-width: 600px;
height: 100%;
margin-top: 15px;
}
.btn-group, .led-group{
width: 100%;
display: flex;
justify-content: center;
margin-top: 10px;
}
.btn{
width: 80%;
height: 70px;
margin: 5px;
border-radius: 25px;
font-size: 25px;

}
.led{
width: 80px;
height: 80px;
margin: 10px;
border-radius: 50px;
display: flex;
justify-content: center;
align-items: center;
}
.led-off, .btn-off{
background-color: rgb(255, 0, 0);
color: #fff;
}
.led-on, .btn-on{
background-color: rgb(19, 255, 30);
color: #000;
}
.led-title{
font-size: 1.5rem;
}
.text{
display: flex;
justify-content: center;
align-items: center;
margin-top: 10px;
}
.text span{
font-size: 2rem;
width: 180px;
background-color: cornflowerblue;
text-align: center;
padding: 5px 5px 5px 5px;
border-radius: 5px;
}
</style>
</head>
<body>
<header>
<span>WebServer FX3U</span>
</header>
<div class="container">
<div class="btn-group">
<button id="btn-y0" class="btn btn-on" onclick="btnY0Click()">Y0</button>
<button id="btn-y1" class="btn btn-on" onclick="btnY1Click()">Y1</button>
</div>
<div class="led-group">
<div id="led-x0" class="led led-on"><span class="led-title">X0</span></div>
<div id="led-x1" class="led led-off"><span class="led-title">X1</span></div>
<div id="led-x2" class="led led-on"><span class="led-title">X2</span></div>
</div>
<div class="text">
<span id="d0">0</span>
</div>
</div>
</body>
<script>
setInterval(updateValue, 500);

function updateValue(){
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
var res = this.responseText;
resJson = JSON.parse(res);
checkData(resJson);
}
};
xhttp.open("GET", "/read", true);
xhttp.send();
}

function checkData(data){
if(data){
if(data.Y0 == 1){
document.getElementById("btn-y0").classList.replace("btn-off", "btn-on")
}
else{
document.getElementById("btn-y0").classList.replace("btn-on", "btn-off")
}

if(data.Y1 == 1){
document.getElementById("btn-y1").classList.replace("btn-off", "btn-on")
}
else{
document.getElementById("btn-y1").classList.replace("btn-on", "btn-off")
}

if(data.X0 == 1){
document.getElementById("led-x0").classList.replace("led-off", "led-on")
}
else{
document.getElementById("led-x0").classList.replace("led-on", "led-off")
}

if(data.X1 == 1){
document.getElementById("led-x1").classList.replace("led-off", "led-on")
}
else{
document.getElementById("led-x1").classList.replace("led-on", "led-off")
}

if(data.X2 == 1){
document.getElementById("led-x2").classList.replace("led-off", "led-on")
}
else{
document.getElementById("led-x2").classList.replace("led-on", "led-off")
}

document.getElementById("d0").innerText = data.D0;
}
}

function btnY0Click(){
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "/Y0", true);
xhttp.send();
}

function btnY1Click(){
var xhttp = new XMLHttpRequest();
xhttp.open("GET", "/Y1", true);
xhttp.send();
}


</script>
</html>
)rawliteral";

uint16_t au16data[16];
uint8_t u8state;
uint8_t u8query;
Modbus master(0, SerialPLC, RS485_PIN_DIR);
modbus_t telegram[4];
unsigned long u32wait;

uint16_t inputs[10];
uint16_t outputs[10];
uint16_t registers[10];

void initModbus()
{

telegram[0].u8id = 1;
telegram[0].u8fct = 3;
telegram[0].u16RegAdd = 0;
telegram[0].u16CoilsNo = 2;
telegram[0].au16reg = registers;

telegram[1].u8id = 1;
telegram[1].u8fct = 1;
telegram[1].u16RegAdd = 0;
telegram[1].u16CoilsNo = 4;
telegram[1].au16reg = inputs;

telegram[2].u8id = 1;
telegram[2].u8fct = 5;
telegram[2].u16RegAdd = 0;
telegram[2].u16CoilsNo = 1;
telegram[2].au16reg = outputs;

telegram[3].u8id = 1;
telegram[3].u8fct = 5;
telegram[3].u16RegAdd = 1;
telegram[3].u16CoilsNo = 1;
telegram[3].au16reg = outputs + 1;

SerialPLC.begin(19200);
master.start();
master.setTimeOut(5000);
u32wait = millis() + 1000;
u8state = u8query = 0;
}

void initWebServer()
{
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{
request->send(200, "text/html", index_html);
});

server.on("/read", HTTP_GET, [](AsyncWebServerRequest *request)
{
String s = "{\"Y0\":" + String(inputs[0]&1) + ",\"Y1\":" + String((inputs[0]&2)>>1) + ",\"X0\":" + String((inputs[0]&4)>>2) + ",\"X1\":" + String((inputs[0]&8)>>3) + ",\"X2\":" + String((inputs[0]&16)>>4) + ",\"D0\":" + String(registers[0]) + "}" ;
request->send_P(200, "text/plain", s.c_str());
});

server.on("/Y0", HTTP_GET, [](AsyncWebServerRequest *request)
{
outputs[0] = !outputs[0];
request->send_P(200, "text/plain", "OK");
});

server.on("/Y1", HTTP_GET, [](AsyncWebServerRequest *request)
{
outputs[1] = !outputs[1];
request->send_P(200, "text/plain", "OK");
});

server.begin();
}

void setup()
{
Serial.begin(9600);

WiFi.mode(WIFI_AP);
WiFi.softAP(ssid);

initModbus();
initWebServer();
}

void loop()
{
switch (u8state)
{
case 0:
if (millis() > u32wait)
u8state++;
break;
case 1:
master.query(telegram[u8query]);
u8state++;
u8query++;
if (u8query > 3)
u8query = 0;
break;
case 2:
master.poll();
if (master.getState() == COM_IDLE)
{
u8state = 1;
u32wait = millis() + 100;
}
break;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////

Video hướng dẫn



Chúc các bạn thành công!

2 comments:

  1. e dùng code này nhưng ESP 8266 ko có phản hồi j hết á a

    ReplyDelete
  2. nếu ko có board ESP như a thì e thay bằng mạch TTL to rs485 hay sao ạ?

    ReplyDelete