Currently I’m using this code (might still adjust to taste… Because it’s not quite the effect yet. But everything is working)
When holding the button for 2 or more seconds. Otamatone gets 5v.
When pressing the button 1 time. The otamatone gets 5v or 0v alternating semi randomly, but increasing in frequency.
Pressing the button again, makes it go from 5v to 2.5v (which is the voltage at around which the otamatone shuts off.)
Now I’m thinking about being able to modify the ribbon controller, so I can make it sing on it’s own. But the housing is quiete small, so I’m not sure yet how I would wire it.
#include <Adafruit_MCP4725.h>
#define BUTTON_PIN 2
#define MCP4725_ADDRESS 0x60
Adafruit_MCP4725 dac;
enum State { IDLE, ALTERNATING_VOLTAGE, TRANSITION_DOWN, TRANSITION_DOWN_ALTERNATE };
State state = IDLE;
bool buttonState = false; // Current state of the button
bool lastButtonState = false; // Previous state of the button
unsigned long buttonPressStartTime = 0; // Time when button was pressed
const unsigned long LONG_PRESS_DURATION = 2000; // 2 seconds
const unsigned long TRANSITION_DURATION = 15000; // 15 seconds
unsigned long alternatingVoltageStartTime = 0; // Time when alternating voltage started
unsigned long alternatingVoltageDurationOn = 1000; // Initial duration for on state (0.1 to 1 second)
unsigned long alternatingVoltageDurationOff = 150; // Initial duration for off state (0.05 to 0.2 seconds)
float currentVoltage = 0.0; // Current voltage state
bool voltageOn = false; // Flag to track voltage state
unsigned long startTime = 0; // Start time for gradual speed increase
const unsigned long SPEED_UP_DURATION = 10000; // Duration over which to increase speed
unsigned long transitionDownStartTime = 0; // Start time for gradual voltage decrease
const unsigned long TRANSITION_DOWN_DURATION = 10000; // 10 seconds
void setup() {
pinMode(BUTTON_PIN, INPUT); // No internal pull-up
Serial.begin(9600);
if (!dac.begin(MCP4725_ADDRESS)) {
Serial.println("Failed to find MCP4725 chip");
while (1) { delay(10); }
}
setVoltage(0); // Set initial voltage to 0V
Serial.println("Setup complete. Initial voltage set to 0V.");
}
void loop() {
buttonState = digitalRead(BUTTON_PIN);
// Check for button press (state change from LOW to HIGH)
if (buttonState != lastButtonState) {
if (buttonState == HIGH) {
buttonPressStartTime = millis();
} else {
handleButtonPress();
}
delay(50); // Debounce delay
}
lastButtonState = buttonState;
// Handle the gradual transition
if (state == TRANSITION_DOWN) {
unsigned long elapsedTime = millis() - transitionDownStartTime;
if (elapsedTime <= TRANSITION_DOWN_DURATION) {
float voltage = 5.0 - (2.5 * (elapsedTime / (float)TRANSITION_DOWN_DURATION));
setVoltage(voltage);
} else {
setVoltage(2.5); // Ensure voltage is set to 2.5V after transition
state = IDLE;
Serial.println("Transition down complete. Voltage set to 2.5V.");
}
} else if (state == ALTERNATING_VOLTAGE) {
unsigned long currentTime = millis();
unsigned long elapsedTime = currentTime - alternatingVoltageStartTime;
if (voltageOn) {
if (elapsedTime >= alternatingVoltageDurationOn) {
// Turn voltage off
setVoltage(0);
voltageOn = false;
// Generate a random duration for off state within updated range
alternatingVoltageDurationOff = random(10, 30); // Between 0.01 and 0.03 seconds
alternatingVoltageStartTime = currentTime;
}
} else {
if (elapsedTime >= alternatingVoltageDurationOff) {
// Turn voltage on
setVoltage(5);
voltageOn = true;
// Generate a random duration for on state within updated range
alternatingVoltageDurationOn = random(50, 150); // Between 0.05 and 0.15 seconds
alternatingVoltageStartTime = currentTime;
}
}
} else if (state == TRANSITION_DOWN_ALTERNATE) {
setVoltage(5); // Set voltage to 5V
state = TRANSITION_DOWN; // Start transitioning down to 2.5V
transitionDownStartTime = millis();
Serial.println("Starting transition down to 2.5V.");
}
// Gradually increase speed over time
unsigned long currentTime = millis();
if (currentTime - startTime <= SPEED_UP_DURATION) {
float progress = (currentTime - startTime) / (float)SPEED_UP_DURATION;
// Update duration ranges for on and off states based on progress
alternatingVoltageDurationOn = lerp(1000, 150, progress); // From 1000 to 150 milliseconds
alternatingVoltageDurationOff = lerp(150, 30, progress); // From 150 to 30 milliseconds
}
}
void handleButtonPress() {
unsigned long pressDuration = millis() - buttonPressStartTime;
if (pressDuration >= LONG_PRESS_DURATION) {
// Long press: Reset to 5V and reset state
setVoltage(5);
state = IDLE;
Serial.println("Long press detected. Resetting to 5V.");
} else {
// Short press: Change state based on current state
switch (state) {
case IDLE:
state = ALTERNATING_VOLTAGE;
Serial.println("Alternating voltage mode activated.");
startTime = millis(); // Start time for gradual speed increase
break;
case ALTERNATING_VOLTAGE:
state = TRANSITION_DOWN_ALTERNATE;
Serial.println("Transitioning down after alternating voltages.");
break;
}
}
}
void setVoltage(float voltage) {
// Ensure voltage is within 0V and 5V range
voltage = constrain(voltage, 0, 5.0);
uint16_t dacValue = (uint16_t)(voltage / 5.0 * 4095);
dac.setVoltage(dacValue, false);
currentVoltage = voltage; // Update current voltage state
Serial.print("Voltage set to ");
Serial.print(voltage);
Serial.println("V");
}
float lerp(float start, float end, float t) {
return start + t * (end - start);
}
Setup:
9v battery
Arduino nano
bc337
Mcp4725 (dac)
1x 1M resistor
1x 10k resistor
1x momentary push button
I’m connecting the mcp as per usual to the arduino (A4 and A5 on the arduino and providing vcc and gnd connections)
The button is connected with 5v from the arduino and pin d2
The output of the mcp is going to the base of the bc337.
The collector of the bc337 is connected to the 9v battery
The emitter is connected to the power input of the otamatone and with a 1M resistor to gnd.
→ This transistor buffer is used to provide enough amperage to the otamatone, which the arduino can’t provide.
A picture of my pro setup: