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

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

ตอนที่ 6 Web Client

01

>>> Download Example Code : TCP Client HTTP GET <<<

>>> Download Example Code : TCP Client HTTP GET_Cut String <<<

         Client คือ ผู้ที่เรียกขอข้อมูลที่ต้องการจากฝั่งของ server ซึ่งโดยมากในชีวิตประจำวันเราได้ใช้งาน Client อยู่กันบ่อยๆ เช่น การเปิดหน้า Website ผ่านโปรแกรม Web Browser ต่างๆ 
         ในการทดลองนี้จะใช้ ESP8266 เพื่อร้องขอข้อมูลจาก Web Server ซึ่งให้บริการ Website ต่างๆ โดยใช้ Hypertext Transfer Protocol (HTTP) เพื่อนำข้อมูลที่ได้มาประมวลผลและตัดสินใจ

การร้องขอข้อมูลโดยใช้ HTTP GET
         เปิดการเชื่อมต่อ TCP Client ไปยัง Web Server ที่ต้องการขอข้อมูลที่ Port 80 แล้วส่งคำสั่ง Request GET ตามรูปแบบของ HTTP ดังนี้

GET /index.html HTTP/1.1
Host: www.example.com

         อ่านข้อมูลเพิ่มเติมได้ที่ https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
ตัวอย่างการใช้ HTTP GET กับ google.co.th โดยใช้โปรแกรม Hercules

  • เปิดการเชื่อมต่อ TCP Client ไปที่ Server google.co.th ที่ Port 80
  • ส่ง GET / HTTP/1.1 ตามด้วย 0x0A
  • ส่ง Host: www.google.co.th ตามด้วย 0x0A , 0x0A
  • Server จะส่ง HTML Code ของหน้า page google.co.th กลับมาดังรูป
63

การรับค่าสภาพอากาศจาก openweathermap.org ด้วย HTTP GET
         ในการทดลองนี้ เราจะใช้ HTTP GET ดึงข้อมูลสภาพอากาศจากเว็บ openweathermap.org ซึ่งเป็นเว็บที่แสดงสภาพอากาศทั่วๆ ไปในแต่ละพื้นที่

openweathermap.org มี API ในการเรียกขอข้อมูลได้ง่ายและมีลูกเล่นให้ใช้งานค่อนข้างมาก โดยผู้ใช้สามารถเข้าไปศึกษาเพิ่มเติมได้ที่ http://openweathermap.org/api แต่ในที่นี้ เราขอยกตัวอย่างการใช้งานแบบง่ายๆ มาเป็นการทดสอบการใช้งานนะครับ 
         เริ่มจากการเรียกขอข้อมูลจากการกำหนด URL โดยกำหนดชื่อจังหวัดที่ต้องการทราบข้อมูล เช่น

http://api.openweathermap.org/data/2.5/weather?q=Bangkok
65

         Server จะส่งข้อมูลสภาพอากาศกลับมาในรูปแบบของ JSON
         หากผู้ใช้ต้องการให้ Server ส่งข้อมูลกลับมาในรูปแบบของ XML ก็สามารถทำได้โดยกำหนดโหมดเป็น XML เช่น?

http://api.openweathermap.org/data/2.5/weather?q=Bangkok&mode=xml
66

         จากข้อมูลที่ได้รับจาก Server จะเห็นว่ามีข้อมูลสภาพดินฟ้าอากาศส่งกลับมาให้เรามากมาย เช่น อุณหภูมิ ความชื้น ความเร็วลม แต่ข้อมูลที่ได้รับมาจะอยู่ในรูปแบบของหน่วย imperial ซึ่งเราคงจะไม่คุ้นเคยกันนัก 
         เราสามารถเปลี่ยนข้อมูลที่ได้รับมาเป็นหน่วย metric ในรูปแบบที่คุ้นเคยได้โดยการกำหนด unit ลงไป เช่น

http://api.openweathermap.org/data/2.5/weather?q=Bangkok&mode=xml&units=metric
67

ทดลองใช้ Hercules ส่งคำสั่ง GET ไปเรียกขอข้อมูลสภาพอากาศ

  • เปิดการเชื่อมต่อ TCP Client ไปที่ Server api.openweathermap.org ที่ Port 80
  • ส่ง GET /data/2.5/weather?q=Nonthaburi&mode=xml&units=metric HTTP/1.1 ตามด้วย 0x0A
  • ส่ง Host: api.openweathermap.org ด้วย 0x0A , 0x0A
  • Server จะส่ง HTML Code ของหน้า page google.co.th กลับมาดังรูป
68

ใช้ ESP8266 ร้องขอข้อมูลสภาพอากาศจาก openweathermap.org 
         การทดลองก่อนหน้านี้ได้แสดงให้เห็นการใช้คำสั่ง GET กับโปรแกรม Hercules บน PC เพื่อให้ได้เห็นรูปแบบและขั้นตอนการใช้คำสั่ง HTTP GET ซึ่งในการใช้งาน HTTP GET บน ESP8266 นั้นก็มีลำดับขั้นตอนการใช้งานเช่นเดียวกันกับบน PC นั่นเองเพียงแต่ใช้ฟังก์ชั่นในการเรียกใช้งานแทนการคลิกเมาส์

#include <ESP8266WiFi.h>
#define SERVER_PORT 80          //กำหนดใช้ Port 80
 
const char* ssid = "stk";               //กำหนด SSID
const char* password = "stk123456";     //กำหนด Password
 
const char* server_ip = "api.openweathermap.org";   //กำหนดชื่อ Server ที่ต้องการเชื่อมต่อ
String city = "Rayong"; // Bangkok,Nonthaburi       //กำหนดชื่อจังหวัดที่ต้องการขอข้อมูล
/* กำหนดค่าคำสั่ง HTTP GET */
String str_get1  = "GET /data/2.5/weather?q=";      
String str_get2  = "&mode=xml&units=metric HTTP/1.1\r\n";
String str_host = "Host: api.openweathermap.org\r\n\r\n";
 
 
 
WiFiServer server(SERVER_PORT);     //เปิดใช้งาน TCP Port 80
WiFiClient client;              //ประกาศใช้  client 
 
unsigned long previousMillis = 0;       //กำหนดตัวแปรเก็บค่า เวลาสุดท้ายที่ทำงาน    
const long interval = 10000;            //กำหนดค่าตัวแปร ให้ทำงานทุกๆ 10 วินาที
 
void setup() 
{
    Serial.begin(115200);
    WiFi.begin(ssid, password);         //เชื่อมต่อกับ AP
   
     while (WiFi.status() != WL_CONNECTED)  //ตรวจเช็ค และ รอจนเชื่อมต่อ AP สำเร็จ
    {
            delay(500);
            Serial.print(".");
    }
   
    Serial.println("");
    Serial.println("WiFi connected");  
    Serial.println("IP address: ");         
    Serial.println(WiFi.localIP());         //แสดง IP Address ที่ได้   
}
 
void loop() 
{
    while(client.available())               //ตรวจเช็คว่ามีการส่งค่ากลับมาจาก Server หรือไม่        
    {
          String line = client.readStringUntil('\n');       //อ่านค่าที่ Server ตอบหลับมาทีละบรรทัด
          Serial.println(line);             //แสดงค่าที่ได้ทาง Serial Port
    }
  unsigned long currentMillis = millis();           //อ่านค่าเวลาที่ ESP เริ่มทำงานจนถึงเวลาปัจจุบัน
  if(currentMillis - previousMillis >= interval)     /*ถ้าหากเวลาปัจจุบันลบกับเวลาก่อหน้านี้ มีค่า
                            มากกว่าค่า interval ให้คำสั่งภายใน if ทำงาน*/ 
  {
        previousMillis = currentMillis;         /*ให้เวลาปัจจุบัน เท่ากับ เวลาก่อนหน้าเพื่อใช้
                            คำนวณเงื่อนไขในรอบถัดไป*/
        Client_Request();               //เรีกใช้งานฟังก์ชั่น Client_Request 
  }   
    
}
 
void Client_Request()
{
    Serial.println("Connect TCP Server");
    int cnt=0;
    while (!client.connect(server_ip,SERVER_PORT))  //เชื่อมต่อกับ Server และรอจนกว่าเชื่อมต่อสำเร็จ
    {
          Serial.print(".");
          delay(100);
          cnt++;
          if(cnt>50)                 //ถ้าหากใช้เวลาเชื่อมต่อเกิน 5 วินาที ให้ออกจากฟังก์ชั่น
          return;
    } 
    Serial.println("Success");
    delay(500);
   client.print(str_get1+city+str_get2+str_host);       //ส่งคำสั่ง HTTP GET ไปยัง Server
   Serial.print(str_get1+city+str_get2+str_host);
   delay(100);
}

ผลการทดลอง

69

ตัวอย่างการนำข้อมูลที่ได้รับจาก openweathermap.org ไปใช้งาน
         จากการทดลองก่อนหน้านี้ เราสามารถรับ Data ที่ได้รับจาก Server มาแล้วก็จะมาถึงขั้นตอนการคัดแยกเอาข้อมูลที่เราสนใจหรือข้อมูลที่เราต้องการไปใช้งาน โดยเราต้องเข้าใจก่อนครับว่า Data ที่เรารับมานั้นยังอยู่ในรูปแบบของ String คือเป็นข้อความ การนำข้อมูลเหล่านี้ไปใช้จึงสามารถทำได้โดยการตัด String ในส่วนที่เราสนใจไปใช้งานซึ่ง Arduino ก็ได้มีเครื่องมืออำนวยความสะดวกในการจัดการกับข้อมูล String นี้อยู่แล้วครับ สามารถศึกษาเพิ่มเติมได้จาก https://www.arduino.cc/en/Reference/StringObject
         ตัวอย่างการตัดข้อมูลในส่วนของชื่อจังหวัด อุณหภูมิ ความชื้น และความกดอากาศมาแสดงผล

#include <ESP8266WiFi.h>
#define SERVER_PORT 80          //กำหนดใช้ Port 80
 
const char* ssid = "stk";               //กำหนด SSID
const char* password = "stk123456";     //กำหนด Password
 
const char* server_ip = "api.openweathermap.org";   //กำหนดชื่อ Server ที่ต้องการเชื่อมต่อ
String city = "Rayong"; // Bangkok,Nonthaburi       //กำหนดชื่อจังหวัดที่ต้องการขอข้อมูล
/* กำหนดค่าคำสั่ง HTTP GET */
String str_get1  = "GET /data/2.5/weather?q=";      
String str_get2  = "&mode=xml&units=metric HTTP/1.1\r\n";
String str_host = "Host: api.openweathermap.org\r\n\r\n";
 
 
 
WiFiServer server(SERVER_PORT);     //เปิดใช้งาน TCP Port 80
WiFiClient client;              //ประกาศใช้  client 
 
unsigned long previousMillis = 0;       //กำหนดตัวแปรเก็บค่า เวลาสุดท้ายที่ทำงาน    
const long interval = 10000;            //กำหนดค่าตัวแปร ให้ทำงานทุกๆ 10 วินาที
 
void setup() 
{
    Serial.begin(115200);
    WiFi.begin(ssid, password);         //เชื่อมต่อกับ AP
   
     while (WiFi.status() != WL_CONNECTED)  //ตรวจเช็ค และ รอจนเชื่อมต่อ AP สำเร็จ
    {
            delay(500);
            Serial.print(".");
    }
   
    Serial.println("");
    Serial.println("WiFi connected");  
    Serial.println("IP address: ");         
    Serial.println(WiFi.localIP());         //แสดง IP Address ที่ได้   
}
 
void loop() 
{
    while(client.available())               //ตรวจเช็คว่ามีการส่งค่ากลับมาจาก Server หรือไม่
    {
          String line = client.readStringUntil('\n');       //อ่านค่าที่ Server ตอบหลับมาทีละบรรทัด
          String get_data = cut_string(line,"city","name"); //ตัด string ข้อมูลส่วนชื่อจังหวัด
          if(get_data != "NULL")            
          {
            Serial.println("City");
            Serial.println(get_data);
          }
          get_data = cut_string(line,"temperature","value");    //ตัด string ข้อมูลส่วน อุณหภูมิ
          if(get_data != "NULL")
          {
             Serial.println("Temperature");
             Serial.println(get_data);
          }
 
          get_data = cut_string(line,"humidity","value");       //ตัด string ข้อมูลส่วน ความชื้น
          if(get_data != "NULL")
          {
              Serial.println("Humidity");
              Serial.println(get_data);
          }
          get_data = cut_string(line,"pressure","value");       //ตัด string ข้อมูลส่วน ความดัน
          if(get_data != "NULL")
          {
              Serial.println("Pressure");
              Serial.println(get_data);
          }
          
         // Serial.print(line);
    }
  unsigned long currentMillis = millis();           //อ่านค่าเวลาที่ ESP เริ่มทำงานจนถึงเวลาปัจจุบัน
  if(currentMillis - previousMillis >= interval)         /*ถ้าหากเวลาปัจจุบันลบกับเวลาก่อหน้านี้ มีค่า
                            มากกว่าค่า interval ให้คำสั่งภายใน if ทำงาน*/
  {
    previousMillis = currentMillis;              /*ให้เวลาปัจจุบัน เท่ากับ เวลาก่อนหน้าเพื่อใช้
                            คำนวณเงื่อนไขในรอบถัดไป*/
    Client_Request();
  }   
    
}
void Client_Request()
{
    Serial.println("Connect TCP Server");
    int cnt=0;
    while (!client.connect(server_ip,SERVER_PORT))  //เชื่อมต่อกับ Server และรอจนกว่าเชื่อมต่อสำเร็จ
    {
          Serial.print(".");
          delay(100);
          cnt++;
          if(cnt>50)                 //ถ้าหากใช้เวลาเชื่อมต่อเกิน 5 วินาที ให้ออกจาก ฟังก์ชั่น
          return;
    } 
    Serial.println("Success");
    delay(500);
   client.print(str_get1+city+str_get2+str_host);       //ส่งคำสั่ง HTTP GET ไปยัง Server
   Serial.print(str_get1+city+str_get2+str_host);
   delay(100);
}
 
String cut_string(String input,String header,String get_string)
{
    if (input.indexOf(header) != -1)    //ตรวจสอบว่าใน input มีข้อความเหมือนใน header หรือไม่
    {
        int num_get = input.indexOf(get_string);  //หาตำแหน่งของข้อความ get_string ใน input
        if (num_get != -1)      //ตรวจสอบว่าตำแหน่งที่ได้ไม่ใช่ -1 (ไม่มีข้อความ get_string ใน input)
        {               
            int start_val = input.indexOf("\"",num_get)+1;  // หาตำแหน่งแรกของ " 
            int stop_val = input.indexOf("\"",start_val);   // หาตำแหน่งสุดท้ายของ "
            return(input.substring(start_val,stop_val));    //ตัดเอาข้อความระหว่า "แรก และ "สุดท้าย  
        }
        else
        {
          return("NULL");   //Return ข้อความ NULL เมื่อไม่ตรงเงื่อนไข
        }
    }
     
    return("NULL"); //Return ข้อความ NULL เมื่อไม่ตรงเงื่อนไข
}

คำอธิบายเพิ่มเติมฟังก์ชั่น cut_string

  • ฟังก์ชั่นนี้จะรับ Data ที่ได้จาก Server มาทีละบรรทัดผ่านทางตัวแปร String input
  • ยกตัวอย่าง Data ที่รับมาจาก Server เป็นบรรทัดที่แสดงอุณหภูมิ
<temperature value="27.59" min="26.67" max="29" unit="celsius"/>
  • ยกตัวอย่างการใช้งานฟังก์ชั่นดังนี้ cut_string(line,”temperature”,”value”);
  • ในขณะนี้จึงสมมติให้ตัวแปร String input คือข้อความบรรทัดที่แสดงอุณหภูมิ
  • ภายในฟังก์ชั่น cut_string จะตรวจเช็คว่ามีข้อความ “temperature” ซึ่งถูกเก็บในตัวแปร header มีอยู่ใน input หรือไม่ (ถ้าไม่มีจะได้ตำแหน่งเป็น -1)
  • หากมีก็จะเช็คว่าตำแหน่งของข้อความ “value” ซึ่งเก็บอยู่ในตัวแปร get_string อยู่ที่ตัวอักษรที่เท่าไรภายใน input (ถ้าไม่มีจะได้ตำแหน่งเป็น -1)
  • ถ้าหากมีข้อความ value อยู่ใน input (ค่าตำแหน่งไม่ใช่ -1)
<temperature value="27.59" min="26.67" max="29" unit="celsius"/>
  • ให้ขาตำแหน่งของเครื่องหมาย “ ทั้ง 2 อัน
  • เมื่อได้ตำแหน่งของ “ ทั้งสองอันแล้วก็ใช้ฟังก์ชั่น String.substring ตัดเอาข้อความระหว่างเครื่องหมาย “ ออกมาก็จะได้ค่า Temp
  • ในรูปแบบเดียวกัน เราสามารถใช้ฟังก์ชั่นนี้ตัดข้อมูลในจุดอื่นๆ ที่เราต้องการได้เช่นกัน เช่น
  • ต้องการค่า min >> cut_string(line,”temper