วันอังคารที่ 1 กันยายน พ.ศ. 2558

Node-RED – ESP8266

เลือกพัฒนาบน Arduino 1.6.5 และ NodeMCU 1.0 (ESP-12E Module)
ถ้าถนัดตัวอื่นก็ลองนำเอาเนื้อหาไปทดลองปรับใช้กันนะครับ
ส่วนตัวเห็นว่าเป็น Arduino ง่ายต่อการทำความเข้าใจ
บอร์ดที่ใช้ อันนี้อุดหนุนจากสมาชิกในกลุ่ม ESP8266 Thailand ไม่แน่ใจว่าจะยังมีจำหน่ายอยู่หรือป่าว
Copy of Copy of LINEcamera_share_2015-08-30-22-46-51
บอร์ดที่ใช้นี้เข้าโหมดโปรแกรมด้วยการปลดสายจ่ายไฟ กดปุ่ม FLASH ค้างไว้
จากนั้นเสียบสายจ่ายไฟ สักพักค่อยปล่อยปุ่ม FLASH แล้วก็สั่ง upload
ขาที่ต่อกับ LED ซึ่งในโค๊ดคือขา BUILTIN_LED - 2
Copy of Copy of LINEcamera_share_2015-08-30-22-48-01
ตัวโค๊ดผมได้วางไว้ท้ายบทความครับ
โค๊ดจะใช้งานไลบรารี่อยู่ 2 ตัวคือ ESP8266WiFi และ ArduinoJson
ตัวแรกน่าจะมีอยู่แล้ว ส่วนของ ArduinoJson เข้าเวบหากันได้ครับ
เพื่อให้เข้าใจ ก็เลยขอแยกโค๊ดในส่วนที่สำคัญมาอธิบาย ดังนี้
ส่วนแรกเป็นการเชื่อมต่อบอร์ดกับ WiFi
const char* ssid = "ชื่อ WiFi router";
const char* password = "รหัสผ่าน";
const char* host = "dweet.io";
ตัวแปร ssid ให้ใส่ชื่อ WiFi router
ตัวแปร password สำหรับเจ้าใช้งาน router
ส่วนของ host ในที่นี้เราเลิอกใช้ Dweet.io ที่ได้ใช้มาตั้งแต่โพสต์ก่อนๆ
สำหรับ Service อื่นๆ สามารถทดลองใช้ดูครับ แต่ก็จะต้องทราบว่าข้อมูลที่ตอบกลับมามีรูปแบบเป็นอย่างไร
การเชื่อมต่อกับ LED
pinMode(BUILTIN_LED - 2, OUTPUT);
BUILTIN_LED ถูกกำหนดอยู่ในไลบรารี่ สำหรับบอร์ดที่ใช้ไม่ได้ต่อขานี้ให้ใช้งาน
แต่พบว่าสามารถใช้ขา BUILTIN_LED - 2 ได้
ในส่วนขอโค๊ดกำหนดขานี้ให้เป็นแบบบ output ต่อกับ LED
WiFi.begin(ssid, password);
ทำการเชื่อมต่อ ESP8266 กับ WiFi router
int count = 0;
while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
if(++count == 10){
Serial.println();
count = 0;
}
}
ส่วนนี้จะทำการเชื่อมต่อกับ router ซึ่งจะวนไปเรื่อยๆ จนกว่าจะเชื่อมต่อได้สำเร็จ
ให้เปิด Serial monitor จะเห็นเครื่องหมายจุดต่อเนื่องกัน
เมื่อต่อสำเร็จก็จะแสดงหมายเลข IP
ถ้าพบว่าไม่สามารถเชื่อมต่อได้ ให้ตรวจสอบ ssid กับ password ว่าถูกต้องหรือไม่
image
เมื่อได้หมายเลข IP แล้ว ก็เป็นการจบกระบวนการ setup
ต่อไปเป็นส่วนของ loop ซึ่งจะทำการส่งคำร้อง (request) ไปที่ Dweet.io
และรอการตอบกลับ เมื่อได้ข้อมูลครบถ้วน ก็จะนำไปจัดการแยdข้อมูลที่มาแบบ Json ออก
โดยอาศัยไลบรารี่ ArduinoJson
WiFiClient client;
const int httpPort = 80;
if(!client.connect(host, httpPort)){
Serial.println("Connection failed");
return;
}
ตัวแปร client ทำหน้าที่เชื่อมต่อ ESP8266 ไปยัง host เพื่อขอข้อมูล เหมือนตอนที่เราใช้ browser
หรือให้ Node-RED ขอข้อมูลจาก Dweet.io ในโพสต์ก่อนหน้า
ถ้าเชื่อมต่อไม่สำเร็จ ก็จะแสดง Connection failed มาที่ Serial monitor
ตรวจสอบชื่อ host อีกครั้ง
String url = "/get/latest/dweet/for/Node-RED-Supotsaeea";
Serial.print("Requesting URL: ");
Serial.println(url);
client.print(String("GET ") + url + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Cache-Control: no-cache\r\n" +
"Connection: close\r\n\r\n");
delay(1000);
ส่วนนี้เห็นแล้วน่าจะคุ้นๆ กันนะครับ ตัวแปร url เป็นส่วนที่เราเขึยนต่อจากชื่อโฮส
ตอนที่เราทดสอบอ่านค่าด้วย browser
ตัวแปร url จะไปประกอบกับส่วนอื่นเพื่อให้ client ส่งการร้องขอไปที่ Dweet.io
มีส่วนหนึ่งที่เพิ่มเติมเข้าไป ส่วนนี้ไม่มีในตัวอย่างของไลบรารี่ ESP8266WiFi
คือ Cache-Control:no-cache
เนื่องจากเมื่อทดลองแล้วพบว่าหลังจากทำการรับค่าไปสักระยะหนึ่ง ค่าที่ส่งกลับมาจะไม่ได้เป็นข้อมูลปัจจุบัน
แต่จะนำเอาค่าเก่าหรือค่าที่อยู่ใน cache ส่งมาให้ ทำให้เมื่อสั่งปิด เปิด LED เหมือนจะไม่ทำงาน
ทั้งๆที่ Dweet.io ก็มีข้อมูลส่งกลับมา
ใครพบปัญหาลักษณะนี้ ก็ลองเพิ่มส่วนนี้เข้าไปในโค๊ดดูครับ
ส่วนของ delay(1000) เช่นกันเป็นส่วนที่เพิ่มเติม เพราะการตอบสนองของโฮสอาจจะไม่ทัน
ทำให้ไม่ได้รับข้อมูล ค่า delay ที่กำหนดอาจจะต้องมากหรือน้อยกว่านี้ ให้ทดลองปรับเปลี่ยนกันเองครับ
String line = "";
while(client.available()){
line = client.readString();
}
line[line.length() + 1] = '}';
Serial.print(line.lastIndexOf("{")); Serial.print(","); Serial.println(line.indexOf("}"));line = line.substring(line.lastIndexOf("{"),line.indexOf("}") + 1);
Serial.println(line);
client จะทำการวนรับข้อมูล โดยจะทำการเก็บข้อมูลไว้ที่ตัวแปร line
ซึ่งต้องทำการปรับตัวแปร line เล็กน้อย ในที่นี้จะทำการเพิ่มตัวอักษร "{"
เพื่อให้ไลบรารี่ ArduinoJson ทำการแยกข้อมูลให้เรา
เนื่องจากเมื่อทดลองเอาข้อมูลทั้งหมดมาแยกจะไม่สำเร็จ เหมือนว่ารูปแบบของตัวแปร line ไม่ถูกต้อง
รูปแบบของ Json เป็นแบบ key:value ซึ่งจะถูกเรียกว่า JsonObject แต่จะมีรูปแบบ JsonArray
ที่ทำการเก็บ JsonObject หลายๆตัวรวมกัน สำหรับข้อมูลที่ส่งคืนมาจาก Dweet.io จะมีรูปแบบ JsonArray อยู่ด้วย
ซึ่งอาจจะทำให้เกิดข้อผิดพลาดขึ้น
ส่วนประกอบของข้อมูลที่ส่งคืนมา
{"this":"succeeded","by":"getting","the":"dweets",
"with":
  [
    {
      "thing":"Node-RED-Supotsaeea",
      "created":"2015-08-30T14:55:48.297Z",
            "content":{"LED":1}
    }
  ]
}

จะเห็นว่าส่วนของข้อมูลที่เราต้องการไปอยู่ในวงเล็บแบบ [ ] เป็นแบบอะเรย์


ยังไม่แน่ใจว่าเป็น bug ของไลบรารี่ ArduinoJon หรือไม่


แต่เพื่อแก้ไขปัญหาจึงดึงเอาข้อมูลเฉพาะ ส่วนของ content มา ซึ่งจริงแล้วตัวแปรที่เราต้องการ


ก็จะอยู่ในส่วนนี้เท่านั้น เอาว่าอันนี้เป็นรูปแบบเฉพาะของ Dweet.io ส่งมา ถ้าเป็น Service ของที่อื่น


อย่าง ThingSpeak อาจมีข้อแตกต่างกันไป






StaticJsonBuffer<300> jsonBuffer;
char json[300];
line.toCharArray(json, line.length()+1);JsonObject& root = jsonBuffer.parseObject(json);
if(!root.success()){
Serial.println("parseObject() failed");
} else {
const char* _LED = root["LED"];

Serial.print("LED:"); Serial.println(_LED);


if(_LED)

{
if(_LED[0] == '1') digitalWrite(BUILTIN_LED - 2, HIGH);
if(_LED[0] == '0') digitalWrite(BUILTIN_LED - 2, LOW);
}
}

ส่วนนี้จะเป็นการแยกข้อมูลโดยใช้ ArduinoJson ผลของการแยกส่วนจะเก็บไว้ที่ตัวแปร root


ในลักษณะของอะเรย์ สำหรับของเรามีตัวแปร LED ตัวเดียว ก็ทำการเก็บค่าไว้ที่ตัวแปร _LED


const char* _LED = root[“LED”];


สมมุติว่ามีการฝากค่า RELAY ไว้ด้วย ก็ทำการดึงค่าเก็บใส่ตัวแปรไว้ได้


const char* _RELAY = root[“RELAY”];


การนำค่าไปใช้ต้องทำการตรวจสอบตัวแปรก่อนว่ามีอยู่หรือไม่


if(_LED)

{
if(_LED[0] == '1') digitalWrite(BUILTIN_LED - 2, HIGH);
if(_LED[0] == '0') digitalWrite(BUILTIN_LED - 2, LOW);
}

นำค่าที่ได้รับไปควบคุม LED ให้ติดหรือดับ


ทดลองปิดเปิด LED ด้วย browser หรือ Dashboard หรือ Flow ใน Node-RED editor ได้เลยครับ


บอร์ดอื่นไม่แน่ใจว่าจะต้องปรับอะไรหรือไม่ ตอนนี้มีบอร์ดนี้ให้ทดลองอยู่แบบเดียวครับ


ส่วนการปรับโปรแกรมด้วย editor ตัวอื่นๆ ก็คงต้องให้ผู้เชี่ยวชาญท่านอื่นช่วย


สำหรับผมคงใช้ Arduino IDE เป็นหลัก


โพสต์หน้าคงจะทดลองให้ส่งข้อความเตือนไปที่ tweeter ดูกันครับ


-------------------------------------------------------------------


Arduino Code (1.6.5)


#include <ESP8266WiFi.h>

#include <ArduinoJson.h>

const char* ssid = "ชื่อ WiFi router";

const char* password = "รหัสผ่าน";
const char* host = "dweet.io";                     //ชื่อโฮสที่เก็บข้อมูล

void setup() {

Serial.begin(115200);
delay(10);
pinMode(BUILTIN_LED - 2, OUTPUT);     // BUILTIN_LED ถูกกำหนดไว้ในไลบรารี่ ESP8266WiFi

Serial.println();

Serial.println();
Serial.print("Connection to ");
Serial.print(ssid);

WiFi.begin(ssid, password);


int count = 0;

while(WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
if(++count == 10){
Serial.println();
count = 0;
}
}

Serial.println("");

Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());

}


void loop() {

delay(500);
Serial.print("Connecting to ");
Serial.println(host);

WiFiClient client;


const int httpPort = 80;


if(!client.connect(host, httpPort)){

Serial.println("Connection failed");
return;
}

String url = "/get/latest/dweet/for/Node-RED-Supotsaeea";


Serial.print("Requesting URL: ");

Serial.println(url);

client.print(String("GET ") + url + " HTTP/1.1\r\n" +

"Host: " + host + "\r\n" +
"Cache-Control: no-cache\r\n" +
"Connection: close\r\n\r\n");

delay(1000);


String line = "";

while(client.available()){
line = client.readString(); // .readStringUntil('\r');
}
line[line.length() + 1] = '}';
Serial.print(line.lastIndexOf("{")); Serial.print(","); Serial.println(line.indexOf("}"));

line = line.substring(line.lastIndexOf("{"),line.indexOf("}") + 1);

Serial.println(line);

StaticJsonBuffer<300> jsonBuffer;

char json[300];
line.toCharArray(json, line.length()+1);
JsonObject& root = jsonBuffer.parseObject(json);

if(!root.success()){

Serial.println("parseObject() failed");
} else {
const char* _LED = root["LED"];

Serial.print("LED:"); Serial.println(_LED);


if(_LED)

{
if(_LED[0] == '1') digitalWrite(BUILTIN_LED - 2, HIGH);
if(_LED[0] == '0') digitalWrite(BUILTIN_LED - 2, LOW);
}
}

Serial.println();

Serial.println("Close connection");
}



1 ความคิดเห็น: