Hi everyone! I’d like to share my first Zynthian build. I’m a music teacher in a secondary school and I’ve realised that Zynthian is the perfect tool for playing MIDI instruments (keyboard, drums, pads…) as it starts right everytime without needing any special attention. My build consists in a Hifiberry Dac+, cheap TFT screen, Pi 3b+ and a Pro Micro for the encoders. I give them power with a 10000 mAh powerbank connected with a switch. It just works great!!! Thank everybody involved in this project as this is maybe one if the most innovative open source inventions that I’ve seen lately. I’m sure this will change the synth world.
Welcome aboard @pablor!
And nice build! Is that duct tape on the outside?
Much love for the tape use
How are you using the pro micro to interface encoders?
I think in this case it’s gaffer tape, very nice touch feeling though.
Hope to own one of the official kits near future! We’ll see…
Yeah! As I’m not that skilled with GPIO and Raspberry, I was left with my Arduino skills and programmed all the encoders to send the midi equivalents. Anyway, that was before I saw that somebody here in the forum shared it Pro Micro solution, didn’t try though.
Interesting! I have done this with STM32.
@smiths73v3 and @wyleu have subsequently worked on similar ideas.
Please share your work with us.
Of course! Mine it’s a very simple implementation but maybe can help somebody. I’ve used Paul Stoffregen’s Encoder library and a very simple time sensitive code (I’m not an expert programmer)
ZynthianMIDI_ProMicro.ino (5.1 KB)
#include <MIDIUSB.h>
#define ENCODER_DO_NOT_USE_INTERRUPTS
#include <Encoder.h>
/*
64 SWITCH_BACK_SHORT E4
63 SWITCH_BACK_BOLD D#4
62 SWITCH_BACK_LONG D4
65 SWITCH_SELECT_SHORT F4
66 SWITCH_SELECT_BOLD F#4
67 SWITCH_SELECT_LONG G4
60 SWITCH_LAYER_SHORT C4
61 SWITCH_LAYER_BOLD C#4
59 SWITCH_LAYER_LONG B3
71 SWITCH_SNAPSHOT_SHORT B4
72 SWITCH_SNAPSHOT_BOLD C5
73 SWITCH_SNAPSHOT_LONG C#5
*/
Encoder layer(0, 1);
Encoder snapshot(3, 2);
Encoder back(5, 4);
Encoder select(7, 6);
// BUTTONS
const int BUTTONS_PINS[4] = {10, 16, 14, 15}; // SELECT, SNAPSHOT, BACK, LAYER
const int SHORT_PRESS_TIME = 300; // Max time to be considered Short Press
const int BOLD_PRESS_TIME = 2000; // Max time to be considered Bold Press
const int DEBOUNCE = 50; // Debounce for the Short Press.
// MIDI
const int SHORT_MIDI[4] = {65, 71, 64, 60}; // SELECT, SNAPSHOT, BACK, LAYER
const int BOLD_MIDI[4] = {66, 72, 63, 61}; // SELECT, SNAPSHOT, BACK, LAYER
const int LONG_MIDI[4] = {67, 73, 62, 59}; // SELECT, SNAPSHOT, BACK, LAYER
const int ENCODERS_LEFT_MIDI[4] = {57, 55, 59, 53}; // ENCODERS LEFT MIDI MESSAGES
const int ENCODERS_RIGHT_MIDI[4] = {56, 54, 58, 52}; // ENCODERS RIGHT MIDI MESSAGES
// ENCODERS
long position[4] = { -999};
long newPos[4] = {};
// Variables will change:
int lastState[4] = {LOW}; // the previous state from the input pin
int currentState[4]; // the current reading from the input pin
unsigned long pressedTime[4] = {0};
unsigned long releasedTime[4] = {0};
void setup() {
for (int i = 0; i < 4; i++) {
pinMode(BUTTONS_PINS[i], INPUT_PULLUP);
}
}
void loop() {
encoders();
buttons();
}
void encoders() {
newPos[0] = layer.read();
newPos[1] = snapshot.read();
newPos[2] = back.read();
newPos[3] = select.read();
if (newPos[0] != position[0]) {
position[0] = newPos[0];
if (position[0] == 4) {
noteOn(15, ENCODERS_RIGHT_MIDI[0], 127);
MidiUSB.flush();
noteOff(15, ENCODERS_RIGHT_MIDI[0], 127);
MidiUSB.flush();
layer.write(0);
}
if (position[0] == -4) {
noteOn(15, ENCODERS_LEFT_MIDI[0], 127);
MidiUSB.flush();
noteOff(15, ENCODERS_LEFT_MIDI[0], 127);
MidiUSB.flush();
layer.write(0);
}
}
if (newPos[1] != position[1]) {
position[1] = newPos[1];
if (position[1] == 4) {
noteOn(15, ENCODERS_RIGHT_MIDI[1], 127);
MidiUSB.flush();
noteOff(15, ENCODERS_RIGHT_MIDI[1], 127);
MidiUSB.flush();
snapshot.write(0);
}
if (position[1] == -4) {
noteOn(15, ENCODERS_LEFT_MIDI[1], 127);
MidiUSB.flush();
noteOff(15, ENCODERS_LEFT_MIDI[1], 127);
MidiUSB.flush();
snapshot.write(0);
}
}
if (newPos[2] != position[2]) {
position[2] = newPos[2];
if (position[2] == 4) {
noteOn(15, ENCODERS_RIGHT_MIDI[2], 127);
MidiUSB.flush();
noteOff(15, ENCODERS_RIGHT_MIDI[2], 127);
MidiUSB.flush();
back.write(0);
}
if (position[2] == -4) {
noteOn(15, ENCODERS_LEFT_MIDI[2], 127);
MidiUSB.flush();
noteOff(15, ENCODERS_LEFT_MIDI[2], 127);
MidiUSB.flush();
back.write(0);
}
}
if (newPos[3] != position[3]) {
position[3] = newPos[3];
if (position[3] == 4) {
noteOn(15, ENCODERS_RIGHT_MIDI[3], 127);
MidiUSB.flush();
noteOff(15, ENCODERS_RIGHT_MIDI[3], 127);
MidiUSB.flush();
select.write(0);
}
if (position[3] == -4) {
noteOn(15, ENCODERS_LEFT_MIDI[3], 127);
MidiUSB.flush();
noteOff(15, ENCODERS_LEFT_MIDI[3], 127);
MidiUSB.flush();
select.write(0);
}
}
}
void buttons() {
// Read the state of the button:
for (int i = 0; i < 4; i++) {
currentState[i] = digitalRead(BUTTONS_PINS[i]);
if (lastState[i] == HIGH && currentState[i] == LOW) // button is pressed
pressedTime[i] = millis();
else if (lastState[i] == LOW && currentState[i] == HIGH) { // button is released
releasedTime[i] = millis();
if ( releasedTime[i] - pressedTime[i] > DEBOUNCE && releasedTime[i] - pressedTime[i] < SHORT_PRESS_TIME ) {
noteOn(15, SHORT_MIDI[i], 127);
MidiUSB.flush();
noteOff(15, SHORT_MIDI[i], 127);
MidiUSB.flush();
}
else if ( releasedTime[i] - pressedTime[i] > SHORT_PRESS_TIME && releasedTime[i] - pressedTime[i] < BOLD_PRESS_TIME ) {
noteOn(15, BOLD_MIDI[i], 127);
MidiUSB.flush();
noteOff(15, BOLD_MIDI[i], 127);
MidiUSB.flush();
}
else if (releasedTime[i] - pressedTime[i] > BOLD_PRESS_TIME) {
noteOn(15, LONG_MIDI[i], 127);
MidiUSB.flush();
noteOff(15, LONG_MIDI[i], 127);
MidiUSB.flush();
}
}
// Save the the last state.
lastState[i] = currentState[i];
}
}
void noteOn(byte channel, byte pitch, byte velocity) {
midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOn);
}
void noteOff(byte channel, byte pitch, byte velocity) {
midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOff);
}
Thanks for sharing @pablor and have fun with zynthian !