#include "sps30.h"
/*define communication channel to use for SPS30
valid options:
* I2C_COMMS use I2C communication
* SOFTWARE_SERIAL Arduino variants and ESP8266 (NOTE)
* SERIALPORT ONLY IF there is NO monitor attached
* SERIALPORT1 Arduino MEGA2560, Sparkfun ESP32 Thing : MUST define new pins as defaults are used for flash memory)
* SERIALPORT2 Arduino MEGA2560 and ESP32
* SERIALPORT3 Arduino MEGA2560 only for now */
#define SP30_COMMS SERIALPORT1
/* define RX and TX pin for softserial and Serial1 on ESP32
* can be set to zero if not applicable / needed */
#define TX_PIN 26
#define RX_PIN 25
/* define new AUTO Clean interval
* Will be remembered after power off
* default is 604800 seconds
* 0 = disable Auto Clean
* -1 = do not change current setting */
#define AUTOCLEANINTERVAL -1
/* Perform a clean NOW ?
* 1 = yes
* 0 = NO */
#define PERFORMCLEANNOW 0
#define DEBUG 0
// function prototypes (sometimes the pre-processor does not create prototypes themself on ESPxx)
void serialTrigger(char * mess);
void ErrtoMess(char *mess, uint8_t r);
void Errorloop(char *mess, uint8_t r);
void GetDeviceInfo();
bool read_all();
void SetAutoClean();
// create constructor
SPS30 sps30;
void setup() {
Serial.begin(115200);
serialTrigger("SPS30-Example2: Basic reading + clean. press <enter> to start");
Serial.println(F("Trying to connect"));
// set driver debug level
sps30.EnableDebugging(DEBUG);
// set pins to use for softserial and Serial1 on ESP32
if (TX_PIN != 0 && RX_PIN != 0) sps30.SetSerialPin(RX_PIN,TX_PIN);
// Begin communication channel;
if (sps30.begin(SP30_COMMS) == false) {
Errorloop("could not initialize communication channel.", 0);
}
// check for SPS30 connection
if (sps30.probe() == false) {
Errorloop("could not probe /connect with SPS30.", 0);
}
else
Serial.println(F("Detected SPS30."));
// reset SPS30 connection
if (sps30.reset() == false) {
Errorloop("could not reset.", 0);
}
// read device info
GetDeviceInfo();
// do Auto Clean interval
SetAutoClean();
// start measurement
if (sps30.start() == true) {
Serial.println(F("Measurement started"));
}
else
Errorloop("Could NOT start measurement", 0);
// clean now requested
if (PERFORMCLEANNOW) {
// clean now
if (sps30.clean() == true)
Serial.println(F("fan-cleaning manually started"));
else
Serial.println(F("Could NOT manually start fan-cleaning"));
}
serialTrigger("Hit <enter> to continue reading");
if (SP30_COMMS == I2C_COMMS) {
if (sps30.I2C_expect() == 4)
Serial.println(F(" !!! Due to I2C buffersize only the SPS30 MASS concentration is available !!! \n"));
}
}
void loop() {
read_all();
delay(3000);
}
/**
* @brief: read and display device info
*/
void GetDeviceInfo()
{
char buf[32];
uint8_t ret;
//try to read serial number
ret = sps30.GetSerialNumber(buf, 32);
if (ret == ERR_OK) {
Serial.print(F("Serial number : "));
if(strlen(buf) > 0) Serial.println(buf);
else Serial.println(F("not available"));
}
else
ErrtoMess("could not get serial number.", ret);
// try to get product name
ret = sps30.GetProductName(buf, 32);
if (ret == ERR_OK) {
Serial.print(F("Product name : "));
if(strlen(buf) > 0) Serial.println(buf);
else Serial.println(F("not available"));
}
else
ErrtoMess("could not get product name.", ret);
// try to get article code
ret = sps30.GetArticleCode(buf, 32);
if (ret == ERR_OK) {
Serial.print(F("Article code : "));
if(strlen(buf) > 0) Serial.println(buf);
else Serial.println(F("not available"));
}
else
ErrtoMess("could not get article code.", ret);
}
/**
* @brief: Get & Set new Auto Clean Interval
*
*/
void SetAutoClean()
{
uint32_t interval;
uint8_t ret;
// try to get interval
ret = sps30.GetAutoCleanInt(&interval);
if (ret == ERR_OK) {
Serial.print(F("Current Auto Clean interval: "));
Serial.print(interval);
Serial.println(F(" seconds"));
}
else
ErrtoMess("could not get clean interval.", ret);
// only if requested
if (AUTOCLEANINTERVAL == -1) {
Serial.println(F("No Auto Clean interval change requested."));
return;
}
// try to set interval
interval = AUTOCLEANINTERVAL;
ret = sps30.SetAutoCleanInt(interval);
if (ret == ERR_OK) {
Serial.print(F("Auto Clean interval now set : "));
Serial.print(interval);
Serial.println(F(" seconds"));
}
else
ErrtoMess("could not set clean interval.", ret);
// try to get interval
ret = sps30.GetAutoCleanInt(&interval);
if (ret == ERR_OK) {
Serial.print(F("Current Auto Clean interval: "));
Serial.print(interval);
Serial.println(F(" seconds"));
}
else
ErrtoMess("could not get clean interval.", ret);
}
/**
* @brief: read and display all values
*/
bool read_all()
{
static bool header = true;
uint8_t ret, error_cnt = 0;
struct sps_values val;
// loop to get data
do {
ret = sps30.GetValues(&val);
// data might not have been ready
if (ret == ERR_DATALENGTH){
if (error_cnt++ > 3) {
ErrtoMess("Error during reading values: ",ret);
return(false);
}
delay(1000);
}
// if other error
else if(ret != ERR_OK) {
ErrtoMess("Error during reading values: ",ret);
return(false);
}
} while (ret != ERR_OK);
// only print header first time
if (header) {
Serial.println(F("-------------Mass ----------- ------------- Number -------------- -Average-"));
Serial.println(F(" Concentration [μg/m3] Concentration [#/cm3] [μm]"));
Serial.println(F("P1.0\tP2.5\tP4.0\tP10\tP0.5\tP1.0\tP2.5\tP4.0\tP10\tPartSize\n"));
header = false;
}
Serial.print(val.MassPM1);
Serial.print(F("\t"));
Serial.print(val.MassPM2);
Serial.print(F("\t"));
Serial.print(val.MassPM4);
Serial.print(F("\t"));
Serial.print(val.MassPM10);
Serial.print(F("\t"));
Serial.print(val.NumPM0);
Serial.print(F("\t"));
Serial.print(val.NumPM1);
Serial.print(F("\t"));
Serial.print(val.NumPM2);
Serial.print(F("\t"));
Serial.print(val.NumPM4);
Serial.print(F("\t"));
Serial.print(val.NumPM10);
Serial.print(F("\t"));
Serial.print(val.PartSize);
Serial.print(F("\n"));
}
/**
* @brief : continued loop after fatal error
* @param mess : message to display
* @param r : error code
*
* if r is zero, it will only display the message
*/
void Errorloop(char *mess, uint8_t r)
{
if (r) ErrtoMess(mess, r);
else Serial.println(mess);
Serial.println(F("Program on hold"));
for(;;) delay(100000);
}
/**
* @brief : display error message
* @param mess : message to display
* @param r : error code
*
*/
void ErrtoMess(char *mess, uint8_t r)
{
char buf[80];
Serial.print(mess);
sps30.GetErrDescription(r, buf, 80);
Serial.println(buf);
}
/**
* serialTrigger prints repeated message, then waits for enter
* to come in from the serial port.
*/
void serialTrigger(char * mess)
{
Serial.println();
while (!Serial.available())
{
Serial.println(mess);
delay(2000);
}
while (Serial.available())
Serial.read();
}