Skip to content

Instantly share code, notes, and snippets.

@kadamski
Last active March 31, 2024 02:03
Show Gist options
  • Save kadamski/92653913a53baf9dd1a8 to your computer and use it in GitHub Desktop.
Save kadamski/92653913a53baf9dd1a8 to your computer and use it in GitHub Desktop.
SDS011 dust sensor reading
#!/usr/bin/python
# coding=utf-8
# "DATASHEET": http://cl.ly/ekot
from __future__ import print_function
import serial, struct, sys, time
DEBUG = 1
CMD_MODE = 2
CMD_QUERY_DATA = 4
CMD_DEVICE_ID = 5
CMD_SLEEP = 6
CMD_FIRMWARE = 7
CMD_WORKING_PERIOD = 8
MODE_ACTIVE = 0
MODE_QUERY = 1
ser = serial.Serial()
ser.port = sys.argv[1]
ser.baudrate = 9600
ser.open()
ser.flushInput()
byte, data = 0, ""
def dump(d, prefix=''):
print(prefix + ' '.join(x.encode('hex') for x in d))
def construct_command(cmd, data=[]):
assert len(data) <= 12
data += [0,]*(12-len(data))
checksum = (sum(data)+cmd-2)%256
ret = "\xaa\xb4" + chr(cmd)
ret += ''.join(chr(x) for x in data)
ret += "\xff\xff" + chr(checksum) + "\xab"
if DEBUG:
dump(ret, '> ')
return ret
def process_data(d):
r = struct.unpack('<HHxxBB', d[2:])
pm25 = r[0]/10.0
pm10 = r[1]/10.0
checksum = sum(ord(v) for v in d[2:8])%256
print("PM 2.5: {} μg/m^3 PM 10: {} μg/m^3 CRC={}".format(pm25, pm10, "OK" if (checksum==r[2] and r[3]==0xab) else "NOK"))
def process_version(d):
r = struct.unpack('<BBBHBB', d[3:])
checksum = sum(ord(v) for v in d[2:8])%256
print("Y: {}, M: {}, D: {}, ID: {}, CRC={}".format(r[0], r[1], r[2], hex(r[3]), "OK" if (checksum==r[4] and r[5]==0xab) else "NOK"))
def read_response():
byte = 0
while byte != "\xaa":
byte = ser.read(size=1)
d = ser.read(size=9)
if DEBUG:
dump(d, '< ')
return byte + d
def cmd_set_mode(mode=MODE_QUERY):
ser.write(construct_command(CMD_MODE, [0x1, mode]))
read_response()
def cmd_query_data():
ser.write(construct_command(CMD_QUERY_DATA))
d = read_response()
if d[1] == "\xc0":
process_data(d)
def cmd_set_sleep(sleep=1):
mode = 0 if sleep else 1
ser.write(construct_command(CMD_SLEEP, [0x1, mode]))
read_response()
def cmd_set_working_period(period):
ser.write(construct_command(CMD_WORKING_PERIOD, [0x1, period]))
read_response()
def cmd_firmware_ver():
ser.write(construct_command(CMD_FIRMWARE))
d = read_response()
process_version(d)
def cmd_set_id(id):
id_h = (id>>8) % 256
id_l = id % 256
ser.write(construct_command(CMD_DEVICE_ID, [0]*10+[id_l, id_h]))
read_response()
if __name__ == "__main__":
cmd_set_sleep(0)
cmd_set_mode(1);
cmd_firmware_ver()
time.sleep(3)
cmd_query_data();
cmd_set_mode(0);
cmd_set_sleep()
@RemBrandNL
Copy link

Thanks for the supports, after more hours of Googling and much trial&error I found out I am not the only one who ran into this issue. For who is interested, this topic summarizes it best: https://www.raspberrypi.org/forums/viewtopic.php?t=219026

Other posts on this topic:

The only option to make it somewhat more compact is to replace the USB > UART board with a angled micro USB so it doesn't stick out that much.

One last hope I have is to pick up power from the USB port to see if that doesn't reboot it, trying that later today. Setting the SDS011 to sleep and waking it up again does work via that USB dongle, so let's see what happens if you draw power from there instead of the GPIO pins directly ;-)
Cheers,
Remco

@RemBrandNL
Copy link

OK, not sure if it's the 'correct' way to go, but I did get it working withouth that stupid dongle. Wiring needs some TLC, but it's working now. I can put the sensor to sleep and wake it up again without my RPI rebooting. Complete lenghty lengthy write-up with more pics on https://tinker.rembrand.nl/2021/01/02/using-sds011-via-uart/

I combined it with a DHT22 sensor for now and it's happily recording every 5 minutes:
image

Cheers,
Remco

image

@kadamski
Copy link
Author

kadamski commented Jan 2, 2021

@remco have you tried.putting a capacitor between the 5v and gnd to smooth out the voltage drop?

@RemBrandNL
Copy link

Wouldn't even know which value to pick; my knowledge of electronics is limited to none ;-) I did read on several posts/boards that others did try that, including circuits with diodes and all, but to no avail. Best I can do is logic and this seems to work as expected. Until I find some other solution, I'll tidy this up and move it to an enclosure outside to do further testing with the scripts I am working on. I want to collect the data to a database, do some graphs, send it properly formated to luftdaten. All knowledge gaps so I have quite some challenges. Your scripts helped me to get started with the SDS011 though, so many thanks for that :-)
Remco

@tyeth
Copy link

tyeth commented Jan 2, 2021

Wicked, also check the /var/log/syslog file or dmesg, i remember low voltage warnings in one of them when I was doing too much with my weak 5v supply

@RemBrandNL
Copy link

Wicked, also check the /var/log/syslog file or dmesg, i remember low voltage warnings in one of them when I was doing too much with my weak 5v supply

image

It's been running in the backyard since yesterday evening, 0 hits on 'voltage' issues it seems. Ever since then it has measured 10 times with 1 second intervals, put the SDS011 to sleep for about 5 minutes, wake up and measure again. So I am still optimistic this works ;-)
Pics of the enclosure you can find here. Currently using a standard weatherproof junction box which was always my entention for the outdoor enclosure.
Remco

@alokvermaiitr
Copy link

What needs to change in the code if I would not use Sensor Hat?

@kadamski
Copy link
Author

@alokvermaiitr what you mean by Sensor Hat?

@FaisalAhmed123
Copy link

For those still having the issue where it maxes out, I wrote some new code which seems to work past the 20,000 mark https://github.com/FaisalAhmed123/Nova-PM2.5-Sensor-Reading-Interpreter/blob/main/main.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment