สวัสดีครับ จากตอนที่แล้ว ที่ผมพูดถึงรายละเอียดต่างๆ ที่จะใช้ในการทำ ระบบเปิด/ปิด ไฟ
ว่าต้องใช้อะไรบ้าง วันนี้ จะมาลงรายละเอียด ในแต่ละส่วนกันครับ ..
อ่านตอนที่ 1 ได้ที่ LINE Bot + node.js + MQTT + ESP32 (IoT) เปิด/ปิด ไฟ (ตอนที่ 1)
การทำงานคือ ผมจะสามารถสั่ง ให้บอท (LINE Messaging API) สั่งเปิด/ปิดไฟ ได้ผ่าน การ chat ดังภาพ
0.ความรู้ที่ต้องมี ในบทความนี้
– การใช้งาน Arduino IDE เบื้องต้น
– การเขียน node.js เบื้องต้น
– การใช้งาน LINE Messaging API เบื้องต้น
– การใช้งาน Git เบื้องต้น
1.เตรียมอุปกรณ์
สำหรับบอร์ด ESP32 ที่ผมจะใช้ในการทดลองนี้ จะเป็น Node32 Lite นะครับ สามารถหาซื้อได้ที่นี่ครับ ราคา 275 บาท
https://www.gravitechthai.com/product-detail.php?WP=pQugZKpmGQAgG2rDqYyc4Uuw
ปล.หรือถ้าเพื่อนๆ มีบอร์ดอื่นๆ เช่น ESP8266, NodeMCU อยู่แล้ว ก็สามารถใช้งานได้เหมือนกันครับ
2.สมัครใช้บริการ LINE Messaging API
ตรงส่วนวิธีการสมัครใช้งาน LINE Messaging API มีหลายๆ ท่านเขียนบทความไว้แล้ว ตัวอย่างเช่น
LINE Bot 101 — จับมือทำบอท ของคุณ Sitthi Thiammekha (LINE API Expert)
สร้าง LINE Bot ด้วย Node.js + Messaging API — A Beginner’s Guide ของคุณ Ingkwan
3.สมัครใช้บริการ Heroku สำหรับ CloudMQTT Server
เมื่อกรอกรายละเอียดการสมัครเสร็จแล้ว เราจะเข้าสู่ Dashboard ของ Heroku ครับ
– ที่หน้า Dashboard เลือก New > Create new app
– ตั้งชื่อ App ของเรา ในที่นี้ผมตั้งชื่อ esp32-mqtt
ถ้าสร้างสำเร็จ เราจะเข้าหน้าแรก ของ App เราได้ละ อย่างของผมจะเป็น
https://esp32-mqtt.herokuapp.com/
– ที่ Resources เลือก Find more add-ons
– เลือก CloudMQTT
– เลือก Install CloudMQTT
– เลือก App ที่จะ Install CloudMQTT ในที่นี้ ของผมคือ esp32-mqtt
– เลือก Provision add-on
– เสร็จแล้วจะได้ดังนี้ ให้เราคลิกเพื่อดูรายละเอียดของ CloudMQTT ของเราได้ที่ menu ด้านล่าง
– ค่า config CloudMQTT ตรงนี้ เดี๋ยวเราจะใช้กับ webhook ที่เป็น node.js ของเรานะครับ
4.LINE Bot API Code (node.js)
File หลักๆ ที่เราจะใช้ ในการ Deploy ขึ้น Heroku App ของเรา มี 2 file ครับคือ
– Procfile (ไม่ต้องมี .txt) นะ ใช้สำหรับบอกว่า ให้ App เราทำงานแบบไหน เรียก file ไหน
– index.js เป็น file หลัก ของ LINE Bot API เราครับ
จากนั้น เรามาดูที่ code ที่เป็น LINE Bot API กันครับ
ส่วนที่เราจะต้องแก้ไข หลักๆ มีด้วยกัน 3 จุดครับคือ
– CH_ACCESS_TOKEN คือค่า ยาวๆ ที่เราได้จาก Channel access token (long-lived) ของ Messaging API
– mqtt_host คือค่าที่เราได้จาก CloudMQTT บน Heroku
– options คือค่าต่างๆ ที่เราได้จาก CloudMQTT บน Heroku เช่นกันครับ
Code LINE Bot API ที่เป็น node.js สำหรับ Deploy ขึ้น Heroku ของเราครับ
ใครที่เขียน code คล่องๆ แก้ไข ให้ code สวยงาม กระชับกว่านี้ได้นะครับ อันนี้ผมเขียนแบบด่วนๆ มาก 🙁
Procfile
web: node index.js
index.js
var express = require('express') var bodyParser = require('body-parser') var request = require('request') var app = express() var mqtt = require('mqtt'); // Your Channel access token (long-lived) const CH_ACCESS_TOKEN = ''; // MQTT Host var mqtt_host = 'mqtt://m15.cloudmqtt.com'; // MQTT Topic var mqtt_topic = '/ESP32'; // MQTT Config var options = { port: 15443, host: 'mqtt://m15.cloudmqtt.com', clientId: 'mqttjs_' + Math.random().toString(16).substr(2, 8), username: 'mqttuser', password: 'mqttpass', keepalive: 60, reconnectPeriod: 1000, protocolId: 'MQIsdp', protocolVersion: 3, clean: true, encoding: 'utf8' }; app.use(bodyParser.json()) app.set('port', (process.env.PORT || 4000)) app.use(bodyParser.urlencoded({extended: true})) app.use(bodyParser.json()) app.post('/webhook', (req, res) => { var text = req.body.events[0].message.text.toLowerCase() var sender = req.body.events[0].source.userId var replyToken = req.body.events[0].replyToken console.log(text, sender, replyToken) console.log(typeof sender, typeof text) // console.log(req.body.events[0]) if (text === 'info' || text === 'รายงาน') { // Info inFo(sender, text) } else if (text === '1' || text === 'เปิด' || text === 'on') { // LED On ledOn(sender, text) } else if (text === '0' || text === 'ปิด' || text === 'off') { // LED Off ledOff(sender, text) } else { // Other sendText(sender, text); } res.sendStatus(200) }) function sendText (sender, text) { let data = { to: sender, messages: [ { type: 'text', text: 'กรุณาพิมพ์ : info | on | off | เปิด | ปิด เท่านั้น' } ] } request({ headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer '+CH_ACCESS_TOKEN+'' }, url: 'https://api.line.me/v2/bot/message/push', method: 'POST', body: data, json: true }, function (err, res, body) { if (err) console.log('error') if (res) console.log('success') if (body) console.log(body) }) } function inFo (sender, text) { let data = { to: sender, messages: [ { type: 'text', text: 'uid: '+sender } ] } request({ headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer '+CH_ACCESS_TOKEN+'' }, url: 'https://api.line.me/v2/bot/message/push', method: 'POST', body: data, json: true }, function (err, res, body) { if (err) console.log('error') if (res) console.log('success') if (body) console.log(body) }) } function ledOn (sender, text) { var client = mqtt.connect(mqtt_host, options); client.on('connect', function() { // When connected console.log('MQTT connected'); // subscribe to a topic client.subscribe(mqtt_topic, function() { // when a message arrives, do something with it client.on('message', function(topic, message, packet) { console.log("Received '" + message + "' on '" + topic + "'"); }); }); // publish a message to a topic client.publish(mqtt_topic, 'on', function() { console.log("Message is published"); client.end(); // Close the connection when published }); }); let data = { to: sender, messages: [ { type: 'text', text: 'LED ON' } ] } request({ headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer '+CH_ACCESS_TOKEN+'' }, url: 'https://api.line.me/v2/bot/message/push', method: 'POST', body: data, json: true }, function (err, res, body) { if (err) console.log('error') if (res) console.log('success') if (body) console.log(body) }) } function ledOff (sender, text) { var client = mqtt.connect(mqtt_host, options); client.on('connect', function() { // When connected console.log('MQTT connected'); // subscribe to a topic client.subscribe(mqtt_topic, function() { // when a message arrives, do something with it client.on('message', function(topic, message, packet) { console.log("Received '" + message + "' on '" + topic + "'"); }); }); // publish a message to a topic client.publish(mqtt_topic, 'off', function() { console.log("Message is published"); client.end(); // Close the connection when published }); }); let data = { to: sender, messages: [ { type: 'text', text: 'LED OFF' } ] } request({ headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer '+CH_ACCESS_TOKEN+'' }, url: 'https://api.line.me/v2/bot/message/push', method: 'POST', body: data, json: true }, function (err, res, body) { if (err) console.log('error') if (res) console.log('success') if (body) console.log(body) }) } app.listen(app.get('port'), function () { console.log('run at port', app.get('port')) })
– CH_ACCESS_TOKEN คือค่ายาวๆ ตรงนี้แหละครับ
– mqtt_host กับ options คือค่าที่ได้จาก CloudMQTT ตรงนี้ครับ
5.Deploy Webhook API code ขึ้น Heroku
จาก Step 3 ที่เราสร้าง App ของเราไว้ อย่างของผมจะชื่อ esp32-mqtt ให้เราเข้ามาที่ Deploy
– อันดับแรกเลย ถ้าเรายังไม่เคยใช้ Heroku ให้ Download และ Install Heroku CLI กันก่อนที่
Download and install the Heroku CLI
– หลังจากลง Heruku CLI เสร็จแล้ว ที่ Terminal พิมพ์ heroku login
– ตัว Browser จะเด้งหน้า Heroku Login ขึ้นมา ให้เราทำการ Login ให้เรียบร้อย
– เมื่อเรา Login สำเร็จแล้ว ก็จะเข้าสู่ขั้นตอนถัดไป ที่ Terminal (CLI)
ทำการสร้าง Git repository
$ cd my-project/
$ git init
$ heroku git:remote -a esp32-mqtt
ทดลอง Deploy
$ git add .
$ git commit -am “make it better”
$ git push heroku master
จากนั้น สร้าง file จาก Step 4 คือ Procfile กับ index.js ที่ Directory ที่สร้างขึ้น อย่างของผมจะเป็น esp32-mqtt
จากนั้น install module ที่จำเป็น ที่เราใช้ในการเขียน App ของเรา ดังนี้
$ npm install express –save
$ npm install request –save
$ npm install mqtt –save
$ npm init
เราจะได้ file ต่างๆ เพิ่มเข้ามา ใน directory ของเรา
ทดลอง run Wehhook API ที่เราเขียน ด้วย node.js ค่า default จะเป็น port 4000
node index.js
เข้าผ่าน localhost:4000 จะได้ดังรูป Error ไม่เป็นไรครับ ถือว่าทำงานได้แล้ว เพราะเราไม่รับ GET
ทำการ Deploy code จริง ขึ้น Heroku
$ git add .
$ git commit -am “add Procfile index.js”
$ git push heroku master
จากนั้นลองเข้า App ของเราผ่าน URL ที่เราทำการตั้งไว้ อย่างของผมจะเป็น
https://esp32-mqtt.herokuapp.com/
หน้าจะจะได้แบบนี้ ไม่ต้องตกใจครับ เพราะเราไม่รับ GET
แต่ถ้าไม่ได้ ให้เราลองใช้ CLI ดู logs ว่าผิดพลาดตรงไหน
$ heroku logs –tail
เสร็จแล้วครับ LINE Bot API (webhook) ของเรา บน Heroku .. 🙂
6.Config Webhook API ของเราเข้ากับ LINE Messaging API
มาถึงขั้นตอนสุดท้ายในส่วนของ Webhook API ของเรากันแล้วครับ นั่นคือ การผูกกับ LINE Messaging API
– ขั้นตอนแรก เราต้องไป Use webhooks ให้ Enabled แล้ว Save ก่อนนะครับ ตกม้าตาย ตรงนี้กันมาเยอะแล้ว 🙁
– จากนั้น ใส่ URL ของ App เรา ที่ได้จาก Heroku แล้วตามด้วย /webhook ครับ อย่างของผมก็จะเป็น
https://esp32-mqtt.herokuapp.com/webhook
– จากนั้น กด Verify ดูครับ ถ้าไม่มีอะไรผิดพลาด ก็จะ Success ครับ 🙂
– ประมาณนี้ ก็เป็นอันว่า เรียบร้อยแล้วครับ LINE Messaging API กับ Webhook บน Heroku เราใช้งานได้แล้ว 🙂
7.เขียน code Arduino ให้กับ Node32 Lite
ดูวิธีการติตตั้งบอร์ดใหม่ๆ (Node32 Lite) เข้าไปใน Arduino IDE ได้ที่นี่ครับ
http://www.ayarafun.com/2018/12/how-to-setup-lamloei-32-lite-with-arduino/
เลือกชนิดของ Board ที่เราใช้ให้ถูกต้อง แล้วทำการ Verify และ Upload Code โลดครับ ..
– ในที่นี้ผมใช้บอร์ด Node32 Lite จะต้องเลือกเป็น Node32s รุ่นพี่มันครับ 🙂
ตัว Arduino IDE ให้เราทำการ add Library PubSubClient กับ WiFiManager เข้าไปด้วยครับ
– ไปที่ Tools > Manage Libraries…
– Manage Libraries…
– Add Lib PubSubClient
– Add Lib WiFiManager
สำหรับ code จะมี 2 แบบ คือ
แบบ ที่ 1 fix SSID กับ password ไปใน code เลย แบบนี้จะดี ตรงที่เราไม่ต้องมา config ให้บอร์ดเราไปเกาะ WiFi อีกรอบ เหมาะสำหรับการทดลอง หรือใช้ SSID เดิมตลอด ..
– ถ้าเราดูจาก Serial Monitor จะเห็นรายละเอียดการ connect แบบ fix SSID ดังนี้
แบบ ที่ 2 ใช้ WiFiManager คือจะสามารถ config ให้ Node32 Lite ของเราไปเกาะ SSID (AP) ตัวที่เราต้องการได้ อันนี้จะสะดวก เวลาไปใช้งานจริง ไม่ต้อง Upload code เข้าไปใหม่
– วิธีการ config ด้วย WiFiManager หลังจาก Upload Code เสร็จ ให้เราไปเกาะ SSID ชื่อ ESP32_xxxxxx จะขึ้นหน้า menu config ดังรูป
– จากนั้นเลือก SSID(AP) ที่เราต้องการจะให้ไปเกาะ ใส่ password แล้วกด Save
– ถ้าเราดูจาก Serial Monitor จะเห็นรายละเอียดการ connect แบบใช้ WiFiManager ดังนี้
มาดูในส่วนของ Code กันครับ
Code แบบที่ 1 fix SSID กับ password ใน code
#include <PubSubClient.h> #include <WiFi.h> // Update these with values suitable for your network. const char* ssid = "tonofarm.io"; // AP Name const char* password = "********"; // AP Password // Config MQTT Server #define mqtt_server "m15.cloudmqtt.com" #define mqtt_port 15443 #define mqtt_user "mqttuser" #define mqtt_password "mqttpass" WiFiClient espClient; PubSubClient client(espClient); void setup() { // Set LED_BUILTIN pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); delay(10); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); } void loop() { if (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect("ESP32Client", mqtt_user, mqtt_password)) { Serial.println("connected"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); return; } } else { // MQTT Topic /ESP32 client.subscribe("/ESP32"); } client.loop(); } void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); String msg = ""; int i = 0; while (i < length) msg += (char)payload[i++]; Serial.println(msg); digitalWrite(LED_BUILTIN, (msg == "on" ? LOW : HIGH)); }
Code แบบที่ 2 ใช้ WiFiManager ในการ config
#include <FS.h> //this needs to be first, or it all crashes and burns... #include <PubSubClient.h> #include <WiFi.h> #include <DNSServer.h> #include <WiFiManager.h> //flag for saving data bool shouldSaveConfig = false; //callback notifying us of the need to save config void saveConfigCallback () { Serial.println("Should save config"); shouldSaveConfig = true; } // Config MQTT Server #define mqtt_server "m15.cloudmqtt.com" #define mqtt_port 15443 #define mqtt_user "mqttuser" #define mqtt_password "mqttpass" WiFiClient espClient; PubSubClient client(espClient); void setup() { // Set LED_BUILTIN pinMode(LED_BUILTIN, OUTPUT); Serial.begin(115200); delay(10); Serial.println(); WiFiManager wifiManager; wifiManager.autoConnect(); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); } void loop() { if (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect("ESP32Client", mqtt_user, mqtt_password)) { Serial.println("connected"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); return; } } else { // MQTT Topic /ESP32 client.subscribe("/ESP32"); } client.loop(); } void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); String msg = ""; int i = 0; while (i < length) msg += (char)payload[i++]; Serial.println(msg); digitalWrite(LED_BUILTIN, (msg == "on" ? LOW : HIGH)); }
Source Code ใน GitHub ตามนี้เลยครับ https://github.com/pornpasok/esp32_line_bot_mqtt
8.เรียบร้อยแล้ว ทดสอบใช้งาน
ทำการ Add LINE Bot ของเรา ก่อนนะ จากนั้นทดลองดังนี้ได้เลย
– ถ้าเราพิมพ์ On/on/เปิด/1 ไฟ LED ที่ตัว Node32 Lite ของเราก็จะติดครับ
– ถ้าเราพิมพ์ Off/off/ปิด/0 ไฟ LED ที่ตัว Node32 Lite ของเราก็จะดับครับ
– ถ้าเราพิมพ์ info/รายงาน บอทของเรา ก็จะรายงาน UID ของเราครับ เพื่อเอาไปใช้งานต่อยอดอื่นๆ
ปล. เดี๋ยวพรุ่งนี้ ถ่าย Video มาให้ดูกันนะครับ ว่าการทำงานของมันเป็นยังไง ถ่ายเองกดเอง ไม่ไหว 🙁
Next Step …
– ต่อกับ Relay เพื่อใช้งานกับอุปกรณ์ไฟฟ้าจริงครับ (อันนี้อันตรายนะครับ ต้องระวังด้วยเพราะเล่นกับไฟ 220v)
– ต่อยอดให้ LINE Bot ของเรา มีความสามารถมากกว่านี้ครับ เช่น monitor หรือรายงาน ค่าต่างๆ ให้เราได้
– ทำ Box ให้ดี ให้สวยงาม กันน้ำ ติด Solar Cells เอาไปใช้งานจริงกันครับ ..
สำหรับท่านใด ที่มีข้อสงสัย หรือไม่เข้าใจตรงส่วนไหน สามารถ Post ถามได้ที่บทความนี้ครับ
หรือ Add LINE ID: pornpasok เข้ามาสอบถามก็ได้ครับ ถ้าตอบได้ผมจะตอบให้นะครับ 🙂
I have question~
When I do “git push heroku master”
GIT CMD respont ” ! No default language could be detected for this app.
HINT: This occurs when Heroku cannot detect the buildpack to use for this application automatically.
See https://devcenter.heroku.com/articles/buildpacks
! Push failed”
How could I fix this problem,please?