1

我正在使用 Teensy 3.2 开发空气质量监测系统。原理很简单。从传感器获取数据,(PM 2.5 ,CO2),将其发送回 Teensy 计算一分钟平均值,记录平均值并将其存储在 microSD 卡上。到目前为止,我分别测试了所有传感器和 Teensy;他们都工作得很好。但是,当我构建主循环时。调用 getPM() 后,循环停止工作。我怀疑这可能是从这些传感器发送回 Teensy 的数据速率与循环的执行速度相比太快了。但我不确定解决方法是什么。

pm 传感器使用 UART,CO2/VOC 使用 I2C 协议

这是我的代码:

#include <SoftwareSerial.h>
//Serial connection for PM / Wifi
#include <SPI.h> 
#include <SD.h> 
#include <stdint.h> 
#include <SD_t3.h>
  // SPI for Micro SD card
#include "RTClib.h"
  // For RTC 
#include <SparkFunBME280.h> 
#include <SparkFunCCS811.h> 
#define CCS811_ADDR 0x5B //Default I2C Address for CCS811 sensor

const int LCDTx = 10;
const int WIFITx = 8;
const int WIFIRx = 7;
const int PMRX = 0;
const int PMTx = 1;
const char * buffer = "teensy.txt";
File myFile;
String writeString;
String year, month, day, second, hour, minute;

//PM Sensors variable
uint16_t pm10 = 0;
uint16_t pm25 = 0;
uint16_t pm100 = 0;
uint16_t tpm10 = 0;
uint16_t tpm25 = 0;
uint16_t tpm100 = 0;

uint16_t tpm10Sum;
uint16_t tpm25Sum;
uint16_t tpm100Sum;
uint16_t pm10Sum;
uint16_t pm25Sum;
uint16_t pm100Sum;
int PMerrors = 0;
uint8_t buf[24];

uint16_t TPM01ValueAvg;
uint16_t TPM2_5ValueAvg;
uint16_t TPM10ValueAvg;
uint16_t PM01ValueAvg;
uint16_t PM2_5ValueAvg;
uint16_t PM10ValueAvg;

//Temperature and Humidity variable

float tempC = 0;
float tempCSum;
float tempCAvg;

float humidity = 0;
float humiditySum;
float humidityAvg;

// CO2 variable 

uint16_t CO2con = 0;
uint16_t CO2conSum;
uint16_t CO2conAvg;

// VOC variable

uint16_t VOCcon = 0;
uint16_t VOCconSum;
uint16_t VOCconAvg;

uint16_t Height = 0;

RTC_DS3231 rtc;
CCS811 myCCS811(CCS811_ADDR);
BME280 myBME280;

SoftwareSerial PMSerial(0, 1); // PM for RX, TX
SoftwareSerial WIFISerial(7, 8); // WIFI for RX , TX
SoftwareSerial LCD = SoftwareSerial(255, LCDTx);

void setup() {
  Serial.begin(9600); // For Serial monitor 
  PMSerial.begin(9600); // For PM sensor
  WIFISerial.begin(9600); // For wifi
  SPI.begin();
  Wire.begin();

  // LCD start up
  pinMode(LCDTx, OUTPUT);
  digitalWrite(LCDTx, HIGH);
  LCD.begin(9600);
  delay(1000);
  LCD.write(17);

  // RTC start up 

  if (!rtc.begin()) {
    Serial.println("Can't fine RTC");
    //Clear screen
    LCD.write(12);
    delay(5);
    //Print out statement, display for 2 seconds
    LCD.print("Can't find RTC");
    LCD.write(13);
    delay(2000);
    //Clear screen
    LCD.write(12);
    while (1);
  } else {
    Serial.println("RTC initialized successfully");
    LCD.write(12);
    delay(5);
    LCD.print("RTC initialized successfully");
    LCD.write(13);
    delay(2000);
    LCD.write(12);
  }
  DateTime now = rtc.now(); // Catch the time on RTC for now
  DateTime PCTime = DateTime(__DATE__, __TIME__); // Catch the time on PC for now

  // If any discrepencies , update with the time on PC 
  // Manually change this code when the timezone is different uncomment the rtc.adjust(DateTime(__DATE__, __TIME__));
  // Upload it again to Arduino and check if the time is correct
  // Comment out rtc.adjust(DateTime(__DATE__, __TIME__)); lastly, Upload the entire code again
  if (now.unixtime() < PCTime.unixtime()) {
    rtc.adjust(DateTime(__DATE__, __TIME__));
  }
  rtc.begin();

  // Set SS / CS pin as 15 , SD.Begin(XX)-> XX is also SS pin number
  pinMode(15, OUTPUT);
  digitalWrite(15, HIGH);

  // SD card reader start up (Due to the bad reader, max clock speed can only be 72 MHz)

  if (SD.begin(15) == false) {
    Serial.println("SD card didn't initialized");
    LCD.write(12);
    delay(5);
    LCD.print("SD card didn't initialized");
    LCD.write(13);
    delay(2000);
    LCD.write(12);
    delay(5);
  } else {
    Serial.println("SD card initialized successfully");
    LCD.write(12);
    delay(5);
    LCD.print("SD card initialized sucessfully");
    LCD.write(13);
    delay(2000);
    LCD.write(12);
    delay(5);
  }

  //This begins the CCS811 sensor and prints error status of .begin()
  CCS811Core::status returnCode = myCCS811.begin();
  Serial.print("CCS811 begin exited with: ");
  //Pass the error code to a function to print the results
  printDriverError(returnCode);
  Serial.println();

  //For I2C, enable the following and disable the SPI section
  myBME280.settings.commInterface = I2C_MODE;
  myBME280.settings.I2CAddress = 0x77;

  //Initialize BME280
  //For I2C, enable the following and disable the SPI section
  myBME280.settings.commInterface = I2C_MODE;
  myBME280.settings.I2CAddress = 0x77;
  myBME280.settings.runMode = 3; //Normal mode
  myBME280.settings.tStandby = 0;
  myBME280.settings.filter = 4;
  myBME280.settings.tempOverSample = 5;
  myBME280.settings.pressOverSample = 5;
  myBME280.settings.humidOverSample = 5;

  //Calling .begin() causes the settings to be loaded
  delay(10); //Make sure sensor had enough time to turn on. BME280 requires 2ms to start up.
  myBME280.begin();

}

//Timer for SD logging
int sampleSize = 0;
elapsedMillis sinceStartup;

void loop() {

  sampleSize++;



  Serial.println("1");
  elapsedMillis SampleTime;
  Serial.println(SampleTime);
 // getTHCV();
 // getPM();




  if (sinceStartup > 60000) {
    Calcavg();
    DateTime now = rtc.now();
    year = String(now.year(), DEC);
    //Convert from Now.year() long to Decimal String object
    month = String(now.month(), DEC);
    day = String(now.day(), DEC);
    hour = String(now.hour(), DEC);
    minute = String(now.minute(), DEC);
    second = String(now.second(), DEC);
    writeString = year + "/" + month + "/" + day + " " + hour + ":" + minute + ":" + second + " ";
    sdLog(buffer, writeString + TPM2_5ValueAvg + " " + PM2_5ValueAvg + " " + humidityAvg + " " + CO2conAvg + " " + VOCconAvg);
    Serial.println(writeString + TPM2_5ValueAvg + " " + PM2_5ValueAvg + " " + humidityAvg + " " + CO2conAvg + " " + VOCconAvg);
    Reset();
  }else{

  Serial.println(CO2con);  
  }

}

void getTHCV() {

  if (myCCS811.dataAvailable()) {
    //Calling this function updates the global tVOC and eCO2 variables
    Serial.println("HelloWorld !");

    myCCS811.readAlgorithmResults();
    //printInfoSerial fetches the values of tVOC and eCO2
    CO2con = myCCS811.getCO2();
    VOCcon = myCCS811.getTVOC();
    tempC = myBME280.readTempC();
    Height = myBME280.readFloatAltitudeMeters();
    humidity = myBME280.readFloatHumidity();

    //This sends the temperature data to the CCS811
    myCCS811.setEnvironmentalData(humidity, tempC);

    //humiditySum += humidity;
    //tempCSum += tempC;
    //CO2conSum += CO2con;
    //VOCconSum += VOCcon;

  } else if (myCCS811.checkForStatusError()) {
    //If the CCS811 found an internal error, print it.
    printSensorError();
      Serial.println("HelloWorld !");

  }

}

void getPM() {
  delay(400);
  int idx = 0;
  memset(buf, 0, 24);
  while (Serial1.available()) {
    buf[idx++] = Serial1.read();
  }

  if (buf[0] == 0x42 && buf[1] == 0x4d) {

    pm25 = (buf[12] << 8) | buf[13];
    pm10 = (buf[10] << 8) | buf[11];
    pm100 = (buf[14] << 8) | buf[15];
    tpm10 = (buf[4] << 8) | buf[5];
    tpm25 = (buf[6] << 8) | buf[7];
    tpm100 = (buf[8] << 8) | buf[9];

    if (checkValue(buf, 24)) {
      tpm10Sum += tpm10;
      tpm25Sum += tpm25;
      tpm100Sum += tpm100;
      pm10Sum += pm10;
      pm25Sum += pm25;
      pm100Sum += pm100;
    }
  }
}

void Calcavg() {

  TPM01ValueAvg = tpm10Sum / sampleSize;
  TPM2_5ValueAvg = tpm25Sum / sampleSize;
  TPM10ValueAvg = tpm100Sum / sampleSize;
  PM01ValueAvg = pm10Sum / sampleSize;
  PM2_5ValueAvg = pm25Sum / sampleSize;
  PM10ValueAvg = pm100Sum / sampleSize;

  CO2conAvg = CO2conSum / sampleSize;
  tempCAvg = tempCSum / sampleSize;
  humidityAvg = humiditySum / sampleSize;
  VOCconAvg = VOCconSum / sampleSize;

}

void Reset() {
  CO2conSum = 0;
  tempCSum = 0;
  humiditySum = 0;

  CO2conAvg = 0;
  tempCAvg = 0;
  humidityAvg = 0;

  sampleSize = 0;

  tpm10Sum = 0;
  tpm25Sum = 0;
  tpm100Sum = 0;
  pm10Sum = 0;
  pm25Sum = 0;
  pm100Sum = 0;

  TPM01ValueAvg = 0;
  TPM2_5ValueAvg = 0;
  TPM10ValueAvg = 0;
  PM01ValueAvg = 0;
  PM2_5ValueAvg = 0;
  PM10ValueAvg = 0;
  sinceStartup = 0;

}

void sdLog(const char * fileName, String stringToWrite) {
  File myFile = SD.open(fileName, FILE_WRITE);
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to ");
    Serial.print(fileName);
    Serial.print("...");
    myFile.println(stringToWrite);
    // close the file:
    myFile.close();
    Serial.println("done.");
    digitalWrite(13, HIGH);
    delay(300);
    digitalWrite(13, LOW);
    delay(300);
  } else {
    // if the file didn't open, print an error:
    Serial.print("error opening ");
    Serial.println(fileName);
  }
}
void printDriverError(CCS811Core::status errorCode) {
  switch (errorCode) {
  case CCS811Core::SENSOR_SUCCESS:
    Serial.print("SUCCESS");
    break;
  case CCS811Core::SENSOR_ID_ERROR:
    Serial.print("ID_ERROR");
    break;
  case CCS811Core::SENSOR_I2C_ERROR:
    Serial.print("I2C_ERROR");
    break;
  case CCS811Core::SENSOR_INTERNAL_ERROR:
    Serial.print("INTERNAL_ERROR");
    break;
  case CCS811Core::SENSOR_GENERIC_ERROR:
    Serial.print("GENERIC_ERROR");
    break;
  default:
    Serial.print("Unspecified error.");
  }
}

void printSensorError() {
  uint8_t error = myCCS811.getErrorRegister();

  if (error == 0xFF) //comm error
  {
    Serial.println("Failed to get ERROR_ID register.");
  } else {
    Serial.print("Error: ");
    if (error & 1 << 5) Serial.print("HeaterSupply");
    if (error & 1 << 4) Serial.print("HeaterFault");
    if (error & 1 << 3) Serial.print("MaxResistance");
    if (error & 1 << 2) Serial.print("MeasModeInvalid");
    if (error & 1 << 1) Serial.print("ReadRegInvalid");
    if (error & 1 << 0) Serial.print("MsgInvalid");
    Serial.println();
  }
}

// ***Checksum for PM values ***//
int checkValue(uint8_t thebuf[24], int leng) {
  char receiveflag = 0;
  int receiveSum = 0;
  int i = 0;

  for (i = 0; i < leng; i++) {
    receiveSum = receiveSum + thebuf[i];
  }

  if (receiveSum == ((thebuf[leng - 2] << 8) + thebuf[leng - 1] + thebuf[leng - 2] + thebuf[leng - 1])) //checksum the serial data
  {
    receiveSum = 0;
    receiveflag = 1;
  }
  return receiveflag;
}        
4

1 回答 1

0

OP现在肯定已经解决了这个问题,但没有提供解决方案。我将解释这段代码中的一个明显问题,希望它可以帮助未来的访问者。

这是违规程序的开始:

void getPM() {
  delay(400);
  int idx = 0;
  memset(buf, 0, 24);
  while (Serial1.available()) {
    buf[idx++] = Serial1.read();
  }
  ...}

在常规getPM()中,只要 Serial1.available() 为真,接收到的字符将使用索引写入 24 个字符的缓冲区,该索引会保持递增。

第一个问题是,如果传感器发送数据的速度足够快,则 Serial1.available() 可能永远为真,在这种情况下,函数将永远不会返回。

更严重的问题是传感器可能在循环之间发送超过 24 个字符。即使传感器发送数据非常缓慢,在第一次通过循环之前可能已经到达超过 24 个字符。如果在调用 getPM() 之前已经到达 25 个或更多字符,则前 24 个字节将被写入字符缓冲区,其余字节将被写入不属于缓冲区的位置的内存中,可能会导致程序崩溃.

对将接收到多少数据以及多快进行假设是不好的做法;与其盲目地增加缓冲区索引,不如明确设置上限和下限以匹配缓冲区大小。

于 2019-01-17T18:22:11.967 回答