話不多說先上視頻,一看就懂
另外可參考這里: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===================
單片機端注意的事項
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.
完整代碼下載:點這里
------------------------------------------------------------------------------------------------------------------
5年前接觸的arduino,做為一個.net程序員15年了,兒子也11歲了
馬上奔4了,15年前是程序員,現在仍舊是程序員,只是年紀大了,身體差了,毛病多了,親人朋友也有離世。。。。
園子里的程序員門,你們還好嗎,多珍重。。。。。