Skip to content

Commit 92bafc8

Browse files
committed
Merge branch 'develop' into main
2 parents e42bb8f + a6b2786 commit 92bafc8

File tree

1 file changed

+108
-101
lines changed

1 file changed

+108
-101
lines changed

README.md

+108-101
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,28 @@
11
[![Upload Python Package](https://github.com/n-elia/MAX30102-MicroPython-driver/actions/workflows/python-publish.yml/badge.svg)](https://github.com/n-elia/MAX30102-MicroPython-driver/actions/workflows/python-publish.yml) [![PyPI version](https://badge.fury.io/py/micropython-max30102.svg)](https://badge.fury.io/py/micropython-max30102)
2-
![PyPI - Downloads](https://img.shields.io/pypi/dm/micropython-max30102?color=blue&label=upip%20installations)
2+
3+
34
# Maxim MAX30102 MicroPython driver
45

56
A port of the SparkFun driver for Maxim MAX30102 sensor to MicroPython.
67

78
It _should_ work for MAX30105, too. If you have the chance to test this library with a MAX30105, please leave your feedback in the Discussions section.
89

9-
## Aknowledgements
10-
11-
This work is a lot based on:
12-
13-
- [SparkFun MAX3010x Sensor Library](https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library "GitHub | SparkFun MAX3010x Sensor Library")
14-
15-
Written by **Peter Jansen** and **Nathan Seidle** (SparkFun)
16-
This is a library written for the Maxim MAX30105 Optical Smoke Detector
17-
It should also work with the MAX30102. However, the MAX30102 does not have a Green LED.
18-
These sensors use I2C to communicate, as well as a single (optional)
19-
interrupt line that is not currently supported in this driver.
20-
Written by Peter Jansen and Nathan Seidle (SparkFun)
21-
BSD license, all text above must be included in any redistribution.
22-
23-
- [esp32-micropython](https://github.com/kandizzy/esp32-micropython/blob/master/PPG/ppg/MAX30105.py "GitHub | esp32-micropython")
24-
25-
A port of the library to MicroPython by **kandizzy**
2610

2711
## Disclaimer
2812

2913
This work is not intended to be used in professional environments, and there are no guarantees on its functionalities. Please do not rely on it for medical purposes or professional usage.
3014

31-
## Repository organisation
3215

33-
- Driver: `./max30102`
34-
- Example: `./example`
16+
## Usage
3517

36-
## Changelog
37-
- v0.3.4
38-
- The package has been refactored to be compliant to PEP standards.
39-
- v0.3.3
40-
- Made a PyPi package. Now you can install this package with upip.
41-
- Tested with Raspberry Pi Pico and non-genuine sensors.
42-
- v0.3
43-
- Tested with TinyPico board (based on ESP32-D4) and genuine Maxim MAX30102 sensor.
18+
Driver usage is quite straightforward. You just need to import the library, and to set up a `SoftI2C` instance.
19+
20+
A full example is provided in `/example` directory.
4421

45-
## How to import the library and run the example
46-
Important note: the library will load the default TinyPico ESP32 board I2C configuration (SDA Pin 21, SCL Pin 22, 400kHz speed). If you're using a different board, follow the instructions given below, in *Setup and configuration* section.
22+
### 1 - Including this library into your project
4723

48-
### Including this library into your project (**network-enabled MicroPython ports**)
49-
To include the library into a network-enabled MicroPython project, it's sufficient to install the package:
24+
#### 1a - **network-enabled MicroPython ports**
25+
To include the library into a network-enabled MicroPython project, it's sufficient to install the package using `upip`:
5026

5127
```python
5228
import upip
@@ -57,68 +33,58 @@ Make sure that your firmware runs these lines **after** an Internet connection h
5733

5834
To run the example in `./example` folder, please set your WiFi credentials in `boot.py` and then upload `./example` content into your microcontroller. If you prefer, you can perform a manual install as explained below.
5935

60-
### Including this library into your project (**manual way**)
36+
#### 1b - **manual way** (no Internet access required)
6137

62-
To directly include the library into a MicroPython project, it's sufficient to copy `max30102/circular_buffer.py` and `max30102/max30102.py` next to your `main.py` file, or into a `lib` directory. Then, import the constructor as follows:
38+
To directly include the library into a MicroPython project, it's sufficient to copy `max30102/circular_buffer.py` and `max30102/max30102.py` next to your `main.py` file, into a `lib` directory. Then, import the constructor as follows:
6339

6440
```python
6541
from max30102 import MAX30102
6642
```
6743

68-
To run the example in `./example` folder, copy `max30102/circular_buffer.py` and `max30102/max30102.py` into the `./example/lib` directory. Then, upload the `./example` directory content into your microcontroller.
69-
The content of the board root should appear as follows:
70-
```
71-
/
72-
└── root.py
73-
└── main.py
74-
└── lib
75-
└── circular_buffer.py
76-
└── max30102.py
77-
```
78-
After the upload, press the reset button of your board are you're good to go.
44+
To run the example in `./example` folder, copy `max30102/circular_buffer.py` and `max30102/max30102.py` into the `./example/lib` directory. Then, upload the `./example` directory content into your microcontroller. After the upload, press the reset button of your board are you're good to go.
7945

80-
### Setup and configuration
81-
#### I2C pins
8246

83-
When creating a sensor instance, if you leave the arguments empty, the library will load the default TinyPico ESP32 board I2C configuration (SDA Pin 21, SCL Pin 22, 400kHz speed).
47+
### 2 - I2C setup and sensor configuration
8448

85-
If you have a different board, you can set different I2C pins as shown in the following example:
49+
#### I2C connection
8650

87-
```python
88-
# Default config (ESP32):
89-
sensor = MAX30102()
51+
Create a `SoftI2C` instance as in the following example:
9052

91-
# Alternative:
53+
```python
9254
from machine import SoftI2C, Pin
55+
56+
my_SDA_pin = 21 # I2C SDA pin number here!
57+
my_SCL_pin = 22 # I2C SCL pin number here!
58+
my_i2c_freq = 400000 # I2C frequency (Hz) here!
59+
9360
i2c = SoftI2C(sda=Pin(my_SDA_pin),
9461
scl=Pin(my_SCL_pin),
95-
freq=100000)
96-
sensor = MAX30102(i2cHexAddress = 0x57, i2c = i2cInstance)
62+
freq=my_i2c_freq)
63+
64+
sensor = MAX30102(i2c=i2c)
9765
```
9866

67+
The I2C pin numbers depend on the board that you are using, and how you wired the sensor to it.
68+
9969
#### Sensor setup
100-
Then, the sensor has to be setup. The library provides a method to setup the sensor at once. Leaving the arguments empty, makes the library load their default values.
10170

102-
> Default configuration values:
103-
>
104-
> Led mode: 2 (RED + IR)
105-
>
106-
> ADC range: 16384
107-
>
108-
> Sample rate: 400 Hz
109-
>
110-
> Led power: maximum (50.0mA - Presence detection of ~12 inch)
111-
>
112-
> Averaged samples: 8
71+
The library provides a method to setup the sensor at once. Leaving the arguments empty, makes the library load the default values.
72+
73+
> **Default configuration values:**
11374
>
114-
> pulse width: 411
75+
> _Led mode_: 2 (RED + IR)
76+
> _ADC range_: 16384
77+
> _Sample rate_: 400 Hz
78+
> _Led power_: maximum (50.0mA - Presence detection of ~12 inch)
79+
> _Averaged samples_: 8
80+
> _Pulse width_: 411
11581
11682
```python
11783
# Setup with default values
11884
sensor.setup_sensor()
11985

12086
# Alternative example:
121-
setup_sensor(self, LED_MODE=2, ADC_RANGE=16384, SAMPLE_RATE=400)
87+
setup_sensor(self, led_mode=2, adc_range=16384, sample_rate=400)
12288
```
12389

12490
The library provides the methods to change the configuration parameters one by one, too. Remember that the `setup_sensor()` method has still to be called before modifying the single parameters.
@@ -165,12 +131,14 @@ LED_POWER = MAX30105_PULSEAMP_MEDIUM
165131
sensor.set_active_leds_amplitude(LED_POWER)
166132
```
167133

168-
#### Data acquisition
134+
### 3 - Data acquisition
169135

170136
The sensor will store all the readings into a FIFO register (FIFO_DATA). Based on the number of active LEDs and other configuration paramenters, the sensor instance will read data from that register, putting it into the_storage_. The_storage_ is a circular buffer, that can be read using the provided methods.
171137

172138
The `check()` method polls the sensor to check if new samples are available in the FIFO queue. If data is available, it will be read and put into the _storage_. We can access those samples using the provided methods such as `popRedFromStorage()`.
173139

140+
#### Read data from sensor
141+
174142
As a consequence, this is an example on how the library can be used to read data from the sensor:
175143

176144
```python
@@ -190,7 +158,7 @@ while (True):
190158
print(red_sample, ",", ir_sample)
191159
```
192160

193-
#### Data acquisition rate
161+
#### Notes on data acquisition rate
194162

195163
Considering the sensor configuration, two main parameters will affect the data throughput of the sensor itself:
196164

@@ -213,27 +181,27 @@ However, there are some limitations on sensor side and on micropocessor side tha
213181
# (Assuming that the sensor instance has been already set-up)
214182
from utime import ticks_diff, ticks_ms
215183

216-
# Starting time of the acquisition
217-
t_start = ticks_ms()
218-
# Number of samples that has been collected
219-
samples_n = 0
220-
221-
while (True):
222-
sensor.check()
223-
224-
if (sensor.available()):
225-
# Access the storage FIFO and gather the readings (integers)
226-
red_sample = sensor.pop_red_from_storage()
227-
ir_sample = sensor.pop_ir_from_storage()
228-
229-
# We can compute the real frequency at which we receive data
230-
if (compute_frequency):
231-
samples_n = samples_n + 1
232-
if (ticks_diff(ticks_ms(), t_start) > 999):
233-
f_HZ = samples_n / 1
234-
samples_n = 0
235-
t_start = ticks_ms()
236-
print("Acquisition frequency = ", f_HZ)
184+
t_start = ticks_us() # Starting time of the acquisition
185+
samples_n = 0 # Number of samples that have been collected
186+
187+
while True:
188+
sensor.check()
189+
if sensor.available():
190+
red_reading = sensor.pop_red_from_storage()
191+
ir_reading = sensor.pop_ir_from_storage()
192+
193+
# Print the acquired data (so that it can be plotted with a Serial Plotter)
194+
print(red_reading, ",", ir_reading)
195+
196+
# Compute the real frequency at which we receive data (with microsecond precision)
197+
if compute_frequency:
198+
if ticks_diff(ticks_us(), t_start) >= 999999:
199+
f_HZ = samples_n
200+
samples_n = 0
201+
print("acquisition frequency = ", f_HZ)
202+
t_start = ticks_us()
203+
else:
204+
samples_n = samples_n + 1
237205
```
238206

239207
#### Die temperature reading
@@ -248,23 +216,62 @@ print("Die temperature: ", temperature_C, "°C")
248216

249217
Note: as stated in the [datasheet](https://datasheets.maximintegrated.com/en/ds/MAX30102.pdf), the internal die temperature sensor is intended for calibrating the temperature dependence of the SpO2 subsystem. It has an inherent resolution of 0.0625°C, but be aware that the accuracy is ±1°C.
250218

251-
#### Realtime plot over Serial
252219

253-
The example proposed in this repository ([main.py](../src/main.py)) contains a print statement in this form: `print(red_reading, ",", IR_reading)`. If you open the Arduino IDE, and connect your board to it, then you will be able to open the *serial plotter* (Ctrl+Maiusc+L) and see a real-time plot of your readings (need help? take a look [here](https://learn.sparkfun.com/tutorials/max30105-particle-and-pulse-ox-sensor-hookup-guide/all)).
220+
## Changelog
221+
222+
- v0.3.5
223+
- A `SoftI2C` instance is now required by the constructor.
224+
- The constructor now raises `RuntimeError` when the sensor is not found on I2C bus.
225+
- The example has been updated to intercept the errors thrown by the constructor.
226+
- The example has been updated to estimate real acquisition frequency with a precision of 1 microsecond.
227+
- The readme has been re-organized to improve readability.
228+
- v0.3.4
229+
- The package has been refactored to be compliant to PEP standards.
230+
- v0.3.3
231+
- Made a PyPi package. Now you can install this package with upip.
232+
- Tested with Raspberry Pi Pico and non-genuine sensors.
233+
- v0.3
234+
- Tested with TinyPico board (based on ESP32-D4) and genuine Maxim MAX30102 sensor.
235+
236+
237+
## Aknowledgements
238+
239+
This work is a lot based on:
240+
241+
- [SparkFun MAX3010x Sensor Library](https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library "GitHub | SparkFun MAX3010x Sensor Library")
242+
243+
Written by **Peter Jansen** and **Nathan Seidle** (SparkFun)
244+
This is a library written for the Maxim MAX30105 Optical Smoke Detector
245+
It should also work with the MAX30102. However, the MAX30102 does not have a Green LED.
246+
These sensors use I2C to communicate, as well as a single (optional)
247+
interrupt line that is not currently supported in this driver.
248+
Written by Peter Jansen and Nathan Seidle (SparkFun)
249+
BSD license, all text above must be included in any redistribution.
250+
251+
- [esp32-micropython](https://github.com/kandizzy/esp32-micropython/blob/master/PPG/ppg/MAX30105.py "GitHub | esp32-micropython")
252+
253+
A port of the library to MicroPython by **kandizzy**
254+
255+
256+
## Other useful things
257+
258+
### Realtime plot over Serial
259+
260+
The example proposed in this repository ([main.py](./example/main.py)) contains a print statement in a CSV-like format: `print(red_reading, ",", IR_reading)`. If you open Arduino IDE and connect your board, then you will be able to open the *serial plotter* (Ctrl+Maiusc+L) and see a real-time plot of your readings (need some help? take a look [here](https://learn.sparkfun.com/tutorials/max30105-particle-and-pulse-ox-sensor-hookup-guide/all)).
261+
254262
For instance, this is an example of my heartbeat taken on the index finger:
255263

256264
![Serial Plotter picture](./img/arduino-IDE-serial-plotter-heartbeat.png "Serial Plotter picture")
257265

258266
### Tested platforms
259267

260-
The example works well on TinyPico (ESP32-D4 board) running 'tinypico-20210418-v1.15.bin' MicroPython firmware, connected to a genuine Maxim 30102 breakout board ([MAXREFDES117#](https://www.maximintegrated.com/en/design/reference-design-center/system-board/6300.html)).
268+
The library has been tested on TinyPico (ESP32-D4 board) running 'tinypico-20210418-v1.15.bin' MicroPython firmware, connected to a genuine Maxim 30102 breakout board ([MAXREFDES117#](https://www.maximintegrated.com/en/design/reference-design-center/system-board/6300.html)).
261269

262270
Tested ([thanks to ebolisa](https://github.com/n-elia/MAX30102-MicroPython-driver/issues/4)) and working on Raspberry Pi Pico + non-Maxim breakout board.
263271

264-
### Other things that it's worth mentioning
265-
266-
- There is an issue involving chinese clones of the Maxim MAX30102: some of them appear to have the red and IR registers inverted (or maybe the LEDs swapped) (see [here](https://github.com/aromring/MAX30102_by_RF/issues/13)). You can easily check if your sensor is inverted by putting it in LED mode 1: only the red LED should work. If you see the IR LED (use your phone camera to check), then you have to collect IR samples as red ones and viceversa.
272+
### Sensor clones
267273

268-
- The sensor has to be wired to the I2C pins of your board
274+
There is an issue involving chinese clones of the Maxim MAX30102: some of them appear to have the red and IR registers inverted (or maybe the LEDs swapped) (see [here](https://github.com/aromring/MAX30102_by_RF/issues/13)). You can easily check if your sensor is inverted by putting it in LED mode 1: only the red LED should work. If you see the IR LED (use your phone camera to check), then you have to collect IR samples as red ones and viceversa.
269275

270-
- If you're looking for algorithms for extracting heartrate and SPO2 from your RAW data, take a look [here](https://github.com/aromring/MAX30102_by_RF) and [here](https://github.com/kandizzy/esp32-micropython/tree/master/PPG)
276+
### Heartrate and SPO2 estimation
277+
If you're looking for algorithms for extracting heartrate and SPO2 from your RAW data, take a look [here](https://github.com/aromring/MAX30102_by_RF) and [here](https://github.com/kandizzy/esp32-micropython/tree/master/PPG)

0 commit comments

Comments
 (0)