แสดงข้อมูลจาก IoT บน Dashboard ด้วย Server-side event (Nodejs) | code.isaranu.com

Follow @isaranu
 Isaranu |  24.09.2019  

ปกติเวลาที่เราต้องการนำข้อมูลจากอุปกรณ์ IoT มาแสดงผลบนหน้า Dashboard เรามีวิธีการทำได้หลายรูปแบบ เช่น ส่ง HTTP request, ทำ Websocket หรือใช้ protocol สำเร็จรูปต่างๆ เพื่อนำข้อมูลมาเก็บ Database แล้วจึงนำไปแสดงผลในหน้า Dashboard อีกทีโดยการทำ API.

วันนี้จะนำเทคนิคอีกรูปแบบหนึ่ง มาเล่าให้ฟังครับ (เนื่องจากไปเห็นบทความ Server-side event emitter เลยนำ use case งาน IoT มาแสดงให้ดูเลยดีกว่า)
บทความนี้ไม่เน้นการอธิบาย code แบบทุกบรรทัดนะครับ ดังนั้น, ผู้อ่านจะต้องพอมีความรู้เบื้องต้นต่อไปนี้ก่อน
- Nodejs
- NPM
- HTML, JavaScript, CSS
- C++ (Code สำหรับบน IoT device)

มาเริ่มจาก concept ก่อน,
โดยเราจะให้แสดงสถาณะ และจำนวนครั้งในการกด Button จาก IoT device ไปโชว์บนหน้า Dashboard.
ซึ่งบน IoT device จะใช้การส่ง POST request แนบข้อมูลการกด Button (สถานะ, จำนวนครั้ง) ไปยัง IoT protocol ที่รันด้วย Node.js + module ที่ชื่อว่า "eventemitter3" และ "express"

จากนั้นก็ push event นี้ไปแสดงผลที่ HTML ด้วยการใช้ EventSource ที่เรียกมาที่เดียวกับ IoT protocol. ซึ่งหน้า HTML จะไม่มีการ polling เข้ามา แต่จะเป็นฝั่ง server ที่ส่ง Event ไปให้ทันที.

"Demo นี้ไม่ได้ทำบน localhost นะครับ, แต่ทำที่ Cloud server เลย"

ไปแบบไวๆ, เพราะถ้าละเอียดเกิน เดี๋ยวก็ขี้เกียจอ่านกันอีก
[IoT device code]
- เมื่อ connect WiFi ได้แล้ว, ก็วนอ่าน status ของ button. เมื่อพบว่า status เปลี่ยนไป(มีการกด หรือ ปล่อย) ให้ส่ง HTTP POST request โดยแนบ data ไปที่ IoT protocol บน server.

ใช้ WeMos (ESP8266) กับ Button shield


  HTTPClient http;
    http.begin("http://{SERVER-IP-ADDRESS}/buttonstatus");
    http.addHeader("Content-Type", "application/x-www-form-urlencoded");

    String params = "buttonstatus=" + String(buttonstatus);
           params += "&";
           params += "buttoncnt=" + String(button_cnt);

    int httpCode = http.POST(params);
[IoT protcol server]
- รับ POST request เข้ามาที่ app.post('/buttonstatus'), ก็นำไปยิง event ต่อด้วยคำสั่ง "emitter.emit". ซึ่งยิงไปที่ event ที่ชื่อว่า "message" ที่ระบุอยู่ใน app.get('/').
และให้ HTML ทำการเรียกเข้ามาที่ app.get('/') คล้ายกับการ subscribe เข้ามานั่นเอง (Header จะถูกเซ็ตไว้ว่าเป็น 'text/event-stream')
  app.get('/', function(req, res){

  res.writeHead(200, {
    "Content-Type": "text/event-stream",
    "Cache-Control": "no-cache",
    "Connection": "keep-alive"
  });

  emitter.on('message', function(data){
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  });

});

app.post('/buttonstatus', urlencodedParser, function(req, res){
  console.log(req.body);
  emitter.emit('message', {buttonstatus:req.body.buttonstatus, buttoncnt:req.body.buttoncnt});
  res.jsonp({status:"push event successful"});
});
[HTML]
- ในส่วน JS, จะทำการเปิด EventSource เพื่อรับข้อมูลเข้ามา ที่เหลือก็นำไป render และแสดงผลตาม Element ต่างๆบน HTML tag ได้ทันที. ในตัวอย่างจะใช้เป็น toggle switch และแสดงจำนวนนับการกด button จากอุปกรณ์ได้.
    var source = new EventSource("http://{SERVER-IP-ADDRESS}");
    source.onmessage = function(e){
      var buttoninfo = JSON.parse(e.data);
      console.log(buttoninfo);
      switch (true) {
        case buttoninfo.buttonstatus == '0':
          $('#toggle_switch').prop('checked', true);
          break;
        case buttoninfo.buttonstatus == '1':
          $('#toggle_switch').prop('checked', false);
          break;
        default:
          //..
      }
      $('#button_cnt_span').text(buttoninfo.buttoncnt);
    }
ทั้งหมดนี้, มี code อยู่ที่ github/IoT-dashboard-with-serverside-event นะครับ. (รอบรรทัดนี้กันเลยใช่ไหม 555)
ลองเล่นกันดูครับ.

ขอบคุณทุกการติดตามนะครับ :D

Isaranu.

Follow @isaranu