WRITE A CODE THAT MEASURES HEART RATE FROM 60-100 AND SHOWS THE DATA ON THE OLED SCREEN. ADD PIOTIMER AND ENCODER CAPABILITIES. HERE IS THE SOURCE CODE: import utime
import machine
from machine import Pin, Timer, ADC, I2C, PWM
from fifo import Fifo
import ssd1306

# Set up I2C, OLED display, and other pins
i2c = I2C(1, scl=Pin(15), sda=Pin(14))
oled = ssd1306.SSD1306_I2C(128, 64, i2c)
heart_rate = 0
peaks = []
peak_intervals = []

encoder_pin_a = Pin(10, Pin.IN, Pin.PULL_UP)
encoder_pin_b = Pin(11, Pin.IN, Pin.PULL_UP)
encoder_button = Pin(12, Pin.IN, Pin.PULL_UP)

led1_pin = 20
led2_pin = 21
led3_pin = 22

led1_pwm = PWM(Pin(led1_pin))
led2_pwm = PWM(Pin(led2_pin))
led3_pwm = PWM(Pin(led3_pin))

# Set up ADC and sampling
sampling_freq = 60  # Hz, default sampling frequency
sampling_period_ms = 1000 // sampling_freq
adc_pin = ADC(Pin(26, Pin.IN))
samples = Fifo(50)

# Set up timer for sampling at precise intervals
timer = machine.Timer()

def on_timer_tick(timer):
    sample = adc_pin.read_u16()
    samples.put(sample)

timer.init(freq=sampling_freq, mode=machine.Timer.PERIODIC, callback=on_timer_tick)

# Find peaks in the data
def find_peaks(data, threshold):
    peaks = []
    for i in range(1, len(data) - 1):
        if data[i] > threshold and data[i] > data[i - 1] and data[i] > data[i + 1]:
            peaks.append(i)
    return peaks

# Main program loop
window_size = 60  # 1 second of data at 60 Hz
window = []
prev_encoder_state = 0
encoder_state = 0
encoder_counter = 0
try:
    
    while True:
        # Read encoder state and adjust sampling frequency accordingly
        encoder_state = (encoder_pin_a.value() << 1) | encoder_pin_b.value()
        if encoder_state != prev_encoder_state:
            if (prev_encoder_state == 0b00 and encoder_state == 0b01) or (prev_encoder_state == 0b01 and encoder_state == 0b11) or (prev_encoder_state == 0b11 and encoder_state == 0b10) or (prev_encoder_state == 0b10 and encoder_state == 0b00):
                encoder_counter += 1
            else:
                encoder_counter -= 1
            encoder_counter = max(min(encoder_counter, 40), -40)
            sampling_freq = max(min(100, 60 + encoder_counter), 60)
            sampling_period_ms = 1000 // sampling_freq
            timer.init(freq=sampling_freq, mode=machine.Timer.PERIODIC, callback=on_timer_tick)
        prev_encoder_state = encoder_state

        if not samples.empty():
            sample = samples.get()
            window.append(sample)

            # Remove the oldest sample if the window is full
            if len(window) > window_size:
                window.pop(0)
            
            if len(window) == window_size:
                peaks = find_peaks(window, threshold=50)
                peak_intervals = [peaks[i+1] - peaks[i] for i in range(len(peaks)-1)]
            if len(peak_intervals) > 0:
                avg_peak_interval = sum(peak_intervals) / len(peak_intervals)
                heart_rate = 60 * sampling_freq / avg_peak_interval
        
        # Adjust the sampling frequency based on encoder input
        encoder_delta = 0
        if encoder_pin_a.value() != encoder_pin_b.value():
            encoder_delta += 1
        else:
            encoder_delta -= 1

        sampling_freq += encoder_delta
        if sampling_freq < 60:
            sampling_freq = 60
        elif sampling_freq > 100:
            sampling_freq = 100

        sampling_period_ms = 1000 // sampling_freq
        timer.init(freq=sampling_freq, mode=machine.Timer.PERIODIC, callback=on_timer_tick)
        
        # Draw the graph
        oled.fill(0)
        oled.text("Heart rate: {:.1f}".format(heart_rate), 0, 0)
        oled.text("BPM: {:.1f}".format(heart_rate), 0, 10)

        prev_x, prev_y = 0, 0
        for i, value in enumerate(window[-128:]):
            x = i
            y = 63 - int((value / 65535) * 32)  # Scale the sample value to fit the OLED screen
            if i != 0:
                oled.line(prev_x, prev_y, x, y, 1)
            prev_x, prev_y = x, y
        oled.show()

        if not peaks:
            print("No peaks detected.")
finally:
    print("Exiting program.")