Skip to content

Commit f6e6adc

Browse files
icyqwqlbuque
authored andcommitted
libs/hat: Support Heart hat.
Signed-off-by: icyqwq <[email protected]> lbuque <[email protected]>
1 parent d8cb242 commit f6e6adc

File tree

14 files changed

+2338
-0
lines changed

14 files changed

+2338
-0
lines changed

docs/en/hats/heart.rst

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
2+
HeartHat
3+
=========
4+
5+
.. include:: ../refs/hat.heart.ref
6+
7+
MAX30102 is a complete pulse oximetry and heart-rate sensor system solution designed for the demanding requirements of wearable devices.
8+
9+
Support the following products:
10+
11+
|HeartHat|
12+
13+
Micropython Example:
14+
15+
.. literalinclude:: ../../../examples/hat/heart/stickc_heart_example.py
16+
:language: python
17+
:linenos:
18+
19+
20+
UIFLOW2 Example:
21+
22+
|example.png|
23+
24+
25+
.. only:: builder_html
26+
27+
|stickc_heart_example.m5f2|
28+
29+
30+
class HeartHat
31+
---------------
32+
33+
Constructors
34+
------------
35+
36+
.. class:: HeartHat(i2c, address)
37+
38+
Initialize the HeartHat.
39+
40+
- ``i2c``: I2C port to use.
41+
- ``address``: I2C address of the HeartHat.
42+
43+
UIFLOW2:
44+
45+
|init.png|
46+
47+
48+
Methods
49+
-------
50+
51+
.. method:: HeartHat.stop()
52+
53+
Stop the HeartHat.
54+
55+
56+
UIFLOW2:
57+
58+
|stop.png|
59+
60+
.. method:: HeartHat.start()
61+
62+
Start the HeartHat.
63+
64+
65+
UIFLOW2:
66+
67+
|start.png|
68+
69+
.. method:: HeartHat.deinit()
70+
71+
Deinitialize the HeartHat.
72+
73+
74+
UIFLOW2:
75+
76+
|deinit.png|
77+
78+
.. method:: HeartHat.get_heart_rate()
79+
80+
Get the heart rate.
81+
82+
83+
UIFLOW2:
84+
85+
|get_heart_rate.png|
86+
87+
.. method:: HeartHat.get_spo2()
88+
89+
Get the SpO2.
90+
91+
92+
UIFLOW2:
93+
94+
|get_spo2.png|
95+
96+
.. method:: HeartHat.get_ir()
97+
98+
Get the IR value.
99+
100+
101+
UIFLOW2:
102+
103+
|get_ir.png|
104+
105+
.. method:: HeartHat.get_red()
106+
107+
Get the red value.
108+
109+
110+
UIFLOW2:
111+
112+
|get_red.png|
113+
114+
.. method:: HeartHat.set_mode(mode)
115+
116+
Set the mode of the HeartHat.
117+
118+
:param int mode: The detect mode of the HeartHat.
119+
Options:
120+
- ``HeartHat.MODE_HR_ONLY``: Only heart rate
121+
- ``HeartHat.MODE_SPO2_HR``: Heart rate and SpO2
122+
123+
UIFLOW2:
124+
125+
|set_mode.png|
126+
127+
.. method:: HeartHat.set_led_current(led_current)
128+
129+
Set the LED current of the HeartHat.
130+
131+
:param int led_current: The LED current of the HeartHat.
132+
Options:
133+
- ``HeartHat.LED_CURRENT_0MA``: 0mA
134+
- ``HeartHat.LED_CURRENT_4_4MA``: 4.4mA
135+
- ``HeartHat.LED_CURRENT_7_6MA``: 7.6mA
136+
- ``HeartHat.LED_CURRENT_11MA``: 11mA
137+
- ``HeartHat.LED_CURRENT_14_2MA``: 14.2mA
138+
- ``HeartHat.LED_CURRENT_17_4MA``: 17.4mA
139+
- ``HeartHat.LED_CURRENT_20_8MA``: 20.8mA
140+
- ``HeartHat.LED_CURRENT_24MA``: 24mA
141+
- ``HeartHat.LED_CURRENT_27_1MA``: 27.1mA
142+
- ``HeartHat.LED_CURRENT_30_6MA``: 30.6mA
143+
- ``HeartHat.LED_CURRENT_33_8MA``: 33.8mA
144+
- ``HeartHat.LED_CURRENT_37MA``: 37mA
145+
- ``HeartHat.LED_CURRENT_40_2MA``: 40.2mA
146+
- ``HeartHat.LED_CURRENT_43_6MA``: 43.6mA
147+
- ``HeartHat.LED_CURRENT_46_8MA``: 46.8mA
148+
- ``HeartHat.LED_CURRENT_50MA``: 50mA
149+
150+
UIFLOW2:
151+
152+
|set_led_current.png|
153+
154+
.. method:: HeartHat.set_pulse_width(pulse_width)
155+
156+
Set the pulse width of the HeartHat.
157+
158+
:param int pulse_width: The pulse width of the HeartHat.
159+
Options:
160+
- ``HeartHat.PULSE_WIDTH_200US_ADC_13``: 200us
161+
- ``HeartHat.PULSE_WIDTH_400US_ADC_14``: 400us
162+
- ``HeartHat.PULSE_WIDTH_800US_ADC_15``: 800us
163+
- ``HeartHat.PULSE_WIDTH_1600US_ADC_16``: 1600us
164+
165+
UIFLOW2:
166+
167+
|set_pulse_width.png|
168+
169+
.. method:: HeartHat.set_sampling_rate(sampling_rate)
170+
171+
Set the sampling rate of the HeartHat.
172+
173+
:param int sampling_rate: The sampling rate of the HeartHat.
174+
Options:
175+
- ``HeartHat.SAMPLING_RATE_50HZ``: 50Hz
176+
- ``HeartHat.SAMPLING_RATE_100HZ``: 100Hz
177+
- ``HeartHat.SAMPLING_RATE_167HZ``: 167Hz
178+
- ``HeartHat.SAMPLING_RATE_200HZ``: 200Hz
179+
- ``HeartHat.SAMPLING_RATE_400HZ``: 400Hz
180+
- ``HeartHat.SAMPLING_RATE_600HZ``: 600Hz
181+
- ``HeartHat.SAMPLING_RATE_800HZ``: 800Hz
182+
- ``HeartHat.SAMPLING_RATE_1000HZ``: 1000Hz
183+
184+
UIFLOW2:
185+
186+
|set_sampling_rate.png|
187+
188+
189+
190+
Constants
191+
---------
192+
193+
.. data:: HeartHat.MODE_HR_ONLY
194+
195+
Detect heart rate only.
196+
197+
.. data:: HeartHat.MODE_SPO2_HR
198+
199+
Detect heart rate and SpO2.
200+
201+
202+

docs/en/hats/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ HAT
2323
thermal.rst
2424
tof.rst
2525
vibrator.rst
26+
heart.rst

docs/en/refs/hat.heart.ref

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
.. |HeartHat| image:: https://static-cdn.m5stack.com/resource/docs/products/hat/hat_heart_rate/hat_heart_rate_01.webp
3+
:target: https://docs.m5stack.com/en/hat/hat_heart_rate
4+
:height: 200px
5+
:width: 200px
6+
7+
.. |init.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/init.png
8+
.. |stop.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/stop.png
9+
.. |start.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/start.png
10+
.. |deinit.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/deinit.png
11+
.. |get_heart_rate.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/get_heart_rate.png
12+
.. |get_spo2.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/get_spo2.png
13+
.. |get_ir.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/get_ir.png
14+
.. |get_red.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/get_red.png
15+
.. |set_mode.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/set_mode.png
16+
.. |set_led_current.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/set_led_current.png
17+
.. |set_pulse_width.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/set_pulse_width.png
18+
.. |set_sampling_rate.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/set_sampling_rate.png
19+
20+
.. |example.png| image:: https://static-cdn.m5stack.com/mpy_docs/hat/heart/example.png
21+
22+
.. |stickc_heart_example.m5f2| raw:: html
23+
24+
<a
25+
href="https://uiflow2.m5stack.com/?example=https://raw.githubusercontent.com/m5stack/uiflow-micropython/develop/examples/hat/heart/stickc_heart_example.m5f2"
26+
target="_blank"
27+
>
28+
stickc_heart_example.m5f2
29+
</a>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"version":"V2.0","versionNumber":"V2.1.2","type":"stickc-plus","components":[{"name":"screen","type":"screen","layer":0,"screenId":"builtin","screenName":"","id":"__stickc-plus_screen","createTime":1723702938574,"x":0,"y":0,"width":135,"height":240,"backgroundColor":"#000000","size":0,"isSelected":true},{"name":"Heart","type":"label","layer":1,"screenId":"builtin","screenName":"","id":"nEvv52m+sRBkR$O9","createTime":1723703055756,"x":1,"y":2,"color":"#ffffff","backgroundColor":"#222222","text":"Heart","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false,"width":51,"height":20},{"name":"SPO2","type":"label","layer":2,"screenId":"builtin","screenName":"","id":"v5eL%H+FXIB-tV8K","createTime":1723703061050,"x":3,"y":122,"color":"#ffffff","backgroundColor":"#222222","text":"SPO2","engine":"gfx","font":"Widgets.FONTS.DejaVu18","rotation":0,"isSelected":false,"width":49,"height":20},{"name":"label0","type":"label","layer":3,"screenId":"builtin","screenName":"","id":"n^-v9d=#Via$is8U","createTime":1723703097718,"x":5,"y":45,"color":"#ffffff","backgroundColor":"#222222","text":"0","engine":"gfx","font":"Widgets.FONTS.DejaVu40","rotation":0,"isSelected":false,"width":25,"height":45},{"name":"label1","type":"label","layer":4,"screenId":"builtin","screenName":"","id":"juUa3qz7BZ4!8@$*","createTime":1723703097718,"x":7,"y":169,"color":"#ffffff","backgroundColor":"#222222","text":"0","engine":"gfx","font":"Widgets.FONTS.DejaVu40","rotation":0,"isSelected":false,"width":25,"height":45}],"resources":[{"hardware":["hardware_button","hardware_pin_button","imu","speaker","ir","i2c"]},{"hat":["hat_heart"]}],"units":[],"hats":[{"type":"hat_heart","name":"hat_heart_0","id":"a5oMuqC9*TjbzAZ%","createTime":1723702946655,"bus":"i2c0","busList":["i2c0"],"initBlockType":"hat_heart_init","initBlockId":"T{I+vZ-T9}2V(FXLCaJt"}],"bases":[],"i2cs":[{"id":"i2c0","portType":"HAT","userPort":[22,21],"freq":"100000","blockId":"k0o$NtzBZa=6GXD]Lg`l"}],"blockly":"<block type=\"basic_on_setup\" id=\"setup_block\" deletable=\"false\" x=\"50\" y=\"50\"><mutation isBegin=\"true\"></mutation><field name=\"UPDATEOP\">true</field><statement name=\"FUNC\"><block type=\"system_m5_begin\" id=\"system_m5_begin\"><next><block type=\"i2c_init\" id=\"k0o$NtzBZa=6GXD]Lg`l\"><field name=\"NAME\">0</field><field name=\"FREQ\">100000</field><value name=\"SCL\"><shadow type=\"math_number\" id=\"h2*$04GK=yn#/Mo{I$1j\"><mutation max=\"Infinity\" min=\"-Infinity\" precision=\"0\"></mutation><field name=\"NUM\">26</field></shadow></value><value name=\"SDA\"><shadow type=\"math_number\" id=\"3)-|4RV^b{5^_f3~}{(3\"><mutation max=\"Infinity\" min=\"-Infinity\" precision=\"0\"></mutation><field name=\"NUM\">0</field></shadow></value><next><block type=\"hat_heart_init\" id=\"T{I+vZ-T9}2V(FXLCaJt\"><field name=\"NAME\">hat_heart_0</field><value name=\"ADDR\"><shadow type=\"hat_heart_addr_option\" id=\"t{Nceh[n,Vl:[(|M-2$h\"><field name=\"VALUE\">0x57</field></shadow></value><next><block type=\"hat_heart_start\" id=\"^ScFzn]dfN|;1bgP;GjE\"><field name=\"NAME\">hat_heart_0</field></block></next></block></next></block></next></block></statement></block><block type=\"basic_on_loop\" id=\"loop_block\" deletable=\"false\" x=\"470\" y=\"50\"><mutation isUpdate=\"true\"></mutation><field name=\"UPDATEOP\">true</field><statement name=\"FUNC\"><block type=\"system_m5_update\" id=\"system_m5_update\"><next><block type=\"label_set_text\" id=\"@nXM*hTDO~UWNwDo}(HA\"><field name=\"NAME\">label0</field><value name=\"TEXT\"><shadow type=\"text\" id=\";Xd:+6R_r.KqGK(zsmxx\"><field name=\"TEXT\">Label</field></shadow><block type=\"hat_heart_get_heart_rate\" id=\"Et~Q#z7mLw5bWpN*AI!6\"><field name=\"NAME\">hat_heart_0</field></block></value><next><block type=\"label_set_text\" id=\"Xe]N~AebX!vRDY(O/=A/\"><field name=\"NAME\">label1</field><value name=\"TEXT\"><shadow type=\"text\" id=\";Xd:+6R_r.KqGK(zsmxx\"><field name=\"TEXT\">Label</field></shadow><block type=\"hat_heart_get_spo2\" id=\"Ao*M@pkgU;OP:C]@6|Fy\"><field name=\"NAME\">hat_heart_0</field></block></value></block></next></block></next></block></statement></block>","screen":[{"simulationName":"Built-in","type":"builtin","width":135,"height":240,"scale":0.92,"screenName":"","blockId":"","screenColorType":0,"id":"builtin","createTime":1723702938573}],"logicWhenNum":0,"customList":[]}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
2+
#
3+
# SPDX-License-Identifier: MIT
4+
5+
import os, sys, io
6+
import M5
7+
from M5 import *
8+
from hardware import *
9+
from hat import HeartHat
10+
11+
12+
Heart = None
13+
SPO2 = None
14+
label0 = None
15+
label1 = None
16+
i2c0 = None
17+
hat_heart_0 = None
18+
19+
20+
def setup():
21+
global Heart, SPO2, label0, label1, i2c0, hat_heart_0
22+
23+
M5.begin()
24+
Heart = Widgets.Label("Heart", 1, 2, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18)
25+
SPO2 = Widgets.Label("SPO2", 3, 122, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu18)
26+
label0 = Widgets.Label("0", 5, 45, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu40)
27+
label1 = Widgets.Label("0", 7, 169, 1.0, 0xFFFFFF, 0x222222, Widgets.FONTS.DejaVu40)
28+
29+
i2c0 = I2C(0, scl=Pin(26), sda=Pin(0), freq=100000)
30+
hat_heart_0 = HeartHat(i2c0, 0x57)
31+
hat_heart_0.start()
32+
33+
34+
def loop():
35+
global Heart, SPO2, label0, label1, i2c0, hat_heart_0
36+
M5.update()
37+
label0.setText(str(hat_heart_0.get_heart_rate()))
38+
label1.setText(str(hat_heart_0.get_spo2()))
39+
40+
41+
if __name__ == "__main__":
42+
try:
43+
setup()
44+
while True:
45+
loop()
46+
except (Exception, KeyboardInterrupt) as e:
47+
try:
48+
from utility import print_error_msg
49+
50+
print_error_msg(e)
51+
except ImportError:
52+
print("please update to latest firmware")

m5stack/cmodules/cdriver/cdriver.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
#include "mphalport.h"
1414

1515
extern const mp_obj_module_t mp_module_max30100;
16+
extern const mp_obj_module_t mp_module_max30102;
1617

1718
STATIC const mp_rom_map_elem_t mp_module_cdriver_globals_table[] = {
1819
/* *FORMAT-OFF* */
1920
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cdriver) },
2021
{ MP_ROM_QSTR(MP_QSTR_max30100), MP_OBJ_FROM_PTR(&mp_module_max30100) },
22+
{ MP_ROM_QSTR(MP_QSTR_max30102), MP_OBJ_FROM_PTR(&mp_module_max30102) },
2123
/* *FORMAT-ON* */
2224
};
2325
STATIC MP_DEFINE_CONST_DICT(mp_module_cdriver_globals, mp_module_cdriver_globals_table);

m5stack/cmodules/cdriver/cdriver.cmake

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ target_sources(usermod_DRIVER INTERFACE
88
${CMAKE_CURRENT_LIST_DIR}/cdriver.c
99
${CMAKE_CURRENT_LIST_DIR}/max30100/max30100.c
1010
${CMAKE_CURRENT_LIST_DIR}/max30100/driver_max30100.c
11+
${CMAKE_CURRENT_LIST_DIR}/max30102/max30102.c
12+
${CMAKE_CURRENT_LIST_DIR}/max30102/driver_max30102.c
1113
)
1214

1315
target_include_directories(usermod_DRIVER INTERFACE

0 commit comments

Comments
 (0)