我正在使用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;
}
OP 现在肯定已经解决了这个问题,但没有提供解决方案。我将解释此代码中的一个明显问题,希望它可以帮助未来的访问者。
这是违规例程的开始:
void getPM() {
delay(400);
int idx = 0;
memset(buf, 0, 24);
while (Serial1.available()) {
buf[idx++] = Serial1.read();
}
...}
在例程getPM()
中,接收到的字符被写入一个24个字符的缓冲区,使用的索引只要Serial1.available((为真,索引就会不断递增。
第一个问题是,如果传感器发送数据的速度足够快,Serial1.available(( 可能永远为真,在这种情况下,该函数将永远不会返回。
更严重的问题是传感器可能会在循环之间发送超过 24 个字符。即使传感器发送数据的速度非常慢,在第一次通过循环之前,也可能有超过 24 个字符到达。如果在调用 getPM(( 之前到达了 25 个或更多字符,则前 24 个字节将写入字符缓冲区,其余字节将写入不属于缓冲区的位置的内存中,这可能会导致程序崩溃。
假设将接收多少数据以及接收速度是一种不好的做法;不应盲目地增加缓冲区索引,而应显式设置上限和下限以匹配缓冲区大小。