Проблема с подключением датчика сердцебиения и температуры на arduino uno, но работает по отдельности
Таким образом, код работает отдельно для датчика температуры и датчика сердцебиения, но когда я объединяю его вместе, он показывает неверные значения. для датчика температуры
Я использую датчик температуры lm35 и питание от USB
#include <LiquidCrystal.h>
const int rs = 13, en = 12, d4 = 11, d5 = 10, d6 = 9, d7 = 8;
LiquidCrystal lcd(13, 12, 11, 10, 9, 8);
int pulsePin = A0;
int blinkPin = 5; // вывод для мигания светодиода при каждом ударе
// Изменчивые переменные, используемые в подпрограмме обслуживания прерывания!
volatile int BPM ; // целое число, которое содержит необработанный аналог в 0. обновляется каждые 2 мс
volatile int Signal; // содержит входящие необработанные данные
volatile int IBI = 600; // int, который содержит временной интервал между ударами! Сеять надо!
volatile boolean Pulse = false; // «Истинно», когда обнаружено живое сердцебиение пользователя. «Ложь», если это не «живой бит».
volatile boolean QS = false; // становится истинным, когда Arduoino находит бит.
static boolean serialVisual = true; // По умолчанию установлено значение «false». Снова установите значение «true», чтобы увидеть визуальный пульс Arduino Serial Monitor ASCII.
volatile int rate[10]; // массив для хранения последних десяти значений IBI
volatile unsigned long sampleCounter = 0; // используется для определения времени импульса
volatile unsigned long lastBeatTime = 0; // используется для поиска IBI
volatile int P = 512; // используется для нахождения пика в пульсовой волне, с затравкой
volatile int T = 512; // используется для поиска впадины в пульсовой волне, засеянной
volatile int thresh = 525; // используется для нахождения мгновенного момента сердцебиения, засеяно
volatile int amp = 100; // используется для хранения амплитуды импульсного сигнала, заполненного
volatile boolean firstBeat = true; // используется для заполнения массива скорости, поэтому мы запускаем с разумным BPM
volatile boolean secondBeat = false; // используется для заполнения массива скорости, поэтому мы запускаем с разумным BPM
const int sensor = A5;
int tempc ;
float vout;
void setup()
{
vout =analogRead(sensor);
pinMode(sensor,INPUT);
pinMode(blinkPin,OUTPUT); // вывод, который будет мигать в такт вашему сердцу!
Serial.begin(9600); // мы согласны говорить быстро!
interruptSetup(); // настраивает чтение сигнала датчика пульса каждые 2 мс
// ЕСЛИ ВЫ ПОДАЕТЕ ПИТАНИЕ НА ДАТЧИК ИМПУЛЬСОВ ПРИ НАПРЯЖЕНИИ МЕНЬШЕ, ЧЕМ НАПРЯЖЕНИЕ НА ПЛАТЕ,
// ОТКОММЕНТИРУЕМ СЛЕДУЮЩУЮ СТРОКУ И ПОДАЕМ ЭТО НАПРЯЖЕНИЕ НА ВЫВОД A-REF
// аналоговая ссылка (ВНЕШНЯЯ);
lcd.begin(16, 4);
lcd.clear();
}
// Где происходят чудеса
void loop()
{
lcd.setCursor(0,0);
lcd.print(" T in c = ");
lcd.print(tempc);
lcd.setCursor(0,1);
delay(100);
serialOutput();
if (QS == true) // Было найдено сердцебиение
{
// BPM и IBI определены
// Quantified Self "QS" true, когда arduino находит сердцебиение
serialOutputWhenBeatHappens(); // Случился бит, выведите его в последовательный порт.
QS = false; // сброс флага Quantified Self для следующего раза
tempc=(vout*500)/1023;
}
delay(20); // сделать перерыв
}
void interruptSetup()
{
// Инициализирует Timer2 для создания прерывания каждые 2 мс.
TCCR2A = 0x02; // ОТКЛЮЧИТЬ ШИМ НА ЦИФРОВЫХ ВЫВОДАХ 3 И 11 И ПЕРЕХОДИТЬ В РЕЖИМ CTC
TCCR2B = 0x06; // НЕ ПРИНИМАТЬ СРАВНЕНИЕ, 256 PRESCALER
OCR2A = 0X7C; // УСТАНОВИМ НАЧАЛЬНУЮ ЧАСТЬ СЧЁТЧИКА НА 124 ДЛЯ ЧАСТОТЫ ВЫБОРКИ 500 Гц
TIMSK2 = 0x02; // ВКЛЮЧАЕМ ПРЕРЫВАНИЕ ПРИ СОВПАДЕНИИ МЕЖДУ TIMER2 И OCR2A
sei(); // УБЕДИТЕСЬ, ЧТО ГЛОБАЛЬНЫЕ ПРЕРЫВАНИЯ РАЗРЕШЕНЫ
}
void serialOutput()
{ // Решаем, как выводить серийный номер.
if (serialVisual == true)
{
arduinoSerialMonitorVisual('-', Signal); // переходит к функции, которая делает Serial Monitor Visualizer
}
else
{
sendDataToSerial('S', Signal); // переходит к функции sendDataToSerial
}
}
void serialOutputWhenBeatHappens()
{
if (serialVisual == true) // Код для работы визуализатора последовательного монитора
{
int din ;
din = analogRead(4);
Serial.print(BPM);
Serial.print(", ");
Serial.print(tempc);
Serial.print(", ");
Serial.print(din);
Serial.print(", ");
Serial.println();
lcd.setCursor(0,2);
lcd.print("Heart-Beat Found ");
lcd.setCursor(6,3);
lcd.print(BPM);
delay(100);
lcd.clear();
}
else
{
sendDataToSerial('B',BPM); // отправляем частоту сердечных сокращений с префиксом «B»
sendDataToSerial('Q',IBI); // отправляем время между ударами с префиксом 'Q'
}
}
void arduinoSerialMonitorVisual(char symbol, int data )
{
const int sensorMin = 0; // минимум сенсора, обнаруженный экспериментально
const int sensorMax = 1024; // максимум сенсора, обнаруженный экспериментально
int sensorReading = data; // сопоставляем диапазон датчика с диапазоном из 12 вариантов:
int range = map(sensorReading, sensorMin, sensorMax, 0, 11);
// делаем что-то другое в зависимости от
// значение диапазона:
}
void sendDataToSerial(char symbol, int data )
{
Serial.print(symbol);
Serial.println(data);
}
ISR(TIMER2_COMPA_vect) // срабатывает, когда Timer2 считает до 124
{
cli(); // отключаем прерывания, пока делаем это
Signal = analogRead(pulsePin); // чтение датчика пульса
sampleCounter += 2; // отслеживать время в миллисекундах с помощью этой переменной
int N = sampleCounter - lastBeatTime; // отслеживаем время с момента последнего удара, чтобы избежать шума
// найти пик и впадину пульсовой волны
if(Signal < thresh && N > (IBI/5)*3) // избегаем дихротического шума, ожидая 3/5 последнего IBI
{
if (Signal < T) // T - впадина
{
T = Signal; // отслеживаем самую низкую точку пульсовой волны
}
}
if(Signal > thresh && Signal > P)
{ // условие thresh помогает избежать шума
P = Signal; // P — пик
} // отслеживаем наивысшую точку пульсовой волны
// ТЕПЕРЬ ПРИШЛО ВРЕМЯ УЗНАТЬ БИЕНИЕ СЕРДЦА
// значение сигнала возрастает каждый раз, когда есть импульс
if (N > 250)
{ // избегаем высокочастотного шума
if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) )
{
Pulse = true; // устанавливаем флаг Pulse, когда мы думаем, что есть пульс
digitalWrite(blinkPin,HIGH); // включить светодиод на контакте 13
IBI = sampleCounter - lastBeatTime; // измеряем время между ударами в мс
lastBeatTime = sampleCounter; // отслеживать время следующего импульса
if(secondBeat)
{ // если это вторая доля, if secondBeat == TRUE
secondBeat = false; // очистить флаг secondBeat
for(int i=0; i<=9; i++) // начальное значение промежуточной суммы для получения реалистичного BPM при запуске
{
rate[i] = IBI;
}
}
if(firstBeat) // если бит найден впервые, if firstBeat == TRUE
{
firstBeat = false; // очистить флаг firstBeat
secondBeat = true; // устанавливаем второй флаг долей
sei(); // снова разрешаем прерывания
return; // Значение IBI ненадежно, поэтому отбрасываем его
}
// сохраняем промежуточный итог последних 10 значений IBI
word runningTotal = 0; // очищаем переменную runningTotal
for(int i=0; i<=8; i++)
{ // сдвигаем данные в массиве ставок
rate[i] = rate[i+1]; // и удалить самое старое значение IBI
runningTotal += rate[i]; // суммируем 9 самых старых значений IBI
}
rate[9] = IBI; // добавляем последний IBI в массив ставок
runningTotal += rate[9]; // добавить последний IBI в runningTotal
runningTotal /= 10; // среднее значение последних 10 значений IBI
BPM = 60000/runningTotal; // сколько ударов может уместиться в минуту? это БПМ!
QS = true; // установить флаг Quantified Self
// ФЛАГ QS НЕ СНЯТ ВНУТРИ ЭТОЙ ISR
}
}
if (Signal < thresh && Pulse == true)
{ // когда значения уменьшаются, бит закончен
digitalWrite(blinkPin,LOW); // выключаем светодиод на контакте 13
Pulse = false; // сбрасываем флаг Pulse, чтобы мы могли сделать это снова
amp = P - T; // получаем амплитуду пульсовой волны
thresh = amp/2 + T; // устанавливаем порог на 50% от амплитуды
P = thresh; // сбросить их для следующего раза
T = thresh;
}
if (N > 2500)
{ // если 2,5 секунды проходят без такта
thresh = 512; // устанавливаем порог по умолчанию
P = 512; // установить P по умолчанию
T = 512; // устанавливаем Т по умолчанию
lastBeatTime = sampleCounter; // обновить lastBeatTime
firstBeat = true; // установите их, чтобы избежать шума
secondBeat = false; // когда мы получим сердцебиение
}
sei();
// разрешить прерывания, когда закончите!
}// конец isr
@Aref Ben Lazrek, 👍2
3 ответа
Кажется, в коде отсутствует повторяющееся обновление tempc через
vout =analogRead(sensor);
Альтернативой может быть изменение строки для установки tempc на
tempc=(int) (analogRead(sensor) * 500.0f/1023.0f);
Почему вы вычисляете tempc только после того, как распечатаете значение в serialOutputWhenBeatHappens`? Вы также забыли постоянно считывать температуру.
Я бы сделал это следующим образом:
void loop()
{
// считываем датчик до того, как он будет напечатан на ЖК-дисплее
vout = analogRead( sensor );
// здесь я не уверен, что вы вычисляете
// вы сопоставляете напряжение датчика с диапазоном от 0°C до 500°C
// вы измеряете сердцебиение и температуру дракона? ;-)
// эта формула такая же, как у вас
tempc = ( vout / 1023.0 ) * 500.0;
// Я бы также очистил ЖК-дисплей, прежде чем писать на него
lcd.clear();
lcd.setCursor( 0, 0 );
lcd.print( "T in c = " );
lcd.print( tempc );
lcd.setCursor( 0,1 );
// Здесь нет необходимости замедлять цикл.
// пусть работает...
// delay(100);
serialOutput();
// Было найдено сердцебиение
if (QS == true)
{
// BPM и IBI определены
// Quantified Self "QS" true, когда arduino находит сердцебиение
// Случился бит, выведите его в последовательный порт.
serialOutputWhenBeatHappens();
// сброс флага Quantified Self для следующего раза
QS = false;
}
// сделать перерыв (почему?)
// delay(20);
}
Если предоставленный источник урезан до строк, связанных с температурой, то это именно так. Если вы попробуете этот скетч, он должен вести себя так же "неправильно":
const int sensor = A5;
int tempc;
float vout;
void setup()
{
vout = analogRead(sensor);
pinMode(sensor, INPUT);
}
void loop()
{
lcd.print(tempc);
tempc = (vout * 500) / 1023;
}
В loop()
значение tempc
печатается до его пересчета из vout
.
Но vout
записывается только один раз в setup()
.
Таким образом, "неправильные" значения, о которых вы говорите, всегда являются одним и тем же значением, которое было измерено во время запуска. (Вы не упомянули, что не так.)
Примечание. Как описано в справке по библиотеке Arduino, нет необходимости устанавливать вывод в режим INPUT
, поскольку он используется по умолчанию.
Я прочитал ваш комментарий к ответу @Abel и даже посмотрел ваше видео. Поскольку непонятно, что вы сделали, я ничего не могу сказать, почему у вас сейчас прыгающие значения. (Пожалуйста, напишите немного прозы и покажите новый код в своем вопросе в следующий раз.)
Если мы применим его совет, исходник будет выглядеть так. Я удалил настройку режима вывода (см. выше) и сделал последовательность инструкций в loop()
более логичной.
const int sensor = A5;
int tempc;
float vout;
void setup()
{
}
void loop()
{
vout = analogRead(sensor);
tempc = (vout * 500) / 1023;
lcd.print(tempc);
}
Этот скетч покажет текущую температуру, отслеживая фактическое значение. Если нет, у вас есть другая проблема, независимая от прерывания и датчика сердцебиения.
Дополнительные примечания:
analogRead()
возвращаетint
. Вы присваиваете егоfloat
, так что это неявное преобразование.- Этот
float
удобен, потому что сопоставление выполняется "вручную". Если бы вы использовалиmap()
, это мог бы бытьint
. - Результат выражения сопоставления имеет тип
float
, и вы присваиваете егоint
. Вот еще одно неявное преобразование, которое удаляет дробную часть. Это нормально из-за точности измерения.
Теперь к вашему предположению, что прерывания мешают измерению температуры.
Из источника я не вижу причин. Итак, вы можете продолжить следующим образом:
- Прокомментируйте все, что связано с получением пульса, включая прерывание таймера. Вы можете сделать это с помощью пар
/* … */
или#if 0
-#endif
. Первые не могут быть объединены друг с другом, а для вторых нужны отдельные строки. - Проверьте, правильно ли отслеживается температура.
- Если да, продолжайте.
- Если нет, то ваша схема измерения температуры каким-то образом неисправна. Схема и фотография (отредактируйте свой вопрос для этого) могут быть нам полезны. Не продолжайте, пока проблема не будет решена.
- Таймер и его программа обработки прерываний снова активируются, но ничего не выполняются.
- Проверьте, в норме ли температура.
- Если да, продолжайте.
- Если нет, то пока у меня нет идей.
- Повторно включите получение пульса.
- Проверьте, в норме ли температура.
- Если да, продолжайте.
- Если нет, то пока у меня нет идей.
- Проблема решена.
- Как получить ненулевой выход из HX711 и ячейки загрузки?
- Использовать все контакты как цифровые входы/выходы
- Сколько датчиков может поддерживать один модуль Arduino?
- Что выбрать между датчиками температуры и влажности: AM230x или DHT22?
- BMP280 возвращает 0
- Как контролировать количество void loop в секунду?
- Печатать на serial monitor, когда он обнаруживает изменение значения
- Нужен усилитель сигнала датчика качества воздуха MQ135? Датчик более высокого качества?
спасибо за ваш ответ, я все еще новичок, не могли бы вы объяснить больше?, @Aref Ben Lazrek
Добавьте, что vout = строка между QS = false; и tempc = и дайте мне знать, если проблема не устранена. По сути, вам нужно обновить vout, прежде чем вы вычислите из него tempc. AnalogRead — это однократное чтение на контакте., @Abel
Спасибо, постараюсь держать вас в курсе, @Aref Ben Lazrek
извините за поздний ответ, я попробовал ваши предложения, но, к сожалению, не получил никакого результата. Я думаю, что проблема связана с инструкциями interrupts() или timer2, потому что когда я добавляю cli() (отключить прерывание) перед vout= аналоговое чтение (датчик); температура дает мне правильное значение, но датчик пульса перестает работать. это то, что я сейчас получаю https://www.youtube.com/watch?v=dBPKmYd08U8 спасибо, @Aref Ben Lazrek