Tương Tác Với Firebase Realtime Database Sử Dụng ESP8266 - TAPIT

1. Chuỗi JSON là gì?

JSON (JavaScript Object Notation) là một định dạng chuyển đổi dữ liệu tổ chức theo các cặp key-value kết hợp với kiểu mảng dùng để lưu trữ và truyền tải dữ liệu. Chuỗi JSON được sử dụng rất phổ biến, đặc biệt trong các ứng dụng Internet of Things liên quan đến truyền tải dữ liệu giữa client-server. Hầu hết các ngôn ngữ lập trình hiện nay đều có thể xử lý trực tiếp hoặc hỗ trợ các thư viện để xử lý chuỗi JSON.

Ví dụ một chuỗi JSON lưu trữ thông tin của nhân viên:

JavaScript { "nhanvien": [ { "ho":"Tran Van", "ten":"A", "diachi": "Danang" }, { "ho":"Nguyen Van", "ten":"B", "diachi": "Quangnam" }, ] }
123456789101112131415 {"nhanvien":[{"ho":"Tran Van","ten":"A","diachi":"Danang"},{"ho":"Nguyen Van","ten":"B","diachi":"Quangnam"},]}

Các quy tắc để tạo nên chuỗi JSON:

  • Chuỗi JSON được bao bởi cặp dấu {}.
  • Mỗi thông tin dữ liệu đều có 2 phần là key-value nằm bên trong cặp dấu “” là:  "key" : "value"
    1 "key":"value"
  • Các phần tử được phân tách với nhau bởi dấu phẩy.
  • Mảng được bao bọc bởi dấu ngoặc vuông.

2. Thư viện ArduinoJSON

Arduino JSON là một thư viện C++ giúp làm việc với chuỗi JSON dùng cho các nền tảng phần cứng chạy trên môi trường Arduino IDE như: các board Arduino, các chip của hãng Espressif (ESP8266, ESP32…), Wemos board (D1, D1 mini), TI board (MSP430)…. Chức năng chính của thư viện Arduino JSON là đọc dữ liệu hay giải mã một chuỗi JSON (Serialize) và ghi dữ liệu hay mã hóa để tạo thành một chuỗi JSON (Deserialize).

Link download thư viện ArduinoJson phiên bản 5.13.1 ở đây.

Sau khi download và cài đặt thư viện vào Arduino, các bạn có thể thử nghiệm cả hai chức năng trên bằng cách sử dụng các example của thư viện:

  • Giải mã chuỗi JSON: File/Examples/ArduinoJson/JsonParserExample
  • Mã hóa chuỗi JSON: File/Examples/ArduinoJson/JsonGeneratorExample

Tài liệu hướng dẫn cách sử dụng thư viện các bạn có thể tham khảo ở đây. 

3. Google Firebase Realtime Database

Firebase Realtime Database (FRD) là một dịch vụ cơ sở dữ liệu của Google. Dữ liệu được lưu trữ dưới dạng cây JSON và đồng bộ hóa thời gian thực đến nhiều client khác nhau. FRD là một nền tảng được sử dụng rất phổ biến hiện nay trong các ứng dụng Internet of Things vì nhiều ưu điểm: lưu trữ dạng chuỗi JSON dễ tương tác, là cloud-hosted (dữ liệu toàn bộ được lưu trữ trên cloud của Google), hỗ trợ SDK đầy đủ cho rất nhiều nền tảng khác nhau như iOS, Android, Web, C++….

Ngoài các bộ SDK hỗ trợ các nền tảng riêng biệt ở trên, một client bất có thể sử dụng REST API để truy vấn với FRD bằng cách gửi các HTTP request từ phía client. Nhìn chung, việc truy vấn với FRD được chia thành 2 loại chính:

  • Save Data: gồm 3 phương thức truy vấn để lưu trữ dữ liệu vào FRD là PUT, PATCH, POST và 1 phương thức để xóa dữ liệu từ FRD là DELETE.
  • Retrieve Data: gồm 1 phương thức để đọc dữ liệu từ FRD là GET.

Giải thích các phương thức lưu trữ dữ liệu:

  • PUT: ghi một dữ liệu mới hoặc thay thế một dữ liệu vào một nhánh được chỉ định đường dẫn, ví dụ: key1/key2/key3/<value>. Lưu ý cách sử dụng PUT là chỉ nên dùng để ghi mới một <value> hoặc thay thế một <value> ở nhánh cuối cùng (tức nó không phải là key của nhánh con nào nữa), nếu không PUT sẽ xóa đi tất cả dữ liệu cũ ở đường dẫn được chỉ định.
  • PATCH: cập nhật dữ liệu của key cụ thể vào nhánh theo đường dẫn được chỉ định nhưng không xóa các dữ liệu của nhánh. Lưu ý khi dùng là PATCH chỉ hiệu lực đối với các giá trị truyền vào có dạng chuỗi JSON {“key” : {….}}, nghĩa là phải chỉ định key đi kèm chứ không giống như PUT dữ liệu có thể có hoặc không có key.
  • POST: thêm dữ liệu vào nhánh của cây JSON tuy nhiên khi sử dụng POST thì Firebase Client sẽ tự động tạo ra một key (key này là duy nhất) để lưu trữ dữ liệu.

Firebase Realtime Database Arduino Library cho ESP8266

Download thư viện Firebase-ESP8266 ở đây.

Thư viện này cung cấp các hàm rất trực quan giúp người dùng thực hiện các thao tác với FRD theo các phương thức REST API cung cấp như get, set, post, patch và delete dễ dàng hơn trên nền tảng ESP8266.

Khởi tạo: Trước khi sử dụng các hàm trong thư viện này phải thực hiện các khai báo và khởi tạo đối tượng như sau:

Arduino //1. Include Firebase ESP8266 library (this library) #include "FirebaseESP8266.h" //2. Include ESP8266WiFi.h and must be included after FirebaseESP8266.h #include <ESP8266WiFi.h> //3. Declare the Firebase Data object in global scope FirebaseData firebaseData; //4. Setup Firebase credential in setup() Firebase.begin("yout_project_id.firebaseio.com", "your_Firebase_database_secret");
1234567891011 //1. Include Firebase ESP8266 library (this library)#include "FirebaseESP8266.h" //2. Include ESP8266WiFi.h and must be included after FirebaseESP8266.h#include <ESP8266WiFi.h> //3. Declare the Firebase Data object in global scopeFirebaseDatafirebaseData; //4. Setup Firebase credential in setup()Firebase.begin("yout_project_id.firebaseio.com","your_Firebase_database_secret");

PUT dữ liệu vào FRD: Các kiểu dữ liệu khác nhau sẽ có các hàm khác nhau để thực hiện PUT vào Database như: setInt, setFloat, setDouble, setBool, setString, setJSON. Nếu lệnh được thực hiện thành công, hàm sẽ trả về true và ngược lại sẽ trả về false. 

Ví dụ: Firebase.setInt(firebaseData, “/Int/test”, 123)

POST dữ liệu vào FRD: tương tự như PUT ở trên thì việc POST dữ liệu cũng có các hàm khác nhau cho các kiểu dữ liệu khác nhau như: pushInt, pushFloat, pushDouble, pushBool, pushString, pushJSON. Các hàm này cũng sẽ return về true nếu thực hiện thành công và ngược lại sẽ return về false.

Ví dụ: Firebase.pushJSON(firebaseData, “/test/append”, jsonData)

PATCH dữ liệu vào FRD: không giống với 2 phương thức trên, việc patch dữ liệu chỉ hoạt động với kiểu dữ liệu là chuỗi JSON. Một số lưu ý khi làm việc với kiểu dữ liệu này: Khi PUT hoặc POST dữ liệu là chuỗi JSON vào Database thì tất cả các dữ liệu cũ ở nhánh được ghi vào sẽ bị xóa và thay vào đó là dữ liệu mới (ghi đè). Phương thức PATCH sinh ra để ghi dữ liệu là chuỗi JSON vào Database mà không ghi đè lên dữ liệu cũ. Các hàm hỗ trợ phương thức này là updateNode và updateNodeSilent.

Ví dụ: Firebase.updateNode(firebaseData, “/test/update”, updateJSONData)

DELETE dữ liệu trong FRD: hàm thực hiện việc delete dữ liệu là deleteNode. Hàm sau đây sẽ xóa tất cả dữ liệu tại đường dẫn “/test/append”

Ví dụ: Firebase.deleteNode(firebaseData, “/test/append”);

GET dữ liệu từ FRDtương tự như các phương thức trên, sẽ có các hàm tương ứng với các kiểu dữ liệu khác nhau cần truy vấn là: getInt, getFlot, getDouble, getBool, getString, getJSON. Các hàm này sẽ trả về true khi thực hiện thành công và ngược lại sẽ trả về false. Dữ liệu được GET về sẽ được thực hiện thông qua đối tượng firebaseData.

Sau đây là một ví dụ về việc dùng thư viện ArduinoJson để tạo một chuỗi JSON đơn giản sau đó lưu vào FRD bằng phương thức PUT sau đó GET dữ liệu về và giải mã chuỗi JSON.

Chuỗi JSON sử dụng là:

CSS { "id":"001", "firstName":"John", "lastName":"Peter", "address": "quangnam" }
123456 {"id":"001","firstName":"John","lastName":"Peter","address":"quangnam"}

Chương trình:

Arduino #include <ArduinoJson.h> #include "FirebaseESP8266.h" #include <ESP8266WiFi.h> #define FIREBASE_HOST "YOUR_FIREBASE_PROJECT.firebaseio.com" #define FIREBASE_AUTH "YOUR_FIREBASE_DATABASE_SECRET" #define WIFI_SSID "YOUR_WIFI_AP" #define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" FirebaseData firebaseData; void setup() { Serial.begin(115200); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Connecting to Wi-Fi"); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(300); } Serial.println(); Serial.print("Connected with IP: "); Serial.println(WiFi.localIP()); Serial.println(); Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH); StaticJsonBuffer<200> jsonBuffer; JsonObject& root = jsonBuffer.createObject(); root["id"] = "001"; root["firstName"] = "John"; root["lastName"] = "Peter"; root["address"] = "quangnam"; root.prettyPrintTo(Serial); Serial.println(); String jsonStr = ""; root.printTo(jsonStr); String path = "/TAPIT_IOT_GROUP"; if (Firebase.setJSON(firebaseData, path + "/Test", jsonStr)) { Serial.println("PUT JSON --------------------"); Serial.println("PASSED"); Serial.println("PATH: " + firebaseData.dataPath()); Serial.println("TYPE: " + firebaseData.dataType()); Serial.println("ETag: " + firebaseData.ETag()); Serial.println(); } else { Serial.println("FAILED"); Serial.println("REASON: " + firebaseData.errorReason()); Serial.println("------------------------------------"); Serial.println(); } String jsonRecvStr = ""; if (Firebase.getJSON(firebaseData, path + "/Test")) { Serial.println("GET JSON -------------------------"); Serial.println("PASSED"); Serial.println("PATH: " + firebaseData.dataPath()); Serial.println("TYPE: " + firebaseData.dataType()); Serial.println("ETag: " + firebaseData.ETag()); Serial.println("VALUE: "); jsonRecvStr = firebaseData.jsonData(); Serial.println(jsonRecvStr); } else { Serial.println("FAILED"); Serial.println("REASON: " + firebaseData.errorReason()); Serial.println("------------------------------------"); Serial.println(); } char jsonParse[jsonRecvStr.length() + 1]; for (int i = 0; i <= jsonRecvStr.length(); i++) { jsonParse[i] = jsonRecvStr[i]; } JsonObject& rootRecv = jsonBuffer.parseObject(jsonParse); if (!rootRecv.success()) { Serial.println("parseObject() failed"); return; } int id = rootRecv["id"]; Serial.println(id); const char* lastName = rootRecv["lastName"]; Serial.println(lastName); const char* address = rootRecv["address"]; Serial.println(address); } void loop() { }
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105 #include <ArduinoJson.h>#include "FirebaseESP8266.h"#include <ESP8266WiFi.h> #define FIREBASE_HOST "YOUR_FIREBASE_PROJECT.firebaseio.com"#define FIREBASE_AUTH "YOUR_FIREBASE_DATABASE_SECRET"#define WIFI_SSID "YOUR_WIFI_AP"#define WIFI_PASSWORD "YOUR_WIFI_PASSWORD" FirebaseDatafirebaseData; voidsetup(){Serial.begin(115200); WiFi.begin(WIFI_SSID,WIFI_PASSWORD);Serial.print("Connecting to Wi-Fi");while(WiFi.status()!=WL_CONNECTED){Serial.print(".");delay(300);}Serial.println();Serial.print("Connected with IP: ");Serial.println(WiFi.localIP());Serial.println(); Firebase.begin(FIREBASE_HOST,FIREBASE_AUTH); StaticJsonBuffer<200>jsonBuffer;JsonObject&root=jsonBuffer.createObject();root["id"]="001";root["firstName"]="John";root["lastName"]="Peter";root["address"]="quangnam"; root.prettyPrintTo(Serial);Serial.println(); StringjsonStr="";root.printTo(jsonStr); Stringpath="/TAPIT_IOT_GROUP"; if(Firebase.setJSON(firebaseData,path+"/Test",jsonStr)){Serial.println("PUT JSON --------------------");Serial.println("PASSED");Serial.println("PATH: "+firebaseData.dataPath());Serial.println("TYPE: "+firebaseData.dataType());Serial.println("ETag: "+firebaseData.ETag());Serial.println();}else{Serial.println("FAILED");Serial.println("REASON: "+firebaseData.errorReason());Serial.println("------------------------------------");Serial.println();} StringjsonRecvStr="";if(Firebase.getJSON(firebaseData,path+"/Test")){Serial.println("GET JSON -------------------------");Serial.println("PASSED");Serial.println("PATH: "+firebaseData.dataPath());Serial.println("TYPE: "+firebaseData.dataType());Serial.println("ETag: "+firebaseData.ETag());Serial.println("VALUE: ");jsonRecvStr=firebaseData.jsonData();Serial.println(jsonRecvStr);}else{Serial.println("FAILED");Serial.println("REASON: "+firebaseData.errorReason());Serial.println("------------------------------------");Serial.println();} charjsonParse[jsonRecvStr.length()+1];for(inti=0;i<=jsonRecvStr.length();i++){jsonParse[i]=jsonRecvStr[i];} JsonObject&rootRecv=jsonBuffer.parseObject(jsonParse);if(!rootRecv.success()){Serial.println("parseObject() failed");return;}intid=rootRecv["id"];Serial.println(id);constchar*lastName=rootRecv["lastName"];Serial.println(lastName);constchar*address=rootRecv["address"];Serial.println(address);} voidloop(){ }

Chúc các bạn thành công! Xem thêm:  Tổng hợp hướng dẫn Internet of Things với NodeMCU ESP8266 và ESP32

Nhóm TAPIT IoTs

Từ khóa » Thư Viện Firebase Arduino