My first Zynthian build for school playing

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.

7 Likes

Welcome aboard @pablor!
And nice build! Is that duct tape on the outside?

Nice build! It recalls the first zynthian prototype! :wink:

Congratulations! Enjoy the toy!

2 Likes

Much love for the tape use :grin:

1 Like

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 :sweat_smile: 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);
}
3 Likes

Thanks for sharing @pablor and have fun with zynthian !

1 Like