Page 3 of 3

Re: Custom, comprehensive "Nord Stage Pedal Board"

Posted: 04 Nov 2017, 14:58
by ericL
guentergunter wrote:
ericL wrote:This is super awesome!!! You really put in a lot of time and effort on this!
I have a related project that is not nearly as complex, but has a similar end result. Posting in a separate thread.
Maybe you wanna share the link? Couldn't find it acually... ;)
Here you go:

http://www.norduserforum.com/nord-stage ... 14152.html

Re: Custom, comprehensive "Nord Stage Pedal Board"

Posted: 22 Feb 2018, 00:18
by pianopop9
nevermind - I got it. Apparently you can't use the 1-128 instead of 0-128 in the midi settings. Thx!

Re: Custom, comprehensive "Nord Stage Pedal Board"

Posted: 13 Dec 2020, 17:03
by Klondyke77
guentergunter wrote:
Honestly, all that ain't necessary to be calculated...
After two days of experimenting, I found out how to reproduce the pedal noise via MIDI and it's working like a charm.
See program code in the first post :thumbup:
Hi guentergunter,
A bit late I found your post :shifty:, but do find it quite interesting how to handle the midi commands to get damper noise. :thumbup:
In the beginning of the year I managed to build a ardunio based Pedal-to-Midi Box based on your posts and sketches 8-)
Especially I have half pedaling with the box. But I needed to turn down damper noise in System menu since I get unwanted pedal noise at pressing or releasing damper pedal. (Now I know: This happens if continuosly sending Midi values on CC64 on pedal movements :clap: )

Reiner

Re: Ultimate NORD Stage MIDI Pedal Board

Posted: 25 Dec 2020, 16:49
by Klondyke77
Hi all,

as guentergunter did, I did some research and reprogramming and could now finalize my "Pedal-To-MIDI Box". :D
My box supports not only 6 pedals but now it supports Pedal Noise and Half Pedal with a Sustain pedal in continous mode (Roland DP-10 in my case).
I had 2 main intentions for the box:
1. Reduce cabling going up to NS3. Keep cabling of (max.) 6 pedals on the floor. (normally I don't need all of them - just to be prepared)
2. Replace expensive Nord Triple Pedal

So finally this box can replace the Nord Triple Pedal with all of it's special features - and more! :thumbup:

My box is based on an Arduino Pro Mini 328 - 5V/16MHz.
Schematic is based on download/file.php?id=9232 (in this thread).
Modifications (based on my preferations):
- MIDI IN: left out
- Program up/down switches: left out
+ Rotary Speed toggle switch
+ Soft (left) pedal
+ Organ volume

I don't post photos of my box since it is a prototype and not very nice :roll:
It is just a small plastic case which can hold the In and Out Jacks and the board with the arduino inside - nothing fancy at all.

Here's my finalized code:

Code: Select all

//
// Midi Box for Nord Stage 3
// by Klondyke77 03/2020
// Finalized V2.0: 16.03.2020
// V2.1: 19.12.2020 (starting improvement tests)
// Trying new code for sustain pedal
// Reducing of Midi commands for sustain pedal successfull
// V2.3: 20.12.2020 (continuing from 2.1, discarding V2.2)
// Pedal noise successfull. Calibrated sustain pedal trigger positions
// V2.4: 21.12.2020 implementing sustain pedal speed detection
// Sustain pedal including Half pedal and Pedal noise is working.
// code may still need some finalizing.
// V2.5: 25.12.2020 code finalizing and documenting
// ---------------------------------
// Contributions:
// code is based on:
// https://www.norduserforum.com/nord-stage-forum-f3/custom-comprehensive-nord-stage-pedal-board-t14141.html
// by "guentergunter"
// My own "Pedal-to-MIDI Box" is based on an Arduino Pro Mini 328 - 5V/16MHz
// Schematic is based on: https://www.norduserforum.com/download/file.php?id=9232 (in above thread)
// Schematic is modified:
// - MIDI IN: left out (not needed)
// - Program up/down switches: left out (not needed)
// + Rotary Speed toggle switch
// + Soft (left) pedal
// + Organ volume
// ---------------------------------
// Main improvement to V2.0 is "Velocity dependant Damper Noise" thanks to engineering of guentergunter!
// New findings regarding Sustain pedal in continous mode:
//
// In the following "send values" means always sending a Midi message on Control Channel (CC) 64, 
// which is Standard Control Channel for Sustain.
// 
// 1. My last implementation of continous sustain pedal was to send position values with every movement of the pedal. 
// This is also the way other manufacturers handle Midi messages with a continous Sustain Pedal (My Yamaha Clavinova does it this way). 
// But with the new knowledge I assume that this is not the way Nord has intended to handle it. 
// This may be the explanation why you get unintended Pedal Noise when doing so. 
// You would have to disable Pedal Noise per program - so did I - until now ;-)
// 
// 2. What Nord expects:
// - Send just one Midi message when pedal meets positions "Up / Half / Down", values: 0 / 64 / 127.
// - To activate Damper Noise via Midi:
//    + Send additional Midi message (on CC 64) right before position message Up or Down
//    + Slow pedal movement (low velocity) is represented by lower values and will cause light and silent pedal noise.
//    + Fast pedal movement (high velocity) is represented by higher values and will cause strong and loud(er) pedal noise.
//    + There are different value ranges for pedal up and pedal down:
//       * Pedal down: 27..42
//       * Pedal up:   10..18
//   Example for pedal down:
//   When pedal reaches defined position for pedal down, the following two commands are sent: 
//     send_MIDI_message(0xB0, MIDI_CC_Sustain, 30); // assuming 30 as derived damper speed
//     send_MIDI_message(0xB0, MIDI_CC_Sustain, 127); // message for pedal down
//           
//     + Speed detection is needed to make the values (2.) velocity dependant.
//        I decided to derive speed from measuring delta pedal position value in a
//        specified amount of time (sample interval).
//        Actually damper_speed is calculated continously but only used once when
//        damper positions down or up is reached.
//        There maybe ways to improve speed detection but for now I'm happy to have 
//        it working fine!
//  3. Half pedal can be realized by sending value 64 once when pedal reaches defined position ("Half").
//     Nord Stage 3 has only one position of half pedal implemented with corresponding samples - not more.
// ---------------------------------

//
// Toggle Debug Mode
// Debug mode configures/redirects serial output for serial monitoring
//#define DEBUG

    #ifdef DEBUG
      const int SERIAL_PORT_RATE = 9600;
    #else
      const int SERIAL_PORT_RATE = 31250;
    #endif


//
// MIDI CC (Control Channel) Numbers for NORD STAGE 3 Keyboard
//
const int MIDI_CC_Sustain = 64;          
const int MIDI_CC_Sostenuto = 66;          
const int MIDI_CC_SoftPedal = 67;
const int MIDI_CC_CrtlPedalExpr = 11;
const int MIDI_CC_RotarySpeed = 108;
const int MIDI_CC_OrganLevel = 13;
const int MIDI_CC_OrganSwell = 4;
//
// MIDI constant values
//
const int RotarySlow = 43;
const int RotaryFast = 127;


#define MIDI_Channel 0

// Pin Numbers

#define PIN_orgs A0     // Organ Swell Pedal (continous) Roland only!
#define PIN_sust A1     // Sustain Pedal (continous)     Roland only!
#define PIN_expr A2     // Expression Pedal (continous) Yamaha only!
#define PIN_sost 2      // Sostenuto Pedal (momentary)
#define PIN_soft 3      // Soft Pedal (momentary)
#define PIN_rota 4      // Rotary Speed (momentary)

// constant values
const int POT_THRESHOLD = 3;            // Threshold amount to guard against false values

// variable init
uint8_t sost = 0;
uint8_t sost_last = 0;
uint8_t soft = 0;
uint8_t soft_last = 0;
uint8_t rota = 0;
uint8_t rota_last = 0;
uint8_t rotary_state = 0;
uint8_t damper = 0;
uint8_t damper_last = 0;

// variables for speed detection
unsigned long currentMillis;
unsigned long previousMillis = 0;
unsigned long time_delta;
int damper_val1 = 0;
int damper_val2 = 0;
int damper_delta = 0;
const long interval = 50; // sample time in milliseconds
bool read_val = true;
int damper_speed = 0;

//
// End variable declaration/initialization
//

//
// Functions
//

void send_MIDI_message (byte cmd, byte byte1, byte byte2)
{
#ifdef DEBUG
  // redirect output in Debug mode
  char buf[100];
  sprintf(buf, "%d %d %d",cmd + MIDI_Channel, byte1, byte2);
  Serial.println(buf);
#else
  Serial.write(cmd + MIDI_Channel);
  Serial.write(byte1);
  Serial.write(byte2);
#endif
}


void setup()                            // Run once, when the sketch starts
{
    pinMode(PIN_sost, INPUT_PULLUP);
    pinMode(PIN_soft, INPUT_PULLUP);
    pinMode(PIN_rota, INPUT_PULLUP);
    Serial.begin(SERIAL_PORT_RATE);     // Starts communication with the serial port
    Serial.flush();
}

void loop()                             // Run over and over again
{
    
// **************************************
// Momentary Switches
// **************************************
    
    //
    // Sostenuto (Middle) Pedal (momentary/closed)
    //    
    sost = digitalRead(PIN_sost); 

    if (sost != sost_last)
    {
      sost_last = sost;
      send_MIDI_message(0xB0, MIDI_CC_Sostenuto, (sost * 127));
    }
    
    //
    // Soft (Left) Pedal (momentary/closed)
    //    
    soft = digitalRead(PIN_soft); 

    if (soft != soft_last)
    {
      soft_last = soft;
      send_MIDI_message(0xB0, MIDI_CC_SoftPedal, (soft * 127));
    }

    //
    // Rotary Speed switch (momentary/closed)
    // (toggle switch)
    //    
    rota = digitalRead(PIN_rota); 

    if (rota != rota_last && rota == 1)
    {
      if (rotary_state == 0)
      { 
        rotary_state = 1; // Rotary fast
        send_MIDI_message(0xB0, MIDI_CC_RotarySpeed, RotaryFast);
      }  
      else if (rotary_state == 1)
      { 
        rotary_state = 0; // Rotary slow
        send_MIDI_message(0xB0, MIDI_CC_RotarySpeed, RotarySlow);
      }
    }
    rota_last = rota;


// **************************************
// Continous Pedals
// **************************************
   
    //
    // Sustain Pedal (continous)
    // Values calibrated for: 
    // Roland DP10 in continous mode (Min=35, Max=1020)
    //
    
      //
      // Sustain Pedal - Speed detection
      //
      
      currentMillis = millis();
      // read and keep first value until second val is read
      if (read_val)
      {
        damper_val1 = analogRead(PIN_sust);
        read_val = false;
      }
  
      // wait interval time befor reading second value
      if (currentMillis - previousMillis >= interval)
      {
        previousMillis = currentMillis;
        damper_val2 = analogRead(PIN_sust);
        damper_delta = damper_val2 - damper_val1;
        // enable next speed detection
        read_val = true;
  
        // disable noise
        if (abs(damper_delta) > 2)
        {
           // calculate Midi values for damper speed
           // pedal down (delta positive): 27..42
           // pedal up (delta negative): 10..18
  
           if (damper_delta > 0) // pedal down
           {
              damper_speed = map (damper_delta, 0, 950, 27, 42);
              damper_speed = constrain (damper_speed, 27, 42);
           }
           if (damper_delta < 0) // pedal up
           {
              damper_speed = map (damper_delta, 0, -900, 10, 18);
              damper_speed = constrain (damper_speed, 10, 18);
           }
          /*
          #ifdef DEBUG
              // monitoring output
              char buf[120];
              sprintf(buf, "damper_delta:%4d damper_speed %2d", damper_delta, damper_speed);
              Serial.println(buf);
          #endif
          */
        }
      }
  
      //
      // Sustain Pedal 
      // position detection and MIDI output
      //
      
      static int s_SPmin = 30;
      static int s_SPmax = 1000;
      static int dposMid = 45;  // treshold relase to half-pressed
      static int dposHigh = 65; // treshold half-pressed to pressed
  
      damper_val2 = constrain((damper_val2),s_SPmin, s_SPmax);
      int damper_pos = map(damper_val2, s_SPmin, s_SPmax, 0, 127);  // Map the value to 0-127
      
      // Manually defined mapping to be able to define non-linear 
      // pedal positions for half-pedal and down position.
      // (calculated positions via map command did not fit my expections)
      //
      if (damper_pos <= dposMid) { 
          damper = 0; 
      }
      else if (damper_pos > dposMid and damper_pos <= dposHigh) {
          damper = 1; 
      }
      else
      { 
          damper = 2;
      }
        
      if(damper != damper_last)
      {
        damper_last = damper;
       
        switch (damper) 
        {
          case 0:
            send_MIDI_message(0xB0, MIDI_CC_Sustain, damper_speed);
            send_MIDI_message(0xB0, MIDI_CC_Sustain, 0);
            break;
          case 1:
            send_MIDI_message(0xB0, MIDI_CC_Sustain, 64);
            break;
          case 2:
            send_MIDI_message(0xB0, MIDI_CC_Sustain, damper_speed);
            send_MIDI_message(0xB0, MIDI_CC_Sustain, 127);
            break;
        }
       
      }
   
    //
    // Expression pedal (continous)
    // Values calibrated for: 
    // Yamaha EV-7 (Min=0, Max=1019)
    //
    
    static int s_EPmin = 0;
    static int s_EPmax = 1019;
    static int s_nLastPot0Value = 0;
    static int s_nLastMapped0Value = 0;

    int nCurrentPot0Value = analogRead(PIN_expr);
    if(abs(nCurrentPot0Value - s_nLastPot0Value) >= POT_THRESHOLD)
    {    
      s_nLastPot0Value = nCurrentPot0Value;
      nCurrentPot0Value = constrain( (nCurrentPot0Value),s_EPmin, s_EPmax);
      
      int nMapped0Value = map(nCurrentPot0Value, s_EPmin, s_EPmax, 0, 127);  // Map the value to 0-127
      if(nMapped0Value != s_nLastMapped0Value)
      {
        s_nLastMapped0Value = nMapped0Value;
        send_MIDI_message(0xB0, MIDI_CC_CrtlPedalExpr, nMapped0Value);
      }
    }

    //
    // Organ Swell pedal (continous)
    // Values calibrated for: 
    // Roland EV-7 (Min=0, Max=900)
    //
    
    static int s_SWmin = 0;
    static int s_SWmax = 900;
    static int s_nLastPot2Value = 0;
    static int s_nLastMapped2Value = 0;

    int nCurrentPot2Value = analogRead(PIN_orgs);
    if(abs(nCurrentPot2Value - s_nLastPot2Value) >= POT_THRESHOLD)
    {    
      s_nLastPot2Value = nCurrentPot2Value;
      int nMapped2Value = map(nCurrentPot2Value, s_SWmin, s_SWmax, 0, 127);  // Map the value to 0-127
      nCurrentPot2Value = constrain( (nCurrentPot2Value),s_SWmin, s_SWmax);

      if(nMapped2Value != s_nLastMapped2Value)
      {

        s_nLastMapped2Value = nMapped2Value;
        send_MIDI_message(0xB0, MIDI_CC_OrganSwell, nMapped2Value);
      }
    }

}
Hope this helps other people to realize similar projects.
Reiner

Re: Ultimate NORD Stage MIDI Pedal Board

Posted: 24 Mar 2024, 21:16
by technlgyst
Digging up an old thread, but nonetheless: nice work! I went the MIDI route and created an app that enables the triggering of pedal noises even when a switch-type sustain pedal is used. It also has a function to do half-pedalling, but you need to manually switch for that to work with your switch-type pedal. That's the downside, but on the upside: installing and using an app is easily done. Check out my other post for more info.