func NewSensor(rwc io.ReadWriteCloser) *Sensor {
	return &Sensor{rwc: rwc}
}
NewSensor returns a sensor that will read its data from the provided
read-write-closer.
package sds011
import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"time"
	log "github.com/golang/glog"
	"github.com/jacobsa/go-serial/serial"
)
type command byte
type mode byte
const (
	commandReportMode command = 2
	commandQuery      command = 4
	commandDeviceID   command = 5
	commandWorkState  command = 6
	commandFirmware   command = 7
	commandCycle      command = 8
	modeGet mode = 0
	modeSet mode = 1
	reportModeActive byte = 0
	reportModeQuery  byte = 1
	workStateSleeping  byte = 0
	workStateMeasuring byte = 1
)
type response struct {
	Header   byte  
	Command  byte  
	Data     [6]byte
	CheckSum byte
	Tail     byte  
}
func (resp *response) IsReply() bool {
	return resp.Command == 0xC5
}
func (resp *response) PM25() float64 {
	if resp.IsReply() {
		panic(fmt.Sprintf("access to field that doesn't work with this type of response %#v", resp))
	}
	return float64(binary.LittleEndian.Uint16(resp.Data[0:2])) / 10.0
}
func (resp *response) PM10() float64 {
	if resp.IsReply() {
		panic(fmt.Sprintf("access to field that doesn't work with this type of response %#v", resp))
	}
	return float64(binary.LittleEndian.Uint16(resp.Data[2:4])) / 10.0
}
func (resp *response) checkMatches(cmd command) {
	if resp.Data[0] != byte(cmd) {
		panic(fmt.Sprintf("access to field that doesn't work with this type of response %#v", resp))
	}
}
func (resp *response) ID() uint16 {
	if resp.IsReply() {
		panic(fmt.Sprintf("access to field that doesn't work with this type of response %#v", resp))
	}
	return binary.LittleEndian.Uint16(resp.Data[4:6])
}
func (resp *response) Firmware() string {
	resp.checkMatches(commandFirmware)
	return fmt.Sprintf("%02d-%02d-%02d", resp.Data[1], resp.Data[2], resp.Data[3])
}
func (resp *response) DeviceID() string {
	resp.checkMatches(commandDeviceID)
	return fmt.Sprintf("%02d%02d", resp.Data[1], resp.Data[2])
}
func (resp *response) ReportMode() byte {
	resp.checkMatches(commandReportMode)
	return resp.Data[2]
}
func (resp *response) Cycle() uint8 {
	resp.checkMatches(commandCycle)
	return resp.Data[2]
}
func (resp *response) WorkState() byte {
	resp.checkMatches(commandWorkState)
	return resp.Data[2]
}
type request struct {
	Header     byte      
	SendMarker byte      
	Command    byte      
	Mode       byte      
	Data       [11]byte  
	DeviceID   [2]byte   
	CheckSum   byte      
	Tail       byte      
}
func makeRequest(cmd command, mod mode, value byte) *request {
	data := [11]byte{}
	data[0] = value
	req := &request{
		Header:     0xAA,
		SendMarker: 0xB4,
		Command:    byte(cmd),
		Mode:       byte(mod),
		Data:       data,
		DeviceID:   [2]byte{0xFF, 0xFF},
		Tail:       0xAB,
	}
	checksum := int(req.Command) + int(req.Mode)
	for _, v := range data {
		checksum += int(v)
	}
	for _, v := range req.DeviceID {
		checksum += int(v)
	}
	req.CheckSum = byte(checksum % 256)
	return req
}
func (resp *response) IsCorrect() error {
	var checkSum byte
	for i := 0; i < 6; i++ {
		checkSum += resp.Data[i]
	}
	if checkSum != resp.CheckSum {
		return fmt.Errorf("bad checksum: %#v", resp)
	}
	return nil
}
type Point struct {
	PM25      float64
	PM10      float64
	Timestamp time.Time
}
func (point *Point) String() string {
	return fmt.Sprintf("PM2.5: %v μg/m³ PM10: %v μg/m³", point.PM25, point.PM10)
}
type Sensor struct {
	rwc io.ReadWriteCloser
}
func (sensor *Sensor) send(cmd command, mod mode, data byte) error {
	b := new(bytes.Buffer)
	if err := binary.Write(b, binary.LittleEndian, makeRequest(cmd, mod, data)); err != nil {
		return err
	}
	log.V(6).Infof("sending bytes: %#v", b.Bytes())
	_, err := sensor.rwc.Write(b.Bytes())
	return err
}
func (sensor *Sensor) receive() (*response, error) {
	data := new(response)
	if err := binary.Read(sensor.rwc, binary.LittleEndian, data); err != nil {
		return nil, err
	}
	if err := data.IsCorrect(); err != nil {
		return nil, err
	}
	return data, nil
}
func (sensor *Sensor) receiveReply() (*response, error) {
	for i := 0; i < 10; i++ {
		resp, err := sensor.receive()
		if err != nil {
			return nil, err
		}
		if resp.IsReply() {
			return resp, nil
		}
		log.V(6).Infof("received data, but not a reply: %#v", resp)
	}
	return nil, errors.New("no reply")
}
func (sensor *Sensor) ReportMode() (bool, error) {
	if err := sensor.send(commandReportMode, modeGet, 0); err != nil {
		return false, err
	}
	data, err := sensor.receiveReply()
	if err != nil {
		return false, err
	}
	log.V(6).Infof("ReportMode response: %#v", data)
	return data.ReportMode() == reportModeActive, nil
}
func (sensor *Sensor) MakeActive() error {
	if err := sensor.send(commandReportMode, modeSet, reportModeActive); err != nil {
		return err
	}
	data, err := sensor.receiveReply()
	if err != nil {
		return err
	}
	log.V(6).Infof("MakeActive: %#v", data)
	return nil
}
func (sensor *Sensor) MakePassive() error {
	log.V(6).Infof("make passive")
	if err := sensor.send(commandReportMode, modeSet, reportModeQuery); err != nil {
		return err
	}
	data, err := sensor.receiveReply()
	if err != nil {
		return err
	}
	log.V(6).Infof("MakePassive response: %#v", data)
	return nil
}
func (sensor *Sensor) DeviceID() (string, error) {
	if err := sensor.send(commandDeviceID, modeGet, 0); err != nil {
		return "", err
	}
	data, err := sensor.receiveReply()
	if err != nil {
		return "", err
	}
	log.V(6).Infof("DeviceID: %#v", data)
	return data.DeviceID(), nil
}
func (sensor *Sensor) Firmware() (string, error) {
	if err := sensor.send(commandFirmware, modeGet, 0); err != nil {
		return "", err
	}
	data, err := sensor.receiveReply()
	if err != nil {
		return "", err
	}
	log.V(6).Infof("Firmare: %#v", data)
	return data.Firmware(), nil
}
func (sensor *Sensor) Cycle() (uint8, error) {
	if err := sensor.send(commandCycle, modeGet, 0); err != nil {
		return 0, err
	}
	data, err := sensor.receiveReply()
	if err != nil {
		return 0, err
	}
	log.V(6).Infof("Cycle: %#v", data)
	return data.Cycle(), nil
}
func (sensor *Sensor) SetCycle(value uint8) error {
	if value < 0 || value > 30 {
		return fmt.Errorf("duty cycle: bad value %v. Should be between 0 and 30.", value)
	}
	if err := sensor.send(commandCycle, modeSet, value); err != nil {
		return err
	}
	data, err := sensor.receiveReply()
	if err != nil {
		return err
	}
	log.V(6).Infof("SetCycle: %#v", data)
	return nil
}
func (sensor *Sensor) Query() (*Point, error) {
	if err := sensor.send(commandQuery, modeGet, 0); err != nil {
		return nil, err
	}
	return sensor.Get()
}
func (sensor *Sensor) IsAwake() (bool, error) {
	if err := sensor.send(commandWorkState, modeGet, 0); err != nil {
		return false, err
	}
	data, err := sensor.receiveReply()
	if err != nil {
		return false, err
	}
	log.V(6).Infof("IsAwake WorkState: %#v", data)
	return data.WorkState() == workStateMeasuring, nil
}
func (sensor *Sensor) Awake() error {
	if err := sensor.send(commandWorkState, modeSet, workStateMeasuring); err != nil {
		return err
	}
	data, err := sensor.receiveReply()
	if err != nil {
		return err
	}
	log.V(6).Infof("Awake WorkState: %#v", data)
	return nil
}
func (sensor *Sensor) Sleep() error {
	if err := sensor.send(commandWorkState, modeSet, workStateSleeping); err != nil {
		return err
	}
	data, err := sensor.receiveReply()
	if err != nil {
		return err
	}
	log.V(6).Infof("WorkState: %#v", data)
	return nil
}
func (sensor *Sensor) Close() {
	sensor.rwc.Close()
}
func New(portPath string) (*Sensor, error) {
	options := serial.OpenOptions{
		PortName:        portPath,
		BaudRate:        9600,
		DataBits:        8,
		StopBits:        1,
		MinimumReadSize: 4,
	}
	port, err := serial.Open(options)
	if err != nil {
		return nil, err
	}
	return NewSensor(port), nil
}