air-sensors

Air Quality Sensor library
git clone https://www.brianlane.com/git/air-sensors
Log | Files | Refs | README | LICENSE

pmsa003i.go (2903B)


      1 // Copyright 2020 by Brian C. Lane <bcl@brianlane.com>. All rights reserved.
      2 // Use of this source code is governed under the Apache License, Version 2.0
      3 // that can be found in the LICENSE file.
      4 
      5 package pmsa003i
      6 
      7 import (
      8 	"fmt"
      9 
     10 	"periph.io/x/periph/conn"
     11 	"periph.io/x/periph/conn/i2c"
     12 )
     13 
     14 func checksum(data []byte) bool {
     15 	var cksum uint16
     16 	for i := 0; i < len(data)-2; i++ {
     17 		cksum = cksum + uint16(data[i])
     18 	}
     19 	return word(data, 0x1e) == cksum
     20 }
     21 
     22 // Results contains the measurements from the PMSA003i
     23 type Results struct {
     24 	CfPm1    uint16 // PM1.0 in μg/m3 standard particle
     25 	CfPm2_5  uint16 // PM2.5 in μg/m3 standard particle
     26 	CfPm10   uint16 // PM10 in μg/m3 standard particle
     27 	EnvPm1   uint16 // PM1.0 in μg/m3 atmospheric environment
     28 	EnvPm2_5 uint16 // PM2.5 in μg/m3 atmospheric environment
     29 	EnvPm10  uint16 // PM10 in μg/m3 atmospheric environment
     30 	Cnt0_3   uint16 // Count of particles > 0.3μm in 0.1L of air
     31 	Cnt0_5   uint16 // Count of particles > 0.5μm in 0.1L of air
     32 	Cnt1     uint16 // Count of particles > 1.0μm in 0.1L of air
     33 	Cnt2_5   uint16 // Count of particles > 2.5μm in 0.1L of air
     34 	Cnt5     uint16 // Count of particles > 5.0μm in 0.1L of air
     35 	Cnt10    uint16 // Count of particles > 10.0μm in 0.1L of air
     36 	Version  uint8
     37 }
     38 
     39 // New returns a PMSA003I device struct for communicating with the device
     40 //
     41 func New(i i2c.Bus) (*Dev, error) {
     42 	d := &Dev{i2c: &i2c.Dev{Bus: i, Addr: 0x12}}
     43 
     44 	_, err := d.ReadSensor()
     45 	if err != nil {
     46 		return nil, err
     47 	}
     48 	return d, nil
     49 }
     50 
     51 // Dev holds the connection and error details for the device
     52 type Dev struct {
     53 	i2c conn.Conn // i2c device handle for the pmsa003i
     54 	err error     //nolint
     55 }
     56 
     57 // Halt implements conn.Resource.
     58 func (d *Dev) Halt() error {
     59 	return nil
     60 }
     61 
     62 // ReadSensor returns particle measurement results
     63 func (d *Dev) ReadSensor() (Results, error) {
     64 	// Receive 32 bytes
     65 	var data [32]byte
     66 	if err := d.i2c.Tx(nil, data[:]); err != nil {
     67 		return Results{}, fmt.Errorf("pmsa003i: Error while reading the sensor: %w", err)
     68 	}
     69 
     70 	if word(data[:], 0) != 0x424d {
     71 		return Results{}, fmt.Errorf("pmsa003i: Bad start word")
     72 	}
     73 	if !checksum(data[:]) {
     74 		return Results{}, fmt.Errorf("pmsa003i: Bad checksum")
     75 	}
     76 	if data[0x1d] != 0x00 {
     77 		return Results{}, fmt.Errorf("pmsa003i: Error code %x", data[0x1d])
     78 	}
     79 
     80 	return Results{
     81 		CfPm1:    word(data[:], 0x04),
     82 		CfPm2_5:  word(data[:], 0x06),
     83 		CfPm10:   word(data[:], 0x08),
     84 		EnvPm1:   word(data[:], 0x0a),
     85 		EnvPm2_5: word(data[:], 0x0c),
     86 		EnvPm10:  word(data[:], 0x0e),
     87 		Cnt0_3:   word(data[:], 0x10),
     88 		Cnt0_5:   word(data[:], 0x12),
     89 		Cnt1:     word(data[:], 0x14),
     90 		Cnt2_5:   word(data[:], 0x16),
     91 		Cnt5:     word(data[:], 0x18),
     92 		Cnt10:    word(data[:], 0x1a),
     93 		Version:  data[0x1c],
     94 	}, nil
     95 }
     96 
     97 // word returns 16 bits from the byte stream, starting at index i
     98 func word(data []byte, i int) uint16 {
     99 	return uint16(data[i])<<8 + uint16(data[i+1])
    100 }