树莓派控制宿舍门开锁(智能开锁装置)
想法由来已久,还在升华的宿舍的时候就想弄了,无奈锁不好拆又不好弄其他装置,只有等着搬来本部的破宿舍才弄。早早就买好了步进电机、薄膜键盘什么的。现在正是找工作,手上两个项目,还有学校的渣渣实习,我居然还做这个,是说明我热爱呢,还是神经病呢,还是分不清主次呢,还是闲的呢?。。。
不说废话了。一个简单的思路是:步进电机用线连接锁,让电机可以拉开,还有个薄膜键盘用来输入密码,输入正确则打开门。
另外,想着用微信控制开门,无奈个人号不能用菜单,所以打算改用手机客户端。网络控制思路是:远端服务器(我的VPS)上运行一个服务用于接收客户端开门请求,树莓派上服务连接远程服务器,远程服务器给树莓派下发开门请求,然后树莓派控制开门。这样做的原因主要是树莓派在内网没有固定IP,花生壳又要钱认证,只好让客户端和树莓派都连接远程服务器。下面是简单原理图
下面简单说明一下过程,所有程序我放在了github上,可以参考
第一部分 控制电机
树莓派中使用Python的GPIO库来控制树莓派输出。首先这个步进电机有四个输出,我分别接在了10,12,16,18针上。由于我是树莓派一代,GPIO不太够,所以这个项目中几乎用到了所有针脚。下面是树莓派针脚图,我在程序中都是使用的主板引脚编号,通过GPIO.setmode(GPIO.BOARD)来设置
控制步进电机就是四个输入轮流高电平就行了,程序见下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def rotateMotor(steps, clockwise=True): if clockwise: arr = [0,1,2,3] else: arr = [3,2,1,0] ports = [10,12,16,18] for p in ports: GPIO.setup(p, GPIO.OUT) for x in range(steps): for j in arr: time.sleep(0.003) for i in range(4): if i == j: GPIO.output(ports[i], True) else: GPIO.output(ports[i], False) for p in ports: GPIO.output(ports[i], False) |
这里steps是控制电机转的角度,clockwise是控制转的方向。有了这个再通过一定的装置控制开门关门,掌握好旋转角度使之正好打开门。
本来是有个大点的齿轮带动线的,那个机构太复杂不稳定就给抛弃了。电机固定在旺仔铁皮中,粘在门上。左边是控制步进电机的芯片,我跟步进电机一起买的。
第二部分 键盘输入
键盘原理我是从这里学习,扫描原理就是首先将4根列线设为输出低电平,将4跟行线设为输入并且上拉,当有键被按下时,对应行线电平被拉低,但是此时只是确定了按下的键位于哪一行,此时再反过来,将列线设置为输入上拉,将已经确定的行线对应行输出低电平,扫描4跟列线就可以确定按键属于哪一列了。
键盘获取按下某个按键代码如下,我是从网上找的修改了下。其中包括按下按键点亮led灯的操作,这个函数的具体实现看gitbug中完整代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
class Keypad(): KEYPAD = [ [1,2,3,"A"], [4,5,6,"B"], [7,8,9,"C"], ["*",0,"#","D"] ] ROW = [7,8,11,13] COLUMN = [15,22,24,26] def __init__(self): self.rowNum = len(self.ROW) self.columnNum = len(self.COLUMN) def getKey(self): # Set all columns as output low for j in range(self.columnNum): GPIO.setup(self.COLUMN[j], GPIO.OUT) GPIO.output(self.COLUMN[j], GPIO.LOW) # Set all rows as input for i in range(self.rowNum): GPIO.setup(self.ROW[i], GPIO.IN, pull_up_down=GPIO.PUD_UP) # Scan rows for pushed key/button # A valid key press should set "rowVal" between 0 and 3. rowVal = -1 for i in range(self.rowNum): tmpRead = GPIO.input(self.ROW[i]) if tmpRead == 0: rowVal = i # if rowVal is not 0 thru 3 then no button was pressed and we can exit if rowVal < 0 or rowVal > self.rowNum-1: self.exit() return # lit led hardware.led(True) # Convert columns to input for j in range(self.columnNum): GPIO.setup(self.COLUMN[j], GPIO.IN, pull_up_down=GPIO.PUD_DOWN) # Switch the i-th row found from scan to output GPIO.setup(self.ROW[rowVal], GPIO.OUT) GPIO.output(self.ROW[rowVal], GPIO.HIGH) # Scan columns for still-pushed key/button # A valid key press should set "colVal" between 0 and 2. colVal = -1 for j in range(self.columnNum): tmpRead = GPIO.input(self.COLUMN[j]) if tmpRead == 1: colVal=j # if colVal is not 0 thru 2 then no button was pressed and we can exit if colVal < 0 or colVal > self.columnNum-1: self.exit() return # Return the value of the key pressed self.exit() return self.KEYPAD[rowVal][colVal] def exit(self): # Reinitialize all rows and columns as input at exit for i in range(self.rowNum): GPIO.setup(self.ROW[i], GPIO.IN, pull_up_down=GPIO.PUD_UP) for j in range(self.columnNum): GPIO.setup(self.COLUMN[j], GPIO.IN, pull_up_down=GPIO.PUD_UP) # close led hardware.led(False) |
键盘获取字符和判断密码、开门等操作在keyserver.py中。*键用于清除输入,#键完成输入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
def main(): kp = hardware.Keypad() password = '' while True: digit = None while digit == None: digit = kp.getKey() # 判断输入 if digit == '*': password = '' elif digit == '#' and password == '123456': print 'open the door' hardware.openThenClose() password = '' else: password += str(digit) time.sleep(0.3) |
由此起码是可以利用键盘开锁了~
第三部分 网络控制
服务器端这边由于是第一次写,可能我的做法并不好(实际上也不好),但目前就知道这种简单可行的办法了。使用SocketServer中的ThreadingTCPServer直接建立多线程服务器。使用名字来标识是树莓派端还是用户端。
树莓派连接上服务器后,服务器端标记树莓派在线状态,只有在线状态下用户端才可以连接。用户端建立连接后发送指令,然后唤醒wait中的树莓派连接线程,就给树莓派发送开门指令。这样会有一个问题,树莓派跟服务器连接不畅的时候貌似会收不到信号。而且我偷懒,树莓派也并不会发送成功返回,客户端也就没有获得是否开门的提示。代码就不贴了,直接看github上面的吧:server.py
在树莓派一端运行了一个连接着服务器的客户端,当接收到开门信号的时候就控制步进电机转动从而实现开门操作。代码是:piserver.py
运行在用户终端上的客户端我也先用Python写了一个简易的程序,过程就是连接服务器发送一个开门信息。至于手机嘛,就先用手机版Python解释器运行这个脚本吧……代码是:client.py
(PS: 现在手机端已经有了安卓客户端,代码及运行图)
手机上python运行界面就这样,用的是QPython这个应用
下面是树莓派接线图,可以看到很多很乱,我也没有整理。中间白线是地线,由于只有一个针脚是接地所以只好引出来。杜邦线的延长是从废弃键盘上剪下来的USB线,长度也就刚刚够。步进电机、薄膜键盘、LED灯一共是16根线,用了5根USB线……
键盘贴在了门上,下面是指示灯。