wetnt 发表于 2016-4-1 15:56:49

攀藤G5空气质量上报乐联网教程,PM2.5 HCHO NodeMcu(8266) si7021

本帖最后由 wetnt 于 2016-12-10 12:56 编辑

   空气质量上报乐联网—攀藤G5(PM2.5)+NodeMcu(8266)+si7021+Lewei

   乐为物联网的活动提供的套件,借机学习下NodeMcu(8266)的用法。之前开工贴在此:http://www.geek-workshop.com/thread-26378-1-1.html

一、模块介绍


    NodeMcu带一个扩展板,支持si7021传感器和攀藤G5传感器。

http://image.geek-workshop.com/forum/201603/24/204333xdlgzjllspg6grrd.jpg
    攀藤G5(PM2.5)传感器,采用串口输出数据模式,每秒钟输出一次数据,串口监听就好。

http://image.geek-workshop.com/forum/201603/24/204333dsm2nylqnnttnv0g.jpg
    si7021采用了IIC总线,利用si7021库可以方便读取数据。

http://image.geek-workshop.com/forum/201603/24/204334hfwizho0u5funpit.jpg
    甲醛传感器,也是采用串口输出数据的方式,不过需要发送特定的字段请求数据。NodeMcu只有一个串口,这次实验暂时没有把它的数据集成进去。(另外一个帖子里有介绍:甲醛测试仪+ESP8266+LeWei)

    在另外一个帖子里,将采用Arduino MEGA上传所有传感器数据。


二、制作过程

    制作过程很简单了,配套的扩展板很完善,直接将模块、传感器插在上面就ok了。

    不过需要注意的是,NodeMcu是和Arduino完全不同的一款设备,Arduino像是个电脑,而NodeMcu则像是一个操作系统,Arduino直接烧录软件,而NodeMcu是对上传到Flash的文件进行解释运行;使用习惯上有非常的不同。真正使用过程中遇到很多问题,纠结了很久才都解决。回头从新开贴说下“NodeMcu的使用心得”。

步骤如下:

1、NodeMcu连上手机USB线,插入电脑USB口,就等系统识别硬件,自动安装驱动程序好了。如果安装不成功,需要手动安装。


2、烧录bin文件。需要下载烧录软件、bin文件。

    其中bin文件下载地址:http://bbs.nodemcu.com/t/nodemcu-firmware-download-build-20150318-new-location/27,也可以在GitHUB上下载:https://github.com/nodemcu/nodemcu-firmware/releases/tag/0.9.6-dev_20150704,不过国家防火墙问题,经常访问不到。



    其中有两个版本:nodemcu_float_0.9.6-dev_20150704.bin、nodemcu_integer_0.9.6-dev_20150704.bin,一个是具备浮点功能,另外一个只是整数功能。差别呢就是“integer”版本不能处理小数,精度要求高的地方的用“float”版本,但我的NodeMcu板子float版本老刷入不成功。建议两个版本都下载,后面有用处。

烧录软件下载,之前在网络上搜索了很多版本,这些版本都可以刷入bin,但板子却没反应,建议大家下载最新的 “ESP8266Flasher.exe”版本。界面如图:





刷入波特率是:230400,而连接波特率是:9600,有些模块需要RST按键才能响应串口连接

如果烧录成功之后板子没有回显响应,应该是有init.lua文件嵌入,你换刚才提到的另外一个bin刷下就ok,不然板子永远不受你控制!这个地方浪费了我大量的时间和精力!

3、下载IDE。NodeMcu官网IDE是垃圾建议不要用,问询了群里许多朋友,推荐用“ESPlorer”,JAVA软件写的需要java环境。




三、软件代码

init.lua

-- init.lua
--=============================================================
print("\nBBK_Lewei_Started_si7021_pm2.5_init.lua")
require("bbk_log")
require("bbk_ip_info")
lg("require_ok")
--=============================================================
function Do_Next_File()
   lg("Do_Next_File")
   dofile("bbk_si7021_pm25_lewei_ok.lua")
end
--=============================================================
wifi.setmode(wifi.STATION)
wifi.sta.disconnect()
Connect2AP("ssid","pass")
--=============================================================


bbk_log.lua

--bbk_log
--=============================================================
function GetRunTime()
   local t = tmr.now()/1000000      --print(t)
   local h = t/3600 t = t - h * 3600--print(h,t)
   local f = t/60                     --print(f,t)
   local s = t%60                     --print(s,t)
   local u = tmr.now()/1000%1000      --print(s,t)
   return string.format("%02d:%02d:%02d.%03d",h,f,s,u)
end
--=============================================================
function lg(s)
   if s==nil then
          print("-------- "..GetRunTime().." --------")
   else
          print("-------- "..GetRunTime().." = "..s)
   end
end
--=============================================================


bbk_ip_info.lua

-- Connect2AP
function Connect2AP(ssid,pass)
   ---------------------------------------------------------
   lg("Connect2AP("..ssid..","..pass..")")
   ---------------------------------------------------------
   tmr.alarm(0, 3000, 1, function()
          ----------------------------------------------------
          wifi.sta.config(ssid,pass)
          wifi.sta.connect()
          wifi.sta.autoconnect(1)
          ----------------------------------------------------
          if wifi.sta.getip() == nil then
               print("Connecting to AP...\n")         
          else
               local ip, nm, gw = wifi.sta.getip() print(ip) --IP_Info_Show()               
               tmr.stop(0)
               Do_Next_File()
          end
          ----------------------------------------------------
   end)
   ---------------------------------------------------------
end
function IP_Info_Show()
   ---------------------------------------------------------
   ip, nm, gw = wifi.sta.getip()
   if wifi.sta.getip() == nil then
          print("IP Info:",ip,nm,gw,'\n')
   else
          print("IP Info:")
          print("IP Address: ",ip)
          print("Netmask   : ",nm)
          print("Gateway Ad: ",gw,'\n')
   end
   ---------------------------------------------------------
end
function Do_Next_File()
   lg("Do_Next_File")
end
---------------------------------------------------------
--wifi.setmode(wifi.STATION)
--wifi.sta.disconnect()
--Connect2AP("acDev","AbroadCar2015()")
---------------------------------------------------------


bbk_si7021_pm25_lewei_ok.lua

--=============================================================
print("\nBBK_Lewei_Started_si7021_pm2.5")
--=============================================================
require("bbk_log")
require("bbk_ip_info")
---------------------------------
require("bbk_http_get_post")
require("bbk_lewei")
require("bbk_string")
---------------------------------
require("si7021")
require("bbk_si7021")
--=============================================================
lg("require_ok")
--=============================================================
function httpResponse(s)
   ----------------------------------------------------------
   conn=nil
   local n = string.len(s)
   if(n<10) then return end
   ----------------------------------------------------------   
   local z = StringGetAB(s,"{","}") s = nil n = nil
   print("{"..z.."}","\r\n")
   ----------------------------------------------------------
end
--=============================================================
--=============================================================
pm25 = 0
upkey = 0
----------------------------------------------------------
si7021_Init()
uart.setup( 0, 9600, 8, 0, 1, 0 )
----------------------------------------------------------
uart.on("data", 0,
function(data)
   if((string.len(data)==32) and (string.byte(data,1)==0x42) and (string.byte(data,2)==0x4d))then
          ----------------------------------------------------------
          pm25 = (string.byte(data,13)*256+string.byte(data,14))
          print("pm25 = "..pm25)
          ----------------------------------------------------------
          upkey = upkey + 1
          if upkey>15 then upkey=0 updateLoop() end
          ----------------------------------------------------------
   end
end, 0)
--=============================================================
function updateLoop()
   lg("updateLoop")
   si7021_Loop() si7021_Integer_Show()
   local d,l = buildValueJson("WD",si7021_T,"SD",si7021_H,"PM",pm25)
   lewei_httpPOST("02",d) d=nil l=nil
   collectgarbage()
end
--=============================================================


bbk_http_get_post.lua

--=============================================================
conn=nil
--=============================================================
function httpResponse(s)
   conn=nil
   print("back==")
   print(s)
end
function httpPOST(h,p,u,o,d)
   ----------------------------------------------------------
   local l=string.len(d)   
   ----------------------------------------------------------   
   local s="POST "..u.." HTTP/1.1\r\n"      
   .."Host:"..h.."\r\n"
      ..o
   .."Connection:close\r\n"
   .."Content-Length: " ..l.. "\r\n"
   .."\r\n"..d.."\r\n"
   .."\r\n"
   --print(str)
   ----------------------------------------------------------
   httpCONNECT(h,p,s)
   ----------------------------------------------------------
end
function httpGET(h,p,u)
   local s="GET "..u.." HTTP/1.1\r\n"
   .."Host:"..h.."\r\n"
   .."Connection:close\r\n"
   .."Accept: */*\r\n\r\n"
   --print(s)
   httpCONNECT(h,p,s)
end
function httpCONNECT(h,p,s)
   conn=net.createConnection(net.TCP, false)
   conn:on("connection",function(conn,r) conn:send(s) end)
   conn:on("receive", function(conn,r) httpResponse(r) end)
   conn:connect(p,h)
end
--=============================================================


bbk_lewei.lua

--=============================================================
port = 80
host = "www.lewei50.com"
urls = "/api/V1/gateway/UpdateSensors/"
usek = "userkey:xxxxxxx\r\n"
--=============================================================
function buildValueJson(...)
   local arg={...} local s=""
   local n = table.getn(arg)
   for i=1,n,2 do
      s = s.."{\"Name\":\""..arg.."\",\"Value\":\"" ..arg.. "\"}"
      if(i~=n-1)then s = s.."," end
   end
   s="["..s.."]"
   return s,string.len(s)
end
function lewei_httpPOST(g,d)
   httpPOST(host,port,urls..g,usek,d)
end
--=============================================================


bbk_string.lua

function StringGetAB(s,a,b)
   local ln = string.len(s)
   local ai,an=string.find(s,a) --print(ai.."="..an)
   local bi,bn=string.find(s,b) --print(bi.."="..bn)
   local ax=0 local bx=0
   if(ai~=nil) then ax=ai end
   if(an~=nil) then ax=an end
   if(bn~=nil) then bx=bn end
   if(bi~=nil) then bx=bi end
   if(ax+1>bx-1) then return "" end
   return string.sub(s,ax+1,bx-1),string.sub(s,bx+1,ln)
end


bbk_si7021.lua

----------------------------------------------------------
si7021_H = 0
si7021_T = 0
si7021 = require("si7021")
----------------------------------------------------------
function si7021_Init()
   local SDA_PIN = 5 -- sda pin, GPIO12
   local SCL_PIN = 6 -- scl pin, GPIO14
   si7021.init(SDA_PIN, SCL_PIN)
end
function si7021_Loop()
   si7021.read()
   si7021_H = si7021.getHumidity()
   si7021_T = si7021.getTemperature()
end
function si7021_Integer_Show()
   print("si7021: ",si7021_T.."C ",si7021_H.."%")
end
function si7021_Float_Show()
   local t = string.format("%.1f",si7021_T)
   local h = string.format("%.1f",si7021_H)
   print("si7021: ",t.."C ",h.."%")
end
----------------------------------------------------------
--si7021_Init()
--si7021_Loop()
--si7021_Show()
----------------------------------------------------------


si7021.lua

-- ***************************************************************************
-- SI7021 module for ESP8266 with nodeMCU
-- Si7021 compatible tested 2015-1-22
--
-- Written by VIP6
--
-- MIT license, http://opensource.org/licenses/MIT
-- ***************************************************************************

local moduleName = ...
local M = {}
_G = M

--I2C slave address of Si70xx
local Si7021_ADDR = 0x40

--Commands
local CMD_MEASURE_HUMIDITY_HOLD = 0xE5
local CMD_MEASURE_HUMIDITY_NO_HOLD = 0xF5
local CMD_MEASURE_TEMPERATURE_HOLD = 0xE3
local CMD_MEASURE_TEMPERATURE_NO_HOLD = 0xF3


-- temperature and humidity
local t,h

local init = false

-- i2c interface ID
local id = 0

-- 16-bittwo's complement
-- value: 16-bit integer
local function twoCompl(value)
if value > 32767 then value = -(65535 - value + 1)
end
return value
end



-- read data from si7021
-- ADDR: slave address
-- commands: Commands of si7021
-- lenght: bytes to read
local function read_data(ADDR, commands, length)
i2c.start(id)
i2c.address(id, ADDR, i2c.TRANSMITTER)
i2c.write(id, commands)
i2c.stop(id)
i2c.start(id)
i2c.address(id, ADDR,i2c.RECEIVER)
tmr.delay(20000)
c = i2c.read(id, length)
i2c.stop(id)
return c
end

-- initialize module
-- sda: SDA pin
-- scl SCL pin
function M.init(sda, scl)
i2c.setup(id, sda, scl, i2c.SLOW)
--print("i2c ok..")
init = true
end

-- read humidity from si7021
local function read_humi()
dataH = read_data(Si7021_ADDR, CMD_MEASURE_HUMIDITY_HOLD, 2)
UH = string.byte(dataH, 1) * 256 + string.byte(dataH, 2)
h = ((UH*12500+65536/2)/65536 - 600)
UH = nil
dataH = nil
return(h)
end

-- read temperature from si7021
local function read_temp()
dataT = read_data(Si7021_ADDR, CMD_MEASURE_TEMPERATURE_HOLD, 2)
UT = string.byte(dataT, 1) * 256 + string.byte(dataT, 2)
t = ((UT*17572+65536/2)/65536 - 4685)
UT = nil
dataT = nil
return(t)
end

-- read temperature and humidity from si7021
function M.read()
if (not init) then
   print("init() must be called before read.")
else
       read_humi()
       read_temp()
end
end;

-- get humidity
function M.getHumidity()
return string.sub(h, 1, 4)/100
end

-- get temperature
function M.getTemperature()
return string.sub(t, 1, 4)/100
end

return M


    (待补充)

    将这些文件都传入NodeMcu,就可以开始你的“空气质量上传乐联网”之旅了!

四、网页展示

    空气质量地图



五、注意事项

    1、NodeMcu的悖论

NodeMcu的悖论: 刷入了一个uart监控串口的程序,一启动就运行。但接下来重启操作、刷bin都不能停止这个监控串口的程序,这块NodeMcu就不能操作,也不能再写入程序了。

    悖论解决办法: nodemcu_integer_0.9.6-dev_20150704.bin、nodemcu_float_0.9.6-dev_20150704.bin 两个版本互相刷能够冲掉内嵌lua文件。

    核心关键 —— NodeMcu 只能支持一个UART,而且上传文件、程序都是使用这个串口,因此遇到串口传感器,特别是发送数据特别密的传感器,NodeMcu立马就不受控制了!

    NodeMcu看到有朋友写了软件串口的库,回头试试能否用软件虚拟串口来解决!

    2、调试时候先不要使用init.lua文件,如果init.lua文件中使用了串口监听,那么基本上板子就不能再调试了,必须得重新刷bin才能用,非常浪费时间!只有确定代码完全没问题了,再写入init.lua文件!!!

Cupid 发表于 2016-4-1 17:02:19

学习了。谢谢,连代码都放出来了,真不错

wetnt 发表于 2016-4-6 16:42:35

自己顶贴!自己顶贴!

xingyuewang 发表于 2016-4-6 23:56:32

我是来顶贴的

Wei 发表于 2016-4-7 10:40:18

:lol超级赞!!!!

dagger00 发表于 2016-4-7 10:43:19

楼主好厉害,膜拜:loveliness::loveliness::loveliness::loveliness:

laoliu1982 发表于 2016-4-7 10:45:13

我也来顶啊

yuzhile 发表于 2016-4-7 10:45:33

不错,山寨之,哈哈

原野动力 发表于 2016-6-4 19:31:27

学习学习支持支持

xiexunwei 发表于 2016-7-25 23:38:17

NodeMcu看到有朋友写了软件串口的库,回头试试能否用软件虚拟串口来解决!

大神,求库共同研究
[email protected]
非常感谢

wetnt 发表于 2016-9-17 10:25:53

软件虚拟串口 http://nodemcu.readthedocs.io/en/master/en/modules/uart/

slxy 发表于 2016-11-18 12:48:54

程序运行起来不能再写入程序这样解决: 在代码uart.on("data"加段逻辑,如果遇到+++退出回调函数,大概这样: if data==“+++" then      uart.on("data") 。。。

wetnt 发表于 2016-12-10 13:08:16

点亮板子上的蓝牙灯

wifi.setmode(wifi.STATION)
wifi.sta.config("xxxx","xxxx")
print(wifi.sta.getip())
led1 = 3
led2 = 4
gpio.mode(led1, gpio.OUTPUT)
gpio.mode(led2, gpio.OUTPUT)
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, "(+) (.+)?(.+) HTTP");
      if(method == nil)then
            _, _, method, path = string.find(request, "(+) (.+) HTTP");
      end
      local _GET = {}
      if (vars ~= nil)then
            for k, v in string.gmatch(vars, "(%w+)=(%w+)&*") do
                _GET = v
            end
      end
      buf = buf.."<h1> ESP8266 Web Server</h1>";
      buf = buf.."<p>GPIO0 <a href=\"?pin=ON1\"><button>ON</button></a> <a href=\"?pin=OFF1\"><button>OFF</button></a></p>";
      buf = buf.."<p>GPIO2 <a href=\"?pin=ON2\"><button>ON</button></a> <a href=\"?pin=OFF2\"><button>OFF</button></a></p>";
      local _on,_off = "",""
      if(_GET.pin == "ON1")then
            gpio.write(led1, gpio.HIGH);
      elseif(_GET.pin == "OFF1")then
            gpio.write(led1, gpio.LOW);
      elseif(_GET.pin == "ON2")then
            gpio.write(led2, gpio.LOW);
      elseif(_GET.pin == "OFF2")then
            gpio.write(led2, gpio.HIGH);
      end
      client:send(buf);
      client:close();
      collectgarbage();
    end)
end)

moyulan 发表于 2017-2-1 17:26:42

先收藏在学习

lipper 发表于 2017-2-12 11:21:51

本帖最后由 lipper 于 2017-3-26 18:36 编辑

这个代码有几个BUG。
1.;P表示“;P”
2.:o表示“:0”
嗯,是的,是网页的问题,
页: [1]
查看完整版本: 攀藤G5空气质量上报乐联网教程,PM2.5 HCHO NodeMcu(8266) si7021