Johannus 900

/*

Johannus 900  Encoder

 ==============================================

Apologies for the double spacing. It's a Google thing I can't squelch.

This is a Beta test version and only the keyboard scans have been implemented at this point.

 

This sketch is designed to scan a pair of keyboards, the pedal, two expression pedal and 18 pistons for 

a Johannus. The Johannus has it's diodes reversed from the preferred direction and is active HIGH. 

For that reason the scanning procedure is turned "inside-out".


Referring to the pin numbers on the keyboard schematic for this organ, the Swell (CON3) is scanned by sequentially bringing 

Pins P0,P1,P2,P3,P4,P5,P6,P7 LOW  (Arduino pins 22,24,26,28,30,32,34,36) 

pins SW0, SW1,SW2,SW3,SW4,SW5,SW6,SW7 (Arduino pins 23,25,27,29,31,33,35,37) 

are then scanned to determine which keys are closed. LOW = switch closed. Output on Ch. 3


The Great (CON2) is scanned by sequentially bringing 

pins  P0,P1,P2,P3,P4,P5,P6,P7 LOW (Arduino pins 39,41,43,45,47,49,51,53) 

Pins GR0,GR1,GR2,GR3,GR4,GR5,GR6,GR7  (Arduino pins 38,40,42,44,46,48,50,52)   

are then scanned to determine which keys are closed. LOW = switch closed. Output on Ch. 2


The Pedal (CON5) is scanned by sequentially bringing 

pins P0,P1,P2,P3,P4,P5,P6,P7 LOW (Arduino pins 54,55,56,57,58,59,60,61). 

Pins PD0,PD1,PD2,PD3 (Arduino pins 62,63,64,65,) 

are then scanned to determine which keys are closed. LOW = switch closed. Output on Ch. 1


**********Not yet implemented. Relevant code has been commented out.

 Arduino pins 2 - 12,  14 - 19, 61 - 66, are piston inputs. Output on Ch. 4

 Pistons are parallel wired and the switches share a common ground to the Arduino.

 

Arduino Pins 67, 68, 69: Reserved for analog input (expression pedals). At the moment only pin 69 is implemented.

The controllerArray values will have to be edited to

reflect the voltage range put out by the device. Connect 15k (+/-) pot across Arduino's

5V and Ground. Centre tap goes to pin 67, 68 or 69. Input must never exceed 5V. Output on Ch. 4

Note: ***** Unused analog inputs must be grounded to avoid spurious control messages *****

Alternatively, the calls to the expression pedal procedures can just be left commented out.

                

Equipment: Arduino Mega with one MIDI shield mounted off the Arduino 

Instead of a MIDI shield, two 220 ohm resistors plus a 5-pin DIN MIDI connector can also be used.


 created 2024 SEP 06

 modified 2024 OCT 03     TURNED CODE AROUND FOR DIODE DIRECTION

 by John Coenraads 


 */

 

// Declarations==========================================


//Counters (old Fortran habit)

int i, j, k;


byte colCount; 

byte rowCount;

byte inputBit;


byte noteStatus;

byte noteNumber;        // low C = 36

byte noteVelocity;


const byte debounceCount = 4;           //Note ON if count reaches decounceCount, OFF if count reaches  0

byte swellDebounceArray [100];          //holds debounce count for each Swell switch

byte greatDebounceArray [100];          //holds debounce count for each Great switch

byte pistonDebounceArray [100];         //holds debounce count for each Piston switch

byte pedalDebounceArray [100];         //holds debounce count for each Piston switch


int oldOldExpression = 0, oldExpression = 0, newExpression = 0; // Expression controller values


// The following array represents the controller values output for analog device voltages

// in 0.31V increments ranging from 0 to 5V. Input must never exceed 5V.

// Array can be edited as desired, but must contain exactly 17 entries with values between 0 and 127 only.


byte controllerArray [17] = {0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 127};



//Initialize =========================================================

void setup() 

{

    //  Set MIDI baud rate:

    Serial.begin(31250); 

    

    //Initialize  output (normally high)

    for (i = 23; i < 54; i+=2)

    {

        pinMode (i, OUTPUT);  

        digitalWrite (i, HIGH);

    }

    

    for (i = 54; i < 62; i++)

    {

        pinMode (i, OUTPUT);  

        digitalWrite (i, HIGH);

    }

    

    //Initialize input. Normally high (via internal pullups)

    for (i = 22; i < 53; i+=2)

        { pinMode (i, INPUT_PULLUP);}

    

    for (i = 62; i < 66; i++)

        { pinMode (i, INPUT_PULLUP);}

       

    //Initialize debounce count arrays to zero

    for (i= 0; i < 100; i++){

      swellDebounceArray[i] = 0;

      greatDebounceArray[i] = 0;

      pistonDebounceArray[i] = 0;

      pedalDebounceArray[i] = 0;

     }

}


//Main Loop ===========================================================

void loop() 

{

    scanSwell();

    scanGreat();

    scanPedal();

    //scanPistons();

    //scanExpressionPedal();

}


//Scan Swell keyboard, convert to MIDI and output via port 0, channel 3.

void scanSwell ()

{

  noteNumber = 36;    //start at low C

  for (colCount = 22; colCount < 37; colCount += 2)

  {


    for (rowCount = 23; rowCount < 38; rowCount += 2)

    {

      

      digitalWrite (rowCount, LOW);

      inputBit  = digitalRead(colCount);    //read bit

      if (inputBit == HIGH)                 //bit HIGH, switch open

      {

        turnOFFswell ();

      }

      else                                  //bit LOW, switch closed

      {

        turnONswell ();

      }

      digitalWrite (rowCount, HIGH);

      noteNumber = noteNumber + 1;                    //move on to next note

    }

  }

}

  


//MIDI ON message is sent only if note is not already ON.

void turnONswell ()

{

    if (swellDebounceArray[noteNumber] == 0)

        {

            Serial.write (0x92);          //note ON, channel 3

            Serial.write (noteNumber);

            Serial.write (0x7f);          //medium velocity

            swellDebounceArray[noteNumber] = debounceCount;

        }   

}


//MIDI OFF message is sent only if note is not already OFF.

void turnOFFswell ()

{

    if (swellDebounceArray[noteNumber] == 1)

        {

            Serial.write (0x92);          //note ON, channel 3

            Serial.write (noteNumber);

            Serial.write (0);          //zero velocity = OFF

        }  

        if (swellDebounceArray[noteNumber] > 0)  {swellDebounceArray[noteNumber] -- ;}        

}



//Scan Great keyboard, convert to MIDI and output via port 0, channel .


void scanGreat ()

{

  noteNumber = 36;    //start at low C

  for (colCount = 38; colCount < 53; colCount += 2)

  {


    for (rowCount = 39; rowCount < 54; rowCount += 2)

    {

      

      digitalWrite (rowCount, LOW);

      inputBit  = digitalRead(colCount);    //read bit

      if (inputBit == HIGH)                 //bit HIGH, switch open

      {

        turnOFFgreat ();

      }

      else                                  //bit LOW, switch closed

      {

        turnONgreat ();

      }

      digitalWrite (rowCount, HIGH);

      noteNumber = noteNumber + 1;                    //move on to next note

    }

  }

}

  

  

//MIDI ON message is sent only if note is not already ON.

void turnONgreat ()

{

    if (greatDebounceArray[noteNumber] == 0)

        {

            Serial.write (0x91);          //note ON, channel 2

            Serial.write (noteNumber);

            Serial.write (0x7f);          //medium velocity

            greatDebounceArray[noteNumber] = debounceCount;

        }   

}


//MIDI OFF message is sent only if note is not already OFF.

void turnOFFgreat ()

{

    if (greatDebounceArray[noteNumber] == 1)

        {

            Serial.write (0x91);          //note ON, channel 2

            Serial.write (noteNumber);

            Serial.write (0);          //zero velocity = OFF

        }  

        if (greatDebounceArray[noteNumber] > 0)  {greatDebounceArray[noteNumber] -- ;}        

}


//Scan pedal keyboard, convert to MIDI and output via port 0, channel 1.

void scanPedal ()

{

  noteNumber = 36;    //start at low C

  for (colCount = 62; colCount < 66; colCount++)

  {


    for (rowCount = 54; rowCount < 62; rowCount++)

    {

      

      digitalWrite (rowCount, LOW);

      inputBit  = digitalRead(colCount);    //read bit

      if (inputBit == HIGH)                 //bit HIGH, switch open

      {

        turnOFFpedal ();

      }

      else                                  //bit LOW, switch closed

      {

        turnONpedal ();

      }

      digitalWrite (rowCount, HIGH);

      noteNumber = noteNumber + 1;                    //move on to next note

    }

  }

}

  

  

//MIDI ON message is sent only if note is not already ON.

void turnONpedal ()

{

    if (pedalDebounceArray[noteNumber] == 0)

        {

            Serial.write (0x90);          //note ON, channel 1

            Serial.write (noteNumber);

            Serial.write (0x7f);          //medium velocity

            pedalDebounceArray[noteNumber] = debounceCount;

        }   

}


//MIDI OFF message is sent only if note is not already OFF.

void turnOFFpedal ()

{

    if (pedalDebounceArray[noteNumber] == 1)

        {

            Serial.write (0x90);          //note ON, channel 1

            Serial.write (noteNumber);

            Serial.write (0);          //zero velocity = OFF

        }  

        if (pedalDebounceArray[noteNumber] > 0)  {pedalDebounceArray[noteNumber] -- ;}        

}


/*

//Scan Pistons, convert to MIDI and output via port 0, channel 4.

//Piston feed is permanently wired to ground

void scanPistons ()

    {

    noteNumber = 36;

    

    for (i = 2; i < 13; i++)

        { if  (digitalRead(i) == LOW) {turnONpiston ();} else {turnOFFpiston ();}

             noteNumber++; }

        

    for (i = 14; i < 20; i++)

        { if  (digitalRead(i) == LOW) {turnONpiston ();} else {turnOFFpiston ();}

             noteNumber++; }

        

     for (i = 61; i < 67; i++)

        { if  (digitalRead(i) == LOW) {turnONpiston ();} else {turnOFFpiston ();}

             noteNumber++; }

     }

     

   

//MIDI ON message is sent only if note is not already ON.

void turnONpiston ()

{

    if (pistonDebounceArray[noteNumber] == 0)

        {

            Serial.write (0x93);          //note ON, channel 4 

            Serial.write (noteNumber);

            Serial.write (0x7f);          //medium velocity

            pistonDebounceArray[noteNumber] = debounceCount;

        }   

}


//MIDI OFF message is sent only if note is not already OFF.

void turnOFFpiston ()

{

    if (pistonDebounceArray[noteNumber] == 1)

        {

           Serial.write (0x93);          //note ON, channel 4  

           Serial.write (noteNumber);

           Serial.write (0);          //zero velocity = OFF

        }  

        if (pistonDebounceArray[noteNumber] > 0)  {pistonDebounceArray[noteNumber] -- ;}        

}


//Expression pedal input: 

//Voltage inputs from 0 to 5V are converted to a 10 bit integer from 0 to 1023 by analog to digital converter.

//Adding 1 and dividing by 64, yields an integer from 0 to 16. This is used as an index into the controllerArray

//to select the controller value to be output.


void scanExpressionPedal()

{

  newExpression = analogRead (69);                      //0 to 1023

  newExpression = (newExpression + 1) / 64;             //0 to 16

  if ((newExpression != oldExpression) && (newExpression != oldOldExpression))       // double check avoids jitter

  {

    oldOldExpression = oldExpression;

    oldExpression = newExpression;

    newExpression = controllerArray [newExpression];   //extract controller value from array

    if (newExpression > 127)

    {

      newExpression = 127;                             //correct out of range controller value

    }

    Serial.write (0xB0);                               //controller output on channel 1

    Serial.write (0);                                  //controller number zero

    Serial.write (newExpression);

  }

}

*/