|
|
|
/*
|
|
|
|
MIDI-Host-Adapter
|
|
|
|
|
|
|
|
(c)2023 H. Wirtz <wirtz@parasitstudio.de>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software Foundation,
|
|
|
|
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <TM1638plus_Model2.h>
|
|
|
|
#include <elapsedMillis.h>
|
|
|
|
#include <limits.h>
|
|
|
|
#include <EEPROM.h>
|
|
|
|
|
|
|
|
#define DEBUG
|
|
|
|
|
|
|
|
#define STROBE_TM 9
|
|
|
|
#define CLOCK_TM 8
|
|
|
|
#define DIO_TM 7
|
|
|
|
#define MIDI_CH_BIT3 3
|
|
|
|
#define MIDI_CH_BIT2 4
|
|
|
|
#define MIDI_CH_BIT1 5
|
|
|
|
#define MIDI_CH_BIT0 2
|
|
|
|
|
|
|
|
bool swap_nibbles = true;
|
|
|
|
bool high_freq = false;
|
|
|
|
|
|
|
|
#define BRIGHTNESS 6
|
|
|
|
#define INITIAL_MIDI_CHANNEL 1
|
|
|
|
#define BUTTON_DEBOUNCE_TIME_MS 200
|
|
|
|
#define BLINK_FREQUENCY_MS 500
|
|
|
|
#define EEPROM_ADDRESS 0x42
|
|
|
|
|
|
|
|
// Constructor object
|
|
|
|
TM1638plus_Model2 tm(STROBE_TM, CLOCK_TM, DIO_TM, swap_nibbles, high_freq);
|
|
|
|
elapsedMillis button_debounce_timer;
|
|
|
|
elapsedMillis blink_timer;
|
|
|
|
uint8_t actual_channel;
|
|
|
|
uint8_t new_channel;
|
|
|
|
|
|
|
|
void setup() {
|
|
|
|
Serial.begin(9600);
|
|
|
|
delay(50);
|
|
|
|
|
|
|
|
#if defined(DEBUG)
|
|
|
|
Serial.println("<SETUP>");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
tm.displayBegin();
|
|
|
|
tm.brightness(BRIGHTNESS);
|
|
|
|
|
|
|
|
actual_channel=read_channel_from_eeprom();
|
|
|
|
new_channel = actual_channel;
|
|
|
|
|
|
|
|
pinMode(MIDI_CH_BIT3, OUTPUT);
|
|
|
|
pinMode(MIDI_CH_BIT2, OUTPUT);
|
|
|
|
pinMode(MIDI_CH_BIT1, OUTPUT);
|
|
|
|
pinMode(MIDI_CH_BIT0, OUTPUT);
|
|
|
|
|
|
|
|
SetMidiChannel(INITIAL_MIDI_CHANNEL);
|
|
|
|
|
|
|
|
tm.DisplayDecNum(actual_channel, 0, false, TMAlignTextRight);
|
|
|
|
|
|
|
|
#if defined(DEBUG)
|
|
|
|
Serial.println("</SETUP>");
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void loop() {
|
|
|
|
uint8_t button;
|
|
|
|
|
|
|
|
button = tm.ReadKey16();
|
|
|
|
|
|
|
|
if (button > 0 && button != actual_channel && button_debounce_timer > BUTTON_DEBOUNCE_TIME_MS) {
|
|
|
|
button_debounce_timer = 0;
|
|
|
|
if (button != new_channel) {
|
|
|
|
// Button pressed once
|
|
|
|
#if defined(DEBUG)
|
|
|
|
Serial.print("Button pressed once: ");
|
|
|
|
Serial.println(button, DEC);
|
|
|
|
#endif
|
|
|
|
blink_timer = 0;
|
|
|
|
new_channel = button;
|
|
|
|
tm.DisplayDecNumNibble(new_channel, actual_channel, 0, false, TMAlignTextRight);
|
|
|
|
} else {
|
|
|
|
// Button pressed twice
|
|
|
|
#if defined(DEBUG)
|
|
|
|
Serial.print("Button pressed twice: ");
|
|
|
|
Serial.println(button, DEC);
|
|
|
|
#endif
|
|
|
|
actual_channel = new_channel;
|
|
|
|
EEPROM.update(EEPROM_ADDRESS, 0xf0 | actual_channel);
|
|
|
|
SetMidiChannel(actual_channel);
|
|
|
|
tm.DisplayDecNum(actual_channel, 0, false, TMAlignTextRight);
|
|
|
|
}
|
|
|
|
} else if (button > 0 && button == actual_channel && button_debounce_timer > BUTTON_DEBOUNCE_TIME_MS) {
|
|
|
|
button_debounce_timer = 0;
|
|
|
|
#if defined(DEBUG)
|
|
|
|
Serial.println("Same button as current MIDI channel pressed, doing nothing.");
|
|
|
|
#endif
|
|
|
|
new_channel = actual_channel;
|
|
|
|
tm.DisplayDecNum(actual_channel, 0, false, TMAlignTextRight);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (actual_channel != new_channel) {
|
|
|
|
if (blink_timer > BLINK_FREQUENCY_MS) {
|
|
|
|
blink_timer = 0;
|
|
|
|
tm.DisplayDecNum(actual_channel, 0, false, TMAlignTextRight);
|
|
|
|
} else if (blink_timer > BLINK_FREQUENCY_MS / 2) {
|
|
|
|
tm.DisplayDecNumNibble(new_channel, actual_channel, 0, false, TMAlignTextRight);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SetMidiChannel(uint8_t channel) {
|
|
|
|
if (channel < 1 || channel > 16) {
|
|
|
|
#if defined(DEBUG)
|
|
|
|
Serial.print("ERROR: MIDI channel number out of range: ");
|
|
|
|
Serial.print(channel, DEC);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(DEBUG)
|
|
|
|
Serial.print("Setting MIDI channel to: ");
|
|
|
|
Serial.print(channel, DEC);
|
|
|
|
Serial.print(" (");
|
|
|
|
printBinary(--channel, 4);
|
|
|
|
Serial.println(")");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
digitalWrite(MIDI_CH_BIT0, channel & 0x01);
|
|
|
|
digitalWrite(MIDI_CH_BIT1, channel & 0x02);
|
|
|
|
digitalWrite(MIDI_CH_BIT2, channel & 0x04);
|
|
|
|
digitalWrite(MIDI_CH_BIT3, channel & 0x08);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t read_channel_from_eeprom(void) {
|
|
|
|
uint8_t tmp_channel = EEPROM.read(EEPROM_ADDRESS);
|
|
|
|
if (tmp_channel & 0xf0 != 0xf0) {
|
|
|
|
actual_channel = 1;
|
|
|
|
#if defined(DEBUG)
|
|
|
|
Serial.println("Setting channel to 1");
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
actual_channel = 0x0f & tmp_channel;
|
|
|
|
#if defined(DEBUG)
|
|
|
|
Serial.print("Reading channel from EEPROM: ");
|
|
|
|
Serial.println(actual_channel, DEC);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(DEBUG)
|
|
|
|
void printBinary(uint32_t value, uint8_t len) {
|
|
|
|
for (uint8_t b = len; b > 0; --b) {
|
|
|
|
Serial.print((char)('0' + ((value >> (b - 1)) & 1)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|