Skip to content

Commit eeb3bee

Browse files
authored
Merge pull request #9 from n-elia/refactoring
Refactoring for PEP compliance. Bumps to v0.3.4.
2 parents 39a8abe + d29cd25 commit eeb3bee

File tree

8 files changed

+921
-957
lines changed

8 files changed

+921
-957
lines changed

README.md

+84-73
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
[![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)
1+
[![PyPi Build and Upload](https://github.com/n-elia/MAX30102-MicroPython-driver/actions/workflows/python-publish.yml/badge.svg?event=release)](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)
22
# Maxim MAX30102 MicroPython driver
33

44
A port of the SparkFun driver for Maxim MAX30102 sensor to MicroPython.
5-
It _should_ work for MAX30105, too. Please check if it works for MAX30105 and report in the Discussions section :)
5+
6+
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.
67

78
## Aknowledgements
89

@@ -31,14 +32,19 @@ This work is not intended to be used in professional environments, and there are
3132
- Driver: `./max30102`
3233
- Example: `./example`
3334

34-
## Additional information
35-
36-
This driver has been tested with Maxim Integrated MAX30102 sensor.
37-
However, it *should* work with MAX30105 sensor, too.
35+
## Changelog
36+
- v0.3.4
37+
- The package has been refactored to be compliant to PEP standards.
38+
- v0.3.3
39+
- Made a PyPi package. Now you can install this package with upip.
40+
- Tested with Raspberry Pi Pico and non-genuine sensors.
41+
- v0.3
42+
- Tested with TinyPico board (based on ESP32-D4) and genuine Maxim MAX30102 sensor.
3843

39-
### How to import the library and run the example
44+
## How to import the library and run the example
45+
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.
4046

41-
#### Including this library into your project (network-enabled MicroPython ports)
47+
### Including this library into your project (**network-enabled MicroPython ports**)
4248
To include the library into a network-enabled MicroPython project, it's sufficient to install the package:
4349

4450
```python
@@ -50,32 +56,37 @@ Make sure that your firmware runs these lines **after** an Internet connection h
5056

5157
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.
5258

53-
#### Including this library into your project (manual way)
59+
### Including this library into your project (**manual way**)
5460

55-
To directly include the library into a MicroPython project, it's sufficient to copy the `max30102` module next to your `main.py`, and then import it as follows:
61+
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. Then, import the constructor as follows:
5662

5763
```python
5864
from max30102 import MAX30102
5965
```
6066

61-
For instance, to run the example in `./example` folder, copy the `./max30102` directory and paste it in the `./example` folder. Then, upload `./example` content into your microcontroller and run it.
67+
To run the example in `./example` folder, copy `max30102/circular_buffer.py` and `max30102/max30102.py` into the `./example` directory. Then, upload the `./example` directory content into your microcontroller.
68+
69+
70+
### Setup and configuration
71+
#### I2C pins
6272

63-
#### Setup and configuration
73+
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).
6474

65-
At first, create 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).
75+
If you have a different board, you can set different I2C pins as shown in the following example:
6676

6777
```python
6878
# Default config (ESP32):
6979
sensor = MAX30102()
7080

7181
# Alternative:
7282
from machine import SoftI2C, Pin
73-
i2cInstance = SoftI2C(sda=Pin(my_SDA_pin),
74-
scl=Pin(my_SCL_pin),
75-
freq=100000)
83+
i2c = SoftI2C(sda=Pin(my_SDA_pin),
84+
scl=Pin(my_SCL_pin),
85+
freq=100000)
7686
sensor = MAX30102(i2cHexAddress = 0x57, i2c = i2cInstance)
7787
```
7888

89+
#### Sensor setup
7990
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.
8091

8192
> Default configuration values:
@@ -104,44 +115,44 @@ The library provides the methods to change the configuration parameters one by o
104115

105116
```python
106117
# Set the number of samples to be averaged by the chip
107-
SAMPLE_AVG = 8 # Options: 1, 2, 4, 8, 16, 32
108-
self.setFIFOAverage(SAMPLE_AVG)
118+
SAMPLE_AVG = 8 # Options: 1, 2, 4, 8, 16, 32
119+
self.set_fifo_average(SAMPLE_AVG)
109120

110121
# Set the ADC range
111-
ADC_RANGE = 4096 # Options: 2048, 4096, 8192, 16384
112-
self.setADCRange(ADC_RANGE)
122+
ADC_RANGE = 4096 # Options: 2048, 4096, 8192, 16384
123+
self.set_adc_range(ADC_RANGE)
113124

114125
# Set the sample rate
115-
SAMPLE_RATE = 400 # Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
116-
self.setSampleRate(SAMPLE_RATE)
126+
SAMPLE_RATE = 400 # Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
127+
self.set_sample_rate(SAMPLE_RATE)
117128

118129
# Set the Pulse Width
119-
PULSE_WIDTH = 118 # Options: 69, 118, 215, 411
120-
self.setPulseWidth(PULSE_WIDTH)
130+
PULSE_WIDTH = 118 # Options: 69, 118, 215, 411
131+
self.set_pulse_width(PULSE_WIDTH)
121132

122133
# Set the LED mode
123-
LED_MODE = 2 # Options: 1 (red), 2 (red + IR), 3 (red + IR + g - MAX30105 only)
124-
self.setLEDMode(LED_MODE)
134+
LED_MODE = 2 # Options: 1 (red), 2 (red + IR), 3 (red + IR + g - MAX30105 only)
135+
self.set_led_mode(LED_MODE)
125136

126137
# Set the LED brightness of each LED
127138
LED_POWER = MAX30105_PULSEAMP_MEDIUM
128139
# Options:
129-
# MAX30105_PULSEAMP_LOWEST = 0x02 # 0.4mA - Presence detection of ~4 inch
130-
# MAX30105_PULSEAMP_LOW = 0x1F # 6.4mA - Presence detection of ~8 inch
131-
# MAX30105_PULSEAMP_MEDIUM = 0x7F # 25.4mA - Presence detection of ~8 inch
132-
# MAX30105_PULSEAMP_HIGH = 0xFF # 50.0mA - Presence detection of ~12 inch
133-
self.setPulseAmplitudeRed(LED_POWER)
134-
self.setPulseAmplitudeIR(LED_POWER)
135-
self.setPulseAmplitudeGreen(LED_POWER)
140+
# MAX30105_PULSE_AMP_LOWEST = 0x02 # 0.4mA - Presence detection of ~4 inch
141+
# MAX30105_PULSE_AMP_LOW = 0x1F # 6.4mA - Presence detection of ~8 inch
142+
# MAX30105_PULSE_AMP_MEDIUM = 0x7F # 25.4mA - Presence detection of ~8 inch
143+
# MAX30105_PULSE_AMP_HIGH = 0xFF # 50.0mA - Presence detection of ~12 inch
144+
self.set_pulse_amplitude_red(LED_POWER)
145+
self.set_pulse_amplitude_it(LED_POWER)
146+
self.set_pulse_amplitude_green(LED_POWER)
136147

137148
# Set the LED brightness of all the active LEDs
138149
LED_POWER = MAX30105_PULSEAMP_MEDIUM
139150
# Options:
140-
# MAX30105_PULSEAMP_LOWEST = 0x02 # 0.4mA - Presence detection of ~4 inch
141-
# MAX30105_PULSEAMP_LOW = 0x1F # 6.4mA - Presence detection of ~8 inch
142-
# MAX30105_PULSEAMP_MEDIUM = 0x7F # 25.4mA - Presence detection of ~8 inch
143-
# MAX30105_PULSEAMP_HIGH = 0xFF # 50.0mA - Presence detection of ~12 inch
144-
sensor.setActiveLEDsAmplitude(LED_POWER)
151+
# MAX30105_PULSE_AMP_LOWEST = 0x02 # 0.4mA - Presence detection of ~4 inch
152+
# MAX30105_PULSE_AMP_LOW = 0x1F # 6.4mA - Presence detection of ~8 inch
153+
# MAX30105_PULSE_AMP_MEDIUM = 0x7F # 25.4mA - Presence detection of ~8 inch
154+
# MAX30105_PULSE_AMP_HIGH = 0xFF # 50.0mA - Presence detection of ~12 inch
155+
sensor.set_active_leds_amplitude(LED_POWER)
145156
```
146157

147158
#### Data acquisition
@@ -153,20 +164,20 @@ The `check()` method polls the sensor to check if new samples are available in t
153164
As a consequence, this is an example on how the library can be used to read data from the sensor:
154165

155166
```python
156-
while(True):
157-
# The check() method has to be continuously polled, to check if
158-
# there are new readings into the sensor's FIFO queue. When new
159-
# readings are available, this function will put them into the storage.
160-
sensor.check()
161-
162-
# Check if the storage contains available samples
163-
if(sensor.available()):
164-
# Access the storage FIFO and gather the readings (integers)
165-
red_sample = sensor.popRedFromStorage()
166-
ir_sample = sensor.popIRFromStorage()
167-
168-
# Print the acquired data (can be plot with Arduino Serial Plotter)
169-
print(red_sample, ",", ir_sample)
167+
while (True):
168+
# The check() method has to be continuously polled, to check if
169+
# there are new readings into the sensor's FIFO queue. When new
170+
# readings are available, this function will put them into the storage.
171+
sensor.check()
172+
173+
# Check if the storage contains available samples
174+
if (sensor.available()):
175+
# Access the storage FIFO and gather the readings (integers)
176+
red_sample = sensor.pop_red_from_storage()
177+
ir_sample = sensor.pop_ir_from_storage()
178+
179+
# Print the acquired data (can be plot with Arduino Serial Plotter)
180+
print(red_sample, ",", ir_sample)
170181
```
171182

172183
#### Data acquisition rate
@@ -183,7 +194,7 @@ The library computes this value, that can be accessed with:
183194

184195
```python
185196
# Get the estimated acquisition rate
186-
acquisition_rate = sensor.getAcquisitionFrequency()
197+
acquisition_rate = sensor.get_acquisition_frequency()
187198
```
188199

189200
However, there are some limitations on sensor side and on micropocessor side that may affect the acquisition rate (see issue #6 for more details about it). Is is possible to measure the real throughput as in [this](https://github.com/sparkfun/SparkFun_MAX3010x_Sensor_Library/blob/master/examples/Example9_RateTesting/Example9_RateTesting.ino) example sketch by SparkFun, using the following snippet:
@@ -195,33 +206,33 @@ from utime import ticks_diff, ticks_ms
195206
# Starting time of the acquisition
196207
t_start = ticks_ms()
197208
# Number of samples that has been collected
198-
samples_n = 0
199-
200-
while(True):
201-
sensor.check()
202-
203-
if(sensor.available()):
204-
# Access the storage FIFO and gather the readings (integers)
205-
red_sample = sensor.popRedFromStorage()
206-
ir_sample = sensor.popIRFromStorage()
207-
208-
# We can compute the real frequency at which we receive data
209-
if (compute_frequency):
210-
samples_n=samples_n+1
211-
if ( ticks_diff(ticks_ms(), t_start) > 999 ):
212-
f_HZ = samples_n/1
213-
samples_n = 0
214-
t_start = ticks_ms()
215-
print("Acquisition frequency = ",f_HZ)
209+
samples_n = 0
210+
211+
while (True):
212+
sensor.check()
213+
214+
if (sensor.available()):
215+
# Access the storage FIFO and gather the readings (integers)
216+
red_sample = sensor.pop_red_from_storage()
217+
ir_sample = sensor.pop_ir_from_storage()
218+
219+
# We can compute the real frequency at which we receive data
220+
if (compute_frequency):
221+
samples_n = samples_n + 1
222+
if (ticks_diff(ticks_ms(), t_start) > 999):
223+
f_HZ = samples_n / 1
224+
samples_n = 0
225+
t_start = ticks_ms()
226+
print("Acquisition frequency = ", f_HZ)
216227
```
217228

218229
#### Die temperature reading
219230

220-
The `readTemperature()` method allows to read the internal die temperature. An example is proposed below.
231+
The `read_temperature()` method allows to read the internal die temperature. An example is proposed below.
221232

222233
```python
223234
# Read the die temperature in Celsius degree
224-
temperature_C = sensor.readTemperature()
235+
temperature_C = sensor.read_temperature()
225236
print("Die temperature: ", temperature_C, "°C")
226237
```
227238

example/boot.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# This file is executed on every boot (including wake-boot from deepsleep)
1+
# This file is executed on every boot (including wake-boot from deep sleep)
22

3-
def do_connect(ssid:str, password:str):
3+
def do_connect(ssid: str, password: str):
44
import network
55
wlan = network.WLAN(network.STA_IF)
66
wlan.active(True)
@@ -13,17 +13,17 @@ def do_connect(ssid:str, password:str):
1313

1414

1515
if __name__ == '__main__':
16-
# Put yor WiFi credentials here
16+
# Put yor Wi-Fi credentials here
1717
my_ssid = "my_ssid"
1818
my_pass = "my_password"
1919

2020
try:
21-
import max30102
21+
from max30102 import MAX30102
2222
except:
23+
print("'max30102' not found!")
2324
try:
2425
import upip
2526
do_connect(my_ssid, my_pass)
2627
upip.install("micropython-max30102")
2728
except:
2829
print("Unable to get 'micropython-max30102' package!")
29-

example/main.py

+32-31
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,70 @@
11
# main.py
22
from machine import sleep
3-
from max30102 import MAX30102
43
from utime import ticks_diff, ticks_ms
54

5+
from max30102 import MAX30102
6+
67
if __name__ == '__main__':
78
# Sensor instance. If left empty, loads default ESP32 I2C configuration
89
sensor = MAX30102()
910
# Alternatively (for other boards):
1011
# sensor = MAX30102(i2cHexAddress = 0x57)
1112
# sensor = MAX30102(i2cHexAddress = 0x57, i2c = i2cInstance)
12-
13+
1314
# The default sensor configuration is:
1415
# Led mode: 2 (RED + IR)
1516
# ADC range: 16384
1617
# Sample rate: 400 Hz
1718
# Led power: maximum (50.0mA - Presence detection of ~12 inch)
1819
# Averaged samples: 8
1920
# pulse width: 411
20-
21-
# It's possible to setup the sensor at once with the setup_sensor() method.
21+
22+
# It's possible to set up the sensor at once with the setup_sensor() method.
2223
# If no parameters are supplied, the default config is loaded.
2324
print("Setting up sensor with default configuration.", '\n')
2425
sensor.setup_sensor()
25-
26+
2627
# It is also possible to tune the configuration parameters one by one.
2728
# Set the sample rate to 800: 800 samples/s are collected by the sensor
28-
sensor.setSampleRate(800)
29+
sensor.set_sample_rate(800)
2930
# Set the number of samples to be averaged per each reading
30-
sensor.setFIFOAverage(8)
31-
31+
sensor.set_fifo_average(8)
32+
3233
sleep(1)
3334

3435
# The readTemperature() method allows to extract the die temperature in °C
3536
print("Reading temperature in °C.", '\n')
36-
print(sensor.readTemperature())
37-
38-
# Select wether to compute the acquisition frequency or not
39-
compute_frequency = False
40-
37+
print(sensor.read_temperature())
38+
39+
# Select whether to compute the acquisition frequency or not
40+
compute_frequency = True
41+
4142
print("Starting data acquisition from RED & IR registers...", '\n')
4243
sleep(1)
43-
44-
t_start = ticks_ms() # Starting time of the acquisition
45-
samples_n = 0 # Number of samples that has been collected
46-
47-
while(True):
44+
45+
t_start = ticks_ms() # Starting time of the acquisition
46+
samples_n = 0 # Number of samples that has been collected
47+
48+
while True:
4849
# The check() method has to be continuously polled, to check if
4950
# there are new readings into the sensor's FIFO queue. When new
5051
# readings are available, this function will put them into the storage.
5152
sensor.check()
52-
53+
5354
# Check if the storage contains available samples
54-
if(sensor.available()):
55+
if sensor.available():
5556
# Access the storage FIFO and gather the readings (integers)
56-
red_reading = sensor.popRedFromStorage()
57-
IR_reading = sensor.popIRFromStorage()
58-
59-
# Print the acquired data (can be plot with Arduino Serial Plotter)
60-
print(red_reading, ",", IR_reading)
61-
57+
red_reading = sensor.pop_red_from_storage()
58+
ir_reading = sensor.pop_ir_from_storage()
59+
60+
# Print the acquired data (so that it can be plotted with a Serial Plotter)
61+
print(red_reading, ",", ir_reading)
62+
6263
# We can compute the real frequency at which we receive data
63-
if (compute_frequency):
64-
samples_n=samples_n+1
65-
if ( ticks_diff(ticks_ms(), t_start) > 999 ):
66-
f_HZ = samples_n/1
64+
if compute_frequency:
65+
samples_n = samples_n + 1
66+
if ticks_diff(ticks_ms(), t_start) > 999:
67+
f_HZ = samples_n / 1
6768
samples_n = 0
6869
t_start = ticks_ms()
69-
print("acquisition frequency = ",f_HZ)
70+
print("acquisition frequency = ", f_HZ)

0 commit comments

Comments
 (0)