Проблемы с последовательным выводом данных на SD-карту PMS5003

serial sd

Я пытаюсь сохранить выходные данные датчика качества воздуха на SD-карту, но у меня возникли некоторые проблемы.

Датчик, который я использую, — это Plantower PMS5003, который выдает данные через последовательный порт. Я значительно переделал пример скетча от Adafruit (здесь), так как я хотел бы использовать библиотеку Low Power для перевода устройства в спящий режим, снятия образца и засечения времени, записи его на SD-карту, а затем возврата в спящий режим.

Я получаю запись временной метки без проблем, но данные выходят повторяющимися, как показано ниже:

22:13:10 - 34,55,61
22:13:18 - 34,55,61
22:13:27 - 34,55,61
22:13:36 - 34,55,61
22:13:44 - 34,55,61
22:13:53 - 34,55,61
22:14:1 - 34,55,61
22:14:10 - 34,55,61
22:14:18 - 34,55,61
22:14:27 - 34,55,61
22:14:35 - 34,55,61
22:14:44 - 34,55,61

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

Я также отказался от использования программного серийного номера, так как не мог заставить его прекратить возиться с библиотекой низкого энергопотребления, он никогда не переходил в спящий режим.

Это и любые общие советы по скетчу приветствуются, но, похоже, это не лучший способ добиться цели.

#include "LowPower.h"
#include <SdFat.h>
#include "RTClib.h"
#include <Wire.h>

SdFat SD;
RTC_DS3231 rtc;

File test;
String tString = "";
String sString = "";

void setup() {
  Serial.begin(9600);

  pinMode(10, OUTPUT);

  if (!rtc.begin()) while (1);

  if (!SD.begin(10)) while (1);
}

struct pms5003data {
  uint16_t framelen;
  uint16_t pm10_standard, pm25_standard, pm100_standard;
  uint16_t pm10_env, pm25_env, pm100_env;
  uint16_t particles_03um, particles_05um, particles_10um, particles_25um, particles_50um, particles_100um;
  uint16_t unused;
  uint16_t checksum;
};

struct pms5003data data;

void loop() {
  //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
  LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);


  DateTime now = rtc.now();
  test = SD.open("test.txt", FILE_WRITE);

  tString = String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second()) + " - ";

  readPMSdata(&Serial);
  sString = String(data.pm10_standard) + "," + String(data.pm25_standard) + "," + String(data.pm100_standard);

if (test) {
  test.print(tString);
  test.println(sString);
  test.close();

  delay(250);
}
}

boolean readPMSdata(Stream *s) {
  if (! s->available()) {
    return false;
  }

  // Read a byte at a time until we get to the special '0x42' start-byte
  if (s->peek() != 0x42) {
    s->read();
    return false;
  }

  // Now read all 32 bytes
  if (s->available() < 32) {
    return false;
  }

  uint8_t buffer[32];
  uint16_t sum = 0;
  s->readBytes(buffer, 32);

  // get checksum ready
  for (uint8_t i = 0; i < 30; i++) {
    sum += buffer[i];
  }

  //debugging
  for (uint8_t i = 2; i < 32; i++) {
    Serial.print("0x"); Serial.print(buffer[i], HEX); Serial.print(", ");
  }
  Serial.println();


  // The data comes in endian'd, this solves it so it works on all platforms
  uint16_t buffer_u16[15];
  for (uint8_t i = 0; i < 15; i++) {
    buffer_u16[i] = buffer[2 + i * 2 + 1];
    buffer_u16[i] += (buffer[2 + i * 2] << 8);
  }

  // put it into a nice struct :)
  memcpy((void *)&data, (void *)buffer_u16, 30);

  if (sum != data.checksum) {
    Serial.println("Checksum failure");
    return false;
  }
  // success!
  return true;
}

, 👍1

Обсуждение

добавьте последовательный вывод к вопросу. Вы уверены, что 3 регистрируемых вами значения изменяются?, @Juraj

Да, они могут довольно сильно измениться в базовом примере эскиза, если я добавлю дым в датчик., @B Wulf

Вы не используете возвращаемое значение для проверки корректности данных. Если функция readPMSdata возвращает false, напишите сообщение об ошибке вместо неверных данных. Обычный способ — сохранять каждый полученный байт в буфере вместо ожидания 32 байтов. Для 9600 бод с arduino uno вы можете попробовать AltSoftSerial. Какую плату arduino вы используете?, @Jot

Видите ли, я размышлял о буфере, и я припоминаю, что на форуме Arduino в одном из длинных руководств предлагался совершенно другой метод для последовательных данных. Сейчас использую обычный 328 arduino, скорее всего, останусь на 328, если уберу это с arduino. Однако есть несколько альтернатив., @B Wulf

AltSoftSerial также был послан богом, он, по крайней мере, позволяет библиотеке LowPower функционировать так, как нужно в течение этого периода отладки., @B Wulf


1 ответ


Лучший ответ:

1

В коде есть несоответствие.
Функция Arduino loop предполагает, что readPMSdata необходимо вызвать только один раз для получения допустимых данных, но сама функция readPMSdata предполагает, что она будет вызываться снова и снова, пока не будет получено достаточное количество байтов, которые станут допустимыми.

Решение состоит в том, чтобы позволить Arduino loop вызывать readPMSdata снова и снова, пока не получит действительные данные. Он возвращает true, когда имеет действительные данные, так что это можно использовать в операторе if.
Это может вызвать проблемы с остальной частью кода в цикле. delay(250); может больше не использоваться, и я не знаю лучшего места для перехода в спящий режим. Поэтому я удалил powerdown.

void loop() {
  if( readPMSdata(&Serial))        // вызываем его много раз для сбора данных
  {
    // есть достоверные данные от датчика
    DateTime now = rtc.now();
    test = SD.open("test.txt", FILE_WRITE);

    tString = String(now.hour()) + ":" + String(now.minute()) + ":" + String(now.second()) + " - ";

    sString = String(data.pm10_standard) + "," + String(data.pm25_standard) + "," + String(data.pm100_standard);

    if (test) {
      test.print(tString);
      test.println(sString);
      test.close();
  }
}
,

Это я вообще не рассматривал! Я люблю Adafruit за то, что они делают, но в долгосрочной перспективе они оставляют много необъясненного. Но как только я прочитал пост, мой разум начал подбирать более подходящие способы сделать это. Спасибо!, @B Wulf

Итак, у меня возникли некоторые трудности с пониманием этого вопроса. Как бы вы порекомендовали зацикливать readPMSdata, пока я не получу данные?, @B Wulf

@BWulf, я добавил это в свой ответ. Чтение последовательного ввода и засыпание не очень хорошо сочетаются., @Jot