Проблемы с последовательным выводом данных на SD-карту PMS5003
Я пытаюсь сохранить выходные данные датчика качества воздуха на 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;
}
@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
- Как разделить входящую строку?
- Как вывести несколько переменных в строке?
- В чем разница между Serial.write и Serial.print? И когда они используются?
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Программы построения последовательных данных
- Как узнать частоту дискретизации?
- Что такое Serial.begin(9600)?
- Очистить существующий массив при получении новой последовательной команды
добавьте последовательный вывод к вопросу. Вы уверены, что 3 регистрируемых вами значения изменяются?, @Juraj
Да, они могут довольно сильно измениться в базовом примере эскиза, если я добавлю дым в датчик., @B Wulf
Вы не используете возвращаемое значение для проверки корректности данных. Если функция readPMSdata возвращает false, напишите сообщение об ошибке вместо неверных данных. Обычный способ — сохранять каждый полученный байт в буфере вместо ожидания 32 байтов. Для 9600 бод с arduino uno вы можете попробовать AltSoftSerial. Какую плату arduino вы используете?, @Jot
Видите ли, я размышлял о буфере, и я припоминаю, что на форуме Arduino в одном из длинных руководств предлагался совершенно другой метод для последовательных данных. Сейчас использую обычный 328 arduino, скорее всего, останусь на 328, если уберу это с arduino. Однако есть несколько альтернатив., @B Wulf
AltSoftSerial также был послан богом, он, по крайней мере, позволяет библиотеке LowPower функционировать так, как нужно в течение этого периода отладки., @B Wulf