max30100, китайский модуль: настроить, чтобы всё работало

runaway
Offline
Зарегистрирован: 25.09.2012

Не реагирует SpO2 ни на дыхание чистым О2, ни на дыхание в кулёк. Дышал примерно по минуте того и другого. Кислородом - из баллона, десять полных вдохов. В кулёк - тоже минуту, но частота вдохов там непроизвольно повышалась; терпел, пока не начало конкретно торкать в ножки. Бррр... Неприятная процедура... Следил за числовыми показаниями, что в окне монитора (открывается нажатием на кнопку с изображением компьютерного монитора, "Opens Serial Terminal"). Если какие-то изменения и были, то в пределах нескольких десятых долей процента. Всё пляшет вокруг 94%, +/- несколько десяток. Магическое число... Не думаю, что это нормально. Надо поиграться с токами светодиодов, я понимаю.

runaway
Offline
Зарегистрирован: 25.09.2012

Пульс обнаруживается только на пальцах. С соблюдением вышеописанных нюансов. Пульс в запястьях и в мочках ушей этот экземпляр не видит.

runaway
Offline
Зарегистрирован: 25.09.2012

Шумный второй китаец (виновник не чип, а что-то в обвязке):

https://mega.nz/file/pIljCIgT#XUlxuYzFnDfnp4gCbEqaDdheF6oVpo3frlgQvs4pUD8

runaway
Offline
Зарегистрирован: 25.09.2012

Переставил чип с шумной платы на исправную (китайскую). Общее впечатление - чувствительность хуже, устойчивую детекцию пульса поймать труднее. Сатурация на дыхание кислородом не реагирует, как и в первом случае. Детекция постоянно сбивается; тыкался всеми пальцами, какие есть в наличии, по-всякому.

Типичная картинка (но в данный момент пульс детектируется, а обычно розовые пики находятся "под водой" - под ровной синей линией, в голубой заливке):

https://mega.nz/file/cB13SYLT#iEzetDeu438v0PBqRQ2nyZIwYC_gCF76kV7ZGtzW6xs

Теперь попробую посадить этот чип на МОЮ плату, где сейчас сидит первый китайский чип...

зы: То ли чип прогрелся, пока писал это сообщение, то ли фаза Луны поменялась - сейчас уже работа второго экземпляра стала очень похожа на работу первого. Но всё равно, как-то медленнннннее он выходит на детекцию пульса, адаптируется гораздо дольше, чем первый... Если не торопиться и подержать палец, то примерно секунд через сорок "подводные пики" "выходят на поверхность", и стартует детекция сердечного ритма (картинка выше)... А вот сатурация всё равно так и не работает. Не думаю, что стОит пересаживать его на мою плату, ибо тут что в лоб, что по лбу...

Снимаю первого китайца с моей платы, сажу туда европейца!

runaway
Offline
Зарегистрирован: 25.09.2012

Европеец:

https://mega.nz/file/RNsBAQBL#OfLpe790isVZQ-AGW9qqvC_NAUVlw5GJ-an6I0AJ5Yo

https://mega.nz/file/wNlF0QiJ#GIi2TgSZmNyrKbaru8pk8phAWL6m4iujvwVeVokdOcU

https://mega.nz/file/Yc1FAYZb#SV7Vf06FQhe1EPuqdthckQUNOGOc8yFxNerc-dcVur4

https://mega.nz/file/wMtlzIwL#cAkR-bHToLOvyiDpgMUGWaDCAOZ8R6nzR6qRriYSDPk

ПОКА каких-то особых отличий - ни в графиках, ни во впечатлениях от работы - НЕ наблюдаю. Кроме того, что на графике Р1 кривые вроде как полностью сместились в положительную область, и через ноль не переходят. Размах сигнала в районе 200 .. 250 ед. - примерно то же, что и было у второго китайца... Хотя, впрочем, побольше, даже раза в полтора-два... Но 600 единиц, как с утра, с первым, пока добиться ни разу не удалось.

ДА, а направленные вниз зубцы синей линии (перед встречей с розовыми пиками), сейчас в несколько раз "глубже", чем было у китайцев. Это то, что бросилось в глаза...

Кислород пока закончился, реакцию показаний сатурации не тестировал. 

nik182
Offline
Зарегистрирован: 04.05.2015

Сатурация быстро набирается простым упражнением - набираем полную грудь воздуха и пытаемся выдохнуть зажав нос и закрыв рот. Усилие прикладывать максимально возможное на 5 -10 секунд, тем самым создавая избыточное давление в лёгких и активно насыщая кровь кислородом. Через несколько вдохов - давление - выдохов начинает кружиться голова - признак высокой сатурации. Мне достаточно 5-7, что бы сатурация с 94 поднялась до 97 минуты за 2. Потом примерно за те же 2 - 3 минуты возвращается к значению 94. Бонусом падает пульс на 10 единиц.       

sadman41
Offline
Зарегистрирован: 19.10.2016

А посмотрите на "заводской Китай". Там съёмник изготовлен так, что палец все время одинаково ложится на мыргалку. Отсюда и повторяемость результатов , а как следствие - пересчет raw в human-readable.

runaway
Offline
Зарегистрирован: 25.09.2012

На выходных уделяю внимание семейным заботам, всё по теме осталось на работе. Продолжим с понедельника. За упражнение, повышающее сатурацию - спасибо, попробуем!

Насчёт заводских китайцев ("напалечных прищепок", надо полагать?) - не имею возможности их препарировать. Здесь покупать под разборку - дорого, а с Али что-то происходит нехорошее, связанное со всемирным фестивалем, думаю. Несколько заказов не пришли, пришлось просить продавцов продлить срок защиты; один уже просрочен оказался - возврата денег тоже нет уже недели две... Такого раньше не было. Как бы не загнулась лавка. Было бы весьма печально, как для голожопых самодельщиков...

runaway
Offline
Зарегистрирован: 25.09.2012

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

Поигрался с настройками:

ток красного светодиода (авторское значение = 27,1 мА);

"магическое число" (65000);

порог срабатывания детектора пульса (100).

По сути вопроса - безуспешно. На изменения настроек система реагирует явным образом (яркость свечения красного; изменения в графике Р0 с изменении "магического числа"; увеличение числа ложных срабатываний детектора пульса с понижением порога срабатывания). Всё якобы работает, но только не так, как это нужно мне. На изменения реагирует только пульс - вне зависимости от комбинаций настроек: во время компрессии воздуха в груди довольно резво повышается на пару десятков ударов, а после выдоха - проседает примерно на ту же величину. Потом восстанавливается. Сатурация на всё это совершенно не реагирует...

Насчёт тока красного светодиода - в настройках, очевидно, имеется в виду НАЧАЛЬНЫЙ ток. Который после прикладывания пальца ступенчато регулируется автоматически. Т.е. какое стартовое значение тока ни выставляй, он либо повысится до некой нормы, нужной системе, либо понизится - до неё же. Просто на автоподстройку (на ступенчатый перебор значений токов) уходит время, и чтобы его сэкономить, пользователю предлагается вручную установить значение, наиболее близкое к тому, которое обычно выбирает система, применительно к данному экземпляру чипа. Тогда пульс начинает детектироваться практически сразу после прикладывания пальца, без автоперебора токов красного светодиода (что легко заметить визуально).

Вообще как-то всё печально. Бьюсь, как рыба об лёд... Смущает несоответствие авторской картинки P0 с тем, что есть у меня. Такое впечатление, что на момент написания статьи, в монитор выводились другие значения для этого графика - а именно БЕЗ отсечки постоянной составляющей. Была надежда, что всё дело именно в этом, но поскольку график Р0 участника nik182 имеет вид практически идентичный с моим, и при этом сатурация адекватно реагирует на физические упражнения, то график Р0 тут ни при чём... 

Куда копать далее - понятия не имею. Вышел на упор.

nik182
Offline
Зарегистрирован: 04.05.2015

На счёт автоматики уровня свечения диодов я не уверен. Посмотрите предпоследнюю картинку в #35. Я вывел сырые данные, без обработки. Средний уровень данных АЦП по RED 24500 из 65000, по IR 28700. Эти уровни зависит только от того, как приложил палец. Если постоянно удерживать палец в одном положении, то они ни как не меняются. Это значит, что никакой подстройки нет. Кроме того, в блоге около  картинки №12 прямо говориться, что надо самому  изменить ток диодов так, что бы отклик АЦП был примерно одинаков для обоих каналов и был меньше 65000 с помощью balanceIntesities. 

Копать дальше в сторону цифровой обработки сигналов. Для получения адекватного значения сатурации надо аккуратно рассчитать RMS флуктуаций двух сигналов на частоте (!) сердцебиения. Любые другие колебания - от наводок сети 50 Гц, от тремора пальца - у меня его видно не вооружённым глазом на кривой пульса с частотой 10-15 Гц, и прочие дают большую погрешность и за ними можно не увидеть необходимые изменения, тем более, что изменения эти до 3% и меньше. Здесь не плохо было бы набирать статистику для уменьшения ошибки.      

P0 в блоге только на картинках 4,5,7 они соответствуют Вашим и моим картинкам. Размах колебаний меньше 1000. 

runaway
Offline
Зарегистрирован: 25.09.2012

По автоподстройке - выставьте в .h начальный ток красного светодиода 4,4 мА. Сохраните, прошейте, включите, дождитесь загрузки и включения красного светодиода. ВЫключите свет, и положите палец на сенсор - чтобы ваш светящийся ноготь был вам хорошо виден. В таких условиях я без труда, чётко и ясно, вижу ступенчатое увеличение яркости красного светодиода до некоторого среднего значения. В конце такого цикла автоподстройки, одна ступень яркости иногда  вроде бы отыгрывает назад, на понижение (процесс как бы совершает колебание вокруг целевого значения), после чего, как правило, происходит устойчивый "захват" пульса детектором, что динамически иллюстрирует график P1. Всего визуально различимо прохождение около пяти градаций яркости, каждая по полсекунды. Кстати, эти полсекунды тоже можно регулировать в .h - у меня, например, и стоит 500 мс, по умолчанию.

Насчёт остального - какие конкретные практические шаги вы порекомендуете? Чтобы крутить параметры более-менее осмысленно? Та модифицированная вами библиотека, которую вы предлагали выше - выводит ли она на P0 сырые данные с неотфильтрованной постоянной составляющей? Как можно практически рассчитать RMS кардиограммы? И что потом с этими расчётами делать, куда конкретно вставлять их результаты?

Если идти по тексту блога, то я имею в виду Р0 (IR LED RAW), изображённый на картинках 9, 10, 11, 12 и, наконец, 14. Я не знаю точно, куда вы смотрите, но на всех этих графиках я вижу вертикальную ось с отметками чуть ниже 1 000 000, напротив которых и расположены обе кривых (ИК и К каналы): https://morf.lv/implementing-pulse-oximeter-using-max30100

зы: Прикольно, на Р2 (красный - пульс, синий - сатурация) я могу чётко наблюдать вспышки своих экстрасистолий. Чуть только сердце затрепыхается - сразу из-за правого среза графика выезжает пик с почти вертикальными фронтом и спадом. Детектор пульса работает чётко! А вот график сатурации (Р2, синий) остаётся совершенно невозмутимым, что ни предпринимай...

nik182
Offline
Зарегистрирован: 04.05.2015

Смотрю я туда же. Картинки 9, 10, 11, 12 и, наконец, 14 это картинки после обработки. К реальным сырым сигналам значения на осях не имеют никакого отношения. Реальные сырые значения на картинках  4,5,7 - это я уже писал. Примите как данность, что остальные картинки без алгоритмов обработки автора статьи Вы повторить не сможете в тех размерах осей как на картинках 9, 10, 11, 12 и, наконец, 14. Если сделать подобную обработку, то форма повториться, амплитуды скорее всего нет. 

На счёт подстройки я уже тоже писал - каждый шаг программы вызывает один раз balanceIntesities - подпрограмму которая пытается подстроить ток. Время подстройки Вы указали правильно. Никакого автомата, исключительно программными средствами. 

Расчёт сатурации не понятен. Похоже,что RMS считается непрерывно, без относительно к сердечному ритму. Соответственно когда кривые гладкие, без шума, по результат как то ворочается, как только на кривых появляется шум то что получается не понятно. Кроме того, если заглянуть в документ TI slaa274b.pdf то увидим что R через логарифм считается со штрихом, а графики сатурации для R без штриха. Как переводить одно в другое нигде не написано, но указывается, что уровни для этой процедуры должны быть близки. Вот гугл перевод оттуда: ===========    

Для каждой длины волны света, то значение постоянного тока удаляются из сигнала, выходящий из части переменного сигнала, который отражает уровень артериальной оксигенации. Среднеквадратичное значение рассчитывается путем усреднения квадрата сигнала по числу циклов сердечных сокращений. Измерение постоянного тока непрерывно рассчитывается путем усреднения сигналов за несколько циклов сердечного сокращения.

Мощность возбуждения каждого светодиода контролируется таким образом, чтобы уровень постоянного тока, видимый на ПИН-диоде, соответствовал заданному целевому уровню с небольшим допуском. Делая это для каждого светодиода, конечный результат заключается в том, что уровни постоянного тока этих двух светодиодов соответствуют друг другу с небольшим допуском. Как только уровни DC совпадают, SaO2 рассчитывается путем деления логарифмов значений RMS (в переводе RMS - Среднеквадратичное значение).
===============
Как этой библиотеке происходит расчёт я не разбирался. А делать надо как и описано в переводе - накопить переменную составляющую за несколько сердечных циклов, рассчитать RMS, рассчитать отношение и по картинке 13 блога вычислить значение сатурации.    
 
runaway
Offline
Зарегистрирован: 25.09.2012

Спасибо за разъяснения. Т.е. ситуация, для меня по крайней мере, выглядит достаточно сложной. Поскольку у меня нет понятных мне инструментов для того, чтобы свести значения постоянных составляющих двух каналов в необходимый коридор. Автор вроде бы приводит в тексте кусок кода, позволяющий это сделать, но как это выполнить практически - не объясняет... Статья для программистов.

Время подстройки

RED_LED_CURRENT_ADJUSTMENT_MS     500

увеличил до 1000 мс. Синяя кривая на графике Р2 (сатурация) начала реагировать на компрессию воздуха в грудной клетке. НО - строго наоборот от того, что описываете вы - у меня она резко проседает ВНИЗ, примерно на 4%, держится там несколько секунд, и так же резко возвращается на исходный уровень (в р-н 94%).

Ну по крайней мере хоть какой-то реакции удалось наконец добиться...

зы: Увеличил также вдвое кол-во периодов усреднения сатурации, с 4 до 8. Вроде сатурация стала адекватно реагировать на "ныряние" с задержкой дыхания. Особенно хорошо видно, если на графике Р2 выключить красную кривую пульса - тогда сатурация растягивается во весь экран, и все реакции кривой становятся хорошо видны.

kettle80
Offline
Зарегистрирован: 01.06.2020

Прочитал, стало интересно. Max30100 не имею, а судя по этому топику, похоже и заказывать не стоит. Вот стало интересно: а если взять простой фоторезистор от набора Ардуино УНО, его можно приспособить как "регистратор" вместо диода или нет?

nik182
Offline
Зарегистрирован: 04.05.2015

Да можно, но только два светодиода красный и инфракрасный и один фотодиод. Если читаете по английски, то есть полное описание как сделать железо со всеми номиналами http://www.ti.com/lit/an/slaa274b/slaa274b.pdf . МК другой, но это не принципиально. Но с max30100 всё же легче. Диоды, усилители, да и корпус датчика - всё в одном флаконе. 

kettle80
Offline
Зарегистрирован: 01.06.2020

Уже прочел, спасибо. Меня смущает что фоторезистор имеет "гистерезис" или "память" и времена релаксации в даташитах по 20-30мсек. Нашел это https://community.alexgyver.ru/threads/arduino-kak-lego-pulsoksimetr-i-ne-tolko.3162/, но там несколько иная схема как мне показалось.

Ваша статья просто кладезь.

P.S. И ещё сильно смущает "математика". И по вашей ссылке там тоже предварительно уровни освещения подбираются так, чтобы отклик на обоих частотах был одинаков, а уже потом, считают переменную составляющую и её обрабатывают. И получается что в части "оксиметр" берется отношение логарифмов .. заранее подогнанных уровней. Меня это несколько напрягает, т.к. подогнать можно что угодно и куда угодно. Или я ошибаюсь? Как это "калибровать"?

По наводке в 50гц. Можно же "заземлять" измеряемый палец, обмотав его проводочком и соединив с Землей АЦП .. должно сильно помочь.

По оксигенации: раз измерение в пальце, если его туго бинтовать, поступление крови снизится и оксигенация должна измениться за счет накопления венозной крови. Так кто-то пробовал смотреть на изменения показаний?

nik182
Offline
Зарегистрирован: 04.05.2015

Берётся отношение RMS, при одинаковом уровне DC составляющей. От уровня DC зависит размах колебаний. Если DC одинаковая, то не надо пересчитывать AC составляющую для которой рассчитывают RMS и вычисления упрощаются.
Бинтовать это лишнее. Достаточно сильнее нажать на датчик, что бы увидеть эффект.
Старые мануалы TI вообще кладезь информации. Я часто ими пользуюсь для получения схемотехнических решений. Достаточно зайти в раздел примеров на МК MSP430. Всё подробно расписано со схемами, номиналами деталей, расчётами режимов работы. Сейчас так не делают.

Bruzzer
Offline
Зарегистрирован: 17.03.2020

nik182 пишет:
Бинтовать это лишнее. Достаточно сильнее нажать на датчик, что бы увидеть эффект.

Как я понял естественный диапазон изменений насыщенности довольно узкий 94 - 99 %. И бинтование, может быть, поможет удовлетворить любопытство - "насколько низкий уровень насыщенности может измерять данный прибор." (Знать бы еще какой должен быть уровень у пережатого пальца.)

kettle80
Offline
Зарегистрирован: 01.06.2020

Нашел фотодиод, буду пробовать. Кстати, по ссылке на Гайвера понравилось решение с RGB светодиодом - можно смотреть отклик на ещё паре частот, что должно позволить определять или надежней или что-то ещё, какие-то HbMet и HbCO2 надо почитать на эту тему..

runaway
Offline
Зарегистрирован: 25.09.2012

> Max30100 не имею, а судя по этому топику, похоже и заказывать не стоит.

Странное заключение...

Если решите собирать на рассыпухе, то с очень большой долей вероятности (если со схемотехникой до сего момента вы общались примерно на уровне ардуинского бредборда с дырочками) - в САМОМ лучшем случае вы получите РОВНО те же проблемы, что и описанные выше, ПЛЮС пляски с железом: усилители, наводки, и т.д. и т.п... Просто, по-пионерски - усилить сигнал с фотодиода, оцифровать его и пытаться потом вытянуть оттуда какую-то полезную инфу - путь в никуда, потому что сперва вы должны отвязаться от массы мешающих, непостоянных факторов. Температура - первое, что приходит в голову. В чипе, насколько я читал, это решается дифференциальным измерением, когда сигнал снимается не относительно какого-то стационарного эталона, а относительно другого сигнала (помехи). И получается железная фильтрация - задолго до АЦП. Это только потом идут программные нормализация и ФНЧ Баттерворта (если иметь в виду статью, ссылку на которую я поместил в начале этого топика). Наконец, вы же не впихнёте схему из апноута по ссылке выше в пластинку объёмом несколько куб.мм... Будут навесные провода, или какая-то разводка, что неизбежно повлечёт за собой цепь традиционных проблем с наводками, которые придётся преодолевать... Смысл? Дешевле - ТОЧНО не получится. ЛУЧШЕ? Гхм... Кхм...

Мне кажется, что 30100 (-1, -2) - одно из лучших решений по теме "любительской", "домашней" оксиметрии, что предлагает рынок на сегодняшний день. 

Бинтовать палец не имеет смысла, потому что вы задавите пульс. А наличие обнаруженного пульса - непременное условие расчёта сатурации. То же самое произойдёт, если вы сильно надавите пальцем на сенсор.

Резюме - не нужно требовать от РАБОТАЮЩЕЙ технологии больше, чем она может дать. Завышенные ожидания рождают разочарование. Если же играться в пределах этого поля - можно достичь счастья!

runaway
Offline
Зарегистрирован: 25.09.2012

Удалено автором.

runaway
Offline
Зарегистрирован: 25.09.2012

> Особенно хорошо видно, если на графике Р2 выключить красную кривую пульса - тогда сатурация растягивается во весь экран, и все реакции кривой становятся хорошо видны.

> Завышенные ожидания рождают разочарование. 

Две ключевые фразы. Ждал слишком многого там, где оно не могло проявиться. Короче, выставил я все параметры в заголовке библиотеки по умолчанию, и выключил красную кривую пульса на P2. Оставшаяся синяя линия (сатурации) уверенно отображает даже обычные глубокие вздохи, безо всяких компрессий и минутных задержек. Просто спокойно посидеть пару минут, с пальцем на датчике, дождаться, пока все переходные пики и неровности уедут за левый срез окна Р2, потом глубоко вздохнуть, задержать на секунду воздух, и глубоко выдохнуть, после чего продолжить спокойное дыхание. И вот такая получается картинка:

https://mega.nz/file/AA0hkJIa#ueoYOkDCVxFEuw70WhzaBidlaRuHctkkZyFsfMgPDXE

Вполне себе приличный отклик, только я бы усреднял штук тридцать-сорок отчётов сатурации. В мануале я бы написал, что измерять сатурацию надо в одно и то же время суток, например, с утра, после пробуждения. Надеть сенсор на палец, и, например, через минуту, после сигнала окончания измерения, считать показания с дисплея. Во время цикла измерения ЖКИ должен отображать режим ожидания результатов - например, заполняющийся статус-бар или что-то в этом роде. Кроме того, прибор должен иметь индикатор детекции пульса, лучше всего звуковой. Чтобы пользователь не ждал напрасно, если палец лёг не так, как надо, и пульс не захвачен. Как пошли щелчки захвата пульса из звукового индикатора - с этого момента ожидаем звукового сигнала окончания измерения, и считываем результаты с ЖКИ. Что-то в этом роде надо написать... Усреднение - среднее арифметическое, с отбрасыванием единичных мусорных отчётов, отличающихся от общей массы на заданный порог. И всё должно получиться...

runaway
Offline
Зарегистрирован: 25.09.2012

Разобрался, наконец, со спорадическими пропаданиями детекции пульса. Руки (пальцы) должны быть ТЁПЛЫМИ. У меня, например, традиционно плохое кровообращение в конечностях (хронически холодные руки и ноги); а погоды сейчас стоят, мягко говоря, нежаркие. Постановил: перед каждым экспериментом несколько минут прогревать пальцы под лампой накаливания. Теперь получаю неизменно превосходный результат - амплитуду импульсов кардиограммы (Р1) наблюдаю от 200 до 700 единиц (амплитуда - это то, что выше нулевой линии, от нуля до вершины импульса). Никогда не менее 300. Отказы детекции пульса просто прекратились... Если долго держу палец на сенсоре, без движения, то вижу, как амплитуда кардиограммы постепенно уменьшается. Всё логично.

Как видим, всё постепенно становится на свои места!

зы: Прикольно наблюдать разницу в амплитуде импульсов кардиограммы между прогретыми (левая рука) и непрогретыми (правая) пальцами. Прогретые - от 200 ед. и выше; непрогретые - до 30 ед. (и, естественно, никакой детекции).

b707
Offline
Зарегистрирован: 26.05.2017

думаю, тема давно переосла Песочницу - ее нужно либо в аппаратные, либо в проекты

nik182
Offline
Зарегистрирован: 04.05.2015

runaway пишет:
зы: Прикольно наблюдать разницу в амплитуде импульсов кардиограммы между прогретыми (левая рука) и непрогретыми (правая) пальцами. Прогретые - от 200 ед. и выше; непрогретые - до 30 ед. (и, естественно, никакой детекции).

У меня руки всегда горячие. Поэтому Ваших проблем не наблюдал. Всё работало сразу. Ещё одно доказательство того, что важны все элементы - и датчик и объект :-) . Ещё раз поздравляю с решением проблемы.  

runaway
Offline
Зарегистрирован: 25.09.2012

> думаю, тема давно переосла Песочницу - ее нужно либо в аппаратные, либо в проекты

Если тут требуется моё согласие - я только "за"! Хочется помочь как можно большему числу таких как я сам горемык, чтоб не плясали с бубном, не наступали на уже пройденные грабли... И в этой связи - было бы здорово добавить слово "Пульсоксиметр" в заглавие топика, для поисковых ботов. Сам я это сделать как будто не могу - вроде как некуда нажимать...

 

runaway
Offline
Зарегистрирован: 25.09.2012

> ... и датчик и объект

)) Если бы у объекта все проблемы решались прогревом конечностей... Умище, УМИЩЕ девать некуда! ))
 

Другими словами: помогите убогому, люди добрые... Пытаюсь скомпилить из двух скетчей один. Чтобы строгоновский скетч выводил данные не только в компорт, но и в 1602 - показания пульса и сатурации. Взял готовый проект, где фигурирует 1602, посмотрел, попытался... Не компилится, хоть ты тресни... Понимаю, что тупо, но не могу сам решить - старенький уже, и нифига в этом не волоку... Уже честно пытался объявить, подставить в функции вывода НУЖНЫЕ имена переменных, но... Выручайте!

#include <Arduino.h>
#include <math.h>
#include <Wire.h>
#include <LiquidCrystal.h>
#include "MAX30100.h"

MAX30100* pulseOxymeter;
LiquidCrystal lcd(13, 12, 11, 10, 9, 8);

// LCD 1206   1   2   4    5   6   11   12   13   14   15   16
// Arduino   GND  +5  13  GND  12  11   10   9    8   +5V   GND

// MAX30100  SDA SCL
// Arduino   A4  A5

void setup() {
  Wire.begin();
  Serial.begin(115200);
  Serial.println("Pulse oxymeter test!");

  lcd.begin(16,2);
  lcd.print("Initializing...");
  delay(3000);
  lcd.clear();

  //pulseOxymeter = new MAX30100( DEFAULT_OPERATING_MODE, DEFAULT_SAMPLING_RATE, DEFAULT_LED_PULSE_WIDTH, DEFAULT_IR_LED_CURRENT, true, true );
  pulseOxymeter = new MAX30100();
  pinMode(2, OUTPUT);

  //pulseOxymeter->printRegisters();
}

void loop() {
  //return;
  //You have to call update with frequency at least 37Hz. But the closer you call it to 100Hz the better, the filter will work.
  pulseoxymeter_t result = pulseOxymeter->update();
  

  if( result.pulseDetected == true )
  {
    Serial.println("BEAT");
    
    Serial.print( "BPM: " );
    Serial.print( result.heartBPM );
    Serial.print( " | " );
  
    Serial.print( "SaO2: " );
    Serial.print( result.SaO2 );
    Serial.println( "%" );

    Serial.print("{P2|BPM|255,40,0|");
    Serial.print(result.heartBPM);
    Serial.print("|SaO2|0,0,255|");
    Serial.print(result.SaO2);
    Serial.println("}");

    lcd.clear();
    lcd.setCursor(1,0);
    lcd.print("BPM: ");
    lcd.print(pulseOxymeter.heartBPM());
 
    lcd.setCursor(6,1);
    lcd.print("SpO2: ");
    lcd.print(pulseOxymeter.SaO2());
    lcd.print(" %");
  }

  //These are special packets for FlexiPlot plotting tool
  Serial.print("{P0|IR|0,0,255|");
  Serial.print(result.dcFilteredIR);
  Serial.print("|RED|255,0,0|");
  Serial.print(result.dcFilteredRed);
  Serial.println("}");
  
  Serial.print("{P1|RED|255,0,255|");
  Serial.print(result.irCardiogram);
  Serial.print("|BEAT|0,0,255|");
  Serial.print(result.lastBeatThreshold);
  Serial.println("}");

  delay(10);

  //Basic way of determening execution of the loop via oscoliscope
  digitalWrite( 2, !digitalRead(2) );
}

nik182
Offline
Зарегистрирован: 04.05.2015

Приведите ошибку копи-пасте в свёрнутый спойлер - галочка свернуть на второй вкладке с кодом.   У меня всё откомпилилось без ошибок.

runaway
Offline
Зарегистрирован: 25.09.2012

У меня пишет следующее:



111111:85:29: error: request for member 'heartBPM' in 'pulseOxymeter', which is of pointer type 'MAX30100*' (maybe you meant to use '->' ?)

111111:89:29: error: request for member 'SaO2' in 'pulseOxymeter', which is of pointer type 'MAX30100*' (maybe you meant to use '->' ?)

exit status 1
request for member 'heartBPM' in 'pulseOxymeter', which is of pointer type 'MAX30100*' (maybe you meant to use '->' ?)

 

runaway
Offline
Зарегистрирован: 25.09.2012

Вижу неточность: у меня стоит библиотека LiquidCrystal_I2C, но в этой схемке ЖКИ подключен к ардуине напрямую, без И2Ц-адаптера. Может ли этот момент послужить причиной?

Я пытался дописывать в функцию вывода "->", как советует компилятор, по-всякому, но безуспешно. Я не знаю, как работать с функциями библиотеки, какой параметр на что влияет, и как всё это правильно оформлять. Буду очень признателен, если вы объясните мне, где в общем случае обычно добывается подобная информация? Подозреваю, что в авторском описании к библиотеке, но какими запросами лучше искать такие документы? Нет ли В САМИХ библиотеках ключа к пониманию, как с ними работать? Может там и не требуется каких-то дополнительных мануалов для того, чтобы ими успешно пользоваться? 

В библиотеках, насколько я понимаю, прописываются-расписываются ФУНКЦИИ, к которым обращается скетч по мере необходимости. Если библиотека НИКАК не откомментирована автором - можно ли понять ИЗ САМОГО ТЕКСТА КОДА, как правильно обращаться с функциями данной библиотеки?

Bruzzer
Offline
Зарегистрирован: 17.03.2020

Вам надо заменить в ошибочных строках pulseOxymeter на result.

pulseOxymeter используется только один раз   pulseoxymeter_t result = pulseOxymeter->update();

nik182
Offline
Зарегистрирован: 04.05.2015
#include <Arduino.h>
#include <math.h>
#include <Wire.h>
#include <LiquidCrystal.h>
#include "MAX30100.h"

MAX30100* pulseOxymeter;
LiquidCrystal lcd(13, 12, 11, 10, 9, 8);

// LCD 1206   1   2   4    5   6   11   12   13   14   15   16
// Arduino   GND  +5  13  GND  12  11   10   9    8   +5V   GND

// MAX30100  SDA SCL
// Arduino   A4  A5

void setup() {
  Wire.begin();
  Serial.begin(115200);
  Serial.println("Pulse oxymeter test!");

  lcd.begin(16,2);
  lcd.clear();
  lcd.print( "Initializing...");
  delay(2000);


  //pulseOxymeter = new MAX30100( DEFAULT_OPERATING_MODE, DEFAULT_SAMPLING_RATE, DEFAULT_LED_PULSE_WIDTH, DEFAULT_IR_LED_CURRENT, true, true );
  pulseOxymeter = new MAX30100();
  pinMode(2, OUTPUT);

  //pulseOxymeter->printRegisters();
}

void loop() {
  //return;
  //You have to call update with frequency at least 37Hz. But the closer you call it to 100Hz the better, the filter will work.
  pulseoxymeter_t result = pulseOxymeter->update();
  

  if( result.pulseDetected == true )
  {
    Serial.println("BEAT");
    
    Serial.print( "BPM: " );
    Serial.print( result.heartBPM );
    Serial.print( " | " );
  
    Serial.print( "SaO2: " );
    Serial.print( result.SaO2 );
    Serial.println( "%" );

    Serial.print("{P2|BPM|255,40,0|");
    Serial.print(result.heartBPM);
    Serial.print("|SaO2|0,0,255|");
    Serial.print(result.SaO2);
    Serial.println("}");

    lcd.setCursor(1,0);
    lcd.print("BPM: ");
    lcd.print(result.heartBPM);
    lcd.print("      ");
    lcd.setCursor(1,1);
    lcd.print("SpO2: ");
    lcd.print(result.SaO2);
    lcd.print(" %");
    lcd.print("    ");
 }

  //These are special packets for FlexiPlot plotting tool
  Serial.print("{P0|IR|0,0,255|");
  Serial.print(result.dcFilteredIR);
  Serial.print("|RED|255,0,0|");
  Serial.print(result.dcFilteredRed);
  Serial.println("}");
  
  Serial.print("{P1|RED|255,0,255|");
  Serial.print(result.irCardiogram);
  Serial.print("|BEAT|0,0,255|");
  Serial.print(result.lastBeatThreshold);
  Serial.println("}");

  delay(10);

  //Basic way of determening execution of the loop via oscoliscope
  digitalWrite( 2, !digitalRead(2) );
}

 

Bruzzer
Offline
Зарегистрирован: 17.03.2020

runaway пишет:

Пытаюсь скомпилить из двух скетчей один. Чтобы строгоновский скетч выводил данные не только в компорт, но и в 1602 - показания пульса и сатурации...

Из описания библиотеки - Производить измерения надо с частотой не менее 37 Гц а лучше 100 Гц

Если вы после каждого измерения  будете пытаться вывести на дисплей - вы и частоту измерений уменьшите, и на экране может быть каша.

Надо выводить на экран реже. Раз в несколько секунд.

Если умеете программировать, то полезно было бы постараться приблизиться к рекомендованным 100 Гц опроса датчика.

runaway
Offline
Зарегистрирован: 25.09.2012

Спасибо! Заменил, как указано выше. Написало, что данное выражение не может быть использовано как функция. Убрал пустые скобки (), т.е. нарисовал в скобках всё точно так, как в строке вывода в компорт. И всё скомпилилось без замечаний.

Прошил, включил. Загрузилось, выводятся показания на ЖКИ. Тестирую.

Ещё раз большое спасибо!

runaway
Offline
Зарегистрирован: 25.09.2012

Программировать не умею, к сожалению. Не знаю, как снизить частоту вывода именно на ЖКИ, не затрагивая частоту опроса датчика и вывод результатов в компорт.

runaway
Offline
Зарегистрирован: 25.09.2012

Да, ваша правда - нестабильно как-то всё работает... То есть показания, то пустой экран. Несколько раз приходится перезапускать, чтобы появились.

runaway
Offline
Зарегистрирован: 25.09.2012

О, только сейчас заметил сообщение nik182 с готовым текстом... Большое спасибо! Но - да, надо бы "кашу из топора" ДОВАРИТЬ! ))

Сейчас проблема с запуском - после слова "Инициализация" вижу пустой экран, надо несколько раз перезапустить, тогда появляются строчки с пульсом и сатурацией. Но и они работают нестабильно - пульс скачет от нескольких сотен до пары десятков, хаотически. Иногда успокаивается, показывает адекватные числа пульса и сатурации...

nik182
Offline
Зарегистрирован: 04.05.2015

Переместите lcd.clear();  в конец setup и поставьте задержку 2 секунды delay(2000);. Чистить экран в цикле не надо. Добавьте lcd.print("    "); в 61 и 66 строки. Обратите внимание на 52 и 54 строки - это данные графика Р2 - на LCD те же цифры, что отображаются на графике. Если на графике прыгает то и на LCD тоже будет прыгать. Это то ,  что даёт программа. Можно усреднить,но от прыжков это не избавит, только уменьшит амплитуду. Замедление не поможет. АЦП работает 1.6 мс. Любой опрос медленнее этого времени даст адекватные данные. Так что 37 Гц  что 100 Гц на конечный результат не влияет. Основной цикл крутится около 20 мс. 

P.S. Изменил текст программы. 

 

 

Bruzzer
Offline
Зарегистрирован: 17.03.2020
#include <Arduino.h>
#include <math.h>
#include <Wire.h>
#include <LiquidCrystal.h>
#include "MAX30100.h"

MAX30100* pulseOxymeter;
LiquidCrystal lcd(13, 12, 11, 10, 9, 8);

// LCD 1206   1   2   4    5   6   11   12   13   14   15   16
// Arduino   GND  +5  13  GND  12  11   10   9    8   +5V   GND

// MAX30100  SDA SCL
// Arduino   A4  A5

void setup() {
  Wire.begin();
  Serial.begin(115200);
  Serial.println("Pulse oxymeter test!");

  lcd.begin(16,2);
  lcd.print("Initializing...");
  delay(3000);
  lcd.clear();

  //pulseOxymeter = new MAX30100( DEFAULT_OPERATING_MODE, DEFAULT_SAMPLING_RATE, DEFAULT_LED_PULSE_WIDTH, DEFAULT_IR_LED_CURRENT, true, true );
  pulseOxymeter = new MAX30100();
  pinMode(2, OUTPUT);

  //pulseOxymeter->printRegisters();
}

//+++
uint32_t  askTime = 0;    // время последнего измерения. Для измерения времени между измерениями
#define   ASK_TIME  10    // Время в мс между измерениями - 10 мс -> 100 Гц;  20 мс -> 50 Гц
uint32_t  lsdTime = 0;    // время последнего вывода на экран. Время в мс для измерения времени между выводом на экран
#define   LSD_TIME  5000  // Время в мс между выводом на экран
//---

void loop() {
  //return;
  //You have to call update with frequency at least 37Hz. But the closer you call it to 100Hz the better, the filter will work.
  //+++                                  // Ожидаем очередное время измерения для требуемой частоты опроса
  uint8_t byteTmp = 0;                   // Флаг - опоздали 0   успели 1
  while(millis() - askTime < ASK_TIME)  
  {
    byteTmp = 1;                        // Раз ждем, значит успели, установим флаг.
  }
  askTime = millis();                   // Обновляем время последнего опроса
  //---
  
  pulseoxymeter_t result = pulseOxymeter->update();

  //+++                 // Результат while(millis() - askTime < ASK_TIME) выводим после pulseOxymeter->update(), 
                        //     чтобы гарантированно не влиять на время его (pulseOxymeter->update()) запуска
  if(byteTmp == 0)      // Если не уложились во временной интервал, то сообщим об ошибке
  {
    Serial.println("TimeErr");
  }
  //---

  if( result.pulseDetected == true )
  {
    Serial.println("BEAT");
    
    Serial.print( "BPM: " );
    Serial.print( result.heartBPM );
    Serial.print( " | " );
  
    Serial.print( "SaO2: " );
    Serial.print( result.SaO2 );
    Serial.println( "%" );

    Serial.print("{P2|BPM|255,40,0|");
    Serial.print(result.heartBPM);
    Serial.print("|SaO2|0,0,255|");
    Serial.print(result.SaO2);
    Serial.println("}");

    //+++
    if(millis() - lsdTime >= LSD_TIME)  // На экран выводим через интервалы LSD_TIME
    {
    lsdTime = millis();                   // Обновляем время последнего вывода на экран
    //---

    lcd.clear();
    lcd.setCursor(1,0);
    lcd.print("BPM: ");
    lcd.print(result.heartBPM);
 
    lcd.setCursor(6,1);
    lcd.print("SpO2: ");
    lcd.print(result.SaO2);
    lcd.print(" %");
    
    //+++
    }    
    //---
  }

  //These are special packets for FlexiPlot plotting tool
  Serial.print("{P0|IR|0,0,255|");
  Serial.print(result.dcFilteredIR);
  Serial.print("|RED|255,0,0|");
  Serial.print(result.dcFilteredRed);
  Serial.println("}");
  
  Serial.print("{P1|RED|255,0,255|");
  Serial.print(result.irCardiogram);
  Serial.print("|BEAT|0,0,255|");
  Serial.print(result.lastBeatThreshold);
  Serial.println("}");

  //+++
  //delay(10);  // Задержка перенесена в while(millis() - askTime < ASK_TIME)
  //---

  //Basic way of determening execution of the loop via oscoliscope
  digitalWrite( 2, !digitalRead(2) );
}

Пример кода. За основу взят код из сообщения #77 после исправления. Свои вставленные куски пометил начало и конец //+++ и //--- . Работоспособность не проверял.

runaway
Offline
Зарегистрирован: 25.09.2012

Перепрошил. После включения устройства пишет "Initializing...", пока подано питание. Несколько перезагрузок могут включить строчки показаний, а могут и не включить. Случайный процесс. Убрал 2000 в конце сетапа, поставил 10. Никаких изменений. 

Но если  строчки показаний включились на экране, то, в принципе, показания, выводимые на ЖКИ, насколько я могу судить, повторяют показания в компорте. И тем вполне меня устраивают на данном этапе. 

Разобраться бы теперь с загрузкой. Что-то там не так...

runaway
Offline
Зарегистрирован: 25.09.2012

А пока пробую вариант Bruzzer...

Bruzzer
Offline
Зарегистрирован: 17.03.2020

У меня был неправильный комментарий к #define   LSD_TIME  5000

правильно

#define   LSD_TIME  5000  // Время в мс между выводом на экран

(в исходном сообщении исправил)

runaway
Offline
Зарегистрирован: 25.09.2012

По варианту Bruzzer: наблюдаю те же проблемы с загрузкой, что и в варианте nik182 (после слова "Инициализация..." - пустой экран, несколько раз надо переподключать питание, чтобы включились строки показаний). Плюс к тому, когда (и если) включились строки - показания иногда самопроизвольно замирают, т.е. перестают обновляться через каждые 5000 (5 сек).

Bruzzer
Offline
Зарегистрирован: 17.03.2020

Я не спец по работе с экраном. Т.к. в моем варианте не учтены рекомендации nik182 то

По рекомендации nik182 добавьте задержку в setup после lcd.clear();

      lcd.begin(16,2);
      lcd.print("Initializing...");
      delay(3000);
      lcd.clear();
      delay(2000);

И уберите       lcd.clear(); в цикле loop (опять же По рекомендации nik182).

runaway
Offline
Зарегистрирован: 25.09.2012

Сделал. Ничего не поменялось. Иногда включаются строчки показаний, но, как правило, после выключения слова "Инициализация..." экран остаётся пустым. А когда удаётся добиться индицирования показаний, то ведут они себя хаотически, и периодически замирают. Потом размораживаются, и опять показывают мусор... Что-то не так.

nik182
Offline
Зарегистрирован: 04.05.2015

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

runaway
Offline
Зарегистрирован: 25.09.2012

Так у меня этот же экземпляр "нормально" работает с другой прошивкой... С той, которая показывает стационарную сатурацию и что попало вместо пульса. Сейчас опять ею прошился - всё работает "нормально", как и работало!

nik182
Offline
Зарегистрирован: 04.05.2015

 Чудес не бывает. Если есть код, где работает нормально , то надо туда попробовать добавить то, что Вам надо из не работающего и посмотреть, на каком этапе начнутся глюки. 

runaway
Offline
Зарегистрирован: 25.09.2012

Ну я попробовал, описал выше... Только строго наоборот: за основу взял строгоновский скетч, куда добавил строчки вывода на ЖКИ - из того скетча, который нормально работает с ЖКИ, но ненормально - с 30100. Но из обсуждения выше я понял, что тупого добавления строчек для решения проблемы отнюдь недостаточно. Нужно понимать, сколько времени занимает процесс опроса датчика, и сколько - процесс вывода на ЖКИ. Понять-то понял, но до практической имплементации этого моего "понятия" - как до Луны...
Может лучше какой-то другой дисплей приобрести, с хорошо обкатанной людьми библиотекой? А то добавить строчки с выводом на ЖКИ - я хоть неправильно, но могу. А вот наоборот, как в тот, "рабочий" скетч (в котором, по сути, работает только ЖКИ) вставить СТРОГОНОВСКИЙ скетч - вообще не представляю...

Bruzzer
Offline
Зарегистрирован: 17.03.2020

Немного поясню свои слова про контроль периода измерений. И насколько меня стоит слушать.

Я не работал с данным датчиком, и не знаю как он работает, так же и с дисплеем.

Я исходил только из того, что автор библиотеки рекомендует вызывать update ее как можно ближе к 100 Гц. А в вашем исходном коде это не проверялось. Может быть это и не надо. Но если переделать скетч на контроль частоты запуска, то можно это и проверить. Задавать разные периоды и смотреть - успевает ли программа и есть ли изменение точности. Менять длительность периода можно при желании и кнопкой. Я не читал вашу тему целиком. Может быть вопрос точности уже не актуален.

Если все же контролировать длительность цикла loop, то обращу ваше внимание, что вы довольно много выводите в serial, я не уверен, но кажется у вас при определенных условиях может полностью заполниться буфер serial. Может быть это и не важно. Если важно, то - или меньше выводить, или поднять скорость serial.