Суммировать значения, сохранить их и распечатать последнее значение
У меня есть массовый расходомер, подключенный к плате Arduino Uno. Мне удалось прочитать объемный расход в л/час, но теперь я изо всех сил пытаюсь реализовать код, который сохраняет значения, чтобы я мог видеть количество использованных литров.
Например, я вижу на своем мониторе это:
0 л/час
0 л/час
И если у меня течет вода,
10 л/час
20 л/час
20 л/час
Затем, когда я останавливаю поток, он возвращается к 0.
0 л/час
Есть ли какие-либо способы суммировать временные интервалы, получить общее количество литров и сохранить его в каждом экземпляре?
Математически это не так сложно (объем = объемный расход в л/час * время), но я не знаю, как это реализовать. Может кто-нибудь помочь с этим? Это может быть в секундах или мс или что-то еще.
Пол
@Physther, 👍2
3 ответа
Это будет полезно.
показания — это показания вашего расходомера в л/час
float reading;//значение расходомера
long startTime;
float total=0;
void setup() {
Serial.begin(9600);
startTime=millis();
}
void loop() {
// читать ваш расходомер для чтения переменной.
total+=reading*float((millis()-startTime))/3600000.0;
Serial.println(total);
}
Что вам нужно сделать, так это интегрировать кривую зависимости скорости потока от времени. Вы должны следить за текущей скоростью потока. Также нужно выбрать какую-то единицу измерения времени; это зависит от того, как часто вы замеряете скорость потока. Например, если вы отбираете пробы каждую секунду, вам следует преобразовать скорость потока л/ч в л/с. Это означает, что скорость потока 10 л/ч эквивалентна 10 л/3600 с = 0,002778 л/с. Итак, если вы получаете показание 10 л/ч в течение 10 секунд, 0,002778 л/с * 10 с = 0,02778 л
воды прошло через ваш клапан или что-то еще. Накопительная сумма – то, что вам нужно. Вы можете попробовать что-то вроде этого:
#define INTERVAL 1000 // интервал выборки в миллисекундах
#define HR_TO_SEC 2.778E-4 // количество часов в секунду
int curr_rate = 0; // текущий расход в л/час
unsigned long lastRead = 0; // когда в последний раз производилась выборка скорости
float totalVolume = 0;
void loop(){
if (millis() - lastRead >= INTERVAL){
lastRead += INTERVAL;
curr_rate = getFlowRate(); // или любую другую функцию, которую вы вызываете; следует взять << 1 сек
totalVolume += curr_rate * HR_TO_SEC; // обновить объем
Serial.println(totalVolume, 4);
}
}
Этот прямоугольный метод достаточно точен, в зависимости от вашего приложения. Если вам нужно более точное приближение, погуглите "трапециевидное численное интегрирование" или "метод Симпсона", если вам нужно еще более точное приближение.
Методы интегрирования более высокого порядка обеспечивают лучшую точность за счет использования _гладкости_ подынтегральной функции. Здесь, поскольку есть вентиль, поток вряд ли будет плавным. Если вам нужна более высокая точность, вам нужно делать выборку с более короткими интервалами., @Edgar Bonet
Кроме того, я заменил lastRead = millis();
на lastRead += INTERVAL;
. В противном случае у вас будет систематическая погрешность, так как средний интервал выборки будет больше, чем «ИНТЕРВАЛ»., @Edgar Bonet
Спасибо вам обоим за помощь. Мне удалось реализовать этот код, и он действительно хранит данные. Я залил через счетчик 1 л воды, а на мониторе 1,2 л. Разница невелика, но я изменю метод интегрирования, и посмотрим, смогу ли я это исправить. Также мне пришлось удалить «unsigned» для «float totalVolume = 0;». В противном случае код выдаст ошибку., @Physther
@Paul Вы можете сделать, как говорит Эдгар Боне, и уменьшить интервал, возможно, до 200 мс. Также не забудьте изменить HR_TO_SEC, чтобы отразить это: конвертируйте интервал в часы. Если это решит вашу проблему, подумайте о том, чтобы принять это., @SoreDakeNoKoto
Хорошо. В эти дни я проведу несколько экспериментов и посмотрю, что из этого получится. Я отпишусь! Спасибо, @Physther
В недавнем комментарии вы разместили ссылку на тему форума с код для считывания датчика. Вы должны были предоставить эту информацию заранее, потому что это полностью меняет проблему. Похоже, что расходомер посылает импульсы с частотой, пропорциональной объемному расходу, и код использует прерывание для подсчета импульсов в течение заданного время.
Ответ TisteAndii предлагает вам интегрировать показания расхода, чтобы получить объем. И это предложение имеет смысл, учитывая ограниченность информация, которой он располагал на тот момент. Теперь, когда мы знаем, как течет счетчик прочитан, смысла больше нет:
- подпрограмма обслуживания прерывания подсчитывает количество импульсов, пропорциональна общему объему, прошедшему через счетчик
- связанный код дифференцирует это чтение, чтобы получить поток
- теперь вы интегрируете этот поток, чтобы получить общий объем.
Вместо того чтобы выполнять интеграцию поверх дифференциации, вы можете просто преобразуйте необработанное чтение в объем. Тогда вам не придется беспокоиться о любых приближениях, сделанных при дифференцировании или интеграция.
В ветке форума указано:
Частота импульсов (Гц) в горизонтальном тесте = 7,5Q, Q – скорость потока в л/мин.
Записав это в более точной математической форме (т. е. модульная правильность) дает:
f/Гц = 7,5 Ом/(л/мин)
где f — частота, а Q — объемный расход.
Учитывая, что 7,5 Гц⋅мин = 450 импульсов, приведенное выше уравнение можно переписать как
f = 450 импульсов/л × Q
или, если говорить об интегрированных количествах:
объем = (количество импульсов) / (450 импульсов/л)
Вот пример кода, который просто преобразует количество импульсов в объем:
// Постоянная калибровки расходомера.
const float calibration = 1/450.0; // 450 импульсов на литр
// Как часто печатать измеренный объем.
const uint32_t print_interval = 1000; // раз в секунду
// Количество подсчитанных импульсов.
volatile uint32_t pulse_count;
// Считаем импульсы внутри ISR.
void count_pulse() { pulse_count++; }
// Возвращаем показания объема, опционально сбрасывая счетчик на ноль.
float totalVolume(bool reset = false)
{
noInterrupts();
uint32_t pulse_count_copy = pulse_count;
if (reset) pulse_count = 0;
interrupts();
return pulse_count_copy * calibration;
}
void setup()
{
pinMode(2, INPUT_PULLUP);
attachInterrupt(0, count_pulse, RISING);
Serial.begin(9600);
}
void loop()
{
static uint32_t last_print;
if (millis() - last_print >= print_interval) {
last_print += print_interval;
Serial.println(totalVolume());
}
}
Обратите внимание, что вы можете время от времени сбрасывать счетчик. я добавил
необязательный параметр для totalVolume()
для этой цели. Если ты никогда
сбросить счетчик, он переполнится примерно через
9544 м3.
- количество акселерметоров снижается после 1 пика
- Как увеличить скорость записи на SD-карту в Ардуино
- Существуют ли библиотеки сглаживания сигналов для Arduino?
- Подключить генератор функций к Arduino
- Онлайн-проблема подключения Arduino к базе данных MySQL
- Файловая система внешней флэш-памяти
- Перекрестная корреляция Arduino?
- ESP32 сохранение данных на SD-карту в формате .csv не форматируется правильно
Большое спасибо! Я пробовал с этим кодом, и он продолжает давать мне 0. Кроме того, мне нужно было добавить long startTime=millis(); вместо того, чтобы просто объявлять long startTime и затем вызывать его. Ответственна ли функция «чтения» за то, что я получаю 0? Это должно быть связано с сигналом или пин-режимом?, @Physther
чтение - это переменная. Вы должны передать показания расходомера в эту переменную как тип с плавающей запятой. вы должны ввести его перед функцией Serial.println(), как я прокомментировал. если вы можете дать предыдущий код, я смогу дать полный код., @user_fs10
Код можно найти здесь: http://forum.arduino.cc/index.php?topic=8548.0 (это слишком долго, чтобы публиковать его здесь). Спасибо!, @Physther