Add clock run/reset

This commit is contained in:
2026-02-21 10:19:09 -08:00
parent 763d58f411
commit bd08ac4352
12 changed files with 324 additions and 162 deletions

View File

@ -107,6 +107,28 @@ void loop() {
} }
} }
// Clock Run
if (app.cv_run == 1 || app.cv_run == 2) {
auto &cv = app.cv_run == 1 ? gravity.cv1 : gravity.cv2;
int val = cv.Read();
if (val > AnalogInput::GATE_THRESHOLD && gravity.clock.IsPaused()) {
gravity.clock.Start();
app.refresh_screen = true;
} else if (val < AnalogInput::GATE_THRESHOLD && !gravity.clock.IsPaused()) {
gravity.clock.Stop();
ResetOutputs();
app.refresh_screen = true;
}
}
// Clock Reset
if ((app.cv_reset == 1 &&
gravity.cv1.IsRisingEdge(AnalogInput::GATE_THRESHOLD)) ||
(app.cv_reset == 2 &&
gravity.cv2.IsRisingEdge(AnalogInput::GATE_THRESHOLD))) {
gravity.clock.Reset();
}
// Check for dirty state eligible to be saved. // Check for dirty state eligible to be saved.
stateManager.update(app); stateManager.update(app);
@ -285,6 +307,14 @@ void editMainParameter(int val) {
gravity.clock.SetTempo(gravity.clock.Tempo() + val); gravity.clock.SetTempo(gravity.clock.Tempo() + val);
app.tempo = gravity.clock.Tempo(); app.tempo = gravity.clock.Tempo();
break; break;
case PARAM_MAIN_RUN:
updateSelection(app.selected_sub_param, val, 3);
app.cv_run = app.selected_sub_param;
break;
case PARAM_MAIN_RESET:
updateSelection(app.selected_sub_param, val, 3);
app.cv_reset = app.selected_sub_param;
break;
case PARAM_MAIN_SOURCE: { case PARAM_MAIN_SOURCE: {
byte source = static_cast<int>(app.selected_source); byte source = static_cast<int>(app.selected_source);
updateSelection(source, val, Clock::SOURCE_LAST); updateSelection(source, val, Clock::SOURCE_LAST);
@ -301,6 +331,7 @@ void editMainParameter(int val) {
} }
break; break;
} }
// These changes are applied upon encoder button press.
case PARAM_MAIN_ENCODER_DIR: case PARAM_MAIN_ENCODER_DIR:
updateSelection(app.selected_sub_param, val, 2); updateSelection(app.selected_sub_param, val, 2);
break; break;

View File

@ -18,24 +18,26 @@
// Global state for settings and app behavior. // Global state for settings and app behavior.
struct AppState { struct AppState {
int tempo = Clock::DEFAULT_TEMPO; int tempo = Clock::DEFAULT_TEMPO;
Channel channel[Gravity::OUTPUT_COUNT]; Channel channel[Gravity::OUTPUT_COUNT];
byte selected_param = 0; byte selected_param = 0;
byte selected_sub_param = 0; // Temporary value for editing params. byte selected_sub_param = 0; // Temporary value for editing params.
byte selected_channel = 0; // 0=tempo, 1-6=output channel byte selected_channel = 0; // 0=tempo, 1-6=output channel
byte selected_swing = 0; byte selected_swing = 0;
byte selected_save_slot = 0; // The currently active save slot. byte selected_save_slot = 0; // The currently active save slot.
Clock::Source selected_source = Clock::SOURCE_INTERNAL; Clock::Source selected_source = Clock::SOURCE_INTERNAL;
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24; Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24;
bool editing_param = false; byte cv_run = 0;
bool encoder_reversed = false; byte cv_reset = 0;
bool refresh_screen = true; bool editing_param = false;
bool encoder_reversed = false;
bool refresh_screen = true;
}; };
extern AppState app; extern AppState app;
static Channel& GetSelectedChannel() { static Channel &GetSelectedChannel() {
return app.channel[app.selected_channel - 1]; return app.channel[app.selected_channel - 1];
} }
#endif // APP_STATE_H #endif // APP_STATE_H

View File

@ -127,6 +127,8 @@ constexpr uint8_t CHANNEL_BOX_HEIGHT = 14;
// Menu items for editing global parameters. // Menu items for editing global parameters.
enum ParamsMainPage : uint8_t { enum ParamsMainPage : uint8_t {
PARAM_MAIN_TEMPO, PARAM_MAIN_TEMPO,
PARAM_MAIN_RUN,
PARAM_MAIN_RESET,
PARAM_MAIN_SOURCE, PARAM_MAIN_SOURCE,
PARAM_MAIN_PULSE, PARAM_MAIN_PULSE,
PARAM_MAIN_ENCODER_DIR, PARAM_MAIN_ENCODER_DIR,
@ -253,6 +255,34 @@ void DisplayMainPage() {
} }
subText = F("BPM"); subText = F("BPM");
break; break;
case PARAM_MAIN_RUN:
mainText = F("RUN");
switch (app.cv_run) {
case 0:
subText = F("NONE");
break;
case 1:
subText = F("CV1 GATE");
break;
case 2:
subText = F("CV2 GATE");
break;
}
break;
case PARAM_MAIN_RESET:
mainText = F("RST");
switch (app.cv_reset) {
case 0:
subText = F("NONE");
break;
case 1:
subText = F("CV1 TRIG");
break;
case 2:
subText = F("CV2 TRIG");
break;
}
break;
case PARAM_MAIN_SOURCE: case PARAM_MAIN_SOURCE:
mainText = F("EXT"); mainText = F("EXT");
switch (app.selected_source) { switch (app.selected_source) {
@ -339,8 +369,9 @@ void DisplayMainPage() {
// Draw Main Page menu items // Draw Main Page menu items
String menu_items[PARAM_MAIN_LAST] = { String menu_items[PARAM_MAIN_LAST] = {
F("TEMPO"), F("SOURCE"), F("PULSE OUT"), F("ENCODER DIR"), F("TEMPO"), F("CLK RUN"), F("CLK RESET"), F("SOURCE"),
F("SAVE"), F("LOAD"), F("RESET"), F("ERASE")}; F("PULSE OUT"), F("ENCODER DIR"), F("SAVE"), F("LOAD"),
F("RESET"), F("ERASE")};
drawMenuItems(menu_items, PARAM_MAIN_LAST); drawMenuItems(menu_items, PARAM_MAIN_LAST);
} }

View File

@ -18,7 +18,7 @@
// Define the constants for the current firmware. // Define the constants for the current firmware.
const char StateManager::SKETCH_NAME[] = "ALT EUCLIDEAN"; const char StateManager::SKETCH_NAME[] = "ALT EUCLIDEAN";
const char StateManager::SEMANTIC_VERSION[] = const char StateManager::SEMANTIC_VERSION[] =
"V2.0.0BETA2"; // NOTE: This should match the version in the "V2.0.0BETA3"; // NOTE: This should match the version in the
// library.properties file. // library.properties file.
// Number of available save slots. // Number of available save slots.
@ -94,6 +94,8 @@ void StateManager::reset(AppState &app) {
app.selected_channel = default_app.selected_channel; app.selected_channel = default_app.selected_channel;
app.selected_source = default_app.selected_source; app.selected_source = default_app.selected_source;
app.selected_pulse = default_app.selected_pulse; app.selected_pulse = default_app.selected_pulse;
app.cv_run = default_app.cv_run;
app.cv_reset = default_app.cv_reset;
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) { for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
app.channel[i].Init(); app.channel[i].Init();
@ -148,6 +150,8 @@ void StateManager::_saveState(const AppState &app, byte slot_index) {
save_data.selected_channel = app.selected_channel; save_data.selected_channel = app.selected_channel;
save_data.selected_source = static_cast<byte>(app.selected_source); save_data.selected_source = static_cast<byte>(app.selected_source);
save_data.selected_pulse = static_cast<byte>(app.selected_pulse); save_data.selected_pulse = static_cast<byte>(app.selected_pulse);
save_data.cv_run = app.cv_run;
save_data.cv_reset = app.cv_reset;
// TODO: break this out into a separate function. Save State should be // TODO: break this out into a separate function. Save State should be
// broken out into global / per-channel save methods. When saving via // broken out into global / per-channel save methods. When saving via
@ -184,6 +188,8 @@ void StateManager::_loadState(AppState &app, byte slot_index) {
app.selected_channel = load_data.selected_channel; app.selected_channel = load_data.selected_channel;
app.selected_source = static_cast<Clock::Source>(load_data.selected_source); app.selected_source = static_cast<Clock::Source>(load_data.selected_source);
app.selected_pulse = static_cast<Clock::Pulse>(load_data.selected_pulse); app.selected_pulse = static_cast<Clock::Pulse>(load_data.selected_pulse);
app.cv_run = load_data.cv_run;
app.cv_reset = load_data.cv_reset;
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) { for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
auto &ch = app.channel[i]; auto &ch = app.channel[i];

View File

@ -72,6 +72,8 @@ public:
byte selected_channel; byte selected_channel;
byte selected_source; byte selected_source;
byte selected_pulse; byte selected_pulse;
byte cv_run;
byte cv_reset;
ChannelState channel_data[Gravity::OUTPUT_COUNT]; ChannelState channel_data[Gravity::OUTPUT_COUNT];
}; };

View File

@ -107,6 +107,28 @@ void loop() {
} }
} }
// Clock Run
if (app.cv_run == 1 || app.cv_run == 2) {
auto &cv = app.cv_run == 1 ? gravity.cv1 : gravity.cv2;
int val = cv.Read();
if (val > AnalogInput::GATE_THRESHOLD && gravity.clock.IsPaused()) {
gravity.clock.Start();
app.refresh_screen = true;
} else if (val < AnalogInput::GATE_THRESHOLD && !gravity.clock.IsPaused()) {
gravity.clock.Stop();
ResetOutputs();
app.refresh_screen = true;
}
}
// Clock Reset
if ((app.cv_reset == 1 &&
gravity.cv1.IsRisingEdge(AnalogInput::GATE_THRESHOLD)) ||
(app.cv_reset == 2 &&
gravity.cv2.IsRisingEdge(AnalogInput::GATE_THRESHOLD))) {
gravity.clock.Reset();
}
// Check for dirty state eligible to be saved. // Check for dirty state eligible to be saved.
stateManager.update(app); stateManager.update(app);
@ -285,6 +307,14 @@ void editMainParameter(int val) {
gravity.clock.SetTempo(gravity.clock.Tempo() + val); gravity.clock.SetTempo(gravity.clock.Tempo() + val);
app.tempo = gravity.clock.Tempo(); app.tempo = gravity.clock.Tempo();
break; break;
case PARAM_MAIN_RUN:
updateSelection(app.selected_sub_param, val, 3);
app.cv_run = app.selected_sub_param;
break;
case PARAM_MAIN_RESET:
updateSelection(app.selected_sub_param, val, 3);
app.cv_reset = app.selected_sub_param;
break;
case PARAM_MAIN_SOURCE: { case PARAM_MAIN_SOURCE: {
byte source = static_cast<int>(app.selected_source); byte source = static_cast<int>(app.selected_source);
updateSelection(source, val, Clock::SOURCE_LAST); updateSelection(source, val, Clock::SOURCE_LAST);
@ -301,6 +331,7 @@ void editMainParameter(int val) {
} }
break; break;
} }
// These changes are applied upon encoder button press.
case PARAM_MAIN_ENCODER_DIR: case PARAM_MAIN_ENCODER_DIR:
updateSelection(app.selected_sub_param, val, 2); updateSelection(app.selected_sub_param, val, 2);
break; break;

View File

@ -18,24 +18,26 @@
// Global state for settings and app behavior. // Global state for settings and app behavior.
struct AppState { struct AppState {
int tempo = Clock::DEFAULT_TEMPO; int tempo = Clock::DEFAULT_TEMPO;
Channel channel[Gravity::OUTPUT_COUNT]; Channel channel[Gravity::OUTPUT_COUNT];
byte selected_param = 0; byte selected_param = 0;
byte selected_sub_param = 0; // Temporary value for editing params. byte selected_sub_param = 0; // Temporary value for editing params.
byte selected_channel = 0; // 0=tempo, 1-6=output channel byte selected_channel = 0; // 0=tempo, 1-6=output channel
byte selected_swing = 0; byte selected_swing = 0;
byte selected_save_slot = 0; // The currently active save slot. byte selected_save_slot = 0; // The currently active save slot.
Clock::Source selected_source = Clock::SOURCE_INTERNAL; byte cv_run = 0;
Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24; byte cv_reset = 0;
bool editing_param = false; Clock::Source selected_source = Clock::SOURCE_INTERNAL;
bool encoder_reversed = false; Clock::Pulse selected_pulse = Clock::PULSE_PPQN_24;
bool refresh_screen = true; bool editing_param = false;
bool encoder_reversed = false;
bool refresh_screen = true;
}; };
extern AppState app; extern AppState app;
static Channel& GetSelectedChannel() { static Channel &GetSelectedChannel() {
return app.channel[app.selected_channel - 1]; return app.channel[app.selected_channel - 1];
} }
#endif // APP_STATE_H #endif // APP_STATE_H

View File

@ -128,6 +128,8 @@ constexpr uint8_t CHANNEL_BOX_HEIGHT = 14;
enum ParamsMainPage : uint8_t { enum ParamsMainPage : uint8_t {
PARAM_MAIN_TEMPO, PARAM_MAIN_TEMPO,
PARAM_MAIN_SOURCE, PARAM_MAIN_SOURCE,
PARAM_MAIN_RUN,
PARAM_MAIN_RESET,
PARAM_MAIN_PULSE, PARAM_MAIN_PULSE,
PARAM_MAIN_ENCODER_DIR, PARAM_MAIN_ENCODER_DIR,
PARAM_MAIN_SAVE_DATA, PARAM_MAIN_SAVE_DATA,
@ -296,6 +298,34 @@ void DisplayMainPage() {
break; break;
} }
break; break;
case PARAM_MAIN_RUN:
mainText = F("RUN");
switch (app.cv_run) {
case 0:
subText = F("NONE");
break;
case 1:
subText = F("CV 1");
break;
case 2:
subText = F("CV 2");
break;
}
break;
case PARAM_MAIN_RESET:
mainText = F("RST");
switch (app.cv_reset) {
case 0:
subText = F("NONE");
break;
case 1:
subText = F("CV 1");
break;
case 2:
subText = F("CV 2");
break;
}
break;
case PARAM_MAIN_PULSE: case PARAM_MAIN_PULSE:
mainText = F("OUT"); mainText = F("OUT");
switch (app.selected_pulse) { switch (app.selected_pulse) {
@ -358,8 +388,9 @@ void DisplayMainPage() {
// Draw Main Page menu items // Draw Main Page menu items
String menu_items[PARAM_MAIN_LAST] = { String menu_items[PARAM_MAIN_LAST] = {
F("TEMPO"), F("SOURCE"), F("PULSE OUT"), F("ENCODER DIR"), F("TEMPO"), F("SOURCE"), F("CLK RUN"),
F("SAVE"), F("LOAD"), F("RESET"), F("ERASE")}; F("CLK RESET"), F("PULSE OUT"), F("ENCODER DIR"),
F("SAVE"), F("LOAD"), F("ERASE")};
drawMenuItems(menu_items, PARAM_MAIN_LAST); drawMenuItems(menu_items, PARAM_MAIN_LAST);
} }

View File

@ -18,7 +18,7 @@
// Define the constants for the current firmware. // Define the constants for the current firmware.
const char StateManager::SKETCH_NAME[] = "ALT GRAVITY"; const char StateManager::SKETCH_NAME[] = "ALT GRAVITY";
const char StateManager::SEMANTIC_VERSION[] = const char StateManager::SEMANTIC_VERSION[] =
"V2.0.0BETA2"; // NOTE: This should match the version in the "V2.0.0BETA4"; // NOTE: This should match the version in the
// library.properties file. // library.properties file.
// Number of available save slots. // Number of available save slots.
@ -94,6 +94,8 @@ void StateManager::reset(AppState &app) {
app.selected_channel = default_app.selected_channel; app.selected_channel = default_app.selected_channel;
app.selected_source = default_app.selected_source; app.selected_source = default_app.selected_source;
app.selected_pulse = default_app.selected_pulse; app.selected_pulse = default_app.selected_pulse;
app.cv_run = default_app.cv_run;
app.cv_reset = default_app.cv_reset;
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) { for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
app.channel[i].Init(); app.channel[i].Init();
@ -148,6 +150,8 @@ void StateManager::_saveState(const AppState &app, byte slot_index) {
save_data.selected_channel = app.selected_channel; save_data.selected_channel = app.selected_channel;
save_data.selected_source = static_cast<byte>(app.selected_source); save_data.selected_source = static_cast<byte>(app.selected_source);
save_data.selected_pulse = static_cast<byte>(app.selected_pulse); save_data.selected_pulse = static_cast<byte>(app.selected_pulse);
save_data.cv_run = app.cv_run;
save_data.cv_reset = app.cv_reset;
// TODO: break this out into a separate function. Save State should be // TODO: break this out into a separate function. Save State should be
// broken out into global / per-channel save methods. When saving via // broken out into global / per-channel save methods. When saving via
@ -185,6 +189,8 @@ void StateManager::_loadState(AppState &app, byte slot_index) {
app.selected_channel = load_data.selected_channel; app.selected_channel = load_data.selected_channel;
app.selected_source = static_cast<Clock::Source>(load_data.selected_source); app.selected_source = static_cast<Clock::Source>(load_data.selected_source);
app.selected_pulse = static_cast<Clock::Pulse>(load_data.selected_pulse); app.selected_pulse = static_cast<Clock::Pulse>(load_data.selected_pulse);
app.cv_run = load_data.cv_run;
app.cv_reset = load_data.cv_reset;
for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) { for (int i = 0; i < Gravity::OUTPUT_COUNT; i++) {
auto &ch = app.channel[i]; auto &ch = app.channel[i];

View File

@ -73,6 +73,8 @@ public:
byte selected_channel; byte selected_channel;
byte selected_source; byte selected_source;
byte selected_pulse; byte selected_pulse;
byte cv_run;
byte cv_reset;
ChannelState channel_data[Gravity::OUTPUT_COUNT]; ChannelState channel_data[Gravity::OUTPUT_COUNT];
}; };

View File

@ -11,78 +11,94 @@
#ifndef ANALOG_INPUT_H #ifndef ANALOG_INPUT_H
#define ANALOG_INPUT_H #define ANALOG_INPUT_H
const int MAX_INPUT = (1 << 10) - 1; // Max 10 bit analog read resolution. const int MAX_INPUT = (1 << 10) - 1; // Max 10 bit analog read resolution.
// estimated default calibration value // estimated default calibration value
const int CALIBRATED_LOW = -566; const int CALIBRATED_LOW = -566;
const int CALIBRATED_HIGH = 512; const int CALIBRATED_HIGH = 512;
class AnalogInput { class AnalogInput {
public: public:
AnalogInput() {} static const int GATE_THRESHOLD = 0;
~AnalogInput() {}
/** AnalogInput() {}
* Initializes a analog input object. ~AnalogInput() {}
*
* @param pin gpio pin for the analog input.
*/
void Init(uint8_t pin) {
pinMode(pin, INPUT);
pin_ = pin;
}
/** /**
* Read the value of the analog input and set instance state. * Initializes a analog input object.
* *
*/ * @param pin gpio pin for the analog input.
void Process() { */
old_read_ = read_; void Init(uint8_t pin) {
int raw = analogRead(pin_); pinMode(pin, INPUT);
read_ = map(raw, 0, MAX_INPUT, low_, high_); pin_ = pin;
read_ = constrain(read_ - offset_, -512, 512); }
if (inverted_) read_ = -read_;
}
// Set calibration values. /**
* Read the value of the analog input and set instance state.
*
*/
void Process() {
old_read_ = read_;
int raw = analogRead(pin_);
read_ = map(raw, 0, MAX_INPUT, low_, high_);
read_ = constrain(read_ - offset_, -512, 512);
if (inverted_)
read_ = -read_;
}
void AdjustCalibrationLow(int amount) { low_ += amount; } // Set calibration values.
void AdjustCalibrationHigh(int amount) { high_ += amount; } void AdjustCalibrationLow(int amount) { low_ += amount; }
void SetOffset(float percent) { offset_ = -(percent)*512; } void AdjustCalibrationHigh(int amount) { high_ += amount; }
void SetAttenuation(float percent) { void SetOffset(float percent) { offset_ = -(percent) * 512; }
low_ = abs(percent) * CALIBRATED_LOW;
high_ = abs(percent) * CALIBRATED_HIGH;
inverted_ = percent < 0;
}
/** void SetAttenuation(float percent) {
* Get the current value of the analog input within a range of +/-512. low_ = abs(percent) * CALIBRATED_LOW;
* high_ = abs(percent) * CALIBRATED_HIGH;
* @return read value within a range of +/-512. inverted_ = percent < 0;
* }
*/
inline int16_t Read() { return read_; }
/** /**
* Return the analog read value as voltage. * Get the current value of the analog input within a range of +/-512.
* *
* @return A float representing the voltage (-5.0 to +5.0). * @return read value within a range of +/-512.
* *
*/ */
inline float Voltage() { return ((read_ / 512.0) * 5.0); } inline int16_t Read() { return read_; }
private: /**
uint8_t pin_; * Return the analog read value as voltage.
int16_t read_; *
uint16_t old_read_; * @return A float representing the voltage (-5.0 to +5.0).
// calibration values. *
int offset_ = 0; */
int low_ = CALIBRATED_LOW; inline float Voltage() { return ((read_ / 512.0) * 5.0); }
int high_ = CALIBRATED_HIGH;
bool inverted_ = false; /**
* Checks for a rising edge transition across a threshold.
*
* @param threshold The value that the input must cross.
* @return True if the value just crossed the threshold from below, false
* otherwise.
*/
inline bool IsRisingEdge(int16_t threshold) const {
bool was_high = old_read_ > threshold;
bool is_high = read_ > threshold;
return is_high && !was_high;
}
private:
uint8_t pin_;
int16_t read_;
uint16_t old_read_;
// calibration values.
int offset_ = 0;
int low_ = CALIBRATED_LOW;
int high_ = CALIBRATED_HIGH;
bool inverted_ = false;
}; };
#endif #endif

View File

@ -4,7 +4,7 @@
* @brief Class for interacting with trigger / gate outputs. * @brief Class for interacting with trigger / gate outputs.
* @version 0.1 * @version 0.1
* @date 2025-04-17 * @date 2025-04-17
* *
* @copyright MIT - (c) 2025 - Adam Wonak - adam.wonak@gmail.com * @copyright MIT - (c) 2025 - Adam Wonak - adam.wonak@gmail.com
* *
*/ */
@ -16,79 +16,81 @@
const byte DEFAULT_TRIGGER_DURATION_MS = 5; const byte DEFAULT_TRIGGER_DURATION_MS = 5;
class DigitalOutput { class DigitalOutput {
public: public:
/** /**
* Initializes an CV Output paired object. * Initializes an CV Output paired object.
* *
* @param cv_pin gpio pin for the cv output * @param cv_pin gpio pin for the cv output
*/ */
void Init(uint8_t cv_pin) { void Init(uint8_t cv_pin) {
pinMode(cv_pin, OUTPUT); // Gate/Trigger Output pinMode(cv_pin, OUTPUT); // Gate/Trigger Output
cv_pin_ = cv_pin; cv_pin_ = cv_pin;
trigger_duration_ = DEFAULT_TRIGGER_DURATION_MS; trigger_duration_ = DEFAULT_TRIGGER_DURATION_MS;
}
/**
* Set the trigger duration in miliseconds.
*
* @param duration_ms trigger duration in miliseconds
*/
void SetTriggerDuration(uint8_t duration_ms) {
trigger_duration_ = duration_ms;
}
/**
* Turn the CV and LED on or off according to the input state.
*
* @param state Arduino digital HIGH or LOW values.
*/
inline void Update(uint8_t state) {
if (state == HIGH)
High(); // Rising
if (state == LOW)
Low(); // Falling
}
// Sets the cv output HIGH to about 5v.
inline void High() { update(HIGH); }
// Sets the cv output LOW to 0v.
inline void Low() { update(LOW); }
/**
* Begin a Trigger period for this output.
*/
inline void Trigger() {
update(HIGH);
last_triggered_ = millis();
}
/**
* Return a bool representing the on/off state of the output.
*/
inline void Process() {
// If trigger is HIGH and the trigger duration time has elapsed, set the
// output low.
if (on_ && (millis() - last_triggered_) >= trigger_duration_) {
update(LOW);
} }
}
/** /**
* Set the trigger duration in miliseconds. * Return a bool representing the on/off state of the output.
* *
* @param duration_ms trigger duration in miliseconds * @return true if current cv state is high, false if current cv state is low
*/ */
void SetTriggerDuration(uint8_t duration_ms) { inline bool On() { return on_; }
trigger_duration_ = duration_ms;
}
/** private:
* Turn the CV and LED on or off according to the input state. unsigned long last_triggered_;
* uint8_t trigger_duration_;
* @param state Arduino digital HIGH or LOW values. uint8_t cv_pin_;
*/ bool on_;
inline void Update(uint8_t state) {
if (state == HIGH) High(); // Rising
if (state == LOW) Low(); // Falling
}
// Sets the cv output HIGH to about 5v. void update(uint8_t state) {
inline void High() { update(HIGH); } digitalWrite(cv_pin_, state);
on_ = state == HIGH;
// Sets the cv output LOW to 0v. }
inline void Low() { update(LOW); }
/**
* Begin a Trigger period for this output.
*/
inline void Trigger() {
update(HIGH);
last_triggered_ = millis();
}
/**
* Return a bool representing the on/off state of the output.
*/
inline void Process() {
// If trigger is HIGH and the trigger duration time has elapsed, set the output low.
if (on_ && (millis() - last_triggered_) >= trigger_duration_) {
update(LOW);
}
}
/**
* Return a bool representing the on/off state of the output.
*
* @return true if current cv state is high, false if current cv state is low
*/
inline bool On() { return on_; }
private:
unsigned long last_triggered_;
uint8_t trigger_duration_;
uint8_t cv_pin_;
uint8_t led_pin_;
bool on_;
void update(uint8_t state) {
digitalWrite(cv_pin_, state);
on_ = state == HIGH;
}
}; };
#endif #endif