ดิว.นินจา

ดิว.นินจา

Friday, January 11, 2019

NETPIE Freeboard : วิธีแยกข้อมูลจาก widgets ควบคุมโดยกำหนดชื่อ topic

ปัญหาหนึ่งที่ผู้เริ่มต้นพัฒนา NETPIE Freeboard ประสบเมื่อเพิ่ม widget สำหรับควบคุมฮาร์ดแวร์จำนวนมากกว่า 1 ตัวขึ้นไป คือต้องการให้อุปกรณ์ทางด้านรับสามารถแยกได้ว่าข้อมูลถูกส่งมาจาก widget ตัวใด เพื่อที่จะตอบสนองต่อคำสั่งนั้นได้อย่างถูกต้อง ทางแก้ที่ผู้เขียนเคยนำเสนอและใช้ได้ดีคือการเพิ่มสตริงส่วนที่เป็นคำสั่งเข้าไปในข้อมูลที่ chat มาจาก widget แต่ละตัว และสร้างฟังก์ชันแปลคำสั่งทางด้านรับ อย่างไรก็ตามสำหรับ IoT ขนาดเล็กที่มี widget ควบคุมเพียง 2-3 ตัว การเขียนฟังก์ชันแปลคำสั่งอาจจะเกินความจำเป็น ดังนั้นในบทความนี้จะนำเสนอทางเลือกอีกวิธีหนึ่ง คือทางด้านส่งใช้ฟังก์ชัน microgear.publish() โดยระบุ topic ที่แตกต่างกัน ทำให้ทางด้านรับสามารถแยกข้อมูลและทำงานตาม topic ที่ตั้งไว้

เพื่อยกตัวอย่างที่เป็นรูปธรรม โจทย์ปัญหาการควบคุมโดย widget หลายตัวในบทความนี้แสดงในรูปที่ 1 คือต้องการปรับความสว่างของหลอดไฟ (Lamp) และอัตราการไหลของวาล์วน้ำ (Valve) โดย slider widgets และปิด/เปิดการให้ปุ๋ยโดย toggle widget ฮาร์ดแวร์ตัวประมวลผลที่ใช้คือ ESP8266 (NodeMCU) โจทย์ตัวอย่างนี้ยกมาจาก LAB 5.3 ในหนังสือ "คู่มือฝึกอบรมเน็ตพายไอโอที" ซึ่งต่อไปจะเรียกสั้นๆ ว่า คู่มือ

รูปที่ 1 โจทย์ปัญหาการควบคุมโดย Freeboard widget 3 ตัว

ย้อนกลับไปอธิบายวิธีการใช้ตัวแปรคำสั่งโดยสังเขป ยกตัวอย่าง slider widget โดยวิธีการเดิมที่ใช้ คำสั่งที่ส่งให้กับอุปกรณ์คือ


microgear["datasource"].chat("alias","text"+value)

สตริง "text" ที่สอดแทรกเข้าไปในข้อความที่ chat จะไม่ซ้ำกันในแต่ละ widget ช่วยให้อุปกรณ์สามารถแยกได้ว่าส่งมาจาก widget ตัวใด โปรแกรมทางด้านรับจะต้องมีกรรมวิธีการประมวลผลสตริง วิธีการที่ผู้เขียนใช้คือกำหนดอักขระพิเศษเพื่อแยกระหว่างคำสั่งกับค่าจาก slider ตัวอย่างเช่น redled=value คำสั่งคือ redled หรือปรับความสว่างของหลอด LED สีแดง เราสามารถใช้ฟังก์ชันของออปเจ็ค String เพื่อแยกคำสั่งได้โดยง่าย เพื่อความง่ายในการบำรุงรักษาโปรแกรม ส่วนรับและแปลคำสั่งจะเขียนเป็นฟังก์ชัน cmdInt() ซึ่งมีจุดเด่นคือคำสั่งเดียวกันสามารถส่งจาก NETPIE และจากผู้ใช้หน้างานผ่านพอร์ตอนุกรม

ในกรณีที่ผู้ใช้ต้องการควบคุมอุปกรณ์ IoT ผ่านอินเทอร์เน็ตเพียงอย่างเดียวและไม่ต้องการเขียนโปรแกรมแยกสตริงให้ยุ่งยาก วิธีที่สองที่นำเสนอคือกำหนดชื่อ topic ที่แตกต่างกันให้กับข้อความที่ publish มาจาก widget แต่ละตัว ซึ่งทางด้านรับสามารถตรวจสอบชื่อ topic และแยกทำงานตามต้องการได้ ซึ่งเป็นวิธีที่จะสาธิตในบทความนี้

การแยกข้อความโดย topic

วิธีการแยกข้อความจาก widget หลายตัวตาม topic แสดงได้ดังรูปที่ 2 โดยทางผู้ส่งข้อความจะใช้วิธี microgear.publish() แทน microgear.chat() ส่วนทางด้านรับจะต้อง subscribe topic ที่ต้องการรับ สามารถใช้ wild cards # เพื่อลดจำนวนคำสั่งที่ใช้ subscribe ได้ รายละเอียดในส่วนนี้ศึกษาได้จากคู่มือและสไลด์ของ NETPIE การแยกข้อมูลด้านผู้รับจะอาศัยความแตกต่างของ topic ซึ่งจะได้กล่าวถึงต่อไป

รูปที่ 2 การแยกข้อความจาก widget หลายตัวตาม topic

ฮาร์ดแวร์ที่ใช้ทดลอง

การทดลองในบทความนี้ต้องการเพียง LED 3 ตัวแทน Lamp, Valve, Fertilizer ต่อกับขา D5, D6, D7 ตามลำดับ ผู้อ่านสามารถต่อวงจรบนบอร์ดทดลอง ใช้ผลิตภัณฑ์ IoT Activity ของบริษัท inex หรือในที่นี้จะใช้บอร์ด IGR (Indoor Greenhouse Regulator) ที่ออกแบบโดยผู้เขียนสำหรับการฝึกอบรมดังในรูปที่ 3 LED ทั้งหมดติดตั้งบนบอร์ดอยู่แล้ว ส่วนเซนเซอร์ DHT11 และ BH1750 จะต่อไว้ด้วยเพื่อการแสดงผลบน NETPIE แต่ไม่มีความสำคัญสำหรับบทความนี้ เพราะจะสนใจเพียงการควบคุมโดย sliders และ toggle widget เท่านั้น

รูปที่ 3 ด้านบนของบอร์ด IGR แสดง LED 3 ตัวที่ต้องการควบคุม

แก้ไข NETPIE Freeboard

การทดลองในบทความนี้จะเริ่มแก้ไขในส่วน NETPIE Freeboard ที่เป็นด้านส่งคำสั่ง เพราะเป็นผู้กำหนดชื่อ topic ใน LAB 5.5 ของคู่มือ เราได้พัฒนา NETPIE Freeboard ไว้โดยสมบูรณ์ ลักษณะดังในรูปที่ 4 สามารถใช้ Freeboard เดิมนี้ในบทความ เพียงแต่แก้ไขคำสั่งใน sliders และ toggle เท่านั้น

รูปที่ 4 หน้า Freeboard เดิมจาก LAB 5.5

เริ่มจาก toggle widget คลิกเพื่อตั้งค่าจะปรากฏหน้าต่างดังรูปที่ 5 ช่องที่ต้องแก้ไขคือ ONTOGGLEON ACTION ใส่คำสั่ง


microgear["FBmcpds"].publish("/igtoggles/fertilizer","1")

และ ONTOGGLEOFF ACTION ใส่คำสั่ง


microgear["FBmcpds"].publish("/igtoggles/fertilizer","0")

โดย "FBmcpds" คือชื่อ datasource สังเกตว่าชื่อ topic สำหรับ toggle widget ได้ถูกกำหนดโดยคำสั่งทั้งสองนี้เท่ากับ "/igtoggles/fertilizer" และค่าที่ส่งคือ 0 และ 1 เมื่อ toggle อยู่ในสถานะ OFF และ ON ตามลำดับ

รูปที่ 5 หน้าต่างสำหรับตั้งค่า toggle widget

ต่อมาเลือกการตั้งค่า slider widget สำหรับ LAMP จะปรากฏหน้าต่างดังรูปที่ 6 ฟิลด์ที่ต้องการแก้ไขคือ ONSTOP ACTION โดยคำสั่งจะทำงานเพียงครั้งเดียวหลังผู้ใช้หยุดเลื่อน slider (หากใส่ช่อง ONSLIDE ACTION คำสั่งจะถูกส่งไปหลายๆ ครั้งระหว่างเลื่อนซึ่งเปลืองแบนด์วิดท์โดยไม่จำเป็น) คำสั่งที่ใช้เป็นดังนี้


microgear["FBmcpds"].publish("/igsliders/lamp",value)

โดยจะเป็นตัวกำหนดชื่อ topic สำหรับ slider widget นี้คือ "/igsliders/lamp" และค่าที่ส่งไปคือตัวแปร value ซึ่งก็คือค่าที่สอดคล้องกับตำแหน่งของ slider ตัวนี้นั่นเอง

รูปที่ 6 การตั้งค่าในช่อง ONSTOP ACTION ของ slider ปรับค่า LAMP

การตั้งค่าสำหรับ valve slider จะใส่ข้อมูลลงในฟิลด์ ONSTOP ACTION เช่นเดียวกับในรูปที่ 6 เพียงแต่เปลี่ยนชื่อ topic เป็น "/igsliders/valve"


microgear["FBmcpds"].publish("/igsliders/valve",value)

โปรแกรม NodeMCU

ในส่วนโปรแกรมด้าน NodeMCU จะแก้ไขจากโปรแกรมเดิมใน LAB 5.5 ของคู่มือ โปรแกรมที่สมบูรณ์แล้วตั้งชื่อว่า DHT11LIGHT_FB_mcp.ino ที่ดาวน์โหลดได้จากด้านล่างของบทความนี้ แต่เพื่อให้เข้าใจหลักการทำงาน จะอธิบายการแก้ไขโปรแกรมเป็นขั้นตอน สิ่งแรกที่ต้องทำคือ subscribe topic ที่ส่งมาจาก Freeboard โดยเพิ่มคำสั่ง microgear.subscribe() ในฟังก์ชัน onConnected()


void onConnected(char *attribute, uint8_t* msg, unsigned int msglen) {
    Serial.println("Connected to NETPIE...");
    microgear.setAlias(ALIAS);
 
    microgear.subscribe("/igtoggles/fertilizer");    
    microgear.subscribe("/igsliders/#");    
}

สังเกตว่าเราใช้ wildcard # ช่วยลดจำนวนครั้งของการ subscribe topic จาก slider widgets

อธิบายหลักการรับข้อมูลของไลบรารี microgear โดยย่อคือ ข้อความใดๆ ที่ส่งมาให้กับ NodeMCU จะถูกจัดการโดยฟังก์ชัน onMsghandler() ที่มีนิยามดังนี้


void onMsghandler(char *topic, uint8_t* msg, unsigned int msglen);

จะเห็นว่ามีอาร์กิวเมนต์ topic อยู่ ซึ่งจะเป็นตัวแปรที่เก็บชื่อของ topic ที่ส่งมาให้เราโดยอัตโนมัติ กล่าวได้ว่าเป็นจุดเด่นของวิธีการนี้เพราะช่วยลดภาระการเขียนโปรแกรมเพื่อแยกสตริง ในขั้นต้นเราต้องการทราบว่าชื่อ topic ที่ส่งมาจาก Freeboard เป็นอย่างไร ดังนั้นจึงเพิ่มคำสั่งเพื่อแสดงข้อความออกพอร์ตอนุกรมดังนี้


String topicstring = String(topic);
Serial.print("Topic = ");
Serial.println(topicstring);

คอมไพล์และโหลดโปรแกรมลงบน NodeMCU เปิดหน้าต่าง Serial Monitor และทดลองกด toggle และเลื่อน sliders บน Freeboard จะเห็นการแสดงข้อความบน Serial Monitor ดังรูปที่ 7 โดยจะเห็นว่าชื่อ APPID ในที่นี้คือ myESP8266nov3 ถูกเพิ่มเข้าไปหน้าชื่อ topic ที่ตั้งไว้สำหรับ widget แต่ละตัว ส่วนค่าตัวเลขคือข้อมูลสถานะของ toggle และตำแหน่งของ sliders

รูปที่ 7 ข้อมูลที่ส่งมาจาก NETPIE widgets

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

ในที่นี้งานที่ต้องการทำมีเพียงควบคุมสถานะและความสว่างของ LED 3 ตัวที่นิยามไว้ใน LAB 5.5 ซึ่งโค้ดมีความยาวไม่มากดังนั้นจึงเขียนทั้งหมดไว้ในฟังก์ชัน onMsghandler() กรณีการทำงานที่ซับซ้อนขึ้นแนะนำให้เขียนเป็นฟังก์ชันแยกต่างหาก


void onMsghandler(char *topic, uint8_t* msg, unsigned int msglen) {
    Serial.print("Incoming message --> ");
    char *m = (char *)msg;  // needed to avoid conversion error
    m[msglen] = '\0';
    String valuestring = m;
    Serial.println(valuestring);
 
    String topicstring = String(topic);
 
    Serial.print("Topic = ");
    Serial.println(topicstring);  
    int value = 0;    // value from sliders and toggle  
 
    if (topicstring.equalsIgnoreCase("/myESP8266nov3/igsliders/lamp"))   {
       value = valuestring.toInt();
       lampvalue = map(value,0,100,0,1023);  
       analogWrite(LAMP,lampvalue);
       update_freeboard();
    }
    else if (topicstring.equalsIgnoreCase("/myESP8266nov3/igsliders/valve"))   {
       value = valuestring.toInt();
       valvevalue = map(value,0,100,0,1023);  
       analogWrite(VALVE,valvevalue);
       update_freeboard();
    }
    else if (topicstring.equalsIgnoreCase("/myESP8266nov3/igtoggles/fertilizer"))   {
        value = valuestring.toInt();
        fertilizer_state = value;  
        digitalWrite(FERTILIZER,fertilizer_state);
        update_freeboard();

    }
 
}

สำหรับการกำหนดค่าให้กับเอาต์พุตและการอัพเดทกลับไปยัง Freeboard widgets จะเหมือนกับใน LAB 5.5 ทุกประการ เมื่อคอมไพล์และโหลดโปรแกรม DHT11LIGHT_FB_mcp.ino ลงบน NodeICU จะพบว่าสามารถทำงานได้เช่นเดียวกับวิธีใช้ฟังก์ชันแปลคำสั่งใน LAB 5.5 (รูปที่ 8,9)

รูปที่ 8 ข้อความที่แสดงบน Serial Monitor ขณะรันโปรแกรม DHT11LIGHT_FB_mcp.ino
รูปที่ 9 การทดสอบโดย NETPIE Mobile App

วีดีโอแสดงการทดสอบ

สรุป

ในบทความนี้เราได้ศึกษาวิธีการควบคุมอุปกรณ์จาก NETPIE widgets หลายตัวโดยวิธีการกำหนด topic ที่แตกต่างกันสำหรับ widget แต่ละตัว วิธีการนี้มีจุดเด่นตรงช่วยลดภาระในการเขียนโปรแกรมเพื่อจัดการสตริง เนื่องจากชื่อ topic จะถูกแยกโดยฟังก์ชัน onMsghandler() ให้อัตโนมัติ ส่วนวิธีการเดิมที่เพิ่มสตริงคำสั่งเข้าไปในข้อความจะต้องเขียนโค้ดส่วนจัดการสตริงเพิ่มขึ้น แต่มีข้อดีคือสามารถกำหนดคำสั่งที่ผู้ใช้สามารถส่งผ่านพอร์ตอนุกรมและ NETPIE ได้โดยใช้ชื่อเดียวกัน ทำให้ควบคุมได้ทั้งจากหน้างานและระยะไกล

โปรแกรมที่ใช้ในตัวอย่าง : NETPIEfbmcp.zip

No comments:

Post a Comment

แนะนำหนังสือ “ตัวควบคุมป้อนกลับบนอินเทอร์เน็ตโดย ESP8266”

ปัจจุบันเมื่อกล่าวถึงอุปกรณ์ IoT (Internet of Things) คงมีน้อยคนที่จะไม่รู้จัก ในยุคที่การเข้าถึงอินเทอร์เน็ตเป็นกิจวัตรประจำวันของมนุษย์เ...