#include <Arduino.h>
#include <LiquidCrystal_I2C.h>
#define LED 2
#define BUTTON_PIN 32
#define SLIDER_BPM 34
#define SLIDER_AMPLITUDE 35
LiquidCrystal_I2C lcd1(0x27, 20, 8); // First LCD
LiquidCrystal_I2C lcd2(0x3F, 20, 8); // Second LCD
hw_timer_t *My_timer = NULL;
volatile bool arythmiaMode = false;
volatile float adValue = -10;
volatile float bpm = 60; // Default BPM
volatile float amplitude = 1.0; // Full amplitude
volatile unsigned long startTime = 0;
const unsigned long runDuration = 30000;
byte waveform[5][8] = { // Custom characters for waveform visualization
{0,0,0,0,0,0,0,31},
{0,0,0,0,0,0,31,31},
{0,0,0,0,0,31,31,31},
{0,0,0,0,31,31,31,31},
{0,0,31,31,31,31,31,31}
};
void IRAM_ATTR onTimer() {
static int vector = 0;
if (millis() - startTime < runDuration) {
float angle = (vector * PI / 180) * bpm / 60;
adValue = sin(angle) * amplitude;
if (++vector >= 360) vector = 0;
} else {
adValue = 0;
}
}
void setup() {
pinMode(LED, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(SLIDER_BPM, INPUT);
pinMode(SLIDER_AMPLITUDE, INPUT);
startTime = millis();
My_timer = timerBegin(0, 80, true);
timerAttachInterrupt(My_timer, &onTimer, true);
timerAlarmWrite(My_timer, 1000000 / (bpm / 60), true);
timerAlarmEnable(My_timer);
Serial.begin(115200);
lcd1.init(); lcd1.backlight();
lcd2.init(); lcd2.backlight();
for (int i = 0; i < 5; i++) {
lcd1.createChar(i, waveform[i]);
lcd2.createChar(i, waveform[i]);
}
}
void loop() {
if (digitalRead(BUTTON_PIN) == LOW) {
arythmiaMode = !arythmiaMode;
delay(500); // Debouncing
}
int newBpmReading = analogRead(SLIDER_BPM);
bpm = map(newBpmReading, 0, 4095, 40, 240);
int newAmplitudeReading = analogRead(SLIDER_AMPLITUDE);
amplitude = map(newAmplitudeReading, 0, 4095, 10, 100) / 100.0;
timerAlarmWrite(My_timer, 1000000 / (bpm / 60), true);
static int pos = 0; // Position tracking across both LCDs
if (adValue != -10) {
int waveHeight = map(adValue * 100, -100, 100, 0, 4);
if (pos < 20) {
lcd1.setCursor(pos, 4);
lcd1.write(waveHeight);
} else {
lcd2.setCursor(pos - 20, 4);
lcd2.write(waveHeight);
}
pos = (pos + 1) % 40; // Wrap around after 40 (20 per LCD)
adValue = -10;
}
}