【目标】
按下手机app中的按钮,寝室的门锁打开。
【基本原理】
Raspberry Pi 作为控制装置:其运行Linux系统,因此功能强大;提供GPIO接口,因此便于控制物理开门机构。
yeelink 平台作为云提供者:自己搭建web服务器成本高难度大,yeelink提供免费的物联网云服务,并开放API,可以用于编写手机app。
在yeelink 平台注册一个开关型传感器,平时其value为0。
RasPi 每5s 通过yeelink HTTP API 检查value 值,当检测到1时开门。
开发可以通过yeelink HTTP API操作value值的安卓app,实现手机远程开门。
【初次配置RasPi】
组装RasPi外壳。应当加装散热片。夏天时考虑加装散热风扇。
(查看CPU温度命令:cat /sys/class/thermal/thermal_zone0/temp)
将TF卡通过读卡器连接到Windows电脑,用 Win32DiskImager 烧录RasPi系统到TF卡。
RasPi系统镜像可以从官网下载。
如需恢复TF卡,可以烧录bootsector镜像。
配置视频输出:
在Windows上打开TF卡,找到config.txt,修改一下内容
hdmi_force_hotplug=1
hdmi_group=2
hdmi_mode=16
hdmi_drive=2
config_hdmi_boost=4
sdtv_mode=2
arm_freq=800
将TF卡,键盘,HDMI线,和其它需要的外设接入RasPi,打开电源。
初次开机进入半图形设置界面,选择扩展存储卡空间,修改pi用户密码,选择开机默认启用命令行,TimeZone设置为Asia,键盘设置为104-US,确认并自动重启。
配置自动连接WiFi:
前提确保无线网卡正常工作。
sudo nano /etc/wpa_supplicant/wpa_supplicant.conf
在文件末尾添加形如以下内容
network={
ssid="名称"
psk="密码"
}
保存退出。
sudo reboot
测试开机后能否自动连接到指定WiFi。
从启动输出或从路由器查看RasPi的ip地址,。
在Windows上使用putty,在安卓手机上使用ConnectBot,进行SSH连接。
在Windows上使用FileZilla,进行sftp连接,连接地址以sftp://开头。
从此可以不再使用独立显示器和键盘。
【定时关机】
sudo crontab -u root -e
添加如下内容
35 22 * * * /sbin/init 0
【开机运行】
sudo nano /etc/rc.local
添加以下内容
sudo nohup python -u /home/pi/unlockhandler.py >/home/pi/unlockhandler.log 2>&1 &
【下载安装依赖】
sudo apt-get install python-dev
下载 RPi.GPIO-0.5.11.tar.gz 到/home/pi/
tar xvzf RPi.GPIO-0.5.11.tar.gz
cd RPi.GPIO-0.5.11
sudo python setup.py install
下载 requests-2.7.0.tar.gz 到/home/pi/
tar xvzf requests-2.7.0.tar.gz
cd requests-2.7.0
sudo python setup.py install
rm requests-2.7.0.tar.gz
rm RPi.GPIO-0.5.11.tar.gz
sudo rm -rf requests-2.7.0
sudo rm -rf RPi.GPIO-0.5.11
【基于Python的GPIO】
四个GPIO针脚控制步进电机,三个GPIO针脚控制RGB三色LED灯。
编写unlockhandler.py
源代码:
import json
import requests
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BOARD)
led_R = 40
led_G = 38
led_B = 36
stepper_A = 37
stepper_B = 35
stepper_C = 33
stepper_D = 31
GPIO.setwarnings(False)
GPIO.setup(led_R, GPIO.OUT)
GPIO.setup(led_G, GPIO.OUT)
GPIO.setup(led_B, GPIO.OUT)
GPIO.setup(stepper_A, GPIO.OUT)
GPIO.setup(stepper_B, GPIO.OUT)
GPIO.setup(stepper_C, GPIO.OUT)
GPIO.setup(stepper_D, GPIO.OUT)
GPIO.setwarnings(True)
GPIO.output(led_R, 0)
GPIO.output(led_G, 0)
GPIO.output(led_B, 0)
GPIO.output(stepper_A, 0)
GPIO.output(stepper_B, 0)
GPIO.output(stepper_C, 0)
GPIO.output(stepper_D, 0)
def setStep(w1, w2, w3, w4):
GPIO.output(stepper_A, w1)
GPIO.output(stepper_B, w2)
GPIO.output(stepper_C, w3)
GPIO.output(stepper_D, w4)
def forward(delay, steps):
for i in range(0, steps):
setStep(1, 0, 1, 0)
time.sleep(delay)
setStep(0, 1, 1, 0)
time.sleep(delay)
setStep(0, 1, 0, 1)
time.sleep(delay)
setStep(1, 0, 0, 1)
time.sleep(delay)
def backwards(delay, steps):
for i in range(0, steps):
setStep(1, 0, 0, 1)
time.sleep(delay)
setStep(0, 1, 0, 1)
time.sleep(delay)
setStep(0, 1, 1, 0)
time.sleep(delay)
setStep(1, 0, 1, 0)
time.sleep(delay)
apiurl = 'http://api.yeelink.net/v1.0/device/*****/sensor/*****/datapoints'
apiheaders = {'U-ApiKey': '********************************'}
payload={'value': 0}
rpost = requests.post(apiurl, headers=apiheaders, data=json.dumps(payload))
print time.strftime('%H:%M:%S'),
print("Ready.")
GPIO.output(led_R, 1)
time.sleep(0.5)
GPIO.output(led_R, 0)
GPIO.output(led_G, 1)
time.sleep(0.5)
GPIO.output(led_G, 0)
GPIO.output(led_B, 1)
time.sleep(0.5)
GPIO.output(led_B, 0)
while True:
rget = requests.get(apiurl,headers=apiheaders)
dic = json.loads(rget.text)
#print time.strftime('%H:%M:%S'),
if dic['value'] == 1:
print time.strftime('%H:%M:%S'),
print("Unlocking!")
GPIO.output(led_R, 1)
# unlock work flow
forward(0.01, 128)
time.sleep(2) backwards(0.01, 128)
setStep(0, 0, 0, 0)
payload={'value': 0}
rpost = requests.post(apiurl, headers=apiheaders, data=json.dumps(payload))
print("Done.")
GPIO.output(led_R, 0)
time.sleep(1)
else:
#print("Stand by.")
GPIO.output(led_G, 1)
time.sleep(0.2)
GPIO.output(led_G, 0)
time.sleep(4.8)
简述:
用 requests 库操作HTTP请求,用 json 库处理返回的 json 数据。
刚运行时,配置好针脚,将value初始化为0,三灯闪烁。
每5s读取一次value值:如果是1则操作步进电机开门,然后将value置回0,过程中亮红灯;如果是0则闪绿灯。
【安卓app】
public class MainActivity extends ActionBarActivity {
PostHandler handler = null;
TextView textView = null;
Button startButton = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new PostHandler();
textView = (TextView) findViewById(R.id.textView);
startButton = (Button) findViewById(R.id.startButton);
startButton.setOnClickListener(new OCL());
}
private class OCL implements View.OnClickListener {
public void onClick(View v) {
PostThread pt = new PostThread();
new Thread(pt).start();
}
}
class PostHandler extends Handler {
public PostHandler() {
}
public PostHandler(Looper L) {
super(L);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 接收消息更新UI
Integer statusCode = msg.what;
String status = null;
switch (statusCode){
case 0: status = "未能发送指令,请检查网络!";break;
case 200: status = "指令发送成功!";startButton.setEnabled(false);break;
default: status = "错误的返回值:" + statusCode + " !";break;
}
textView.append("\n\n" + status);
}
}
class PostThread implements Runnable {
public void run() {
HttpClient httpClient = null;
HttpGet httpGet = null;
HttpPost httpPost = null;
HttpResponse response = null;
Integer statusCode = 0;
httpClient = new DefaultHttpClient();
httpGet = new HttpGet("http://api.yeelink.net/v1.0/device/*****/sensor/*****/datapoints");
httpPost = new HttpPost("http://api.yeelink.net/v1.0/device/*****/sensor/*****/datapoints");
httpGet.addHeader("U-ApiKey", "***************************************");
httpPost.addHeader("U-ApiKey", "******************************************");
JSONObject obj = new JSONObject();
try {
obj.put("value", 1);
httpPost.setEntity(new StringEntity(obj.toString()));
response = httpClient.execute(httpPost);
statusCode = response.getStatusLine().getStatusCode();
} catch (Exception e) {
e.printStackTrace();
}
// 网络线程发送消息
Message msg = new Message();
msg.what = statusCode;
handler.sendMessage(msg);
}
}