MAX30102
main.py
from machine import Pin, I2C
import time
from max30102 import MAX30102
from biosensor import IIRFilter, FingerDetect, SpO2Calculator
#from led_driver import print_like_console , print_multiline_console
i2c = I2C(0, scl=Pin(5), sda=Pin(6), freq=400000)
sensor = MAX30102(i2c)
finger = FingerDetect()
spo2_calc = SpO2Calculator()
print("READY")
while True:
red, ir = sensor.read_fifo()
# 1️⃣ авто-детект пальца
if not finger.update(ir):
print("NO FINGER")
#print_like_console('NO FINGER', x=0, y=0, delay=0.005)
time.sleep(0.1)
continue
# 2️⃣ SpO2
spo2 = spo2_calc.update(red, ir)
print("RED:", red, " IR:", ir,
" SpO₂:", int(spo2) if spo2 else "...")
#print_like_console(f" SpO: {spo2}", x=0, y=0, delay=0.005)
time.sleep(0.05)
max30102.py
# max30102.py — MicroPython версия
from machine import I2C
import time
# register addresses
REG_INTR_STATUS_1 = 0x00
REG_INTR_STATUS_2 = 0x01
REG_INTR_ENABLE_1 = 0x02
REG_INTR_ENABLE_2 = 0x03
REG_FIFO_WR_PTR = 0x04
REG_OVF_COUNTER = 0x05
REG_FIFO_RD_PTR = 0x06
REG_FIFO_DATA = 0x07
REG_FIFO_CONFIG = 0x08
REG_MODE_CONFIG = 0x09
REG_SPO2_CONFIG = 0x0A
REG_LED1_PA = 0x0C
REG_LED2_PA = 0x0D
REG_PILOT_PA = 0x10
class MAX30102:
def __init__(self, i2c, address=0x57):
self.i2c = i2c
self.address = address
self.reset()
time.sleep(1)
# clear interrupt flags
self._read(REG_INTR_STATUS_1)
self._read(REG_INTR_STATUS_2)
self.setup()
# -------------------
# LOW LEVEL FUNCTIONS
# -------------------
def _write(self, reg, value):
self.i2c.writeto_mem(self.address, reg, bytes([value]))
def _read(self, reg, n=1):
return self.i2c.readfrom_mem(self.address, reg, n)
# -------------------
# DEVICE CONTROL
# -------------------
def shutdown(self):
self._write(REG_MODE_CONFIG, 0x80)
def reset(self):
self._write(REG_MODE_CONFIG, 0x40)
def setup(self, led_mode=0x03):
# enable interrupts
self._write(REG_INTR_ENABLE_1, 0xC0)
self._write(REG_INTR_ENABLE_2, 0x00)
# FIFO
self._write(REG_FIFO_WR_PTR, 0x00)
self._write(REG_OVF_COUNTER, 0x00)
self._write(REG_FIFO_RD_PTR, 0x00)
self._write(REG_FIFO_CONFIG, 0x4F) # avg=4, almost full=17
# MODE — SpO2 (RED + IR)
self._write(REG_MODE_CONFIG, led_mode)
# SPO2 config: ADC range=4096, sample rate=100Hz, pulse width=411us
self._write(REG_SPO2_CONFIG, 0x27)
# LED amplitudes
self._write(REG_LED1_PA, 0x24) # ~7mA IR
self._write(REG_LED2_PA, 0x24) # ~7mA RED
self._write(REG_PILOT_PA, 0x7F) # ~25mA
# -------------------
# READING
# -------------------
def get_data_present(self):
read_ptr = self._read(REG_FIFO_RD_PTR)[0]
write_ptr = self._read(REG_FIFO_WR_PTR)[0]
n = write_ptr - read_ptr
if n < 0:
n += 32
return n
def read_fifo(self):
# clear interrupts
self._read(REG_INTR_STATUS_1)
self._read(REG_INTR_STATUS_2)
d = self._read(REG_FIFO_DATA, 6)
red = (d[0] << 16 | d[1] << 8 | d[2]) & 0x03FFFF
ir = (d[3] << 16 | d[4] << 8 | d[5]) & 0x03FFFF
return red, ir
def read_sequential(self, amount=100):
red_buf = []
ir_buf = []
count = amount
while count > 0:
n = self.get_data_present()
while n > 0:
red, ir = self.read_fifo()
red_buf.append(red)
ir_buf.append(ir)
n -= 1
count -= 1
return red_buf, ir_buf
biosensor.py
import time
class IIRFilter:
def __init__(self, alpha=0.1):
self.alpha = alpha
self.value = None
def add(self, x):
if self.value is None:
self.value = x
else:
self.value = self.alpha * x + (1 - self.alpha) * self.value
return self.value
class FingerDetect:
def __init__(self):
self.present = False
self.threshold = 20000 # автопорог
def update(self, ir_value):
# адаптивный порог: помним среднее
if ir_value < 10000:
self.present = False
elif ir_value > 30000:
self.present = True
return self.present
class SpO2Calculator:
def __init__(self):
self.red_dc = IIRFilter(0.01)
self.ir_dc = IIRFilter(0.01)
self.red_ac = IIRFilter(0.7)
self.ir_ac = IIRFilter(0.7)
self.spO2 = None
def update(self, red, ir):
# обновляем DC компоненты
red_dc_val = self.red_dc.add(red)
ir_dc_val = self.ir_dc.add(ir)
# AC = текущее - DC
red_ac_val = self.red_ac.add(red - red_dc_val)
ir_ac_val = self.ir_ac.add(ir - ir_dc_val)
# R = (AC_red/DC_red) / (AC_ir/DC_ir)
if ir_dc_val > 0 and red_dc_val > 0 and ir_ac_val != 0:
R = (red_ac_val / red_dc_val) / (ir_ac_val / ir_dc_val)
spo2 = 104 - 17 * R
# фильтр результата
if 70 < spo2 < 100:
self.spO2 = spo2
return self.spO2


Комментарии
Отправить комментарий