【开源日记】宿舍断电自动关灯设备(一)
一个多月前,再一次早上被灯的光线刺醒之后,我随口吐槽一下学校这断电的骚操作,但是转念一想,为什么我不能做一个断电就自动把开关打下去的装置呢?于是开始了长达一个多月的“低成本开发历程”。 首先分析一下思路:熄灯之后自动把开关打下去,那首先得能检测到光线的变化,也就是需要一个光线传感器。其次是开关拨动的操作,虽然说还可以考虑把开关撬开,然后强行给它接一个继电器然后再去控制啥的,但是一来是不敢动这
文章目录
前言
一个多月前,再一次早上被灯的光线刺醒之后,我随口吐槽一下学校这断电的骚操作,但是转念一想,为什么我不能做一个断电就自动把开关打下去的装置呢?于是开始了长达一个多月的“低成本开发历程”。
思路分析
首先分析一下思路:熄灯之后自动把开关打下去,那首先得能检测到光线的变化,也就是需要一个光线传感器。其次是开关拨动的操作,虽然说还可以考虑把开关撬开,然后强行给它接一个继电器然后再去控制啥的,但是一来是不敢动这个线路(难度不大,主要是怕整短路然后被宿管找上门),二来是这方面的继电器选型问题,成本和安全无法保证。所以就采用了机械动作来拨动开关,所以需要一个舵机。既然有舵机,就需要给舵机配置一个控制电路。
确定选型
综合以上想法,开始选型。
打开淘宝,搜索光线传感器,发现大部分卖的都是利用光电二极管来实现光线检测,找到一张卖家附的电路图:
第一眼的想法就是:就这?那还是自己整一个!于是花了几块钱买了几个核心器件——光电二极管(光敏二极管),平均下来价格是成品是十分之一不到,而且还可以自己画板,超值!
然后是舵机。找到一个某比赛剩下来的SG90舵机,这样的:
确定选型之后,接下来就是真正开始实施方案了,也是我踩坑的开始。
踩坑记录
1. 用555定时器来控制舵机?
之前参加各种科创比赛,舵机都是使用单片机来控制,但这个小玩意用单片机来控制感觉有点浪费。于是试图寻找其他办法。
众所周知,舵机的控制原理实际上就是给它输入T=20ms,T_on = 0.5~2.5ms的脉冲波,既然这样,那为什么不用最简单的555定时器来做?说干就干,先仿真得到阻容大致取值,然后在面包板对着参数表开始调,为了效果更加直观,还借了一个示波器来看波形。
但是,搭好电路就非常尴尬地发现,用555定时器正常产生一个脉冲波是没问题的,但是一旦接上舵机,波形立马就乱了,似乎是电流不够引起电压不稳?那就加电容,但是一直加到670μF还是一样的结果,查了一些资料,发现B站上一位大佬一样的电路(舵机不一样)舵机转得非常稳定,私信他也没有得出什么帮助。于是陷入了死局。
最后在一位同学的提醒下,采用了STC低端的单片机——STC8G1K08A,SOP-8的封装,8个引脚却拥有两个定时器、一个10位ADC等外设,而价格也就2块钱一片,堪称国货之光!
确定单片机型号之后,接下来就是看手册、编程的过程了,不再赘述。
2. 用升压电路来减少电池的数量?
在一开始拟定的方案中,实际上我是想只用2节干电池来实现这些功能,但舵机和STC的单片机都是5V,没办法,只能再增加一个升压电路。
查阅相关资料后,我首先想到的就是最常用的MC34063,然后首先整了一个仿真,Proteus 8.10
在找资料的过程中,发现一个利用7402六非门实现升压的电路,基础功底确实了得!
原文链接(暂时没找到)
仿真似乎没什么问题,但打板之后再焊接就发现问题了,虽然它datasheet上写的是输入电压大于3V,但是两节干电池还是有点太极限了,因为试了5V是能升到12V以上的。无奈,这个方案也得放弃。
第二次尝试是直接在淘宝上搜升压电路,买了一个基于SB6286的现成的升压模块,据说可以输出2A,上手试了一下,发现升压倒是没什么问题,输出5V非常稳定,但是不知道为啥带不动300mA的舵机,去找店家质问,客服请教完技术之后来来回回就是一个回复:2A是最大电流,不能带电机。属于是解决不了开始扯皮了。无奈,这个方案也得放弃。
最后,没办法,翻出了某比赛剩下的干电池,凑了四个,也总算是不需要升压模块了,电源问题就此解决。
3. 光电二极管需要聚光吗?
众所周知,光电二极管实际上就是一个光敏开关,光线越强,导通电阻越小,这样就可以根据光线的强弱来得到不同的电压输出。然后再输入到比较器中,得到亮暗的二值表示。
一开始,担心光线可能会不够强,是不是还需要设置一个聚光的结构,甚至还专门买了一个透镜。但最后实验发现,其实光线问题完全可以通过调整阈值来实现。而且熄灯和开灯之间的亮度差别足够大,不会出现单阈值抖动的问题。因此完全不需要透镜,这样电路板也能更加简洁。
4. 为什么比较器不能输出高电平?
根据淘宝上扒来的那张电路图,我设计了一个光敏部分的电路:
这里有两个注意点:
- 光电二极管正负极问题
淘宝上扒的图好像是反过来的,不知道可不可行,但上面这张电路图肯定是对的(光电二极管和普通二极管一样,长脚的为正) - 输出上拉问题
这个问题就比较尴尬了,好久没画电路导致忽略了这个细节。记住:LM393等一些常见的比较器都是开漏输出!
5. 舵机咋拨动开关?
为了能够拨动墙上的开关,那舵机就必须得固定到墙上,为了提高集成度,便在电路板上做了几个定位孔,然后为舵机量身定制了一个支架:
此外,由于这种SG90的舵机配备的桨都是那种非常短小的,而且宿舍有两个开关,需要同时按下去,因此需要给舵机的桨“延申一下”。一开始做了一个这样的模型:
3D打印之后接上舵机的桨长这样:
但是这个支架延长之后,似乎力度就不够了,实测只能拨动一个开关,因此必须要更换结构。既然力度不够,那能不能整个杠杆来放大力?于是又翻出某比赛剩下的木条搭建了小型杠杆:
最后测试的时候还是用了原来的那种短小的桨来拨动开关,实现了预期目标。
电路及实际安装展示
得意之处——省电问题
考虑到这个设备要长期使用,但实际使用也就是拨动开关的一下,如果让它一直上电,感觉有点浪费,所以就给它设计了一个互锁电路,即单片机可以在设备不需要工作时自己断自己的电,上电需要人为操作,当时的想法是:每次熄灯之后,设备拨动开关,然后自己断电,第二天晚上开灯的时候,人为按一下上电的开关,这样设备每天工作时长从24小时变成6小时(假设18:00开灯),实现大幅省电。
这个电路是从前辈那里学到的,确实巧妙!
总结
回想整个“开发历程”,会发现其实有很多不符合项目开发流程,包括进度控制,未验证先实践等问题,但最主要反应的还是工程经验的不足,基础功底不扎实,再接再励吧!
后续版本期待
- 增加ADC,实现光线模拟量测量
- 长期运行,实现全自动化
后续
源码 //2023.4.18
本来以为这个代码本身应该是很简单的,会一点51即可,担心太占篇幅,所以当初没加,但好在现在csdn可以自动折叠代码,还是附一下吧,需要自取
#include <reg51.h>
#define uint unsigned int
#define uchar unsigned char
sfr P5 = 0xC8;
sfr P5M0 = 0xCA;
sfr P5M1 = 0xC9;
sfr P3M0 = 0xB2;
sfr P3M1 = 0xB1;
sbit PWM = P5 ^ 5; //舵机PWM输出引脚
sbit LED = P3 ^ 2; //光敏二极管输出信号
sbit relay = P3 ^ 3; //继电器控制引脚
sbit mode = P5 ^ 4; //用来切换模式
uchar time; //定义频率(周期)的变量
static uchar th; //阈值,用来改变占空比(范围在8-27左右,最使用static)
uint delay = 0; //延时变量
uchar msec = 0; //用来定时的变量
uchar sec = 0;
uchar min = 0;
uchar hour = 0;
/************************************************************************
--------------------------------------
PxM0 | PxM1 | 功能
--------------------------------------
0 | 0 | 准双向口
0 | 1 | 高阻输入(默认)
1 | 0 | 推挽输出
1 | 1 | 开漏输出
--------------------------------------
P5.4 : 高阻输入(mode) //P5M0 : 0010 0000
P5.5 : 推挽输出(PWM) //P5M1 : 1101 1111
P3.2 : 高阻输入(LED) //P3M0 : 0000 1000
P3.3 : 推挽输出(relay) //P3M1 : 1111 0100 (保持两个串口为双向口)
*************************************************************************/
void IO_Init() //初始化端口
{
P5M0 = 0x20;
P5M1 = 0xDF;
P3M0 = 0x00;
P3M1 = 0xF4;
}
void Timer_Init() //初始化定时器
{
TMOD = 0x00;//定时器0工作方式0【16位自动重装】
TR0 = 0; //关闭定时器0
/*f_out = Sysclk/12/(65536-[TH0, TL0])*/
/*频率手动设置为12MHz*/
TH0 = 0xFF; //赋初值——目的是得到周期为0.1ms【10000Hz】
TL0 = 0x9C; //舵机需要的周期为20ms,相当于要200分频,即舵机有200级可调
ET0 = 1; //开定时器0中断
EA = 1; //开总中断
TR0 = 1; //启动定时器0
}
int main()
{
IO_Init();
Timer_Init();
LED = 1; //设置为输出1,才能接收外界的输入
mode = 1;
th = 15; //初始状态
while(1)
{
if(mode == 1) //长效使用模式
{
if(msec == 50)
{
msec = 0;
sec ++;
if(sec == 60)
{
sec = 0;
min ++;
if(min == 60)
{
min = 0;
hour ++;
if(hour == 24)
hour = 0;
}
}
}
if(hour == 0 && min == 0 && sec == 0) //实现上电时关灯,且之后每间隔24小时进行一次
{
th = 5;
delay = 0;
while(delay < 50);
th = 15;
}
}
else
{
if(LED == 0)//注意要用双等号
{
th = 5; //拨动开关
delay = 0;
while(delay < 50); //延时
th = 15; //复位
delay = 0;
while(delay < 50);
relay = 0;
}
else th = 15;
}
}
}
void tim0() interrupt 1
{
time++;
if(time >= 200) //50hz
{
time = 0;
delay ++;
msec ++;
}
if(time <= th) //设置占空比
PWM = 1;
else PWM = 0;
}
更多推荐
所有评论(0)