SDS011 - mjdeventer/AQ-JDMB GitHub Wiki

Table of contents

  1. Communication
  2. Interfacing via Python3
  3. Readout function

Serial UART protocol

Number Byte Name Content
0 msg header AA
1 Command No C0
2 Data 1 PM2.5 low byte
3 Data 2 PM2.5 high byte
4 Data 3 PM10 low byte
5 Data 4 PM10 high byte
6 Data 5 ID byte 1
7 Data 6 ID byte 2
8 Checksum Check-sum
9 msg tail AB

Interfacing via Python3

the sensor always talks, i.e. there is no poll command
thus we need to identify the start and end character [AA] and [AB] to make sure we get a full data delivery from the sensor
here a Python3 code (modified from geoffwatts, and dhaeb)

import serial, time, struct
ser = serial.Serial()
ser.port = "/dev/ttyUSB0" # Set this to your serial port, e.g. "com3" on windows or "/dev/ttyUSB0" on linux, depending on which number of com you are using, check hardware manager
ser.baudrate = 9600
ser.open()
ser.flushInput()

byte, lastbyte = "\x00", b'\xab' #the actual start byte is \xc0, here byte is just a pre-allocation
while True:
    byte = ser.read(size=1); #print(byte)
    
    # We got a valid packet header when following is true:
    if byte == b'\xc0' and lastbyte == b'\xab':
        sentence = ser.read(size=8) # Read 8 more bytes
        readings = struct.unpack('<hhxxcc',sentence) # Decode the packet - little endian, 2 shorts for pm2.5 and pm10, 2 reserved bytes, checksum, message tail
        
        pm_25 = readings[0]/10
        pm_10 = readings[1]/10
        # ignoring the checksum and message tail
        
        print("PM 2.5:",pm_25,"μg/m^3  PM 10:",pm_10,"μg/m^3")
        lastbyte = readings[3]`#the last byte needs to be b'\xab' if a full data string has been received  ``

Readout function

It is useful to define the SDS011 readout as a function to trigger in a Data Acquisition loop for live plotting and storing the data

import struct, serial, time
def getsds011(comport):
    """Opens COM to SDS011 and returns 1 data touple of PM25 and PM10
    input - string of the comport the SDS011 is connected to
    output - a 1x2 toubple with [0] entry being pm25 and [1] being pm10"""
    
    portflagsds011 = True
    ser = serial.Serial()
    ser.port = comport # Set this to your serial port
    ser.baudrate = 9600
    ser.open()
    ser.flushInput()
    byte, lastbyte = "\x00", b'\xab'
    while portflagsds011== True:
        #lastbyte = byte
        byte = ser.read(size=1); #print(byte)
        
        # We got a valid packet header
        if byte == b'\xc0' and lastbyte == b'\xab':
        #if lastbyte == "\x00" and byte == b'\xaa':
            sentence = ser.read(size=8) # Read 8 more bytes
            readings = struct.unpack('<hhxxcc',sentence) # Decode the packet - little endian, 2 shorts for pm2.5 and pm10, 2 reserved bytes, checksum, message tail
            
            pm_25 = readings[0]/10
            pm_10 = readings[1]/10
            # ignoring the checksum and message tail
            
            print("PM 2.5:",pm_25,"μg/m^3  PM 10:",pm_10,"μg/m^3")
            lastbyte = readings[3]
            del readings
            portflagsds011 = False
            #ser.flushInput()
            ser.close()
            return pm_25, pm_10