#本片教程介紹了具體如何實現天貓精靈控制一個燈。
前提:
HASS平台
- 你已經搭建一個可以在公網IP訪問到的HASS平台--- 我用的是租了阿里雲服務器,買了個域名,ubuntu1604系統
- 你已經搭建一個可以在公網IP訪問到的MQTT服務器----沒有好的話也可以使用官方測試用的服務器湊合下
- 在HASS論壇注冊一個賬戶和密碼---- 一般人不給注冊,你需要給管理員發送郵件證明你會基本的HASS搭建(這都不會玩個蛇)
天貓精靈
- 花60元在咸魚買個二手的 天貓精靈-方糖 (官網89元新的)
- 下載天貓精靈手機APP,綁定自己的 天貓精靈-方糖
ESP8266模塊
- 隨便找個能夠使用arduino ide 開發的ESP8266(這里使用的是 esp8266 d1 pro min 14元)
- 一個繼電器,控制台燈開和關
運行服務器
- 遠程登錄阿里雲服務器
2 為了防止關閉遠程訪問,而導致正在運行的程序跟着關閉,開啟一個screen背后運行
首先查一下正在運行的screen
screen -ls
這里發現已經有一個背后運行的screen(里面是我之前開啟的mqtt和hass程序)
screen -r 1102 # 查詢指定編號的screen里運行的程序
然后來到 編號為1102的screen下面
- 如果想創建一個也可以
- screen -r 1102
- exit
- 執行完上面兩句,代號1102的screen就被銷毀了
- screen -S hass_mqtt #開啟一個名字為hass_mqtt 的screen的背后運行
- 所有在hass_mqtt 下面開啟的程序不會隨着遠程訪問斷開而跟隨關閉,會獨立運行。
3運行 mqtt 服務器
上一步,我們已經來到 編號為1102的screen下面(或者你新建的screen下面)
進入 emqtt 文件路徑下
cd Dongdong/emqttd
命令行進入路徑,開啟mqtt服務
./bin/emqttd start
- ./bin/emqttd stop 關閉命令
4 運行hass
hass
回車開始運行
5 查看hass
打開谷歌游覽器
輸入 域名:8123
密碼
看到自己的頁面
圖中是我默認添加的兩個測試設備
一 HASS配置-發現燈設備
目的:告訴HASS平台,現在有一個新的設備---燈要被你控制管理
手動添加模式
0 打開配置文件手動配置HASS要連接的MQTT服務器
這里兩個選擇
一 蹭一下別人的mqtt服務器
二 直接在HASS的服務器上運行一個,這里我安裝了EMQ版本的MQTT服務器,所以網址是本地的MQTT服務器 127.0.0.1:1883
# configuration.yaml配置樣例 mqtt: # MQTT Broker的IP地址或者域名,這里蹭的官網測試服務器 #broker: broker.mqtt-dashboard.com
#我在hass同一個服務器上,開了一個MQTT服務器
broker: 127.0.0.1
# MQTT Broker的端口號,缺省為1883 port: 1883 #client_id: home-assistant-1 # 用戶名 不用設置 #username: homeassistant # 密碼 不用設置 #password: 123456
1 打開配置文件手動增加一個設備
platform: mqtt name: "light_on" state_topic: "hachina/rgb1/light/status" command_topic: "hachina/rgb1/light/switch" brightness_state_topic: "hachina/rgb1/brightness/status" brightness_command_topic: "hachina/rgb1/brightness/set" rgb_state_topic: "hachina/rgb1/rgb/status" rgb_command_topic: "hachina/rgb1/rgb/set" state_value_template: "{{ value_json.state }}" brightness_value_template: "{{ value_json.brightness }}" rgb_value_template: "{{ value_json.rgb | join(',') }}" qos: 0 payload_on: "ON" payload_off: "OFF" optimistic: false
手動添加的需要重啟 hass服務。
自動添加模式
0 HASS配置要連接的MQTT服務器
1 HASS配置文件中開啟自動發現設備。
# configuration.yaml配置樣例 mqtt: # MQTT Broker的IP地址或者域名,這里蹭的官網測試服務器 #broker: broker.mqtt-dashboard.com #我在hass同一個服務器上,開了一個MQTT服務器 broker: 127.0.0.1 # MQTT Broker的端口號,缺省為1883 port: 1883 #client_id: home-assistant-1 # 用戶名 不用設置 #username: homeassistant # 密碼 不用設置 #password: 123456
2 ESP8266 WIFI模塊(燈)發送自己的配置信息給HASS的配置話題。
hass配置話題 位置
homeassistant/light/garden/config
garden可以隨意換--設備ID
發送的配置信息
{"name": "light_on", "command_topic": "hachina/rgb1/light/switch", "state_topic": "hachina/rgb1/light/status","brightness_command_topic": "hachina/rgb1/brightness/set", "brightness_state_topic": "hachina/rgb1/brightness/status","rgb_command_topic": "hachina/rgb1/rgb/set","rgb_state_topic": "hachina/rgb1/rgb/status","state_value_template": "{{ value_json.state }}","brightness_value_template": "{{ value_json.brightness }}","rgb_value_template": "{{ value_json.rgb | join(',') }}","optimistic": false}
然后可以看到 HASS平台上多了一個燈 light_on(其他兩個設備忽略)
light_on是我用esp8266自動注冊的一個只有開關狀態的燈
TestLed2_light_MQTT是我手動在配置文件中添加的燈,具備開關 顏色 亮度
上述兩個燈除了名字不一樣,其他接收開關的話題我設置成一摸一樣。
可以直接用HASS來控制燈
手機HASS app
編輯--添加設備--里面有個我們自定義的燈設備 light_on--添加進來
短按開關,長按跳出顏色控制板塊
下一步,接入天貓精靈,使用語音間接控制HASS的設備(HASS自帶語音識別和播放服務,也可使用)
二 天貓精靈添加燈設備--將HASS上發現的燈設備添加到天貓精靈上,從而確保貓精間接通過HASS來控制燈
疑問: 為何貓精不直接控制燈?
世界燈種類千千萬,鬼知道你這是什么燈,所以具體控制業務由專門開發智能家居的公司來搞,貓精只負責把語音控制解析信息給智能家居平台公司,由他們自己去控制自己平台下的燈。
1 登陸hass論壇,注冊賬戶和密碼
https://bbs.hassbian.com
2打開天貓精靈APP,在智能家居---綁定平台賬號----綁定HASS賬戶和密碼
這樣貓精就和HASS這個具體的智能家居公司對接起來了(然而HASS不是一個公司,是一個開源項目,申請成為開發合作者)
3將HASS上已有的設備同步到天貓精靈手機APP-智能家居控制列表里,從而使得貓精間接通過HASS控制我們的燈
具體過程:
打開HASS論壇架設的配置網址
https://bbs.hassbian.com/tmall/discovery.php
輸入自己的HASS地址和密碼信息,進入自己的HASS設備管理
將第一步HASS發現的ID為 light_on的燈添加到天貓精靈設備管理中。
選擇增加--真實設備
設置燈的信息
這里 天貓精靈看到hass上有三個設備
只具備 開關 不具備顏色 亮度支持
只具備 開關 不具備顏色 亮度支持
具備 開關 顏色 亮度
這里我們先添加個功能完全的
完成后,多出一個彩燈設備
打開天貓精靈APP,在智能家居中發現,多出一個彩燈設備(我已經在app里重新改名字,截圖不是)
為了提高語音識別准確度,我按照天貓精靈APP的設置重新取了名字 --- 卧室的燈。
就這樣,語音告訴貓精開燈,貓精解析語音后告訴HASS平台,去開哪盞燈。
-------------------------------------------------------------------------
hass是個智能家居管理平台,可以介入各種設備,具體怎么控制燈,這里需要借助MQTT通信協議和服務。
MQTT服務典型: 我想和女朋友說“我愛你”,並不是我直接告訴她,而是我在 “love”這個話題下,發布了“我愛你”這個消息,她訂閱“love”話題,這樣每次從“love”這個話題下,接收到“我愛你”的消息
這種服務好在哪: 凡是訂閱這個話題的人,都能收到同樣的消息,反之,也都可以往這個話題發消息。就像QQ和微信討論組一樣。
-------------------------------------------------------------------------
HASS接收到開哪個燈命令后,找到這個燈的信息,把開關,顏色,亮度控制命令通過MQTT放在指定話題上,等待燈來這個話題上取消息。
這個燈如何取到消息?
1能聯網----這里選擇ESP8266 wifi模塊
2能使用MQTT-- esp8266 在 aruino ide開發平台 下有現成的MQTT通信庫。使用這個庫可以很輕易從對應話題拿到想要的數據。
3能當單片機控制-- ESP8266可以當一塊單片機 開發,外接繼電器可以控制220V的開和關。
三 對接現實世界------ESP8266程序
燒錄時板型選擇信息(根據自己使用的ESP866 wifi板類型)
兩種控制模式:
1 使用HASS網頁版或者手機APP直接控制
開關
亮度
顏色
2 天貓精靈語音控制hass,間接控制燈
天貓精靈 打開卧室的的燈
天貓精靈 關閉卧室的的燈
天貓精靈 將卧室的的燈顏色調成紅色
天貓精靈 將卧室的的燈亮度調為60
ESP8266串口打印輸出:
程序功能:
- 連接指定MQTT服務器
- 在hass約定好的配置話題上發送ESP8266自己的配置信息,從而HASS注冊這個燈設備
- HASS注冊設備后,將開燈關閉,顏色,亮度等控制信息發送到對應約定好的話題上,ESP8266wifi模塊到對應話題取出命令,控制繼電器開關台燈
未來擴展:
- 增加一鍵配網,可靈活修改WIFI信息
- 增加flash保存WIFI聯網信息,斷后電重新自動連接上次記錄的WIFI信息
- 增加觸摸開關,從而開關本身+HASS軟件+天貓語音三者都能獨立控制開關,同時彼此更新開關狀態
- 完善電路,可控硅代替繼電器,電路微薄化處理,做成玻璃面板,好看美觀
目前bug
由於ESP本身內存小,無法發送太多信息。只能發送簡單的開關
警告:
自動發現設備的時候,一定要改配置話題第三個參數ID,
- 跟換設備名沒關系。設備名重復,ID不重復,可以添加。
- ID重復,不管設備名重復不重復,只能有一個
第一個設備 名字 RGBlight ID garden 存在
homeassistant/light/garden/config
{"name": "RGBlight", "command_topic": "hachina/rgb1/light/switch",
第二個設備 名字light ID garden1 存在
homeassistant/light/garden1/config
{"name": "light", "command_topic": "hachina/rgb1/light/switch",
第三個設備 名字light ID garden2 存在
homeassistant/light/garden2/config
{"name": "light", "command_topic": "hachina/rgb1/light/switch",
第四個設備 名字light ID garden 不存在
homeassistant/light/garden/config
{"name": "light", "command_topic": "hachina/rgb1/light/switch",
ID和第一個配置ID沖突,只能存在一個
esp8266燒錄代碼
/*************************************************** ****************************************************/ #include <ESP8266WiFi.h> #include "Adafruit_MQTT.h" #include "Adafruit_MQTT_Client.h" /*------------------------------------------------------------------------------------------- * 配置WIFI信息 *------------------------------------------------------------------------------------------*/ #define WLAN_SSID "dongdong" #define WLAN_PASS "ldd123456" /*------------------------------------------------------------------------------------------- * 配置MQTT服務器信息 *------------------------------------------------------------------------------------------*/ //#define AIO_SERVER "io.adafruit.com" // 不穩定 //#define AIO_SERVER "broker.mqtt-dashboard.com" // 穩定 #define AIO_SERVER "自己的mqtt服務器地址" #define AIO_SERVERPORT 1883 // use 8883 for SSL #define AIO_USERNAME "" #define AIO_KEY "" /*------------------------------------------------------------------------------------------- * 配置外接設備 *------------------------------------------------------------------------------------------*/ // 燈的接口 D4口-繼電器 int Light_d1 = D4; /*------------------------------------------------------------------------------------------- * 開啟MQTT服務 *------------------------------------------------------------------------------------------*/ // Create an ESP8266 WiFiClient class to connect to the MQTT server. WiFiClient client; // Setup the MQTT client class by passing in the WiFi client and MQTT server and login details. Adafruit_MQTT_Client mqtt(&client, AIO_SERVER, AIO_SERVERPORT, AIO_USERNAME, AIO_USERNAME, AIO_KEY); /*------------------------------------------------------------------------------------------- -由於MQTT發布信息有長度限制 130個左右,不能一次性發送太多自身配置信息,因此發雜的設備無法注冊 */ // 發送自身配置信息 開關 // 名字 String name_s="light_on"; // 接收開關命令話題 String command_topic_s="hachina/rgb1/light/switch"; // 自身開關狀態發布話題 String state_topic_s = "hachina/rgb1/light/status"; // 接收亮度命令話題 String brightness_command_topic_s="hachina/rgb1/brightness/set"; // 自身亮度發布話題 String brightness_state_topic_s="hachina/rgb1/brightness/status"; // 接收顏色命令話題 String rgb_command_topic_s="hachina/rgb1/rgb/set"; // 自身顏色狀態發布話題 String rgb_state_topic_s="hachina/rgb1/rgb/status"; // 話題可以性 String optimistic_c="false"; // 可用1 注冊一個只有開關的燈 沒有亮度和顏色 String my_config_s= String("{\"name\":\"")+ name_s +String("\",\"command_topic\":\"")+command_topic_s +String("\",\"state_topic\":\"")+state_topic_s //+String("\",\"brightness_command_topic\":\"")+brightness_command_topic_s //+String("\",\"brightness_state_topic\":\"")+brightness_state_topic_s //+String("\",\"rgb_command_topic_topic\":\"")+rgb_command_topic_s //+String("\",\"rgb_state_topic\":\"")+rgb_state_topic_s +String("\",\"optimistic\":\"")+optimistic_c +String("\"}"); String hass_config_s1="homeassistant/light/garden/config"; // 發布自己的配置信息 Adafruit_MQTT_Publish hass_config = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "homeassistant/light/garden/config"); // 發布自己的開關信息 Adafruit_MQTT_Publish state_topic = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "hachina/rgb1/light/status"); //發布自己的亮度信息 Adafruit_MQTT_Publish brightness_state_topic = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "hachina/rgb1/brightness/status"); //發布自己的顏色信息 Adafruit_MQTT_Publish rgb_state_topic = Adafruit_MQTT_Publish(&mqtt, AIO_USERNAME "hachina/rgb1/rgb/status"); /*------------------------------------------------------------------------------------------- * 功能:訂閱開關信息 * 輸入:顏色數據 * 輸出:空 *------------------------------------------------------------------------------------------*/ //訂閱開關命令 Adafruit_MQTT_Subscribe command_topic = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "hachina/rgb1/light/switch", MQTT_QOS_1); //訂閱亮度命令 Adafruit_MQTT_Subscribe brightness_command_topic = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "hachina/rgb1/brightness/set", MQTT_QOS_1); //訂閱顏色命令 Adafruit_MQTT_Subscribe rgb_command_topic = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "hachina/rgb1/rgb/set", MQTT_QOS_1); /*************************** 接受信息訂閱函數 ************************************/ /*------------------------------------------------------------------------------------------- * 功能:接收開關命令回掉函數 * on 開燈 off 關燈 * 輸入:接收數據+數據長度 * 輸出:空 *------------------------------------------------------------------------------------------*/ void command_topic_call(char *data, uint16_t len) { Serial.print("the button value is: "); Serial.println(data); String msg=String(data); if(msg=="ON") { Serial.println("light is open"); digitalWrite(Light_d1, HIGH); if (! state_topic.publish("ON")) { Serial.println(F("state_topic.publish Failed")); } else { Serial.println(F("state_topic.publish ON!")); } } if(msg=="OFF") { Serial.println("light is close"); digitalWrite(Light_d1, LOW); if (! state_topic.publish("OFF")) { Serial.println(F("state_topic.publish Failed")); } else { Serial.println(F("state_topic.publish OFF!")); } } } /*------------------------------------------------------------------------------------------- * 功能:接收亮度命令回掉函數 * 向電腦打印輸出亮度值 0-255 * 輸入:亮度數據 * 輸出:空 *------------------------------------------------------------------------------------------*/ void brightness_command_topic_call(double x) { Serial.print("Hey we're in a slider callback, the slider value is: "); Serial.println(x); if (! brightness_state_topic.publish(x)) { Serial.println(F("brightness_state_topic.publish Failed")); } else { Serial.print(F("brightness_state_topic.publish "));Serial.println(x); } } /*------------------------------------------------------------------------------------------- * 功能:接收顏色命令回掉函數 * 向電腦打印輸出亮度值 255,255,255 * 輸入:顏色數據 * 輸出:空 *------------------------------------------------------------------------------------------*/ void rgb_command_topic_call(char *data, uint16_t len) { Serial.print("the button value is: "); Serial.println(data); } void setup() { pinMode(Light_d1, OUTPUT); digitalWrite(Light_d1, LOW); Serial.begin(115200); delay(10); Serial.println(F("Adafruit MQTT demo")); // Connect to WiFi access point. Serial.println(); Serial.println(); Serial.print("Connecting to "); Serial.println(WLAN_SSID); WiFi.begin(WLAN_SSID, WLAN_PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); // 配置信息轉換 // strcpy( my_config_c, my_config.c_str()); // 回掉函數 command_topic.setCallback(command_topic_call); brightness_command_topic.setCallback(brightness_command_topic_call); rgb_command_topic.setCallback(rgb_command_topic_call); // 訂閱話題注冊 mqtt.subscribe(&command_topic); mqtt.subscribe(&brightness_command_topic); mqtt.subscribe(&rgb_command_topic); } uint32_t x=0; void loop() { MQTT_connect(); // this is our 'wait for incoming subscription packets and callback em' busy subloop // try to spend your time here: mqtt.processPackets(10000); // ping the server to keep the mqtt connection alive // NOT required if you are publishing once every KEEPALIVE seconds if(! mqtt.ping()) { mqtt.disconnect(); } } // Function to connect and reconnect as necessary to the MQTT server. // Should be called in the loop function and it will take care if connecting. void MQTT_connect() { int8_t ret; // Stop if already connected. if (mqtt.connected()) { return; } Serial.print("Connecting to MQTT... "); uint8_t retries = 3; while ((ret = mqtt.connect()) != 0) { // connect will return 0 for connected Serial.println(mqtt.connectErrorString(ret)); Serial.println("Retrying MQTT connection in 3 seconds..."); mqtt.disconnect(); delay(3000); // wait 10 seconds retries--; if (retries == 0) { // basically die and wait for WDT to reset me while (1); // ESP.wdtFeed(); } } Serial.println("MQTT Connected!"); hass_config.publish(my_config_s.c_str()); // 內存不夠大 ,一直重啟 只能注冊個簡單的有開關功能 Serial.println(my_config_s); }
程序燒錄后,ESP一直觸發看門狗重啟,是MQTT庫默認允許發送數據最大長度引起的.
打開mqttq庫文件源代碼,修改。
注意修改 esp8266 mqtt庫允許發送最大的 數據長度(默認 150),修改為1024
然后還是沒什用,報錯 但是最大只能傳送約 130個char
這意味着通過MQTT自動配置,只能發送 開關的信息,顏色和亮度信息發送不了。需手動添加,后者使用手機MQTT調試助手發過去,長度最大限制1024。