NETPIE Series
จากตัวอย่าง ex5_3.ino ในหนังสือ “ตัวควบคุมป้อนกลับบนอินเทอร์เน็ตโดย ESP8266” ได้แสดงการควบคุมและแสดงผลผ่าน NETPIE เบื้องต้น โดยใช้ slider widget ส่งคำสั่งอ้างอิงให้กับตัวควบคุม และส่งข้อมูล 3 ค่ากลับไปแสดงผล จากตัวอย่างจะเห็นว่าการส่งข้อมูลหลายชุดไปยัง NETPIE สามารถทำได้โดยง่าย แต่การที่จะส่งข้อมูลจาก NETPIE ไปยังบอร์ดมากกว่าหนึ่งตัวยังคงเป็นปัญหาที่ยังไม่มีการแสดงวิธีการอย่างชัดเจน ซึ่งจากการบรรยายในชั้นก็มีคำถามนี้จากผู้เรียน อ้างถึงตัวอย่างในหนังสือ เราได้ใช้คำสั่ง microgear.chat ใน Slider widget
microgear["myIoFCcontrol"].chat("myIoFCdevice",value)
ซึ่งทางด้านESP8266 จะมีฟังก์ชัน onMsghandler() เพื่อรับข้อความ
void onMsghandler(char *topic, uint8_t* msg, unsigned int msglen) {
char *m = (char *)msg;
m[msglen] = '\0';
float rc = atof(m);
Serial.print("new r = ");
if (rc>=0 && rc<=3) {
r = rc;
Serial.println(rc);
microgear.publish("/myIoFCdevice/rc",rc);
}
}
และแปลงให้เป็นตัวแปรแบบ float ก่อนจะอัพเดทตัวแปรคำสั่งอ้างอิง r และ publish กลับไปเพื่ออัพเดทตำแหน่งของ slider
ปัญหาที่เป็นประเด็นหลักของบทความนี้คือ สมมุติว่านอกจากส่งคำสั่งอ้างอิงแล้ว ต้องการมี sliders อีก 3 ตัวเพื่อปรับค่าพารามิเตอร์ PID จะทำอย่างไรให้ฟังก์ชัน onMsghandler() สามารถแยกได้ว่าคำสั่งมาจาก slider ตัวใด? วันนี้เราจะมาศึกษาส่วนนี้กันครับ
ผู้เขียนยอมรับว่ายังไม่มีความรู้เกี่ยวกับ NETPIE มากกว่าการใช้งานพื้นฐาน จึงได้ตั้งคำถามใน NETPIE Public Group และได้รับคำแนะนำที่เป็นประโยชน์จาก Project Leader ของ NETPIE เองว่า “ต้องระบุ มาใน msg ด้วยว่าค่าที่ส่งมานี้สำหรับ parameter ไหน อย่าส่งแต่ตัวเลข”
Bingo! ฟังดูแล้วเหมือนกับว่าวิธีการนี้ก็ไม่แตกต่างจากการสร้างฟังก์ชันรับคำสั่งที่อธิบายในหนังสือ เพียงแต่เปลี่ยนจากสื่อสารผ่านพอร์ตอนุกรมเป็นจาก Slider widgets มายัง onMsghandler() นั้นเอง เช่นหากรับข้อความว่า “r=1” ก็ต้องแยกคำสั่งคือ r และค่าที่ต้องการตั้งให้กับตัวแปร r คือ 1 ถ้าได้รับ “kp=0.4” ก็ตั้งค่าตัวแปร kp เป็น 0.4 เป็นต้น
ซึ่งโค้ดตรงส่วนนี้เรารู้แล้วว่าจะทำอย่างไรหากท่านมีหนังสือ หรือเพียงแต่ดาวน์โหลดตัวอย่างโปรแกรมจากหน้าเพจของหนังสือ สิ่งที่ต้องเรียนรู้เพิ่มคือจะทำอย่างไรให้ slider widget ส่งข้อมูลเพิ่มเติมมาพร้อมตัวเลข
หลังจากอ่านตัวอย่างจากคู่มือของ NECTEC และทดลองดู พบว่าเพียงเพิ่มข้อความที่ต้องการเข้าไปก็ทำงานได้ ตัวอย่างเช่นหากต้องการส่งข้อความ “r=xx” แทนตัวเลข “xx” อย่างเดียว แก้คำสั่งใน slider เป็นดังนี้
microgear["myIoFCcontrol"].chat("myIoFCdevice","r="+value)
เพื่อแสดงให้เห็นเป็นรูปธรรม เราจะสร้างตัวอย่างง่ายๆ ขึ้นมาใหม่โดยสนใจเพียงการส่งค่าจาก slider 3 ตัวมายังบอร์ด ESPino โดยฮาร์ดแวร์ที่ใช้ประกอบคือบอร์ด LAG3 ที่ออกแบบมาสำหรับทดลองใน workshop ดังแสดงในรูปที่ 1
โจทย์ของตัวอย่างนี้คือใช้ Slider 3 ตัว ส่งค่า R, G, B ในช่วง 0 – 1023 (PWM ของ ESP8266 มีขนาด 10 บิต) ให้กับบอร์ด ESPino ซึ่งจะทำหน้าที่ขับ RGB LED ให้มีเฉดสีตามค่าที่ตั้งโดย Sliders สำหรับตัวอย่างนี้จะไม่มีการส่งข้อมูลใดๆ ไปแสดงผลบน NETPIE
เริ่มต้นโดยล็อกอินเข้าไปที่ netpie.io และสร้างแอปปลิเคชันใหม่ ให้ชื่อว่า myRGB และสร้าง datasource บน freeboard ชื่อ myRGBboard รายละเอียดส่วนนี้จะเหมือนกับที่อธิบายในหนังสือจึงไม่ขอกล่าวถึงอีก เพิ่ม Sliders 3 ตัวบน Freeboard ให้ชื่อว่า R, G, B ดังในรูปที่ 2
microgear["myRGBboard"].chat("myRGBdevice","r="+value)
ซึ่งก็คือการเพิ่มสตริง “r=” ก่อนหน้าค่าตัวเลขจาก slider นั่นเอง สมมุติว่า slider ถูกปรับไปที่ 275 สตริงที่ส่งให้กับ ESP8266 ก็คือ “r=275” ทำให้เราสามารถเขียนโปรแกรมเพื่อตรวจสอบได้ว่าค่านี้ถูกส่งมาจาก R slider
สำหรับ G และ B Sliders ตั้งค่าด้วยคำสั่งตามลำดับดังนี้
microgear["myRGBboard"].chat("myRGBdevice","g="+value)
microgear["myRGBboard"].chat("myRGBdevice","b="+value)
ทดลองเลื่อนตัวปรับของ slider แต่ละอันแล้วดูที่ Serial Monitor รูปที่ 4 แสดงให้เห็นว่าฟังก์ชัน onMsghandler() สามารถรับค่าสตริงที่ถูกส่งมาได้อย่างถูกต้อง (ถึงแม้ว่าในการปรับแต่ละครั้งอาจเห็นข้อความซ้ำกันมากว่า 1 ครั้งก็ไม่เป็นปัญหาแต่อย่างใดสำหรับตัวอย่างนี้)
pinMode(RLED, OUTPUT);
pinMode(GLED, OUTPUT);
pinMode(BLED, OUTPUT);
โดยนิยาม RLED=12, GLED=2, BLED = 14 ไว้ส่วนบนของโปรแกรม สำหรับการส่งค่า PWM ให้กับขา R,G,B ของ LED เขียนเป็นฟังก์ชันเพื่อความเป็นระเบียบ
void lidRGBled(int rval, int gval, int bval)
{
analogWrite(RLED, rval);
analogWrite(GLED, gval);
analogWrite(BLED, bval);
}
การแยกค่า R,G,B ทำได้หลายวิธีตามที่ท่านถนัด ในที่นี้เราใช้วิธีที่เคยทดลองแล้วว่าได้ผล เขียนเป็นฟังก์ชันได้ดังนี้
void cmdInt(void)
{
rcvdstring.trim(); // remove leading&trailing whitespace, if any
// find index of separator "="
sepIndex = rcvdstring.indexOf('=');
// extract command and parameter
cmdstring = rcvdstring.substring(0, sepIndex);
parmstring = rcvdstring.substring(sepIndex+1);
if (cmdstring.equalsIgnoreCase("r")) {
Rval=parmstring.toInt();
}
else if (cmdstring.equalsIgnoreCase("g")) {
Gval=parmstring.toInt();
}
else if (cmdstring.equalsIgnoreCase("b")) {
Bval=parmstring.toInt();
}
}
อธิบายโดยสังเขปได้ว่าค่าสตริงที่ถูกเก็บในตัวแปร rcvdstring ถูกแยกเป็นส่วนของคำสั่ง (ก่อนหน้าเครื่องหมาย “=”) และพารามิเตอร์ (หลังเครื่องหมาย “=”) เก็บในตัวแปร cmdstring และ parmstring ที่เป็น String object มีข้อดีคือมี methods ให้ใช้ได้โดยสะดวก ตัวอย่างเช่นการเปลี่ยนค่าสตริงเป็นเลขจำนวนเต็มใช้ parmstring.toInt()
สำหรับฟังก์ชัน onMsghandler() เขียนใหม่ได้เป็นดังนี้
void onMsghandler(char *topic, uint8_t* msg, unsigned int msglen) {
char *m = (char *)msg;
m[msglen] = '\0';
rcvdstring = m; // คัดลอกข้อความใส่ใน String object
cmdInt(); // แยกคำสั่งตามสี R,G,B
lidRGBled(Rval,Gval,Bval); // ส่งเอาต์พุตไปยัง LED
showOLED(Rval,Gval,Bval); // แสดงค่าบนจอ OLED
}
หมายเหตุ: ตัวแปรบางส่วนนิยามไว้แบบ global ที่ส่วนบน ซึ่งโปรแกรมทั้งหมดรวมอยู่ใน rgb3sliders.ino สามารถดาวน์โหลดได้ด้านล่าง
คอมไพล์ rgb3sliders.ino (อย่าลืมใส่ข้อมูล NETPIE Application และ WiFi AP ของท่านก่อน) และโหลดลงบนบอร์ด (ที่ต่ออยู่กับ RGB LED) วีดีโอด้านล่างนี้แสดงให้เห็นว่า slider ทั้งสามสามารถส่งคำสั่งให้กับ RGB LED ได้อย่างถูกต้อง)
555+
ReplyDeleteขอบคุณมากๆครับ กำลังหาแนวทางใช้ slider พอดีเลยครับผม ^_^
ReplyDeleteขอโคดแบบเต็มๆหน่อยได้ใหมครับ ผมพึ่งเริ่มเรียน
ReplyDeleteโค้ดเต็มๆ อยู่ในไฟล์ rgb3sliders.ino มีลิงก์ให้โหลดได้ครับที่ด้านล่างบทความ
ReplyDelete