土壤湿度计Project综述

这节介绍如何用NodeMCU来检测花盆里土壤的湿度,效果如下: nodemcu_humidity_wiring_2.JPG

本Project中,仍然是利用NodeMCU收集传感器的环境数据,并传送到ThingSpeak的Channel上存储,和上一个收集温度信息的Project相比,这里NodeMCU脱离了USB供电,使用了四节AA的电池盒加上一个3.3V的Step Down Regulator,还增加了一个LED显示工作状态。在程序中我们规定每隔10分钟向ThingSpeak发送一次测量结果(HTTP POST),4节电池一共维持了三天,这大约相当于400-500次使用ESP8266。

在电池基础上,今后可以尝试的改进方向是:

  • 利用USB的插头转换电压,再通过Step Down Regular供电。
  • 寻找是否有更节能的维持NodeMCU的工作方式,比如定时唤醒硬件,类似Amazon的Dash。

NodeMCU Analog Input

NodeMCU只有一个模拟输入,即A0。LUA读A0的输入很简单

function getHumidity()
   hum = adc.read(0);
   return hum;
end

使用LED提示WIFI连接状态

在Deploy的工作模式下,有时ESP8266和Router的连接可能出现问题,通常不希望需要通过检查ThingSpeak的Channel才能发现其实NodeMCUM没有正常工作,所以这里还使用了LED指示灯在表示连接状态,正常的LED状态是在发送POST并且完成,LED会短暂闪烁一次,表示开始准备发送,发送完毕。如果有连接问题,LED将一直停留红灯状态,如果发现,只需重新启动电源,一般NodeMCU会重新成功连接到Router上。

function postToThingspeak(hum)
   gpio.write(8, gpio.HIGH);   --开始POST,置LED HIGH
   .... 发送HTTP POST ...
   conn:on("receive", function(conn, payload)
		      gpio.write(8, gpio.LOW);    --发送成功,置LED LOW
		      conn:close()
		      end)

NodeMCU和湿度器的连线方式

这里使用的土壤湿度传感器如下所示 soilsensor.jpg

插入土壤中的叫的装置做soil probe. 工作电压3.3-5V,它的读数范围在0-1023之间,读数越大土壤越干燥. NodeMCUM直接给传感器其供3.3V电压。Probel的PCB板上的有三个PIN,分别是Vin,GND,AO,和NodeMCU的连接方式很简单

YL-69 NodeMCU
A0 Analog A0
3.3V VCC
GND GND

土壤湿度计的读数范围

Reading Interpretation
1000<=s Disconnected,or not in soil
600<=s<1000 Dry
370<=s<600 Humid
s<370 In Water

土壤的Humidity随时间变化

整体上Humidity随时间变化比较缓慢,部分原因可能是因为植物放在室内,光合作用不明显。数值越大,土壤越干燥,在5月26日时,土壤的湿度的一下子增加是因为给植物浇了水,可以看出浇水之后,湿度立即变大之后(干燥度变小),然后迅速回到350以上,这说明浇的部分的水分很快被植物吸收一部分,然后湿度就一直维持在350以上。 nodemcu_humidity.png

一种可靠的Deploy的途径

Deploy物联网开发板有两个重要的问题需要解决:

  1. 电池的寿命问题
  2. WIFI连接的可靠程度

目前我采用的方式是:

通过USB直接供电

如果deploy的环境中有110/220V交流电源,则可以通过一个USB转接口+USB cable通过microusb直接给NodeMCU供电,这极大的简化的程序的复杂程度和日后对Deploy开发版的电池维护(更换,重启等等)。

经常重启node.restart()

如果供电不是问题,那么每次调用node.restart(),之后再连接WIFI,几乎可以确保每次的Request能够成功的发送。 需要斟酌的是要在合适的地方调用restart. 因为NodeMCU的net library的工作方式都是异步的,restart可能会造成之前的connection 对象销毁。目前看来,如下的工作方式较可靠(前提是对能耗没有限制)

setupWifi()   -- 第一次启动需要连接一次WIFI
tmr.alarm(1,30000,1,function()
   postToThingspeak()
end)
function postToThingspeak()

    ...
    conn:on("receive", function(conn, payload)
	print("received")
	gpio.write(2,gpio.LOW)

	conn:close()
	node.restart()   --在receive的回调函数中重启node
	setupWifi()      --重启之后还需要重新连接wifi    
    end)
t = tmr.now()    
conn:connect(80,'api.thingspeak.com') 
end

经常重启wifi

如果NodeMCU仅仅用来收集环境的信息,重启NodeMCU不会对其收集的信息造成任何的影响,那么restart是一个可靠,简单的手段。如果NodeMCU还用来控制外界的电路,比如Relay,那么短暂的重启可能会影响Relay的状态,这个时候我们只需要重启WiFi就可以了,事实上,重启WIFI要比重启NodeMCU要更简单可靠,因为重启本身的原因就是WIFI的连接问题

----------------------------
---- post to thingspeak  ---
----------------------------
function postToThingspeak()

    conn=net.createConnection(net.TCP, 0)

    conn:on("connection",function(conn, payload)
      cmd ="POST /update.json?api_key=TheToken&field1=".."1"
	.. " HTTP/1.1\r\n"
	.. "Host: api.thingspeak.com\r\n"
	.. "Connection: close\r\n"
	.. "Accept: */*\r\n"
	.. "User-Agent: Mozilla/4.0 (compatible; esp8266 Lua; Windows NT 5.1)\r\n"
	.. "\r\n";
	    print(cmd);
	    conn:send(cmd);
	    gpio.write(6,gpio.HIGH)
	    end)

    conn:on("receive", function(conn, payload)
	print("received")
	gpio.write(6,gpio.LOW)
	conn:close()

	-- 示例用
	-- 这段内容保证每次Post完成之后都重启一次WIFI
	-- 尽管这不是很必要
	wifi.sta.disconnect()
	wifi.sta.connect()


    end)

    t = tmr.now()    
    conn:connect(80,'api.thingspeak.com') 

end

更多情况下,如果我们发现NodeMCU的WIFI工作的结果,不在我们的预计之内,可以通过一个If Statement重启WIFI连接

if  (result =='0') then

elseif  (result=='1') then

else
   -- restart will disturb the relay
   -- ideally repull should not affect 
   -- replay's status
   -- print("need repull");
   -- node.restart();

   wifi.sta.disconnect()
   wifi.sta.connect()
   do_some_work_again()                    
end