บทความการใช้งานเริ่มต้น ESP8266 NodeMCU และการใช้งาน Application ต่างๆ ตอนที่ 5 Web Server

บทความการใช้งานเริ่มต้น ESP8266 NodeMCU และการใช้งาน Application ต่างๆ

ตอนที่ 5 Web Server

01

>>> Download Example Code <<<

         Web Server คือ Server ที่ให้บริการเว็บไซต์แก่ Client ที่เข้ามาเรียกขอหน้าเว็บโดยใช้ Hypertext Transfer Protocol (HTTP) 
         ถ้าหากเราลองมองรูปแบบการทำงานของ Web Server แบบง่ายๆ Web Server ก็คือ TCP Server ที่เปิด Port 80 เอาไว้เพื่อคอยทำหน้าที่รอรับ การร้องขอข้อมูลจาก Client ซึ่งในที่นี้คือ Web browser โดยใช้ Protocol แบบ HTTP เมื่อ Web Server ได้รับการร้องขอก็จะส่งข้อมูลที่ถูกร้องขอกลับไปยัง Client เพื่อนำไปแสดงผลนั่นเอง 
ทดลองทำ Web Server จากโปรแกรม Hercules
         จากการทดลองในเรื่อง TCP ในบทความก่อนหน้านี้ ได้แนะนำโปรแกรม Hercules ที่ใช้ช่วยในการทดสอบการทำงานของ TCP ไปแล้วนั้น จะเห็นได้ว่าโปรแกรม Hercules สามารถใช้งานได้ทั้งในโหมด TCP Client และ TCP Server ในตอนต้น ผมได้กล่าวไปแล้วว่า Web Server นั้นทำงานอยู่บนพื้นฐานของ TCP Server ในการทดลองนี้ ผมจะทดลองใช้โปรแกรม Hercules เป็น TCP Server เพื่อดูว่าเมื่อ Web browser เรียกขอหน้าเว็บไซต์ไปยัง Server นั้น ที่ Server จะได้ข้อมูลอะไรมาบ้างและจะทดลองส่ง Code HTML ง่ายๆ กลับไปแสดงผลยัง Web browser
         ขั้นตอนแรก เปิดโปรแกรม Hercules ขึ้นมาแล้วไปที่ Tab TCP Server กำหนดใช้ Port 8000 (โดยปกติการใช้งานเว็บไซต์จะใช้ที่ Port 80)

จากนั้นเปิด Web browser ขึ้นมาแล้วเรียกเปิดเว็บไปที่ IP ของเครื่องเราเองที่ Port 8000 ซึ่งในที่นี้เครื่องของผมใช้หมายเลข IP 192.168.1.36 จึงเรียกหน้า web ไปที่ http://192.168.1.36:8000

45

         ที่ TCP Server ที่เราเปิด Port รอรับเอาไว้จะมีข้อมูลเข้ามา ซึ่งเป็นข้อมูลการเรียกขอหน้าเว็บไซต์จาก Web browser ให้สังเกตที่ GET / HTTP/1.1 หมายถึงต้องการเรียกขอ File ที่อยู่ใน root ซึ่งโดยปรกติแล้วการ / (root) จะหมายถึงการเรียกหน้า Home page ของเรานั่นคือ index.html นั่นเอง

GET / HTTP/1.1
Host: 192.168.1.36:8000
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.81 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: th-TH,th;q=0.8

         จากนั้น ทดลองส่ง HTML Code ตอบกลับไปดังนี้?

<html>
<body>
<h1>My First Web</h1>
<p>Test Simple Web Server</p>
</body>
</html>

         จะพบว่า HTM Code ของเราที่ส่งกลับไปได้ขึ้นไปแสดงที่ Web browser แล้ว

สามารถอ่านข้อมูลเพิ่มเติมเรื่องของ Hypertext Transfer Protocol เพิ่มเติมได้ที่ http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
ทดลองสร้าง Web Server อย่างง่ายบน Node MCU
         การทดลองนี้เป็นการนำเอา ESP8266 มาสร้างเป็น Web Server โดยลอกเลียนแบบกระบวนการทำงานของ TCP Server บนโปรแกรม Hercules แบบการทดลองก่อนหน้า และนำการร้องขอหน้าเว็บไซต์มาเป็นตัวกำหนดให้หลอด LED ติดดับ 
         ต่อวงจรดังนี้

47
#include <ESP8266WiFi.h>
#define LED D1                  //กำหนดขาที่ต่อ LED เป็นขา D1
const char* ssid = "stk";               //กำหนด SSID (อย่าลืมแก้เป็นของตัวเอง)
const char* password = "stk123456";     //กำหนด Password(อย่าลืมแก้เป็นของตัวเอง)
unsigned char status_led=0;         //กำหนดตัวแปร ที่เก็บค่าสถานะของ LED
WiFiServer server(80);              //กำหนดใช้งาน TCP Server ที่ Port 80
 
void setup() {
  Serial.begin(115200);             //เปิดใช้ Serial
  pinMode(LED, OUTPUT);         //กำหนด Pin ที่ต่อกับ LED เป็น Output
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);           //เชื่อมต่อกับ AP
  while (WiFi.status() != WL_CONNECTED)     //รอการเชื่อมต่อ
  {
        delay(500);
        Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");     //แสดงข้อความเชื่อมต่อสำเร็จ  
  server.begin();                   //เปิด TCP Server
  Serial.println("Server started");
  Serial.println(WiFi.localIP());           // แสดงหมายเลข IP ของ Server
}
 
void loop() {
  WiFiClient client = server.available();       //รอรับ การเชื่อมต่อจาก Client
  if (!client) {            //ถ้าไม่มี Client เข้ามาให้เริ่มกับไปวน loop รอรับใหม่
    return;
  }
   
  Serial.println("new client");
  while(!client.available())
  {
    delay(1);
  }
  String req = client.readStringUntil('\r');        //อ่านค่าที่ได้รับจากclient จากข้อมูลแรกถึง '\r' 
  Serial.println(req);              //แสดงค่าที่ได้รับทาง Serial
  client.flush();
  if (req.indexOf("/ledoff") != -1)         //ตรวจสอบว่า data ที่เข้ามามีข้อความ"/ledoff"
                                                                           หรือไม่  
  {
    status_led=0;                   //ถ้ามีให้กำหนดค่า ในตัวแปรใน status_led=0
    digitalWrite(LED,LOW);          //ให้ LED ดับ
    Serial.println("LED OFF");
  }
  else if(req.indexOf("/ledon") != -1)      //ตรวจสอบว่า data ที่เข้ามามีข้อความ"/ledon"
                                                                           หรือไม่
  {
    status_led=1;                   //ถ้ามีให้กำหนดค่า ในตัวแปรใน status_led=1
    digitalWrite(LED,HIGH);         //ให้ LED ติด
    Serial.println("LED ON");
  }
//เก็บ Code HTML ลงในตัวแปรสตริง web
  String web = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
  web += "<html>\r\n";
  web += "<body>\r\n";
  web += "<h1>LED Status</h1>\r\n";
  web += "<p>\r\n";
  if(status_led==1) // ตรวจเช็คสถานะของ LED ว่า On หรือ Off
      web += "LED On\r\n";
  else
      web += "LED Off\r\n";
  web += "</p>\r\n";
  web += "</body>\r\n";
  web += "</html>\r\n";
  client.print(web);    //ส่ง HTML Code ไปยัง client
}

ทดลอง Run Program 

เปิด Web Browser แล้วกำหนด url ไปที่ IP ของ NodeMCU

เรียกหน้าเว็บไปที่ xxx.xxx.xxx.xxx/ledon สังเกต >> หลอด LED จะติด

เรียกหน้าเว็บไปที่ xxx.xxx.xxx.xxx/ledoff สังเกต >> หลอด LED จะดับ

ตัวอย่างทดลองใช้ Button เปิด/ปิด LED แทนการเรียกผ่าน URL         

เพิ่ม Code HTML สร้าง Button LED On และ LED Off ลงไป

<html>
<body>
<h1>LED Status</h1>
<p>
LED Off
</p>
<p>
<a href="/ledon">
    <button>LED On</button>
</a>
</p>
<a href="/ledoff">
    <button>LED Off</button>
</a>
</body>
</html>

แก้ไข Code ในส่วนเก็บ Code HTML ลงในตัวแปรสตริง web

String web = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
  web += "<html>\r\n";
  web += "<body>\r\n";
  web += "<h1>LED Status</h1>\r\n";
  web += "<p>\r\n";
  if(status_led==1)
      web += "LED On\r\n";
  else
      web += "LED Off\r\n";
  web += "</p>\r\n";
  web += "<p>\r\n";
  web += "<a href=\"/ledon\">\r\n";
  web += "<button>LED On</button>\r\n";
  web += "</a>\r\n";
  web += "</p>\r\n";
  web += "<a href=\"/ledoff\">\r\n";
  web += "<button>LED Off</button>\r\n";
  web += "</a>\r\n";
  web += "</body>\r\n";
  web += "</html>\r\n";
  client.print(web);

ตัวอย่าง Web Server and Digital Input
         ต่อวงจรดังนี้


         ก่อนจะเขียนโปรแกรมเราลองมาดู Code HTML ที่ใช้ในการทดลองนี้กันก่อนนะครับ

<html>
<body>
<head>Read Switch
<style>
    .circle-gray,.circle-yellow     //กำหนดขนาด และ เส้นขอบของวงกลม
    {
        width: 100px;       
        height: 100px;      
        border-radius: 50%;
    }
    .circle-gray            //กำหนดสี พื้นหลังของวงกลมเป็นสีเทา 
    {
        background-color: gray
    }
    .circle-yellow          //กำหนดสี พื้นหลังของวงกลมเป็นสีเหลือง
    {
        background-color: yellow
    }
</style>  
</head>
<meta http-equiv="refresh" content="1">   //สั่งให้ Refresh หน้าpage ทุกๆ 1 วินาที 
<p>
<div class="circle-gray"></div>         //แสดงวงกลมสีเทา
<p>SW = 1</p>                   //แสดงข้อความ "SW=1"
</p>
</body>
</html>
53

         ใน Code HTML นี้ใช้ CSS สร้างวงกลม โดยมีวงกลม 2 ชื่อได้แก่ circle-gray และ circle-yellow โดยทั้ง 2 มีขนาดเท่ากัน แตกต่างกันเพียงสีพื้นหลังและสามารถเรียกใช้งานได้เช่นนี้ 
         <div class=”circle-gray”></div> => แสดงวงกลมสีเทา
         <div class=”circle-yellow”></div> => แสดงวงกลมสีเหลือง
         และ Code HTML อีกส่วนที่น่าสนใจคือ <meta http-equiv=”refresh” content=”1″> เป็นคำสั่งที่ใช้สำหรับสั่งให้หน้าเว็บ refresh ซึ่งสามารถกำหนดความถี่ในการ refresh ได้ โดยกำหนดค่า content ดังในตัวอย่าง เป็นการกำหนดให้ refresh ทุกๆ 1 วินาที
Code Program

#include <ESP8266WiFi.h>
#define SW D2                   //กำหนดรับ input จาก Switch ที่ Pin D2
const char* ssid = "stk";               //กำหนด SSID (อย่าลืมเปลี่ยนเป็นของตัวเอง)
const char* password = "stk123456";     //กำหนด password (อย่าลืมเปลี่ยนเป็นของตัวเอง)
WiFiServer server(80);              //กำหนดใช้งาน TCP Server ที่ Port 80
void setup() {
  Serial.begin(115200);
  pinMode(SW,INPUT);                //กำหนดให้ pin D2 เป็น input
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);           // เชื่อมต่อกับ AP
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  server.begin();                   //เริ่มใช้งาน TCP Server
  Serial.println("Server started");
  Serial.println(WiFi.localIP());           //แสดง IP
}
 
void loop() {
  WiFiClient client = server.available();       //รอรับ การเชื่อมต่อจาก Client
  if (!client) {                //ถ้าไม่มี Client เข้ามาให้เริ่มกับไปวน loop รอรับใหม่
    return;
  }
   
  Serial.println("new client");
  while(!client.available())
  {
    delay(1);
  }
  String req = client.readStringUntil('\r');        //อ่านค่าที่ได้รับจากclient จากข้อมูลแรกถึง '\r'
  Serial.println(req);
  client.flush();
 
// เก็บ HTML Code ลงในตัวแปร String web
  String web = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
  web += "<html>\r\n";
  web += "<body>\r\n";
  web += "<head>Read Switch\r\n";
  web += "<style>\r\n";
  web += ".circle-gray,.circle-yellow\r\n";
  web += "{width: 100px;\r\n";
  web += "height: 100px;\r\n";
  web += "border-radius: 50%;}\r\n";
  web += ".circle-gray\r\n";
  web += "{background-color: gray}\r\n";
  web += ".circle-yellow\r\n";
  web += "{background-color: yellow}\r\n";
  web += "</style>\r\n";  
  web += "</head>\r\n";
  web += "<meta http-equiv=\"refresh\" content=\"1\">";
  web += "<p>\r\n";
  unsigned char sw_input = digitalRead(SW);     //อ่านค่าจาก Switch