07 November 2016

[Microcontroller] มาลองใช้งาน Firebase Realtime Database กับ ESP8266 ด้วย Arduino กันเถอะ

Updated on


        ไม่ได้เขียนบทความเกี่ยวกับไมโครคอนโทรลเลอร์มานานมากแล้วเหมือนกันแฮะ เพราะช่วงหลังๆมานี้แทบไม่ได้จับฮาร์ดแวร์เหล่านั้นซักเท่าไรนัก จนกระทั่งเมื่อกลางปีนี้ช่วงงาน Google I/O ก็ได้ชุดทดลองที่เป็นบอร์ดไมโครคอนโทรลเลอร์มาดูแลชั่วคราวหนึ่งชุด แต่เก็บดองไว้ครึ่งปีจนลืมไป แล้วอยู่ๆก็นึกขึ้นได้ เลยหยิบมาเล่นนี่แหละ

        คำว่า Firebase Realtime Database จะเรียกสั้นๆว่า Firebase RTDB นะจ๊ะ

        ในช่วงที่ไปงาน Google I/O 2016 ก็มีงานอย่างอื่นของทาง Google ด้วย ซึ่งหนึ่งในนั้นก็จะมีงานของ GDG ซึ่งเจ้าของบล็อกก็ไม่ได้เข้าไปหรอก แต่มีพี่ที่ไปด้วยกันเค้าเป็น GDG แล้วไปงานที่ว่านี้ ซึ่งก็จะมี Workshop เกี่ยวกับ Firebase กับบอร์ดไมโครคอนโทรเลอร์ให้ได้ลองทำกัน

        ตอนแรกเจ้าของบล็อกก็นึกว่าจะเป็น Workshop เกี่ยวกับ Brillo ซะอีก เพราะ Google เคยปูทางให้กับ IoT Platform ด้วย Brillo มาเป็นระยะเวลาหนึ่งแล้ว และเมื่อมี Firebase ชุดใหม่เพิ่มเข้ามาอีกก็น่าจะเพิ่มพลังให้​​กับ Platform ของ Google ได้อีกมากมาย

        ซึ่งใน Workshop ก็จะให้กล่องอุปกรณ์ขนาดใหญ่ ข้างในก็จะมีชุดตัวต่อ Lego แล้วก็บอร์ดไมโครคอนโทรลเลอร์และเซ็นเซอร์ต่างๆ


        แอบแปลกใจเหมือนกันว่าเอา Lego มาทำอะไรหรือว่า Lego เริ่มหันมาจับมือกับ Google? (เพราะปกติ Lego มีชุดหุ่นยนต์สำหรับเขียนโปรแกรมเป็นของตัวเองอยู่แล้ว) มารู้ทีหลังก็คือเป็นตัวช่วยดึงดูดความสนใจเฉยๆ เพราะ Workshop จะแบ่งกลุ่มละ 2 คน ช่วยกันทำ ซึ่งคนแรกก็ต้องทำในส่วนของบอร์ดไมโครฯ และอีกคนก็นั่งต่อ Lego นั่นเอง....

        พอมานั่งดูชิ้นส่วน Lego ก็พบว่ามันคือชุด The Big Bang Theory นี่นาาาาาาา เข้าใจเลือกมาเหมือนกันนะ ให้ความรู้สึกเนิร์ดๆดี ฮาๆ



        ก็เลยใช้เวลาไปสองชั่วโมงเพื่อต่อจนเสร็จ


        ก็ขอจบบทความรีวิวชุดตัวต่อ Lego : The Big Bang Theory เพียงเท่านี้ครับ ส่วนคราวหน้าจะเป็นชุดไหน น่าสนใจมากแค่ไหน ราคาเท่าไร ก็อย่าลืมติดตามกันนะครับ บ๊ายบายยยยยยยยย

        .
        .
        .
        .
        .
        .

ลืมไปว่าไม่ได้มารีวิว Lego...

        กลับมาที่บอร์ดไมโครฯสำหรับ Workshop นี้ และสิ่งแรกที่เจ้าของบล็อกคิดอยู่ในหัวสมองก็ประมาณว่า

        ในที่สุดก็จะได้จับ Brillo แล้ว เย้!! ได้ลองใช้กับ Firebase ด้วย เย้!!


        อ่าวเฮ้ย ทำไมเป็นบอร์ด Wio Link ของ Seeed Studio ซะงั้นล่ะ!!!

        พอไปดูรายละเอียดอีกที ก็เพิ่งจะเข้าใจว่ามันเป็น Workshop สำหรับ Firebase​ RTDB กับ Arduino Platform ซึ่งไม่ได้เกี่ยวอะไรกับ Brillo เล้ยยยยย (ในงาน Google I/O 2016 ก็ไม่มีอะไรที่เกี่ยวกับ Brillo เปิดตัวด้วยนะ...)

รู้จักกับ Wio Link และเหล่าผองเพื่อน

        Wio Link เป็นบอร์ดไมโครคอนโทรลเลอร์ที่ใช้ชิป ESP8266 โดยที่สามารถใช้งานได้เลยเนื่องจาก Wio Link ได้เตรียมทุกๆอย่างไว้ให้พร้อมเกือบหมดแล้ว ไม่ว่าจะจุดต่อ JST 2.0, ปุ่มควบคุมการทำงาน จุดต่อ Micro USB และจุดต่อแบตเตอรีภายนอกที่สามารถชาร์จได้ด้วย

        ก่อนจะไปต่อ ขอพูดถึง ESP8266 นิดหน่อยละกัน เผื่อมีผู้ที่หลงเข้ามาอ่านคนใดที่ไม่รู้จัก

        ย้อนไปราวๆ 3-4 ปี (นับจากวันที่เขียนบทความนี้) บริษัท Espressif Systems ได้พัฒนาชิป ESP8266 ขึ้นมาแล้วชิปก็ได้ถูกนำไปทำเป็นโมดูลโดยบริษัท AI-Thinker เพื่อขายให้กับเหล่านักพัฒนาทั่วๆโลกอีกทีหนึ่งในราคาไม่ถึงสองร้อย (เมื่อก่อนพวก WiFi Module ที่มีอยู่ในตลาด ราคาถูกสุดก็ประมาณพันกว่าๆแล้ว) ซึ่งนับจากนั้นก็ถือว่าเป็นจุดเปลี่ยนที่ทำให้ก้าวเข้าสู่ยุคของ IoT ได้ง่ายขึ้น (ทั้งถูก ฉลาด และเป็น Access Point ได้ด้วยล่ะ!!)


        ถึงแม้ว่าเดิมที ESP8266 นั้นทำหน้าที่เป็น WiFi Module ก็จริง แต่ตัวมันเองแท้จริงแล้วก็คือบอร์ดไมโครคอนโทรลเลอร์ตัวหนึ่งที่สามารถเข้าไปเขียนโปรแกรมสั่งงานมันได้ จึงมีการเอาเจ้า ESP8266 ไปใช้เป็นบอร์ดไมโครคอนโทรลเลอร์โดยตรงซะเลย ซึ่งก็มีการทำออกมาเป็นบอร์ดต่างๆ และ Wio Link ก็คือหนึ่งในนั้นนั่นเอง และนอกจากนี้ยังมีการพัฒนาให้ ESP8266 สามารถใช้งานได้สะดวกขึ้นและแพร่หลายมากขึ้น จนตอนนี้มันกลายเป็นบอร์ดที่สามารถเขียนด้วย Arduino IDE ได้แล้ว

        ดังนั้นในบทความนี้ก็จะเป็นการใช้ ESP8266 เชื่อมต่อกับ Firebase RTDB ด้วย Arduino IDE นั่นเอง


        และนอกจาก Wio Link แล้ว ในกล่องก็จะมี Module ต่างๆที่ใช้กับ Wio Link ไม่ว่าจะเป็น LED, Button, Light Sensor, 7-Segment, Smoke Sensor และอื่นๆมากมายที่แถมมาให้ครบชุดในกล่องนี้ เรียกได้ว่าเอาไปลองทำอะไรเล่นได้หลายๆอย่างเลยทีเดียว

        สำหรับจุดต่อ JST 2.0 ของ Wio Link จะมีทั้งหมด 6 จุดด้วยกัน ซึ่งมีเลข Pin และคุณสมบัติต่างกันดังนี้


        ถึงแม้จะดูว่ามีให้ใช้งานน้อย แต่เอาเข้าจริงเท่านี้ก็เพียงพอและครอบคลุมกับการทำงานหลายๆอย่างแล้วล่ะ

เตรียมโปรแกรมต่างๆให้พร้อม

        • ติดตั้งไดรเวอร์ของ Wio Link บนคอมพิวเตอร์ โดยดาวน์โหลดจาก Silicon Labs VCP Driver
        • ดาวน์โหลด Arduino IDE ได้ที่ Download [Arduino IDE] 
        • ติดตั้ง ESP8266 ลงใน Board Manager ของ Arduino IDE ดูขั้นตอนได้จาก Installing with Boards Manager [ESP8266]


        • ดาวน์โหลด Library สำหรับ Firebase บน Arduino ที่ FirebaseArduino Library แล้วเพิ่มเข้าไปใน Arduino IDE ให้เรียบร้อย (Sketch > Include Library > Add .ZIP Library)


การตั้งค่าใน Arduino IDE สำหรับ Wio Link

        • เลือก Board เป็น NodeMCU 1.0 (ESP-12E Module) 
        • เลือก CPU Frequency เป็น 80Mhz
        • เลือก Flash Size เป็น 4M (3M SPIFFS)
        • เลือก Upload Speed เป็น 115200
        • เลือก Port เป็นอันไหนก็ได้ที่เสียบ Wio Link มันแล้วโผล่ขึ้นมา (ของเจ้าของบล็อกเป็น /dev/cu.SLAB_USBtoUART)


        แล้วก็เพื่อความมั่นใจให้ลองสร้างโปรเจคเปล่าๆขึ้นมาแล้วกด Upload เพื่อดูว่าสามารถใช้งานได้ปกติมั้ย


        โดยปกติแล้วถ้าเป็นบอร์ด Arduino ทั่วไป เมื่อ Upload เสร็จแล้วก็จะทำงานทันที แต่สำหรับ Wio Link นั้นเวลา Upload เสร็จแล้วจะต้องกดปุ่ม Reset หนึ่งครั้งก่อนถึงจะเริ่มทำงาน ซึ่งเจ้าของบล็อกก็ไม่รู้ว่าปกติมันเป็นแบบนี้หรือป่าวนะ...

เมื่อพร้อมแล้วก็มาเริ่มกันเถอะ~

        การใช้งานคำสั่งของ Wio Link นั้นสามารถใช้งานได้ทันทีเหมือนบอร์ด Arduino เลยก็ได้ แต่ถ้าอยากจะใช้ WiFi และ Firebase ก็ให้ประกาศ ESP8266WiFi.h กับ FirebaseArduino.h ไว้ด้วยนะจ๊ะ

#include <ESP8266WiFi.h>
#include <FirebaseArduino.h>

void setup() {

}

void loop() {

}

        และถ้าอยากจะสั่งให้ต่อ WiFi ซักแห่ง ก็ให้กำหนดค่า SSID และ Password ให้ถูกต้องด้วยคำสั่งแบบนี้

#include <ESP8266WiFi.h>
#include <FirebaseArduino.h>

#define WIFI_SSID       "MyHomeWifi"
#define WIFI_PASSWORD   "1234567890"

void setup() {
  Serial.begin(9600);
  Serial.println(WiFi.localIP());
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("connecting");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("connected: ");
  Serial.println(WiFi.localIP());

  // Do something
}

void loop() {
  // Do something
}

        นึกถึงสมัยที่เจ้าของบล็อกเล่น ESP8266 ใหม่ๆ เมื่อก่อนแค่ต่อ WiFi ซักตัวก็ต้องเขียนเองแทบทั้งหมด พอกาลเวลาผ่านไป ความนิยมเริ่มมีมากขึ้น จนมีไลบรารีออกมาให้ใช้งานง่ายๆ แต่ก็ไม่คิดว่ามันจะง่ายถึงขนาดนี้... (เมื่อก่อนตอนออกมาใหม่ๆต้องสั่งงานเป็น AT Command เข้าไปโดยตรง)

        ดูสถานะการเชื่อมต่อ WiFi ผ่าน Serial Monitor ได้เลย เมื่อเชื่อมต่อสำเร็จก็จะแสดง IP Address ให้เห็นแบบนี้

การติดต่อใช้งาน Firebase RTDB

        อย่างแรกเลยจะต้องไปสร้างโปรเจคของ Firebase บน Console ให้เรียบร้อยเสียก่อน

        เมื่อสร้างเสร็จแล้วให้เก็บ Host Name ของโปรเจคนั้นๆไว้ด้วย เพราะจะต้องใช้ในโค้ด ซึ่ง Host Name ไม่จำเป็นต้องมี https:// กับ / ปิดท้าย


        โปรเจคของเจ้าของบล็อกตั้งชื่อว่า Wio Link โดยมี Host Name เป็น wio-link-d6bxx.firebaseiocom (ของแต่ละคนจะไม่เหมือนกันนะ)

        และในโปรเจคจะต้องสร้าง Token หรือ Secret Key ขึ้นมาเพื่อให้ Wio Link สามารถเข้าไปใช้งาน Firebase RTDB ได้

        โดยเข้าไปในหน้าโปรเจคแล้วกดปุ่ม Settings (ปุ่มรูปเฟือง) > Project Settings


        จากนั้นเลือกไปที่ Service Accounts > Database Secret


        หมายเหตุ - จะเห็นข้อความสีแดงๆแจ้งเตือนบางอย่าง โดยมีเนื้อหาว่า

        "Database secrets are currently deprecated and use a legacy Firebase token generator. Update your source code with the Firebase Admin SDK."

        นั่นก็เพราะว่า Realtime Database ของ Firebase เวอร์ชันล่าสุดนั้นได้เลิกใช้ Database Secrets แล้ว โดยจะให้ใช้ Firebase Admin SDK แทน เพื่อใช้เป็น Private Key แทน Secret Key แบบเก่า แต่ทว่ายังไม่ได้รองรับกับการใช้งานบน Arduino ดังนั้นจึงให้ใช้เป็น Secret Key ไปก่อนจนกว่าเวอร์ชันใหม่นี้จะมีวิธีอื่นที่รองรับการใช้งานบน Arduino ด้วย (ถ้าใช้งานเป็นเรื่องเป็นราวก็ควรอัปเดตข้อมูลเรื่องนี้บ่อยๆ)

        ให้ดูที่ข้างล่างของเมนู Database Secrets จะเห็นว่าของเจ้าของบล็อกมี Secret Key อยู่แล้วหนึ่งตัว ถ้าของผู้ที่หลงเข้ามาอ่านยังไม่มีให้กดที่ปุ่ม Add Secret เพื่อสร้างขึ้นมาได้เลย


        กดปุ่ม Show เพื่อให้แสดง Secret Key แล้วคัดลอกเก็บไว้ เพราะจะต้องเอาไปใช้ในโค้ดเช่นกัน


        เตรียมพร้อมเสร็จเรียบร้อย เท่านี้ก็เหลือในส่วนของโค้ดบน Wio Link แล้วล่ะ

        ถึงขั้นตอนนี้ผู้ที่หลงเข้ามาอ่านจะต้องมี Host Name และ Secret Key ของโปรเจค Firebase นั้นๆ เก็บไว้เพื่อเอาไปใช้งานในโค้ดนะ

มาดูโค้ดใน FirebaseArduino Library กันต่อ

        สำหรับการเริ่มใช้งาน Firebase  จะต้องกำหนด Host Name และ Secret Key แบบนี้ก่อนทุกครั้ง (และควรต่อ Internet ได้ด้วย)

...

#define FIREBASE_HOST "wio-link-d6bXX.firebaseio.com"
#define FIREBASE_KEY "qUohEtHyvCcOzgxq95ZheaVsaMePi2P5FDd8XXXX"

void setup() {
  
  ...

  Firebase.begin(FIREBASE_HOST, FIREBASE_KEY);
  
  // Do something
}

void loop() {
  // Do something
}

        แค่นี้เองหรอ?

        มันก็มีแค่นี้ล่ะฮะ ใช้คำสั่ง begin แล้วกำหนด Host Name กับ Secret Key เข้าไป เพียงแค่นี้ก็สามารถใช้งานได้แล้ว

        ทีนี้เรามาดูคำสั่งต่างๆของ Library ตัวนี้กันเสียหน่อยว่ามีคำสั่งอะไรบ้าง

void set(String path, JsonVariant value)
void setBool(String path, String value)
void setInt(String path, String value)
void setFloat(String path, String value)
void setString(String path, String value)

String push(String path, JsonVariant value)
String pushBool(String path, String value)
String pushInt(String path, String value)
String pushFloat(String path, String value)
String pushString(String path, String value)

FirebaseObject get(String path)
bool getBool(String path)
int getInt(String path)
float getFloat(String path)
String getString(String path)

void remove(String path)
void stream(String path)
bool available()
bool success()
bool failed()
String error()
FirebaseObject readEvent()

การกำหนดค่าลงใน Database

        สามารถทำได้ 2 วิธีด้วยกันคือ Set กับ Push เหมือนกับบน Platform อื่นๆเลย

        ซึ่งการ Set นั้นคือการกำหนดค่าบางอย่างลงไปในฐานข้อมูล ยกตัวอย่างเช่น

Firebase.set("name", "Wio Link 001");


        ซึ่งการใช้คำสั่ง Set เพื่อกำหนด Path เดิมซ้ำๆก็จะเป็นการกำหนดค่าแทนที่ของเก่าตลอดเวลานั่นเอง

        โดยคำสั่ง Set จะมี 5 แบบคือกำหนดข้อมูลเป็นแบบ Boolean, Integer, Float, String และ JSON (ใช้เป็น JsonVariant) แต่ในขณะเดียวกัน ผู้ที่หลงเข้ามาอ่านก็สามารถกำหนดค่า Boolean, Integer, Float หรือ String  ลงในคำสั่ง set(String path, JsonVariant value) โดยตรงได้เลยเช่นกัน เพราะคำสั่งมันฉลาดพอที่จะจัดการได้ในบางกรณี

        ดังเช่นตัวอย่างก่อนหน้าที่เจ้าของบล็อกกำหนดค่า String ลงในคำสั่ง set(String path, JsonVariant value) โดยตรงเลย แทนที่จะกำหนดผ่านคำสั่ง setString(String path, String value)

        ที่ทำเช่นนั้นได้ก็เพราะว่าเจ้าของบล็อกกำหนดค่าลงไปตรงๆ แต่ในกรณีที่โยนตัวแปร String เข้ามาก็อาจจะมีปัญหาได้ ดังนั้นทางที่ดีควรใช้คำสั่ง Set ให้ถูกต้องตามค่าที่ต้องการกำหนดลงใน Database ดีกว่านะ

String deviceName = "Wio Link 001";
Firebase.setString("name", deviceName);

        และถ้าต้องการกำหนดแบบ Child ก็ให้ระบุจาก Path ได้เลย แล้ว Firebase RTDB จะสร้างเป็น Child เข้าไปให้ตามที่กำหนดไว้

...

const String deviceNumber = "001";
const String deviceName = "Wio Link " + deviceNumber;
const int tempSensorPin = 13;

void setup() {
  ...

  Firebase.setString(deviceNumber + "/name", deviceName);
  
  pinMode(tempSensorPin, INPUT);
}

void loop() {
  int temperature = convertTemperature(digitalRead(tempSensorPin));
  Firebase.setInt(deviceNumber + "/value", temperature);
  delay(1000);
}

int convertTemperature(int value) {
  ...
}



        ดังนั้นจึงสะดวกต่อการใช้อุปกรณ์หลายๆตัวที่ใช้ Database ร่วมกัน เพราะสามารถกำหนด Child ได้เท่าที่ต้องการ จะ Child ซ้อน Child เข้าไปเรื่อยๆก็ทำได้เช่นกัน อยู่ที่ว่าผู้ที่หลงเข้ามาอ่านจะออกแบบรูปแบบการเก็บข้อมูลในฐานข้อมูลอย่างไร


การเก็บข้อมูลในรูปแบบของ JSON

        สำหรับ JSON ที่ใช้ใน FirebaseArduino Library นั้นจะเป็น Arduino JSON library อีกทีหนึ่ง ซึ่งเป็น Library ที่จะช่วยให้สามารถจัดการกับ JSON บน Arduino ได้

        เนื่องจากบน Firebase RTDB นั้น ข้อมูลที่อยู่ข้างในจะเป็นแบบ NoSQL ดังนั้นการนำ JSON เข้ามาใช้ก็จะทำให้สะดวกมากขึ้น โดยเฉพาะเวลาที่มีข้อมูลหลายตัว การโยนข้อมูลขึ้นไปทีละตัวก็ดูจะเสียเวลาเกินไปหน่อย ดังนั้นเพื่อความรวดเร็วจึงควรโยนข้อมูลเป็นก้อน JSON แล้ว Firebase RTDB จะจัดการเก็บลง Realtime Database ให้เอง

        ซึ่งการสร้าง JSON สำหรับ Firebase RTDB จะใช้วิธีแบบนี้

StaticJsonBuffer<200> jsonBuffer;
JsonObject& valueObject = jsonBuffer.createObject();
valueObject["temperature"] = 27.5;
valueObject["raw"] = 389;

        ผลลัพธ์ในรูปแบบ JSON ที่ได้จากคำสั่งข้างบน

{
  "temperature": 27.5,
  "raw": 389
}

        ถ้าต้องการซ้อนกันหลายชั้นก็ให้กำหนด JsonObject ซ้อนกันได้เลย หรือถ้าเป็น Array ก็มี JsonArray ให้ใช้ ซึ่งรายละเอียดพวกนี้เจ้าของบล็อกไม่ขออธิบาย แต่สามารถไปอ่านวิธีการใช้งานได้ที่ Arduino JSON Library - Wiki

        จากตัวอย่างคำสั่งจะเห็นว่าต้องมีการสร้าง StaticJsonBuffer ก่อน ซึ่งเจ้าของบล็อกกำหนด Buffer เป็น 200 ไป ทั้งนี้ขึ้นอยู่กับว่า JSON ที่จะใช้งาน​ซึ่งสามารถลองจำลองข้อมูล JSON ที่จะใช้งานขึ้นมาเพื่อเอาไปเช็คที่ JsonBuffer size calculator จะได้รู้ว่าต้องกำหนด Buffer ประมาณเท่าไร (อย่าลืมกำหนดเผื่อด้วยล่ะ)

        และนี่คือตัวอย่างการส่งข้อมูลเป็น JSON

...

void setup() {

  ...

  Firebase.setString(deviceNumber + "/name", deviceName);
}

void loop() {
  int rawValue = digitalRead(tempSensorPin);
  float temperature = convertTemperature(rawValue);
  
  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& valueObject = jsonBuffer.createObject();
  valueObject["temperature"] = temperature;
  valueObject["raw"] = rawValue;

  Firebase.set(deviceNumber + "/value", valueObject);
  delay(1000);
}

float convertTemperature(int value) {
  ...
}

        ผลที่ได้บน Firebase RTDB


        ก็ให้เลือกใช้รูปแบบข้อมูลตามความเหมาะสมกับงานของผู้ที่หลงเข้ามาอ่านนะ

การเพิ่มข้อมูลลงใน Database

        ก่อนหน้านี้เจ้าของบล็อกได้ลองใช้คำสั่ง Set ซึ่งเป็นคำสั่งสำหรับกำหนดข้อมูลบางอย่างลงใน Database และถ้ามีข้อมูลก่อนหน้าอยู่แล้วก็จะเป็นการกำหนดทับลงไป ซึ่งอาจจะไม่เหมาะกับการใช้งานในบางรูปแบบ เช่น ต้องการบันทึกค่าที่ต้องการตลอดเวลาก็จริง แต่ว่าอยากกลับไปดูข้อมูลย้อนหลังได้ด้วย ดังนั้นข้อมูลดังกล่าวก็ควรจะอยู่ในรูปของ Array สิ

        นั่นล่ะครับ หน้าที่ของ Push เพราะ Push จะไม่ไปแทนที่ข้อมูลเก่า แต่จะเพิ่มข้อมูลเข้าไปเป็นแบบ Array ซึ่งแบ่งออกเป็น 5 ประเภทตามรูปแบบข้อมูลที่จะเก็บเช่นกัน (Boolean, Integer, Float, String และ JSON)

        ถ้าอิงจากโค้ดล่าสุดที่เจ้าของบล็อกยกตัวอย่างให้ดู ลองเปลี่ยนจากคำสั่ง Set ให้กลายเป็น Push แบบนี้แทน

...

void loop() {
  int rawValue = digitalRead(tempSensorPin);
  float temperature = convertTemperature(rawValue);
  
  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& valueObject = jsonBuffer.createObject();
  valueObject["temperature"] = temperature;
  valueObject["raw"] = rawValue;

  Firebase.push(deviceNumber + "/value", valueObject);
  delay(1000);
}

        แนะนำว่าให้ลบข้อมูลของเก่าบน Firebase RTDB ก่อนนะ เพราะข้อมูลมันเป็นคนละรูปแบบกัน

        ข้อมูลที่เก็บอยู่บน Firebase RTDB ก็จะเปลี่ยนเป็นแบบนี้แทน


        บน Firebase RTDB นั้นข้อมูลที่อยู่ในรูปของ Array ทุกตัวจะมี Unique ID กำกับ เพื่อให้สามารถดึงข้อมูลชุดที่ต้องการจาก Unique ID เหล่านี้ได้เลย ซึ่งมันจะถูกส่งออกมาให้เมื่อใช้คำสั่ง Push ทุกครั้ง

String id = Firebase.push(deviceNumber + "/value", valueObject);

        โดยส่วนตัวแล้ว เจ้าของบล็อกคิดว่าการ Push ข้อมูลจาก Arduino นั้นไม่จำเป็นต้องเก็บ Unique ID ไว้ก็ได้ เพราะไม่ค่อยได้ใช้กันซักเท่าไร Arduino ไม่ได้มีการอ่านข้อมูลที่เป็น Array ทั้งหมดอยู่แล้ว (เดี๋ยว Buffer ทะลักจน Memory หมดเกลี้ยงกันพอดี) และข้อมูลดังกล่าวมักจะถูกดึงไปแสดงผลบน Mobile App หรือ Web App ซะมากกว่า

        ถ้าจะให้เหมาะสม Arduino ควรอ่านข้อมูลที่เกิดจากคำสั่ง Set และเวลาเก็บข้อมูลก็เลือกเอาว่าจะเก็บเป็น Set หรือ Push ขึ้นอยู่กับปลายทางที่จะมาอ่านข้อมูลว่าเป็นอุปกรณ์แบบไหน หรือจะเก็บข้อมูลทั้งแบบ Set และ Push พร้อมๆกันเลยก็ได้ เช่น Set สำหรับเก็บข้อมูลปัจจุบัน ส่วน Push สำหรับเก็บลง History เป็นต้น

การลบข้อมูลใน Database

        การลบข้อมูลนั้นค่อนข้างง่าย เพราะสามารถกำหนด Path ของข้อมูลที่ต้องการลบได้เลย

Firebase.remove(path);

        ยกตัวอย่างเช่น เจ้าของบล็อกต้องการเคลียร์ข้อมูลทิ้งทุกครั้งก่อนที่จะเริ่มทำงาน

...

void setup() {
  
  ...

  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
  Firebase.remove(deviceNumber);

  ...
  
}

...



        เจ้าของบล็อกชอบ Firebase RTDB ก็ตรงความเป็น Realtime นี่แหละ ดังนั้นเวลาดูผลลัพธ์การทำงานของโปรแกรมก็สามารถดูได้จากหน้า Console เลย ไม่ว่าข้อมูลจะถูกเพิ่ม ถูกแก้ไข หรือถูกลบ ก็เห็นได้ทันที


การอ่านข้อมูลจาก Database

        เมื่อเพิ่ม/แก้ไข/ลบข้อมูลได้ ก็เหลือแค่อ่านข้อมูลแล้วล่ะ

        สมมติว่าเจ้าของบล็อกมีข้อมูลอยู่ใน Database แบบนี้ (ก็เอาของเก่ามาใช้น่ะแหละ)


        ถ้าเจ้าของบล็อกอยากจะดึงข้อมูล name ก็สามารถกำหนด Path ให้ตรงกับตำแหน่งข้อมูลที่ต้องการได้เลย

String name = Firebase.getString("001/name");
Serial.print("Name : ");
Serial.println(name);


        เนื่องจาก name เป็นข้อมูลแบบ String ดังนั้นเจ้าของบล็อกจึงใช้คำสั่ง getString แต่ถ้าต้องการดึงข้อมูลทั้งก้อนล่ะ? (ทั้ง name และ value)

        ก็จะต้องใช้คำสั่งแบบนี้แทน

const String deviceNumer = "001";

...

FirebaseObject data = Firebase.get(deviceNumber);

        ข้อมูลทั้งหมดในชุดนั้นจะถูกเก็บไว้ใน FirebaseObject ประมาณนี้

{
  "name": "Wio Link 001",
  "value": {
    "temperature": 27.5,
    "raw": 389
  }
}

        เมื่อต้องการดึงข้อมูลแต่ละอย่างก็จะใช้คำสั่งแบบนี้

const String deviceNumer = "001";

...

FirebaseObject data = Firebase.get(deviceNumber);
String name = data.getString("name");
float temperature = data.getFloat("value/temperature");

        และยังสามารถแปลงให้กลายเป็น JsonVariant ด้วยคำสั่ง getJsonVariant(String path) ได้อีกด้วยในกรณีที่ต้องการเอาไปใช้งานอย่างอื่น

        คำเตือน - ไม่ควรดึงข้อมูลที่มีข้อมูลอยู่ข้างในเป็นจำนวนเยอะ เพราะ Buffer จะไม่พอและทำให้ไม่สามารถดึงข้อมูลมาได้ (แต่สถานะก็ดันบอกว่า Success) ดังนั้นอย่าคิดจะดึงข้อมูลที่เก็บเป็น Array จำนวนเยอะๆ

วิธีการเช็คสถานะของคำสั่งที่เรียกใช้งาน

        สำหรับคำสั่งต่างๆไม่ว่าจะเป็น Set, Push, Remove หรือ Get จะสามารถเช็คการทำงานได้ทั้งหมด

bool isSuccess = Firebase.success();
bool isFailed = Firebase.failed();

        ซึ่งคำสั่งนี้จะอัปเดตทุกครั้งที่มีการเรียกใช้คำสั่งต่างๆ โดยสถานะล่าสุดจะถูกเก็บไว้และสามารถเช็คได้ด้วย 2 คำสั่งนี้

        และในกรณีของ Failed จะสามารถเช็ค Error Message ได้จาก

bool isFailed = Firebase.failed();
if(isFailed) {
  Serial.println(Firebase.error());
}

        เท่านี้ก็จะช่วยให้สามารถว่าเกิดปัญหาอะไรขึ้นในคำสั่งก่อนหน้า

อ่านข้อมูลก็ต่อเมื่อมีการเปลี่ยนแปลง

        คำสั่งยังไม่สมบูรณ์เนื่องจากมีบั๊กอยู่ ต้องรอผู้พัฒนา Library ตัวนี้แก้ไขให้

        โดยปกติแล้วคำสั่งอ่านข้อมูลอย่าง Get นั้นจะเป็นการดึงข้อมูลจาก Database มาทันที แล้วถ้าอยากจะให้ทำงานเฉพาะตอนที่ค่านั้นๆบน Database มีการเปลี่ยนแปลงล่ะ?

        เพื่อให้ใช้งานได้สะดวกขึ้น จึงมีคำสั่ง Stream มาให้ใช้งานครับ

Firebase.stream("001/name");

        คำสั่งดังกล่าวจะเช็คกับ Database ว่ามีการเปลี่ยนแปลงหรือไม่ โดยจะยังไม่ดึงข้อมูลมาทันที และจะบอกผลลัพธ์มาให้ผ่านคำสั่ง available() ซึ่งมีเงื่อนไขว่าจะต้องเช็คก่อนว่าคำสั่ง Stream ทำงานได้สำเร็จจริงๆหรือไม่ โดยเช็คจากคำสั่ง success()

Firebase.stream("001/name");
if(Firebase.success()) {
  if(Firebase.available()) {
    // Data has been changed
  } 
}

        และเมื่อต้องการดึงข้อมูลตัวดังกล่าว ให้ใช้คำสั่ง readEvent()

Firebase.stream("001/name");
if(Firebase.success()) {
  if(Firebase.available()) {
    FirebaseObject event = Firebase.readEvent();
  } 
}

        โดยจะได้ผลลัพธ์เป็น FirebaseObject ที่ข้างในจะบอกว่าเปลี่ยนแปลงยังไง (Type), Path ที่มีการเปลี่ยนแปลง (Path) และค่าล่าสุด (Data)

FirebaseObject event = Firebase.readEvent();
String type = event.getString("type");
String path = event.getString("path");

String data = event.getString("data"); << ในกรณีที่ข้อมูลตัวนั้นเป็น String
int data = event.getInt("data");       << ในกรณีที่ข้อมูลตัวนั้นเป็น Integer

        ซึ่งคำสั่ง available() กับ readEvent() จะทำงานร่วมกับ stream(String path) เท่านั้น ดังนั้นถ้าไม่สั่งให้ Stream ก่อน คำสั่งทั้ง 2 ก็จะไม่มีผลใดๆ

        คำสั่งนี้ค่อนข้างมีประโยชน์พอสมควร เพราะไม่ต้องมานั่งเก็บข้อมูลเพื่อเช็คเองว่าข้อมูลมีการเปลี่ยนแปลงหรือไม่ เพราะบางครั้งเจ้าของบล็อกก็ไม่ได้อยากให้ทำคำสั่งเดิมซ้ำๆถ้าข้อมูลมีค่าเท่าเดิม แต่ก็อย่างที่บอกในตอนแรกว่าคำสั่งนี้ยังมีปัญหาอยู่ เจ้าของบล็อกเจอปัญหาคำสั่ง Stream ทำให้เกิด Exception และตัวบอร์ดก็ Reset แล้วเริ่มทำงานใหม่ทันที ซึ่งต้องรอจนกว่าเจ้าของ Library ตัวนี้จะแก้ไขให้ล่ะนะ

ลองเขียนโปรเจคง่ายๆที่ใช้งานกับ Realtime Database

        เพื่อความคลาสสิค เจ้าของบล็อกจึงเขียนโปรแกรมควบคุมไฟติด/ดับที่จะอ่านข้อมูลจาก Database แล้วสั่งงาน LED ตามค่าที่ได้แล้วกันเนอะ (ไม่ทำไฟกระพริบก็บุญแล้ว)

        ซึ่งโค้ดก็จะไม่ยากมากนัก (ถ้าอ่านเนื้อหาที่ผ่านมาทั้งหมดนะ)

#include <ESP8266WiFi.h>
#include <FirebaseArduino.h>

#define WIFI_SSID       "MyHomeWifi"
#define WIFI_PASSWORD   "1234567890"
#define FIREBASE_HOST "wio-link-d6bXX.firebaseio.com"
#define FIREBASE_AUTH "qUohEtHyvCcOzgxq95ZheaVsaMePi2P5FDd8XXXX"

const int ledPin = 14;

void setup() {
  Serial.begin(9600);
  Serial.println(WiFi.localIP());
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("connecting");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.println();
  Serial.print("connected: ");
  Serial.println(WiFi.localIP());
  
  pinMode(ledPin, OUTPUT);

  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);
  Firebase.setInt("LED", 0);
}

void loop() {
  digitalWrite(ledPin, Firebase.getInt("LED"));
  delay(500);
}

        จากนั้นก็ต่อ LED Socket Kit เข้าที่ขา 14 ของบอร์ด



        เมื่อบอร์ดทำงานก็จะได้ข้อมูลบน Firebase RTDB แบบนี้


        ให้ลองแก้ไขค่าระหว่าง 0 กับ 1 แล้วสังเกตผลลัพธ์ที่เกิดขึ้นกับ LED ดู เพราะ LED จะต้องติดเมื่อกำหนดค่าเป็น 1 และจะต้องดับเมื่อกำหนดค่าเป็น 0

        หมายเหตุ - ไม่รู้บอร์ดเจ้าของบล็อกผิดปกติหรือป่าว แต่ต้องกำหนดให้ขา 15 ของบอร์ดเป็น Digital Output และเป็น High เสียก่อนถึงจะสั่งงานขาอื่นๆได้... ถ้าผู้ที่หลงเข้ามาอ่านเจอปัญหาแบบเจ้าของบล็อกก็ลองใช้วิธีแบบนี้ดูนะ

เรื่องที่ควรรู้เกี่ยวกับ Firebase RTDB บน Arduino

        FirebaseArduino Library นั้นยังคงอยู่ใน Development อยู่ แต่น่าจะอีกยาวนานแน่นอน ถ้าจำเป็นต้องใช้งานจริงๆก็ควรหมั่นติดตามดูการอัปเดทและ Issue ที่เกิดขึ้นใน GitHub Repository ของโปรเจคตัวนี้ด้วย เพื่อดูว่าจะต้องเจอกับปัญหาอะไรบ้าง

        สำหรับการใช้งานจริงๆร่วมกับ Platform อื่นๆไม่ว่าจะเป็น Web หรือ Mobile App ต้องอย่าลืมว่า Arduino ไม่ได้มีทรัพยากรเยอะเท่ากับ Platform อื่นๆ ดังนั้นอย่าคิดที่จะใช้ข้อมูลร่วมกันโดยตรง อย่างเช่น ให้หน้าเว็ป Push ข้อมูลเข้า RTDB แล้วให้ Arduino ดึงเฉพาะข้อมูลตัวล่าสุดไปแสดง เพราะ Arduino ทำงานอะไรซับซ้อนมากๆไม่ได้ ดังนั้นควรแยกข้อมูลไว้อีกชุดเพื่อให้ Arduino ดึงข้อมูลไปใช้ได้ทันที

สรุป

        สำหรับ Firebase RTDB กับ ESP8266 จะบอกว่าเป็นเนื้อคู่ก็คงจะเวอร์ไป บอกว่าเป็นคู่ใจก็พอ เพราะโดยปกติแล้วการทำอุปกรณ์ IoT ส่วนใหญ่นั้นก็คงไม่พ้น ESP8266 ด้วยเรื่องของราคาและความสามารถที่เพียงพอแต่การเป็น IoT เพราะไม่ต้องการทำงานที่ครอบจักรวาล ขอแค่เพียงทำหน้าที่บางอย่างก็พอ และสามารถส่งข้อมูลขึ้นเซิฟเวอร์ได้

        สิ่งที่ขาดไปไม่ได้ก็คือ Web Server ที่คอยเก็บข้อมูล ประมวลผลข้อมูล และนำข้อมูลไปใช้งานต่างๆให้เกิดประโยชน์ เรียกว่าเป็นหัวใจสำคัญหลักของ IoT เลยก็ว่าได้

        ถึงแม้ว่าทุกวันนี้จะมีหลายๆเจ้าที่เปิดให้บริการสำหรับ IoT บ้างก็เป็นชุดครบวงจรมีทั้ง Hardware และ Server ให้ใช้งาน เพื่อช่วยให้นักพัฒนาสามารถสร้าง Product ได้ไวและง่ายขึ้น แต่เอาเข้าจริงก็มีหลายๆอย่างที่ไม่รู้สึกว่าโอเคซักเท่าไร อย่างเช่น

        • ผู้ให้บริการบางเจ้ามีเซิฟเวอร์หลักอยู่ที่ประเทศจีนที่อะไรก็เกิดขึ้นได้ หรือบางที่ก็นำข้อมูลไป Implement ต่อไม่ได้ ได้แค่ Monitoring บนหน้าเว็ปที่มีให้เท่านั้น
        • ความปลอดภัยของการเชื่อมต่อกับ Server เพราะอุปกรณ์ IoT นั้นได้กลายเป็นเป้าหมายของเหล่าแฮคเกอร์แล้วในยุคนี้ เนื่องจากความปลอดภัยต่ำกว่าคอมพิวเตอร์หรือโทรศัพท์มือถือ
        • การทำ Server เองก็ต้องคำนึงถึงวางระบบบนเครื่อง Server ไม่ว่าจะเป็นเรื่องฐานข้อมูล, API และอื่นๆที่เกี่ยวข้อง

        ซึ่ง Firebase RTDB นั้นจะมาช่วยแก้ปัญหา (บางอย่าง) ได้ เพราะเป็น Product ของ Google ดังนั้นจึงมีความเชื่อถือ (แต่ตัว Library อันนี้ก็อีกเรื่องนึงนะ) ไม่ต้องกลัวว่า Server จะมีปัญหา รวมไปถึงการทำงานของ Realtime Database ที่เทพมากถึงมากที่สุด โดยที่ผู้ที่หลงเข้ามาอ่านแทบจะไม่ต้องทำอะไร เข้าใจการใช้งานและรูปแบบการเก็บข้อมูลก็ใช้ได้เลย และสามารถนำข้อมูลไป Implement บน Platform อย่าง Mobile App หรือ Web App ได้ง่ายมาก (เพราะเดิมทีมันทำขึ้นมาเพื่อ Platform พวกนี้อยู่แล้ว)


        แต่ก็มีข้อเสียที่ไม่สามารถ Monitor ข้อมูลต่างๆได้แบบ Platform สำเร็จรูปเฉพาะทาง เพราะ Realtime Database ไม่ได้ทำขึ้นมาเพื่อใช้งานด้านใดด้านหนึ่ง แต่ใช้กับอะไรก็ได้ (มีความยืดหยุ่นสูง) ดังนั้นการ Monitor ข้อมูลในนั้นก็จะต้องทำเอง รวมไปถึงการนำข้อมูลไปใช้งานด้วยเช่นกัน และอนาคตของ FirebaseAndroid Library ก็ไม่แน่นอนด้วยว่าจะไปได้ไกลแค่ไหน รวมไปถึงยังไม่เสร็จสมบูรณ์ด้วย ดังที่ผู้พัฒนาใส่ไว้ใน Readme ว่า

        "The Arduino library is under heavy development, experimental, unversioned and its API is not stable."

        สุดท้ายแล้วก็อยู่ที่ผู้หลงเข้ามาอ่านแหละครับ ที่จะต้องเรียนรู้เครื่องมือทุกอย่างที่มีเพื่อตัดสินใจและเลือกใช้งานเพื่อตอบโจทย์ตามความต้องการให้ดีที่สุด

แหล่งข้อมูลอ้างอิง

        • Firebase Arduino [GitHub]
        • Firebase Arduino - Documentation
        • Wio Link [Seeed Studio]