summaryrefslogtreecommitdiff
path: root/sds011.lua
blob: 06cd5ac8049f5a807ead7a736cfcb7e4d4a08dd6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
local sds011 = {}

local c_head = 0xaa
local c_tail = 0xab
local c_id = 0xb4

local c_read = 0x00
local c_write = 0x01

local c_report_mode = 0x02
local c_active = 0x00
local c_passive = 0x01

local c_query = 0x04

local c_sleepcmd = 0x06
local c_sleep = 0x00
local c_work = 0x01
local c_workperiod = 0x08

sds011.active_mode = nil
sds011.work_period = nil
sds011.working = nil

function sds011.finish_cmd(cmd)
	cmd = cmd .. string.char(0xff, 0xff)
	local checksum = 0
	for i = 3, string.len(cmd) do
		checksum = (checksum + string.byte(cmd, i)) % 256
	end
	cmd = cmd .. string.char(checksum, c_tail)
	return cmd
end

function sds011.query()
	local cmd = string.char(c_head, c_id, c_query)
	cmd = cmd .. string.char(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
	return sds011.finish_cmd(cmd)
end

function sds011.set_report_mode(active)
	local op = c_write
	local cmd = c_passive
	if active == nil then
		op = c_read
		active = false
	elseif active then
		cmd = c_active
	end
	cmd = string.char(c_head, c_id, c_report_mode, op, cmd)
	cmd = cmd .. string.char(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
	return sds011.finish_cmd(cmd)
end

function sds011.set_sleep(sleep)
	local op = c_write
	local cmd = c_work
	if sleep == nil then
		-- if the device is sleeping, it will not respond to a query
		sds011.working = false
		op = c_read
	elseif sleep then
		cmd = c_sleep
	end
	cmd = string.char(c_head, c_id, c_sleepcmd, op, cmd)
	cmd = cmd .. string.char(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
	return sds011.finish_cmd(cmd)
end

function sds011.set_work_period(period)
	-- period == 0 : continuous operation, about one measurement per second
	-- period > 0  : about one measurement every <period> minutes, fan is turned off in-between
	if period ~= nil and (period < 0 or period > 30) then
		return
	end
	local op = c_write
	if period == nil then
		op = c_read
		period = 0
	end
	local cmd = string.char(c_head, c_id, c_workperiod, op, period)
	cmd = cmd .. string.char(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
	return sds011.finish_cmd(cmd)
end

function sds011.parse_frame(data)
	local header, command, pm25l, pm25h, pm10l, pm10h, id1, id2, sum, tail = struct.unpack("BBBBBBBBBB", data)
	if header ~= c_head or (pm25l + pm25h + pm10l + pm10h + id1 + id2) % 256 ~= sum or tail ~= c_tail then
		return false
	end
	if command == 0xc0 then
		local pm25 = pm25h * 256 + pm25l
		local pm10 = pm10h * 256 + pm10l
		sds011.pm2_5i = pm25 / 10
		sds011.pm2_5f = pm25 % 10
		sds011.pm10i = pm10 / 10
		sds011.pm10f = pm10 % 10
		return true
	elseif command == 0xc5 and pm25l == 0x02 then
		sds011.active_mode = pm10l == 0
		return true
	elseif command == 0xc5 and pm25l == 0x06 then
		if pm25h == 0 or pm10l == 1 then
			sds011.working = true
		else
			sds011.working = false
		end
		return true
	elseif command == 0xc5 and pm25l == 0x08 then
		sds011.work_period = pm10l
		return true
	end
	return false
end

return sds011