C#+Arduino Uno 實現聲控系統完全實施手冊


話不多說先上視頻,一看就懂

另外可參考這里:https://www.cnblogs.com/dehai/p/4285749.html ,這個近6年前的帖子

程序結構

程序分成上位機(PC端)與下位機(單片機):

PC端,使用的是WinForm ,聲音識別采用微軟的System.Speech.dll(這個也有很多坑后面會每坑說明)

程序由 :指令庫,任務庫,Http服務3部分組成

指令庫:
系統會選擇加載目錄下XH.Cmd.xxxx.dll文件,並加載XHCmd開頭的類型,類型需要做XHCmdAttribute標注,並且繼承XHCommandBasic類

任務庫:
系統會選擇加載目錄下XH.Task.xxxx.dll文件,並且加載XHTask開頭的定義且繼承XHTaskBasic的類,這可以用來提醒下班,自動關燈,關空調

定期檢查機房服務器狀態等等。。。。可以做很多擴展。

可以在在配置文件AppSetting節中ExcludeCmd與ExcludeTask,排除不需要加載的類型,另外下班提醒時間,開燈開空調提醒域值也在配置文件中

 下面是PC端需要注意的問題---各種坑點

1.操作系統用的是Win7 64位完整版,很多Ghost版不帶語音模塊,程序能編譯但一運行就會出錯

2.電腦的麥克風跟楊聲器最好是2個設備並且分開一段距離防

3.無法把程序改成Windows服務的形式,必需是可視化的Windows Form形式, 遠程桌面連接或計划任務啟動的都無法使用語音識別功能

  如果要長期處於監聽狀態,只能將程序執行文件的快捷方式拖進Windows的 “啟動”目錄下,並且取消windows的登陸界面

4.為保證程序穩定運行,建議在計划任務里設置每天凌晨重啟PC、

5.當然為了保證一定的安全,PC機專用,開啟防火牆(但要開放TCP ,9495端口),另外可以在網關設備或核心交換機處禁止PC訪問外網或被外網訪問。

6.提供的代碼里定義PC的IP是10.2.8.188,端口是9495, 使用時務必修改成實際的IP地址

7.app.Config中的DefaultClientIP用來區分多個單片機用的,目前只涉及一個單片機的情況下設置成服務器IP:Port形式,如果要支持多個單片機,需要自己調整程序,框架是支持的。

  具體原理是,單片機每個http請求會在QueryString攜帶傳感器讀數,並且帶上自己的標識(也就是TagIP), 而PC會將下發的指令(指開燈,開空調等)通過這個請求的Response 返回給

 指定tagIp的單片機,下發指令存放在一個以tagIp作為key的字典中。收到http請求后,在生成Response時會查看對應Key的字典中有無要攜帶回去的指令,有的話就會在Response中體現。每個單片機每隔2-3秒左右發送一次請求,也就是系統延遲在2-3秒左右。

8.執行目錄下的data下面可以放mp3,語音指令是”播放音樂“,程序會隨機選擇一個mp3文件播放,注意不是所有的mp3多可以有些不能播放,可以先用后面遙控板項目提供的mp3測試

 

 

單片機是arduino uno 配合網絡板擴展,采用的是有線網絡,硬件模塊也是近6年前的老物件

單片上連了紅外頭用來控制空調,光敏電阻采集網照強度可以在昏暗時提醒開燈,用溫濕傳感器DHT11--這個也比較坑,用電流傳感器--用來確定空調是否打開,還有控制燈的繼電器

完整的單片機代碼入下,注意接口

#define DHT11_PIN 0   ---dht11 連的是arduino的A0 口
#define CurrentIndexPin A3   --- 電流傳感器連A3口
#define IlluminationPin A2     --光敏電阻連A2口
#define relayAPin 8             ---繼電器連 8號口
#define RebootPin 5           --這個是5號口連的是三級管的基級,用來物理重啟單片機用的,這樣你的系統可以非常非常強壯

/*
  Web client
 
 This sketch connects to a website (http://www.google.com)
 using an Arduino Wiznet Ethernet shield. 
 
 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13
 
 created 18 Dec 2009
 by David A. Mellis
 modified 9 Apr 2012
 by Tom Igoe, based on work by Adrian McEwen
 
 */
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <SPI.h>
#include <Ethernet.h>
#include <IRremote.h>
//#include <avr/wdt.h>  


#define  DHT11_PIN 0
#define  CurrentIndexPin A3
#define  IlluminationPin A2
#define  relayAPin 8
#define  RebootPin 5
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAA, 0xBE, 0xEF, 0xFF, 0xE2 };
// if you don't want to use DNS (and reduce your sketch size)
// use the numeric IP instead of the name for the server:
//IPAddress server(74,125,232,128);  // numeric IP for Google (no DNS)
char server[] = "10.2.8.188";    // name address for Google (using DNS)

// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(10,2,8,198);
IPAddress gateway(10,2,8,1);
IPAddress dns_server(60,191,134,196);
IPAddress subnet(255,255,255,0);
// Initialize the Ethernet client library
// with the IP address and port of the server 
// that you want to connect to (port 80 is default for HTTP):
EthernetClient client;

// Media Control RAW Code
IRsend irsend;
//open_cool_20_strong
const unsigned int  PowerOn[] PROGMEM={
4610,4152,742,1395,740,354,737,1398,724,1438,718,350,714,380,709,1439,688,408,665,
405,641,1522,628,442,621,472,618,1518,612,1550,607,469,591,1567,586,1555,574,522,566,
1573,555,1608,554,1608,547,1589,543,1619,540,1595,546,547,544,1592,545,548,546,522,
541,553,548,519,544,550,542,525,544,550,545,522,544,1617,545,524,542,551,543,524,543,
524,543,551,548,1587,545,1617,543,524,570,1591,542,1619,547,1589,545,1616,546,1590,
543,5188,4375,4380,544,1590,548,545,546,1590,541,1621,545,522,544,550,545,1590,546,
547,546,521,542,1620,544,522,544,550,543,1592,543,1619,549,519,543,1618,541,1621,545,
521,546,1617,544,1592,546,1616,546,1589,543,1619,546,1615,545,524,541,1620,542,524,
543,551,545,523,543,524,542,551,545,523,543,549,548,520,539,1622,541,527,546,547,543,
523,542,551,544,524,540,1622,541,1594,546,548,543,1592,545,1616,543,1593,545,1616,
548,1614,540
};  //共有199條數據
 
//close
 const unsigned int  PowerOff[] PROGMEM={
4589,4171,734,1402,725,369,717,1419,715,1447,711,361,700,404,673,1466,653,443,634,
435,626,1535,624,445,620,474,614,1522,610,1552,601,468,597,1567,590,490,569,1590,567,
1574,553,1609,549,1612,549,519,541,1620,551,1586,542,1620,545,522,546,548,544,523,
546,548,546,1589,533,561,535,532,534,1628,535,1601,537,1624,541,527,545,547,543,526,
546,547,544,523,545,549,547,522,537,529,573,1589,543,1619,544,1592,555,1605,551,1584,
548,5184,4376,4377,542,1594,544,549,544,1592,544,1618,548,519,543,552,546,1589,544,
550,543,523,547,1616,546,521,544,549,546,1589,546,1616,544,523,545,1618,543,523,571,
1592,546,1615,549,1587,546,1616,549,518,546,1616,543,1593,545,1616,542,550,545,523,
545,522,544,549,546,1590,546,547,546,520,549,1615,544,1617,545,1590,546,548,545,521,
548,520,544,547,545,524,545,549,544,524,544,550,544,1592,542,1618,546,1590,543,1619,
546,1615,540
};  //共有199條數據


byte relayAStatus=0;
int il=0;
int ci=0;
int errCount=0;



void setup() {
  
    pinMode(relayAPin,OUTPUT);
    digitalWrite(relayAPin, relayAStatus);
    pinMode(RebootPin,OUTPUT);
    digitalWrite(RebootPin,LOW);   

  //wdt_enable(WDTO_8S);    
  
  DDRC|=_BV(DHT11_PIN);
  PORTC|=_BV(DHT11_PIN);
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }

  // start the Ethernet connection:
  // give the Ethernet shield a second to initialize:

  
   if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    Ethernet.begin(mac, ip,dns_server,gateway,subnet);
  }
  
  delay(2000);
  Serial.println("Ready");
}


void loop()
{
  
 double t=0;
 double h=0;
 bool isOK=false;

isOK=readDHT11Data(t,h);
 httpRequest(t,h);
 if(relayAStatus==1){
  digitalWrite(relayAPin, HIGH);
 }else{
   digitalWrite(relayAPin,LOW) ;
  }
 il=analogRead(IlluminationPin);
 ci=getMaxValue();
 Serial.println(il);
 Serial.println(ci);
 
 delay(1000);
  

  

}
//================DHT11 Code==================


//================DT11 Code 1=======================
bool readDHT11Data(double &t,double &h){
  t=-888;
  h=-888;
  
 
  byte dht11_dat[5];
  byte dht11_in;
  byte i;
  PORTC &= ~_BV(DHT11_PIN);
  delay(18);
  PORTC|=_BV(DHT11_PIN);
  delayMicroseconds(40);
  DDRC &= ~_BV(DHT11_PIN);
  delayMicroseconds(40);
  dht11_in = PINC & _BV(DHT11_PIN);
  if(dht11_in)
  {
    Serial.println("dht11 start condition 1 not met");
    return false;
  }

  delayMicroseconds(80);
  dht11_in=PINC & _BV(DHT11_PIN);
  if(!dht11_in)

  {
    Serial.println("dht11 start condition 2 not met");
    return false;
  }

  delayMicroseconds(80);
  for(i=0;i<5;i++)
    dht11_dat[i]=read_dht11_dat();
  DDRC|=_BV(DHT11_PIN);
  PORTC|=_BV(DHT11_PIN);
  byte dht11_check_sum = dht11_dat[0]+dht11_dat[1]+dht11_dat[2]+dht11_dat[3];
  if(dht11_dat[4]!=dht11_check_sum)
  {
    Serial.println("DHT11 checksum error");
    return false;
  }




 t= dht11_dat[2] + (double)dht11_dat[3]/100;
 h=dht11_dat[0] + (double)dht11_dat[1] /100;
 

  Serial.print("temperature = ");
  Serial.print(t,DEC);
  Serial.print("C");
  Serial.print("Current humdity= ");
  Serial.print(String(h));
  Serial.println("%");

  
  return true;
}
byte read_dht11_dat()
{
  byte i = 0;
  byte result = 0;
  for(i=0;i<8;i++)
  {
    while(!(PINC&_BV(DHT11_PIN)));
    delayMicroseconds(30);
    if(PINC&_BV(DHT11_PIN))
      result|=(1<<(7-i));
    while((PINC&_BV(DHT11_PIN)));
  }
  return result;
}

//================End DHT11 Code==================

//================Http Request Code========================
void httpRequest(double t,double h){
    // if you get a connection, report back via serial:
    
  if(!client.connected()){
     Serial.println("disconnecting.");
     client.stop();
     if (client.connect(server, 9495)) {
        Serial.println("connected");
     }else{
     // kf you didn't get a connection to the server:
        Serial.println("connection failed"); 
        errCount++;
        if(errCount>=3){
          digitalWrite(RebootPin,HIGH);
          delay(1000);
        } 
    }
  } else{
       
    // Make a HTTP request:
    client.println("GET /?m=c&mil=" + String(millis()) + "&t="+String(t)+"&h="+String(h)+ "&ra=" +String(relayAStatus)+"&i="+String(il) +"&ci=" +String(ci) +" HTTP/1.1");
    client.println("Host:10.2.8.188");
    //client.println("Connection: close");
    
    client.println();
    delay(10);
    String reply="";
    // if there are incoming bytes available 
    // from the server, read them and print them:
    while (client.available()) {
      char c = client.read();
      reply+=c;
      
    }
    //Serial.print(reply);
    if(reply.indexOf("\"Model\":\"OPENA\"")>=0){
      Serial.println("OPEN");
      powerOnA();
    }
    if(reply.indexOf("\"Model\":\"CLOSEA\"")>=0){
      Serial.println("CLOSE");
       powerOffA();      
    }
    if(reply.indexOf("\"Model\":\"OPENL\"")>=0){
      Serial.println("OPENL");
      powerOnL();
    }
    if(reply.indexOf("\"Model\":\"CLOSEL\"")>=0){
      Serial.println("CLOSEL");
       powerOffL();      
    }
    errCount=0;
    //wdt_reset();
  }

  

}
//================End Http Request Code===============

//================AirConditioner && Light Control===========
void powerOffA(){

  unsigned int t[199];
  for(int i=0;i<199;i++){
   t[i] = pgm_read_word_near(PowerOff+i);
 
  }
  irsend.sendRaw2(t,199,38);
   delay(3000);
}
void powerOnA(){

  
  unsigned int t[199];
  for(int i=0;i<199;i++){
   t[i] = pgm_read_word_near(PowerOn+i);
   
  }
  
   irsend.sendRaw2(t,199,38);
   delay(3000);
}
void powerOffL(){
  relayAStatus=0;
}
void powerOnL(){
  relayAStatus=1;
  
}

//================CurrentIndex Read Max Value=================
    int getMaxValue()
    {
        int sensorValue;             //value read from the sensor
        int sensorMax = 0;
        uint32_t start_time = millis();
        while((millis()-start_time) < 1000)//sample for 1000ms
        {
            sensorValue = analogRead(CurrentIndexPin);
            if (sensorValue > sensorMax)
            {
                /*record the maximum sensor value*/
                sensorMax = sensorValue;
            }
        }
        return sensorMax;
    }

//================End CurrentIndex ReadMax Value=============

//================End AirConditioner Control===================
View Code

 

 單片機端注意的事項

1.DHT11 正負極一定不能接反,數據線要檢測下是否導通的,話說折騰了幾個小時發現是陳年的數據線時通時不通導致的。。。

如果單片機用mega2560那么使用DHT11庫比較方便.

2.紅外發射頭針對Uno 使用的是3號腳,而針對mega2560使用的是9號腳,另外紅外頭必需足夠靠近空調。同時務必注意連線接觸要
好,發送的代碼是采集的美的空調遙控器,其他空調要需要另外采集--教程自己百度

3.繼電器,剛開始用的機械的,然后是光藕隔離機械的,但是由於負載是LED(220AC,70W)結果經常一吸合上就跳不開了。

所以如果也是LED等帶交流轉直流的務必采用固態繼電器,這里使用的是5V高電平觸發,220V AC 2A 的固態繼電器,可自己
上某寶或某東找去。

4.電流傳感器,這個是檢測空調是否啟動的,某寶上能找到的是最大支持5A電流,用來檢測空調有點問題(空調最大16A),所以要改動空調電源線的火線(L線)
,在主空調火線上旁路一條細線分流,

線徑是主線的1/5,電流傳感器(感應式的)就裝旁線上,理論上可將電流降低1/6,就能符合傳感器的規格了。

5.最后是三極管(S8050),基極過1K電阻連5號口,集電極過270歐電阻連Vcc,同時集電極連單片機RESET口,發射極連GND,單片機
代碼里檢測到連續3次錯誤后會給5號口高電平,這樣就可以重啟單片機,5號口默認是數字輸出LOW.

 

完整代碼下載:點這里

                         點這里V2版

------------------------------------------------------------------------------------------------------------------

 5年前接觸的arduino,做為一個.net程序員15年了,兒子也11歲了

馬上奔4了,15年前是程序員,現在仍舊是程序員,只是年紀大了,身體差了,毛病多了,親人朋友也有離世。。。。

園子里的程序員門,你們還好嗎,多珍重。。。。。

 

何須問


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM