Основной цикл Arduino / Teensy не выполняется после вызова другой void-функции - PullRequest
0 голосов
/ 04 июля 2018

Я использую 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;
}        

1 Ответ

0 голосов
/ 17 января 2019

ОП, безусловно, уже решил эту проблему, но не предоставил решения. Я объясню одну очевидную проблему в этом коде в надежде, что он может помочь будущему посетителю.

Вот начало обидной рутины:

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

В процедуре getPM() полученные символы записываются в 24-символьный буфер с использованием индекса, который продолжает увеличиваться до тех пор, пока Serial1.available () имеет значение true.

Первая проблема заключается в том, что если датчик отправляет данные достаточно быстро, Serial1.available () может быть истинным всегда, и в этом случае функция никогда не вернется.

Более серьезная проблема заключается в том, что датчик может отправлять более 24 символов между циклами. Даже если датчик отправляет данные очень медленно, до первого прохождения цикла может появиться более 24 символов. Если до вызова getPM () поступило 25 или более символов, первые 24 байта будут записаны в буфер символов, а оставшиеся байты будут записаны в память в местах, которые не принадлежат этому буферу, что может привести к сбою программы. ,

Недопустимо делать предположения о том, сколько данных будет получено и как быстро; вместо слепого увеличения индекса буфера, верхний и нижний пределы должны быть установлены явно, чтобы соответствовать размеру буфера.

...