Linux/Raspbian 每个目录用途说明

本文转自无聊小博,很多刚接触树莓派/Linux 的同学会在给树莓派安装、卸载、配置软件时,软件和配置文件等存放在哪儿产生疑惑。也会遇到诸如“磁盘分区”、U盘挂载等涉及到的目录路径问题。Linux 的目录是有非常明确的规则的,这里推荐这篇文章助你快速了解、快速上手。

为什么会有这篇文章

因为在使用apt-get purge remove openvpn之后,发现系统里依然有很多openvpn名字的目录或者文件,比如像下面这样

/usr/sbin/openvpn
/etc/default/openvpn
/etc/network/if-down.d/openvpn
/etc/network/if-up.d/openvpn
/etc/bash_completion.d/openvpn
/etc/init.d/openvpn
/run/openvpn

有强迫症+洁癖的我当然就很不爽了,挨个查看之后,就删掉了这些,但是对于一些目录的用户比较感兴趣,就找到了下面这篇文章,英文的,翻译了,留在这里,增加自己对LINUX系统的了解。

Linux和Windows的区别

的显著区别之一就是其不同的目录结构,并不仅仅是格式上的不同,而是不同位置上保存的东西区别很大。

在Windows中,典型的路径可能是这样的 D:\Folder\subfolder\file.txt,而在Linux中,路径则是这样的 /Folder/subfolder/file.txt

斜线倾斜的方向不同,并且,在Linux中,也没有C盘D盘的概念,Linux系统启动之后,根分区 就”挂载”在了在了 / 的位置,并且所有的文件、文件夹、设备以及不同的硬盘光驱之类的,也都挂载在了 /

虽然可能在下面这个例子中并不明显,但是Linux系统对文件或者文路径的名称中的大小写字符是敏感的。

比如 /Folder/subfolder/file.txt 与 /folder/subfolder/file.txt并不是同一个文件。

Linux系统目录说明

Unix中和Linux的目录结构是一个统一的目录结构,所有的目录和文件最终都统一到”/“根文件系统下。文件系统是无论是不是挂载过来的,最终都分层排列到以”/“为起始的文件系统之下。
Linux目录结构遵循”文件系统层次结构(Filesystem Hierarchy Structure,FHS)”,这标准是由“自由标准组织(Free Standards Group)”进行维护的,然而大多数LINUX发行版都有意或者无意的与这一规范背离。

“/” 根路径

这是Linux系统的“根”目录,也是所有目录结构的最底层。在UNIX以及和它兼容的系统中,”/“是一个单独的目录。

/boot

这个目录下包含系统启动文件(boot loader),例如Grub,Lilo或者Kernel,以及initrd,system.map等配置文件。

Initrd ramdisk或者””initrd””是指一个临时文件系统,它在启动阶段被Linux内核调用。initrd主要用于当“根”文件系统被挂载之前,进行准备工作。

/sys

这个目录下包含内核、固件以及系统相关文件。

/sbin

包含系统操作和运作所必需的二进制文件以及管理工具,主要就是可执行文件。类似WINDOWS下的EXE文件。

/bin

包含单用户模式下的二进制文件以及工具程序,比如cat,ls,cp这些命令。

/lib

包含/sbin和/bin目录下二进制文件运行所需要的库文件。

/dev

内含必需的系统文件和驱动器。

/etc

内含系统配置文件,其下的目录,比如 /etc/hosts, /etc/resolv.conf, nsswitch.conf, 以及系统缺省设置,网络配置文件等等。以及一些系统和应用程序的配置文件。

/home

每一个用户的在这个目录下,都会单独有一个以其用户名命令的目录,在这里保存着用户的个人设置文件,尤其是以 profile结尾的文件。但是也有例外,root用户的数据就不在这个目录中,而是单独在根路径下,保存在单独的/root文件夹下。

/media

一个给所有可移动设备比如光驱、USB外接盘、软盘提供的常规挂载点。

/mnt

临时文件系统挂载点。比如,你并不想长期挂载某个驱动器,而是只是临时挂载一会U盘烤个MP3之类的,那么应该挂载在这个位置下。

/opt

在Linux系统中,这个目录用到的并不多,opt是 可选系统程序包(Optional Software Packages)的简称。这个目录在UNIX系统,如Sun Solaris用途要广泛的多。

/usr

用户数据目录,包含了属于用户的实用程序和应用程序。这里有很多重要的,但并非关键的文件系统挂载这个路径下面。在这里,你会重新找到一个 bin、sbin 和 lib目录,其中包含非关键用户和系统二进制文件以及相关的库和共享目录,以及一些库文件。

/usr/sbin

包含系统中非必备和并不是特别重要的系统二进制文件以及网络应用工具。

/usr/bin

包含用户的非必备和并不是特别重要的二进制文件。

/usr/lib

保存着/usr/sbin以及/usr/bin中二进制文件所需要的库文件。

/usr/share

“平台无关”的共享数据目录。

/usr/local

是/usr下的二级目录,这里主要保存着包含系统二进制文件以及运行库在内的本地系统数据。

/var

这个路径下通常保存着包括系统日志、打印机后台文件(spool files)、定时任务(crontab)、邮件、运行进程、进程锁文件等。这个目录尤其需要注意进行日常的检查和维护,因为这个目录下文件的大小可能会增长很快,以致于很快占满硬盘,然后导致系统便会出现各种奇奇怪怪的问题。

/tmp

顾名思义,这是一个临时文件夹,专门用来保存临时文件,每次系统重启之后,这个目录下的”临时”文件便会被清空。同样,/var/tmp 也同样保存着临时文件。两者唯一的不同是,后者 /var/tmp目录保存的文件会受到系统保护,系统重启之后这个目录下的文件也不会被清空。

/proc

这个目录是驻留在系统内存中的虚拟(psuedo,伪)文件系统,其中保存的都是文本格式的系统内核和进程信息。

LINUX系统目录结构图

需要注意的是,不同LINUX发行版本的目录结构会有一些差异,这对LINUX新手来说比较纠结,但是大体上,所以LINUX的不同发行版本,都符合上面这幅图片中的路径结构。

参考文章

1.Linux Directory Structure Overview

本文来自:树莓派实验室

修改树莓派交换分区 SWAP 的正确姿势

Swap分区是磁盘上的一个特殊用途的分区。是当系统的物理内存不够用的时候,把物理内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间被临时保存到Swap分区中,等到那些程序要运行时,再从Swap分区中恢复保存的数据到内存中。

分配太多的Swap空间,会浪费磁盘空间,而Swap空间太少,则系统会发生错误。一般在内存小于2G的情况下,交换分区应为内存的2倍。对于树莓派来说,可以设置为2GB。当然也需要考虑 microSD 卡容量和使用情况。

错误的做法

下面是错误的做法!错误的做法!错误的做法!重要的事情说三遍!!!

一直以来,我以为树莓派的 swap 和普通 linux 的差不多,也就照着某些网站上的方法修改树莓派的交换分区大小。

熟悉的套路开始了!

首先,查看内存大小:

$ free -m
total used free shared buff/cache available
Mem: 976 35 226 12 714 864
Swap: 99 0 99

然后:

$ sudo dd if=/dev/zero of=/swapfile bs=1024 count=102400
$ sudo mkswap /swapfile
$ sudo chown root:root /swapfile
$ sudo chmod 0600 /swapfile
$ sudo swapon /swapfile
$ sudo vi /etc/fstab

接着,添加以下内容到fstab文件尾:

/swapfile swap swap defaults 0 0

查看是否有swap空间

$ free -m

重启后,你就会发现树莓派的 swap 又回到还原了。

正确的做法

如果你按照上面的方法做了,只需删除 swap 分区:

$ sudo swapoff /swapfile
$ sudo rm /swapfile

然后将自己在 /etc/fstab 文件添加的那一行删除。

下面开始正式修改树莓派的 swap 的大小:

sudo vi /etc/dphys-swapfile

将 CONF_SWAPSIZE 的值修改成你想要的大小。 一般在内存小于2G的情况下,交换分区应为内存的2倍!

然后,重新启动 dphys-swapfile 文件服务:

sudo /etc/init.d/dphys-swapfile restart

最后查看大小:

$ free -h
total used free shared buff/cache available
Mem: 976M 66M 801M 12M 108M 849M
Swap: 2.0G 0B 2.0G

后记

在修改 /etc/fstab 文件的时候,我在下面的注释里发现了这样的两句话:

# a swapfile is not a swap partition, no line here
# use dphys-swapfile swap[on|off] for that

不过当时并不知道 dphys-swapfile 是什么,也不知道它在哪。后来通过搜索 dphys-swapfile 查阅到这篇文章 How to change Raspberry Pi’s Swapfile Size on Raspbian,终于找到了正确修改树莓派交换分区的方法。

转载自 树莓派实验室

无线安全审计工具FruityWifi初体验

FruityWIfi是一款有名的无线安全审计的开源工具,其灵感来自于wifipineapple,目前该工具已经更新到2.4。它能够让用户通过web界面来控制和管理模块,十分方便。FriutyWifi最初的设想是能够在树莓派上使用,但是也可以安装在Debian系列的操作系统上,例如Debian,kali Linux等,甚至可以安装在NetHunter上,在某些移动设备上使用。

一、本次使用的环境

硬件为树莓派3B,系统为raspbian jessie。

二、安装方法

-下载zip压缩文件:https://github.com/xtr4nge/FruityWifi

-解压后用root权限运行对应目录下的install-FruityWifi.sh,如果树莓派系统为raspbian stretch,运行install-FruityWifi-PHP7.sh。

安装时很顺利,安装完成后,访问地址为:

http://localhost:8000

https://localhost:8443

三、使用过程

打开页面后会发现一个简约的界面,用户和密码都是admin

进入FruityWifi的web管理界面后可以看到目录上的几个选项。

status:这里可以看到安装的模块,能够控制模块的运行和停止,还能够看到网卡的信息和连接热点设备的ip和名字。

 

status-ws:和status提供的功能和信息一样,不过这里能够看到模块的当前日志信息,适合和移动设备的访问。

config:这里能够设置软AP,可以看到很多模式,能够灵活的对接口进行组合。IN口为内网接口,一般设置为能够提供软AP服务的那个网口。OUT口为外网接口,设置为连上互联网的网络接口。配置好IN口和OUT口的地址后,点击save保存。AP模式最初有两个可以提供选择,分别是Hostpad和Airmon-ng,如果安装了karma和mana后,会多出两个模式来,会增加Hostapd-Mana和Hostapd-Karma两个模式,当使用这两个模块时,配置对应的AP模式即可。当无线网卡使用Realtek芯片时,只能选择Airmon-ng模式,并且只有使用 atheros芯片的无线网卡才支持karma和mana攻击。下载好AP模块后,就能够很好的管理和启动热点。

modules:这里列出了许多可用的插件,可供下载使用。树莓派3B自带的无线网卡是不支持monitor模式的,如果想使用mdk3这种需将网卡设置为monitor模式的模块,需外置个支持monitor模式的USB无线网卡。

下面列举几个常用的基本的模块:

1、Autostart,启用FriutyWifi时自动开启相对应的模块。

2、karma和mana,karma能够响应客户端设备主动扫描的探测请求,并伪造同名热点吸引客户端的连接。由于各厂商改成了被动扫描,karma的攻击逐渐不好用了,而mana可以说是karma的改进,它能够收集周围空间的SSID信息,当收到BoradcastProbeRequest 后,会根据列表中的每一个SSID构成ProbeRsponse向客户端回复。关于karma和mana的具体原理,可以看下这两篇文章:http://www.freebuf.com/articles/77055.html ,http://www.freebuf.com/articles/wireless/145259.html

3、mdk3,能够发起能够发起Beacon Flood、Authentication Dos、 Deauthentication等模式的攻击。需要将网卡设置为monitor模式,在config界面中能够很轻松的设置, mkd3的DOS攻击配和karma|mana能够使客户端断开原来的热点并主动连接钓鱼热点。

4、DetectDeauth,一款检测eauthentication attacks的模块。能够设置周期性的检测deauthentication frame,如果超过自己设置的阈值,则会记录下来。

当客户端连上钓鱼的热点后,客户端设备的所有流量都经过钓鱼热点,这时可以使用一些模块获取想要的数据,例如ngrep来抓取cookie ,URLSnarf抓取URL信息等;也有许多中间人攻击的模块提供使用,比如ettercap ,sslstrip等。有些插件笔者使用时未能正常启动,比如nessus、DeviceFinder 等。

logs:各个模块的输出日志信息,能够很方便的查看。

四、后记

作为一个款无线安全审计工具,FriutyWifi从安装到使用都非常简单,也方便管理。有些模块在树莓派3B上未能正常的使用,希望新版能够修正这些模块,也希望这款开源工具能够越来越丰富,越来越稳定。

 本文作者scu-igroup,转载自FreeBuf.com

 

 

 

配置树莓派USB接口的电流限制

树莓派的USB接口是有电流限制的,默认只能达到600mA。这给外接一些对电流要求较高的设备时会遇到电流不足的问题,导致无法工作或工作不稳定。

例如外接某些移动硬盘,我们通常需要加入额外的电源来对这些设备供电。当然可以选择有源 USB HUB 来很好地解决。

而今天介绍的一种方案,只需要修改树莓派系统的配置,即可提高 USB 接口供电的电流限制(最大为 1.2A)。

首先申明一些重要的注意事项:

  • 首先需要合理评估超载的电流是否在可以接受的范围内,如超出默认值 600mA 50%以上,则墙裂建议用有源 HUB 方案。
  • 本方案不可用于树莓派 Zero。也不适用于 2017-04-10 之前的 Raspbian 系统。
  • 使用该方案之后,树莓派将失去保修的条件。
  • 分享本方案仅供专业玩家了解这个配置项,如果你无法理解或无法对负载进行合理评估请勿继续阅读,我们非常不愿意看到因为忽视了以上事项而导致你的树莓派主板因过载而报废。同时对进行该项尝试而发生的任何可能的损失免责。

因树莓派的USB接口电流大小由USB电流保护芯片进行限制,所以通过旁路USB电流保护芯片(即不让电流从电流保护芯片流过)就可以实现更大电流。

具体配置需要修改 /boot/config.txt 这个文件,在最后面添加三行。

max_usb_current=1
current_limit_override=0x5A000020
avoid_warnings=2

由于电源芯片限制,最大电流为1.2A。重新启动树莓派即可生效。

曾经有一个树莓派摆在你的面前正常工作着,你要好好珍惜哦~(采用请慎重)

 

本文来自:树莓派实验室

利用ESP8266芯片制作无线网屏蔽仪

只需花费几十块钱买一块ESP8266芯片就可以DIY一个无线网络屏蔽仪,体积小巧,使用充电宝或手机供电,可以屏蔽有效范围内的任意一个无线网络信号。原理是802.11无线网络协议中有一个解除认证帧,通常是用来断开某个无线网络连接。由于这些帧数据包不加密,因此只要获取了无线路由器和设备的无线网卡MAC地址,就可以在无线网络有效范围内断开某个设备的连接。通过这个方法,黑客还可以进行信标垃圾、随机信标垃圾攻击或干脆屏蔽所有无线网络。

完整教程视频:

http://v.youku.com/v_show/id_XMzE5MTkzMDA1Ng==.html?spm=a2h3j.8428770.3416059.1

可以在手机上下载应用程序控制无线网络屏蔽:

geni.us/PQhB

第一步:材料准备

1、 购买ESP8266芯片组(必备)。

2、 电池(选购)

第二步:下载最新版的Arduino编译软件arduino.cc/en/Main/Soft

1、 安装后打开

2、 选择“文件”,“参数”

3、 在“其他板载管理URLs”中添加以下地址:arduino.esp8266.com/sta

4、 选择“工具”,“板”,“板载管理器”

5、 输入ESP8266

6、 必须选择“版本2.0.0”

7、 选择“文件”,“参数”

8、 打开“更多”,进入编辑模式,最后记得保存文件。

9、 找到ESP8266页面,选择“硬件”,“ESP8266”,“2.0.0”,“工具”,“sdk”,“include”

10、用文本编辑器打开user_interface.h文件

11、在文件中#end标记前插入以下内容:

typedef void (**freedom_outside_cb__t)(uint8 status);
int wifi_register_send_pkt_freedom_cb(freedom_outside_cb_t cb); 
void wifi_unregister_send_pkt_freedom__cb(void); 
int wifi_send_pkt_freedom(uint8 **buf, int len, bool sys_seq);

12、保存文件

第三步:下载项目文件

1、 下载项目文件geni.us/A5cOYc

2、 打开Wifi Jammer> esp8266_deauther,用Arduino打开esp8266_deauther.ino文件

3、 在“工具”中选择对应的ESP8266型号,本文中用的是NodeMCU 0.9,如果不行可以尝试选择NodeMCU 1.0 或Generic ESP8266 Module

4、 在“工具”中,选择“编译器”> ArduinoISP

5、 在“工具”中,选择对应的端口号

6、 上传成功

第四步:如何使用

1、 ESP8266芯片加电

2、 可以使用智能手机或电脑控制芯片组,扫描无线网络名称为AndroidAP,初始密码killwifi。连接成功后,可以使用手机应用程序或在电脑浏览器中输入192.168.4.1来屏蔽周围的无线网络信号了。

3、 选择“攻击”>解除验证所有,所有的无线网络都会被屏蔽。

本文转载自 W-Pwn科技知乎专栏

 

Python+树莓派制作IoT(物联网)门控设备

前些天写了篇文章是利用树莓派制作一个开关门监控设备的雏形《Python+树莓派 是谁在开门?》,但是这个设备运行的前提是需要树莓派正常供电并已经连接了网络,但是如果需要在没有电、没有宽带网络的户外实现随时掌控开关门的状态该怎么办?今天就和大家分享一个正在制作的物联网开关门监控设备。

因为考虑需要在户外使用这套物联网门控设备,所以利用树莓派完成这个设备有两个问题需要解决,
第一是需要解决树莓派和相关模块的供电问题。
第二就是需要户外没有宽带网络情况下的信号传输问题。
只要解决这两个问题那么剩下来的问题就是编程方面的了,针对以上两个问题,这里我们采用比较大众化的方式解决,设备的供电问题我们使用太阳能配合蓄电池进行实现7X24小时供电,信号的传输问题我们使用一块叫做SIM868的通讯模块来实现。下面来介绍一下设备制作的材料准备、制作过程以及程序的编写和调试。

1.准备材料及工具
1.树莓派(Raspberry Pi 3B) 数量:1块

2.SIM868通讯模块(这里使用的是适配树莓派的微雪电子的SIM868通讯模块) 数量:1块

3.电磁感应磁条(常开常闭型) 数量:1组

4.SIM868模块外接天线 数量:1根(根据现场安设实际情况确定)

5.树莓派T型扩展板 数量:1块

6.实验面包板 数量:1块

7.杜邦线(公对公,公对母) 数量:若干

8.太阳能板 数量:1块

9.带保护板的锂电池 数量:2组(根据电池使用的性能情况可适当增加)

10.电压电流转换板 数量:1块

11.连接电线 数量:(根据现场安设实际情况确定)

12.两芯屏蔽线 数量:10米(0.3粗即可根据现场安设实际情况确定)

13.防爆箱 (防尘、防雾) 数量:1个(规格根据实际情况确定)

14.Micro USB充电线 数量:2根

15.SIM卡(移动和联通均可,模块暂不支持电信CDMA) 数量:1张

16.电烙铁及焊锡

2.设备供电及模块链接说明
(1)供电原理:设备的供电依靠太阳能板配合锂电池进行供电,需要一块可以将太阳能板和锂电池的输出电压和电流转换成树莓派和SIM868通讯模块工作电压和电流的转换板,设备供电链接图如图。
实现效果,白天可以通过太阳能负责给树莓派及通讯模块供电并同时给锂电池充电。晚上将由充电完毕的锂电池负责给设备供电。

(2)设备链接:树莓派的GPIO PIN# 2针脚(5V)和 PIN# 23针脚 链接电磁感应模块的引线。负责接收电磁感应模块的开关状态,树莓派的GPIO PIN# 4(5V),PIN# 6(Ground) ,PIN# 8(TX),PIN# 10(RX)分别链接SIM868通讯模块的5V,ground,串口TX和RX,负责实现模块的树莓派与SIM868通讯模块的串口通讯和供电链接,并将信号树莓派的接收到的电磁感应磁条开关信号,通过SIM868通讯模块出输出去(这里采用的是http传输协议,具体实现方法见程序编码部分)

3.程序代码:
(1)python程序源码:

import  RPi.GPIO as GPIO
import time
import serial  
def gpio_init():
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(23,GPIO.OUT)
def send_data(param):
    W_http_6='AT+HTTPPARA="URL","http://**************/iot.php?status='+param+'"\r\n'
    ser.write(W_http_6)
    time.sleep(2)
    W_http_7='AT+HTTPACTION=0\r\n'
    ser.write(W_http_7)
    time.sleep(3)
if __name__ == '__main__':
    ser = serial.Serial("/dev/ttyS0",115200)
    W_http_1='AT+HTTPTERM\r\n'
    ser.write(W_http_1)
    time.sleep(3)
    W_http_2='AT+SAPBR=3,1,"CONTYPE","GPRS"\r\n'
    ser.write(W_http_2)
    time.sleep(3)
    W_http_3='AT+SAPBR=3,1,"APN","CMNET"\r\n'
    ser.write(W_http_3)
    time.sleep(3)
    W_http_4='AT+SAPBR=1,1\r\n'
    ser.write(W_http_4)
    time.sleep(3)
    W_http_5='AT+HTTPINIT\r\n'
    ser.write(W_http_5)
    time.sleep(3)
    gpio_init()
    status=1
    while True:
        if GPIO.input(23) == True:
            if status==1:
                send_data(2)
                status=2
                print "门的状态:关闭"
            else:
                pass
        else:
            if status==2:
                send_data(1)
                status=1
                print "门的状态:打开"
            else:
                pass
        time.sleep(3)
    GPIO.cleanup()

(2)php程序源码:

require_once("../../func/dbaccess.php");
if (doConnect($cn) == false) {         
    return false;
}
$strSQL = "insert into iot_tbl (position,status,create_time) values ("'.第一号门.'",'".
            $_GET['status']."',now())"; 
doInsertUpdate($strSQL);                
doClose($cn);

4.代码解析:
这里使用的Python版本号为2.7.9

import  RPi.GPIO as GPIO
import time
import serial

引用了python的3个类库GPIO,time以及串口调试库serial,为实现程序的调试可编写,在使用serial串口调试库前,要实现树莓派的串口配置和Linux系统下的串口调试工具minicom的安装。具体方法可参考之前的文章《树莓派串口配置及minicom的安装》

ser = serial.Serial("/dev/ttyS0",115200)
W_http_1='AT+HTTPTERM\r\n'
ser.write(W_http_1)
time.sleep(3)
W_http_2='AT+SAPBR=3,1,"CONTYPE","GPRS"\r\n'
ser.write(W_http_2)
time.sleep(3)
W_http_3='AT+SAPBR=3,1,"APN","CMNET"\r\n'
ser.write(W_http_3)
time.sleep(3)
W_http_4='AT+SAPBR=1,1\r\n'
ser.write(W_http_4)
time.sleep(3)
W_http_5='AT+HTTPINIT\r\n'
ser.write(W_http_5)
time.sleep(3)

以上代码实在通过调用serial库,设置树莓派串口通信的波特率为115200,并使用 ser.write()函数向串口写入可操作SIM868模块进行通讯的AT指令,这里的AT指令是对SIM868进行HTTP通讯前的配置,具体AT指令的含义在这里不再赘述,可自行百度查找。这是使用time.sleep()函数控制程序执行的等待时间,确保串口写入数据成功。

def gpio_init():
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BOARD)
    GPIO.setup(23,GPIO.OUT)

gpio_init()函数是实现对接收电磁感应模块的GPIO PIN#23针脚的初始化配置。

status=1
    while True:
        if GPIO.input(23) == True:
            if status==1:
                send_data(2)
                status=2
                print "门的状态:关闭"
            else:
                pass
        else:
            if status==2:
                send_data(1)
                status=1
                print "门的状态:打开"
            else:
                pass
        time.sleep(3)

这里使用status变量初始化门的状态为1表示门已打开,并使用while True循环分割三秒钟(time.sleep(3))检测GPIO PIN#23 号针脚的电流状态,从而判断门的开关状态,并调用send_data()函数进行数据的发送。

def send_data(param):
    W_http_6='AT+HTTPPARA="URL","http://**************/iot.php?status='+param+'"\r\n'
    ser.write(W_http_6)
    time.sleep(2)
    W_http_7='AT+HTTPACTION=0\r\n'
    ser.write(W_http_7)
    time.sleep(3)

这里将门的状态status变量的值当做参数传送给你个send_data()函数,在添加到HTTP请求的URL中,在服务器端写了个PHP程序iot.php(程序源码j解析如下)用以接收SIM868通讯模块发送过来的HTTP请求,并使用GET的方式获得到HTTP请求URL中传入的status值,并插入到MySQL数据库中。

require_once("../../func/dbaccess.php");//封装链接操作MySQL数据库的函数
if (doConnect($cn) == false) {           //链接数据库
    return false;
}
$strSQL = "insert into iot_tbl (position,status,create_time) values ("'.第一号门.'",'".
            $_GET['status']."',now())";  //将数据插入MySQL数据库的SQL语句
doInsertUpdate($strSQL);                 //执行SQL
doClose($cn);                            //关闭数据库链接

4.程序拓展:
以上程序完成的是对开关门信号的检测、发送和接收数据,属于整个设备接收和处理数据的核心部分,对接收到的数据,还要做进一步的展示,这里我采用了HTML+JQuery+AJAX的方式,并配合在HTML中播放音频文件和刷新开关门状态图表,来动态展示开关门的效果。实现原理是使用AJAX操作PHP程序循环实时读取MySQL数据库,查看当前门的开关状态,并循环局部刷新HTML页面播放音频和刷新html页面图标,对门的开关效果进行动态展示。每个人的需求不同,展示部分的代码就不做说明,也参照上诉方案自行编写。

本文来自:树莓派实验室

 

 

 

借助树莓派模拟Wimonitor并实现WiFi窃听和嗅探

Wimonitor是一款非常优秀的黑客工具,它不仅可以帮渗透测试人员省去配置虚拟机和无线网卡等一系列麻烦事,而且它的Web接口配置起来也非常的方便。实际上,它就是一款TP-Link-MR3020路由器,只不过它使用的是自定义固件,并能够将监控到的网络数据包转发给主机电脑(Mac或Windows等主机),而我们此时就可以使用WireShark来对数据包进行分析了。

但是,我们可不可以使用树莓派来实现一样的功能呢?答案当然是肯定的,因此在这篇文章中,我们将教会大家如何使用树莓派3B来搭建一个稳定的WiFi监控平台,并通过几个简单的步骤来将其模拟成一个Wimonitor。

硬件要求

1.      运行Windows的笔记本电脑(测试设备使用的是Windows 8.1)

2.      树莓派3B、Micro SD卡、电源适配器(USB 3.0足够驱动树莓派+无线网卡了)

3.      网线(连接树莓派和笔记本电脑)

4.      支持监听模式的无线网卡(例如TL-WN722N v1)

安装配置

首先,将RASPBIAN STRETCH LITE刻到Micro SD卡中,这是一个轻量级操作系统,并且支持例如TL-WN722N之类的无线网卡,具体的步骤请参考【操作指南】。

刻录完成之后,我们可以通过在SD卡中创建一个名叫ssh的空文件(无需文件扩展名)来启用树莓派的SSH功能。

为了确保笔记本电脑能够跟Pi正常通信,最简单的方法就是用笔记本给Pi共享WiFi,这样就可以保证Pi的IP地址在192.168.137.x范围内。进入网络连接设置(ncap.cpl),右键点击Wi-Fi适配器,然后选择属性。在“Sharing”标签页中,选择需要插入Pi的以太网适配器,点击OK。

接下来,将Pi与笔记本主机连接,然后把SD卡插到Pi的卡槽中,将无线网卡插入到USB端口连接网线(笔记本主机-Pi),然后启动设备。

当Pi启动之后,它将会使用共享链接的IP地址,你可以在笔记本主机上使用nmap(对192.168.137.1/24子网进行主机搜搜)来查看Pi的IP地址。

打开PuTTY,然后使用ssh登录到Pi,我们所使用的Raspbian操作系统默认的用户名和密码分别为pi和raspberry。

给Pi设置一个静态地址,打开/etc/dhcpcd.conf并将下列代码添加到文件末尾:

interfaceeth0
staticip_address=192.168.137.100/24
staticrouters=192.168.137.1
staticdomain_name_servers=192.168.137.1

我个人比较喜欢使用基于密钥的SSH登录验证。打开PuTTYgen,然后生成一个密钥对。

在Pi的home目录中创建一个.ssh文件夹,然后在这个文件夹中创建一个名叫authorized_keys的文件,将PuTTYgen生成的公钥复制到这个文件中,最后自己保存好PuTTYgen生成的私钥,并重启Pi的SSH服务。结束之后,别忘了修改Pi的默认密码。

Raspbian操作系统没有自带与网络监控有关的数据包,因此你可以使用下列命令来安装这些包:

sudo apt update 
sudo apt install aircrack-ng tcpdump -y

接下来,我们需要在Pi上测试无线网卡的监听功能是否正常。

既然我们已经验证了Pi能够正常进行网络监控了,我们就可以使用SSH登录到Pi,然后在Pi上运行rcpdump,并将信息转发给运行在笔记本主机中的Wireshark。

plink.exe是PuTTY.exe的Windows命令行接口,为了方便起见,我把plink.exe和PuTTYgen生成的SSH私钥放在了同一个文件夹中。如果这些文件不在同一文件夹中,你就需要相应地修改下列代码中的路径了:

plink.exe-ssh -i pi-monitor-ssh-key.ppk pi@192.168.137.234 "sudo tcpdump -niwlan1mon -s 0 -w -" | "C:\Program Files\Wireshark\Wireshark.exe"-k -i -

如果你只想监听信道1,你可以使用下列命令:

sudo iwconfig wlan1mon channel 1

下面的命令可以开启Pi无线网卡的监听模式:

plink.exe-ssh -i pi-monitor-ssh-key.ppk pi@192.168.137.100 "sudo airmon-ng startwlan1"

接下来,我们可以在笔记本主机中开启Wireshark并开始监听无线网络数据:

plink.exe-ssh -i pi-monitor-ssh-key.ppk pi@192.168.137.100 "sudo tcpdump -niwlan1mon -s 0 -w -" | "C:\Program Files\Wireshark\Wireshark.exe"-k -i -

OK,一切搞定!大家快去自己动手尝试一下吧!

转载自FreeBuf.COM

 

 

 

使用MQTT连接树莓派IoT设备

有时候,你可能会发现自己处于一种你想让设备通过互联网相互通信的情况。例如,当我到另一个城市旅行的时候,我意识到我忘了关掉我房间的灯光。在这种情况下,我希望有一个Android应用程序或一个网站,我可以监视我家的灯光状态,并能打开或关闭它们。

所以,假设你想在一个可以在世界任何地方操作的房间里连接一个电灯开关。一个快速而稳定的选项是使用MQTT。

什么是MQTT?

MQTT(消息队列遥测传输)是一种轻量级消息传递协议,非常适用于物联网连接设备的通信。

MQTT有三个组件:代理,发布者和订阅者。经纪人是处理设备之间进行通信的中介实体。发布者是发送消息的设备。订阅者收听发布者发送的消息。

在MQTT中还有一件更重要的事情,那就是一个话题。不同设备之间的通信需要一个主题。例如,设备A想要向设备B发送消息。为此,两者之间应该有一些共同之处,那就是主题。把它想象成一个电话号码。

CloudMQTT

CloudMQTT是为特定数量的设备提供免费MQTT通信的代理服务。

设置MQTT

  • 在“名称”字段中输入CMQTT

  • 点击创建
  • 记下您的数据,如下图所示

 

 

  • 在同一页面上,在管理用户中添加一个用户:
    • 名字:pi
    • 密码:pi

 

  • 点击“保存”
  • 再次在最后的同一页上:
    • 用户:pi
    • 主题:pi

 

  • 并点击“保存”
  • 现在,在顶部的栏中,点击“WebSocket UI”

 

 

  • 你会看到一个页面,所有的传感器数据将被显示
  • 将以下代码上传到Arduino

下面的代码是用虚拟字符串代替传感器的示例代码。您可以用来自传感器的值替换字符串。

String sensorsData=""; 
String randSensorsData=""; 
void setup() {
   // put your setup code here, to run once: 
Serial.begin(9600); 
pinMode(A0,INPUT);//temperature sensor 
pinMode(A1,INPUT);//windspeed sensor 
pinMode(A2,INPUT);//wind direction sensor 
pinMode(A3,INPUT);//rain fall sensor 
pinMode(A4,INPUT);///barometric pressure sensor
} 

void loop() {
   // put your main code here, to run repeatedly:      
sensorsData=String("-")+String("S1=")+String(analogRead(A0))+String(",")+String("S2=")+String(analogRead(A1))+ \
String(",")+String("S3=")+String(analogRead(A2))+String(",")+String("S4=")+  \
String(analogRead(A3))+String(",")+String("S5=")+String(analogRead(A4))+String("+");     
randSensorsData=String("-")+String("S1=")+String(random(100))+String(",")+String("S2=")+ \
String(random(200))+String(",")+String("S3=")+String(random(125))+String(",")+String("S4=")+\
String(random(500))+String(",")+String("S5=")+String(random(50))+String("+");     
   Serial.println(sensorsData);     
   Serial.println(randSensorsData);   
   delay(200); 
}

此代码使用Arduino的模拟引脚来获取传感器的值,并传递到Raspberry Pi进行处理和MQTT通信。上面的代码使用随机函数来生成随机值来模拟模拟引脚。

  • 传感器应连接到A0,A1,A2,A3,A4引脚,
  • Serial.println(randSensorsData);
  • 这一行发送随机数据来检查服务器。
  • 您也可以检查在WebSocket UI中显示的随机数据。
  • 打开python 2.7并在其中编写下面的代码:
import paho.mqtt.client as mqtt
import time
data=”Hello from Raspberry Pi!”
while True:
    print(data)
    try:
        client=mqtt.Client()
        client.username_pw_set("yoyojacky","MyRPiPr0J")#替换成你的用户名和密码
        client.connect("m13.cloudmqtt.com",13017,60)
        client.publish("pi",data) #这里的 pi 是你的话题,客户端就订阅这个话题
        time.sleep(1)
    except KeyboardInterrupt:
        print("end")
        client.disconnect()
  • 现在使用python 2.7运行代码
  • 您将在每秒钟后看到传感器数据显示在CloudMQTT的WebScoket UI中。

  • 在这里,您将看到从Raspberry Pi发送的数据。

令人惊讶的是,CloudMQTT提供了一个完整的dashboard用于测试目的。您可以使用此dashboard订阅或发布主题.

在接下来就可以去配置你的 arduino 和树莓派来管理家里的设备或者查询家里的设备信息了!

 

如何使用树莓派自制网络监视器

本文所介绍的工具适合家庭环境下的“黑盒测试”,它可以帮助你记录网络中发生的所有事情。你可以用它来检测网络威胁,或将数据提供给相关专家来进行网络取证分析。

购物清单

1.      树莓派3 (外壳+电源+电路板)

2.      闪迪Class 10 microSD卡 64GB(80Mb/s)

3.      Debian OS-Linux RaspbianLite

4.      网件千兆交换机或其他支持端口镜像的设备,我使用的是D-Link1100-08P

5.      Critical Stack API(Threat Intel/ IOCs)

6.      Mailgun账号或类似支持警报/通知的邮件服务

概览图

关键技术介绍

什么是端口镜像?

将一个端口的流量数据复制到另一个端口(被动式),会增加交换机的运行负荷。

什么是Bro

一款IDS协议分析工具,你可以把它当作Wireshark的协议分析器,但是它没有GUI界面,而且速度更快。

什么是Netsniff?

进行数据包捕获的守护进程,它使用了AF-packet来提升数据包的捕捉速度。

什么是LOKI

基于YARA的文件扫描守护进程。有些类似基于签名检测的反病毒产品,但是你可以自行制定检测规则。

什么是Critical Stack

一个威胁情报平台,你可以在树莓派上通过API来与该平台链接。

什么是Team Cymru MHR

一个恶意软件哈希库,你可以使用该数据库中的信息来对检测到的恶意程序哈希进行匹配。

开始动手

1.      把系统刷到树莓派中

2.      给树莓派分配一个IP

3.      运行bash脚本

4.      搞定

 

一、把Raspbian刷到microSD卡中

我使用的是MacBook,所以不同平台的方法可能会有所不同,其他平台用户可以参考【这篇教程】来获取更多内容。

插入microSD卡:

diskutil <span class="hljs-built_in">list</span>

找到磁盘号:

diskutil unmountDisk /dev/disk<disk#from diskutil>

将Raspbian镜像刷入到microSD卡’disk’中:

sudo dd bs=<span class="hljs-number">1</span>m <span class="hljs-keyword">if</span>=image.imgof=<span class="hljs-regexp">/dev/rdisk</span><disk<span class="hljs-comment"># from diskutil></span>

完成之后,卸载microSD卡:

diskutil unmountDisk <span class="hljs-meta-keyword">/dev/</span>disk<span class="hljs-params"><disk#from diskutil></span>

二、配置网络

使用默认配置登录。用户名:pi,密码:raspberry。

设置wlan0(wifi)的IP,用于受信管理访问:

sudo nano<span class="hljs-meta-keyword">/etc/</span>wpa_supplicant/wpa_supplicant.conf
network={

ssid="The_ESSID_from_earlier"

psk="Your_wifi_password"

}
sudo ifdown wlan0

sudo ifup wlan0

ifconfig wlan0

当你获取到了一个DHCP IP之后,你可以使用SSH访问这个节点了。接下来,将eth0留下当作镜像接口,它不需要设置IP地址。

sudo apt-get update && sudo apt-get-y install vim

sudo vim /etc/network/interfaces

添加下列代码:

iface eth0 inet static

static ip_address=0.0.0.0

重启eth0接口:

<span class="hljs-attribute">sudo</span> ifconfig eth0 down && sudoifconfig eth0 up

三、部署

下载并运行bash脚本,脚本已在Raspbian上成功测试。

-安装程序的核心组件

-配置网络选项(禁用NIC offloading)

-给每一个程序创建服务

-使用Mailgun/SSMTP创建邮件警报

-配置cron任务

pi@foxhound:~# sudo su -

root@foxhound:~# apt-get install -y git

root@foxhound:~# cd ~

root@foxhound:~# git clonehttps://github.com/sneakymonk3y/foxhound-nsm.git

root@foxhound:~# chmod +xfoxhound-nsm/foxhound.sh

root@foxhound:~# ./foxhound-nsm/foxhound.sh

现在,环境部署已经完全完成啦!

接下来呢?当脚本完成运行之后,所有的服务都会被立刻激活,然后你就可以看到所有流入的数据啦!

性能

通过pcap数据来向镜像端口eth0发送一些垃圾信息,我使用的是一些提前准备好的数据:

pi@foxhound:~ $ sudo tcpreplay -t -K -q--loop=10 --intf1=eth0 /opt/foxhound-1476541443.pcap<br><br>Actual: 1048570 packets (1050923190 bytes)sent in 87.62 seconds. Rated:11994102.0 bps, 91.51 Mbps, 11967.25 pps

下面是发送前和发送后的broctl netstats数据:

root@foxhound:/etc/network# broctl netstats
bro: 1476547903.768150 recvd=1951368 dropped=5408 link=1956776
root@foxhound:/etc/network# broctl netstats
bro: 1476548144.248161 recvd=3012168 dropped=14608 link=3026776

对于家庭或实验室环境来说,它的性能相对还算很好了(1000000个数据包只会丢弃10000个)。我的带宽速度为40Mbps,对于IDS系统来说已经足够了。如果你还需要提升性能,建议考虑换掉树莓派。

如果你想进行更多的NIC perf测试,你可以在服务器上运行下列命令:

mark@ubuntu:~$ sudo apt install iperf3

mark@ubuntu:~$ iperf3 -s

-----------------------------------------------------------

Serverlistening on 5201

然后在树莓派上运行:

root@foxhound:~# apt install iperf3

root@foxhound:~# iperf3 -c 10.0.0.7 -i 1 -t20

Connecting to host 10.0.0.7, port 5201

[ 4]local 10.0.0.180 port 38562 connected to 10.0.0.7 port 5201

[ ID] Interval Transfer Bandwidth Retr Cwnd

[ 4] 0.00-1.00 sec 8.86 MBytes 74.3 Mbits/sec 0 89.1 KBytes

...

...

...

[ 4] 19.00-20.00 sec 9.26 MBytes 77.7 Mbits/sec 0 1.23 MBytes

- - - - - - - - - - - - - - - - - - - - - -- - -

[ ID] Interval Transfer Bandwidth Retr

[ 4] 0.00-20.00 sec 185 MBytes 77.5 Mbits/sec 139 sender

[ 4] 0.00-20.00 sec 184 MBytes 77.1 Mbits/sec receiver

iperf Done.

我假设这里的瓶颈是microSD卡,它的速度大约是80MB/s。

Bro基础

-所有Bro日志的默认存储路径为/nsm/bro/logs/

-默认的脚本路径为/usr/local/bro/share/bro/site/bro-scripts/

日志目录结构大致如下所示:

pi@foxhound:/nsm/bro/logs/current $ ls-lash

total 6.9M

4.0K drwxr-xr-x 3 root root 4.0K Oct 15 16:11 .

4.0K drwxr-xr-x 5 root staff 4.0K Oct 1516:50 ..

4.0K -rw-r--r-- 1 root root 349 Oct 15 16:51 app_stats.log

4.0K -rw-r--r-- 1 root root 121 Oct 15 15:51 .cmdline

16K-rw-r--r-- 1 root root 14K Oct 15 16:30communication.log

2.9M -rw-r--r-- 1 root root 2.9M Oct 15 16:52 conn.log

16K-rw-r--r-- 1 root root 14K Oct 15 16:52dhcp.log

384K -rw-r--r-- 1 root root 379K Oct 15 16:52 dns.log

4.0K -rw-r--r-- 1 root root 345 Oct 15 15:51 .env_vars

1.2M -rw-r--r-- 1 root root 1.2M Oct 15 16:52 files.log

1.6M -rw-r--r-- 1 root root 1.6M Oct 15 16:52 http.log

4.0K -rw-r--r-- 1 root root 291 Oct 15 16:44 known_hosts.log

4.0K -rw-r--r-- 1 root root 327 Oct 15 16:34 known_services.log

12K-rw-r--r-- 1 root root 11K Oct 15 16:50notice.log

4.0K -rw-r--r-- 1 root root 5 Oct 15 15:51 .pid

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.communication

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.conn

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:01 .rotated.conn-summary

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.dhcp

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.dns

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.files

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.http

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.known_hosts

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.known_services

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.loaded_scripts

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.notice

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.packet_filter

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.software

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.ssl

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.weird

4.0K -rw-r--r-- 1 root root 18 Oct 15 16:00 .rotated.x509

4.0K -rw-r--r-- 1 root root 3.0K Oct 15 16:51 software.log

320K -rw-r--r-- 1 root root 314K Oct 15 16:52 ssl.log

4.0K -rw-r--r-- 1 root root 58 Oct 15 15:51 .startup

4.0K drwx------ 3 root root 4.0K Oct 15 15:51 .state

4.0K -rwx------ 1 root root 18 Oct 15 15:51 .status

4.0K -rw-r--r-- 1 root root 46Oct 15 15:51 stderr.log

4.0K -rw-r--r-- 1 root root 188 Oct 15 15:51 stdout.log

24K-rw-r--r-- 1 root root 17K Oct 15 16:52weird.log

416K -rw-r--r-- 1 root root 412K Oct 15 16:52 x509.log

你可以使用head命令来查找文本域名称:

pi@foxhound:$ head dns.log<br><br>#separator \x09<br><br>#set_separator ,<br><br>#empty_field (empty)<br><br>#unset_field -<br><br>#path dns<br><br>#open 2016-10-15-16-00-01<br><br>#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto trans_id query qclass qclass_name qtype qtype_name rcode rcode_name AA TC RD RA Z answers TTLs rejected<br><br>#types time string addr port addr port enum count string count string count string count string bool bool bool bool count vector[string] vector[interval] bool

使用bro-cut来解析日志:

pi@foxhound:$ cat dns.log | bro-cut -D tsid.orig_h <span class="hljs-keyword">id</span>.orig_p <span class="hljs-keyword">id</span>.resp_h <span class="hljs-keyword">id</span>.resp_p proto query answers TTLs

基础报告:

pi@foxhound:$ bro-cut query < dns.<span class="hljs-keyword">log</span> |<span class="hljs-keyword">sort</span> | uniq -c | <span class="hljs-keyword">sort</span> -rn | head -n <span class="hljs-number">10</span>

如果你还需要更多的示例,你可以查看Bro提供的官方练习【传送门】。

你可以使用tail命令输出DNS日志并在客户端生成一些DNS流量:

<span class="hljs-attribute">tail</span> -f dns.log | awk  <span class="hljs-string">'{print <span class="hljs-variable">$3</span>, <span class="hljs-variable">$7</span>, <span class="hljs-variable">$9</span>}'</span>

请注意,只有conn.log才可以启用MaxMind GEOIP查询。

pi@foxhound:$ bro-cut resp_cc < conn.log| sort | uniq -c | sort -rn | head -n 10<br><br>755 US<br><br>524 RO<br><br>123 GB<br><br>49 NL<br><br>28 EU<br><br>25 IE<br><br>10 DE<br><br>7 ES<br><br>6 CA

我还没有制作仪表盘(Dashboard),如果你不想自己做的话,你可以直接使用VPS ELK实例在云端存储所有的日志,或者你也可以使用ELK/Splunk/Graylog将日志存储在本地。

基础维护

开启/停止netsniff-ng:

pi@foxhound:~ $ sudo service netsniff-ngstop<br><br>pi@foxhound:~ $ sudo service netsniff-ngstart<br><br>pi@foxhound:~ $ sudo service netsniff-ngstatus

开启/停止bro(网络统计):

pi@foxhound:~ $ sudo -i broctl stop<br><br>pi@foxhound:~ $ sudo -i broctl start<br><br>pi@foxhound:~ $ sudo -i broctl netstats<br><br>pi@foxhound:~ $ sudo -i broctl status

手动运行Loki:

root@foxhound:~ $ python /nsm/Loki/loki.py--noprocscan --dontwait --onlyrelevant -p /nsm/bro/extracted -l /nsm/Loki/log

检测CriticalStack的入侵威胁指标(IoCs),可通过cron脚本实现定期检查:

root@foxhound:~ $ sudo -u critical-stackcritical-stack-intel list

 

参考资料

1.      部署网络安全监控:http://www.appliednsm.com/

2.      网络安全监控实践:https://www.nostarch.com/nsm

3.      Laika BOSS-对象扫描系统:https://github.com/lmco/laikaboss

4.      PassiveDNS:https://github.com/gamelinux/passivedns

5.      D3js(图形化工具):https://d3js.org/

6.      Graylog:https://www.graylog.org/

FB小编Alpha_h4ck编译,转载来自FreeBuf.COM

[找回失落的记忆]树莓派+Pygame模块编写俄罗斯方块

昨天晚上,在家整理东西的时候, 突然翻找出来很早以前小时候的 Gameboy(可能不是这个名字了), 装电池的那种,很古老,只有一款游戏就是俄罗斯方块, 突然回忆就拉回了小学时代和同学一起比拼俄罗斯方块分数的日子,每天都无忧无虑的,不用考虑房贷,车保险….然后突然有一个想法,为什么不用树莓派做一个游戏机呢? 网上教程一堆一堆的, 都是用 retropie, lakka之类的系统实现一个模拟器的平台, 我觉得不够极客, 如果自己用 python 写一个游戏不是更好? 又能巩固 python 的代码能力,又可以让树莓派一机多用, 不仅只是个游戏机, 还可以是下载器,gitlab, 或者是一个 OSMC, 家庭媒体中心呢?

翻找了一下我的 Maker 材料箱, 找到一个吃灰已久的树莓派3B, 通过官方下载全新的镜像,然后烧录系统, 更新系统,配置 ssh, 配置主机名,配置字符集,配置 IP 地址等等一系列操作后. 利用 python 命令执行一下看看版本信息:

$ python -V
Python 2.7.10

就用默认版本吧, 然后去检查一下 pygame 的状态,默认情况下, pygame 已经集成在系统里面了, 如果没有要去官方站点上下载一下并安装, 连接如下: http://pygame.org/wiki/tutorials,官方的这个链接上已经说明:http://pygame.org/wiki/GettingStarted#Raspberry PI

Raspberry PI

This comes with pygame already installed on the default raspberian installation.

那么接下来的事儿就简单多了. 下面是我准备的材料:

  1. 树莓派3 B 型  x1
  2. 16GB class 10 的 TF 卡一张, 最好有SD 卡套方便进行系统烧录
  3. 5v2.5A 电源一套,为了方便,我这里采用的是52Pi 官方的 USB-HUB, 专为树莓派设计, 购买点我
  4. 5寸电阻触摸屏( GPIO 引脚控制), 购买点我 
  5. 外壳你可以自己 DIY 或者利用以下链接下载 STL 文件然后转码成 Gcode 交给3D 打印机打印.3D打印外壳链接

好了,我要开始操作了,首先你要做的事儿是把设备接驳起来,像这样:

然后启动后配置好网络后执行我写好的脚本就可以进行分辨率调节:

git clone https://github.com/yoyojacky/52Pi.git

cd ~/52Pi/

chmod +x restool.sh

sudo ./restool.sh

然后重启, 屏幕的分辨率和电容触摸的驱动就加载好了.

后面需要执行屏幕校准:

cd ~/52Pi/

chmod +x calibrator.sh

sudo ./calibrator.sh

基本配置告一段落了,然后检查 pygame 的版本:

pip list

或者通过 python 的 IDE 直接进行查询,在终端键入python 然后回车继续输入如下 python 代码:

 import  pygame

pygame.ver

看到如下图示:

说明pygame 的模块已经安装过,系统中包含这个模块了,就可以开始下面的操作了.

然后创建一个目录:

mkdir -pv  /home/pi/mygame/music

sudo vim.tiny  /home/pi/mygame/russianblock.py

我一次性创建了一个目录结构, mygame 目录下面创建一个 python 文件, 名字为: russianblock.py

然后可以两首 MP3的歌曲进去, 也可以直接下载我提供的素材:

Have A Drink On Me,Rolling in the Deep

然后继续添加如下python 代码:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import random, time, pygame, sys
from pygame.locals import *

FPS = 25
WINDOWWIDTH = 640
WINDOWHEIGHT = 480
BOXSIZE = 20
BOARDWIDTH = 10
BOARDHEIGHT = 20
BLANK = '.'

MOVESIDEWAYSFREQ = 0.15
MOVEDOWNFREQ = 0.1

XMARGIN = int((WINDOWWIDTH - BOARDWIDTH * BOXSIZE) / 2)
TOPMARGIN = WINDOWHEIGHT - (BOARDHEIGHT * BOXSIZE) - 5
#        R  G  B
WHITE    = (255, 255, 255)
GRAY    = (185, 185, 185)
BLACK    = ( 0,  0,  0)
RED     = (155,  0,  0)
LIGHTRED  = (175, 20, 20)
GREEN    = ( 0, 155,  0)
LIGHTGREEN = ( 20, 175, 20)
BLUE    = ( 0,  0, 155)
LIGHTBLUE  = ( 20, 20, 175)
YELLOW   = (155, 155,  0)
LIGHTYELLOW = (175, 175, 20)

BORDERCOLOR = BLUE
BGCOLOR = BLACK
TEXTCOLOR = WHITE
TEXTSHADOWCOLOR = GRAY
COLORS   = (   BLUE,   GREEN,   RED,   YELLOW)
LIGHTCOLORS = (LIGHTBLUE, LIGHTGREEN, LIGHTRED, LIGHTYELLOW)
assert len(COLORS) == len(LIGHTCOLORS) # each color must have light color

TEMPLATEWIDTH = 5
TEMPLATEHEIGHT = 5

S_SHAPE_TEMPLATE = [['.....',
           '.....',
           '..OO.',
           '.OO..',
           '.....'],
          ['.....',
           '..O..',
           '..OO.',
           '...O.',
           '.....']]

Z_SHAPE_TEMPLATE = [['.....',
           '.....',
           '.OO..',
           '..OO.',
           '.....'],
          ['.....',
           '..O..',
           '.OO..',
           '.O...',
           '.....']]

I_SHAPE_TEMPLATE = [['..O..',
           '..O..',
           '..O..',
           '..O..',
           '.....'],
          ['.....',
           '.....',
           'OOOO.',
           '.....',
           '.....']]

O_SHAPE_TEMPLATE = [['.....',
           '.....',
           '.OO..',
           '.OO..',
           '.....']]

J_SHAPE_TEMPLATE = [['.....',
           '.O...',
           '.OOO.',
           '.....',
           '.....'],
          ['.....',
           '..OO.',
           '..O..',
           '..O..',
           '.....'],
          ['.....',
           '.....',
           '.OOO.',
           '...O.',
           '.....'],
          ['.....',
           '..O..',
           '..O..',
           '.OO..',
           '.....']]

L_SHAPE_TEMPLATE = [['.....',
           '...O.',
           '.OOO.',
           '.....',
           '.....'],
          ['.....',
           '..O..',
           '..O..',
           '..OO.',
           '.....'],
          ['.....',
           '.....',
           '.OOO.',
           '.O...',
           '.....'],
          ['.....',
           '.OO..',
           '..O..',
           '..O..',
           '.....']]

T_SHAPE_TEMPLATE = [['.....',
           '..O..',
           '.OOO.',
           '.....',
           '.....'],
          ['.....',
           '..O..',
           '..OO.',
           '..O..',
           '.....'],
          ['.....',
           '.....',
           '.OOO.',
           '..O..',
           '.....'],
          ['.....',
           '..O..',
           '.OO..',
           '..O..',
           '.....']]

PIECES = {'S': S_SHAPE_TEMPLATE,
     'Z': Z_SHAPE_TEMPLATE,
     'J': J_SHAPE_TEMPLATE,
     'L': L_SHAPE_TEMPLATE,
     'I': I_SHAPE_TEMPLATE,
     'O': O_SHAPE_TEMPLATE,
     'T': T_SHAPE_TEMPLATE}


def main():
  global FPSCLOCK, DISPLAYSURF, BASICFONT, BIGFONT
  pygame.init()
  FPSCLOCK = pygame.time.Clock()
  DISPLAYSURF = pygame.display.set_mode((WINDOWWIDTH, WINDOWHEIGHT))
  BASICFONT = pygame.font.Font('freesansbold.ttf', 18)
  BIGFONT = pygame.font.Font('freesansbold.ttf', 100)
  pygame.display.set_caption('Tetromino')

  showTextScreen('YoYoJacky')
  while True: # game loop
    if random.randint(0, 1) == 0:
      pygame.mixer.music.load('music/Have A Drink On Me.mp3')
    else:
      pygame.mixer.music.load('music/Rolling In The Deep.mp3')
    pygame.mixer.music.play(-1, 0.0)
    runGame()
    pygame.mixer.music.stop()
    showTextScreen('Game Over')


def runGame():
  # setup variables for the start of the game
  board = getBlankBoard()
  lastMoveDownTime = time.time()
  lastMoveSidewaysTime = time.time()
  lastFallTime = time.time()
  movingDown = False # note: there is no movingUp variable
  movingLeft = False
  movingRight = False
  score = 0
  level, fallFreq = calculateLevelAndFallFreq(score)

  fallingPiece = getNewPiece()
  nextPiece = getNewPiece()

  while True: # game loop
    if fallingPiece == None:
      # No falling piece in play, so start a new piece at the top
      fallingPiece = nextPiece
      nextPiece = getNewPiece()
      lastFallTime = time.time() # reset lastFallTime

      if not isValidPosition(board, fallingPiece):
        return # can't fit a new piece on the board, so game over

    checkForQuit()
    for event in pygame.event.get(): # event handling loop
      if event.type == KEYUP:
        if (event.key == K_p):
          # Pausing the game
          DISPLAYSURF.fill(BGCOLOR)
          pygame.mixer.music.stop()
          showTextScreen('Paused') # pause until a key press
          pygame.mixer.music.play(-1, 0.0)
          lastFallTime = time.time()
          lastMoveDownTime = time.time()
          lastMoveSidewaysTime = time.time()
        elif (event.key == K_LEFT or event.key == K_a):
          movingLeft = False
        elif (event.key == K_RIGHT or event.key == K_d):
          movingRight = False
        elif (event.key == K_DOWN or event.key == K_s):
          movingDown = False

      elif event.type == KEYDOWN:
        # moving the piece sideways
        if (event.key == K_LEFT or event.key == K_a) and isValidPosition(board, fallingPiece, adjX=-1):
          fallingPiece['x'] -= 1
          movingLeft = True
          movingRight = False
          lastMoveSidewaysTime = time.time()

        elif (event.key == K_RIGHT or event.key == K_d) and isValidPosition(board, fallingPiece, adjX=1):
          fallingPiece['x'] += 1
          movingRight = True
          movingLeft = False
          lastMoveSidewaysTime = time.time()

        # rotating the piece (if there is room to rotate)
        elif (event.key == K_UP or event.key == K_w):
          fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']])
          if not isValidPosition(board, fallingPiece):
            fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])
        elif (event.key == K_q): # rotate the other direction
          fallingPiece['rotation'] = (fallingPiece['rotation'] - 1) % len(PIECES[fallingPiece['shape']])
          if not isValidPosition(board, fallingPiece):
            fallingPiece['rotation'] = (fallingPiece['rotation'] + 1) % len(PIECES[fallingPiece['shape']])

        # making the piece fall faster with the down key
        elif (event.key == K_DOWN or event.key == K_s):
          movingDown = True
          if isValidPosition(board, fallingPiece, adjY=1):
            fallingPiece['y'] += 1
          lastMoveDownTime = time.time()

        # move the current piece all the way down
        elif event.key == K_SPACE:
          movingDown = False
          movingLeft = False
          movingRight = False
          for i in range(1, BOARDHEIGHT):
            if not isValidPosition(board, fallingPiece, adjY=i):
              break
          fallingPiece['y'] += i - 1

    # handle moving the piece because of user input
    if (movingLeft or movingRight) and time.time() - lastMoveSidewaysTime > MOVESIDEWAYSFREQ:
      if movingLeft and isValidPosition(board, fallingPiece, adjX=-1):
        fallingPiece['x'] -= 1
      elif movingRight and isValidPosition(board, fallingPiece, adjX=1):
        fallingPiece['x'] += 1
      lastMoveSidewaysTime = time.time()

    if movingDown and time.time() - lastMoveDownTime > MOVEDOWNFREQ and isValidPosition(board, fallingPiece, adjY=1):
      fallingPiece['y'] += 1
      lastMoveDownTime = time.time()

    # let the piece fall if it is time to fall
    if time.time() - lastFallTime > fallFreq:
      # see if the piece has landed
      if not isValidPosition(board, fallingPiece, adjY=1):
        # falling piece has landed, set it on the board
        addToBoard(board, fallingPiece)
        score += removeCompleteLines(board)
        level, fallFreq = calculateLevelAndFallFreq(score)
        fallingPiece = None
      else:
        # piece did not land, just move the piece down
        fallingPiece['y'] += 1
        lastFallTime = time.time()

    # drawing everything on the screen
    DISPLAYSURF.fill(BGCOLOR)
    drawBoard(board)
    drawStatus(score, level)
    drawNextPiece(nextPiece)
    if fallingPiece != None:
      drawPiece(fallingPiece)

    pygame.display.update()
    FPSCLOCK.tick(FPS)


def makeTextObjs(text, font, color):
  surf = font.render(text, True, color)
  return surf, surf.get_rect()


def terminate():
  pygame.quit()
  sys.exit()


def checkForKeyPress():
  # Go through event queue looking for a KEYUP event.
  # Grab KEYDOWN events to remove them from the event queue.
  checkForQuit()

  for event in pygame.event.get([KEYDOWN, KEYUP]):
    if event.type == KEYDOWN:
      continue
    return event.key
  return None


def showTextScreen(text):
  # This function displays large text in the
  # center of the screen until a key is pressed.
  # Draw the text drop shadow
  titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTSHADOWCOLOR)
  titleRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2))
  DISPLAYSURF.blit(titleSurf, titleRect)

  # Draw the text
  titleSurf, titleRect = makeTextObjs(text, BIGFONT, TEXTCOLOR)
  titleRect.center = (int(WINDOWWIDTH / 2) - 3, int(WINDOWHEIGHT / 2) - 3)
  DISPLAYSURF.blit(titleSurf, titleRect)

  # Draw the additional "Press a key to play." text.
  pressKeySurf, pressKeyRect = makeTextObjs('Press a key to play.', BASICFONT, TEXTCOLOR)
  pressKeyRect.center = (int(WINDOWWIDTH / 2), int(WINDOWHEIGHT / 2) + 100)
  DISPLAYSURF.blit(pressKeySurf, pressKeyRect)

  while checkForKeyPress() == None:
    pygame.display.update()
    FPSCLOCK.tick()


def checkForQuit():
  for event in pygame.event.get(QUIT): # get all the QUIT events
    terminate() # terminate if any QUIT events are present
  for event in pygame.event.get(KEYUP): # get all the KEYUP events
    if event.key == K_ESCAPE:
      terminate() # terminate if the KEYUP event was for the Esc key
    pygame.event.post(event) # put the other KEYUP event objects back


def calculateLevelAndFallFreq(score):
  # Based on the score, return the level the player is on and
  # how many seconds pass until a falling piece falls one space.
  level = int(score / 10) + 1
  fallFreq = 0.27 - (level * 0.02)
  return level, fallFreq

def getNewPiece():
  # return a random new piece in a random rotation and color
  shape = random.choice(list(PIECES.keys()))
  newPiece = {'shape': shape,
        'rotation': random.randint(0, len(PIECES[shape]) - 1),
        'x': int(BOARDWIDTH / 2) - int(TEMPLATEWIDTH / 2),
        'y': -2, # start it above the board (i.e. less than 0)
        'color': random.randint(0, len(COLORS)-1)}
  return newPiece


def addToBoard(board, piece):
  # fill in the board based on piece's location, shape, and rotation
  for x in range(TEMPLATEWIDTH):
    for y in range(TEMPLATEHEIGHT):
      if PIECES[piece['shape']][piece['rotation']][y][x] != BLANK:
        board[x + piece['x']][y + piece['y']] = piece['color']


def getBlankBoard():
  # create and return a new blank board data structure
  board = []
  for i in range(BOARDWIDTH):
    board.append([BLANK] * BOARDHEIGHT)
  return board


def isOnBoard(x, y):
  return x >= 0 and x < BOARDWIDTH and y < BOARDHEIGHT


def isValidPosition(board, piece, adjX=0, adjY=0):
  # Return True if the piece is within the board and not colliding
  for x in range(TEMPLATEWIDTH):
    for y in range(TEMPLATEHEIGHT):
      isAboveBoard = y + piece['y'] + adjY < 0
      if isAboveBoard or PIECES[piece['shape']][piece['rotation']][y][x] == BLANK:
        continue
      if not isOnBoard(x + piece['x'] + adjX, y + piece['y'] + adjY):
        return False
      if board[x + piece['x'] + adjX][y + piece['y'] + adjY] != BLANK:
        return False
  return True

def isCompleteLine(board, y):
  # Return True if the line filled with boxes with no gaps.
  for x in range(BOARDWIDTH):
    if board[x][y] == BLANK:
      return False
  return True


def removeCompleteLines(board):
  # Remove any completed lines on the board, move everything above them down, and return the number of complete lines.
  numLinesRemoved = 0
  y = BOARDHEIGHT - 1 # start y at the bottom of the board
  while y >= 0:
    if isCompleteLine(board, y):
      # Remove the line and pull boxes down by one line.
      for pullDownY in range(y, 0, -1):
        for x in range(BOARDWIDTH):
          board[x][pullDownY] = board[x][pullDownY-1]
      # Set very top line to blank.
      for x in range(BOARDWIDTH):
        board[x][0] = BLANK
      numLinesRemoved += 1
      # Note on the next iteration of the loop, y is the same.
      # This is so that if the line that was pulled down is also
      # complete, it will be removed.
    else:
      y -= 1 # move on to check next row up
  return numLinesRemoved


def convertToPixelCoords(boxx, boxy):
  # Convert the given xy coordinates of the board to xy
  # coordinates of the location on the screen.
  return (XMARGIN + (boxx * BOXSIZE)), (TOPMARGIN + (boxy * BOXSIZE))


def drawBox(boxx, boxy, color, pixelx=None, pixely=None):
  # draw a single box (each tetromino piece has four boxes)
  # at xy coordinates on the board. Or, if pixelx & pixely
  # are specified, draw to the pixel coordinates stored in
  # pixelx & pixely (this is used for the "Next" piece).
  if color == BLANK:
    return
  if pixelx == None and pixely == None:
    pixelx, pixely = convertToPixelCoords(boxx, boxy)
  pygame.draw.rect(DISPLAYSURF, COLORS[color], (pixelx + 1, pixely + 1, BOXSIZE - 1, BOXSIZE - 1))
  pygame.draw.rect(DISPLAYSURF, LIGHTCOLORS[color], (pixelx + 1, pixely + 1, BOXSIZE - 4, BOXSIZE - 4))


def drawBoard(board):
  # draw the border around the board
  pygame.draw.rect(DISPLAYSURF, BORDERCOLOR, (XMARGIN - 3, TOPMARGIN - 7, (BOARDWIDTH * BOXSIZE) + 8, (BOARDHEIGHT * BOXSIZE) + 8), 5)

  # fill the background of the board
  pygame.draw.rect(DISPLAYSURF, BGCOLOR, (XMARGIN, TOPMARGIN, BOXSIZE * BOARDWIDTH, BOXSIZE * BOARDHEIGHT))
  # draw the individual boxes on the board
  for x in range(BOARDWIDTH):
    for y in range(BOARDHEIGHT):
      drawBox(x, y, board[x][y])


def drawStatus(score, level):
  # draw the score text
  scoreSurf = BASICFONT.render('Score: %s' % score, True, TEXTCOLOR)
  scoreRect = scoreSurf.get_rect()
  scoreRect.topleft = (WINDOWWIDTH - 150, 20)
  DISPLAYSURF.blit(scoreSurf, scoreRect)

  # draw the level text
  levelSurf = BASICFONT.render('Level: %s' % level, True, TEXTCOLOR)
  levelRect = levelSurf.get_rect()
  levelRect.topleft = (WINDOWWIDTH - 150, 50)
  DISPLAYSURF.blit(levelSurf, levelRect)


def drawPiece(piece, pixelx=None, pixely=None):
  shapeToDraw = PIECES[piece['shape']][piece['rotation']]
  if pixelx == None and pixely == None:
    # if pixelx & pixely hasn't been specified, use the location stored in the piece data structure
    pixelx, pixely = convertToPixelCoords(piece['x'], piece['y'])

  # draw each of the boxes that make up the piece
  for x in range(TEMPLATEWIDTH):
    for y in range(TEMPLATEHEIGHT):
      if shapeToDraw[y][x] != BLANK:
        drawBox(None, None, piece['color'], pixelx + (x * BOXSIZE), pixely + (y * BOXSIZE))


def drawNextPiece(piece):
  # draw the "next" text
  nextSurf = BASICFONT.render('Next:', True, TEXTCOLOR)
  nextRect = nextSurf.get_rect()
  nextRect.topleft = (WINDOWWIDTH - 120, 80)
  DISPLAYSURF.blit(nextSurf, nextRect)
  # draw the "next" piece
  drawPiece(piece, pixelx=WINDOWWIDTH-120, pixely=100)


if __name__ == '__main__':
  main()

写好以后,先用命令执行测试一下:

sudo python russianblock.py

执行结果看到如下内容就说明成功了:

随便按一个键就可以开始玩儿了,使用键盘的上下左右控制移动, 用空格控制反转,记得把你的耳机插入树莓派3.5mm 的音频复合接口, 然后开始享受游戏的乐趣吧!

如果失败,音乐就停啦!

好了! 这个简单的教程就写到这里, 后期教大家利用 pygame 和树莓派制作更多的好玩儿的小游戏!