×

移动电话开源分享

消耗积分:0 | 格式:zip | 大小:0.67 MB | 2022-11-03

李雪

分享资料个

描述

介绍

世界某些地区的学校资源非常有限,健康问题并未优先考虑地方预算决定。

这些学校的孩子可能正在处理由病毒感染引发的发烧,并且在与其他孩子分享病毒后无法被识别。

该设备是一种廉价、移动、节能的方式,可以获取儿童的生命体征并将重要信息传输到中央指挥部健康中心以采取进一步行动。

动机

从更国际化的角度来看,“无国界医生”是紧急情况、流行病和自然灾害的全球第一响应者。他们可以受益于可以链接到网络的设备,该设备可以像 SigFox 这样快速、轻松地部署。对流行病爆发的信息的快速共享可能是在当地遏制或更广泛传播疾病方面的区别。

我们希望该设备的简单性能够收集空间、时间和生命体征数据,以便在受影响的学校迅速部署医疗资源。

请注意,我们使用手指温度来识别发烧的儿童。这可能很棘手,我们还没有找到正确的参数来自信地触发发烧警报。我们希望进一步的研究和与社区的互动可以使我们的设置更接近可靠的系统。

材料

以下是我们用于设备的材料的快照。考虑到我们使用 MikroE 板时没有合适的接口平台板。电路板的紧凑格式和 I2C 接口便于连接。

SIGFOX 网络

对于无法建设大规模基础设施的地区,SigFox 网络是明智的选择。使用 SigFox Access Station Micro SMBS-T4 可以轻松实现网络部署。

我们使用 PyCom LoPy 4 板与 SigFox 网络连接。首先,我们必须按照此处的说明在网络上注册设备

注册后,您必须将第一个有效负载发送到网络才能激活设备。在我们的案例中很困难,因为我们当地的城镇没有 SigFox 网关。必须开车 80 英里才能到达最近的网关服务区。

现在设备已被激活,您可以使用 SigFox 后端服务器访问消息和设置。

为了通过 REST Api 访问您的设备组,您需要设置适当的权限以允许访问,您可以通过在后端服务器界面中选择组来执行此操作。

pYYBAGNiBdqAaPGuAABAGp-NSfs165.png
 

在左右菜单中选择 API 访问选项。

poYBAGNiBd2ADsL6AAAzfg3chs8805.png
SigFox 后端服务器菜单
 

该页面将更新并在右上角显示一个新链接。点击它。

poYBAGNiBd-ASQYvAAA-E5uGCbg941.png
 

Api 访问创建表单将显示您可以授予应用程序访问权限的配置文件。我们允许通过 REST API 访问设备和消息。

pYYBAGNiBeGAXUAxAABEvmw_wTQ103.png
 

按确定后,表单将刷新并显示访问组设置。在里面,您将看到分配的登录名和密码,这与您的后端服务器不同。使用 SigFoxApi 函数时将需要这些凭据。

pYYBAGNiBeaATSitAAA6l19ZMU0192.png
 

我们可以在 SigFox -- 访问数据部分访问我们的设备消息。

我们都准备好了,现在让我们开发我们的设备和前端接入点。

设备

该设备将体温读取为心率,将数据保存在 SD 卡中,显示数据并将信息发送到 SigFox 网络,安装在一个紧凑的移动外壳中。目前,我们使用来自 5 折扣店的 USB 充电电池为设备供电。

洛皮 4

Pycom LoPy 4是一款支持四重承载 MicroPython 的开发板,支持 SigFox 网络。

使用 SigFox 网络需要按照 SigFox 网络部分中的说明注册设备。激活后,您可以使用以下类创建通信套接字。

下面的类使用适当的区域和频率初始化 SigFox 无线电,然后创建通信套接字,最后将传输方向设置为上行链路。该类包括用于发送数据和关闭通信套接字的函数。

from network import Sigfox
import socket
import pycom
import time

class mySigFox():

    def __init__(self):
        self.loadSocket = False

    def loadSigFox(self):
        try:
            # init Sigfox for RCZ1 (Europe) RCZ2 (America, Mexico and Brazil)
            self.sigfox = Sigfox(mode=Sigfox.SIGFOX, rcz=Sigfox.RCZ2)

            # create a Sigfox socket
            self.s = socket.socket(socket.AF_SIGFOX, socket.SOCK_RAW)

            # make the socket blocking
            self.s.setblocking(True)

            # configure it as uplink only
            self.s.setsockopt(socket.SOL_SIGFOX, socket.SO_RX, False)
            self.loadSocket = True

        except Exception as e:
            print(e)


    def sendMsg(self, data):
        self.s.send(data)

    def closeSigFox(self):
        self.s.close()
        self.loadSocket = False

TheSigFox 网络每天允许 140 条 12 字节的消息。我们传输 GPS、患者人数、温度、BPM、患者编号和消息块代码。数据被编码在两个连续的消息中,格式如下:

第 1 块,PACKAGE_REPORT

纬度(浮点数:4 字节) 经度(浮点数:4 字节) 患者人数(短:2 字节) 块码(短:2 字节)

块 2,PACKAGE_PATIENT:

我们使用 Python 的 struct 库将数据格式化为要发送的字节数组,如下面的代码所示。

#Encode GPS and number of patients
ba = bytearray(struct.pack('f',float(0.0)) + struct.pack('f',float(0.0)) + struct.pack('h',patCounter) + struct.pack('h',PACKAGE_REPORT))

#Encode patient temperature and BPM
ba = bytearray(struct.pack("f", max.hr_avg)+struct.pack("f", float(txtTemp))+struct.pack("h", patCheck)+ struct.pack('h',PACKAGE_PATIENT))

There根据 SigFox 的建议,发送消息之间有 30 秒的延迟。

PyTrack

PyTrack是Pycom多网络模块的扩展板,带有 GPS、加速度计和 MicroSD 卡。

PyTrack 有一个 I2C 总线,可与支持的内部传感器(GPS、加速度计、MicroSD)进行通信,还有一个使用引脚 9 和 10(P9、P10)在外部 IO 接头中定义的 I2C 总线。下面的代码将第二条 I2C 总线初始化为主机和 400k 的波特率(注意连接到此总线的传感器支持此波特率速度)

#Initialize the I2C bus
i2c = I2C(2, I2C.MASTER, baudrate=400000,pins=('P9','P10'))

We通过允许参数使用活动 I2C 总线对象初始化内部属性,更新了连接到电路板的传感器的类。此外,还具有扫描 I2C 端口并确认设备已找到并连接的功能。在下面的代码中,它恰好是显示器。

def __init__(self, i2c):
    if (i2c != None):
       self.temperature = 0.00
       self.i2c = i2c
       self.isConnected()    

    def isConnected(self):
       if self.i2c != None:
           print("Scanning...")
           # Check I2C devices
           devices = self.i2c.scan() # returns list of slave addresses
           print(devices)
           for d in devices:
               print(d)
               if d == SSD1306_I2C_ADDRESS:
                   print("found display")
                   return True
           print("Not found")
           return False
       else:
           # No check for SPI
           return True

The扩展板管理 MicroSD 设备。我们初始化SD对象,挂载它并打开日志文件,如下图。

#Initialize the SD card and file
sd = SD()
os.mount(sd, '/sd')
f = open('/sd/gps-record.txt', 'a')

最后,扩展板控制 GPS 传感器。我们初始化传感器,查询坐标,格式化并将它们写入日志文件,如下面的代码所示。

l76 = L76GNSS(timeout=30)
coord = l76.coordinates()
f.write("{} - {}\n".format(coord, rtc.now()))

显示的一些代码发生在程序的不同部分,我们只是聚集在一起以了解每个传感器的过程。

温度

我们使用来自 Adafruit 的 python 驱动程序,它支持 MAX30205 温度传感器,并更新为使用来自 Pycom 的 I2C 功能。

更改如下所示。

readRaw = self.i2c.readfrom_mem(MAX30205_ADDRESS,MAX30205_TEMPERATURE,2)
#I2CreadBytes(MAX30205_ADDRESS,MAX30205_TEMPERATURE, &readRaw[0] ,2); //read two bytes

#I2CwriteByte(MAX30205_ADDRESS, MAX30205_CONFIGURATION, reg | 0x80);
self.i2c.writeto_mem(MAX30205_ADDRESS, MAX30205_CONFIGURATION, bytearray([(reg | 0x80)]))

Adafruit 库使用不同的函数来支持 I2C,我们更新调用以使用 Pycom I2C 实现,如上所示。该函数允许设置寄存器的基地址,然后从设备寄存器写入或读取偏移值。

然后我们使用下面的函数调用循环收集当前读取的温度。

txtTemp = temp.getTemperature()

心率

我们从https://github.com/zerynth/lib-maxim-max30101获取 Python 库并更新为使用 Pycom I2C。我们没有更改对 I2C 库上写入和读取函数的每次调用,而是重载这些函数并从内部调用活动的 I2C 库。

def write_read(self, reg, nbytes):
   self.i2c.writeto(MAX30101_I2CADDR, bytearray([reg]))
   # data = self.i2c.readfrom_mem(MAX30101_I2CADDR, MAX30205_TEMPERATURE,1)
   data = self.i2c.readfrom(MAX30101_I2CADDR, nbytes)
   # write_read(MAX30101_REV_ID,1)
   return data


def write_bytes(self, addr, reg):
   self.i2c.writeto_mem(MAX30101_I2CADDR, addr, bytearray([reg]))

以下调用该函数来检查脉搏信号并更新每分钟节拍 (BPM) 值的类属性。

detect_pulse(max)

展示

起始代码取自 Adafruit python SSD1306,我们创建了一个类并更新了 128x64 像素案例的代码。该类更新 I2C 调用以使用 Pycom 接口。我们还为原始驱动程序添加了大字体支持。

该类用作以下代码。显示数据的循环包括清除当前缓冲区、添加内容(addString、addString2),然后将缓冲区绘制到显示器上。

lopyLCD.clearBuffer()
lopyLCD.addString(0, 0,  "GPS:{}    VisteliLabs".format(txtGPS))
lopyLCD.addString(0, 1,  "P Check {} P Count {}".format(patCheck,patCounter))
if (tryHeartRate):
    lopyLCD.addString2(0,2,"{:0.2f}  C".format(txtTemp))
    lopyLCD.addString2(0,5,"{:0.2f} BPM".format(max.hr_avg))
else:
    lopyLCD.addString2(0,3,"{:0.2f}  C".format(txtTemp)) #2
    lopyLCD.addString(0, 7,  "Package: {}".format(packageCounter))
lopyLCD.drawBuffer()

通过显示来自心率传感器的动画图标和其他信息,可以改进显示格式。

外壳

附件取自 Thingiverse:Lopy 的简单案例从快照中可以看出,我们做了一些改动。外壳左上角的切口用于安装显示器,底座上的孔用于支撑 Pycom 板和 I2C 集线器。盖子也被剪掉了,以便 MikroE 针穿过。使用高速钻进行修改。

 
 
 
pYYBAGNiBfyAMYVgAAiDqsvpDWg579.jpg
 
1 / 4正面
 

把它们放在一起。

现在有趣的部分开始了。下面的快照显示了组装 MoBitals 设备所采取的步骤。

步骤是

  • 使用外部 IO 接头(P9、P10、3V3 Pymodule、GND)将电缆连接到 PyTrack 板,以启用 I2C 总线 2 服务。
  • 将电路板和 I2C 集线器安装到外壳中。
  • 连接 LoPy 4 板。确保正确对齐电路板。
  • 将温度和心率 4 click 板连接到外壳盖上。销钉应该穿过切割。
  • 将原理图所示的相应电缆连接到点击板上。
  • 将 I2C Grove 电缆连接到集线器,
  • 用盖子关闭外壳。在这个原型上,我们使用橡皮筋固定。外壳不够深,无法处理我们获得的 I2C Grove 电缆。这可以通过使用较短的电缆来解决。

您的机箱应该看起来像下面的最后两个快照。

 
 
 
pYYBAGNiBgCAL59YAAIIIwOYSuY544.jpg
 
1 / 7PyTrack I2C 连接
 

一旦设备连接到电源(我们使用 USB 充电电池),心率传感器中的 LED 灯就会将一根手指放在 LED 上方,另一根手指放在温度传感器上方(黑色小方块)。

pYYBAGNiBiGAZqimAAvH2AbPYCQ986.jpg
将手指放在传感器上
 

显示屏将显示以下格式:

pYYBAGNiBiiAEWLuAACmRmDMB4Q918.png
格式显示
 

显示屏显示

  • 左上角第一行:GPS传感器状态;如果传感器锁定在某个位置,它将显示 OK,如果它没有显示 NG(不好)。
  • 右上角第一行:商标 VisteliLabs :)
  • 左上角第二行:检查的患者人数(有无发热的总和)。
  • 右上角第二行:检测到发烧或出现疾病迹象的患者人数。
  • 中央第一行:当前读取的摄氏温度
  • 中央第二行:当前每分钟节拍读数。
  • 右下角:发送的包裹数量。此屏幕中的一个包意味着发送了两个有效负载(报告和患者)

每次读取心率传感器时屏幕都会刷新。

 
 
 
poYBAGNiBiuACrBTAABxdBSgLSw761.png
 
1 / 2发送包裹 1
 

一旦达到发烧条件(在我们的例子中为 29 摄氏度),就会开始向 SigFox 网络传输有效载荷。传输由两个包(每个 12 字节)组成,它们之间有 30 秒的等待时间。第一个有效载荷包含 GPS 坐标和病人计数器;第二个有效载荷包含当前患者的体温和心跳率。

该设备将继续测量生命体征,直到手指从传感器上移开;等待下一位患者的到来。

SigFoxApi——访问数据

第一步是安装 SigFox 库以访问 REST API 接口。使用以下命令

pip install sigfoxapi

您可以在此处找到文档

我们使用 sigfoxapi 与 REST API 服务器进行通信,使用 Tk API 作为用户界面,使用 tkinterTable 显示消息数据,使用 webbrowser 链接到用于 GPSlocation 的 Google 地图服务。

import tkinter as tk
from tkinter.ttk import *
from sigfoxapi import Sigfox
from webbrowser import *
from tkintertable.Tables import TableCanvas
from tkintertable.TableModels import TableModel

然后我们使用在 SigFox 网络部分获得的登录名和密码连接到 SigFox 后端服务器。该函数返回一个用作连接器的对象。使用此对象,我们查询后端以向组返回已注册设备的列表。

#Connect to sigfox backend server
s = Sigfox('login', 'password')
#get a list of the available devices
listDevices = s.devicetype_list()

然后,我们使用列出的设备的 id 元素来获取已注册设备的设备唯一名称。

devices = {}
devCounter =0
#Extract the device id
for dev in listDevices:
   devices[devCounter] = s.device_list(dev['id'])
   devCounter+=1

然后我们检索第一个注册设备收到的所有消息。我们的程序只支持两个设备,因为这就是我们所有的项目预算。如有必要,该程序可以适应更多适配器。

#Get the messages from first device
mes=s.device_messages(devices[0][0]['id'])
data,data1 = createDataSigFox(mes)

The以下代码不是完整版本。为了显示接收数据的解码,它已被最小化。

此函数接收来自已注册设备的消息并解码有效负载。我们使用 struct 包来帮助解码。接收到的数据为字符串格式,使用bytearray.fromhex函数转换为字节数组。然后我们使用 LoPy 4 部分中解释的数据格式。

# PACKAGE_REPORT  = 1
# PACKAGE_PATIENT = 2

def createDataSigFox(sfData):
   data = {}
   data1 = {}


         b = bytearray.fromhex(c['data'])
         dataCode = struct.unpack_from('h', b[10:12], 0)
         if (dataCode[0] != 0):

            for key, value in c.items():
               if (key == 'data'):
                  a = bytearray.fromhex(value)
                  if (dataCode[0] == 1):

                     data[rowName]['Latitude']=struct.unpack_from('f', a[0:4], 0)
                     data[rowName]['Longitude'] = struct.unpack_from('f', a[4:8], 0)

                     data[rowName]['NumPat'] = struct.unpack_from('h', a[8:10], 0)

                  elif (dataCode[0] == 2):
                     data1[rowName]['BPM'] = struct.unpack_from('f', a[0:4], 0)
                     data1[rowName]['Temperature'] = struct.unpack_from('f', a[4:8],0)
                     data1[rowName]['Patient'] = struct.unpack_from('h', a[8:10], 0)

The收到的时间戳也具有挑战性。时间戳作为包含在字符串中的长整数接收。我们将字符串值转换为整数,并使用datetime.fromtimestamp函数来获取时间戳对象。然后为了将值显示为正常的日期/时间格式,我们使用 strftime 函数。

elif (key =='time'):
    mydate = datetime.fromtimestamp(int(value))
    if (dataCode[0] == 1):
       data[rowName]['TImestamp'] = mydate.strftime("%Y-%m-%d %H:%M:%S")

    elif (dataCode[0] == 2):
       data1[rowName]['TImestamp'] = mydate.strftime("%Y-%m-%d %H:%M:%S")

最后,我们使用 tkinterTable 库来显示表格上的数据。

一个挑战是添加一个链接以在地图中显示来自设备的 GPS 坐标。我们使用谷歌地图来显示位置。

以下代码显示了如何将按钮附加到表格单元格以执行 Web 链接并显示地图。

x1, y1, x2, y2 = table.getCellCoords(counter, 5)  
btnText = "Btn{}".format(counter)
print(btnText)
linkGMaps[btnText] = tk.Button(table,,,cursor="hand2")
linkGMaps[btnText].pack()
print(linkGMaps)
linkText = "https://www.google.com/maps/?q="+mydata[c]['GPS']
print(linkText)
linkGMaps[btnText].bind("<Button-1>", lambda e,a=linkText: callback(a))
table.create_window(((x1 + x2) // 2, (y1 + y2) // 2), window=linkGMaps[btnText])

真正的时候到了,在执行程序时,我们会为每个设备获得一个带有两个数据表的单独窗口。设备名称显示在窗口标题中。

第一个数据表显示时间戳、GPS 坐标(请注意,出于隐私目的,我们已删除坐标)、有效载荷时记录的发烧患者数量、接收到的原始数据(有效载荷)和一个按钮触发网络浏览器会话并在 Google 地图上显示位置。

第二个表显示了时间戳、每分钟的心跳次数、测量时记录的温度和患者人数,以及接收到的原始数据。

 
 
 
pYYBAGNiBi6ABw96AAF1Hi95dn4896.png
 
1 / 2数据表第一设备
 

下面的快照显示了 Google 地图上位置 0.0、0.0(纬度、经度)的示例。

poYBAGNiBjOAb3CfAAJyL_VphmE042.png
映射
 

得到教训。意识到!

虽然我们希望有一条清晰的发展道路,但我们没有那么幸运。这是我们必须处理的几件事。

pymakr.conf上,我们有时在将脚本上传到 LoPy 4 板时遇到超时错误。我们发现将 safe_boot_on_upload 设置更改为 true。

{
    "address": "COM26",
    "username": "",
    "password": "",
    "sync_folder": "",
    "open_on_start": true,
    "safe_boot_on_upload": true,
    "py_ignore": [],
    "fast_upload": false
}

我们最初的设计包括 LED 灯和可爱的熊爪,以吸引孩子们的注意力,同时检查他们的生命体征。但是,LED 所需功率的增加会在 LoPy 设备上产生反馈,从而触发 I2C 总线严重错误。我们发现不仅是功耗,而且我们使用 RMT 库来控制 LED 的脉冲会干扰 I2C 时序,或者至少我们是这么认为的。

pYYBAGNiBkyAFsAaAArgpHulo7Q374.jpg
LED熊掌
 

最后,点击板需要特殊的连接才能正常工作。Fever click 要求接地引脚连接到与 I2C 连接相同的原始引脚上。而且,Heart Rate 4 Click 板需要连接 5 V 和 3V3 电源引脚,否则 LED 将不亮,这就是我们使用 I2C 电源连接器添加满足额外需求的原因。

未来的工作

添加压力按钮以允许进行个人设置、实时温度阈值和 BPM 刷新率。

通过这个项目,我们学到了很多东西。从需要像无国界医生这样的第一响应者到将 MikroE 板连接到不合规板的复杂性。

我们希望这个项目可以激发更多应急人员和当地学校可以用来帮助儿童的设备。

谢谢阅读。


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !