设计图
连接图
代码
总体方案分析:
由于需要随时监听430的消息并发送,而且要随时处理传感器的信息,如光照较强时需要开窗,光照较弱时需要关窗,因此使用前后台模式。
//前台轮询 void loop() { switch(sunlight_mode) { case SUNLIGHT_LOW: if(command_mode == AUTO && curtain_mode != CURTAIN_CLOSE) close_curtain(); break; case SUNLIGHT_HIGH: if(command_mode == AUTO && curtain_mode != CURTAIN_OPEN) open_curtain(); break; default:break; } if(Serial.available()) ReceivePacket(); if(send_deviceinfo == 1) SendDeviceInfo(); delay(500); }
//时间中断跑后台,修改各种标志,以便前台做相应处理 void timer_handler(int irq) { if(irq!=SIGALRM) return; curtain_handler(); //调用窗帘的处理 timer_count++; //时间计数器增加 if(timer_count == 100000) timer_count = 0; //当时间计数器达到100000时归零重新计数 if(timer_count%light_time == 0) //当时间计数器达到光敏所要求的间隔,则调用光敏的后台处理 { light_handler(); } if((timer_count%com_time)== 0) //当时间计数器达到430所要求的间隔,则调用430的后台处理 { com_handler(); } }
宏定义各种状态
#define SONAR_TIMEOUT 1000000 //超过这个数值认为声呐超时 //表示窗帘的状态 #define CURTAIN_OPEN 0 #define CURTAIN_MOVING 1 #define CURTAIN_CLOSE 2 //电机使用的GPIO引脚 #define ENGINE_OPEN_PIN 6 #define ENGINE_CLOSE_PIN 7 //光敏传感器所使用的GPIO引脚 #define LIGHT_PIN 2 //光照状态,分别表示强光、中等光、弱光 #define SUNLIGHT_HIGH 2 #define SUNLIGHT_MIDDLE 1 #define SUNLIGHT_LOW 0 //用来判断光照的阈值,高于HIGH则认为是强光,低于LOW则认为是弱光,否则是中等光 //双阈值用来防止单阈值在threshold旁的抖动 #define THRESHOLD_HIGH 100 #define THRESHOLD_LOW 40 //当前系统的状态,手动或者自动 #define HAND 0 #define AUTO 1
初始化变量
int trig[2] = {2,4}; int echo[2] = {3,5}; //两个超声波传感器的GPIO引脚 int sunlight_mode = SUNLIGHT_LOW; int curtain_mode = CURTAIN_CLOSE; int command_mode = AUTO; //初始化各个状态 int timer_count = 0; //用来计数时间中断产生的次数,用处在之后可以观察到 int light_time = 10; int com_time = 100; //用来表示中断间隔,在时间中断中可以看到 int send_ack = 0; //表示是否需要发送一个ack包 int send_seqnumber = 1; int receive_seqnumber = 0; //表示430通信时的seqnumber int mydeviceid = 883; //设备的ID号
重要函数解析:
//接受430传输的信号 void ReceivePacket() { count = 0; //从430模块中获取数据 while (Serial.available()>0) { c = Serial.read(); receive_buffer[count++] = c; printf ("%02x ",c); } printf("\n"); receive_buffer[count] = 0; if(*(int*)receive_buffer!=mydeviceid) return; //如果目标不是自己,则放弃这个包 printf("%d\n",receive_buffer[8]); //根据协议,第9位为类型位 switch(receive_buffer[8]) { //接收到了一个Ack,说明当前的包发送成功 case 0:if(send_seqnumber+1 == int(receive_buffer[4])) { send_deviceinfo = 0; send_seqnumber++; } break; //接受到一个数据包 case 1: SendAck(int(receive_buffer[4])+1); //发送这个数据包的Ack //根据第10位(控制位)判断是自动模式还是手动模式 command_mode = HAND; if(receive_buffer[9] == 2) { //自动模式 command_mode = AUTO; } else if(receive_buffer[9] == 1) open_curtain(); //手动模式要求开窗帘 else close_curtain(); //手动模式要求关窗帘 receive_seqnumber++; //接受Ack的seqnumber增加 break; default:break; } } //检测光敏状态 int detect_light() { int v = analogRead(LIGHT_PIN); if(v > THRESHOLD_HIGH) return SUNLIGHT_HIGH; else if(v < THRESHOLD_LOW) return SUNLIGHT_LOW; else return SUNLIGHT_MIDDLE; //双阈值法,生成三个状态,实际上有用的状态只有HIGH、LOW两个,MIDDLE只是用来防止抖动的。 } //检测窗帘状态 int get_curtain_state(int* echo,int* trig) { int covered[2] = {0,0}; int i,sum = 0; for(i=0;i<2;i++) { covered[i] = supersonic_is_covered(echo[i],trig[i]); sum+=covered[i]; } if(sum == 2) return CURTAIN_OPEN; if(sum == 1) return CURTAIN_MOVING; if(sum == 0) return CURTAIN_CLOSE; //两个超声波都没被挡住说明窗帘打开了 //只有一个超声波被挡住说明窗帘正在移动 //两个超声波都被挡住则说明窗帘关上 }