ทำ Smart Home ด้วย Line Message API

Sarut Yentakham
7 min readMar 16, 2019

--

จากบทความเมื่อสองอาทิตย์ก่อน ก็ห่างหายไปนานนิดนึง วันนี้ผมจะมาทำให้ Line สามารถควบคุมอุปกรณ์ อิเล็กทรอนิกส์ภายในบ้าน เปลี่ยนอุปกรณ์ไฟฟ้าธรรมดาให้สามารถควบคุมได้ผ่านปลายนิ้วของเราเอง บทความนี้จะเป็นการรวมการใช้ง่านต่างๆในบทความก่อนหน้ามาต่อยอดไปเรื่อยๆ ตอนนี้เรามาเริ่มกันเลยดีกว่า ว่าวันนี้เราต้องใช้อะไรกันบ้างในการทำ

อุปกรณ์ที่ต้องใช้

  • NodeMCU V.3 1 ตัว
  • หลอดไดโอต 3 หลอด สีตามใจชอบ
  • บอร์ดทดลอง 1 อัน
  • สายไฟธรรมดา

รอบนี้ของใช้น้อยเพื่อให้ง่าย ต่อคนที่อยากจะลองไปทำตาม ปล.​ถ้าใครหาซื้อไม่ได้ก็ทักผมก็ได้นะครับเดียวแนะนำให้ได้ สำหรับคนที่ไม่เคยรู้ว่า NodeMCU คืออะไรและต้อง Setup ยังไง ลองอ่านบทความนี้ก่อนนนะครับ

อุปกรณ์พร้อมก็มาเริ่มกันเลย

มาเริ่มต่อสายกันก่อนดีกว่าว่าผมต่ออะไรไวบ้าง

  • D0 ไดโอตแสดงไฟแสดงว่าสามารถ Connect WiFi ได้สำเร็จ
  • D1 ไดโอตแสดงการควบคุมอุปกรณ์ชิ้นที่ 1 (สามารถประยุกต์ใช้กับ Relay ได้)
  • D2 ไดโอตแสดงการควบคุมอุปกรณ์ชิ้นที่ 2 (สามารถประยุกต์ใช้กับ Relay ได้)
  • G อันนี้ต้องเชื่อมต่อทุกอันนะครับเป็นสาย (- Ground)

************************ ข้อควรระวัง ********************************
สำหรับน้องๆ ที่ไม่เคยทำอยากให้มีผู้ปกครองนั่งอยู่ด้วยนะครับและ เก็บของพวกนี้ให้ห่างจากมือเด็กขณะทำการทดลองด้วย สำคัญมากนะครับ
*******************************************************************ผู้ที่ต่อ Relay ใช้กับไฟ 220V ไฟบ้านเราอันตรายถึงชีวิต กรุณาทำอย่างระมัดระวังนะครับ เรื่องไฟไม่ใช่เรื่องเล่น
*******************************************************************

Node MCU
การเชื่อมต่อสาย
ตัวอย่างการเชื่อมต่อที่เสร็จแล้ว

ต่อเสร็จก็ Code สิรออะไร

เรามาเริ่มที่ตัว NodeMCU กันก่อนเลยเปิด Arduino โลด~~ งานนี้เราใช้ MQTT ด้วยสำหรับคนที่ไม่รู้ว่า MQTT ต้อง Setup ยังไง สามารถอ่านได้ที่นี่นะครับ How to MQTT สำหรับผู้ที่รู้อยู่แล้วก็จัดตามด้านล่างเลยครับ อย่าได้รอช้า

ปล.​อย่าลืมเปลี่ยนข้อมูลตรงที่ผม Comment ไว้ด้วยนะครับ

#include <string.h>
#include <ESP8266WiFi.h> //https://github.com/esp8266/Arduino
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h> //https://github.com/tzapu/WiFiManager
#include <PubSubClient.h>
#include "DHT.h"
//WiFi
#define LED_WIFI D0
#define ConfigWiFi_Pin D9
#define ESP_AP_NAME "xxxxxxxxxxxxx" // ชื่ออุปกรณ์ของเราตอนจะเชื่อม Wifi
WiFiClient espClient;
PubSubClient client(espClient);
// Config MQTT Server
#define mqtt_server "xxxxxxxxxx" // Server MQTT ของเรานะ
#define mqtt_port xxxxxx // Port MQTT ของเรานะ
#define mqtt_user "xxxxxxxxx" // User MQTT ของเรานะ
#define mqtt_password "xxxxxxxx" // Password MQTT ของเรานะ
// LED
#define LED_ONE D1
#define LED_TWO D2
void setup() {
Serial.begin(115200);
// WiFi
pinMode(ConfigWiFi_Pin, INPUT_PULLUP);
pinMode(LED_WIFI, OUTPUT);
digitalWrite(LED_WIFI, LOW);
startUpWiFi();
//LED
pinMode(LED_ONE, OUTPUT);
pinMode(LED_TWO, OUTPUT);
digitalWrite(LED_ONE, LOW); // Turn OFF the LED
digitalWrite(LED_TWO, LOW); // Turn OFF the LED
// MQTT
client.setServer(mqtt_server, mqtt_port);
client.setCallback(callback);
}
void loop() {
/*
* MQTT Ssevice
*/
if (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("ESP8266Client", mqtt_user, mqtt_password)) {
Serial.println("connected");
client.subscribe("/ESP/LED");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
return;
}
}
client.loop();
}
void callback(char* topic, byte* payload, unsigned int length)
{
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
String msg = "";
String topicChannel = "";
int i = 0;
while (i < length) msg += (char)payload[i++];
topicChannel = topic;
if (msg == "GET") {
int led_one = digitalRead(LED_ONE);
int led_two = digitalRead(LED_TWO);
char str[100];
sprintf(str, "%d,%d", led_one, led_two);
client.publish("/ESP/LED", str);
Serial.println("Send !");
return;
}
if (topicChannel == "/ESP/LED") {
if (msg == "LEDON_ONE" || msg == "LEDOFF_ONE") {
digitalWrite(LED_ONE, (msg == "LEDON_ONE" ? HIGH : LOW));
}
if (msg == "LEDON_TWO" || msg == "LEDOFF_TWO") {
digitalWrite(LED_TWO, (msg == "LEDON_TWO" ? HIGH : LOW));
}
}
if (msg == "CLEAR") {
digitalWrite(LED_ONE, LOW);
digitalWrite(LED_TWO, LOW);
}
}
void startUpWiFi()
{
//WiFiManager
//Local intialization. Once its business is done, there is no need to keep it around
WiFiManager wifiManager;
if (digitalRead(ConfigWiFi_Pin) == LOW) // Press button
{
//reset saved settings
wifiManager.resetSettings(); // go to ip 192.168.4.1 to config
}
//fetches ssid and password from EEPROM and tries to connect
//if it does not connect, it starts an access point with the specified name
//and goes into a blocking loop awaiting configuration
wifiManager.autoConnect(ESP_AP_NAME);
while (WiFi.status() != WL_CONNECTED)
{
digitalWrite(LED_WIFI, LOW);
delay(250);
Serial.print(".");
}
Serial.println("WiFi connected");
Serial.println("IP address: ");
digitalWrite(LED_WIFI, HIGH);
Serial.println(WiFi.localIP());
}

หลังจากนั้นก็ Build ลง NodeMCU กันเลย หลังจากนั้นเราก็มาเชื่อมต่อ NodeMCU ให้สามารถ Connect Internet ได้นะครับ

เชื่อมต่อ WiFi กด Configure WiFi เลย
หาตัว WiFi ที่เราต้องการเกาะ และ Save เลย

หลังจากที่ทำขั้นตอนนี้เสร็จแล้ว มีไฟสีเขียนขึ้นแสดงว่า Connect WiFi สำเร็จตามภาพด้านบนนะครับที่ผ่านมาแล้ว สำหรับ Code ส่วนของ NodeMCU เสร็จแล้วไปกันต่อในส่วนของ NodeJS กันเลย ว่าจะสั่งงานยังไงไปดูหน้าตาของ Code กันเลยดีกว่าว่าทำงานยังไง สร้างไฟล์ app.js หรือตามใจเราก็ได้เอาที่ชอบเลย

สำหรับคนที่ไม่เคยทำ ให้ สมัคร Line Developer และ Heroku แนะนำให้อ่านอันนี้ก่อนนะครับ

Code NodeJS

require('dotenv').config();
const request = require('request');
const express = require('express');
const port = process.env.PORT || 3001;
const _ = require('lodash');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// STATUS LED
let status = [false, false];
// TOPIC
const LED_TOPIC = `/ESP/LED`;
// Create a MQTT Client
const mqtt = require('mqtt');
// Create a client connection to CloudMQTT for live data
const client = mqtt.connect('xxxxxxxx', // Server MQTT ของเรานะ
{
username: 'xxxxxxxxxxx', // Username MQTT ของเรานะ
password: 'xxxxxxxxxxx', // Password MQTT ของเรานะ
port: xxxxx // Port MQTT ของเรานะ
});
client.on('connect', function() {
// When connected
console.log("Connected to CloudMQTT");
client.subscribe('/ESP/LED', function() {
// when a message arrives, do something with it
client.on('message', function(topic, message, packet) {
switch(topic) {
case LED_TOPIC:
messageFromBuffer = message.toString('utf8');
if (messageFromBuffer != 'GET') {
const splitStatus = messageFromBuffer.split(',');
if (splitStatus.length > 0) {
splitStatus.map((ele, index)=> {
console.log(`DOIT ${ele} ${index} ${parseInt(ele)}`);
if (ele == 0) {
status[index] = false;
} else {
status[index] = true;
}
});
}
}
console.log(`Received '${message}' on '${topic}`);
break;
default:
console.log(`Unknow Topic group`);
}
});
});
});
app.post('/webhook', async (req, res) => {const message = req.body.events[0].message.text;
const reply_token = req.body.events[0].replyToken;
const TOKEN = `xxxxxxx`; // Token ที่ได้จาก Channel access token
const HEADERS = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${TOKEN}`
};
if (message == 'เปิดไฟ หน้าบ้าน' || message == 'ปิดไฟ หน้าบ้าน') {
if (message == 'เปิดไฟ หน้าบ้าน') {
await mqttMessage(LED_TOPIC, 'LEDON_ONE');
} else {
await mqttMessage(LED_TOPIC, 'LEDOFF_ONE');
}
}
if (message == 'เปิดไฟ หลังบ้าน' || message == 'ปิดไฟ หลังบ้าน') {
if (message == 'เปิดไฟ หลังบ้าน') {
await mqttMessage(LED_TOPIC, 'LEDON_TWO');
} else {
await mqttMessage(LED_TOPIC, 'LEDOFF_TWO');
}
}
mqttMessage(LED_TOPIC, 'GET');if (message == 'สถานะทั้งหมด') {
await checkStatus();
} else {
await checkStatus();
}
console.log(status);
const objectMessage = genFlexMessage(status[0], status[1]);
const body = JSON.stringify({
replyToken: reply_token,
messages: [
objectMessage
]
});
request({
method: `POST`,
url: 'https://api.line.me/v2/bot/message/reply',
headers: HEADERS,
body: body
});
res.sendStatus(200);
});
let mqttMessage = async (topic, message) => {
client.publish(topic, message);
await checkStatus();
}
let checkStatus = async () => {
await new Promise(done => setTimeout(done, 3000));
}
let genFlexMessage = (ledOne, ledTwo) => {
return {
"type": "flex",
"altText": "สถานะระบบไฟ",
"contents": {
"type": "bubble",
"hero": {
"type": "image",
"url": "https://www.ihome108.com/wp-content/uploads/2017/05/home-slide-01.jpg",
"size": "full",
"aspectRatio": "20:13",
"aspectMode": "cover",
"action": {
"type": "uri",
"label": "Line",
"uri": "https://linecorp.com/"
}
},
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": "ระบบไฟ",
"flex": 0,
"size": "xl",
"weight": "bold"
},
{
"type": "box",
"layout": "horizontal",
"flex": 1,
"margin": "md",
"contents": [
{
"type": "text",
"text": "ไฟหน้าบ้าน",
"align": "start",
"gravity": "top",
"weight": "bold"
},
{
"type": "text",
"text": (ledOne == true) ? "Open" : "Close",
"align": "start",
"weight": "bold",
"color": (ledOne == true) ? "#FF0000" : "#000000",
}
]
},
{
"type": "box",
"layout": "horizontal",
"flex": 1,
"margin": "md",
"contents": [
{
"type": "text",
"text": "ไฟหลังบ้าน",
"align": "start",
"gravity": "top",
"weight": "bold"
},
{
"type": "text",
"text": (ledTwo == true) ? "Open" : "Close",
"align": "start",
"weight": "bold",
"color": (ledTwo == true) ? "#FF0000" : "#000000",
}
]
}
]
},
"footer": {
"type": "box",
"layout": "vertical",
"flex": 0,
"spacing": "sm",
"contents": [
{
"type": "button",
"action": {
"type": "message",
"label": `${(ledOne == true) ? "ปิดไฟ" : "เปิดไฟ"}หน้าบ้าน`,
"text": `${(ledOne == true) ? "ปิดไฟ" : "เปิดไฟ"} หน้าบ้าน`
},
"height": "sm",
"style": "link"
},
{
"type": "button",
"action": {
"type": "message",
"label": `${(ledTwo == true) ? "ปิดไฟ" : "เปิดไฟ"}หลังบ้าน`,
"text": `${(ledTwo == true) ? "ปิดไฟ" : "เปิดไฟ"} หลังบ้าน`
},
"height": "sm",
"style": "link"
},
{
"type": "spacer",
"size": "sm"
}
]
}
}
};
}
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

ตัวอย่างแบบฉบับเต็มครับ

หลังจากนั้นก็ Deploy เข้า Heroku กันเลย สำหรับการ Deploy บน Heroku ผมเคยได้เขียนไว้นานแล้ว ถ้าใครทำไม่เป็นจัดเลยครับ ตามนี้

ทำเสร็จหมดแล้วเราก็มาลองเทสกันว่าเป็นได้ผลลัพธ์เป็นยังไงกันบ้าง

ผลลัพธ์ที่ได้

เข้ามาเราก็เขียนก่อนเลย ว่า “สถานะทั้งหมด” จะได้ Flex Message แบบนี้ออกมา

Flex Message Control
สถานะของ NodeMCU ที่ปิดไฟทั้งหมดไว้ แต่ WiFi ยัง Connent อยู่เลยมีสีเขียนเป็น แสงในการบอกสถานะ

มาลองเปิดไฟกันเลยดีกว่า เปิดไฟหลังบ้านกันหน่อย ไฟหลังบ้านอยู่ที่ D2

แจ้งสถานะการเปิด และเปลี่ยน Control จากเปิด เป็น ปิด
ผลลัพธ์หลังจากสั่งผ่าน Line ของเรา

ง่านนิดเดียวเองสำหรับการที่จะนำสิ่งใกล้ตัวเรามาทำให้มัน Smart มากขึ้นเปลี่ยนสิ่ง Manual ธรรมดาทั้วๆไปให้มัน Smart มากขึ้น โดนผ่าน Internet ทำให้ใช้ชีวิตง่ายขึ้น และก็ต้องขอบคุณ​ Line ที่ทำให้สามารถ Application และเอาเข้ามาใช้ผ่านตัว Messaging API ทำให้ผมสามารถทดลองอะไรได้หลายๆอย่าง ง่านและเข้าใจง่าย

สุดท้ายเหมือนเคย

ผมได้แนบ github ไว้ให้เหมือนเดิม

สำหรับคนที่ ไม่เข้าใจและ งง สามารถส่ง Email มาถามได้นะครับ หรือจะ Line ก็ได้

LineID: benzintel , Email: benz20003@gmail.com

ขอขอบคุณ ผู้สนับสนุนใจดีแบบไม่ต้องขอ

เพิ่มเติม

มีหลายท่านอยากได้การต่อแบบละเอียดผมแนบไฟล์ภาพเพื่อความง่ายในการต่อให้เห็นได้ชัดเจนขึ้นนะครับ

การต่อวงจร เข้ากับ NodeMCU

--

--