[Node-RED] REST API with ESP8266 & Node-RED & MySQL

 

[Node-RED] REST API with ESP8266 & Node-RED & MySQL

1. REST API là gì?

REST API (còn được biết với tên gọi RESTful API) là một giao diện lập trình ứng dụng (API) mà tuân thủ các ràng buộc và quy ước kiến trúc REST được sử dụng trong việc giao tiếp giữa client và server. REST là viết tắt của REpresentational State Transfer, nó được tạo ra bởi nhà khoa học máy tính Roy Fielding.

REST API thường vẫn sử dụng giao thức HTTP/1 kèm theo các định nghĩa trước đó mà cả client và server cần tuân thủ. Ví dụ server cung cấp một API lấy số dư của một tài khoản, phương thức là GET, cần có Authorization header, response sẽ là đoạn text có phần đầu là account number, phần sau là số dư.

2. Hai thành phần trong REST API

API (Application Programming Interface) dịch ra là giao diện lập trình ứng dụng. Thật ra cái giao diện này không phải cho người dùng cuối mà dành cho các nhà phát triển (developer). Đúng hơn thì nó là cái “bề mặt” thôi, chỉ thấy được phần khai báo (tên, tham số, kiểu trả về), bộ đồ lòng body thì không biết. “Biết mặt không biết lòng” chính là API.

REST (REpresentational State Transfer) nghĩa là một đại diện cho sự chuyển đổi dữ liệu. Gì vậy trời!! Nghĩa là vầy, trong kiến trúc này client và server hoàn toàn độc lập, chúng không biết gì về nhau. Mỗi một request REST API đều không mang theo trạng thái trước đó (stateless). Như vậy để đôi bên trao đổi state, chúng sẽ buộc thông qua các resources. Các resource này chính là phần đại diện cho sự thay đổi dữ liệu.

3. Request và Response trong REST API

3.1 Methods: Phương thức

Như đã đề cập ở trên, để trao đổi state chúng sẽ cần giao tiếp resource thông qua việc gởi các request response thông qua HTTP/1. Cụ thể việc giao tiếp này là thế nào thì chúng cần chỉ định các method tương ứng bao gồm:
  • GET: Trả về một Resource hoặc một danh sách Resource.
  • POST: Tạo mới một Resource.
  • PUT: Cập nhật thông tin cho Resource (toàn bộ resource).
  • PATCH: Cật nhật thông tin cho resourse (một phần resource).
  • DELETE: Xoá một Resource.

Nếu bạn từng nghe qua CRUD APIs thì chúng đại diện cho Create, Read, Update và Delete một resource nào đó.

3.2 Header: Authentication và quy định kiểu dữ liệu trả về

Hãy nhớ rằng REST API là stateless. Mỗi một request không hề biết bất kỳ thông tin gì trước đó. Khác với khi chúng ta truy cập web, trình duyệt sẽ có session và cookie để hỗ trợ phân biệt request đấy là của ai, thông tin trước đó là gì.

Trong REST, nếu một request cần xác thực quyền truy cập, chúng sẽ phải dùng thêm thông tin trong header. Ví dụ như thông tin Authorization sẽ mang theo một user token. Hiện có 3 cơ chế Authentication chính:
  • HTTP Basic
  • JSON Web Token (JWT)
  • OAuth2

Ngoài ra Header còn giúp client chỉ định được loại content cần trả về từ server – content type. Việc này được thực hiện thông qua phần Accept trong header. Giá trị của nó thường là MIME type:
  • image — image/png, image/jpeg, image/gif
  • audio — audio/wav, audio/mpeg
  • video — video/mp4, video/ogg
  • application — application/json, application/pdf, application/xml, application/octet-stream

3.3 Status Code

Response trong REST API sẽ bao gồm một status code quy định cụ thể từng trường hơp. Các bạn có thể xem full danh sách tại đây: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status

Một số status phổ biến:
  • 200 OK – Trả về thành công cho những phương thức GET, PUT, PATCH hoặc DELETE.
  • 201 Created – Trả về khi một Resouce vừa được tạo thành công.
  • 204 No Content – Trả về khi Resource xoá thành công.
  • 304 Not Modified – Client có thể sử dụng dữ liệu cache, resource server không đổi gì.
  • 400 Bad Request – Request không hợp lệ
  • 401 Unauthorized – Request cần có xác thực.
  • 403 Forbidden – bị từ chối không cho phép.
  • 404 Not Found – Không tìm thấy resource từ URI
  • 405 Method Not Allowed – Phương thức không cho phép với user hiện tại.
  • 410 Gone – Resource không còn tồn tại, Version cũ đã không còn hỗ trợ.
  • 415 Unsupported Media Type – Không hỗ trợ kiểu Resource này.
  • 422 Unprocessable Entity – Dữ liệu không được xác thực
  • 429 Too Many Requests – Request bị từ chối do bị giới hạn

Ví dụ giao tiếp REST API với ESP8266

Code Node-RED

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
[
    {
        "id": "62d71a1a4ab2b6cf",
        "type": "mysql",
        "z": "ddb9aa4cd0f2e235",
        "mydb": "b31d23b620e5cf4a",
        "name": "",
        "x": 830,
        "y": 120,
        "wires": [
            [
                "f7738bad014f0fd0"
            ]
        ]
    },
    {
        "id": "0e946e589a868172",
        "type": "function",
        "z": "ddb9aa4cd0f2e235",
        "name": "",
        "func": "// var temp = msg.req.params.temp;\n// var humi = msg.req.params.humi;\n// var time = msg.req.params.time;\n// var query = `INSERT INTO data_log (Temp, Humi, Time) VALUES (${temp}, ${humi}, ${time})`;\n// msg.topic = query;\n// return msg;\nvar query = `INSERT INTO data_log (Temp, Humi, Time) VALUES (:Temp, :Humi, :Time)`;\nmsg.topic = query;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 640,
        "y": 120,
        "wires": [
            [
                "62d71a1a4ab2b6cf"
            ]
        ]
    },
    {
        "id": "1b325e7e50a72b90",
        "type": "http in",
        "z": "ddb9aa4cd0f2e235",
        "name": "",
        "url": "/api/esp8266/data-log",
        "method": "get",
        "upload": false,
        "swaggerDoc": "",
        "x": 270,
        "y": 120,
        "wires": [
            [
                "0e946e589a868172"
            ]
        ]
    },
    {
        "id": "f7738bad014f0fd0",
        "type": "http response",
        "z": "ddb9aa4cd0f2e235",
        "name": "",
        "statusCode": "",
        "headers": {},
        "x": 990,
        "y": 120,
        "wires": []
    },
    {
        "id": "cd9c939114cd6bea",
        "type": "http in",
        "z": "ddb9aa4cd0f2e235",
        "name": "",
        "url": "/api/esp8266/data-log",
        "method": "post",
        "upload": false,
        "swaggerDoc": "",
        "x": 280,
        "y": 160,
        "wires": [
            [
                "a20fedba3b1baac8"
            ]
        ]
    },
    {
        "id": "34c735563d53d034",
        "type": "function",
        "z": "ddb9aa4cd0f2e235",
        "name": "",
        "func": "var query = `INSERT INTO data_log (Temp, Humi, Time) VALUES (:Temp, :Humi, :Time)`;\nmsg.topic = query;\nreturn msg;",
        "outputs": 1,
        "noerr": 0,
        "initialize": "",
        "finalize": "",
        "libs": [],
        "x": 640,
        "y": 160,
        "wires": [
            [
                "62d71a1a4ab2b6cf"
            ]
        ]
    },
    {
        "id": "a20fedba3b1baac8",
        "type": "json",
        "z": "ddb9aa4cd0f2e235",
        "name": "",
        "property": "payload",
        "action": "obj",
        "pretty": false,
        "x": 490,
        "y": 160,
        "wires": [
            [
                "34c735563d53d034"
            ]
        ]
    },
    {
        "id": "b31d23b620e5cf4a",
        "type": "MySQLdatabase",
        "name": "",
        "host": "127.0.0.1",
        "port": "3306",
        "db": "test",
        "tz": "+7:00",
        "charset": "UTF8",
        "credentials": {}
    }
]
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Code ESP8266

////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClient.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

const char *ssid = "Phong 3";
const char *password = "0392071213";

String serverName = "http://192.168.1.25:1880/api/esp8266/data-log";
unsigned long lastTime = 0;
unsigned long timerDelay = 5000;

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

String getTime()
{
timeClient.update();

time_t epochTime = timeClient.getEpochTime();

String formattedTime = timeClient.getFormattedTime();

// Get a time structure
struct tm *ptm = gmtime((time_t *)&epochTime);
int monthDay = ptm->tm_mday;
int currentMonth = ptm->tm_mon + 1;
int currentYear = ptm->tm_year + 1900;
String currentDate = String(currentYear) + "-" + String(currentMonth) + "-" + String(monthDay);
return currentDate + " " + formattedTime;
}

String getTimeURL()
{
timeClient.update();

time_t epochTime = timeClient.getEpochTime();

String formattedTime = timeClient.getFormattedTime();

// Get a time structure
struct tm *ptm = gmtime((time_t *)&epochTime);
int monthDay = ptm->tm_mday;
int currentMonth = ptm->tm_mon + 1;
int currentYear = ptm->tm_year + 1900;
String currentDate = String(currentYear) + "-" + String(currentMonth) + "-" + String(monthDay);
return currentDate + "%20" + formattedTime;
}

void httpGET()
{
WiFiClient client;
HTTPClient http;

int temp = random(50, 100);
int humi = random(50, 100);
String time = getTimeURL();
String serverPath = serverName + "?Temp=" + String(temp) + "&Humi=" + String(humi) + "&Time=" + time;
Serial.println(serverPath);
http.begin(client, serverPath.c_str());

int httpResponseCode = http.GET();

if (httpResponseCode > 0)
{
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
String payload = http.getString();
Serial.println(payload);
}
else
{
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
http.end();
}

void httpPOST()
{
WiFiClient client;
HTTPClient http;

http.begin(client, serverName.c_str());

int temp = random(50, 100);
int humi = random(50, 100);
String data = "{\"Temp\":" + String(temp) + ",\"Humi\":" + String(humi) + ",\"Time\":\"" + getTime() + "\"}";

int httpResponseCode = http.POST(data);

if (httpResponseCode > 0)
{
Serial.print("HTTP Response code: ");
Serial.println(httpResponseCode);
String payload = http.getString();
Serial.println(payload);
}
else
{
Serial.print("Error code: ");
Serial.println(httpResponseCode);
}
http.end();
}

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

WiFi.begin(ssid, password);
Serial.println("Connecting");
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to WiFi network with IP Address: ");
Serial.println(WiFi.localIP());

timeClient.setTimeOffset(3600 * 7); // GMT +1 = 3600
timeClient.begin();
}

void loop()
{
if ((millis() - lastTime) > timerDelay)
{
if (WiFi.status() == WL_CONNECTED)
{
// httpGET();
httpPOST();
}
else
{
Serial.println("WiFi Disconnected");
}
lastTime = millis();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Download Library


Video hướng dẫn



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

No comments:

Post a Comment