//Working copy of ATTiny85 beatbug with TM1637 display.
//Includes the moving average library to display agerage of last three beats.
// One side of button goes to GPIO2 (pin 7) and the other goes to ground.
//
// ATMEL ATTINY85
//
// +-\/-+
// A0 (D 5) PB5 1| |8 Vcc
// A3/ DIO (D 3) PB3 2| |7 PB2 (D 2) A1/SCL
// CLK(D 4) PB4 3| |6 PB1 (D 1)
// GND 4| |5 PB0 (D 0) SDA
// +----+
# include <TM1637Display.h>
# include <math.h>
# include <Bounce2.h>
# include <movingAvg.h>
// Instantiate a button object
Bounce2::Button button = Bounce2::Button();
// Module connection pins (Digital Pins)
# define CLK 4 //pin 3
# define DIO 3 // pin 2
# define BUTTON_PIN 2
// synthetic drummer
# define SYNTH_OUT_PIN 0 // PB0 = pin 5
# define SLIDER_PIN A0 // PB5 = pin 1
// Variables, button state, and debounce
int buttonState = 0;
int lastButtonState = HIGH;
int reading;
long lastBeat;
// Set up display library
TM1637Display display(CLK, DIO);
// Names the moving average. Number in () sets the number of values to evaluate
movingAvg bpmavg(3);
int calculateBPM(long thisTime, long lastTime) {
long elapsed = thisTime - lastTime;
double bpm = round(1000. / elapsed * 60.);
return (int)bpm;
}
void setup() {
// BUTTON SETUP
// SELECT ONE OF THE FOLLOWING :
// 1) IF YOUR BUTTON HAS AN INTERNAL PULL-UP
button.attach( BUTTON_PIN , INPUT_PULLUP ); // USE INTERNAL PULL-UP
// 2) IF YOUR BUTTON USES AN EXTERNAL PULL-UP
// button.attach( BUTTON_PIN, INPUT ); // USE EXTERNAL PULL-UP
// DEBOUNCE INTERVAL IN MILLISECONDS
button.interval(5);
// INDICATE THAT THE HIGH STATE CORRESPONDS TO PHYSICALLY PRESSING THE BUTTON
button.setPressedState(HIGH);
// Begins the averaging
bpmavg.begin();
// Set display brightness
display.setBrightness(0x02);
// Record first hit as now
lastBeat = millis();
// synthetic drummer
pinMode(SYNTH_OUT_PIN, OUTPUT);
digitalWrite(SYNTH_OUT_PIN, LOW); // start low
pinMode(SLIDER_PIN, INPUT); // simulator fader
}
void loop() {
// UPDATE THE BUTTON
// YOU MUST CALL THIS EVERY LOOP
button.update();
if ( button.pressed() )
{
static int toggler;
toggler++;
// skip every other beat
if (toggler & 0x1) {
// Calculate bpm and display average result
long thisBeat = millis();
int bpm = calculateBPM(thisBeat, lastBeat);
int avg = bpmavg.reading(bpm);
display.showNumberDec(avg);
// thisBeat is now lastBeat for the next button press
lastBeat = thisBeat;
}
}
// fake the input
// read an analog pin (will be a slide fader)
// and synthesize a pulse train that runs from 40 BPM to 220 BPM
// --- SYNTHETIC DRUMMER (FAKE INPUT) ---
// Non-blocking square-wave generator whose output is wired into BUTTON_PIN.
//
// Reads the slider, maps it to BPM (40–220), then produces a 50% duty cycle
// pulse train on SYNTH_OUT_PIN. No interference with button logic.
{ // let us have our own local variables!
// 1. Read slider and map to BPM
int raw = analogRead(SLIDER_PIN);
int bpm = map(raw, 0, 1023, 80, 440);
// Convert BPM to half-period (ms per HIGH or LOW phase)
// BPM → beat period (ms) = 60000 / BPM
// Half period = (60000 / BPM) / 2
static unsigned long lastToggle = 0;
static bool synthState = false;
// Protect against divide-by-zero if slider is loose
if (bpm < 80) bpm = 80;
if (bpm > 440) bpm = 440;
unsigned long halfPeriod = 60000UL / (unsigned long)bpm / 2UL;
// 2. Toggle output state when time expires
unsigned long now = millis();
if (now - lastToggle >= halfPeriod) {
synthState = !synthState;
digitalWrite(SYNTH_OUT_PIN, synthState);
lastToggle = now;
}
}
}
SYNTHETIC
DRUMMER