3003| 1
|
[ESP8266/ESP32] FireBeetle:USB 脉搏血氧仪改蓝牙 |
本帖最后由 zoologist 于 2023-4-15 10:07 编辑 脉搏血氧仪(pulseoximeter)是以无创方式测量病人动脉血液中血氧饱和度或动脉血红蛋白氧饱和度的一种医疗设备,现已成为世界各地医院的重要设备,也是近几年来来全球对抗新型冠状病毒不可或缺的仪器。脉搏血氧仪主要测量指标分别为脉率、血氧饱和度(oxygensaturation简写为SO2)、灌注指数(PI)。这些指标的含义如下: 1.脉率是每分钟的脉搏数。 2. 血氧饱和度是指在全部血容量中被结合O2容量占全部可结合的O2容量的百分比。 人体所消耗的氧主要来源于血红蛋白(在正常的血液中存在四种血红蛋白:氧合血红蛋白(HbO2)、还原血红蛋白(Hb)、碳氧血红蛋白(CoHb)、高铁血红蛋白(MetHb)。其中与氧气做可逆性结合的是还原血红蛋白,与氧气不相结合的是碳氧血红蛋白和高铁血红蛋白。)所携带的氧。通常称血液中氧含量即指血液中氧合血红蛋白的多少,用血氧饱和度这个物理量来描述血液中氧含量的变化。正常人体动脉血的血氧饱和度为98%, 静脉血为75% 。它是反映机体内氧状况的重要指标,一般认为血氧饱度正常值应不低于94%,在94%以下被视为供氧不足。 3.PI是指血流灌注指数(PerfusionIndex,PI), PI值反映了脉动血流情况,即反映了血流灌注能力。脉动的血流越大,脉动分量就越多,PI值就越大。因此,测量部位(皮肤、指甲、骨骼等影响)和病人本身的血流灌注情况(动脉血液的流动情况)都将影响PI值。由于交感神经会影响心率和动脉血压(影响脉搏动脉血流),所以人体的神经调节系统或精神状态 也会间接影响PI值。因而,不同麻醉的状态下,其PI值也会不同。 在医学上,许多临床疾病会造成患者氧气供给的缺乏,甚至导致病人因手术时缺氧而死亡,因此,动脉血氧浓度的实时监测在临床救护中非常重要。 传统的血氧饱和度测量方法是先进行人体采血(一般采桡动脉和股动脉的动脉血),再利用血气分析仪分析并测出血氧饱和度。然而,这种反复抽血容易导致病人的假性动脉瘤,也会带来许多不必要的痛苦。最大的麻烦是,这种方法通常至少需要20-30分钟才能获得结果,不能进行实时监测,而这种延迟可能是致命的,因为低氧会在5分钟内发生严重的脑损伤。因此,当时的许多医务工作者希望能为麻醉患者充分供氧并实时监测其血氧含量。 在第二次世界大战期间,美国的一位名叫GlennAllen Millikan的年轻生理学家开发了一种耳部血氧计,用于监控军事飞行员的缺氧状况。然而,这种固定在耳朵上的设备监测结果并不精准。 1958年,一位名叫青柳卓雄的年轻人从新潟大学工学部电气工学专业毕业后进入京都的一家科学仪器公司工作。1971年,他转入日本一家名为NihonKohden的医疗设备公司,开始从事医疗器械的开发工作。 当年轻的青柳卓雄就职于NihonKohden之时,已有能监测人体血氧含量的早期设备问世,但这些设备体积笨重,读数也不够精准。青柳卓雄便开始寻找一种更为轻便和准确,无需抽血便能检测出人体的血氧饱和度的方法。 开始从事脉搏血氧仪的研究工作之初,二战时期的耳部血氧计便引起了青柳卓雄的注意,他开始研究这一仪器背后的秘密:承担血液中氧气输送功能的血红蛋白与氧气结合时吸收光线的方式不同,而这一设备正是利用了这一点,使用两种不同波长的光(红色光和红外光)来测量飞行员的血氧含量。 受这个方法的启发,3年后,这个日本青年便研究出了不需要采血、只需将光线照射到指尖血管便可进行人血氧含量测定的血氧仪,当时,他38岁。 1972年,在研究以染料稀释法(即将无毒的染料作为指示剂注入患者体内)来测量人体心脏泵血量时,青柳卓雄发现人体血液流动时会产生一种干扰染料稀释曲线的“噪音”,这种噪音中携带着动脉血液氧合的重要信息。他利用创新技术消除了这种噪音,从而可以清晰读取氧气含量。这使他发现了利用两种不同波长的光信号进行脉搏血氧测定的方法。 1974年4月26日,青柳卓雄向日本医学电子与生物逻辑工程学会(MEBE)报告了他的脉搏血氧测定法,并与合作者,也是同事的木井一夫(MichioKishi)一起创建了一个脉搏血氧计原型,1974年他们的公司提交了一份专利申请,将两人列为发明人。5年后,这项发明被授予专利。 1975年,第一个利用青柳卓雄的方法做的商用脉搏血氧计面世。这款血氧仪由一个包含发光装置的探针和两个光电探测器组成,可以由两个波长的光通过耳垂到达光电探测器,根据动脉血的脉动来测量每个波长的吸光度变化。该设备能够快速、无创地评估患者的氧合,使临床医生能更早地发现血流动力学和呼吸的异常情况,避免患者受到伤害,并实时评估临床干预的效果。【参考2】 脉搏血氧仪的工作原理:使用波长660nm的红光和940nm的近红外光作为射入光源,测定通过组织床的光传导强度,来计算血红蛋白浓度及血氧饱和度(SpO2),仪器即可显示人体血氧饱和度。一般情况下,正常人体动脉血的血氧饱和度≥98%,大于95%的氧饱和度被认为是正常读数,90%以下被认为是供氧不足。 这次的作品是通过FireBeetle将手上的 USB接口的血氧仪改装成蓝牙接口的,这样电脑和设备之间可以通过无线进行连接。我手上的血氧仪是力康(HealForce)KS-CM01指夹式血氧仪 。可以看到一段是USB接口,PC端运行软件能够获得血氧数据。 上位机软件界面: 首先,我们对这个设备进行简单分析。插入电脑后,设备管理器中会出现 Prolific USB-to-Serial Comm Port 这个设备。 从属性中可以看到 VID为 0x067B ,PID 为 0x2303 ,这说明这个血氧仪内部使用 PL2303 串口转 USB 芯片将数据转化为 USB信号输出。因此,我们需要使用 USB Host Shield 读取USB数据。 接下来我们还需要对软件动作进行分析,这里使用Serial Port Monitor , 这是一款能够监视串口通讯的软件,就是说能够查看某个串口上的所有通讯。 运行之后选择要监视的串口 开启监视之后,再取打开血氧仪配套软件,SerialPort Monitor 就能抓取记录串口上的数据了。 抓取完成后,可以将数据直接保存为 TXT格式。数据很多,但是关键的信息不多,其中可以看到如下的数据信息:
IOCTL_SERIAL_SET_BAUD_RATE 是发送给串口驱动要求设置波特率的命令,因此,从上面可以看到应用程序使用 38400波特率进行串口通讯。 类似的还有设置串口参数的命令:
从 IOCTL_SERIAL_SET_LINE_CONTROL 我们能够得知停止位为1,无奇偶校验,数据长度为 8Bits。 此外,还能看到上位机通过 IOCTL_SERIAL_SET_HANDFLOW 设置了DTR、RTS流控,但是实际测试下来并没有使用这些进行实际控制。 接下来是上位机对设备发送的数据(从CPU到外设的方向被定义为OUT,也就是 WRITE的动作;反之从设备到CPU的方向称作IN,对应着READ的动作)。下面的IRP_MJ_WRITE就是上位机程序对设备发送数据的操作:
下面是一个从设备读取的动作,就是说设备发送了 0x03 和 0x01 给上位机程序:
这次,我们的目标是使用 USB Host Shield 和KS-CM01血氧仪通讯,然后使用 FireBeetle 模拟为一个蓝牙串口设备,连接到电脑后,将数据在设备和蓝牙串口之间传输。这样在上位机看起来是普通的串口通讯,而在KS-CM01看来,是和一台计算机的USB通讯。 首先进行硬件的设计,基本原理是 DFRobot 出品的 FireBeetle ESP32和基于 MAX3421e 芯片的 Super Mini USB Host 通讯,后者负责和USB脉搏血氧仪通讯。 电路图如下: 左侧是FireBeetleESP32, 它负责和 USB Super Mini USB Host 打交道,同时负责将数据转发到蓝牙串口上。 这是Super MicroUSB Host ,它和下面的USB母头相连用于实现USB通讯。SuperMicro USB Host板子的设计,可以在【参考5】看到。需要注意的是二者的连接有 R5/R6/R7/R8,其中的 R5 R7 是 MAX3421e 参考手册给出的建议电阻;R6R8 是0Ω电阻,放置这个的原因是我在实验中发现一些特殊情况下PL2303对 MAX3421存在兼容性问题,从经验判断是因为阻抗不匹配导致的。如果出现这种情况,可以尝试调整0Ω电阻为其他数值从而避免问题。 此外,下面是一个 USB公头,用于从外部给整个系统提供电力: PCB 设计如下: 焊接安装后的成品如下: 代码部分比较简单,关键部分解释如下: 1. 蓝牙串口的实现。 1.1在头部引用BluetoothSerial的库 #include "BluetoothSerial.h" 1.2声明一个蓝牙串口实例 // 声明蓝牙串口 BluetoothSerial SerialBT; 1.3 注册回调函数用于获取蓝牙连接很断开事件 // 注册回调函数 SerialBT.register_callback(callback); 1.4 当响应回调事件,当连接之后我们会直接对上位机发送 0x5F 和 0x4D 两个值。这样做的原因是:经过研究我发现上位机会尝试连接系统中的全部串口,连接后对该串口发送数据等待回复。如果无法打开串口或者连接之后上位机在短时间内没有回复数据,那就关闭之再测试下一个端口。为了防止KS-CM01没有及时回复而导致端口被关闭,这里在连接之后会返回任意的数值。 // 蓝牙连接事件 void callback(esp_spp_cb_event_t event, esp_spp_cb_param_t*param) { if (event ==ESP_SPP_SRV_OPEN_EVT) { // 蓝牙已连接 Serial.println("Client Connected"); // 标记蓝牙已连接 Connected = true; SerialBT.write(0x5f); SerialBT.write(0x4d); } if (event ==ESP_SPP_CLOSE_EVT ) { // 蓝牙已断开 Serial.println("Client disconnected"); // 标记蓝牙已断开 Connected = false; } } 2. USB Host 部分的代码 2.1 PL2303 通讯参数的设置 uint8_t PLAsyncOper::OnInit(ACM *pacm) { uint8_t rcode; LINE_CODING lc; lc.dwDTERate =38400; // 设备使用38400 波特率 lc.bCharFormat = 0; lc.bParityType = 0; lc.bDataBits = 8; rcode =pacm->SetLineCoding(&lc); if (rcode) ErrorMessage<uint8_t>(PSTR("SetLineCoding"), rcode); return rcode; } 2.2 处理计算机发送给设备的数据(FireBeetle蓝牙串口收到数据,转发给设备) // 如果已经连接 if (Connected) { // 蓝牙串口有数据送出,就将数据转发给设备 if(SerialBT.available()) { // 取得蓝牙串口数据数量 uint8_tcounter = SerialBT.available(); // 将数据存放在Buffer 中 for (uint8_t i= 0; i < counter; i++) { Buffer =SerialBT.read(); } // 一次性将Buffer 内容发送到设备上 rcode =Pl.SndData(counter, (uint8_t *)&Buffer); if (rcode) ErrorMessage<uint8_t>(PSTR("SndData"), rcode); // 这个数据是从电脑发送给设备的 Serial.print("PC->D:"); for (uint8_t i= 0; i < counter; i++) { Serial.print(Buffer, HEX); Serial.print(" "); } Serial.println(""); } } 2.3 从USB Host Shied 取得设备的数据,转给计算机(USB Host取得数据,通过蓝牙串口转发给计算机) // 从血氧设备取得数据 uint16_t rcvd = 64; rcode = Pl.RcvData(&rcvd, Buffer); if (rcode && rcode != hrNAK) ErrorMessage<uint8_t>(PSTR("Ret"), rcode); if ( rcvd ) { // 收到的数据长度不为0 // 如果已经连接 if (Connected) { // 转发数据到蓝牙串口 for (uint16_t i = 0; i < rcvd; i++ ){ SerialBT.write(Buffer); } // 这个数据是从设备发送给电脑的 Serial.print("D->PC:"); for (uint16_t i = 0; i < rcvd; i++ ){ Serial.print(Buffer, HEX); Serial.print(" "); } Serial.println(""); } }//if( rcvd ... 使用时,首先给FireBeetle上电,之后打开计算机上的蓝牙和其他设备页面搜索OxiSensor设备,配对后计算机上会多出一个串口。之后打开自带的上位机软件即可获得数据。 参考: |
© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed