Freeboard : get ค่าปัจจุบัน และ รับ-ส่ง หลายคำสั่งในข้อความเดียว

พูดคุยเกี่ยวกับปัญหา

สวัสดีครับ บทความนี้เราจะมาพูดถึงปัญหาที่มีผู้ใช้สอบถามกันเข้ามาและจะแนะแนวทางในการแก้ไข โดยในบทความนี้ได้ผมได้เลือกปัญหาที่เกี่ยวกับ Freeboard มา 2 ประเด็น

1. Freeboard รับสถานะปัจจุบันของ LED เมื่อเปิดใช้งาน

2. การใช้ Indicator light แสดงสถานะของ LED หลายอันที่ถูกส่งมาในข้อความเดียว

ประเด็นแรก Freeboard รับสถานะปัจจุบันของ LED เมื่อเปิดใช้งาน ต้องทำอย่างไร

การ chat หรือ publish เป็นการส่งข้อมูลแบบ realtime ก็จริง การส่งข้อมูลจะเป็นแบบส่งผ่าน ผู้ส่งก็มีหน้าที่ส่งไปเรื่อย ถ้าไม่มีผู้รับหรือผู้รับ offline อยู่ข้อความนั้นก็จะหายไป สำหรับหลายท่านที่ผ่านการใช้ NETPIE มาสักพักแล้ว เมื่อได้เจอกับปัญหานี้คงจะแก้ไขได้อย่างสบาย โดยการใช้ retain เพื่อให้ NETPIE จำข้อความสุดท้ายไว้ให้ เมื่อผู้รับ online ก็จะได้รับข้อความสุดท้ายนั้นทันที ซึ่ง retain จะได้เฉพาะกับ publish เท่านั้น แต่ retain ก็ไม่ได้เหมาะกับทุกงานหรือแก้ปัญหาได้ทั้งหมด

ยกตัวอย่างเช่น

กรณีข้อความที่ส่งเป็นสถานะของการทำงานเครื่องจักร ผู้ส่งมีการส่งข้อความบอกสถานะเครื่องออกมาเรื่อยๆ มีการ retain ไว้ ต่อมาผู้ส่งเกิด offline ไป ในจังหวะที่ ผู้ส่ง offline นั้นเครื่องจักรเปลี่ยนสถานะการทำงาน ทำให้ผู้รับที่ online เข้ามาได้รับข้อความที่ไม่อัพเดทและเป็นข้อความสถานะเครื่องจักรที่ผิดนั้นเอง

สำหรับปัญหานี้ แนะนำให้ใช้วิธีถาม-ตอบจะดีกว่า เมื่อผู้รับ online ก็ให้ส่งข้อความไปถามสถานะปัจจุบันจากผู้ส่ง เมื่อผู้ส่งได้รับข้อความคำถามก็ตอบกลับไป หรือในกรณีที่ผู้ส่ง offline หรือ ไม่ตอบ ก็จะได้รู้ไปเลยว่าการทำงานผิดปกติ

ประเด็นที่สอง การรับส่งหลายคำสั่ง/ค่าในข้อความเดียวกัน

เมื่ออ่านหัวข้อประเด็นที่สอง คงจะเกิดคำถามว่าทำไมถึงต้องส่งรวมกันในข้อความเดียว ไม่ส่งแยกกันหนึ่งคำสั่งต่อหนึ่งข้อความ คำตอบคือ ถ้าใน 1 วินาทีมีการส่งข้อความเยอะๆอาจทำให้ติด limit ได้ ซึ่งการติด limit จะทำให้ข้อความถูกปฎิเสธไม่ถูกส่งออกไปนั้นเอง

ยกตัวอย่างเช่น

จากวิธีที่แนะนะในประเด็นที่หนึ่ง ใช้การถาม-ตอบสถานะ สมมุติ ภายใน 1 วินาที มีการส่งข้อความมาถามสถานะ 6 ครั้ง ทำให้อีกฝั่งต้องส่งข้อความตอบกลับไป 6 ครั้ง แต่ในความเป็นจริง อาจจะส่งข้อความได้ไม่ครบทั้ง 6 ครั้ง เพราะเป็นการส่งข้อความติดๆกัน ทำให้ติด limit ส่งผลให้ข้อความครั้งหลังๆถูกปฏิเสธ

สำหรับการงานใช้ Freeboard ผู้ใช้มือใหม่คงจะงงกันไม่น้อย เพราะ Freeboard ถูกออกแบบให้เก็บค่าไว้ใน path หรือ ตัวแปร ของ Datasource ซึ่งตัว Microgear เองก็เป็น Datasource เช่นกัน เมื่อ Microgear ได้รับข้อความก็จะเก็บบน path นั้นเอง

ยกตัวอย่างเช่น

สมมุตบน Freeboard มี Microgear

appid คือ armydev

name คือ dashboard

เมื่อได้รับข้อความจากการ chat

microgear.chat(“dashboard”,”hello”)

ข้อความจะถูกเก็บใน path ของ

datasource[“dashboard”][“/armydev/gearname/dashboard”] จะมีค่าเท่ากับ “hello”

ส่วนข้อความจากการ publish นั้น

microgear.publish(“led/state”,”on”)

ข้อความจะถูกเก็บใน path ของ

datasource[“dashboard”][“/armydev/led/state”] จะมีค่าเท่ากับ ”on”

แต่ด้วยข้อจำกัดทาง limit หรือ การส่งข้อความถูกออกแบบให้คำสั่งหรือค่าหลายๆอันในข้อความเดียว ยกตัวอย่างเช่น

ต้องการส่ง state ของ led 3 ดวงในข้อความเดียว

microgear.publish(“led/state”,”on/off/off”)
datasource[“dashboard”][“/armydev/led/state”] จะมีค่าเท่ากับ ”on,off,off”

จะเห็นว่า state ของ led 3 ดวง ”on/off/off” ถูกเก็บไว้ใน datasource[“dashboard”][“/armydev/led/state”] ทำให้ยากต่อการใช้งาน สำหรับคนที่เขียนโปรแกรมคงรู้ว่าต้องใช้คำสั่งเพื่อแยกตัวแปรออกจากกันหรือเรียกว่าการ split และสงสัยว่า บน Freeboard จะทำได้ไหม คำตอบคือ บน Freeboard สามารถใช้ javascript ได้

datasource[“dashboard”][“/armydev/led/state”].split(“/”) จะกลายเป็น [“on”,”off”,”off”]

เพราะถูกแยกออกจากกันด้วยเครื่องหมาย “/” และสามารถเรียกค่าออกมาใช้งานทีละตัวได้โดย

datasource[“dashboard”][“/armydev/led/state”].split(“,”)[0] จะมีค่าเท่ากับ “on”
datasource[“dashboard”][“/armydev/led/state”].split(“,”)[1] จะมีค่าเท่ากับ “off”
datasource[“dashboard”][“/armydev/led/state”].split(“,”)[2] จะมีค่าเท่ากับ “off”

การใช้งานจริง

ในบทความนี้เราตั้งโจทย์ไว้ว่า จะสั่งเปิด/ปิดไฟ LED 2 ดวง จาก Freeboard ไป ยัง esp8266 และเมื่อตัว esp8266 ทำงานตามคำสั่งสำเร็จ ให้อัพเดตสถานะการเปิด/ปิดไฟ นั้นไปที่ Freeboard และเมื่อเปิดใช้งาน Freeboard จะให้ Freeboard ส่งข้อความไปถามสถานะของ LED ด้วย

การใช้บริการของ NETPIE ในการส่งข้อความ โดยใช้ Microgear นั้นมี function ให้เลือกใช้งานดังนี้

  • publish เป็นการส่งข้อความเข้าไปใน Topic ex. microgear.publish(“topic”,”message”)
  • chat เป็นการส่งข้อความไปถึงคนที่เราระบุถึง ex. microgear.chat(“name”,”message”)

ซึ่งโจทย์ที่เรามี chat จึงเหมาะเพราะ เป็นแค่การส่งข้อความไป-กลับ ระหว่าง 2 อุปกรณ์ และมีการสื่อสารกันเรื่องเดียวคือการเปิด/ปิดไฟ LED

การสมัครสมาชิก NETPIE

https://netpie.gitbooks.io/doc/content/

การสร้าง Freeboard บน NETPIE.io

https://netpie.gitbooks.io/5-Freeboard/content/

esp8266

ALIAS = esp8266

NEIGHBOR = dashboard ชื่อของ Microgear ที่อยู่บน Freeboard

LED1 D1 ตั้งค่า pin

LED2 D3 ตั้งค่า pin

STATE = “0,0” ค่าเริ่มต้น มาจาก STATELED1+”,”+STATELED2

STATELED1 = 0 ค่าเริ่มต้น LED1

STATELED2 = 0 ค่าเริ่มต้น LED2

onMsghander จะทำงานเมื่อมีข้อความส่งมาที่ esp8266

เมื่อได้รับ message “?” จะส่ง STATE กลับไปที่ NEIGHBOR

เมื่อได้รับ message

LED1#0 : ปิดไฟ LED1

LED1#1 : เปิดไฟ LED1

LED2#0 : ปิดไฟ LED2

LED2#1 : เปิดไฟ LED2

เมื่อทำการ เปิด/ปิด ตามคำสั่งสำเร็จ จะส่ง STATE กลับไปที่ NEIGHBOR โดยใช้คำสั่ง

microgear.chat(NEIGHBOR,STATE)

Freeboard

Datasource

Microgear

Alias : dashboard ชื่อของ Microgear นี้

Widget

Text : STATE แสดง STATE ที่ถูกส่งมาจาก esp8266

Indicator light : LED1 แสดง STATE ของ LED1 ที่ถูกส่งมาจาก esp8266

Indicator light : LED2 แสดง STATE ของ LED2 ที่ถูกส่งมาจาก esp8266

Toggle : LED1 ควบคุม LED1 บน esp8266

Toggle : LED2 ควบคุม LED2 บน esp8266

Datasource : Microgear

สังเกตที่ ONCONNECTED ACTION เมื่อ Microgear online จะเรียกใช้ function microgear.chat(“esp8266”,”?”) เพื่อส่งข้อความไปถาม STATE ของ LED จาก esp8266

Widget : Text

สังเกตที่ VALUE จะแสดง STATE ของ LED ที่ถูกส่งกลับมาจาก esp8266

Widget : Indicator Light : LED1

สังเกตที่ VALUE จะแสดงเฉพาะ STATE ของ LED1 ที่ถูกส่งกลับมาจาก esp8266 โดยการ split และเลือกเอาตำแหน่งแรก หรือ index = 0 นั้นเอง

Widget : Indicator Light : LED2

สังเกตที่ VALUE จะแสดงเฉพาะ STATE ของ LED2 ที่ถูกส่งกลับมาจาก esp8266 โดยการ split และเลือกเอาตำแหน่งที่สอง หรือ index = 1 นั้นเอง

Widget : Toggle : LED1

TOGGLE STATE จะแสดงเฉพาะ STATE ของ LED1 ที่ถูกส่งกลับมาจาก esp8266 โดยการ split และเลือกเอาตำแหน่งแรก หรือ index = 0

ONTOGGLEON ACTION เมื่อ toggle ถูกคลิกเปลี่ยนจาก สถานะ off เป็น on จะเรียกว่า function

microgear.chat(“esp8266”,”LED1#1”)

หมายถึง สั่งให้ esp8266 เปิดไฟ LED1

ONTOGGLEOFF ACTION เมื่อ toggle ถูกคลิกเปลี่ยนจาก สถานะ on เป็น off จะเรียกว่า function

microgear.chat(“esp8266”,”LED1#0”)

หมายถึง สั่งให้ esp8266 ปิดไฟ LED1

Widget : Toggle : LED2

TOGGLE STATE จะแสดงเฉพาะ STATE ของ LED2 ที่ถูกส่งกลับมาจาก esp8266 โดยการ split และเลือกเอาตำแหน่งที่สอง หรือ index = 1

ONTOGGLEON ACTION เมื่อ toggle ถูกคลิกเปลี่ยนจาก สถานะ off เป็น on จะเรียกว่า function

microgear.chat(“esp8266”,”LED2#1”)

หมายถึง สั่งให้ esp8266 เปิดไฟ LED2

ONTOGGLEOFF ACTION เมื่อ toggle ถูกคลิกเปลี่ยนจาก สถานะ on เป็น off จะเรียกว่า function

microgear.chat(“esp8266”,”LED2#0”)

หมายถึง สั่งให้ esp8266 ปิดไฟ LED2

สุดท้ายโค้ดส่วนของ esp8266 ฉบับเต็ม

#include <ESP8266WiFi.h>
#include <MicroGear.h>
// — — — แก้ค่า config 7 ค่าข้างล่างนี้ — — — — — — — — — — — — — — — — — — — — — — — — — — — — 
const char* ssid = “ssid”; // ชื่อ ssid
const char* password = “password”; // รหัสผ่าน wifi
#define APPID “APPIDAPPID”
#define KEY “KEYKEY”
#define SECRET “SECRETSECRET”
#define ALIAS “esp8266” // แทนที่ด้วยหมายเลขของท่าน เช่น “A01”
#define NEIGHBOR “dashboard” // ชื่ออุปกรณ์ของเพื่อน เช่น “A02”
// — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
#define LED1 D1 // pin ที่ต่อกับไฟ LED บนบอร์ด NodeMCU
#define LED2 D3
String m; // ตัวแปรเก็บข้อความชนิด string
const char delimiter = ‘#’; // ใช้สำหรับเช็ครูปแบบข้อความที่คั่นด้วยเครื่องหมาย sharp
String STATE =”0,0";
String STATELED1 = “0”;
String STATELED2 = “0”;
WiFiClient client;
MicroGear microgear(client);
void onMsghandler(char *topic, uint8_t* msg, unsigned int msglen) {
 Serial.print(“Incoming message → “);
 msg[msglen] = ‘\0’;
 Serial.println((char *)msg);
 if(*(char *)msg == ‘?’){
 STATE = STATELED1;
 STATE += “,”;
 STATE += STATELED2;
 microgear.chat(NEIGHBOR,STATE);
 }
 else{
 m = (char*)msg;
 if (m.length() > 0){
 if(m.indexOf(delimiter)!=-1){
 String LED = m.substring(0, m.indexOf(delimiter));
 String Logic = m.substring(m.indexOf(delimiter)+1);
 if(LED == “LED1”){
 if(Logic==”0"){
 digitalWrite(LED1, LOW);
 }
 if(Logic==”1"){
 digitalWrite(LED1, HIGH);
 }
 STATELED1 = Logic;
 }
 if(LED == “LED2”){
 if(Logic==”0"){
 digitalWrite(LED2, LOW);
 }
 if(Logic==”1"){
 digitalWrite(LED2, HIGH);
 }
 STATELED2 = Logic;
 }
 STATE = STATELED1;
 STATE += “,”;
 STATE += STATELED2;
 microgear.chat(NEIGHBOR,STATE);
 }
 }
 }
}
void onConnected(char *attribute, uint8_t* msg, unsigned int msglen) {
 Serial.println(“Connected to NETPIE…”);
 microgear.setAlias(ALIAS);
}
void setup() {
 microgear.on(MESSAGE,onMsghandler);
 microgear.on(CONNECTED,onConnected);
Serial.begin(115200);
 Serial.println(“Starting…”);
// กำหนดชนิดของ PIN (ขาI/O) เช่น INPUT, OUTPUT เป็นต้น
 pinMode(LED1, OUTPUT); // LED pin mode กำหนดค่า
 pinMode(LED2, OUTPUT); // LED pin mode กำหนดค่า
if (WiFi.begin(ssid, password)) {
 while (WiFi.status() != WL_CONNECTED) {
 delay(1000);
 Serial.print(“.”);
 }
 }
 
 Serial.println(“WiFi connected”);
 Serial.println(“IP address: “);
 Serial.println(WiFi.localIP());
microgear.init(KEY,SECRET,ALIAS); // กำหนดค่าตันแปรเริ่มต้นให้กับ microgear
 microgear.connect(APPID); // ฟังก์ชั่นสำหรับเชื่อมต่อ NETPIE
}
void loop() {
 if (microgear.connected()) {
 microgear.loop();
 }
 else {
 Serial.println(“connection lost, reconnect…”);
 microgear.connect(APPID); 
 }
}
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.