背景
前阵子树莓派由于没外壳不小心短路烧掉了。最近得手nodeMCU的板子,不仅便宜还支持串口交互式调试,很是方便。寻思着实现个自动化控制玩玩。自从“穹顶之下”的信息被普及,不仅要出门带口罩,还要家里开空气净化器。于是照着知乎也DIY了一个。但作为一个程序员,不捣鼓一点硬件软件怎么可以,于是花了两三天捣鼓了lua,顺便配了个web端的语音播报,软硬件组合达到了期望的效果。(顺便吐槽一下tinyOS的组件式编程,文档也少得可怜)
实现思路
硬件:
MQ烟雾/可燃气体传感器
dth温湿度传感器
空气颗粒物(pm1.0,pm2.5,pm10)传感器
继电器*4(用来控制 取暖器,加湿器,净化器,烟雾报警装置)
软件:
万能的web(html js jq)
php(负责爬天气预报和空气质量,以及和nodeMCU通讯)
坑
用socket拼接http去获取天气,结果写完发现它的socket接受发送最大1440字节,本来想直接写进MCU做个lua server。可惜了,于是控制规则干脆写到前端,php做大量的数据获取支持。
用的空气颗粒物传感器接TX口,init.lua加载前会根据D3 D4 TX 来判断是否进入烧写模式,之前接继电器总是上电乱跳,最终把data out 的2口放到D3 D4,解决了这个问题。
效果图
nodeMCU代码(init.lua)
--家庭空气质量管家
--sensor为传感器data口
--power为继电器控制口
--D0 CH1 0
--D1 CH2 0
--D2 CH4 0
--D3 MQ-DATA 1
--D4 DHT-DATA 0
--D5 CH3 0
--D6 DHT-VCC 1
--RX PM-TX
Air={
pm010=0,pm025=0,pm100=0,
temp=0,humi=0,smoke=1,
gpio_pm=0,gpio_temp=0,gpio_humi=0,gpio_smoke=0,
humi_power=0, pm_power=1,
smoke_power=2, mq_sensor=3,
dht_sensor=4, temp_power=5
}
--引脚初始化
for pi=0,6,1 do
gpio.mode(pi, gpio.OUTPUT,(pi==6)and gpio.PULLUP or gpio.FLOAT)
gpio.write(pi,(pi==3 or pi==6)and gpio.HIGH or gpio.LOW)
end
--网络
wifi.setmode(wifi.STATION)
wifi.sta.config("SSID","PASSWORD")
--串口监听
uart.setup( 0, 9600, 8, 0, 1, 0 )
uart.on("data",
function(data)
if(string.len(data)==32 and string.byte(data)==66) then
--颗粒
Air.pm010=tonumber(string.byte(data,11))*256+tonumber(string.byte(data,12))
Air.pm025=tonumber(string.byte(data,13))*256+tonumber(string.byte(data,14))
Air.pm100=tonumber(string.byte(data,15))*256+tonumber(string.byte(data,16))
--温湿度
_,Air.temp,Air.humi, _, _ =dht.read(Air.dht_sensor)
--烟雾
Air.smoke=gpio.read(Air.mq_sensor)
--继电器
Air.gpio_pm,Air.gpio_temp,Air.gpio_humi,Air.gpio_smoke=gpio.read(Air.pm_power),gpio.read(Air.temp_power),gpio.read(Air.humi_power),gpio.read(Air.smoke_power)
end
end, 0)
--http
srv=net.createServer(net.TCP)
srv:listen(80,function(conn)
conn:on("receive", function(client,request)
local buf = "{"
local _, _, method, path, vars = string.find(request, "([A-Z]+) (.+)?(.+) HTTP")
if(method == nil)then
_, _, method, path = string.find(request, "([A-Z]+) (.+) HTTP")
end
local _GET = {}
if (vars ~= nil)then
for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
_GET[k] = v
end
end
if(_GET.action == "read")then
for key,value in pairs(Air) do
buf=buf..key..":"..value..","
end
elseif(_GET.action == "write")then
gpio.write(_GET.pin,_GET.level)
elseif(_GET.action == "debug")then
node.input(_GET.data)
end
client:send(buf.."error:0}")
client:close()
collectgarbage()
end)
end)
源码