话不多说先上视频,一看就懂
另外可参考这里: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文件播放。
单片机是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年前是程序员,现在仍旧是程序员,只是年纪大了,身体差了,毛病多了,亲人朋友也有离世。。。。
园子里的程序员门,你们还好吗,多珍重。。。。。