使用NodeMCU和AM2302(DHT22)制作的在线温湿度监控系统

NodeMCU可以用了,而手头上还有一块AM2302(DHT22)的温湿度测量模块。于是就想折腾一个可以实时监控温湿度并且上传到服务器,存到数据库供随时查看变化的温湿度监控系统。

首先查阅了NodeMCU的文档,初步确定了使用wifi模块连接wifi,http模块负责利用post上传数据,net模块来建立本地服务器供实时查看,tmr模块来做定时器定时获取数据,DHT模块来读取DHT22的数据,所以重新编译了内置了DHT Module的NodeMCU固件刷入。

首先在服务器上建立了一个数据库的两个数据表来存储相应数据,其中station表存储不同的NodeMCU模块的IP和上线时间,以供未来扩展多个监测点的时候使用,而data表存储具体的温度湿度和数据采集时间。

硬件连接首先是AM2302(DHT22)的VCC和GND分别接NodeMCU的3.3和GND输出。其次是DATA和NodeMCU的D5口相连,即可。

这里定义dhtpin=5

首先通过

wifi.setmode(wifi.STATION)
wifi.sta.config("wifi SSID","password")
wifi.sta.connect()

连接wifi,不要忘记设定dns地址

net.dns.setdnsserver("114.114.114.114", 0)

并且做了一个定时器,每隔一秒确认是否连接成功,连接成功以后会像服务器发起post请求把自己的IP和station ID汇报给服务器,代表监测站上线成功。其中监测站id即station变量我以地理位置+chipid来定义。

tmr.alarm(2, 1000, 1, function()
if wifi.sta.status()==5 then
http.post('URL',
      'Content-Type: application/json\r\n',
      '{"station":"'..station..'","ip":"'..wifi.sta.getip()..'","pass":"password"}',
      function(code, data)
        if (code < 0) then
          print("HTTP request failed")
        else
            tmr.stop(2)
          print(code, data)
        end
      end)
 else
    print("connecting")
 end
end)

json中的pass是为了服务器做简单的安全性校验用。
接下来是建立一个简单的服务器以供本地实时查询,代码如下

srv = net.createServer(net.TCP)
srv:listen(80, function(conn)
    conn:on("receive", function(sck, payload)
        gpio.write(led,gpio.LOW)
        status, temp, humi, temp_dec, humi_dec = dht.read(dhtpin)
        if status == dht.OK then
            sck:send("HTTP/1.0 200 OK\r\nContent-Type: text/html\r\n\r\n<h1>DHT Temperature:"..temp..";".."Humidity:"..humi.."</h1>")
            gpio.write(led,gpio.HIGH)
        end
        end)
    conn:on("sent", function(sck) sck:close() end)
end)

其中status, temp, humi, temp_dec, humi_dec = dht.read(dhtpin)为DHT模块读取语句

最后是建立另一个定时器,定时采集温湿度参数并且发送给服务器,代码为一分钟间隔采集。

tmr.alarm(1, 60000, 1, function()
    status, temp, humi, temp_dec, humi_dec = dht.read(dhtpin)
    if status == dht.OK then
           http.post('url',
          'Content-Type: application/json\r\n',
          '{"temp":"'..temp..'","humi":"'..humi..'","station":"'..station..'","pass":"tms"}',
          function(code, data)
            if (code < 0) then
              print("HTTP request failed")
            else
              print(code, data)
            end
          end)
    end
end)

至此客户端的代码就算告一段落了。服务端代码无非是数据库插入,查询,echarts展示。

效果参见http://tms.im/Mo/tms.php

成品图
TH

折腾NodeMCU

从淘宝买回来一个CP2102+ESP8266的小模块准备刷NodeMCU固件玩下,却发现插上电脑以后没有任何反应,本来应该电脑提示发现新硬件,但是试了多次均没有。

装驱动,找文档,看了好久都没有类似这种现象的发生,找淘宝店家也不理我。只好自己折腾下。

nodemcu

经过测试发现,flash按钮和复位按钮是有效的,于是推测是USB转UART芯片CP2102坏掉了导致电脑不识别这块板。而ESP8266应该是完好的。

查了一下NodeMCU的资料发现这货其实已经把ESP8266的全部引脚都引出了。既然这样,为何不自己做外围下载电路和UART模块来救活这个ESP8266。

翻了一下抽屉,发现手头上只有一块古老的PL-2303。于是用他做了一个USB转UART的电路。对照这块板的pinmap把RX->TXD0和TX->RXD0,VCC->+5V和GND->GND分别接好。

pinmap

连上电脑,发现了虚拟串口设备,装好PL2303的驱动,打开ESP8266Flasher,选择好编译好的NodeMCU固件点击刷入。按住板子上的flash后点按reset。电脑成功识别到了ESP8266。

经过十几分钟的等待,终于刷入成功。

迫不及待的打开ESPlorer,连接成功。果然是CP2102的锅。终于救活了这块ESP8266,接下来就可以好好折腾NodeMCU了。

关于touch值不值得买

今天看到V2ex上面有人问 “touch6 值得入手吗?”,作为一个用了一年touch6的人就顺手回答了一下。这里也记录一下。
touch6 刚出就买了一个,说实话我觉得还是挺值的。首先我是安卓党,所以需要一个可以玩 APP 的 IOS 设备。买 IPhone 用不到电话功能也太贵,买 IPad 感觉太大了,最后买了 touch6 。用了快一年了,感受就是:
1 、轻,非常轻。带着很舒服。在路上听听歌看看电影玩玩游戏不会消耗手机的电量,很棒。
2 、只能接入 WIFI 上网是劣势也是优势。我把一部分只离线用的支付帐号放在上面了。感觉不怕被木马盗。心理上安心许多。
3 、手机上面装的软件大大减少了。能离线用或者只是 wifi 条件下才会用的软件只装在 touch 上。手机的续航时间大大增加。最多也就用来跑个聊天软件之类的。买 touch 的一大初衷也是为了分担手机电量。
4 、虽说 touch 不支持 GPS ,但是我还是用他来做一些健康管理。比如计步。记录睡眠状态。记录心率之类的。顺便安利 Heart Rate Pro 、 Sleep Cycle 、 Gyroscope 这几款健康管理软件。
5 、关于电池,我还真没觉得会很烂。至少我听歌的话连续放三四个小时也试过。用一半电吧。是外放。用耳机的话大概能连续听七八个小时。看视频玩游戏连续三四个小时不是问题。电池电量小是肯定的。但是 touch 用电也少。而且更好的一点是。充电特别快!特别快!特别快!重要的事情说三遍。大概半小时左右就充满了。所以还是很爽的。
6 、颜值高就不用说了。这小东西确实好看又方便携带。经常听歌的人表示随时带着。带个手机再带个 touch 完全没感觉重量增加。
7 、还有,拍照效果也还不错。暗光效果下比我的小米 4C 成像好的多。不得不说苹果的优化做的真无敌。所以我也经常用他来拍照。手机没电的时候也可以找个有 wifi 的地方应急用一下,上个聊天软件啥的。平时就是用来挂 QQ 微信小号了。
总之
对于IPhone用户来说,touch用途不大。
对于android用户来说,看你买touch的目的是什么。
对于追求大屏幕的人来说IPad更适合你。
对于用来当备机的人当然应该再买个手机,touch不适合你。
如果你只想买个用来玩的,方便携带的 IOS 设备,顺便可以分担手机电量。 touch6 是个很好的选择。

新玩具

新玩具

跨IDC数据备份

为了数据安全性。除了做了主从和多机互备份以外,在异地容灾和数据备份方面也不容忽视。
这里是实现跨IDC数据备份的一种思路。分为文件备份和数据库备份两部分。文件备份可以保证用户上传的文件,图片和网站的代码不会丢失。而数据库备份则是数据保护的重中之重。
之所以采用跨IDC进行数据备份也是考虑到数据的安全性和同IDC的网络崩溃和主机崩溃的可能性。

文件备份

文件数量和大小决定了需要做到文件同步备份必须采用增量备份的方式。传统的方法是使用 inotify + rsync 来进行同步备份。
这里采用Sersync 来进行服务器间文件同步。

配置 Slave 上的 WWW 同步

Slave上安装rsync
然后配置文件写明,其中rsync.pass里面写入密码

log file = /var/log/rsyncd.log 
pidfile = /var/run/rsyncd.pid 
lock file = /var/run/rsync.lock  
secrets file = /etc/rsyncd/rsync.pass
[tongbu]
path = /home/wwwroot/
comment = tongbu
uid = root
gid = root
port=873
use chroot = no
read only = no
list = no
max connections = 200
timeout = 600 
auth users = tms
hosts allow = 主机的IP

最后启动Rsync的 rsync –daemon

启动 Master 上的 Sersync 做数据同步

主机上配置sersync

 <sersync>
	<localpath watch="/home/wwwroot">
	    <remote ip="远程IP" name="tongbu"/>
	</localpath>

其他配置略

第一次启动加上 -r 参数做首次同步,之后就不要再加  -r 参数了
./sersync2 -n 2 -d -r -o confxml.xml

文件备份到此结束。

数据库备份

数据库备份这里采用主从的方式,而这里的slave纯粹做备份使用。无业务访问他。
前期准备工作步骤如下:
1、修改备份服务器上my.cnf中的server-id

2、登录主服务器创建同步账号GRANT REPLICATION SLAVE ON . TO ‘slave’@‘Slave服务器IP’ IDENTIFIED BY ‘password’;

备份主服务器步骤:
1、FLUSH TABLES WITH READ LOCK;

2、cp -ar /var/lib/mysql /home/DATA/tmp

3、show master status;并且记录file和position

4、UNLOCK TABLES;

复制主服务器的/home/DATA/tmp到从服务器并且替换mysql的var目录

登录从服务器的mysql

CHANGE MASTER TO
 
    ->     MASTER_HOST='master_host_name',
 
    ->     MASTER_USER='replication_user_name',
 
    ->     MASTER_PASSWORD='replication_password',
 
    ->     MASTER_LOG_FILE='前面让你记录下的 master 状态显示的 logfile 名字',
 
    ->     MASTER_LOG_POS=前面记录下的 postion;

START SLAVE;

show slave status \G

这样Master —> Slave 就运行起来了。数据库备份结束。

为了进一步的安全考虑,采用crontab每隔一天做一次全量备份,在备份服务器进行。脚本如下

#!/bin/bash
DBName=数据库名
DBUser=备份用户
DBPasswd=备份用户的密码
BackupPath=/root/Dropbox/   #保存到同步目录

LogFile=/root/db.log
DBPath=/var/lib/mysql/ #备份的数据库目录
BackupMethod=mysqldump
#BackupMethod=mysqlhotcopy
#BackupMethod=tar

NewFile="$BackupPath"db$(date +%y%m%d).tgz
DumpFile="$BackupPath"db$(date +%y%m%d)
OldFile="$BackupPath"db$(date +%y%m%d --date='5 days ago').tgz  #自动删除5天前的备份
echo "-------------------------------------------" >> $LogFile
echo $(date +"%y-%m-%d %H:%M:%S") >> $LogFile
echo "--------------------------" >> $LogFile
#Delete Old File
if [ -f $OldFile ]
then
        rm -f $OldFile >> $LogFile 2>&1
        echo "[$OldFile]Delete Old File Success!" >> $LogFile
else
        echo "[$OldFile]No Old Backup File!" >> $LogFile
fi
if [ -f $NewFile ]
then
        echo "[$NewFile]The Backup File is exists,Can't Backup!" >> $LogFile
else
        case $BackupMethod in
        mysqldump)
                if [ -z $DBPasswd ]
                then
                        mysqldump -u $DBUser --opt $DBName > $DumpFile
                else
                        mysqldump -u $DBUser -p$DBPasswd --opt $DBName > $DumpFile
                fi
                tar czvf $NewFile $DumpFile >> $LogFile 2>&1
                echo "[$NewFile]Backup Success!" >> $LogFile
                rm -rf $DumpFile
                ;;
        mysqlhotcopy)
                rm -rf $DumpFile
                mkdir $DumpFile
                if [ -z $DBPasswd ]
                then
                        mysqlhotcopy -u $DBUser $DBName $DumpFile >> $LogFile 2>&1
                else
                        mysqlhotcopy -u $DBUser -p $DBPasswd $DBName $DumpFile >>$LogFile 2>&1
                fi
                tar czvf $NewFile $DumpFile >> $LogFile 2>&1
                echo "[$NewFile]Backup Success!" >> $LogFile
                rm -rf $DumpFile
                ;;
        *)
                service mysql stop >/dev/null 2>&1
                tar czvf $NewFile $DBPath$DBName >> $LogFile 2>&1
                service mysql start >/dev/null 2>&1
                echo "[$NewFile]Backup Success!" >> $LogFile
                ;;
        esac
fi
echo "-------------------------------------------" >> $LogFile

同时采用dropbox实时上传网盘。保证每日的数据库在云端存有一份备份。