一百元的智能家居——Asp.Net Mvc Api+讯飞语音+Android+Arduino

如今智能家居已经不再停留在概念阶段,高大上的科技公司都已经推出了自己的部分或全套的智能家居解决方案,不过就目前的现状而言,大多还停留在展厅阶段,还没有广泛的推广起来,有人说最大的问题是标准不统一云云,但在我看来,最大的问题在于两个方面,一个是价格,一个是操作的简便性,技术上的问题并不是阻碍智能家居推广的核心因素。

再来说说最近很火很惹人爱的微软小娜,Cortana。本人作为微软的死忠,作为一名靠.Net混饭的屌丝程序男,自然是有一部撸妹的,并且在小娜推送当天更新了手机,迫不及待地体验了小娜的功能,小娜会记事、会提醒、会唱歌、会说笑话、会学林志玲等等着实让我惊叹了一把,而且语气非常接近自然人的语气,不是那种生涩的机器语调。作为平时生活中不使用Android,Iphone的我,那晚想当然的觉得小娜应该就是当今世界上最好的语音助手了吧,怀着这样的心情,让小娜给我定了个闹钟,第二天早早起床迫不及待要向同事得瑟一把了。

第二天给同事小马看后,小马淡然一笑,拿出他的Android手机,默默的打开手机上的一个叫“灵犀”的软件,递给我说:“试试这个”。不试不知道,一试,心情还有点小低落,优越感顿无,怎么说呢,应该说除了在语气上小娜占上风外,在识准率,识别速度,各类功能应用上,小娜和“灵犀”已然不是一个档次,可能是小娜刚起步吧,希望以后可以不断完善。

看了一下“灵犀”官网,恍然大悟,这就是科大讯飞的产品啊,再次把玩灵犀,就只想真心点赞了,当时脑子里还飘过一个画面,未来能够对百度造成威胁的,很有可能就是科大讯飞,科大讯飞将把持移动互联网的使用入口!哦,那画面太美,不知李彦宏怎么想这个问题。

语音识别不仅仅会影响到人们使用手机的方式,在智能家居方面也大有可为,最直接的就是带来操作上的便捷性,试想,如果你在家时,你慢悠悠的拿出你的手机,慢悠悠的打开你的操作应用,慢悠悠的点击一个控制按钮,然后你的电视或者调动窗帘才执行你的命令,这图的个什么啊,还不如直接走过去手动暴力解决,而如果你在家的时候,只需要说“小文,换到体育频道”,或者“小文,打开窗帘”,然后安放在屋子里的中控盒子二十四小时随时听候理解你的命令,出门在外的时候,也只需对着手机说一声就行,这才叫易用的智能家居啊!

今天我们就采用Arduino开发板+Asp.Net MVC Api+Android版的讯飞语言SDK,实现用语音来控制一个小Led的实验。

关于Arduino

我的硬件准备

091911085374196

 

左边是Arduino主板,右边是网络拓展板,下边的是什么?哈哈,实验室小孟同学亲手给我焊接的小小Led灯,用来模仿开关性质的设备,这么小,看着都是泪水啊。组合后就是这么个样子了

091919281318224

 

以上总造价不超过100元。

Ardunio是一个开源的开发板,据说国外都是小朋友和艺术家都可以玩的开发板,简单易学,功能完善,目前也是非常火爆。事实也确实如此,看了几个示例工程后,有一定开发基础的人一看就知道怎么玩了。大部分web开发者基本上可以直接上手Arduino的网络部分的开发,看一遍示例保证你明白怎么个搞法,不信?那上个截图吧,这是Ardunio提供的IDE,内置了很多常用的开发场景示例,基本上在示例的基础上简单修改就可以完成一些简单的任务。

090158394433266

 

如果你是一个web开发者,好吧,看到菜单你也应该联想到你熟悉的网站开发模式了吧,是的,Arduino为你提供了客户端模式,服务端模式,还有些不常用的其他模式,让我们来看看Ardunio的WebClientRepeatping,这是一个客户端轮询服务端的方式,简单粗暴,通信稳定,容易理解和上手,下面是Ardunio提供的该模式示例代码

/*
  Repeating Web client

 This sketch connects to a a web server and makes a request
 using a Wiznet Ethernet shield. You can use the Arduino Ethernet shield, or
 the Adafruit Ethernet shield, either one will work, as long as it's got
 a Wiznet Ethernet module on board.

 This example uses DNS, by assigning the Ethernet client with a MAC address,
 IP address, and DNS address.

 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13

 created 19 Apr 2012
 by Tom Igoe
 modified 21 Jan 2014
 by Federico Vanzati

 http://arduino.cc/en/Tutorial/WebClientRepeating
 This code is in the public domain.

 */

#include <SPI.h>
#include <Ethernet.h>

// assign a MAC address for the ethernet controller.
// fill in your address here:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
// fill in an available IP address on your network here,
// for manual configuration:
IPAddress ip(192, 168, 1, 177);

// fill in your Domain Name Server address here:
IPAddress myDns(1, 1, 1, 1);

// initialize the library instance:
EthernetClient client;

char server[] = "www.arduino.cc";
//IPAddress server(64,131,82,241);

unsigned long lastConnectionTime = 0;             // last time you connected to the server, in milliseconds
const unsigned long postingInterval = 10L * 1000L; // delay between updates, in milliseconds
// the "L" is needed to use long type numbers

void setup() {
  // start serial port:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  // give the ethernet module time to boot up:
  delay(1000);
  // start the Ethernet connection using a fixed IP address and DNS server:
  Ethernet.begin(mac, ip, myDns);
  // print the Ethernet board/shield's IP address:
  Serial.print("My IP address: ");
  Serial.println(Ethernet.localIP());
}

void loop() {
  // if there's incoming data from the net connection.
  // send it out the serial port.  This is for debugging
  // purposes only:
  if (client.available()) {
    char c = client.read();
    Serial.write(c);
  }

  // if ten seconds have passed since your last connection,
  // then connect again and send data:
  if (millis() - lastConnectionTime > postingInterval) {
    httpRequest();
  }

}

// this method makes a HTTP connection to the server:
void httpRequest() {
  // close any connection before send a new request.
  // This will free the socket on the WiFi shield
  client.stop();

  // if there's a successful connection:
  if (client.connect(server, 80)) {
    Serial.println("connecting...");
    // send the HTTP PUT request:
    client.println("GET /latest.txt HTTP/1.1");
    client.println("Host: www.arduino.cc");
    client.println("User-Agent: arduino-ethernet");
    client.println("Connection: close");
    client.println();

    // note the time that the connection was made:
    lastConnectionTime = millis();
  }
  else {
    // if you couldn't make a connection:
    Serial.println("connection failed");
  }
}

请从头到尾看一变,我保证如果你有编程基础看一遍也就知道的差不多该怎么玩了。无非就是定时轮询,请求服务器,对返回的字符串进行处理。下面的代码就是我在上面代码的基础上做的修改,实现了定时从服务器获取命令,如果是1则打开Led,如果是2则关闭Led。

/*
  Repeating Web client

 This sketch connects to a a web server and makes a request
 using a Wiznet Ethernet shield. You can use the Arduino Ethernet shield, or
 the Adafruit Ethernet shield, either one will work, as long as it's got
 a Wiznet Ethernet module on board.

 This example uses DNS, by assigning the Ethernet client with a MAC address,
 IP address, and DNS address.

 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13

 created 19 Apr 2012
 by Tom Igoe
 modified 21 Jan 2014
 by Federico Vanzati

 http://arduino.cc/en/Tutorial/WebClientRepeating
 This code is in the public domain.

 */

#include <SPI.h>
#include <Ethernet.h>
const int ledPin =  2; 
// assign a MAC address for the ethernet controller.
// fill in your address here:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF
};
// fill in an available IP address on your network here,
// for manual configuration:
IPAddress ip(192, 168, 1, 177);

EthernetClient client;

IPAddress server(你的服务器地址);

unsigned long lastConnectionTime = 0;             // last time you connected to the server, in milliseconds
const unsigned long postingInterval = 2L * 1000L; // delay between updates, in milliseconds
// the "L" is needed to use long type numbers

void setup() {
  // start serial port:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  // give the ethernet module time to boot up:
  delay(1000);
  pinMode(ledPin, OUTPUT);
  // start the Ethernet connection using a fixed IP address and DNS server:
  Ethernet.begin(mac);
  // print the Ethernet board/shield's IP address:
  Serial.print("My IP address: ");
  Serial.println(Ethernet.localIP());

}
int iscoming=0;

void loop() {
  // if there's incoming data from the net connection.
  // send it out the serial port.  This is for debugging
  // purposes only:
  if (client.available()) {
    char c = client.read();
    if(iscoming==1){
      handle(c);
      iscoming=0;
    }
    if(c=='@'){
     iscoming=1;
    }
    Serial.write(c);
  }

  // if ten seconds have passed since your last connection,
  // then connect again and send data:
  if (millis() - lastConnectionTime > postingInterval) {
    httpRequest();
  }

}

void handle(char value){
 if(value=='1'){
    Serial.print("dakai");
  digitalWrite(ledPin, LOW);
 }
 if(value=='2'){
    Serial.print("guanbi");
  digitalWrite(ledPin, HIGH);
 }
}
// this method makes a HTTP connection to the server:
void httpRequest() {
  // close any connection before send a new request.
  // This will free the socket on the WiFi shield
  client.stop();
  // if there's a successful connection:
  delay(1000);
  if (client.connect(server, 8009)) {
    Serial.println("connecting...");
    // send the HTTP PUT request:
    //api/getcmd是请求的网站资源
    client.println("GET /api/getcmd HTTP/1.1");
    client.println("Host: www.arduino.cc");
    client.println("User-Agent: arduino-ethernet");
    client.println("Connection: close");
    client.println();
    // note the time that the connection was made:
    lastConnectionTime = millis();
  }
  else {
    // if you couldn't make a connection:
    Serial.println("connection failed");

  }
}

Android客户端

关于科大讯飞的介绍,自己去官网看吧,深耕语音领域二十年,不是一般企业可以超越的,很好很强大。申请成为开发者,创建应用,开通服务,下载SDK(我下的是带UI的SDK),参考SDK的代码就可以开始自己的开发了。话不多说,直接上核心代码

/**
     * 听写UI监听器
     */
    private RecognizerDialogListener recognizerDialogListener=new RecognizerDialogListener(){
        public void onResult(RecognizerResult results, boolean isLast) {
            String text = JsonParser.parseIatResult(results.getResultString());
            mResultText.append(text);
            mResultText.setSelection(mResultText.length());
            sendmeg(text);
        }

    private void sendmeg(String msg){
         AsyncHttpClient client = new AsyncHttpClient();
         try {
                String url = "http://服务器地址/api/values/"+msg;
                client.get(url, new AsyncHttpResponseHandler() {
                    @Override
                    public void onSuccess(int statusCode, Header[] headers,
                            byte[] responseBody) {
                        super.onSuccess(statusCode, headers, responseBody);
                        Toast.makeText(CmdActivity.this,
                                "请求成功" + new String(responseBody), Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onFailure(int statusCode, Header[] headers,
                            byte[] responseBody, Throwable error) {
                        // TODO Auto-generated method stub
                        super.onFailure(statusCode, headers, responseBody, error);
                    }
                });
            } catch (Exception e) {
            }

    }    /**

Asp.Net Mvc Api

服务端的代码相对就简单了,我这里创建了两个控制器,一个负责接收手机端的命令,存入数据库,一个负责返回Arduino的网络请求,将命令代码返回。虽然觉得没必要,还是上一下代码吧

接收手机端传过来的命令

// GET api/values/5
        public string Get(string id)
        {
            ardunioEntities a = new ardunioEntities();
            string tempid = "123456789";
            cmd c = a.cmd.Where<cmd>(n => n.ardunioid ==tempid).SingleOrDefault();
            if (c == null)
            {
                c = new cmd();
                c.ardunioid = tempid;
                c.command = "0";
                c.status = "未读";
                a.cmd.Add(c);
            }
            if (id.Contains("打开")) 
            {
                {
                    c.command = "1";
                    c.status = "未读";

                }
            }
            if (id.Contains("关闭"))
            {
                {
                    c.command = "2";
                    c.status = "未读";
                }
            }
            a.SaveChanges();
            return "接收成功!";

        }

返回命令给Arduino的请求

public string Get()
        {

            ardunioEntities a = new ardunioEntities();
            string tempid = "123456789";
            cmd c = a.cmd.Where<cmd>(n => n.ardunioid == tempid & n.status == "未读").SingleOrDefault();
            if (c == null)
            {
                return "@0";
            }
            c.status = "已读";
            a.Entry(c).State = EntityState.Modified;
            a.SaveChanges();
            return "@"+c.command;
        }

 

效果实现与总结

对着手机说“打开灯泡”,然后灯就亮了!话说程序员看到这一幕,兴奋之情溢于言表啊。(请忽略旁边的美桃,女友由于对我崇拜的五体投地给我洗的,嘻嘻)。

这篇文章只是一个小实验而已,后续还可以做很多有趣的玩法,比如在这个程序的基础上开发语音控制电动窗帘的的工作(目前正在玩耍…),比如直接弄一个Android开发板或者使用语音模块做一个中控,二十四小时伺服,回到家直接说话“小文,换台到体育频道”等等等等,现有的智能家居产品都可以把语音技术集成过来,相信会大大增加智能家居的易用性。

本月科大讯飞即将召开智能家庭产品发布会,我们实验室也申请了票,期待看到高大上的东东开开眼。。。。一场革命或许已经来临。

看完了就默默点个赞吧:)

092014084902993

一百元的智能家居——Asp.Net Mvc Api+讯飞语音+Android+Arduino,首发于极客范 – GeekFan.net