大半夜的,先說些廢話提提神
如今智能家居已經不再停留在概念階段,高大上的科技公司都已經推出了自己的部分或全套的智能家居解決方案,不過就目前的現狀而言,大多還停留在展廳階段,還沒有廣泛的推廣起來,有人說最大的問題是標准不統一雲雲,但在我看來,最大的問題在於兩個方面,一個是價格,一個是操作的簡便性,技術上的問題並不是阻礙智能家居推廣的核心因素。
再來說說最近很火很惹人愛的微軟小娜,Cortana。本人作為微軟的死忠,作為一名靠.Net混飯的屌絲程序男,自然是有一部擼妹的,並且在小娜推送當天更新了手機,迫不及待地體驗了小娜的功能,小娜會記事、會提醒、會唱歌、會說笑話、會學林志玲等等着實讓我驚嘆了一把,而且語氣非常接近自然人的語氣,不是那種生澀的機器語調。作為平時生活中不使用Android,Iphone的我,那晚想當然的覺得小娜應該就是當今世界上最好的語音助手了吧,懷着這樣的心情,讓小娜給我定了個鬧鍾,第二天早早起床迫不及待要向同事得瑟一把了。
第二天給同事小馬看后,小馬淡然一笑,拿出他的Android手機,默默的打開手機上的一個叫“靈犀”的軟件,遞給我說:“試試這個”。不試不知道,一試,心情還有點小低落,優越感頓無,怎么說呢,應該說除了在語氣上小娜占上風外,在識准率,識別速度,各類功能應用上,小娜和“靈犀”已然不是一個檔次,可能是小娜剛起步吧,希望以后可以不斷完善。
看了一下“靈犀”官網,恍然大悟,這就是科大訊飛的產品啊,再次把玩靈犀,就只想真心點贊了,當時腦子里還飄過一個畫面,未來能夠對百度造成威脅的,很有可能就是科大訊飛,科大訊飛將把持移動互聯網的使用入口!哦,那畫面太美,不知李彥宏怎么想這個問題。
語音識別不僅僅會影響到人們使用手機的方式,在智能家居方面也大有可為,最直接的就是帶來操作上的便捷性,試想,如果你在家時,你慢悠悠的拿出你的手機,慢悠悠的打開你的操作應用,慢悠悠的點擊一個控制按鈕,然后你的電視或者調動窗簾才執行你的命令,這圖的個什么啊,還不如直接走過去手動暴力解決,而如果你在家的時候,只需要說“小文,換到體育頻道”,或者“小文,打開窗簾”,然后安放在屋子里的中控盒子二十四小時隨時聽候理解你的命令,出門在外的時候,也只需對着手機說一聲就行,這才叫易用的智能家居啊!
今天我們就采用Arduino開發板+Asp.Net MVC Api+Android版的訊飛語言SDK,實現用語音來控制一個小Led的實驗。
關於Arduino
我的硬件准備
左邊是Arduino主板,右邊是網絡拓展板,下邊的是什么?哈哈,實驗室小孟同學親手給我焊接的小小Led燈,用來模仿開關性質的設備,這么小,看着都是淚水啊。組合后就是這么個樣子了
以上總造價不超過100元。
Ardunio是一個開源的開發板,據說國外都是小朋友和藝術家都可以玩的開發板,簡單易學,功能完善,目前也是非常火爆。事實也確實如此,看了幾個示例工程后,有一定開發基礎的人一看就知道怎么玩了。大部分web開發者基本上可以直接上手Arduino的網絡部分的開發,看一遍示例保證你明白怎么個搞法,不信?那上個截圖吧,這是Ardunio提供的IDE,內置了很多常用的開發場景示例,基本上在示例的基礎上簡單修改就可以完成一些簡單的任務。
如果你是一個web開發者,好吧,看到菜單你也應該聯想到你熟悉的網站開發模式了吧,是的,Arduino為你提供了客戶端模式,服務端模式,還有些不常用的其他模式,讓我們來看看Ardunio的WebClientRepeatping,這是一個客戶端輪詢服務端的方式,簡單粗暴,通信穩定,容易理解和上手,下面是Ardunio提供的該模式示例代碼

1 /* 2 Repeating Web client 3 4 This sketch connects to a a web server and makes a request 5 using a Wiznet Ethernet shield. You can use the Arduino Ethernet shield, or 6 the Adafruit Ethernet shield, either one will work, as long as it's got 7 a Wiznet Ethernet module on board. 8 9 This example uses DNS, by assigning the Ethernet client with a MAC address, 10 IP address, and DNS address. 11 12 Circuit: 13 * Ethernet shield attached to pins 10, 11, 12, 13 14 15 created 19 Apr 2012 16 by Tom Igoe 17 modified 21 Jan 2014 18 by Federico Vanzati 19 20 http://arduino.cc/en/Tutorial/WebClientRepeating 21 This code is in the public domain. 22 23 */ 24 25 #include <SPI.h> 26 #include <Ethernet.h> 27 28 // assign a MAC address for the ethernet controller. 29 // fill in your address here: 30 byte mac[] = { 31 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED 32 }; 33 // fill in an available IP address on your network here, 34 // for manual configuration: 35 IPAddress ip(192, 168, 1, 177); 36 37 // fill in your Domain Name Server address here: 38 IPAddress myDns(1, 1, 1, 1); 39 40 // initialize the library instance: 41 EthernetClient client; 42 43 char server[] = "www.arduino.cc"; 44 //IPAddress server(64,131,82,241); 45 46 unsigned long lastConnectionTime = 0; // last time you connected to the server, in milliseconds 47 const unsigned long postingInterval = 10L * 1000L; // delay between updates, in milliseconds 48 // the "L" is needed to use long type numbers 49 50 void setup() { 51 // start serial port: 52 Serial.begin(9600); 53 while (!Serial) { 54 ; // wait for serial port to connect. Needed for Leonardo only 55 } 56 57 // give the ethernet module time to boot up: 58 delay(1000); 59 // start the Ethernet connection using a fixed IP address and DNS server: 60 Ethernet.begin(mac, ip, myDns); 61 // print the Ethernet board/shield's IP address: 62 Serial.print("My IP address: "); 63 Serial.println(Ethernet.localIP()); 64 } 65 66 void loop() { 67 // if there's incoming data from the net connection. 68 // send it out the serial port. This is for debugging 69 // purposes only: 70 if (client.available()) { 71 char c = client.read(); 72 Serial.write(c); 73 } 74 75 // if ten seconds have passed since your last connection, 76 // then connect again and send data: 77 if (millis() - lastConnectionTime > postingInterval) { 78 httpRequest(); 79 } 80 81 } 82 83 // this method makes a HTTP connection to the server: 84 void httpRequest() { 85 // close any connection before send a new request. 86 // This will free the socket on the WiFi shield 87 client.stop(); 88 89 // if there's a successful connection: 90 if (client.connect(server, 80)) { 91 Serial.println("connecting..."); 92 // send the HTTP PUT request: 93 client.println("GET /latest.txt HTTP/1.1"); 94 client.println("Host: www.arduino.cc"); 95 client.println("User-Agent: arduino-ethernet"); 96 client.println("Connection: close"); 97 client.println(); 98 99 // note the time that the connection was made: 100 lastConnectionTime = millis(); 101 } 102 else { 103 // if you couldn't make a connection: 104 Serial.println("connection failed"); 105 } 106 }
請從頭到尾看一變,我保證如果你有編程基礎看一遍也就知道的差不多該怎么玩了。無非就是定時輪詢,請求服務器,對返回的字符串進行處理。下面的代碼就是我在上面代碼的基礎上做的修改,實現了定時從服務器獲取命令,如果是1則打開Led,如果是2則關閉Led。

1 /* 2 Repeating Web client 3 4 This sketch connects to a a web server and makes a request 5 using a Wiznet Ethernet shield. You can use the Arduino Ethernet shield, or 6 the Adafruit Ethernet shield, either one will work, as long as it's got 7 a Wiznet Ethernet module on board. 8 9 This example uses DNS, by assigning the Ethernet client with a MAC address, 10 IP address, and DNS address. 11 12 Circuit: 13 * Ethernet shield attached to pins 10, 11, 12, 13 14 15 created 19 Apr 2012 16 by Tom Igoe 17 modified 21 Jan 2014 18 by Federico Vanzati 19 20 http://arduino.cc/en/Tutorial/WebClientRepeating 21 This code is in the public domain. 22 23 */ 24 25 #include <SPI.h> 26 #include <Ethernet.h> 27 const int ledPin = 2; 28 // assign a MAC address for the ethernet controller. 29 // fill in your address here: 30 byte mac[] = { 31 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF 32 }; 33 // fill in an available IP address on your network here, 34 // for manual configuration: 35 IPAddress ip(192, 168, 1, 177); 36 37 EthernetClient client; 38 39 IPAddress server(你的服務器地址); 40 41 unsigned long lastConnectionTime = 0; // last time you connected to the server, in milliseconds 42 const unsigned long postingInterval = 2L * 1000L; // delay between updates, in milliseconds 43 // the "L" is needed to use long type numbers 44 45 void setup() { 46 // start serial port: 47 Serial.begin(9600); 48 while (!Serial) { 49 ; // wait for serial port to connect. Needed for Leonardo only 50 } 51 // give the ethernet module time to boot up: 52 delay(1000); 53 pinMode(ledPin, OUTPUT); 54 // start the Ethernet connection using a fixed IP address and DNS server: 55 Ethernet.begin(mac); 56 // print the Ethernet board/shield's IP address: 57 Serial.print("My IP address: "); 58 Serial.println(Ethernet.localIP()); 59 60 } 61 int iscoming=0; 62 63 void loop() { 64 // if there's incoming data from the net connection. 65 // send it out the serial port. This is for debugging 66 // purposes only: 67 if (client.available()) { 68 char c = client.read(); 69 if(iscoming==1){ 70 handle(c); 71 iscoming=0; 72 } 73 if(c=='@'){ 74 iscoming=1; 75 } 76 Serial.write(c); 77 } 78 79 // if ten seconds have passed since your last connection, 80 // then connect again and send data: 81 if (millis() - lastConnectionTime > postingInterval) { 82 httpRequest(); 83 } 84 85 } 86 87 void handle(char value){ 88 if(value=='1'){ 89 Serial.print("dakai"); 90 digitalWrite(ledPin, LOW); 91 } 92 if(value=='2'){ 93 Serial.print("guanbi"); 94 digitalWrite(ledPin, HIGH); 95 } 96 } 97 // this method makes a HTTP connection to the server: 98 void httpRequest() { 99 // close any connection before send a new request. 100 // This will free the socket on the WiFi shield 101 client.stop(); 102 // if there's a successful connection: 103 delay(1000); 104 if (client.connect(server, 8009)) { 105 Serial.println("connecting..."); 106 // send the HTTP PUT request: 107 //api/getcmd是請求的網站資源 108 client.println("GET /api/getcmd HTTP/1.1"); 109 client.println("Host: www.arduino.cc"); 110 client.println("User-Agent: arduino-ethernet"); 111 client.println("Connection: close"); 112 client.println(); 113 // note the time that the connection was made: 114 lastConnectionTime = millis(); 115 } 116 else { 117 // if you couldn't make a connection: 118 Serial.println("connection failed"); 119 120 } 121 }
Android客戶端
關於科大訊飛的介紹,自己去官網看吧,深耕語音領域二十年,不是一般企業可以超越的,很好很強大。申請成為開發者,創建應用,開通服務,下載SDK(我下的是帶UI的SDK),參考SDK的代碼就可以開始自己的開發了。話不多說,直接上核心代碼

1 /** 2 * 聽寫UI監聽器 3 */ 4 private RecognizerDialogListener recognizerDialogListener=new RecognizerDialogListener(){ 5 public void onResult(RecognizerResult results, boolean isLast) { 6 String text = JsonParser.parseIatResult(results.getResultString()); 7 mResultText.append(text); 8 mResultText.setSelection(mResultText.length()); 9 sendmeg(text); 10 } 11 12 private void sendmeg(String msg){ 13 AsyncHttpClient client = new AsyncHttpClient(); 14 try { 15 String url = "http://服務器地址/api/values/"+msg; 16 client.get(url, new AsyncHttpResponseHandler() { 17 @Override 18 public void onSuccess(int statusCode, Header[] headers, 19 byte[] responseBody) { 20 super.onSuccess(statusCode, headers, responseBody); 21 Toast.makeText(CmdActivity.this, 22 "請求成功" + new String(responseBody), Toast.LENGTH_SHORT).show(); 23 } 24 25 @Override 26 public void onFailure(int statusCode, Header[] headers, 27 byte[] responseBody, Throwable error) { 28 // TODO Auto-generated method stub 29 super.onFailure(statusCode, headers, responseBody, error); 30 } 31 }); 32 } catch (Exception e) { 33 } 34 35 } /**
Asp.Net Mvc Api
服務端的代碼相對就簡單了,我這里創建了兩個控制器,一個負責接收手機端的命令,存入數據庫,一個負責返回Arduino的網絡請求,將命令代碼返回。雖然覺得沒必要,還是上一下代碼吧
接收手機端傳過來的命令

1 // GET api/values/5 2 public string Get(string id) 3 { 4 ardunioEntities a = new ardunioEntities(); 5 string tempid = "123456789"; 6 cmd c = a.cmd.Where<cmd>(n => n.ardunioid ==tempid).SingleOrDefault(); 7 if (c == null) 8 { 9 c = new cmd(); 10 c.ardunioid = tempid; 11 c.command = "0"; 12 c.status = "未讀"; 13 a.cmd.Add(c); 14 } 15 if (id.Contains("打開")) 16 { 17 { 18 c.command = "1"; 19 c.status = "未讀"; 20 21 } 22 } 23 if (id.Contains("關閉")) 24 { 25 { 26 c.command = "2"; 27 c.status = "未讀"; 28 } 29 } 30 a.SaveChanges(); 31 return "接收成功!"; 32 33 }
返回命令給Arduino的請求

1 public string Get() 2 { 3 4 ardunioEntities a = new ardunioEntities(); 5 string tempid = "123456789"; 6 cmd c = a.cmd.Where<cmd>(n => n.ardunioid == tempid & n.status == "未讀").SingleOrDefault(); 7 if (c == null) 8 { 9 return "@0"; 10 } 11 c.status = "已讀"; 12 a.Entry(c).State = EntityState.Modified; 13 a.SaveChanges(); 14 return "@"+c.command; 15 }
效果實現與總結
對着手機說“打開燈泡”,然后燈就亮了!話說程序員看到這一幕,興奮之情溢於言表啊。(請忽略旁邊的美桃,女友由於對我崇拜的五體投地給我洗的,嘻嘻)。
這篇文章只是一個小實驗而已,后續還可以做很多有趣的玩法,比如在這個程序的基礎上開發語音控制電動窗簾的的工作(目前正在玩耍...),比如直接弄一個Android開發板或者使用語音模塊做一個中控,二十四小時伺服,回到家直接說話“小文,換台到體育頻道”等等等等,現有的智能家居產品都可以把語音技術集成過來,相信會大大增加智能家居的易用性。
本月科大訊飛即將召開智能家庭產品發布會,我們實驗室也申請了票,期待看到高大上的東東開開眼。。。。一場革命或許已經來臨。
看完了就默默點個贊吧:)