树莓派上的家庭监控中心

前言

上回在 树莓派上的温湿度环境监控 里提及过,Open-Falcon 也是可以直接装在树莓派上的。所以其实可以使用树莓派在家里做一个小型的监控中心,把相应的监控指标采集过来推给树莓派上的 Open-Falcon 就好了嘛

根据这个思路,我尝试了温湿度,PM2.5 和路由器这几块的监控,这里做个分享

环境

树莓派:RaspberryPi 3
温湿度传感器:DHT11,DS18B20
PM2.5 传感器:攀藤 PMS3003(G3)
路由器:华硕 RT-AC68U,华硕 RT-AC66U

安装 Open-Falcon

说到底,树莓派就是个 CPU 是 ARMv6 的 Linux 而已,所以我们只要准备好 ARMv6 上的 Golang 环境然后编译就好了
获取 golang 的二进制包

wget https://storage.googleapis.com/golang/go1.9.linux-armv6l.tar.gz

解压

tar -C /usr/local -xzf go1.9.linux-armv6l.tar.gz

配置环境(永久生效应该写道 profile 里去)

export PATH=$PATH:/usr/local/go/bin
export GOPATH=/usr/local/gopath
# go version
go version go1.9 linux/arm

然后编译 Open-Falcon 就好了~
环境准备,Raspbian 是基于 Debain 魔改的,因此是 apt-get 系统

apt-get install mysql-server
apt-get install redis-server
mkdir -p $GOPATH/src/github.com/open-falcon
cd $GOPATH/src/github.com/open-falcon
git clone https://github.com/open-falcon/falcon-plus.git

cd $GOPATH/src/github.com/open-falcon/falcon-plus/scripts/mysql/db_schema/
mysql -h 127.0.0.1 -u root -p < 1_uic-db-schema.sql
mysql -h 127.0.0.1 -u root -p < 2_portal-db-schema.sql
mysql -h 127.0.0.1 -u root -p < 3_dashboard-db-schema.sql
mysql -h 127.0.0.1 -u root -p < 4_graph-db-schema.sql
mysql -h 127.0.0.1 -u root -p < 5_alarms-db-schema.sql

cd $GOPATH/src/github.com/open-falcon/falcon-plus/

# make all modules
make all

# make specified module
make agent

# pack all modules
make pack

export WorkDir="$HOME/open-falcon"
mkdir -p $WorkDir
tar -xzvf open-falcon-vx.x.x.tar.gz -C $WorkDir
cd $WorkDir

cd $WorkDir
./open-falcon start

当然还有 Dashboard

export HOME=/home/work/

mkdir -p $HOME/open-falcon/
cd $HOME/open-falcon && git clone https://github.com/open-falcon/dashboard.git
cd dashboard;

apt-get install -y python-virtualenv
apt-get install -y slapd ldap-utils
apt-get install -y libmysqld-dev
apt-get install -y build-essential
apt-get install -y python-dev libldap2-dev libsasl2-dev libssl-dev

cd $HOME/open-falcon/dashboard/
virtualenv ./env

./env/bin/pip install -r pip_requirements.txt -i https://pypi.douban.com/simple
image.png

是不是很亲切?

环境监控

温湿度监控

温湿度监控用 DHT11 ,我觉得这个精度已经够用了。我这里放了一个 DS18B20 作为对比,可以看一下两者的温度精度差异。

image.png

温湿度监控的具体实现可以看 树莓派上的温湿度环境监控

PM 2.5 监控

空气中的颗粒度浓度也是我们很关心的指标。这里用了攀藤的 G3 来做。

image.png
针脚说明
PIN 定义 说明
PIN1 VCC 电源,5V
PIN2 GND 电源负
PIN3 SET 待机设置
PIN4 RXD 串口接收管脚
PIN5 TXD 串口发送管脚
PIN6 RESET 模块复位信号
PIN7/8 NC 悬空
连接

再次祭出树莓派针脚图

image.png

连接就很简单了

PMS3003 Raspbian Pi 3
Pin1 接 5V ,例如 Pin2
Pin2 接地,例如 Pin6
Pin3 设置口,随便接个,例如 Pin11(GPIO17),或者悬空。
Pin5 数据接收口,接 Pin8
串口

PMS3003 的数据是通过串口来传输的,因此我们需要配置一下树莓派,把串口功能打开,由于树莓派的串口略坑,稍微扯点题外话:

大家知道 Unix 一切为文件,因此串口的驱动会创建对应的字符设备(文件)以供读写。通常这些设备是以类型+序号来命名。

例如:x86 架构下串口通常命名为 /dev/ttyS0(Serial 0),大家可以找台 x86 的 Linux 服务器去看看。 Raspbian 是 ARM 架构的因此其串口命名为 /dev/ttyAMA0(TeleTyper ARM UART 0)

然而在 Raspbian Pi 3 上,这里情况就不太一样了。Pi3 的 /dev/ttyAMA0 被蓝牙给默认占用了, 于是它给出一个 /dev/ttyS0 设备用于软串口的 Mini UART。 然后为了使得新老 Raspbian Pi 之间的兼容性 ,又把 3 代之前版本 的 /dev/ttyAMA0 和 Raspberry Pi 3 的 /dev/ttyS0 软链接到同一个 /dev/serial0 。这样子绕了一圈,然而Minu UART 的性能又是相当令人捉急的。。。

因此我们得调整一下配置,把默认的 /etc/ttyAMA0 给搞回来。

启用 UART,修改/boot/config.txt,增加(新版 Raspbian 已经默认开启了)

enable_uart=1

关掉默认的 console

sudo systemctl disable serial-getty@ttyS0.service

在启动脚本/boot/cmdline.txt里干掉 console=ttyAMA0 或者 console=ttyS0 的配置,类似这样

dwc_otg.lpm_enable=0 console=tty1 root=/dev/mmcblk0p2 rootfstype=ext4 elevator=deadline ...

关掉蓝牙

dtoverlay=pi3-disable-bt 

重启之

数据传输

PMS3003 的数据格式如下所示

数据 功能
起始符1 0x42(固定)
起始符2 0x4d(固定)
帧长度:高八位,低八位 帧长度=20 (数据:9x2+校验位:2)
数据1:高八位,低八位 数据1表示PM1.0浓度(CF=1,标准颗粒物)单位ug/m3
数据2:高八位,低八位 数据2表示PM2.5浓度(CF=1,标准颗粒物)单位ug/m3
数据3:高八位,低八位 数据3表示PM10浓度(CF=1,标准颗粒物)单位ug/m3
数据4:高八位,低八位 数据4表示PM1.0浓度(大气环境下)单位ug/m3
数据5:高八位,低八位 数据5表示PM2.5浓度(大气环境下)单位ug/m3
数据6:高八位,低八位 数据6表示PM10浓度(大气环境下)单位ug/m3
数据7:高八位,低八位 数据7(保留)
数据8:高八位,低八位 数据8(保留)
数据9:高八位,低八位 数据9(保留)
数据校验和:高八位,低八位 校验码=前 22 位字节累加

可以看到,一个完整的数据包长度一共是 24 个字节,我们不妨先看一下他的数据到底长什么样子。
写一段简单的串口读取代码

#!/usr/bin/python
import serial
import time


def pm():
    ser = serial.Serial("/dev/ttyAMA0", 9600)
    while True:
        time.sleep(1) # 等个 1 秒钟等数据进来
        count = ser.inWaiting()
        if count >= 24:      # 由于一个完整的数据包长度是24个字节,因此等到缓冲区字节数超过 24 个了,开始读取。
            recv = ser.read(count) # 读取数据
            # 这里如果直接 print recv 肯定是一堆无意义的乱码,即便 print repr(recv),
            # 由于 python 默认的 ACSII 编码,他还是会把其中一部分字符给编码成字符打印出来,
            # 所以要看到原始的 16 进制报文,得额外处理一下
            print map(lambda c: hex(ord(c)), recv) # 按 16 进制格式打印接受到的数据
        ser.flushInput() #读完了清掉缓冲区
    ser.close()
    return

if __name__ == '__main__':
        pm()

执行之

# python pm25_test.py
['0x42', '0x4d', '0x0', '0x14', '0x0', '0x4e', '0x0', '0x6d', '0x0', '0x78', '0x0', '0x33', '0x0', '0x48', '0x0', '0x50', '0x10', '0x44', '0x0', '0x30', '0x91', '0x0', '0x3', '0xb6']

可以看到,起始符分别为 0x420x4d,然后是高低位的帧长度 = 20(24 个字节去掉 2 个字节起始符和高低位 2 个字节的帧长度)。接着是 2 个字节一组的数据,一共 9 个。最后是校验和,高位0x3,低位0xb6,也就是校验和为 0x3b6。
我们来尝试计算一下校验和

root@raspberrypi:/opt/falcon-scripts# python
Python 2.7.9 (default, Sep 17 2016, 20:26:04)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 0x42 + 0x4d + 0x0 + 0x14 + 0x0 + 0x4e + 0x0 + 0x6d + 0x0 + 0x78 + 0x0 + 0x33 + 0x0 + 0x48 + 0x0 + 0x50 + 0x10 + 0x44 + 0x0 + 0x30 + 0x91 + 0x0
>>> print hex(a)
0x3b6
>>>

校验和正确,所以这组数据是正确的,可以被接收的。

因此 PM2.5 等的数据采集大体是这个逻辑。由于我们是每分钟定期采集一次数据,没有必要实时的去处理串口的数据流(实际上 PMS3003 也是间隔 200ms ~ 800ms 定期往串口发数据)。只要在开始采集的时候等待上 1 秒钟,等到有至少 24 个字节输入进入后进行处理即可。
对于数据异常,例如起始符不正确,校验和不正确等数据,直接抛弃等下一轮就好了。

完整代码

#!/usr/bin/python
#encoding=utf-8
import serial
import time
import json
import requests
import copy


def pm():
    ser = serial.Serial("/dev/ttyAMA0", 9600)
    while True:
        time.sleep(1)
        count = ser.inWaiting()
        if count >= 24:
            recv = ser.read(count)
            data = map(lambda c: ord(c), recv) # 上面写 hex() 是为了打印出来方便人看,实际运行就不需要了,不然后面还得转换
            if check_data(data) != True: # 数据校验不合法,抛弃之等下一轮
                ser.flushInput()
                continue
            return get_data(data) # 读到数据了直接返回退出循环
        ser.flushInput()
    ser.close()
    return 0,0,0

def check_data(data):
        sum = 0x00
        if data[0] != int('0x42',16): # 起始符校验
                return False
        if data[1] != int('0x4d',16):
                return False
        for d in data[0:21]:
                sum = sum + d
        check_sum_high = data[22]<<8 # 校验和校验,位移 8 位到高位
        check_sum_low = data[23]
        check_sum = check_sum_high | check_sum_low # 按位或运算,高低位合并
        if sum == check_sum:
                return True
        return False

def get_data(data):
        pm1 = (data[10] << 8) | data[11] # 高位位移,按位或运算合并高低位
        pm2_5 = (data[12] << 8) | data[13]
        pm10 = (data[14] << 8) | data[15]
        return pm1,pm2_5,pm10

def push_falcon(value_name,value):
        ts = int(time.time())
        push_url = "http://127.0.0.1:1988/v1/push"
        payload = [{"endpoint":"home","metric":"room.air_quality","timestamp":ts,"step":60,"value":value,"counterType":"GAUGE","tags":"module="+value_name}]
        r = requests.post(push_url, data=json.dumps(payload))

if __name__ == '__main__':
        pm1, pm2_5,pm10 = pm()
        push_falcon("pm1",pm1)
        push_falcon("pm2.5",pm2_5)
        push_falcon("pm10",pm10)

效果如下所示

image.png

如果要控制 PMS3003 的工作状态的话,可以通过调节他的 Pin3,也就是 set 口。可以让他处于待机状态。但注意转入工作状态之后,要至少工作 30 秒以上,读取的数据才比较准确。

import RPi.GPIO as GPIO
import time
pin = 17 # 11管脚对于的BCM管脚号码
GPIO.setmode(GPIO.BCM)
GPIO.setup(pin, GPIO.OUT) # 写入 GPIO
GPIO.output(pin, GPIO.HIGH) # 工作
GPIO.output(pin, GPIO.LOW) # 待机

路由器监控

监控路由器,那肯定是走 snmp 了。虽然在路由器的环境里(例如 RC-66U,openwrt 系统, cpu MIPS 指令集) 应该也能够编译出 golang 来 go-mips32 ,但是考虑到易用性和减少路由器的资源占用,肯定还是开启路由器的 net-snmp 比较合适。

以华硕的 RT-AC66U 和 RT-AC68U 为例。

开启 snmp —— 官版系统

华硕的官版固件默认并没有开启 snmp,如果要开启的话需要有一个曲折的过程~

  1. 首先找一个 U 盘插上路由器
  2. 在路由器后台里开启 Download Master (为了激活 ipkg 包管理器),并开启 telnet
  3. 安装 net-snmp
ipkg install net-snmp
app_set_enabled.sh net-snmp yes

这一步过程中,有很大概率遭遇不可描述的网络故障导致网络连接异常下载失败
可能需要通过一些不可描述的方法来规避网络连接的异常

  1. 如果成功了,就可以 snmpwalk -v 2c -c public 192.168.1.1 测试了
开启 snmp —— 梅林

更省事的办法是用 梅林 的系统。话说都买了华硕的路由器了,不刷个梅林好意思么?

刷完梅林之后,直接在路由器 web 后台就能够开启 snmp

监控 - swcollector

要在 open-falcon 下的通过 snmp 监控路由器的话,自然考虑用 swcollector 了。这篇文章也可以提供参考 Open-Falcon 中的交换机监控

在编译 swcollector 之前,我们不妨先 snmpwalk 测试一下我们的路由器,以防踩坑,比如:
1号坑:

root@raspberrypi:/home/pi# snmpwalk -v 2c -c public 192.168.2.2 1.3.6.1.2.1.31.1.1.1.15
iso.3.6.1.2.1.31.1.1.1.15.1 = Gauge32: 10
iso.3.6.1.2.1.31.1.1.1.15.2 = Gauge32: 10
iso.3.6.1.2.1.31.1.1.1.15.3 = Gauge32: 0
iso.3.6.1.2.1.31.1.1.1.15.4 = Gauge32: 0
iso.3.6.1.2.1.31.1.1.1.15.5 = Gauge32: 10
iso.3.6.1.2.1.31.1.1.1.15.6 = Gauge32: 10
iso.3.6.1.2.1.31.1.1.1.15.7 = Gauge32: 0
root@raspberrypi:/home/pi# snmpwalk -v 2c -c public 192.168.2.2 1.3.6.1.2.1.2.2.1.5
iso.3.6.1.2.1.2.2.1.5.1 = Gauge32: 10000000
iso.3.6.1.2.1.2.2.1.5.2 = Gauge32: 10000000
iso.3.6.1.2.1.2.2.1.5.3 = Gauge32: 0
iso.3.6.1.2.1.2.2.1.5.4 = Gauge32: 0
iso.3.6.1.2.1.2.2.1.5.5 = Gauge32: 10000000
iso.3.6.1.2.1.2.2.1.5.6 = Gauge32: 10000000
iso.3.6.1.2.1.2.2.1.5.7 = Gauge32: 0

好嘛,无论是 ifSpeed 还是 ifHighSpeed,这货返回的结果都根本不对嘛。这有点像是 net-snmp 的一个 bug。但是在 asuswrt-merlin 里,snmpd 已经是以 admin ,也就是 root 权限在跑了。所以具体原因也吃不太准,我在 github 上给 asuswrt-merlin 提了个 issue,希望能有个答案吧。

总之,这样的话我们就没办法愉快的监控了。因为 swcollector 自 4.0 版本起,就会根据 ifSpeed 的值对采集到的流量进行合法性判断,如果采集到的流量计算出来超过了接口的速率,讲被视作异常值而抛弃的。你这里给我来个统统 10M,这岂不是一开下载监控就要断流的节奏嘛。。。

这样只好人工设置 speedlimit 为 1G 了

"speedlimit":1000000000,

除了常规的 cpu,内存,接口各种流量,包数之外。我们还可以通过自定义监控的方式来拿一些更多的信息,比如系统的 load 信息

{
        "metrics":
                [
                        {
                        "ipRange":[
                   "192.168.2.1-192.168.2.3"
                        ],
                        "metric":"switch.system.load",
                        "tag":"",
                        "type":"GAUGE",
                        "oid":"1.3.6.1.4.1.2021.10.1.3.1"
                        }
                ]
}

现在我们可以去 Open-Falcon 里看看我们监控的路由器了。欸?路由器的内存好像有点不太对?

image.png

没错,此处就是 2号坑,net-snmp 在这里有内存泄露,如果你这么就一直跑下去,路由器倒是不会死球,snmpd 肯定是妥妥的会死球无响应的。

解决的办法只能是在路由器上起一个定时任务,每天半夜重启一下 snmpd 咯

admin@RT-AC66U-F5C8:/usr/sbin# crontab -e
0 5 * * * service restart_snmpd

同时把我们监控的采集周期调高到 5 分钟,避免一个晚上他内存就炸了,好歹撑到凌晨内存释放

    "transfer": {
        "enabled": true,
        "addr": "127.0.0.1:8433",
        "interval": 300,
        "timeout": 1000
    },

好啦,现在监控木有什么问题了。我们会看到 ASUS 有以下这么些个接口

switch.if.In/ifIndex=1,ifName=lo
switch.if.In/ifIndex=2,ifName=eth0
switch.if.In/ifIndex=3,ifName=eth1
switch.if.In/ifIndex=4,ifName=eth2
switch.if.In/ifIndex=5,ifName=vlan1
switch.if.In/ifIndex=6,ifName=vlan2
switch.if.In/ifIndex=7,ifName=br0

这些接口都是什么意思呢?这张 DD-WRT 的原理图可供参考。 ASUS 与此之的差异在于还有个 eth2 专门跑 WiFi 5G

image.png

所以接口的关系是这样的

接口 功能
eth0 WAN + LAN
br0 Bridge
eth1 WiFi 2.4G
eth2 WiFi 5G
vlan1 LAN
vlan2 WAN

在路由器上,可以通过这个命令来进一步自定义配置

admin@RT-AC68U-AB48:/tmp/home/root# robocfg
Broadcom BCM5325/535x/536x/5311x switch configuration utility
Copyright (C) 2005-2008 Oleg I. Vdovikin (oleg@cs.msu.su)
Copyright (C) 2005 Dmitry 'dimss' Ivanov of "Telecentrs" (Riga, Latvia)

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

Usage: robocfg <op> ... <op>
Operations are as below:
        show -- show current config
        showmacs -- show known MAC addresses
        showports -- show only port config
        switch <enable|disable>
        port <port_number> [state <enabled|rx_disabled|tx_disabled|disabled>]
                [stp none|disable|block|listen|learn|forward] [tag <vlan_tag>]
                [media auto|10HD|10FD|100HD|100FD|1000HD|1000FD]
                [mdi-x auto|on|off] [jumbo off|on]
        vlan <vlan_number> [ports <ports_list>]
        vlans <enable|disable|reset>

        ports_list should be one argument, space separated, quoted if needed,
        port number could be followed by 't' to leave packet vlan tagged (CPU
        port default) or by 'u' to untag packet (other ports default) before
        bringing it to the port, '*' is ignored

Samples:
1) ASUS WL-500g Deluxe stock config (eth0 is WAN, eth0.1 is LAN):
robocfg switch disable vlans enable reset vlan 0 ports "0 5u" vlan 1 ports "1 2 3 4 5t" port 0 state enabled stp none switch enable
2) WRT54g, WL-500g Deluxe OpenWRT config (vlan0 is LAN, vlan1 is WAN):
robocfg switch disable vlans enable reset vlan 0 ports "1 2 3 4 5t" vlan 1 ports "0 5t" port 0 state enabled stp none switch enable

运行截图

image.png
image.png

树莓派自身

当然还有树莓派自己的监控。这个 Open-Falcon 的 agent 就已经基本解决问题。我们再加一个小脚本来采一下树莓派的 cpu 温度就好了
树莓派的 cpu 温度可以在 /sys/class/thermal/thermal_zone0/temp 这里读到

root@raspberrypi:/opt/falcon-scripts# cat /sys/class/thermal/thermal_zone0/temp
52615

写个脚本推给 Open-Falcon

#!/usr/bin/python
# -*- coding: utf-8 -*-

import requests
import time
import json

def get_Pitemp():
    file = open("/sys/class/thermal/thermal_zone0/temp")
    temp = float(file.read()) / 1000
    file.close()
    return temp

if __name__ == '__main__':
    temp = get_Pitemp()
    ts = int(time.time())
    push_url = "http://127.0.0.1:1988/v1/push"
    payload = [{"endpoint":"raspberrypi","metric":"cpu.temperature","timestamp":ts,"step":60,"value":temp,"counterType":"GAUGE","tags":""}]
    r = requests.post(push_url, data=json.dumps(payload))
~                                                              
image.png

以上

参考文档

Falcon+
Falcon-Dashboard
Raspbian Jessie 的 GPIO 串口配置
Raspberry Pi 读取攀藤G3 PM2.5
PM2.5传感器PMS3003,同时能测PM1.0 PM2.5 PM10
Install and Configure SNMP on the Asus RT-AC66U Router
DD-WRT leading two separate networks (Asus RT-AC68)

转载授权

CC BY-SA

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • 前言 前阵子入了一个树莓派,作为一个尽责(苦逼)的IT运维狗,自然想到拿这玩意来做做看看温湿度的环境监控了。想法很...
    freedomkk_qfeng阅读 19,727评论 2 31
  • 本文遵循「知识共享许可协议 CC-BY-NC-SA 4.0 International」,未经作者(laiwei)...
    laiwei阅读 6,384评论 3 19
  • 简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者...
    保川阅读 5,903评论 1 13
  • 【慢点俱乐部】易效能初级学习100天第一阶段 3组-褚旭 20171028 心得打卡 【1-14讲理论模块】 —...
    褚洪嘉阅读 190评论 0 0
  • 以前总以为人与人之间的感情一旦形成便会经久不衰,现在才知道自己和一些人,一些事从一开始就注定了相忘于江湖。 少点感...
    y姑娘712阅读 171评论 0 0