Unverified Commit a07247be authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

initial commit

parents
Loading
Loading
Loading
Loading

.gitignore

0 → 100644
+1 −0
Original line number Diff line number Diff line
config.lua

README.md

0 → 100644
+55 −0
Original line number Diff line number Diff line
# ESP8266 Lua/NodeMCU module for Vindriktning PM1006 particle monitor

This repository contains a Lua module (`pm1006.lua`) as well as ESP8266/NodeMCU
MQTT gateway application example (`init.lua`) for the **PM1006** particulate
matter (PM2.5) sensor found in IKEA Vindriktning.

## Dependencies

pm1006.lua has been tested with Lua 5.1 on NodeMCU firmware 3.0.1 (Release
202112300746, integer build). Most practical applications (such as the example
in init.lua) require the following modules.

* gpio
* mqtt
* node
* softuart
* tmr
* uart
* wifi

## Setup

Connect the PM1006 sensor to your ESP8266/NodeMCU board as [documented by Hypfer](https://github.com/Hypfer/esp8266-vindriktning-particle-sensor).

If you use a different UART pin, you need to adjust the softuart.setup call in
the examples provided in this repository to reflect that change. Keep in mind
that some ESP8266 pins must have well-defined logic levels at boot time and may
therefore be unsuitable for PM1006 connection.

## Usage

Copy **pm1006.lua** to your NodeMCU board and set it up as follows.

```lua
pm1006 = require("pm1006")
port = softuart.setup(9600, nil, 2)
port:on("data", 20, uart_callback)

function uart_callback(data)
	local pm2_5 = pm1006.parse_frame(data)
	if pm25i ~= nil then
		-- pm2_5 contains PM2.5 value in µg/m³
	else
		-- invalid frame header or checksum
	end
end
```

See **init.lua** for an example. To use it, you need to create a **config.lua** file with WiFI and MQTT settings:

```lua
station_cfg.ssid = "..."
station_cfg.pwd = "..."
mqtt_host = "..."
```

init.lua

0 → 100644
+75 −0
Original line number Diff line number Diff line
station_cfg = {}
dofile("config.lua")

delayed_restart = tmr.create()
chipid = node.chipid()
mqtt_prefix = "sensor/esp8266_" .. chipid
mqttclient = mqtt.Client("esp8266_" .. chipid, 120)

print("ESP8266 " .. chipid)

ledpin = 4
gpio.mode(ledpin, gpio.OUTPUT)
gpio.write(ledpin, 0)

pm1006 = require("pm1006")

function log_restart()
	print("Network error " .. wifi.sta.status() .. ". Restarting in 20 seconds.")
	delayed_restart:start()
end

function setup_client()
	print("Connected")
	gpio.write(ledpin, 1)
	publishing = true
	mqttclient:publish(mqtt_prefix .. "/state", "online", 0, 1, function(client)
		publishing = false
	end)
	port = softuart.setup(9600, nil, 2)
	port:on("data", 20, uart_callback)
end

function connect_mqtt()
	print("IP address: " .. wifi.sta.getip())
	print("Connecting to MQTT " .. mqtt_host)
	delayed_restart:stop()
	mqttclient:on("connect", setup_client)
	mqttclient:on("offline", log_restart)
	mqttclient:lwt(mqtt_prefix .. "/state", "offline", 0, 1)
	mqttclient:connect(mqtt_host)
end

function connect_wifi()
	print("WiFi MAC: " .. wifi.sta.getmac())
	print("Connecting to ESSID " .. station_cfg.ssid)
	wifi.eventmon.register(wifi.eventmon.STA_GOT_IP, connect_mqtt)
	wifi.eventmon.register(wifi.eventmon.STA_DHCP_TIMEOUT, log_restart)
	wifi.eventmon.register(wifi.eventmon.STA_DISCONNECTED, log_restart)
	wifi.setmode(wifi.STATION)
	wifi.sta.config(station_cfg)
	wifi.sta.connect()
end

function uart_callback(data)
	local pm2_5 = pm1006.parse_frame(data)
	if pm2_5 == nil then
		print("Invalid PM1006 frame")
		return
	else
		local json_str = string.format('{"pm25_ugm3": %d, "rssi_dbm": %d}', pm2_5, wifi.sta.getrssi())
		if not publishing then
			publishing = true
			gpio.write(ledpin, 0)
			mqttclient:publish(mqtt_prefix .. "/data", json_str, 0, 0, function(client)
				publishing = false
				gpio.write(ledpin, 1)
				collectgarbage()
			end)
		end
	end
end

delayed_restart:register(20 * 1000, tmr.ALARM_SINGLE, node.restart)

connect_wifi()

pm1006.lua

0 → 100644
+20 −0
Original line number Diff line number Diff line
local pm1006 = {}

function pm1006.parse_frame(data)
	if string.byte(data, 1) ~= 0x16 or string.byte(data, 2) ~= 0x11 or string.byte(data, 3) ~= 0x0b then
		-- invalid header
		return nil
	end
	local checksum = 0
	for i = 1, 20 do
		checksum = (checksum + string.byte(data, i)) % 256
	end
	if checksum ~= 0 then
		-- invalid checksum
		return nil
	end
	local pm2_5 = string.byte(data, 6) * 256 + string.byte(data, 7)
	return pm2_5
end

return pm1006